#include "build.h"
#include "global.h"
#include "library.h"
#include "scanner.h"
#include "version.h"
#include "vp.h"
#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
#include <ncurses.h>
#else
#include <curses.h>
#endif
BOOL buildonly = NO;
BOOL unconditional = NO;
BOOL fileschanged;
char *invname = INVNAME;
char *invpost = INVPOST;
char *reffile = REFFILE;
char *newreffile;
FILE *newrefs;
FILE *postings;
int symrefs = -1;
INVCONTROL invcontrol;
static char *newinvname;
static char *newinvpost;
static long traileroffset;
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 putinclude(char *s);
static void putlist(char **names, int count);
static BOOL samelist(FILE *oldrefs, char **names, int count);
static void
cannotindex(void)
{
(void) fprintf(stderr, "cscope: cannot create inverted index; ignoring -q option\n");
invertedindex = NO;
errorsfound = YES;
(void) fprintf(stderr, "cscope: removed files %s and %s\n", newinvname, newinvpost);
(void) unlink(newinvname);
(void) unlink(newinvpost);
}
static BOOL
samelist(FILE *oldrefs, char **names, int count)
{
char oldname[PATHLEN + 1];
int oldcount;
int i;
if (fscanf(oldrefs, "%d", &oldcount) != 1 ||
oldcount != count) {
return(NO);
}
for (i = 0; i < count; ++i) {
if (fscanf(oldrefs, "%s", oldname) != 1 ||
strnotequal(oldname, names[i])) {
return(NO);
}
}
return(YES);
}
void setup_build_filenames(char *reffile)
{
char *path;
char *s;
path = mymalloc(strlen(reffile) + 10);
(void) strcpy(path, reffile);
s = mybasename(path);
*s = '\0';
(void) strcat(path, "n");
++s;
(void) strcpy(s, mybasename(reffile));
newreffile = stralloc(path);
(void) strcpy(s, mybasename(invname));
newinvname = stralloc(path);
(void) strcpy(s, mybasename(invpost));
newinvpost = stralloc(path);
free(path);
}
void
opendatabase(void)
{
if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) {
cannotopen(reffile);
myexit(1);
}
blocknumber = -1;
if (invertedindex == YES &&
invopen(&invcontrol, invname, invpost, INVAVAIL) == -1) {
askforreturn();
invertedindex = NO;
}
}
void
rebuild(void)
{
(void) close(symrefs);
if (invertedindex == YES) {
invclose(&invcontrol);
nsrcoffset = 0;
npostings = 0;
}
build();
opendatabase();
if (refsfound != NULL) {
(void) fclose(refsfound);
refsfound = NULL;
}
}
void
build(void)
{
int i;
FILE *oldrefs;
time_t reftime;
char *file;
char *oldfile;
char newdir[PATHLEN + 1];
char olddir[PATHLEN + 1];
char oldname[PATHLEN + 1];
int oldnum;
struct stat statstruct;
int firstfile;
int lastfile;
int built = 0;
int copied = 0;
long fileindex;
BOOL interactive = YES;
(void) strcpy(newdir, currentdir);
if (strcmp(currentdir, home) == 0) {
(void) strcpy(newdir, "$HOME");
}
else if (strncmp(currentdir, home, strlen(home)) == 0) {
(void) sprintf(newdir, "$HOME%s", currentdir + strlen(home));
}
qsort(srcfiles, (unsigned) nsrcfiles, sizeof(char *), compare);
if ((oldrefs = vpfopen(reffile, "rb")) != NULL && unconditional == NO &&
fscanf(oldrefs, "cscope %d %s", &fileversion, olddir) == 2 &&
(strcmp(olddir, currentdir) == 0 ||
strcmp(olddir, newdir) == 0)) {
(void) fstat(fileno(oldrefs), &statstruct);
reftime = statstruct.st_mtime;
if (fileversion >= 8) {
BOOL oldcompress = YES;
BOOL oldinvertedindex = NO;
BOOL oldtruncate = NO;
int c;
for (;;) {
while((c = getc(oldrefs)) == ' ') {
;
}
if (c != '-') {
(void) ungetc(c, oldrefs);
break;
}
switch (c = getc(oldrefs)) {
case 'c':
oldcompress = NO;
break;
case 'q':
oldinvertedindex = YES;
(void) fscanf(oldrefs, "%ld", &totalterms);
break;
case 'T':
oldtruncate = YES;
break;
}
}
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) {
(void) 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);
(void) unlink(invname);
(void) unlink(invpost);
}
goto outofdate;
}
if (fscanf(oldrefs, "%ld", &traileroffset) != 1 ||
fseek(oldrefs, traileroffset, SEEK_SET) == -1) {
posterr("cscope: incorrect symbol database file format\n");
goto force;
}
}
if (fileschanged == YES) {
goto outofdate;
}
if (samelist(oldrefs, srcdirs, nsrcdirs) == NO ||
samelist(oldrefs, incdirs, nincdirs) == NO ||
fscanf(oldrefs, "%d", &oldnum) != 1 ||
(fileversion >= 9 && fscanf(oldrefs, "%*s") != 0)) {
goto outofdate;
}
for (i = 0; i < nsrcfiles; ++i) {
if (fscanf(oldrefs, "%s", oldname) != 1 ||
strnotequal(oldname, srcfiles[i]) ||
lstat(srcfiles[i], &statstruct) != 0 ||
statstruct.st_mtime > reftime) {
goto outofdate;
}
}
while (i++ < oldnum && fscanf(oldrefs, "%s", oldname) == 1) {
addsrcfile(oldname);
}
(void) fclose(oldrefs);
return;
outofdate:
if (fileversion != FILEVERSION) {
(void) fprintf(stderr, "cscope: converting to new symbol database file format\n");
goto force;
}
if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) {
(void) fprintf(stderr, "cscope: cannot open file %s\n", reffile);
myexit(1);
}
blocknumber = -1;
(void) readblock();
(void) scanpast('\t');
oldfile = getoldfile();
}
else {
force: reftime = 0;
oldfile = NULL;
}
if ((newrefs = myfopen(newreffile, "wb")) == NULL) {
(void) fprintf(stderr, "cscope: cannot open file %s\n", reffile);
myexit(1);
}
if (invertedindex == YES && (postings = myfopen(temp1, "wb")) == NULL) {
cannotwrite(temp1);
cannotindex();
}
putheader(newdir);
fileversion = FILEVERSION;
if (buildonly == YES && !isatty(0)) {
interactive = NO;
}
else {
searchcount = 0;
}
dbputc('\t');
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();
for (fileindex = firstfile; fileindex < lastfile; ++fileindex) {
if (interactive == YES && fileindex % 10 == 0) {
progress("Building symbol database",
(long)fileindex, (long)lastfile);
}
file = srcfiles[fileindex];
while (oldfile != NULL && strcmp(file, oldfile) > 0) {
oldfile = getoldfile();
}
if (oldfile == NULL || strcmp(file, oldfile) < 0) {
crossref(file);
++built;
}
else if (lstat(file, &statstruct) == 0 &&
statstruct.st_mtime > reftime) {
crossref(file);
++built;
oldfile = getoldfile();
}
else {
putfilename(file);
if (invertedindex == YES) {
copyinverted();
}
else {
copydata();
}
++copied;
oldfile = getoldfile();
}
}
if (lastfile == nsrcfiles) {
break;
}
firstfile = lastfile;
lastfile = nsrcfiles;
if (invertedindex == YES) {
srcoffset = myrealloc(srcoffset,
(nsrcfiles + 1) * sizeof(long));
}
qsort(&srcfiles[firstfile], (unsigned) (lastfile -
firstfile), sizeof(char *), compare);
}
putfilename("");
dbputc('\n');
traileroffset = dboffset;
putlist(srcdirs, nsrcdirs);
putlist(incdirs, nincdirs);
putlist(srcfiles, nsrcfiles);
if (fflush(newrefs) == EOF) {
cannotwrite(newreffile);
}
if (invertedindex == YES) {
char sortcommand[PATHLEN + 1];
if (fflush(postings) == EOF) {
cannotwrite(temp1);
}
(void) fstat(fileno(postings), &statstruct);
(void) fclose(postings);
(void) sprintf(sortcommand, "env LC_ALL=C sort -T %s %s", tmpdir, temp1);
if ((postings = mypopen(sortcommand, "r")) == NULL) {
(void) 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();
}
(void) mypclose(postings);
}
(void) unlink(temp1);
(void) free(srcoffset);
}
rewind(newrefs);
putheader(newdir);
(void) fclose(newrefs);
if (symrefs >= 0) {
(void) close(symrefs);
}
if (oldrefs != NULL) {
(void) fclose(oldrefs);
}
movefile(newreffile, reffile);
}
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));
}
void seek_to_trailer(FILE *f)
{
if (fscanf(f, "%ld", &traileroffset) != 1) {
posterr("cscope: cannot read trailer offset from file %s\n", reffile);
myexit(1);
}
if (fseek(f, traileroffset, SEEK_SET) == -1) {
posterr("cscope: cannot seek to trailer in file %s\n", reffile);
myexit(1);
}
}
static char *
getoldfile(void)
{
static char file[PATHLEN + 1];
if (blockp != NULL) {
do {
if (*blockp == NEWFILE) {
skiprefchar();
putstring(file);
if (file[0] != '\0') {
return(file);
}
return(NULL);
}
} while (scanpast('\t') != NULL);
}
return(NULL);
}
void free_newbuildfiles(void)
{
free(newinvname);
free(newinvpost);
free(newreffile);
}
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 {
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
}
static void
putlist(char **names, int count)
{
int i, size = 0;
(void) fprintf(newrefs, "%d\n", count);
if (names == srcfiles) {
for (i = 0; i < count; ++i) {
size += strlen(names[i]) + 1;
}
(void) fprintf(newrefs, "%d\n", size);
}
for (i = 0; i < count; ++i) {
if (fputs(names[i], newrefs) == EOF ||
putc('\n', newrefs) == EOF) {
cannotwrite(newreffile);
}
}
}
static void
copydata(void)
{
char symbol[PATLEN + 1];
char *cp;
setmark('\t');
cp = blockp;
for (;;) {
do {
while (*cp != '\t') {
dbputc(*cp++);
}
} while (*++cp == '\0' && (cp = readblock()) != NULL);
dbputc('\t');
if (*(cp + 1) == '\0') {
cp = readblock();
}
if (cp == NULL || *cp == NEWFILE) {
break;
}
if (*cp == INCLUDE) {
blockp = cp;
putinclude(symbol);
writestring(symbol);
setmark('\t');
cp = blockp;
}
}
blockp = cp;
}
static void
copyinverted(void)
{
char *cp;
char c;
int type;
char symbol[PATLEN + 1];
cp = blockp;
for (;;) {
setmark('\n');
do {
while (*cp != '\n') {
dbputc(*cp++);
}
} while (*++cp == '\0' && (cp = readblock()) != NULL);
dbputc('\n');
if (*(cp + 1) == '\0') {
cp = readblock();
}
if (cp == NULL) {
break;
}
switch (*cp) {
case '\n':
lineoffset = dboffset + 1;
continue;
case '\t':
dbputc('\t');
blockp = cp;
type = getrefchar();
switch (type) {
case NEWFILE:
return;
case INCLUDE:
putinclude(symbol);
goto output;
}
dbputc(type);
skiprefchar();
putstring(symbol);
goto output;
}
c = *cp;
if (c & 0200) {
c = dichar1[(c & 0177) / 8];
}
if (isalpha((unsigned char)c) || c == '_') {
blockp = cp;
putstring(symbol);
type = ' ';
output:
putposting(symbol, type);
writestring(symbol);
if (blockp == NULL) {
return;
}
cp = blockp;
}
}
blockp = cp;
}
static void
movefile(char *new, char *old)
{
(void) unlink(old);
if (rename(new, old) == -1) {
(void) myperror("cscope");
posterr("cscope: cannot rename file %s to file %s\n",
new, old);
myexit(1);
}
}
static void
putinclude(char *s)
{
dbputc(INCLUDE);
skiprefchar();
putstring(s);
incfile(s + 1, s);
}