X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/1875d994439974e29f0005b68167a6a991aa1564..6469b6d26b6a3abe42953da81726b8575469d205:/lib-src/etags.c diff --git a/lib-src/etags.c b/lib-src/etags.c index 066e430ebc..536ca567f1 100644 --- a/lib-src/etags.c +++ b/lib-src/etags.c @@ -1,5 +1,6 @@ /* Tags file maker to go with GNU Emacs - Copyright (C) 1984, 1987, 1988, 1989, 1993 Free Software Foundation, Inc. and Ken Arnold + Copyright (C) 1984, 87, 88, 89, 93, 94, 95 + Free Software Foundation, Inc. and Ken Arnold This file is not considered part of GNU Emacs. @@ -14,8 +15,8 @@ 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, write to the Free Software -Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +along with this program; if not, write to the Free Software Foundation, +Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* * Authors: @@ -24,26 +25,48 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ * Ed Pelegri-Llopart added C typedefs. * Gnu Emacs TAGS format and modifications by RMS? * Sam Kendall added C++. + * Francesco Potorti` reorganised C and C++ based on work by Joe Wells. + * Regexp tags by Tom Tromey. * - * Francesco Potorti` (pot@cnuce.cnr.it) is the current maintainer. + * Francesco Potorti` (F.Potorti@cnuce.cnr.it) is the current maintainer. */ -char pot_etags_version[] = "@(#) pot revision number is 10.31"; +char pot_etags_version[] = "@(#) pot revision number is 11.66"; + +#define TRUE 1 +#define FALSE 0 + +#ifndef DEBUG +# define DEBUG FALSE +#endif #ifdef MSDOS -#include +# include +# include +# include #endif /* MSDOS */ +#ifdef WINDOWSNT +# include +# include +# include +# include +# define MAXPATHLEN _MAX_PATH +#endif + #ifdef HAVE_CONFIG_H -#include <../src/config.h> -/* On some systems, Emacs defines static as nothing - for the sake of unexec. We don't want that here - since we don't use unexec. */ -#undef static +# include + /* On some systems, Emacs defines static as nothing for the sake + of unexec. We don't want that here since we don't use unexec. */ +# undef static #endif #include #include +#include +#ifndef errno +extern int errno; +#endif #include #include @@ -51,10 +74,11 @@ char pot_etags_version[] = "@(#) pot revision number is 10.31"; # define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif -#include "getopt.h" - -extern char *getenv (); +#include +#ifdef ETAGS_REGEXPS +# include +#endif /* ETAGS_REGEXPS */ /* Define CTAGS to make the program "ctags" compatible with the usual one. Let it undefined to make the program "etags", which makes emacs-style @@ -68,246 +92,365 @@ extern char *getenv (); /* Exit codes for success and failure. */ #ifdef VMS -#define GOOD 1 -#define BAD 0 +# define GOOD 1 +# define BAD 0 #else -#define GOOD 0 -#define BAD 1 +# define GOOD 0 +# define BAD 1 #endif -/* - * The FILEPOS abstract type, which represents a position in a file, - * plus the following accessor functions: - * - * long GET_CHARNO (pos) - * returns absolute char number. - * void SET_FILEPOS (pos, fp, charno) - * FILE *fp; long charno; - * sets `pos' from the current file - * position of `fp' and from `charno', - * which must be the absolute character - * number corresponding to the current - * position of `fp'. - * - * The `pos' parameter is an lvalue expression of type FILEPOS. - * Parameters to the accessor functions are evaluated 0 or more times, - * and so must have no side effects. - * - * FILEPOS objects can also be assigned and passed to and from - * functions in the normal C manner. - * - * Implementation notes: the `+ 0' is to enforce rvalue-ness. - */ - -#ifndef DEBUG - /* real implementation */ -typedef long FILEPOS; -#define GET_CHARNO(pos) ((pos) + 0) -#define SET_FILEPOS(pos, fp, cno) ((void) ((pos) = (cno))) -#else - /* debugging implementation */ -typedef struct -{ - long charno; -} FILEPOS; - -#define GET_CHARNO(pos) ((pos).charno + 0) -#define SET_FILEPOS(pos, fp, cno) \ - ((void) ((pos).charno = (cno), \ - (cno) != ftell (fp) ? (error ("SET_FILEPOS inconsistency"), 0) \ - : 0)) -#endif +/* C extensions. */ +#define C_PLPL 0x00001 /* C++ */ +#define C_STAR 0x00003 /* C* */ +#define YACC 0x10000 /* yacc file */ -#define streq(s, t) (strcmp (s, t) == 0) -#define strneq(s, t, n) (strncmp (s, t, n) == 0) -#define logical int +#define streq(s,t) ((DEBUG &&!(s)&&!(t)&&(abort(),1)) || !strcmp(s,t)) +#define strneq(s,t,n) ((DEBUG &&!(s)&&!(t)&&(abort(),1)) || !strncmp(s,t,n)) -#define TRUE 1 -#define FALSE 0 +#define lowcase(c) tolower ((unsigned char)c) #define iswhite(arg) (_wht[arg]) /* T if char is white */ #define begtoken(arg) (_btk[arg]) /* T if char can start token */ #define intoken(arg) (_itk[arg]) /* T if char can be in token */ #define endtoken(arg) (_etk[arg]) /* T if char ends tokens */ -#define max(I1,I2) ((I1) > (I2) ? (I1) : (I2)) +#ifdef DOS_NT +# define absolutefn(fn) (fn[0] == '/' \ + || (fn[1] == ':' && fn[2] == '/')) +#else +# define absolutefn(fn) (fn[0] == '/') +#endif + -struct nd_st -{ /* sorting structure */ +/* + * xnew -- allocate storage + * + * SYNOPSIS: Type *xnew (int n, Type); + */ +#define xnew(n,Type) ((Type *) xmalloc ((n) * sizeof (Type))) + +typedef int logical; + +typedef struct nd_st +{ /* sorting structure */ char *name; /* function or type name */ char *file; /* file name */ logical is_func; /* use pattern or line no */ - logical named; /* list name separately */ logical been_warned; /* set if noticed dup */ int lno; /* line number tag is on */ long cno; /* character number line starts on */ char *pat; /* search pattern */ struct nd_st *left, *right; /* left and right sons */ -}; - -typedef struct nd_st NODE; +} NODE; -logical header_file; /* TRUE if .h file, FALSE o.w. */ -/* boolean "functions" (see init) */ -logical _wht[0177], _etk[0177], _itk[0177], _btk[0177]; - -char *cwd; /* current working directory */ -char *tagfiledir; /* directory of tagfile */ +extern char *getenv (); char *concat (); char *savenstr (), *savestr (); char *etags_strchr (), *etags_strrchr (); char *etags_getcwd (); char *relative_filename (), *absolute_filename (), *absolute_dirname (); -char *xmalloc (), *xrealloc (); -int total_size_of_entries (); -long readline (); - +long *xmalloc (), *xrealloc (); + +typedef void Lang_function (); +#if FALSE /* many compilers barf on this */ +Lang_function Asm_labels; +Lang_function default_C_entries; +Lang_function C_entries; +Lang_function Cplusplus_entries; +Lang_function Cstar_entries; +Lang_function Erlang_functions; +Lang_function Fortran_functions; +Lang_function Yacc_entries; +Lang_function Lisp_functions; +Lang_function Pascal_functions; +Lang_function Perl_functions; +Lang_function Prolog_functions; +Lang_function Scheme_functions; +Lang_function TeX_functions; +Lang_function just_read_file; +#else /* so let's write it this way */ void Asm_labels (); void C_entries (); -int Fortran_functions (); +void default_C_entries (); +void plain_C_entries (); +void Cplusplus_entries (); +void Cstar_entries (); +void Erlang_functions (); +void Fortran_functions (); +void Yacc_entries (); void Lisp_functions (); void Pascal_functions (); +void Perl_functions (); void Prolog_functions (); void Scheme_functions (); void TeX_functions (); +void just_read_file (); +#endif + +Lang_function *get_language_from_name (); +Lang_function *get_language_from_interpreter (); +Lang_function *get_language_from_suffix (); +int total_size_of_entries (); +long readline (); +long readline_internal (); +#ifdef ETAGS_REGEXPS +void add_regex (); +#endif void add_node (); void error (); -void fatal (); -logical find_entries (); +void suggest_asking_for_help (); +void fatal (), pfatal (); +void find_entries (); void free_tree (); void getit (); void init (); void initbuffer (); -void initbuffer (); void pfnote (); void process_file (); void put_entries (); void takeprec (); -/* - * MACRO - * xnew -- allocate storage - * - * SYNOPSIS - * Type *xnew (int n, Type); - */ -#define xnew(n, Type) ((Type *) xmalloc ((n) * sizeof (Type))) - -/* - * Symbol table types. - */ -enum sym_type -{ - st_none, st_C_struct, st_C_enum, st_C_define, st_C_typedef, st_C_typespec -}; +char searchar = '/'; /* use /.../ searches */ +int lineno; /* line number of current line */ +long charno; /* current character number */ +long linecharno; /* charno of start of line */ -typedef int LINENO; +char *curfile; /* current input file name */ +char *tagfile; /* output file */ +char *progname; /* name this program was invoked with */ +char *cwd; /* current working directory */ +char *tagfiledir; /* directory of tagfile */ -typedef struct -{ - char *p; - int len; - LINENO lineno; - logical named; -} TOKEN; +FILE *tagf; /* ioptr for tags file */ +NODE *head; /* the head of the binary tree of tags */ -/* C extensions. +/* + * A `struct linebuffer' is a structure which holds a line of text. + * `readline' reads a line from a stream into a linebuffer and works + * regardless of the length of the line. */ -#define C_PLPL 0x00001 /* C++ */ -#define C_STAR 0x00003 /* C* */ -#define YACC 0x10000 /* yacc file */ - -char searchar = '/'; /* use /.../ searches */ +#define GROW_LINEBUFFER(buf,toksize) \ +while (buf.size < toksize) \ + buf.buffer = (char *) xrealloc (buf.buffer, buf.size *= 2) +struct linebuffer +{ + long size; + char *buffer; +}; -LINENO lineno; /* line number of current line */ -long charno; /* current character number */ +struct linebuffer lb; /* the current line */ +struct linebuffer token_name; /* used by C_entries as a temporary area */ +struct +{ + long linepos; + struct linebuffer lb; /* used by C_entries instead of lb */ +} lbs[2]; -long linecharno; /* charno of start of line; not used by C, but - * by every other language. - */ - -char *curfile, /* current input file name */ - *tagfile, /* output file */ - *white = " \f\t\n", /* white chars */ - *endtk = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?", /* token ending chars */ - /* token starting chars */ - *begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$~", - /* valid in-token chars */ - *intk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789"; - -int append_to_tagfile; /* -a: append to tags */ -/* The following three default to 1 for etags, but to 0 for ctags. */ -int typedefs; /* -t: create tags for typedefs */ -int typedefs_and_cplusplus; /* -T: create tags for typedefs, level */ +/* boolean "functions" (see init) */ +logical _wht[0177], _etk[0177], _itk[0177], _btk[0177]; +char + /* white chars */ + *white = " \f\t\n\013", + /* token ending chars */ + *endtk = " \t\n\013\"'#()[]{}=-+%*/&|^~!<>;,.:?", + /* token starting chars */ + *begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$~@", + /* valid in-token chars */ + *intk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789"; + +logical append_to_tagfile; /* -a: append to tags */ +/* The following three default to TRUE for etags, but to FALSE for ctags. */ +logical typedefs; /* -t: create tags for typedefs */ +logical typedefs_and_cplusplus; /* -T: create tags for typedefs, level */ /* 0 struct/enum/union decls, and C++ */ /* member functions. */ -int constantypedefs; /* -d: create tags for C #define and enum */ +logical constantypedefs; /* -d: create tags for C #define and enum */ /* constants. Enum consts not implemented. */ /* -D: opposite of -d. Default under ctags. */ -int update; /* -u: update tags */ -int vgrind_style; /* -v: create vgrind style index output */ -int no_warnings; /* -w: suppress warnings */ -int cxref_style; /* -x: create cxref style output */ -int cplusplus; /* .[hc] means C++, not C */ -int noindentypedefs; /* -S: ignore indentation in C */ - -/* Name this program was invoked with. */ -char *progname; - -struct option longopts[] = { +logical update; /* -u: update tags */ +logical vgrind_style; /* -v: create vgrind style index output */ +logical no_warnings; /* -w: suppress warnings */ +logical cxref_style; /* -x: create cxref style output */ +logical cplusplus; /* .[hc] means C++, not C */ +logical noindentypedefs; /* -I: ignore indentation in C */ + +struct option longopts[] = +{ { "append", no_argument, NULL, 'a' }, - { "backward-search", no_argument, NULL, 'B' }, + { "backward-search", no_argument, NULL, 'B' }, { "c++", no_argument, NULL, 'C' }, { "cxref", no_argument, NULL, 'x' }, { "defines", no_argument, NULL, 'd' }, - { "forward-search", no_argument, NULL, 'F' }, + { "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'H' }, - { "ignore-indentation", no_argument, NULL, 'S' }, + { "ignore-indentation", no_argument, NULL, 'I' }, { "include", required_argument, NULL, 'i' }, + { "language", required_argument, NULL, 'l' }, { "no-defines", no_argument, NULL, 'D' }, + { "no-regex", no_argument, NULL, 'R' }, { "no-warn", no_argument, NULL, 'w' }, { "output", required_argument, NULL, 'o' }, + { "regex", required_argument, NULL, 'r' }, { "typedefs", no_argument, NULL, 't' }, { "typedefs-and-c++", no_argument, NULL, 'T' }, - { "update", no_argument, NULL, 'u' }, + { "update", no_argument, NULL, 'u' }, { "version", no_argument, NULL, 'V' }, - { "vgrind", no_argument, NULL, 'v' }, + { "vgrind", no_argument, NULL, 'v' }, { 0 } }; -FILE *tagf; /* ioptr for tags file */ -NODE *head; /* the head of the binary tree of tags */ -logical permit_duplicates = TRUE; /* allow duplicate tags */ +#ifdef ETAGS_REGEXPS +/* Structure defining a regular expression. Elements are + the compiled pattern, and the name string. */ +struct pattern +{ + struct re_pattern_buffer *pattern; + struct re_registers regs; + char *name_pattern; + logical error_signaled; +}; -/* A `struct linebuffer' is a structure which holds a line of text. - `readline' reads a line from a stream into a linebuffer - and works regardless of the length of the line. */ +/* Number of regexps found. */ +int num_patterns = 0; -struct linebuffer +/* Array of all regexps. */ +struct pattern *patterns = NULL; +#endif /* ETAGS_REGEXPS */ + +/* + * Language stuff. + */ + +/* Non-NULL if language fixed. */ +Lang_function *lang_func = NULL; + +/* Assembly code */ +char *Asm_suffixes [] = { "a", /* Unix assembler */ + "asm", /* Microcontroller assembly */ + "def", /* BSO/Tasking definition includes */ + "inc", /* Microcontroller include files */ + "ins", /* Microcontroller include files */ + "s", "sa", /* Unix assembler */ + "src", /* BSO/Tasking C compiler output */ + NULL + }; + +/* Note that .c and .h can be considered C++, if the --c++ flag was + given. That is why default_C_entries is called here. */ +char *default_C_suffixes [] = + { "c", "h", NULL }; + +/* .M is for Objective C++ files. */ +char *Cplusplus_suffixes [] = + { "C", "H", "c++", "cc", "cpp", "cxx", "h++", "hh", "hpp", "hxx", "M", NULL}; + +char *Cstar_suffixes [] = + { "cs", "hs", NULL }; + +char *Erlang_suffixes [] = + { "erl", "hrl", NULL }; + +char *Fortran_suffixes [] = + { "F", "f", "f90", "for", NULL }; + +char *Lisp_suffixes [] = + { "cl", "clisp", "el", "l", "lisp", "lsp", "ml", NULL }; + +char *Pascal_suffixes [] = + { "p", "pas", NULL }; + +char *Perl_suffixes [] = + { "pl", "pm", NULL }; +char *Perl_interpreters [] = + { "perl", "@PERL@", NULL }; + +char *plain_C_suffixes [] = + { "pc", /* Pro*C file */ + "m", /* Objective C file */ + "lm", /* Objective lex file */ + NULL }; + +char *Prolog_suffixes [] = + { "prolog", NULL }; + +/* Can't do the `SCM' or `scm' prefix with a version number. */ +char *Scheme_suffixes [] = + { "SCM", "SM", "oak", "sch", "scheme", "scm", "sm", "t", NULL }; + +char *TeX_suffixes [] = + { "TeX", "bib", "clo", "cls", "ltx", "sty", "tex", NULL }; + +char *Yacc_suffixes [] = + { "y", "ym", NULL }; /* .ym is Objective yacc file */ + +/* Table of language names and corresponding functions, file suffixes + and interpreter names. + It is ok for a given function to be listed under more than one + name. I just didn't. */ +struct lang_entry { - long size; - char *buffer; + char *name; + Lang_function *function; + char **suffixes; + char **interpreters; }; -struct linebuffer lb; /* the current line */ -struct linebuffer filename_lb; /* used to read in filenames */ -struct +struct lang_entry lang_names [] = { - FILEPOS linepos; - struct linebuffer lb; /* used by C_entries instead of lb */ -} lbs[2]; + { "asm", Asm_labels, Asm_suffixes, NULL }, + { "c", default_C_entries, default_C_suffixes, NULL }, + { "c++", Cplusplus_entries, Cplusplus_suffixes, NULL }, + { "c*", Cstar_entries, Cstar_suffixes, NULL }, + { "erlang", Erlang_functions, Erlang_suffixes, NULL }, + { "fortran", Fortran_functions, Fortran_suffixes, NULL }, + { "lisp", Lisp_functions, Lisp_suffixes, NULL }, + { "pascal", Pascal_functions, Pascal_suffixes, NULL }, + { "perl", Perl_functions, Perl_suffixes, Perl_interpreters }, + { "proc", plain_C_entries, plain_C_suffixes, NULL }, + { "prolog", Prolog_functions, Prolog_suffixes, NULL }, + { "scheme", Scheme_functions, Scheme_suffixes, NULL }, + { "tex", TeX_functions, TeX_suffixes, NULL }, + { "yacc", Yacc_entries, Yacc_suffixes, NULL }, + { "auto", NULL }, /* default guessing scheme */ + { "none", just_read_file }, /* regexp matching only */ + { NULL, NULL } /* end of list */ +}; + void +print_language_names () +{ + struct lang_entry *lang; + char **ext; + + puts ("\nThese are the currently supported languages, along with the\n\ +default file name suffixes:"); + for (lang = lang_names; lang->name != NULL; lang++) + { + printf ("\t%s\t", lang->name); + if (lang->suffixes != NULL) + for (ext = lang->suffixes; *ext != NULL; ext++) + printf (" .%s", *ext); + puts (""); + } + puts ("Where `auto' means use default language for files based on file\n\ +name suffix, and `none' means only do regexp processing on files.\n\ +If no language is specified and no matching suffix is found,\n\ +the first line of the file is read for a sharp-bang (#!) sequence\n\ +followed by the name of an interpreter. If no such sequence is found,\n\ +Fortran is tried first; if no tags are found, C is tried next."); +} + +#ifndef VERSION +# define VERSION "19" +#endif +void print_version () { -#ifdef VERSION - printf ("%s for Emacs version %g.\n", (CTAGS) ? "CTAGS" : "ETAGS", VERSION); -#else - printf ("%s for Emacs version 19.\n", (CTAGS) ? "CTAGS" : "ETAGS"); -#endif + printf ("%s for Emacs version %s\n", (CTAGS) ? "ctags" : "etags", VERSION); exit (GOOD); } @@ -316,8 +459,12 @@ void print_help () { printf ("These are the options accepted by %s. You may use unambiguous\n\ -abbreviations for the long option names. A - as file name means read file\n\ -names from stdin.\n\n", progname); +abbreviations for the long option names. A - as file name means read\n\ +names from stdin.", progname); + if (!CTAGS) + printf (" Absolute names are stored in the output file as they\n\ +are. Relative ones are stored relative to the output file's directory."); + puts ("\n"); puts ("-a, --append\n\ Append tag entries to existing tags file."); @@ -325,35 +472,43 @@ names from stdin.\n\n", progname); if (CTAGS) puts ("-B, --backward-search\n\ Write the search commands for the tag entries using '?', the\n\ - backward-search command."); + backward-search command instead of '/', the forward-search command."); puts ("-C, --c++\n\ - Treat files with `.c' and `.h' extensions as C++ code, not C\n\ - code. Files with `.C', `.H', `.cxx', `.hxx', or `.cc'\n\ - extensions are always assumed to be C++ code."); + Treat files whose name suffix defaults to C language as C++ files."); if (CTAGS) puts ("-d, --defines\n\ - Create tag entries for C #defines, too."); + Create tag entries for constant C #defines, too."); else puts ("-D, --no-defines\n\ - Don't create tag entries for C #defines. This makes the tags\n\ - file smaller."); - - if (CTAGS) - puts ("-F, --forward-search\n\ - Write the search commands for the tag entries using '/', the\n\ - forward-search command."); + Don't create tag entries for constant C #defines. This makes\n\ + the tags file smaller."); if (!CTAGS) - puts ("-i FILE, --include=FILE\n\ + { + puts ("-i FILE, --include=FILE\n\ Include a note in tag file indicating that, when searching for\n\ a tag, one should also consult the tags file FILE after\n\ checking the current file."); + puts ("-l LANG, --language=LANG\n\ + Force the following files to be considered as written in the\n\ + named language up to the next --language=LANG option."); + } +#ifdef ETAGS_REGEXPS + puts ("-r /REGEXP/, --regex=/REGEXP/\n\ + Make a tag for each line matching pattern REGEXP in the\n\ + following files. REGEXP is anchored (as if preceded by ^).\n\ + The form /REGEXP/NAME/ creates a named tag. For example Tcl\n\ + named tags can be created with:\n\ + --regex=/proc[ \\t]+\\([^ \\t]+\\)/\\1/."); + puts ("-R, --no-regex\n\ + Don't create tags from regexps for the following files."); +#endif /* ETAGS_REGEXPS */ puts ("-o FILE, --output=FILE\n\ Write the tags to FILE."); - puts ("-S, --ignore-indentation\n\ + puts ("-I, --ignore-indentation\n\ Don't rely on indentation quite as much as normal. Currently,\n\ this means not to assume that a closing brace in the first\n\ column is the final brace of a function or structure\n\ @@ -377,59 +532,209 @@ names from stdin.\n\n", progname); Generates an index of items intended for human consumption,\n\ similar to the output of vgrind. The index is sorted, and\n\ gives the page number of each item."); + puts ("-w, --no-warn\n\ + Suppress warning messages about entries defined in multiple\n\ + files."); puts ("-x, --cxref\n\ Like --vgrind, but in the style of cxref, rather than vgrind.\n\ The output uses line numbers instead of page numbers, but\n\ beyond that the differences are cosmetic; try both to see\n\ which you like."); - puts ("-w, --no-warn\n\ - Suppress warning messages about entries defined in multiple\n\ - files."); } puts ("-V, --version\n\ Print the version of the program.\n\ --H, --help\n\ +-h, --help\n\ Print this help message."); + print_language_names (); + exit (GOOD); } -void +enum argument_type +{ + at_language, + at_regexp, + at_filename +}; + +/* This structure helps us allow mixing of --lang and filenames. */ +typedef struct +{ + enum argument_type arg_type; + char *what; + Lang_function *function; +} argument; + +#ifdef VMS /* VMS specific functions */ + +#define EOS '\0' + +/* This is a BUG! ANY arbitrary limit is a BUG! + Won't someone please fix this? */ +#define MAX_FILE_SPEC_LEN 255 +typedef struct { + short curlen; + char body[MAX_FILE_SPEC_LEN + 1]; +} vspec; + +/* + v1.05 nmm 26-Jun-86 fn_exp - expand specification of list of file names + returning in each successive call the next filename matching the input + spec. The function expects that each in_spec passed + to it will be processed to completion; in particular, up to and + including the call following that in which the last matching name + is returned, the function ignores the value of in_spec, and will + only start processing a new spec with the following call. + If an error occurs, on return out_spec contains the value + of in_spec when the error occurred. + + With each successive filename returned in out_spec, the + function's return value is one. When there are no more matching + names the function returns zero. If on the first call no file + matches in_spec, or there is any other error, -1 is returned. +*/ + +#include +#include +#define OUTSIZE MAX_FILE_SPEC_LEN +short +fn_exp (out, in) + vspec *out; + char *in; +{ + static long context = 0; + static struct dsc$descriptor_s o; + static struct dsc$descriptor_s i; + static logical pass1 = TRUE; + long status; + short retval; + + if (pass1) + { + pass1 = FALSE; + o.dsc$a_pointer = (char *) out; + o.dsc$w_length = (short)OUTSIZE; + i.dsc$a_pointer = in; + i.dsc$w_length = (short)strlen(in); + i.dsc$b_dtype = DSC$K_DTYPE_T; + i.dsc$b_class = DSC$K_CLASS_S; + o.dsc$b_dtype = DSC$K_DTYPE_VT; + o.dsc$b_class = DSC$K_CLASS_VS; + } + if ((status = lib$find_file(&i, &o, &context, 0, 0)) == RMS$_NORMAL) + { + out->body[out->curlen] = EOS; + return 1; + } + else if (status == RMS$_NMF) + retval = 0; + else + { + strcpy(out->body, in); + retval = -1; + } + lib$find_file_end(&context); + pass1 = TRUE; + return retval; +} + +/* + v1.01 nmm 19-Aug-85 gfnames - return in successive calls the + name of each file specified by the provided arg expanding wildcards. +*/ +char * +gfnames (arg, p_error) + char *arg; + logical *p_error; +{ + static vspec filename = {MAX_FILE_SPEC_LEN, "\0"}; + + switch (fn_exp (&filename, arg)) + { + case 1: + *p_error = FALSE; + return filename.body; + case 0: + *p_error = FALSE; + return NULL; + default: + *p_error = TRUE; + return filename.body; + } +} + +#ifndef OLD /* Newer versions of VMS do provide `system'. */ +system (cmd) + char *cmd; +{ + fprintf (stderr, "system() function not implemented under VMS\n"); +} +#endif + +#define VERSION_DELIM ';' +char *massage_name (s) + char *s; +{ + char *start = s; + + for ( ; *s; s++) + if (*s == VERSION_DELIM) + { + *s = EOS; + break; + } + else + *s = lowcase (*s); + return start; +} +#endif /* VMS */ + + +int main (argc, argv) int argc; char *argv[]; { - char cmd[100]; int i; unsigned int nincluded_files = 0; char **included_files = xnew (argc, char *); char *this_file; + argument *argbuffer; + int current_arg = 0, file_count = 0; + struct linebuffer filename_lb; #ifdef VMS - char got_err; - - extern char *gfnames (); - extern char *massage_name (); + logical got_err; #endif - -#ifdef MSDOS + +#ifdef DOS_NT _fmode = O_BINARY; /* all of files are treated as binary files */ -#endif /* MSDOS */ +#endif /* DOS_NT */ progname = argv[0]; + /* Allocate enough no matter what happens. Overkill, but each one + is small. */ + argbuffer = xnew (argc, argument); + +#ifdef ETAGS_REGEXPS + /* Set syntax for regular expression routines. */ + re_set_syntax (RE_SYNTAX_EMACS); +#endif /* ETAGS_REGEXPS */ + /* * If etags, always find typedefs and structure tags. Why not? * Also default is to find macro constants. */ if (!CTAGS) - typedefs = typedefs_and_cplusplus = constantypedefs = 1; + typedefs = typedefs_and_cplusplus = constantypedefs = TRUE; - for (;;) + while (1) { - int opt; - opt = getopt_long (argc, argv, "aCdDf:o:StTi:BFuvxwVH", longopts, 0); + int opt = getopt_long (argc, argv, + "-aCdDf:Il:o:r:RStTi:BuvxwVhH", longopts, 0); if (opt == EOF) break; @@ -441,163 +746,192 @@ main (argc, argv) long-named option. We should do nothing. */ break; + case 1: + /* This means that a filename has been seen. Record it. */ + argbuffer[current_arg].arg_type = at_filename; + argbuffer[current_arg].what = optarg; + ++current_arg; + ++file_count; + break; + /* Common options. */ case 'a': - append_to_tagfile++; + append_to_tagfile = TRUE; break; case 'C': - cplusplus = 1; + cplusplus = TRUE; break; case 'd': - constantypedefs = 1; + constantypedefs = TRUE; break; case 'D': - constantypedefs = 0; + constantypedefs = FALSE; break; case 'f': /* for compatibility with old makefiles */ case 'o': if (tagfile) { - fprintf (stderr, - "%s: -%c flag may only be given once\n", progname, opt); - goto usage; + fprintf (stderr, "%s: -%c option may only be given once.\n", + progname, opt); + suggest_asking_for_help (); } tagfile = optarg; break; - case 'S': - noindentypedefs++; + case 'I': + case 'S': /* for backward compatibility */ + noindentypedefs = TRUE; + break; + case 'l': + argbuffer[current_arg].function = get_language_from_name (optarg); + argbuffer[current_arg].arg_type = at_language; + ++current_arg; + break; +#ifdef ETAGS_REGEXPS + case 'r': + argbuffer[current_arg].arg_type = at_regexp; + argbuffer[current_arg].what = optarg; + ++current_arg; + break; + case 'R': + argbuffer[current_arg].arg_type = at_regexp; + argbuffer[current_arg].what = NULL; + ++current_arg; break; +#endif /* ETAGS_REGEXPS */ case 'V': print_version (); break; + case 'h': case 'H': print_help (); break; - + case 't': + typedefs = TRUE; + break; + case 'T': + typedefs = typedefs_and_cplusplus = TRUE; + break; #if (!CTAGS) - /* Etags options */ case 'i': included_files[nincluded_files++] = optarg; break; - #else /* CTAGS */ - /* Ctags options. */ case 'B': searchar = '?'; break; - case 'F': - searchar = '/'; - break; - case 't': - typedefs++; - break; - case 'T': - typedefs++; - typedefs_and_cplusplus++; - break; case 'u': - update++; + update = TRUE; break; case 'v': - vgrind_style++; + vgrind_style = TRUE; /*FALLTHRU*/ case 'x': - cxref_style++; + cxref_style = TRUE; break; case 'w': - no_warnings++; + no_warnings = TRUE; break; - #endif /* CTAGS */ - default: - goto usage; + suggest_asking_for_help (); } } - if (optind == argc && nincluded_files == 0) + for (; optind < argc; ++optind) { - fprintf (stderr, "%s: No input files specified.\n", progname); - - usage: - fprintf (stderr, "%s: Try `%s --help' for a complete list of options.\n", - progname, progname); - exit (BAD); + argbuffer[current_arg].arg_type = at_filename; + argbuffer[current_arg].what = argv[optind]; + ++current_arg; + ++file_count; } - if (tagfile == NULL) + if (nincluded_files == 0 && file_count == 0) { - tagfile = CTAGS ? "tags" : "TAGS"; + fprintf (stderr, "%s: No input files specified.\n", progname); + suggest_asking_for_help (); } + + if (tagfile == NULL) + tagfile = CTAGS ? "tags" : "TAGS"; cwd = etags_getcwd (); /* the current working directory */ - strcat (cwd, "/"); + if (cwd[strlen (cwd) - 1] != '/') + cwd = concat (cwd, "/", ""); if (streq (tagfile, "-")) - { - tagfiledir = cwd; - } + tagfiledir = cwd; else - { - tagfiledir = absolute_dirname (tagfile, cwd); - } + tagfiledir = absolute_dirname (tagfile, cwd); - init (); /* set up boolean "functions" */ + init (); /* set up boolean "functions" */ initbuffer (&lb); + initbuffer (&token_name); initbuffer (&lbs[0].lb); initbuffer (&lbs[1].lb); initbuffer (&filename_lb); - /* - * loop through files finding functions - */ + if (!CTAGS) { if (streq (tagfile, "-")) - tagf = stdout; + { + tagf = stdout; +#ifdef DOS_NT + /* Switch redirected `stdout' to binary mode (setting `_fmode' + doesn't take effect until after `stdout' is already open). */ + if (!isatty (fileno (stdout))) + setmode (fileno (stdout), O_BINARY); +#endif /* DOS_NT */ + } else tagf = fopen (tagfile, append_to_tagfile ? "a" : "w"); if (tagf == NULL) - { - perror (tagfile); - exit (BAD); - } + pfatal (tagfile); } -#ifdef VMS - argc -= optind; - argv += optind; - while (gfnames (&argc, &argv, &got_err) != NULL) + /* + * Loop through files finding functions. + */ + for (i = 0; i < current_arg; ++i) { - if (got_err) - { - error ("Can't find file %s\n", this_file); - argc--, argv++; - } - else + switch (argbuffer[i].arg_type) { - this_file = massage_name (this_file); -#if 0 - } - } /* solely to balance out the ifdef'd parens above */ + case at_language: + lang_func = argbuffer[i].function; + break; +#ifdef ETAGS_REGEXPS + case at_regexp: + add_regex (argbuffer[i].what); + break; #endif + case at_filename: +#ifdef VMS + while ((this_file = gfnames (argbuffer[i].what, &got_err)) != NULL) + { + if (got_err) + { + error ("Can't find file %s\n", this_file); + argc--, argv++; + } + else + { + this_file = massage_name (this_file); + } #else - for (; optind < argc; optind++) - { - this_file = argv[optind]; + this_file = argbuffer[i].what; #endif - /* Input file named "-" means read file names from stdin and use them. */ - if (streq (this_file, "-")) - { - while (!feof (stdin)) - { - (void) readline (&filename_lb, stdin); - if (strlen (filename_lb.buffer) > 0) - process_file (filename_lb.buffer); + /* Input file named "-" means read file names from stdin + and use them. */ + if (streq (this_file, "-")) + while (readline_internal (&filename_lb, stdin) > 0) + process_file (filename_lb.buffer); + else + process_file (this_file); +#ifdef VMS } +#endif + break; } - else - process_file (this_file); } if (!CTAGS) @@ -605,45 +939,126 @@ main (argc, argv) while (nincluded_files-- > 0) fprintf (tagf, "\f\n%s,include\n", *included_files++); - (void) fclose (tagf); + fclose (tagf); exit (GOOD); } + /* If CTAGS, we are here. process_file did not write the tags yet, + because we want them ordered. Let's do it now. */ if (cxref_style) { + tagf = fopen (tagfile, append_to_tagfile ? "a" : "w"); + if (tagf == NULL) + pfatal (tagfile); put_entries (head); exit (GOOD); } + if (update) { - /* update cannot be set under VMS, so we may assume that argc - and argv have not been munged. */ - for (i = optind; i < argc; i++) + char cmd[BUFSIZ]; + for (i = 0; i < current_arg; ++i) { + if (argbuffer[i].arg_type != at_filename) + continue; sprintf (cmd, "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS", - tagfile, argv[i], tagfile); - (void) system (cmd); + tagfile, argbuffer[i].what, tagfile); + if (system (cmd) != GOOD) + fatal ("failed to execute shell command"); } - append_to_tagfile++; + append_to_tagfile = TRUE; } + tagf = fopen (tagfile, append_to_tagfile ? "a" : "w"); if (tagf == NULL) - { - perror (tagfile); - exit (GOOD); - } + pfatal (tagfile); put_entries (head); - (void) fclose (tagf); + fclose (tagf); + if (update) { + char cmd[BUFSIZ]; sprintf (cmd, "sort %s -o %s", tagfile, tagfile); - (void) system (cmd); + exit (system (cmd)); } exit (GOOD); } +/* + * Return a Lang_function given the name. + */ +Lang_function * +get_language_from_name (name) + char *name; +{ + struct lang_entry *lang; + + if (name != NULL) + for (lang = lang_names; lang->name != NULL; lang++) + { + if (streq (name, lang->name)) + return lang->function; + } + + fprintf (stderr, "%s: language \"%s\" not recognized.\n", + progname, optarg); + suggest_asking_for_help (); + + /* This point should never be reached. The function should either + return a function pointer or never return. Note that a NULL + pointer cannot be considered as an error, as it means that the + language has not been explicitely imposed by the user ("auto"). */ + return NULL; /* avoid warnings from compiler */ +} + + +/* + * Return a Lang_function given the interpreter name. + */ +Lang_function * +get_language_from_interpreter (interpreter) + char *interpreter; +{ + struct lang_entry *lang; + char **iname; + + if (interpreter == NULL) + return NULL; + for (lang = lang_names; lang->name != NULL; lang++) + if (lang->interpreters != NULL) + for (iname = lang->interpreters; *iname != NULL; iname++) + if (streq (*iname, interpreter)) + return lang->function; + + return NULL; +} + + + +/* + * Return a Lang_function given the file suffix. + */ +Lang_function * +get_language_from_suffix (suffix) + char *suffix; +{ + struct lang_entry *lang; + char **ext; + + if (suffix == NULL) + return NULL; + for (lang = lang_names; lang->name != NULL; lang++) + if (lang->suffixes != NULL) + for (ext = lang->suffixes; *ext != NULL; ext++) + if (streq (*ext, suffix)) + return lang->function; + + return NULL; +} + + /* * This routine is called on each file argument. */ @@ -652,6 +1067,14 @@ process_file (file) char *file; { struct stat stat_buf; + FILE *inf; +#ifdef DOS_NT + char *p; + + for (p = file; *p != '\0'; p++) + if (*p == '\\') + *p = '/'; +#endif if (stat (file, &stat_buf) == 0 && !S_ISREG (stat_buf.st_mode)) { @@ -663,15 +1086,20 @@ process_file (file) fprintf (stderr, "Skipping inclusion of %s in self.\n", file); return; } - if (!find_entries (file)) + inf = fopen (file, "r"); + if (inf == NULL) { + perror (file); return; } + + find_entries (file, inf); + if (!CTAGS) { char *filename; - if (file[0] == '/') + if (absolutefn (file)) { /* file is an absolute filename. Canonicalise it. */ filename = absolute_filename (file, cwd); @@ -683,6 +1111,7 @@ process_file (file) filename = relative_filename (file, tagfiledir); } fprintf (tagf, "\f\n%s,%d\n", filename, total_size_of_entries (head)); + free (filename); put_entries (head); free_tree (head); head = NULL; @@ -723,214 +1152,139 @@ init () * This routine opens the specified file and calls the function * which finds the function and type definitions. */ -logical -find_entries (file) +void +find_entries (file, inf) char *file; + FILE *inf; { - char *cp, *cp1; - FILE *inf; + char *cp; + Lang_function *function; + NODE *old_last_node; + extern NODE *last_node; - inf = fopen (file, "r"); - if (inf == NULL) + + /* Memory leakage here: the memory block pointed by curfile is never + released. The amount of memory leaked here is the sum of the + lengths of the input file names. */ + curfile = savestr (file); + + /* If user specified a language, use it. */ + function = lang_func; + if (function != NULL) { - perror (file); - return FALSE; + function (inf); + fclose (inf); + return; } - curfile = savestr (file); + cp = etags_strrchr (file, '.'); - cp1 = cp + 1; - - header_file = (cp && (streq (cp1, "h"))); - - /* .tex, .aux or .bbl implies LaTeX source code */ - if (cp && (streq (cp1, "tex") || streq (cp1, "aux") - || streq (cp1, "bbl"))) - { - TeX_functions (inf); - goto close_and_return; - } - /* .l or .el or .lisp (or .cl or .clisp or ...) implies lisp source code */ - if (cp && (streq (cp1, "l") - || streq (cp1, "el") - || streq (cp1, "lsp") - || streq (cp1, "lisp") - || streq (cp1, "cl") - || streq (cp1, "clisp"))) - { - Lisp_functions (inf); - goto close_and_return; - } - /* .scm or .sm or .scheme or ... implies scheme source code */ - if (cp && (streq (cp1, "sm") - || streq (cp1, "scm") - || streq (cp1, "scheme") - || streq (cp1, "t") - || streq (cp1, "sch") - || streq (cp1, "ss") - || streq (cp1, "SM") - || streq (cp1, "SCM") - /* The `SCM' or `scm' prefix with a version number */ - || (cp[-1] == 'm' && cp[-2] == 'c' && cp[-3] == 's' - && string_numeric_p (cp1)) - || (cp[-1] == 'M' && cp[-2] == 'C' && cp[-3] == 'S' - && string_numeric_p (cp1)))) - { - Scheme_functions (inf); - goto close_and_return; - } - /* Assembly code */ - if (cp && (streq (cp1, "s") - || streq (cp1, "a") /* Unix assembler */ - || streq (cp1, "sa") /* Unix assembler */ - || streq (cp1, "asm") /* Microcontroller assembly */ - || streq (cp1, "src") /* BSO/Tasking C compiler output */ - || streq (cp1, "def") /* BSO/Tasking definition includes */ - || streq (cp1, "ins") /* Microcontroller include files */ - || streq (cp1, "inc")))/* Microcontroller include files */ - { - Asm_labels (inf); - goto close_and_return; - } - /* .C or .H or .cxx or .hxx or .cc: a C++ file */ - if (cp && (streq (cp1, "C") - || streq (cp1, "H") - || streq (cp1, "cxx") - || streq (cp1, "hxx") - || streq (cp1, "cc"))) - { - C_entries (C_PLPL, inf); /* C++ */ - goto close_and_return; - } - /* .cs or .hs: a C* file */ - if (cp && (streq (cp1, "cs") - || streq (cp1, "hs"))) - { - C_entries (C_STAR, inf); - goto close_and_return; - } - /* .y: a yacc file */ - if (cp && (streq (cp1, "y"))) - { - C_entries (YACC, inf); - goto close_and_return; - } - /* .pl implies prolog source code */ - if (cp && streq (cp1, "pl")) - { - Prolog_functions (inf); - goto close_and_return; - } - /* .p or .pas: a Pascal file */ - if (cp && (streq (cp1, "p") - || streq (cp1, "pas"))) - { - Pascal_functions (inf); - goto close_and_return; - } - /* If .f or .for, assume it is fortran or nothing. */ - if (cp && (streq (cp1, "f") - || streq (cp1, "for"))) - { - (void) Fortran_functions (inf); - goto close_and_return; - } - /* if not a .c or .h or .y file, try fortran */ - if (cp && ((cp[1] != 'c' - && cp[1] != 'h' - && cp[1] != 'y') - || (cp[1] != 0 && cp[2] != 0))) - { - if (Fortran_functions (inf) != 0) - goto close_and_return; - rewind (inf); /* no fortran tags found, try C */ + if (cp != NULL) + { + cp += 1; + function = get_language_from_suffix (cp); + if (function != NULL) + { + function (inf); + fclose (inf); + return; + } } - C_entries (cplusplus ? C_PLPL : 0, inf); -close_and_return: - (void) fclose (inf); - return TRUE; -} + /* Look for sharp-bang as the first two characters. */ + if (readline_internal (&lb, inf) > 2 + && lb.buffer[0] == '#' + && lb.buffer[1] == '!') + { + char *lp; + + /* Set lp to point at the first char after the last slash in the + line or, if no slashes, at the first nonblank. Then set cp to + the first successive blank and terminate the string. */ + lp = etags_strrchr (lb.buffer+2, '/'); + if (lp != NULL) + lp += 1; + else + for (lp = lb.buffer+2; *lp != '\0' && isspace (*lp); lp++) + continue; + for (cp = lp; *cp != '\0' && !isspace (*cp); cp++) + continue; + *cp = '\0'; -/* Nonzero if string STR is composed of digits. */ + if (strlen (lp) > 0) + { + function = get_language_from_interpreter (lp); + if (function != NULL) + { + function (inf); + fclose (inf); + return; + } + } + } + rewind (inf); -int -string_numeric_p (str) - char *str; -{ - while (*str) + /* Try Fortran. */ + old_last_node = last_node; + Fortran_functions (inf); + + /* No Fortran entries found. Try C. */ + if (old_last_node == last_node) { - if (*str < '0' || *str > '9') - return 0; + rewind (inf); + default_C_entries (inf); } - return 1; + fclose (inf); + return; } /* Record a tag. */ -/* Should take a TOKEN* instead!! */ void -pfnote (name, is_func, named, linestart, linelen, lno, cno) - char *name; /* tag name */ - logical is_func; /* function or type name? */ - logical named; /* tag different from text of definition? */ - char *linestart; - int linelen; - int lno; - long cno; -{ - register char *fp; +pfnote (name, is_func, linestart, linelen, lno, cno) + char *name; /* tag name, or NULL if unnamed */ + logical is_func; /* tag is a function */ + char *linestart; /* start of the line where tag is */ + int linelen; /* length of the line where tag is */ + int lno; /* line number */ + long cno; /* character number */ +{ register NODE *np; - char tem[51]; - char c; + + if (CTAGS && name == NULL) + return; np = xnew (1, NODE); - if (np == NULL) - { - if (CTAGS) - { - /* It's okay to output early in etags -- it only disrupts the - * character count of the tag entries, which is no longer used - * by tags.el anyway. - */ - error ("too many entries to sort", 0); - } - put_entries (head); - free_tree (head); - head = NULL; - np = xnew (1, NODE); - } + /* If ctags mode, change name "main" to M. */ if (CTAGS && !cxref_style && streq (name, "main")) { - fp = etags_strrchr (curfile, '/'); - name = concat ("M", fp == 0 ? curfile : fp + 1, ""); - fp = etags_strrchr (name, '.'); + register char *fp = etags_strrchr (curfile, '/'); + np->name = concat ("M", fp == 0 ? curfile : fp + 1, ""); + fp = etags_strrchr (np->name, '.'); if (fp && fp[1] != '\0' && fp[2] == '\0') - *fp = 0; - named = TRUE; + fp[0] = 0; } - np->name = savestr (name); + else + np->name = name; + np->been_warned = FALSE; np->file = curfile; np->is_func = is_func; - np->named = named; np->lno = lno; - /* UNCOMMENT THE +1 HERE: */ - np->cno = cno /* + 1 */ ; /* our char numbers are 0-base; emacs's are 1-base */ - np->left = np->right = 0; - if (!CTAGS) + /* Our char numbers are 0-base, because of C language tradition? + ctags compatibility? old versions compatibility? I don't know. + Anyway, since emacs's are 1-base we expect etags.el to take care + of the difference. If we wanted to have 1-based numbers, we would + uncomment the +1 below. */ + np->cno = cno /* + 1 */ ; + np->left = np->right = NULL; + if (CTAGS && !cxref_style) { - c = linestart[linelen]; - linestart[linelen] = 0; - } - else if (cxref_style == 0) - { - sprintf (tem, strlen (linestart) < 50 ? "%s$" : "%.50s", linestart); - linestart = tem; - } - np->pat = savestr (linestart); - if (!CTAGS) - { - linestart[linelen] = c; + if (strlen (linestart) < 50) + np->pat = concat (linestart, "$", ""); + else + np->pat = savenstr (linestart, 50); } + else + np->pat = savenstr (linestart, linelen); add_node (np, &head); } @@ -947,7 +1301,8 @@ free_tree (node) { register NODE *node_right = node->right; free_tree (node->left); - free (node->name); + if (node->name != NULL) + free (node->name); free (node->pat); free ((char *) node); node = node_right; @@ -963,10 +1318,7 @@ free_tree (node) * add_node is the only function allowed to add nodes, so it can * maintain state. */ -/* Must avoid static vars within functions since some systems - #define static as nothing. */ NODE *last_node = NULL; - void add_node (node, cur_node_p) NODE *node, **cur_node_p; @@ -1000,7 +1352,7 @@ add_node (node, cur_node_p) */ if (!dif) { - if (node->file == cur_node->file) + if (streq (node->file, cur_node->file)) { if (!no_warnings) { @@ -1008,26 +1360,18 @@ add_node (node, cur_node_p) node->file, lineno, node->name); fprintf (stderr, "Second entry ignored\n"); } - return; } - if (!cur_node->been_warned && !no_warnings) + else if (!cur_node->been_warned && !no_warnings) { - fprintf (stderr, - "Duplicate entry in files %s and %s: %s (Warning only)\n", - node->file, cur_node->file, node->name); + fprintf + (stderr, + "Duplicate entry in files %s and %s: %s (Warning only)\n", + node->file, cur_node->file, node->name); + cur_node->been_warned = TRUE; } - cur_node->been_warned = TRUE; return; } - /* Maybe refuse to add duplicate nodes. */ - if (!permit_duplicates) - { - if (streq (node->name, cur_node->name) - && streq (node->file, cur_node->file)) - return; - } - /* Actually add the node */ add_node (node, dif < 0 ? &cur_node->left : &cur_node->right); } @@ -1049,49 +1393,51 @@ put_entries (node) if (!CTAGS) { - if (node->named) + if (node->name != NULL) + fprintf (tagf, "%s\177%s\001%d,%d\n", + node->pat, node->name, node->lno, node->cno); + else + fprintf (tagf, "%s\177%d,%d\n", + node->pat, node->lno, node->cno); + } + else + { + if (node->name == NULL) + error ("internal error: NULL name in ctags mode.", 0); + + if (cxref_style) { - fprintf (tagf, "%s\177%s\001%d,%d\n", - node->pat, node->name, - node->lno, node->cno); + if (vgrind_style) + fprintf (stdout, "%s %s %d\n", + node->name, node->file, (node->lno + 63) / 64); + else + fprintf (stdout, "%-16s %3d %-16s %s\n", + node->name, node->lno, node->file, node->pat); } else { - fprintf (tagf, "%s\177%d,%d\n", - node->pat, - node->lno, node->cno); - } - } - else if (!cxref_style) - { - fprintf (tagf, "%s\t%s\t", - node->name, node->file); + fprintf (tagf, "%s\t%s\t", node->name, node->file); - if (node->is_func) - { /* a function */ - putc (searchar, tagf); - putc ('^', tagf); + if (node->is_func) + { /* a function */ + putc (searchar, tagf); + putc ('^', tagf); - for (sp = node->pat; *sp; sp++) - { - if (*sp == '\\' || *sp == searchar) - putc ('\\', tagf); - putc (*sp, tagf); + for (sp = node->pat; *sp; sp++) + { + if (*sp == '\\' || *sp == searchar) + putc ('\\', tagf); + putc (*sp, tagf); + } + putc (searchar, tagf); } - putc (searchar, tagf); - } - else - { /* a typedef; text pattern inadequate */ - fprintf (tagf, "%d", node->lno); + else + { /* a typedef; text pattern inadequate */ + fprintf (tagf, "%d", node->lno); + } + putc ('\n', tagf); } - putc ('\n', tagf); } - else if (vgrind_style) - fprintf (stdout, "%s %s %d\n", - node->name, node->file, (node->lno + 63) / 64); - else - fprintf (stdout, "%-16s %3d %-16s %s\n", - node->name, node->lno, node->file, node->pat); /* Output subentries that follow this one */ put_entries (node->right); @@ -1135,7 +1481,7 @@ total_size_of_entries (node) /* Count this entry */ total += strlen (node->pat) + 1; total += number_len ((long) node->lno) + 1 + number_len (node->cno) + 1; - if (node->named) + if (node->name != NULL) total += 1 + strlen (node->name); /* \001name */ } @@ -1145,19 +1491,30 @@ total_size_of_entries (node) /* * The C symbol tables. */ +enum sym_type +{ + st_none, st_C_objprot, st_C_objimpl, st_C_objend, st_C_gnumacro, + st_C_struct, st_C_enum, st_C_define, st_C_typedef, st_C_typespec +}; /* Feed stuff between (but not including) %[ and %] lines to: - gperf -c -k1,3 -o -p -r -t + gperf -c -k 1,3 -o -p -r -t %[ struct C_stab_entry { char *name; int c_ext; enum sym_type type; } %% +@interface, 0, st_C_objprot +@protocol, 0, st_C_objprot +@implementation,0, st_C_objimpl +@end, 0, st_C_objend class, C_PLPL, st_C_struct +namespace, C_PLPL, st_C_struct domain, C_STAR, st_C_struct union, 0, st_C_struct struct, 0, st_C_struct enum, 0, st_C_enum typedef, 0, st_C_typedef define, 0, st_C_define +bool, C_PLPL, st_C_typespec long, 0, st_C_typespec short, 0, st_C_typespec int, 0, st_C_typespec @@ -1172,93 +1529,122 @@ extern, 0, st_C_typespec static, 0, st_C_typespec const, 0, st_C_typespec volatile, 0, st_C_typespec +explicit, C_PLPL, st_C_typespec +mutable, C_PLPL, st_C_typespec +typename, C_PLPL, st_C_typespec +# DEFUN used in emacs, the next three used in glibc (SYSCALL only for mach). +DEFUN, 0, st_C_gnumacro +SYSCALL, 0, st_C_gnumacro +ENTRY, 0, st_C_gnumacro +PSEUDO, 0, st_C_gnumacro +# These are defined inside C functions, so currently they are not met. +# EXFUN used in glibc, DEFVAR_* in emacs. +#EXFUN, 0, st_C_gnumacro +#DEFVAR_, 0, st_C_gnumacro %] and replace lines between %< and %> with its output. */ /*%<*/ -/* C code produced by gperf version 1.8.1 (K&R C version) */ -/* Command-line: gperf -c -k1,3 -o -p -r -t */ +/* C code produced by gperf version 2.1 (K&R C version) */ +/* Command-line: gperf -c -k 1,3 -o -p -r -t */ struct C_stab_entry { char *name; int c_ext; enum sym_type type; }; #define MIN_WORD_LENGTH 3 -#define MAX_WORD_LENGTH 8 -#define MIN_HASH_VALUE 10 -#define MAX_HASH_VALUE 62 +#define MAX_WORD_LENGTH 15 +#define MIN_HASH_VALUE 34 +#define MAX_HASH_VALUE 121 /* - 21 keywords - 53 is the maximum key range + 34 keywords + 88 is the maximum key range */ static int hash (str, len) - register char *str; - register int len; + register char *str; + register unsigned int len; { static unsigned char hash_table[] = { - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 62, 62, 62, - 62, 62, 62, 62, 62, 62, 62, 2, 62, 7, - 6, 9, 15, 30, 62, 24, 62, 62, 1, 24, - 7, 27, 13, 62, 19, 26, 18, 27, 1, 62, - 62, 62, 62, 62, 62, 62, 62, 62, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 45, 121, 121, 121, 16, 19, + 61, 121, 121, 121, 121, 121, 121, 121, 121, 121, + 10, 121, 121, 20, 53, 121, 121, 121, 121, 121, + 121, 121, 121, 121, 121, 121, 121, 41, 45, 22, + 60, 47, 37, 28, 121, 55, 121, 121, 20, 14, + 29, 30, 5, 121, 50, 59, 30, 54, 6, 121, + 121, 121, 121, 121, 121, 121, 121, 121, }; return len + hash_table[str[2]] + hash_table[str[0]]; } struct C_stab_entry * -in_word_set (str, len) +in_word_set (str, len) register char *str; - register int len; + register unsigned int len; { static struct C_stab_entry wordlist[] = { {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, - {"",}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"volatile", 0, st_C_typespec}, - {"",}, + {"PSEUDO", 0, st_C_gnumacro}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"typedef", 0, st_C_typedef}, + {"typename", C_PLPL, st_C_typespec}, + {"",}, {"",}, {"",}, + {"SYSCALL", 0, st_C_gnumacro}, + {"",}, {"",}, {"",}, + {"mutable", C_PLPL, st_C_typespec}, + {"namespace", C_PLPL, st_C_struct}, {"long", 0, st_C_typespec}, - {"char", 0, st_C_typespec}, - {"class", C_PLPL, st_C_struct}, - {"",}, {"",}, {"",}, {"",}, + {"",}, {"",}, {"const", 0, st_C_typespec}, + {"",}, {"",}, {"",}, + {"explicit", C_PLPL, st_C_typespec}, {"",}, {"",}, {"",}, {"",}, - {"auto", 0, st_C_typespec}, - {"",}, {"",}, - {"define", 0, st_C_define}, - {"",}, {"void", 0, st_C_typespec}, + {"",}, + {"char", 0, st_C_typespec}, + {"class", C_PLPL, st_C_struct}, {"",}, {"",}, {"",}, - {"extern", 0, st_C_typespec}, - {"static", 0, st_C_typespec}, + {"float", 0, st_C_typespec}, {"",}, - {"domain", C_STAR, st_C_struct}, + {"@implementation", 0, st_C_objimpl}, + {"auto", 0, st_C_typespec}, {"",}, - {"typedef", 0, st_C_typedef}, - {"double", 0, st_C_typespec}, - {"enum", 0, st_C_enum}, - {"",}, {"",}, {"",}, {"",}, - {"int", 0, st_C_typespec}, + {"ENTRY", 0, st_C_gnumacro}, + {"@end", 0, st_C_objend}, + {"bool", C_PLPL, st_C_typespec}, + {"domain", C_STAR, st_C_struct}, {"",}, - {"float", 0, st_C_typespec}, + {"DEFUN", 0, st_C_gnumacro}, + {"extern", 0, st_C_typespec}, + {"@interface", 0, st_C_objprot}, {"",}, {"",}, {"",}, - {"struct", 0, st_C_struct}, + {"int", 0, st_C_typespec}, {"",}, {"",}, {"",}, {"",}, - {"union", 0, st_C_struct}, - {"",}, + {"signed", 0, st_C_typespec}, {"short", 0, st_C_typespec}, - {"",}, {"",}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"define", 0, st_C_define}, + {"@protocol", 0, st_C_objprot}, + {"enum", 0, st_C_enum}, + {"static", 0, st_C_typespec}, + {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, {"",}, + {"union", 0, st_C_struct}, + {"struct", 0, st_C_struct}, + {"",}, {"",}, {"",}, {"",}, + {"double", 0, st_C_typespec}, {"unsigned", 0, st_C_typespec}, - {"signed", 0, st_C_typespec}, }; if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) @@ -1269,7 +1655,7 @@ in_word_set (str, len) { register char *s = wordlist[key].name; - if (*s == *str && strneq (str + 1, s + 1, len - 1)) + if (*s == *str && !strncmp (str + 1, s + 1, len - 1)) return &wordlist[key]; } } @@ -1294,7 +1680,7 @@ C_symtype(str, len, c_ext) * C functions are recognized using a simple finite automaton. * funcdef is its state variable. */ -typedef enum +enum { fnone, /* nothing seen */ ftagseen, /* function-like tag seen */ @@ -1302,63 +1688,83 @@ typedef enum finlist, /* in parameter list */ flistseen, /* after parameter list */ fignore /* before open brace */ -} FUNCST; -FUNCST funcdef; +} funcdef; /* * typedefs are recognized using a simple finite automaton. - * typeddef is its state variable. + * typdef is its state variable. */ -typedef enum +enum { tnone, /* nothing seen */ ttypedseen, /* typedef keyword seen */ tinbody, /* inside typedef body */ tend, /* just before typedef tag */ tignore /* junk after typedef tag */ -} TYPEDST; -TYPEDST typdef; +} typdef; - /* + /* * struct-like structures (enum, struct and union) are recognized * using another simple finite automaton. `structdef' is its state * variable. */ -typedef enum +enum { snone, /* nothing seen yet */ skeyseen, /* struct-like keyword seen */ stagseen, /* struct-like tag seen */ scolonseen, /* colon seen after struct-like tag */ sinbody /* in struct body: recognize member func defs*/ -} STRUCTST; -STRUCTST structdef; +} structdef; /* * When structdef is stagseen, scolonseen, or sinbody, structtag is the - * struct tag, and structtype is the type of the preceding struct-like + * struct tag, and structtype is the type of the preceding struct-like * keyword. */ -char structtag[BUFSIZ]; +char *structtag = ""; enum sym_type structtype; +/* + * When objdef is different from onone, objtag is the name of the class. + */ +char *objtag = ""; + /* * Yet another little state machine to deal with preprocessor lines. */ -typedef enum +enum { dnone, /* nothing seen */ dsharpseen, /* '#' seen as first char on line */ ddefineseen, /* '#' and 'define' seen */ dignorerest /* ignore rest of line */ -} DEFINEST; -DEFINEST definedef; +} definedef; + +/* + * State machine for Objective C protocols and implementations. + */ +enum +{ + onone, /* nothing seen */ + oprotocol, /* @interface or @protocol seen */ + oimplementation, /* @implementations seen */ + otagseen, /* class name seen */ + oparenseen, /* parenthesis before category seen */ + ocatseen, /* category name seen */ + oinbody, /* in @implementation body */ + omethodsign, /* in @implementation body, after +/- */ + omethodtag, /* after method name */ + omethodcolon, /* after method colon */ + omethodparm, /* after method parameter */ + oignore /* wait for @end */ +} objdef; /* * Set this to TRUE, and the next token considered is called a function. - * Used only for GNUmacs's function-defining macros. + * Used only for GNU emacs's function-defining macros. */ logical next_token_is_func; @@ -1367,6 +1773,11 @@ logical next_token_is_func; */ logical yacc_rules; +/* + * methodlen is the length of the method name stored in token_name. + */ +int methodlen; + /* * consider_token () * checks to see if the current token is at the start of a @@ -1386,18 +1797,21 @@ logical yacc_rules; * structdef IN OUT * definedef IN OUT * typdef IN OUT + * objdef IN OUT * next_token_is_func IN OUT */ logical -consider_token (c, tokp, c_ext, cblev, is_func) +consider_token (str, len, c, c_ext, cblev, parlev, is_func) + register char *str; /* IN: token pointer */ + register int len; /* IN: token length */ register char c; /* IN: first char after the token */ - register TOKEN *tokp; /* IN: token pointer */ int c_ext; /* IN: C extensions mask */ int cblev; /* IN: curly brace level */ - logical *is_func; /* OUT */ + int parlev; /* IN: parenthesis level */ + logical *is_func; /* OUT: function found */ { - enum sym_type toktype = C_symtype(tokp->p, tokp->len, c_ext); + enum sym_type toktype = C_symtype (str, len, c_ext); /* * Advance the definedef state machine. @@ -1416,19 +1830,20 @@ consider_token (c, tokp, c_ext, cblev, is_func) { definedef = dignorerest; } - return (FALSE); + return FALSE; case ddefineseen: /* - * Make a tag for any macro. + * Make a tag for any macro, unless it is a constant + * and constantypedefs is FALSE. */ definedef = dignorerest; *is_func = (c == '('); if (!*is_func && !constantypedefs) - return (FALSE); + return FALSE; else - return (TRUE); + return TRUE; case dignorerest: - return (FALSE); + return FALSE; default: error ("internal error: definedef value.", 0); } @@ -1444,7 +1859,7 @@ consider_token (c, tokp, c_ext, cblev, is_func) if (typedefs) typdef = ttypedseen; funcdef = fnone; - return (FALSE); + return FALSE; } break; case ttypedseen: @@ -1466,9 +1881,9 @@ consider_token (c, tokp, c_ext, cblev, is_func) case st_C_typespec: case st_C_struct: case st_C_enum: - return (FALSE); + return FALSE; } - return (TRUE); + return TRUE; } /* @@ -1481,9 +1896,9 @@ consider_token (c, tokp, c_ext, cblev, is_func) * file is plain C. This is because a struct tag may have the same * name as another tag, and this loses with ctags. * - * This if statement deals with the typdef state machine as + * This if statement deals with the typdef state machine as * follows: if typdef==ttypedseen and token is struct/union/class/enum, - * return (FALSE). All the other code here is for the structdef + * return FALSE. All the other code here is for the structdef * state machine. */ switch (toktype) @@ -1496,53 +1911,111 @@ consider_token (c, tokp, c_ext, cblev, is_func) structdef = skeyseen; structtype = toktype; } - return (FALSE); + return FALSE; } if (structdef == skeyseen) { + /* Save the tag for struct/union/class, for functions that may be + defined inside. */ if (structtype == st_C_struct) - { - strncpy (structtag, tokp->p, tokp->len); - structtag[tokp->len] = '\0'; /* for struct/union/class */ - } + structtag = savenstr (str, len); else - { - structtag[0] = '\0'; /* for enum (why is it treated differently?) */ - } + structtag = ""; structdef = stagseen; - return (TRUE); + return TRUE; } /* Avoid entering funcdef stuff if typdef is going on. */ if (typdef != tnone) { definedef = dnone; - return (FALSE); + return FALSE; } - /* Detect GNUmacs's function-defining macros. */ - if (definedef == dnone) + /* Detect GNU macros. */ + if (definedef == dnone && toktype == st_C_gnumacro) { - if (strneq (tokp->p, "DEF", 3) - || strneq (tokp->p, "ENTRY", 5) - || strneq (tokp->p, "SYSCALL", 7) - || strneq (tokp->p, "PSEUDO", 6)) - { - next_token_is_func = TRUE; - return (FALSE); - } - if (strneq (tokp->p, "EXFUN", 5)) - { - next_token_is_func = FALSE; - return (FALSE); - } + next_token_is_func = TRUE; + return FALSE; } if (next_token_is_func) { next_token_is_func = FALSE; - funcdef = fnone; - *is_func = TRUE; /* to force search string in ctags */ - return (TRUE); + funcdef = fignore; + *is_func = TRUE; + return TRUE; + } + + /* + * Detecting Objective C constructs. + */ + switch (objdef) + { + case onone: + switch (toktype) + { + case st_C_objprot: + objdef = oprotocol; + return FALSE; + case st_C_objimpl: + objdef = oimplementation; + return FALSE; + } + break; + case oimplementation: + /* Save the class tag for functions that may be defined inside. */ + objtag = savenstr (str, len); + objdef = oinbody; + return FALSE; + case oprotocol: + /* Save the class tag for categories. */ + objtag = savenstr (str, len); + objdef = otagseen; + *is_func = TRUE; + return TRUE; + case oparenseen: + objdef = ocatseen; + *is_func = TRUE; + return TRUE; + case oinbody: + break; + case omethodsign: + if (parlev == 0) + { + objdef = omethodtag; + methodlen = len; + GROW_LINEBUFFER (token_name, methodlen+1); + strncpy (token_name.buffer, str, len); + token_name.buffer[methodlen] = '\0'; + return TRUE; + } + return FALSE; + case omethodcolon: + if (parlev == 0) + objdef = omethodparm; + return FALSE; + case omethodparm: + if (parlev == 0) + { + objdef = omethodtag; + methodlen += len; + GROW_LINEBUFFER (token_name, methodlen+1); + strncat (token_name.buffer, str, len); + return TRUE; + } + return FALSE; + case oignore: + if (toktype == st_C_objend) + { + /* Memory leakage here: the string pointed by objtag is + never released, because many tests would be needed to + avoid breaking on incorrect input code. The amount of + memory leaked here is the sum of the lengths of the + class tags. + free (objtag); */ + objdef = onone; + } + return FALSE; } /* A function? */ @@ -1551,17 +2024,17 @@ consider_token (c, tokp, c_ext, cblev, is_func) case st_C_typespec: if (funcdef != finlist && funcdef != fignore) funcdef = fnone; /* should be useless */ - return (FALSE); + return FALSE; default: if (funcdef == fnone) { funcdef = ftagseen; *is_func = TRUE; - return (TRUE); + return TRUE; } } - return (FALSE); + return FALSE; } /* @@ -1570,6 +2043,19 @@ consider_token (c, tokp, c_ext, cblev, is_func) * struct/union/enum definitions in C syntax and adds them * to the list. */ +typedef struct +{ + logical valid; + char *str; + logical named; + int linelen; + int lineno; + long linepos; + char *buffer; +} TOKEN; + +#define current_lb_is_new (newndx == curndx) +#define switch_line_buffers() (curndx = 1 - curndx) #define curlb (lbs[curndx].lb) #define othlb (lbs[1-curndx].lb) @@ -1578,67 +2064,68 @@ consider_token (c, tokp, c_ext, cblev, is_func) #define othlinepos (lbs[1-curndx].linepos) #define newlinepos (lbs[newndx].linepos) -/* Save and restore token state. This is used when preprocessor defines - are handled, to avoid disturbing active function/typedef/struct states. */ -#define TOKEN_SAVED_P (savetok.lineno > 0) -#define SAVE_TOKEN (savetok = tok, savetok.p = (char *) tokoff, \ - savetok.len = toklen, strcpy(savenameb, nameb)) -#define RESTORE_TOKEN (tok = savetok, tokoff = (int) tok.p, \ - toklen = tok.len, strcpy(nameb, savenameb), \ - savetok.lineno = 0) - #define CNL_SAVE_DEFINEDEF \ do { \ - SET_FILEPOS (curlinepos, inf, charno); \ + curlinepos = charno; \ lineno++; \ + linecharno = charno; \ charno += readline (&curlb, inf); \ lp = curlb.buffer; \ quotednl = FALSE; \ newndx = curndx; \ -} while (FALSE) +} while (0) #define CNL \ do { \ CNL_SAVE_DEFINEDEF; \ - if (TOKEN_SAVED_P) \ - RESTORE_TOKEN; \ + if (savetok.valid) \ + { \ + tok = savetok; \ + savetok.valid = FALSE; \ + } \ definedef = dnone; \ -} while (FALSE) - -#define MAKE_TAG_FROM_NEW_LB(isfun) pfnote (nameb, isfun, tok.named, \ - newlb.buffer, tokoff + toklen + 1, tok.lineno, GET_CHARNO (newlinepos)) -#define MAKE_TAG_FROM_OTH_LB(isfun) pfnote (nameb, isfun, tok.named, \ - othlb.buffer, tokoff + toklen + 1, tok.lineno, GET_CHARNO (othlinepos)) +} while (0) + +/* Ideally this macro should never be called wihen tok.valid is FALSE, + but this would mean that the state machines always guess right. */ +#define make_tag(isfun) do \ +if (tok.valid) { \ + char *name = NULL; \ + if (CTAGS || tok.named) \ + name = savestr (token_name.buffer); \ + pfnote (name, isfun, tok.buffer, tok.linelen, tok.lineno, tok.linepos); \ + tok.valid = FALSE; \ +} while (0) void C_entries (c_ext, inf) - int c_ext; /* extension of C? */ - FILE *inf; + int c_ext; /* extension of C */ + FILE *inf; /* input file */ { register char c; /* latest char read; '\0' for end of line */ register char *lp; /* pointer one beyond the character `c' */ int curndx, newndx; /* indices for current and new lb */ - TOKEN tok; /* latest token read for funcdef & structdef */ - char nameb[BUFSIZ]; /* latest token name for funcdef & structdef */ - register int tokoff; /* offset in line of start of latest token */ - register int toklen; /* length of latest token */ + TOKEN tok; /* latest token read */ + register int tokoff; /* offset in line of start of current token */ + register int toklen; /* length of current token */ int cblev; /* current curly brace level */ int parlev; /* current parenthesis level */ logical incomm, inquote, inchar, quotednl, midtoken; logical cplpl; - TOKEN savetok; /* saved token during preprocessor handling */ - char savenameb[BUFSIZ]; /* ouch! */ + TOKEN savetok; /* token saved during preprocessor handling */ + - savetok.lineno = 0; curndx = newndx = 0; lineno = 0; charno = 0; lp = curlb.buffer; *lp = 0; - definedef = dnone; funcdef = fnone; typdef = tnone; structdef = snone; + funcdef = fnone; typdef = tnone; structdef = snone; + definedef = dnone; objdef = onone; next_token_is_func = yacc_rules = FALSE; midtoken = inquote = inchar = incomm = quotednl = FALSE; + tok.valid = savetok.valid = FALSE; cblev = 0; parlev = 0; cplpl = c_ext & C_PLPL; @@ -1708,7 +2195,7 @@ C_entries (c_ext, inf) } continue; } - else + else switch (c) { case '"': @@ -1728,9 +2215,9 @@ C_entries (c_ext, inf) incomm = TRUE; continue; } - else if (cplpl && *lp == '/') + else if (/* cplpl && */ *lp == '/') { - c = 0; + c = '\0'; break; } else @@ -1751,29 +2238,50 @@ C_entries (c_ext, inf) else break; case '#': - if (lp == newlb.buffer + 1 && definedef == dnone) - definedef = dsharpseen; + if (definedef == dnone) + { + char *cp; + logical cpptoken = TRUE; + + /* Look back on this line. If all blanks, or nonblanks + followed by an end of comment, this is a preprocessor + token. */ + for (cp = newlb.buffer; cp < lp-1; cp++) + if (!iswhite (*cp)) + { + if (*cp == '*' && *(cp+1) == '/') + { + cp++; + cpptoken = TRUE; + } + else + cpptoken = FALSE; + } + if (cpptoken) + definedef = dsharpseen; + } /* if (definedef == dnone) */ + continue; } /* switch (c) */ /* Consider token only if some complicated conditions are satisfied. */ - if (((cblev == 0 && structdef != scolonseen) + if ((definedef != dnone + || (cblev == 0 && structdef != scolonseen) || (cblev == 1 && cplpl && structdef == sinbody)) && typdef != tignore && definedef != dignorerest - && (funcdef != finlist - || definedef != dnone)) + && funcdef != finlist) { if (midtoken) { if (endtoken (c)) { - if (cplpl && c == ':' && *lp == ':' && begtoken(*(lp + 1))) + if (c == ':' && cplpl && *lp == ':' && begtoken(*(lp + 1))) { /* - * This handles :: in the middle, but not at beginning - * of an identifier. + * This handles :: in the middle, but not at the + * beginning of an identifier. */ lp += 2; toklen += 3; @@ -1782,43 +2290,72 @@ C_entries (c_ext, inf) { logical is_func = FALSE; - tok.lineno = lineno; - tok.p = newlb.buffer + tokoff; - tok.len = toklen; - tok.named = FALSE; if (yacc_rules - || consider_token (c, &tok, c_ext, cblev, &is_func)) + || consider_token (newlb.buffer + tokoff, toklen, c, + c_ext, cblev, parlev, &is_func)) { if (structdef == sinbody && definedef == dnone && is_func) /* function defined in C++ class body */ + { + GROW_LINEBUFFER (token_name, + strlen(structtag)+2+toklen+1); + strcpy (token_name.buffer, structtag); + strcat (token_name.buffer, "::"); + strncat (token_name.buffer, + newlb.buffer+tokoff, toklen); + tok.named = TRUE; + } + else if (objdef == ocatseen) + /* Objective C category */ + { + GROW_LINEBUFFER (token_name, + strlen(objtag)+2+toklen+1); + strcpy (token_name.buffer, objtag); + strcat (token_name.buffer, "("); + strncat (token_name.buffer, + newlb.buffer+tokoff, toklen); + strcat (token_name.buffer, ")"); + tok.named = TRUE; + } + else if (objdef == omethodtag + || objdef == omethodparm) + /* Objective C method */ { tok.named = TRUE; - sprintf (nameb, "%s::%.*s", - ((structtag[0] == '\0') - ? "_anonymous_" : structtag), - tok.len, tok.p); } else { - sprintf (nameb, "%.*s", tok.len, tok.p); + GROW_LINEBUFFER (token_name, toklen+1); + strncpy (token_name.buffer, + newlb.buffer+tokoff, toklen); + token_name.buffer[toklen] = '\0'; + if (structdef == stagseen + || typdef == tend + || (is_func + && definedef == dignorerest)) /* macro */ + tok.named = TRUE; + else + tok.named = FALSE; } - - if (structdef == stagseen - || typdef == tend) - tok.named = TRUE; + tok.lineno = lineno; + tok.linelen = tokoff + toklen + 1; + tok.buffer = newlb.buffer; + tok.linepos = newlinepos; + tok.valid = TRUE; if (definedef == dnone && (funcdef == ftagseen || structdef == stagseen - || typdef == tend)) + || typdef == tend + || objdef != onone)) { - if (newndx == curndx) - curndx = 1 - curndx; /* switch line buffers */ + if (current_lb_is_new) + switch_line_buffers (); } else - MAKE_TAG_FROM_NEW_LB (is_func); + make_tag (is_func); } midtoken = FALSE; } @@ -1840,7 +2377,7 @@ C_entries (c_ext, inf) funcdef = finlist; continue; case flistseen: - MAKE_TAG_FROM_OTH_LB (TRUE); + make_tag (TRUE); funcdef = fignore; break; case ftagseen: @@ -1851,19 +2388,7 @@ C_entries (c_ext, inf) structdef = snone; break; case dsharpseen: - /* Take a quick peek ahead for define directive, - so we can avoid saving the token when not absolutely - necessary. [This is a speed hack.] */ - if (c == 'd' && strneq(lp, "efine", 5) - && iswhite(*(lp + 5))) - { - SAVE_TOKEN; - definedef = ddefineseen; - lp += 6; - } - else - definedef = dignorerest; - continue; + savetok = tok; } if (!yacc_rules || lp == newlb.buffer + 1) { @@ -1872,7 +2397,7 @@ C_entries (c_ext, inf) midtoken = TRUE; } continue; - } + } /* if (begtoken) */ } /* if must look at token */ @@ -1883,6 +2408,20 @@ C_entries (c_ext, inf) case ':': if (definedef != dnone) break; + switch (objdef) + { + case otagseen: + objdef = oignore; + make_tag (TRUE); + break; + case omethodtag: + case omethodparm: + objdef = omethodcolon; + methodlen += 1; + GROW_LINEBUFFER (token_name, methodlen+1); + strcat (token_name.buffer, ":"); + break; + } if (structdef == stagseen) structdef = scolonseen; else @@ -1891,7 +2430,7 @@ C_entries (c_ext, inf) case ftagseen: if (yacc_rules) { - MAKE_TAG_FROM_OTH_LB (FALSE); + make_tag (FALSE); funcdef = fignore; } break; @@ -1907,19 +2446,33 @@ C_entries (c_ext, inf) switch (typdef) { case tend: - MAKE_TAG_FROM_OTH_LB (FALSE); + make_tag (FALSE); /* FALLTHRU */ default: typdef = tnone; } if (funcdef != fignore) - funcdef = fnone; + { + funcdef = fnone; + /* The following instruction invalidates the token. + Probably the token should be invalidated in all + other cases where some state machine is reset. */ + tok.valid = FALSE; + } if (structdef == stagseen) structdef = snone; break; case ',': if (definedef != dnone) break; + switch (objdef) + { + case omethodtag: + case omethodparm: + make_tag (TRUE); + objdef = oinbody; + break; + } if (funcdef != finlist && funcdef != fignore) funcdef = fnone; if (structdef == stagseen) @@ -1931,7 +2484,7 @@ C_entries (c_ext, inf) if (cblev == 0 && typdef == tend) { typdef = tignore; - MAKE_TAG_FROM_OTH_LB (FALSE); + make_tag (FALSE); break; } if (funcdef != finlist && funcdef != fignore) @@ -1942,8 +2495,26 @@ C_entries (c_ext, inf) case '(': if (definedef != dnone) break; + if (objdef == otagseen && parlev == 0) + objdef = oparenseen; switch (funcdef) { + case fnone: + switch (typdef) + { + case ttypedseen: + case tend: + /* Make sure that the next char is not a '*'. + This handles constructs like: + typedef void OperatorFun (int fun); */ + if (*lp != '*') + { + typdef = tignore; + make_tag (FALSE); + } + break; + } /* switch (typdef) */ + break; case ftagseen: funcdef = fstartlist; break; @@ -1956,6 +2527,11 @@ C_entries (c_ext, inf) case ')': if (definedef != dnone) break; + if (objdef == ocatseen && parlev == 1) + { + make_tag (TRUE); + objdef = oignore; + } if (--parlev == 0) { switch (funcdef) @@ -1968,7 +2544,7 @@ C_entries (c_ext, inf) if (cblev == 0 && typdef == tend) { typdef = tignore; - MAKE_TAG_FROM_OTH_LB (FALSE); + make_tag (FALSE); } } else if (parlev < 0) /* can happen due to ill-conceived #if's. */ @@ -1982,30 +2558,43 @@ C_entries (c_ext, inf) switch (structdef) { case skeyseen: /* unnamed struct */ - structtag[0] = '\0'; + structtag = "_anonymous_"; structdef = sinbody; break; case stagseen: case scolonseen: /* named struct */ structdef = sinbody; - MAKE_TAG_FROM_OTH_LB (FALSE); + make_tag (FALSE); break; } switch (funcdef) { case flistseen: - MAKE_TAG_FROM_OTH_LB (TRUE); + make_tag (TRUE); /* FALLTHRU */ case fignore: funcdef = fnone; break; case fnone: - /* Neutralize `extern "C" {' grot. - if (cblev == 0 && structdef == snone && typdef == tnone) - cblev--; */; - } - cblev++; - break; + switch (objdef) + { + case otagseen: + make_tag (TRUE); + objdef = oignore; + break; + case omethodtag: + case omethodparm: + make_tag (TRUE); + objdef = oinbody; + break; + default: + /* Neutralize `extern "C" {' grot and look inside structs. */ + if (cblev == 0 && structdef == snone && typdef == tnone) + cblev = -1; + } + } + cblev++; + break; case '*': if (definedef != dnone) break; @@ -2026,12 +2615,27 @@ C_entries (c_ext, inf) { if (typdef == tinbody) typdef = tend; + /* Memory leakage here: the string pointed by structtag is + never released, because I fear to miss something and + break things while freeing the area. The amount of + memory leaked here is the sum of the lengths of the + struct tags. + if (structdef == sinbody) + free (structtag); */ + structdef = snone; - strcpy (structtag, ""); + structtag = ""; } break; - case '=': - case '#': case '+': case '-': case '~': case '&': case '%': case '/': + case '+': + case '-': + if (objdef == oinbody && cblev == 0) + { + objdef = omethodsign; + break; + } + /* FALLTHRU */ + case '=': case '#': case '~': case '&': case '%': case '/': case '|': case '^': case '!': case '<': case '>': case '.': case '?': if (definedef != dnone) break; @@ -2040,6 +2644,11 @@ C_entries (c_ext, inf) funcdef = fnone; break; case '\0': + if (objdef == otagseen) + { + make_tag (TRUE); + objdef = oignore; + } /* If a macro spans multiple lines don't reset its state. */ if (quotednl) CNL_SAVE_DEFINEDEF; @@ -2050,11 +2659,53 @@ C_entries (c_ext, inf) } /* while not eof */ } + +/* + * Process either a C++ file or a C file depending on the setting + * of a global flag. + */ +void +default_C_entries (inf) + FILE *inf; +{ + C_entries (cplusplus ? C_PLPL : 0, inf); +} + +/* Always do plain ANSI C. */ +void +plain_C_entries (inf) + FILE *inf; +{ + C_entries (0, inf); +} + +/* Always do C++. */ +void +Cplusplus_entries (inf) + FILE *inf; +{ + C_entries (C_PLPL, inf); +} + +/* Always do C*. */ +void +Cstar_entries (inf) + FILE *inf; +{ + C_entries (C_STAR, inf); +} + +/* Always do Yacc. */ +void +Yacc_entries (inf) + FILE *inf; +{ + C_entries (YACC, inf); +} /* Fortran parsing */ char *dbp; -int pfcnt; logical tail (cp) @@ -2062,14 +2713,14 @@ tail (cp) { register int len = 0; - while (*cp && (*cp | ' ') == (dbp[len] | ' ')) + while (*cp && lowcase(*cp) == lowcase(dbp[len])) cp++, len++; - if (*cp == 0) + if (*cp == '\0' && !intoken(dbp[len])) { dbp += len; - return (TRUE); + return TRUE; } - return (FALSE); + return FALSE; } void @@ -2082,6 +2733,11 @@ takeprec () dbp++; while (isspace (*dbp)) dbp++; + if (strneq (dbp, "(*)", 3)) + { + dbp += 3; + return; + } if (!isdigit (*dbp)) { --dbp; /* force failure */ @@ -2097,8 +2753,6 @@ getit (inf) FILE *inf; { register char *cp; - char c; - char nambuf[BUFSIZ]; while (isspace (*dbp)) dbp++; @@ -2123,22 +2777,16 @@ getit (inf) && (isalpha (*cp) || isdigit (*cp) || (*cp == '_') || (*cp == '$'))); cp++) continue; - c = *cp; - *cp = '\0'; - strcpy (nambuf, dbp); - *cp = c; - pfnote (nambuf, TRUE, FALSE, lb.buffer, - cp - lb.buffer + 1, lineno, linecharno); - pfcnt++; + pfnote ((CTAGS) ? savenstr (dbp, cp-dbp) : NULL, TRUE, + lb.buffer, cp - lb.buffer + 1, lineno, linecharno); } -int +void Fortran_functions (inf) FILE *inf; { lineno = 0; charno = 0; - pfcnt = 0; while (!feof (inf)) { @@ -2150,9 +2798,9 @@ Fortran_functions (inf) dbp++; /* Ratfor escape to fortran */ while (isspace (*dbp)) dbp++; - if (*dbp == 0) + if (*dbp == '\0') continue; - switch (*dbp | ' ') + switch (lowcase (*dbp)) { case 'i': if (tail ("integer")) @@ -2175,7 +2823,7 @@ Fortran_functions (inf) { while (isspace (*dbp)) dbp++; - if (*dbp == 0) + if (*dbp == '\0') continue; if (tail ("precision")) break; @@ -2185,9 +2833,9 @@ Fortran_functions (inf) } while (isspace (*dbp)) dbp++; - if (*dbp == 0) + if (*dbp == '\0') continue; - switch (*dbp | ' ') + switch (lowcase (*dbp)) { case 'f': if (tail ("function")) @@ -2212,7 +2860,6 @@ Fortran_functions (inf) continue; } } - return (pfcnt); } /* @@ -2224,13 +2871,10 @@ void Asm_labels (inf) FILE *inf; { - char nambuf[BUFSIZ]; register char *cp; - char c; lineno = 0; charno = 0; - pfcnt = 0; while (!feof (inf)) { @@ -2250,65 +2894,83 @@ Asm_labels (inf) if (*cp == ':' || isspace (*cp)) { /* Found end of label, so copy it and add it to the table. */ - c = *cp; - *cp = '\0'; - strcpy (nambuf, lb.buffer); - *cp = c; - pfnote (nambuf, TRUE, FALSE, lb.buffer, - cp - lb.buffer + 1, lineno, linecharno); - pfcnt++; + pfnote ((CTAGS) ? savenstr(lb.buffer, cp-lb.buffer) : NULL, TRUE, + lb.buffer, cp - lb.buffer + 1, lineno, linecharno); } } } } -/* Added by Mosur Mohan, 4/22/88 */ -/* Pascal parsing */ +/* + * Perl support by Bart Robinson + * Perl sub names: look for /^sub[ \t\n]+[^ \t\n{]+/ + */ +void +Perl_functions (inf) + FILE *inf; +{ + register char *cp; -#define GET_NEW_LINE \ -{ \ - linecharno = charno; lineno++; \ - charno += 1 + readline (&lb, inf); \ - dbp = lb.buffer; \ + lineno = 0; + charno = 0; + + while (!feof (inf)) + { + lineno++; + linecharno = charno; + charno += readline (&lb, inf); + cp = lb.buffer; + + if (*cp++ == 's' && *cp++ == 'u' && *cp++ == 'b' && isspace(*cp++)) + { + while (*cp && isspace(*cp)) + cp++; + while (*cp && ! isspace(*cp) && *cp != '{') + cp++; + pfnote ((CTAGS) ? savenstr (lb.buffer, cp-lb.buffer) : NULL, TRUE, + lb.buffer, cp - lb.buffer + 1, lineno, linecharno); + } + } } + +/* Added by Mosur Mohan, 4/22/88 */ +/* Pascal parsing */ -/* Locates tags for procedures & functions. - * Doesn't do any type- or var-definitions. - * It does look for the keyword "extern" or "forward" - * immediately following the procedure statement; - * if found, the tag is skipped. +/* + * Locates tags for procedures & functions. Doesn't do any type- or + * var-definitions. It does look for the keyword "extern" or + * "forward" immediately following the procedure statement; if found, + * the tag is skipped. */ - void Pascal_functions (inf) FILE *inf; { struct linebuffer tline; /* mostly copied from C_entries */ long save_lcno; - int save_lineno; - char c, *cp; - char nambuf[BUFSIZ]; + int save_lineno, save_len; + char c, *cp, *namebuf; logical /* each of these flags is TRUE iff: */ - incomm1, /* point is inside {..} comment */ - incomm2, /* point is inside (*..*) comment */ + incomment, /* point is inside a comment */ inquote, /* point is inside '..' string */ - get_tagname, /* point is after PROCEDURE/FUNCTION */ - /* keyword, so next item = potential tag */ + get_tagname, /* point is after PROCEDURE/FUNCTION + keyword, so next item = potential tag */ found_tag, /* point is after a potential tag */ inparms, /* point is within parameter-list */ - verify_tag; /* point has passed the parm-list, so the */ - /* next token will determine whether */ - /* this is a FORWARD/EXTERN to be */ - /* ignored, or whether it is a real tag */ + verify_tag; /* point has passed the parm-list, so the + next token will determine whether this + is a FORWARD/EXTERN to be ignored, or + whether it is a real tag */ lineno = 0; charno = 0; dbp = lb.buffer; - *dbp = 0; + *dbp = '\0'; + save_len = 0; initbuffer (&tline); - incomm1 = incomm2 = inquote = FALSE; + incomment = inquote = FALSE; found_tag = FALSE; /* have a proc name; check if extern */ get_tagname = FALSE; /* have found "procedure" keyword */ inparms = FALSE; /* found '(' after "proc" */ @@ -2318,33 +2980,28 @@ Pascal_functions (inf) while (!feof (inf)) { c = *dbp++; - if (c == 0) /* if end of line */ + if (c == '\0') /* if end of line */ { - GET_NEW_LINE; - if (*dbp == 0) + lineno++; + linecharno = charno; + charno += readline (&lb, inf); + dbp = lb.buffer; + if (*dbp == '\0') continue; if (!((found_tag && verify_tag) || get_tagname)) - c = *dbp++; /* only if don't need *dbp pointing */ - /* to the beginning of the name of */ - /* the procedure or function */ - } - if (incomm1) /* within { - } comments */ - { - if (c == '}') - incomm1 = FALSE; - continue; + c = *dbp++; /* only if don't need *dbp pointing + to the beginning of the name of + the procedure or function */ } - else if (incomm2) /* within (* - *) comments */ + if (incomment) { - if (c == '*') + if (c == '}') /* within { } comments */ + incomment = FALSE; + else if (c == '*' && *dbp == ')') /* within (* *) comments */ { - while ((c = *dbp++) == '*') - continue; - if (c == 0) - GET_NEW_LINE; - if (c == ')') - incomm2 = FALSE; + dbp++; + incomment = FALSE; } continue; } @@ -2360,13 +3017,13 @@ Pascal_functions (inf) case '\'': inquote = TRUE; /* found first quote */ continue; - case '{': /* found open-{-comment */ - incomm1 = TRUE; + case '{': /* found open { comment */ + incomment = TRUE; continue; case '(': - if (*dbp == '*') /* found open-(*-comment */ + if (*dbp == '*') /* found open (* comment */ { - incomm2 = TRUE; + incomment = TRUE; dbp++; } else if (found_tag) /* found '(' after tag, i.e., parm-list */ @@ -2377,19 +3034,19 @@ Pascal_functions (inf) inparms = FALSE; continue; case ';': - if ((found_tag) && (!inparms)) /* end of proc or fn stmt */ + if (found_tag && !inparms) /* end of proc or fn stmt */ { verify_tag = TRUE; break; } continue; } - if ((found_tag) && (verify_tag) && (*dbp != ' ')) + if (found_tag && verify_tag && (*dbp != ' ')) { /* check if this is an "extern" declaration */ - if (*dbp == 0) + if (*dbp == '\0') continue; - if ((*dbp == 'e') || (*dbp == 'E')) + if (lowcase (*dbp == 'e')) { if (tail ("extern")) /* superfluous, really! */ { @@ -2397,7 +3054,7 @@ Pascal_functions (inf) verify_tag = FALSE; } } - else if ((*dbp == 'f') || (*dbp == 'F')) + else if (lowcase (*dbp) == 'f') { if (tail ("forward")) /* check for forward reference */ { @@ -2405,23 +3062,22 @@ Pascal_functions (inf) verify_tag = FALSE; } } - if ((found_tag) && (verify_tag)) /* not external proc, so make tag */ + if (found_tag && verify_tag) /* not external proc, so make tag */ { found_tag = FALSE; verify_tag = FALSE; - pfnote (nambuf, TRUE, FALSE, - tline.buffer, cp - tline.buffer + 1, - save_lineno, save_lcno); + pfnote (namebuf, TRUE, + tline.buffer, save_len, save_lineno, save_lcno); continue; } } if (get_tagname) /* grab name of proc or fn */ { - if (*dbp == 0) + if (*dbp == '\0') continue; /* save all values for later tagging */ - tline.size = lb.size; + GROW_LINEBUFFER (tline, strlen (lb.buffer) + 1); strcpy (tline.buffer, lb.buffer); save_lineno = lineno; save_lcno = linecharno; @@ -2429,22 +3085,19 @@ Pascal_functions (inf) /* grab block name */ for (cp = dbp + 1; *cp && (!endtoken (*cp)); cp++) continue; - c = cp[0]; - cp[0] = 0; - strcpy (nambuf, dbp); - cp[0] = c; - dbp = cp; /* restore dbp to e-o-token */ + namebuf = (CTAGS) ? savenstr (dbp, cp-dbp) : NULL; + dbp = cp; /* set dbp to e-o-token */ + save_len = dbp - lb.buffer + 1; get_tagname = FALSE; found_tag = TRUE; continue; /* and proceed to check for "extern" */ } - if ((!incomm1) && (!incomm2) && (!inquote) && - (!found_tag) && (!get_tagname)) + else if (!incomment && !inquote && !found_tag) { /* check for proc/fn keywords */ - switch (c | ' ') + switch (lowcase (c)) { case 'p': if (tail ("rocedure")) /* c = 'p', dbp has advanced */ @@ -2457,40 +3110,39 @@ Pascal_functions (inf) } } } /* while not eof */ + + free (tline.buffer); } /* * lisp tag functions - * just look for (def or (DEF + * look for (def or (DEF, quote or QUOTE */ - int -L_isdef (dbp) - register char *dbp; +L_isdef (strp) + register char *strp; { - return ((dbp[1] == 'd' || dbp[1] == 'D') - && (dbp[2] == 'e' || dbp[2] == 'E') - && (dbp[3] == 'f' || dbp[3] == 'F')); + return ((strp[1] == 'd' || strp[1] == 'D') + && (strp[2] == 'e' || strp[2] == 'E') + && (strp[3] == 'f' || strp[3] == 'F')); } int -L_isquote (dbp) - register char *dbp; +L_isquote (strp) + register char *strp; { - return ((*(++dbp) == 'q' || *dbp == 'Q') - && (*(++dbp) == 'u' || *dbp == 'U') - && (*(++dbp) == 'o' || *dbp == 'O') - && (*(++dbp) == 't' || *dbp == 'T') - && (*(++dbp) == 'e' || *dbp == 'E') - && isspace(*(++dbp))); + return ((*(++strp) == 'q' || *strp == 'Q') + && (*(++strp) == 'u' || *strp == 'U') + && (*(++strp) == 'o' || *strp == 'O') + && (*(++strp) == 't' || *strp == 'T') + && (*(++strp) == 'e' || *strp == 'E') + && isspace(*(++strp))); } void L_getit () { register char *cp; - char c; - char nambuf[BUFSIZ]; if (*dbp == '\'') /* Skip prefix quote */ dbp++; @@ -2500,18 +3152,15 @@ L_getit () while (isspace(*dbp)) dbp++; } - for (cp = dbp /*+1*/; *cp && *cp != '(' && *cp != ' ' && *cp != ')'; cp++) + for (cp = dbp /*+1*/; + *cp && *cp != '(' && *cp != ' ' && *cp != ')'; + cp++) continue; if (cp == dbp) return; - - c = cp[0]; - cp[0] = 0; - strcpy (nambuf, dbp); - cp[0] = c; - pfnote (nambuf, TRUE, FALSE, lb.buffer, - cp - lb.buffer + 1, lineno, linecharno); - pfcnt++; + + pfnote ((CTAGS) ? savenstr (dbp, cp-dbp) : NULL, TRUE, + lb.buffer, cp - lb.buffer + 1, lineno, linecharno); } void @@ -2520,7 +3169,6 @@ Lisp_functions (inf) { lineno = 0; charno = 0; - pfcnt = 0; while (!feof (inf)) { @@ -2581,7 +3229,6 @@ Scheme_functions (inf) { lineno = 0; charno = 0; - pfcnt = 0; while (!feof (inf)) { @@ -2622,24 +3269,16 @@ void get_scheme () { register char *cp; - char c; - char nambuf[BUFSIZ]; - if (*dbp == 0) + if (*dbp == '\0') return; /* Go till you get to white space or a syntactic break */ - for (cp = dbp + 1; *cp && *cp != '(' && *cp != ')' && !isspace (*cp); cp++) + for (cp = dbp + 1; + *cp && *cp != '(' && *cp != ')' && !isspace (*cp); + cp++) continue; - /* Null terminate the string there. */ - c = cp[0]; - cp[0] = 0; - /* Copy the string */ - strcpy (nambuf, dbp); - /* Unterminate the string */ - cp[0] = c; - /* Announce the change */ - pfnote (nambuf, TRUE, FALSE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno); - pfcnt++; + pfnote ((CTAGS) ? savenstr (dbp, cp-dbp) : NULL, TRUE, + lb.buffer, cp - lb.buffer + 1, lineno, linecharno); } /* Find tags in TeX and LaTeX input files. */ @@ -2647,7 +3286,6 @@ get_scheme () /* TEX_toktab is a table of TeX control sequences that define tags. Each TEX_tabent records one such control sequence. CONVERT THIS TO USE THE Stab TYPE!! */ - struct TEX_tabent { char *name; @@ -2660,12 +3298,15 @@ struct TEX_tabent *TEX_toktab = NULL; /* Table with tag tokens */ The value of environment var TEXTAGS is prepended to this. */ char *TEX_defenv = "\ -:chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem:typeout"; +:chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\ +:part:appendix:entry:index"; void TEX_mode (); struct TEX_tabent *TEX_decode_env (); -void TEX_getit (); int TEX_Token (); +#if TeX_named_tokens +void TEX_getit (); +#endif char TEX_esc = '\\'; char TEX_opgrp = '{'; @@ -2674,7 +3315,6 @@ char TEX_clgrp = '}'; /* * TeX/LaTeX scanning loop. */ - void TeX_functions (inf) FILE *inf; @@ -2683,7 +3323,6 @@ TeX_functions (inf) lineno = 0; charno = 0; - pfcnt = 0; /* Select either \ or ! as escape character. */ TEX_mode (inf); @@ -2710,7 +3349,11 @@ TeX_functions (inf) i = TEX_Token (lasthit); if (0 <= i) { + pfnote (NULL, TRUE, + lb.buffer, strlen (lb.buffer), lineno, linecharno); +#if TeX_named_tokens TEX_getit (lasthit, TEX_toktab[i].len); +#endif break; /* We only save a line once */ } } @@ -2721,9 +3364,8 @@ TeX_functions (inf) #define TEX_SESC '!' #define TEX_cmt '%' -/* Figure out whether TeX's escapechar is '\\' or '!' and set grouping */ -/* chars accordingly. */ - +/* Figure out whether TeX's escapechar is '\\' or '!' and set grouping + chars accordingly. */ void TEX_mode (inf) FILE *inf; @@ -2755,9 +3397,8 @@ TEX_mode (inf) rewind (inf); } -/* Read environment and prepend it to the default string. */ -/* Build token table. */ - +/* Read environment and prepend it to the default string. + Build token table. */ struct TEX_tabent * TEX_decode_env (evarname, defenv) char *evarname; @@ -2807,36 +3448,32 @@ TEX_decode_env (evarname, defenv) return tab; } +#if TeX_named_tokens /* Record a tag defined by a TeX command of length LEN and starting at NAME. The name being defined actually starts at (NAME + LEN + 1). But we seem to include the TeX command in the tag name. */ - void TEX_getit (name, len) char *name; int len; { char *p = name + len; - char nambuf[BUFSIZ]; - if (*name == 0) + if (*name == '\0') return; /* Let tag name extend to next group close (or end of line) */ while (*p && *p != TEX_clgrp) p++; - strncpy (nambuf, name, p - name); - nambuf[p - name] = 0; - - pfnote (nambuf, TRUE, FALSE, lb.buffer, strlen (lb.buffer), lineno, linecharno); - pfcnt++; + pfnote (savenstr (name, p-name), TRUE, + lb.buffer, strlen (lb.buffer), lineno, linecharno); } +#endif /* If the text at CP matches one of the tag-defining TeX command names, return the pointer to the first occurrence of that command in TEX_toktab. - Otherwise return -1. */ - -/* Keep the capital `T' in `Token' for dumb truncating compilers + Otherwise return -1. + Keep the capital `T' in `Token' for dumb truncating compilers (this distinguishes it from `TEX_toktab' */ int TEX_Token (cp) @@ -2850,137 +3487,577 @@ TEX_Token (cp) return -1; } -/* Support for Prolog. */ +/* + * Prolog support (rewritten) by Anders Lindgren, Mar. 96 + * + * Assumes that the predicate starts at column 0. + * Only the first clause of a predicate is added. + */ +void +Prolog_functions (inf) + FILE *inf; +{ + int prolog_pred (); + void prolog_skip_comment (); + + char * last; + int len; + int allocated; + + allocated = 0; + len = 0; + last = NULL; + + lineno = 0; + linecharno = 0; + charno = 0; + + while (!feof (inf)) + { + lineno++; + linecharno += charno; + charno = readline (&lb, inf); + dbp = lb.buffer; + if (dbp[0] == '\0') /* Empty line */ + continue; + else if (isspace (dbp[0])) /* Not a predicate */ + continue; + else if (dbp[0] == '/' && dbp[1] == '*') /* comment. */ + prolog_skip_comment (&lb, inf, &lineno, &linecharno); + else if (len = prolog_pred (dbp, last)) + { + /* Predicate. Store the function name so that we only + * generates a tag for the first clause. */ + if (last == NULL) + last = xnew(len + 1, char); + else if (len + 1 > allocated) + last = (char *) xrealloc(last, len + 1); + allocated = len + 1; + strncpy (last, dbp, len); + last[len] = '\0'; + } + } +} -/* whole head (not only functor, but also arguments) - is gotten in compound term. */ void -prolog_getit (s, lineno, linecharno) +prolog_skip_comment (plb, inf) + struct linebuffer *plb; + FILE *inf; +{ + char *cp; + + do + { + for (cp = plb->buffer; *cp != '\0'; cp++) + if (cp[0] == '*' && cp[1] == '/') + return; + lineno++; + linecharno += readline (plb, inf); + } + while (!feof(inf)); +} + +/* + * A predicate definition is added if it matches: + * ( + * + * It is added to the tags database if it doesn't match the + * name of the previous clause header. + * + * Return the size of the name of the predicate, or 0 if no header + * was found. + */ +int +prolog_pred (s, last) char *s; - int lineno; - long linecharno; + char *last; /* Name of last clause. */ { - char nambuf[BUFSIZ], *save_s, tmpc; - int insquote, npar; + int prolog_atom(); + int prolog_white(); - save_s = s; - insquote = FALSE; - npar = 0; - while (1) + int pos; + int len; + + pos = prolog_atom(s, 0); + if (pos < 1) + return 0; + + len = pos; + pos += prolog_white(s, pos); + + if ((s[pos] == '(') || (s[pos] == '.')) { - if (*s == '\0') /* syntax error. */ - return; - else if (insquote && *s == '\'' && *(s + 1) == '\'') - s += 2; - else if (*s == '\'') + if (s[pos] == '(') + pos++; + + /* Save only the first clause. */ + if ((last == NULL) || + (len != strlen(last)) || + (strncmp(s, last, len) != 0)) { - insquote = !insquote; - s++; + pfnote ((CTAGS) ? savenstr (s, len) : NULL, TRUE, + s, pos, lineno, linecharno); + return len; } - else if (!insquote && *s == '(') + } + return 0; +} + +/* + * Consume a Prolog atom. + * Return the number of bytes consumed, or -1 if there was an error. + * + * A prolog atom, in this context, could be one of: + * - An alphanumeric sequence, starting with a lower case letter. + * - A quoted arbitrary string. Single quotes can escape themselves. + * Backslash quotes everything. + */ +int +prolog_atom (s, pos) + char *s; + int pos; +{ + int origpos; + + origpos = pos; + + if (islower(s[pos]) || (s[pos] == '_')) + { + /* The atom is unquoted. */ + pos++; + while (isalnum(s[pos]) || (s[pos] == '_')) { - npar++; - s++; + pos++; } - else if (!insquote && *s == ')') + return pos - origpos; + } + else if (s[pos] == '\'') + { + pos++; + + while (1) { - npar--; - s++; - if (npar == 0) - break; - else if (npar < 0) /* syntax error. */ - return; - } - else if (!insquote && *s == '.' && (isspace (*(s + 1)) || *(s + 1) == '\0')) - { /* fullstop. */ - if (npar != 0) /* syntax error. */ - return; - s++; - break; + if (s[pos] == '\'') + { + pos++; + if (s[pos] != '\'') + break; + pos++; /* A double quote */ + } + else if (s[pos] == '\0') + /* Multiline quoted atoms are ignored. */ + return -1; + else if (s[pos] == '\\') + { + if (s[pos+1] == '\0') + return -1; + pos += 2; + } + else + pos++; } - else - s++; + return pos - origpos; } - tmpc = *s; - *s = '\0'; - strcpy (nambuf, save_s); - *s = tmpc; - pfnote (nambuf, TRUE, FALSE, save_s, strlen (nambuf), lineno, linecharno); + else + return -1; } -/* It is assumed that prolog predicate starts from column 0. */ +/* Consume whitespace. Return the number of bytes eaten. */ +int +prolog_white (s, pos) + char *s; + int pos; +{ + int origpos; + + origpos = pos; + while (isspace(s[pos])) + pos++; + + return pos - origpos; +} + +/* + * Support for Erlang -- Anders Lindgren, Feb 1996. + * + * Generates tags for functions, defines, and records. + * + * Assumes that Erlang functions start at column 0. + */ void -Prolog_functions (inf) +Erlang_functions (inf) FILE *inf; { - void skip_comment (), prolog_getit (); + int erlang_func (); + void erlang_attribute (); + + char * last; + int len; + int allocated; + + allocated = 0; + len = 0; + last = NULL; + + lineno = 0; + linecharno = 0; + charno = 0; - lineno = linecharno = charno = 0; while (!feof (inf)) { lineno++; linecharno += charno; - charno = readline (&lb, inf) + 1; /* 1 for newline. */ + charno = readline (&lb, inf); dbp = lb.buffer; - if (isspace (dbp[0])) /* not predicate header. */ + if (dbp[0] == '\0') /* Empty line */ continue; - else if (dbp[0] == '%') /* comment. */ + else if (isspace (dbp[0])) /* Not function nor attribute */ continue; - else if (dbp[0] == '/' && dbp[1] == '*') /* comment. */ - skip_comment (&lb, inf, &lineno, &linecharno); - else /* found. */ - prolog_getit (dbp, lineno, linecharno); + else if (dbp[0] == '%') /* comment */ + continue; + else if (dbp[0] == '"') /* Sometimes, strings start in column one */ + continue; + else if (dbp[0] == '-') /* attribute, e.g. "-define" */ + { + erlang_attribute(dbp); + last = NULL; + } + else if (len = erlang_func (dbp, last)) + { + /* + * Function. Store the function name so that we only + * generates a tag for the first clause. + */ + if (last == NULL) + last = xnew(len + 1, char); + else if (len + 1 > allocated) + last = (char *) xrealloc(last, len + 1); + allocated = len + 1; + strncpy (last, dbp, len); + last[len] = '\0'; + } + } +} + + +/* + * A function definition is added if it matches: + * ( + * + * It is added to the tags database if it doesn't match the + * name of the previous clause header. + * + * Return the size of the name of the function, or 0 if no function + * was found. + */ +int +erlang_func (s, last) + char *s; + char *last; /* Name of last clause. */ +{ + int erlang_atom (); + int erlang_white (); + + int pos; + int len; + + pos = erlang_atom(s, 0); + if (pos < 1) + return 0; + + len = pos; + pos += erlang_white(s, pos); + + if (s[pos++] == '(') + { + /* Save only the first clause. */ + if ((last == NULL) || + (len != strlen(last)) || + (strncmp(s, last, len) != 0)) + { + pfnote ((CTAGS) ? savenstr (s, len) : NULL, TRUE, + s, pos, lineno, linecharno); + return len; + } } + return 0; } + +/* + * Handle attributes. Currently, tags are generated for defines + * and records. + * + * They are on the form: + * -define(foo, bar). + * -define(Foo(M, N), M+N). + * -record(graph, {vtab = notable, cyclic = true}). + */ void -skip_comment (plb, inf, plineno, plinecharno) - struct linebuffer *plb; - FILE *inf; - int *plineno; /* result */ - long *plinecharno; /* result */ +erlang_attribute (s) + char *s; { - while (!substr ("*/", plb->buffer)) + int erlang_atom (); + int erlang_white (); + + int pos; + int len; + + if ((strncmp(s, "-define", 7) == 0) || + (strncmp(s, "-record", 7) == 0)) { - (*plineno)++; - *plinecharno += readline (plb, inf) + 1; - } /* 1 for newline. */ + pos = 7; + pos += erlang_white(s, pos); + + if (s[pos++] == '(') + { + pos += erlang_white(s, pos); + + if (len = erlang_atom(s, pos)) + { + pfnote ((CTAGS) ? savenstr (& s[pos], len) : NULL, TRUE, + s, pos + len, lineno, linecharno); + } + } + } + return; } -/* Return TRUE if 'sub' exists somewhere in 's'. */ +/* + * Consume an Erlang atom (or variable). + * Return the number of bytes consumed, or -1 if there was an error. + */ int -substr (sub, s) - char *sub; +erlang_atom (s, pos) char *s; + int pos; { - while (*s && (s = etags_strchr (s, *sub))) - if (prestr (sub, s)) - return (TRUE); - else - s++; - return (FALSE); -} + int origpos; + + origpos = pos; + + if (isalpha (s[pos]) || s[pos] == '_') + { + /* The atom is unquoted. */ + pos++; + while (isalnum (s[pos]) || s[pos] == '_') + pos++; + return pos - origpos; + } + else if (s[pos] == '\'') + { + pos++; -/* Return TRUE if 'pre' is prefix of string 's'. */ + while (1) + { + if (s[pos] == '\'') + { + pos++; + break; + } + else if (s[pos] == '\0') + /* Multiline quoted atoms are ignored. */ + return -1; + else if (s[pos] == '\\') + { + if (s[pos+1] == '\0') + return -1; + pos += 2; + } + else + pos++; + } + return pos - origpos; + } + else + return -1; +} +/* Consume whitespace. Return the number of bytes eaten */ int -prestr (pre, s) - char *pre; +erlang_white (s, pos) char *s; + int pos; +{ + int origpos; + + origpos = pos; + + while (isspace (s[pos])) + pos++; + + return pos - origpos; +} + +#ifdef ETAGS_REGEXPS +/* Take a string like "/blah/" and turn it into "blah", making sure + that the first and last characters are the same, and handling + quoted separator characters. Actually, stops on the occurrence of + an unquoted separator. Also turns "\t" into a Tab character. + Returns pointer to terminating separator. Works in place. Null + terminates name string. */ +char * +scan_separators (name) + char *name; +{ + char sep = name[0]; + char *copyto = name; + logical quoted = FALSE; + + for (++name; *name != '\0'; ++name) + { + if (quoted) + { + if (*name == 't') + *copyto++ = '\t'; + else if (*name == sep) + *copyto++ = sep; + else + { + /* Something else is quoted, so preserve the quote. */ + *copyto++ = '\\'; + *copyto++ = *name; + } + quoted = FALSE; + } + else if (*name == '\\') + quoted = TRUE; + else if (*name == sep) + break; + else + *copyto++ = *name; + } + + /* Terminate copied string. */ + *copyto = '\0'; + return name; +} + +/* Turn a name, which is an ed-style (but Emacs syntax) regular + expression, into a real regular expression by compiling it. */ +void +add_regex (regexp_pattern) + char *regexp_pattern; { - if (*pre == '\0') - return (TRUE); - else if (*pre == *s) - return (prestr (pre + 1, s + 1)); + char *name; + const char *err; + struct re_pattern_buffer *patbuf; + + if (regexp_pattern == NULL) + { + /* Remove existing regexps. */ + num_patterns = 0; + patterns = NULL; + return; + } + + if (regexp_pattern[0] == '\0') + { + error ("missing regexp", 0); + return; + } + if (regexp_pattern[strlen(regexp_pattern)-1] != regexp_pattern[0]) + { + error ("%s: unterminated regexp", regexp_pattern); + return; + } + name = scan_separators (regexp_pattern); + if (regexp_pattern[0] == '\0') + { + error ("null regexp", 0); + return; + } + (void) scan_separators (name); + + patbuf = xnew (1, struct re_pattern_buffer); + patbuf->translate = NULL; + patbuf->fastmap = NULL; + patbuf->buffer = NULL; + patbuf->allocated = 0; + + err = re_compile_pattern (regexp_pattern, strlen (regexp_pattern), patbuf); + if (err != NULL) + { + error ("%s while compiling pattern", err); + return; + } + + num_patterns += 1; + if (num_patterns == 1) + patterns = xnew (1, struct pattern); else - return (FALSE); + patterns = ((struct pattern *) + xrealloc (patterns, + (num_patterns * sizeof (struct pattern)))); + patterns[num_patterns - 1].pattern = patbuf; + patterns[num_patterns - 1].name_pattern = savestr (name); + patterns[num_patterns - 1].error_signaled = FALSE; +} + +/* + * Do the substitutions indicated by the regular expression and + * arguments. + */ +char * +substitute (in, out, regs) + char *in, *out; + struct re_registers *regs; +{ + char *result = NULL, *t; + int size = 0; + + /* Pass 1: figure out how much size to allocate. */ + for (t = out; *t; ++t) + { + if (*t == '\\') + { + ++t; + if (!*t) + { + fprintf (stderr, "%s: pattern substitution ends prematurely\n", + progname); + return NULL; + } + if (isdigit (*t)) + { + int dig = *t - '0'; + size += regs->end[dig] - regs->start[dig]; + } + } + } + + /* Allocate space and do the substitutions. */ + result = xnew (size + 1, char); + size = 0; + for (; *out; ++out) + { + if (*out == '\\') + { + ++out; + if (isdigit (*out)) + { + /* Using "dig2" satisfies my debugger. Bleah. */ + int dig2 = *out - '0'; + strncpy (result + size, in + regs->start[dig2], + regs->end[dig2] - regs->start[dig2]); + size += regs->end[dig2] - regs->start[dig2]; + } + else + result[size++] = *out; + } + else + result[size++] = *out; + } + result[size] = '\0'; + + return result; } +#endif /* ETAGS_REGEXPS */ /* Initialize a linebuffer for use */ - void initbuffer (linebuffer) struct linebuffer *linebuffer; @@ -2995,14 +4072,14 @@ initbuffer (linebuffer) * which is the length of the line including the newline, if any. */ long -readline (linebuffer, stream) +readline_internal (linebuffer, stream) struct linebuffer *linebuffer; register FILE *stream; { char *buffer = linebuffer->buffer; register char *p = linebuffer->buffer; register char *pend; - int newline; /* 1 if ended with newline, 0 if ended with EOF */ + int chars_deleted; pend = p + linebuffer->size; /* Separate to avoid 386/IX compiler bug. */ @@ -3017,18 +4094,123 @@ readline (linebuffer, stream) pend = buffer + linebuffer->size; linebuffer->buffer = buffer; } - if (c == EOF || c == '\n') + if (c == EOF) { - *p = 0; - newline = (c == '\n') ? 1 : 0; + *p = '\0'; + chars_deleted = 0; + break; + } + if (c == '\n') + { + if (p > buffer && p[-1] == '\r') + { + *--p = '\0'; +#ifdef DOS_NT + /* Assume CRLF->LF translation will be performed by Emacs + when loading this file, so CRs won't appear in the buffer. + It would be cleaner to compensate within Emacs; + however, Emacs does not know how many CRs were deleted + before any given point in the file. */ + chars_deleted = 1; +#else + chars_deleted = 2; +#endif + } + else + { + *p = '\0'; + chars_deleted = 1; + } break; } *p++ = c; } - return p - buffer + newline; + return p - buffer + chars_deleted; +} + +/* + * Like readline_internal, above, but try to match the input + * line against any existing regular expressions. + */ +long +readline (linebuffer, stream) + struct linebuffer *linebuffer; + FILE *stream; +{ + /* Read new line. */ + long result = readline_internal (linebuffer, stream); +#ifdef ETAGS_REGEXPS + int i; + + /* Match against all listed patterns. */ + for (i = 0; i < num_patterns; ++i) + { + int match = re_match (patterns[i].pattern, linebuffer->buffer, + (int)result, 0, &patterns[i].regs); + switch (match) + { + case -2: + /* Some error. */ + if (!patterns[i].error_signaled) + { + error ("error while matching pattern %d", i); + patterns[i].error_signaled = TRUE; + } + break; + case -1: + /* No match. */ + break; + default: + /* Match occurred. Construct a tag. */ + if (patterns[i].name_pattern[0] != '\0') + { + /* Make a named tag. */ + char *name = substitute (linebuffer->buffer, + patterns[i].name_pattern, + &patterns[i].regs); + if (name != NULL) + pfnote (name, TRUE, + linebuffer->buffer, match, lineno, linecharno); + } + else + { + /* Make an unnamed tag. */ + pfnote (NULL, TRUE, + linebuffer->buffer, match, lineno, linecharno); + } + break; + } + } +#endif /* ETAGS_REGEXPS */ + + return result; +} + +/* + * Read a file, but do no processing. This is used to do regexp + * matching on files that have no language defined. + */ +void +just_read_file (inf) + FILE *inf; +{ + lineno = 0; + charno = 0; + + while (!feof (inf)) + { + ++lineno; + linecharno = charno; + charno += readline (&lb, inf) + 1; + } } + +/* + * Return a pointer to a space of size strlen(cp)+1 allocated + * with xnew where the string CP has been copied. + */ char * savestr (cp) char *cp; @@ -3036,6 +4218,10 @@ savestr (cp) return savenstr (cp, strlen (cp)); } +/* + * Return a pointer to a space of size LEN+1 allocated with xnew where + * the string CP has been copied for at most the first LEN characters. + */ char * savenstr (cp, len) char *cp; @@ -3055,7 +4241,6 @@ savenstr (cp, len) * * Identical to System V strrchr, included for portability. */ - char * etags_strrchr (sp, c) register char *sp, c; @@ -3068,7 +4253,7 @@ etags_strrchr (sp, c) if (*sp == c) r = sp; } while (*sp++); - return (r); + return r; } @@ -3078,7 +4263,6 @@ etags_strrchr (sp, c) * * Identical to System V strchr, included for portability. */ - char * etags_strchr (sp, c) register char *sp, c; @@ -3086,14 +4270,12 @@ etags_strchr (sp, c) do { if (*sp == c) - return (sp); - } while (*sp++); - return (NULL); + return sp; + } while (*sp++); + return NULL; } /* Print error message and exit. */ - -/* VARARGS1 */ void fatal (s1, s2) char *s1, *s2; @@ -3102,9 +4284,23 @@ fatal (s1, s2) exit (BAD); } -/* Print error message. `s1' is printf control string, `s2' is arg for it. */ +void +pfatal (s1) + char *s1; +{ + perror (s1); + exit (BAD); +} -/* VARARGS1 */ +void +suggest_asking_for_help () +{ + fprintf (stderr, "\tTry `%s --help' for a complete list of options.\n", + progname); + exit (BAD); +} + +/* Print error message. `s1' is printf control string, `s2' is arg for it. */ void error (s1, s2) char *s1, *s2; @@ -3116,7 +4312,6 @@ error (s1, s2) /* Return a newly-allocated string whose contents concatenate those of s1, s2, s3. */ - char * concat (s1, s2, s3) char *s1, *s2, *s3; @@ -3133,50 +4328,63 @@ concat (s1, s2, s3) } /* Does the same work as the system V getcwd, but does not need to - guess buffer size in advance. Included mostly for compatibility. */ + guess the buffer size in advance. */ char * etags_getcwd () { - FILE *pipe; - char *buf; - int bufsize = 256; +#ifdef HAVE_GETCWD + int bufsize = 200; + char *path = xnew (bufsize, char); - do + while (getcwd (path, bufsize) == NULL) { - buf = xnew (bufsize, char); + if (errno != ERANGE) + pfatal ("getcwd"); + bufsize *= 2; + path = xnew (bufsize, char); + } - pipe = popen ("pwd 2>/dev/null", "r"); - if (pipe == NULL) - { - perror ("pwd"); - exit (BAD); - } - if (fgets (buf, bufsize, pipe) == NULL) - { - perror ("pwd"); - exit (BAD); - } - pclose (pipe); + return path; +#else /* not HAVE_GETCWD */ +#ifdef MSDOS + char *p, path[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS. */ - bufsize *= 2; + getwd (path); - } while (buf[strlen (buf) - 1] != '\n'); + for (p = path; *p != '\0'; p++) + if (*p == '\\') + *p = '/'; + else + *p = lowcase (*p); + + return strdup (path); +#else /* not MSDOS */ + struct linebuffer path; + FILE *pipe; - return buf; + initbuffer (&path); + pipe = (FILE *) popen ("pwd 2>/dev/null", "r"); + if (pipe == NULL || readline_internal (&path, pipe) == 0) + pfatal ("pwd"); + pclose (pipe); + + return path.buffer; +#endif /* not MSDOS */ +#endif /* not HAVE_GETCWD */ } /* Return a newly allocated string containing the filename of FILE relative to the absolute directory DIR (which should end with a slash). */ - char * relative_filename (file, dir) char *file, *dir; { - char *fp, *dp, *res; + char *fp, *dp, *abs, *res; /* Find the common root of file and dir. */ - fp = absolute_filename (file, cwd); + abs = absolute_filename (file, cwd); + fp = abs; dp = dir; while (*fp++ == *dp++) continue; @@ -3197,22 +4405,28 @@ relative_filename (file, dir) /* Add the filename relative to the common root of file and dir. */ res = concat (res, fp + 1, ""); + free (abs); - return res; /* temporary stub */ + return res; } /* Return a newly allocated string containing the absolute filename of FILE given CWD (which should end with a slash). */ - char * absolute_filename (file, cwd) char *file, *cwd; { char *slashp, *cp, *res; - if (file[0] == '/') + if (absolutefn (file)) res = concat (file, "", ""); +#ifdef DOS_NT + /* We don't support non-absolute filenames with a drive + letter, like `d:NAME' (it's too much hassle). */ + else if (file[1] == ':') + fatal ("%s: relative filenames with drive letters not supported", file); +#endif else res = concat (cwd, file, ""); @@ -3228,11 +4442,18 @@ absolute_filename (file, cwd) cp = slashp; do cp--; - while (cp >= res && *cp != '/'); + while (cp >= res && !absolutefn (cp)); if (*cp == '/') { strcpy (cp, slashp + 3); } +#ifdef DOS_NT + /* Under MSDOS and NT we get `d:/NAME' as absolute + filename, so the luser could say `d:/../NAME'. + We silently treat this as `d:/NAME'. */ + else if (cp[1] == ':') + strcpy (cp + 3, slashp + 4); +#endif else /* else (cp == res) */ { if (slashp[3] != '\0') @@ -3241,16 +4462,16 @@ absolute_filename (file, cwd) return "."; } slashp = cp; + continue; } else if (slashp[2] == '/' || slashp[2] == '\0') { strcpy (slashp, slashp + 2); + continue; } } - else - { - slashp = etags_strchr (slashp + 1, '/'); - } + + slashp = etags_strchr (slashp + 1, '/'); } return res; @@ -3259,13 +4480,19 @@ absolute_filename (file, cwd) /* Return a newly allocated string containing the absolute filename of dir where FILE resides given CWD (which should end with a slash). */ - char * absolute_dirname (file, cwd) char *file, *cwd; { char *slashp, *res; char save; +#ifdef DOS_NT + char *p; + + for (p = file; *p != '\0'; p++) + if (*p == '\\') + *p = '/'; +#endif slashp = etags_strrchr (file, '/'); if (slashp == NULL) @@ -3279,23 +4506,22 @@ absolute_dirname (file, cwd) } /* Like malloc but get fatal error if memory is exhausted. */ - -char * +long * xmalloc (size) unsigned int size; { - char *result = (char *) malloc (size); + long *result = (long *) malloc (size); if (result == NULL) fatal ("virtual memory exhausted", 0); return result; } -char * +long * xrealloc (ptr, size) char *ptr; unsigned int size; { - char *result = (char *) realloc (ptr, size); + long *result = (long *) realloc (ptr, size); if (result == NULL) fatal ("virtual memory exhausted"); return result;