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