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