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