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