#include "build.h"
#include "global.h"
#include "library.h"
#include "alloc.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_buf[] = INVNAME;
char invpost_buf[] = INVPOST;
char reffile_buf[] = REFFILE;
char *invname = invname_buf;
char *invpost = invpost_buf;
char *reffile = reffile_buf;
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 fetch_include_from_dbase(char *, size_t);
static void putlist(char **names, int count);
static BOOL samelist(FILE *oldrefs, char **names, int count);
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);
}
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 (! fgets(oldname, sizeof(oldname), oldrefs)||
strnotequal(oldname, names[i])) {
return(NO);
}
}
return(YES);
}
void setup_build_filenames(char *reffile)
{
char *path;
char *s;
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);
}
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)
{
close(symrefs);
if (invertedindex == YES) {
invclose(&invcontrol);
nsrcoffset = 0;
npostings = 0;
}
build();
opendatabase();
if (refsfound != NULL) {
fclose(refsfound);
refsfound = NULL;
}
}
void
build(void)
{
unsigned long i;
FILE *oldrefs;
time_t reftime;
char *file;
char *oldfile;
char newdir[PATHLEN + 1];
char olddir[PATHLEN + 1];
char oldname[PATHLEN + 1];
unsigned long oldnum;
struct stat statstruct;
unsigned long firstfile;
unsigned long lastfile;
int built = 0;
int copied = 0;
unsigned long fileindex;
BOOL interactive = YES;
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));
}
qsort(srcfiles, nsrcfiles, sizeof(char *), compare);
if ((oldrefs = vpfopen(reffile, "rb")) != NULL
&& unconditional == NO
&& fscanf(oldrefs, "cscope %d %" PATHLEN_STR "s", &fileversion, olddir) == 2
&& (strcmp(olddir, currentdir) == 0
|| strcmp(olddir, newdir) == 0)) {
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 != '-') {
ungetc(c, oldrefs);
break;
}
switch (c = getc(oldrefs)) {
case 'c':
oldcompress = NO;
break;
case 'q':
oldinvertedindex = YES;
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) {
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;
}
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, "%lu", &oldnum) != 1
|| (fileversion >= 9 && fscanf(oldrefs, "%*s") != 0)) {
goto outofdate;
}
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;
}
}
while (i++ < oldnum && fgets(oldname, sizeof(oldname), oldrefs)) {
addsrcfile(oldname);
}
fclose(oldrefs);
return;
outofdate:
if (fileversion != FILEVERSION) {
fprintf(stderr, "\
cscope: converting to new symbol database file format\n");
goto force;
}
if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) {
postfatal("cscope: cannot open file %s\n", reffile);
}
blocknumber = -1;
read_block();
scanpast('\t');
oldfile = getoldfile();
} else {
force: reftime = 0;
oldfile = NULL;
}
if ((newrefs = myfopen(newreffile, "wb")) == NULL) {
postfatal("cscope: cannot open file %s\n", reffile);
}
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;
}
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", fileindex, 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], (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);
}
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);
}
rewind(newrefs);
putheader(newdir);
fclose(newrefs);
if (symrefs >= 0) {
close(symrefs);
}
if (oldrefs != NULL) {
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) {
postfatal("cscope: cannot read trailer offset from file %s\n", reffile);
}
if (fseek(f, traileroffset, SEEK_SET) == -1) {
postfatal("cscope: cannot seek to trailer in file %s\n", reffile);
}
}
static char *
getoldfile(void)
{
static char file[PATHLEN + 1];
if (blockp != NULL) {
do {
if (*blockp == NEWFILE) {
skiprefchar();
fetch_string_from_dbase(file, sizeof(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;
fprintf(newrefs, "%d\n", count);
if (names == srcfiles) {
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);
}
}
}
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 = read_block()) != NULL);
dbputc('\t');
if (*(cp + 1) == '\0') {
cp = read_block();
}
if (cp == NULL || *cp == NEWFILE) {
break;
}
if (*cp == INCLUDE) {
blockp = cp;
fetch_include_from_dbase(symbol, sizeof(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 = read_block()) != NULL);
dbputc('\n');
if (*(cp + 1) == '\0') {
cp = read_block();
}
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:
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) {
c = dichar1[(c & 0177) / 8];
}
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;
}
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);
}
}
static void
fetch_include_from_dbase(char *s, size_t length)
{
dbputc(INCLUDE);
skiprefchar();
fetch_string_from_dbase(s, length);
incfile(s + 1, s);
}