/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/* Copyright (c) 1988 AT&T */
/* All Rights Reserved */
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* cscope - interactive C symbol or text cross-reference
*
* searching functions
*/
#include <unistd.h>
#include <stdio.h>
#include <libgen.h>
#include "global.h"
#include "vp.h"
/*
* most of these functions have been optimized so their innermost loops have
* only one test for the desired character by putting the char and
* an end-of-block marker (\0) at the end of the disk block buffer.
* When the inner loop exits on the char, an outer loop will see if
* the char is followed by a \0. If so, it will read the next block
* and restart the inner loop.
*/
/* computed for */
static POSTING *getposting(void);
static void findcalledbysub(char *file);
static void findterm(void);
static void fileprogress(void);
static void putpostingref(POSTING *p);
static char *strtolower(char *s);
/* find the symbol in the cross-reference */
void
findsymbol(void)
{
char *cp;
char c;
char *s;
if (invertedindex == YES) {
long lastline = 0;
POSTING *p;
findterm();
while ((p = getposting()) != NULL) {
putpostingref(p);
lastline = p->lineoffset;
}
}
return;
}
skiprefchar(); /* skip the file marker */
*function = '\0';
/* a macro can be inside a function, but not vice versa */
*macro = '\0';
/* find the next symbol */
/* note: this code was expanded in-line for speed */
/* while (scanpast('\n') != NULL) { */
/* other macros were replaced by code using cp instead of blockp */
for (;;) {
setmark('\n');
do { /* innermost loop optimized to only one test */
while (*cp != '\n') {
++cp;
}
/* skip the found character */
}
break;
}
/* look for a source file or function name */
if (*cp == '\t') {
switch (getrefchar()) {
case NEWFILE: /* file name */
/* save the name */
skiprefchar();
/* check for the end of the symbols */
if (*file == '\0') {
return;
}
fileprogress();
/* FALLTHROUGH */
case FCNEND: /* function end */
*function = '\0';
goto notmatched; /* don't match name */
case FCNDEF: /* function name */
s = function;
break;
case DEFINE: /* could be a macro */
if (fileversion >= 10) {
s = macro;
} else {
s = symbol;
}
break;
case DEFINEEND:
*macro = '\0';
goto notmatched; /* don't match name */
case INCLUDE: /* #include file */
goto notmatched; /* don't match name */
default: /* other symbol */
s = symbol;
}
/* save the name */
skiprefchar();
getstring(s);
/* see if this is a regular expression pattern */
s = strtolower(s);
}
goto matched;
}
}
/* match the symbol to the text pattern */
goto matched;
}
goto notmatched;
}
/* if this is a regular expression pattern */
c = *cp;
if (c & 0200) { /* digraph char? */
}
/* if this is a symbol */
if (isalpha(c) || c == '_') {
s = symbol;
s = strtolower(s);
}
/* match the symbol to the regular expression */
goto matched;
}
goto notmatched;
}
}
/* match the character to the text pattern */
/* match the rest of the symbol to the text pattern */
if (matchrest()) {
s = NULL;
/*
* output the file, calling function or macro,
* and source line
*/
} else if (s != function) {
} else {
}
return;
}
}
}
}
}
/* find the function definition or #define */
void
finddef(void)
{
char *s;
if (invertedindex == YES) {
POSTING *p;
findterm();
while ((p = getposting()) != NULL) {
switch (p->type) {
case DEFINE: /* could be a macro */
case FCNDEF:
case CLASSDEF:
case ENUMDEF:
case MEMBERDEF:
case STRUCTDEF:
case TYPEDEF:
case UNIONDEF:
case GLOBALDEF: /* other global definition */
case LOCALDEF: /* other local definition */
case PARAMETER:
putpostingref(p);
}
}
return;
}
/* find the next file name or definition */
*function = '\0';
/* a macro can be inside a function, but not vice versa */
*macro = '\0';
switch (*blockp) {
case NEWFILE:
skiprefchar(); /* save file name */
return;
}
fileprogress();
/* FALLTHROUGH */
case FCNEND: /* function end */
*function = '\0';
break;
case FCNDEF: /* function name */
s = function;
goto def;
case DEFINE: /* could be a macro */
if (fileversion >= 10) {
s = macro;
} else {
s = symbol;
}
goto def;
case DEFINEEND:
*macro = '\0';
break;
case CLASSDEF:
case ENUMDEF:
case MEMBERDEF:
case STRUCTDEF:
case TYPEDEF:
case UNIONDEF:
case GLOBALDEF: /* other global definition */
case LOCALDEF: /* other local definition */
case PARAMETER:
s = symbol;
def:
/* save the name */
skiprefchar();
getstring(s);
/* see if this is a regular expression pattern */
s = strtolower(s);
}
goto matched;
}
/* match the symbol to the text pattern */
/*
* output the file, calling function or macro,
* and source line
*/
} else if (s != function) {
} else {
}
}
}
}
}
/* find all function definitions (used by samuel only) */
void
findallfcns(void)
{
/* find the next file name or definition */
switch (*blockp) {
case NEWFILE:
skiprefchar(); /* save file name */
return;
}
fileprogress();
break;
case FCNDEF:
case CLASSDEF:
skiprefchar(); /* save function name */
/* output the file, function and source line */
break;
}
}
}
/* find the functions called by this function */
void
findcalledby(void)
{
if (invertedindex == YES) {
POSTING *p;
findterm();
while ((p = getposting()) != NULL) {
switch (p->type) {
case DEFINE: /* could be a macro */
case FCNDEF:
}
}
}
return;
}
/* find the function definition(s) */
switch (*blockp) {
case NEWFILE:
skiprefchar(); /* save file name */
return;
}
fileprogress();
break;
case DEFINE: /* could be a macro */
if (fileversion < 10) {
break;
}
/* FALLTHROUGH */
case FCNDEF:
skiprefchar(); /* match name to pattern */
if (match()) {
}
break;
}
}
}
static void
{
/* find the next function call or the end of this function */
switch (*blockp) {
case DEFINE: /* #define inside a function */
;
}
break;
case FCNCALL: /* function call */
/* output the file name */
/* output the function name */
skiprefchar();
/* output the source line */
break;
case DEFINEEND: /* #define end */
case FCNEND: /* function end */
case FCNDEF: /* function end (pre 9.5) */
case NEWFILE: /* file end */
return;
}
}
}
/* find the functions calling this function */
void
findcalling(void)
{
if (invertedindex == YES) {
POSTING *p;
findterm();
while ((p = getposting()) != NULL) {
putpostingref(p);
}
}
return;
}
/* find the next file name or function definition */
/* a macro can be inside a function, but not vice versa */
*macro = '\0';
switch (*blockp) {
case NEWFILE: /* save file name */
skiprefchar();
return;
}
fileprogress();
/* FALLTHROUGH */
case FCNEND: /* function end */
*function = '\0';
break;
case DEFINE: /* could be a macro */
if (fileversion >= 10) {
skiprefchar();
}
break;
case DEFINEEND:
*macro = '\0';
break;
case FCNDEF: /* save calling function name */
skiprefchar();
break;
case FCNCALL: /* match function called to pattern */
skiprefchar();
if (match()) {
/* output the file, calling function or */
/* macro, and source */
if (*macro != '\0') {
} else {
}
}
}
}
}
/* find direct assignment to, and increment and decrement of, this variable */
void
findassignments(void)
{
if (fileversion < 13) {
putmsg("Database built with cscope version < 13 does not "
"have assignment information");
(void) sleep(3);
return;
}
#if CTRACE
ctroff();
#endif
if (invertedindex == YES) {
POSTING *p;
findterm();
while ((p = getposting()) != NULL) {
switch (p->type) {
case ASSIGNMENT:
case GLOBALDEF: /* can have initializer */
case LOCALDEF: /* can have initializer */
case PARAMETER: /* initial value */
putpostingref(p);
}
}
return;
}
/* find the next file name or function definition */
/* a macro can be inside a function, but not vice versa */
*macro = '\0';
switch (*blockp) {
case NEWFILE: /* save file name */
skiprefchar();
return;
}
fileprogress();
/* FALLTHROUGH */
case FCNEND: /* function end */
*function = '\0';
break;
case DEFINE: /* could be a macro */
if (fileversion >= 10) {
skiprefchar();
}
break;
case DEFINEEND:
*macro = '\0';
break;
case FCNDEF: /* save calling function name */
skiprefchar();
break;
case ASSIGNMENT: /* match assignment to pattern */
case GLOBALDEF: /* can have initializer */
case LOCALDEF: /* can have initializer */
case PARAMETER: /* initial value */
skiprefchar();
if (match()) {
/* output the file, calling function or */
/* macro, and source */
if (*macro != '\0') {
} else {
}
}
}
}
}
/* find the grep pattern in the source files */
char *
findgreppat(void)
{
/* translate egrep special characters in the regular expression */
*cp++ = '\\';
}
}
*cp = '\0';
/* search the source files */
return (findegreppat(egreppat));
}
/* find this regular expression in the source files */
char *
{
int i;
char *egreperror;
/* compile the pattern */
/* search the files */
for (i = 0; i < nsrcfiles; ++i) {
fileprogress();
file);
}
}
}
return (egreperror);
}
/* find matching file names */
void
findfile(void)
{
int i;
char *s;
for (i = 0; i < nsrcfiles; ++i) {
s = srcfiles[i];
s = strtolower(s);
}
}
}
}
/* find files #including this file */
void
findinclude(void)
{
if (invertedindex == YES) {
POSTING *p;
findterm();
while ((p = getposting()) != NULL) {
putpostingref(p);
}
}
return;
}
/* find the next file name or function definition */
switch (*blockp) {
case NEWFILE: /* save file name */
skiprefchar();
return;
}
fileprogress();
break;
case INCLUDE: /* match function called to pattern */
skiprefchar();
/* skip global or local #include marker */
skiprefchar();
if (match()) {
/* output the file and source line */
}
}
}
}
/* initialize */
findinit(void)
{
int i;
char *s;
unsigned c;
/* remove trailing white space */
*s = '\0';
}
/* allow a partial match for a file name */
}
return (REGCMPERROR);
}
return (NOERROR);
}
/* see if the pattern is a regular expression */
} else {
/* check for a valid C symbol */
s = pattern;
if (!isalpha(*s) && *s != '_') {
return (NOTSYMBOL);
}
while (*++s != '\0') {
if (!isalnum(*s) && *s != '_') {
return (NOTSYMBOL);
}
}
/*
* look for use of the -T option (truncate symbol to 8
* characters) on a database not built with -T
*/
}
}
/* if this is a regular expression or letter case is to be ignored */
/* or there is an inverted index */
/* remove a leading ^ */
s = pattern;
if (*s == '^') {
}
/* remove a trailing $ */
i = strlen(s) - 1;
if (s[i] == '$') {
s[i] = '\0';
}
/* if requested, try to truncate a C symbol pattern */
s[8] = '\0';
}
/* must be an exact match */
/*
* note: regcmp doesn't recognize ^*keypad$ as an syntax error
* unless it is given as a single arg
*/
return (REGCMPERROR);
}
} else {
/* if requested, truncate a C symbol pattern */
}
/* compress the string pattern for matching */
s = cpattern;
for (i = 0; (c = pattern[i]) != '\0'; ++i) {
++i;
}
*s++ = (char)c;
}
*s = '\0';
}
return (NOERROR);
}
void
findcleanup(void)
{
/* discard any regular expression */
}
}
/* find this term, which can be a regular expression */
static void
findterm(void)
{
char *s;
int len;
npostings = 0; /* will be non-zero after database built */
lastfcnoffset = 0; /* clear the last function name found */
boolclear(); /* clear the posting set */
/* get the string prefix (if any) of the regular expression */
*s = '\0';
}
/* if letter case is to be ignored */
/*
* convert the prefix to upper case because it is lexically
* less than lower case
*/
s = prefix;
while (*s != '\0') {
*s = toupper(*s);
++s;
}
}
/* find the term lexically >= the prefix */
}
/*
* a null prefix matches the null term in the inverted index,
* so move to the first real term
*/
if (*prefix == '\0') {
(void) invforward(&invcontrol);
}
do {
s = term;
s = strtolower(s); /* make it lower case */
}
/* if it matches */
/* add it's postings to the set */
break;
}
} else if (len > 0) {
/* if there is a prefix */
/*
* if ignoring letter case and the term is out of the
* range of possible matches
*/
break; /* stop searching */
}
}
/* if using letter case and the prefix doesn't match */
break; /* stop searching */
}
}
/* display progress about every three seconds */
if (++searchcount % 50 == 0) {
progress("%ld of %ld symbols matched",
}
/* initialize the progress message for retrieving the references */
initprogress();
}
/* display the file search progress about every three seconds */
static void
fileprogress(void)
{
if (++searchcount % 10 == 0) {
(long)nsrcfiles);
}
}
/* initialize the progress message */
void
initprogress(void)
{
searchcount = 0;
}
/* display the progress every three seconds */
void
{
long now;
/* print after 2 seconds so the average is nearer 3 seconds */
}
}
/* match the pattern to the string */
match(void)
{
char *s;
/* see if this is a regular expression pattern */
if (*string == '\0') {
return (NO);
}
s = string;
s = strtolower(s);
}
}
/* it is a string pattern */
}
/* match the rest of the pattern to the name */
matchrest(void)
{
int i = 1;
skiprefchar();
do {
++blockp;
++i;
}
return (YES);
}
return (NO);
}
/* get the next posting for this term */
static POSTING *
getposting(void)
{
if (npostings-- <= 0) {
return (NULL);
}
/* display progress about every three seconds */
if (++searchcount % 100 == 0) {
progress("%ld of %ld possible references retrieved",
}
return (postingp++);
}
/* put the posting reference into the file */
static void
{
if (p->fcnoffset == 0) {
*function = '\0';
} else if (p->fcnoffset != lastfcnoffset) {
lastfcnoffset = p->fcnoffset;
}
}
}
}
/* put the reference into the file */
static void
{
/* put global references first */
if (*function == '\0') {
function = "<global>";
} else {
}
/* NOTREACHED */
}
}
/* put the source line into the file */
static void
{
if (fileversion <= 5) {
(void) scanpast(' ');
return;
}
/* scan back to the beginning of the source line */
/* read the previous block */
}
}
/* there must be a double newline followed by a line number */
putmsg("Internal error: cannot get source line from database");
myexit(1);
}
/* until a double newline is found */
do {
/* skip a symbol type */
if (*blockp == '\t') {
skiprefchar();
skiprefchar();
}
/* output a piece of the source line */
}
/* put the rest of the cross-reference line into the file */
static void
{
char *cp;
unsigned c;
setmark('\n');
do {
while ((c = *cp) != '\n') {
/* check for a compressed digraph */
if (c & 0200) {
c &= 0177;
} else if (c < ' ') {
/* a compressed keyword */
}
}
} else {
}
++cp;
}
}
/* put the rest of the cross-reference line into the string */
void
getstring(char *s)
{
char *cp;
unsigned c;
setmark('\n');
do {
while ((c = *cp) != '\n') {
if (c & 0200) {
c &= 0177;
*s++ = dichar1[c / 8];
*s++ = dichar2[c & 7];
} else {
*s++ = (char)c;
}
++cp;
}
*s = '\0';
}
/* scan past the next occurence of this character in the cross-reference */
char *
scanpast(int c)
{
char *cp;
setmark(c);
do { /* innermost loop optimized to only one test */
while (*cp != c) {
++cp;
}
skiprefchar(); /* skip the found character */
}
return (blockp);
}
/* read a block of the cross-reference */
char *
readblock(void)
{
/* read the next block */
/* add the search character and end-of-block mark */
/* return NULL on end-of-file */
if (blocklen == 0) {
} else {
++blocknumber;
}
return (blockp);
}
/* seek to the database offset */
long
{
long n;
int rc = 0;
myperror("Lseek failed");
(void) sleep(3);
return (rc);
}
(void) readblock();
blocknumber = n;
}
return (rc);
}
/* convert the string to lower case */
static char *
strtolower(char *s)
{
while (*s != '\0') {
/*
* note: s in not incremented in this line because the BSD
* compatibility tolower macro evaluates its argument twice
*/
++s;
}
*lp = '\0';
return (buf);
}
/* if needed, convert a relative path to a full path */
static char *
{
int i;
if (*file != '/') {
/* if same file as last time, return the same path */
return (path);
}
/* if requested, prepend a path to a relative file path */
if (prependpath != NULL) {
return (path);
}
/*
* if the database was built with a view path, return a
* full path so "cscope -d -f" does not have to be called
* from the build directory with the same view path
*/
if (dbvpndirs > 1) {
for (i = 0; i < dbvpndirs; i++) {
return (path);
}
}
}
}
return (file);
}