1 /*===========================================================================
2 Copyright (c) 1998-2000, The Santa Cruz Operation
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
8 *Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
11 *Redistributions in binary form must reproduce the above copyright notice,
12 this list of conditions and the following disclaimer in the documentation
13 and/or other materials provided with the distribution.
15 *Neither name of The Santa Cruz Operation nor the names of its contributors
16 may be used to endorse or promote products derived from this software
17 without specific prior written permission.
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
20 IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
27 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 =========================================================================*/
34 /* cscope - interactive C symbol cross-reference
41 #include "global.h" /* FIXME: get rid of this! */
46 #include "version.h" /* for FILEVERSION */
49 #if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
55 /* Exported variables: */
57 BOOL buildonly = NO; /* only build the database */
58 BOOL unconditional = NO; /* unconditionally build database */
59 BOOL fileschanged; /* assume some files changed */
61 /* variable copies of the master strings... */
62 char invname_buf[] = INVNAME;
63 char invpost_buf[] = INVPOST;
64 char reffile_buf[] = REFFILE;
65 char *invname = invname_buf; /* inverted index to the database */
66 char *invpost = invpost_buf; /* inverted index postings */
67 char *reffile = reffile_buf; /* cross-reference file path name */
69 char *newreffile; /* new cross-reference file name */
70 FILE *newrefs; /* new cross-reference */
71 FILE *postings; /* new inverted index postings */
72 int symrefs = -1; /* cross-reference file */
74 INVCONTROL invcontrol; /* inverted file control structure */
77 /* Local variables: */
78 static char *newinvname; /* new inverted index file name */
79 static char *newinvpost; /* new inverted index postings file name */
80 static long traileroffset; /* file trailer offset */
83 /* Internal prototypes: */
84 static void cannotindex(void);
85 static int compare(const void *s1, const void *s2);
86 static void copydata(void);
87 static void copyinverted(void);
88 static char *getoldfile(void);
89 static void movefile(char *new, char *old);
90 static void putheader(char *dir);
91 static void fetch_include_from_dbase(char *, size_t);
92 static void putlist(char **names, int count);
93 static BOOL samelist(FILE *oldrefs, char **names, int count);
96 /* Error handling routine if inverted index creation fails */
101 cscope: cannot create inverted index; ignoring -q option\n");
105 cscope: removed files %s and %s\n",
106 newinvname, newinvpost);
112 /* see if the name list is the same in the cross-reference file */
114 samelist(FILE *oldrefs, char **names, int count)
116 char oldname[PATHLEN + 1]; /* name in old cross-reference */
120 /* see if the number of names is the same */
121 if (fscanf(oldrefs, "%d", &oldcount) != 1 ||
125 /* see if the name list is the same */
126 for (i = 0; i < count; ++i) {
127 if ((1 != fscanf(oldrefs," %[^\n]",oldname)) ||
128 strnotequal(oldname, names[i])) {
136 /* create the file name(s) used for a new cross-referene */
138 void setup_build_filenames(char *reffile)
140 char *path; /* file pathname */
141 char *s; /* pointer to basename in path */
143 path = mymalloc(strlen(reffile) + 10);
144 strcpy(path, reffile);
145 s = mybasename(path);
149 strcpy(s, mybasename(reffile));
150 newreffile = my_strdup(path);
151 strcpy(s, mybasename(invname));
152 newinvname = my_strdup(path);
153 strcpy(s, mybasename(invpost));
154 newinvpost = my_strdup(path);
158 /* open the database */
163 if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) {
167 blocknumber = -1; /* force next seek to read the first block */
169 /* open any inverted index */
170 if (invertedindex == YES &&
171 invopen(&invcontrol, invname, invpost, INVAVAIL) == -1) {
172 askforreturn(); /* so user sees message */
178 /* rebuild the database */
183 if (invertedindex == YES) {
184 invclose(&invcontrol);
191 /* revert to the initial display */
192 if (refsfound != NULL) {
199 /* build the cross-reference */
204 FILE *oldrefs; /* old cross-reference file */
205 time_t reftime; /* old crossref modification time */
206 char *file; /* current file */
207 char *oldfile; /* file in old cross-reference */
208 char newdir[PATHLEN + 1]; /* directory in new cross-reference */
209 char olddir[PATHLEN + 1]; /* directory in old cross-reference */
210 char oldname[PATHLEN + 1]; /* name in old cross-reference */
211 unsigned long oldnum; /* number in old cross-ref */
212 struct stat statstruct; /* file status */
213 unsigned long firstfile; /* first source file in pass */
214 unsigned long lastfile; /* last source file in pass */
215 int built = 0; /* built crossref for these files */
216 int copied = 0; /* copied crossref for these files */
217 unsigned long fileindex; /* source file name index */
218 BOOL interactive = YES; /* output progress messages */
220 /* normalize the current directory relative to the home directory so
221 the cross-reference is not rebuilt when the user's login is moved */
222 strcpy(newdir, currentdir);
223 if (strcmp(currentdir, home) == 0) {
224 strcpy(newdir, "$HOME");
225 } else if (strncmp(currentdir, home, strlen(home)) == 0) {
226 snprintf(newdir, sizeof(newdir), "$HOME%s", currentdir + strlen(home));
228 /* sort the source file names (needed for rebuilding) */
229 qsort(srcfiles, nsrcfiles, sizeof(char *), compare);
231 /* if there is an old cross-reference and its current directory matches */
232 /* or this is an unconditional build */
233 if ((oldrefs = vpfopen(reffile, "rb")) != NULL
234 && unconditional == NO
235 && fscanf(oldrefs, "cscope %d %" PATHLEN_STR "s", &fileversion, olddir) == 2
236 && (strcmp(olddir, currentdir) == 0 /* remain compatible */
237 || strcmp(olddir, newdir) == 0)) {
238 /* get the cross-reference file's modification time */
239 fstat(fileno(oldrefs), &statstruct);
240 reftime = statstruct.st_mtime;
241 if (fileversion >= 8) {
242 BOOL oldcompress = YES;
243 BOOL oldinvertedindex = NO;
244 BOOL oldtruncate = NO;
247 /* see if there are options in the database */
249 while((c = getc(oldrefs)) == ' ')
255 switch (getc(oldrefs)) {
256 case 'c': /* ASCII characters only */
259 case 'q': /* quick search */
260 oldinvertedindex = YES;
261 fscanf(oldrefs, "%ld", &totalterms);
263 case 'T': /* truncate symbols to 8 characters */
268 /* check the old and new option settings */
269 if (oldcompress != compress || oldtruncate != trun_syms) {
271 cscope: -c or -T option mismatch between command line and old symbol database\n");
274 if (oldinvertedindex != invertedindex) {
276 cscope: -q option mismatch between command line and old symbol database\n");
277 if (invertedindex == NO) {
278 posterr("cscope: removed files %s and %s\n",
285 /* seek to the trailer */
286 if (fscanf(oldrefs, "%ld", &traileroffset) != 1 ||
287 fseek(oldrefs, traileroffset, SEEK_SET) == -1) {
288 posterr("cscope: incorrect symbol database file format\n");
292 /* if assuming that some files have changed */
293 if (fileschanged == YES) {
296 /* see if the directory lists are the same */
297 if (samelist(oldrefs, srcdirs, nsrcdirs) == NO
298 || samelist(oldrefs, incdirs, nincdirs) == NO
299 /* get the old number of files */
300 || fscanf(oldrefs, "%lu", &oldnum) != 1
301 /* skip the string space size */
302 || (fileversion >= 9 && fscanf(oldrefs, "%*s") != 0)) {
305 /* see if the list of source files is the same and
306 none have been changed up to the included files */
307 for (i = 0; i < nsrcfiles; ++i) {
308 if ((1 != fscanf(oldrefs," %[^\n]",oldname))
309 || strnotequal(oldname, srcfiles[i])
310 || (lstat(srcfiles[i], &statstruct) != 0)
311 || (statstruct.st_mtime > reftime)
316 /* the old cross-reference is up-to-date */
317 /* so get the list of included files */
318 while (i++ < oldnum && fgets(oldname, sizeof(oldname), oldrefs)) {
325 /* if the database format has changed, rebuild it all */
326 if (fileversion != FILEVERSION) {
328 cscope: converting to new symbol database file format\n");
331 /* reopen the old cross-reference file for fast scanning */
332 if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) {
333 postfatal("cscope: cannot open file %s\n", reffile);
336 /* get the first file name in the old cross-reference */
338 read_block(); /* read the first cross-ref block */
339 scanpast('\t'); /* skip the header */
340 oldfile = getoldfile();
341 } else { /* force cross-referencing of all the source files */
346 /* open the new cross-reference file */
347 if ((newrefs = myfopen(newreffile, "wb")) == NULL) {
348 postfatal("cscope: cannot open file %s\n", reffile);
351 if (invertedindex == YES && (postings = myfopen(temp1, "wb")) == NULL) {
356 fileversion = FILEVERSION;
357 if (buildonly == YES && verbosemode != YES && !isatty(0)) {
362 /* output the leading tab expected by crossref() */
365 /* make passes through the source file list until the last level of
366 included files is processed */
368 lastfile = nsrcfiles;
369 if (invertedindex == YES) {
370 srcoffset = mymalloc((nsrcfiles + 1) * sizeof(long));
373 progress("Building symbol database", (long)built,
378 /* get the next source file name */
379 for (fileindex = firstfile; fileindex < lastfile; ++fileindex) {
381 /* display the progress about every three seconds */
382 if (interactive == YES && fileindex % 10 == 0) {
383 progress("Building symbol database", fileindex, lastfile);
385 /* if the old file has been deleted get the next one */
386 file = srcfiles[fileindex];
387 while (oldfile != NULL && strcmp(file, oldfile) > 0) {
388 oldfile = getoldfile();
390 /* if there isn't an old database or this is a new file */
391 if (oldfile == NULL || strcmp(file, oldfile) < 0) {
394 } else if (lstat(file, &statstruct) == 0
395 && statstruct.st_mtime > reftime) {
396 /* if this file was modified */
400 /* skip its old crossref so modifying the last source
401 * file does not cause all included files to be built.
402 * Unfortunately a new file that is alphabetically
403 * last will cause all included files to be build, but
404 * this is less likely */
405 oldfile = getoldfile();
407 /* copy its cross-reference */
409 if (invertedindex == YES) {
415 oldfile = getoldfile();
418 /* see if any included files were found */
419 if (lastfile == nsrcfiles) {
422 firstfile = lastfile;
423 lastfile = nsrcfiles;
424 if (invertedindex == YES) {
425 srcoffset = myrealloc(srcoffset,
426 (nsrcfiles + 1) * sizeof(long));
428 /* sort the included file names */
429 qsort(&srcfiles[firstfile], (lastfile - firstfile),
430 sizeof(char *), compare);
432 /* add a null file name to the trailing tab */
436 /* get the file trailer offset */
437 traileroffset = dboffset;
439 /* output the source and include directory and file lists */
440 putlist(srcdirs, nsrcdirs);
441 putlist(incdirs, nincdirs);
442 putlist(srcfiles, nsrcfiles);
443 if (fflush(newrefs) == EOF) {
444 /* rewind doesn't check for write failure */
445 cannotwrite(newreffile);
449 /* create the inverted index if requested */
450 if (invertedindex == YES) {
451 char sortcommand[PATHLEN + 1];
453 if (fflush(postings) == EOF) {
457 fstat(fileno(postings), &statstruct);
459 snprintf(sortcommand, sizeof(sortcommand), "env LC_ALL=C sort -T %s %s", tmpdir, temp1);
460 if ((postings = mypopen(sortcommand, "r")) == NULL) {
461 fprintf(stderr, "cscope: cannot open pipe to sort command\n");
464 if ((totalterms = invmake(newinvname, newinvpost, postings)) > 0) {
465 movefile(newinvname, invname);
466 movefile(newinvpost, invpost);
475 /* rewrite the header with the trailer offset and final option list */
480 /* close the old database file */
484 if (oldrefs != NULL) {
487 /* replace it with the new database file */
488 movefile(newreffile, reffile);
492 /* string comparison function for qsort */
494 compare(const void *arg_s1, const void *arg_s2)
496 const char **s1 = (const char **) arg_s1;
497 const char **s2 = (const char **) arg_s2;
499 return(strcmp(*s1, *s2));
503 /* seek to the trailer, in a given file */
505 seek_to_trailer(FILE *f)
507 if (fscanf(f, "%ld", &traileroffset) != 1) {
508 postfatal("cscope: cannot read trailer offset from file %s\n", reffile);
511 if (fseek(f, traileroffset, SEEK_SET) == -1) {
512 postfatal("cscope: cannot seek to trailer in file %s\n", reffile);
518 /* get the next file name in the old cross-reference */
522 static char file[PATHLEN + 1]; /* file name in old crossref */
524 if (blockp != NULL) {
526 if (*blockp == NEWFILE) {
528 fetch_string_from_dbase(file, sizeof(file));
529 if (file[0] != '\0') { /* if not end-of-crossref */
534 } while (scanpast('\t') != NULL);
540 /* Free all storage allocated for filenames: */
541 void free_newbuildfiles(void)
549 /* output the cscope version, current directory, database format options, and
550 the database trailer offset */
554 dboffset = fprintf(newrefs, "cscope %d %s", FILEVERSION, dir);
555 if (compress == NO) {
556 dboffset += fprintf(newrefs, " -c");
558 if (invertedindex == YES) {
559 dboffset += fprintf(newrefs, " -q %.10ld", totalterms);
561 /* leave space so if the header is overwritten without -q
562 * because writing the inverted index failed, the header
563 * is the same length */
564 dboffset += fprintf(newrefs, " ");
566 if (trun_syms == YES) {
567 dboffset += fprintf(newrefs, " -T");
570 dboffset += fprintf(newrefs, " %.10ld\n", traileroffset);
571 #ifdef PRINTF_RETVAL_BROKEN
572 dboffset = ftell(newrefs);
577 /* put the name list into the cross-reference file */
579 putlist(char **names, int count)
583 fprintf(newrefs, "%d\n", count);
584 if (names == srcfiles) {
586 /* calculate the string space needed */
587 for (i = 0; i < count; ++i) {
588 size += strlen(names[i]) + 1;
590 fprintf(newrefs, "%d\n", size);
592 for (i = 0; i < count; ++i) {
593 if (fputs(names[i], newrefs) == EOF ||
594 putc('\n', newrefs) == EOF) {
595 cannotwrite(newreffile);
602 /* copy this file's symbol data */
606 char symbol[PATLEN + 1];
612 /* copy up to the next \t */
613 do { /* innermost loop optimized to only one test */
614 while (*cp != '\t') {
617 } while (*++cp == '\0' && (cp = read_block()) != NULL);
618 dbputc('\t'); /* copy the tab */
620 /* get the next character */
621 /* HBB 2010-08-21: potential problem if above loop was left
623 if (cp && (*(cp + 1) == '\0')) {
626 /* exit if at the end of this file's data */
627 if (cp == NULL || *cp == NEWFILE) {
630 /* look for an #included file */
631 if (*cp == INCLUDE) {
633 fetch_include_from_dbase(symbol, sizeof(symbol));
642 /* copy this file's symbol data and output the inverted index postings */
649 int type; /* reference type (mark character) */
650 char symbol[PATLEN + 1];
652 /* note: this code was expanded in-line for speed */
653 /* while (scanpast('\n') != NULL) { */
654 /* other macros were replaced by code using cp instead of blockp */
658 do { /* innermost loop optimized to only one test */
659 while (*cp != '\n') {
662 } while (*++cp == '\0' && (cp = read_block()) != NULL);
663 dbputc('\n'); /* copy the newline */
665 /* get the next character */
666 /* HBB 2010-08-21: potential problem if above loop was left
668 if (cp && (*(cp + 1) == '\0')) {
671 /* exit if at the end of this file's data */
677 lineoffset = dboffset + 1;
684 case NEWFILE: /* file name */
686 case INCLUDE: /* #included file */
687 fetch_include_from_dbase(symbol, sizeof(symbol));
692 fetch_string_from_dbase(symbol, sizeof(symbol));
696 if (c & 0200) { /* digraph char? */
697 c = dichar1[(c & 0177) / 8];
699 /* if this is a symbol */
700 if (isalpha((unsigned char)c) || c == '_') {
702 fetch_string_from_dbase(symbol, sizeof(symbol));
705 putposting(symbol, type);
707 if (blockp == NULL) {
717 /* replace the old file with the new file */
719 movefile(char *new, char *old)
722 if (rename(new, old) == -1) {
724 postfatal("cscope: cannot rename file %s to file %s\n",
731 /* process the #included file in the old database */
733 fetch_include_from_dbase(char *s, size_t length)
737 fetch_string_from_dbase(s, length);