Removed KLUDGELINEMODE option.
[unix-history] / usr.bin / ref / ref.c
CommitLineData
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
20extern char *cktagdir P_((char *, char *));
21extern int getline P_((char *, int, FILE *));
22extern int lookup P_((char *, char *));
23extern int find P_((char *));
24extern void usage P_((void));
25extern int countcolons P_((char *));
26extern 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. */
56int taginfo; /* boolean: give only the tag info? (not header?) */
57char *def_file; /* default filename for static functions */
58char *def_class; /* default classname for class members */
59int 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 */
67char *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 */
136int 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 */
169int 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 */
341int 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
424void 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
434int 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
452void 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}