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