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
main(int argc,char ** argv)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
options(int argc,char ** argv)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
printusage(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
removeindex(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
cannotindex(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
cannotopen(char * file)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
cannotwrite(char * file)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
initcompress(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
opendatabase(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
closedatabase(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
rebuild(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
build(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
compare(const void * s1,const void * s2)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 *
getoldfile(void)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
putheader(char * dir)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
putlist(char ** names,int count)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
samelist(FILE * oldrefs,char ** names,int count)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
skiplist(FILE * oldrefs)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
copydata(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
copyinverted(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
putinclude(char * s)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
movefile(char * new,char * old)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
entercurses(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
exitcurses(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
timedout(int sig)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
myexit(int sig)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