xref: /illumos-gate/usr/src/tools/cscope-fast/main.c (revision d48be21240dfd051b689384ce2b23479d757f2d8)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License, Version 1.0 only
6  * (the "License").  You may not use this file except in compliance
7  * with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or http://www.opensolaris.org/os/licensing.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*	Copyright (c) 1988 AT&T	*/
23 /*	  All Rights Reserved  	*/
24 
25 
26 /*
27  * Copyright 2005 Sun Microsystems, Inc.  All rights reserved.
28  * Use is subject to license terms.
29  */
30 
31 /*
32  *	cscope - interactive C symbol cross-reference
33  *
34  *	main functions
35  */
36 
37 #include <curses.h>	/* stdscr and TRUE */
38 #include <fcntl.h>	/* O_RDONLY */
39 #include <sys/types.h>	/* needed by stat.h */
40 #include <unistd.h>	/* O_RDONLY */
41 #include <unistd.h>	/* O_RDONLY */
42 #include <sys/stat.h>	/* stat */
43 #include <libgen.h>	/* O_RDONLY */
44 #include "global.h"
45 #include "version.h"	/* FILEVERSION and FIXVERSION */
46 #include "vp.h"		/* vpdirs and vpndirs */
47 
48 #define	OPTSEPS	" \t"	/* CSCOPEOPTION separators */
49 #define	MINHOURS 4	/* minimum no activity timeout hours */
50 
51 /* defaults for unset environment variables */
52 #define	EDITOR	"vi"
53 #define	SHELL	"sh"
54 #define	TMPDIR	"/tmp"
55 
56 /*
57  * note: these digraph character frequencies were calculated from possible
58  * printable digraphs in the cross-reference for the C compiler
59  */
60 char	dichar1[] = " teisaprnl(of)=c";	/* 16 most frequent first chars */
61 char	dichar2[] = " tnerpla";		/* 8 most frequent second chars */
62 					/* using the above as first chars */
63 char	dicode1[256];		/* digraph first character code */
64 char	dicode2[256];		/* digraph second character code */
65 
66 char	*editor, *home, *shell;	/* environment variables */
67 BOOL	compress = YES;		/* compress the characters in the crossref */
68 int	cscopedepth;		/* cscope invocation nesting depth */
69 char	currentdir[PATHLEN + 1]; /* current directory */
70 BOOL	dbtruncated;		/* database symbols are truncated to 8 chars */
71 char	**dbvpdirs;		/* directories (including current) in */
72 				/* database view path */
73 int	dbvpndirs;		/* # of directories in database view path */
74 int	dispcomponents = 1;	/* file path components to display */
75 BOOL	editallprompt = YES;	/* prompt between editing files */
76 int	fileargc;		/* file argument count */
77 char	**fileargv;		/* file argument values */
78 int	fileversion;		/* cross-reference file version */
79 BOOL	incurses;		/* in curses */
80 INVCONTROL invcontrol;		/* inverted file control structure */
81 BOOL	invertedindex;		/* the database has an inverted index */
82 BOOL	isuptodate;		/* consider the crossref up-to-date */
83 BOOL	linemode;		/* use line oriented user interface */
84 char	*namefile;		/* file of file names */
85 char	*newinvname;		/* new inverted index file name */
86 char	*newinvpost;		/* new inverted index postings file name */
87 char	*newreffile;		/* new cross-reference file name */
88 FILE	*newrefs;		/* new cross-reference */
89 BOOL	noacttimeout;		/* no activity timeout occurred */
90 BOOL	ogs;			/* display OGS book and subsystem names */
91 FILE	*postings;		/* new inverted index postings */
92 char	*prependpath;		/* prepend path to file names */
93 BOOL	returnrequired;		/* RETURN required after selection number */
94 int	symrefs = -1;		/* cross-reference file */
95 char	temp1[PATHLEN + 1];	/* temporary file name */
96 char	temp2[PATHLEN + 1];	/* temporary file name */
97 long	totalterms;		/* total inverted index terms */
98 BOOL	truncatesyms;		/* truncate symbols to 8 characters */
99 
100 static	BOOL	buildonly;		/* only build the database */
101 static	BOOL	fileschanged;		/* assume some files changed */
102 static	char	*invname = INVNAME;	/* inverted index to the database */
103 static	char	*invpost = INVPOST;	/* inverted index postings */
104 static	unsigned noacttime;		/* no activity timeout in seconds */
105 static	BOOL	onesearch;		/* one search only in line mode */
106 static	char	*reffile = REFFILE;	/* cross-reference file path name */
107 static	char	*reflines;		/* symbol reference lines file */
108 static	char	*tmpdir;		/* temporary directory */
109 static	long	traileroffset;		/* file trailer offset */
110 static	BOOL	unconditional;		/* unconditionally build database */
111 
112 static void options(int argc, char **argv);
113 static void printusage(void);
114 static void removeindex(void);
115 static void cannotindex(void);
116 static void initcompress(void);
117 static void opendatabase(void);
118 static void closedatabase(void);
119 static void build(void);
120 static int compare(const void *s1, const void *s2);
121 static char *getoldfile(void);
122 static void putheader(char *dir);
123 static void putlist(char **names, int count);
124 static BOOL samelist(FILE *oldrefs, char **names, int count);
125 static void skiplist(FILE *oldrefs);
126 static void copydata(void);
127 static void copyinverted(void);
128 static void putinclude(char *s);
129 static void movefile(char *new, char *old);
130 static void timedout(int sig);
131 
132 int
133 main(int argc, char **argv)
134 {
135 	int	envc;			/* environment argument count */
136 	char	**envv;			/* environment argument list */
137 	FILE	*names;			/* name file pointer */
138 	int	oldnum;			/* number in old cross-ref */
139 	char	path[PATHLEN + 1];	/* file path */
140 	FILE	*oldrefs;	/* old cross-reference file */
141 	char	*s;
142 	int	c, i;
143 	pid_t	pid;
144 
145 	/* save the command name for messages */
146 	argv0 = basename(argv[0]);
147 
148 	/* get the current directory for build() and line-oriented P command */
149 	if (mygetwd(currentdir) == NULL) {
150 		(void) fprintf(stderr,
151 		    "cscope: warning: cannot get current directory name\n");
152 		(void) strcpy(currentdir, "<unknown>");
153 	}
154 	/* initialize any view path; (saves time since currendir is known) */
155 	vpinit(currentdir);
156 	dbvpndirs = vpndirs; /* number of directories in database view path */
157 	/* directories (including current) in database view path */
158 	dbvpdirs = vpdirs;
159 
160 	/* the first source directory is the current directory */
161 	sourcedir(".");
162 
163 	/* read the environment */
164 	editor = mygetenv("EDITOR", EDITOR);
165 	editor = mygetenv("VIEWER", editor);	/* use viewer if set */
166 	home = getenv("HOME");
167 	shell = mygetenv("SHELL", SHELL);
168 	tmpdir = mygetenv("TMPDIR", TMPDIR);
169 	/* increment nesting depth */
170 	cscopedepth = atoi(mygetenv("CSCOPEDEPTH", "0"));
171 	(void) sprintf(path, "CSCOPEDEPTH=%d", ++cscopedepth);
172 	(void) putenv(stralloc(path));
173 	if ((s = getenv("CSCOPEOPTIONS")) != NULL) {
174 
175 		/* parse the environment option string */
176 		envc = 1;
177 		envv = mymalloc(sizeof (char *));
178 		s = strtok(stralloc(s), OPTSEPS);
179 		while (s != NULL) {
180 			envv = myrealloc(envv, ++envc * sizeof (char *));
181 			envv[envc - 1] = stralloc(s);
182 			s = strtok((char *)NULL, OPTSEPS);
183 		}
184 		/* set the environment options */
185 		options(envc, envv);
186 	}
187 	/* set the command line options */
188 	options(argc, argv);
189 
190 	/* create the temporary file names */
191 	pid = getpid();
192 	(void) sprintf(temp1, "%s/cscope%d.1", tmpdir, (int)pid);
193 	(void) sprintf(temp2, "%s/cscope%d.2", tmpdir, (int)pid);
194 
195 	/* if running in the foreground */
196 	if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
197 
198 		/* cleanup on the interrupt and quit signals */
199 		(void) signal(SIGINT, myexit);
200 		(void) signal(SIGQUIT, myexit);
201 	}
202 
203 	/* cleanup on the hangup signal */
204 	(void) signal(SIGHUP, myexit);
205 	/* if the database path is relative and it can't be created */
206 	if (reffile[0] != '/' && access(".", WRITE) != 0) {
207 
208 		/* if the database may not be up-to-date or can't be read */
209 		(void) sprintf(path, "%s/%s", home, reffile);
210 		if (isuptodate == NO || access(reffile, READ) != 0) {
211 
212 			/* put it in the home directory */
213 			reffile = stralloc(path);
214 			(void) sprintf(path, "%s/%s", home, invname);
215 			invname = stralloc(path);
216 			(void) sprintf(path, "%s/%s", home, invpost);
217 			invpost = stralloc(path);
218 			(void) fprintf(stderr,
219 			    "cscope: symbol database will be %s\n", reffile);
220 		}
221 	}
222 	/* if the cross-reference is to be considered up-to-date */
223 	if (isuptodate == YES) {
224 		if ((oldrefs = vpfopen(reffile, "r")) == NULL) {
225 			cannotopen(reffile);
226 			exit(1);
227 		}
228 		/*
229 		 * get the crossref file version but skip the current
230 		 * directory
231 		 */
232 		if (fscanf(oldrefs, "cscope %d %*s", &fileversion) != 1) {
233 			(void) fprintf(stderr,
234 			    "cscope: cannot read file version from file %s\n",
235 			    reffile);
236 			exit(1);
237 		}
238 		if (fileversion >= 8) {
239 
240 			/* override these command line options */
241 			compress = YES;
242 			invertedindex = NO;
243 
244 			/* see if there are options in the database */
245 			for (;;) {
246 				/* no -q leaves multiple blanks */
247 				while ((c = getc(oldrefs)) == ' ') {
248 					;
249 				}
250 				if (c != '-') {
251 					(void) ungetc(c, oldrefs);
252 					break;
253 				}
254 				switch (c = getc(oldrefs)) {
255 				case 'c':	/* ASCII characters only */
256 					compress = NO;
257 					break;
258 				case 'q':	/* quick search */
259 					invertedindex = YES;
260 					(void) fscanf(oldrefs,
261 					    "%ld", &totalterms);
262 					break;
263 				case 'T':
264 					/* truncate symbols to 8 characters */
265 					dbtruncated = YES;
266 					truncatesyms = YES;
267 					break;
268 				}
269 			}
270 			initcompress();
271 
272 			/* seek to the trailer */
273 			if (fscanf(oldrefs, "%ld", &traileroffset) != 1) {
274 				(void) fprintf(stderr,
275 				    "cscope: cannot read trailer offset from "
276 				    "file %s\n", reffile);
277 				exit(1);
278 			}
279 			if (fseek(oldrefs, traileroffset, 0) != 0) {
280 				(void) fprintf(stderr,
281 				    "cscope: cannot seek to trailer in "
282 				    "file %s\n", reffile);
283 				exit(1);
284 			}
285 		}
286 		/*
287 		 * read the view path for use in converting relative paths to
288 		 * full paths
289 		 *
290 		 * note: don't overwrite vp[n]dirs because this can cause
291 		 * the wrong database index files to be found in the viewpath
292 		 */
293 		if (fileversion >= 13) {
294 			if (fscanf(oldrefs, "%d", &dbvpndirs) != 1) {
295 				(void) fprintf(stderr,
296 				    "cscope: cannot read view path size from "
297 				    "file %s\n", reffile);
298 				exit(1);
299 			}
300 			if (dbvpndirs > 0) {
301 				dbvpdirs = mymalloc(
302 				    dbvpndirs * sizeof (char *));
303 				for (i = 0; i < dbvpndirs; ++i) {
304 					if (fscanf(oldrefs, "%s", path) != 1) {
305 						(void) fprintf(stderr,
306 						    "cscope: cannot read view "
307 						    "path from file %s\n",
308 						    reffile);
309 						exit(1);
310 					}
311 					dbvpdirs[i] = stralloc(path);
312 				}
313 			}
314 		}
315 		/* skip the source and include directory lists */
316 		skiplist(oldrefs);
317 		skiplist(oldrefs);
318 
319 		/* get the number of source files */
320 		if (fscanf(oldrefs, "%d", &nsrcfiles) != 1) {
321 			(void) fprintf(stderr,
322 			    "cscope: cannot read source file size from "
323 			    "file %s\n", reffile);
324 			exit(1);
325 		}
326 		/* get the source file list */
327 		srcfiles = mymalloc(nsrcfiles * sizeof (char *));
328 		if (fileversion >= 9) {
329 
330 			/* allocate the string space */
331 			if (fscanf(oldrefs, "%d", &oldnum) != 1) {
332 				(void) fprintf(stderr,
333 				    "cscope: cannot read string space size "
334 				    "from file %s\n", reffile);
335 				exit(1);
336 			}
337 			s = mymalloc(oldnum);
338 			(void) getc(oldrefs);	/* skip the newline */
339 
340 			/* read the strings */
341 			if (fread(s, oldnum, 1, oldrefs) != 1) {
342 				(void) fprintf(stderr,
343 				    "cscope: cannot read source file names "
344 				    "from file %s\n", reffile);
345 				exit(1);
346 			}
347 			/* change newlines to nulls */
348 			for (i = 0; i < nsrcfiles; ++i) {
349 				srcfiles[i] = s;
350 				for (++s; *s != '\n'; ++s) {
351 					;
352 				}
353 				*s = '\0';
354 				++s;
355 			}
356 			/* if there is a file of source file names */
357 			if (namefile != NULL &&
358 			    (names = vpfopen(namefile, "r")) != NULL ||
359 			    (names = vpfopen(NAMEFILE, "r")) != NULL) {
360 
361 				/* read any -p option from it */
362 				while (fscanf(names, "%s", path) == 1 &&
363 				    *path == '-') {
364 					i = path[1];
365 					s = path + 2;	/* for "-Ipath" */
366 					if (*s == '\0') {
367 						/* if "-I path" */
368 						(void) fscanf(names,
369 						    "%s", path);
370 						s = path;
371 					}
372 					switch (i) {
373 					case 'p':
374 						/* file path components */
375 						/* to display */
376 						if (*s < '0' || *s > '9') {
377 							(void) fprintf(stderr,
378 							    "cscope: -p option "
379 							    "in file %s: "
380 							    "missing or "
381 							    "invalid numeric "
382 							    "value\n",
383 							    namefile);
384 						}
385 						dispcomponents = atoi(s);
386 					}
387 				}
388 				(void) fclose(names);
389 			}
390 		} else {
391 			for (i = 0; i < nsrcfiles; ++i) {
392 				if (fscanf(oldrefs, "%s", path) != 1) {
393 					(void) fprintf(stderr,
394 					    "cscope: cannot read source file "
395 					    "name from file %s\n", reffile);
396 					exit(1);
397 				}
398 				srcfiles[i] = stralloc(path);
399 			}
400 		}
401 		(void) fclose(oldrefs);
402 	} else {
403 		/* get source directories from the environment */
404 		if ((s = getenv("SOURCEDIRS")) != NULL) {
405 			sourcedir(s);
406 		}
407 		/* make the source file list */
408 		srcfiles = mymalloc(msrcfiles * sizeof (char *));
409 		makefilelist();
410 		if (nsrcfiles == 0) {
411 			(void) fprintf(stderr,
412 			    "cscope: no source files found\n");
413 			printusage();
414 			exit(1);
415 		}
416 		/* get include directories from the environment */
417 		if ((s = getenv("INCLUDEDIRS")) != NULL) {
418 			includedir(s);
419 		}
420 		/* add /usr/include to the #include directory list */
421 		includedir("/usr/include");
422 
423 		/* initialize the C keyword table */
424 		initsymtab();
425 
426 		/* create the file name(s) used for a new cross-reference */
427 		(void) strcpy(path, reffile);
428 		s = basename(path);
429 		*s = '\0';
430 		(void) strcat(path, "n");
431 		++s;
432 		(void) strcpy(s, basename(reffile));
433 		newreffile = stralloc(path);
434 		(void) strcpy(s, basename(invname));
435 		newinvname = stralloc(path);
436 		(void) strcpy(s, basename(invpost));
437 		newinvpost = stralloc(path);
438 
439 		/* build the cross-reference */
440 		initcompress();
441 		build();
442 		if (buildonly == YES) {
443 			exit(0);
444 		}
445 	}
446 	opendatabase();
447 
448 	/*
449 	 * removing a database will not release the disk space if a cscope
450 	 * process has the file open, so a project may want unattended cscope
451 	 * processes to exit overnight, including their subshells and editors
452 	 */
453 	if (noacttime) {
454 		(void) signal(SIGALRM, timedout);
455 		(void) alarm(noacttime);
456 	}
457 	/*
458 	 * if using the line oriented user interface so cscope can be a
459 	 * subprocess to emacs or samuel
460 	 */
461 	if (linemode == YES) {
462 		if (*pattern != '\0') {		/* do any optional search */
463 			if (search() == YES) {
464 				while ((c = getc(refsfound)) != EOF) {
465 					(void) putchar(c);
466 				}
467 			}
468 		}
469 		if (onesearch == YES) {
470 			myexit(0);
471 		}
472 		for (;;) {
473 			char buf[PATLEN + 2];
474 			if (noacttime) {
475 				(void) alarm(noacttime);
476 			}
477 			(void) printf(">> ");
478 			(void) fflush(stdout);
479 			if (fgets(buf, sizeof (buf), stdin) == NULL) {
480 				myexit(0);
481 			}
482 			/* remove any trailing newline character */
483 			if (*(s = buf + strlen(buf) - 1) == '\n') {
484 				*s = '\0';
485 			}
486 			switch (*buf) {
487 			case '0':
488 			case '1':
489 			case '2':
490 			case '3':
491 			case '4':
492 			case '5':
493 			case '6':
494 			case '7':
495 			case '8':
496 			case '9':	/* samuel only */
497 				field = *buf - '0';
498 				(void) strcpy(pattern, buf + 1);
499 				(void) search();
500 				(void) printf("cscope: %d lines\n", totallines);
501 				while ((c = getc(refsfound)) != EOF) {
502 					(void) putchar(c);
503 				}
504 				break;
505 
506 			case 'c':	/* toggle caseless mode */
507 			case ctrl('C'):
508 				if (caseless == NO) {
509 					caseless = YES;
510 				} else {
511 					caseless = NO;
512 				}
513 				egrepcaseless(caseless);
514 				break;
515 
516 			case 'r':	/* rebuild database cscope style */
517 			case ctrl('R'):
518 				freefilelist();
519 				makefilelist();
520 				/* FALLTHROUGH */
521 
522 			case 'R':	/* rebuild database samuel style */
523 				rebuild();
524 				(void) putchar('\n');
525 				break;
526 
527 			case 'C':	/* clear file names */
528 				freefilelist();
529 				(void) putchar('\n');
530 				break;
531 
532 			case 'F':	/* add a file name */
533 				(void) strcpy(path, buf + 1);
534 				if (infilelist(path) == NO &&
535 				    vpaccess(path, READ) == 0) {
536 					addsrcfile(path);
537 				}
538 				(void) putchar('\n');
539 				break;
540 
541 			case 'P':	/* print the path to the files */
542 				if (prependpath != NULL) {
543 					(void) puts(prependpath);
544 				} else {
545 					(void) puts(currentdir);
546 				}
547 				break;
548 
549 			case 'q':	/* quit */
550 			case ctrl('D'):
551 			case ctrl('Z'):
552 				myexit(0);
553 
554 			default:
555 				(void) fprintf(stderr,
556 				    "cscope: unknown command '%s'\n", buf);
557 				break;
558 			}
559 		}
560 		/* NOTREACHED */
561 	}
562 	/* pause before clearing the screen if there have been error messages */
563 	if (errorsfound == YES) {
564 		errorsfound = NO;
565 		askforreturn();
566 	}
567 	(void) signal(SIGINT, SIG_IGN);	/* ignore interrupts */
568 	(void) signal(SIGPIPE, SIG_IGN); /* | command can cause pipe signal */
569 	/* initialize the curses display package */
570 	(void) initscr();	/* initialize the screen */
571 	setfield();	/* set the initial cursor position */
572 	entercurses();
573 	(void) keypad(stdscr, TRUE);	/* enable the keypad */
574 	dispinit();	/* initialize display parameters */
575 	putmsg("");	/* clear any build progress message */
576 	display();	/* display the version number and input fields */
577 
578 	/* do any optional search */
579 	if (*pattern != '\0') {
580 		atfield();		/* move to the input field */
581 		(void) command(ctrl('A'));	/* search */
582 		display();		/* update the display */
583 	} else if (reflines != NULL) {
584 		/* read any symbol reference lines file */
585 		(void) readrefs(reflines);
586 		display();		/* update the display */
587 	}
588 	for (;;) {
589 		if (noacttime) {
590 			(void) alarm(noacttime);
591 		}
592 		atfield();	/* move to the input field */
593 
594 		/* exit if the quit command is entered */
595 		if ((c = mygetch()) == EOF || c == ctrl('D') ||
596 		    c == ctrl('Z')) {
597 			break;
598 		}
599 		/* execute the commmand, updating the display if necessary */
600 		if (command(c) == YES) {
601 			display();
602 		}
603 	}
604 	/* cleanup and exit */
605 	myexit(0);
606 	/* NOTREACHED */
607 	return (0);
608 }
609 
610 static void
611 options(int argc, char **argv)
612 {
613 	char	path[PATHLEN + 1];	/* file path */
614 	int	c;
615 	char	*s;
616 
617 	while (--argc > 0 && (*++argv)[0] == '-') {
618 		for (s = argv[0] + 1; *s != '\0'; s++) {
619 			/* look for an input field number */
620 			if (isdigit(*s)) {
621 				field = *s - '0';
622 				if (*++s == '\0' && --argc > 0) {
623 					s = *++argv;
624 				}
625 				if (strlen(s) > PATLEN) {
626 					(void) fprintf(stderr,
627 					    "cscope: pattern too long, cannot "
628 					    "be > %d characters\n", PATLEN);
629 					exit(1);
630 				}
631 				(void) strcpy(pattern, s);
632 				goto nextarg;
633 			}
634 			switch (*s) {
635 			case '-':	/* end of options */
636 				--argc;
637 				++argv;
638 				goto lastarg;
639 			case 'V':	/* print the version number */
640 				(void) fprintf(stderr,
641 				    "%s: version %d%s\n", argv0,
642 				    FILEVERSION, FIXVERSION);
643 				exit(0);
644 				/*NOTREACHED*/
645 			case 'b':	/* only build the cross-reference */
646 				buildonly = YES;
647 				break;
648 			case 'c':	/* ASCII characters only in crossref */
649 				compress = NO;
650 				break;
651 			case 'C':
652 				/* turn on caseless mode for symbol searches */
653 				caseless = YES;
654 				/* simulate egrep -i flag */
655 				egrepcaseless(caseless);
656 				break;
657 			case 'd':	/* consider crossref up-to-date */
658 				isuptodate = YES;
659 				break;
660 			case 'e':	/* suppress ^E prompt between files */
661 				editallprompt = NO;
662 				break;
663 			case 'L':
664 				onesearch = YES;
665 				/* FALLTHROUGH */
666 			case 'l':
667 				linemode = YES;
668 				break;
669 			case 'o':
670 				/* display OGS book and subsystem names */
671 				ogs = YES;
672 				break;
673 			case 'q':	/* quick search */
674 				invertedindex = YES;
675 				break;
676 			case 'r':	/* display as many lines as possible */
677 				returnrequired = YES;
678 				break;
679 			case 'T':	/* truncate symbols to 8 characters */
680 				truncatesyms = YES;
681 				break;
682 			case 'u':
683 				/* unconditionally build the cross-reference */
684 				unconditional = YES;
685 				break;
686 			case 'U':	/* assume some files have changed */
687 				fileschanged = YES;
688 				break;
689 			case 'f':	/* alternate cross-reference file */
690 			case 'F':	/* symbol reference lines file */
691 			case 'i':	/* file containing file names */
692 			case 'I':	/* #include file directory */
693 			case 'p':	/* file path components to display */
694 			case 'P':	/* prepend path to file names */
695 			case 's':	/* additional source file directory */
696 			case 'S':
697 			case 't':	/* no activity timeout in hours */
698 				c = *s;
699 				if (*++s == '\0' && --argc > 0) {
700 					s = *++argv;
701 				}
702 				if (*s == '\0') {
703 					(void) fprintf(stderr,
704 					    "%s: -%c option: missing or empty "
705 					    "value\n", argv0, c);
706 					goto usage;
707 				}
708 				switch (c) {
709 				case 'f':
710 					/* alternate cross-reference file */
711 					reffile = s;
712 					(void) strcpy(path, s);
713 					/* System V has a 14 character limit */
714 					s = basename(path);
715 					if ((int)strlen(s) > 11) {
716 						s[11] = '\0';
717 					}
718 					s = path + strlen(path);
719 					(void) strcpy(s, ".in");
720 					invname = stralloc(path);
721 					(void) strcpy(s, ".po");
722 					invpost = stralloc(path);
723 					break;
724 				case 'F':
725 					/* symbol reference lines file */
726 					reflines = s;
727 					break;
728 				case 'i':	/* file containing file names */
729 					namefile = s;
730 					break;
731 				case 'I':	/* #include file directory */
732 					includedir(s);
733 					break;
734 				case 'p':
735 					/* file path components to display */
736 					if (*s < '0' || *s > '9') {
737 						(void) fprintf(stderr,
738 						    "%s: -p option: missing "
739 						    "or invalid numeric "
740 						    "value\n", argv0);
741 						goto usage;
742 					}
743 					dispcomponents = atoi(s);
744 					break;
745 				case 'P':	/* prepend path to file names */
746 					prependpath = s;
747 					break;
748 				case 's':
749 				case 'S':
750 					/* additional source directory */
751 					sourcedir(s);
752 					break;
753 				case 't':
754 					/* no activity timeout in hours */
755 					if (*s < '1' || *s > '9') {
756 						(void) fprintf(stderr,
757 						    "%s: -t option: missing or "
758 						    "invalid numeric value\n",
759 						    argv0);
760 						goto usage;
761 					}
762 					c = atoi(s);
763 					if (c < MINHOURS) {
764 						(void) fprintf(stderr,
765 						    "cscope: minimum timeout "
766 						    "is %d hours\n", MINHOURS);
767 						(void) sleep(3);
768 						c = MINHOURS;
769 					}
770 					noacttime = c * 3600;
771 					break;
772 				}
773 				goto nextarg;
774 			default:
775 				(void) fprintf(stderr,
776 				    "%s: unknown option: -%c\n", argv0, *s);
777 			usage:
778 				printusage();
779 				exit(1);
780 			}
781 		}
782 nextarg:	continue;
783 	}
784 lastarg:
785 	/* save the file arguments */
786 	fileargc = argc;
787 	fileargv = argv;
788 }
789 
790 static void
791 printusage(void)
792 {
793 	(void) fprintf(stderr,
794 	    "Usage:  cscope [-bcdelLoqrtTuUV] [-f file] [-F file] [-i file] "
795 	    "[-I dir] [-s dir]\n");
796 	(void) fprintf(stderr,
797 	    "               [-p number] [-P path] [-[0-8] pattern] "
798 	    "[source files]\n");
799 	(void) fprintf(stderr,
800 	    "-b		Build the database only.\n");
801 	(void) fprintf(stderr,
802 	    "-c		Use only ASCII characters in the database file, "
803 	    "that is,\n");
804 	(void) fprintf(stderr,
805 	    "		do not compress the data.\n");
806 	(void) fprintf(stderr,
807 	    "-d		Do not update the database.\n");
808 	(void) fprintf(stderr,
809 	    "-f \"file\"	Use \"file\" as the database file name "
810 	    "instead of\n");
811 	(void) fprintf(stderr,
812 	    "		the default (cscope.out).\n");
813 	(void) fprintf(stderr,
814 	    "-F \"file\"	Read symbol reference lines from file, just\n");
815 /* BEGIN CSTYLED */
816 	(void) fprintf(stderr,
817 	    "		like the \"<\" command.\n");
818 /* END CSTYLED */
819 	(void) fprintf(stderr,
820 	    "-i \"file\"	Read any -I, -p, -q, and -T options and the\n");
821 	(void) fprintf(stderr,
822 	    "		list of source files from \"file\" instead of the \n");
823 	(void) fprintf(stderr,
824 	    "		default (cscope.files).\n");
825 	(void) fprintf(stderr,
826 	    "-I \"dir\"	Look in \"dir\" for #include files.\n");
827 	(void) fprintf(stderr,
828 	    "-q		Build an inverted index for quick symbol seaching.\n");
829 	(void) fprintf(stderr,
830 	    "-s \"dir\"	Look in \"dir\" for additional source files.\n");
831 }
832 
833 static void
834 removeindex(void)
835 {
836 	(void) fprintf(stderr,
837 	    "cscope: removed files %s and %s\n", invname, invpost);
838 	(void) unlink(invname);
839 	(void) unlink(invpost);
840 }
841 
842 static void
843 cannotindex(void)
844 {
845 	(void) fprintf(stderr,
846 	    "cscope: cannot create inverted index; ignoring -q option\n");
847 	invertedindex = NO;
848 	errorsfound = YES;
849 	(void) fprintf(stderr,
850 	    "cscope: removed files %s and %s\n", newinvname, newinvpost);
851 	(void) unlink(newinvname);
852 	(void) unlink(newinvpost);
853 	removeindex();	/* remove any existing index to prevent confusion */
854 }
855 
856 void
857 cannotopen(char *file)
858 {
859 	char	msg[MSGLEN + 1];
860 
861 	(void) sprintf(msg, "Cannot open file %s", file);
862 	putmsg(msg);
863 }
864 
865 void
866 cannotwrite(char *file)
867 {
868 	char	msg[MSGLEN + 1];
869 
870 	(void) sprintf(msg, "Removed file %s because write failed", file);
871 	myperror(msg);	/* display the reason */
872 	(void) unlink(file);
873 	myexit(1);	/* calls exit(2), which closes files */
874 }
875 
876 /* set up the digraph character tables for text compression */
877 
878 static void
879 initcompress(void)
880 {
881 	int	i;
882 
883 	if (compress == YES) {
884 		for (i = 0; i < 16; ++i) {
885 			dicode1[(unsigned)(dichar1[i])] = i * 8 + 1;
886 		}
887 		for (i = 0; i < 8; ++i) {
888 			dicode2[(unsigned)(dichar2[i])] = i + 1;
889 		}
890 	}
891 }
892 
893 /* open the database */
894 
895 static void
896 opendatabase(void)
897 {
898 	if ((symrefs = vpopen(reffile, O_RDONLY)) == -1) {
899 		cannotopen(reffile);
900 		myexit(1);
901 	}
902 	blocknumber = -1;	/* force next seek to read the first block */
903 
904 	/* open any inverted index */
905 	if (invertedindex == YES &&
906 	    invopen(&invcontrol, invname, invpost, INVAVAIL) == -1) {
907 		askforreturn();		/* so user sees message */
908 		invertedindex = NO;
909 	}
910 }
911 
912 /* close the database */
913 
914 static void
915 closedatabase(void)
916 {
917 	(void) close(symrefs);
918 	if (invertedindex == YES) {
919 		invclose(&invcontrol);
920 		nsrcoffset = 0;
921 		npostings = 0;
922 	}
923 }
924 
925 /* rebuild the database */
926 
927 void
928 rebuild(void)
929 {
930 	closedatabase();
931 	build();
932 	opendatabase();
933 
934 	/* revert to the initial display */
935 	if (refsfound != NULL) {
936 		(void) fclose(refsfound);
937 		refsfound = NULL;
938 	}
939 	*lastfilepath = '\0';	/* last file may have new path */
940 }
941 
942 /* build the cross-reference */
943 
944 static void
945 build(void)
946 {
947 	int	i;
948 	FILE	*oldrefs;	/* old cross-reference file */
949 	time_t	reftime;	/* old crossref modification time */
950 	char	*file;			/* current file */
951 	char	*oldfile;		/* file in old cross-reference */
952 	char	newdir[PATHLEN + 1];	/* directory in new cross-reference */
953 	char	olddir[PATHLEN + 1];	/* directory in old cross-reference */
954 	char	oldname[PATHLEN + 1];	/* name in old cross-reference */
955 	int	oldnum;			/* number in old cross-ref */
956 	struct	stat statstruct;	/* file status */
957 	int	firstfile;		/* first source file in pass */
958 	int	lastfile;		/* last source file in pass */
959 	int	built = 0;		/* built crossref for these files */
960 	int	copied = 0;		/* copied crossref for these files */
961 	BOOL	interactive = YES;	/* output progress messages */
962 
963 	/*
964 	 * normalize the current directory relative to the home directory so
965 	 * the cross-reference is not rebuilt when the user's login is moved
966 	 */
967 	(void) strcpy(newdir, currentdir);
968 	if (strcmp(currentdir, home) == 0) {
969 		(void) strcpy(newdir, "$HOME");
970 	} else if (strncmp(currentdir, home, strlen(home)) == 0) {
971 		(void) sprintf(newdir, "$HOME%s", currentdir + strlen(home));
972 	}
973 	/* sort the source file names (needed for rebuilding) */
974 	qsort((char *)srcfiles, (unsigned)nsrcfiles, sizeof (char *), compare);
975 
976 	/*
977 	 * if there is an old cross-reference and its current directory
978 	 * matches or this is an unconditional build
979 	 */
980 	if ((oldrefs = vpfopen(reffile, "r")) != NULL && unconditional == NO &&
981 	    fscanf(oldrefs, "cscope %d %s", &fileversion, olddir) == 2 &&
982 	    (strcmp(olddir, currentdir) == 0 || /* remain compatible */
983 	    strcmp(olddir, newdir) == 0)) {
984 
985 		/* get the cross-reference file's modification time */
986 		(void) fstat(fileno(oldrefs), &statstruct);
987 		reftime = statstruct.st_mtime;
988 		if (fileversion >= 8) {
989 			BOOL	oldcompress = YES;
990 			BOOL	oldinvertedindex = NO;
991 			BOOL	oldtruncatesyms = NO;
992 			int	c;
993 
994 			/* see if there are options in the database */
995 			for (;;) {
996 				while ((c = getc(oldrefs)) == ' ') {
997 				}
998 				if (c != '-') {
999 					(void) ungetc(c, oldrefs);
1000 					break;
1001 				}
1002 				switch (c = getc(oldrefs)) {
1003 				case 'c':	/* ASCII characters only */
1004 					oldcompress = NO;
1005 					break;
1006 				case 'q':	/* quick search */
1007 					oldinvertedindex = YES;
1008 					(void) fscanf(oldrefs,
1009 					    "%ld", &totalterms);
1010 					break;
1011 				case 'T':
1012 					/* truncate symbols to 8 characters */
1013 					oldtruncatesyms = YES;
1014 					break;
1015 				}
1016 			}
1017 			/* check the old and new option settings */
1018 			if (oldcompress != compress ||
1019 			    oldtruncatesyms != truncatesyms) {
1020 				(void) fprintf(stderr,
1021 				    "cscope: -c or -T option mismatch between "
1022 				    "command line and old symbol database\n");
1023 				goto force;
1024 			}
1025 			if (oldinvertedindex != invertedindex) {
1026 				(void) fprintf(stderr,
1027 				    "cscope: -q option mismatch between "
1028 				    "command line and old symbol database\n");
1029 				if (invertedindex == NO) {
1030 					removeindex();
1031 				}
1032 				goto outofdate;
1033 			}
1034 			/* seek to the trailer */
1035 			if (fscanf(oldrefs, "%ld", &traileroffset) != 1 ||
1036 			    fseek(oldrefs, traileroffset, 0) == -1) {
1037 				(void) fprintf(stderr,
1038 				    "cscope: incorrect symbol database file "
1039 				    "format\n");
1040 				goto force;
1041 			}
1042 		}
1043 		/* if assuming that some files have changed */
1044 		if (fileschanged == YES) {
1045 			goto outofdate;
1046 		}
1047 		/* see if the view path is the same */
1048 		if (fileversion >= 13 &&
1049 		    samelist(oldrefs, vpdirs, vpndirs) == NO) {
1050 			goto outofdate;
1051 		}
1052 		/* see if the directory lists are the same */
1053 		if (samelist(oldrefs, srcdirs, nsrcdirs) == NO ||
1054 		    samelist(oldrefs, incdirs, nincdirs) == NO ||
1055 		    fscanf(oldrefs, "%d", &oldnum) != 1 ||
1056 		    fileversion >= 9 && fscanf(oldrefs, "%*s") != 0) {
1057 			/* skip the string space size */
1058 			goto outofdate;
1059 		}
1060 		/*
1061 		 * see if the list of source files is the same and
1062 		 * none have been changed up to the included files
1063 		 */
1064 		for (i = 0; i < nsrcfiles; ++i) {
1065 			if (fscanf(oldrefs, "%s", oldname) != 1 ||
1066 			    strnotequal(oldname, srcfiles[i]) ||
1067 			    vpstat(srcfiles[i], &statstruct) != 0 ||
1068 			    statstruct.st_mtime > reftime) {
1069 				goto outofdate;
1070 			}
1071 		}
1072 		/* the old cross-reference is up-to-date */
1073 		/* so get the list of included files */
1074 		while (i++ < oldnum && fscanf(oldrefs, "%s", oldname) == 1) {
1075 			addsrcfile(oldname);
1076 		}
1077 		(void) fclose(oldrefs);
1078 		return;
1079 
1080 outofdate:
1081 		/* if the database format has changed, rebuild it all */
1082 		if (fileversion != FILEVERSION) {
1083 			(void) fprintf(stderr,
1084 			    "cscope: converting to new symbol database file "
1085 			    "format\n");
1086 			goto force;
1087 		}
1088 		/* reopen the old cross-reference file for fast scanning */
1089 		if ((symrefs = vpopen(reffile, O_RDONLY)) == -1) {
1090 			cannotopen(reffile);
1091 			myexit(1);
1092 		}
1093 		/* get the first file name in the old cross-reference */
1094 		blocknumber = -1;
1095 		(void) readblock();	/* read the first cross-ref block */
1096 		(void) scanpast('\t');	/* skip the header */
1097 		oldfile = getoldfile();
1098 	} else {	/* force cross-referencing of all the source files */
1099 force:
1100 		reftime = 0;
1101 		oldfile = NULL;
1102 	}
1103 	/* open the new cross-reference file */
1104 	if ((newrefs = fopen(newreffile, "w")) == NULL) {
1105 		cannotopen(newreffile);
1106 		myexit(1);
1107 	}
1108 	if (invertedindex == YES && (postings = fopen(temp1, "w")) == NULL) {
1109 		cannotopen(temp1);
1110 		cannotindex();
1111 	}
1112 	(void) fprintf(stderr, "cscope: building symbol database\n");
1113 	putheader(newdir);
1114 	fileversion = FILEVERSION;
1115 	if (buildonly == YES && !isatty(0)) {
1116 		interactive = NO;
1117 	} else {
1118 		initprogress();
1119 	}
1120 	/* output the leading tab expected by crossref() */
1121 	dbputc('\t');
1122 
1123 	/*
1124 	 * make passes through the source file list until the last level of
1125 	 * included files is processed
1126 	 */
1127 	firstfile = 0;
1128 	lastfile = nsrcfiles;
1129 	if (invertedindex == YES) {
1130 		srcoffset = mymalloc((nsrcfiles + 1) * sizeof (long));
1131 	}
1132 	for (;;) {
1133 
1134 		/* get the next source file name */
1135 		for (fileindex = firstfile; fileindex < lastfile; ++fileindex) {
1136 			/* display the progress about every three seconds */
1137 			if (interactive == YES && fileindex % 10 == 0) {
1138 				if (copied == 0) {
1139 					progress("%ld files built",
1140 					    (long)built, 0L);
1141 				} else {
1142 					progress("%ld files built, %ld "
1143 					    "files copied", (long)built,
1144 					    (long)copied);
1145 				}
1146 			}
1147 			/* if the old file has been deleted get the next one */
1148 			file = srcfiles[fileindex];
1149 			while (oldfile != NULL && strcmp(file, oldfile) > 0) {
1150 				oldfile = getoldfile();
1151 			}
1152 			/*
1153 			 * if there isn't an old database or this is
1154 			 * a new file
1155 			 */
1156 			if (oldfile == NULL || strcmp(file, oldfile) < 0) {
1157 				crossref(file);
1158 				++built;
1159 			} else if (vpstat(file, &statstruct) == 0 &&
1160 			    statstruct.st_mtime > reftime) {
1161 				/* if this file was modified */
1162 				crossref(file);
1163 				++built;
1164 
1165 				/*
1166 				 * skip its old crossref so modifying the last
1167 				 * source file does not cause all included files
1168 				 * to be built.  Unfortunately a new file that
1169 				 * is alphabetically last will cause all
1170 				 * included files to be built, but this is
1171 				 * less likely
1172 				 */
1173 				oldfile = getoldfile();
1174 			} else {	/* copy its cross-reference */
1175 				putfilename(file);
1176 				if (invertedindex == YES) {
1177 					copyinverted();
1178 				} else {
1179 					copydata();
1180 				}
1181 				++copied;
1182 				oldfile = getoldfile();
1183 			}
1184 		}
1185 		/* see if any included files were found */
1186 		if (lastfile == nsrcfiles) {
1187 			break;
1188 		}
1189 		firstfile = lastfile;
1190 		lastfile = nsrcfiles;
1191 		if (invertedindex == YES) {
1192 			srcoffset = myrealloc(srcoffset,
1193 			    (nsrcfiles + 1) * sizeof (long));
1194 		}
1195 		/* sort the included file names */
1196 		qsort((char *)&srcfiles[firstfile],
1197 		    (unsigned)(lastfile - firstfile), sizeof (char *), compare);
1198 	}
1199 	/* add a null file name to the trailing tab */
1200 	putfilename("");
1201 	dbputc('\n');
1202 
1203 	/* get the file trailer offset */
1204 
1205 	traileroffset = dboffset;
1206 
1207 	/*
1208 	 * output the view path and source and include directory and
1209 	 * file lists
1210 	 */
1211 	putlist(vpdirs, vpndirs);
1212 	putlist(srcdirs, nsrcdirs);
1213 	putlist(incdirs, nincdirs);
1214 	putlist(srcfiles, nsrcfiles);
1215 	if (fflush(newrefs) == EOF) {
1216 		/* rewind doesn't check for write failure */
1217 		cannotwrite(newreffile);
1218 		/* NOTREACHED */
1219 	}
1220 	/* create the inverted index if requested */
1221 	if (invertedindex == YES) {
1222 		char	sortcommand[PATHLEN + 1];
1223 
1224 		if (fflush(postings) == EOF) {
1225 			cannotwrite(temp1);
1226 			/* NOTREACHED */
1227 		}
1228 		(void) fstat(fileno(postings), &statstruct);
1229 		(void) fprintf(stderr,
1230 		    "cscope: building symbol index: temporary file size is "
1231 		    "%ld bytes\n", statstruct.st_size);
1232 		(void) fclose(postings);
1233 	/*
1234 	 * sort -T is broken until it is fixed we don't have too much choice
1235 	 */
1236 	/*
1237 	 * (void) sprintf(sortcommand, "sort -y -T %s %s", tmpdir, temp1);
1238 	 */
1239 	(void) sprintf(sortcommand, "LC_ALL=C sort %s", temp1);
1240 		if ((postings = popen(sortcommand, "r")) == NULL) {
1241 			(void) fprintf(stderr,
1242 			    "cscope: cannot open pipe to sort command\n");
1243 			cannotindex();
1244 		} else {
1245 			if ((totalterms = invmake(newinvname, newinvpost,
1246 			    postings)) > 0) {
1247 				movefile(newinvname, invname);
1248 				movefile(newinvpost, invpost);
1249 			} else {
1250 				cannotindex();
1251 			}
1252 			(void) pclose(postings);
1253 		}
1254 		(void) unlink(temp1);
1255 		(void) free(srcoffset);
1256 		(void) fprintf(stderr,
1257 		    "cscope: index has %ld references to %ld symbols\n",
1258 		    npostings, totalterms);
1259 	}
1260 	/* rewrite the header with the trailer offset and final option list */
1261 	rewind(newrefs);
1262 	putheader(newdir);
1263 	(void) fclose(newrefs);
1264 
1265 	/* close the old database file */
1266 	if (symrefs >= 0) {
1267 		(void) close(symrefs);
1268 	}
1269 	if (oldrefs != NULL) {
1270 		(void) fclose(oldrefs);
1271 	}
1272 	/* replace it with the new database file */
1273 	movefile(newreffile, reffile);
1274 }
1275 
1276 /* string comparison function for qsort */
1277 
1278 static int
1279 compare(const void *s1, const void *s2)
1280 {
1281 	return (strcmp((char *)s1, (char *)s2));
1282 }
1283 
1284 /* get the next file name in the old cross-reference */
1285 
1286 static char *
1287 getoldfile(void)
1288 {
1289 	static	char	file[PATHLEN + 1];	/* file name in old crossref */
1290 
1291 	if (blockp != NULL) {
1292 		do {
1293 			if (*blockp == NEWFILE) {
1294 				skiprefchar();
1295 				getstring(file);
1296 				if (file[0] != '\0') {
1297 					/* if not end-of-crossref */
1298 					return (file);
1299 				}
1300 				return (NULL);
1301 			}
1302 		} while (scanpast('\t') != NULL);
1303 	}
1304 	return (NULL);
1305 }
1306 
1307 /*
1308  * output the cscope version, current directory, database format options, and
1309  * the database trailer offset
1310  */
1311 
1312 static void
1313 putheader(char *dir)
1314 {
1315 	dboffset = fprintf(newrefs, "cscope %d %s", FILEVERSION, dir);
1316 	if (compress == NO) {
1317 		dboffset += fprintf(newrefs, " -c");
1318 	}
1319 	if (invertedindex == YES) {
1320 		dboffset += fprintf(newrefs, " -q %.10ld", totalterms);
1321 	} else {
1322 		/*
1323 		 * leave space so if the header is overwritten without -q
1324 		 * because writing the inverted index failed, the header is
1325 		 * the same length
1326 		 */
1327 		dboffset += fprintf(newrefs, "              ");
1328 	}
1329 	if (truncatesyms == YES) {
1330 		dboffset += fprintf(newrefs, " -T");
1331 	}
1332 	dbfprintf(newrefs, " %.10ld\n", traileroffset);
1333 }
1334 
1335 /* put the name list into the cross-reference file */
1336 
1337 static void
1338 putlist(char **names, int count)
1339 {
1340 	int	i, size = 0;
1341 
1342 	(void) fprintf(newrefs, "%d\n", count);
1343 	if (names == srcfiles) {
1344 
1345 		/* calculate the string space needed */
1346 		for (i = 0; i < count; ++i) {
1347 			size += strlen(names[i]) + 1;
1348 		}
1349 		(void) fprintf(newrefs, "%d\n", size);
1350 	}
1351 	for (i = 0; i < count; ++i) {
1352 		if (fputs(names[i], newrefs) == EOF ||
1353 		    putc('\n', newrefs) == EOF) {
1354 			cannotwrite(newreffile);
1355 			/* NOTREACHED */
1356 		}
1357 	}
1358 }
1359 
1360 /* see if the name list is the same in the cross-reference file */
1361 
1362 static BOOL
1363 samelist(FILE *oldrefs, char **names, int count)
1364 {
1365 	char	oldname[PATHLEN + 1];	/* name in old cross-reference */
1366 	int	oldcount;
1367 	int	i;
1368 
1369 	/* see if the number of names is the same */
1370 	if (fscanf(oldrefs, "%d", &oldcount) != 1 ||
1371 	    oldcount != count) {
1372 		return (NO);
1373 	}
1374 	/* see if the name list is the same */
1375 	for (i = 0; i < count; ++i) {
1376 		if (fscanf(oldrefs, "%s", oldname) != 1 ||
1377 		    strnotequal(oldname, names[i])) {
1378 			return (NO);
1379 		}
1380 	}
1381 	return (YES);
1382 }
1383 
1384 /* skip the list in the cross-reference file */
1385 
1386 static void
1387 skiplist(FILE *oldrefs)
1388 {
1389 	int	i;
1390 
1391 	if (fscanf(oldrefs, "%d", &i) != 1) {
1392 		(void) fprintf(stderr,
1393 		    "cscope: cannot read list size from file %s\n", reffile);
1394 		exit(1);
1395 	}
1396 	while (--i >= 0) {
1397 		if (fscanf(oldrefs, "%*s") != 0) {
1398 			(void) fprintf(stderr,
1399 			    "cscope: cannot read list name from file %s\n",
1400 			    reffile);
1401 			exit(1);
1402 		}
1403 	}
1404 }
1405 
1406 /* copy this file's symbol data */
1407 
1408 static void
1409 copydata(void)
1410 {
1411 	char	symbol[PATLEN + 1];
1412 	char	*cp;
1413 
1414 	setmark('\t');
1415 	cp = blockp;
1416 	for (;;) {
1417 		/* copy up to the next \t */
1418 		do {	/* innermost loop optimized to only one test */
1419 			while (*cp != '\t') {
1420 				dbputc(*cp++);
1421 			}
1422 		} while (*++cp == '\0' && (cp = readblock()) != NULL);
1423 		dbputc('\t');	/* copy the tab */
1424 
1425 		/* get the next character */
1426 		if (*(cp + 1) == '\0') {
1427 			cp = readblock();
1428 		}
1429 		/* exit if at the end of this file's data */
1430 		if (cp == NULL || *cp == NEWFILE) {
1431 			break;
1432 		}
1433 		/* look for an #included file */
1434 		if (*cp == INCLUDE) {
1435 			blockp = cp;
1436 			putinclude(symbol);
1437 			putstring(symbol);
1438 			setmark('\t');
1439 			cp = blockp;
1440 		}
1441 	}
1442 	blockp = cp;
1443 }
1444 
1445 /* copy this file's symbol data and output the inverted index postings */
1446 
1447 static void
1448 copyinverted(void)
1449 {
1450 	char	*cp;
1451 	int	c;
1452 	int	type;	/* reference type (mark character) */
1453 	char	symbol[PATLEN + 1];
1454 
1455 	/* note: this code was expanded in-line for speed */
1456 	/* while (scanpast('\n') != NULL) { */
1457 	/* other macros were replaced by code using cp instead of blockp */
1458 	cp = blockp;
1459 	for (;;) {
1460 		setmark('\n');
1461 		do {	/* innermost loop optimized to only one test */
1462 			while (*cp != '\n') {
1463 				dbputc(*cp++);
1464 			}
1465 		} while (*++cp == '\0' && (cp = readblock()) != NULL);
1466 		dbputc('\n');	/* copy the newline */
1467 
1468 		/* get the next character */
1469 		if (*(cp + 1) == '\0') {
1470 			cp = readblock();
1471 		}
1472 		/* exit if at the end of this file's data */
1473 		if (cp == NULL) {
1474 			break;
1475 		}
1476 		switch (*cp) {
1477 		case '\n':
1478 			lineoffset = dboffset + 1;
1479 			continue;
1480 		case '\t':
1481 			dbputc('\t');
1482 			blockp = cp;
1483 			type = getrefchar();
1484 			switch (type) {
1485 			case NEWFILE:		/* file name */
1486 				return;
1487 			case INCLUDE:		/* #included file */
1488 				putinclude(symbol);
1489 				goto output;
1490 			}
1491 			dbputc(type);
1492 			skiprefchar();
1493 			getstring(symbol);
1494 			goto output;
1495 		}
1496 		c = *cp;
1497 		if (c & 0200) {	/* digraph char? */
1498 			c = dichar1[(c & 0177) / 8];
1499 		}
1500 		/* if this is a symbol */
1501 		if (isalpha(c) || c == '_') {
1502 			blockp = cp;
1503 			getstring(symbol);
1504 			type = ' ';
1505 		output:
1506 			putposting(symbol, type);
1507 			putstring(symbol);
1508 			if (blockp == NULL) {
1509 				return;
1510 			}
1511 			cp = blockp;
1512 		}
1513 	}
1514 	blockp = cp;
1515 }
1516 
1517 /* process the #included file in the old database */
1518 
1519 static void
1520 putinclude(char *s)
1521 {
1522 	dbputc(INCLUDE);
1523 	skiprefchar();
1524 	getstring(s);
1525 	incfile(s + 1, *s);
1526 }
1527 
1528 /* replace the old file with the new file */
1529 
1530 static void
1531 movefile(char *new, char *old)
1532 {
1533 	(void) unlink(old);
1534 	if (link(new, old) == -1) {
1535 		(void) perror("cscope");
1536 		(void) fprintf(stderr,
1537 		    "cscope: cannot link file %s to file %s\n", new, old);
1538 		myexit(1);
1539 	}
1540 	if (unlink(new) == -1) {
1541 		(void) perror("cscope");
1542 		(void) fprintf(stderr, "cscope: cannot unlink file %s\n", new);
1543 		errorsfound = YES;
1544 	}
1545 }
1546 
1547 /* enter curses mode */
1548 
1549 void
1550 entercurses(void)
1551 {
1552 	incurses = YES;
1553 	(void) nonl();		/* don't translate an output \n to \n\r */
1554 	(void) cbreak();	/* single character input */
1555 	(void) noecho();	/* don't echo input characters */
1556 	(void) clear();		/* clear the screen */
1557 	initmouse();		/* initialize any mouse interface */
1558 	drawscrollbar(topline, nextline, totallines);
1559 	atfield();
1560 }
1561 
1562 /* exit curses mode */
1563 
1564 void
1565 exitcurses(void)
1566 {
1567 	/* clear the bottom line */
1568 	(void) move(LINES - 1, 0);
1569 	(void) clrtoeol();
1570 	(void) refresh();
1571 
1572 	/* exit curses and restore the terminal modes */
1573 	(void) endwin();
1574 	incurses = NO;
1575 
1576 	/* restore the mouse */
1577 	cleanupmouse();
1578 	(void) fflush(stdout);
1579 }
1580 
1581 /* no activity timeout occurred */
1582 
1583 static void
1584 timedout(int sig)
1585 {
1586 	/* if there is a child process, don't exit until it does */
1587 	if (childpid) {
1588 		closedatabase();
1589 		noacttimeout = YES;
1590 		return;
1591 	}
1592 	exitcurses();
1593 	(void) fprintf(stderr, "cscope: no activity for %d hours--exiting\n",
1594 	    noacttime / 3600);
1595 	myexit(sig);
1596 }
1597 
1598 /* cleanup and exit */
1599 
1600 void
1601 myexit(int sig)
1602 {
1603 	/* deleted layer causes multiple signals */
1604 	(void) signal(SIGHUP, SIG_IGN);
1605 	/* remove any temporary files */
1606 	if (temp1[0] != '\0') {
1607 		(void) unlink(temp1);
1608 		(void) unlink(temp2);
1609 	}
1610 	/* restore the terminal to its original mode */
1611 	if (incurses == YES) {
1612 		exitcurses();
1613 	}
1614 
1615 	/* dump core for debugging on the quit signal */
1616 	if (sig == SIGQUIT) {
1617 		(void) abort();
1618 	}
1619 	exit(sig);
1620 }
1621