commit a56c9e681886408305863ace84e8a13929ca1456 Author: anon Date: Thu Jul 27 20:04:50 2023 +0200 bk diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b650193 --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +obj/*.o +obj/*.gch +csope +**/*.out +src/lex.yy.c diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..b08b553 --- /dev/null +++ b/Makefile @@ -0,0 +1,38 @@ +CC=gcc +CCFLAGS:=-ggdb #-D PRODUCTION +LDLIBS=-I ${CHDRD} $$(pkg-config --libs ncurses) +LEX:=flex + +LEXD:=src/ +LEXF:=$(shell find ${LEXD} -iname '*.l') +GENLEX:=$(subst .l,.c,${LEXF}) + +SRCD:=src/ +OBJD:=obj/ +SRC:=$(shell find ${SRCD} -iname '*.c') ${GENLEX} +OBJ:=$(subst .c,.o,$(subst ${SRCD},${OBJD},${SRC})) + +HDRD:=${SRCD} +CHDRD:=${OBJD} +HDR:=$(shell find ${HDRD} -iname '*.h') +CHDR:=$(addsuffix .gch,$(subst ${HDRD},${CHDRD},${HDR})) + +OUTPUT:=csope + +main: ${CHDR} ${OBJ} + ${LINK.c} ${LDLIBS} ${OBJ} -o ${OUTPUT} + +obj/%.o: src/%.c + ${COMPILE.c} $< -o $@ + +src/%.c: src/%.l + ${LEX} -o $@ $< + +obj/%.h.gch: src/%.h + ${CC} $< -o $@ + +clean: + -rm ${CHDR} + -rm ${GENLEX} + -rm ${OBJ} + -rm ${OUTPUT} diff --git a/README.md b/README.md new file mode 100644 index 0000000..0542b3b --- /dev/null +++ b/README.md @@ -0,0 +1,12 @@ +# Csope +Fork of Cscope, with various improvements, because cscope is good and shall not be forgotten. + +# Improvements/Changes +## User side ++ improved gui /*pending*/ ++ GNU Readline integration (ie. VI/EMACS mode, command history) /*pending*/ +## To the code ++ nuked autoconf, replaced with single Makefile ++ removed ++ encapsulated changes to the TUI into display.c ++ removed macros hell put in place to allow compiling on a dead badger diff --git a/TODO b/TODO new file mode 100644 index 0000000..ae2614f --- /dev/null +++ b/TODO @@ -0,0 +1,7 @@ +# BUGS ++ Changing text double frees: + free(): double free detected in tcache 2 + Aborted ++ Normalize tabs and spaces ++ Ordering function declarations in global.h by alpha order is not smart ++ Handle unused parameters gracefully (#define UNUSED(x) (void)(x)) diff --git a/ass/emacs.e b/ass/emacs.e new file mode 100644 index 0000000..ded6fbe --- /dev/null +++ b/ass/emacs.e @@ -0,0 +1,99 @@ +/ ========================================================================= / +/ 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 NOT 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 NOT 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. / +/ ========================================================================= / + +/ emacs menu for cscope / +((X) cscope (find current word [MACRO]) + (extern symbol-character) + + / if this character is not part of a symbol / + (cond ((not symbol-character) + + / if the previous character is not part of a symbol, go to + the next word / + back + (cond ((not symbol-character) forward-word back-word)) + )) + / get the current symbol (leave cursor at beginning of symbol) / + (while symbol-character forward) / go past last symbol character / + mark / mark this point / + back / back to last symbol character / + (while (cond (symbol-character (return back)))) / back fails at BOF / + (cond ((not symbol-character) forward)) / forward if not at BOF / + pickup-region / get the symbol / + (local-string symbol) + symbol= + + / if arg > 0 then display the menu / + (cond ((> arg 0) (display-menu + (format symbol "5 Find functions calling %l()") + (format symbol "4 Find functions called by %l()") + (format symbol "3 Find global definition of %l") + (format symbol "2 Find symbol %l") + "1 Call cscope" + 5) + )) + / get the selection / + (local selection) + (selection= (read-character "Selection?")) + + / if the selection is in range / + (cond ((&& (>= selection '1') (<= selection '5')) + + / if the selection requests finding the symbol / + (local-string findsymbol) + (findsymbol= "") + (cond ((>= selection '2') + (findsymbol= (format (char-to-string (- selection 2)) symbol "-%l '%l'")))) + + / if arg > 1 or < 0 then don't update the cross-reference database / + (local-string doption) + (doption= "") + (cond ((|| (> arg 1) (< arg 0)) (doption= "-d"))) + + / call cscope with usilent mode off / + (local oldmode) / save old usilent mode / + (oldmode= (set-mode "usilent" 0)) / turn off usilent mode / + (run-command (format doption findsymbol "cscope %l %l")) + (set-mode "usilent" oldmode) / restore usilent mode / + )) +) +/ see if the current character is part of a symbol / +(symbol-character () + (local c) + (c= current-character) + (return (cond ((&& (>= c 'a') (<= c 'z'))) + ((&& (>= c 'A') (<= c 'Z'))) + ((&& (>= c '0') (<= c '9'))) + ((== c '_')) + ) + ) +) diff --git a/ass/gmacs.ml b/ass/gmacs.ml new file mode 100644 index 0000000..ca4505a --- /dev/null +++ b/ass/gmacs.ml @@ -0,0 +1,63 @@ + ; =========================================================================== + ; 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 NOT 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 NOT 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.ml (s.cscope.ml) - 1.4 (2/21/84 14:53:58) + ; + ; Macro to handle invocation of gmacs by cscope from the + ; experimental tools. Cscope invokes gmacs with two arguments: + ; + ; gmacs +line file + ; + ; This macro gobbles the line number, visits the specified file, + ; and moves to the specified line number. + +(progn + args + pluses + (setq pluses 0) + (setq args (argc)) + (if (> args 1) + (progn + (if (= (string-to-char "+") (string-to-char (argv 1))) + (setq pluses 1) + ) + (setq args (- args 1)) + (while (> args pluses) + (visit-file (argv args)) + (setq args (- args 1)) + ) + (if (= (> (argc) 2) (> pluses 0)) + (goto-line (argv 1)) + ) + ) + ) +) diff --git a/ass/ylwrap b/ass/ylwrap new file mode 100755 index 0000000..d788f2d --- /dev/null +++ b/ass/ylwrap @@ -0,0 +1,247 @@ +#! /bin/sh +# ylwrap - wrapper for lex/yacc invocations. + +scriptversion=2016-01-11.22; # UTC + +# Copyright (C) 1996-2017 Free Software Foundation, Inc. +# +# Written by Tom Tromey . +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to or send patches to +# . + +get_dirname () +{ + case $1 in + */*|*\\*) printf '%s\n' "$1" | sed -e 's|\([\\/]\)[^\\/]*$|\1|';; + # Otherwise, we want the empty string (not "."). + esac +} + +# guard FILE +# ---------- +# The CPP macro used to guard inclusion of FILE. +guard () +{ + printf '%s\n' "$1" \ + | sed \ + -e 'y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/' \ + -e 's/[^ABCDEFGHIJKLMNOPQRSTUVWXYZ]/_/g' \ + -e 's/__*/_/g' +} + +# quote_for_sed [STRING] +# ---------------------- +# Return STRING (or stdin) quoted to be used as a sed pattern. +quote_for_sed () +{ + case $# in + 0) cat;; + 1) printf '%s\n' "$1";; + esac \ + | sed -e 's|[][\\.*]|\\&|g' +} + +case "$1" in + '') + echo "$0: No files given. Try '$0 --help' for more information." 1>&2 + exit 1 + ;; + --basedir) + basedir=$2 + shift 2 + ;; + -h|--h*) + cat <<\EOF +Usage: ylwrap [--help|--version] INPUT [OUTPUT DESIRED]... -- PROGRAM [ARGS]... + +Wrapper for lex/yacc invocations, renaming files as desired. + + INPUT is the input file + OUTPUT is one file PROG generates + DESIRED is the file we actually want instead of OUTPUT + PROGRAM is program to run + ARGS are passed to PROG + +Any number of OUTPUT,DESIRED pairs may be used. + +Report bugs to . +EOF + exit $? + ;; + -v|--v*) + echo "ylwrap $scriptversion" + exit $? + ;; +esac + + +# The input. +input=$1 +shift +# We'll later need for a correct munging of "#line" directives. +input_sub_rx=`get_dirname "$input" | quote_for_sed` +case $input in + [\\/]* | ?:[\\/]*) + # Absolute path; do nothing. + ;; + *) + # Relative path. Make it absolute. + input=`pwd`/$input + ;; +esac +input_rx=`get_dirname "$input" | quote_for_sed` + +# Since DOS filename conventions don't allow two dots, +# the DOS version of Bison writes out y_tab.c instead of y.tab.c +# and y_tab.h instead of y.tab.h. Test to see if this is the case. +y_tab_nodot=false +if test -f y_tab.c || test -f y_tab.h; then + y_tab_nodot=true +fi + +# The parser itself, the first file, is the destination of the .y.c +# rule in the Makefile. +parser=$1 + +# A sed program to s/FROM/TO/g for all the FROM/TO so that, for +# instance, we rename #include "y.tab.h" into #include "parse.h" +# during the conversion from y.tab.c to parse.c. +sed_fix_filenames= + +# Also rename header guards, as Bison 2.7 for instance uses its header +# guard in its implementation file. +sed_fix_header_guards= + +while test $# -ne 0; do + if test x"$1" = x"--"; then + shift + break + fi + from=$1 + # Handle y_tab.c and y_tab.h output by DOS + if $y_tab_nodot; then + case $from in + "y.tab.c") from=y_tab.c;; + "y.tab.h") from=y_tab.h;; + esac + fi + shift + to=$1 + shift + sed_fix_filenames="${sed_fix_filenames}s|"`quote_for_sed "$from"`"|$to|g;" + sed_fix_header_guards="${sed_fix_header_guards}s|"`guard "$from"`"|"`guard "$to"`"|g;" +done + +# The program to run. +prog=$1 +shift +# Make any relative path in $prog absolute. +case $prog in + [\\/]* | ?:[\\/]*) ;; + *[\\/]*) prog=`pwd`/$prog ;; +esac + +dirname=ylwrap$$ +do_exit="cd '`pwd`' && rm -rf $dirname > /dev/null 2>&1;"' (exit $ret); exit $ret' +trap "ret=129; $do_exit" 1 +trap "ret=130; $do_exit" 2 +trap "ret=141; $do_exit" 13 +trap "ret=143; $do_exit" 15 +mkdir $dirname || exit 1 + +cd $dirname + +case $# in + 0) "$prog" "$input" ;; + *) "$prog" "$@" "$input" ;; +esac +ret=$? + +if test $ret -eq 0; then + for from in * + do + to=`printf '%s\n' "$from" | sed "$sed_fix_filenames"` + if test -f "$from"; then + # If $2 is an absolute path name, then just use that, + # otherwise prepend '../'. + case $to in + [\\/]* | ?:[\\/]*) target=$to;; + *) target=../$to;; + esac + + # Do not overwrite unchanged header files to avoid useless + # recompilations. Always update the parser itself: it is the + # destination of the .y.c rule in the Makefile. Divert the + # output of all other files to a temporary file so we can + # compare them to existing versions. + if test $from != $parser; then + realtarget=$target + target=tmp-`printf '%s\n' "$target" | sed 's|.*[\\/]||g'` + fi + + # Munge "#line" or "#" directives. Don't let the resulting + # debug information point at an absolute srcdir. Use the real + # output file name, not yy.lex.c for instance. Adjust the + # include guards too. + sed -e "/^#/!b" \ + -e "s|$input_rx|$input_sub_rx|" \ + -e "$sed_fix_filenames" \ + -e "$sed_fix_header_guards" \ + "$from" >"$target" || ret=$? + + # Check whether files must be updated. + if test "$from" != "$parser"; then + if test -f "$realtarget" && cmp -s "$realtarget" "$target"; then + echo "$to is unchanged" + rm -f "$target" + else + echo "updating $to" + mv -f "$target" "$realtarget" + fi + fi + else + # A missing file is only an error for the parser. This is a + # blatant hack to let us support using "yacc -d". If -d is not + # specified, don't fail when the header file is "missing". + if test "$from" = "$parser"; then + ret=1 + fi + fi + done +fi + +# Remove the directory. +cd .. +rm -rf $dirname + +exit $ret + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: diff --git a/comp.sh b/comp.sh new file mode 100755 index 0000000..869038d --- /dev/null +++ b/comp.sh @@ -0,0 +1,4 @@ +#!/bin/bash + +make clean +make diff --git a/obj/.placeholder b/obj/.placeholder new file mode 100644 index 0000000..e69de29 diff --git a/src/basename.c b/src/basename.c new file mode 100644 index 0000000..94d8ffd --- /dev/null +++ b/src/basename.c @@ -0,0 +1,46 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +/* get a file's base name from its path name */ + +#include "global.h" + +char * +basename(char *path) +{ + char *s; + + if ((s = strrchr(path, '/')) != 0) { + return(s + 1); + } + return(path); +} diff --git a/src/build.c b/src/build.c new file mode 100644 index 0000000..0ff5049 --- /dev/null +++ b/src/build.c @@ -0,0 +1,738 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * main functions + */ + +#include "build.h" + +#include "global.h" /* FIXME: get rid of this! */ + +#include "library.h" + +#include "scanner.h" +#include "version.h" /* for FILEVERSION */ +#include "vp.h" + +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +#include +#else +#include +#endif + +/* Exported variables: */ + +BOOL buildonly = NO; /* only build the database */ +BOOL unconditional = NO; /* unconditionally build database */ +BOOL fileschanged; /* assume some files changed */ + +/* variable copies of the master strings... */ +char invname_buf[] = INVNAME; +char invpost_buf[] = INVPOST; +char reffile_buf[] = REFFILE; +char *invname = invname_buf; /* inverted index to the database */ +char *invpost = invpost_buf; /* inverted index postings */ +char *reffile = reffile_buf; /* cross-reference file path name */ + +char *newreffile; /* new cross-reference file name */ +FILE *newrefs; /* new cross-reference */ +FILE *postings; /* new inverted index postings */ +int symrefs = -1; /* cross-reference file */ + +INVCONTROL invcontrol; /* inverted file control structure */ + + +/* Local variables: */ +static char *newinvname; /* new inverted index file name */ +static char *newinvpost; /* new inverted index postings file name */ +static long traileroffset; /* file trailer offset */ + + +/* Internal prototypes: */ +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); + + +/* Error handling routine if inverted index creation fails */ +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); +} + + +/* see if the name list is the same in the cross-reference file */ +static BOOL +samelist(FILE *oldrefs, char **names, int count) +{ + char oldname[PATHLEN + 1]; /* name in old cross-reference */ + int oldcount; + int i; + + /* see if the number of names is the same */ + if (fscanf(oldrefs, "%d", &oldcount) != 1 || + oldcount != count) { + return(NO); + } + /* see if the name list is the same */ + for (i = 0; i < count; ++i) { + if ((1 != fscanf(oldrefs," %[^\n]",oldname)) || + strnotequal(oldname, names[i])) { + return(NO); + } + } + return(YES); +} + + +/* create the file name(s) used for a new cross-referene */ + +void setup_build_filenames(char *reffile) +{ + char *path; /* file pathname */ + char *s; /* pointer to basename in path */ + + path = malloc(strlen(reffile) + 10u); + strcpy(path, reffile); + s = basename(path); + *s = '\0'; + strcat(path, "n"); + ++s; + strcpy(s, basename(reffile)); + newreffile = strdup(path); + strcpy(s, basename(invname)); + newinvname = strdup(path); + strcpy(s, basename(invpost)); + newinvpost = strdup(path); + free(path); +} + +/* open the database */ + +void +opendatabase(void) +{ + if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) { + cannotopen(reffile); + myexit(1); + } + blocknumber = -1; /* force next seek to read the first block */ + + /* open any inverted index */ + if (invertedindex == YES && + invopen(&invcontrol, invname, invpost, INVAVAIL) == -1) { + askforreturn(); /* so user sees message */ + invertedindex = NO; + } +} + + +/* rebuild the database */ +void +rebuild(void) +{ + close(symrefs); + if (invertedindex == YES) { + invclose(&invcontrol); + nsrcoffset = 0; + npostings = 0; + } + build(); + opendatabase(); + + /* revert to the initial display */ + if (refsfound != NULL) { + fclose(refsfound); + refsfound = NULL; + } +} + + +/* build the cross-reference */ +void +build(void) +{ + unsigned long i; + FILE *oldrefs; /* old cross-reference file */ + time_t reftime; /* old crossref modification time */ + char *file; /* current file */ + char *oldfile; /* file in old cross-reference */ + char newdir[PATHLEN + 1]; /* directory in new cross-reference */ + char olddir[PATHLEN + 1]; /* directory in old cross-reference */ + char oldname[PATHLEN + 1]; /* name in old cross-reference */ + unsigned long oldnum; /* number in old cross-ref */ + struct stat statstruct; /* file status */ + unsigned long firstfile; /* first source file in pass */ + unsigned long lastfile; /* last source file in pass */ + int built = 0; /* built crossref for these files */ + int copied = 0; /* copied crossref for these files */ + unsigned long fileindex; /* source file name index */ + BOOL interactive = YES; /* output progress messages */ + + /* normalize the current directory relative to the home directory so + the cross-reference is not rebuilt when the user's login is moved */ + strcpy(newdir, currentdir); + if (strcmp(currentdir, home) == 0) { + strcpy(newdir, "$HOME"); + } else if (strncmp(currentdir, home, strlen(home)) == 0) { + snprintf(newdir, sizeof(newdir), "$HOME%s", currentdir + strlen(home)); + } + /* sort the source file names (needed for rebuilding) */ + qsort(srcfiles, nsrcfiles, sizeof(*srcfiles), compare); + + /* if there is an old cross-reference and its current directory matches */ + /* or this is an unconditional build */ + if ((oldrefs = vpfopen(reffile, "rb")) != NULL + && unconditional == NO + && fscanf(oldrefs, "cscope %d %" PATHLEN_STR "s", &fileversion, olddir) == 2 + && (strcmp(olddir, currentdir) == 0 /* remain compatible */ + || strcmp(olddir, newdir) == 0)) { + /* get the cross-reference file's modification time */ + fstat(fileno(oldrefs), &statstruct); + reftime = statstruct.st_mtime; + if (fileversion >= 8) { + BOOL oldcompress = YES; + BOOL oldinvertedindex = NO; + BOOL oldtruncate = NO; + int c; + + /* see if there are options in the database */ + for (;;) { + while((c = getc(oldrefs)) == ' ') + ; /* do nothing */ + if (c != '-') { + ungetc(c, oldrefs); + break; + } + switch (getc(oldrefs)) { + case 'c': /* ASCII characters only */ + oldcompress = NO; + break; + case 'q': /* quick search */ + oldinvertedindex = YES; + fscanf(oldrefs, "%ld", &totalterms); + break; + case 'T': /* truncate symbols to 8 characters */ + oldtruncate = YES; + break; + } + } + /* check the old and new option settings */ + 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; + } + /* seek to the trailer */ + if (fscanf(oldrefs, "%ld", &traileroffset) != 1 || + fseek(oldrefs, traileroffset, SEEK_SET) == -1) { + posterr("cscope: incorrect symbol database file format\n"); + goto force; + } + } + /* if assuming that some files have changed */ + if (fileschanged == YES) { + goto outofdate; + } + /* see if the directory lists are the same */ + if (samelist(oldrefs, srcdirs, nsrcdirs) == NO + || samelist(oldrefs, incdirs, nincdirs) == NO + /* get the old number of files */ + || fscanf(oldrefs, "%lu", &oldnum) != 1 + /* skip the string space size */ + || (fileversion >= 9 && fscanf(oldrefs, "%*s") != 0)) { + goto outofdate; + } + /* see if the list of source files is the same and + none have been changed up to the included files */ + for (i = 0; i < nsrcfiles; ++i) { + if ((1 != fscanf(oldrefs," %[^\n]",oldname)) + || strnotequal(oldname, srcfiles[i]) + || (lstat(srcfiles[i], &statstruct) != 0) + || (statstruct.st_mtime > reftime) + ) { + goto outofdate; + } + } + /* the old cross-reference is up-to-date */ + /* so get the list of included files */ + while (i++ < oldnum && fgets(oldname, sizeof(oldname), oldrefs)) { + addsrcfile(oldname); + } + fclose(oldrefs); + return; + + outofdate: + /* if the database format has changed, rebuild it all */ + if (fileversion != FILEVERSION) { + fprintf(stderr, "\ +cscope: converting to new symbol database file format\n"); + goto force; + } + /* reopen the old cross-reference file for fast scanning */ + if ((symrefs = vpopen(reffile, O_BINARY | O_RDONLY)) == -1) { + postfatal("cscope: cannot open file %s\n", reffile); + /* NOTREACHED */ + } + /* get the first file name in the old cross-reference */ + blocknumber = -1; + read_block(); /* read the first cross-ref block */ + scanpast('\t'); /* skip the header */ + oldfile = getoldfile(); + } else { /* force cross-referencing of all the source files */ + force: + reftime = 0; + oldfile = NULL; + } + /* open the new cross-reference file */ + if ((newrefs = myfopen(newreffile, "wb")) == NULL) { + postfatal("cscope: cannot open file %s\n", reffile); + /* NOTREACHED */ + } + 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; + } + /* output the leading tab expected by crossref() */ + dbputc('\t'); + + /* make passes through the source file list until the last level of + included files is processed */ + firstfile = 0; + lastfile = nsrcfiles; + if (invertedindex == YES) { + srcoffset = malloc((nsrcfiles + 1u) * sizeof(*srcoffset)); + } + for (;;) { + progress("Building symbol database", (long)built, + (long)lastfile); + if (linemode == NO) + refresh(); + + /* get the next source file name */ + for (fileindex = firstfile; fileindex < lastfile; ++fileindex) { + + /* display the progress about every three seconds */ + if (interactive == YES && fileindex % 10 == 0) { + progress("Building symbol database", fileindex, lastfile); + } + /* if the old file has been deleted get the next one */ + file = srcfiles[fileindex]; + while (oldfile != NULL && strcmp(file, oldfile) > 0) { + oldfile = getoldfile(); + } + /* if there isn't an old database or this is a new file */ + if (oldfile == NULL || strcmp(file, oldfile) < 0) { + crossref(file); + ++built; + } else if (lstat(file, &statstruct) == 0 + && statstruct.st_mtime > reftime) { + /* if this file was modified */ + crossref(file); + ++built; + + /* skip its old crossref so modifying the last source + * file does not cause all included files to be built. + * Unfortunately a new file that is alphabetically + * last will cause all included files to be build, but + * this is less likely */ + oldfile = getoldfile(); + } else { + /* copy its cross-reference */ + putfilename(file); + if (invertedindex == YES) { + copyinverted(); + } else { + copydata(); + } + ++copied; + oldfile = getoldfile(); + } + } + /* see if any included files were found */ + if (lastfile == nsrcfiles) { + break; + } + firstfile = lastfile; + lastfile = nsrcfiles; + if (invertedindex == YES) { + srcoffset = realloc(srcoffset, (nsrcfiles + 1) * sizeof(*srcoffset)); + } + /* sort the included file names */ + qsort(srcfiles + firstfile, lastfile - firstfile, sizeof(*srcfiles), compare); + } + /* add a null file name to the trailing tab */ + putfilename(""); + dbputc('\n'); + + /* get the file trailer offset */ + traileroffset = dboffset; + + /* output the source and include directory and file lists */ + putlist(srcdirs, nsrcdirs); + putlist(incdirs, nincdirs); + putlist(srcfiles, nsrcfiles); + if (fflush(newrefs) == EOF) { + /* rewind doesn't check for write failure */ + cannotwrite(newreffile); + /* NOTREACHED */ + } + + /* create the inverted index if requested */ + if (invertedindex == YES) { + char sortcommand[PATHLEN + 1]; + + if (fflush(postings) == EOF) { + cannotwrite(temp1); + /* NOTREACHED */ + } + fstat(fileno(postings), &statstruct); + fclose(postings); + snprintf(sortcommand, sizeof(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); + } + /* rewrite the header with the trailer offset and final option list */ + rewind(newrefs); + putheader(newdir); + fclose(newrefs); + + /* close the old database file */ + if (symrefs >= 0) { + close(symrefs); + } + if (oldrefs != NULL) { + fclose(oldrefs); + } + /* replace it with the new database file */ + movefile(newreffile, reffile); +} + + +/* string comparison function for qsort */ +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)); +} + + +/* seek to the trailer, in a given file */ +void +seek_to_trailer(FILE *f) +{ + if (fscanf(f, "%ld", &traileroffset) != 1) { + postfatal("cscope: cannot read trailer offset from file %s\n", reffile); + /* NOTREACHED */ + } + if (fseek(f, traileroffset, SEEK_SET) == -1) { + postfatal("cscope: cannot seek to trailer in file %s\n", reffile); + /* NOTREACHED */ + } +} + + +/* get the next file name in the old cross-reference */ +static char * +getoldfile(void) +{ + static char file[PATHLEN + 1]; /* file name in old crossref */ + + if (blockp != NULL) { + do { + if (*blockp == NEWFILE) { + skiprefchar(); + fetch_string_from_dbase(file, sizeof(file)); + if (file[0] != '\0') { /* if not end-of-crossref */ + return(file); + } + return(NULL); + } + } while (scanpast('\t') != NULL); + } + return(NULL); +} + + +/* Free all storage allocated for filenames: */ +void free_newbuildfiles(void) +{ + free(newinvname); + free(newinvpost); + free(newreffile); +} + + +/* output the cscope version, current directory, database format options, and + the database trailer offset */ +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 { + /* leave space so if the header is overwritten without -q + * because writing the inverted index failed, the header + * is the same length */ + 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 +} + + +/* put the name list into the cross-reference file */ +static void +putlist(char **names, int count) +{ + int i, size = 0; + + fprintf(newrefs, "%d\n", count); + if (names == srcfiles) { + + /* calculate the string space needed */ + 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); + /* NOTREACHED */ + } + } +} + + +/* copy this file's symbol data */ +static void +copydata(void) +{ + char *cp; + + setmark('\t'); + cp = blockp; + for (;;) { + /* copy up to the next \t */ + do { /* innermost loop optimized to only one test */ + while (*cp != '\t') { + dbputc(*cp++); + } + } while (*++cp == '\0' && (cp = read_block()) != NULL); + dbputc('\t'); /* copy the tab */ + + /* get the next character */ + /* HBB 2010-08-21: potential problem if above loop was left + * with cp==NULL */ + if (cp && (*(cp + 1) == '\0')) { + cp = read_block(); + } + /* exit if at the end of this file's data */ + if (cp == NULL || *cp == NEWFILE) { + break; + } + /* look for an #included file */ + if (*cp == INCLUDE) { + char symbol[PATLEN + 1]; + blockp = cp; + fetch_include_from_dbase(symbol, sizeof(symbol)); + writestring(symbol); + setmark('\t'); + cp = blockp; + } + } + blockp = cp; +} + +/* copy this file's symbol data and output the inverted index postings */ + +static void +copyinverted(void) +{ + char *cp; + char c; + int type; /* reference type (mark character) */ + char symbol[PATLEN + 1]; + + /* note: this code was expanded in-line for speed */ + /* while (scanpast('\n') != NULL) { */ + /* other macros were replaced by code using cp instead of blockp */ + cp = blockp; + for (;;) { + setmark('\n'); + do { /* innermost loop optimized to only one test */ + while (*cp != '\n') { + dbputc(*cp++); + } + } while (*++cp == '\0' && (cp = read_block()) != NULL); + dbputc('\n'); /* copy the newline */ + + /* get the next character */ + /* HBB 2010-08-21: potential problem if above loop was left + * with cp==NULL */ + if (cp && (*(cp + 1) == '\0')) { + cp = read_block(); + } + /* exit if at the end of this file's data */ + if (cp == NULL) { + break; + } + switch (*cp) { + case '\n': + lineoffset = dboffset + 1; + continue; + case '\t': + dbputc('\t'); + blockp = cp; + type = getrefchar(); + switch (type) { + case NEWFILE: /* file name */ + return; + case INCLUDE: /* #included file */ + 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) { /* digraph char? */ + c = dichar1[(c & 0177) / 8]; + } + /* if this is a symbol */ + 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; +} + + +/* replace the old file with the new file */ +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); + /* NOTREACHED */ + } +} + + +/* process the #included file in the old database */ +static void +fetch_include_from_dbase(char *s, size_t length) +{ + dbputc(INCLUDE); + skiprefchar(); + fetch_string_from_dbase(s, length); + incfile(s + 1, s); +} + diff --git a/src/build.h b/src/build.h new file mode 100644 index 0000000..83c9c7b --- /dev/null +++ b/src/build.h @@ -0,0 +1,70 @@ +/*=========================================================================== + Copyright (c) 2001, 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 NOT 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 NOT 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. + =========================================================================*/ + +#ifndef CSCOPE_BUILD_H +#define CSCOPE_BUILD_H + +#include "global.h" /* FIXME: temp. only */ +#include "invlib.h" + +/* types and macros of build.c to be used by other modules */ + +/* database output macros that update its offset */ +#define dbputc(c) (++dboffset, (void) putc(c, newrefs)) +#define dbfputs(s) (dboffset += strlen(s), fputs(s, newrefs)) + +/* declarations for globals defined in build.c */ + +extern BOOL buildonly; /* only build the database */ +extern BOOL unconditional; /* unconditionally build database */ +extern BOOL fileschanged; /* assume some files changed */ + +extern char *reffile; /* cross-reference file path name */ +extern char *invname; /* inverted index to the database */ +extern char *invpost; /* inverted index postings */ +extern char *newreffile; /* new cross-reference file name */ +extern FILE *newrefs; /* new cross-reference */ +extern FILE *postings; /* new inverted index postings */ +extern int symrefs; /* cross-reference file */ + +extern INVCONTROL invcontrol; /* inverted file control structure */ + +/* Prototypes of external functions defined by build.c */ + +void build(void); +void free_newbuildfiles(void); +void opendatabase(void); +void rebuild(void); +void setup_build_filenames(char *reffile); +void seek_to_trailer(FILE *f); + +#endif /* CSCOPE_BUILD_H */ diff --git a/src/command.c b/src/command.c new file mode 100644 index 0000000..920c36e --- /dev/null +++ b/src/command.c @@ -0,0 +1,934 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 or text cross-reference + * + * command functions + */ + +#include "global.h" +#include "build.h" /* for rebuild() */ + + +#include +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +#include +#else +#include +#endif +#include + +int selecting; /* whether the (upper) symbol list is being browsed */ +unsigned int curdispline = 0; + +BOOL caseless; /* ignore letter case when searching */ +BOOL *change; /* change this line */ +BOOL changing; /* changing text */ +char newpat[PATLEN + 1]; /* new pattern */ +/* HBB 20040430: renamed to avoid lots of clashes with function arguments + * also named 'pattern' */ +char Pattern[PATLEN + 1]; /* symbol or text pattern */ + +/* HBB FIXME 20060419: these should almost certainly be const */ +static char appendprompt[] = "Append to file: "; +static char pipeprompt[] = "Pipe to shell command: "; +static char readprompt[] = "Read from file: "; +static char toprompt[] = "To: "; + + +/* Internal prototypes: */ +static BOOL changestring(void); +static void clearprompt(void); +static void mark(unsigned int i); +static void scrollbar(MOUSE *p); + + +/* execute the command */ +BOOL +command(int commandc) +{ + char filename[PATHLEN + 1]; /* file path name */ + MOUSE *p; /* mouse data */ + int c, i; + FILE *file; + struct cmd *curritem, *item; /* command history */ + char *s; + + switch (commandc) { + case ctrl('C'): /* toggle caseless mode */ + if (caseless == NO) { + caseless = YES; + postmsg2("Caseless mode is now ON"); + } else { + caseless = NO; + postmsg2("Caseless mode is now OFF"); + } + egrepcaseless(caseless); /* turn on/off -i flag */ + return(NO); + + case ctrl('R'): /* rebuild the cross reference */ + if (isuptodate == YES) { + postmsg("The -d option prevents rebuilding the symbol database"); + return(NO); + } + exitcurses(); + freefilelist(); /* remake the source file list */ + makefilelist(); + rebuild(); + if (errorsfound == YES) { + errorsfound = NO; + askforreturn(); + } + entercurses(); + clearmsg(); /* clear any previous message */ + totallines = 0; + disprefs = 0; + topline = nextline = 1; + selecting = 0; + break; + +#if UNIXPC + case ESC: /* possible unixpc mouse selection */ +#endif + case ctrl('X'): /* mouse selection */ + if ((p = getmouseaction(DUMMYCHAR)) == NULL) { + return(NO); /* unknown control sequence */ + } + /* if the button number is a scrollbar tag */ + if (p->button == '0') { + scrollbar(p); + break; + } + /* ignore a sweep */ + if (p->x2 >= 0) { + return(NO); + } + /* if this is a line selection */ + if (p->y1 < FLDLINE) { + + /* find the selected line */ + /* note: the selection is forced into range */ + for (i = disprefs - 1; i > 0; --i) { + if (p->y1 >= displine[i]) { + break; + } + } + /* display it in the file with the editor */ + editref(i); + } else { /* this is an input field selection */ + field = p->y1 - FLDLINE; + /* force it into range */ + if (field >= FIELDS) { + field = FIELDS - 1; + } + setfield(); + resetcmd(); + return(NO); + } + break; + + case '\t': /* go to next input field */ + if (disprefs) { + selecting = !selecting; + if (selecting) { + move(displine[curdispline], 0); + refresh(); + } else { + atfield(); + resetcmd(); + } + } + return(NO); + +#ifdef KEY_ENTER + case KEY_ENTER: +#endif + case '\r': + case '\n': /* go to reference */ + if (selecting) { + editref(curdispline); + return(YES); + } + /* FALLTHROUGH */ + + case ctrl('N'): +#ifdef KEY_DOWN + case KEY_DOWN: +#endif +#ifdef KEY_RIGHT + case KEY_RIGHT: +#endif + if (selecting) { + if ((curdispline + 1) < disprefs) { + move(displine[++curdispline], 0); + refresh(); + } + } else { + field = (field + 1) % FIELDS; + setfield(); + atfield(); + resetcmd(); + } + return(NO); + + case ctrl('P'): /* go to previous input field */ +#ifdef KEY_UP + case KEY_UP: +#endif +#ifdef KEY_LEFT + case KEY_LEFT: +#endif + if (selecting) { + if (curdispline) { + move(displine[--curdispline], 0); + refresh(); + } + } else { + field = (field + (FIELDS - 1)) % FIELDS; + setfield(); + atfield(); + resetcmd(); + } + return(NO); +#ifdef KEY_HOME + case KEY_HOME: /* go to first input field */ + if (selecting) { + curdispline = 0; + move(REFLINE, 0); + refresh(); + } else { + field = 0; + setfield(); + atfield(); + resetcmd(); + } + return(NO); +#endif + +#ifdef KEY_LL + case KEY_LL: /* go to last input field */ + if (selecting) { + move(displine[disprefs - 1], 0); + refresh(); + } else { + field = FIELDS - 1; + setfield(); + atfield(); + resetcmd(); + } + return(NO); +#endif /* def(KEY_LL) */ + + case ' ': /* display next page */ + case '+': + case ctrl('V'): +#ifdef KEY_NPAGE + case KEY_NPAGE: +#endif + /* don't redisplay if there are no lines */ + if (totallines == 0) { + return(NO); + } + /* note: seekline() is not used to move to the next + * page because display() leaves the file pointer at + * the next page to optimize paging forward + */ + curdispline = 0; + break; + + case ctrl('H'): + case '-': /* display previous page */ +#ifdef KEY_PPAGE + case KEY_PPAGE: +#endif + /* don't redisplay if there are no lines */ + if (totallines == 0) { + return(NO); + } + + curdispline = 0; + + /* if there are only two pages, just go to the other one */ + if (totallines <= 2 * mdisprefs) { + break; + } + /* if on first page but not at beginning, go to beginning */ + nextline -= mdisprefs; /* already at next page */ + if (nextline > 1 && nextline <= mdisprefs) { + nextline = 1; + } else { + nextline -= mdisprefs; + if (nextline < 1) { + nextline = totallines - mdisprefs + 1; + if (nextline < 1) { + nextline = 1; + } + } + } + seekline(nextline); + break; + + case '>': /* write or append the lines to a file */ + if (totallines == 0) { + postmsg("There are no lines to write to a file"); + } else { /* get the file name */ + move(PRLINE, 0); + addstr("Write to file: "); + s = "w"; + if ((c = mygetch()) == '>') { + move(PRLINE, 0); + addstr(appendprompt); + c = '\0'; + s = "a"; + } + if (c != '\r' && + mygetline("", newpat, COLS - sizeof(appendprompt), c, NO) > 0) { + shellpath(filename, sizeof(filename), newpat); + if ((file = myfopen(filename, s)) == NULL) { + cannotopen(filename); + } else { + seekline(1); + while ((c = getc(refsfound)) != EOF) { + putc(c, file); + } + seekline(topline); + fclose(file); + } + } + clearprompt(); + } + return(NO); /* return to the previous field */ + + case '<': /* read lines from a file */ + move(PRLINE, 0); + addstr(readprompt); + if (mygetline("", newpat, COLS - sizeof(readprompt), '\0', NO) > 0) { + clearprompt(); + shellpath(filename, sizeof(filename), newpat); + if (readrefs(filename) == NO) { + postmsg2("Ignoring an empty file"); + return(NO); + } + return(YES); + } + clearprompt(); + return(NO); + + case '^': /* pipe the lines through a shell command */ + case '|': /* pipe the lines to a shell command */ + if (totallines == 0) { + postmsg("There are no lines to pipe to a shell command"); + return(NO); + } + /* get the shell command */ + move(PRLINE, 0); + addstr(pipeprompt); + if (mygetline("", newpat, COLS - sizeof(pipeprompt), '\0', NO) == 0) { + clearprompt(); + return(NO); + } + /* if the ^ command, redirect output to a temp file */ + if (commandc == '^') { + strcat(strcat(newpat, " >"), temp2); + /* HBB 20020708: somebody might have even + * their non-interactive default shells + * complain about clobbering + * redirections... --> delete before + * overwriting */ + remove(temp2); + } + exitcurses(); + if ((file = mypopen(newpat, "w")) == NULL) { + fprintf(stderr, "\ +cscope: cannot open pipe to shell command: %s\n", newpat); + } else { + seekline(1); + while ((c = getc(refsfound)) != EOF) { + putc(c, file); + } + seekline(topline); + mypclose(file); + } + if (commandc == '^') { + if (readrefs(temp2) == NO) { + postmsg("Ignoring empty output of ^ command"); + } + } + askforreturn(); + entercurses(); + break; +#if defined(KEY_RESIZE) && !defined(__DJGPP__) + case KEY_RESIZE: + /* XXX: fill in*/ + break; +#endif + case ctrl('L'): /* redraw screen */ +#ifdef KEY_CLEAR + case KEY_CLEAR: +#endif + clearmsg2(); + clearok(curscr, TRUE); + wrefresh(curscr); + drawscrollbar(topline, bottomline); + return(NO); + + case '!': /* shell escape */ + execute(shell, shell, NULL); + seekline(topline); + break; + + case '?': /* help */ + clear(); + help(); + clear(); + seekline(topline); + break; + + case ctrl('E'): /* edit all lines */ + editall(); + break; + + case ctrl('A'): /* HBB 20050428: added alt. keymapping */ + case ctrl('Y'): /* repeat last pattern */ + if (*Pattern != '\0') { + addstr(Pattern); + goto repeat; + } + break; + + case ctrl('B'): /* cmd history back */ + case ctrl('F'): /* cmd history fwd */ + if (selecting) { + selecting = 0; + } + + curritem = currentcmd(); + item = (commandc == ctrl('F')) ? nextcmd() : prevcmd(); + clearmsg2(); + if (curritem == item) { /* inform user that we're at history end */ + postmsg2("End of input field and search pattern history"); + } + if (item) { + field = item->field; + setfield(); + atfield(); + addstr(item->text); + strcpy(Pattern, item->text); + switch (c = mygetch()) { + case '\r': + case '\n': + goto repeat; + case ctrl('F'): + case ctrl('B'): + myungetch(c); + atfield(); + clrtoeol(); /* clear current field */ + break; + default: + myungetch(c); + if (mygetline(Pattern, newpat, COLS - fldcolumn - 1, '\0', caseless )) { + strcpy (Pattern, newpat); + resetcmd(); + } + goto repeat; + break; + } + } + return(NO); + + case '\\': /* next character is not a command */ + addch('\\'); /* display the quote character */ + + /* get a character from the terminal */ + if ((commandc = mygetch()) == EOF) { + return(NO); /* quit */ + } + addstr("\b \b"); /* erase the quote character */ + goto ispat; + + case '.': + postmsg("The . command has been replaced by ^Y"); + atfield(); /* move back to the input field */ + /* FALLTHROUGH */ + default: + if (selecting && !mouse) { + char *c; + + if ((c = strchr(dispchars, commandc))) + editref(c - dispchars); + + /* if this is the start of a pattern */ + } else if (isprint(commandc)) { + ispat: + if (mygetline("", newpat, COLS - fldcolumn - 1, + commandc, caseless) > 0) { + strcpy(Pattern, newpat); + resetcmd(); /* reset command history */ + repeat: + addcmd(field, Pattern); /* add to command history */ + if (field == CHANGE) { + /* prompt for the new text */ + move(PRLINE, 0); + addstr(toprompt); + mygetline("", newpat, + COLS - sizeof(toprompt), + '\0', NO); + } + /* search for the pattern */ + if (search() == YES) { + curdispline = 0; + ++selecting; + + switch (field) { + case DEFINITION: + case FILENAME: + if (totallines > 1) { + break; + } + topline = 1; + editref(0); + break; + case CHANGE: + return(changestring()); + } + + } else if (field == FILENAME && + access(newpat, READ) == 0) { + /* try to edit the file anyway */ + edit(newpat, "1"); + } + } else { /* no pattern--the input was erased */ + return(NO); + } + } else { /* control character */ + return(NO); + } + } /* switch(commandc) */ + return(YES); +} + +/* clear the prompt line */ + +static void +clearprompt(void) +{ + move(PRLINE, 0); + clrtoeol(); +} + +/* read references from a file */ + +BOOL +readrefs(char *filename) +{ + FILE *file; + int c; + + if ((file = myfopen(filename, "rb")) == NULL) { + cannotopen(filename); + return(NO); + } + if ((c = getc(file)) == EOF) { /* if file is empty */ + fclose(file); + return(NO); + } + totallines = 0; + disprefs = 0; + nextline = 1; + if (writerefsfound() == YES) { + putc(c, refsfound); + while ((c = getc(file)) != EOF) { + putc(c, refsfound); + } + fclose(file); + fclose(refsfound); + if ( (refsfound = myfopen(temp1, "rb")) == NULL) { + cannotopen(temp1); + return(NO); + } + countrefs(); + } else + fclose(file); + return(YES); +} + +/* change one text string to another */ + +static BOOL +changestring(void) +{ + char newfile[PATHLEN + 1]; /* new file name */ + char oldfile[PATHLEN + 1]; /* old file name */ + char linenum[NUMLEN + 1]; /* file line number */ + char msg[MSGLEN + 1]; /* message */ + FILE *script; /* shell script file */ + BOOL anymarked = NO; /* any line marked */ + MOUSE *p; /* mouse data */ + int c; + unsigned int i; + char *s; + + /* open the temporary file */ + if ((script = myfopen(temp2, "w")) == NULL) { + cannotopen(temp2); + return(NO); + } + /* create the line change indicators */ + change = calloc(totallines, sizeof(*change)); + changing = YES; + mousemenu(); + + /* until the quit command is entered */ + for (;;) { + /* display the current page of lines */ + display(); + same: + atchange(); + + /* get a character from the terminal */ + if ((c = mygetch()) == EOF || c == ctrl('D')) { + break; /* change lines */ + } + if (c == ctrl('Z')) { +#ifdef SIGTSTP + kill(0, SIGTSTP); + goto same; +#else + break; /* change lines */ +#endif + } + /* see if the input character is a command */ + switch (c) { + case ' ': /* display next page */ + case '+': + case ctrl('V'): +#ifdef KEY_NPAGE + case KEY_NPAGE: +#endif + case '-': /* display previous page */ +#ifdef KEY_PPAGE + case KEY_PPAGE: +#endif + case '!': /* shell escape */ + case '?': /* help */ + command(c); + break; + + case ctrl('L'): /* redraw screen */ +#ifdef KEY_CLEAR + case KEY_CLEAR: +#endif + command(c); + goto same; + + case ESC: /* don't change lines */ +#if UNIXPC + if((p = getmouseaction(DUMMYCHAR)) == NULL) { + goto nochange; /* unknown escape sequence */ + } + break; +#endif + case ctrl('G'): + goto nochange; + + case '*': /* mark/unmark all displayed lines */ + for (i = 0; topline + i < nextline; ++i) { + mark(i); + } + goto same; + + case ctrl('A'): /* mark/unmark all lines */ + for (i = 0; i < totallines; ++i) { + if (change[i] == NO) { + change[i] = YES; + } else { + change[i] = NO; + } + } + /* show that all have been marked */ + seekline(totallines); + break; + + case ctrl('X'): /* mouse selection */ + if ((p = getmouseaction(DUMMYCHAR)) == NULL) { + goto same; /* unknown control sequence */ + } + /* if the button number is a scrollbar tag */ + if (p->button == '0') { + scrollbar(p); + break; + } + /* find the selected line */ + /* note: the selection is forced into range */ + for (i = disprefs - 1; i > 0; --i) { + if (p->y1 >= displine[i]) { + break; + } + } + mark(i); + goto same; + + default: + { + /* if a line was selected */ + char *cc; + + if ((cc = strchr(dispchars, c))) + mark(cc - dispchars); + + goto same; + } /* default case */ + } /* switch(change code character) */ + } /* for(ever) */ + + /* for each line containing the old text */ + fprintf(script, "ed - <<\\!\n"); + *oldfile = '\0'; + seekline(1); + for (i = 0; + fscanf(refsfound, "%" PATHLEN_STR "s%*s%" NUMLEN_STR "s%*[^\n]", newfile, linenum) == 2; + ++i) { + /* see if the line is to be changed */ + if (change[i] == YES) { + anymarked = YES; + + /* if this is a new file */ + if (strcmp(newfile, oldfile) != 0) { + + /* make sure it can be changed */ + if (access(newfile, WRITE) != 0) { + snprintf(msg, sizeof(msg), "Cannot write to file %s", newfile); + postmsg(msg); + anymarked = NO; + break; + } + /* if there was an old file */ + if (*oldfile != '\0') { + fprintf(script, "w\n"); /* save it */ + } + /* edit the new file */ + strcpy(oldfile, newfile); + fprintf(script, "e %s\n", oldfile); + } + /* output substitute command */ + fprintf(script, "%ss/", linenum); /* change */ + for (s = Pattern; *s != '\0'; ++s) { + /* old text */ + if (strchr("/\\[.^*", *s) != NULL) { + putc('\\', script); + } + if (caseless == YES && isalpha((unsigned char)*s)) { + putc('[', script); + if(islower((unsigned char)*s)) { + putc(toupper((unsigned char)*s), script); + putc(*s, script); + } else { + putc(*s, script); + putc(tolower((unsigned char)*s), script); + } + putc(']', script); + } else + putc(*s, script); + } + putc('/', script); /* to */ + for (s = newpat; *s != '\0'; ++s) { /* new text */ + if (strchr("/\\&", *s) != NULL) { + putc('\\', script); + } + putc(*s, script); + } + fprintf(script, "/gp\n"); /* and print */ + } + } + fprintf(script, "w\nq\n!\n"); /* write and quit */ + fclose(script); + + /* if any line was marked */ + if (anymarked == YES) { + + /* edit the files */ + clearprompt(); + refresh(); + fprintf(stderr, "Changed lines:\n\r"); + execute("sh", "sh", temp2, NULL); + askforreturn(); + seekline(1); + } else { + nochange: + clearprompt(); + } + changing = NO; + mousemenu(); + fclose(script); + free(change); + return(anymarked); +} + + +/* mark/unmark this displayed line to be changed */ +static void +mark(unsigned int i) +{ + unsigned int j; + + j = i + topline - 1; + if (j < totallines) { + move(displine[i], 1); + + if (change[j] == NO) { + change[j] = YES; + addch('>'); + } else { + change[j] = NO; + addch(' '); + } + } +} + + +/* scrollbar actions */ +static void +scrollbar(MOUSE *p) +{ + /* reposition list if it makes sense */ + if (totallines == 0) { + return; + } + switch (p->percent) { + + case 101: /* scroll down one page */ + if (nextline + mdisprefs > totallines) { + nextline = totallines - mdisprefs + 1; + } + break; + + case 102: /* scroll up one page */ + nextline = topline - mdisprefs; + if (nextline < 1) { + nextline = 1; + } + break; + + case 103: /* scroll down one line */ + nextline = topline + 1; + break; + + case 104: /* scroll up one line */ + if (topline > 1) { + nextline = topline - 1; + } + break; + default: + nextline = p->percent * totallines / 100; + } + seekline(nextline); +} + + +/* count the references found */ +void +countrefs(void) +{ + char *subsystem; /* OGS subsystem name */ + char *book; /* OGS book name */ + char file[PATHLEN + 1]; /* file name */ + char function[PATLEN + 1]; /* function name */ + char linenum[NUMLEN + 1]; /* line number */ + int i; + + /* count the references found and find the length of the file, + function, and line number display fields */ + subsystemlen = 9; /* strlen("Subsystem") */ + booklen = 4; /* strlen("Book") */ + filelen = 4; /* strlen("File") */ + fcnlen = 8; /* strlen("Function") */ + numlen = 0; + /* HBB NOTE 2012-04-07: it may look like we shouldn't assing tempstring here, + * since it's not used. But it has to be assigned just so the return value + * of fscanf will actually reach 4. */ + while (EOF != (i = fscanf(refsfound, + "%" PATHLEN_STR "s%" PATLEN_STR "s%" NUMLEN_STR "s %" TEMPSTRING_LEN_STR "[^\n]", + file, function, linenum, tempstring + ) + ) + ) { + if ( (i != 4) + || !isgraph((unsigned char) *file) + || !isgraph((unsigned char) *function) + || !isdigit((unsigned char) *linenum) + ) { + postmsg("File does not have expected format"); + totallines = 0; + disprefs = 0; + return; + } + if ((i = strlen(pathcomponents(file, dispcomponents))) > filelen) { + filelen = i; + } + if (ogs == YES) { + ogsnames(file, &subsystem, &book); + if ((i = strlen(subsystem)) > subsystemlen) { + subsystemlen = i; + } + if ((i = strlen(book)) > booklen) { + booklen = i; + } + } + if ((i = strlen(function)) > fcnlen) { + fcnlen = i; + } + if ((i = strlen(linenum)) > numlen) { + numlen = i; + } + ++totallines; + } + rewind(refsfound); + + /* restrict the width of displayed columns */ + /* HBB FIXME 20060419: magic number alert! */ + i = (COLS - 5) / 3; + if (ogs == YES) { + i = (COLS - 7) / 5; + } + if (filelen > i && i > 4) { + filelen = i; + } + if (subsystemlen > i && i > 9) { + subsystemlen = i; + } + if (booklen > i && i > 4) { + booklen = i; + } + if (fcnlen > i && i > 8) { + fcnlen = i; + } +} diff --git a/src/compath.c b/src/compath.c new file mode 100644 index 0000000..037d341 --- /dev/null +++ b/src/compath.c @@ -0,0 +1,211 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +/* + * compath(pathname) + * + * This compresses pathnames. All strings of multiple slashes are + * changed to a single slash. All occurrences of "./" are removed. + * Whenever possible, strings of "/.." are removed together with + * the directory names that they follow. + * + * WARNING: since pathname is altered by this function, it should + * be located in a temporary buffer. This avoids the problem + * of accidently changing strings obtained from makefiles + * and stored in global structures. + */ + +#include "global.h" + +#ifndef NULL +#define NULL 0 +#endif + +char * +compath(char *pathname) /*FDEF*/ +{ + char *nextchar; + char *lastchar; + char *sofar; + char *pnend; + + int pnlen; + + /* + * do not change the path if it has no "/" + */ + + if (strchr(pathname, '/') == NULL) + return(pathname); + + /* + * find all strings consisting of more than one '/' + */ + + for (lastchar = pathname + 1; *lastchar != '\0'; lastchar++) + if ((*lastchar == '/') && (*(lastchar - 1) == '/')) + { + + /* + * find the character after the last slash + */ + + nextchar = lastchar; + while (*++lastchar == '/') + { + } + + /* + * eliminate the extra slashes by copying + * everything after the slashes over the slashes + */ + + sofar = nextchar; + while ((*nextchar++ = *lastchar++) != '\0') + ; + lastchar = sofar; + } + + /* + * find all strings of "./" + */ + + for (lastchar = pathname + 1; *lastchar != '\0'; lastchar++) + if ((*lastchar == '/') && (*(lastchar - 1) == '.') && + ((lastchar - 1 == pathname) || (*(lastchar - 2) == '/'))) + { + + /* + * copy everything after the "./" over the "./" + */ + + nextchar = lastchar - 1; + sofar = nextchar; + while ((*nextchar++ = *++lastchar) != '\0') + ; + lastchar = sofar; + } + + /* + * find each occurrence of "/.." + */ + + for (lastchar = pathname + 1; *lastchar != '\0'; lastchar++) + if ((lastchar != pathname) && (*lastchar == '/') && + (*(lastchar + 1) == '.') && (*(lastchar + 2) == '.') && + ((*(lastchar + 3) == '/') || (*(lastchar + 3) == '\0'))) + { + + /* + * find the directory name preceding the "/.." + */ + + nextchar = lastchar - 1; + while ((nextchar != pathname) && + (*(nextchar - 1) != '/')) + --nextchar; + + /* + * make sure the preceding directory's name + * is not "." or ".." + */ + + if ((*nextchar == '.') && + ((*(nextchar + 1) == '/') || + ((*(nextchar + 1) == '.') && (*(nextchar + 2) == '/')))) + /* EMPTY */; + else + { + + /* + * prepare to eliminate either + * "dir_name/../" or "dir_name/.." + */ + + if (*(lastchar + 3) == '/') + lastchar += 4; + else + lastchar += 3; + + /* + * copy everything after the "/.." to + * before the preceding directory name + */ + + sofar = nextchar - 1; + while ((*nextchar++ = *lastchar++) != '\0'); + + lastchar = sofar; + + /* + * if the character before what was taken + * out is '/', set up to check if the + * slash is part of "/.." + */ + + if ((sofar + 1 != pathname) && (*sofar == '/')) + --lastchar; + } + } + + /* + * if the string is more than a character long and ends + * in '/', eliminate the '/'. + */ + + pnlen = strlen(pathname); + pnend = strchr(pathname, '\0') - 1; + + if ((pnlen > 1) && (*pnend == '/')) + { + *pnend-- = '\0'; + pnlen--; + } + + /* + * if the string has more than two characters and ends in + * "/.", remove the "/.". + */ + + if ((pnlen > 2) && (*(pnend - 1) == '/') && (*pnend == '.')) + *--pnend = '\0'; + + /* + * if all characters were deleted, return "."; + * otherwise return pathname + */ + + if (*pathname == '\0') + (void) strcpy(pathname, "."); + + return(pathname); +} diff --git a/src/constants.h b/src/constants.h new file mode 100644 index 0000000..873a720 --- /dev/null +++ b/src/constants.h @@ -0,0 +1,133 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * preprocessor macro and constant definitions + */ + +#ifndef CSCOPE_CONSTANTS_H +#define CSCOPE_CONSTANTS_H + +#define ctrl(x) (x & 037) /* control character macro */ + +/* fast string equality tests (avoids most strcmp() calls) */ +#define strequal(s1, s2) (*(s1) == *(s2) && strcmp(s1, s2) == 0) +#define strnotequal(s1, s2) (*(s1) != *(s2) || strcmp(s1, s2) != 0) + +/* set the mark character for searching the cross-reference file */ +#define setmark(c) (blockmark = c, block[blocklen] = blockmark) + +/* get the next character in the cross-reference */ +/* note that blockp is assumed not to be null */ +#define getrefchar() (*(++blockp + 1) != '\0' ? *blockp : \ + (read_block() != NULL ? *blockp : '\0')) + +/* skip the next character in the cross-reference */ +/* note that blockp is assumed not to be null and that + this macro will always be in a statement by itself */ +#define skiprefchar() if (*(++blockp + 1) == '\0') (void) read_block() + +#define ESC '\033' /* escape character */ +#define DEL '\177' /* delete character */ +#define DUMMYCHAR ' ' /* use space as a dummy character */ +#define MSGLEN ((PATLEN) + 80) /* displayed message length */ +#define NUMLEN 10 /* line number length */ +#define PATHLEN 250 /* file pathname length */ +#define PATLEN 250 /* symbol pattern length */ +#define TEMPSTRING_LEN 8191 /* max strlen() of the global temp string */ +#define REFFILE "cscope.out" /* cross-reference output file */ +#define NAMEFILE "cscope.files" /* default list-of-files file */ +#define INVNAME "cscope.in.out" /* inverted index to the database */ +#define INVPOST "cscope.po.out" /* inverted index postings */ +#define INVNAME2 "cscope.out.in"/* follows correct naming convention */ +#define INVPOST2 "cscope.out.po"/* follows correct naming convention */ + +#define STMTMAX 10000 /* maximum source statement length */ + +#define STR2(x) #x +#define STRINGIZE(x) STR2(x) +#define PATLEN_STR STRINGIZE(PATLEN) +#define PATHLEN_STR STRINGIZE(PATHLEN) +#define NUMLEN_STR STRINGIZE(NUMLEN) +#define TEMPSTRING_LEN_STR STRINGIZE(TEMPSTRING_LEN) + +/* screen lines */ +#define FLDLINE (LINES - FIELDS - 1 - 1) /* first input field line */ +#define MSGLINE 0 /* message line */ +#define PRLINE (LINES - 1) /* input prompt line */ +#define REFLINE 3 /* first displayed reference line */ + +/* input fields (value matches field order on screen) */ +#define SYMBOL 0 +#define DEFINITION 1 +#define CALLEDBY 2 +#define CALLING 3 +#define STRING 4 +#define CHANGE 5 +#define REGEXP 6 +#define FILENAME 7 +#define INCLUDES 8 +#define FIELDS 10 + +#if (BSD || V9) && !__NetBSD__ && !__FreeBSD__ && !__APPLE__ +# define TERMINFO 0 /* no terminfo curses */ +#else +# define TERMINFO 1 +#endif + + +#if !TERMINFO +# ifndef KEY_BREAK +# define KEY_BREAK 0400 /* easier to define than to add #if around the use */ +# endif +# ifndef KEY_ENTER +# define KEY_ENTER 0401 +# endif +# ifndef KEY_BACKSPACE +# define KEY_BACKSPACE 0402 +# endif + +# if !sun +# define cbreak() crmode() /* name change */ +# endif + +# if UNIXPC +# define erasechar() (_tty.c_cc[VERASE]) /* equivalent */ +# define killchar() (_tty.c_cc[VKILL]) /* equivalent */ +# else +# define erasechar() (_tty.sg_erase) /* equivalent */ +# define killchar() (_tty.sg_kill) /* equivalent */ +# endif /* if UNIXPC */ +#endif /* if !TERMINFO */ + +#endif /* CSCOPE_CONSTANTS_H */ diff --git a/src/crossref.c b/src/crossref.c new file mode 100644 index 0000000..b10ed75 --- /dev/null +++ b/src/crossref.c @@ -0,0 +1,492 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 +#include + +/* 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 = YES; + return; + } + + entry_no = 0; + /* open the source file */ + if ((yyin = myfopen(srcfile, "r")) == NULL) { + cannotopen(srcfile); + errorsfound = YES; + 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 == YES && 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 == YES) { + 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 = NO; + for (i = 0; i < my_yyleng; ++i) { + + /* change a tab to a blank and compress blanks */ + if ((c = my_yytext[i]) == ' ' || c == '\t') { + blank = YES; + } else if (symput < symbols && i == symbol[symput].first) { + /* look for the start of a symbol */ + + /* check for compressed blanks */ + if (blank == YES) { + blank = NO; + 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 == YES) { + 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 == NO) { + if (blank == YES) { + dbputc(' '); + blank = NO; + } + 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 == YES) { + 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 = NO; + + /* 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 == NO) { + /* 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, "cscope: \"%s\", line %d: warning: %s\n", filename, + myylineno, text); + errorsfound = YES; +} diff --git a/src/dir.c b/src/dir.c new file mode 100644 index 0000000..7688dea --- /dev/null +++ b/src/dir.c @@ -0,0 +1,747 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * directory searching functions + */ + +#include "global.h" + +#include "vp.h" /* vpdirs and vpndirs */ + +#include +#include /* needed by stat.h and dirent.h */ +#include +#include /* stat */ +#include + +#define DIRSEPS " ,:" /* directory list separators */ +#define DIRINC 10 /* directory list size increment */ +#define HASHMOD 2003 /* must be a prime number */ +#define SRCINC HASHMOD /* source file list size increment */ + /* largest known database had 22049 files */ + +char currentdir[PATHLEN + 1];/* current directory */ +char **incdirs; /* #include directories */ +char **srcdirs; /* source directories */ +char **srcfiles; /* source files */ +unsigned long nincdirs; /* number of #include directories */ +unsigned long nsrcdirs; /* number of source directories */ +unsigned long nsrcfiles; /* number of source files */ +unsigned long msrcfiles = SRCINC; /* maximum number of source files */ + +static char **incnames; /* #include directory names without view pathing */ +static unsigned long mincdirs = DIRINC; /* maximum number of #include directories */ +static unsigned long msrcdirs; /* maximum number of source directories */ +static unsigned long nvpsrcdirs; /* number of view path source directories */ + +static struct listitem { /* source file names without view pathing */ + char *text; + struct listitem *next; +} *srcnames[HASHMOD]; + +/* Internal prototypes: */ +static BOOL accessible_file(char *file); +static BOOL issrcfile(char *file); +static void addsrcdir(char *dir); +static void addincdir(char *name, char *path); +static void scan_dir(const char *dirfile, BOOL recurse); +static void makevpsrcdirs(void); + + +/* make the view source directory list */ + +static void +makevpsrcdirs(void) +{ + int i; + + /* return if this function has already been called */ + if (nsrcdirs > 0) { + return; + } + /* get the current directory name */ + if (getcwd(currentdir, PATHLEN) == NULL) { + fprintf(stderr, "cscope: warning: cannot get current directory name\n"); + strcpy(currentdir, ""); + } + /* see if there is a view path and this directory is in it */ + vpinit(currentdir); + if (vpndirs > 1) { + nsrcdirs = vpndirs; + } else { + nsrcdirs = 1; + } + /* create the source directory list */ + msrcdirs = nsrcdirs + DIRINC; + srcdirs = malloc(msrcdirs * sizeof(*srcdirs)); + *srcdirs = "."; /* first source dir is always current dir */ + for (i = 1; i < vpndirs; ++i) { + srcdirs[i] = vpdirs[i]; + } + /* save the number of original source directories in the view path */ + nvpsrcdirs = nsrcdirs; +} + +/* add a source directory to the list for each view path source directory */ + +void +sourcedir(char *dirlist) +{ + char path[PATHLEN + 1]; + char *dir; + unsigned int i; + + makevpsrcdirs(); /* make the view source directory list */ + dirlist = strdup(dirlist); /* don't change environment variable text */ + + /* parse the directory list */ + dir = strtok(dirlist, DIRSEPS); + while (dir != NULL) { + int dir_len = strlen(dir); + + addsrcdir(dir); + + /* if it isn't a full path name and there is a + multi-directory view path */ + if (*dirlist != '/' && vpndirs > 1) { + + /* compute its path from higher view path source dirs */ + for (i = 1; i < nvpsrcdirs; ++i) { + snprintf(path, sizeof(path), "%.*s/%s", + PATHLEN - 2 - dir_len, + srcdirs[i], dir); + addsrcdir(path); + } + } + dir = strtok(NULL, DIRSEPS); + } + free(dirlist); /* HBB 20000421: avoid memory leaks */ +} + +/* add a source directory to the list */ + +static void +addsrcdir(char *dir) +{ + struct stat statstruct; + + /* make sure it is a directory */ + if (lstat(compath(dir), &statstruct) == 0 && + S_ISDIR(statstruct.st_mode)) { + + /* note: there already is a source directory list */ + if (nsrcdirs == msrcdirs) { + msrcdirs += DIRINC; + srcdirs = realloc(srcdirs, msrcdirs * sizeof(*srcdirs)); + } + srcdirs[nsrcdirs++] = strdup(dir); + } +} + +/* HBB 20000421: new function, for avoiding leaks */ +/* free list of src directories */ +void +freesrclist() +{ + if (!srcdirs) + return; + while(nsrcdirs>1) + free(srcdirs[--nsrcdirs]); + free(srcdirs); +} + +/* add a #include directory to the list for each view path source directory */ + +void +includedir(char *dirlist) +{ + char path[PATHLEN + 1]; + char *dir; + unsigned int i; + + makevpsrcdirs(); /* make the view source directory list */ + dirlist = strdup(dirlist); /* don't change environment variable text */ + + /* parse the directory list */ + dir = strtok(dirlist, DIRSEPS); + while (dir != NULL) { + size_t dir_len = strlen(dir); + + addincdir(dir, dir); + + /* if it isn't a full path name and there is a + multi-directory view path */ + if (*dirlist != '/' && vpndirs > 1) { + + /* compute its path from higher view path source dirs */ + for (i = 1; i < nvpsrcdirs; ++i) { + snprintf(path, sizeof(path), "%.*s/%s", + (int)(PATHLEN - 2 - dir_len), + srcdirs[i], dir); + addincdir(dir, path); + } + } + dir = strtok(NULL, DIRSEPS); + } + free(dirlist); /* HBB 20000421: avoid leaks */ +} + +/* add a #include directory to the list */ + +static void +addincdir(char *name, char *path) +{ + struct stat statstruct; + + /* make sure it is a directory */ + if (lstat(compath(path), &statstruct) == 0 && + S_ISDIR(statstruct.st_mode)) { + if (incdirs == NULL) { + incdirs = malloc(mincdirs * sizeof(*incdirs)); + incnames = malloc(mincdirs * sizeof(*incnames)); + } else if (nincdirs == mincdirs) { + mincdirs += DIRINC; + incdirs = realloc(incdirs, mincdirs * sizeof(*incdirs)); + incnames = realloc(incnames, mincdirs * sizeof(*incnames)); + } + incdirs[nincdirs] = strdup(path); + incnames[nincdirs++] = strdup(name); + } +} + +/* HBB 2000421: new function, for avoiding memory leaks */ +/* free the list of include files, if wanted */ + +void +freeinclist() +{ + if (!incdirs) + return; + while(nincdirs>0) { + free(incdirs[--nincdirs]); + free(incnames[nincdirs]); + } + free(incdirs); + free(incnames); +} + +/* make the source file list */ + +void +makefilelist(void) +{ + static BOOL firstbuild = YES; /* first time through */ + FILE *names; /* name file pointer */ + char dir[PATHLEN + 1]; + char path[PATHLEN + 1]; + char line[PATHLEN * 10]; + char *file; + char *s; + unsigned int i; + + makevpsrcdirs(); /* make the view source directory list */ + + /* if -i was NOT given and there are source file arguments */ + if (namefile == NULL && fileargc > 0) { + + /* put them in a list that can be expanded */ + for (i = 0; i < fileargc; ++i) { + file = fileargv[i]; + if (infilelist(file) == NO) { + if ((s = inviewpath(file)) != NULL) { + addsrcfile(s); + } else { + fprintf(stderr, "cscope: cannot find file %s\n", + file); + errorsfound = YES; + } + } + } + return; + } + + /* see if a file name file exists */ + if (namefile == NULL && vpaccess(NAMEFILE, READ) == 0) { + namefile = NAMEFILE; + } + + if (namefile == NULL) { + /* No namefile --> make a list of all the source files + * in the directories */ + for (i = 0; i < nsrcdirs; ++i) { + scan_dir(srcdirs[i], recurse_dir); + } + return; + } + + /* Came here --> there is a file of source file names */ + + if (strcmp(namefile, "-") == 0) + names = stdin; + else if ((names = vpfopen(namefile, "r")) == NULL) { + cannotopen(namefile); + myexit(1); + } + + /* get the names in the file */ + while (fgets(line, 10*PATHLEN, names) != NULL) { + char *point_in_line = line + (strlen(line) - 1); + size_t length_of_name = 0; + int unfinished_option = 0; + BOOL done = NO; + + /* Kill away \n left at end of fgets()'d string: */ + if (*point_in_line == '\n') + *point_in_line = '\0'; + + /* Parse whitespace-terminated strings in line: */ + point_in_line = line; + while (sscanf(point_in_line, "%" PATHLEN_STR "s", path) == 1) { + /* Have to store this length --- inviewpath() will + * modify path, later! */ + length_of_name = strlen(path); + + if (*path == '-') { /* if an option */ + if (unfinished_option) { + /* Can't have another option directly after an + * -I or -p option with no name after it! */ + fprintf(stderr, "\ +cscope: Syntax error in namelist file %s: unfinished -I or -p option\n", + namefile); + unfinished_option = 0; + } + + i = path[1]; + switch (i) { + case 'c': /* ASCII characters only in crossref */ + compress = NO; + break; + case 'k': /* ignore DFLT_INCDIR */ + kernelmode = YES; + break; + case 'q': /* quick search */ + invertedindex = YES; + break; + case 'T': /* truncate symbols to 8 characters */ + trun_syms = YES; + break; + case 'I': /* #include file directory */ + case 'p': /* file path components to display */ + /* coverity[overwrite_var] */ + s = path + 2; /* for "-Ipath" */ + if (*s == '\0') { /* if "-I path" */ + unfinished_option = i; + break; + } + + /* this code block used several times in here + * --> make it a macro to avoid unnecessary + * duplication */ +#define HANDLE_OPTION_ARGUMENT(i, s) \ + switch (i) { \ + case 'I': /* #include file directory */ \ + if (firstbuild == YES) { \ + /* expand $ and ~ */ \ + shellpath(dir, sizeof(dir), (s)); \ + includedir(dir); \ + } \ + unfinished_option = 0; \ + done = YES; \ + break; \ + case 'p': /* file path components to display */ \ + if (*(s) < '0' || *(s) > '9') { \ + fprintf(stderr, \ +"cscope: -p option in file %s: missing or invalid numeric value\n", \ + namefile); \ + } \ + dispcomponents = atoi(s); \ + unfinished_option = 0; \ + done = YES; \ + break; \ + default: \ + done = NO; \ + } /* switch(i) */ + + /* ... and now call it for the first time */ + HANDLE_OPTION_ARGUMENT(i, s) + break; + default: + fprintf(stderr, "cscope: only -I, -c, -k, -p, and -T options can be in file %s\n", + namefile); + } /* switch(i) */ + } /* if('-') */ + else if (*path == '"') { + /* handle quoted filenames... */ + size_t in = 1, out = 0; + char *newpath = malloc(PATHLEN + 1); + + while (in < PATHLEN && point_in_line[in] != '\0') { + if (point_in_line[in] == '"') { + newpath[out] = '\0'; + /* Tell outer loop to skip over this entire + * quoted string */ + length_of_name = in + 1; + break; /* found end of quoted string */ + } else if (point_in_line[in] == '\\' + && in < PATHLEN - 1 + && (point_in_line[in + 1]== '"' + || point_in_line[in + 1] == '\\')) { + /* un-escape \" or \\ sequence */ + newpath[out++] = point_in_line[in + 1]; + in += 2; + } else { + newpath[out++] = point_in_line[in++]; + } + } /* while(in) */ + if (in >= PATHLEN) { /* safeguard against almost-overflow */ + newpath[out]='\0'; + } + + /* If an -I or -p arguments was missing before, + * treat this name as the argument: */ + HANDLE_OPTION_ARGUMENT(unfinished_option, newpath); + if (! done) { + /* coverity[overwrite_var] */ + if ((s = inviewpath(newpath)) != NULL) { + addsrcfile(s); + } else { + fprintf(stderr, "cscope: cannot find file %s\n", + newpath); + errorsfound = YES; + } + } + free(newpath); + } /* if(quoted name) */ + else { + /* ... so this is an ordinary file name, unquoted */ + + /* If an -I or -p arguments was missing before, + * treat this name as the argument: */ + HANDLE_OPTION_ARGUMENT(unfinished_option, path); + if (!done) { + if ((s = inviewpath(path)) != NULL) { + addsrcfile(s); + } else { + fprintf(stderr, "cscope: cannot find file %s\n", + path); + errorsfound = YES; + } + } + } /* else(ordinary name) */ + + point_in_line += length_of_name; + while (isspace((unsigned char) *point_in_line)) + point_in_line ++; + } /* while(sscanf(line)) */ + } /* while(fgets(line)) */ + + if (names == stdin) + clearerr(stdin); + else + fclose(names); + firstbuild = NO; + return; + +} + +/* scan a directory (recursively?) for source files */ +static void +scan_dir(const char *adir, BOOL recurse_dir) +{ + DIR *dirfile; + int adir_len = strlen(adir); + + /* FIXME: no guards against adir_len > PATHLEN, yet */ + + if ((dirfile = opendir(adir)) != NULL) { + struct dirent *entry; + char path[PATHLEN + 1]; + + while ((entry = readdir(dirfile)) != NULL) { + if ((strcmp(".",entry->d_name) != 0) + && (strcmp("..",entry->d_name) != 0)) { + struct stat buf; + + snprintf(path, sizeof(path), "%s/%.*s", adir, + PATHLEN - 2 - adir_len, + entry->d_name); + + if (lstat(path,&buf) == 0) { + if (recurse_dir + && S_ISDIR(buf.st_mode) ) { + scan_dir(path, recurse_dir); + } else if (issrcfile(path) + && infilelist(path) == NO + && access(path, R_OK) == 0) { + addsrcfile(path); + } + } + } + } + closedir(dirfile); + } + return; +} + + +/* see if this is a source file */ +static BOOL +issrcfile(char *path) +{ + struct stat statstruct; + char *file = basename(path); + char *s = strrchr(file, '.'); + BOOL looks_like_source = NO; + + /* ensure there is some file suffix */ + if (s == NULL || *++s == '\0') + return NO; + + /* if an SCCS or versioned file */ + if (file[1] == '.' && file + 2 != s) { /* 1 character prefix */ + switch (*file) { + case 's': + case 'S': + return(NO); + } + } + + if (s[1] == '\0') { /* 1 character suffix */ + switch (*s) { + case 'c': + case 'h': + case 'l': + case 'y': + case 'C': + case 'G': + case 'H': + case 'L': + looks_like_source = YES; + } + } else if ((s[2] == '\0') /* 2 char suffix */ + && ((s[0] == 'b' && s[1] == 'p') /* breakpoint listing */ + || (s[0] == 'q' + && (s[1] == 'c' || s[1] == 'h')) /* Ingres */ + || (s[0] == 's' && s[1] == 'd') /* SDL */ + || (s[0] == 'c' && s[1] == 'c') /* C++ source */ + || (s[0] == 'h' && s[1] == 'h'))) { /* C++ header */ + looks_like_source = YES; + + } else if((s[3] == '\0') /* 3 char suffix */ + /* C++ template source */ + && ((s[0] == 't' && s[1] == 'c' && s[2] == 'c' ) + /* C++ source: */ + || (s[0] == 'c' && s[1] == 'p' && s[2] == 'p' ) + || (s[0] == 'c' && s[1] == 'x' && s[2] == 'x' ) + || (s[0] == 'h' && s[1] == 'p' && s[2] == 'p' ) + || (s[0] == 'h' && s[1] == 'x' && s[2] == 'x' )) + ) { + looks_like_source = YES; + } + + if (looks_like_source != YES) + return NO; + + /* make sure it is a file */ + if (lstat(path, &statstruct) == 0 && + S_ISREG(statstruct.st_mode)) { + return(YES); + } + return NO; +} + + +/* add an include file to the source file list */ +void +incfile(char *file, char *type) +{ + char name[PATHLEN + 1]; + char path[PATHLEN + 1]; + char *s; + unsigned int i; + + assert(file != NULL); /* should never happen, but let's make sure anyway */ + /* see if the file is already in the source file list */ + if (infilelist(file) == YES) { + return; + } + /* look in current directory if it was #include "file" */ + if (type[0] == '"' && (s = inviewpath(file)) != NULL) { + addsrcfile(s); + } else { + size_t file_len = strlen(file); + + /* search for the file in the #include directory list */ + for (i = 0; i < nincdirs; ++i) { + /* don't include the file from two directories */ + snprintf(name, sizeof(name), "%.*s/%s", + (int)(PATHLEN - 2 - file_len), incnames[i], + file); + if (infilelist(name) == YES) { + break; + } + /* make sure it exists and is readable */ + snprintf(path, sizeof(path), "%.*s/%s", + (int)(PATHLEN - 2 - file_len), incdirs[i], + file); + if (access(compath(path), READ) == 0) { + addsrcfile(path); + break; + } + } + } +} + + +/* see if the file is already in the list */ +BOOL +infilelist(char *path) +{ + struct listitem *p; + + for (p = srcnames[hash(compath(path)) % HASHMOD]; + p != NULL; + p = p->next) { + if (strequal(path, p->text)) { + return(YES); + } + } + return(NO); +} + + +/* check if a file is readable enough to be allowed in the + * database */ +static BOOL +accessible_file(char *file) +{ + if (access(compath(file), READ) == 0) { + struct stat stats; + + if (lstat(file, &stats) == 0 + && S_ISREG(stats.st_mode)) { + return YES; + } + } + return NO; +} + +/* search for the file in the view path */ +char * +inviewpath(char *file) +{ + static char path[PATHLEN + 1]; + unsigned int i; + + /* look for the file */ + if (accessible_file(file)) { + return(file); + } + + /* if it isn't a full path name and there is a multi-directory + * view path */ + if (*file != '/' && vpndirs > 1) { + int file_len = strlen(file); + + /* compute its path from higher view path source dirs */ + for (i = 1; i < nvpsrcdirs; ++i) { + snprintf(path, sizeof(path), "%.*s/%s", + PATHLEN - 2 - file_len, srcdirs[i], + file); + if (accessible_file(path)) { + return(path); + } + } + } + return(NULL); +} + +/* add a source file to the list */ + +void +addsrcfile(char *path) +{ + struct listitem *p; + int i; + + /* make sure there is room for the file */ + if (nsrcfiles == msrcfiles) { + msrcfiles += SRCINC; + srcfiles = realloc(srcfiles, msrcfiles * sizeof(*srcfiles)); + } + /* add the file to the list */ + srcfiles[nsrcfiles++] = strdup(compath(path)); + p = malloc(sizeof(*p)); + p->text = strdup(compath(path)); + i = hash(p->text) % HASHMOD; + p->next = srcnames[i]; + srcnames[i] = p; +} + +/* free the memory allocated for the source file list */ + +void +freefilelist(void) +{ + struct listitem *p, *nextp; + int i; + + /* if '-d' option is used a string space block is allocated */ + if (isuptodate == NO) { + while (nsrcfiles > 0) { + free (srcfiles[--nsrcfiles]); + } + } else { + /* for '-d' option free the string space block */ + /* protect against empty list */ + if (nsrcfiles > 0) + free (srcfiles[0]); + nsrcfiles = 0; + } + + free (srcfiles); /* HBB 20000421: avoid leak */ + msrcfiles = 0; + srcfiles=0; + + for (i = 0; i < HASHMOD; ++i) { + for (p = srcnames[i]; p != NULL; p = nextp) { + /* HBB 20000421: avoid memory leak */ + free(p->text); + nextp = p->next; + free(p); + } + srcnames[i] = NULL; + } +} diff --git a/src/display.c b/src/display.c new file mode 100644 index 0000000..b28fdef --- /dev/null +++ b/src/display.c @@ -0,0 +1,784 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * display functions + */ + +#include "global.h" +#include "build.h" + +#ifdef CCS +#include "sgs.h" /* ESG_PKG and ESG_REL */ +#else +#include "version.h" /* FILEVERSION and FIXVERSION */ +#endif + +#include +#include /* jmp_buf */ +#include /* va_list stuff */ +#include +#include +#include + +int booklen; /* OGS book name display field length */ +int *displine; /* screen line of displayed reference */ +unsigned int disprefs; /* displayed references */ +int field; /* input field */ +int filelen; /* file name display field length */ +int fcnlen; /* function name display field length */ +unsigned int mdisprefs; /* maximum displayed references */ +unsigned int nextline; /* next line to be shown */ +FILE *nonglobalrefs; /* non-global references file */ +int numlen; /* line number display field length */ +unsigned int topline = 1; /* top line of page */ +int bottomline; /* bottom line of page */ +long searchcount; /* count of files searched */ +int subsystemlen; /* OGS subsystem name display field length */ +unsigned int totallines; /* total reference lines */ +unsigned fldcolumn; /* input field column */ +WINDOW* body; +WINDOW* input_fields; + +static enum { + CH_BODY = 0x0001, + CH_INPUT_FIELDS = CH_BODY << 1, + CH_COMMAND_FIELD = CH_BODY << 2, + CH_ALL = CH_BODY | CH_INPUT_FIELDS | CH_COMMAND_FIELD +}; + +const char dispchars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + +static int fldline; /* input field line */ +static sigjmp_buf env; /* setjmp/longjmp buffer */ +static int lastdispline; /* last displayed reference line */ +static char lastmsg[MSGLEN + 1]; /* last message displayed */ +static const char helpstring[] = "Press the ? key for help"; +static const char selprompt[] = + "Select lines to change (press the ? key for help): "; + +typedef char * (*FP)(char *); /* pointer to function returning a character pointer */ + +/* HBB 2000/05/05: I removed the casts to function pointer type. It is + * fundamentally unsafe to call a function through a pointer of a + * different type ('undefined behaviour' in the words of the ANSI/ISO + * C standard). Instead, I made all the find...() functions adhere to + * the same function type, by changing argument passing a bit. */ +static struct { /* text of input fields */ + char *text1; + char *text2; + FP findfcn; +} fields[FIELDS + 1] = { /* samuel has a search that is not part of the cscope display */ + {"Find this", "C symbol", findsymbol}, + {"Find this", "global definition", finddef}, + {"Find", "functions called by this function", findcalledby}, + {"Find", "functions calling this function", findcalling}, + {"Find this", "text string", findstring}, + {"Change this", "text string", findstring}, + {"Find this", "egrep pattern", findregexp}, + {"Find this", "file", findfile}, + {"Find", "files #including this file", findinclude}, + {"Find", "assignments to this symbol", findassign}, + {"Find all", "function definitions", findallfcns}, /* samuel only */ +}; + +/* Internal prototypes: */ +static void jumpback(int sig); + +/* initialize display parameters */ + +void +dispinit(void) +{ + /* initialize the curses display package */ + initscr(); /* initialize the screen */ + entercurses(); + keypad(stdscr, TRUE); /* enable the keypad */ + //fixkeypad(); /* fix for getch() intermittently returning garbage */ + standend(); /* turn off reverse video */ + + /* calculate the maximum displayed reference lines */ + lastdispline = FLDLINE - 3; + mdisprefs = lastdispline - REFLINE + 1; + + + if (mdisprefs <= 0) { + postfatal("%s: screen too small\n", argv0); + /* NOTREACHED */ + } + + if (mouse == NO && mdisprefs > strlen(dispchars)) + mdisprefs = strlen(dispchars); + + /* allocate the displayed line array */ + displine = malloc(mdisprefs * sizeof(*displine)); + + /* initialize windows */ + body = newwin(LINES-2-FIELDS, COLS-2, 1, 1); + input_fields = newwin(FIELDS, COLS-2, FLDLINE, 1); + refresh(); +} + +static inline void display_frame(){ + + box(stdscr, 0, 0); + /* Title*/ + const int LEFT_PADDING = 5; + wmove(stdscr, 0, LEFT_PADDING); +#if CCS + if (displayversion == YES) { + wprintw(stdscr, "cscope %s", ESG_REL); + } + else { + waddstr(stdscr, "cscope"); + } +#else + wprintw(stdscr, "Cscope version %d%s", FILEVERSION, FIXVERSION); +#endif + wmove(stdscr, 0, COLS - (int) sizeof(helpstring)); + waddstr(stdscr, helpstring); + wmove(input_fields, 0, 0); + for(int i = 0; i < COLS-2; i++){ + waddch(input_fields, ACS_HLINE); + } +} + +static inline void display_input_fields(){ + /* display the input fields */ + wmove(input_fields, 1, 0); + for(int i = 0; i < FIELDS; ++i){ + wprintw(input_fields, "%s %s:\n", fields[i].text1, fields[i].text2); + } +} + +static inline void display_command_field(){ + +} + +void +display(void) +{ + char *subsystem; /* OGS subsystem name */ + char *book; /* OGS book name */ + char file[PATHLEN + 1]; /* file name */ + char function[PATLEN + 1]; /* function name */ + char linenum[NUMLEN + 1]; /* line number */ + int screenline; /* screen line number */ + int width; /* source line display width */ + int i; + char *s; + + erase(); + display_frame(); + + if (totallines == 0) { + /* if no references were found */ + /* redisplay the last message */ + waddstr(body, lastmsg); + } else { + /* display the pattern */ + if (changing == YES) { + wprintw(body, "Change \"%s\" to \"%s\"", Pattern, newpat); + } else { + wprintw(body, "%c%s: %s", toupper((unsigned char)fields[field].text2[0]), + fields[field].text2 + 1, Pattern); + } + /* display the column headings */ + wmove(body, 2, 2); + if (ogs == YES && field != FILENAME) { + wprintw(body, "%-*s ", subsystemlen, "Subsystem"); + wprintw(body, "%-*s ", booklen, "Book"); + } + if (dispcomponents > 0) + wprintw(body, "%-*s ", filelen, "File"); + + if (field == SYMBOL || field == CALLEDBY || field == CALLING) { + wprintw(body, "%-*s ", fcnlen, "Function"); + } + if (field != FILENAME) { + waddstr(body, "Line"); + } + waddch(body, '\n'); + + /* if at end of file go back to beginning */ + if (nextline > totallines) { + seekline(1); + } + /* calculate the source text column */ + + width = COLS - numlen - 3; + + if (ogs == YES) { + width -= subsystemlen + booklen + 2; + } + if (dispcomponents > 0) { + width -= filelen + 1; + } + if (field == SYMBOL || field == CALLEDBY || field == CALLING) { + width -= fcnlen + 1; + } + + /* until the max references have been displayed or + there is no more room */ + topline = nextline; + for (disprefs = 0, screenline = REFLINE; + disprefs < mdisprefs && screenline <= lastdispline; + ++disprefs, ++screenline) { + /* read the reference line */ + if (fscanf(refsfound, "%" PATHLEN_STR "s%" PATHLEN_STR "s%" NUMLEN_STR "s %" TEMPSTRING_LEN_STR "[^\n]", file, function, + linenum, tempstring) < 4) { + break; + } + ++nextline; + displine[disprefs] = screenline; + + /* if no mouse, display the selection number */ + if (mouse == YES) { + waddch(body, ' '); + } else { + wprintw(body, "%c", dispchars[disprefs]); + } + + /* display any change mark */ + if (changing == YES && + change[topline + disprefs - 1] == YES) { + waddch(body, '>'); + } else { + waddch(body, ' '); + } + + /* display the file name */ + if (field == FILENAME) { + wprintw(body, "%-*s ", filelen, file); + } else { + /* if OGS, display the subsystem and book names */ + if (ogs == YES) { + ogsnames(file, &subsystem, &book); + wprintw(body, "%-*.*s ", subsystemlen, subsystemlen, subsystem); + wprintw(body, "%-*.*s ", booklen, booklen, book); + } + /* display the requested path components */ + if (dispcomponents > 0) { + wprintw(body, "%-*.*s ", filelen, filelen, + pathcomponents(file, dispcomponents)); + } + } /* else(field == FILENAME) */ + + /* display the function name */ + if (field == SYMBOL || field == CALLEDBY || field == CALLING) { + wprintw(body, "%-*.*s ", fcnlen, fcnlen, function); + } + if (field == FILENAME) { + waddch(body, '\n'); /* go to next line */ + continue; + } + + /* display the line number */ + wprintw(body, "%*s ", numlen, linenum); + /* there may be tabs in egrep output */ + while ((s = strchr(tempstring, '\t')) != NULL) { + *s = ' '; + } + + /* display the source line */ + s = tempstring; + for (;;) { + /* see if the source line will fit */ + if ((i = strlen(s)) > width) { + + /* find the nearest blank */ + for (i = width; s[i] != ' ' && i > 0; --i) { + ; + } + if (i == 0) { + i = width; /* no blank */ + } + } + /* print up to this point */ + wprintw(body, "%.*s", i, s); + s += i; + + /* if line didn't wrap around */ + if (i < width) { + waddch(body, '\n'); /* go to next line */ + } + /* skip blanks */ + while (*s == ' ') { + ++s; + } + /* see if there is more text */ + if (*s == '\0') { + break; + } + /* if the source line is too long */ + if (++screenline > lastdispline) { + + /* if this is the first displayed line, + display what will fit on the screen */ + if (topline == nextline -1) { + disprefs++; + /* break out of two loops */ + goto endrefs; + } + + /* erase the reference */ + while (--screenline >= displine[disprefs]) { + wmove(body, screenline, 0); + clrtoeol(); + } + ++screenline; + + /* go back to the beginning of this reference */ + --nextline; + seekline(nextline); + goto endrefs; + } + /* indent the continued source line */ + wmove(body, screenline, COLS - width); + } /* for(ever) */ + } /* for(reference output lines) */ + endrefs: + /* position the cursor for the message */ + i = FLDLINE - 1; + if (screenline < i) { + waddch(body, '\n'); + } + else { + wmove(body, i, 0); + } + /* check for more references */ + i = totallines - nextline + 1; + bottomline = nextline; + if (i > 0) { + wprintw(body, "* Lines %d-%d of %d, %d more - press the space bar to display more *", topline, bottomline, totallines, i); + } + /* if this is the last page of references */ + else if (topline > 1 && nextline > totallines) { + waddstr(body, "* Press the space bar to display the first lines again *"); + } + } + drawscrollbar(topline, nextline); /* display the scrollbar */ + + atfield(); + + display_input_fields(); + display_prompt(); + + refresh(); + wrefresh(body); + wrefresh(input_fields); +} + +/* set the cursor position for the field */ +//void +//setfield(void) +//{ +// fldline = FLDLINE + field; +// fldcolumn = strlen(fields[field].text1) + strlen(fields[field].text2) + 3; +//} + +/* move to the current input field */ +// +//void +//atfield(void) +//{ +// wmove(input_fields, fldline, fldcolumn); +//} + +/* move to the changing lines prompt */ + +//void +//atchange(void) +//{ +// wmove(body, PRLINE, (int) sizeof(selprompt) - 1); +//} + +/* search for the symbol or text pattern */ + +/*ARGSUSED*/ +static void +jumpback(int sig) +{ + signal(sig, jumpback); + siglongjmp(env, 1); +} + +BOOL +search(void) +{ + char *findresult = NULL; /* find function output */ + BOOL funcexist = YES; /* find "function" error */ + FINDINIT rc = NOERROR; /* findinit return code */ + sighandler_t savesig; /* old value of signal */ + FP f; /* searching function */ + int c; + + /* open the references found file for writing */ + if (writerefsfound() == NO) { + return(NO); + } + /* find the pattern - stop on an interrupt */ + if (linemode == NO) { + postmsg("Searching"); + } + searchcount = 0; + savesig = signal(SIGINT, jumpback); + if (sigsetjmp(env, 1) == 0) { + f = fields[field].findfcn; + if (f == findregexp || f == findstring) { + findresult = (*f)(Pattern); + } else { + if ((nonglobalrefs = myfopen(temp2, "wb")) == NULL) { + cannotopen(temp2); + return(NO); + } + if ((rc = findinit(Pattern)) == NOERROR) { + (void) dbseek(0L); /* read the first block */ + findresult = (*f)(Pattern); + if (f == findcalledby) + funcexist = (*findresult == 'y'); + findcleanup(); + + /* append the non-global references */ + (void) fclose(nonglobalrefs); + if ((nonglobalrefs = myfopen(temp2, "rb")) + == NULL) { + cannotopen(temp2); + return(NO); + } + while ((c = getc(nonglobalrefs)) != EOF) { + (void) putc(c, refsfound); + } + } + (void) fclose(nonglobalrefs); + } + } + signal(SIGINT, savesig); + + /* rewind the cross-reference file */ + (void) lseek(symrefs, (long) 0, 0); + + /* reopen the references found file for reading */ + (void) fclose(refsfound); + if ((refsfound = myfopen(temp1, "rb")) == NULL) { + cannotopen(temp1); + return(NO); + } + nextline = 1; + totallines = 0; + disprefs = 0; + + /* see if it is empty */ + if ((c = getc(refsfound)) == EOF) { + if (findresult != NULL) { + (void) snprintf(lastmsg, sizeof(lastmsg), "Egrep %s in this pattern: %s", + findresult, Pattern); + } else if (rc == NOTSYMBOL) { + (void) snprintf(lastmsg, sizeof(lastmsg), "This is not a C symbol: %s", + Pattern); + } else if (rc == REGCMPERROR) { + (void) snprintf(lastmsg, sizeof(lastmsg), "Error in this regcomp(3) regular expression: %s", + Pattern); + + } else if (funcexist == NO) { + (void) snprintf(lastmsg, sizeof(lastmsg), "Function definition does not exist: %s", + Pattern); + } else { + (void) snprintf(lastmsg, sizeof(lastmsg), "Could not find the %s: %s", + fields[field].text2, Pattern); + } + return(NO); + } + /* put back the character read */ + (void) ungetc(c, refsfound); + + /* HBB 20041027: this used to hold a copy of the code of + * countrefs(), but with the crucial display width adjustments + * missing. Just call the real thing instead! */ + countrefs(); + return(YES); +} + +/* display search progress with default custom format */ + +void +progress(char *what, long current, long max) +{ + static long start; + long now; + char msg[MSGLEN + 1]; + int i; + + /* save the start time */ + if (searchcount == 0) { + start = time(NULL); + } + if ((now = time(NULL)) - start >= 1) + { + if (linemode == NO) + { + wmove(body, MSGLINE, 0); + clrtoeol(); + waddstr(body, what); + snprintf(msg, sizeof(msg), "%ld", current); + wmove(body, MSGLINE, (COLS / 2) - (strlen(msg) / 2)); + waddstr(body, msg); + snprintf(msg, sizeof(msg), "%ld", max); + wmove(body, MSGLINE, COLS - strlen(msg)); + waddstr(body, msg); + refresh(); + } + else if (verbosemode == YES) + { + snprintf(msg, sizeof(msg), "> %s %ld of %ld", what, current, max); + } + + start = now; + if ((linemode == NO) && (incurses == YES)) + { + wmove(body, MSGLINE, 0); + i = (float)COLS * (float)current / (float)max; + + standout(); + for (; i > 0; i--) + waddch(body, inch()); + standend(); + refresh(); + } + else + if (linemode == NO || verbosemode == YES) + postmsg(msg); + } + ++searchcount; +} + +/* print error message on system call failure */ + +void +myperror(char *text) +{ + char msg[MSGLEN + 1]; /* message */ + char *s; + + s = strerror(errno); + + (void) snprintf(msg, sizeof(msg), "%s: %s", text, s); + postmsg(msg); +} + +/* postmsg clears the message line and prints the message */ + +/* VARARGS */ +void +postmsg(char *msg) +{ + if (linemode == YES || incurses == NO) { + (void) printf("%s\n", msg); + fflush(stdout); + } + else { + clearmsg(); + waddstr(body, msg); + refresh(); + } + (void) strncpy(lastmsg, msg, sizeof(lastmsg) - 1); +} + +/* clearmsg clears the first message line */ + +void +clearmsg(void) +{ + if (linemode == NO) { + wmove(body, MSGLINE, 0); + clrtoeol(); + } +} + +/* clearmsg2 clears the second message line */ + +void +clearmsg2(void) +{ + if (linemode == NO) { + wmove(body, MSGLINE + 1, 0); + clrtoeol(); + } +} + +/* postmsg2 clears the second message line and prints the message */ + +void +postmsg2(char *msg) +{ + if (linemode == YES) { + (void) printf("%s\n", msg); + } + else { + clearmsg2(); + waddstr(body, msg); + refresh(); + } +} + +/* display an error mesg - stdout or on second msg line */ +void +posterr(char *msg, ...) +{ + va_list ap; + char errbuf[MSGLEN]; + + va_start(ap, msg); + if (linemode == YES || incurses == NO) + { + (void) vfprintf(stderr, msg, ap); + (void) fputc('\n', stderr); + } else { + vsnprintf(errbuf, sizeof(errbuf), msg, ap); + postmsg2(errbuf); + } + va_end(ap); +} + +/* display a fatal error mesg -- stderr *after* shutting down curses */ +void +postfatal(const char *msg, ...) +{ + va_list ap; + char errbuf[MSGLEN]; + + va_start(ap, msg); + vsnprintf(errbuf, sizeof(errbuf), msg, ap); + /* restore the terminal to its original mode */ + if (incurses == YES) { + exitcurses(); + } + + /* display fatal error messages */ + fprintf(stderr,"%s",errbuf); + + /* shut down */ + myexit(1); +} + + /* position references found file at specified line */ + +void +seekline(unsigned int line) +{ + int c; + + /* verify that there is a references found file */ + if (refsfound == NULL) { + return; + } + /* go to the beginning of the file */ + rewind(refsfound); + + /* find the requested line */ + nextline = 1; + while (nextline < line && (c = getc(refsfound)) != EOF) { + if (c == '\n') { + nextline++; + } + } +} + +/* get the OGS subsystem and book names */ + +void +ogsnames(char *file, char **subsystem, char **book) +{ + static char buf[PATHLEN + 1]; + char *s, *slash; + + *subsystem = *book = ""; + (void) strcpy(buf,file); + s = buf; + if (*s == '/') { + ++s; + } + while ((slash = strchr(s, '/')) != NULL) { + *slash = '\0'; + if ((int)strlen(s) >= 3 && strncmp(slash - 3, ".ss", 3) == 0) { + *subsystem = s; + s = slash + 1; + if ((slash = strchr(s, '/')) != NULL) { + *book = s; + *slash = '\0'; + } + break; + } + s = slash + 1; + } +} + +/* get the requested path components */ + +char * +pathcomponents(char *path, int components) +{ + int i; + char *s; + + s = path + strlen(path) - 1; + for (i = 0; i < components; ++i) { + while (s > path && *--s != '/') { + ; + } + } + if (s > path && *s == '/') { + ++s; + } + return(s); +} + +/* open the references found file for writing */ + +BOOL +writerefsfound(void) +{ + if (refsfound == NULL) { + if ((refsfound = myfopen(temp1, "wb")) == NULL) { + cannotopen(temp1); + return(NO); + } + } else { + (void) fclose(refsfound); + if ( (refsfound = myfopen(temp1, "wb")) == NULL) { + postmsg("Cannot reopen temporary file"); + return(NO); + } + } + return(YES); +} diff --git a/src/edit.c b/src/edit.c new file mode 100644 index 0000000..f2935c1 --- /dev/null +++ b/src/edit.c @@ -0,0 +1,137 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * file editing functions + */ + +#include "global.h" +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +#include +#else +#include +#endif + +/* edit this displayed reference */ + +void +editref(int i) +{ + char file[PATHLEN + 1]; /* file name */ + char linenum[NUMLEN + 1]; /* line number */ + + /* verify that there is a references found file */ + if (refsfound == NULL) { + return; + } + /* get the selected line */ + seekline(i + topline); + + /* get the file name and line number */ + if (fscanf(refsfound, "%" PATHLEN_STR "s%*s%" NUMLEN_STR "s", file, linenum) == 2) { + edit(file, linenum); /* edit it */ + } + seekline(topline); /* restore the line pointer */ +} + +/* edit all references */ + +void +editall(void) +{ + char file[PATHLEN + 1]; /* file name */ + char linenum[NUMLEN + 1]; /* line number */ + int c; + + /* verify that there is a references found file */ + if (refsfound == NULL) { + return; + } + /* get the first line */ + seekline(1); + + /* get each file name and line number */ + while (fscanf(refsfound, "%" PATHLEN_STR "s%*s%" NUMLEN_STR "s%*[^\n]", file, linenum) == 2) { + edit(file, linenum); /* edit it */ + if (editallprompt == YES) { + addstr("Type ^D to stop editing all lines, or any other character to continue: "); + if ((c = mygetch()) == EOF || c == ctrl('D') || c == ctrl('Z')) { + break; + } + } + } + seekline(topline); +} + +/* call the editor */ + +void +edit(char *file, char *linenum) +{ + char msg[MSGLEN + 1]; /* message */ + char plusnum[NUMLEN + 20]; /* line number option: allow space for wordy line# flag */ + char *s; + + file = filepath(file); + (void) snprintf(msg, sizeof(msg), "%s +%s %s", basename(editor), linenum, file); + postmsg(msg); + (void) snprintf(plusnum, sizeof(plusnum), lineflag, linenum); + /* if this is the more or page commands */ + if (strcmp(s = basename(editor), "more") == 0 || strcmp(s, "page") == 0) { + + /* get it to pause after displaying a file smaller than the screen + length */ + (void) execute(editor, editor, plusnum, file, "/dev/null", NULL); + } + else if (lineflagafterfile) { + (void) execute(editor, editor, file, plusnum, NULL); + } + else { + (void) execute(editor, editor, plusnum, file, NULL); + } + clear(); /* redisplay screen */ +} + +/* if requested, prepend a path to a relative file name */ + +char * +filepath(char *file) +{ + static char path[PATHLEN + 1]; + + if (prependpath != NULL && *file != '/') { + (void) snprintf(path, sizeof(path), "%s/%s", prependpath, file); + file = path; + } + return(file); +} diff --git a/src/egrep.c b/src/egrep.c new file mode 100644 index 0000000..b00b40a --- /dev/null +++ b/src/egrep.c @@ -0,0 +1,2053 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison implementation for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output, and Bison version. */ +#define YYBISON 30802 + +/* Bison version string. */ +#define YYBISON_VERSION "3.8.2" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 0 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + + + + +/* First part of user prologue. */ +#line 1 "src/egrep.y" + +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +/* + * egrep -- fine lines containing a regular expression + */ +#line 45 "src/egrep.y" + +#include "global.h" +#include +#include + +#include /* jmp_buf */ + +#define nextch() (*input++) + +#define MAXLIN 350 +#define MAXPOS 4000 +#define NCHARS 256 +#define NSTATES 128 +#define FINAL -1 +static char gotofn[NSTATES][NCHARS]; +static int state[NSTATES]; +static char out[NSTATES]; +static unsigned int line; +static int name[MAXLIN]; +static unsigned int left[MAXLIN]; +static unsigned int right[MAXLIN]; +static unsigned int parent[MAXLIN]; +static int foll[MAXLIN]; +static int positions[MAXPOS]; +static char chars[MAXLIN]; +static int nxtpos; +static int nxtchar; +static int tmpstat[MAXLIN]; +static int initstat[MAXLIN]; +static int xstate; +static int count; +static int icount; +static char *input; +static long lnum; +static int iflag; +static jmp_buf env; /* setjmp/longjmp buffer */ +static char *message; /* error message */ + +/* Internal prototypes: */ +static void cfoll(int v); +static void cgotofn(void); +static int cstate(int v); +static int member(int symb, int set, int torf); +static int notin(int n); +static void synerror(void); +static void overflo(void); +static void add(int *array, int n); +static void follow(unsigned int v); +static int unary(int x, int d); +static int node(int x, int l, int r); +static unsigned int cclenter(int x); +static unsigned int enter(int x); + +static int yylex(void); +static int yyerror(char *); + +#line 165 "y.tab.c" + +# ifndef YY_CAST +# ifdef __cplusplus +# define YY_CAST(Type, Val) static_cast (Val) +# define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast (Val) +# else +# define YY_CAST(Type, Val) ((Type) (Val)) +# define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val)) +# endif +# endif +# ifndef YY_NULLPTR +# if defined __cplusplus +# if 201103L <= __cplusplus +# define YY_NULLPTR nullptr +# else +# define YY_NULLPTR 0 +# endif +# else +# define YY_NULLPTR ((void*)0) +# endif +# endif + + +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + CHAR = 258, /* CHAR */ + DOT = 259, /* DOT */ + CCL = 260, /* CCL */ + NCCL = 261, /* NCCL */ + OR = 262, /* OR */ + CAT = 263, /* CAT */ + STAR = 264, /* STAR */ + PLUS = 265, /* PLUS */ + QUEST = 266 /* QUEST */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif +/* Token kinds. */ +#define YYEMPTY -2 +#define YYEOF 0 +#define YYerror 256 +#define YYUNDEF 257 +#define CHAR 258 +#define DOT 259 +#define CCL 260 +#define NCCL 261 +#define OR 262 +#define CAT 263 +#define STAR 264 +#define PLUS 265 +#define QUEST 266 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + + +int yyparse (void); + + + +/* Symbol kind. */ +enum yysymbol_kind_t +{ + YYSYMBOL_YYEMPTY = -2, + YYSYMBOL_YYEOF = 0, /* "end of file" */ + YYSYMBOL_YYerror = 1, /* error */ + YYSYMBOL_YYUNDEF = 2, /* "invalid token" */ + YYSYMBOL_CHAR = 3, /* CHAR */ + YYSYMBOL_DOT = 4, /* DOT */ + YYSYMBOL_CCL = 5, /* CCL */ + YYSYMBOL_NCCL = 6, /* NCCL */ + YYSYMBOL_OR = 7, /* OR */ + YYSYMBOL_CAT = 8, /* CAT */ + YYSYMBOL_STAR = 9, /* STAR */ + YYSYMBOL_PLUS = 10, /* PLUS */ + YYSYMBOL_QUEST = 11, /* QUEST */ + YYSYMBOL_12_ = 12, /* '(' */ + YYSYMBOL_13_ = 13, /* ')' */ + YYSYMBOL_YYACCEPT = 14, /* $accept */ + YYSYMBOL_s = 15, /* s */ + YYSYMBOL_t = 16, /* t */ + YYSYMBOL_b = 17, /* b */ + YYSYMBOL_r = 18 /* r */ +}; +typedef enum yysymbol_kind_t yysymbol_kind_t; + + + + +#ifdef short +# undef short +#endif + +/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure + and (if available) are included + so that the code can choose integer types of a good width. */ + +#ifndef __PTRDIFF_MAX__ +# include /* INFRINGES ON USER NAME SPACE */ +# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_STDINT_H +# endif +#endif + +/* Narrow types that promote to a signed type and that can represent a + signed or unsigned integer of at least N bits. In tables they can + save space and decrease cache pressure. Promoting to a signed type + helps avoid bugs in integer arithmetic. */ + +#ifdef __INT_LEAST8_MAX__ +typedef __INT_LEAST8_TYPE__ yytype_int8; +#elif defined YY_STDINT_H +typedef int_least8_t yytype_int8; +#else +typedef signed char yytype_int8; +#endif + +#ifdef __INT_LEAST16_MAX__ +typedef __INT_LEAST16_TYPE__ yytype_int16; +#elif defined YY_STDINT_H +typedef int_least16_t yytype_int16; +#else +typedef short yytype_int16; +#endif + +/* Work around bug in HP-UX 11.23, which defines these macros + incorrectly for preprocessor constants. This workaround can likely + be removed in 2023, as HPE has promised support for HP-UX 11.23 + (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of + . */ +#ifdef __hpux +# undef UINT_LEAST8_MAX +# undef UINT_LEAST16_MAX +# define UINT_LEAST8_MAX 255 +# define UINT_LEAST16_MAX 65535 +#endif + +#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST8_TYPE__ yytype_uint8; +#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST8_MAX <= INT_MAX) +typedef uint_least8_t yytype_uint8; +#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX +typedef unsigned char yytype_uint8; +#else +typedef short yytype_uint8; +#endif + +#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__ +typedef __UINT_LEAST16_TYPE__ yytype_uint16; +#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \ + && UINT_LEAST16_MAX <= INT_MAX) +typedef uint_least16_t yytype_uint16; +#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX +typedef unsigned short yytype_uint16; +#else +typedef int yytype_uint16; +#endif + +#ifndef YYPTRDIFF_T +# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__ +# define YYPTRDIFF_T __PTRDIFF_TYPE__ +# define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__ +# elif defined PTRDIFF_MAX +# ifndef ptrdiff_t +# include /* INFRINGES ON USER NAME SPACE */ +# endif +# define YYPTRDIFF_T ptrdiff_t +# define YYPTRDIFF_MAXIMUM PTRDIFF_MAX +# else +# define YYPTRDIFF_T long +# define YYPTRDIFF_MAXIMUM LONG_MAX +# endif +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__ +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned +# endif +#endif + +#define YYSIZE_MAXIMUM \ + YY_CAST (YYPTRDIFF_T, \ + (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1) \ + ? YYPTRDIFF_MAXIMUM \ + : YY_CAST (YYSIZE_T, -1))) + +#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X)) + + +/* Stored state numbers (used for stacks). */ +typedef yytype_int8 yy_state_t; + +/* State numbers in computations. */ +typedef int yy_state_fast_t; + +#ifndef YY_ +# if defined YYENABLE_NLS && YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(Msgid) dgettext ("bison-runtime", Msgid) +# endif +# endif +# ifndef YY_ +# define YY_(Msgid) Msgid +# endif +#endif + + +#ifndef YY_ATTRIBUTE_PURE +# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_PURE __attribute__ ((__pure__)) +# else +# define YY_ATTRIBUTE_PURE +# endif +#endif + +#ifndef YY_ATTRIBUTE_UNUSED +# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__) +# define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__)) +# else +# define YY_ATTRIBUTE_UNUSED +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YY_USE(E) ((void) (E)) +#else +# define YY_USE(E) /* empty */ +#endif + +/* Suppress an incorrect diagnostic about yylval being uninitialized. */ +#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__ +# if __GNUC__ * 100 + __GNUC_MINOR__ < 407 +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") +# else +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"") \ + _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") +# endif +# define YY_IGNORE_MAYBE_UNINITIALIZED_END \ + _Pragma ("GCC diagnostic pop") +#else +# define YY_INITIAL_VALUE(Value) Value +#endif +#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN +# define YY_IGNORE_MAYBE_UNINITIALIZED_END +#endif +#ifndef YY_INITIAL_VALUE +# define YY_INITIAL_VALUE(Value) /* Nothing. */ +#endif + +#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__ +# define YY_IGNORE_USELESS_CAST_BEGIN \ + _Pragma ("GCC diagnostic push") \ + _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"") +# define YY_IGNORE_USELESS_CAST_END \ + _Pragma ("GCC diagnostic pop") +#endif +#ifndef YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_BEGIN +# define YY_IGNORE_USELESS_CAST_END +#endif + + +#define YY_ASSERT(E) ((void) (0 && (E))) + +#if !defined yyoverflow + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS +# include /* INFRINGES ON USER NAME SPACE */ + /* Use EXIT_SUCCESS as a witness for stdlib.h. */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's 'empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined EXIT_SUCCESS \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef EXIT_SUCCESS +# define EXIT_SUCCESS 0 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined EXIT_SUCCESS +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined EXIT_SUCCESS +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* !defined yyoverflow */ + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yy_state_t yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +# define YYCOPY_NEEDED 1 + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYPTRDIFF_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / YYSIZEOF (*yyptr); \ + } \ + while (0) + +#endif + +#if defined YYCOPY_NEEDED && YYCOPY_NEEDED +/* Copy COUNT objects from SRC to DST. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(Dst, Src, Count) \ + __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src))) +# else +# define YYCOPY(Dst, Src, Count) \ + do \ + { \ + YYPTRDIFF_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (Dst)[yyi] = (Src)[yyi]; \ + } \ + while (0) +# endif +# endif +#endif /* !YYCOPY_NEEDED */ + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 6 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 108 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 14 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 5 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 18 +/* YYNSTATES -- Number of states. */ +#define YYNSTATES 25 + +/* YYMAXUTOK -- Last valid token kind. */ +#define YYMAXUTOK 266 + + +/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM + as returned by yylex, with out-of-bounds checking. */ +#define YYTRANSLATE(YYX) \ + (0 <= (YYX) && (YYX) <= YYMAXUTOK \ + ? YY_CAST (yysymbol_kind_t, yytranslate[YYX]) \ + : YYSYMBOL_YYUNDEF) + +/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM + as returned by yylex. */ +static const yytype_int8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 12, 13, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11 +}; + +#if YYDEBUG +/* YYRLINE[YYN] -- Source line where rule number YYN was defined. */ +static const yytype_uint8 yyrline[] = +{ + 0, 103, 103, 108, 110, 112, 114, 118, 121, 123, + 125, 127, 131, 133, 135, 137, 139, 141, 143 +}; +#endif + +/** Accessing symbol of state STATE. */ +#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State]) + +#if YYDEBUG || 0 +/* The user-facing name of the symbol whose (internal) number is + YYSYMBOL. No bounds checking. */ +static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED; + +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "\"end of file\"", "error", "\"invalid token\"", "CHAR", "DOT", "CCL", + "NCCL", "OR", "CAT", "STAR", "PLUS", "QUEST", "'('", "')'", "$accept", + "s", "t", "b", "r", YY_NULLPTR +}; + +static const char * +yysymbol_name (yysymbol_kind_t yysymbol) +{ + return yytname[yysymbol]; +} +#endif + +#define YYPACT_NINF (-5) + +#define yypact_value_is_default(Yyn) \ + ((Yyn) == YYPACT_NINF) + +#define YYTABLE_NINF (-14) + +#define yytable_value_is_error(Yyn) \ + 0 + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +static const yytype_int8 yypact[] = +{ + 2, -5, 3, -5, 1, 1, -5, -5, -5, -5, + -5, -5, 1, 47, 60, 72, 86, -5, -5, -5, + 19, 96, 1, -5, 33 +}; + +/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM. + Performed when YYTABLE does not specify something else to do. Zero + means the default is an error. */ +static const yytype_int8 yydefact[] = +{ + 7, 7, 0, 2, 0, 0, 1, 18, 8, 9, + 10, 11, 0, 0, 0, 0, 0, 14, 15, 16, + 0, 0, 0, 17, 0 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int8 yypgoto[] = +{ + -5, -5, -5, 9, -4 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int8 yydefgoto[] = +{ + 0, 2, 3, 4, 20 +}; + +/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule whose + number is the opposite. If YYTABLE_NINF, syntax error. */ +static const yytype_int8 yytable[] = +{ + 13, 14, 7, 6, 8, 9, 10, 11, 15, 1, + 5, 0, 24, 12, 0, 0, 0, 24, 24, -13, + 7, 0, -13, -13, -13, -13, -13, 0, 17, 18, + 19, -13, -13, -12, 7, 0, 8, 9, 10, 11, + -12, 0, 17, 18, 19, 12, -12, -3, 7, 0, + 8, 9, 10, 11, 16, 0, 17, 18, 19, 12, + -5, 7, 0, 8, 9, 10, 11, 21, 0, 17, + 18, 19, 12, 7, 0, 8, 9, 10, 11, 22, + 0, 17, 18, 19, 12, 23, -6, 7, 0, 8, + 9, 10, 11, 0, 0, 0, -4, 7, 12, 8, + 9, 10, 11, 0, 0, 0, 0, 0, 12 +}; + +static const yytype_int8 yycheck[] = +{ + 4, 5, 1, 0, 3, 4, 5, 6, 12, 7, + 1, -1, 16, 12, -1, -1, -1, 21, 22, 0, + 1, -1, 3, 4, 5, 6, 7, -1, 9, 10, + 11, 12, 13, 0, 1, -1, 3, 4, 5, 6, + 7, -1, 9, 10, 11, 12, 13, 0, 1, -1, + 3, 4, 5, 6, 7, -1, 9, 10, 11, 12, + 0, 1, -1, 3, 4, 5, 6, 7, -1, 9, + 10, 11, 12, 1, -1, 3, 4, 5, 6, 7, + -1, 9, 10, 11, 12, 13, 0, 1, -1, 3, + 4, 5, 6, -1, -1, -1, 0, 1, 12, 3, + 4, 5, 6, -1, -1, -1, -1, -1, 12 +}; + +/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of + state STATE-NUM. */ +static const yytype_int8 yystos[] = +{ + 0, 7, 15, 16, 17, 17, 0, 1, 3, 4, + 5, 6, 12, 18, 18, 18, 7, 9, 10, 11, + 18, 7, 7, 13, 18 +}; + +/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr1[] = +{ + 0, 14, 15, 16, 16, 16, 16, 17, 18, 18, + 18, 18, 18, 18, 18, 18, 18, 18, 18 +}; + +/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM. */ +static const yytype_int8 yyr2[] = +{ + 0, 2, 1, 2, 4, 3, 3, 0, 1, 1, + 1, 1, 3, 2, 2, 2, 2, 3, 1 +}; + + +enum { YYENOMEM = -2 }; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab +#define YYNOMEM goto yyexhaustedlab + + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ + do \ + if (yychar == YYEMPTY) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + YYPOPSTACK (yylen); \ + yystate = *yyssp; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ + while (0) + +/* Backward compatibility with an undocumented macro. + Use YYerror or YYUNDEF. */ +#define YYERRCODE YYUNDEF + + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + + + + +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Kind, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + + +/*-----------------------------------. +| Print this symbol's value on YYO. | +`-----------------------------------*/ + +static void +yy_symbol_value_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + FILE *yyoutput = yyo; + YY_USE (yyoutput); + if (!yyvaluep) + return; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/*---------------------------. +| Print this symbol on YYO. | +`---------------------------*/ + +static void +yy_symbol_print (FILE *yyo, + yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep) +{ + YYFPRINTF (yyo, "%s %s (", + yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind)); + + yy_symbol_value_print (yyo, yykind, yyvaluep); + YYFPRINTF (yyo, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +static void +yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop) +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +static void +yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp, + int yyrule) +{ + int yylno = yyrline[yyrule]; + int yynrhs = yyr2[yyrule]; + int yyi; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, + YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]), + &yyvsp[(yyi + 1) - (yynrhs)]); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyssp, yyvsp, Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) ((void) 0) +# define YY_SYMBOL_PRINT(Title, Kind, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + + + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +static void +yydestruct (const char *yymsg, + yysymbol_kind_t yykind, YYSTYPE *yyvaluep) +{ + YY_USE (yyvaluep); + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp); + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + YY_USE (yykind); + YY_IGNORE_MAYBE_UNINITIALIZED_END +} + + +/* Lookahead token kind. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; +/* Number of syntax errors so far. */ +int yynerrs; + + + + +/*----------. +| yyparse. | +`----------*/ + +int +yyparse (void) +{ + yy_state_fast_t yystate = 0; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus = 0; + + /* Refer to the stacks through separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* Their size. */ + YYPTRDIFF_T yystacksize = YYINITDEPTH; + + /* The state stack: array, bottom, top. */ + yy_state_t yyssa[YYINITDEPTH]; + yy_state_t *yyss = yyssa; + yy_state_t *yyssp = yyss; + + /* The semantic value stack: array, bottom, top. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + YYSTYPE *yyvsp = yyvs; + + int yyn; + /* The return value of yyparse. */ + int yyresult; + /* Lookahead symbol kind. */ + yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yychar = YYEMPTY; /* Cause a token to be read. */ + + goto yysetstate; + + +/*------------------------------------------------------------. +| yynewstate -- push a new state, which is found in yystate. | +`------------------------------------------------------------*/ +yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + +/*--------------------------------------------------------------------. +| yysetstate -- set current state (the top of the stack) to yystate. | +`--------------------------------------------------------------------*/ +yysetstate: + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + YY_ASSERT (0 <= yystate && yystate < YYNSTATES); + YY_IGNORE_USELESS_CAST_BEGIN + *yyssp = YY_CAST (yy_state_t, yystate); + YY_IGNORE_USELESS_CAST_END + YY_STACK_PRINT (yyss, yyssp); + + if (yyss + yystacksize - 1 <= yyssp) +#if !defined yyoverflow && !defined YYSTACK_RELOCATE + YYNOMEM; +#else + { + /* Get the current used size of the three stacks, in elements. */ + YYPTRDIFF_T yysize = yyssp - yyss + 1; + +# if defined yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + yy_state_t *yyss1 = yyss; + YYSTYPE *yyvs1 = yyvs; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * YYSIZEOF (*yyssp), + &yyvs1, yysize * YYSIZEOF (*yyvsp), + &yystacksize); + yyss = yyss1; + yyvs = yyvs1; + } +# else /* defined YYSTACK_RELOCATE */ + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + YYNOMEM; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yy_state_t *yyss1 = yyss; + union yyalloc *yyptr = + YY_CAST (union yyalloc *, + YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize)))); + if (! yyptr) + YYNOMEM; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YY_IGNORE_USELESS_CAST_BEGIN + YYDPRINTF ((stderr, "Stack size increased to %ld\n", + YY_CAST (long, yystacksize))); + YY_IGNORE_USELESS_CAST_END + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } +#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */ + + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yypact_value_is_default (yyn)) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either empty, or end-of-input, or a valid lookahead. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token\n")); + yychar = yylex (); + } + + if (yychar <= YYEOF) + { + yychar = YYEOF; + yytoken = YYSYMBOL_YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else if (yychar == YYerror) + { + /* The scanner already issued an error message, process directly + to error recovery. But do not keep the error token as + lookahead, it is too special and may lead us to an endless + loop in error recovery. */ + yychar = YYUNDEF; + yytoken = YYSYMBOL_YYerror; + goto yyerrlab1; + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yytable_value_is_error (yyn)) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + yystate = yyn; + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + /* Discard the shifted token. */ + yychar = YYEMPTY; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + '$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: /* s: t */ +#line 104 "src/egrep.y" + { unary(FINAL, yyvsp[0]); + line--; + } +#line 1248 "y.tab.c" + break; + + case 3: /* t: b r */ +#line 109 "src/egrep.y" + { yyval = node(CAT, yyvsp[-1], yyvsp[0]); } +#line 1254 "y.tab.c" + break; + + case 4: /* t: OR b r OR */ +#line 111 "src/egrep.y" + { yyval = node(CAT, yyvsp[-2], yyvsp[-1]); } +#line 1260 "y.tab.c" + break; + + case 5: /* t: OR b r */ +#line 113 "src/egrep.y" + { yyval = node(CAT, yyvsp[-1], yyvsp[0]); } +#line 1266 "y.tab.c" + break; + + case 6: /* t: b r OR */ +#line 115 "src/egrep.y" + { yyval = node(CAT, yyvsp[-2], yyvsp[-1]); } +#line 1272 "y.tab.c" + break; + + case 7: /* b: %empty */ +#line 118 "src/egrep.y" + { yyval = enter(DOT); + yyval = unary(STAR, yyval); } +#line 1279 "y.tab.c" + break; + + case 8: /* r: CHAR */ +#line 122 "src/egrep.y" + { yyval = enter(yyvsp[0]); } +#line 1285 "y.tab.c" + break; + + case 9: /* r: DOT */ +#line 124 "src/egrep.y" + { yyval = enter(DOT); } +#line 1291 "y.tab.c" + break; + + case 10: /* r: CCL */ +#line 126 "src/egrep.y" + { yyval = cclenter(CCL); } +#line 1297 "y.tab.c" + break; + + case 11: /* r: NCCL */ +#line 128 "src/egrep.y" + { yyval = cclenter(NCCL); } +#line 1303 "y.tab.c" + break; + + case 12: /* r: r OR r */ +#line 132 "src/egrep.y" + { yyval = node(OR, yyvsp[-2], yyvsp[0]); } +#line 1309 "y.tab.c" + break; + + case 13: /* r: r r */ +#line 134 "src/egrep.y" + { yyval = node(CAT, yyvsp[-1], yyvsp[0]); } +#line 1315 "y.tab.c" + break; + + case 14: /* r: r STAR */ +#line 136 "src/egrep.y" + { yyval = unary(STAR, yyvsp[-1]); } +#line 1321 "y.tab.c" + break; + + case 15: /* r: r PLUS */ +#line 138 "src/egrep.y" + { yyval = unary(PLUS, yyvsp[-1]); } +#line 1327 "y.tab.c" + break; + + case 16: /* r: r QUEST */ +#line 140 "src/egrep.y" + { yyval = unary(QUEST, yyvsp[-1]); } +#line 1333 "y.tab.c" + break; + + case 17: /* r: '(' r ')' */ +#line 142 "src/egrep.y" + { yyval = yyvsp[-1]; } +#line 1339 "y.tab.c" + break; + + +#line 1343 "y.tab.c" + + default: break; + } + /* User semantic actions sometimes alter yychar, and that requires + that yytoken be updated with the new translation. We take the + approach of translating immediately before every use of yytoken. + One alternative is translating here after every semantic action, + but that translation would be missed if the semantic action invokes + YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or + if it invokes YYBACKUP. In the case of YYABORT or YYACCEPT, an + incorrect destructor might then be invoked immediately. In the + case of YYERROR or YYBACKUP, subsequent parser actions might lead + to an incorrect destructor call or verbose syntax error message + before the lookahead is translated. */ + YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + + *++yyvsp = yyval; + + /* Now 'shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + { + const int yylhs = yyr1[yyn] - YYNTOKENS; + const int yyi = yypgoto[yylhs] + *yyssp; + yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp + ? yytable[yyi] + : yydefgoto[yylhs]); + } + + goto yynewstate; + + +/*--------------------------------------. +| yyerrlab -- here on detecting error. | +`--------------------------------------*/ +yyerrlab: + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar); + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; + yyerror (YY_("syntax error")); + } + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + /* Pacify compilers when the user code never invokes YYERROR and the + label yyerrorlab therefore never appears in user code. */ + if (0) + YYERROR; + ++yynerrs; + + /* Do not reclaim the symbols of the rule whose action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + /* Pop stack until we find a state that shifts the error token. */ + for (;;) + { + yyn = yypact[yystate]; + if (!yypact_value_is_default (yyn)) + { + yyn += YYSYMBOL_YYerror; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + YY_ACCESSING_SYMBOL (yystate), yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN + *++yyvsp = yylval; + YY_IGNORE_MAYBE_UNINITIALIZED_END + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturnlab; + + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturnlab; + + +/*-----------------------------------------------------------. +| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here. | +`-----------------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + goto yyreturnlab; + + +/*----------------------------------------------------------. +| yyreturnlab -- parsing is finished, clean up and return. | +`----------------------------------------------------------*/ +yyreturnlab: + if (yychar != YYEMPTY) + { + /* Make sure we have latest lookahead translation. See comments at + user semantic actions for why this is necessary. */ + yytoken = YYTRANSLATE (yychar); + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + } + /* Do not reclaim the symbols of the rule whose action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + YY_ACCESSING_SYMBOL (+*yyssp), yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + + return yyresult; +} + +#line 146 "src/egrep.y" + +static int +yyerror(char *s) +{ + message = s; + longjmp(env, 1); + return 1; /* silence a warning */ +} + +static int +yylex(void) +{ + int cclcnt, x; + char c, d; + + switch(c = nextch()) { + case '|': + case '\n': + return (OR); + case '*': + return (STAR); + case '+': + return (PLUS); + case '?': + return (QUEST); + case '(': + case ')': + return (c); + case '.': + return (DOT); + case '\0': + return (0); + case '[': + x = CCL; + cclcnt = 0; + count = nxtchar++; + if ((c = nextch()) == '^') { + x = NCCL; + c = nextch(); + } + do { + if (c == '\0') + synerror(); + if ( (c == '-') + && (cclcnt > 0) + && (chars[nxtchar-1] != 0) + ) { + if ((d = nextch()) != 0) { + c = chars[nxtchar-1]; + while ((unsigned int)c < (unsigned int)d) { + if (nxtchar >= MAXLIN) + overflo(); + chars[nxtchar++] = ++c; + cclcnt++; + } + continue; + } /* if() */ + } /* if() */ + if (nxtchar >= MAXLIN) + overflo(); + chars[nxtchar++] = c; + cclcnt++; + } while ((c = nextch()) != ']'); + chars[count] = cclcnt; + return (x); + case '\\': + if ((c = nextch()) == '\0') + synerror(); + yylval = c; + return (CHAR); + case '$': + case '^': + c = '\n'; + yylval = c; + return (CHAR); + default: + yylval = c; + return (CHAR); + } +} + +static void +synerror(void) +{ + yyerror("Syntax error"); +} + +static unsigned int +enter(int x) +{ + if(line >= MAXLIN) + overflo(); + name[line] = x; + left[line] = 0; + right[line] = 0; + return(line++); +} + +static unsigned int +cclenter(int x) +{ + unsigned int linno; + + linno = enter(x); + right[linno] = count; + return (linno); +} + +static int +node(int x, int l, int r) +{ + if(line >= MAXLIN) + overflo(); + name[line] = x; + left[line] = l; + right[line] = r; + parent[l] = line; + parent[r] = line; + return(line++); +} + +static int +unary(int x, int d) +{ + if(line >= MAXLIN) + overflo(); + name[line] = x; + left[line] = d; + right[line] = 0; + parent[d] = line; + return(line++); +} + +static void +overflo(void) +{ + yyerror("internal table overflow"); +} + +static void +cfoll(int v) +{ + unsigned int i; + + if (left[v] == 0) { + count = 0; + for (i = 1; i <= line; i++) + tmpstat[i] = 0; + follow(v); + add(foll, v); + } else if (right[v] == 0) + cfoll(left[v]); + else { + cfoll(left[v]); + cfoll(right[v]); + } +} + +static void +cgotofn(void) +{ + unsigned int i, n, s; + int c, k; + char symbol[NCHARS]; + unsigned int j, l, pc, pos; + unsigned int nc; + int curpos; + unsigned int num, number, newpos; + + count = 0; + for (n=3; n<=line; n++) + tmpstat[n] = 0; + if (cstate(line-1)==0) { + tmpstat[line] = 1; + count++; + out[0] = 1; + } + for (n=3; n<=line; n++) + initstat[n] = tmpstat[n]; + count--; /*leave out position 1 */ + icount = count; + tmpstat[1] = 0; + add(state, 0); + n = 0; + for (s = 0; s <= n; s++) { + if (out[s] == 1) + continue; + for (i = 0; i < NCHARS; i++) + symbol[i] = 0; + num = positions[state[s]]; + count = icount; + for (i = 3; i <= line; i++) + tmpstat[i] = initstat[i]; + pos = state[s] + 1; + for (i = 0; i < num; i++) { + curpos = positions[pos]; + if ((c = name[curpos]) >= 0) { + if (c < NCHARS) { + symbol[c] = 1; + } else if (c == DOT) { + for (k = 0; k < NCHARS; k++) + if (k != '\n') + symbol[k] = 1; + } else if (c == CCL) { + nc = chars[right[curpos]]; + pc = right[curpos] + 1; + for (j = 0; j < nc; j++) + symbol[(unsigned char)chars[pc++]] = 1; + } else if (c == NCCL) { + nc = chars[right[curpos]]; + for (j = 0; j < NCHARS; j++) { + pc = right[curpos] + 1; + for (l = 0; l < nc; l++) + if (j==(unsigned char)chars[pc++]) + goto cont; + if (j != '\n') + symbol[j] = 1; + cont: + ; + } + } + } + pos++; + } /* for (i) */ + for (c=0; c= 0) + if ((k == c) + || (k == DOT) + || (k == CCL && member(c, right[curpos], 1)) + || (k == NCCL && member(c, right[curpos], 0)) + ) { + number = positions[foll[curpos]]; + newpos = foll[curpos] + 1; + for (j = 0; j < number; j++) { + if (tmpstat[positions[newpos]] != 1) { + tmpstat[positions[newpos]] = 1; + count++; + } + newpos++; + } + } + pos++; + } /* end nextstate */ + if (notin(n)) { + if (n >= NSTATES) + overflo(); + add(state, ++n); + if (tmpstat[line] == 1) + out[n] = 1; + gotofn[s][c] = n; + } else { + gotofn[s][c] = xstate; + } + } /* if (symbol) */ + } /* for(c) */ + } /* for(s) */ +} + +static int +cstate(int v) +{ + int b; + if (left[v] == 0) { + if (tmpstat[v] != 1) { + tmpstat[v] = 1; + count++; + } + return(1); + } + else if (right[v] == 0) { + if (cstate(left[v]) == 0) return (0); + else if (name[v] == PLUS) return (1); + else return (0); + } + else if (name[v] == CAT) { + if (cstate(left[v]) == 0 && cstate(right[v]) == 0) return (0); + else return (1); + } + else { /* name[v] == OR */ + b = cstate(right[v]); + if (cstate(left[v]) == 0 || b == 0) return (0); + else return (1); + } +} + +static int +member(int symb, int set, int torf) +{ + unsigned int i, num, pos; + + num = chars[set]; + pos = set + 1; + for (i = 0; i < num; i++) + if (symb == (unsigned char)(chars[pos++])) + return (torf); + return (!torf); +} + +static int +notin(int n) +{ + int i, j, pos; + for (i=0; i<=n; i++) { + if (positions[state[i]] == count) { + pos = state[i] + 1; + for (j=0; j < count; j++) + if (tmpstat[positions[pos++]] != 1) goto nxt; + xstate = i; + return (0); + } + nxt: ; + } + return (1); +} + +static void +add(int *array, int n) +{ + unsigned int i; + + if (nxtpos + count > MAXPOS) + overflo(); + array[n] = nxtpos; + positions[nxtpos++] = count; + for (i=3; i <= line; i++) { + if (tmpstat[i] == 1) { + positions[nxtpos++] = i; + } + } +} + +static void +follow(unsigned int v) +{ + unsigned int p; + + if (v == line) + return; + p = parent[v]; + switch(name[p]) { + case STAR: + case PLUS: cstate(v); + follow(p); + return; + + case OR: + case QUEST: follow(p); + return; + + case CAT: + if (v == left[p]) { + if (cstate(right[p]) == 0) { + follow(p); + return; + } + } else + follow(p); + return; + case FINAL: + if (tmpstat[line] != 1) { + tmpstat[line] = 1; + count++; + } + return; + } +} + +char * +egrepinit(char *egreppat) +{ + /* initialize the global data */ + memset(gotofn, 0, sizeof(gotofn)); + memset(state, 0, sizeof(state)); + memset(out, 0, sizeof(out)); + line = 1; + memset(name, 0, sizeof(name)); + memset(left, 0, sizeof(left)); + memset(right, 0, sizeof(right)); + memset(parent, 0, sizeof(parent)); + memset(foll, 0, sizeof(foll)); + memset(positions, 0, sizeof(positions)); + memset(chars, 0, sizeof(chars)); + nxtpos = 0; + nxtchar = 0; + memset(tmpstat, 0, sizeof(tmpstat)); + memset(initstat, 0, sizeof(initstat)); + xstate = 0; + count = 0; + icount = 0; + input = egreppat; + message = NULL; + if (setjmp(env) == 0) { + yyparse(); + cfoll(line-1); + cgotofn(); + } + return(message); +} + +static char buf[2 * BUFSIZ]; +static const char *buf_end = buf + (sizeof(buf) / sizeof(*buf)); + +static size_t read_next_chunk(char **p, FILE *fptr) +{ + if (*p <= (buf + BUFSIZ)) { + /* bwlow the middle, so enough space left for one entire BUFSIZ */ + return fread(*p, sizeof(**p), BUFSIZ, fptr); + } else if (*p == buf_end) { + /* exactly at end ... wrap around and use lower half */ + *p = buf; + return fread(*p, sizeof(**p), BUFSIZ, fptr); + } + /* somewhere in second half, so do a limited read */ + return fread(*p, sizeof(**p), buf_end - *p, fptr); +} + +int +egrep(char *file, FILE *output, char *format) +{ + char *p; + unsigned int cstat; + int ccount; + char *nlp; + unsigned int istat; + int in_line; + FILE *fptr; + + if ((fptr = myfopen(file, "r")) == NULL) + return(-1); + + lnum = 1; + p = buf; + nlp = p; + ccount = read_next_chunk(&p, fptr); + + if (ccount <= 0) { + fclose(fptr); + return(0); + } + in_line = 1; + istat = cstat = (unsigned int) gotofn[0]['\n']; + if (out[cstat]) + goto found; + for (;;) { + if (!iflag) { + /* all input chars made positive */ + cstat = (unsigned int) gotofn[cstat][(unsigned char)*p]; + } else { + /* for -i option*/ + cstat = (unsigned int) gotofn[cstat][tolower((unsigned char)*p)]; + } + if (out[cstat]) { + found: + for(;;) { + if (*p++ == '\n') { + in_line = 0; + succeed: + fprintf(output, format, file, lnum); + if (p <= nlp) { + while (nlp < buf_end) + putc(*nlp++, output); + nlp = buf; + } + while (nlp < p) + putc(*nlp++, output); + lnum++; + nlp = p; + if (out[cstat = istat] == 0) + goto brk2; + } /* if (p++ == \n) */ + cfound: + if (--ccount <= 0) { + ccount = read_next_chunk(&p, fptr); + if (ccount <= 0) { + if (in_line) { + in_line = 0; + goto succeed; + } + fclose(fptr); + return(0); + } + } /* if(ccount <= 0) */ + in_line = 1; + } /* for(ever) */ + } /* if(out[cstat]) */ + + if (*p++ == '\n') { + in_line = 0; + lnum++; + nlp = p; + if (out[(cstat=istat)]) + goto cfound; + } + brk2: + if (--ccount <= 0) { + ccount = read_next_chunk(&p, fptr); + if (ccount <= 0) + break; + } + in_line = 1; + } + fclose(fptr); + return(0); +} + +void +egrepcaseless(int i) +{ + iflag = i; /* simulate "egrep -i" */ +} diff --git a/src/egrep.h b/src/egrep.h new file mode 100644 index 0000000..2671791 --- /dev/null +++ b/src/egrep.h @@ -0,0 +1,98 @@ +/* A Bison parser, made by GNU Bison 3.8.2. */ + +/* Bison interface for Yacc-like parsers in C + + Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation, + Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual, + especially those whose name start with YY_ or yy_. They are + private implementation details that can be changed or removed. */ + +#ifndef YY_YY_EGREP_H_INCLUDED +# define YY_YY_EGREP_H_INCLUDED +/* Debug traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif +#if YYDEBUG +extern int yydebug; +#endif + +/* Token kinds. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + enum yytokentype + { + YYEMPTY = -2, + YYEOF = 0, /* "end of file" */ + YYerror = 256, /* error */ + YYUNDEF = 257, /* "invalid token" */ + CHAR = 258, /* CHAR */ + DOT = 259, /* DOT */ + CCL = 260, /* CCL */ + NCCL = 261, /* NCCL */ + OR = 262, /* OR */ + CAT = 263, /* CAT */ + STAR = 264, /* STAR */ + PLUS = 265, /* PLUS */ + QUEST = 266 /* QUEST */ + }; + typedef enum yytokentype yytoken_kind_t; +#endif +/* Token kinds. */ +#define YYEMPTY -2 +#define YYEOF 0 +#define YYerror 256 +#define YYUNDEF 257 +#define CHAR 258 +#define DOT 259 +#define CCL 260 +#define NCCL 261 +#define OR 262 +#define CAT 263 +#define STAR 264 +#define PLUS 265 +#define QUEST 266 + +/* Value type. */ +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef int YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define YYSTYPE_IS_DECLARED 1 +#endif + + +extern YYSTYPE yylval; + + +int yyparse (void); + + +#endif /* !YY_YY_EGREP_H_INCLUDED */ diff --git a/src/egrep.y b/src/egrep.y new file mode 100644 index 0000000..cb7c0c4 --- /dev/null +++ b/src/egrep.y @@ -0,0 +1,663 @@ +%{ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +/* + * egrep -- fine lines containing a regular expression + */ +%} + +%token CHAR DOT CCL NCCL OR CAT STAR PLUS QUEST +%left OR +%left CHAR DOT CCL NCCL '(' +%left CAT +%left STAR PLUS QUEST + +%{ +#include "global.h" +#include +#include + +#include /* jmp_buf */ + +#define nextch() (*input++) + +#define MAXLIN 350 +#define MAXPOS 4000 +#define NCHARS 256 +#define NSTATES 128 +#define FINAL -1 +static char gotofn[NSTATES][NCHARS]; +static int state[NSTATES]; +static char out[NSTATES]; +static unsigned int line; +static int name[MAXLIN]; +static unsigned int left[MAXLIN]; +static unsigned int right[MAXLIN]; +static unsigned int parent[MAXLIN]; +static int foll[MAXLIN]; +static int positions[MAXPOS]; +static char chars[MAXLIN]; +static int nxtpos; +static int nxtchar; +static int tmpstat[MAXLIN]; +static int initstat[MAXLIN]; +static int xstate; +static int count; +static int icount; +static char *input; +static long lnum; +static int iflag; +static jmp_buf env; /* setjmp/longjmp buffer */ +static char *message; /* error message */ + +/* Internal prototypes: */ +static void cfoll(int v); +static void cgotofn(void); +static int cstate(int v); +static int member(int symb, int set, int torf); +static int notin(int n); +static void synerror(void); +static void overflo(void); +static void add(int *array, int n); +static void follow(unsigned int v); +static int unary(int x, int d); +static int node(int x, int l, int r); +static unsigned int cclenter(int x); +static unsigned int enter(int x); + +static int yylex(void); +static int yyerror(char *); +%} + +%% +s: t + { unary(FINAL, $1); + line--; + } + ; +t: b r + { $$ = node(CAT, $1, $2); } + | OR b r OR + { $$ = node(CAT, $2, $3); } + | OR b r + { $$ = node(CAT, $2, $3); } + | b r OR + { $$ = node(CAT, $1, $2); } + ; +b: + { $$ = enter(DOT); + $$ = unary(STAR, $$); } + ; +r: CHAR + { $$ = enter($1); } + | DOT + { $$ = enter(DOT); } + | CCL + { $$ = cclenter(CCL); } + | NCCL + { $$ = cclenter(NCCL); } + ; + +r: r OR r + { $$ = node(OR, $1, $3); } + | r r %prec CAT + { $$ = node(CAT, $1, $2); } + | r STAR + { $$ = unary(STAR, $1); } + | r PLUS + { $$ = unary(PLUS, $1); } + | r QUEST + { $$ = unary(QUEST, $1); } + | '(' r ')' + { $$ = $2; } + | error + ; + +%% +static int +yyerror(char *s) +{ + message = s; + longjmp(env, 1); + return 1; /* silence a warning */ +} + +static int +yylex(void) +{ + int cclcnt, x; + char c, d; + + switch(c = nextch()) { + case '|': + case '\n': + return (OR); + case '*': + return (STAR); + case '+': + return (PLUS); + case '?': + return (QUEST); + case '(': + case ')': + return (c); + case '.': + return (DOT); + case '\0': + return (0); + case '[': + x = CCL; + cclcnt = 0; + count = nxtchar++; + if ((c = nextch()) == '^') { + x = NCCL; + c = nextch(); + } + do { + if (c == '\0') + synerror(); + if ( (c == '-') + && (cclcnt > 0) + && (chars[nxtchar-1] != 0) + ) { + if ((d = nextch()) != 0) { + c = chars[nxtchar-1]; + while ((unsigned int)c < (unsigned int)d) { + if (nxtchar >= MAXLIN) + overflo(); + chars[nxtchar++] = ++c; + cclcnt++; + } + continue; + } /* if() */ + } /* if() */ + if (nxtchar >= MAXLIN) + overflo(); + chars[nxtchar++] = c; + cclcnt++; + } while ((c = nextch()) != ']'); + chars[count] = cclcnt; + return (x); + case '\\': + if ((c = nextch()) == '\0') + synerror(); + yylval = c; + return (CHAR); + case '$': + case '^': + c = '\n'; + yylval = c; + return (CHAR); + default: + yylval = c; + return (CHAR); + } +} + +static void +synerror(void) +{ + yyerror("Syntax error"); +} + +static unsigned int +enter(int x) +{ + if(line >= MAXLIN) + overflo(); + name[line] = x; + left[line] = 0; + right[line] = 0; + return(line++); +} + +static unsigned int +cclenter(int x) +{ + unsigned int linno; + + linno = enter(x); + right[linno] = count; + return (linno); +} + +static int +node(int x, int l, int r) +{ + if(line >= MAXLIN) + overflo(); + name[line] = x; + left[line] = l; + right[line] = r; + parent[l] = line; + parent[r] = line; + return(line++); +} + +static int +unary(int x, int d) +{ + if(line >= MAXLIN) + overflo(); + name[line] = x; + left[line] = d; + right[line] = 0; + parent[d] = line; + return(line++); +} + +static void +overflo(void) +{ + yyerror("internal table overflow"); +} + +static void +cfoll(int v) +{ + unsigned int i; + + if (left[v] == 0) { + count = 0; + for (i = 1; i <= line; i++) + tmpstat[i] = 0; + follow(v); + add(foll, v); + } else if (right[v] == 0) + cfoll(left[v]); + else { + cfoll(left[v]); + cfoll(right[v]); + } +} + +static void +cgotofn(void) +{ + unsigned int i, n, s; + int c, k; + char symbol[NCHARS]; + unsigned int j, l, pc, pos; + unsigned int nc; + int curpos; + unsigned int num, number, newpos; + + count = 0; + for (n=3; n<=line; n++) + tmpstat[n] = 0; + if (cstate(line-1)==0) { + tmpstat[line] = 1; + count++; + out[0] = 1; + } + for (n=3; n<=line; n++) + initstat[n] = tmpstat[n]; + count--; /*leave out position 1 */ + icount = count; + tmpstat[1] = 0; + add(state, 0); + n = 0; + for (s = 0; s <= n; s++) { + if (out[s] == 1) + continue; + for (i = 0; i < NCHARS; i++) + symbol[i] = 0; + num = positions[state[s]]; + count = icount; + for (i = 3; i <= line; i++) + tmpstat[i] = initstat[i]; + pos = state[s] + 1; + for (i = 0; i < num; i++) { + curpos = positions[pos]; + if ((c = name[curpos]) >= 0) { + if (c < NCHARS) { + symbol[c] = 1; + } else if (c == DOT) { + for (k = 0; k < NCHARS; k++) + if (k != '\n') + symbol[k] = 1; + } else if (c == CCL) { + nc = chars[right[curpos]]; + pc = right[curpos] + 1; + for (j = 0; j < nc; j++) + symbol[(unsigned char)chars[pc++]] = 1; + } else if (c == NCCL) { + nc = chars[right[curpos]]; + for (j = 0; j < NCHARS; j++) { + pc = right[curpos] + 1; + for (l = 0; l < nc; l++) + if (j==(unsigned char)chars[pc++]) + goto cont; + if (j != '\n') + symbol[j] = 1; + cont: + ; + } + } + } + pos++; + } /* for (i) */ + for (c=0; c= 0) + if ((k == c) + || (k == DOT) + || (k == CCL && member(c, right[curpos], 1)) + || (k == NCCL && member(c, right[curpos], 0)) + ) { + number = positions[foll[curpos]]; + newpos = foll[curpos] + 1; + for (j = 0; j < number; j++) { + if (tmpstat[positions[newpos]] != 1) { + tmpstat[positions[newpos]] = 1; + count++; + } + newpos++; + } + } + pos++; + } /* end nextstate */ + if (notin(n)) { + if (n >= NSTATES) + overflo(); + add(state, ++n); + if (tmpstat[line] == 1) + out[n] = 1; + gotofn[s][c] = n; + } else { + gotofn[s][c] = xstate; + } + } /* if (symbol) */ + } /* for(c) */ + } /* for(s) */ +} + +static int +cstate(int v) +{ + int b; + if (left[v] == 0) { + if (tmpstat[v] != 1) { + tmpstat[v] = 1; + count++; + } + return(1); + } + else if (right[v] == 0) { + if (cstate(left[v]) == 0) return (0); + else if (name[v] == PLUS) return (1); + else return (0); + } + else if (name[v] == CAT) { + if (cstate(left[v]) == 0 && cstate(right[v]) == 0) return (0); + else return (1); + } + else { /* name[v] == OR */ + b = cstate(right[v]); + if (cstate(left[v]) == 0 || b == 0) return (0); + else return (1); + } +} + +static int +member(int symb, int set, int torf) +{ + unsigned int i, num, pos; + + num = chars[set]; + pos = set + 1; + for (i = 0; i < num; i++) + if (symb == (unsigned char)(chars[pos++])) + return (torf); + return (!torf); +} + +static int +notin(int n) +{ + int i, j, pos; + for (i=0; i<=n; i++) { + if (positions[state[i]] == count) { + pos = state[i] + 1; + for (j=0; j < count; j++) + if (tmpstat[positions[pos++]] != 1) goto nxt; + xstate = i; + return (0); + } + nxt: ; + } + return (1); +} + +static void +add(int *array, int n) +{ + unsigned int i; + + if (nxtpos + count > MAXPOS) + overflo(); + array[n] = nxtpos; + positions[nxtpos++] = count; + for (i=3; i <= line; i++) { + if (tmpstat[i] == 1) { + positions[nxtpos++] = i; + } + } +} + +static void +follow(unsigned int v) +{ + unsigned int p; + + if (v == line) + return; + p = parent[v]; + switch(name[p]) { + case STAR: + case PLUS: cstate(v); + follow(p); + return; + + case OR: + case QUEST: follow(p); + return; + + case CAT: + if (v == left[p]) { + if (cstate(right[p]) == 0) { + follow(p); + return; + } + } else + follow(p); + return; + case FINAL: + if (tmpstat[line] != 1) { + tmpstat[line] = 1; + count++; + } + return; + } +} + +char * +egrepinit(char *egreppat) +{ + /* initialize the global data */ + memset(gotofn, 0, sizeof(gotofn)); + memset(state, 0, sizeof(state)); + memset(out, 0, sizeof(out)); + line = 1; + memset(name, 0, sizeof(name)); + memset(left, 0, sizeof(left)); + memset(right, 0, sizeof(right)); + memset(parent, 0, sizeof(parent)); + memset(foll, 0, sizeof(foll)); + memset(positions, 0, sizeof(positions)); + memset(chars, 0, sizeof(chars)); + nxtpos = 0; + nxtchar = 0; + memset(tmpstat, 0, sizeof(tmpstat)); + memset(initstat, 0, sizeof(initstat)); + xstate = 0; + count = 0; + icount = 0; + input = egreppat; + message = NULL; + if (setjmp(env) == 0) { + yyparse(); + cfoll(line-1); + cgotofn(); + } + return(message); +} + +static char buf[2 * BUFSIZ]; +static const char *buf_end = buf + (sizeof(buf) / sizeof(*buf)); + +static size_t read_next_chunk(char **p, FILE *fptr) +{ + if (*p <= (buf + BUFSIZ)) { + /* bwlow the middle, so enough space left for one entire BUFSIZ */ + return fread(*p, sizeof(**p), BUFSIZ, fptr); + } else if (*p == buf_end) { + /* exactly at end ... wrap around and use lower half */ + *p = buf; + return fread(*p, sizeof(**p), BUFSIZ, fptr); + } + /* somewhere in second half, so do a limited read */ + return fread(*p, sizeof(**p), buf_end - *p, fptr); +} + +int +egrep(char *file, FILE *output, char *format) +{ + char *p; + unsigned int cstat; + int ccount; + char *nlp; + unsigned int istat; + int in_line; + FILE *fptr; + + if ((fptr = myfopen(file, "r")) == NULL) + return(-1); + + lnum = 1; + p = buf; + nlp = p; + ccount = read_next_chunk(&p, fptr); + + if (ccount <= 0) { + fclose(fptr); + return(0); + } + in_line = 1; + istat = cstat = (unsigned int) gotofn[0]['\n']; + if (out[cstat]) + goto found; + for (;;) { + if (!iflag) { + /* all input chars made positive */ + cstat = (unsigned int) gotofn[cstat][(unsigned char)*p]; + } else { + /* for -i option*/ + cstat = (unsigned int) gotofn[cstat][tolower((unsigned char)*p)]; + } + if (out[cstat]) { + found: + for(;;) { + if (*p++ == '\n') { + in_line = 0; + succeed: + fprintf(output, format, file, lnum); + if (p <= nlp) { + while (nlp < buf_end) + putc(*nlp++, output); + nlp = buf; + } + while (nlp < p) + putc(*nlp++, output); + lnum++; + nlp = p; + if (out[cstat = istat] == 0) + goto brk2; + } /* if (p++ == \n) */ + cfound: + if (--ccount <= 0) { + ccount = read_next_chunk(&p, fptr); + if (ccount <= 0) { + if (in_line) { + in_line = 0; + goto succeed; + } + fclose(fptr); + return(0); + } + } /* if(ccount <= 0) */ + in_line = 1; + } /* for(ever) */ + } /* if(out[cstat]) */ + + if (*p++ == '\n') { + in_line = 0; + lnum++; + nlp = p; + if (out[(cstat=istat)]) + goto cfound; + } + brk2: + if (--ccount <= 0) { + ccount = read_next_chunk(&p, fptr); + if (ccount <= 0) + break; + } + in_line = 1; + } + fclose(fptr); + return(0); +} + +void +egrepcaseless(int i) +{ + iflag = i; /* simulate "egrep -i" */ +} diff --git a/src/exec.c b/src/exec.c new file mode 100644 index 0000000..2ab611c --- /dev/null +++ b/src/exec.c @@ -0,0 +1,188 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * process execution functions + */ + +#include +#include "global.h" +#include +#include +#include /* pid_t */ +#ifdef __DJGPP__ +#include +#endif +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +#include +#else +#include +#endif + +static sighandler_t oldsigquit; /* old value of quit signal */ +static sighandler_t oldsighup; /* old value of hangup signal */ +static sighandler_t oldsigtstp; /* old value of SIGTSTP */ + +#ifndef __MSDOS__ /* none of these is needed, there */ +static int join(pid_t p); +static int myexecvp(char *a, char **args); +static pid_t myfork(void); +#endif + +/* execute forks and executes a program or shell script, waits for it to + * finish, and returns its exit code. + */ + +/*VARARGS1*/ +int +execute(char *a, ...) /* note: "exec" is already defined on u370 */ +{ + va_list ap; + int exitcode = -1; /* initialize, to avoid warning */ + char *argv[BUFSIZ]; + pid_t p; + + /* fork and exec the program or shell script */ + endwin(); /* restore the terminal modes */ + mousecleanup(); + fflush(stdout); + va_start(ap, a); + for (p = 0; (argv[p] = va_arg(ap, char *)) != 0; p++) + ; +#ifdef __MSDOS__ + /* HBB 20010313: in MSDOG, everything is completely different. + * No fork()/exec()/wait(), but rather a single libc call: */ + exitcode = spawnvp(P_WAIT, a, argv); +#else + if ((p = myfork()) == 0) { + myexecvp(a, argv); /* child */ + } + else { + exitcode = join(p); /* parent */ + } +#endif /* MSDOS */ + + /* the menu and scrollbar may be changed by the command executed */ +#if UNIXPC || !TERMINFO +# ifndef __DJGPP__ /* leave CRLF handling as is */ + nonl(); +# endif + raw(); /* endwin() turns off cbreak mode so restore it */ + noecho(); +#endif + mousemenu(); + drawscrollbar(topline, nextline); + va_end(ap); + return(exitcode); +} + +#ifndef __MSDOS__ /* None of the following functions is used there */ + +/* myexecvp is an interface to the execvp system call to + * modify argv[0] to reference the last component of its path-name. + */ +static int +myexecvp(char *a, char **args) +{ + char msg[MSGLEN + 1]; + + /* modify argv[0] to reference the last component of its path name */ + args[0] = basename(args[0]); + + /* execute the program or shell script */ + execvp(a, args); /* returns only on failure */ + snprintf(msg, sizeof(msg), "\nCannot exec %s", a); + perror(msg); /* display the reason */ + askforreturn(); /* wait until the user sees the message */ + myexit(1); /* exit the child */ + /* NOTREACHED */ +} + +/* myfork acts like fork but also handles signals */ + +static pid_t +myfork(void) +{ + pid_t p; /* process number */ + + p = fork(); + + /* the parent ignores the interrupt, quit, and hangup signals */ + if (p > 0) { + oldsigquit = signal(SIGQUIT, SIG_IGN); + oldsighup = signal(SIGHUP, SIG_IGN); +#ifdef SIGTSTP + oldsigtstp = signal(SIGTSTP, SIG_DFL); +#endif + } + /* so they can be used to stop the child */ + else if (p == 0) { + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGHUP, SIG_DFL); +#ifdef SIGTSTP + signal(SIGTSTP, SIG_DFL); +#endif + } + /* check for fork failure */ + if (p == -1) { + myperror("Cannot fork"); + } + return p; +} + +/* join is the compliment of fork */ + +static int +join(pid_t p) +{ + int status = -1; + pid_t w; + + /* wait for the correct child to exit */ + do { + w = wait(&status); + } while (p != -1 && w != p); + + /* restore signal handling */ + signal(SIGQUIT, oldsigquit); + signal(SIGHUP, oldsighup); +#ifdef SIGTSTP + signal(SIGTSTP, oldsigtstp); +#endif + + /* return the child's exit code */ + return(status >> 8); +} + +#endif /* !MSDOS */ diff --git a/src/find.c b/src/find.c new file mode 100644 index 0000000..d7a66f0 --- /dev/null +++ b/src/find.c @@ -0,0 +1,1301 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 or text cross-reference + * + * searching functions + */ + +#include "global.h" + +#include "build.h" +#include "scanner.h" /* for token definitions */ + +#include +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +#include +#else +#include +#endif +#include + +/* 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. + */ + +char *blockp; /* pointer to current char in block */ +char block[BUFSIZ + 2]; /* leave room for end-of-block mark */ +int blocklen; /* length of disk block read */ +char blockmark; /* mark character to be searched for */ +long blocknumber; /* block number */ + +static char global[] = ""; /* dummy global function name */ +static char cpattern[PATLEN + 1]; /* compressed pattern */ +static long lastfcnoffset; /* last function name offset */ +static POSTING *postingp; /* retrieved posting set pointer */ +static long postingsfound; /* retrieved number of postings */ +static regex_t regexp; /* regular expression */ +static BOOL isregexp_valid = NO; /* regular expression status */ + +static BOOL match(void); +static BOOL matchrest(void); +static POSTING *getposting(void); +static char *lcasify(char *s); +static void findcalledbysub(char *file, BOOL macro); +static void findterm(char *pattern); +static void putline(FILE *output); +static char *find_symbol_or_assignment(char *pattern, BOOL assign_flag); +static BOOL check_for_assignment(void); +static void putpostingref(POSTING *p, char *pat); +static void putref(int seemore, char *file, char *func); +static void putsource(int seemore, FILE *output); + + /* find the symbol in the cross-reference */ + +char * +findsymbol(char *pattern) +{ + return find_symbol_or_assignment(pattern, NO); +} + + /* find the symbol in the cross-reference, and look for assignments */ +char * +findassign(char *pattern) +{ + return find_symbol_or_assignment(pattern, YES); +} + + /* Test reference whether it's an assignment to the symbol found at + * (global variable) 'blockp' */ +static BOOL +check_for_assignment(void) +{ + /* Do the extra work here to determine if this is an + * assignment or not. Do this by examining the next character + * or two in blockp */ + char *asgn_char = blockp; + + while (isspace((unsigned char) asgn_char[0])) { + /* skip any whitespace or \n */ + asgn_char++; + if (asgn_char[0] == '\0') { + /* get the next block when we reach the end of + * the current block */ + if (NULL == (asgn_char = read_block())) + return NO; + } + } + /* check for digraph starting with = */ + if ((asgn_char[0] & 0x80) && (dichar1[(asgn_char[0] & 0177)/8] == '=')) { + return YES; + } + /* check for plain '=', not '==' */ + if ((asgn_char[0] == '=') && + (((asgn_char[1] != '=') && !(asgn_char[1] & 0x80)) || + ((asgn_char[1] & 0x80) && (dichar1[(asgn_char[1]& 0177)/8] != '=')))) { + return YES; + } + + /* check for operator assignments: +=, ... ^= ? */ + if ( ( (asgn_char[0] == '+') + || (asgn_char[0] == '-') + || (asgn_char[0] == '*') + || (asgn_char[0] == '/') + || (asgn_char[0] == '%') + || (asgn_char[0] == '&') + || (asgn_char[0] == '|') + || (asgn_char[0] == '^') + ) + && ((asgn_char[1] == '=') || ((asgn_char[1] & 0x80) && (dichar1[(asgn_char[1] &0177)/8] == '='))) + + ) { + return YES; + } + + /* check for two-letter operator assignments: <<= or >>= ? */ + if ( ( (asgn_char[0] == '<') + || (asgn_char[0] == '>') + ) + && (asgn_char[1] == asgn_char[0]) + && ((asgn_char[2] == '=') || ((asgn_char[2] & 0x80) && (dichar1[(asgn_char[2] & 0177)/8] == '='))) + ) + return YES; + return NO; +} + + /* The actual routine that does the work for findsymbol() and +* findassign() */ +static char * +find_symbol_or_assignment(char *pattern, BOOL assign_flag) +{ + char file[PATHLEN + 1]; /* source file name */ + char function[PATLEN + 1]; /* function name */ + char macro[PATLEN + 1]; /* macro name */ + char symbol[PATLEN + 1]; /* symbol name */ + char *cp; + char *s; + size_t s_len = 0; + char firstchar; /* first character of a potential symbol */ + BOOL fcndef = NO; + + if ((invertedindex == YES) && (assign_flag == NO)) { + long lastline = 0; + POSTING *p; + + findterm(pattern); + while ((p = getposting()) != NULL) { + if (p->type != INCLUDE && p->lineoffset != lastline) { + putpostingref(p, 0); + lastline = p->lineoffset; + } + } + return NULL; + } + + (void) scanpast('\t'); /* find the end of the header */ + skiprefchar(); /* skip the file marker */ + fetch_string_from_dbase(file, sizeof(file)); + strcpy(function, global); /* set the dummy global function name */ + strcpy(macro, global); /* set the dummy global macro name */ + + /* find the next symbol */ + /* note: this code was expanded in-line for speed */ + /* other macros were replaced by code using cp instead of blockp */ + cp = blockp; + for (;;) { + setmark('\n'); + do { /* innermost loop optimized to only one test */ + while (*cp != '\n') { + ++cp; + } + } while (*(cp + 1) == '\0' && (cp = read_block()) != NULL); + + /* skip the found character */ + if (cp != NULL && *(++cp + 1) == '\0') { + cp = read_block(); + } + if (cp == NULL) { + break; + } + /* look for a source file, function, or macro name */ + if (*cp == '\t') { + blockp = cp; + switch (getrefchar()) { + + case NEWFILE: /* file name */ + + /* save the name */ + skiprefchar(); + fetch_string_from_dbase(file, sizeof(file)); + + /* check for the end of the symbols */ + if (*file == '\0') { + return NULL; + } + progress("Search", searchcount, nsrcfiles); + /* FALLTHROUGH */ + + case FCNEND: /* function end */ + (void) strcpy(function, global); + goto notmatched; /* don't match name */ + + case FCNDEF: /* function name */ + fcndef = YES; + s = function; + s_len = sizeof(function); + break; + + case DEFINE: /* macro name */ + if (fileversion >= 10) { + s = macro; + s_len = sizeof(macro); + } else { + s = symbol; + s_len = sizeof(symbol); + } + break; + + case DEFINEEND: /* macro end */ + (void) strcpy(macro, global); + goto notmatched; + + case INCLUDE: /* #include file */ + goto notmatched; /* don't match name */ + + default: /* other symbol */ + s = symbol; + s_len = sizeof(symbol); + } + /* save the name */ + skiprefchar(); + fetch_string_from_dbase(s, s_len); + + /* see if this is a regular expression pattern */ + if (isregexp_valid == YES) { + if (caseless == YES) { + s = lcasify(s); + } + if (*s != '\0' && regexec (®exp, s, (size_t)0, NULL, 0) == 0) { + goto matched; + } + } + /* match the symbol to the text pattern */ + else if (strequal(pattern, s)) { + goto matched; + } + goto notmatched; + } + /* if this is a regular expression pattern */ + if (isregexp_valid == YES) { + + /* if this is a symbol */ + + /************************************************** + * The first character may be a digraph'ed char, so + * unpack it into firstchar, and then test that. + * + * Assume that all digraphed chars have the 8th bit + * set (0200). + **************************************************/ + if (*cp & 0200) { /* digraph char? */ + firstchar = dichar1[(*cp & 0177) / 8]; + } + else { + firstchar = *cp; + } + + if (isalpha((unsigned char)firstchar) || firstchar == '_') { + blockp = cp; + fetch_string_from_dbase(symbol, sizeof(symbol)); + if (caseless == YES) { + s = lcasify(symbol); /* point to lower case version */ + } + else { + s = symbol; + } + + /* match the symbol to the regular expression */ + if (*s != '\0' && regexec (®exp, s, (size_t)0, NULL, 0) == 0) { + goto matched; + } + goto notmatched; + } + } + /* match the character to the text pattern */ + else if (*cp == cpattern[0]) { + blockp = cp; + + /* match the rest of the symbol to the text pattern */ + if (matchrest()) { + s = NULL; + matched: + /* if the assignment flag is set then + * we are looking for assignments and + * some extra filtering is needed */ + if(assign_flag == YES + && ! check_for_assignment()) + goto notmatched; + + + /* output the file, function or macro, and source line */ + if (strcmp(macro, global) && s != macro) { + putref(0, file, macro); + } + else if (fcndef == YES || s != function) { + fcndef = NO; + putref(0, file, function); + } + else { + putref(0, file, global); + } + } + notmatched: + if (blockp == NULL) { + return NULL; + } + fcndef = NO; + cp = blockp; + } + } + blockp = cp; + + return NULL; +} + /* find the function definition or #define */ + +char * +finddef(char *pattern) +{ + char file[PATHLEN + 1]; /* source file name */ + + if (invertedindex == YES) { + POSTING *p; + + findterm(pattern); + 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 */ + putpostingref(p, pattern); + } + } + return NULL; + } + + + /* find the next file name or definition */ + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case NEWFILE: + skiprefchar(); /* save file name */ + fetch_string_from_dbase(file, sizeof(file)); + if (*file == '\0') { /* if end of symbols */ + return NULL; + } + progress("Search", searchcount, nsrcfiles); + break; + + 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 */ + skiprefchar(); /* match name to pattern */ + if (match()) { + + /* output the file, function and source line */ + putref(0, file, pattern); + } + break; + } + } + + return NULL; +} + /* find all function definitions (used by samuel only) */ + +char * +findallfcns(char *dummy) +{ + char file[PATHLEN + 1]; /* source file name */ + char function[PATLEN + 1]; /* function name */ + + (void) dummy; /* unused argument */ + + /* find the next file name or definition */ + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case NEWFILE: + skiprefchar(); /* save file name */ + fetch_string_from_dbase(file, sizeof(file)); + if (*file == '\0') { /* if end of symbols */ + return NULL; + } + progress("Search", searchcount, nsrcfiles); + /* FALLTHROUGH */ + + case FCNEND: /* function end */ + (void) strcpy(function, global); + break; + + case FCNDEF: + case CLASSDEF: + skiprefchar(); /* save function name */ + fetch_string_from_dbase(function, sizeof(function)); + + /* output the file, function and source line */ + putref(0, file, function); + break; + } + } + return NULL; +} + + /* find the functions calling this function */ + +char * +findcalling(char *pattern) +{ + char file[PATHLEN + 1]; /* source file name */ + char function[PATLEN + 1]; /* function name */ + char tmpfunc[10][PATLEN + 1];/* 10 temporary function names */ + char macro[PATLEN + 1]; /* macro name */ + char *tmpblockp; + int morefuns, i; + + if (invertedindex == YES) { + POSTING *p; + + findterm(pattern); + while ((p = getposting()) != NULL) { + if (p->type == FCNCALL) { + putpostingref(p, 0); + } + } + return NULL; + } + /* find the next file name or function definition */ + *macro = '\0'; /* a macro can be inside a function, but not vice versa */ + tmpblockp = 0; + morefuns = 0; /* one function definition is normal case */ + for (i = 0; i < 10; i++) *(tmpfunc[i]) = '\0'; + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case NEWFILE: /* save file name */ + skiprefchar(); + fetch_string_from_dbase(file, sizeof(file)); + if (*file == '\0') { /* if end of symbols */ + return NULL; + } + progress("Search", searchcount, nsrcfiles); + (void) strcpy(function, global); + break; + + case DEFINE: /* could be a macro */ + if (fileversion >= 10) { + skiprefchar(); + fetch_string_from_dbase(macro, sizeof(macro)); + } + break; + + case DEFINEEND: + *macro = '\0'; + break; + + case FCNDEF: /* save calling function name */ + skiprefchar(); + fetch_string_from_dbase(function, sizeof(function)); + for (i = 0; i < morefuns; i++) + if ( !strcmp(tmpfunc[i], function) ) + break; + if (i == morefuns) { + (void) strcpy(tmpfunc[morefuns], function); + if (++morefuns >= 10) morefuns = 9; + } + break; + + case FCNEND: + for (i = 0; i < morefuns; i++) + *(tmpfunc[i]) = '\0'; + morefuns = 0; + break; + + case FCNCALL: /* match function called to pattern */ + skiprefchar(); + if (match()) { + + /* output the file, calling function or macro, and source */ + if (*macro != '\0') { + putref(1, file, macro); + } + else { + tmpblockp = blockp; + for (i = 0; i < morefuns; i++) { + blockp = tmpblockp; + putref(1, file, tmpfunc[i]); + } + } + } + } + } + + return NULL; +} + +/* find the text in the source files */ + +char * +findstring(char *pattern) +{ + char egreppat[2 * PATLEN]; + char *cp, *pp; + + /* translate special characters in the regular expression */ + cp = egreppat; + for (pp = pattern; *pp != '\0'; ++pp) { + if (strchr(".*[\\^$+?|()", *pp) != NULL) { + *cp++ = '\\'; + } + *cp++ = *pp; + } + *cp = '\0'; + + /* search the source files */ + return(findregexp(egreppat)); +} + +/* find this regular expression in the source files */ + +char * +findregexp(char *egreppat) +{ + unsigned int i; + char *egreperror; + + /* compile the pattern */ + if ((egreperror = egrepinit(egreppat)) == NULL) { + + /* search the files */ + for (i = 0; i < nsrcfiles; ++i) { + char *file = filepath(srcfiles[i]); + + progress("Search", searchcount, nsrcfiles); + if (egrep(file, refsfound, "%s %ld ") < 0) { + posterr ("Cannot open file %s", file); + } + } + } + return(egreperror); +} + +/* find matching file names */ + +char * +findfile(char *dummy) +{ + unsigned int i; + + (void) dummy; /* unused argument */ + + for (i = 0; i < nsrcfiles; ++i) { + char *s; + + if (caseless == YES) { + s = lcasify(srcfiles[i]); + } else { + s = srcfiles[i]; + } + if (regexec (®exp, s, (size_t)0, NULL, 0) == 0) { + (void) fprintf(refsfound, "%s 1 \n", + srcfiles[i]); + } + } + + return NULL; +} + +/* find files #including this file */ + +char * +findinclude(char *pattern) +{ + char file[PATHLEN + 1]; /* source file name */ + + if (invertedindex == YES) { + POSTING *p; + + findterm(pattern); + while ((p = getposting()) != NULL) { + if (p->type == INCLUDE) { + putpostingref(p, 0); + } + } + return NULL; + } + + /* find the next file name or function definition */ + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case NEWFILE: /* save file name */ + skiprefchar(); + fetch_string_from_dbase(file, sizeof(file)); + if (*file == '\0') { /* if end of symbols */ + return NULL; + } + progress("Search", searchcount, nsrcfiles); + break; + + case INCLUDE: /* match function called to pattern */ + skiprefchar(); + skiprefchar(); /* skip global or local #include marker */ + if (match()) { + + /* output the file and source line */ + putref(0, file, global); + } + } + } + + return NULL; +} + +/* initialize */ + +FINDINIT +findinit(char *pattern) +{ + char buf[PATLEN + 3]; + BOOL isregexp = NO; + int i; + char *s; + unsigned char c; /* HBB 20010427: changed uint to uchar */ + + /* HBB: be nice: free regexp before allocating a new one */ + if(isregexp_valid == YES) + regfree(®exp); + + isregexp_valid = NO; + + /* remove trailing white space */ + for (s = pattern + strlen(pattern) - 1; + isspace((unsigned char)*s); + --s) { + *s = '\0'; + } + + /* HBB 20020620: new: make sure pattern is lowercased. Curses + * mode gets this right all on its own, but at least -L mode + * doesn't */ + if (caseless == YES) { + pattern = lcasify(pattern); + } + + /* allow a partial match for a file name */ + if (field == FILENAME || field == INCLUDES) { + if (regcomp (®exp, pattern, REG_EXTENDED | REG_NOSUB) != 0) { + return(REGCMPERROR); + } else { + isregexp_valid = YES; + } + return(NOERROR); + } + /* see if the pattern is a regular expression */ + if (strpbrk(pattern, "^.[{*+$|(") != NULL) { + isregexp = YES; + } else { + /* check for a valid C symbol */ + s = pattern; + if (!isalpha((unsigned char)*s) && *s != '_') { + return(NOTSYMBOL); + } + while (*++s != '\0') { + if (!isalnum((unsigned char)*s) && *s != '_') { + return(NOTSYMBOL); + } + } + /* look for use of the -T option (truncate symbol to 8 + characters) on a database not built with -T */ + if (trun_syms == YES && isuptodate == YES && + dbtruncated == NO && s - pattern >= 8) { + (void) strcpy(pattern + 8, ".*"); + isregexp = YES; + } + } + /* if this is a regular expression or letter case is to be ignored */ + /* or there is an inverted index */ + if (isregexp == YES || caseless == YES || invertedindex == YES) { + + /* remove a leading ^ */ + s = pattern; + if (*s == '^') { + (void) strcpy(newpat, s + 1); + (void) strcpy(s, newpat); + } + /* remove a trailing $ */ + i = strlen(s) - 1; + if (s[i] == '$') { + if (i > 0 && s[i-1] == '\\' ) { + s[i-1] = '$'; + } + s[i] = '\0'; + } + /* if requested, try to truncate a C symbol pattern */ + if (trun_syms == YES && strpbrk(s, "[{*+") == NULL) { + s[8] = '\0'; + } + /* must be an exact match */ + /* note: regcomp doesn't recognize ^*keypad$ as a syntax error + unless it is given as a single arg */ + (void) snprintf(buf, sizeof(buf), "^%s$", s); + if (regcomp (®exp, buf, REG_EXTENDED | REG_NOSUB) != 0) { + return(REGCMPERROR); + } + else + { + isregexp_valid = YES; + } + } + else { + /* if requested, truncate a C symbol pattern */ + if (trun_syms == YES && field <= CALLING) { + pattern[8] = '\0'; + } + /* compress the string pattern for matching */ + s = cpattern; + for (i = 0; (c = pattern[i]) != '\0'; ++i) { + if (IS_A_DICODE(c, pattern[i + 1])) { + c = DICODE_COMPRESS(c, pattern[i + 1]); + ++i; + } + *s++ = c; + } + *s = '\0'; + } + return(NOERROR); +} + +void +findcleanup(void) +{ + /* discard any regular expression */ +} + +/* match the pattern to the string */ + +static BOOL +match(void) +{ + char string[PATLEN + 1]; + + /* see if this is a regular expression pattern */ + if (isregexp_valid == YES) { + fetch_string_from_dbase(string, sizeof(string)); + if (*string == '\0') { + return(NO); + } + if (caseless == YES) { + return (regexec (®exp, lcasify(string), (size_t)0, NULL, 0) ? NO : YES); + } + else { + return (regexec (®exp, string, (size_t)0, NULL, 0) ? NO : YES); + } + } + /* it is a string pattern */ + return((BOOL) (*blockp == cpattern[0] && matchrest())); +} + +/* match the rest of the pattern to the name */ + +static BOOL +matchrest(void) +{ + int i = 1; + + skiprefchar(); + do { + while (*blockp == cpattern[i]) { + ++blockp; + ++i; + } + } while (*(blockp + 1) == '\0' && read_block() != NULL); + + if (*blockp == '\n' && cpattern[i] == '\0') { + return(YES); + } + return(NO); +} + +/* put the reference into the file */ + +static void +putref(int seemore, char *file, char *func) +{ + FILE *output; + + if (strcmp(func, global) == 0) { + output = refsfound; + } + else { + output = nonglobalrefs; + } + (void) fprintf(output, "%s %s ", file, func); + putsource(seemore, output); +} + +/* put the source line into the file */ + +static void +putsource(int seemore, FILE *output) +{ + char *tmpblockp; + char *cp, nextc = '\0'; + BOOL Change = NO, retreat = NO; + + if (fileversion <= 5) { + (void) scanpast(' '); + putline(output); + (void) putc('\n', output); + return; + } + /* scan back to the beginning of the source line */ + cp = tmpblockp = blockp; + while (*cp != '\n' || nextc != '\n') { + nextc = *cp; + if (--cp < block) { + retreat = YES; + /* read the previous block */ + (void) dbseek((blocknumber - 1) * BUFSIZ); + cp = block + (BUFSIZ - 1); + } + } + blockp = cp; + if (*blockp != '\n' || getrefchar() != '\n' || + (!isdigit(getrefchar()) && fileversion >= 12)) { + postfatal("Internal error: cannot get source line from database"); + /* NOTREACHED */ + } + /* until a double newline is found */ + do { + /* skip a symbol type */ + if (*blockp == '\t') { + /* if retreat == YES, that means tmpblockp and blockp + * point to different blocks. Offset comparison should + * NOT be performed until they point to the same block. + */ + if (seemore && Change == NO && retreat == NO && + blockp > tmpblockp) { + Change = YES; + cp = blockp; + } + skiprefchar(); + skiprefchar(); + } + /* output a piece of the source line */ + putline(output); + if (retreat == YES) retreat = NO; + } while (blockp != NULL && getrefchar() != '\n'); + (void) putc('\n', output); + if (Change == YES) blockp = cp; +} + +/* put the rest of the cross-reference line into the file */ + +static void +putline(FILE *output) +{ + char *cp; + unsigned c; + + setmark('\n'); + cp = blockp; + do { + while ((c = (unsigned)(*cp)) != '\n') { + + /* check for a compressed digraph */ + if (c > '\177') { + c &= 0177; + (void) putc(dichar1[c / 8], output); + (void) putc(dichar2[c & 7], output); + } + /* check for a compressed keyword */ + else if (c < ' ') { + (void) fputs(keyword[c].text, output); + if (keyword[c].delim != '\0') { + (void) putc(' ', output); + } + if (keyword[c].delim == '(') { + (void) putc('(', output); + } + } + else { + (void) putc((int) c, output); + } + ++cp; + } + } while (*(cp + 1) == '\0' && (cp = read_block()) != NULL); + blockp = cp; +} + + +/* put the rest of the cross-reference line into the string */ +void +fetch_string_from_dbase(char *s, size_t length) +{ + char *cp; + unsigned int c; + + assert(length > sizeof (char *)); + + setmark('\n'); + cp = blockp; + do { + while (length > 1 && (c = (unsigned int)(*cp)) != '\n') { + if (c >= 0x80 && length > 2) { + c &= 0x7f; + *s++ = dichar1[c / 8]; + *s++ = dichar2[c & 7]; + length -= 2; + } else { + *s++ = c; + length--; + } + ++cp; + } + } while (length > 0 && cp[1] == '\0' && (cp = read_block()) != NULL); + blockp = cp; + *s = '\0'; +} + + +/* scan past the next occurence of this character in the cross-reference */ +char * +scanpast(char c) +{ + char *cp; + + setmark(c); + cp = blockp; + do { /* innermost loop optimized to only one test */ + while (*cp != c) { + ++cp; + } + } while (*(cp + 1) == '\0' && (cp = read_block()) != NULL); + blockp = cp; + if (cp != NULL) { + skiprefchar(); /* skip the found character */ + } + return(blockp); +} + +/* read a block of the cross-reference */ +/* HBB 20040430: renamed from readblock(), to avoid name clash on QNX */ +char * +read_block(void) +{ + /* read the next block */ + blocklen = read(symrefs, block, BUFSIZ); + blockp = block; + + /* add the search character and end-of-block mark */ + block[blocklen] = blockmark; + block[blocklen + 1] = '\0'; + + /* return NULL on end-of-file */ + if (blocklen == 0) { + blockp = NULL; + } + else { + ++blocknumber; + } + return(blockp); +} + +static char * +lcasify(char *s) +{ + static char ls[PATLEN+1]; /* largest possible match string */ + char *lptr = ls; + + while(*s) { + *lptr = tolower((unsigned char)*s); + lptr++; + s++; + } + *lptr = '\0'; + return(ls); +} + +/* find the functions called by this function */ + +/* HBB 2000/05/05: for consitency of calling interface between the + * different 'find...()' functions, this now returns a char pointer, + * too. Implemented as a pointer to static storage containing 'y' or + * 'n', for the boolean result values YES and NO */ + +char * +findcalledby(char *pattern) +{ + char file[PATHLEN + 1]; /* source file name */ + static char found_caller = 'n'; /* seen calling function? */ + BOOL macro = NO; + + if (invertedindex == YES) { + POSTING *p; + + findterm(pattern); + while ((p = getposting()) != NULL) { + switch (p->type) { + case DEFINE: /* could be a macro */ + case FCNDEF: + if (dbseek(p->lineoffset) != -1 && + scanpast('\t') != NULL) { /* skip def */ + found_caller = 'y'; + findcalledbysub(srcfiles[p->fileindex], macro); + } + } + } + return(&found_caller); + } + /* find the function definition(s) */ + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case NEWFILE: + skiprefchar(); /* save file name */ + fetch_string_from_dbase(file, sizeof(file)); + if (*file == '\0') { /* if end of symbols */ + return(&found_caller); + } + progress("Search", searchcount, nsrcfiles); + break; + + case DEFINE: /* could be a macro */ + if (fileversion < 10) { + break; + } + macro = YES; + /* FALLTHROUGH */ + + case FCNDEF: + skiprefchar(); /* match name to pattern */ + if (match()) { + found_caller = 'y'; + findcalledbysub(file, macro); + } + break; + } + } + + return (&found_caller); +} + +/* find this term, which can be a regular expression */ + +static void +findterm(char *pattern) +{ + char *s; + int len; + char prefix[PATLEN + 1]; + char term[PATLEN + 1]; + + 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 */ + (void) strcpy(prefix, pattern); + if ((s = strpbrk(prefix, ".[{*+")) != NULL) { + *s = '\0'; + } + /* if letter case is to be ignored */ + if (caseless == YES) { + + /* convert the prefix to upper case because it is lexically + less than lower case */ + s = prefix; + while (*s != '\0') { + *s = toupper((unsigned char)*s); + ++s; + } + } + /* find the term lexically >= the prefix */ + (void) invfind(&invcontrol, prefix); + if (caseless == YES) { /* restore lower case */ + (void) strcpy(prefix, lcasify(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); + } + len = strlen(prefix); + do { + (void) invterm(&invcontrol, term); /* get the term */ + s = term; + if (caseless == YES) { + s = lcasify(s); /* make it lower case */ + } + /* if it matches */ + if (regexec (®exp, s, (size_t)0, NULL, 0) == 0) { + + /* add its postings to the set */ + if ((postingp = boolfile(&invcontrol, &npostings, BOOL_OR)) == NULL) { + break; + } + } + /* if there is a prefix */ + else if (len > 0) { + + /* if ignoring letter case and the term is out of the + range of possible matches */ + if (caseless == YES) { + if (strncmp(term, prefix, len) > 0) { + break; /* stop searching */ + } + } + /* if using letter case and the prefix doesn't match */ + else if (strncmp(term, prefix, len) != 0) { + break; /* stop searching */ + } + } + /* display progress about every three seconds */ + if (++searchcount % 50 == 0) { + progress("Symbols matched", searchcount, totalterms); + } + } while (invforward(&invcontrol)); /* while didn't wrap around */ + + /* initialize the progress message for retrieving the references */ + searchcount = 0; + postingsfound = npostings; +} + +/* 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("Possible references retrieved", searchcount, + postingsfound); + } + return(postingp++); +} + +/* put the posting reference into the file */ + +static void +putpostingref(POSTING *p, char *pat) +{ + // initialize function to "unknown" so that the first line of temp1 + // is properly formed if symbol matches a header file entry first time + static char function[PATLEN + 1] = "unknown";/* function name */ + + if (p->fcnoffset == 0) { + if (p->type == FCNDEF) { /* need to find the function name */ + if (dbseek(p->lineoffset) != -1) { + scanpast(FCNDEF); + fetch_string_from_dbase(function, sizeof(function)); + } + } + else if (p->type != FCNCALL) { + strcpy(function, global); + } + } + else if (p->fcnoffset != lastfcnoffset) { + if (dbseek(p->fcnoffset) != -1) { + fetch_string_from_dbase(function, sizeof(function)); + lastfcnoffset = p->fcnoffset; + } + } + if (dbseek(p->lineoffset) != -1) { + if (pat) + putref(0, srcfiles[p->fileindex], pat); + else + putref(0, srcfiles[p->fileindex], function); + } +} + +/* seek to the database offset */ + +long +dbseek(long offset) +{ + long n; + int rc = 0; + + if ((n = offset / BUFSIZ) != blocknumber) { + if ((rc = lseek(symrefs, n * BUFSIZ, 0)) == -1) { + myperror("Lseek failed"); + (void) sleep(3); + return(rc); + } + (void) read_block(); + blocknumber = n; + } + blockp = block + offset % BUFSIZ; + return(rc); +} + +static void +findcalledbysub(char *file, BOOL macro) +{ + /* find the next function call or the end of this function */ + while (scanpast('\t') != NULL) { + switch (*blockp) { + + case DEFINE: /* #define inside a function */ + if (fileversion >= 10) { /* skip it */ + while (scanpast('\t') != NULL && + *blockp != DEFINEEND) + ; + } + break; + + case FCNCALL: /* function call */ + + /* output the file name */ + (void) fprintf(refsfound, "%s ", file); + + /* output the function name */ + skiprefchar(); + putline(refsfound); + (void) putc(' ', refsfound); + + /* output the source line */ + putsource(1, refsfound); + break; + + case DEFINEEND: /* #define end */ + + if (invertedindex == NO) { + if (macro == YES) { + return; + } + break; /* inside a function */ + } + /* FALLTHROUGH */ + + case FCNDEF: /* function end (pre 9.5) */ + + if (invertedindex == NO) break; + /* FALLTHROUGH */ + + case FCNEND: /* function end */ + case NEWFILE: /* file end */ + return; + } + } +} diff --git a/src/fscanner.l b/src/fscanner.l new file mode 100644 index 0000000..5d6e8d1 --- /dev/null +++ b/src/fscanner.l @@ -0,0 +1,904 @@ +%{ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * C symbol scanner + */ +#include "global.h" + +#include "scanner.h" +#include "lookup.h" + +#include + +/* the line counting has been moved from character reading for speed */ +/* comments are discarded */ + +#ifndef FLEX_SCANNER +# error Sorry, this scanner needs flex. It is not usable with AT&T Lex. +#endif + +#define IFLEVELINC 5 /* #if nesting level size increment */ +#define YY_NO_TOP_STATE 1 + +int first; /* buffer index for first char of symbol */ +int last; /* buffer index for last char of symbol */ +int lineno; /* symbol line number */ +int myylineno = 1; + +/* HBB 20001007: new variables, emulating yytext in a way that allows + * the yymore() simulation, my_yymore(), to be used even in the presence of + * yyless(). */ +size_t my_yyleng = 0; +char *my_yytext = NULL; + +static BOOL arraydimension; /* inside array dimension declaration */ +static BOOL bplisting; /* breakpoint listing */ +static int braces; /* unmatched left brace count */ +static BOOL classdef; /* c++ class definition */ +static BOOL elseelif; /* #else or #elif found */ +static BOOL esudef; /* enum/struct/union global definition */ +static BOOL external; /* external definition */ +static int externalbraces; /* external definition outer brace count */ +static BOOL fcndef; /* function definition */ +static BOOL global; /* file global scope (outside functions) */ +static size_t iflevel; /* #if nesting level */ +static BOOL initializer; /* data initializer */ +static int initializerbraces; /* data initializer outer brace count */ +static BOOL lex; /* lex file */ +static size_t miflevel = IFLEVELINC; /* maximum #if nesting level */ +static int *maxifbraces; /* maximum brace count within #if */ +static int *preifbraces; /* brace count before #if */ +static int parens; /* unmatched left parenthesis count */ +static BOOL ppdefine; /* preprocessor define statement */ +static BOOL pseudoelif; /* pseudo-#elif */ +static BOOL oldtype; /* next identifier is an old type */ +static BOOL rules; /* lex/yacc rules */ +static BOOL sdl; /* sdl file */ +static BOOL structfield; /* structure field declaration */ +static int tagdef; /* class/enum/struct/union tag definition */ +static BOOL template; /* function template */ +static int templateparens; /* function template outer parentheses count */ +static int typedefbraces = -1; /* initial typedef brace count */ +static int token; /* token found */ +static int ident_start; /* begin of preceding identifier */ + +static void my_yymore(void); + +%} +identifier [a-zA-Z_$][a-zA-Z_0-9$]* +number \.?[0-9][.0-9a-fA-FlLuUxX]* +comment "/*"([^*]*("*"+[^/])?)*"*/"|"//"[^\n]*\n +ws [ \t\r\v\f] +wsnl [ \t\r\v\f\n]|{comment} + +/* flex options: stack of start conditions, and don't use yywrap() */ +%option stack +%option noyywrap + +%start SDL +%a 4000 +%o 7000 + +/* exclusive start conditions. not available in AT&T lex -> use flex! */ +%x IN_PREPROC WAS_ENDIF WAS_IDENTIFIER WAS_ESU IN_DQUOTE IN_SQUOTE COMMENT + +%% + +%\{ { /* lex/yacc C declarations/definitions */ + global = YES; + goto more; + /* NOTREACHED */ + } +%\} { + global = NO; + goto more; + /* NOTREACHED */ + } +^%% { /* lex/yacc rules delimiter */ + braces = 0; + if (rules == NO) { + /* this %% starts the section containing the rules */ + rules = YES; + + /* Copy yytext to private buffer, to be able to add further + * content following it: */ + my_yymore(); + + /* simulate a yylex() or yyparse() definition */ + (void) strcat(my_yytext, " /* "); + first = strlen(my_yytext); + if (lex == YES) { + (void) strcat(my_yytext, "yylex"); + } else { + /* yacc: yyparse implicitly calls yylex */ + char *s = " yylex()"; + char *cp = s + strlen(s); + while (--cp >= s) { + unput(*cp); + } + (void) strcat(my_yytext, "yyparse"); + } + last = strlen(my_yytext); + (void) strcat(my_yytext, " */"); + my_yyleng = strlen(my_yytext); + return(FCNDEF); + } else { + /* were in the rules section, now comes the closing one */ + rules = NO; + global = YES; + last = first; + my_yymore(); + return(FCNEND); + /* NOTREACHED */ + } + } + +STATE[ \t]+({identifier}|\*) { /* sdl state, treat as function def */ + braces = 1; + fcndef = YES; + token = FCNDEF; + goto findident; + /* NOTREACHED */ + } +ENDSTATE[ \t] { /* end of an sdl state, treat as end of a function */ + goto endstate; + /* NOTREACHED */ + } + +\{ { /* count unmatched left braces for fcn def detection */ + ++braces; + + /* mark an untagged enum/struct/union so its beginning + can be found */ + if (tagdef) { + if (braces == 1) { + esudef = YES; + } + token = tagdef; + tagdef = '\0'; + last = first; + my_yymore(); + return(token); + } + goto more; + /* NOTREACHED */ + } + +\#{ws}* { /* start a preprocessor line */ + if (rules == NO) /* don't consider CPP for lex/yacc rules */ + BEGIN(IN_PREPROC); + yyleng = 1; /* get rid of the blanks, if any */ + goto more; + /* NOTREACHED */ + } +endif([^a-zA-Z0-9_$\n].*)? { /* #endif */ + /* delay treatment of #endif depending on whether an + * #if comes right after it, or not */ + /* HBB 20010619: new pattern allows trailing garbage + * after the #endif */ + BEGIN(WAS_ENDIF); + goto more; + /* NOTREACHED */ + } +\n{wsnl}*#{ws}*if(ndef|def)?{ws}+ { + /* attempt to correct erroneous brace count caused by: + * + * #if ... + * ... { + * #endif + * #if ... + * ... { + * #endif + */ + /* the current #if must not have an #else or #elif */ + if (elseelif == YES) { + goto endif; + /* NOTREACHED */ + } + pseudoelif = YES; + BEGIN(INITIAL); + yyless(1); /* rescan all but the line ending */ + yy_set_bol(1); + goto eol; + /* NOTREACHED */ + } +\n{wsnl}* { /* an #endif with no #if right after it */ + endif: + if (iflevel > 0) { + /* get the maximum brace count for this #if */ + if (braces < maxifbraces[--iflevel]) { + braces = maxifbraces[iflevel]; + } + } + BEGIN(INITIAL); + yyless(1); + yy_set_bol(1); + goto eol; + /* NOTREACHED */ + } + +ifndef{ws}+ | +ifdef{ws}+ | +if{ws}+ { /* #if directive */ + elseelif = NO; + if (pseudoelif == YES) { + pseudoelif = NO; + goto elif; + /* NOTREACHED */ + } + /* make sure there is room for the current brace count */ + if (iflevel == miflevel) { + miflevel += IFLEVELINC; + maxifbraces = realloc(maxifbraces, miflevel * sizeof(*maxifbraces)); + preifbraces = realloc(preifbraces, miflevel * sizeof(*preifbraces)); + } + /* push the current brace count */ + preifbraces[iflevel] = braces; + maxifbraces[iflevel++] = 0; + BEGIN(INITIAL); + goto more; + /* NOTREACHED */ + } +else({ws}.*)? { /* #else --- eat up whole line */ + elseelif = YES; + if (iflevel > 0) { + + /* save the maximum brace count for this #if */ + if (braces > maxifbraces[iflevel - 1]) { + maxifbraces[iflevel - 1] = braces; + } + /* restore the brace count to before the #if */ + braces = preifbraces[iflevel - 1]; + } + BEGIN(INITIAL); + goto more; + /* NOTREACHED */ + } +elif{ws}+ { /* #elif */ + /* elseelif = YES; --- HBB I doubt this is correct */ + elif: + if (iflevel > 0) { + + /* save the maximum brace count for this #if */ + if (braces > maxifbraces[iflevel - 1]) { + maxifbraces[iflevel - 1] = braces; + } + /* restore the brace count to before the #if */ + braces = preifbraces[iflevel - 1]; + } + BEGIN(INITIAL); + goto more; + /* NOTREACHED */ + } + +include{ws}*\"[^"\n]+\" | +include{ws}*<[^>\n]+> { /* #include file */ + char *s; + char remember = yytext[yyleng-1]; + + my_yymore(); + s = strpbrk(my_yytext, "\"<"); + if (!s) + return(LEXERR); + my_yytext[my_yyleng-1] = '\0'; + incfile(s + 1, s); + my_yytext[my_yyleng-1] = remember; + first = s - my_yytext; + last = my_yyleng - 1; + if (compress == YES) { + my_yytext[0] = '\2'; /* compress the keyword */ + } + BEGIN(INITIAL); + return(INCLUDE); + /* NOTREACHED */ + } + +\} { + /* could be the last enum member initializer */ + if (braces == initializerbraces) { + initializerbraces = -1; + initializer = NO; + } + if (--braces <= 0) { + endstate: + braces = 0; + classdef = NO; + } + if (braces == 0 || (braces == 1 && classdef == YES)) { + + /* if the end of an enum/struct/union definition */ + if (esudef == YES) { + esudef = NO; + } + /* if the end of the function */ + else if (fcndef == YES) { + fcndef = NO; + last = first; + my_yymore(); + return(FCNEND); + } + } + goto more; + /* NOTREACHED */ + } + +\( { /* count unmatched left parentheses for function templates */ + ++parens; + goto more; + /* NOTREACHED */ + } +\) { + if (--parens <= 0) { + parens = 0; + } + /* if the end of a function template */ + if (parens == templateparens) { + templateparens = -1; + template = NO; + } + goto more; + /* NOTREACHED */ + } += { /* if a global definition initializer */ + if (!my_yytext) + return(LEXERR); + if (global == YES && ppdefine == NO && my_yytext[0] != '#') { + initializerbraces = braces; + initializer = YES; + } + goto more; + /* NOTREACHED */ + } +: { /* a if global structure field */ + if (!my_yytext) + return(LEXERR); + if (global == YES && ppdefine == NO && my_yytext[0] != '#') { + structfield = YES; + } + goto more; + /* NOTREACHED */ + } +\, { + if (braces == initializerbraces) { + initializerbraces = -1; + initializer = NO; + } + structfield = NO; + goto more; + /* NOTREACHED */ + } +; { /* if the enum/struct/union was not a definition */ + if (braces == 0) { + esudef = NO; + } + /* if the end of a typedef */ + if (braces == typedefbraces) { + typedefbraces = -1; + } + /* if the end of a external definition */ + if (braces == externalbraces) { + externalbraces = -1; + external = NO; + } + structfield = NO; + initializer = NO; + goto more; + /* NOTREACHED */ + } +define{ws}+{identifier} { + + /* preprocessor macro or constant definition */ + ppdefine = YES; + token = DEFINE; + if (compress == YES) { + my_yytext[0] = '\1'; /* compress the keyword */ + } + findident: + /* search backwards through yytext[] to find the identifier */ + /* NOTE: this had better be left to flex, by use of + * yet another starting condition */ + my_yymore(); + first = my_yyleng - 1; + while (my_yytext[first] != ' ' && my_yytext[first] != '\t') { + --first; + } + ++first; + last = my_yyleng; + BEGIN(INITIAL); + goto definition; + /* NOTREACHED */ + } +\n { /* unknown preprocessor line */ + BEGIN(INITIAL); + ++myylineno; + goto more; + /* NOTREACHED */ + } +. | +{identifier} { /* unknown preprocessor line */ + BEGIN(INITIAL); + goto more; + /* NOTREACHED */ + } + +class{wsnl}+{identifier}({wsnl}|{identifier}|[():])*\{ { /* class definition */ + classdef = YES; + tagdef = 'c'; + yyless(5); /* eat up 'class', and re-scan */ + yy_set_bol(0); + goto more; + /* NOTREACHED */ + } + +("enum"|"struct"|"union") { + ident_start = first; + BEGIN(WAS_ESU); + goto more; + } +{ +({wsnl}+{identifier}){wsnl}*\{ { /* e/s/u definition */ + tagdef = my_yytext[ident_start]; + BEGIN(WAS_IDENTIFIER); + goto ident; + } +{wsnl}*\{ { /* e/s/u definition without a tag */ + tagdef = my_yytext[ident_start]; + BEGIN(INITIAL); + if (braces == 0) { + esudef = YES; + } + last = first; + yyless(0); /* re-scan all this as normal text */ + tagdef = '\0'; + goto more; + } +({wsnl}+{identifier})?{wsnl}* | +.|\n { /* e/s/u usage */ + BEGIN(WAS_IDENTIFIER); + goto ident; + } +} + +if{wsnl}*\( { /* ignore 'if' */ + yyless(2); + yy_set_bol(0); + goto more; +} + +{identifier} { /* identifier found: do nothing, yet. (!) */ + BEGIN(WAS_IDENTIFIER); + ident_start = first; + goto more; + /* NOTREACHED */ + } + +{ +{ws}*\(({wsnl}|{identifier}|{number}|[*&[\]=,.:])*\)([()]|{wsnl})*[:a-zA-Z_#{] { + /* a function definition */ + /* note: "#define a (b) {" and "#if defined(a)\n#" + * are not fcn definitions! */ + /* warning: "if (...)" must not overflow yytext, + * so the content of function argument definitions + * is restricted, in particular parentheses are + * not allowed */ + /* FIXME HBB 20001003: the above 'not allowed' may well be the + * reason for the parsing bug concerning function pointer usage, + * I suspect. --- I think my new special-case rule for 'if' + * could be helpful in removing that limitation */ + if ((braces == 0 && ppdefine == NO && my_yytext[0] != '#' && rules == NO) || + (braces == 1 && classdef == YES)) { + fcndef = YES; + token = FCNDEF; + goto fcn; + /* NOTREACHED */ + } + goto fcncal; + /* NOTREACHED */ + } +{ws}*\(([*&[\]=,.]|{identifier}|{number}|{wsnl})* { /* function call */ + fcncal: if (fcndef == YES || ppdefine == YES || rules == YES) { + token = FCNCALL; + goto fcn; + /* NOTREACHED */ + } + if (template == NO) { + templateparens = parens; + template = YES; + } + goto ident; + /* NOTREACHED */ + } +("*"|{wsnl})+{identifier} { /* typedef name or modifier use */ + goto ident; + /* NOTREACHED */ + } +.|\n { /* general identifer usage */ + char *s; + + if (global == YES && ppdefine == NO && my_yytext[0] != '#' && + external == NO && initializer == NO && + arraydimension == NO && structfield == NO && + template == NO && fcndef == NO) { + if (esudef == YES) { + /* if enum/struct/union */ + token = MEMBERDEF; + } else { + token = GLOBALDEF; + } + } else { + ident: + token = IDENT; + } + fcn: + if (YYSTATE == WAS_IDENTIFIER) { + /* Position back to the actual identifier: */ + last = first; + first = ident_start; + yyless(0); + /* HBB 20001008: if the anti-backup-pattern above matched, + * and the matched context ended with a \n, then the scanner + * believes it's at the start of a new line. But the yyless() + * should feeds that \n back into the input, so that's + * wrong. --> force 'beginning-of-line' status off. */ + yy_set_bol(0); + BEGIN(INITIAL); + } else { + my_yymore(); + last = my_yyleng; + } + definition: + + /* if a long line */ + if (yyleng > STMTMAX) { + int c; + + /* skip to the end of the line */ + warning("line too long"); + while ((c = input()) > LEXEOF) { + if (c == '\n') { + unput(c); + break; + } + } + } + /* truncate a long symbol */ + if (yyleng > PATLEN) { + warning("symbol too long"); + my_yyleng = first + PATLEN; + my_yytext[my_yyleng] = '\0'; + } + + /* if found word was a keyword: */ + if ((s = lookup(my_yytext + first)) != NULL) { + first = my_yyleng; + + /* if the start of a typedef */ + if (s == typedeftext) { + typedefbraces = braces; + oldtype = YES; + } + /* if an enum/struct/union */ + /* (needed for "typedef struct tag name;" so + tag isn't marked as the typedef name) */ + else if (s == enumtext || s == structtext || s == uniontext) { + /* do nothing */ + } else if (s == externtext) { + /* if an external definition */ + externalbraces = braces; + external = YES; + } else if (templateparens == parens && template == YES) { + /* keyword doesn't start a function + * template */ + templateparens = -1; + template = NO; + } else { + /* identifier after typedef was a + * keyword */ + oldtype = NO; + } + } else { + /* not a keyword --> found an identifier */ + /* last = yyleng; */ + + /* if a class/enum/struct/union tag definition */ + /* FIXME HBB 20001001: why reject "class"? */ + if (tagdef && strnotequal(my_yytext + first, "class")) { + token = tagdef; + tagdef = '\0'; + if (braces == 0) { + esudef = YES; + } + } else if (braces == typedefbraces && oldtype == NO && + arraydimension == NO) { + /* if a typedef name */ + token = TYPEDEF; + } else { + oldtype = NO; + } + /* my_yymore(); */ + return(token); + /* NOTREACHED */ + } + } +} + +\[ { /* array dimension (don't worry or about subscripts) */ + arraydimension = YES; + goto more; + /* NOTREACHED */ + } +\] { + arraydimension = NO; + goto more; + /* NOTREACHED */ + } +\\\n { /* preprocessor statement is continued on next line */ + /* save the '\\' to the output file, but not the '\n': */ + yyleng = 1; + my_yymore(); + goto eol; + /* NOTREACHED */ + } +\n { /* end of the line */ + if (ppdefine == YES) { /* end of a #define */ + ppdefine = NO; + yyless(yyleng - 1); + last = first; + my_yymore(); + return(DEFINEEND); + } + /* skip the first 8 columns of a breakpoint listing line */ + /* and skip the file path in the page header */ + if (bplisting == YES) { + int c, i; + + /* FIXME HBB 20001007: should call input() instead */ + switch (input()) { /* tab and EOF just fall through */ + case ' ': /* breakpoint number line */ + case '[': + for (i = 1; i < 8 && input() > LEXEOF; ++i) + ; + break; + case '.': /* header line */ + case '/': + /* skip to the end of the line */ + while ((c = input()) > LEXEOF) { + if (c == '\n') { + unput(c); + break; + } + } + break; + case '\n': /* empty line */ + unput('\n'); + break; + } + } + eol: + ++myylineno; + first = 0; + last = 0; + if (symbols > 0) { + /* no my_yymore(): \n doesn't need to be in my_yytext */ + return(NEWLINE); + } + /* line ended --> flush my_yytext */ + if (my_yytext) + *my_yytext = '\0'; + my_yyleng = 0; + lineno = myylineno; + } + +\' { /* character constant */ + if (sdl == NO) + BEGIN(IN_SQUOTE); + goto more; + /* NOTREACHED */ + } +\' { + BEGIN(INITIAL); + goto more; + /* NOTREACHED */ + } +\" { /* string constant */ + BEGIN(IN_DQUOTE); + goto more; + /* NOTREACHED */ + } +\" { + BEGIN(INITIAL); + goto more; + /* NOTREACHED */ + } +{ +\n { /* syntax error: unexpected EOL */ + BEGIN(INITIAL); + goto eol; + /* NOTREACHED */ + } +\\. | +. { + goto more; + /* NOTREACHED */ + } +\\\n { /* line continuation inside a string! */ + myylineno++; + goto more; + /* NOTREACHED */ + } +} + +^{ws}+ { /* don't save leading white space */ + } + +{ws}+\n { /* eat whitespace at end of line */ + unput('\n'); + } + +[\t\r\v\f]+ { /* eat non-blank whitespace sequences, replace + * by single blank */ + unput(' '); + } + +{ws}{2,} { /* compress sequential whitespace here, not in putcrossref() */ + unput(' '); + } + +"/*" yy_push_state(COMMENT); +{ +[^*\n]* | +"*"+[^*/\n]* ; /* do nothing */ +[^*\n]*\n | +"*"+[^*/\n]*\n { + if (ppdefine == NO) { + goto eol; + } else { + ++myylineno; + } + /* NOTREACHED */ + } +"*"+"/" { + /* replace the comment by a single blank */ + unput(' '); + yy_pop_state(); + } +} + +"//".*\n? { + /* C++-style one-line comment */ + goto eol; + /* NOTREACHED */ + } + +{number} | /* number */ +STATE[ \t]+ | /* ... and other syntax error catchers... */ +. { /* punctuation and operators */ + more: + my_yymore(); + first = my_yyleng; + } + +%% + +void +initscanner(char *srcfile) +{ + char *s; + + if (maxifbraces == NULL) { + maxifbraces = malloc(miflevel * sizeof(*maxifbraces)); + preifbraces = malloc(miflevel * sizeof(*preifbraces)); + } + first = 0; /* buffer index for first char of symbol */ + last = 0; /* buffer index for last char of symbol */ + lineno = 1; /* symbol line number */ + myylineno = 1; /* input line number */ + arraydimension = NO; /* inside array dimension declaration */ + bplisting = NO; /* breakpoint listing */ + braces = 0; /* unmatched left brace count */ + classdef = NO; /* c++ class definition */ + elseelif = NO; /* #else or #elif found */ + esudef = NO; /* enum/struct/union global definition */ + external = NO; /* external definition */ + externalbraces = -1; /* external definition outer brace count */ + fcndef = NO; /* function definition */ + global = YES; /* file global scope (outside functions) */ + iflevel = 0; /* #if nesting level */ + initializer = NO; /* data initializer */ + initializerbraces = -1; /* data initializer outer brace count */ + lex = NO; /* lex file */ + parens = 0; /* unmatched left parenthesis count */ + ppdefine = NO; /* preprocessor define statement */ + pseudoelif = NO; /* pseudo-#elif */ + oldtype = NO; /* next identifier is an old type */ + rules = NO; /* lex/yacc rules */ + sdl = NO; /* sdl file */ + structfield = NO; /* structure field declaration */ + tagdef = '\0'; /* class/enum/struct/union tag definition */ + template = NO; /* function template */ + templateparens = -1; /* function template outer parentheses count */ + typedefbraces = -1; /* initial typedef braces count */ + ident_start = 0; /* start of previously found identifier */ + + if (my_yytext) + *my_yytext = '\0'; + my_yyleng = 0; + + BEGIN(INITIAL); + + /* if this is not a C file */ + if ((s = strrchr(srcfile, '.')) != NULL) { + switch (*++s) { /* this switch saves time on C files */ + case 'b': + if (strcmp(s, "bp") == 0) { /* breakpoint listing */ + bplisting = YES; + } + break; + case 'l': + if (strcmp(s, "l") == 0) { /* lex */ + lex = YES; + global = NO; + } + break; + case 's': + if (strcmp(s, "sd") == 0) { /* sdl */ + sdl = YES; + BEGIN(SDL); + } + break; + case 'y': + if (strcmp(s, "y") == 0) { /* yacc */ + global = NO; + } + break; + } + } +} + +#define MY_YY_ALLOCSTEP 1000 +static void +my_yymore(void) +{ + static size_t yytext_size = 0; + + /* my_yytext is an ever-growing buffer. It will not ever + * shrink, nor will it be freed at end of program, for now */ + while (my_yyleng + yyleng + 1 >= yytext_size) { + my_yytext = realloc(my_yytext, yytext_size += MY_YY_ALLOCSTEP); + } + + strncpy (my_yytext + my_yyleng, yytext, yyleng+1); + my_yyleng += yyleng; +} diff --git a/src/global.h b/src/global.h new file mode 100644 index 0000000..d1ec944 --- /dev/null +++ b/src/global.h @@ -0,0 +1,321 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * global type, data, and function definitions + */ + +#ifndef CSCOPE_GLOBAL_H +#define CSCOPE_GLOBAL_H + +//#include "config.h" +#include +#include +#include /* isalpha, isdigit, etc. */ +#include /* SIGINT and SIGQUIT */ +#include /* standard I/O package */ +#include /* standard library functions */ + +#include /* string functions */ + +#include "constants.h" /* misc. constants */ +#include "invlib.h" /* inverted index library */ +#include "library.h" /* library function return values */ + +typedef void (*sighandler_t)(int); + +#include + +#include + +typedef enum { /* boolean data type */ + NO, + YES +} BOOL; + +typedef enum { /* findinit return code */ + NOERROR, + NOTSYMBOL, + REGCMPERROR +} FINDINIT; + +typedef struct { /* mouse action */ + int button; + int percent; + int x1; + int y1; + int x2; + int y2; +} MOUSE; + +struct cmd { /* command history struct */ + struct cmd *prev, *next; /* list ptrs */ + int field; /* input field number */ + char *text; /* input field text */ +}; + +#ifndef R_OK +# define READ R_OK +#else +# define READ 4 +#endif +#ifdef W_OK +# define WRITE W_OK +#else +# define WRITE 2 +#endif + +#define O_TEXT 0x00 +#define O_BINARY 0x00 + +#ifndef DFLT_INCDIR +# define DFLT_INCDIR "/usr/include" +#endif + +/* digraph data for text compression */ +extern char dichar1[]; /* 16 most frequent first chars */ +extern char dichar2[]; /* 8 most frequent second chars + using the above as first chars */ +extern char dicode1[]; /* digraph first character code */ +extern char dicode2[]; /* digraph second character code */ + +/* and some macros to help using dicodes: */ +/* Check if a given pair of chars is compressable as a dicode: */ +#define IS_A_DICODE(inchar1, inchar2) \ + (dicode1[(unsigned char)(inchar1)] && dicode2[(unsigned char)(inchar2)]) +/* Combine the pair into a dicode */ +#define DICODE_COMPRESS(inchar1, inchar2) \ + ((0200 - 2) + dicode1[(unsigned char)(inchar1)] \ + + dicode2[(unsigned char)(inchar2)]) + +/* main.c global data */ +extern char *editor, *home, *shell, *lineflag; /* environment variables */ +extern char *home; /* Home directory */ +extern BOOL lineflagafterfile; +extern char *argv0; /* command name */ +extern BOOL compress; /* compress the characters in the crossref */ +extern BOOL dbtruncated; /* database symbols truncated to 8 chars */ +extern int dispcomponents; /* file path components to display */ +#if CCS +extern BOOL displayversion; /* display the C Compilation System version */ +#endif +extern BOOL editallprompt; /* prompt between editing files */ +extern unsigned int fileargc; /* file argument count */ +extern char **fileargv; /* file argument values */ +extern int fileversion; /* cross-reference file version */ +extern BOOL incurses; /* in curses */ +extern BOOL invertedindex; /* the database has an inverted index */ +extern BOOL isuptodate; /* consider the crossref up-to-date */ +extern BOOL kernelmode; /* don't use DFLT_INCDIR - bad for kernels */ +extern BOOL linemode; /* use line oriented user interface */ +extern BOOL verbosemode; /* print extra information on line mode */ +extern BOOL recurse_dir; /* recurse dirs when searching for src files */ +extern char *namefile; /* file of file names */ +extern BOOL ogs; /* display OGS book and subsystem names */ +extern char *prependpath; /* prepend path to file names */ +extern FILE *refsfound; /* references found file */ +extern char temp1[]; /* temporary file name */ +extern char temp2[]; /* temporary file name */ +extern long totalterms; /* total inverted index terms */ +extern BOOL trun_syms; /* truncate symbols to 8 characters */ +extern char tempstring[TEMPSTRING_LEN + 1]; /* global dummy string buffer */ +extern char *tmpdir; /* temporary directory */ + +/* command.c global data */ +extern BOOL caseless; /* ignore letter case when searching */ +extern BOOL *change; /* change this line */ +extern BOOL changing; /* changing text */ +extern int selecting; +extern unsigned int curdispline; +extern char newpat[]; /* new pattern */ +extern char Pattern[]; /* symbol or text pattern */ + +/* crossref.c global data */ +extern long dboffset; /* new database offset */ +extern BOOL errorsfound; /* prompt before clearing error messages */ +extern long lineoffset; /* source line database offset */ +extern long npostings; /* number of postings */ +extern unsigned long symbols; /* number of symbols */ + +/* dir.c global data */ +extern char currentdir[]; /* current directory */ +extern char **incdirs; /* #include directories */ +extern char **srcdirs; /* source directories */ +extern char **srcfiles; /* source files */ +extern unsigned long nincdirs; /* number of #include directories */ +extern unsigned long nsrcdirs; /* number of source directories */ +extern unsigned long nsrcfiles; /* number of source files */ +extern unsigned long msrcfiles; /* maximum number of source files */ + +/* display.c global data */ +extern int booklen; /* OGS book name display field length */ +extern int *displine; /* screen line of displayed reference */ +extern unsigned int disprefs; /* displayed references */ +extern int fcnlen; /* function name display field length */ +extern int field; /* input field */ +extern int filelen; /* file name display field length */ +extern unsigned fldcolumn; /* input field column */ +extern unsigned int mdisprefs; /* maximum displayed references */ +extern unsigned int nextline; /* next line to be shown */ +extern FILE *nonglobalrefs; /* non-global references file */ +extern int numlen; /* line number display field length */ +extern unsigned int topline; /* top line of page */ +extern int bottomline; /* bottom line of page */ +extern long searchcount; /* count of files searched */ +extern int subsystemlen; /* OGS subsystem name display field length */ +extern unsigned int totallines; /* total reference lines */ +extern const char dispchars[]; /* display chars for jumping to lines */ + +/* find.c global data */ +extern char block[]; /* cross-reference file block */ +extern char blockmark; /* mark character to be searched for */ +extern long blocknumber; /* block number */ +extern char *blockp; /* pointer to current character in block */ +extern int blocklen; /* length of disk block read */ + +/* lookup.c global data */ +extern struct keystruct { + char *text; + char delim; + struct keystruct *next; +} keyword[]; + +/* mouse.c global data */ +extern BOOL mouse; /* mouse interface */ + +#if UNIXPC +extern BOOL unixpcmouse; /* UNIX PC mouse interface */ +#endif + +/* cscope functions called from more than one function or between files */ + +char *filepath(char *file); +char *findcalledby(char *pattern); +char *findcalling(char *pattern); +char *findallfcns(char *dummy); +char *finddef(char *pattern); +char *findfile(char *dummy); +char *findinclude(char *pattern); +char *findsymbol(char *pattern); +char *findassign(char *pattern); +char *findregexp(char *egreppat); +char *findstring(char *pattern); +char *inviewpath(char *file); +char *lookup(char *ident); +char *pathcomponents(char *path, int components); +char *read_block(void); +char *scanpast(char c); + +char ** parse_options(int *argc, char **argv); +void error_usage(void); +void longusage(void); +void usage(void); +extern BOOL remove_symfile_onexit; +extern BOOL onesearch; /* one search only in line mode */ +extern char *reflines; /* symbol reference lines file */ + +void addcmd(int f, char *s); +void addsrcfile(char *path); +void askforchar(void); +void askforreturn(void); +void atchange(void); +void atfield(void); +void cannotwrite(char *file); +void cannotopen(char *file); +void clearmsg(void); +void clearmsg2(void); +void countrefs(void); +void crossref(char *srcfile); +void dispinit(void); +void display(void); +void drawscrollbar(int top, int bot); +void edit(char *file, char *linenum); +void editall(void); +void editref(int); +void entercurses(void); +void exitcurses(void); +void findcleanup(void); +void freesrclist(void); +void freeinclist(void); +void freecrossref(void); +void freefilelist(void); +void help(void); +void incfile(char *file, char *type); +void includedir(char *_dirname); +void initsymtab(void); +void makefilelist(void); +void mousecleanup(void); +void mousemenu(void); +void mouseinit(void); +void mousereinit(void); +void myexit(int sig); +void myperror(char *text); +void ogsnames(char *file, char **subsystem, char **book); +void progress(char *what, long current, long max); +void putfilename(char *srcfile); +void postmsg(char *msg); +void postmsg2(char *msg); +void posterr(char *msg,...); +void postfatal(const char *msg,...); +void putposting(char *term, int type); +void fetch_string_from_dbase(char *, size_t); +void resetcmd(void); +void seekline(unsigned int line); +void setfield(void); +void shellpath(char *out, int limit, char *in); +void sourcedir(char *dirlist); +void myungetch(int c); +void warning(char *text); +void writestring(char *s); + +BOOL command(int commandc); +BOOL infilelist(char *file); +BOOL readrefs(char *filename); +BOOL search(void); +BOOL writerefsfound(void); + +FINDINIT findinit(char *pattern); +MOUSE *getmouseaction(char leading_char); +struct cmd *currentcmd(void); +struct cmd *prevcmd(void); +struct cmd *nextcmd(void); + +int egrep(char *file, FILE *output, char *format); +int mygetline(char p[], char s[], unsigned size, int firstchar, BOOL iscaseless); +int mygetch(void); +int hash(char *ss); +int execute(char *a, ...); +long dbseek(long offset); + + +#endif /* CSCOPE_GLOBAL_H */ diff --git a/src/gscope.c b/src/gscope.c new file mode 100644 index 0000000..f26f1b1 --- /dev/null +++ b/src/gscope.c @@ -0,0 +1 @@ +/* Placeholder file --- replace by real gscope.c if you have it */ \ No newline at end of file diff --git a/src/help.c b/src/help.c new file mode 100644 index 0000000..3c61942 --- /dev/null +++ b/src/help.c @@ -0,0 +1,209 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * display help + * + */ + +#include "global.h" +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +#include +#else +#include +#endif +/* + max num of lines of help screen - + this number needs to be increased if more than n help items are needed +*/ +#define MAXHELP 50 /* maximum number of help strings */ + +void +help(void) +{ + char **ep, *s, **tp, *text[MAXHELP]; + int ln; + + tp = text; + if (changing == NO) { + if (mouse) { + *tp++ = "Point with the mouse and click button 1 to move to the desired input field,\n"; + *tp++ = "type the pattern to search for, and then press the RETURN key. For the first 4\n"; + *tp++ = "and last 2 input fields, the pattern can be a regcomp(3) regular expression.\n"; + *tp++ = "If the search is successful, you can edit the file containing a displayed line\n"; + *tp++ = "by pointing with the mouse and clicking button 1.\n"; + *tp++ = "\nYou can either use the button 2 menu or these single-character commands:\n\n"; + } else { + *tp++ = "Press the RETURN key repeatedly to move to the desired input field, type the\n"; + *tp++ = "pattern to search for, and then press the RETURN key. For the first 4 and\n"; + *tp++ = "last 2 input fields, the pattern can be a regcomp(3) regular expression.\n"; + *tp++ = "If the search is successful, you can use these single-character commands:\n\n"; + *tp++ = "0-9a-zA-Z\tEdit the file containing the displayed line.\n"; + } + *tp++ = "space bar\tDisplay next set of matching lines.\n"; + *tp++ = "+\t\tDisplay next set of matching lines.\n"; + *tp++ = "^V\t\tDisplay next set of matching lines.\n"; + *tp++ = "-\t\tDisplay previous set of matching lines.\n"; + *tp++ = "^E\t\tEdit all lines.\n"; + *tp++ = ">\t\tWrite the list of lines being displayed to a file.\n"; + *tp++ = ">>\t\tAppend the list of lines being displayed to a file.\n"; + *tp++ = "<\t\tRead lines from a file.\n"; + *tp++ = "^\t\tFilter all lines through a shell command.\n"; + *tp++ = "|\t\tPipe all lines to a shell command.\n"; + if (!mouse) { + *tp++ = "\nAt any time you can use these single-character commands:\n\n"; + *tp++ = "TAB\t\tSwap positions between input and output areas.\n"; + *tp++ = "RETURN\t\tMove to the next input field.\n"; + *tp++ = "^N\t\tMove to the next input field.\n"; + *tp++ = "^P\t\tMove to the previous input field.\n"; + } + *tp++ = "^Y / ^A\t\tSearch with the last pattern typed.\n"; + *tp++ = "^B\t\tRecall previous input field and search pattern.\n"; + *tp++ = "^F\t\tRecall next input field and search pattern.\n"; + if(caseless) + *tp++ = "^C\t\tToggle ignore/use letter case when searching (IGNORE).\n"; + else + *tp++ = "^C\t\tToggle ignore/use letter case when searching (USE).\n"; + *tp++ = "^R\t\tRebuild the cross-reference.\n"; + *tp++ = "!\t\tStart an interactive shell (type ^D to return to cscope).\n"; + *tp++ = "^L\t\tRedraw the screen.\n"; + *tp++ = "?\t\tDisplay this list of commands.\n"; + *tp++ = "^D\t\tExit cscope.\n"; + *tp++ = "\nNote: If the first character of the pattern you want to search for matches\n"; + *tp++ = "a command, type a \\ character first.\n"; + *tp++ = "Note: Some ctrl keys may be occupied by your terminal configuration.\n"; + } else { + if (mouse) { + *tp++ = "Point with the mouse and click button 1 to mark or unmark the line to be\n"; + *tp++ = "changed. You can also use the button 2 menu or these single-character\n"; + *tp++ = "commands:\n\n"; + } + else { + *tp++ = "When changing text, you can use these single-character commands:\n\n"; + *tp++ = "0-9a-zA-Z\tMark or unmark the line to be changed.\n"; + } + *tp++ = "*\t\tMark or unmark all displayed lines to be changed.\n"; + *tp++ = "space bar\tDisplay next set of lines.\n"; + *tp++ = "+\t\tDisplay next set of lines.\n"; + *tp++ = "-\t\tDisplay previous set of lines.\n"; + *tp++ = "^A\t\tMark or unmark all lines to be changed.\n"; + *tp++ = "^D\t\tChange the marked lines and exit.\n"; + *tp++ = "ESC\t\tExit without changing the marked lines.\n"; + *tp++ = "!\t\tStart an interactive shell (type ^D to return to cscope).\n"; + *tp++ = "^L\t\tRedraw the screen.\n"; + *tp++ = "?\t\tDisplay this list of commands.\n"; + } + /* print help, a screen at a time */ + ep = tp; + ln = 0; + for (tp = text; tp < ep; ) { + if (ln < LINES - 1) { + for (s = *tp; *s != '\0'; ++s) { + if (*s == '\n') { + ++ln; + } + } + (void) addstr(*tp++); + } + else { + (void) addstr("\n"); + askforchar(); + (void) clear(); + ln = 0; + } + } + if (ln) { + (void) addstr("\n"); + askforchar(); + } +} + +/* error exit including short usage information */ +void +error_usage(void) +{ + usage(); + fprintf(stderr, "Try the -h option for more information.\n"); + myexit(1); +} + +/* normal usage message */ +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"); +} + + +/* long usage message */ +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 -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); +} diff --git a/src/history.c b/src/history.c new file mode 100644 index 0000000..6efca0e --- /dev/null +++ b/src/history.c @@ -0,0 +1,103 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 or text cross-reference + * + * command history + */ + +#include "global.h" + + + +static struct cmd *tail, *current; + +/* add a cmd to the history list */ +void +addcmd(int f, char *s) /* field number and command text */ +{ + struct cmd *h; + + h = malloc(sizeof(struct cmd)); + if( tail) { + tail->next = h; + h->next = 0; + h->prev = tail; + tail = h; + } else { + tail = h; + h->next = h->prev = 0; + } + h->field = f; + h->text = strdup( s); + current = 0; +} + + /* return previous history item */ +struct cmd * +prevcmd(void) +{ + if( current) { + if( current->prev) /* stay on first item */ + return current = current->prev; + else + return current; + } else if( tail) + return current = tail; + else + return NULL; +} + + /* return next history item */ +struct cmd * +nextcmd(void) +{ + if( current) { + if( current->next) /* stay on first item */ + return current = current->next; + else + return current; + } else + return NULL; +} + /* reset current to tail */ +void +resetcmd(void) +{ + current = 0; +} + +struct cmd * +currentcmd(void) +{ + return current; +} diff --git a/src/input.c b/src/input.c new file mode 100644 index 0000000..a8d6dea --- /dev/null +++ b/src/input.c @@ -0,0 +1,333 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * terminal input functions + */ + +#include "global.h" +#if defined(USE_NCURSES) && !defined(RENAMED_NCURSES) +#include +#else +#include +#endif +#include /* jmp_buf */ +#include +#include +#if HAVE_SYS_TERMIOS_H +#include +#endif + +static jmp_buf env; /* setjmp/longjmp buffer */ +static int prevchar; /* previous, ungotten character */ + +/* Internal prototypes: */ +static void catchint(int sig); + +/* catch the interrupt signal */ + +/*ARGSUSED*/ +static void +catchint(int sig) +{ + (void) sig; /* 'use' it, to avoid a warning */ + + signal(SIGINT, catchint); + longjmp(env, 1); +} + +/* unget a character */ +void +myungetch(int c) +{ + prevchar = c; +} + +/* get a character from the terminal */ +int +mygetch(void) +{ + sighandler_t savesig = 0; /* old value of signal */ + int c; + + /* change an interrupt signal to a break key character */ + if (setjmp(env) == 0) { + savesig = signal(SIGINT, catchint); + refresh(); /* update the display */ + mousereinit(); /* curses can change the menu number */ + if(prevchar) { + c = prevchar; + prevchar = 0; + } else { + c = -1; + while (c == -1) { + /* get a character from the terminal */ + c = getch(); + if ((c == -1) && (errno != EINTR)) + break; + } + } + } else { /* longjmp to here from signal handler */ + c = KEY_BREAK; + } + signal(SIGINT, savesig); + return(c); +} + + +/* get a line from the terminal in non-canonical mode */ +int +mygetline(char p[], char s[], unsigned size, int firstchar, BOOL iscaseless) +{ + int c; + unsigned int i = 0, j; + char *sright; /* substring to the right of the cursor */ + unsigned int ri = 0; /* position in right-string */ + + /* Inserts and deletes are always performed on the left-string, + * but we'll also have a right-string 'sright' to hold characters + * which are on the right of the cursor [insertion point]. + * + * Think of 'sright' as a stack -- we push chars into it when the cursor + * moves left, and we pop chars off it when the cursor moves right again. + * At the end of the function, we'll pop off any remaining characters + * onto the end of 's' + */ + sright = calloc(size, sizeof(*sright)); + + strcpy ( s, p); + i += strlen(p); + /* if a character already has been typed */ + if (firstchar != '\0') { + if(iscaseless == YES) { + firstchar = tolower(firstchar); + } + addch(firstchar); /* display it */ + s[i++] = firstchar; /* save it */ + } + /* until the end of the line is reached */ + while ((c = mygetch()) != '\r' && c != '\n' && c != KEY_ENTER) { + if (c == KEY_LEFT || c == ctrl('B')) { /* left */ + if (i > 0) { + addch('\b'); + /* move this char into the second (rhs) string */ + sright[ri++] = s[--i]; + } + } else if (c == KEY_RIGHT || c == ctrl('F')) { /* right */ + if (i < size && ri > 0) { + /* move this char to the left of the cursor */ + s[i++] = sright[--ri]; + addch(s[i-1]); + } + } else if ( +#ifdef KEY_HOME + c == KEY_HOME || +#endif + c == ctrl('A') ) { + while (i > 0) { + sright[ri++] = s[--i]; + addch('\b'); + addch(s[i]); + addch('\b'); + } + } else if ( +#ifdef KEY_END + c == KEY_END || +#endif + c == ctrl('E') ) { + while (ri > 0) { + s[i++] = sright[--ri]; + addch(s[i-1]); + } + } else if (c == erasechar() || c == KEY_BACKSPACE + || c == DEL || c == ctrl('H') ) { + /* erase */ + if (i > 0) { + if (ri == 0) { + addstr("\b \b"); + } else { + addch('\b'); + delch(); + } + s[i] = '\0'; + --i; + } + } else if (c == killchar() || c == KEY_BREAK) { + /* kill */ + for (j = 0; j < i; ++j) { + addch('\b'); + } + for (j = 0; j < i; ++j) { + addch(' '); + } + for (j = 0; j < i; ++j) { + addch('\b'); + } + i = 0; + } else if (isprint(c) || c == '\t') { + /* printable */ + if(iscaseless == YES) { + c = tolower(c); + } + /* if it will fit on the line */ + if (i < size) { + s[i++] = c; /* save it */ + if (ri == 0) { + addch(c); /* display it */ + } else { + insch(c); /* display it */ + addch(c); /* advance cursor */ + } + } +#if UNIXPC + } else if (unixpcmouse == YES && c == ESC) { /* mouse */ + getmouseaction(ESC); /* ignore it */ +#endif + } else if (mouse == YES && c == ctrl('X')) { + getmouseaction(ctrl('X')); /* ignore it */ + } else if (c == EOF) { /* end-of-file */ + break; + } + + /* return on an empty line to allow a command to be entered */ + if (firstchar != '\0' && (i+ri) == 0) { + break; + } + } + + /* move any remaining chars on the rhs of the cursor + * onto the end of our string + */ + while (ri > 0) { + s[i++] = sright[--ri]; + } + free(sright); + + s[i] = '\0'; + return(i); +} + +/* ask user to enter a character after reading the message */ + +void +askforchar(void) +{ + addstr("Type any character to continue: "); + mygetch(); +} + +/* ask user to press the RETURN key after reading the message */ + +void +askforreturn(void) +{ + fprintf(stderr, "Press the RETURN key to continue: "); + getchar(); + /* HBB 20060419: message probably messed up the screen --- redraw */ + if (incurses == YES) { + redrawwin(curscr); + } +} + +/* expand the ~ and $ shell meta characters in a path */ + +void +shellpath(char *out, int limit, char *in) +{ + char *lastchar; + char *s, *v; + + /* skip leading white space */ + while (isspace((unsigned char)*in)) { + ++in; + } + lastchar = out + limit - 1; + + /* a tilde (~) by itself represents $HOME; followed by a name it + represents the $LOGDIR of that login name */ + if (*in == '~') { + *out++ = *in++; /* copy the ~ because it may not be expanded */ + + /* get the login name */ + s = out; + while (s < lastchar && *in != '/' && *in != '\0' && !isspace((unsigned char)*in)) { + *s++ = *in++; + } + *s = '\0'; + + /* if the login name is null, then use $HOME */ + if (*out == '\0') { + v = getenv("HOME"); + } else { /* get the home directory of the login name */ + v = logdir(out); + } + /* copy the directory name if it isn't too big */ + if (v != NULL && strlen(v) < (lastchar - out)) { + strcpy(out - 1, v); + out += strlen(v) - 1; + } else { + /* login not found, so ~ must be part of the file name */ + out += strlen(out); + } + } + /* get the rest of the path */ + while (out < lastchar && *in != '\0' && !isspace((unsigned char)*in)) { + + /* look for an environment variable */ + if (*in == '$') { + *out++ = *in++; /* copy the $ because it may not be expanded */ + + /* get the variable name */ + s = out; + while (s < lastchar && *in != '/' && *in != '\0' && + !isspace((unsigned char)*in)) { + *s++ = *in++; + } + *s = '\0'; + + /* get its value, but only it isn't too big */ + if ((v = getenv(out)) != NULL && strlen(v) < (lastchar - out)) { + strcpy(out - 1, v); + out += strlen(v) - 1; + } else { + /* var not found, or too big, so assume $ must be part of the + * file name */ + out += strlen(out); + } + } + else { /* ordinary character */ + *out++ = *in++; + } + } + *out = '\0'; +} diff --git a/src/invlib.c b/src/invlib.c new file mode 100644 index 0000000..cd15c35 --- /dev/null +++ b/src/invlib.c @@ -0,0 +1,1204 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + + +#include +#include +#include +#if SHARE +#include +#include +#include +#define ERR -1 +#endif +#include "invlib.h" +#include "global.h" + +#include + +#define DEBUG 0 /* debugging code and realloc messages */ +#define BLOCKSIZE 2 * BUFSIZ /* logical block size */ +#define POSTINC 10000 /* posting buffer size increment */ +#define SEP ' ' /* sorted posting field separator */ +#define SETINC 100 /* posting set size increment */ +#define STATS 0 /* print statistics */ +#define SUPERINC 10000 /* super index size increment */ +#define TERMMAX 512 /* term max size */ +#define FMTVERSION 1 /* inverted index format version */ +#define ZIPFSIZE 200 /* zipf curve size */ + +#if DEBUG +/* FIXME HBB 20010705: nowhere in the source is `invbreak' ever set to + * a value other than the (silent) initialization to zero. Pretty + * useless, that looks */ +int invbreak; +#endif + +static int boolready(void); +static int invnewterm(void); +static void invstep(INVCONTROL *invcntl); +static void invcannotalloc(unsigned n); +static void invcannotopen(char *file); +static void invcannotwrite(char *file); + +#if STATS +int showzipf; /* show postings per term distribution */ +#endif + +static POSTING *item, *enditem, *item1 = NULL, *item2 = NULL; +static unsigned int setsize1, setsize2; +static long numitems, totterm, zerolong; +static char *indexfile, *postingfile; +static FILE *outfile, *fpost; +static size_t supersize = SUPERINC, supintsize; +static unsigned int numpost, numlogblk, amtused, nextpost; +static unsigned int lastinblk, numinvitems; +static POSTING *POST, *postptr; +static unsigned long *SUPINT, *supint, nextsupfing; +static char *SUPFING, *supfing; +static char thisterm[TERMMAX]; +typedef union logicalblk { + long invblk[BLOCKSIZE / sizeof(long)]; + char chrblk[BLOCKSIZE]; +} t_logicalblk; +static t_logicalblk logicalblk; + +#if DEBUG || STATS +static long totpost; +#endif + +#if STATS +static int zipf[ZIPFSIZE + 1]; +#endif + +long +invmake(char *invname, char *invpost, FILE *infile) +{ + unsigned char *s; + long num; + int i; + long fileindex = 0; /* initialze, to avoid warning */ + unsigned postsize = POSTINC * sizeof(*POST); + unsigned long *intptr; + char line[TERMMAX]; + long tlong; + PARAM param; + POSTING posting; + char temp[BLOCKSIZE]; +#if STATS + int j; + unsigned maxtermlen = 0; +#endif + /* output file */ + if ((outfile = vpfopen(invname, "w+b")) == NULL) { + invcannotopen(invname); + return(0); + } + indexfile = invname; + fseek(outfile, BUFSIZ, SEEK_SET); + + /* posting file */ + if ((fpost = vpfopen(invpost, "wb")) == NULL) { + invcannotopen(invpost); + return(0); + } + postingfile = invpost; + nextpost = 0; + /* get space for the postings list */ + if ((POST = malloc(postsize)) == NULL) { + invcannotalloc(postsize); + return(0); + } + postptr = POST; + /* get space for the superfinger (superindex) */ + if ((SUPFING = malloc(supersize)) == NULL) { + invcannotalloc(supersize); + return(0); + } + supfing = SUPFING; + /* FIXME HBB: magic number alert (40) */ + supintsize = supersize / 40u; + /* also for the superfinger index */ + if ((SUPINT = malloc(supintsize * sizeof(*SUPINT))) == NULL) { + invcannotalloc(supintsize * sizeof(*SUPINT)); + return(0); + } + supint = SUPINT; + supint++; /* leave first term open for a count */ + /* initialize using an empty term */ + strcpy(thisterm, ""); + *supint++ = 0; + *supfing++ = ' '; + *supfing++ = '\0'; + nextsupfing = 2; +#if DEBUG || STATS + totpost = 0L; +#endif + totterm = 0L; + numpost = 1; + + /* set up as though a block had come and gone, i.e., set up for new block */ + /* 3 longs needed for: numinvitems, next block, and previous block */ + amtused = 3 * sizeof(long); + numinvitems = 0; + numlogblk = 0; + lastinblk = sizeof(t_logicalblk); + + /* now loop as long as more to read (till eof) */ + while (fgets(line, TERMMAX, infile) != NULL) { +#if DEBUG || STATS + ++totpost; +#endif + s = strchr(line, SEP); + if (s != NULL) { + *s = '\0'; + } + else { + continue; + } +#if STATS + if ((i = strlen(line)) > maxtermlen) { + maxtermlen = i; + } +#endif +#if DEBUG + printf("%ld: %s ", totpost, line); + fflush(stdout); +#endif + if (strcmp(thisterm, line) == 0) { + if ((postptr + 10) > (POST + (postsize / sizeof(*POST)))) { + i = postptr - POST; + postsize += POSTINC * sizeof(*POST); + if ((POST = realloc(POST, postsize)) == NULL) { + invcannotalloc(postsize); + return(0); + } + postptr = i + POST; +#if DEBUG + printf("reallocated post space to %u, totpost=%ld\n", + postsize, totpost); +#endif + } + numpost++; + } else { + /* have a new term */ + if (!invnewterm()) { + return(0); + } + strcpy(thisterm, line); + numpost = 1; + postptr = POST; + fileindex = 0; + } + /* get the new posting */ + num = *++s - '!'; + i = 1; + do { + num = BASE * num + *++s - '!'; + } while (++i < PRECISION); + posting.lineoffset = num; + while (++fileindex < nsrcfiles && num > srcoffset[fileindex]) { + ; + } + posting.fileindex = --fileindex; + posting.type = *++s; + ++s; + if (*s != '\n') { + num = *++s - '!'; + while (*++s != '\n') { + num = BASE * num + *s - '!'; + } + posting.fcnoffset = num; + } + else { + posting.fcnoffset = 0; + } + *postptr++ = posting; +#if DEBUG + printf("%ld %ld %ld %ld\n", posting.fileindex, + posting.fcnoffset, posting.lineoffset, posting.type); + fflush(stdout); +#endif + } + if (!invnewterm()) { + return(0); + } + /* now clean up final block */ + logicalblk.invblk[0] = numinvitems; + /* loops pointer around to start */ + logicalblk.invblk[1] = 0; + logicalblk.invblk[2] = numlogblk - 1; + if (fwrite(&logicalblk, sizeof(t_logicalblk), 1, outfile) == 0) { + goto cannotwrite; + } + numlogblk++; + /* write out block to save space. what in it doesn't matter */ + if (fwrite(&logicalblk, sizeof(t_logicalblk), 1, outfile) == 0) { + goto cannotwrite; + } + /* finish up the super finger */ + *SUPINT = numlogblk; + /* add to the offsets the size of the offset pointers */ + intptr = (SUPINT + 1); + i = (char *)supint - (char *)SUPINT; + while (intptr < supint) + *intptr++ += i; + /* write out the offsets (1 for the N at start) and the super finger */ + if (fwrite(SUPINT, sizeof(*SUPINT), numlogblk + 1, outfile) == 0 || + fwrite(SUPFING, 1, supfing - SUPFING, outfile) == 0) { + goto cannotwrite; + } + /* save the size for reference later */ + nextsupfing = sizeof(long) + sizeof(long) * numlogblk + (supfing - SUPFING); + /* make sure the file ends at a logical block boundary. This is + necessary for invinsert to correctly create extended blocks + */ + i = nextsupfing % sizeof(t_logicalblk); + /* write out junk to fill log blk */ + if (fwrite(temp, sizeof(t_logicalblk) - i, 1, outfile) == 0 || + fflush(outfile) == EOF) { /* rewind doesn't check for write failure */ + goto cannotwrite; + } + /* write the control area */ + rewind(outfile); + param.version = FMTVERSION; + param.filestat = 0; + param.sizeblk = sizeof(t_logicalblk); + param.startbyte = (numlogblk + 1) * sizeof(t_logicalblk) + BUFSIZ;; + param.supsize = nextsupfing; + param.cntlsize = BUFSIZ; + param.share = 0; + if (fwrite(¶m, sizeof(param), 1, outfile) == 0) { + goto cannotwrite; + } + for (i = 0; i < 10; i++) /* for future use */ + if (fwrite(&zerolong, sizeof(zerolong), 1, outfile) == 0) { + goto cannotwrite; + } + + /* make first block loop backwards to last block */ + if (fflush(outfile) == EOF) { /* fseek doesn't check for write failure */ + goto cannotwrite; + } + /* get to second word first block */ + fseek(outfile, BUFSIZ + 2 * sizeof(long), SEEK_SET); + tlong = numlogblk - 1; + if (fwrite(&tlong, sizeof(tlong), 1, outfile) == 0 || + fclose(outfile) == EOF) { + cannotwrite: + invcannotwrite(invname); + return(0); + } + if (fclose(fpost) == EOF) { + invcannotwrite(postingfile); + return(0); + } + --totterm; /* don't count null term */ +#if STATS + printf("logical blocks = %d, postings = %ld, terms = %ld, max term length = %d\n", + numlogblk, totpost, totterm, maxtermlen); + if (showzipf) { + printf("\n************* ZIPF curve ****************\n"); + for (j = ZIPFSIZE; j > 1; j--) + if (zipf[j]) + break; + for (i = 1; i < j; ++i) { + printf("%3d -%6d ", i, zipf[i]); + if (i % 6 == 0) putchar('\n'); + } + printf(">%d-%6d\n", ZIPFSIZE, zipf[0]); + } +#endif + /* free all malloc'd memory */ + free(POST); + free(SUPFING); + free(SUPINT); + return(totterm); +} + +/* add a term to the data base */ + +static int +invnewterm(void) +{ + int backupflag, i, j, holditems, gooditems, howfar; + unsigned int maxback, len, numwilluse, wdlen; + char *tptr, *tptr3; + + union { + unsigned long packword[2]; + ENTRY e; + } iteminfo; + + gooditems = 0; /* initialize, to avoid warning */ + totterm++; +#if STATS + /* keep zipfian info on the distribution */ + if (numpost <= ZIPFSIZE) + zipf[numpost]++; + else + zipf[0]++; +#endif + len = strlen(thisterm); + /* length of term rounded up to long boundary */ + wdlen = (len + (sizeof(long) - 1)) / sizeof(long); + /* each term needs 2 longs for its iteminfo and + * 1 long for its offset */ + numwilluse = (wdlen + 3) * sizeof(long); + /* new block if at least 1 item in block */ + if (numinvitems && numwilluse + amtused > sizeof(t_logicalblk)) { + /* set up new block */ + if (supfing + 500u > SUPFING + supersize) { + i = supfing - SUPFING; + supersize += 20000u; + if ((SUPFING = realloc(SUPFING, supersize)) == NULL) { + invcannotalloc(supersize); + return(0); + } + supfing = i + SUPFING; +#if DEBUG + printf("reallocated superfinger space to %d, totpost=%ld\n", + supersize, totpost); +#endif + } + /* check that room for the offset as well */ + /* FIXME HBB: magic number alert (10) */ + if ((numlogblk + 10) > supintsize) { + i = supint - SUPINT; + supintsize += SUPERINC; + if ((SUPINT = realloc(SUPINT, supintsize * sizeof(*SUPINT))) == NULL) { + invcannotalloc(supintsize * sizeof(*SUPINT)); + return(0); + } + supint = i + SUPINT; +#if DEBUG + printf("reallocated superfinger offset to %d, totpost = %ld\n", supintsize * sizeof(*SUPINT), totpost); +#endif + } + /* See if backup is efficatious */ + backupflag = 0; + maxback = (int) strlen(thisterm) / 10; + holditems = numinvitems; + if (maxback > numinvitems) + maxback = numinvitems - 2; + howfar = 0; + while (maxback-- > 1) { + howfar++; + iteminfo.packword[0] = + logicalblk.invblk[--holditems * 2 + (sizeof(long) - 1)]; + if ((i = iteminfo.e.size / 10) < maxback) { + maxback = i; + backupflag = howfar; + gooditems = holditems; + } + } + /* see if backup will occur */ + if (backupflag) { + numinvitems = gooditems; + } + logicalblk.invblk[0] = numinvitems; + /* set forward pointer pointing to next */ + logicalblk.invblk[1] = numlogblk + 1; + /* set back pointer to last block */ + logicalblk.invblk[2] = numlogblk - 1; + if (fwrite(logicalblk.chrblk, 1, sizeof(t_logicalblk), outfile) == 0) { + invcannotwrite(indexfile); + return(0); + } + /* 3 longs needed for: numinvitems, next block, and previous block */ + amtused = 3 * sizeof(long); + numlogblk++; + /* check if had to back up, if so do it */ + if (backupflag) { + char *tptr2; + + /* find out where the end of the new block is */ + iteminfo.packword[0] = logicalblk.invblk[numinvitems*2+1]; + tptr3 = logicalblk.chrblk + iteminfo.e.offset; + /* move the index for this block */ + for (i = 3; i <= (backupflag * 2 + 2); i++) + logicalblk.invblk[i] = logicalblk.invblk[numinvitems*2+i]; + /* move the word into the super index */ + iteminfo.packword[0] = logicalblk.invblk[3]; + iteminfo.packword[1] = logicalblk.invblk[4]; + tptr2 = logicalblk.chrblk + iteminfo.e.offset; + strncpy(supfing, tptr2, (int) iteminfo.e.size); + *(supfing + iteminfo.e.size) = '\0'; +#if DEBUG + printf("backup %d at term=%s to term=%s\n", + backupflag, thisterm, supfing); +#endif + *supint++ = nextsupfing; + nextsupfing += strlen(supfing) + 1; + supfing += strlen(supfing) + 1; + /* now fix up the logical block */ + tptr = logicalblk.chrblk + lastinblk; + lastinblk = sizeof(t_logicalblk); + tptr2 = logicalblk.chrblk + lastinblk; + j = tptr3 - tptr; + while (tptr3 > tptr) + *--tptr2 = *--tptr3; + lastinblk -= j; + amtused += ((2 * sizeof(long)) * backupflag + j); + for (i = 3; i < (backupflag * 2 + 2); i += 2) { + iteminfo.packword[0] = logicalblk.invblk[i]; + iteminfo.e.offset += (tptr2 - tptr3); + logicalblk.invblk[i] = iteminfo.packword[0]; + } + numinvitems = backupflag; + } else { /* no backup needed */ + numinvitems = 0; + lastinblk = sizeof(t_logicalblk); + /* add new term to superindex */ + strcpy(supfing, thisterm); + supfing += strlen(thisterm) + 1; + *supint++ = nextsupfing; + nextsupfing += strlen(thisterm) + 1; + } + } + /* HBB 20010501: Fixed bug by replacing magic number '8' by + * what it actually represents. */ + lastinblk -= (numwilluse - 2 * sizeof(long)); + iteminfo.e.offset = lastinblk; + iteminfo.e.size = len; + iteminfo.e.space = 0; + iteminfo.e.post = numpost; + strncpy(logicalblk.chrblk + lastinblk, thisterm, len); + amtused += numwilluse; + logicalblk.invblk[(lastinblk/sizeof(long))+wdlen] = nextpost; + if ((i = postptr - POST) > 0) { + if (fwrite(POST, sizeof(*POST), i, fpost) == 0) { + invcannotwrite(postingfile); + return(0); + } + nextpost += i * sizeof(*POST); + } + logicalblk.invblk[3+2*numinvitems++] = iteminfo.packword[0]; + logicalblk.invblk[2+2*numinvitems] = iteminfo.packword[1]; + return(1); +} + +/* + * If 'invname' ends with the 'from' substring, it is replaced inline with the + * 'to' substring (which must be of the exact same length), and the function + * returns 0. Otherwise, returns -1. + */ + +static int +invflipname(char * invname, const char *from, const char *to) +{ + char *temp, *i = NULL; + + assert(strlen(from) == strlen(to)); + + temp = invname - 1; + while( (temp = strstr(temp + 1, from))) + i = temp; + if (!i || i[strlen(from)] != '\0') + return -1; + while(*to) + *i++ = *to++; + return 0; +} + +/* small helper function to centralize handling of binary opening + * for reading, and use of the 'stat" flag */ +static FILE * +open_for_reading(char *name, int stat) +{ + return vpfopen(name, ((stat == 0) ? "rb" : "r+b")); +} + +/* handle opening of a file under a possibly "flipped" name */ +/* If db created without '-f', but now invoked with '-f cscope.out', + * we need to check for 'cscope.in.out', rather than 'cscope.out.in': + * I.e, hack around our own violation of the inverse db naming convention */ +/* more silliness: if you create the db with '-f cscope', then try to open + * it without '-f cscope', you'll fail unless we check for 'cscope.out.in' + * here. */ +static FILE * +open_file_with_flipped_name(char *name, const char *flip_in, const char *flip_out, int stat) +{ + if (! invflipname(name, flip_in, flip_out)) { + FILE *fptr = open_for_reading(name, stat); + if (! fptr) + /* flip back for error message */ + invflipname(name, flip_out, flip_in); + return fptr; + }; + return 0; +} + +static FILE * +open_file_with_possibly_flipped_name(char *name, const char *flip1, const char *flip2, int stat) +{ + FILE *fptr = open_for_reading(name, stat); + + if (! fptr) + fptr = open_file_with_flipped_name(name, flip2, flip1, stat); + if (! fptr) + fptr = open_file_with_flipped_name(name, flip1, flip2, stat); + return fptr; +} + +int +invopen(INVCONTROL *invcntl, char *invname, char *invpost, int stat) +{ + int read_index; + + invcntl->invfile = open_file_with_possibly_flipped_name(invname, INVNAME, INVNAME2, stat); + if (! invcntl->invfile) { + invcannotopen(invname); + return(-1); + } + if (fread(&invcntl->param, sizeof(invcntl->param), 1, invcntl->invfile) == 0) { + fprintf(stderr, "%s: empty inverted file\n", argv0); + fclose(invcntl->invfile); + return(-1); + } + if (invcntl->param.version != FMTVERSION) { + fprintf(stderr, "%s: cannot read old index format; use -U option to force database to rebuild\n", argv0); + fclose(invcntl->invfile); + return(-1); + } + assert(invcntl->param.sizeblk == sizeof(t_logicalblk)); + + if (stat == 0 && invcntl->param.filestat == INVALONE) { + fprintf(stderr, "%s: inverted file is locked\n", argv0); + fclose(invcntl->invfile); + return(-1); + } + + invcntl->postfile = open_file_with_possibly_flipped_name(invpost, INVPOST, INVPOST2, stat); + if (! invcntl->postfile) { + invcannotopen(invpost); + fclose(invcntl->invfile); + return(-1); + } + + /* allocate core for a logical block */ + if ((invcntl->logblk = malloc((size_t) invcntl->param.sizeblk)) == NULL) { + invcannotalloc((size_t) invcntl->param.sizeblk); + fclose(invcntl->postfile); + fclose(invcntl->invfile); + return(-1); + } + /* allocate for and read in superfinger */ + read_index = 1; + invcntl->iindex = NULL; +#if SHARE + if (invcntl->param.share == 1) { + key_t shm_key; + struct shmid_ds shm_buf; + int shm_id; + + /* see if the shared segment exists */ + shm_key = ftok(invname, 2); + shm_id = shmget(shm_key, 0, 0); + /* Failure simply means (hopefully) that segment doesn't exists */ + if (shm_id == -1) { + /* Have to give general write permission due to AMdahl not having protected segments */ + shm_id = shmget(shm_key, invcntl->param.supsize + sizeof(long), IPC_CREAT | 0666); + if (shm_id == -1) + perror("Could not create shared memory segment"); + } else + read_index = 0; + + if (shm_id != -1) { + invcntl->iindex = shmat(shm_id, 0, ((read_index) ? 0 : SHM_RDONLY)); + if (invcntl->iindex == (char *)ERR) { + fprintf(stderr, "%s: shared memory link failed\n", argv0); + invcntl->iindex = NULL; + read_index = 1; + } + } + } +#endif + if (invcntl->iindex == NULL) + /* FIXME HBB: magic number alert (4, sizeof(long)) */ + invcntl->iindex = malloc((size_t) invcntl->param.supsize + 4 *sizeof(long)); + if (invcntl->iindex == NULL) { + invcannotalloc((size_t) invcntl->param.supsize); + free(invcntl->logblk); + fclose(invcntl->postfile); + fclose(invcntl->invfile); + return(-1); + } + if (read_index) { + fseek(invcntl->invfile, invcntl->param.startbyte, SEEK_SET); + fread(invcntl->iindex, (int) invcntl->param.supsize, 1, + invcntl->invfile); + } + invcntl->numblk = -1; + if (boolready() == -1) { + fclose(invcntl->postfile); + fclose(invcntl->invfile); + return(-1); + } + /* write back out the control block if anything changed */ + invcntl->param.filestat = stat; + if (stat > invcntl->param.filestat ) { + rewind(invcntl->invfile); + fwrite(&invcntl->param, sizeof(invcntl->param), 1, invcntl->invfile); + } + return(1); +} + +/** invclose must be called to wrap things up and deallocate core **/ +void +invclose(INVCONTROL *invcntl) +{ + /* write out the control block in case anything changed */ + if (invcntl->param.filestat > 0) { + invcntl->param.filestat = 0; + rewind(invcntl->invfile); + fwrite(&invcntl->param, 1, + sizeof(invcntl->param), invcntl->invfile); + } + if (invcntl->param.filestat == INVALONE) { + /* write out the super finger */ + fseek(invcntl->invfile, invcntl->param.startbyte, SEEK_SET); + fwrite(invcntl->iindex, 1, + (int) invcntl->param.supsize, invcntl->invfile); + } + fclose(invcntl->invfile); + fclose(invcntl->postfile); +#if SHARE + if (invcntl->param.share > 0) { + shmdt(invcntl->iindex); + invcntl->iindex = NULL; + } +#endif + if (invcntl->iindex != NULL) + free(invcntl->iindex); + free(invcntl->logblk); +} + +/** invstep steps the inverted file forward one item **/ +static void +invstep(INVCONTROL *invcntl) +{ + if (invcntl->keypnt < (invcntl->logblk->invblk[0] - 1)) { + invcntl->keypnt++; + return; + } + + /* move forward a block else wrap */ + invcntl->numblk = invcntl->logblk->invblk[1]; /* was: *(int *)(invcntl->logblk + sizeof(long))*/ + + /* now read in the block */ + fseek(invcntl->invfile, + invcntl->numblk*invcntl->param.sizeblk + invcntl->param.cntlsize, + SEEK_SET); + fread(invcntl->logblk, (int) invcntl->param.sizeblk, 1, + invcntl->invfile); + invcntl->keypnt = 0; +} + +/** invforward moves forward one term in the inverted file **/ +int +invforward(INVCONTROL *invcntl) +{ + invstep(invcntl); + /* skip things with 0 postings */ + /* FIXME HBB: magic number alert! (3) */ + while (((ENTRY * )(invcntl->logblk->invblk + 3) + invcntl->keypnt)->post == 0) { + invstep(invcntl); + } + /* Check for having wrapped - reached start of inverted file! */ + if ((invcntl->numblk == 0) && (invcntl->keypnt == 0)) + return(0); + return(1); +} + +/** invterm gets the present term from the present logical block **/ +long +invterm(INVCONTROL *invcntl, char *term) +{ + ENTRY * entryptr; + + /* FIXME HBB: magic number alert! (3) */ + entryptr = (ENTRY *)(invcntl->logblk->invblk + 3) + invcntl->keypnt; + strncpy(term, invcntl->logblk->chrblk + entryptr->offset, + (int) entryptr->size); + *(term + entryptr->size) = '\0'; + return(entryptr->post); +} + +/** invfind searches for an individual item in the inverted file **/ +long +invfind(INVCONTROL *invcntl, char *searchterm) /* term being searched for */ +{ + int imid, ilow, ihigh; + long num; + int i; + unsigned long *intptr, *intptr2; + ENTRY *entryptr; + + /* make sure it is initialized via invready */ + if (invcntl->invfile == 0) + return(-1L); + + /* now search for the appropriate finger block */ + intptr = (unsigned long *)invcntl->iindex; + + ilow = 0; + ihigh = *intptr++ - 1; + while (ilow <= ihigh) { + imid = (ilow + ihigh) / 2; + intptr2 = intptr + imid; + i = strcmp(searchterm, (invcntl->iindex + *intptr2)); + if (i < 0) + ihigh = imid - 1; + else if (i > 0) + ilow = ++imid; + else { + ilow = imid + 1; + break; + } + } + /* be careful about case where searchterm is after last in this block */ + imid = (ilow) ? ilow - 1 : 0; + + /* fetch the appropriate logical block if not in core */ + /* note always fetch it if the file is busy */ + if ((imid != invcntl->numblk) || (invcntl->param.filestat >= INVBUSY)) { + fseek(invcntl->invfile, + (imid*invcntl->param.sizeblk) + invcntl->param.cntlsize, + SEEK_SET); + invcntl->numblk = imid; + fread(invcntl->logblk, (int)invcntl->param.sizeblk, 1, + invcntl->invfile); + } + +srch_ext: + /* now find the term in this block. tricky this */ + intptr = (unsigned long *) invcntl->logblk->invblk; + + ilow = 0; + ihigh = *intptr - 1; + intptr += 3; + num = 0; + while (ilow <= ihigh) { + imid = (ilow + ihigh) / 2; + entryptr = (ENTRY *)intptr + imid; + i = strncmp(searchterm, invcntl->logblk->chrblk + entryptr->offset, + (int) entryptr->size ); + if (i == 0) + i = strlen(searchterm) - entryptr->size; + if (i < 0) + ihigh = imid - 1; + else if (i > 0) + ilow = ++imid; + else { + num = entryptr->post; + break; + } + } + /* be careful about case where searchterm is after last in this block */ + if (imid >= invcntl->logblk->invblk[0]) { + invcntl->keypnt = invcntl->logblk->invblk[0]; + invstep(invcntl); + /* note if this happens the term could be in extended block */ + if (invcntl->param.startbyte < invcntl->numblk * invcntl->param.sizeblk) + goto srch_ext; + } else + invcntl->keypnt = imid; + return(num); +} + +#if DEBUG + +/** invdump dumps the block the term parameter is in **/ +void +invdump(INVCONTROL *invcntl, char *term) +{ + long i, j, n, *longptr; + ENTRY * entryptr; + char temp[512], *ptr; + + /* dump superindex if term is "-" */ + if (*term == '-') { + j = atoi(term + 1); + longptr = (long *)invcntl->iindex; + n = *longptr++; + printf("Superindex dump, num blocks=%ld\n", n); + longptr += j; + while ((longptr <= ((long *)invcntl->iindex) + n) && invbreak == 0) { + printf("%2ld %6ld %s\n", j++, *longptr, invcntl->iindex + *longptr); + longptr++; + } + return; + } else if (*term == '#') { + j = atoi(term + 1); + /* fetch the appropriate logical block */ + invcntl->numblk = j; + fseek(invcntl->invfile, + (j * invcntl->param.sizeblk) + invcntl->param.cntlsize, + SEEK_SET); + fread(invcntl->logblk, (int) invcntl->param.sizeblk, 1, + invcntl->invfile); + } else + i = abs((int) invfind(invcntl, term)); + longptr = invcntl->logblk->invblk; + n = *longptr++; + printf("Entry term to invdump=%s, postings=%ld, forwrd ptr=%ld, back ptr=%ld\n" + , term, i, *(longptr), *(longptr + 1)); + /* FIXME HBB: magic number alert! (3) */ + entryptr = (ENTRY *) (invcntl->logblk->invblk + 3); + printf("%ld terms in this block, block=%ld\n", n, invcntl->numblk); + printf("\tterm\t\t\tposts\tsize\toffset\tspace\t1st word\n"); + for (j = 0; j < n && invbreak == 0; j++) { + ptr = invcntl->logblk->chrblk + entryptr->offset; + strncpy(temp, ptr, (int) entryptr->size); + temp[entryptr->size] = '\0'; + ptr += (sizeof(long) * (long)((entryptr->size + (sizeof(long) - 1)) / sizeof(long))); + printf("%2ld %-24s\t%5ld\t%3d\t%d\t%d\t%ld\n", j, temp, entryptr->post, + entryptr->size, entryptr->offset, entryptr->space, + *(long *)ptr); + entryptr++; + } +} +#endif + +static int +boolready(void) +{ + numitems = 0; + if (item1 != NULL) + free(item1); + setsize1 = SETINC; + if ((item1 = malloc(SETINC * sizeof(*item1))) == NULL) { + invcannotalloc(SETINC); + return(-1); + } + if (item2 != NULL) + free(item2); + setsize2 = SETINC; + if ((item2 = malloc(SETINC * sizeof(*item2))) == NULL) { + invcannotalloc(SETINC); + return(-1); + } + item = item1; + enditem = item; + return(0); +} + +void +boolclear(void) +{ + numitems = 0; + item = item1; + enditem = item; +} + +POSTING * +boolfile(INVCONTROL *invcntl, long *num, int boolarg) +{ + ENTRY *entryptr; + FILE *file; + void *ptr; + unsigned long *ptr2; + POSTING *newitem = NULL; /* initialize, to avoid warning */ + POSTING posting; + unsigned u; + POSTING *newsetp = NULL, *set1p; + long newsetc, set1c, set2c; + + /* FIXME HBB: magic number alert! (3) */ + entryptr = (ENTRY *) (invcntl->logblk->invblk + 3) + invcntl->keypnt; + ptr = invcntl->logblk->chrblk + entryptr->offset; + ptr2 = ((unsigned long *) ptr) + (entryptr->size + (sizeof(long) - 1)) / sizeof(long); + *num = entryptr->post; + switch (boolarg) { + case BOOL_OR: + case NOT: + if (*num == 0) { + *num = numitems; + return(item); + } + } + /* make room for the new set */ + u = 0; + switch (boolarg) { + case AND: + case NOT: + newsetp = item; + break; + + case BOOL_OR: + u = enditem - item; + /* FALLTHROUGH */ + case REVERSENOT: + u += *num; + if (item == item2) { + if (u > setsize1) { + u += SETINC; + if ((item1 = realloc(item1, u * sizeof(*item1))) == NULL) { + invcannotalloc(u * sizeof(*item1)); + boolready(); + *num = -1; + return(NULL); + } + setsize1 = u; + } + newitem = item1; + } + else { + if (u > setsize2) { + u += SETINC; + if ((item2 = realloc(item2, u * sizeof(*item2))) == NULL) { + invcannotalloc(u * sizeof(*item2)); + boolready(); + *num = -1; + return(NULL); + } + setsize2 = u; + } + newitem = item2; + } +#if 0 /* this write is only need by commented-out code later */ + set1p = item; +#endif + newsetp = newitem; + } + file = invcntl->postfile; + fseek(file, *ptr2, SEEK_SET); + fread(&posting, sizeof(posting), 1, file); + newsetc = 0; + switch (boolarg) { + case BOOL_OR: + /* while something in both sets */ + set1p = item; + newsetp = newitem; + for (set1c = 0, set2c = 0; + set1c < numitems && set2c < *num; newsetc++) { + if (set1p->lineoffset < posting.lineoffset) { + *newsetp++ = *set1p++; + set1c++; + } + else if (set1p->lineoffset > posting.lineoffset) { + *newsetp++ = posting; + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + else if (set1p->type < posting.type) { + *newsetp++ = *set1p++; + set1c++; + } + else if (set1p->type > posting.type) { + *newsetp++ = posting; + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + else { /* identical postings */ + *newsetp++ = *set1p++; + set1c++; + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + } + /* find out what ran out and move the rest in */ + if (set1c < numitems) { + newsetc += numitems - set1c; + while (set1c++ < numitems) { + *newsetp++ = *set1p++; + } + } else { + while (set2c++ < *num) { + *newsetp++ = posting; + newsetc++; + fread(&posting, (int) sizeof(posting), 1, file); + } + } + item = newitem; + break; /* end of BOOL_OR */ +#if 0 + case AND: + for (set1c = 0, set2c = 0; set1c < numitems && set2c < *num; ) { + if (set1p->lineoffset < posting.lineoffset) { + set1p++; + set1c++; + } + else if (set1p->lineoffset > posting.lineoffset) { + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + else if (set1p->type < posting.type) { + *set1p++; + set1c++; + } + else if (set1p->type > posting.type) { + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + else { /* identical postings */ + *newsetp++ = *set1p++; + newsetc++; + set1c++; + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + } + break; /* end of AND */ + + case NOT: + for (set1c = 0, set2c = 0; set1c < numitems && set2c < *num; ) { + if (set1p->lineoffset < posting.lineoffset) { + *newsetp++ = *set1p++; + newsetc++; + set1c++; + } + else if (set1p->lineoffset > posting.lineoffset) { + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + else if (set1p->type < posting.type) { + *newsetp++ = *set1p++; + newsetc++; + set1c++; + } + else if (set1p->type > posting.type) { + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + else { /* identical postings */ + set1c++; + set1p++; + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + } + newsetc += numitems - set1c; + while (set1c++ < numitems) { + *newsetp++ = *set1p++; + } + break; /* end of NOT */ + + case REVERSENOT: /* core NOT incoming set */ + for (set1c = 0, set2c = 0; set1c < numitems && set2c < *num; ) { + if (set1p->lineoffset < posting.lineoffset) { + set1p++; + set1c++; + } + else if (set1p->lineoffset > posting.lineoffset) { + *newsetp++ = posting; + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + else if (set1p->type < posting.type) { + set1p++; + set1c++; + } + else if (set1p->type > posting.type) { + *newsetp++ = posting; + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + else { /* identical postings */ + set1c++; + set1p++; + fread(&posting, (int) sizeof(posting), 1, file); + set2c++; + } + } + while (set2c++ < *num) { + *newsetp++ = posting; + newsetc++; + fread(&posting, (int) sizeof(posting), 1, file); + } + item = newitem; + break; /* end of REVERSENOT */ +#endif + } + numitems = newsetc; + *num = newsetc; + enditem = (POSTING *) newsetp; + return((POSTING *) item); +} + +#if 0 +POSTING * +boolsave(int clear) /* flag about whether to clear core */ +{ + int i; + POSTING *ptr; + POSTING *oldstuff, *newstuff; + + if (numitems == 0) { + if (clear) + boolclear(); + return(NULL); + } + /* if clear then give them what we have and use boolready to realloc */ + if (clear) { + ptr = item; + /* free up the space we didn't give them */ + if (item == item1) + item1 = NULL; + else + item2 = NULL; + boolready(); + return(ptr); + } + i = (enditem - item) * sizeof(*ptr) + 100; + if ((ptr = malloc(i)) == NULL) { + invcannotalloc(i); + return(ptr); + } + /* move present set into place */ + oldstuff = item; + newstuff = ptr; + while (oldstuff < enditem) + *newstuff++ = *oldstuff++; + return(ptr); +} +#endif + +static void +invcannotalloc(unsigned n) +{ + fprintf(stderr, "%s: cannot allocate %u bytes\n", argv0, n); +} + +static void +invcannotopen(char *file) +{ + fprintf(stderr, "%s: cannot open file %s\n", argv0, file); +} + +static void +invcannotwrite(char *file) +{ + perror(argv0); /* must be first to preserve errno */ + fprintf(stderr, "%s: write to file %s failed\n", argv0, file); +} diff --git a/src/invlib.h b/src/invlib.h new file mode 100644 index 0000000..c47b874 --- /dev/null +++ b/src/invlib.h @@ -0,0 +1,114 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +#ifndef CSCOPE_INVLIB_H +#define CSCOPE_INVLIB_H + +#include /* need definition of FILE* */ +#include /* need definition of CHAR_MAX */ + +/* inverted index definitions */ + +/* postings temporary file long number coding into characters */ +#if CHAR_MAX==255 +# define BASE 223 /* 255 - ' ' */ +# define PRECISION 4 /* maximum digits after converting a long */ +#else +# if CHAR_MAX==127 /* assume sign-extension of a char when converted to an int */ +# define BASE 95 /* 127 - ' ' */ +# define PRECISION 5 /* maximum digits after converting a long */ +# else + #error Need a platform with 8 bits in a char value +# endif +#endif + +/* inverted index access parameters */ +#define INVAVAIL 0 +#define INVBUSY 1 +#define INVALONE 2 + +/* boolean set operations */ +#define BOOL_OR 3 +#define AND 4 +#define NOT 5 +#define REVERSENOT 6 + +/* note that the entire first block is for parameters */ +typedef struct { + long version; /* inverted index format version */ + long filestat; /* file status word */ + long sizeblk; /* size of logical block in bytes */ + long startbyte; /* first byte of superfinger */ + long supsize; /* size of superfinger in bytes */ + long cntlsize; /* size of max cntl space (should be a multiple of BUFSIZ) */ + long share; /* flag whether to use shared memory */ +} PARAM; + +typedef struct { + FILE *invfile; /* the inverted file ptr */ + FILE *postfile; /* posting file ptr */ + PARAM param; /* control parameters for the file */ + char *iindex; /* ptr to space for superindex */ + union logicalblk *logblk; /* ptr to space for a logical block */ + long numblk; /* number of block presently at *logblk */ + long keypnt; /* number item in present block found */ +} INVCONTROL; + +typedef struct { + short offset; /* offset in this logical block */ + unsigned char size; /* size of term */ + unsigned char space; /* number of longs of growth space */ + long post; /* number of postings for this entry */ +} ENTRY; + +typedef struct { + long lineoffset; /* source line database offset */ + long fcnoffset; /* function name database offset */ + long fileindex : 24; /* source file name index */ + long type : 8; /* reference type (mark character) */ +} POSTING; + +extern long *srcoffset; /* source file name database offsets */ +extern int nsrcoffset; /* number of file name database offsets */ + + +void boolclear(void); +POSTING *boolfile(INVCONTROL *invcntl, long *num, int boolarg); +void invclose(INVCONTROL *invcntl); +void invdump(INVCONTROL *invcntl, char *term); +long invfind(INVCONTROL *invcntl, char *searchterm); +int invforward(INVCONTROL *invcntl); +int invopen(INVCONTROL *invcntl, char *invname, char *invpost, int status); +long invmake(char *invname, char *invpost, FILE *infile); +long invterm(INVCONTROL *invcntl, char *term); + +#endif /* CSCOPE_INVLIB_H */ diff --git a/src/library.h b/src/library.h new file mode 100644 index 0000000..4786730 --- /dev/null +++ b/src/library.h @@ -0,0 +1,51 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +#ifndef CSCOPE_LIBRARY_H +#define CSCOPE_LIBRARY_H + +#include /* need FILE* type def. */ + +/* private library */ +char *compath(char *pathname); +char *egrepinit(char *egreppat); +char *logdir(char *name); +char *basename(char *path); +FILE *myfopen(char *path, char *mode); +char *mygetenv(char *variable, char *deflt); +int myopen(char *path, int flag, int mode); +FILE *mypopen(char *cmd, char *mode); +int mypclose(FILE *ptr); +FILE *vpfopen(char *filename, char *type); +void egrepcaseless(int i); + +#endif /* CSCOPE_LIBRARY_H */ diff --git a/src/logdir.c b/src/logdir.c new file mode 100644 index 0000000..63903f8 --- /dev/null +++ b/src/logdir.c @@ -0,0 +1,100 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +/* + * logdir() + * + * This routine does not use the getpwent(3) library routine + * because the latter uses the stdio package. The allocation of + * storage in this package destroys the integrity of the shell's + * storage allocation. + */ + +#include +#include +#include "global.h" + +#define OURBUFSIZ 160 /* renamed: avoid conflict with */ + +static char line[OURBUFSIZ+1]; + +/* Internal prototypes: */ +static char *nextfield(char *p); + + +static char * +nextfield(char *p) +{ + while (*p && *p != ':') + ++p; + if (*p) *p++ = 0; + return(p); +} + +char * +logdir(char *name) +{ + char *p; + int i, j; + int pwf; + + /* attempt to open the password file */ + if ((pwf = myopen("/etc/passwd", 0, 0)) == -1) + return(0); + + /* find the matching password entry */ + do { + /* get the next line in the password file */ + i = read(pwf, line, OURBUFSIZ); + for (j = 0; j < i; j++) + if (line[j] == '\n') + break; + /* return a null pointer if the whole file has been read */ + if (j >= i) + return(0); + line[++j] = 0; /* terminate the line */ + (void) lseek(pwf, (long) (j - i), 1); /* point at the next line */ + p = nextfield(line); /* get the logname */ + } while (*name != *line || /* fast pretest */ + strcmp(name, line) != 0); + (void) close(pwf); + + /* skip the intervening fields */ + p = nextfield(p); + p = nextfield(p); + p = nextfield(p); + p = nextfield(p); + + /* return the login directory */ + (void) nextfield(p); + return(p); +} diff --git a/src/lookup.c b/src/lookup.c new file mode 100644 index 0000000..d8595db --- /dev/null +++ b/src/lookup.c @@ -0,0 +1,149 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * keyword look-up routine for the C symbol scanner + */ + +#include "global.h" +#include "lookup.h" + +/* keyword text for fast testing of keywords in the scanner */ +char enumtext[] = "enum"; +char externtext[] = "extern"; +char structtext[] = "struct"; +char typedeftext[] = "typedef"; +char uniontext[] = "union"; + +/* This keyword table is also used for keyword text compression. Keywords + * with an index less than the numeric value of a space are replaced with the + * control character corresponding to the index, so they cannot be moved + * without changing the database file version and adding compatibility code + * for old databases. + */ +struct keystruct keyword[] = { + {"", '\0', NULL}, /* dummy entry */ + {"#define", ' ', NULL}, /* must be table entry 1 */ + {"#include", ' ', NULL}, /* must be table entry 2 */ + {"break", '\0', NULL}, /* rarely in cross-reference */ + {"case", ' ', NULL}, + {"char", ' ', NULL}, + {"continue", '\0', NULL}, /* rarely in cross-reference */ + {"default", '\0', NULL}, /* rarely in cross-reference */ + {"double", ' ', NULL}, + {"\t", '\0', NULL}, /* must be the table entry 9 */ + {"\n", '\0', NULL}, /* must be the table entry 10 */ + {"else", ' ', NULL}, + {enumtext, ' ', NULL}, + {externtext, ' ', NULL}, + {"float", ' ', NULL}, + {"for", '(', NULL}, + {"goto", ' ', NULL}, + {"if", '(', NULL}, + {"int", ' ', NULL}, + {"long", ' ', NULL}, + {"register", ' ', NULL}, + {"return", '\0', NULL}, + {"short", ' ', NULL}, + {"sizeof", '\0', NULL}, + {"static", ' ', NULL}, + {structtext, ' ', NULL}, + {"switch", '(', NULL}, + {typedeftext, ' ', NULL}, + {uniontext, ' ', NULL}, + {"unsigned", ' ', NULL}, + {"void", ' ', NULL}, + {"while", '(', NULL}, + + /* these keywords are not compressed */ + {"do", '\0', NULL}, + {"auto", ' ', NULL}, + {"fortran", ' ', NULL}, + {"const", ' ', NULL}, + {"signed", ' ', NULL}, + {"volatile", ' ', NULL}, +}; +#define KEYWORDS (sizeof(keyword) / sizeof(keyword[0])) + +#define HASHMOD (KEYWORDS * 2 + 1) + +static struct keystruct *hashtab[HASHMOD]; /* pointer table */ + +/* put the keywords into the symbol table */ + +void +initsymtab(void) +{ + unsigned int i, j; + struct keystruct *p; + + for (i = 1; i < KEYWORDS; ++i) { + p = keyword + i; + j = hash(p->text) % HASHMOD; + p->next = hashtab[j]; + hashtab[j] = p; + } +} + +/* see if this identifier is a keyword */ + +char * +lookup(char *ident) +{ + struct keystruct *p; + int c; + + /* look up the identifier in the keyword table */ + for (p = hashtab[hash(ident) % HASHMOD]; p != NULL; p = p->next) { + if (strequal(ident, p->text)) { + if (compress == YES && (c = p - keyword) < ' ') { + ident[0] = c; /* compress the keyword */ + } + return(p->text); + } + } + /* this is an identifier */ + return(NULL); +} + +/* form hash value for string */ +int +hash(char *ss) +{ + int i; + unsigned char *s = (unsigned char *)ss; + + for (i = 0; *s != '\0'; ) + i += *s++; /* += is faster than <<= for cscope */ + return(i); +} diff --git a/src/lookup.h b/src/lookup.h new file mode 100644 index 0000000..47c37bb --- /dev/null +++ b/src/lookup.h @@ -0,0 +1,45 @@ +/*=========================================================================== + Copyright (c) 2001, 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 NOT 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 NOT 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. + =========================================================================*/ + +#ifndef CSCOPE_LOOKUP_H +#define CSCOPE_LOOKUP_H + +/* declarations for objects defined in lookup.c */ + +/* keyword text for fast testing of keywords in the scanner */ +extern char enumtext[]; +extern char externtext[]; +extern char structtext[]; +extern char typedeftext[]; +extern char uniontext[]; + +#endif /* CSCOPE_LOOKUP_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..843d787 --- /dev/null +++ b/src/main.c @@ -0,0 +1,674 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * main functions + */ + +#include "global.h" + +#include "build.h" +#include "vp.h" +#include "version.h" /* FILEVERSION and FIXVERSION */ +#include "scanner.h" + +#include /* atoi */ +#include +#include /* needed by stat.h */ +#include /* stat */ +#include +#include + +/* defaults for unset environment variables */ +#define EDITOR "vi" +#define HOME "/" /* no $HOME --> use root directory */ +#define SHELL "sh" +#define LINEFLAG "+%s" /* default: used by vi and emacs */ +#define TMPDIR "/tmp" + +/* note: these digraph character frequencies were calculated from possible + printable digraphs in the cross-reference for the C compiler */ +char dichar1[] = " teisaprnl(of)=c"; /* 16 most frequent first chars */ +char dichar2[] = " tnerpla"; /* 8 most frequent second chars + using the above as first chars */ +char dicode1[256]; /* digraph first character code */ +char dicode2[256]; /* digraph second character code */ + +char *editor, *shell, *lineflag; /* environment variables */ +char *home; /* Home directory */ +BOOL lineflagafterfile; +char *argv0; /* command name */ +BOOL compress = YES; /* compress the characters in the crossref */ +BOOL dbtruncated; /* database symbols are truncated to 8 chars */ +int dispcomponents = 1; /* file path components to display */ +#if CCS +BOOL displayversion; /* display the C Compilation System version */ +#endif +BOOL editallprompt = YES; /* prompt between editing files */ +unsigned int fileargc; /* file argument count */ +char **fileargv; /* file argument values */ +int fileversion; /* cross-reference file version */ +BOOL incurses = NO; /* in curses */ +BOOL invertedindex; /* the database has an inverted index */ +BOOL isuptodate; /* consider the crossref up-to-date */ +BOOL kernelmode; /* don't use DFLT_INCDIR - bad for kernels */ +BOOL linemode = NO; /* use line oriented user interface */ +BOOL verbosemode = NO; /* print extra information on line mode */ +BOOL recurse_dir = NO; /* recurse dirs when searching for src files */ +char *namefile; /* file of file names */ +BOOL ogs; /* display OGS book and subsystem names */ +char *prependpath; /* prepend path to file names */ +FILE *refsfound; /* references found file */ +char temp1[PATHLEN + 1]; /* temporary file name */ +char temp2[PATHLEN + 1]; /* temporary file name */ +char tempdirpv[PATHLEN + 1]; /* private temp directory */ +long totalterms; /* total inverted index terms */ +BOOL trun_syms; /* truncate symbols to 8 characters */ +char tempstring[TEMPSTRING_LEN + 1]; /* use this as a buffer, instead of 'yytext', + * which had better be left alone */ +char *tmpdir; /* temporary directory */ + + +/* Internal prototypes: */ +static void skiplist(FILE *oldrefs); +static void initcompress(void); +static inline void readenv(void); + +#if defined(KEY_RESIZE) && !defined(__DJGPP__) +void +sigwinch_handler(int sig, siginfo_t *info, void *unused) +{ + if(incurses == YES){ + ungetch(KEY_RESIZE); + } +} +#endif + +int +main(int argc, char **argv) +{ + FILE *names; /* name file pointer */ + int oldnum; /* number in old cross-ref */ + char path[PATHLEN + 1]; /* file path */ + FILE *oldrefs; /* old cross-reference file */ + 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; + /* save the command name for messages */ + argv0 = argv[0]; + + /* set the options */ + argv = parse_options(&argc, argv); + + /* read the environment */ + readenv(); + + /* XXX remove if/when clearerr() in dir.c does the right thing. */ + if (namefile && strcmp(namefile, "-") == 0 && !buildonly) { + postfatal("cscope: Must use -b if file list comes from stdin\n"); + /* NOTREACHED */ + } + + /* make sure that tmpdir exists */ + 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); + } + + /* create the temporary file names */ + 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 running in the foreground */ + if (signal(SIGINT, SIG_IGN) != SIG_IGN) { + /* cleanup on the interrupt and quit signals */ + signal(SIGINT, myexit); + signal(SIGQUIT, myexit); + } + /* cleanup on the hangup signal */ + signal(SIGHUP, myexit); + + /* ditto the TERM signal */ + signal(SIGTERM, myexit); + + /* ignore PIPE signal, so myexit() will have a chance to clean up in + * linemode, while in curses mode the "|" command can cause a pipe signal + * too + */ + signal(SIGPIPE, SIG_IGN); + + /* if the database path is relative and it can't be created */ + if (reffile[0] != '/' && access(".", WRITE) != 0) { + + /* put it in the home directory if the database may not be + * up-to-date or doesn't exist in the relative directory, + * so a database in the current directory will be + * used instead of failing to open a non-existant database in + * the home directory + */ + snprintf(path, sizeof(path), "%s/%s", home, reffile); + if (isuptodate == NO || access(path, READ) == 0) { + reffile = strdup(path); + snprintf(path, sizeof(path), "%s/%s", home, invname); + invname = strdup(path); + snprintf(path, sizeof(path), "%s/%s", home, invpost); + invpost = strdup(path); + } + } + + if (linemode == NO) { + signal(SIGINT, SIG_IGN); /* ignore interrupts */ + +#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 + + dispinit(); /* initialize display parameters */ + setfield(); /* set the initial cursor position */ + clearmsg(); /* clear any build progress message */ + display(); /* display the version number and input fields */ + } + + + /* if the cross-reference is to be considered up-to-date */ + if (isuptodate == YES) { + if ((oldrefs = vpfopen(reffile, "rb")) == NULL) { + postfatal("cscope: cannot open file %s\n", reffile); + /* NOTREACHED */ + } + /* get the crossref file version but skip the current directory */ + if (fscanf(oldrefs, "cscope %d %*s", &fileversion) != 1) { + postfatal("cscope: cannot read file version from file %s\n", + reffile); + /* NOTREACHED */ + } + if (fileversion >= 8) { + + /* override these command line options */ + compress = YES; + invertedindex = NO; + + /* see if there are options in the database */ + for (;;) { + getc(oldrefs); /* skip the blank */ + if ((c = getc(oldrefs)) != '-') { + ungetc(c, oldrefs); + break; + } + switch (getc(oldrefs)) { + case 'c': /* ASCII characters only */ + compress = NO; + break; + case 'q': /* quick search */ + invertedindex = YES; + fscanf(oldrefs, "%ld", &totalterms); + break; + case 'T': /* truncate symbols to 8 characters */ + dbtruncated = YES; + trun_syms = YES; + break; + } + } + initcompress(); + seek_to_trailer(oldrefs); + } + /* skip the source and include directory lists */ + skiplist(oldrefs); + skiplist(oldrefs); + + /* get the number of source files */ + if (fscanf(oldrefs, "%lu", &nsrcfiles) != 1) { + postfatal( + "cscope: cannot read source file size from file %s\n", + reffile); + /* NOTREACHED */ + } + /* get the source file list */ + srcfiles = malloc(nsrcfiles * sizeof(*srcfiles)); + if (fileversion >= 9) { + + /* allocate the string space */ + if (fscanf(oldrefs, "%d", &oldnum) != 1) { + postfatal( + "cscope: cannot read string space size from file %s\n", + reffile); + /* NOTREACHED */ + } + s = malloc(oldnum); + getc(oldrefs); /* skip the newline */ + + /* read the strings */ + if (fread(s, oldnum, 1, oldrefs) != 1) { + postfatal( + "cscope: cannot read source file names from file %s\n", + reffile); + /* NOTREACHED */ + } + /* change newlines to nulls */ + for (i = 0; i < nsrcfiles; ++i) { + srcfiles[i] = s; + for (++s; *s != '\n'; ++s) { + ; + } + *s = '\0'; + ++s; + } + /* if there is a file of source file names */ + if ((namefile != NULL && (names = vpfopen(namefile, "r")) != NULL) + || (names = vpfopen(NAMEFILE, "r")) != NULL) { + + /* read any -p option from it */ + while (fgets(path, sizeof(path), names) != NULL && *path == '-') { + i = path[1]; + s = path + 2; /* for "-Ipath" */ + if (*s == '\0') { /* if "-I path" */ + fgets(path, sizeof(path), names); + s = path; + } + switch (i) { + case 'p': /* file path components to display */ + 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); + /* NOTREACHED */ + } + srcfiles[i] = strdup(path); + } + } + fclose(oldrefs); + } else { + /* save the file arguments */ + fileargc = argc; + fileargv = argv; + + /* get source directories from the environment */ + if ((s = getenv("SOURCEDIRS")) != NULL) { + sourcedir(s); + } + /* make the source file list */ + srcfiles = malloc(msrcfiles * sizeof(*srcfiles)); + makefilelist(); + if (nsrcfiles == 0) { + postfatal("cscope: no source files found\n"); + /* NOTREACHED */ + } + /* get include directories from the environment */ + if ((s = getenv("INCLUDEDIRS")) != NULL) { + includedir(s); + } + /* add /usr/include to the #include directory list, + but not in kernelmode... kernels tend not to use it. */ + if (kernelmode == NO) { + if (NULL != (s = getenv("INCDIR"))) { + includedir(s); + } else { + includedir(DFLT_INCDIR); + } + } + + /* initialize the C keyword table */ + initsymtab(); + + /* Tell build.c about the filenames to create: */ + setup_build_filenames(reffile); + + /* build the cross-reference */ + initcompress(); + if (linemode == NO || verbosemode == YES) /* display if verbose as well */ + postmsg("Building cross-reference..."); + build(); + if (linemode == NO ) + clearmsg(); /* clear any build progress message */ + if (buildonly == YES) { + myexit(0); + } + } + opendatabase(); + + /* if using the line oriented user interface so cscope can be a + subprocess to emacs or samuel */ + if (linemode == YES) { + if (*Pattern != '\0') { /* do any optional search */ + if (search() == YES) { + /* print the total number of lines in + * verbose mode */ + 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); + } + /* remove any trailing newline character */ + 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': /* samuel only */ + field = *buf - '0'; + strcpy(Pattern, buf + 1); + if (search() == NO) { + printf("Unable to search database\n"); + } else { + printf("cscope: %d lines\n", totallines); + while ((c = getc(refsfound)) != EOF) { + putchar(c); + } + } + break; + + case 'c': /* toggle caseless mode */ + case ctrl('C'): + if (caseless == NO) { + caseless = YES; + } else { + caseless = NO; + } + egrepcaseless(caseless); + break; + + case 'r': /* rebuild database cscope style */ + case ctrl('R'): + freefilelist(); + makefilelist(); + /* FALLTHROUGH */ + + case 'R': /* rebuild database samuel style */ + rebuild(); + putchar('\n'); + break; + + case 'C': /* clear file names */ + freefilelist(); + putchar('\n'); + break; + + case 'F': /* add a file name */ + strcpy(path, buf + 1); + if (infilelist(path) == NO && + (s = inviewpath(path)) != NULL) { + addsrcfile(s); + } + putchar('\n'); + break; + + case 'q': /* quit */ + case ctrl('D'): + case ctrl('Z'): + myexit(0); + + default: + fprintf(stderr, "cscope: unknown command '%s'\n", buf); + break; + } + } + /* NOTREACHED */ + } + /* pause before clearing the screen if there have been error messages */ + if (errorsfound == YES) { + errorsfound = NO; + askforreturn(); + } + /* do any optional search */ + if (*Pattern != '\0') { + atfield(); /* move to the input field */ + command(ctrl('Y')); /* search */ + } else if (reflines != NULL) { + /* read any symbol reference lines file */ + readrefs(reflines); + } + display(); /* update the display */ + + for (;;) { + c = mygetch(); + + /* exit if the quit command is entered */ + if (c == EOF || c == ctrl('D')) { + break; + } + if (c == ctrl('Z')) { + kill(0, SIGTSTP); + continue; + } + + command(c); + display(); + } + /* cleanup and exit */ + myexit(0); + /* NOTREACHED */ + return 0; /* avoid warning... */ +} + +void +cannotopen(char *file) +{ + posterr("Cannot open file %s", file); +} + +/* FIXME MTE - should use postfatal here */ +void +cannotwrite(char *file) +{ + char msg[MSGLEN + 1]; + + snprintf(msg, sizeof(msg), "Removed file %s because write failed", file); + + myperror(msg); /* display the reason */ + + unlink(file); + myexit(1); /* calls exit(2), which closes files */ +} + + +/* set up the digraph character tables for text compression */ +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; + } + } +} + +/* skip the list in the cross-reference file */ + +static void +skiplist(FILE *oldrefs) +{ + int i; + + if (fscanf(oldrefs, "%d", &i) != 1) { + postfatal("cscope: cannot read list size from file %s\n", reffile); + /* NOTREACHED */ + } + while (--i >= 0) { + if (fscanf(oldrefs, "%*s") != 0) { + postfatal("cscope: cannot read list name from file %s\n", reffile); + /* NOTREACHED */ + } + } +} + + +/* enter curses mode */ +void +entercurses(void) +{ + incurses = YES; +#ifndef __MSDOS__ /* HBB 20010313 */ + nonl(); /* don't translate an output \n to \n\r */ +#endif + raw(); /* single character input */ + noecho(); /* don't echo input characters */ + clear(); /* clear the screen */ + mouseinit(); /* initialize any mouse interface */ + drawscrollbar(topline, nextline); +} + + +/* exit curses mode */ +void +exitcurses(void) +{ + /* clear the bottom line */ + move(LINES - 1, 0); + clrtoeol(); + refresh(); + + /* exit curses and restore the terminal modes */ + endwin(); + incurses = NO; + + /* restore the mouse */ + mousecleanup(); + fflush(stdout); +} + +/* cleanup and exit */ +void +myexit(int sig) +{ + /* HBB 20010313; close file before unlinking it. Unix may not care + * about that, but DOS absolutely needs it */ + if (refsfound != NULL) + fclose(refsfound); + + /* remove any temporary files */ + if (temp1[0] != '\0') { + unlink(temp1); + unlink(temp2); + rmdir(tempdirpv); + } + /* restore the terminal to its original mode */ + if (incurses == YES) { + exitcurses(); + } + /* dump core for debugging on the quit signal */ + if (sig == SIGQUIT) { + abort(); + } + /* HBB 20000421: be nice: free allocated data */ + freefilelist(); + freeinclist(); + freesrclist(); + freecrossref(); + free_newbuildfiles(); + + if( remove_symfile_onexit == YES ) { + unlink( reffile ); + unlink( invname ); + unlink( invpost ); + } + + exit(sig); +} + +static inline void readenv(void){ + editor = mygetenv("EDITOR", EDITOR); + editor = mygetenv("VIEWER", editor); /* use viewer if set */ + editor = mygetenv("CSCOPE_EDITOR", editor); /* has last word */ + 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); +} diff --git a/src/mouse.c b/src/mouse.c new file mode 100644 index 0000000..ea75b37 --- /dev/null +++ b/src/mouse.c @@ -0,0 +1,431 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * mouse functions + */ + +#include "global.h" + +BOOL mouse = NO; /* mouse interface */ + +#ifdef UNIXPC /* build command requires #ifdef instead of #if */ +#include +BOOL unixpcmouse = NO; /* running with a mouse on the Unix PC? */ +static int uw_hs, uw_vs; /* character height and width */ +#endif + +typedef struct { /* menu */ + char *text; + char *value; +} MENU; + +static MENU mainmenu[] = { /* main menu */ + {"Send", "##\033s##\r"}, + {"Repeat", "\031"}, + {"Edit All", "\05"}, + {"Rebuild", "\022"}, + {"Shell", "!"}, + {"Redraw", "\f"}, + {"Help", "?"}, + {"Exit", "\04"}, + {NULL, NULL} +}; + +static MENU changemenu[] = { /* change mode menu */ + {"Mark Screen", "*"}, + {"Mark All", "a"}, + {"Change", "\04"}, + {"No Change", "\033"}, + {"Shell", "!"}, + {"Redraw", "\f"}, + {"Help", "?"}, + {NULL, NULL} +}; + +static MENU *loaded; /* menu loaded */ +static BOOL emacsviterm = NO; /* terminal type */ + +static void loadmenu(MENU *menu); +static int getcoordinate(void); +static int getpercent(void); + +/* see if there is a mouse interface */ + +void +mouseinit(void) +{ + char *term; + + /* see if this is emacsterm or viterm */ + term = mygetenv("TERM", ""); + if (strcmp(term, "emacsterm") == 0 || + strcmp(term, "viterm") == 0) { + emacsviterm = YES; + mouse = YES; + } + /* the MOUSE enviroment variable is for 5620 terminal programs that have + mouse support but the TERM environment variable is the same as a + terminal without a mouse, such as myx */ + else if (strcmp(mygetenv("MOUSE", ""), "myx") == 0) { + mouse = YES; + } +#if UNIXPC + else if (strcmp(term,"s4") == 0 || + strcmp(term,"s120") == 0 || + strcmp(term,"s90") == 0) { + int retval; + struct uwdata uwd; /* Window data structure */ + struct umdata umd; /* Mouse data structure */ + + /* Ask for character size info */ + + retval = ioctl(1,WIOCGETD,&uwd); + if(retval || uwd.uw_hs <= 0 || uwd.uw_vs <= 0) { + /************************************************** + * something wrong with the kernel, so fake it... + **************************************************/ + if(!strcmp(term,"s4")) { + uw_hs = 9; + uw_vs = 12; + } + else { + uw_hs = 6; + uw_vs = 10; + } + } + else { + /* Kernel is working and knows about this font */ + uw_hs = uwd.uw_hs; + uw_vs = uwd.uw_vs; + } + + /************************************************** + * Now turn on mouse reporting so we can actually + * make use of all this stuff. + **************************************************/ + if((retval = ioctl(1,WIOCGETMOUSE,&umd)) != -1) { + umd.um_flags= MSDOWN+MSUP; + ioctl(1,WIOCSETMOUSE,&umd); + } + unixpcmouse = YES; + } +#endif + if (mouse == YES) { + loadmenu(mainmenu); + } +} + +/* load the correct mouse menu */ + +void +mousemenu(void) +{ + if (mouse == YES) { + if (changing == YES) { + loadmenu(changemenu); + } + else { + loadmenu(mainmenu); + } + } +} + +/* download a menu */ + +static void +loadmenu(MENU *menu) +{ + int i; + + if (emacsviterm == YES) { + mousereinit(); + (void) printf("\033V1"); /* display the scrollbar */ + (void) printf("\033M0@%s@%s@", menu[0].text, menu[0].value); + for (i = 1; menu[i].text != NULL; ++i) { + (void) printf("\033M@%s@%s@", menu[i].text, menu[i].value); + } + } + else { /* myx */ + int len; + + mousecleanup(); + (void) printf("\033[6;1X\033[9;1X"); + for (i = 0; menu[i].text != NULL; ++i) { + len = strlen(menu[i].text); + (void) printf("\033[%d;%dx%s%s", len, + (int) (len + strlen(menu[i].value)), + menu[i].text, menu[i].value); + } + loaded = menu; + } + (void) fflush(stdout); +} + +/* reinitialize the mouse in case curses changed the attributes */ + +void +mousereinit(void) +{ + if (emacsviterm == YES) { + + /* enable the mouse click and sweep coordinate control sequence */ + /* and switch to menu 2 */ + (void) printf("\033{2\033#2"); + (void) fflush(stdout); + } +} + +/* restore the mouse attributes */ + +void +mousecleanup(void) +{ + int i; + + if (loaded != NULL) { /* only true for myx */ + + /* remove the mouse menu */ + (void) printf("\033[6;0X\033[9;0X"); + for (i = 0; loaded[i].text != NULL; ++i) { + (void) printf("\033[0;0x"); + } + loaded = NULL; + } +} + +/* draw the scrollbar */ + +void +drawscrollbar(int top, int bot) +{ + int p1, p2; + + if (emacsviterm == YES) { + if (bot > top) { + p1 = 16 + (top - 1) * 100 / totallines; + p2 = 16 + (bot - 1) * 100 / totallines; + if (p2 > 116) { + p2 = 116; + } + if (p1 < 16) { + p1 = 16; + } + /* don't send ^S or ^Q because it will hang a layer using cu(1) */ + if (p1 == ctrl('Q') || p1 == ctrl('S')) { + ++p1; + } + if (p2 == ctrl('Q') || p2 == ctrl('S')) { + ++p2; + } + } + else { + p1 = p2 = 16; + } + (void) printf("\033W%c%c", p1, p2); + } +} + +/* get the mouse information */ + +MOUSE * +getmouseaction(char leading_char) +{ + static MOUSE m; + +#if UNIXPC + + if(unixpcmouse == YES && leading_char == ESC) { + + /* Called if cscope received an ESC character. See if it is + * a mouse report and if so, decipher it. A mouse report + * looks like: "[?xx;yy;b;rM" + */ + int x = 0, y = 0, button = 0, reason = 0; + int i; + + /* Get a mouse report. The form is: XX;YY;B;RM where + * XX is 1,2, or 3 decimal digits with the X pixel position. + * Similarly for YY. B is a single decimal digit with the + * button number (4 for one, 2 for two, and 1 for three). + * R is the reason for the mouse report. + * + * In general, the input is read until the mouse report has + * been completely read in or we have discovered that this + * escape sequence is NOT a mouse report. In the latter case + * return the last character read to the input stream with + * myungetch(). + */ + + /* Check for "[?" being next 2 chars */ + if(((i = mygetch()) != '[') || ((i = mygetch()) != '?')) { + myungetch(i); + return(NULL); + } + + /* Grab the X position (in pixels) */ + while(isdigit(i = mygetch())) { + x = (x*10) + (i - '0'); + } + if(i != ';') { + myungetch(i); + return(NULL); /* not a mouse report after all */ + } + + /* Grab the Y position (in pixels) */ + while(isdigit(i = mygetch())) { + y = (y*10) + (i - '0'); + } + if(i != ';') { + myungetch(i); + return(NULL); + } + + /* Get which button */ + if((button = mygetch()) > '4') { + myungetch(button); + return(NULL); + } + if((i = mygetch()) != ';') { + myungetch(i); + return(NULL); + } + + /* Get the reason for this mouse report */ + if((reason = mygetch()) > '8') { + myungetch(reason); + return(NULL); + } + + /* sequence should terminate with an 'M' */ + if((i = mygetch()) != 'M') { + myungetch(i); + return(NULL); + } + + + /* OK. We get a mouse report whenever a button is depressed + * or released. Let's ignore the report whenever the button + * is depressed until when I am ready to implement sweeping. + */ + if(reason != '2') { + return(NULL); /* '2' means button is released */ + } + + /************************************************************ + * Always indicate button 1 irregardless of which button was + * really pushed. + ************************************************************/ + m.button = 1; + + /************************************************************ + * Convert pixel coordinates to line and column coords. + * The height and width are obtained using an ioctl() call + * in mouseinit(). This assumes that variable width chars + * are not being used ('though it would probably work anyway). + ************************************************************/ + + m.x1 = x/uw_hs; /* pixel/horizontal_spacing */ + m.y1 = y/uw_vs; /* pixel/vertical_spacing */ + + /* "null" out the other fields */ + m.percent = m.x2 = m.y2 = -1; + } + else +#endif /* not UNIXPC */ + + if (mouse == YES && leading_char == ctrl('X')) { + + switch (mygetch()) { + case ctrl('_'): /* click */ + if ((m.button = mygetch()) == '0') { /* if scrollbar */ + m.percent = getpercent(); + } + else { + m.x1 = getcoordinate(); + m.y1 = getcoordinate(); + m.x2 = m.y2 = -1; + } + break; + + case ctrl(']'): /* sweep */ + m.button = mygetch(); + m.x1 = getcoordinate(); + m.y1 = getcoordinate(); + m.x2 = getcoordinate(); + m.y2 = getcoordinate(); + break; + default: + return(NULL); + } + } + else return(NULL); + + return(&m); +} + +/* get a row or column coordinate from a mouse button click or sweep */ + +static int +getcoordinate(void) +{ + int c, next; + + c = mygetch(); + next = 0; + if (c == ctrl('A')) { + next = 95; + c = mygetch(); + } + if (c < ' ') { + return (0); + } + return (next + c - ' '); +} + +/* get a percentage */ + +static int +getpercent(void) +{ + int c; + + c = mygetch(); + if (c < 16) { + return(0); + } + if (c > 120) { + return(100); + } + return(c - 16); +} diff --git a/src/mygetenv.c b/src/mygetenv.c new file mode 100644 index 0000000..0a6f53b --- /dev/null +++ b/src/mygetenv.c @@ -0,0 +1,49 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +#include "library.h" + +#include + +/* return the non-null environment value or the default argument */ + +char * +mygetenv(char *variable, char *deflt) +{ + char *value; + + value = getenv(variable); + if (value == NULL || *value == '\0') { + return(deflt); + } + return(value); +} diff --git a/src/mypopen.c b/src/mypopen.c new file mode 100644 index 0000000..24e55d5 --- /dev/null +++ b/src/mypopen.c @@ -0,0 +1,208 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +#include +#include +#include +#include +#include +#include "global.h" /* pid_t, shell, and basename() */ + +#define tst(a,b) (*mode == 'r'? (b) : (a)) +#define RDR 0 +#define WTR 1 + +/* HBB 20010312: make this a bit safer --- don't blindly assume it's 1 */ +#ifdef FD_CLOEXEC +# define CLOSE_ON_EXEC FD_CLOEXEC +#else +# define CLOSE_ON_EXEC 1 +#endif + +#ifdef HAVE_IO_H +# include /* for setmode() */ +#endif + +static pid_t popen_pid[20]; +static void (*tstat)(int); + +int +myopen(char *path, int flag, int mode) +{ + /* opens a file descriptor and then sets close-on-exec for the file */ + int fd; + + /* 20020103: if file is not explicitly in Binary mode, make + * sure we override silly Cygwin behaviour of automatic binary + * mode for files in "binary mounted" paths */ +#if O_BINARY != O_TEXT + if (! (flag | O_BINARY)) + flag |= O_TEXT; +#endif + if(mode) + fd = open(path, flag, mode); + else + fd = open(path, flag); + +#ifdef __DJGPP__ /* FIXME: test feature, not platform */ + /* HBB 20010312: DOS GCC doesn't have FD_CLOEXEC (yet), so it + * always fails this call. Have to skip that step */ + if(fd != -1) + return(fd); +#endif + if(fd != -1 && (fcntl(fd, F_SETFD, CLOSE_ON_EXEC) != -1)) + return(fd); + + else + { + /* Ensure that if the fcntl fails and fd is valid, then + the file is closed properly. In general this should + not happen. */ + if (fd != -1) + { + close (fd); + } + + return(-1); + } +} + +FILE * +myfopen(char *path, char *mode) +{ + /* opens a file pointer and then sets close-on-exec for the file */ + FILE *fp; + + fp = fopen(path, mode); + +#ifdef SETMODE + if (fp && ! strchr(mode, 'b')) { + SETMODE(fileno(fp), O_TEXT); + } +#endif /* SETMODE */ + +#ifdef __DJGPP__ /* FIXME: test feature, not platform */ + /* HBB 20010312: DOS GCC doesn't have FD_CLOEXEC (yet), so it + * always fails this call. Have to skip that step */ + if(fp) +#else + if(fp && (fcntl(fileno(fp), F_SETFD, CLOSE_ON_EXEC) != -1)) +#endif + return(fp); + + else { + if (fp) + fclose(fp); + return(NULL); + } +} + +FILE * +mypopen(char *cmd, char *mode) +{ +#ifdef __DJGPP__ + /* HBB 20010312: Has its own implementation of popen(), which + * is better suited to the platform than cscope's */ + return (popen)(cmd, mode); +#else + int p[2]; + pid_t *poptr; + int myside, yourside; + pid_t pid; + + if(pipe(p) < 0) + return(NULL); + myside = tst(p[WTR], p[RDR]); + yourside = tst(p[RDR], p[WTR]); + if((pid = fork()) == 0) { + /* myside and yourside reverse roles in child */ + int stdio; + + /* close all pipes from other popen's */ + for (poptr = popen_pid; poptr < popen_pid+20; poptr++) { + if(*poptr) + (void) close(poptr - popen_pid); + } + stdio = tst(0, 1); + close(myside); + close(stdio); +#if V9 + dup2(yourside, stdio); +#else + fcntl(yourside, F_DUPFD, stdio); +#endif + close(yourside); + execlp(shell, basename(shell), "-c", cmd, (void *)0); + _exit(1); + } else if (pid > 0) + tstat = signal(SIGTSTP, SIG_DFL); + if(pid == -1) + return(NULL); + popen_pid[myside] = pid; + (void) close(yourside); + return(fdopen(myside, mode)); +#endif /* DJGPP */ +} + +/* HBB 20010705: renamed from 'pclose', which would collide with + * system-supplied function of same name */ +int +mypclose(FILE *ptr) +{ +#ifdef __DJGPP__ + /* HBB 20010705: This system has its own pclose(), which we + * don't want to replace */ + return (pclose)(ptr); +#else + int f; + pid_t r; + int status = -1; + sighandler_t hstat, istat, qstat; + + f = fileno(ptr); + (void) fclose(ptr); + istat = signal(SIGINT, SIG_IGN); + qstat = signal(SIGQUIT, SIG_IGN); + hstat = signal(SIGHUP, SIG_IGN); + while((r = wait(&status)) != popen_pid[f] && r != -1) + ; /* nothing */ + if(r == -1) + status = -1; + (void) signal(SIGINT, istat); + (void) signal(SIGQUIT, qstat); + (void) signal(SIGHUP, hstat); + (void) signal(SIGTSTP, tstat); + /* mark this pipe closed */ + popen_pid[f] = 0; + return(status); +#endif /* DJGPP */ +} diff --git a/src/opt.c b/src/opt.c new file mode 100644 index 0000000..58bee40 --- /dev/null +++ b/src/opt.c @@ -0,0 +1,159 @@ +#include "global.h" + +#include "build.h" +#include "vp.h" +#include "version.h" /* FILEVERSION and FIXVERSION */ +#include "scanner.h" + +#include /* atoi */ +#include + +BOOL remove_symfile_onexit = NO; +BOOL onesearch; /* one search only in line mode */ +char *reflines; /* symbol reference lines file */ + +char ** parse_options(int *argc, char **argv) +{ + int opt; + int longind; + char path[PATHLEN + 1]; /* file path */ + char *s; + int argcc = *argc; + + struct option lopts[] = { + {"help", 0, NULL, 'h'}, + {"version", 0, NULL, 'V'}, + {0, 0, 0, 0} + }; + + while ((opt = getopt_long(argcc, argv, + "hVbcCdeF:f:I:i:kLl0:1:2:3:4:5:6:7:8:9:P:p:qRs:TUuvX", + lopts, &longind)) != -1) { + switch(opt) { + + case '?': + usage(); + myexit(1); + break; + case 'X': + remove_symfile_onexit = YES; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* The input fields numbers for line mode operation */ + field = opt - '0'; + if (strlen(optarg) > PATHLEN) { + postfatal("\ + cscope: pattern too long, cannot be > \ + %d characters\n", PATLEN); + } + strcpy(Pattern, optarg); + break; + case 'b': /* only build the cross-reference */ + buildonly = YES; + linemode = YES; + break; + case 'c': /* ASCII characters only in crossref */ + compress = NO; + break; + case 'C': /* turn on caseless mode for symbol searches */ + caseless = YES; + egrepcaseless(caseless); /* simulate egrep -i flag */ + break; + case 'd': /* consider crossref up-to-date */ + isuptodate = YES; + break; + case 'e': /* suppress ^E prompt between files */ + editallprompt = NO; + break; + case 'h': + longusage(); + myexit(1); + break; + case 'k': /* ignore DFLT_INCDIR */ + kernelmode = YES; + break; + case 'L': + onesearch = YES; + /* FALLTHROUGH */ + case 'l': + linemode = YES; + break; + case 'v': + verbosemode = YES; + break; + case 'V': + fprintf(stderr, "%s: version %d%s\n", argv0, + FILEVERSION, FIXVERSION); + myexit(0); + break; + case 'q': /* quick search */ + invertedindex = YES; + break; + case 'T': /* truncate symbols to 8 characters */ + trun_syms = YES; + break; + case 'u': /* unconditionally build the cross-reference */ + unconditional = YES; + break; + case 'U': /* assume some files have changed */ + fileschanged = YES; + break; + case 'R': + recurse_dir = YES; + break; + case 'f': /* alternate cross-reference file */ + reffile = optarg; + if (strlen(reffile) > sizeof(path) - 3) { + postfatal("\ + cscope: reffile too long, cannot \ + be > %d characters\n", sizeof(path) - 3); + /* NOTREACHED */ + } + strcpy(path, reffile); + + s = path + strlen(path); + strcpy(s, ".in"); + /*coverity[overwrite_var]*/ + invname = strdup(path); + strcpy(s, ".po"); + /*coverity[overwrite_var]*/ + invpost = strdup(path); + break; + + case 'F': /* symbol reference lines file */ + reflines = optarg; + break; + case 'i': /* file containing file names */ + namefile = optarg; + break; + case 'I': /* #include file directory */ + includedir(optarg); + break; + case 'p': /* file path components to display */ + dispcomponents = atoi(optarg); + break; + case 'P': /* prepend path to file names */ + prependpath = optarg; + break; + case 's': /* additional source file directory */ + sourcedir(optarg); + break; + } + } + /* + * This adjusts argv so that we only see the remaining + * args. Its ugly, but we need to do it so that the rest + * of the main routine doesn't get all confused + */ + *argc = *argc - optind; + return argv + optind; +} diff --git a/src/scanner.h b/src/scanner.h new file mode 100644 index 0000000..f387600 --- /dev/null +++ b/src/scanner.h @@ -0,0 +1,94 @@ +/*=========================================================================== + Copyright (c) 2001, 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 NOT 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 NOT 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. + =========================================================================*/ + +#ifndef CSCOPE_SCANNER_H +#define CSCOPE_SCANNER_H + +#include + +#undef YYLMAX +#define YYLMAX STMTMAX + PATLEN + 1 /* scanner line buffer size */ + +/* cross-reference database mark characters (when new ones are added, + * update the cscope.out format description in cscope.1) + */ +#define CLASSDEF 'c' +#define DEFINE '#' +#define DEFINEEND ')' +#define ENUMDEF 'e' +#define FCNCALL '`' +#define FCNDEF '$' +#define FCNEND '}' +#define GLOBALDEF 'g' +#define INCLUDE '~' +#define MEMBERDEF 'm' +#define NEWFILE '@' +#define STRUCTDEF 's' +#define TYPEDEF 't' +#define UNIONDEF 'u' + +/* other scanner token types */ +#define LEXEOF 0 +#define LEXERR 1 +#define IDENT 2 +#define NEWLINE 3 + +/* scanner.l global data */ +extern int first; /* buffer index for first char of symbol */ +extern int last; /* buffer index for last char of symbol */ +extern int lineno; /* symbol line number */ +extern FILE *yyin; /* input file descriptor */ +extern FILE *yyout; /* output file */ +extern int myylineno; /* input line number */ + +#ifdef USING_LEX +/* HBB 20010430: if lex is used instead of flex, have to simulate the + * private copies of yytext and yytext for the world outside scanner.l: */ +/* FIXME: there should be a feature test for this! */ +#if defined(__OSF1__) || defined(__sun) || defined(_AIX) +extern char yytext[]; +#else +extern unsigned char yytext[]; +#endif +extern int yyleng; +# define my_yytext yytext +# define my_yyleng yyleng +#else +extern char *my_yytext; /* private copy of input line */ +extern size_t my_yyleng; /* ... and current length of it */ +#endif + +/* The master function exported by scanner.l */ +int yylex(void); +void initscanner(char *srcfile); + +#endif /* CSCOPE_SCANNER_H ends */ diff --git a/src/version.h b/src/version.h new file mode 100644 index 0000000..6f8b33e --- /dev/null +++ b/src/version.h @@ -0,0 +1,45 @@ +/*=========================================================================== + 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 NOT 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 NOT 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 + * + * Changing the cross-reference file part of the program version + * forces rebuilding of the cross-reference. + */ + +#ifndef CSCOPE_VERSION_H +#define CSCOPE_VERSION_H + +#define FILEVERSION 15 /* Initial Open Source and Linux Port */ +#define FIXVERSION ".9" /* feature and bug fix version */ + +#endif /* CSCOPE_VERSION_H */ diff --git a/src/vp.h b/src/vp.h new file mode 100644 index 0000000..e5aa4ef --- /dev/null +++ b/src/vp.h @@ -0,0 +1,70 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +/* + * VPATH assumptions: + * VPATH is the environment variable containing the view path + * where each path name is followed by ':', '\n', or '\0'. + * Embedded blanks are considered part of the path. + */ + +#ifndef CSCOPE_VP_H +#define CSCOPE_VP_H + +#define MAXPATH 200 /* max length for entire name */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#else +# define HAVE_FCNTL_H 1 /* in case of doubt, assume it's there */ +#endif +#ifdef HAVE_FCNTL_H +# include /* needed for O_... open flags */ +#endif + +#include +#include + +#if !NOMALLOC +extern char **vpdirs; /* directories (including current) in view path */ +#else +#define MAXDIR 25 /* same as libVP */ +#define DIRLEN 80 /* same as libVP */ +extern char vpdirs[MAXDIR][DIRLEN + 1]; +#endif +extern int vpndirs; /* number of directories in view path */ + +void vpinit(char *current_dir); +int vpopen(char *path, int oflag); +int vpaccess(char *path, mode_t amode); + +#endif /* CSCOPE_VP_H */ diff --git a/src/vpaccess.c b/src/vpaccess.c new file mode 100644 index 0000000..d8c8be7 --- /dev/null +++ b/src/vpaccess.c @@ -0,0 +1,57 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +/* vpaccess - view path version of the access system call */ + +#include +#include +#include "vp.h" +#include + +int +vpaccess(char *path, mode_t amode) +{ + char buf[MAXPATH + 1]; + int returncode; + int i; + + if ((returncode = access(path, amode)) == -1 && path[0] != '/') { + vpinit(NULL); + for (i = 1; i < vpndirs; i++) { + (void) snprintf(buf, sizeof(buf), "%s/%s", vpdirs[i], path); + if ((returncode = access(buf, amode)) != -1) { + break; + } + } + } + return(returncode); +} diff --git a/src/vpfopen.c b/src/vpfopen.c new file mode 100644 index 0000000..b236864 --- /dev/null +++ b/src/vpfopen.c @@ -0,0 +1,62 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +/* vpfopen - view path version of the fopen library function */ + +#include +#include +#include "vp.h" +#include "global.h" + +FILE * +vpfopen(char *filename, char *type) +{ + char buf[MAXPATH + 1]; + FILE *returncode; + int i; + + if ((returncode = myfopen(filename, type)) == NULL + && filename[0] != '/' + /* && strcmp(type, "r") == 0 */ /* HBB: this breaks if type=="rb" */ + && type[0] == 'r' + ) { + vpinit(NULL); + for (i = 1; i < vpndirs; i++) { + (void) snprintf(buf, sizeof(buf), "%s/%s", vpdirs[i], filename); + if ((returncode = myfopen(buf, type)) != NULL) { + break; + } + + } + } + return(returncode); +} diff --git a/src/vpinit.c b/src/vpinit.c new file mode 100644 index 0000000..9535f8a --- /dev/null +++ b/src/vpinit.c @@ -0,0 +1,169 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + +/* vpinit - initialize vpdirs or update vpdirs based on currentdir */ + +#include /* stderr */ +#include +#include +#include +#include "vp.h" + +#include "library.h" +#include "global.h" +#include "constants.h" + +#if !NOMALLOC +char **vpdirs; /* directories (including current) in view path */ +#else +char vpdirs[MAXDIR][DIRLEN + 1]; +#define MAXVPATH (MAXDIR * (DIRLEN + 1)) +#endif +int vpndirs; /* number of directories in view path */ + +void +vpinit(char *current_dir) +{ + char *suffix; /* path from view path node */ + char *vpath; /* VPATH environment variable value */ + char buf[MAXPATH + 1]; + int i; + char *s; +#if NOMALLOC + char *node; /* view path node */ + char vpathbuf[MAXVPATH + 1]; +#endif + + /* if an existing directory list is to be updated, free it */ + if (current_dir != NULL && vpndirs > 0) { +#if !NOMALLOC + for (i = 0; i < vpndirs; ++i) { + free(vpdirs[i]); + } + free(vpdirs); +#endif + vpndirs = 0; + } + /* return if the directory list has been computed */ + /* or there isn't a view path environment variable */ + if (vpndirs > 0 || (vpath = getenv("VPATH")) == NULL || + *vpath == '\0') { + return; + } + /* if not given, get the current directory name */ + if (current_dir == NULL && (current_dir = getcwd(buf, MAXPATH)) == NULL) { + (void) fprintf(stderr, "%s: cannot get current directory name\n", argv0); + return; + } + /* see if this directory is in the first view path node */ + for (i = 0; vpath[i] == current_dir[i] && vpath[i] != '\0'; ++i) { + ; + } + if ((vpath[i] != ':' && vpath[i] != '\0') || + (current_dir[i] != '/' && current_dir[i] != '\0')) { + return; + } + suffix = ¤t_dir[i]; +#if !NOMALLOC + + /* count the nodes in the view path */ + vpndirs = 1; + for (i = 0; vpath[i] != '\0'; ++i) { + if (vpath[i] == ':' && vpath[i + 1]) { + ++vpndirs; + } + } + /* create the source directory list */ + vpdirs = malloc(vpndirs * sizeof(*vpdirs)); + + /* don't change VPATH in the environment */ + vpath = strdup(vpath); + + /* split the view path into nodes */ + for (i = 0, s = vpath; *s != '\0'; ++i) { + vpdirs[i] = s; + while (*s != '\0' && *++s != ':') { + if (*s == '\n') { + *s = '\0'; + } + } + if (*s != '\0') { + *s++ = '\0'; + } + } + /* convert the view path nodes to directories */ + for (i = 0; i < vpndirs; ++i) { + s = malloc(strlen(vpdirs[i]) + strlen(suffix) + 1); + (void) strcpy(s, vpdirs[i]); + (void) strcat(s, suffix); + vpdirs[i] = s; + } + free(vpath); +#else + /* don't change VPATH in the environment */ + if (strlen(vpath) > MAXVPATH) { + (void) fprintf(stderr, "%s: VPATH is longer than %d characters: %s\n", argv0, MAXVPATH, vpath); + return; + } + (void) strcpy(vpathbuf, vpath); + s = vpathbuf; + + /* convert the view path nodes to directories */ + while (*s != '\0') { + + /* get the next node */ + node = s; + while (*s != '\0' && *++s != ':') { + if (*s == '\n') { + *s = '\0'; + } + } + if (*s != '\0') { + *s++ = '\0'; + } + /* ignore a directory that is too long */ + if (strlen(node) + strlen(suffix) > DIRLEN) { + (void) fprintf(stderr, "%s: VPATH directory is longer than %d characters: %s%s\n", argv0, DIRLEN, node, suffix); + } + else if (vpndirs >= MAXDIR) { + (void) fprintf(stderr, "%s: VPATH has more than %d nodes\n", argv0, vpndirs); + return; + } + else { + /* create the view path directory */ + (void) strcpy(vpdirs[vpndirs], node); + (void) strcat(vpdirs[vpndirs], suffix); + ++vpndirs; + } + } +#endif +} diff --git a/src/vpopen.c b/src/vpopen.c new file mode 100644 index 0000000..8eac788 --- /dev/null +++ b/src/vpopen.c @@ -0,0 +1,60 @@ +/*=========================================================================== + 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 NOT 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 NOT 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. + =========================================================================*/ + + +/* vpopen - view path version of the open system call */ + +#include +#include "global.h" +#include "vp.h" + +#define OPENFLAG_READ 0 + +int +vpopen(char *path, int oflag) +{ + char buf[MAXPATH + 1]; + int returncode; + int i; + + if ((returncode = myopen(path, oflag, 0666)) == -1 && path[0] != '/' && + oflag == OPENFLAG_READ) { + vpinit(NULL); + for (i = 1; i < vpndirs; i++) { + (void) snprintf(buf, sizeof(buf), "%s/%s", vpdirs[i], path); + if ((returncode = myopen(buf, oflag, 0666)) != -1) { + break; + } + } + } + return(returncode); +}