char pot_etags_version[] = "@(#) pot revision number is 14.21";
#define TRUE 1
#define FALSE 0
#ifdef DEBUG
# undef DEBUG
# define DEBUG TRUE
#else
# define DEBUG FALSE
# define NDEBUG
#endif
#if defined(__STDC__) && (__STDC__ || defined(__SUNPRO_C))
# define P_(proto) proto
#else
# define P_(proto) ()
#endif
#ifdef HAVE_CONFIG_H
# include <config.h>
# undef static
# define ETAGS_REGEXPS
# define LONG_OPTIONS
#else
# ifndef __STDC__
# define static
# endif
#endif
#ifndef _GNU_SOURCE
# define _GNU_SOURCE 1
#endif
#ifdef WIN32_NATIVE
# undef MSDOS
# undef WINDOWSNT
# define WINDOWSNT
#endif
#ifdef MSDOS
# undef MSDOS
# define MSDOS TRUE
# include <fcntl.h>
# include <sys/param.h>
# include <io.h>
# ifndef HAVE_CONFIG_H
# define DOS_NT
# include <sys/config.h>
# endif
#else
# define MSDOS FALSE
#endif
#ifdef WINDOWSNT
# include <stdlib.h>
# include <fcntl.h>
# include <string.h>
# include <direct.h>
# include <io.h>
# define MAXPATHLEN _MAX_PATH
# undef HAVE_NTGUI
# undef DOS_NT
# define DOS_NT
# ifndef HAVE_GETCWD
# define HAVE_GETCWD
# endif
#else
# ifdef STDC_HEADERS
# include <stdlib.h>
# include <string.h>
# else
extern char *getenv ();
# endif
#endif
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#else
# if defined (HAVE_GETCWD) && !defined (WINDOWSNT)
extern char *getcwd (char *buf, size_t size);
# endif
#endif
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#ifndef errno
extern int errno;
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <assert.h>
#ifdef NDEBUG
# undef assert
# define assert(x) ((void) 0)
#endif
#if !defined (S_ISREG) && defined (S_IFREG)
# define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
#endif
#ifdef LONG_OPTIONS
# include <getopt.h>
#else
# define getopt_long(argc,argv,optstr,lopts,lind) getopt (argc, argv, optstr)
extern char *optarg;
extern int optind, opterr;
#endif
#ifdef ETAGS_REGEXPS
# include <regex.h>
#endif
#ifdef CTAGS
# undef CTAGS
# define CTAGS TRUE
#else
# define CTAGS FALSE
#endif
#ifdef VMS
# define GOOD 1
# define BAD 0
#else
# define GOOD 0
# define BAD 1
#endif
#define streq(s,t) (assert((s)!=NULL || (t)!=NULL), !strcmp (s, t))
#define strneq(s,t,n) (assert((s)!=NULL || (t)!=NULL), !strncmp (s, t, n))
#define CHARS 256
#define CHAR(x) ((unsigned int)(x) & (CHARS - 1))
#define iswhite(c) (_wht[CHAR(c)])
#define notinname(c) (_nin[CHAR(c)])
#define begtoken(c) (_btk[CHAR(c)])
#define intoken(c) (_itk[CHAR(c)])
#define endtoken(c) (_etk[CHAR(c)])
#define ISALNUM(c) isalnum (CHAR(c))
#define ISALPHA(c) isalpha (CHAR(c))
#define ISDIGIT(c) isdigit (CHAR(c))
#define ISLOWER(c) islower (CHAR(c))
#define lowcase(c) tolower (CHAR(c))
#define upcase(c) toupper (CHAR(c))
#if DEBUG
# include "chkmalloc.h"
# define xnew(n,Type) ((Type *) trace_malloc (__FILE__, __LINE__, \
(n) * sizeof (Type)))
# define xrnew(op,n,Type) ((op) = (Type *) trace_realloc (__FILE__, __LINE__, \
(char *) (op), (n) * sizeof (Type)))
#else
# define xnew(n,Type) ((Type *) xmalloc ((n) * sizeof (Type)))
# define xrnew(op,n,Type) ((op) = (Type *) xrealloc ( \
(char *) (op), (n) * sizeof (Type)))
#endif
typedef int bool;
typedef void Lang_function P_((FILE *));
typedef struct
{
char *suffix;
char *command;
} compressor;
typedef struct
{
char *name;
Lang_function *function;
char **filenames;
char **suffixes;
char **interpreters;
} language;
typedef struct node_st
{
char *name;
char *file;
bool is_func;
bool been_warned;
int lno;
long cno;
char *pat;
struct node_st *left, *right;
} node;
typedef struct
{
long size;
int len;
char *buffer;
} linebuffer;
static void Ada_funcs P_((FILE *));
static void Asm_labels P_((FILE *));
static void C_entries P_((int c_ext, FILE *));
static void default_C_entries P_((FILE *));
static void plain_C_entries P_((FILE *));
static void Cjava_entries P_((FILE *));
static void Cobol_paragraphs P_((FILE *));
static void Cplusplus_entries P_((FILE *));
static void Cstar_entries P_((FILE *));
static void Erlang_functions P_((FILE *));
static void Fortran_functions P_((FILE *));
static void Yacc_entries P_((FILE *));
static void Lisp_functions P_((FILE *));
static void Makefile_targets P_((FILE *));
static void Pascal_functions P_((FILE *));
static void Perl_functions P_((FILE *));
static void Postscript_functions P_((FILE *));
static void Prolog_functions P_((FILE *));
static void Python_functions P_((FILE *));
static void Scheme_functions P_((FILE *));
static void TeX_commands P_((FILE *));
static void Texinfo_nodes P_((FILE *));
static void just_read_file P_((FILE *));
static void print_language_names P_((void));
static void print_version P_((void));
static void print_help P_((void));
int main P_((int, char **));
static int number_len P_((long));
static compressor *get_compressor_from_suffix P_((char *, char **));
static language *get_language_from_langname P_((char *));
static language *get_language_from_interpreter P_((char *));
static language *get_language_from_filename P_((char *));
static int total_size_of_entries P_((node *));
static long readline P_((linebuffer *, FILE *));
static long readline_internal P_((linebuffer *, FILE *));
static void get_tag P_((char *));
#ifdef ETAGS_REGEXPS
static void analyse_regex P_((char *, bool));
static void add_regex P_((char *, bool, language *));
static void free_patterns P_((void));
#endif
static void error P_((const char *, const char *));
static void suggest_asking_for_help P_((void));
void fatal P_((char *, char *));
static void pfatal P_((char *));
static void add_node P_((node *, node **));
static void init P_((void));
static void initbuffer P_((linebuffer *));
static void find_entries P_((char *, FILE *));
static void free_tree P_((node *));
static void pfnote P_((char *, bool, char *, int, int, long));
static void new_pfnote P_((char *, int, bool, char *, int, int, long));
static void process_file P_((char *));
static void put_entries P_((node *));
static void takeprec P_((void));
static char *concat P_((char *, char *, char *));
static char *skip_spaces P_((char *));
static char *skip_non_spaces P_((char *));
static char *savenstr P_((char *, int));
static char *savestr P_((char *));
static char *etags_strchr P_((const char *, int));
static char *etags_strrchr P_((const char *, int));
static char *etags_getcwd P_((void));
static char *relative_filename P_((char *, char *));
static char *absolute_filename P_((char *, char *));
static char *absolute_dirname P_((char *, char *));
static bool filename_is_absolute P_((char *f));
static void canonicalize_filename P_((char *));
static void linebuffer_setlen P_((linebuffer *, int));
long *xmalloc P_((unsigned int));
long *xrealloc P_((char *, unsigned int));
char searchar = '/';
char *tagfile;
char *progname;
char *cwd;
char *tagfiledir;
FILE *tagf;
char *curfile;
language *curlang;
int lineno;
long charno;
long linecharno;
char *dbp;
node *head;
linebuffer lb;
bool _wht[CHARS], _nin[CHARS], _itk[CHARS], _btk[CHARS], _etk[CHARS];
char
*white = " \f\t\n\r\v",
*nonam = " \f\t\n\r(=,[;",
*endtk = " \t\n\r\"'#()[]{}=-+%*/&|^~!<>;,.:?",
*begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$~@",
*midtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789";
bool append_to_tagfile;
bool typedefs;
bool typedefs_or_cplusplus;
bool constantypedefs;
bool declarations;
bool globals;
bool members;
bool update;
bool vgrind_style;
bool no_warnings;
bool cxref_style;
bool cplusplus;
bool noindentypedefs;
bool packages_only;
#ifdef LONG_OPTIONS
struct option longopts[] =
{
{ "packages-only", no_argument, &packages_only, TRUE },
{ "append", no_argument, NULL, 'a' },
{ "backward-search", no_argument, NULL, 'B' },
{ "c++", no_argument, NULL, 'C' },
{ "cxref", no_argument, NULL, 'x' },
{ "defines", no_argument, NULL, 'd' },
{ "declarations", no_argument, &declarations, TRUE },
{ "no-defines", no_argument, NULL, 'D' },
{ "globals", no_argument, &globals, TRUE },
{ "no-globals", no_argument, &globals, FALSE },
{ "help", no_argument, NULL, 'h' },
{ "help", no_argument, NULL, 'H' },
{ "ignore-indentation", no_argument, NULL, 'I' },
{ "include", required_argument, NULL, 'i' },
{ "language", required_argument, NULL, 'l' },
{ "members", no_argument, &members, TRUE },
{ "no-members", no_argument, &members, FALSE },
{ "no-warn", no_argument, NULL, 'w' },
{ "output", required_argument, NULL, 'o' },
#ifdef ETAGS_REGEXPS
{ "regex", required_argument, NULL, 'r' },
{ "no-regex", no_argument, NULL, 'R' },
{ "ignore-case-regex", required_argument, NULL, 'c' },
#endif
{ "typedefs", no_argument, NULL, 't' },
{ "typedefs-and-c++", no_argument, NULL, 'T' },
{ "update", no_argument, NULL, 'u' },
{ "version", no_argument, NULL, 'V' },
{ "vgrind", no_argument, NULL, 'v' },
{ NULL }
};
#endif
#ifdef ETAGS_REGEXPS
typedef struct pattern
{
struct pattern *p_next;
language *language;
char *regex;
struct re_pattern_buffer *pattern;
struct re_registers regs;
char *name_pattern;
bool error_signaled;
} pattern;
pattern *p_head = NULL;
#define CHAR_SET_SIZE 256
char lc_trans[CHAR_SET_SIZE];
#endif
compressor compressors[] =
{
{ "z", "gzip -d -c"},
{ "Z", "gzip -d -c"},
{ "gz", "gzip -d -c"},
{ "GZ", "gzip -d -c"},
{ "bz2", "bzip2 -d -c" },
{ NULL }
};
language *forced_lang = NULL;
char *Ada_suffixes [] =
{ "ads", "adb", "ada", NULL };
char *Asm_suffixes [] = { "a",
"asm",
"def",
"inc",
"ins",
"s", "sa",
"S",
"src",
NULL
};
char *default_C_suffixes [] =
{ "c", "h", NULL };
char *Cplusplus_suffixes [] =
{ "C", "c++", "cc", "cpp", "cxx", "H", "h++", "hh", "hpp", "hxx",
"M",
"pdb",
NULL };
char *Cjava_suffixes [] =
{ "java", NULL };
char *Cobol_suffixes [] =
{ "COB", "cob", 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", "lsp", "ml", NULL };
char *Makefile_filenames [] =
{ "Makefile", "makefile", "GNUMakefile", "Makefile.in", "Makefile.am", NULL};
char *Pascal_suffixes [] =
{ "p", "pas", NULL };
char *Perl_suffixes [] =
{ "pl", "pm", NULL };
char *Perl_interpreters [] =
{ "perl", "@PERL@", NULL };
char *plain_C_suffixes [] =
{ "lm",
"m",
"pc",
NULL };
char *Postscript_suffixes [] =
{ "ps", "psw", NULL };
char *Prolog_suffixes [] =
{ "prolog", NULL };
char *Python_suffixes [] =
{ "py", NULL };
char *Scheme_suffixes [] =
{ "oak", "sch", "scheme", "SCM", "scm", "SM", "sm", "ss", "t", NULL };
char *TeX_suffixes [] =
{ "bib", "clo", "cls", "ltx", "sty", "TeX", "tex", NULL };
char *Texinfo_suffixes [] =
{ "texi", "texinfo", "txi", NULL };
char *Yacc_suffixes [] =
{ "y", "y++", "ym", "yxx", "yy", NULL };
language lang_names [] =
{
{ "ada", Ada_funcs, NULL, Ada_suffixes, NULL },
{ "asm", Asm_labels, NULL, Asm_suffixes, NULL },
{ "c", default_C_entries, NULL, default_C_suffixes, NULL },
{ "c++", Cplusplus_entries, NULL, Cplusplus_suffixes, NULL },
{ "c*", Cstar_entries, NULL, Cstar_suffixes, NULL },
{ "cobol", Cobol_paragraphs, NULL, Cobol_suffixes, NULL },
{ "erlang", Erlang_functions, NULL, Erlang_suffixes, NULL },
{ "fortran", Fortran_functions, NULL, Fortran_suffixes, NULL },
{ "java", Cjava_entries, NULL, Cjava_suffixes, NULL },
{ "lisp", Lisp_functions, NULL, Lisp_suffixes, NULL },
{ "makefile", Makefile_targets, Makefile_filenames, NULL, NULL },
{ "pascal", Pascal_functions, NULL, Pascal_suffixes, NULL },
{ "perl", Perl_functions, NULL, Perl_suffixes, Perl_interpreters },
{ "postscript", Postscript_functions, NULL, Postscript_suffixes, NULL },
{ "proc", plain_C_entries, NULL, plain_C_suffixes, NULL },
{ "prolog", Prolog_functions, NULL, Prolog_suffixes, NULL },
{ "python", Python_functions, NULL, Python_suffixes, NULL },
{ "scheme", Scheme_functions, NULL, Scheme_suffixes, NULL },
{ "tex", TeX_commands, NULL, TeX_suffixes, NULL },
{ "texinfo", Texinfo_nodes, NULL, Texinfo_suffixes, NULL },
{ "yacc", Yacc_entries, NULL, Yacc_suffixes, NULL },
{ "auto", NULL },
{ "none", just_read_file },
{ NULL, NULL }
};
static void
print_language_names ()
{
language *lang;
char **name, **ext;
puts ("\nThese are the currently supported languages, along with the\n\
default file names and dot suffixes:");
for (lang = lang_names; lang->name != NULL; lang++)
{
printf (" %-*s", 10, lang->name);
if (lang->filenames != NULL)
for (name = lang->filenames; *name != NULL; name++)
printf (" %s", *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.\n\
When parsing any C file, a \"class\" keyword switches to C++.\n\
Compressed files are supported using gzip and bzip2.");
}
#ifndef EMACS_NAME
# define EMACS_NAME "GNU Emacs"
#endif
#ifndef VERSION
# define VERSION "21"
#endif
static void
print_version ()
{
printf ("%s (%s %s)\n", (CTAGS) ? "ctags" : "etags", EMACS_NAME, VERSION);
puts ("Copyright (C) 1999 Free Software Foundation, Inc. and Ken Arnold");
puts ("This program is distributed under the same terms as Emacs");
exit (GOOD);
}
static void
print_help ()
{
printf ("Usage: %s [options] [[regex-option ...] file-name] ...\n\
\n\
These are the options accepted by %s.\n", progname, progname);
#ifdef LONG_OPTIONS
puts ("You may use unambiguous abbreviations for the long option names.");
#else
puts ("Long option names do not work with this executable, as it is not\n\
linked with GNU getopt.");
#endif
puts ("A - as file name means read names from stdin (one per line).");
if (!CTAGS)
printf (" Absolute names are stored in the output file as they are.\n\
Relative ones are stored relative to the output file's directory.");
puts ("\n");
puts ("-a, --append\n\
Append tag entries to existing tags file.");
puts ("--packages-only\n\
For Ada files, only generate tags for packages .");
if (CTAGS)
puts ("-B, --backward-search\n\
Write the search commands for the tag entries using '?', the\n\
backward-search command instead of '/', the forward-search command.");
puts ("--declarations\n\
In C and derived languages, create tags for function declarations,");
if (CTAGS)
puts ("\tand create tags for extern variables if --globals is used.");
else
puts
("\tand create tags for extern variables unless --no-globals is used.");
if (CTAGS)
puts ("-d, --defines\n\
Create tag entries for C #define constants and enum constants, too.");
else
puts ("-D, --no-defines\n\
Don't create tag entries for C #define constants and enum constants.\n\
This makes the tags file smaller.");
if (!CTAGS)
{
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.");
}
if (CTAGS)
puts ("--globals\n\
Create tag entries for global variables in some languages.");
else
puts ("--no-globals\n\
Do not create tag entries for global variables in some\n\
languages. This makes the tags file smaller.");
puts ("--members\n\
Create tag entries for member variables in C and derived languages.");
#ifdef ETAGS_REGEXPS
puts ("-r /REGEXP/, --regex=/REGEXP/ or --regex=@regexfile\n\
Make a tag for each line matching pattern REGEXP in the following\n\
files. {LANGUAGE}/REGEXP/ uses REGEXP for LANGUAGE files only.\n\
regexfile is a file containing one REGEXP per line.\n\
REGEXP is anchored (as if preceded by ^).\n\
The form /REGEXP/NAME/ creates a named tag.\n\
For example Tcl named tags can be created with:\n\
--regex=\"/proc[ \\t]+\\([^ \\t]+\\)/\\1/.\"");
puts ("-c /REGEXP/, --ignore-case-regex=/REGEXP/ or --ignore-case-regex=@regexfile\n\
Like -r, --regex but ignore case when matching expressions.");
puts ("-R, --no-regex\n\
Don't create tags from regexps for the following files.");
#endif
puts ("-o FILE, --output=FILE\n\
Write the tags to FILE.");
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\
definition in C and C++.");
if (CTAGS)
{
puts ("-t, --typedefs\n\
Generate tag entries for C and Ada typedefs.");
puts ("-T, --typedefs-and-c++\n\
Generate tag entries for C typedefs, C struct/enum/union tags,\n\
and C++ member functions.");
puts ("-u, --update\n\
Update the tag entries for the given files, leaving tag\n\
entries for other files in place. Currently, this is\n\
implemented by deleting the existing entries for the given\n\
files and then rewriting the new entries at the end of the\n\
tags file. It is often faster to simply rebuild the entire\n\
tag file than to use this.");
puts ("-v, --vgrind\n\
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 ("-V, --version\n\
Print the version of the program.\n\
-h, --help\n\
Print this help message.");
print_language_names ();
puts ("");
puts ("Report bugs to bug-gnu-emacs@gnu.org");
exit (GOOD);
}
enum argument_type
{
at_language,
at_regexp,
at_filename,
at_icregexp
};
typedef struct
{
enum argument_type arg_type;
char *what;
language *lang;
} argument;
#ifdef VMS
#define EOS '\0'
#define MAX_FILE_SPEC_LEN 255
typedef struct {
short curlen;
char body[MAX_FILE_SPEC_LEN + 1];
} vspec;
#include <rmsdef.h>
#include <descrip.h>
#define OUTSIZE MAX_FILE_SPEC_LEN
static 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 bool 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;
}
static char *
gfnames (arg, p_error)
char *arg;
bool *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
system (cmd)
char *cmd;
{
error ("%s", "system() function not implemented under VMS");
}
#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
int
main (argc, argv)
int argc;
char *argv[];
{
int i;
unsigned int nincluded_files;
char **included_files;
char *this_file;
argument *argbuffer;
int current_arg, file_count;
linebuffer filename_lb;
#ifdef VMS
bool got_err;
#endif
#ifdef DOS_NT
_fmode = O_BINARY;
#endif
progname = argv[0];
nincluded_files = 0;
included_files = xnew (argc, char *);
current_arg = 0;
file_count = 0;
argbuffer = xnew (argc, argument);
#ifdef ETAGS_REGEXPS
re_set_syntax (RE_SYNTAX_EMACS | RE_INTERVALS);
for (i = 0; i < CHAR_SET_SIZE; i++)
lc_trans[i] = lowcase (i);
#endif
if (!CTAGS)
{
typedefs = typedefs_or_cplusplus = constantypedefs = TRUE;
globals = TRUE;
declarations = FALSE;
members = FALSE;
}
while (1)
{
int opt;
char *optstring;
#ifdef ETAGS_REGEXPS
optstring = "-aCdDf:Il:o:r:c:RStTi:BuvxwVhH";
#else
optstring = "-aCdDf:Il:o:StTi:BuvxwVhH";
#endif
#ifndef LONG_OPTIONS
optstring = optstring + 1;
#endif
opt = getopt_long (argc, argv, optstring, longopts, 0);
if (opt == EOF)
break;
switch (opt)
{
case 0:
break;
case 1:
argbuffer[current_arg].arg_type = at_filename;
argbuffer[current_arg].what = optarg;
++current_arg;
++file_count;
break;
case 'a': append_to_tagfile = TRUE; break;
case 'C': cplusplus = TRUE; break;
case 'd': constantypedefs = TRUE; break;
case 'D': constantypedefs = FALSE; break;
case 'f':
case 'o':
if (tagfile)
{
error ("-o option may only be given once.", (char *)NULL);
suggest_asking_for_help ();
}
tagfile = optarg;
break;
case 'I':
case 'S':
noindentypedefs = TRUE;
break;
case 'l':
{
language *lang = get_language_from_langname (optarg);
if (lang != NULL)
{
argbuffer[current_arg].lang = lang;
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;
case 'c':
argbuffer[current_arg].arg_type = at_icregexp;
argbuffer[current_arg].what = optarg;
++current_arg;
break;
#endif
case 'V':
print_version ();
break;
case 'h':
case 'H':
print_help ();
break;
case 't':
typedefs = TRUE;
break;
case 'T':
typedefs = typedefs_or_cplusplus = TRUE;
break;
#if (!CTAGS)
case 'i':
included_files[nincluded_files++] = optarg;
break;
#else
case 'B': searchar = '?'; break;
case 'u': update = TRUE; break;
case 'v': vgrind_style = TRUE;
case 'x': cxref_style = TRUE; break;
case 'w': no_warnings = TRUE; break;
#endif
default:
suggest_asking_for_help ();
}
}
for (; optind < argc; ++optind)
{
argbuffer[current_arg].arg_type = at_filename;
argbuffer[current_arg].what = argv[optind];
++current_arg;
++file_count;
}
if (nincluded_files == 0 && file_count == 0)
{
error ("no input files specified.", (char *)NULL);
suggest_asking_for_help ();
}
if (tagfile == NULL)
tagfile = CTAGS ? "tags" : "TAGS";
cwd = etags_getcwd ();
if (cwd[strlen (cwd) - 1] != '/')
{
char *oldcwd = cwd;
cwd = concat (oldcwd, "/", "");
free (oldcwd);
}
if (streq (tagfile, "-"))
tagfiledir = cwd;
else
tagfiledir = absolute_dirname (tagfile, cwd);
init ();
initbuffer (&lb);
initbuffer (&filename_lb);
if (!CTAGS)
{
if (streq (tagfile, "-"))
{
tagf = stdout;
#ifdef DOS_NT
if (!isatty (fileno (stdout)))
setmode (fileno (stdout), O_BINARY);
#endif
}
else
tagf = fopen (tagfile, append_to_tagfile ? "a" : "w");
if (tagf == NULL)
pfatal (tagfile);
}
for (i = 0; i < current_arg; ++i)
{
switch (argbuffer[i].arg_type)
{
case at_language:
forced_lang = argbuffer[i].lang;
break;
#ifdef ETAGS_REGEXPS
case at_regexp:
analyse_regex (argbuffer[i].what, FALSE);
break;
case at_icregexp:
analyse_regex (argbuffer[i].what, TRUE);
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
this_file = argbuffer[i].what;
#endif
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;
}
}
#ifdef ETAGS_REGEXPS
free_patterns ();
#endif
if (!CTAGS)
{
while (nincluded_files-- > 0)
fprintf (tagf, "\f\n%s,include\n", *included_files++);
fclose (tagf);
exit (GOOD);
}
if (cxref_style)
{
put_entries (head);
free_tree (head);
head = NULL;
exit (GOOD);
}
if (update)
{
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, argbuffer[i].what, tagfile);
if (system (cmd) != GOOD)
fatal ("failed to execute shell command", (char *)NULL);
}
append_to_tagfile = TRUE;
}
tagf = fopen (tagfile, append_to_tagfile ? "a" : "w");
if (tagf == NULL)
pfatal (tagfile);
put_entries (head);
free_tree (head);
head = NULL;
fclose (tagf);
if (update)
{
char cmd[BUFSIZ];
sprintf (cmd, "sort %s -o %s", tagfile, tagfile);
exit (system (cmd));
}
return GOOD;
}
static compressor *
get_compressor_from_suffix (file, extptr)
char *file;
char **extptr;
{
compressor *compr;
char *slash, *suffix;
slash = etags_strrchr (file, '/');
suffix = etags_strrchr (file, '.');
if (suffix == NULL || suffix < slash)
return NULL;
if (extptr != NULL)
*extptr = suffix;
suffix += 1;
do
{
for (compr = compressors; compr->suffix != NULL; compr++)
if (streq (compr->suffix, suffix))
return compr;
if (!MSDOS)
break;
if (extptr != NULL)
*extptr = ++suffix;
} while (*suffix != '\0');
return NULL;
}
static language *
get_language_from_langname (name)
char *name;
{
language *lang;
if (name == NULL)
error ("empty language name", (char *)NULL);
else
{
for (lang = lang_names; lang->name != NULL; lang++)
if (streq (name, lang->name))
return lang;
error ("unknown language \"%s\"", name);
}
return NULL;
}
static language *
get_language_from_interpreter (interpreter)
char *interpreter;
{
language *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;
return NULL;
}
static language *
get_language_from_filename (file)
char *file;
{
language *lang;
char **name, **ext, *suffix;
for (lang = lang_names; lang->name != NULL; lang++)
if (lang->filenames != NULL)
for (name = lang->filenames; *name != NULL; name++)
if (streq (*name, file))
return lang;
suffix = etags_strrchr (file, '.');
if (suffix == NULL)
return NULL;
suffix += 1;
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;
return NULL;
}
static void
process_file (file)
char *file;
{
struct stat stat_buf;
FILE *inf;
compressor *compr;
char *compressed_name, *uncompressed_name;
char *ext, *real_name;
canonicalize_filename (file);
if (streq (file, tagfile) && !streq (tagfile, "-"))
{
error ("skipping inclusion of %s in self.", file);
return;
}
if ((compr = get_compressor_from_suffix (file, &ext)) == NULL)
{
compressed_name = NULL;
real_name = uncompressed_name = savestr (file);
}
else
{
real_name = compressed_name = savestr (file);
uncompressed_name = savenstr (file, ext - file);
}
{
typedef struct processed_file
{
char *filename;
struct processed_file *next;
} processed_file;
static processed_file *pf_head = NULL;
register processed_file *fnp;
for (fnp = pf_head; fnp != NULL; fnp = fnp->next)
if (streq (uncompressed_name, fnp->filename))
goto exit;
fnp = pf_head;
pf_head = xnew (1, struct processed_file);
pf_head->filename = savestr (uncompressed_name);
pf_head->next = fnp;
}
if (stat (real_name, &stat_buf) != 0)
{
real_name = NULL;
if (compressed_name != NULL)
{
if (stat (uncompressed_name, &stat_buf) == 0)
real_name = uncompressed_name;
}
else
{
for (compr = compressors; compr->suffix != NULL; compr++)
{
compressed_name = concat (file, ".", compr->suffix);
if (stat (compressed_name, &stat_buf) != 0)
{
if (MSDOS)
{
char *suf = compressed_name + strlen (file);
size_t suflen = strlen (compr->suffix) + 1;
for ( ; suf[1]; suf++, suflen--)
{
memmove (suf, suf + 1, suflen);
if (stat (compressed_name, &stat_buf) == 0)
{
real_name = compressed_name;
break;
}
}
if (real_name != NULL)
break;
}
free (compressed_name);
compressed_name = NULL;
}
else
{
real_name = compressed_name;
break;
}
}
}
if (real_name == NULL)
{
perror (file);
goto exit;
}
}
if (!S_ISREG (stat_buf.st_mode))
{
error ("skipping %s: it is not a regular file.", real_name);
goto exit;
}
if (real_name == compressed_name)
{
char *cmd = concat (compr->command, " ", real_name);
inf = (FILE *) popen (cmd, "r");
free (cmd);
}
else
inf = fopen (real_name, "r");
if (inf == NULL)
{
perror (real_name);
goto exit;
}
find_entries (uncompressed_name, inf);
if (real_name == compressed_name)
pclose (inf);
else
fclose (inf);
if (!CTAGS)
{
char *filename;
if (filename_is_absolute (uncompressed_name))
{
filename = absolute_filename (uncompressed_name, cwd);
}
else
{
filename = relative_filename (uncompressed_name, tagfiledir);
}
fprintf (tagf, "\f\n%s,%d\n", filename, total_size_of_entries (head));
free (filename);
put_entries (head);
free_tree (head);
head = NULL;
}
exit:
if (compressed_name) free(compressed_name);
if (uncompressed_name) free(uncompressed_name);
return;
}
static void
init ()
{
register char *sp;
register int i;
for (i = 0; i < CHARS; i++)
iswhite(i) = notinname(i) = begtoken(i) = intoken(i) = endtoken(i) = FALSE;
for (sp = white; *sp != '\0'; sp++) iswhite (*sp) = TRUE;
for (sp = nonam; *sp != '\0'; sp++) notinname (*sp) = TRUE;
notinname('\0') = notinname('\n');
for (sp = begtk; *sp != '\0'; sp++) begtoken (*sp) = TRUE;
begtoken('\0') = begtoken('\n');
for (sp = midtk; *sp != '\0'; sp++) intoken (*sp) = TRUE;
intoken('\0') = intoken('\n');
for (sp = endtk; *sp != '\0'; sp++) endtoken (*sp) = TRUE;
endtoken('\0') = endtoken('\n');
}
node *last_node = NULL;
static void
find_entries (file, inf)
char *file;
FILE *inf;
{
char *cp;
language *lang;
node *old_last_node;
curfile = savestr (file);
lang = forced_lang;
if (lang != NULL && lang->function != NULL)
{
curlang = lang;
lang->function (inf);
return;
}
lang = get_language_from_filename (file);
if (lang != NULL && lang->function != NULL)
{
curlang = lang;
lang->function (inf);
return;
}
if (readline_internal (&lb, inf) > 0
&& lb.len >= 2
&& lb.buffer[0] == '#'
&& lb.buffer[1] == '!')
{
char *lp;
lp = etags_strrchr (lb.buffer+2, '/');
if (lp != NULL)
lp += 1;
else
lp = skip_spaces (lb.buffer + 2);
cp = skip_non_spaces (lp);
*cp = '\0';
if (strlen (lp) > 0)
{
lang = get_language_from_interpreter (lp);
if (lang != NULL && lang->function != NULL)
{
curlang = lang;
lang->function (inf);
return;
}
}
}
rewind (inf);
old_last_node = last_node;
curlang = get_language_from_langname ("fortran");
Fortran_functions (inf);
if (old_last_node == last_node)
{
rewind (inf);
curlang = get_language_from_langname (cplusplus ? "c++" : "c");
default_C_entries (inf);
}
return;
}
static void
pfnote (name, is_func, linestart, linelen, lno, cno)
char *name;
bool is_func;
char *linestart;
int linelen;
int lno;
long cno;
{
register node *np;
if (CTAGS && name == NULL)
return;
np = xnew (1, node);
if (CTAGS && !cxref_style && streq (name, "main"))
{
register char *fp = etags_strrchr (curfile, '/');
np->name = concat ("M", fp == NULL ? curfile : fp + 1, "");
fp = etags_strrchr (np->name, '.');
if (fp != NULL && fp[1] != '\0' && fp[2] == '\0')
fp[0] = '\0';
}
else
np->name = name;
np->been_warned = FALSE;
np->file = curfile;
np->is_func = is_func;
np->lno = lno;
np->cno = cno ;
np->left = np->right = NULL;
if (CTAGS && !cxref_style)
{
if (strlen (linestart) < 50)
np->pat = concat (linestart, "$", "");
else
np->pat = savenstr (linestart, 50);
}
else
np->pat = savenstr (linestart, linelen);
add_node (np, &head);
}
#define traditional_tag_style TRUE
static void
new_pfnote (name, namelen, is_func, linestart, linelen, lno, cno)
char *name;
int namelen;
bool is_func;
char *linestart;
int linelen;
int lno;
long cno;
{
register char *cp;
bool named;
named = TRUE;
if (!CTAGS)
{
for (cp = name; !notinname (*cp); cp++)
continue;
if (*cp == '\0')
{
cp = linestart + linelen - namelen;
if (notinname (linestart[linelen-1]))
cp -= 1;
if (cp >= linestart
&& (cp == linestart
|| notinname (cp[-1]))
&& strneq (name, cp, namelen))
named = FALSE;
}
}
if (named)
name = savenstr (name, namelen);
else
name = NULL;
pfnote (name, is_func, linestart, linelen, lno, cno);
}
static void
free_tree (np)
register node *np;
{
while (np)
{
register node *node_right = np->right;
free_tree (np->left);
if (np->name != NULL)
free (np->name);
free (np->pat);
free (np);
np = node_right;
}
}
static void
add_node (np, cur_node_p)
node *np, **cur_node_p;
{
register int dif;
register node *cur_node = *cur_node_p;
if (cur_node == NULL)
{
*cur_node_p = np;
last_node = np;
return;
}
if (!CTAGS)
{
if (last_node == NULL)
fatal ("internal error in add_node", (char *)NULL);
last_node->right = np;
last_node = np;
}
else
{
dif = strcmp (np->name, cur_node->name);
if (!dif)
{
if (streq (np->file, cur_node->file))
{
if (!no_warnings)
{
fprintf (stderr, "Duplicate entry in file %s, line %d: %s\n",
np->file, lineno, np->name);
fprintf (stderr, "Second entry ignored\n");
}
}
else if (!cur_node->been_warned && !no_warnings)
{
fprintf
(stderr,
"Duplicate entry in files %s and %s: %s (Warning only)\n",
np->file, cur_node->file, np->name);
cur_node->been_warned = TRUE;
}
return;
}
add_node (np, dif < 0 ? &cur_node->left : &cur_node->right);
}
}
static void
put_entries (np)
register node *np;
{
register char *sp;
if (np == NULL)
return;
put_entries (np->left);
if (!CTAGS)
{
if (np->name != NULL)
fprintf (tagf, "%s\177%s\001%d,%ld\n",
np->pat, np->name, np->lno, np->cno);
else
fprintf (tagf, "%s\177%d,%ld\n",
np->pat, np->lno, np->cno);
}
else
{
if (np->name == NULL)
error ("internal error: NULL name in ctags mode.", (char *)NULL);
if (cxref_style)
{
if (vgrind_style)
fprintf (stdout, "%s %s %d\n",
np->name, np->file, (np->lno + 63) / 64);
else
fprintf (stdout, "%-16s %3d %-16s %s\n",
np->name, np->lno, np->file, np->pat);
}
else
{
fprintf (tagf, "%s\t%s\t", np->name, np->file);
if (np->is_func)
{
putc (searchar, tagf);
putc ('^', tagf);
for (sp = np->pat; *sp; sp++)
{
if (*sp == '\\' || *sp == searchar)
putc ('\\', tagf);
putc (*sp, tagf);
}
putc (searchar, tagf);
}
else
{
fprintf (tagf, "%d", np->lno);
}
putc ('\n', tagf);
}
}
put_entries (np->right);
}
static int
number_len (num)
long num;
{
int len = 1;
while ((num /= 10) > 0)
len += 1;
return len;
}
static int
total_size_of_entries (np)
register node *np;
{
register int total;
if (np == NULL)
return 0;
for (total = 0; np != NULL; np = np->right)
{
total += total_size_of_entries (np->left);
total += strlen (np->pat) + 1;
total += number_len ((long) np->lno) + 1 + number_len (np->cno) + 1;
if (np->name != NULL)
total += 1 + strlen (np->name);
}
return total;
}
#define C_EXT 0x00fff
#define C_PLAIN 0x00000
#define C_PLPL 0x00001
#define C_STAR 0x00003
#define C_JAVA 0x00005
#define C_AUTO 0x01000
#define YACC 0x10000
enum sym_type
{
st_none,
st_C_objprot, st_C_objimpl, st_C_objend,
st_C_gnumacro,
st_C_ignore,
st_C_javastruct,
st_C_operator,
st_C_class, st_C_template,
st_C_struct, st_C_extern, st_C_enum, st_C_define, st_C_typedef, st_C_typespec
};
static unsigned int hash P_((const char *, unsigned int));
static struct C_stab_entry * in_word_set P_((const char *, unsigned int));
static enum sym_type C_symtype P_((char *, int, int));
struct C_stab_entry { char *name; int c_ext; enum sym_type type; };
#define TOTAL_KEYWORDS 47
#define MIN_WORD_LENGTH 2
#define MAX_WORD_LENGTH 15
#define MIN_HASH_VALUE 18
#define MAX_HASH_VALUE 138
#ifdef __GNUC__
__inline
#endif
static unsigned int
hash (str, len)
register const char *str;
register unsigned int len;
{
static unsigned char asso_values[] =
{
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 63, 139, 139, 139, 33, 44,
62, 139, 139, 139, 139, 139, 139, 139, 139, 139,
42, 139, 139, 12, 32, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 34, 59, 37,
24, 58, 33, 3, 139, 16, 139, 139, 42, 60,
18, 11, 39, 139, 23, 57, 4, 63, 6, 20,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139, 139, 139, 139, 139,
139, 139, 139, 139, 139, 139
};
register int hval = len;
switch (hval)
{
default:
case 3:
hval += asso_values[(unsigned char)str[2]];
case 2:
case 1:
hval += asso_values[(unsigned char)str[0]];
break;
}
return hval;
}
#ifdef __GNUC__
__inline
#endif
static struct C_stab_entry *
in_word_set (str, len)
register const char *str;
register unsigned int len;
{
static struct C_stab_entry wordlist[] =
{
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{"if", 0, st_C_ignore},
{""}, {""}, {""}, {""},
{"int", 0, st_C_typespec},
{""}, {""},
{"void", 0, st_C_typespec},
{""}, {""},
{"interface", C_JAVA, st_C_struct},
{""},
{"SYSCALL", 0, st_C_gnumacro},
{""},
{"return", 0, st_C_ignore},
{""}, {""}, {""}, {""}, {""}, {""}, {""},
{"while", 0, st_C_ignore},
{"auto", 0, st_C_typespec},
{""}, {""}, {""}, {""}, {""}, {""},
{"float", 0, st_C_typespec},
{"typedef", 0, st_C_typedef},
{"typename", C_PLPL, st_C_typespec},
{""}, {""}, {""},
{"friend", C_PLPL, st_C_ignore},
{"volatile", 0, st_C_typespec},
{""}, {""},
{"for", 0, st_C_ignore},
{"const", 0, st_C_typespec},
{"import", C_JAVA, st_C_ignore},
{""},
{"define", 0, st_C_define},
{"long", 0, st_C_typespec},
{"implements", C_JAVA, st_C_javastruct},
{"signed", 0, st_C_typespec},
{""},
{"extern", 0, st_C_extern},
{"extends", C_JAVA, st_C_javastruct},
{""},
{"mutable", C_PLPL, st_C_typespec},
{"template", 0, st_C_template},
{"short", 0, st_C_typespec},
{"bool", C_PLPL, st_C_typespec},
{"char", 0, st_C_typespec},
{"class", 0, st_C_class},
{"operator", C_PLPL, st_C_operator},
{""},
{"switch", 0, st_C_ignore},
{""},
{"ENTRY", 0, st_C_gnumacro},
{""},
{"package", C_JAVA, st_C_ignore},
{"union", 0, st_C_struct},
{"@end", 0, st_C_objend},
{"struct", 0, st_C_struct},
{"namespace", C_PLPL, st_C_struct},
{""}, {""},
{"domain", C_STAR, st_C_struct},
{"@interface", 0, st_C_objprot},
{"PSEUDO", 0, st_C_gnumacro},
{"double", 0, st_C_typespec},
{""},
{"@protocol", 0, st_C_objprot},
{""},
{"static", 0, st_C_typespec},
{""}, {""},
{"DEFUN", 0, st_C_gnumacro},
{""}, {""}, {""}, {""},
{"explicit", C_PLPL, st_C_typespec},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{""},
{"enum", 0, st_C_enum},
{""}, {""},
{"unsigned", 0, st_C_typespec},
{""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
{"@implementation",0, st_C_objimpl}
};
if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
{
register int key = hash (str, len);
if (key <= MAX_HASH_VALUE && key >= 0)
{
register const char *s = wordlist[key].name;
if (*str == *s && !strncmp (str + 1, s + 1, len - 1))
return &wordlist[key];
}
}
return 0;
}
static enum sym_type
C_symtype (str, len, c_ext)
char *str;
int len;
int c_ext;
{
register struct C_stab_entry *se = in_word_set (str, len);
if (se == NULL || (se->c_ext && !(c_ext & se->c_ext)))
return st_none;
return se->type;
}
enum
{
fvnone,
fdefunkey,
fdefunname,
foperator,
fvnameseen,
fstartlist,
finlist,
flistseen,
fignore,
vignore
} fvdef;
bool fvextern;
enum
{
tnone,
tkeyseen,
ttypeseen,
tinbody,
tend,
tignore
} typdef;
enum
{
snone,
skeyseen,
stagseen,
sintemplate,
scolonseen
} structdef;
char *objtag = "<uninited>";
enum
{
dnone,
dsharpseen,
ddefineseen,
dignorerest
} definedef;
enum
{
onone,
oprotocol,
oimplementation,
otagseen,
oparenseen,
ocatseen,
oinbody,
omethodsign,
omethodtag,
omethodcolon,
omethodparm,
oignore
} objdef;
struct tok
{
bool valid;
bool named;
int offset;
int length;
int lineno;
long linepos;
char *line;
} token;
linebuffer token_name;
static void pushclass_above P_((int, char *, int));
static void popclass_above P_((int));
static void write_classname P_((linebuffer *, char *qualifier));
struct {
char **cname;
int *cblev;
int nl;
int size;
} cstack;
#define nestlev (cstack.nl)
#define instruct (structdef == snone && nestlev > 0 \
&& cblev == cstack.cblev[nestlev-1] + 1)
static void
pushclass_above (cblev, str, len)
int cblev;
char *str;
int len;
{
int nl;
popclass_above (cblev);
nl = cstack.nl;
if (nl >= cstack.size)
{
int size = cstack.size *= 2;
xrnew (cstack.cname, size, char *);
xrnew (cstack.cblev, size, int);
}
assert (nl == 0 || cstack.cblev[nl-1] < cblev);
cstack.cname[nl] = (str == NULL) ? NULL : savenstr (str, len);
cstack.cblev[nl] = cblev;
cstack.nl = nl + 1;
}
static void
popclass_above (cblev)
int cblev;
{
int nl;
for (nl = cstack.nl - 1;
nl >= 0 && cstack.cblev[nl] >= cblev;
nl--)
{
if (cstack.cname[nl] != NULL)
free (cstack.cname[nl]);
cstack.nl = nl;
}
}
static void
write_classname (cn, qualifier)
linebuffer *cn;
char *qualifier;
{
int i, len;
int qlen = strlen (qualifier);
if (cstack.nl == 0 || cstack.cname[0] == NULL)
{
len = 0;
cn->len = 0;
cn->buffer[0] = '\0';
}
else
{
len = strlen (cstack.cname[0]);
linebuffer_setlen (cn, len);
strcpy (cn->buffer, cstack.cname[0]);
}
for (i = 1; i < cstack.nl; i++)
{
char *s;
int slen;
s = cstack.cname[i];
if (s == NULL)
continue;
slen = strlen (s);
len += slen + qlen;
linebuffer_setlen (cn, len);
strncat (cn->buffer, qualifier, qlen);
strncat (cn->buffer, s, slen);
}
}
static bool consider_token P_((char *, int, int, int *, int, int, bool *));
static void make_C_tag P_((bool));
static bool
consider_token (str, len, c, c_extp, cblev, parlev, is_func_or_var)
register char *str;
register int len;
register int c;
int *c_extp;
int cblev;
int parlev;
bool *is_func_or_var;
{
static enum sym_type structtype;
static int structcblev;
static enum sym_type toktype;
toktype = C_symtype (str, len, *c_extp);
switch (definedef)
{
case dnone:
if (toktype == st_C_gnumacro)
{
fvdef = fdefunkey;
return FALSE;
}
break;
case dsharpseen:
if (toktype == st_C_define)
{
definedef = ddefineseen;
}
else
{
definedef = dignorerest;
}
return FALSE;
case ddefineseen:
definedef = dignorerest;
*is_func_or_var = (c == '(');
if (!*is_func_or_var && !constantypedefs)
return FALSE;
else
return TRUE;
case dignorerest:
return FALSE;
default:
error ("internal error: definedef value.", (char *)NULL);
}
switch (typdef)
{
case tnone:
if (toktype == st_C_typedef)
{
if (typedefs)
typdef = tkeyseen;
fvextern = FALSE;
fvdef = fvnone;
return FALSE;
}
break;
case tkeyseen:
switch (toktype)
{
case st_none:
case st_C_typespec:
case st_C_class:
case st_C_struct:
case st_C_enum:
typdef = ttypeseen;
break;
}
break;
case ttypeseen:
if (structdef == snone && fvdef == fvnone)
{
fvdef = fvnameseen;
return TRUE;
}
break;
case tend:
switch (toktype)
{
case st_C_typespec:
case st_C_class:
case st_C_struct:
case st_C_enum:
return FALSE;
}
return TRUE;
}
switch (toktype)
{
case st_C_javastruct:
if (structdef == stagseen)
structdef = scolonseen;
return FALSE;
case st_C_template:
case st_C_class:
if (cblev == 0
&& (*c_extp & C_AUTO)
&& definedef == dnone && structdef == snone
&& typdef == tnone && fvdef == fvnone)
*c_extp = (*c_extp | C_PLPL) & ~C_AUTO;
if (toktype == st_C_template)
break;
case st_C_struct:
case st_C_enum:
if (parlev == 0
&& fvdef != vignore
&& (typdef == tkeyseen
|| (typedefs_or_cplusplus && structdef == snone)))
{
structdef = skeyseen;
structtype = toktype;
structcblev = cblev;
}
return FALSE;
}
if (structdef == skeyseen)
{
structdef = stagseen;
return TRUE;
}
if (typdef != tnone)
definedef = dnone;
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:
objtag = savenstr (str, len);
objdef = oinbody;
return FALSE;
case oprotocol:
objtag = savenstr (str, len);
objdef = otagseen;
*is_func_or_var = TRUE;
return TRUE;
case oparenseen:
objdef = ocatseen;
*is_func_or_var = TRUE;
return TRUE;
case oinbody:
break;
case omethodsign:
if (parlev == 0)
{
objdef = omethodtag;
linebuffer_setlen (&token_name, len);
strncpy (token_name.buffer, str, len);
token_name.buffer[len] = '\0';
return TRUE;
}
return FALSE;
case omethodcolon:
if (parlev == 0)
objdef = omethodparm;
return FALSE;
case omethodparm:
if (parlev == 0)
{
objdef = omethodtag;
linebuffer_setlen (&token_name, token_name.len + len);
strncat (token_name.buffer, str, len);
return TRUE;
}
return FALSE;
case oignore:
if (toktype == st_C_objend)
{
objdef = onone;
}
return FALSE;
}
switch (toktype)
{
case st_C_extern:
fvextern = TRUE;
case st_C_typespec:
if (fvdef != finlist && fvdef != fignore && fvdef != vignore)
fvdef = fvnone;
return FALSE;
case st_C_ignore:
fvextern = FALSE;
fvdef = vignore;
return FALSE;
case st_C_operator:
fvdef = foperator;
*is_func_or_var = TRUE;
return TRUE;
case st_none:
if (constantypedefs
&& structdef == snone
&& structtype == st_C_enum && cblev > structcblev)
return TRUE;
switch (fvdef)
{
case fdefunkey:
if (cblev > 0)
break;
fvdef = fdefunname;
*is_func_or_var = TRUE;
return TRUE;
case fvnone:
if ((strneq (str, "asm", 3) && endtoken (str[3]))
|| (strneq (str, "__asm__", 7) && endtoken (str[7])))
{
fvdef = vignore;
return FALSE;
}
if ((*c_extp & C_PLPL) && strneq (str+len-10, "::operator", 10))
{
fvdef = foperator;
*is_func_or_var = TRUE;
return TRUE;
}
if (cblev > 0 && !instruct)
break;
fvdef = fvnameseen;
*is_func_or_var = TRUE;
return TRUE;
}
break;
}
return FALSE;
}
struct
{
long linepos;
linebuffer lb;
} lbs[2];
#define current_lb_is_new (newndx == curndx)
#define switch_line_buffers() (curndx = 1 - curndx)
#define curlb (lbs[curndx].lb)
#define newlb (lbs[newndx].lb)
#define curlinepos (lbs[curndx].linepos)
#define newlinepos (lbs[newndx].linepos)
#define CNL_SAVE_DEFINEDEF() \
do { \
curlinepos = charno; \
lineno++; \
linecharno = charno; \
charno += readline (&curlb, inf); \
lp = curlb.buffer; \
quotednl = FALSE; \
newndx = curndx; \
} while (0)
#define CNL() \
do { \
CNL_SAVE_DEFINEDEF(); \
if (savetoken.valid) \
{ \
token = savetoken; \
savetoken.valid = FALSE; \
} \
definedef = dnone; \
} while (0)
static void
make_C_tag (isfun)
bool isfun;
{
if (DEBUG || token.valid)
{
if (traditional_tag_style)
{
char *name = NULL;
if (CTAGS || token.named)
name = savestr (token_name.buffer);
if (DEBUG && !token.valid)
{
if (token.named)
name = concat (name, "##invalid##", "");
else
name = savestr ("##invalid##");
}
pfnote (name, isfun, token.line,
token.offset+token.length+1, token.lineno, token.linepos);
}
else
new_pfnote (token_name.buffer, token_name.len, isfun, token.line,
token.offset+token.length+1, token.lineno, token.linepos);
token.valid = FALSE;
}
}
static void
C_entries (c_ext, inf)
int c_ext;
FILE *inf;
{
register char c;
register char *lp;
int curndx, newndx;
register int tokoff;
register int toklen;
char *qualifier;
int qlen;
int cblev;
int parlev;
int typdefcblev;
bool incomm, inquote, inchar, quotednl, midtoken;
bool cplpl, cjava;
bool yacc_rules;
struct tok savetoken;
initbuffer (&token_name);
initbuffer (&lbs[0].lb);
initbuffer (&lbs[1].lb);
if (cstack.size == 0)
{
cstack.size = (DEBUG) ? 1 : 4;
cstack.nl = 0;
cstack.cname = xnew (cstack.size, char *);
cstack.cblev = xnew (cstack.size, int);
}
tokoff = toklen = typdefcblev = 0;
curndx = newndx = 0;
lineno = 0;
charno = 0;
lp = curlb.buffer;
*lp = 0;
fvdef = fvnone; fvextern = FALSE; typdef = tnone;
structdef = snone; definedef = dnone; objdef = onone;
yacc_rules = FALSE;
midtoken = inquote = inchar = incomm = quotednl = FALSE;
token.valid = savetoken.valid = FALSE;
cblev = 0;
parlev = 0;
cplpl = (c_ext & C_PLPL) == C_PLPL;
cjava = (c_ext & C_JAVA) == C_JAVA;
if (cjava)
{ qualifier = "."; qlen = 1; }
else
{ qualifier = "::"; qlen = 2; }
while (!feof (inf))
{
c = *lp++;
if (c == '\\')
{
if (*lp == '\0')
{
quotednl = TRUE;
continue;
}
lp++;
c = ' ';
}
else if (incomm)
{
switch (c)
{
case '*':
if (*lp == '/')
{
c = *lp++;
incomm = FALSE;
}
break;
case '\0':
CNL_SAVE_DEFINEDEF ();
break;
}
continue;
}
else if (inquote)
{
switch (c)
{
case '"':
inquote = FALSE;
break;
case '\0':
CNL_SAVE_DEFINEDEF ();
break;
}
continue;
}
else if (inchar)
{
switch (c)
{
case '\0':
CNL ();
case '\'':
inchar = FALSE;
break;
}
continue;
}
else
switch (c)
{
case '"':
inquote = TRUE;
switch (fvdef)
{
case fdefunkey:
case fstartlist:
case finlist:
case fignore:
case vignore:
break;
default:
fvextern = FALSE;
fvdef = fvnone;
}
continue;
case '\'':
inchar = TRUE;
if (fvdef != finlist && fvdef != fignore && fvdef !=vignore)
{
fvextern = FALSE;
fvdef = fvnone;
}
continue;
case '/':
if (*lp == '*')
{
lp++;
incomm = TRUE;
continue;
}
else if ( *lp == '/')
{
c = '\0';
break;
}
else
break;
case '%':
if ((c_ext & YACC) && *lp == '%')
{
lp++;
definedef = dnone; fvdef = fvnone; fvextern = FALSE;
typdef = tnone; structdef = snone;
midtoken = inquote = inchar = incomm = quotednl = FALSE;
cblev = 0;
yacc_rules = !yacc_rules;
continue;
}
else
break;
case '#':
if (definedef == dnone)
{
char *cp;
bool cpptoken = TRUE;
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;
}
continue;
}
if (typdef != tignore
&& definedef != dignorerest
&& fvdef != finlist
&& structdef != sintemplate
&& (definedef != dnone
|| structdef != scolonseen))
{
if (midtoken)
{
if (endtoken (c))
{
if (c == ':' && cplpl && *lp == ':' && begtoken (lp[1]))
{
lp += 2;
toklen += 2;
c = lp[-1];
goto still_in_token;
}
else
{
bool funorvar = FALSE;
if (yacc_rules
|| consider_token (newlb.buffer + tokoff, toklen, c,
&c_ext, cblev, parlev, &funorvar))
{
if (fvdef == foperator)
{
char *oldlp = lp;
lp = skip_spaces (lp-1);
if (*lp != '\0')
lp += 1;
while (*lp != '\0'
&& !iswhite (*lp) && *lp != '(')
lp += 1;
c = *lp++;
toklen += lp - oldlp;
}
token.named = FALSE;
if ((c_ext & C_EXT)
&& nestlev > 0 && definedef == dnone)
{
write_classname (&token_name, qualifier);
linebuffer_setlen (&token_name,
token_name.len+qlen+toklen);
strcat (token_name.buffer, qualifier);
strncat (token_name.buffer,
newlb.buffer + tokoff, toklen);
token.named = TRUE;
}
else if (objdef == ocatseen)
{
int len = strlen (objtag) + 2 + toklen;
linebuffer_setlen (&token_name, len);
strcpy (token_name.buffer, objtag);
strcat (token_name.buffer, "(");
strncat (token_name.buffer,
newlb.buffer + tokoff, toklen);
strcat (token_name.buffer, ")");
token.named = TRUE;
}
else if (objdef == omethodtag
|| objdef == omethodparm)
{
token.named = TRUE;
}
else if (fvdef == fdefunname)
{
bool defun = (newlb.buffer[tokoff] == 'F');
int off = tokoff;
int len = toklen;
if (defun)
{
off += 1;
len -= 1;
}
len = toklen;
linebuffer_setlen (&token_name, len);
strncpy (token_name.buffer,
newlb.buffer + off, len);
token_name.buffer[len] = '\0';
if (defun)
while (--len >= 0)
if (token_name.buffer[len] == '_')
token_name.buffer[len] = '-';
token.named = defun;
}
else
{
linebuffer_setlen (&token_name, toklen);
strncpy (token_name.buffer,
newlb.buffer + tokoff, toklen);
token_name.buffer[toklen] = '\0';
token.named = (structdef == stagseen
|| typdef == ttypeseen
|| typdef == tend
|| (funorvar
&& definedef == dignorerest)
|| (funorvar
&& definedef == dnone
&& structdef == snone
&& cblev > 0));
}
token.lineno = lineno;
token.offset = tokoff;
token.length = toklen;
token.line = newlb.buffer;
token.linepos = newlinepos;
token.valid = TRUE;
if (definedef == dnone
&& (fvdef == fvnameseen
|| fvdef == foperator
|| structdef == stagseen
|| typdef == tend
|| typdef == ttypeseen
|| objdef != onone))
{
if (current_lb_is_new)
switch_line_buffers ();
}
else if (definedef != dnone
|| fvdef == fdefunname
|| instruct)
make_C_tag (funorvar);
}
midtoken = FALSE;
}
}
else if (intoken (c))
still_in_token:
{
toklen++;
continue;
}
}
else if (begtoken (c))
{
switch (definedef)
{
case dnone:
switch (fvdef)
{
case fstartlist:
fvdef = finlist;
continue;
case flistseen:
make_C_tag (TRUE);
fvdef = fignore;
break;
case fvnameseen:
fvdef = fvnone;
break;
}
if (structdef == stagseen && !cjava)
{
popclass_above (cblev);
structdef = snone;
}
break;
case dsharpseen:
savetoken = token;
}
if (!yacc_rules || lp == newlb.buffer + 1)
{
tokoff = lp - 1 - newlb.buffer;
toklen = 1;
midtoken = TRUE;
}
continue;
}
}
switch (c)
{
case ':':
if (yacc_rules && token.offset == 0 && token.valid)
{
make_C_tag (FALSE);
break;
}
if (definedef != dnone)
break;
switch (objdef)
{
case otagseen:
objdef = oignore;
make_C_tag (TRUE);
break;
case omethodtag:
case omethodparm:
objdef = omethodcolon;
linebuffer_setlen (&token_name, token_name.len + 1);
strcat (token_name.buffer, ":");
break;
}
if (structdef == stagseen)
structdef = scolonseen;
break;
case ';':
if (definedef != dnone)
break;
switch (typdef)
{
case tend:
case ttypeseen:
make_C_tag (FALSE);
typdef = tnone;
fvdef = fvnone;
break;
case tnone:
case tinbody:
case tignore:
switch (fvdef)
{
case fignore:
if (typdef == tignore)
fvdef = fvnone;
break;
case fvnameseen:
if ((globals && cblev == 0 && (!fvextern || declarations))
|| (members && instruct))
make_C_tag (FALSE);
fvextern = FALSE;
fvdef = fvnone;
token.valid = FALSE;
break;
case flistseen:
if ((declarations && typdef == tnone && !instruct)
|| (members && typdef != tignore && instruct))
make_C_tag (TRUE);
default:
fvextern = FALSE;
fvdef = fvnone;
if (declarations
&& structdef == stagseen && (c_ext & C_PLPL))
make_C_tag (FALSE);
else
token.valid = FALSE;
}
default:
if (!instruct)
typdef = tnone;
}
if (structdef == stagseen)
structdef = snone;
break;
case ',':
if (definedef != dnone)
break;
switch (objdef)
{
case omethodtag:
case omethodparm:
make_C_tag (TRUE);
objdef = oinbody;
break;
}
switch (fvdef)
{
case fdefunkey:
case foperator:
case fstartlist:
case finlist:
case fignore:
case vignore:
break;
case fdefunname:
fvdef = fignore;
break;
case fvnameseen:
if ((globals && cblev == 0 && (!fvextern || declarations))
|| (members && instruct))
make_C_tag (FALSE);
break;
case flistseen:
if ((declarations && typdef == tnone && !instruct)
|| (members && typdef != tignore && instruct))
{
make_C_tag (TRUE);
fvdef = fvnameseen;
}
else if (!declarations)
fvdef = fvnone;
token.valid = FALSE;
break;
default:
fvdef = fvnone;
}
if (structdef == stagseen)
structdef = snone;
break;
case '[':
if (definedef != dnone)
break;
if (structdef == stagseen)
structdef = snone;
switch (typdef)
{
case ttypeseen:
case tend:
typdef = tignore;
make_C_tag (FALSE);
break;
case tnone:
case tinbody:
switch (fvdef)
{
case foperator:
case finlist:
case fignore:
case vignore:
break;
case fvnameseen:
if ((members && cblev == 1)
|| (globals && cblev == 0
&& (!fvextern || declarations)))
make_C_tag (FALSE);
default:
fvdef = fvnone;
}
break;
}
break;
case '(':
if (definedef != dnone)
break;
if (objdef == otagseen && parlev == 0)
objdef = oparenseen;
switch (fvdef)
{
case fvnameseen:
if (typdef == ttypeseen
&& *lp != '*'
&& !instruct)
{
make_C_tag (FALSE);
typdef = tignore;
fvdef = fignore;
break;
}
case foperator:
fvdef = fstartlist;
break;
case flistseen:
fvdef = finlist;
break;
}
parlev++;
break;
case ')':
if (definedef != dnone)
break;
if (objdef == ocatseen && parlev == 1)
{
make_C_tag (TRUE);
objdef = oignore;
}
if (--parlev == 0)
{
switch (fvdef)
{
case fstartlist:
case finlist:
fvdef = flistseen;
break;
}
if (!instruct
&& (typdef == tend
|| typdef == ttypeseen))
{
typdef = tignore;
make_C_tag (FALSE);
}
}
else if (parlev < 0)
parlev = 0;
break;
case '{':
if (definedef != dnone)
break;
if (typdef == ttypeseen)
{
typdef = tinbody;
typdefcblev = cblev;
}
switch (fvdef)
{
case flistseen:
make_C_tag (TRUE);
case fignore:
fvdef = fvnone;
break;
case fvnone:
switch (objdef)
{
case otagseen:
make_C_tag (TRUE);
objdef = oignore;
break;
case omethodtag:
case omethodparm:
make_C_tag (TRUE);
objdef = oinbody;
break;
default:
if (cblev == 0 && structdef == snone && nestlev == 0
&& typdef == tnone)
cblev = -1;
}
}
switch (structdef)
{
case skeyseen:
pushclass_above (cblev, NULL, 0);
structdef = snone;
break;
case stagseen:
case scolonseen:
pushclass_above (cblev, token.line+token.offset, token.length);
structdef = snone;
make_C_tag (FALSE);
break;
}
cblev++;
break;
case '*':
if (definedef != dnone)
break;
if (fvdef == fstartlist)
fvdef = fvnone;
break;
case '}':
if (definedef != dnone)
break;
if (!noindentypedefs && lp == newlb.buffer + 1)
{
cblev = 0;
parlev = 0;
}
else if (cblev > 0)
cblev--;
popclass_above (cblev);
structdef = snone;
if (typdef == tinbody && cblev <= typdefcblev)
{
assert (cblev == typdefcblev);
typdef = tend;
}
break;
case '=':
if (definedef != dnone)
break;
switch (fvdef)
{
case foperator:
case finlist:
case fignore:
case vignore:
break;
case fvnameseen:
if ((members && cblev == 1)
|| (globals && cblev == 0 && (!fvextern || declarations)))
make_C_tag (FALSE);
default:
fvdef = vignore;
}
break;
case '<':
if (cplpl && structdef == stagseen)
{
structdef = sintemplate;
break;
}
goto resetfvdef;
case '>':
if (structdef == sintemplate)
{
structdef = stagseen;
break;
}
goto resetfvdef;
case '+':
case '-':
if (objdef == oinbody && cblev == 0)
{
objdef = omethodsign;
break;
}
resetfvdef:
case '#': case '~': case '&': case '%': case '/': case '|':
case '^': case '!': case '.': case '?': case ']':
if (definedef != dnone)
break;
switch (fvdef)
{
case foperator:
case finlist:
case fignore:
case vignore:
break;
default:
fvdef = fvnone;
}
break;
case '\0':
if (objdef == otagseen)
{
make_C_tag (TRUE);
objdef = oignore;
}
if (quotednl)
CNL_SAVE_DEFINEDEF ();
else
CNL ();
break;
}
}
free (token_name.buffer);
free (lbs[0].lb.buffer);
free (lbs[1].lb.buffer);
}
static void
default_C_entries (inf)
FILE *inf;
{
C_entries (cplusplus ? C_PLPL : C_AUTO, inf);
}
static void
plain_C_entries (inf)
FILE *inf;
{
C_entries (0, inf);
}
static void
Cplusplus_entries (inf)
FILE *inf;
{
C_entries (C_PLPL, inf);
}
static void
Cjava_entries (inf)
FILE *inf;
{
C_entries (C_JAVA, inf);
}
static void
Cstar_entries (inf)
FILE *inf;
{
C_entries (C_STAR, inf);
}
static void
Yacc_entries (inf)
FILE *inf;
{
C_entries (YACC, inf);
}
#define LOOP_ON_INPUT_LINES(file_pointer, line_buffer, char_pointer) \
for (lineno = charno = 0; \
!feof (file_pointer) \
&& (lineno++, \
linecharno = charno, \
charno += readline (&line_buffer, file_pointer), \
char_pointer = lb.buffer, \
TRUE); \
)
static void
just_read_file (inf)
FILE *inf;
{
register char *dummy;
LOOP_ON_INPUT_LINES (inf, lb, dummy)
continue;
}
static bool tail P_((char *));
static void takeprec P_((void));
static void getit P_((FILE *));
static bool
tail (cp)
char *cp;
{
register int len = 0;
while (*cp != '\0' && lowcase (*cp) == lowcase (dbp[len]))
cp++, len++;
if (*cp == '\0' && !intoken (dbp[len]))
{
dbp += len;
return TRUE;
}
return FALSE;
}
static void
takeprec ()
{
dbp = skip_spaces (dbp);
if (*dbp != '*')
return;
dbp++;
dbp = skip_spaces (dbp);
if (strneq (dbp, "(*)", 3))
{
dbp += 3;
return;
}
if (!ISDIGIT (*dbp))
{
--dbp;
return;
}
do
dbp++;
while (ISDIGIT (*dbp));
}
static void
getit (inf)
FILE *inf;
{
register char *cp;
dbp = skip_spaces (dbp);
if (*dbp == '\0')
{
lineno++;
linecharno = charno;
charno += readline (&lb, inf);
dbp = lb.buffer;
if (dbp[5] != '&')
return;
dbp += 6;
dbp = skip_spaces (dbp);
}
if (!ISALPHA (*dbp) && *dbp != '_' && *dbp != '$')
return;
for (cp = dbp + 1; *cp != '\0' && intoken (*cp); cp++)
continue;
pfnote (savenstr (dbp, cp-dbp), TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
static void
Fortran_functions (inf)
FILE *inf;
{
LOOP_ON_INPUT_LINES (inf, lb, dbp)
{
if (*dbp == '%')
dbp++;
dbp = skip_spaces (dbp);
if (*dbp == '\0')
continue;
switch (lowcase (*dbp))
{
case 'i':
if (tail ("integer"))
takeprec ();
break;
case 'r':
if (tail ("real"))
takeprec ();
break;
case 'l':
if (tail ("logical"))
takeprec ();
break;
case 'c':
if (tail ("complex") || tail ("character"))
takeprec ();
break;
case 'd':
if (tail ("double"))
{
dbp = skip_spaces (dbp);
if (*dbp == '\0')
continue;
if (tail ("precision"))
break;
continue;
}
break;
}
dbp = skip_spaces (dbp);
if (*dbp == '\0')
continue;
switch (lowcase (*dbp))
{
case 'f':
if (tail ("function"))
getit (inf);
continue;
case 's':
if (tail ("subroutine"))
getit (inf);
continue;
case 'e':
if (tail ("entry"))
getit (inf);
continue;
case 'b':
if (tail ("blockdata") || tail ("block data"))
{
dbp = skip_spaces (dbp);
if (*dbp == '\0')
pfnote (savestr ("blockdata"), TRUE,
lb.buffer, dbp - lb.buffer, lineno, linecharno);
else
getit (inf);
}
continue;
}
}
}
static void adagetit P_((FILE *, char *));
static void
adagetit (inf, name_qualifier)
FILE *inf;
char *name_qualifier;
{
register char *cp;
char *name;
char c;
while (!feof (inf))
{
dbp = skip_spaces (dbp);
if (*dbp == '\0'
|| (dbp[0] == '-' && dbp[1] == '-'))
{
lineno++;
linecharno = charno;
charno += readline (&lb, inf);
dbp = lb.buffer;
}
switch (*dbp)
{
case 'b':
case 'B':
if (tail ("body"))
{
name_qualifier = "/b";
continue;
}
break;
case 't':
case 'T':
if (tail ("type"))
continue;
break;
}
if (*dbp == '"')
{
dbp += 1;
for (cp = dbp; *cp != '\0' && *cp != '"'; cp++)
continue;
}
else
{
dbp = skip_spaces (dbp);
for (cp = dbp;
(*cp != '\0'
&& (ISALPHA (*cp) || ISDIGIT (*cp) || *cp == '_' || *cp == '.'));
cp++)
continue;
if (cp == dbp)
return;
}
c = *cp;
*cp = '\0';
name = concat (dbp, name_qualifier, "");
*cp = c;
pfnote (name, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
if (c == '"')
dbp = cp + 1;
return;
}
}
static void
Ada_funcs (inf)
FILE *inf;
{
bool inquote = FALSE;
LOOP_ON_INPUT_LINES (inf, lb, dbp)
{
while (*dbp != '\0')
{
if (inquote || (*dbp == '"'))
{
dbp = etags_strchr ((inquote) ? dbp : dbp+1, '"');
if (dbp != NULL)
{
inquote = FALSE;
dbp += 1;
continue;
}
else
{
inquote = TRUE;
break;
}
}
if (dbp[0] == '-' && dbp[1] == '-')
break;
if (*dbp == '\'')
{
dbp++ ;
if (*dbp != '\0')
dbp++;
continue;
}
if (!begtoken (*dbp))
{
dbp++;
continue;
}
switch (*dbp)
{
case 'f':
case 'F':
if (!packages_only && tail ("function"))
adagetit (inf, "/f");
else
break;
continue;
case 'p':
case 'P':
if (!packages_only && tail ("procedure"))
adagetit (inf, "/p");
else if (tail ("package"))
adagetit (inf, "/s");
else if (tail ("protected"))
adagetit (inf, "/t");
else
break;
continue;
case 't':
case 'T':
if (!packages_only && tail ("task"))
adagetit (inf, "/k");
else if (typedefs && !packages_only && tail ("type"))
{
adagetit (inf, "/t");
while (*dbp != '\0')
dbp += 1;
}
else
break;
continue;
}
while (!endtoken (*dbp))
dbp++;
}
}
}
static void
Asm_labels (inf)
FILE *inf;
{
register char *cp;
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
if (ISALPHA (*cp) || *cp == '_' || *cp == '.' || *cp == '$')
{
cp++;
while (ISALNUM (*cp) || *cp == '_' || *cp == '.' || *cp == '$')
cp++;
if (*cp == ':' || iswhite (*cp))
{
pfnote (savenstr(lb.buffer, cp-lb.buffer), TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
}
}
}
static void
Perl_functions (inf)
FILE *inf;
{
register char *cp;
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
if (*cp++ == 's'
&& *cp++ == 'u'
&& *cp++ == 'b' && iswhite (*cp++))
{
cp = skip_spaces (cp);
if (*cp != '\0')
{
char *sp = cp;
while (*cp != '\0'
&& !iswhite (*cp) && *cp != '{' && *cp != '(')
cp++;
pfnote (savenstr (sp, cp-sp), TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
}
else if (globals
&& ((cp = lb.buffer,
*cp++ == 'm'
&& *cp++ == 'y')
|| (cp = lb.buffer,
*cp++ == 'l'
&& *cp++ == 'o'
&& *cp++ == 'c'
&& *cp++ == 'a'
&& *cp++ == 'l'))
&& (*cp == '(' || iswhite (*cp)))
{
char *varname = NULL;
cp = skip_spaces (cp);
if (*cp == '$' || *cp == '@' || *cp == '%')
{
char* varstart = ++cp;
while (ISALNUM (*cp) || *cp == '_')
cp++;
varname = savenstr (varstart, cp-varstart);
}
else
{
while (*cp != '\0' && *cp != ';' && *cp != '=' && *cp != ')')
cp++;
}
pfnote ((CTAGS) ? savenstr (lb.buffer, cp-lb.buffer) : varname,
FALSE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
}
}
static void
Python_functions (inf)
FILE *inf;
{
register char *cp;
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
cp = skip_spaces (cp);
if (*cp++ == 'd'
&& *cp++ == 'e'
&& *cp++ == 'f' && iswhite (*cp++))
{
cp = skip_spaces (cp);
while (*cp != '\0' && !iswhite (*cp) && *cp != '(' && *cp != ':')
cp++;
pfnote (NULL, TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
cp = skip_spaces (lb.buffer);
if (*cp++ == 'c'
&& *cp++ == 'l'
&& *cp++ == 'a'
&& *cp++ == 's'
&& *cp++ == 's' && iswhite (*cp++))
{
cp = skip_spaces (cp);
while (*cp != '\0' && !iswhite (*cp) && *cp != '(' && *cp != ':')
cp++;
pfnote (NULL, TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
}
}
static void
Cobol_paragraphs (inf)
FILE *inf;
{
register char *bp, *ep;
LOOP_ON_INPUT_LINES (inf, lb, bp)
{
if (lb.len < 9)
continue;
bp += 8;
if (bp[-1] != ' ' || !ISALNUM (bp[0]))
continue;
for (ep = bp; ISALNUM (*ep) || *ep == '-'; ep++)
continue;
if (*ep++ == '.')
pfnote (savenstr (bp, ep-bp), TRUE,
lb.buffer, ep - lb.buffer + 1, lineno, linecharno);
}
}
static void
Makefile_targets (inf)
FILE *inf;
{
register char *bp;
LOOP_ON_INPUT_LINES (inf, lb, bp)
{
if (*bp == '\t' || *bp == '#')
continue;
while (*bp != '\0' && *bp != '=' && *bp != ':')
bp++;
if (*bp == ':')
pfnote (savenstr (lb.buffer, bp - lb.buffer), TRUE,
lb.buffer, bp - lb.buffer + 1, lineno, linecharno);
}
}
static void
Pascal_functions (inf)
FILE *inf;
{
linebuffer tline;
long save_lcno;
int save_lineno, save_len;
char c, *cp, *namebuf;
bool
incomment,
inquote,
get_tagname,
found_tag,
inparms,
verify_tag;
save_lcno = save_lineno = save_len = 0;
namebuf = NULL;
lineno = 0;
charno = 0;
dbp = lb.buffer;
*dbp = '\0';
initbuffer (&tline);
incomment = inquote = FALSE;
found_tag = FALSE;
get_tagname = FALSE;
inparms = FALSE;
verify_tag = FALSE;
while (!feof (inf))
{
c = *dbp++;
if (c == '\0')
{
lineno++;
linecharno = charno;
charno += readline (&lb, inf);
dbp = lb.buffer;
if (*dbp == '\0')
continue;
if (!((found_tag && verify_tag)
|| get_tagname))
c = *dbp++;
}
if (incomment)
{
if (c == '}')
incomment = FALSE;
else if (c == '*' && *dbp == ')')
{
dbp++;
incomment = FALSE;
}
continue;
}
else if (inquote)
{
if (c == '\'')
inquote = FALSE;
continue;
}
else
switch (c)
{
case '\'':
inquote = TRUE;
continue;
case '{':
incomment = TRUE;
continue;
case '(':
if (*dbp == '*')
{
incomment = TRUE;
dbp++;
}
else if (found_tag)
inparms = TRUE;
continue;
case ')':
if (inparms)
inparms = FALSE;
continue;
case ';':
if (found_tag && !inparms)
{
verify_tag = TRUE;
break;
}
continue;
}
if (found_tag && verify_tag && (*dbp != ' '))
{
if (*dbp == '\0')
continue;
if (lowcase (*dbp == 'e'))
{
if (tail ("extern"))
{
found_tag = FALSE;
verify_tag = FALSE;
}
}
else if (lowcase (*dbp) == 'f')
{
if (tail ("forward"))
{
found_tag = FALSE;
verify_tag = FALSE;
}
}
if (found_tag && verify_tag)
{
found_tag = FALSE;
verify_tag = FALSE;
pfnote (namebuf, TRUE,
tline.buffer, save_len, save_lineno, save_lcno);
continue;
}
}
if (get_tagname)
{
if (*dbp == '\0')
continue;
linebuffer_setlen (&tline, lb.len);
strcpy (tline.buffer, lb.buffer);
save_lineno = lineno;
save_lcno = linecharno;
for (cp = dbp + 1; *cp != '\0' && !endtoken (*cp); cp++)
continue;
namebuf = savenstr (dbp, cp-dbp);
dbp = cp;
save_len = dbp - lb.buffer + 1;
get_tagname = FALSE;
found_tag = TRUE;
continue;
}
else if (!incomment && !inquote && !found_tag)
{
switch (lowcase (c))
{
case 'p':
if (tail ("rocedure"))
get_tagname = TRUE;
continue;
case 'f':
if (tail ("unction"))
get_tagname = TRUE;
continue;
}
}
}
free (tline.buffer);
}
static int L_isdef P_((char *));
static int L_isquote P_((char *));
static void L_getit P_((void));
static int
L_isdef (strp)
register char *strp;
{
return ((strp[1] == 'd' || strp[1] == 'D')
&& (strp[2] == 'e' || strp[2] == 'E')
&& (strp[3] == 'f' || strp[3] == 'F'));
}
static int
L_isquote (strp)
register char *strp;
{
return ((*++strp == 'q' || *strp == 'Q')
&& (*++strp == 'u' || *strp == 'U')
&& (*++strp == 'o' || *strp == 'O')
&& (*++strp == 't' || *strp == 'T')
&& (*++strp == 'e' || *strp == 'E')
&& iswhite (*++strp));
}
static void
L_getit ()
{
register char *cp;
if (*dbp == '\'')
dbp++;
else if (*dbp == '(')
{
if (L_isquote (dbp))
dbp += 7;
else
dbp += 1;
dbp = skip_spaces (dbp);
}
for (cp = dbp ;
*cp != '\0' && *cp != '(' && !iswhite(*cp) && *cp != ')';
cp++)
continue;
if (cp == dbp)
return;
pfnote (savenstr (dbp, cp-dbp), TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
static void
Lisp_functions (inf)
FILE *inf;
{
LOOP_ON_INPUT_LINES (inf, lb, dbp)
{
if (dbp[0] == '(')
{
if (L_isdef (dbp))
{
dbp = skip_non_spaces (dbp);
dbp = skip_spaces (dbp);
L_getit ();
}
else
{
do
dbp++;
while (*dbp != '\0' && !iswhite (*dbp)
&& *dbp != ':' && *dbp != '(' && *dbp != ')');
if (*dbp == ':')
{
do
dbp++;
while (*dbp == ':');
if (L_isdef (dbp - 1))
{
dbp = skip_non_spaces (dbp);
dbp = skip_spaces (dbp);
L_getit ();
}
}
}
}
}
}
static void
Postscript_functions (inf)
FILE *inf;
{
register char *bp, *ep;
LOOP_ON_INPUT_LINES (inf, lb, bp)
{
if (bp[0] == '/')
{
for (ep = bp+1;
*ep != '\0' && *ep != ' ' && *ep != '{';
ep++)
continue;
pfnote (savenstr (bp, ep-bp), TRUE,
lb.buffer, ep - lb.buffer + 1, lineno, linecharno);
}
else if (strneq (bp, "defineps", 8))
{
bp = skip_non_spaces (bp);
bp = skip_spaces (bp);
get_tag (bp);
}
}
}
static void
Scheme_functions (inf)
FILE *inf;
{
register char *bp;
LOOP_ON_INPUT_LINES (inf, lb, bp)
{
if (bp[0] == '('
&& (bp[1] == 'D' || bp[1] == 'd')
&& (bp[2] == 'E' || bp[2] == 'e')
&& (bp[3] == 'F' || bp[3] == 'f'))
{
bp = skip_non_spaces (bp);
while (iswhite (*bp) || *bp == '(')
bp++;
get_tag (bp);
}
if (bp[0] == '('
&& (bp[1] == 'S' || bp[1] == 's')
&& (bp[2] == 'E' || bp[2] == 'e')
&& (bp[3] == 'T' || bp[3] == 't')
&& (bp[4] == '!' || bp[4] == '!')
&& (iswhite (bp[5])))
{
bp = skip_non_spaces (bp);
bp = skip_spaces (bp);
get_tag (bp);
}
}
}
struct TEX_tabent
{
char *name;
int len;
};
struct TEX_tabent *TEX_toktab = NULL;
char *TEX_defenv = "\
:chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\
:part:appendix:entry:index";
static void TEX_mode P_((FILE *));
static struct TEX_tabent *TEX_decode_env P_((char *, char *));
static int TEX_Token P_((char *));
char TEX_esc = '\\';
char TEX_opgrp = '{';
char TEX_clgrp = '}';
static void
TeX_commands (inf)
FILE *inf;
{
char *cp, *lasthit;
register int i;
TEX_mode (inf);
if (!TEX_toktab)
TEX_toktab = TEX_decode_env ("TEXTAGS", TEX_defenv);
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
lasthit = cp;
while ((cp = etags_strchr (cp, TEX_esc)) != NULL)
{
if (*++cp == '\0')
break;
linecharno += cp - lasthit;
lasthit = cp;
i = TEX_Token (lasthit);
if (i >= 0)
{
pfnote ( (char *)NULL, TRUE,
lb.buffer, lb.len, lineno, linecharno);
break;
}
}
}
}
#define TEX_LESC '\\'
#define TEX_SESC '!'
#define TEX_cmt '%'
static void
TEX_mode (inf)
FILE *inf;
{
int c;
while ((c = getc (inf)) != EOF)
{
if (c == TEX_cmt)
while (c != '\n')
c = getc (inf);
else if (c == TEX_LESC || c == TEX_SESC )
break;
}
if (c == TEX_LESC)
{
TEX_esc = TEX_LESC;
TEX_opgrp = '{';
TEX_clgrp = '}';
}
else
{
TEX_esc = TEX_SESC;
TEX_opgrp = '<';
TEX_clgrp = '>';
}
rewind (inf);
}
static struct TEX_tabent *
TEX_decode_env (evarname, defenv)
char *evarname;
char *defenv;
{
register char *env, *p;
struct TEX_tabent *tab;
int size, i;
env = getenv (evarname);
if (!env)
env = defenv;
else
{
char *oldenv = env;
env = concat (oldenv, defenv, "");
}
for (size = 1, p = env; p;)
if ((p = etags_strchr (p, ':')) && *++p != '\0')
size++;
tab = xnew (size + 1, struct TEX_tabent);
for (i = 0; *env;)
{
p = etags_strchr (env, ':');
if (!p)
p = env + strlen (env);
if (p - env > 0)
{
tab[i].name = savenstr (env, p - env);
tab[i].len = strlen (tab[i].name);
i++;
}
if (*p)
env = p + 1;
else
{
tab[i].name = NULL;
tab[i].len = 0;
break;
}
}
return tab;
}
static int
TEX_Token (cp)
char *cp;
{
int i;
for (i = 0; TEX_toktab[i].len > 0; i++)
if (strneq (TEX_toktab[i].name, cp, TEX_toktab[i].len))
return i;
return -1;
}
static void
Texinfo_nodes (inf)
FILE * inf;
{
char *cp, *start;
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
if ((*cp++ == '@'
&& *cp++ == 'n'
&& *cp++ == 'o'
&& *cp++ == 'd'
&& *cp++ == 'e' && iswhite (*cp++)))
{
start = cp = skip_spaces(cp);
while (*cp != '\0' && *cp != ',')
cp++;
pfnote (savenstr (start, cp - start), TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
}
}
static int prolog_pred P_((char *, char *));
static void prolog_skip_comment P_((linebuffer *, FILE *));
static int prolog_atom P_((char *, int));
static void
Prolog_functions (inf)
FILE *inf;
{
char *cp, *last;
int len;
int allocated;
allocated = 0;
len = 0;
last = NULL;
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
if (cp[0] == '\0')
continue;
else if (iswhite (cp[0]))
continue;
else if (cp[0] == '/' && cp[1] == '*')
prolog_skip_comment (&lb, inf);
else if ((len = prolog_pred (cp, last)) > 0)
{
if (last == NULL)
last = xnew(len + 1, char);
else if (len + 1 > allocated)
xrnew (last, len + 1, char);
allocated = len + 1;
strncpy (last, cp, len);
last[len] = '\0';
}
}
}
static void
prolog_skip_comment (plb, inf)
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));
}
static int
prolog_pred (s, last)
char *s;
char *last;
{
int pos;
int len;
pos = prolog_atom (s, 0);
if (pos < 1)
return 0;
len = pos;
pos = skip_spaces (s + pos) - s;
if ((s[pos] == '(') || (s[pos] == '.'))
{
if (s[pos] == '(')
pos++;
if (last == NULL
|| len != (int)strlen (last)
|| !strneq (s, last, len))
{
pfnote (savenstr (s, len), TRUE, s, pos, lineno, linecharno);
return len;
}
}
return 0;
}
static int
prolog_atom (s, pos)
char *s;
int pos;
{
int origpos;
origpos = pos;
if (ISLOWER(s[pos]) || (s[pos] == '_'))
{
pos++;
while (ISALNUM(s[pos]) || (s[pos] == '_'))
{
pos++;
}
return pos - origpos;
}
else if (s[pos] == '\'')
{
pos++;
while (1)
{
if (s[pos] == '\'')
{
pos++;
if (s[pos] != '\'')
break;
pos++;
}
else if (s[pos] == '\0')
return -1;
else if (s[pos] == '\\')
{
if (s[pos+1] == '\0')
return -1;
pos += 2;
}
else
pos++;
}
return pos - origpos;
}
else
return -1;
}
static int erlang_func P_((char *, char *));
static void erlang_attribute P_((char *));
static int erlang_atom P_((char *, int));
static void
Erlang_functions (inf)
FILE *inf;
{
char *cp, *last;
int len;
int allocated;
allocated = 0;
len = 0;
last = NULL;
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
if (cp[0] == '\0')
continue;
else if (iswhite (cp[0]))
continue;
else if (cp[0] == '%')
continue;
else if (cp[0] == '"')
continue;
else if (cp[0] == '-')
{
erlang_attribute (cp);
last = NULL;
}
else if ((len = erlang_func (cp, last)) > 0)
{
if (last == NULL)
last = xnew (len + 1, char);
else if (len + 1 > allocated)
xrnew (last, len + 1, char);
allocated = len + 1;
strncpy (last, cp, len);
last[len] = '\0';
}
}
}
static int
erlang_func (s, last)
char *s;
char *last;
{
int pos;
int len;
pos = erlang_atom (s, 0);
if (pos < 1)
return 0;
len = pos;
pos = skip_spaces (s + pos) - s;
if (s[pos++] == '('
&& (last == NULL
|| len != (int)strlen (last)
|| !strneq (s, last, len)))
{
pfnote (savenstr (s, len), TRUE, s, pos, lineno, linecharno);
return len;
}
return 0;
}
static void
erlang_attribute (s)
char *s;
{
int pos;
int len;
if (strneq (s, "-define", 7) || strneq (s, "-record", 7))
{
pos = skip_spaces (s + 7) - s;
if (s[pos++] == '(')
{
pos = skip_spaces (s + pos) - s;
len = erlang_atom (s, pos);
if (len != 0)
pfnote (savenstr (& s[pos], len), TRUE,
s, pos + len, lineno, linecharno);
}
}
return;
}
static int
erlang_atom (s, pos)
char *s;
int pos;
{
int origpos;
origpos = pos;
if (ISALPHA (s[pos]) || s[pos] == '_')
{
pos++;
while (ISALNUM (s[pos]) || s[pos] == '_')
pos++;
return pos - origpos;
}
else if (s[pos] == '\'')
{
pos++;
while (1)
{
if (s[pos] == '\'')
{
pos++;
break;
}
else if (s[pos] == '\0')
return -1;
else if (s[pos] == '\\')
{
if (s[pos+1] == '\0')
return -1;
pos += 2;
}
else
pos++;
}
return pos - origpos;
}
else
return -1;
}
#ifdef ETAGS_REGEXPS
static char *scan_separators P_((char *));
static void analyse_regex P_((char *, bool));
static void add_regex P_((char *, bool, language *));
static char *substitute P_((char *, char *, struct re_registers *));
static char *
scan_separators (name)
char *name;
{
char sep = name[0];
char *copyto = name;
bool quoted = FALSE;
for (++name; *name != '\0'; ++name)
{
if (quoted)
{
if (*name == 't')
*copyto++ = '\t';
else if (*name == sep)
*copyto++ = sep;
else
{
*copyto++ = '\\';
*copyto++ = *name;
}
quoted = FALSE;
}
else if (*name == '\\')
quoted = TRUE;
else if (*name == sep)
break;
else
*copyto++ = *name;
}
*copyto = '\0';
return name;
}
static void
analyse_regex (regex_arg, ignore_case)
char *regex_arg;
bool ignore_case;
{
if (regex_arg == NULL)
{
free_patterns ();
return;
}
switch (regex_arg[0])
{
case '\0':
case ' ':
case '\t':
break;
case '@':
{
FILE *regexfp;
linebuffer regexbuf;
char *regexfile = regex_arg + 1;
regexfp = fopen (regexfile, "r");
if (regexfp == NULL)
{
pfatal (regexfile);
return;
}
initbuffer (®exbuf);
while (readline_internal (®exbuf, regexfp) > 0)
analyse_regex (regexbuf.buffer, ignore_case);
free (regexbuf.buffer);
fclose (regexfp);
}
break;
case '{':
{
language *lang;
char *lang_name = regex_arg + 1;
char *cp;
for (cp = lang_name; *cp != '}'; cp++)
if (*cp == '\0')
{
error ("unterminated language name in regex: %s", regex_arg);
return;
}
*cp = '\0';
lang = get_language_from_langname (lang_name);
if (lang == NULL)
return;
add_regex (cp + 1, ignore_case, lang);
}
break;
default:
add_regex (regex_arg, ignore_case, NULL);
break;
}
}
static void
add_regex (regexp_pattern, ignore_case, lang)
char *regexp_pattern;
bool ignore_case;
language *lang;
{
static struct re_pattern_buffer zeropattern;
char *name;
const char *err;
struct re_pattern_buffer *patbuf;
pattern *pp;
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", (char *)NULL);
return;
}
(void) scan_separators (name);
patbuf = xnew (1, struct re_pattern_buffer);
*patbuf = zeropattern;
if (ignore_case)
patbuf->translate = lc_trans;
err = re_compile_pattern (regexp_pattern, strlen (regexp_pattern), patbuf);
if (err != NULL)
{
error ("%s while compiling pattern", err);
return;
}
pp = p_head;
p_head = xnew (1, pattern);
p_head->regex = savestr (regexp_pattern);
p_head->p_next = pp;
p_head->language = lang;
p_head->pattern = patbuf;
p_head->name_pattern = savestr (name);
p_head->error_signaled = FALSE;
}
static char *
substitute (in, out, regs)
char *in, *out;
struct re_registers *regs;
{
char *result, *t;
int size, dig, diglen;
result = NULL;
size = strlen (out);
if (out[size - 1] == '\\')
fatal ("pattern error in \"%s\"", out);
for (t = etags_strchr (out, '\\');
t != NULL;
t = etags_strchr (t + 2, '\\'))
if (ISDIGIT (t[1]))
{
dig = t[1] - '0';
diglen = regs->end[dig] - regs->start[dig];
size += diglen - 2;
}
else
size -= 1;
result = xnew (size + 1, char);
for (t = result; *out != '\0'; out++)
if (*out == '\\' && ISDIGIT (*++out))
{
dig = *out - '0';
diglen = regs->end[dig] - regs->start[dig];
strncpy (t, in + regs->start[dig], diglen);
t += diglen;
}
else
*t++ = *out;
*t = '\0';
assert (t <= result + size && t - result == (int)strlen (result));
return result;
}
static void
free_patterns ()
{
pattern *pp;
while (p_head != NULL)
{
pp = p_head->p_next;
free (p_head->regex);
free (p_head->name_pattern);
free (p_head);
p_head = pp;
}
return;
}
#endif
static void
get_tag (bp)
register char *bp;
{
register char *cp;
if (*bp == '\0')
return;
for (cp = bp + 1;
*cp != '\0' && *cp != '(' && *cp != ')' && !iswhite (*cp);
cp++)
continue;
pfnote (savenstr (bp, cp-bp), TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
static void
initbuffer (lbp)
linebuffer *lbp;
{
lbp->size = (DEBUG) ? 3 : 200;
lbp->buffer = xnew (lbp->size, char);
lbp->buffer[0] = '\0';
lbp->len = 0;
}
static long
readline_internal (lbp, stream)
linebuffer *lbp;
register FILE *stream;
{
char *buffer = lbp->buffer;
register char *p = lbp->buffer;
register char *pend;
int chars_deleted;
pend = p + lbp->size;
while (1)
{
register int c = getc (stream);
if (p == pend)
{
lbp->size *= 2;
xrnew (buffer, lbp->size, char);
p += buffer - lbp->buffer;
pend = buffer + lbp->size;
lbp->buffer = buffer;
}
if (c == EOF)
{
*p = '\0';
chars_deleted = 0;
break;
}
if (c == '\n')
{
if (p > buffer && p[-1] == '\r')
{
p -= 1;
#ifdef DOS_NT
chars_deleted = 1;
#else
chars_deleted = 2;
#endif
}
else
{
chars_deleted = 1;
}
*p = '\0';
break;
}
*p++ = c;
}
lbp->len = p - buffer;
return lbp->len + chars_deleted;
}
static long
readline (lbp, stream)
linebuffer *lbp;
FILE *stream;
{
long result = readline_internal (lbp, stream);
#ifdef ETAGS_REGEXPS
int match;
pattern *pp;
if (lbp->len > 0)
for (pp = p_head; pp != NULL; pp = pp->p_next)
{
if (pp->language != NULL && pp->language != curlang)
continue;
match = re_match (pp->pattern, lbp->buffer, lbp->len, 0, &pp->regs);
switch (match)
{
case -2:
if (!pp->error_signaled)
{
error ("error while matching \"%s\"", pp->regex);
pp->error_signaled = TRUE;
}
break;
case -1:
break;
default:
if (pp->name_pattern[0] != '\0')
{
char *name = substitute (lbp->buffer,
pp->name_pattern, &pp->regs);
if (name != NULL)
pfnote (name, TRUE, lbp->buffer, match, lineno, linecharno);
}
else
{
pfnote ((char *)NULL, TRUE,
lbp->buffer, match, lineno, linecharno);
}
break;
}
}
#endif
return result;
}
static char *
savestr (cp)
char *cp;
{
return savenstr (cp, strlen (cp));
}
static char *
savenstr (cp, len)
char *cp;
int len;
{
register char *dp;
dp = xnew (len + 1, char);
strncpy (dp, cp, len);
dp[len] = '\0';
return dp;
}
static char *
etags_strrchr (sp, c)
register const char *sp;
register int c;
{
register const char *r;
r = NULL;
do
{
if (*sp == c)
r = sp;
} while (*sp++);
return (char *)r;
}
static char *
etags_strchr (sp, c)
register const char *sp;
register int c;
{
do
{
if (*sp == c)
return (char *)sp;
} while (*sp++);
return NULL;
}
static char *
skip_spaces (cp)
char *cp;
{
while (iswhite (*cp))
cp++;
return cp;
}
static char *
skip_non_spaces (cp)
char *cp;
{
while (*cp != '\0' && !iswhite (*cp))
cp++;
return cp;
}
void
fatal (s1, s2)
char *s1, *s2;
{
error (s1, s2);
exit (BAD);
}
static void
pfatal (s1)
char *s1;
{
perror (s1);
exit (BAD);
}
static void
suggest_asking_for_help ()
{
fprintf (stderr, "\tTry `%s %s' for a complete list of options.\n",
progname,
#ifdef LONG_OPTIONS
"--help"
#else
"-h"
#endif
);
exit (BAD);
}
static void
error (s1, s2)
const char *s1, *s2;
{
fprintf (stderr, "%s: ", progname);
fprintf (stderr, s1, s2);
fprintf (stderr, "\n");
}
static char *
concat (s1, s2, s3)
char *s1, *s2, *s3;
{
int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
char *result = xnew (len1 + len2 + len3 + 1, char);
strcpy (result, s1);
strcpy (result + len1, s2);
strcpy (result + len1 + len2, s3);
result[len1 + len2 + len3] = '\0';
return result;
}
static char *
etags_getcwd ()
{
#ifdef HAVE_GETCWD
int bufsize = 200;
char *path = xnew (bufsize, char);
while (getcwd (path, bufsize) == NULL)
{
if (errno != ERANGE)
pfatal ("getcwd");
bufsize *= 2;
free (path);
path = xnew (bufsize, char);
}
canonicalize_filename (path);
return path;
#else
#if MSDOS
char *p, path[MAXPATHLEN + 1];
getwd (path);
for (p = path; *p != '\0'; p++)
if (*p == '\\')
*p = '/';
else
*p = lowcase (*p);
return strdup (path);
#else
linebuffer path;
FILE *pipe;
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
#endif
}
static char *
relative_filename (file, dir)
char *file, *dir;
{
char *fp, *dp, *afn, *res;
int i;
afn = absolute_filename (file, cwd);
fp = afn;
dp = dir;
while (*fp++ == *dp++)
continue;
fp--, dp--;
#ifdef DOS_NT
if (fp == afn && afn[0] != '/')
return afn;
#endif
do
fp--, dp--;
while (*fp != '/');
i = 0;
while ((dp = etags_strchr (dp + 1, '/')) != NULL)
i += 1;
res = xnew (3*i + strlen (fp + 1) + 1, char);
res[0] = '\0';
while (i-- > 0)
strcat (res, "../");
strcat (res, fp + 1);
free (afn);
return res;
}
static char *
absolute_filename (file, dir)
char *file, *dir;
{
char *slashp, *cp, *res;
if (filename_is_absolute (file))
res = savestr (file);
#ifdef DOS_NT
else if (file[1] == ':')
fatal ("%s: relative file names with drive letters not supported", file);
#endif
else
res = concat (dir, file, "");
slashp = etags_strchr (res, '/');
while (slashp != NULL && slashp[0] != '\0')
{
if (slashp[1] == '.')
{
if (slashp[2] == '.'
&& (slashp[3] == '/' || slashp[3] == '\0'))
{
cp = slashp;
do
cp--;
while (cp >= res && !filename_is_absolute (cp));
if (cp < res)
cp = slashp;
#ifdef DOS_NT
else if (cp[0] != '/')
cp = slashp;
#endif
strcpy (cp, slashp + 3);
slashp = cp;
continue;
}
else if (slashp[2] == '/' || slashp[2] == '\0')
{
strcpy (slashp, slashp + 2);
continue;
}
}
slashp = etags_strchr (slashp + 1, '/');
}
if (res[0] == '\0')
return savestr ("/");
else
return res;
}
static char *
absolute_dirname (file, dir)
char *file, *dir;
{
char *slashp, *res;
char save;
canonicalize_filename (file);
slashp = etags_strrchr (file, '/');
if (slashp == NULL)
return savestr (dir);
save = slashp[1];
slashp[1] = '\0';
res = absolute_filename (file, dir);
slashp[1] = save;
return res;
}
static bool
filename_is_absolute (fn)
char *fn;
{
return (fn[0] == '/'
#ifdef DOS_NT
|| (ISALPHA(fn[0]) && fn[1] == ':' && fn[2] == '/')
#endif
);
}
static void
canonicalize_filename (fn)
register char *fn;
{
#ifdef DOS_NT
if (fn[0] != '\0' && fn[1] == ':' && ISLOWER (fn[0]))
fn[0] = upcase (fn[0]);
for (; *fn != '\0'; fn++)
if (*fn == '\\')
*fn = '/';
#else
fn = NULL;
#endif
}
static void
linebuffer_setlen (lbp, toksize)
linebuffer *lbp;
int toksize;
{
while (lbp->size <= toksize)
{
lbp->size *= 2;
xrnew (lbp->buffer, lbp->size, char);
}
lbp->len = toksize;
}
long *
xmalloc (size)
unsigned int size;
{
long *result = (long *) malloc (size);
if (result == NULL)
fatal ("virtual memory exhausted", (char *)NULL);
return result;
}
long *
xrealloc (ptr, size)
char *ptr;
unsigned int size;
{
long *result = (long *) realloc (ptr, size);
if (result == NULL)
fatal ("virtual memory exhausted", (char *)NULL);
return result;
}