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