summaryrefslogtreecommitdiff
path: root/src/build.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/build.c')
-rw-r--r--src/build.c734
1 files changed, 734 insertions, 0 deletions
diff --git a/src/build.c b/src/build.c
new file mode 100644
index 0000000..ada2ea1
--- /dev/null
+++ b/src/build.c
@@ -0,0 +1,734 @@
+/*===========================================================================
+ Copyright (c) 1998-2000, The Santa Cruz Operation
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ *Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ *Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+ *Neither name of The Santa Cruz Operation nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS
+ IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION)
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ DAMAGE.
+ =========================================================================*/
+
+
+/* cscope - interactive C symbol cross-reference
+ *
+ * main functions
+ */
+
+#include "build.h"
+
+#include "global.h" /* FIXME: get rid of this! */
+
+#include "library.h"
+#include "alloc.h"
+#include "scanner.h"
+#include "version.h" /* for FILEVERSION */
+#include "vp.h"
+
+#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
+#include <ncurses.h>
+#else
+#include <curses.h>
+#endif
+
+/* Exported variables: */
+
+BOOL buildonly = NO; /* only build the database */
+BOOL unconditional = NO; /* unconditionally build database */
+BOOL fileschanged; /* assume some files changed */
+
+/* variable copies of the master strings... */
+char invname_buf[] = INVNAME;
+char invpost_buf[] = INVPOST;
+char reffile_buf[] = REFFILE;
+char *invname = invname_buf; /* inverted index to the database */
+char *invpost = invpost_buf; /* inverted index postings */
+char *reffile = reffile_buf; /* cross-reference file path name */
+
+char *newreffile; /* new cross-reference file name */
+FILE *newrefs; /* new cross-reference */
+FILE *postings; /* new inverted index postings */
+int symrefs = -1; /* cross-reference file */
+
+INVCONTROL invcontrol; /* inverted file control structure */
+
+
+/* Local variables: */
+static char *newinvname; /* new inverted index file name */
+static char *newinvpost; /* new inverted index postings file name */
+static long traileroffset; /* file trailer offset */
+
+
+/* Internal prototypes: */
+static void cannotindex(void);
+static int compare(const void *s1, const void *s2);
+static void copydata(void);
+static void copyinverted(void);
+static char *getoldfile(void);
+static void movefile(char *new, char *old);
+static void putheader(char *dir);
+static void fetch_include_from_dbase(char *, size_t);
+static void putlist(char **names, int count);
+static BOOL samelist(FILE *oldrefs, char **names, int count);
+
+
+/* Error handling routine if inverted index creation fails */
+static void
+cannotindex(void)
+{
+ fprintf(stderr, "\
+cscope: cannot create inverted index; ignoring -q option\n");
+ invertedindex = NO;
+ errorsfound = YES;
+ fprintf(stderr, "\
+cscope: removed files %s and %s\n",
+ newinvname, newinvpost);
+ unlink(newinvname);
+ unlink(newinvpost);
+}
+
+
+/* see if the name list is the same in the cross-reference file */
+static BOOL
+samelist(FILE *oldrefs, char **names, int count)
+{
+ char oldname[PATHLEN + 1]; /* name in old cross-reference */
+ int oldcount;
+ int i;
+
+ /* see if the number of names is the same */
+ if (fscanf(oldrefs, "%d", &oldcount) != 1 ||
+ oldcount != count) {
+ return(NO);
+ }
+ /* see if the name list is the same */
+ for (i = 0; i < count; ++i) {
+ if (! fgets(oldname, sizeof(oldname), oldrefs)||
+ strnotequal(oldname, names[i])) {
+ return(NO);
+ }
+ }
+ return(YES);
+}
+
+
+/* create the file name(s) used for a new cross-referene */
+
+void setup_build_filenames(char *reffile)
+{
+ char *path; /* file pathname */
+ char *s; /* pointer to basename in path */
+
+ path = mymalloc(strlen(reffile) + 10);
+ strcpy(path, reffile);
+ s = mybasename(path);
+ *s = '\0';
+ strcat(path, "n");
+ ++s;
+ strcpy(s, mybasename(reffile));
+ newreffile = my_strdup(path);
+ strcpy(s, mybasename(invname));
+ newinvname = my_strdup(path);
+ strcpy(s, mybasename(invpost));
+ newinvpost = my_strdup(path);
+ free(path);
+}
+
+/* open the database */
+
+void
+opendatabase(void)
+{
+ if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) {
+ cannotopen(reffile);
+ myexit(1);
+ }
+ blocknumber = -1; /* force next seek to read the first block */
+
+ /* open any inverted index */
+ if (invertedindex == YES &&
+ invopen(&invcontrol, invname, invpost, INVAVAIL) == -1) {
+ askforreturn(); /* so user sees message */
+ invertedindex = NO;
+ }
+}
+
+
+/* rebuild the database */
+void
+rebuild(void)
+{
+ close(symrefs);
+ if (invertedindex == YES) {
+ invclose(&invcontrol);
+ nsrcoffset = 0;
+ npostings = 0;
+ }
+ build();
+ opendatabase();
+
+ /* revert to the initial display */
+ if (refsfound != NULL) {
+ fclose(refsfound);
+ refsfound = NULL;
+ }
+}
+
+
+/* build the cross-reference */
+void
+build(void)
+{
+ unsigned long i;
+ FILE *oldrefs; /* old cross-reference file */
+ time_t reftime; /* old crossref modification time */
+ char *file; /* current file */
+ char *oldfile; /* file in old cross-reference */
+ char newdir[PATHLEN + 1]; /* directory in new cross-reference */
+ char olddir[PATHLEN + 1]; /* directory in old cross-reference */
+ char oldname[PATHLEN + 1]; /* name in old cross-reference */
+ unsigned long oldnum; /* number in old cross-ref */
+ struct stat statstruct; /* file status */
+ unsigned long firstfile; /* first source file in pass */
+ unsigned long lastfile; /* last source file in pass */
+ int built = 0; /* built crossref for these files */
+ int copied = 0; /* copied crossref for these files */
+ unsigned long fileindex; /* source file name index */
+ BOOL interactive = YES; /* output progress messages */
+
+ /* normalize the current directory relative to the home directory so
+ the cross-reference is not rebuilt when the user's login is moved */
+ strcpy(newdir, currentdir);
+ if (strcmp(currentdir, home) == 0) {
+ strcpy(newdir, "$HOME");
+ } else if (strncmp(currentdir, home, strlen(home)) == 0) {
+ sprintf(newdir, "$HOME%s", currentdir + strlen(home));
+ }
+ /* sort the source file names (needed for rebuilding) */
+ qsort(srcfiles, nsrcfiles, sizeof(char *), compare);
+
+ /* if there is an old cross-reference and its current directory matches */
+ /* or this is an unconditional build */
+ if ((oldrefs = vpfopen(reffile, "rb")) != NULL
+ && unconditional == NO
+ && fscanf(oldrefs, "cscope %d %" PATHLEN_STR "s", &fileversion, olddir) == 2
+ && (strcmp(olddir, currentdir) == 0 /* remain compatible */
+ || strcmp(olddir, newdir) == 0)) {
+ /* get the cross-reference file's modification time */
+ fstat(fileno(oldrefs), &statstruct);
+ reftime = statstruct.st_mtime;
+ if (fileversion >= 8) {
+ BOOL oldcompress = YES;
+ BOOL oldinvertedindex = NO;
+ BOOL oldtruncate = NO;
+ int c;
+
+ /* see if there are options in the database */
+ for (;;) {
+ while((c = getc(oldrefs)) == ' ')
+ ; /* do nothing */
+ if (c != '-') {
+ ungetc(c, oldrefs);
+ break;
+ }
+ switch (c = getc(oldrefs)) {
+ case 'c': /* ASCII characters only */
+ oldcompress = NO;
+ break;
+ case 'q': /* quick search */
+ oldinvertedindex = YES;
+ fscanf(oldrefs, "%ld", &totalterms);
+ break;
+ case 'T': /* truncate symbols to 8 characters */
+ oldtruncate = YES;
+ break;
+ }
+ }
+ /* check the old and new option settings */
+ if (oldcompress != compress || oldtruncate != trun_syms) {
+ posterr("\
+cscope: -c or -T option mismatch between command line and old symbol database\n");
+ goto force;
+ }
+ if (oldinvertedindex != invertedindex) {
+ posterr("\
+cscope: -q option mismatch between command line and old symbol database\n");
+ if (invertedindex == NO) {
+ posterr("cscope: removed files %s and %s\n",
+ invname, invpost);
+ unlink(invname);
+ unlink(invpost);
+ }
+ goto outofdate;
+ }
+ /* seek to the trailer */
+ if (fscanf(oldrefs, "%ld", &traileroffset) != 1 ||
+ fseek(oldrefs, traileroffset, SEEK_SET) == -1) {
+ posterr("cscope: incorrect symbol database file format\n");
+ goto force;
+ }
+ }
+ /* if assuming that some files have changed */
+ if (fileschanged == YES) {
+ goto outofdate;
+ }
+ /* see if the directory lists are the same */
+ if (samelist(oldrefs, srcdirs, nsrcdirs) == NO
+ || samelist(oldrefs, incdirs, nincdirs) == NO
+ /* get the old number of files */
+ || fscanf(oldrefs, "%lu", &oldnum) != 1
+ /* skip the string space size */
+ || (fileversion >= 9 && fscanf(oldrefs, "%*s") != 0)) {
+ goto outofdate;
+ }
+ /* see if the list of source files is the same and
+ none have been changed up to the included files */
+ for (i = 0; i < nsrcfiles; ++i) {
+ if (! fgets(oldname, sizeof(oldname), oldrefs) ||
+ strnotequal(oldname, srcfiles[i]) ||
+ lstat(srcfiles[i], &statstruct) != 0 ||
+ statstruct.st_mtime > reftime) {
+ goto outofdate;
+ }
+ }
+ /* the old cross-reference is up-to-date */
+ /* so get the list of included files */
+ while (i++ < oldnum && fgets(oldname, sizeof(oldname), oldrefs)) {
+ addsrcfile(oldname);
+ }
+ fclose(oldrefs);
+ return;
+
+ outofdate:
+ /* if the database format has changed, rebuild it all */
+ if (fileversion != FILEVERSION) {
+ fprintf(stderr, "\
+cscope: converting to new symbol database file format\n");
+ goto force;
+ }
+ /* reopen the old cross-reference file for fast scanning */
+ if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) {
+ postfatal("cscope: cannot open file %s\n", reffile);
+ /* NOTREACHED */
+ }
+ /* get the first file name in the old cross-reference */
+ blocknumber = -1;
+ read_block(); /* read the first cross-ref block */
+ scanpast('\t'); /* skip the header */
+ oldfile = getoldfile();
+ } else { /* force cross-referencing of all the source files */
+ force: reftime = 0;
+ oldfile = NULL;
+ }
+ /* open the new cross-reference file */
+ if ((newrefs = myfopen(newreffile, "wb")) == NULL) {
+ postfatal("cscope: cannot open file %s\n", reffile);
+ /* NOTREACHED */
+ }
+ if (invertedindex == YES && (postings = myfopen(temp1, "wb")) == NULL) {
+ cannotwrite(temp1);
+ cannotindex();
+ }
+ putheader(newdir);
+ fileversion = FILEVERSION;
+ if (buildonly == YES && verbosemode != YES && !isatty(0)) {
+ interactive = NO;
+ } else {
+ searchcount = 0;
+ }
+ /* output the leading tab expected by crossref() */
+ dbputc('\t');
+
+ /* make passes through the source file list until the last level of
+ included files is processed */
+ firstfile = 0;
+ lastfile = nsrcfiles;
+ if (invertedindex == YES) {
+ srcoffset = mymalloc((nsrcfiles + 1) * sizeof(long));
+ }
+ for (;;) {
+ progress("Building symbol database", (long)built,
+ (long)lastfile);
+ if (linemode == NO)
+ refresh();
+
+ /* get the next source file name */
+ for (fileindex = firstfile; fileindex < lastfile; ++fileindex) {
+
+ /* display the progress about every three seconds */
+ if (interactive == YES && fileindex % 10 == 0) {
+ progress("Building symbol database", fileindex, lastfile);
+ }
+ /* if the old file has been deleted get the next one */
+ file = srcfiles[fileindex];
+ while (oldfile != NULL && strcmp(file, oldfile) > 0) {
+ oldfile = getoldfile();
+ }
+ /* if there isn't an old database or this is a new file */
+ if (oldfile == NULL || strcmp(file, oldfile) < 0) {
+ crossref(file);
+ ++built;
+ } else if (lstat(file, &statstruct) == 0
+ && statstruct.st_mtime > reftime) {
+ /* if this file was modified */
+ crossref(file);
+ ++built;
+
+ /* skip its old crossref so modifying the last source
+ * file does not cause all included files to be built.
+ * Unfortunately a new file that is alphabetically
+ * last will cause all included files to be build, but
+ * this is less likely */
+ oldfile = getoldfile();
+ } else {
+ /* copy its cross-reference */
+ putfilename(file);
+ if (invertedindex == YES) {
+ copyinverted();
+ } else {
+ copydata();
+ }
+ ++copied;
+ oldfile = getoldfile();
+ }
+ }
+ /* see if any included files were found */
+ if (lastfile == nsrcfiles) {
+ break;
+ }
+ firstfile = lastfile;
+ lastfile = nsrcfiles;
+ if (invertedindex == YES) {
+ srcoffset = myrealloc(srcoffset,
+ (nsrcfiles + 1) * sizeof(long));
+ }
+ /* sort the included file names */
+ qsort(&srcfiles[firstfile], (lastfile - firstfile),
+ sizeof(char *), compare);
+ }
+ /* add a null file name to the trailing tab */
+ putfilename("");
+ dbputc('\n');
+
+ /* get the file trailer offset */
+ traileroffset = dboffset;
+
+ /* output the source and include directory and file lists */
+ putlist(srcdirs, nsrcdirs);
+ putlist(incdirs, nincdirs);
+ putlist(srcfiles, nsrcfiles);
+ if (fflush(newrefs) == EOF) {
+ /* rewind doesn't check for write failure */
+ cannotwrite(newreffile);
+ /* NOTREACHED */
+ }
+
+ /* create the inverted index if requested */
+ if (invertedindex == YES) {
+ char sortcommand[PATHLEN + 1];
+
+ if (fflush(postings) == EOF) {
+ cannotwrite(temp1);
+ /* NOTREACHED */
+ }
+ fstat(fileno(postings), &statstruct);
+ fclose(postings);
+ sprintf(sortcommand, "env LC_ALL=C sort -T %s %s", tmpdir, temp1);
+ if ((postings = mypopen(sortcommand, "r")) == NULL) {
+ fprintf(stderr, "cscope: cannot open pipe to sort command\n");
+ cannotindex();
+ } else {
+ if ((totalterms = invmake(newinvname, newinvpost, postings)) > 0) {
+ movefile(newinvname, invname);
+ movefile(newinvpost, invpost);
+ } else {
+ cannotindex();
+ }
+ mypclose(postings);
+ }
+ unlink(temp1);
+ free(srcoffset);
+ }
+ /* rewrite the header with the trailer offset and final option list */
+ rewind(newrefs);
+ putheader(newdir);
+ fclose(newrefs);
+
+ /* close the old database file */
+ if (symrefs >= 0) {
+ close(symrefs);
+ }
+ if (oldrefs != NULL) {
+ fclose(oldrefs);
+ }
+ /* replace it with the new database file */
+ movefile(newreffile, reffile);
+}
+
+
+/* string comparison function for qsort */
+static int
+compare(const void *arg_s1, const void *arg_s2)
+{
+ const char **s1 = (const char **) arg_s1;
+ const char **s2 = (const char **) arg_s2;
+
+ return(strcmp(*s1, *s2));
+}
+
+
+/* seek to the trailer, in a given file */
+void
+seek_to_trailer(FILE *f)
+{
+ if (fscanf(f, "%ld", &traileroffset) != 1) {
+ postfatal("cscope: cannot read trailer offset from file %s\n", reffile);
+ /* NOTREACHED */
+ }
+ if (fseek(f, traileroffset, SEEK_SET) == -1) {
+ postfatal("cscope: cannot seek to trailer in file %s\n", reffile);
+ /* NOTREACHED */
+ }
+}
+
+
+/* get the next file name in the old cross-reference */
+static char *
+getoldfile(void)
+{
+ static char file[PATHLEN + 1]; /* file name in old crossref */
+
+ if (blockp != NULL) {
+ do {
+ if (*blockp == NEWFILE) {
+ skiprefchar();
+ fetch_string_from_dbase(file, sizeof(file));
+ if (file[0] != '\0') { /* if not end-of-crossref */
+ return(file);
+ }
+ return(NULL);
+ }
+ } while (scanpast('\t') != NULL);
+ }
+ return(NULL);
+}
+
+
+/* Free all storage allocated for filenames: */
+void free_newbuildfiles(void)
+{
+ free(newinvname);
+ free(newinvpost);
+ free(newreffile);
+}
+
+
+/* output the cscope version, current directory, database format options, and
+ the database trailer offset */
+static void
+putheader(char *dir)
+{
+ dboffset = fprintf(newrefs, "cscope %d %s", FILEVERSION, dir);
+ if (compress == NO) {
+ dboffset += fprintf(newrefs, " -c");
+ }
+ if (invertedindex == YES) {
+ dboffset += fprintf(newrefs, " -q %.10ld", totalterms);
+ } else {
+ /* leave space so if the header is overwritten without -q
+ * because writing the inverted index failed, the header
+ * is the same length */
+ dboffset += fprintf(newrefs, " ");
+ }
+ if (trun_syms == YES) {
+ dboffset += fprintf(newrefs, " -T");
+ }
+
+ dboffset += fprintf(newrefs, " %.10ld\n", traileroffset);
+#ifdef PRINTF_RETVAL_BROKEN
+ dboffset = ftell(newrefs);
+#endif
+}
+
+
+/* put the name list into the cross-reference file */
+static void
+putlist(char **names, int count)
+{
+ int i, size = 0;
+
+ fprintf(newrefs, "%d\n", count);
+ if (names == srcfiles) {
+
+ /* calculate the string space needed */
+ for (i = 0; i < count; ++i) {
+ size += strlen(names[i]) + 1;
+ }
+ fprintf(newrefs, "%d\n", size);
+ }
+ for (i = 0; i < count; ++i) {
+ if (fputs(names[i], newrefs) == EOF ||
+ putc('\n', newrefs) == EOF) {
+ cannotwrite(newreffile);
+ /* NOTREACHED */
+ }
+ }
+}
+
+
+/* copy this file's symbol data */
+static void
+copydata(void)
+{
+ char symbol[PATLEN + 1];
+ char *cp;
+
+ setmark('\t');
+ cp = blockp;
+ for (;;) {
+ /* copy up to the next \t */
+ do { /* innermost loop optimized to only one test */
+ while (*cp != '\t') {
+ dbputc(*cp++);
+ }
+ } while (*++cp == '\0' && (cp = read_block()) != NULL);
+ dbputc('\t'); /* copy the tab */
+
+ /* get the next character */
+ if (*(cp + 1) == '\0') {
+ cp = read_block();
+ }
+ /* exit if at the end of this file's data */
+ if (cp == NULL || *cp == NEWFILE) {
+ break;
+ }
+ /* look for an #included file */
+ if (*cp == INCLUDE) {
+ blockp = cp;
+ fetch_include_from_dbase(symbol, sizeof(symbol));
+ writestring(symbol);
+ setmark('\t');
+ cp = blockp;
+ }
+ }
+ blockp = cp;
+}
+
+/* copy this file's symbol data and output the inverted index postings */
+
+static void
+copyinverted(void)
+{
+ char *cp;
+ char c;
+ int type; /* reference type (mark character) */
+ char symbol[PATLEN + 1];
+
+ /* note: this code was expanded in-line for speed */
+ /* while (scanpast('\n') != NULL) { */
+ /* other macros were replaced by code using cp instead of blockp */
+ cp = blockp;
+ for (;;) {
+ setmark('\n');
+ do { /* innermost loop optimized to only one test */
+ while (*cp != '\n') {
+ dbputc(*cp++);
+ }
+ } while (*++cp == '\0' && (cp = read_block()) != NULL);
+ dbputc('\n'); /* copy the newline */
+
+ /* get the next character */
+ if (*(cp + 1) == '\0') {
+ cp = read_block();
+ }
+ /* exit if at the end of this file's data */
+ if (cp == NULL) {
+ break;
+ }
+ switch (*cp) {
+ case '\n':
+ lineoffset = dboffset + 1;
+ continue;
+ case '\t':
+ dbputc('\t');
+ blockp = cp;
+ type = getrefchar();
+ switch (type) {
+ case NEWFILE: /* file name */
+ return;
+ case INCLUDE: /* #included file */
+ fetch_include_from_dbase(symbol, sizeof(symbol));
+ goto output;
+ }
+ dbputc(type);
+ skiprefchar();
+ fetch_string_from_dbase(symbol, sizeof(symbol));
+ goto output;
+ }
+ c = *cp;
+ if (c & 0200) { /* digraph char? */
+ c = dichar1[(c & 0177) / 8];
+ }
+ /* if this is a symbol */
+ if (isalpha((unsigned char)c) || c == '_') {
+ blockp = cp;
+ fetch_string_from_dbase(symbol, sizeof(symbol));
+ type = ' ';
+ output:
+ putposting(symbol, type);
+ writestring(symbol);
+ if (blockp == NULL) {
+ return;
+ }
+ cp = blockp;
+ }
+ }
+ blockp = cp;
+}
+
+
+/* replace the old file with the new file */
+static void
+movefile(char *new, char *old)
+{
+ unlink(old);
+ if (rename(new, old) == -1) {
+ myperror("cscope");
+ postfatal("cscope: cannot rename file %s to file %s\n",
+ new, old);
+ /* NOTREACHED */
+ }
+}
+
+
+/* process the #included file in the old database */
+static void
+fetch_include_from_dbase(char *s, size_t length)
+{
+ dbputc(INCLUDE);
+ skiprefchar();
+ fetch_string_from_dbase(s, length);
+ incfile(s + 1, s);
+}
+