454 lines
13 KiB
C
454 lines
13 KiB
C
/*===========================================================================
|
|
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 falseT 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 falseT 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
|
|
*
|
|
* build cross-reference file
|
|
*/
|
|
|
|
#include "global.h"
|
|
|
|
#include "build.h"
|
|
#include "scanner.h"
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <sys/stat.h>
|
|
|
|
/* convert long to a string in base BASE notation */
|
|
#define ltobase(value) \
|
|
do { \
|
|
n = (value); \
|
|
s = buf + (sizeof(buf) - 1); \
|
|
*s = '\0'; \
|
|
digits = 1; \
|
|
while(n >= BASE) { \
|
|
++digits; \
|
|
i = n; \
|
|
n /= BASE; \
|
|
*--s = i - n * BASE + '!'; \
|
|
} \
|
|
*--s = n + '!'; \
|
|
} while(0)
|
|
|
|
#define SYMBOLINC 20 /* symbol list size increment */
|
|
|
|
long dboffset; /* new database offset */
|
|
bool errorsfound; /* prompt before clearing messages */
|
|
long lineoffset; /* source line database offset */
|
|
long npostings; /* number of postings */
|
|
int nsrcoffset; /* number of file name database offsets */
|
|
long *srcoffset; /* source file name database offsets */
|
|
unsigned long symbols; /* number of symbols */
|
|
|
|
static char *filename; /* file name for warning messages */
|
|
static long fcnoffset; /* function name database offset */
|
|
static long macrooffset; /* macro name database offset */
|
|
static unsigned long msymbols = SYMBOLINC; /* maximum number of symbols */
|
|
|
|
struct symbol { /* symbol data */
|
|
int type; /* type */
|
|
unsigned int first; /* index of first character in text */
|
|
unsigned int last; /* index of last+1 character in text */
|
|
unsigned int length; /* symbol length */
|
|
unsigned int fcn_level; /* function level of the symbol */
|
|
};
|
|
static struct symbol *symbol;
|
|
|
|
static void putcrossref(void);
|
|
static void savesymbol(int token, int num);
|
|
|
|
void crossref(char *srcfile) {
|
|
unsigned int i;
|
|
unsigned int length; /* symbol length */
|
|
unsigned int entry_no; /* function level of the symbol */
|
|
int token; /* current token */
|
|
struct stat st;
|
|
|
|
if(!((stat(srcfile, &st) == 0) && S_ISREG(st.st_mode))) {
|
|
cannotopen(srcfile);
|
|
errorsfound = true;
|
|
return;
|
|
}
|
|
|
|
entry_no = 0;
|
|
/* open the source file */
|
|
if((yyin = myfopen(srcfile, "r")) == NULL) {
|
|
cannotopen(srcfile);
|
|
errorsfound = true;
|
|
return;
|
|
}
|
|
filename = srcfile; /* save the file name for warning messages */
|
|
putfilename(srcfile); /* output the file name */
|
|
dbputc('\n');
|
|
dbputc('\n');
|
|
|
|
/* read the source file */
|
|
initscanner(srcfile);
|
|
fcnoffset = macrooffset = 0;
|
|
symbols = 0;
|
|
if(symbol == NULL) { symbol = malloc(msymbols * sizeof(*symbol)); }
|
|
for(;;) {
|
|
|
|
/* get the next token */
|
|
switch(token = yylex()) {
|
|
default:
|
|
/* if requested, truncate C symbols */
|
|
length = last - first;
|
|
if(trun_syms == true && length > 8 && token != INCLUDE &&
|
|
token != NEWFILE) {
|
|
length = 8;
|
|
last = first + 8;
|
|
}
|
|
/* see if the token has a symbol */
|
|
if(length == 0) {
|
|
savesymbol(token, entry_no);
|
|
break;
|
|
}
|
|
/* update entry_no if see function entry */
|
|
if(token == FCNDEF) { entry_no++; }
|
|
/* see if the symbol is already in the list */
|
|
for(i = 0; i < symbols; ++i) {
|
|
if(length == symbol[i].length &&
|
|
strncmp(my_yytext + first, my_yytext + symbol[i].first, length) ==
|
|
0 &&
|
|
entry_no == symbol[i].fcn_level &&
|
|
token == symbol[i].type) { /* could be a::a() */
|
|
break;
|
|
}
|
|
}
|
|
if(i == symbols) { /* if not already in list */
|
|
savesymbol(token, entry_no);
|
|
}
|
|
break;
|
|
|
|
case NEWLINE: /* end of line containing symbols */
|
|
entry_no = 0; /* reset entry_no for each line */
|
|
#ifdef USING_LEX
|
|
--yyleng; /* remove the newline */
|
|
#endif
|
|
putcrossref(); /* output the symbols and source line */
|
|
lineno = myylineno; /* save the symbol line number */
|
|
#ifndef USING_LEX
|
|
/* HBB 20010425: replaced yyleng-- by this chunk: */
|
|
if(my_yytext) *my_yytext = '\0';
|
|
my_yyleng = 0;
|
|
#endif
|
|
break;
|
|
|
|
case LEXERR: /* Lexer error, abort further parsing of this file */
|
|
case LEXEOF: /* end of file; last line may not have \n */
|
|
|
|
/* if there were symbols, output them and the source line */
|
|
if(symbols > 0) { putcrossref(); }
|
|
(void)fclose(yyin); /* close the source file */
|
|
|
|
/* output the leading tab expected by the next call */
|
|
dbputc('\t');
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* save the symbol in the list */
|
|
|
|
static void savesymbol(int token, int num) {
|
|
/* make sure there is room for the symbol */
|
|
if(symbols == msymbols) {
|
|
msymbols += SYMBOLINC;
|
|
symbol = realloc(symbol, msymbols * sizeof(*symbol));
|
|
}
|
|
/* save the symbol */
|
|
symbol[symbols].type = token;
|
|
symbol[symbols].first = first;
|
|
symbol[symbols].last = last;
|
|
symbol[symbols].length = last - first;
|
|
symbol[symbols].fcn_level = num;
|
|
++symbols;
|
|
}
|
|
|
|
/* output the file name */
|
|
|
|
void putfilename(char *srcfile) {
|
|
/* check for file system out of space */
|
|
/* note: dbputc is not used to avoid lint complaint */
|
|
if(putc(NEWFILE, newrefs) == EOF) {
|
|
cannotwrite(newreffile);
|
|
/* NOTREACHED */
|
|
}
|
|
++dboffset;
|
|
if(invertedindex == true) { srcoffset[nsrcoffset++] = dboffset; }
|
|
dbfputs(srcfile);
|
|
fcnoffset = macrooffset = 0;
|
|
}
|
|
|
|
/* output the symbols and source line */
|
|
|
|
static void putcrossref(void) {
|
|
unsigned int i, j;
|
|
unsigned char c;
|
|
bool blank; /* blank indicator */
|
|
unsigned int symput = 0; /* symbols output */
|
|
int type;
|
|
|
|
/* output the source line */
|
|
lineoffset = dboffset;
|
|
dboffset += fprintf(newrefs, "%d ", lineno);
|
|
#ifdef PRINTF_RETVAL_BROKEN
|
|
dboffset = ftell(newrefs); /* fprintf doesn't return chars written */
|
|
#endif
|
|
|
|
/* HBB 20010425: added this line: */
|
|
my_yytext[my_yyleng] = '\0';
|
|
|
|
blank = false;
|
|
for(i = 0; i < my_yyleng; ++i) {
|
|
|
|
/* change a tab to a blank and compress blanks */
|
|
if((c = my_yytext[i]) == ' ' || c == '\t') {
|
|
blank = true;
|
|
} else if(symput < symbols && i == symbol[symput].first) {
|
|
/* look for the start of a symbol */
|
|
|
|
/* check for compressed blanks */
|
|
if(blank == true) {
|
|
blank = false;
|
|
dbputc(' ');
|
|
}
|
|
dbputc('\n'); /* symbols start on a new line */
|
|
|
|
/* output any symbol type */
|
|
if((type = symbol[symput].type) != IDENT) {
|
|
dbputc('\t');
|
|
dbputc(type);
|
|
} else {
|
|
type = ' ';
|
|
}
|
|
/* output the symbol */
|
|
j = symbol[symput].last;
|
|
c = my_yytext[j];
|
|
my_yytext[j] = '\0';
|
|
if(invertedindex == true) { putposting(my_yytext + i, type); }
|
|
writestring(my_yytext + i);
|
|
dbputc('\n');
|
|
my_yytext[j] = c;
|
|
i = j - 1;
|
|
++symput;
|
|
} else {
|
|
/* HBB: try to save some time by early-out handling of
|
|
* non-compressed mode */
|
|
if(compress == false) {
|
|
if(blank == true) {
|
|
dbputc(' ');
|
|
blank = false;
|
|
}
|
|
j = i + strcspn(my_yytext + i, "\t ");
|
|
if(symput < symbols && j >= symbol[symput].first)
|
|
j = symbol[symput].first;
|
|
c = my_yytext[j];
|
|
my_yytext[j] = '\0';
|
|
writestring(my_yytext + i);
|
|
my_yytext[j] = c;
|
|
i = j - 1;
|
|
/* finished this 'i', continue with the blank */
|
|
continue;
|
|
}
|
|
|
|
/* check for compressed blanks */
|
|
if(blank == true) {
|
|
if(dicode2[c]) {
|
|
c = DICODE_COMPRESS(' ', c);
|
|
} else {
|
|
dbputc(' ');
|
|
}
|
|
} else if(IS_A_DICODE(c, my_yytext[i + 1]) && symput < symbols &&
|
|
i + 1 != symbol[symput].first) {
|
|
/* compress digraphs */
|
|
c = DICODE_COMPRESS(c, my_yytext[i + 1]);
|
|
++i;
|
|
}
|
|
dbputc((int)c);
|
|
blank = false;
|
|
|
|
/* skip compressed characters */
|
|
if(c < ' ') {
|
|
++i;
|
|
|
|
/* skip blanks before a preprocesor keyword */
|
|
/* note: don't use isspace() because \f and \v
|
|
are used for keywords */
|
|
while((j = my_yytext[i]) == ' ' || j == '\t') {
|
|
++i;
|
|
}
|
|
/* skip the rest of the keyword */
|
|
while(isalpha((unsigned char)my_yytext[i])) {
|
|
++i;
|
|
}
|
|
/* skip space after certain keywords */
|
|
if(keyword[c].delim != '\0') {
|
|
while((j = my_yytext[i]) == ' ' || j == '\t') {
|
|
++i;
|
|
}
|
|
}
|
|
/* skip a '(' after certain keywords */
|
|
if(keyword[c].delim == '(' && my_yytext[i] == '(') { ++i; }
|
|
--i; /* compensate for ++i in for() */
|
|
} /* if compressed char */
|
|
} /* else: not a symbol */
|
|
} /* for(i) */
|
|
|
|
/* ignore trailing blanks */
|
|
dbputc('\n');
|
|
dbputc('\n');
|
|
|
|
/* output any #define end marker */
|
|
/* note: must not be part of #define so putsource() doesn't discard it
|
|
so findcalledbysub() can find it and return */
|
|
if(symput < symbols && symbol[symput].type == DEFINEEND) {
|
|
dbputc('\t');
|
|
dbputc(DEFINEEND);
|
|
dbputc('\n');
|
|
dbputc('\n'); /* mark beginning of next source line */
|
|
macrooffset = 0;
|
|
}
|
|
symbols = 0;
|
|
}
|
|
|
|
/* HBB 20000421: new function, for avoiding memory leaks */
|
|
/* free the cross reference symbol table */
|
|
void freecrossref() {
|
|
if(symbol) free(symbol);
|
|
symbol = NULL;
|
|
symbols = 0;
|
|
}
|
|
|
|
/* output the inverted index posting */
|
|
|
|
void putposting(char *term, int type) {
|
|
long i, n;
|
|
char *s;
|
|
int digits; /* digits output */
|
|
long offset; /* function/macro database offset */
|
|
char buf[11]; /* number buffer */
|
|
|
|
/* get the function or macro name offset */
|
|
offset = fcnoffset;
|
|
if(macrooffset != 0) { offset = macrooffset; }
|
|
/* then update them to avoid negative relative name offset */
|
|
switch(type) {
|
|
case DEFINE:
|
|
macrooffset = dboffset;
|
|
break;
|
|
case DEFINEEND:
|
|
macrooffset = 0;
|
|
return; /* null term */
|
|
case FCNDEF:
|
|
fcnoffset = dboffset;
|
|
break;
|
|
case FCNEND:
|
|
fcnoffset = 0;
|
|
return; /* null term */
|
|
}
|
|
/* ignore a null term caused by a enum/struct/union without a tag */
|
|
if(*term == '\0') { return; }
|
|
/* skip any #include secondary type char (< or ") */
|
|
if(type == INCLUDE) { ++term; }
|
|
/* output the posting, which should be as small as possible to reduce
|
|
the temp file size and sort time */
|
|
(void)fputs(term, postings);
|
|
(void)putc(' ', postings);
|
|
|
|
/* the line offset is padded so postings for the same term will sort
|
|
in ascending line offset order to order the references as they
|
|
appear withing a source file */
|
|
ltobase(lineoffset);
|
|
for(i = PRECISION - digits; i > 0; --i) {
|
|
(void)putc('!', postings);
|
|
}
|
|
do {
|
|
(void)putc(*s, postings);
|
|
} while(*++s != '\0');
|
|
|
|
/* postings are also sorted by type */
|
|
(void)putc(type, postings);
|
|
|
|
/* function or macro name offset */
|
|
if(offset > 0) {
|
|
(void)putc(' ', postings);
|
|
ltobase(offset);
|
|
do {
|
|
(void)putc(*s, postings);
|
|
} while(*++s != '\0');
|
|
}
|
|
if(putc('\n', postings) == EOF) {
|
|
cannotwrite(temp1);
|
|
/* NOTREACHED */
|
|
}
|
|
++npostings;
|
|
}
|
|
|
|
/* put the string into the new database */
|
|
|
|
void writestring(char *s) {
|
|
unsigned char c;
|
|
int i;
|
|
|
|
if(compress == false) {
|
|
/* Save some I/O overhead by using puts() instead of putc(): */
|
|
dbfputs(s);
|
|
return;
|
|
}
|
|
/* compress digraphs */
|
|
for(i = 0; (c = s[i]) != '\0'; ++i) {
|
|
if(/* dicode1[c] && dicode2[(unsigned char) s[i + 1]] */
|
|
IS_A_DICODE(c, s[i + 1])) {
|
|
/* c = (0200 - 2) + dicode1[c] + dicode2[(unsigned char) s[i + 1]]; */
|
|
c = DICODE_COMPRESS(c, s[i + 1]);
|
|
++i;
|
|
}
|
|
dbputc(c);
|
|
}
|
|
}
|
|
|
|
/* print a warning message with the file name and line number */
|
|
|
|
void warning(char *text) {
|
|
|
|
(void)fprintf(stderr,
|
|
PROGRAM_NAME ": \"%s\", line %d: warning: %s\n",
|
|
filename,
|
|
myylineno,
|
|
text);
|
|
errorsfound = true;
|
|
}
|