[9] | 1 | /* ref2.c */
|
---|
| 2 |
|
---|
| 3 | /* This is a totally rewritten version of ref. This version looks for the
|
---|
| 4 | * desired function name in the "tags" file, and then reads the header out
|
---|
| 5 | * from the source file. There is no longer any need for a "refs" file.
|
---|
| 6 | *
|
---|
| 7 | * Usage: ref [-a] [-t] [-f file] [-c class] tag
|
---|
| 8 | * Options: -t output tag info, not the description
|
---|
| 9 | * -f file default filename for static functions
|
---|
| 10 | * -c class default class names for class functions
|
---|
| 11 | */
|
---|
| 12 |
|
---|
| 13 | #include <stdio.h>
|
---|
| 14 | #include "config.h"
|
---|
| 15 | extern char *getenv();
|
---|
| 16 | extern char *fgets();
|
---|
| 17 |
|
---|
| 18 |
|
---|
| 19 | /* This is the default path that is searched for tags */
|
---|
| 20 | #if OSK
|
---|
| 21 | # define DEFTAGPATH ".:/dd/defs:/dd/defs/sys:/dd/usr/src/lib:../lib:/dd/usr/lib"
|
---|
| 22 | #else
|
---|
| 23 | # if ANY_UNIX
|
---|
| 24 | # define DEFTAGPATH ".:/usr/include:/usr/include/sys:/usr/src/lib:../lib:/usr/local/lib"
|
---|
| 25 | # else
|
---|
| 26 | # if MSDOS || TOS
|
---|
| 27 | # define DEFTAGPATH ".;C:\\include;C:\\include\\sys;C:\\lib;..\\lib"
|
---|
| 28 | # define SEP ';'
|
---|
| 29 | # else
|
---|
| 30 | # if AMIGA
|
---|
| 31 | # define DEFTAGPATH ".;Include:;Include:sys"
|
---|
| 32 | # define SEP ';'
|
---|
| 33 | # else /* any other OS */
|
---|
| 34 | # define DEFTAGPATH "."
|
---|
| 35 | # endif
|
---|
| 36 | # endif
|
---|
| 37 | # endif
|
---|
| 38 | #endif
|
---|
| 39 |
|
---|
| 40 | #ifndef SEP
|
---|
| 41 | # define SEP ':'
|
---|
| 42 | #endif
|
---|
| 43 |
|
---|
| 44 |
|
---|
| 45 | /* These variables reflect the command-line options given by the user. */
|
---|
| 46 | int taginfo; /* boolean: give only the tag info? (not header?) */
|
---|
| 47 | char *def_file; /* default filename for static functions */
|
---|
| 48 | char *def_class; /* default classname for class members */
|
---|
| 49 | int colons; /* #colons in tag: 0=normal, 1=static, 2=member */
|
---|
| 50 |
|
---|
| 51 | /* This function checks for a tag in the "tags" file of given directory.
|
---|
| 52 | * If the tag is found, then it returns a pointer to a static buffer which
|
---|
| 53 | * contains the filename, a tab character, and a linespec for finding the
|
---|
| 54 | * the tag. If the tag is not found in the "tags" file, or if the "tags"
|
---|
| 55 | * file cannot be opened or doesn't exist, then this function returns NULL.
|
---|
| 56 | */
|
---|
| 57 | char *cktagdir(tag, dir)
|
---|
| 58 | char *tag; /* name of the tag to look for */
|
---|
| 59 | char *dir; /* name of the directory to check */
|
---|
| 60 | {
|
---|
| 61 | char buf[BLKSIZE];
|
---|
| 62 | static char found[BLKSIZE];
|
---|
| 63 | FILE *tfile;
|
---|
| 64 | int len;
|
---|
| 65 |
|
---|
| 66 | #if AMIGA
|
---|
| 67 | if (dir[strlen(dir) - 1] == COLON)
|
---|
| 68 | sprintf(buf, "%s%s", dir, TAGS); /* no slash after colon. */
|
---|
| 69 | else
|
---|
| 70 | #endif
|
---|
| 71 | /* construct the name of the "tags" file in this directory */
|
---|
| 72 | sprintf(buf, "%s%c%s", dir, SLASH, TAGS);
|
---|
| 73 |
|
---|
| 74 | /* Try to open the tags file. Return NULL if can't open */
|
---|
| 75 | #if AMIGA
|
---|
| 76 | if (buf[0] == '.' && buf[1] == SLASH)
|
---|
| 77 | tfile = fopen(&buf[2], "r");
|
---|
| 78 | else
|
---|
| 79 | #endif
|
---|
| 80 | tfile = fopen(buf, "r");
|
---|
| 81 | if (!tfile)
|
---|
| 82 | {
|
---|
| 83 | return (char *)0;
|
---|
| 84 | }
|
---|
| 85 |
|
---|
| 86 | /* compute the length of the tagname once */
|
---|
| 87 | len = strlen(tag);
|
---|
| 88 |
|
---|
| 89 | /* read lines until we get the one for this tag */
|
---|
| 90 | found[0] = '\0';
|
---|
| 91 | while (fgets(buf, sizeof buf, tfile))
|
---|
| 92 | {
|
---|
| 93 | /* is this the one we want? */
|
---|
| 94 | if (!strncmp(buf, tag, len) && buf[len] == '\t')
|
---|
| 95 | {
|
---|
| 96 | /* we've found a match -- remember it */
|
---|
| 97 | strcpy(found, buf);
|
---|
| 98 |
|
---|
| 99 | /* if there is no default file, or this match is in
|
---|
| 100 | * the default file, then we've definitely found the
|
---|
| 101 | * one we want. Break out of the loop now.
|
---|
| 102 | */
|
---|
| 103 | if (!def_file || !strncmp(&buf[len + 1], def_file, strlen(def_file)))
|
---|
| 104 | {
|
---|
| 105 | break;
|
---|
| 106 | }
|
---|
| 107 | }
|
---|
| 108 | }
|
---|
| 109 |
|
---|
| 110 | /* we're through reading */
|
---|
| 111 | fclose(tfile);
|
---|
| 112 |
|
---|
| 113 | /* if there's anything in found[], use it */
|
---|
| 114 | if (found[0])
|
---|
| 115 | {
|
---|
| 116 | return &found[len + 1];
|
---|
| 117 | }
|
---|
| 118 |
|
---|
| 119 | /* else we didn't find it */
|
---|
| 120 | return (char *)0;
|
---|
| 121 | }
|
---|
| 122 |
|
---|
| 123 | /* This function reads a single textline from a binary file. It returns
|
---|
| 124 | * the number of bytes read, or 0 at EOF.
|
---|
| 125 | */
|
---|
| 126 | int getline(buf, limit, fp)
|
---|
| 127 | char *buf; /* buffer to read into */
|
---|
| 128 | int limit; /* maximum characters to read */
|
---|
| 129 | FILE *fp; /* binary stream to read from */
|
---|
| 130 | {
|
---|
| 131 | int bytes; /* number of bytes read so far */
|
---|
| 132 | int ch; /* single character from file */
|
---|
| 133 |
|
---|
| 134 | for (bytes = 0, ch = 0; ch != '\n' && --limit > 0 && (ch = getc(fp)) != EOF; bytes++)
|
---|
| 135 | {
|
---|
| 136 | #if MSDOS || TOS
|
---|
| 137 | /* since this is a binary file, we'll need to manually strip CR's */
|
---|
| 138 | if (ch == '\r')
|
---|
| 139 | {
|
---|
| 140 | continue;
|
---|
| 141 | }
|
---|
| 142 | #endif
|
---|
| 143 | *buf++ = ch;
|
---|
| 144 | }
|
---|
| 145 | *buf = '\0';
|
---|
| 146 |
|
---|
| 147 | return bytes;
|
---|
| 148 | }
|
---|
| 149 |
|
---|
| 150 |
|
---|
| 151 | /* This function reads a source file, looking for a given tag. If it finds
|
---|
| 152 | * the tag, then it displays it and returns TRUE. Otherwise it returns FALSE.
|
---|
| 153 | * To display the tag, it attempts to output any introductory comment, the
|
---|
| 154 | * tag line itself, and any arguments. Arguments are assumed to immediately
|
---|
| 155 | * follow the tag line, and start with whitespace. Comments are assumed to
|
---|
| 156 | * start with lines that begin with "/*", "//", "(*", or "--", and end at the
|
---|
| 157 | * tag line or at a blank line.
|
---|
| 158 | */
|
---|
| 159 | int lookup(dir, entry)
|
---|
| 160 | char *dir; /* name of the directory that contains the source */
|
---|
| 161 | char *entry; /* source filename, <Tab>, linespec */
|
---|
| 162 | {
|
---|
| 163 | char buf[BLKSIZE]; /* pathname of sourcefile */
|
---|
| 164 | long lnum; /* line number */
|
---|
| 165 | long here; /* seek position where current line began */
|
---|
| 166 | long comment; /* seek position of introductory comment, or -1L */
|
---|
| 167 | FILE *sfile; /* used for reading the source file */
|
---|
| 168 | int len; /* length of string */
|
---|
| 169 | char *ptr;
|
---|
| 170 |
|
---|
| 171 |
|
---|
| 172 | /* construct the pathname of the source file */
|
---|
| 173 | strcpy(buf, dir);
|
---|
| 174 | ptr = buf + strlen(buf);
|
---|
| 175 | #if AMIGA
|
---|
| 176 | if (ptr[-1] != COLON)
|
---|
| 177 | #endif
|
---|
| 178 | *ptr++ = SLASH;
|
---|
| 179 | while (*entry != '\t')
|
---|
| 180 | {
|
---|
| 181 | *ptr++ = *entry++;
|
---|
| 182 | }
|
---|
| 183 | *ptr = '\0';
|
---|
| 184 | entry++;
|
---|
| 185 |
|
---|
| 186 | /* searching for string or number? */
|
---|
| 187 | if (*entry >= '0' && *entry <= '9')
|
---|
| 188 | {
|
---|
| 189 | /* given a specific line number */
|
---|
| 190 | lnum = atol(entry);
|
---|
| 191 | entry = (char *)0;
|
---|
| 192 | }
|
---|
| 193 | else
|
---|
| 194 | {
|
---|
| 195 | /* given a string -- strip off "/^" and "$/\n" */
|
---|
| 196 | entry += 2;
|
---|
| 197 | len = strlen(entry) - 2;
|
---|
| 198 | if (entry[len - 1] == '$')
|
---|
| 199 | {
|
---|
| 200 | entry[len - 1] = '\n';
|
---|
| 201 | }
|
---|
| 202 | lnum = 0L;
|
---|
| 203 | }
|
---|
| 204 |
|
---|
| 205 | /* Open the file. Note that we open the file in binary mode even
|
---|
| 206 | * though we know it is a text file, because ftell() and fseek()
|
---|
| 207 | * don't work on text files.
|
---|
| 208 | */
|
---|
| 209 | #if MSDOS || TOS
|
---|
| 210 | sfile = fopen(buf, "rb");
|
---|
| 211 | #else
|
---|
| 212 | # if AMIGA
|
---|
| 213 | if (buf[0] == '.' && buf[1] == SLASH)
|
---|
| 214 | sfile = fopen(&buf[2], "r");
|
---|
| 215 | else
|
---|
| 216 | # endif
|
---|
| 217 | sfile = fopen(buf, "r");
|
---|
| 218 | #endif
|
---|
| 219 | if (!sfile)
|
---|
| 220 | {
|
---|
| 221 | /* can't open the real source file. Try "refs" instead */
|
---|
| 222 | #if AMIGA
|
---|
| 223 | if (dir[strlen(dir) - 1] == COLON)
|
---|
| 224 | sprintf(buf, "%srefs", dir);
|
---|
| 225 | else
|
---|
| 226 | #endif
|
---|
| 227 | sprintf(buf, "%s%crefs", dir, SLASH);
|
---|
| 228 | #if MSDOS || TOS
|
---|
| 229 | sfile = fopen(buf, "rb");
|
---|
| 230 | #else
|
---|
| 231 | # if AMIGA
|
---|
| 232 | if (buf[0] == '.' && buf[1] == SLASH)
|
---|
| 233 | sfile = fopen(&buf[2], "r");
|
---|
| 234 | else
|
---|
| 235 | # endif
|
---|
| 236 | sfile = fopen(buf, "r");
|
---|
| 237 | #endif
|
---|
| 238 | if (!sfile)
|
---|
| 239 | {
|
---|
| 240 | /* failed! */
|
---|
| 241 | return 0;
|
---|
| 242 | }
|
---|
| 243 | }
|
---|
| 244 |
|
---|
| 245 | /* search the file */
|
---|
| 246 | for (comment = -1L; here = ftell(sfile), getline(buf, BLKSIZE, sfile) > 0; )
|
---|
| 247 | {
|
---|
| 248 | /* Is this the start/end of a comment? */
|
---|
| 249 | if (comment == -1L)
|
---|
| 250 | {
|
---|
| 251 | /* starting a comment? */
|
---|
| 252 | if (buf[0] == '/' && buf[1] == '*'
|
---|
| 253 | || buf[0] == '/' && buf[1] == '/'
|
---|
| 254 | || buf[0] == '(' && buf[1] == '*'
|
---|
| 255 | || buf[0] == '-' && buf[1] == '-')
|
---|
| 256 | {
|
---|
| 257 | comment = here;
|
---|
| 258 | }
|
---|
| 259 | }
|
---|
| 260 | else
|
---|
| 261 | {
|
---|
| 262 | /* ending a comment? */
|
---|
| 263 | if (buf[0] == '\n' || buf[0] == '#')
|
---|
| 264 | {
|
---|
| 265 | comment = -1L;
|
---|
| 266 | }
|
---|
| 267 | }
|
---|
| 268 |
|
---|
| 269 | /* is this the tag line? */
|
---|
| 270 | if (--lnum == 0L || (entry && !strncmp(buf, entry, len)))
|
---|
| 271 | {
|
---|
| 272 | /* if there were introductory comments, show them */
|
---|
| 273 | if (comment != -1L)
|
---|
| 274 | {
|
---|
| 275 | fseek(sfile, comment, 0);
|
---|
| 276 | while (comment != here)
|
---|
| 277 | {
|
---|
| 278 | getline(buf, BLKSIZE, sfile);
|
---|
| 279 | fputs(buf, stdout);
|
---|
| 280 | comment = ftell(sfile);
|
---|
| 281 | }
|
---|
| 282 |
|
---|
| 283 | /* re-fetch the tag line */
|
---|
| 284 | fgets(buf, BLKSIZE, sfile);
|
---|
| 285 | }
|
---|
| 286 |
|
---|
| 287 | /* show the tag line */
|
---|
| 288 | fputs(buf, stdout);
|
---|
| 289 |
|
---|
| 290 | /* show any argument lines */
|
---|
| 291 | while (getline(buf, BLKSIZE, sfile) > 0
|
---|
| 292 | && buf[0] != '#'
|
---|
| 293 | && strchr(buf, '{') == (char *)0)
|
---|
| 294 | {
|
---|
| 295 | fputs(buf, stdout);
|
---|
| 296 | }
|
---|
| 297 |
|
---|
| 298 | /* Done! Close the file, and return TRUE */
|
---|
| 299 | fclose(sfile);
|
---|
| 300 | return 1;
|
---|
| 301 | }
|
---|
| 302 | }
|
---|
| 303 |
|
---|
| 304 | /* not found -- return FALSE */
|
---|
| 305 | return 0;
|
---|
| 306 | }
|
---|
| 307 |
|
---|
| 308 | /* This function searches through the entire search path for a given tag.
|
---|
| 309 | * If it finds the tag, then it displays the info and returns TRUE;
|
---|
| 310 | * otherwise it returns FALSE.
|
---|
| 311 | */
|
---|
| 312 | int find(tag)
|
---|
| 313 | char *tag; /* the tag to look up */
|
---|
| 314 | {
|
---|
| 315 | char *tagpath;
|
---|
| 316 | char dir[80];
|
---|
| 317 | char *ptr;
|
---|
| 318 | int len;
|
---|
| 319 |
|
---|
| 320 | if (colons == 1)
|
---|
| 321 | {
|
---|
| 322 | /* looking for static function -- only look in current dir */
|
---|
| 323 | tagpath = ".";
|
---|
| 324 | }
|
---|
| 325 | else
|
---|
| 326 | {
|
---|
| 327 | /* get the tagpath from the environment. Default to DEFTAGPATH */
|
---|
| 328 | tagpath = getenv("TAGPATH");
|
---|
| 329 | if (!tagpath)
|
---|
| 330 | {
|
---|
| 331 | tagpath = DEFTAGPATH;
|
---|
| 332 | }
|
---|
| 333 | }
|
---|
| 334 |
|
---|
| 335 | /* for each entry in the path... */
|
---|
| 336 | while (*tagpath)
|
---|
| 337 | {
|
---|
| 338 | /* Copy the entry into the dir[] buffer */
|
---|
| 339 | for (ptr = dir; *tagpath && *tagpath != SEP; tagpath++)
|
---|
| 340 | {
|
---|
| 341 | *ptr++ = *tagpath;
|
---|
| 342 | }
|
---|
| 343 | if (*tagpath == SEP)
|
---|
| 344 | {
|
---|
| 345 | tagpath++;
|
---|
| 346 | }
|
---|
| 347 |
|
---|
| 348 | /* if the entry ended with "/tags", then strip that off */
|
---|
| 349 | len = strlen(TAGS);
|
---|
| 350 | if (&dir[len] < ptr && ptr[-len - 1] == SLASH && !strncmp(&ptr[-len], TAGS, len))
|
---|
| 351 | {
|
---|
| 352 | ptr -= len + 1;
|
---|
| 353 | }
|
---|
| 354 |
|
---|
| 355 | /* if the entry is now an empty string, then assume "." */
|
---|
| 356 | if (ptr == dir)
|
---|
| 357 | {
|
---|
| 358 | *ptr++ = '.';
|
---|
| 359 | }
|
---|
| 360 | *ptr = '\0';
|
---|
| 361 |
|
---|
| 362 | /* look for the tag in this path. If found, then display it
|
---|
| 363 | * and exit.
|
---|
| 364 | */
|
---|
| 365 | ptr = cktagdir(tag, dir);
|
---|
| 366 | if (ptr)
|
---|
| 367 | {
|
---|
| 368 | /* just supposed to display tag info? */
|
---|
| 369 | if (taginfo)
|
---|
| 370 | {
|
---|
| 371 | /* then do only that! */
|
---|
| 372 | if (strcmp(dir, "."))
|
---|
| 373 | {
|
---|
| 374 | printf("%s%c%s", dir, SLASH, ptr);
|
---|
| 375 | }
|
---|
| 376 | else
|
---|
| 377 | {
|
---|
| 378 | /* avoid leading "./" if possible */
|
---|
| 379 | fputs(ptr, stdout);
|
---|
| 380 | }
|
---|
| 381 | return 1;
|
---|
| 382 | }
|
---|
| 383 | else
|
---|
| 384 | {
|
---|
| 385 | /* else look up the declaration of the thing */
|
---|
| 386 | return lookup(dir, ptr);
|
---|
| 387 | }
|
---|
| 388 | }
|
---|
| 389 | }
|
---|
| 390 |
|
---|
| 391 | /* if we get here, then the tag wasn't found anywhere */
|
---|
| 392 | return 0;
|
---|
| 393 | }
|
---|
| 394 |
|
---|
| 395 | void usage()
|
---|
| 396 | {
|
---|
| 397 | fputs("usage: ref [-a] [-t] [-c class] [-f file] tag\n", stderr);
|
---|
| 398 | fputs(" -a function's args may be flush against left margin\n", stderr);
|
---|
| 399 | fputs(" -t output tag info, instead of the function header\n", stderr);
|
---|
| 400 | fputs(" -f File tag might be a static function in File\n", stderr);
|
---|
| 401 | fputs(" -c Class tag might be a member of class Class\n", stderr);
|
---|
| 402 | exit(2);
|
---|
| 403 | }
|
---|
| 404 |
|
---|
| 405 |
|
---|
| 406 | int countcolons(str)
|
---|
| 407 | char *str;
|
---|
| 408 | {
|
---|
| 409 | while (*str != ':' && *str)
|
---|
| 410 | {
|
---|
| 411 | str++;
|
---|
| 412 | }
|
---|
| 413 | if (str[0] != ':')
|
---|
| 414 | {
|
---|
| 415 | return 0;
|
---|
| 416 | }
|
---|
| 417 | else if (str[1] != ':')
|
---|
| 418 | {
|
---|
| 419 | return 1;
|
---|
| 420 | }
|
---|
| 421 | return 2;
|
---|
| 422 | }
|
---|
| 423 |
|
---|
| 424 | int main(argc, argv)
|
---|
| 425 | int argc;
|
---|
| 426 | char **argv;
|
---|
| 427 | {
|
---|
| 428 | char def_tag[100]; /* used to build tag name with default file/class */
|
---|
| 429 | int i;
|
---|
| 430 |
|
---|
| 431 | /* parse flags */
|
---|
| 432 | for (i = 1; i < argc && argv[i][0] == '-'; i++)
|
---|
| 433 | {
|
---|
| 434 | switch (argv[i][1])
|
---|
| 435 | {
|
---|
| 436 | case 't':
|
---|
| 437 | taginfo = 1;
|
---|
| 438 | break;
|
---|
| 439 |
|
---|
| 440 | case 'f':
|
---|
| 441 | if (argv[i][2])
|
---|
| 442 | {
|
---|
| 443 | def_file = &argv[i][2];
|
---|
| 444 | }
|
---|
| 445 | else if (++i < argc)
|
---|
| 446 | {
|
---|
| 447 | def_file = argv[i];
|
---|
| 448 | }
|
---|
| 449 | else
|
---|
| 450 | {
|
---|
| 451 | usage();
|
---|
| 452 | }
|
---|
| 453 | break;
|
---|
| 454 |
|
---|
| 455 | case 'c':
|
---|
| 456 | if (argv[i][2])
|
---|
| 457 | {
|
---|
| 458 | def_class = &argv[i][2];
|
---|
| 459 | }
|
---|
| 460 | else if (++i < argc)
|
---|
| 461 | {
|
---|
| 462 | def_class = argv[i];
|
---|
| 463 | }
|
---|
| 464 | else
|
---|
| 465 | {
|
---|
| 466 | usage();
|
---|
| 467 | }
|
---|
| 468 | break;
|
---|
| 469 |
|
---|
| 470 | default:
|
---|
| 471 | usage();
|
---|
| 472 | }
|
---|
| 473 | }
|
---|
| 474 |
|
---|
| 475 | /* if no tag was given, complain */
|
---|
| 476 | if (i + 1 != argc)
|
---|
| 477 | {
|
---|
| 478 | usage();
|
---|
| 479 | }
|
---|
| 480 |
|
---|
| 481 | /* does the tag have an explicit class or file? */
|
---|
| 482 | colons = countcolons(argv[i]);
|
---|
| 483 |
|
---|
| 484 | /* if not, then maybe try some defaults */
|
---|
| 485 | if (colons == 0)
|
---|
| 486 | {
|
---|
| 487 | /* try a static function in the file first */
|
---|
| 488 | if (def_file)
|
---|
| 489 | {
|
---|
| 490 | sprintf(def_tag, "%s:%s", def_file, argv[i]);
|
---|
| 491 | colons = 1;
|
---|
| 492 | if (find(def_tag))
|
---|
| 493 | {
|
---|
| 494 | exit(0);
|
---|
| 495 | }
|
---|
| 496 | }
|
---|
| 497 |
|
---|
| 498 | /* try a member function for a class */
|
---|
| 499 | if (def_class)
|
---|
| 500 | {
|
---|
| 501 | sprintf(def_tag, "%s::%s", def_class, argv[i]);
|
---|
| 502 | colons = 2;
|
---|
| 503 | if (find(def_tag))
|
---|
| 504 | {
|
---|
| 505 | exit(0);
|
---|
| 506 | }
|
---|
| 507 | }
|
---|
| 508 |
|
---|
| 509 | /* oh, well */
|
---|
| 510 | colons = 0;
|
---|
| 511 | }
|
---|
| 512 |
|
---|
| 513 | /* find the tag */
|
---|
| 514 | if (find(argv[i]))
|
---|
| 515 | {
|
---|
| 516 | exit(0);
|
---|
| 517 | }
|
---|
| 518 |
|
---|
| 519 | exit(1);
|
---|
| 520 | /*NOTREACHED*/
|
---|
| 521 | }
|
---|