#include "global.h"
#include "build.h"
#include "vp.h"
#include "version.h"
#include "scanner.h"
#include "alloc.h"
#include <stdlib.h>
#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES)
#include <ncurses.h>
#else
#include <curses.h>
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <signal.h>
#define EDITOR "vi"
#define HOME "/"
#define SHELL "sh"
#define LINEFLAG "+%s"
#define TMPDIR "/tmp"
#ifndef DFLT_INCDIR
#define DFLT_INCDIR "/usr/include"
#endif
static char const rcsid[] = "$Id: main.c,v 1.41 2006/08/20 15:00:34 broeker Exp $";
char dichar1[] = " teisaprnl(of)=c";
char dichar2[] = " tnerpla";
char dicode1[256];
char dicode2[256];
char *editor, *shell, *lineflag;
char *home;
BOOL lineflagafterfile;
char *argv0;
BOOL compress = YES;
BOOL dbtruncated;
int dispcomponents = 1;
#if CCS
BOOL displayversion;
#endif
BOOL editallprompt = YES;
unsigned int fileargc;
char **fileargv;
int fileversion;
BOOL incurses = NO;
BOOL invertedindex;
BOOL isuptodate;
BOOL kernelmode;
BOOL linemode = NO;
BOOL verbosemode = NO;
BOOL recurse_dir = NO;
char *namefile;
BOOL ogs;
char *prependpath;
FILE *refsfound;
char temp1[PATHLEN + 1];
char temp2[PATHLEN + 1];
char tempdirpv[PATHLEN + 1];
long totalterms;
BOOL trun_syms;
char tempstring[TEMPSTRING_LEN + 1];
char *tmpdir;
static BOOL onesearch;
static char *reflines;
static void initcompress(void);
static void longusage(void);
static void skiplist(FILE *oldrefs);
static void usage(void);
#ifdef HAVE_FIXKEYPAD
void fixkeypad();
#endif
#if defined(KEY_RESIZE) && !defined(__DJGPP__)
void
sigwinch_handler(int sig, siginfo_t *info, void *unused)
{
(void) sig;
(void) info;
(void) unused;
ungetch(KEY_RESIZE);
}
#endif
int
main(int argc, char **argv)
{
FILE *names;
int oldnum;
char path[PATHLEN + 1];
FILE *oldrefs;
char *s;
int c;
unsigned int i;
pid_t pid;
struct stat stat_buf;
#if defined(KEY_RESIZE) && !defined(__DJGPP__)
struct sigaction winch_action;
#endif
mode_t orig_umask;
yyin = stdin;
yyout = stdout;
argv0 = argv[0];
#if defined(KEY_RESIZE) && !defined(__DJGPP__)
winch_action.sa_sigaction = sigwinch_handler;
sigemptyset(&winch_action.sa_mask);
winch_action.sa_flags = SA_SIGINFO;
sigaction(SIGWINCH,&winch_action,NULL);
#endif
while (--argc > 0 && (*++argv)[0] == '-') {
if (strequal(argv[0], "--help")
|| strequal(argv[0], "-h")) {
longusage();
myexit(0);
}
if (strequal(argv[0], "--version")
|| strequal(argv[0], "-V")) {
#if CCS
displayversion = YES;
#else
fprintf(stderr, "%s: version %d%s\n", argv0,
FILEVERSION, FIXVERSION);
myexit(0);
#endif
}
for (s = argv[0] + 1; *s != '\0'; s++) {
if (isdigit((unsigned char) *s)) {
field = *s - '0';
if (field > 8) {
field = 8;
}
if (*++s == '\0' && --argc > 0) {
s = *++argv;
}
if (strlen(s) > PATLEN) {
postfatal("\
cscope: pattern too long, cannot be > %d characters\n", PATLEN);
}
strcpy(Pattern, s);
goto nextarg;
}
switch (*s) {
case '-':
--argc;
++argv;
goto lastarg;
case 'b':
buildonly = YES;
linemode = YES;
break;
case 'c':
compress = NO;
break;
case 'C':
caseless = YES;
egrepcaseless(caseless);
break;
case 'd':
isuptodate = YES;
break;
case 'e':
editallprompt = NO;
break;
case 'k':
kernelmode = YES;
break;
case 'L':
onesearch = YES;
case 'l':
linemode = YES;
break;
case 'v':
verbosemode = YES;
break;
case 'o':
ogs = YES;
break;
case 'q':
invertedindex = YES;
break;
case 'T':
trun_syms = YES;
break;
case 'u':
unconditional = YES;
break;
case 'U':
fileschanged = YES;
break;
case 'R':
recurse_dir = YES;
break;
case 'f':
case 'F':
case 'i':
case 'I':
case 'p':
case 'P':
case 's':
case 'S':
c = *s;
if (*++s == '\0' && --argc > 0) {
s = *++argv;
}
if (*s == '\0') {
fprintf(stderr, "%s: -%c option: missing or empty value\n",
argv0, c);
goto usage;
}
switch (c) {
case 'f':
reffile = s;
if (strlen(reffile) > sizeof(path) - 1) {
postfatal("\
cscope: reffile too long, cannot be > %d characters\n", sizeof(path) - 1);
}
strcpy(path, s);
#ifdef SHORT_NAMES_ONLY
s = mybasename(path);
if (strlen(s) > 11) {
s[11] = '\0';
}
#endif
s = path + strlen(path);
strcpy(s, ".in");
invname = my_strdup(path);
strcpy(s, ".po");
invpost = my_strdup(path);
break;
case 'F':
reflines = s;
break;
case 'i':
namefile = s;
break;
case 'I':
includedir(s);
break;
case 'p':
if (*s < '0' || *s > '9' ) {
fprintf(stderr, "\
%s: -p option: missing or invalid numeric value\n",
argv0);
goto usage;
}
dispcomponents = atoi(s);
break;
case 'P':
prependpath = s;
break;
case 's':
case 'S':
sourcedir(s);
break;
}
goto nextarg;
default:
fprintf(stderr, "%s: unknown option: -%c\n", argv0,
*s);
usage:
usage();
fprintf(stderr, "Try the -h option for more information.\n");
myexit(1);
}
}
nextarg:
;
}
lastarg:
editor = mygetenv("EDITOR", EDITOR);
editor = mygetenv("VIEWER", editor);
editor = mygetenv("CSCOPE_EDITOR", editor);
home = mygetenv("HOME", HOME);
shell = mygetenv("SHELL", SHELL);
lineflag = mygetenv("CSCOPE_LINEFLAG", LINEFLAG);
lineflagafterfile = getenv("CSCOPE_LINEFLAG_AFTER_FILE") ? 1 : 0;
tmpdir = mygetenv("TMPDIR", TMPDIR);
if (namefile && strcmp(namefile, "-") == 0 && !buildonly) {
postfatal("cscope: Must use -b if file list comes from stdin\n");
}
if (lstat (tmpdir, &stat_buf)) {
fprintf (stderr, "\
cscope: Temporary directory %s does not exist or cannot be accessed\n",
tmpdir);
fprintf (stderr, "\
cscope: Please create the directory or set the environment variable\n\
cscope: TMPDIR to a valid directory\n");
myexit(1);
}
orig_umask = umask(S_IRWXG|S_IRWXO);
pid = getpid();
snprintf(tempdirpv, sizeof(tempdirpv), "%s/cscope.%d", tmpdir, pid);
if(mkdir(tempdirpv,S_IRWXU)) {
fprintf(stderr, "\
cscope: Could not create private temp dir %s\n",
tempdirpv);
myexit(1);
}
umask(orig_umask);
snprintf(temp1, sizeof(temp1), "%s/cscope.1", tempdirpv);
snprintf(temp2, sizeof(temp2), "%s/cscope.2", tempdirpv);
if (signal(SIGINT, SIG_IGN) != SIG_IGN) {
signal(SIGINT, myexit);
signal(SIGQUIT, myexit);
}
signal(SIGHUP, myexit);
if (reffile[0] != '/' && access(".", WRITE) != 0) {
snprintf(path, sizeof(path), "%s/%s", home, reffile);
if (isuptodate == NO || access(path, READ) == 0) {
reffile = my_strdup(path);
snprintf(path, sizeof(path), "%s/%s", home, invname);
invname = my_strdup(path);
snprintf(path, sizeof(path), "%s/%s", home, invpost);
invpost = my_strdup(path);
}
}
if (linemode == NO) {
signal(SIGINT, SIG_IGN);
signal(SIGPIPE, SIG_IGN);
initscr();
entercurses();
#if TERMINFO
keypad(stdscr, TRUE);
# ifdef HAVE_FIXKEYPAD
fixkeypad();
# endif
#endif
#if UNIXPC
standend();
#endif
dispinit();
setfield();
clearmsg();
display();
}
if (isuptodate == YES) {
if ((oldrefs = vpfopen(reffile, "rb")) == NULL) {
postfatal("cscope: cannot open file %s\n", reffile);
}
if (fscanf(oldrefs, "cscope %d %*s", &fileversion) != 1) {
postfatal("cscope: cannot read file version from file %s\n",
reffile);
}
if (fileversion >= 8) {
compress = YES;
invertedindex = NO;
for (;;) {
getc(oldrefs);
if ((c = getc(oldrefs)) != '-') {
ungetc(c, oldrefs);
break;
}
switch (c = getc(oldrefs)) {
case 'c':
compress = NO;
break;
case 'q':
invertedindex = YES;
fscanf(oldrefs, "%ld", &totalterms);
break;
case 'T':
dbtruncated = YES;
trun_syms = YES;
break;
}
}
initcompress();
seek_to_trailer(oldrefs);
}
skiplist(oldrefs);
skiplist(oldrefs);
if (fscanf(oldrefs, "%lu", &nsrcfiles) != 1) {
postfatal("\
cscope: cannot read source file size from file %s\n", reffile);
}
srcfiles = mymalloc(nsrcfiles * sizeof(char *));
if (fileversion >= 9) {
if (fscanf(oldrefs, "%d", &oldnum) != 1) {
postfatal("\
cscope: cannot read string space size from file %s\n", reffile);
}
s = mymalloc(oldnum);
getc(oldrefs);
if (fread(s, oldnum, 1, oldrefs) != 1) {
postfatal("\
cscope: cannot read source file names from file %s\n", reffile);
}
for (i = 0; i < nsrcfiles; ++i) {
srcfiles[i] = s;
for (++s; *s != '\n'; ++s) {
;
}
*s = '\0';
++s;
}
if ((namefile != NULL && (names = vpfopen(namefile, "r")) != NULL)
|| (names = vpfopen(NAMEFILE, "r")) != NULL) {
while (fgets(path, sizeof(path), names) != NULL && *path == '-') {
i = path[1];
s = path + 2;
if (*s == '\0') {
fgets(path, sizeof(path), names);
s = path;
}
switch (i) {
case 'p':
if (*s < '0' || *s > '9') {
posterr("cscope: -p option in file %s: missing or invalid numeric value\n", namefile);
}
dispcomponents = atoi(s);
}
}
fclose(names);
}
} else {
for (i = 0; i < nsrcfiles; ++i) {
if (!fgets(path, sizeof(path), oldrefs) ) {
postfatal("\
cscope: cannot read source file name from file %s\n",
reffile);
}
srcfiles[i] = my_strdup(path);
}
}
fclose(oldrefs);
} else {
fileargc = argc;
fileargv = argv;
if ((s = getenv("SOURCEDIRS")) != NULL) {
sourcedir(s);
}
srcfiles = mymalloc(msrcfiles * sizeof(char *));
makefilelist();
if (nsrcfiles == 0) {
postfatal("cscope: no source files found\n");
}
if ((s = getenv("INCLUDEDIRS")) != NULL) {
includedir(s);
}
if (kernelmode == NO) {
includedir(DFLT_INCDIR);
}
initsymtab();
setup_build_filenames(reffile);
initcompress();
if (linemode == NO || verbosemode == YES)
postmsg("Building cross-reference...");
build();
if (linemode == NO )
clearmsg();
if (buildonly == YES) {
myexit(0);
}
}
opendatabase();
if (linemode == YES) {
if (*Pattern != '\0') {
if (search() == YES) {
if (verbosemode == YES)
printf("cscope: %d lines\n",
totallines);
while ((c = getc(refsfound)) != EOF)
putchar(c);
}
}
if (onesearch == YES)
myexit(0);
for (;;) {
char buf[PATLEN + 2];
printf(">> ");
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) == NULL) {
myexit(0);
}
if (*(s = buf + strlen(buf) - 1) == '\n') {
*s = '\0';
}
switch (*buf) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
field = *buf - '0';
strcpy(Pattern, buf + 1);
search();
printf("cscope: %d lines\n", totallines);
while ((c = getc(refsfound)) != EOF) {
putchar(c);
}
break;
case 'c':
case ctrl('C'):
if (caseless == NO) {
caseless = YES;
} else {
caseless = NO;
}
egrepcaseless(caseless);
break;
case 'r':
case ctrl('R'):
freefilelist();
makefilelist();
case 'R':
rebuild();
putchar('\n');
break;
case 'C':
freefilelist();
putchar('\n');
break;
case 'F':
strcpy(path, buf + 1);
if (infilelist(path) == NO &&
(s = inviewpath(path)) != NULL) {
addsrcfile(s);
}
putchar('\n');
break;
case 'q':
case ctrl('D'):
case ctrl('Z'):
myexit(0);
default:
fprintf(stderr, "cscope: unknown command '%s'\n", buf);
break;
}
}
}
if (errorsfound == YES) {
errorsfound = NO;
askforreturn();
}
if (*Pattern != '\0') {
atfield();
command(ctrl('Y'));
} else if (reflines != NULL) {
readrefs(reflines);
}
display();
for (;;) {
if (!selecting)
atfield();
if ((c = mygetch()) == EOF || c == ctrl('D') || c == ctrl('Z')) {
break;
}
if (command(c) == YES) {
display();
}
if (selecting) {
move(displine[curdispline], 0);
refresh();
}
}
myexit(0);
return 0;
}
void
cannotopen(char *file)
{
posterr("Cannot open file %s", file);
}
void
cannotwrite(char *file)
{
char msg[MSGLEN + 1];
snprintf(msg, sizeof(msg), "Removed file %s because write failed", file);
myperror(msg);
unlink(file);
myexit(1);
}
static void
initcompress(void)
{
int i;
if (compress == YES) {
for (i = 0; i < 16; ++i) {
dicode1[(unsigned char) (dichar1[i])] = i * 8 + 1;
}
for (i = 0; i < 8; ++i) {
dicode2[(unsigned char) (dichar2[i])] = i + 1;
}
}
}
static void
skiplist(FILE *oldrefs)
{
int i;
if (fscanf(oldrefs, "%d", &i) != 1) {
postfatal("cscope: cannot read list size from file %s\n", reffile);
}
while (--i >= 0) {
if (fscanf(oldrefs, "%*s") != 0) {
postfatal("cscope: cannot read list name from file %s\n", reffile);
}
}
}
void
entercurses(void)
{
incurses = YES;
#ifndef __MSDOS__
nonl();
#endif
cbreak();
noecho();
clear();
mouseinit();
drawscrollbar(topline, nextline);
}
void
exitcurses(void)
{
move(LINES - 1, 0);
clrtoeol();
refresh();
endwin();
incurses = NO;
mousecleanup();
fflush(stdout);
}
static void
usage(void)
{
fprintf(stderr, "Usage: cscope [-bcCdehklLqRTuUvV] [-f file] [-F file] [-i file] [-I dir] [-s dir]\n");
fprintf(stderr, " [-p number] [-P path] [-[0-8] pattern] [source files]\n");
}
static void
longusage(void)
{
usage();
fprintf(stderr, "\
\n\
-b Build the cross-reference only.\n\
-C Ignore letter case when searching.\n\
-c Use only ASCII characters in the cross-ref file (don't compress).\n\
-d Do not update the cross-reference.\n\
-e Suppress the <Ctrl>-e command prompt between files.\n\
-F symfile Read symbol reference lines from symfile.\n\
-f reffile Use reffile as cross-ref file name instead of %s.\n",
REFFILE);
fprintf(stderr, "\
-h This help screen.\n\
-I incdir Look in incdir for any #include files.\n\
-i namefile Browse through files listed in namefile, instead of %s\n",
NAMEFILE);
fprintf(stderr, "\
-k Kernel Mode - don't use %s for #include files.\n",
DFLT_INCDIR);
fputs("\
-L Do a single search with line-oriented output.\n\
-l Line-oriented interface.\n\
-num pattern Go to input field num (counting from 0) and find pattern.\n\
-P path Prepend path to relative file names in pre-built cross-ref file.\n\
-p n Display the last n file path components.\n\
-q Build an inverted index for quick symbol searching.\n\
-R Recurse directories for files.\n\
-s dir Look in dir for additional source files.\n\
-T Use only the first eight characters to match against C symbols.\n\
-U Check file time stamps.\n\
-u Unconditionally build the cross-reference file.\n\
-v Be more verbose in line mode.\n\
-V Print the version number.\n\
\n\
Please see the manpage for more information.\n",
stderr);
}
void
myexit(int sig)
{
if (refsfound != NULL)
fclose(refsfound);
if (temp1[0] != '\0') {
unlink(temp1);
unlink(temp2);
rmdir(tempdirpv);
}
if (incurses == YES) {
exitcurses();
}
if (sig == SIGQUIT) {
abort();
}
freefilelist();
freeinclist();
freesrclist();
freecrossref();
free_newbuildfiles();
exit(sig);
}