1 /*===========================================================================
2 Copyright (c) 1998-2000, The Santa Cruz Operation
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 *Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10
11 *Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
14
15 *Neither name of The Santa Cruz Operation nor the names of its contributors
16 may be used to endorse or promote products derived from this software
17 without specific prior written permission.
18
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
20 IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION)
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30 DAMAGE.
31 =========================================================================*/
32
33
34 /* cscope - interactive C symbol cross-reference
35 *
36 * main functions
37 */
38
39 #include "global.h"
40
41 #include "build.h"
42 #include "vp.h"
43 #include "version.h" /* FILEVERSION and FIXVERSION */
44 #include "scanner.h"
45 #include "alloc.h"
46
47 #include <stdlib.h> /* atoi */
48 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
49 #include <ncurses.h>
50 #else
51 #include <curses.h>
52 #endif
53 #include <sys/types.h> /* needed by stat.h */
54 #include <sys/stat.h> /* stat */
55 #include <signal.h>
56 #ifdef HAVE_GETOPT_LONG
57 #include <getopt.h>
58 #endif
59
60 /* defaults for unset environment variables */
61 #define EDITOR "vi"
62 #define HOME "/" /* no $HOME --> use root directory */
63 #define SHELL "sh"
64 #define LINEFLAG "+%s" /* default: used by vi and emacs */
65 #define TMPDIR "/tmp"
66 #ifndef DFLT_INCDIR
67 #define DFLT_INCDIR "/usr/include"
68 #endif
69
70 /* note: these digraph character frequencies were calculated from possible
71 printable digraphs in the cross-reference for the C compiler */
72 char dichar1[] = " teisaprnl(of)=c"; /* 16 most frequent first chars */
73 char dichar2[] = " tnerpla"; /* 8 most frequent second chars
74 using the above as first chars */
75 char dicode1[256]; /* digraph first character code */
76 char dicode2[256]; /* digraph second character code */
77
78 char *editor, *shell, *lineflag; /* environment variables */
79 char *home; /* Home directory */
80 BOOL lineflagafterfile;
81 char *argv0; /* command name */
82 BOOL compress = YES; /* compress the characters in the crossref */
83 BOOL dbtruncated; /* database symbols are truncated to 8 chars */
84 int dispcomponents = 1; /* file path components to display */
85 #if CCS
86 BOOL displayversion; /* display the C Compilation System version */
87 #endif
88 BOOL editallprompt = YES; /* prompt between editing files */
89 unsigned int fileargc; /* file argument count */
90 char **fileargv; /* file argument values */
91 int fileversion; /* cross-reference file version */
92 BOOL incurses = NO; /* in curses */
93 BOOL invertedindex; /* the database has an inverted index */
94 BOOL isuptodate; /* consider the crossref up-to-date */
95 BOOL kernelmode; /* don't use DFLT_INCDIR - bad for kernels */
96 BOOL linemode = NO; /* use line oriented user interface */
97 BOOL verbosemode = NO; /* print extra information on line mode */
98 BOOL recurse_dir = NO; /* recurse dirs when searching for src files */
99 BOOL remove_symfile_onexit=NO;
100 char *namefile; /* file of file names */
101 BOOL ogs; /* display OGS book and subsystem names */
102 char *prependpath; /* prepend path to file names */
103 FILE *refsfound; /* references found file */
104 char temp1[PATHLEN + 1]; /* temporary file name */
105 char temp2[PATHLEN + 1]; /* temporary file name */
106 char tempdirpv[PATHLEN + 1]; /* private temp directory */
107 long totalterms; /* total inverted index terms */
108 BOOL trun_syms; /* truncate symbols to 8 characters */
109 char tempstring[TEMPSTRING_LEN + 1]; /* use this as a buffer, instead of 'yytext',
110 * which had better be left alone */
111 char *tmpdir; /* temporary directory */
112
113 static BOOL onesearch; /* one search only in line mode */
114 static char *reflines; /* symbol reference lines file */
115
116 /* Internal prototypes: */
117 static void error_usage(void);
118 static void initcompress(void);
119 static void longusage(void);
120 static void skiplist(FILE *oldrefs);
121 static void usage(void);
122
123 #ifdef HAVE_FIXKEYPAD
124 void fixkeypad();
125 #endif
126
127 #if defined(KEY_RESIZE) && !defined(__DJGPP__)
128 void
sigwinch_handler(int sig,siginfo_t * info,void * unused)129 sigwinch_handler(int sig, siginfo_t *info, void *unused)
130 {
131 (void) sig;
132 (void) info;
133 (void) unused;
134 if(incurses == YES)
135 ungetch(KEY_RESIZE);
136 }
137 #endif
138
139 #ifdef HAVE_GETOPT_LONG
140 struct option lopts[] = {
141 {"help", 0, NULL, 'h'},
142 {"version", 0, NULL, 'V'},
143 {0, 0, 0, 0}
144 };
145
parse_options(int * argc,char ** argv)146 char ** parse_options(int *argc, char **argv)
147 {
148 int opt;
149 int longind;
150 char path[PATHLEN + 1]; /* file path */
151 char *s;
152 int argcc = *argc;
153
154
155 while ((opt = getopt_long(argcc, argv,
156 "hVbcCdeF:f:I:i:kLl0:1:2:3:4:5:6:7:8:9:P:p:qRs:TUuvX",
157 lopts, &longind)) != -1) {
158 switch(opt) {
159
160 case '?':
161 usage();
162 myexit(1);
163 break;
164 case 'X':
165 remove_symfile_onexit = YES;
166 break;
167 case '0':
168 case '1':
169 case '2':
170 case '3':
171 case '4':
172 case '5':
173 case '6':
174 case '7':
175 case '8':
176 case '9':
177 /* The input fields numbers for line mode operation */
178 field = opt - '0';
179 if (strlen(optarg) > PATHLEN) {
180 postfatal("\
181 cscope: pattern too long, cannot be > \
182 %d characters\n", PATLEN);
183 }
184 strcpy(Pattern, optarg);
185 break;
186 case 'b': /* only build the cross-reference */
187 buildonly = YES;
188 linemode = YES;
189 break;
190 case 'c': /* ASCII characters only in crossref */
191 compress = NO;
192 break;
193 case 'C': /* turn on caseless mode for symbol searches */
194 caseless = YES;
195 egrepcaseless(caseless); /* simulate egrep -i flag */
196 break;
197 case 'd': /* consider crossref up-to-date */
198 isuptodate = YES;
199 break;
200 case 'e': /* suppress ^E prompt between files */
201 editallprompt = NO;
202 break;
203 case 'h':
204 longusage();
205 myexit(1);
206 break;
207 case 'k': /* ignore DFLT_INCDIR */
208 kernelmode = YES;
209 break;
210 case 'L':
211 onesearch = YES;
212 /* FALLTHROUGH */
213 case 'l':
214 linemode = YES;
215 break;
216 case 'v':
217 verbosemode = YES;
218 break;
219 case 'V':
220 fprintf(stderr, "%s: version %d%s\n", argv0,
221 FILEVERSION, FIXVERSION);
222 myexit(0);
223 break;
224 case 'q': /* quick search */
225 invertedindex = YES;
226 break;
227 case 'T': /* truncate symbols to 8 characters */
228 trun_syms = YES;
229 break;
230 case 'u': /* unconditionally build the cross-reference */
231 unconditional = YES;
232 break;
233 case 'U': /* assume some files have changed */
234 fileschanged = YES;
235 break;
236 case 'R':
237 recurse_dir = YES;
238 break;
239 case 'f': /* alternate cross-reference file */
240 reffile = optarg;
241 if (strlen(reffile) > sizeof(path) - 3) {
242 postfatal("\
243 cscope: reffile too long, cannot \
244 be > %d characters\n", sizeof(path) - 3);
245 /* NOTREACHED */
246 }
247 strcpy(path, reffile);
248
249 s = path + strlen(path);
250 strcpy(s, ".in");
251 /*coverity[overwrite_var]*/
252 invname = my_strdup(path);
253 strcpy(s, ".po");
254 /*coverity[overwrite_var]*/
255 invpost = my_strdup(path);
256 break;
257
258 case 'F': /* symbol reference lines file */
259 reflines = optarg;
260 break;
261 case 'i': /* file containing file names */
262 namefile = optarg;
263 break;
264 case 'I': /* #include file directory */
265 includedir(optarg);
266 break;
267 case 'p': /* file path components to display */
268 dispcomponents = atoi(optarg);
269 break;
270 case 'P': /* prepend path to file names */
271 prependpath = optarg;
272 break;
273 case 's': /* additional source file directory */
274 sourcedir(optarg);
275 break;
276 }
277 }
278 /*
279 * This adjusts argv so that we only see the remaining
280 * args. Its ugly, but we need to do it so that the rest
281 * of the main routine doesn't get all confused
282 */
283 *argc = *argc - optind;
284 return argv + optind;
285 }
286 #endif
287
288 int
main(int argc,char ** argv)289 main(int argc, char **argv)
290 {
291 FILE *names; /* name file pointer */
292 int oldnum; /* number in old cross-ref */
293 char path[PATHLEN + 1]; /* file path */
294 FILE *oldrefs; /* old cross-reference file */
295 char *s;
296 int c;
297 unsigned int i;
298 pid_t pid;
299 struct stat stat_buf;
300 #if defined(KEY_RESIZE) && !defined(__DJGPP__)
301 struct sigaction winch_action;
302 #endif
303 mode_t orig_umask;
304
305 yyin = stdin;
306 yyout = stdout;
307 /* save the command name for messages */
308 argv0 = argv[0];
309
310 /* set the options */
311 #ifdef HAVE_GETOPT_LONG
312 argv = parse_options(&argc, argv);
313 #else
314 while (--argc > 0 && (*++argv)[0] == '-') {
315 /* HBB 20030814: add GNU-style --help and --version options */
316 if (strequal(argv[0], "--help")
317 || strequal(argv[0], "-h")) {
318 longusage();
319 myexit(0);
320 }
321 if (strequal(argv[0], "--version")
322 || strequal(argv[0], "-V")) {
323 #if CCS
324 displayversion = YES;
325 #else
326 fprintf(stderr, "%s: version %d%s\n", argv0,
327 FILEVERSION, FIXVERSION);
328 myexit(0);
329 #endif
330 }
331
332 for (s = argv[0] + 1; *s != '\0'; s++) {
333
334 /* look for an input field number */
335 if (isdigit((unsigned char) *s)) {
336 field = *s - '0';
337 if (field > 8) {
338 field = 8;
339 }
340 if (*++s == '\0' && --argc > 0) {
341 s = *++argv;
342 }
343 if (strlen(s) > PATLEN) {
344 postfatal("\
345 cscope: pattern too long, cannot be > %d characters\n", PATLEN);
346 /* NOTREACHED */
347 }
348 strcpy(Pattern, s);
349 goto nextarg;
350 }
351 switch (*s) {
352 case '-': /* end of options */
353 --argc;
354 ++argv;
355 goto lastarg;
356 case 'b': /* only build the cross-reference */
357 buildonly = YES;
358 linemode = YES;
359 break;
360 case 'c': /* ASCII characters only in crossref */
361 compress = NO;
362 break;
363 case 'C': /* turn on caseless mode for symbol searches */
364 caseless = YES;
365 egrepcaseless(caseless); /* simulate egrep -i flag */
366 break;
367 case 'd': /* consider crossref up-to-date */
368 isuptodate = YES;
369 break;
370 case 'e': /* suppress ^E prompt between files */
371 editallprompt = NO;
372 break;
373 case 'k': /* ignore DFLT_INCDIR */
374 kernelmode = YES;
375 break;
376 case 'L':
377 onesearch = YES;
378 /* FALLTHROUGH */
379 case 'l':
380 linemode = YES;
381 break;
382 case 'v':
383 verbosemode = YES;
384 break;
385 case 'o': /* display OGS book and subsystem names */
386 ogs = YES;
387 break;
388 case 'q': /* quick search */
389 invertedindex = YES;
390 break;
391 case 'T': /* truncate symbols to 8 characters */
392 trun_syms = YES;
393 break;
394 case 'u': /* unconditionally build the cross-reference */
395 unconditional = YES;
396 break;
397 case 'U': /* assume some files have changed */
398 fileschanged = YES;
399 break;
400 case 'R':
401 recurse_dir = YES;
402 break;
403 case 'X':
404 remove_symfile_onexit = YES;
405 break;
406 case 'f': /* alternate cross-reference file */
407 case 'F': /* symbol reference lines file */
408 case 'i': /* file containing file names */
409 case 'I': /* #include file directory */
410 case 'p': /* file path components to display */
411 case 'P': /* prepend path to file names */
412 case 's': /* additional source file directory */
413 case 'S':
414 c = *s;
415 if (*++s == '\0' && --argc > 0) {
416 s = *++argv;
417 }
418 if (*s == '\0') {
419 fprintf(stderr, "%s: -%c option: missing or empty value\n",
420 argv0, c);
421 error_usage();
422 }
423 switch (c) {
424 case 'f': /* alternate cross-reference file */
425 reffile = s;
426 if (strlen(reffile) > sizeof(path) - 3) {
427 postfatal("\
428 cscope: reffile too long, cannot be > %d characters\n", sizeof(path) - 3);
429 /* NOTREACHED */
430 }
431 strcpy(path, s);
432 #ifdef SHORT_NAMES_ONLY
433 /* System V has a 14 character limit */
434 s = mybasename(path);
435 if (strlen(s) > 11) {
436 s[11] = '\0';
437 }
438 #endif
439 s = path + strlen(path);
440 strcpy(s, ".in");
441 invname = my_strdup(path);
442 strcpy(s, ".po");
443 invpost = my_strdup(path);
444 break;
445 case 'F': /* symbol reference lines file */
446 reflines = s;
447 break;
448 case 'i': /* file containing file names */
449 namefile = s;
450 break;
451 case 'I': /* #include file directory */
452 includedir(s);
453 break;
454 case 'p': /* file path components to display */
455 if (*s < '0' || *s > '9' ) {
456 fprintf(stderr, "\
457 %s: -p option: missing or invalid numeric value\n",
458 argv0);
459 error_usage();
460 }
461 dispcomponents = atoi(s);
462 break;
463 case 'P': /* prepend path to file names */
464 prependpath = s;
465 break;
466 case 's': /* additional source directory */
467 case 'S':
468 sourcedir(s);
469 break;
470 }
471 goto nextarg;
472 default:
473 fprintf(stderr, "%s: unknown option: -%c\n", argv0,
474 *s);
475 error_usage();
476 } /* switch(option letter) */
477 } /* for(option) */
478 nextarg:
479 ;
480 } /* while(argv) */
481
482 lastarg:
483 #endif
484 /* read the environment */
485 editor = mygetenv("EDITOR", EDITOR);
486 editor = mygetenv("VIEWER", editor); /* use viewer if set */
487 editor = mygetenv("CSCOPE_EDITOR", editor); /* has last word */
488 home = mygetenv("HOME", HOME);
489 shell = mygetenv("SHELL", SHELL);
490 lineflag = mygetenv("CSCOPE_LINEFLAG", LINEFLAG);
491 lineflagafterfile = getenv("CSCOPE_LINEFLAG_AFTER_FILE") ? 1 : 0;
492 tmpdir = mygetenv("TMPDIR", TMPDIR);
493
494 /* XXX remove if/when clearerr() in dir.c does the right thing. */
495 if (namefile && strcmp(namefile, "-") == 0 && !buildonly) {
496 postfatal("cscope: Must use -b if file list comes from stdin\n");
497 /* NOTREACHED */
498 }
499
500 /* make sure that tmpdir exists */
501 if (lstat (tmpdir, &stat_buf)) {
502 fprintf (stderr, "\
503 cscope: Temporary directory %s does not exist or cannot be accessed\n",
504 tmpdir);
505 fprintf (stderr, "\
506 cscope: Please create the directory or set the environment variable\n\
507 cscope: TMPDIR to a valid directory\n");
508 myexit(1);
509 }
510
511 /* create the temporary file names */
512 orig_umask = umask(S_IRWXG|S_IRWXO);
513 pid = getpid();
514 snprintf(tempdirpv, sizeof(tempdirpv), "%s/cscope.%d", tmpdir, pid);
515 if(mkdir(tempdirpv,S_IRWXU)) {
516 fprintf(stderr, "\
517 cscope: Could not create private temp dir %s\n",
518 tempdirpv);
519 myexit(1);
520 }
521 umask(orig_umask);
522
523 snprintf(temp1, sizeof(temp1), "%s/cscope.1", tempdirpv);
524 snprintf(temp2, sizeof(temp2), "%s/cscope.2", tempdirpv);
525
526 /* if running in the foreground */
527 if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
528 /* cleanup on the interrupt and quit signals */
529 signal(SIGINT, myexit);
530 signal(SIGQUIT, myexit);
531 }
532 /* cleanup on the hangup signal */
533 signal(SIGHUP, myexit);
534
535 /* ditto the TERM signal */
536 signal(SIGTERM, myexit);
537
538 /* ignore PIPE signal, so myexit() will have a chance to clean up in
539 * linemode, while in curses mode the "|" command can cause a pipe signal
540 * too
541 */
542 signal(SIGPIPE, SIG_IGN);
543
544 /* if the database path is relative and it can't be created */
545 if (reffile[0] != '/' && access(".", WRITE) != 0) {
546
547 /* put it in the home directory if the database may not be
548 * up-to-date or doesn't exist in the relative directory,
549 * so a database in the current directory will be
550 * used instead of failing to open a non-existant database in
551 * the home directory
552 */
553 snprintf(path, sizeof(path), "%s/%s", home, reffile);
554 if (isuptodate == NO || access(path, READ) == 0) {
555 reffile = my_strdup(path);
556 snprintf(path, sizeof(path), "%s/%s", home, invname);
557 invname = my_strdup(path);
558 snprintf(path, sizeof(path), "%s/%s", home, invpost);
559 invpost = my_strdup(path);
560 }
561 }
562
563 if (linemode == NO) {
564 signal(SIGINT, SIG_IGN); /* ignore interrupts */
565
566 #if defined(KEY_RESIZE) && !defined(__DJGPP__)
567 winch_action.sa_sigaction = sigwinch_handler;
568 sigemptyset(&winch_action.sa_mask);
569 winch_action.sa_flags = SA_SIGINFO;
570 sigaction(SIGWINCH,&winch_action,NULL);
571 #endif
572
573 /* initialize the curses display package */
574 initscr(); /* initialize the screen */
575 entercurses();
576 #if TERMINFO
577 keypad(stdscr, TRUE); /* enable the keypad */
578 # ifdef HAVE_FIXKEYPAD
579 fixkeypad(); /* fix for getch() intermittently returning garbage */
580 # endif
581 #endif /* TERMINFO */
582 #if UNIXPC
583 standend(); /* turn off reverse video */
584 #endif
585 dispinit(); /* initialize display parameters */
586 setfield(); /* set the initial cursor position */
587 clearmsg(); /* clear any build progress message */
588 display(); /* display the version number and input fields */
589 }
590
591
592 /* if the cross-reference is to be considered up-to-date */
593 if (isuptodate == YES) {
594 if ((oldrefs = vpfopen(reffile, "rb")) == NULL) {
595 postfatal("cscope: cannot open file %s\n", reffile);
596 /* NOTREACHED */
597 }
598 /* get the crossref file version but skip the current directory */
599 if (fscanf(oldrefs, "cscope %d %*s", &fileversion) != 1) {
600 postfatal("cscope: cannot read file version from file %s\n",
601 reffile);
602 /* NOTREACHED */
603 }
604 if (fileversion >= 8) {
605
606 /* override these command line options */
607 compress = YES;
608 invertedindex = NO;
609
610 /* see if there are options in the database */
611 for (;;) {
612 getc(oldrefs); /* skip the blank */
613 if ((c = getc(oldrefs)) != '-') {
614 ungetc(c, oldrefs);
615 break;
616 }
617 switch (getc(oldrefs)) {
618 case 'c': /* ASCII characters only */
619 compress = NO;
620 break;
621 case 'q': /* quick search */
622 invertedindex = YES;
623 fscanf(oldrefs, "%ld", &totalterms);
624 break;
625 case 'T': /* truncate symbols to 8 characters */
626 dbtruncated = YES;
627 trun_syms = YES;
628 break;
629 }
630 }
631 initcompress();
632 seek_to_trailer(oldrefs);
633 }
634 /* skip the source and include directory lists */
635 skiplist(oldrefs);
636 skiplist(oldrefs);
637
638 /* get the number of source files */
639 if (fscanf(oldrefs, "%lu", &nsrcfiles) != 1) {
640 postfatal("\
641 cscope: cannot read source file size from file %s\n", reffile);
642 /* NOTREACHED */
643 }
644 /* get the source file list */
645 srcfiles = mymalloc(nsrcfiles * sizeof(*srcfiles));
646 if (fileversion >= 9) {
647
648 /* allocate the string space */
649 if (fscanf(oldrefs, "%d", &oldnum) != 1) {
650 postfatal("\
651 cscope: cannot read string space size from file %s\n", reffile);
652 /* NOTREACHED */
653 }
654 s = mymalloc(oldnum);
655 getc(oldrefs); /* skip the newline */
656
657 /* read the strings */
658 if (fread(s, oldnum, 1, oldrefs) != 1) {
659 postfatal("\
660 cscope: cannot read source file names from file %s\n", reffile);
661 /* NOTREACHED */
662 }
663 /* change newlines to nulls */
664 for (i = 0; i < nsrcfiles; ++i) {
665 srcfiles[i] = s;
666 for (++s; *s != '\n'; ++s) {
667 ;
668 }
669 *s = '\0';
670 ++s;
671 }
672 /* if there is a file of source file names */
673 if ((namefile != NULL && (names = vpfopen(namefile, "r")) != NULL)
674 || (names = vpfopen(NAMEFILE, "r")) != NULL) {
675
676 /* read any -p option from it */
677 while (fgets(path, sizeof(path), names) != NULL && *path == '-') {
678 i = path[1];
679 s = path + 2; /* for "-Ipath" */
680 if (*s == '\0') { /* if "-I path" */
681 fgets(path, sizeof(path), names);
682 s = path;
683 }
684 switch (i) {
685 case 'p': /* file path components to display */
686 if (*s < '0' || *s > '9') {
687 posterr("cscope: -p option in file %s: missing or invalid numeric value\n", namefile);
688
689 }
690 dispcomponents = atoi(s);
691 }
692 }
693 fclose(names);
694 }
695 } else {
696 for (i = 0; i < nsrcfiles; ++i) {
697 if (!fgets(path, sizeof(path), oldrefs) ) {
698 postfatal("\
699 cscope: cannot read source file name from file %s\n",
700 reffile);
701 /* NOTREACHED */
702 }
703 srcfiles[i] = my_strdup(path);
704 }
705 }
706 fclose(oldrefs);
707 } else {
708 /* save the file arguments */
709 fileargc = argc;
710 fileargv = argv;
711
712 /* get source directories from the environment */
713 if ((s = getenv("SOURCEDIRS")) != NULL) {
714 sourcedir(s);
715 }
716 /* make the source file list */
717 srcfiles = mymalloc(msrcfiles * sizeof(*srcfiles));
718 makefilelist();
719 if (nsrcfiles == 0) {
720 postfatal("cscope: no source files found\n");
721 /* NOTREACHED */
722 }
723 /* get include directories from the environment */
724 if ((s = getenv("INCLUDEDIRS")) != NULL) {
725 includedir(s);
726 }
727 /* add /usr/include to the #include directory list,
728 but not in kernelmode... kernels tend not to use it. */
729 if (kernelmode == NO) {
730 if (NULL != (s = getenv("INCDIR"))) {
731 includedir(s);
732 } else {
733 includedir(DFLT_INCDIR);
734 }
735 }
736
737 /* initialize the C keyword table */
738 initsymtab();
739
740 /* Tell build.c about the filenames to create: */
741 setup_build_filenames(reffile);
742
743 /* build the cross-reference */
744 initcompress();
745 if (linemode == NO || verbosemode == YES) /* display if verbose as well */
746 postmsg("Building cross-reference...");
747 build();
748 if (linemode == NO )
749 clearmsg(); /* clear any build progress message */
750 if (buildonly == YES) {
751 myexit(0);
752 }
753 }
754 opendatabase();
755
756 /* if using the line oriented user interface so cscope can be a
757 subprocess to emacs or samuel */
758 if (linemode == YES) {
759 if (*Pattern != '\0') { /* do any optional search */
760 if (search() == YES) {
761 /* print the total number of lines in
762 * verbose mode */
763 if (verbosemode == YES)
764 printf("cscope: %d lines\n",
765 totallines);
766
767 while ((c = getc(refsfound)) != EOF)
768 putchar(c);
769 }
770 }
771 if (onesearch == YES)
772 myexit(0);
773
774 for (;;) {
775 char buf[PATLEN + 2];
776
777 printf(">> ");
778 fflush(stdout);
779 if (fgets(buf, sizeof(buf), stdin) == NULL) {
780 myexit(0);
781 }
782 /* remove any trailing newline character */
783 if (*(s = buf + strlen(buf) - 1) == '\n') {
784 *s = '\0';
785 }
786 switch (*buf) {
787 case '0':
788 case '1':
789 case '2':
790 case '3':
791 case '4':
792 case '5':
793 case '6':
794 case '7':
795 case '8':
796 case '9': /* samuel only */
797 field = *buf - '0';
798 strcpy(Pattern, buf + 1);
799 if (search() == NO) {
800 printf("Unable to search database\n");
801 } else {
802 printf("cscope: %d lines\n", totallines);
803 while ((c = getc(refsfound)) != EOF) {
804 putchar(c);
805 }
806 }
807 break;
808
809 case 'c': /* toggle caseless mode */
810 case ctrl('C'):
811 if (caseless == NO) {
812 caseless = YES;
813 } else {
814 caseless = NO;
815 }
816 egrepcaseless(caseless);
817 break;
818
819 case 'r': /* rebuild database cscope style */
820 case ctrl('R'):
821 freefilelist();
822 makefilelist();
823 /* FALLTHROUGH */
824
825 case 'R': /* rebuild database samuel style */
826 rebuild();
827 putchar('\n');
828 break;
829
830 case 'C': /* clear file names */
831 freefilelist();
832 putchar('\n');
833 break;
834
835 case 'F': /* add a file name */
836 strcpy(path, buf + 1);
837 if (infilelist(path) == NO &&
838 (s = inviewpath(path)) != NULL) {
839 addsrcfile(s);
840 }
841 putchar('\n');
842 break;
843
844 case 'q': /* quit */
845 case ctrl('D'):
846 case ctrl('Z'):
847 myexit(0);
848
849 default:
850 fprintf(stderr, "cscope: unknown command '%s'\n", buf);
851 break;
852 }
853 }
854 /* NOTREACHED */
855 }
856 /* pause before clearing the screen if there have been error messages */
857 if (errorsfound == YES) {
858 errorsfound = NO;
859 askforreturn();
860 }
861 /* do any optional search */
862 if (*Pattern != '\0') {
863 atfield(); /* move to the input field */
864 command(ctrl('Y')); /* search */
865 } else if (reflines != NULL) {
866 /* read any symbol reference lines file */
867 readrefs(reflines);
868 }
869 display(); /* update the display */
870
871 for (;;) {
872 if (!selecting)
873 atfield(); /* move to the input field */
874
875 /* exit if the quit command is entered */
876 if ((c = mygetch()) == EOF || c == ctrl('D')) {
877 break;
878 }
879 if (c == ctrl('Z')) {
880 #ifdef SIGTSTP
881 kill(0, SIGTSTP);
882 continue;
883 #else
884 break;
885 #endif
886 }
887 /* execute the commmand, updating the display if necessary */
888 if (command(c) == YES) {
889 display();
890 }
891
892 if (selecting) {
893 move(displine[curdispline], 0);
894 refresh();
895 }
896 }
897 /* cleanup and exit */
898 myexit(0);
899 /* NOTREACHED */
900 return 0; /* avoid warning... */
901 }
902
903 void
cannotopen(char * file)904 cannotopen(char *file)
905 {
906 posterr("Cannot open file %s", file);
907 }
908
909 /* FIXME MTE - should use postfatal here */
910 void
cannotwrite(char * file)911 cannotwrite(char *file)
912 {
913 char msg[MSGLEN + 1];
914
915 snprintf(msg, sizeof(msg), "Removed file %s because write failed", file);
916
917 myperror(msg); /* display the reason */
918
919 unlink(file);
920 myexit(1); /* calls exit(2), which closes files */
921 }
922
923
924 /* set up the digraph character tables for text compression */
925 static void
initcompress(void)926 initcompress(void)
927 {
928 int i;
929
930 if (compress == YES) {
931 for (i = 0; i < 16; ++i) {
932 dicode1[(unsigned char) (dichar1[i])] = i * 8 + 1;
933 }
934 for (i = 0; i < 8; ++i) {
935 dicode2[(unsigned char) (dichar2[i])] = i + 1;
936 }
937 }
938 }
939
940 /* skip the list in the cross-reference file */
941
942 static void
skiplist(FILE * oldrefs)943 skiplist(FILE *oldrefs)
944 {
945 int i;
946
947 if (fscanf(oldrefs, "%d", &i) != 1) {
948 postfatal("cscope: cannot read list size from file %s\n", reffile);
949 /* NOTREACHED */
950 }
951 while (--i >= 0) {
952 if (fscanf(oldrefs, "%*s") != 0) {
953 postfatal("cscope: cannot read list name from file %s\n", reffile);
954 /* NOTREACHED */
955 }
956 }
957 }
958
959
960 /* enter curses mode */
961 void
entercurses(void)962 entercurses(void)
963 {
964 incurses = YES;
965 #ifndef __MSDOS__ /* HBB 20010313 */
966 nonl(); /* don't translate an output \n to \n\r */
967 #endif
968 raw(); /* single character input */
969 noecho(); /* don't echo input characters */
970 clear(); /* clear the screen */
971 mouseinit(); /* initialize any mouse interface */
972 drawscrollbar(topline, nextline);
973 }
974
975
976 /* exit curses mode */
977 void
exitcurses(void)978 exitcurses(void)
979 {
980 /* clear the bottom line */
981 move(LINES - 1, 0);
982 clrtoeol();
983 refresh();
984
985 /* exit curses and restore the terminal modes */
986 endwin();
987 incurses = NO;
988
989 /* restore the mouse */
990 mousecleanup();
991 fflush(stdout);
992 }
993
994 /* error exit including short usage information */
995 static void
error_usage(void)996 error_usage(void)
997 {
998 usage();
999 fprintf(stderr, "Try the -h option for more information.\n");
1000 myexit(1);
1001 }
1002
1003 /* normal usage message */
1004 static void
usage(void)1005 usage(void)
1006 {
1007 fprintf(stderr, "Usage: cscope [-bcCdehklLqRTuUvV] [-f file] [-F file] [-i file] [-I dir] [-s dir]\n");
1008 fprintf(stderr, " [-p number] [-P path] [-[0-8] pattern] [source files]\n");
1009 }
1010
1011
1012 /* long usage message */
1013 static void
longusage(void)1014 longusage(void)
1015 {
1016 usage();
1017 fprintf(stderr, "\
1018 \n\
1019 -b Build the cross-reference only.\n\
1020 -C Ignore letter case when searching.\n\
1021 -c Use only ASCII characters in the cross-ref file (don't compress).\n\
1022 -d Do not update the cross-reference.\n\
1023 -e Suppress the <Ctrl>-e command prompt between files.\n\
1024 -F symfile Read symbol reference lines from symfile.\n\
1025 -f reffile Use reffile as cross-ref file name instead of %s.\n",
1026 REFFILE);
1027 fprintf(stderr, "\
1028 -h This help screen.\n\
1029 -I incdir Look in incdir for any #include files.\n\
1030 -i namefile Browse through files listed in namefile, instead of %s\n",
1031 NAMEFILE);
1032 fprintf(stderr, "\
1033 -k Kernel Mode - don't use %s for #include files.\n",
1034 DFLT_INCDIR);
1035 fputs("\
1036 -L Do a single search with line-oriented output.\n\
1037 -l Line-oriented interface.\n\
1038 -num pattern Go to input field num (counting from 0) and find pattern.\n\
1039 -P path Prepend path to relative file names in pre-built cross-ref file.\n\
1040 -p n Display the last n file path components.\n\
1041 -q Build an inverted index for quick symbol searching.\n\
1042 -R Recurse directories for files.\n\
1043 -s dir Look in dir for additional source files.\n\
1044 -T Use only the first eight characters to match against C symbols.\n\
1045 -U Check file time stamps.\n\
1046 -u Unconditionally build the cross-reference file.\n\
1047 -v Be more verbose in line mode.\n\
1048 -V Print the version number.\n\
1049 \n\
1050 Please see the manpage for more information.\n",
1051 stderr);
1052 }
1053
1054 /* cleanup and exit */
1055
1056 void
myexit(int sig)1057 myexit(int sig)
1058 {
1059 /* HBB 20010313; close file before unlinking it. Unix may not care
1060 * about that, but DOS absolutely needs it */
1061 if (refsfound != NULL)
1062 fclose(refsfound);
1063
1064 /* remove any temporary files */
1065 if (temp1[0] != '\0') {
1066 unlink(temp1);
1067 unlink(temp2);
1068 rmdir(tempdirpv);
1069 }
1070 /* restore the terminal to its original mode */
1071 if (incurses == YES) {
1072 exitcurses();
1073 }
1074 /* dump core for debugging on the quit signal */
1075 if (sig == SIGQUIT) {
1076 abort();
1077 }
1078 /* HBB 20000421: be nice: free allocated data */
1079 freefilelist();
1080 freeinclist();
1081 freesrclist();
1082 freecrossref();
1083 free_newbuildfiles();
1084
1085 if( remove_symfile_onexit == YES ) {
1086 unlink( reffile );
1087 unlink( invname );
1088 unlink( invpost );
1089 }
1090
1091 exit(sig);
1092 }
1093