char pot_etags_version[] = "@(#) pot revision number is 17.26";
#define TRUE 1
#define FALSE 0
#ifdef DEBUG
# undef DEBUG
# define DEBUG TRUE
#else
# define DEBUG FALSE
# define NDEBUG
#endif
#ifdef HAVE_CONFIG_H
# include <config.h>
# undef static
# ifndef PTR
# define PTR void *
# endif
# ifndef __P
# define __P(args) args
# endif
#else
# if defined(__STDC__) && (__STDC__ || defined(__SUNPRO_C))
# define __P(args) args
# define PTR void *
# else
# define __P(args) ()
# define const
# define PTR long *
# 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 ();
# ifdef VMS
# define EXIT_SUCCESS 1
# define EXIT_FAILURE 0
# else
# define EXIT_SUCCESS 0
# define EXIT_FAILURE 1
# endif
# 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 NO_LONG_OPTIONS
# define NO_LONG_OPTIONS TRUE
# define getopt_long(argc,argv,optstr,lopts,lind) getopt (argc, argv, optstr)
extern char *optarg;
extern int optind, opterr;
#else
# define NO_LONG_OPTIONS FALSE
# include <getopt.h>
#endif
#ifndef HAVE_CONFIG_H
# ifdef __CYGWIN__
!!! NOTICE !!!
the regex.h distributed with Cygwin is not compatible with etags, alas!
If you want regular expression support, you should delete this notice and
arrange to use the GNU regex.h and regex.c.
# endif
#endif
#include <regex.h>
#ifdef CTAGS
# undef CTAGS
# define CTAGS TRUE
#else
# define CTAGS FALSE
#endif
#define streq(s,t) (assert((s)!=NULL || (t)!=NULL), !strcmp (s, t))
#define strcaseeq(s,t) (assert((s)!=NULL && (t)!=NULL), !etags_strcasecmp (s, t))
#define strneq(s,t,n) (assert((s)!=NULL || (t)!=NULL), !strncmp (s, t, n))
#define strncaseeq(s,t,n) (assert((s)!=NULL && (t)!=NULL), !etags_strncasecmp (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
#define bool int
typedef void Lang_function __P((FILE *));
typedef struct
{
char *suffix;
char *command;
} compressor;
typedef struct
{
char *name;
char *help;
Lang_function *function;
char **suffixes;
char **filenames;
char **interpreters;
bool metasource;
} language;
typedef struct fdesc
{
struct fdesc *next;
char *infname;
char *infabsname;
char *infabsdir;
char *taggedfname;
language *lang;
char *prop;
bool usecharno;
bool written;
} fdesc;
typedef struct node_st
{
struct node_st *left, *right;
fdesc *fdp;
char *name;
char *regex;
bool valid;
bool is_func;
bool been_warned;
int lno;
long cno;
} node;
typedef struct
{
long size;
int len;
char *buffer;
} linebuffer;
typedef struct
{
enum {
at_language,
at_regexp,
at_filename,
at_stdin,
at_end
} arg_type;
language *lang;
char *what;
} argument;
typedef struct regexp
{
struct regexp *p_next;
language *lang;
char *pattern;
char *name;
struct re_pattern_buffer *pat;
struct re_registers regs;
bool error_signaled;
bool force_explicit_name;
bool ignore_case;
bool multi_line;
} regexp;
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 Forth_words __P((FILE *));
static void Fortran_functions __P((FILE *));
static void HTML_labels __P((FILE *));
static void Lisp_functions __P((FILE *));
static void Lua_functions __P((FILE *));
static void Makefile_targets __P((FILE *));
static void Pascal_functions __P((FILE *));
static void Perl_functions __P((FILE *));
static void PHP_functions __P((FILE *));
static void PS_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 Yacc_entries __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((argument *));
int main __P((int, char **));
static compressor *get_compressor_from_suffix __P((char *, char **));
static language *get_language_from_langname __P((const char *));
static language *get_language_from_interpreter __P((char *));
static language *get_language_from_filename __P((char *, bool));
static void readline __P((linebuffer *, FILE *));
static long readline_internal __P((linebuffer *, FILE *));
static bool nocase_tail __P((char *));
static void get_tag __P((char *, char **));
static void analyse_regex __P((char *));
static void free_regexps __P((void));
static void regex_tag_multiline __P((void));
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 process_file_name __P((char *, language *));
static void process_file __P((FILE *, char *, language *));
static void find_entries __P((FILE *));
static void free_tree __P((node *));
static void free_fdesc __P((fdesc *));
static void pfnote __P((char *, bool, char *, int, int, long));
static void make_tag __P((char *, int, bool, char *, int, int, long));
static void invalidate_nodes __P((fdesc *, node **));
static void put_entries __P((node *));
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 int etags_strcasecmp __P((const char *, const char *));
static int etags_strncasecmp __P((const char *, 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_init __P((linebuffer *));
static void linebuffer_setlen __P((linebuffer *, int));
static PTR xmalloc __P((unsigned int));
static PTR xrealloc __P((char *, unsigned int));
static char searchar = '/';
static char *tagfile;
static char *progname;
static char *cwd;
static char *tagfiledir;
static FILE *tagf;
static fdesc *fdhead;
static fdesc *curfdp;
static int lineno;
static long charno;
static long linecharno;
static char *dbp;
static const int invalidcharno = -1;
static node *nodehead;
static node *last_node;
static linebuffer lb;
static linebuffer filebuf;
static linebuffer token_name;
static bool _wht[CHARS], _nin[CHARS], _itk[CHARS], _btk[CHARS], _etk[CHARS];
static char
*white = " \f\t\n\r\v",
*nonam = " \f\t\n\r()=,;",
*endtk = " \t\n\r\"'#()[]{}=-+%*/&|^~!<>;,.:?",
*begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$~@",
*midtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789";
static bool append_to_tagfile;
static bool typedefs;
static bool typedefs_or_cplusplus;
static bool constantypedefs;
static bool globals;
static bool members;
static bool declarations;
static bool no_line_directive;
static bool no_duplicates;
static bool update;
static bool vgrind_style;
static bool no_warnings;
static bool cxref_style;
static bool cplusplus;
static bool ignoreindent;
static bool packages_only;
#ifdef STDIN
# undef STDIN
#endif
#define STDIN 0x1001
static bool parsing_stdin;
static regexp *p_head;
static bool need_filebuf;
static struct option longopts[] =
{
{ "append", no_argument, NULL, 'a' },
{ "packages-only", no_argument, &packages_only, TRUE },
{ "c++", no_argument, NULL, 'C' },
{ "declarations", no_argument, &declarations, TRUE },
{ "no-line-directive", no_argument, &no_line_directive, TRUE },
{ "no-duplicates", no_argument, &no_duplicates, TRUE },
{ "help", no_argument, NULL, 'h' },
{ "help", no_argument, NULL, 'H' },
{ "ignore-indentation", no_argument, NULL, 'I' },
{ "language", required_argument, NULL, 'l' },
{ "members", no_argument, &members, TRUE },
{ "no-members", no_argument, &members, FALSE },
{ "output", required_argument, NULL, 'o' },
{ "regex", required_argument, NULL, 'r' },
{ "no-regex", no_argument, NULL, 'R' },
{ "ignore-case-regex", required_argument, NULL, 'c' },
{ "parse-stdin", required_argument, NULL, STDIN },
{ "version", no_argument, NULL, 'V' },
#if CTAGS
{ "backward-search", no_argument, NULL, 'B' },
{ "cxref", no_argument, NULL, 'x' },
{ "defines", no_argument, NULL, 'd' },
{ "globals", no_argument, &globals, TRUE },
{ "typedefs", no_argument, NULL, 't' },
{ "typedefs-and-c++", no_argument, NULL, 'T' },
{ "update", no_argument, NULL, 'u' },
{ "vgrind", no_argument, NULL, 'v' },
{ "no-warn", no_argument, NULL, 'w' },
#else
{ "no-defines", no_argument, NULL, 'D' },
{ "no-globals", no_argument, &globals, FALSE },
{ "include", required_argument, NULL, 'i' },
#endif
{ NULL }
};
static compressor compressors[] =
{
{ "z", "gzip -d -c"},
{ "Z", "gzip -d -c"},
{ "gz", "gzip -d -c"},
{ "GZ", "gzip -d -c"},
{ "bz2", "bzip2 -d -c" },
{ NULL }
};
static char *Ada_suffixes [] =
{ "ads", "adb", "ada", NULL };
static char Ada_help [] =
"In Ada code, functions, procedures, packages, tasks and types are\n\
tags. Use the `--packages-only' option to create tags for\n\
packages only.\n\
Ada tag names have suffixes indicating the type of entity:\n\
Entity type: Qualifier:\n\
------------ ----------\n\
function /f\n\
procedure /p\n\
package spec /s\n\
package body /b\n\
type /t\n\
task /k\n\
Thus, `M-x find-tag <RET> bidule/b <RET>' will go directly to the\n\
body of the package `bidule', while `M-x find-tag <RET> bidule <RET>'\n\
will just search for any tag `bidule'.";
static char *Asm_suffixes [] =
{ "a",
"asm",
"def",
"inc",
"ins",
"s", "sa",
"S",
"src",
NULL
};
static char Asm_help [] =
"In assembler code, labels appearing at the beginning of a line,\n\
followed by a colon, are tags.";
static char *default_C_suffixes [] =
{ "c", "h", NULL };
static char default_C_help [] =
"In C code, any C function or typedef is a tag, and so are\n\
definitions of `struct', `union' and `enum'. `#define' macro\n\
definitions and `enum' constants are tags unless you specify\n\
`--no-defines'. Global variables are tags unless you specify\n\
`--no-globals' and so are struct members unless you specify\n\
`--no-members'. Use of `--no-globals', `--no-defines' and\n\
`--no-members' can make the tags table file much smaller.\n\
You can tag function declarations and external variables by\n\
using `--declarations'.";
static char *Cplusplus_suffixes [] =
{ "C", "c++", "cc", "cpp", "cxx", "H", "h++", "hh", "hpp", "hxx",
"M",
"pdb",
NULL };
static char Cplusplus_help [] =
"In C++ code, all the tag constructs of C code are tagged. (Use\n\
--help --lang=c --lang=c++ for full help.)\n\
In addition to C tags, member functions are also recognized. Member\n\
variables are recognized unless you use the `--no-members' option.\n\
Tags for variables and functions in classes are named `CLASS::VARIABLE'\n\
and `CLASS::FUNCTION'. `operator' definitions have tag names like\n\
`operator+'.";
static char *Cjava_suffixes [] =
{ "java", NULL };
static char Cjava_help [] =
"In Java code, all the tags constructs of C and C++ code are\n\
tagged. (Use --help --lang=c --lang=c++ --lang=java for full help.)";
static char *Cobol_suffixes [] =
{ "COB", "cob", NULL };
static char Cobol_help [] =
"In Cobol code, tags are paragraph names; that is, any word\n\
starting in column 8 and followed by a period.";
static char *Cstar_suffixes [] =
{ "cs", "hs", NULL };
static char *Erlang_suffixes [] =
{ "erl", "hrl", NULL };
static char Erlang_help [] =
"In Erlang code, the tags are the functions, records and macros\n\
defined in the file.";
char *Forth_suffixes [] =
{ "fth", "tok", NULL };
static char Forth_help [] =
"In Forth code, tags are words defined by `:',\n\
constant, code, create, defer, value, variable, buffer:, field.";
static char *Fortran_suffixes [] =
{ "F", "f", "f90", "for", NULL };
static char Fortran_help [] =
"In Fortran code, functions, subroutines and block data are tags.";
static char *HTML_suffixes [] =
{ "htm", "html", "shtml", NULL };
static char HTML_help [] =
"In HTML input files, the tags are the `title' and the `h1', `h2',\n\
`h3' headers. Also, tags are `name=' in anchors and all\n\
occurrences of `id='.";
static char *Lisp_suffixes [] =
{ "cl", "clisp", "el", "l", "lisp", "LSP", "lsp", "ml", NULL };
static char Lisp_help [] =
"In Lisp code, any function defined with `defun', any variable\n\
defined with `defvar' or `defconst', and in general the first\n\
argument of any expression that starts with `(def' in column zero\n\
is a tag.";
static char *Lua_suffixes [] =
{ "lua", "LUA", NULL };
static char Lua_help [] =
"In Lua scripts, all functions are tags.";
static char *Makefile_filenames [] =
{ "Makefile", "makefile", "GNUMakefile", "Makefile.in", "Makefile.am", NULL};
static char Makefile_help [] =
"In makefiles, targets are tags; additionally, variables are tags\n\
unless you specify `--no-globals'.";
static char *Objc_suffixes [] =
{ "lm",
"m",
NULL };
static char Objc_help [] =
"In Objective C code, tags include Objective C definitions for classes,\n\
class categories, methods and protocols. Tags for variables and\n\
functions in classes are named `CLASS::VARIABLE' and `CLASS::FUNCTION'.\n\
(Use --help --lang=c --lang=objc --lang=java for full help.)";
static char *Pascal_suffixes [] =
{ "p", "pas", NULL };
static char Pascal_help [] =
"In Pascal code, the tags are the functions and procedures defined\n\
in the file.";
static char *Perl_suffixes [] =
{ "pl", "pm", NULL };
static char *Perl_interpreters [] =
{ "perl", "@PERL@", NULL };
static char Perl_help [] =
"In Perl code, the tags are the packages, subroutines and variables\n\
defined by the `package', `sub', `my' and `local' keywords. Use\n\
`--globals' if you want to tag global variables. Tags for\n\
subroutines are named `PACKAGE::SUB'. The name for subroutines\n\
defined in the default package is `main::SUB'.";
static char *PHP_suffixes [] =
{ "php", "php3", "php4", NULL };
static char PHP_help [] =
"In PHP code, tags are functions, classes and defines. Unless you use\n\
the `--no-members' option, vars are tags too.";
static char *plain_C_suffixes [] =
{ "pc",
NULL };
static char *PS_suffixes [] =
{ "ps", "psw", NULL };
static char PS_help [] =
"In PostScript code, the tags are the functions.";
static char *Prolog_suffixes [] =
{ "prolog", NULL };
static char Prolog_help [] =
"In Prolog code, tags are predicates and rules at the beginning of\n\
line.";
static char *Python_suffixes [] =
{ "py", NULL };
static char Python_help [] =
"In Python code, `def' or `class' at the beginning of a line\n\
generate a tag.";
static char *Scheme_suffixes [] =
{ "oak", "sch", "scheme", "SCM", "scm", "SM", "sm", "ss", "t", NULL };
static char Scheme_help [] =
"In Scheme code, tags include anything defined with `def' or with a\n\
construct whose name starts with `def'. They also include\n\
variables set with `set!' at top level in the file.";
static char *TeX_suffixes [] =
{ "bib", "clo", "cls", "ltx", "sty", "TeX", "tex", NULL };
static char TeX_help [] =
"In LaTeX text, the argument of any of the commands `\\chapter',\n\
`\\section', `\\subsection', `\\subsubsection', `\\eqno', `\\label',\n\
`\\ref', `\\cite', `\\bibitem', `\\part', `\\appendix', `\\entry',\n\
`\\index', `\\def', `\\newcommand', `\\renewcommand',\n\
`\\newenvironment' or `\\renewenvironment' is a tag.\n\
\n\
Other commands can be specified by setting the environment variable\n\
`TEXTAGS' to a colon-separated list like, for example,\n\
TEXTAGS=\"mycommand:myothercommand\".";
static char *Texinfo_suffixes [] =
{ "texi", "texinfo", "txi", NULL };
static char Texinfo_help [] =
"for texinfo files, lines starting with @node are tagged.";
static char *Yacc_suffixes [] =
{ "y", "y++", "ym", "yxx", "yy", NULL };
static char Yacc_help [] =
"In Bison or Yacc input files, each rule defines as a tag the\n\
nonterminal it constructs. The portions of the file that contain\n\
C code are parsed as C code (use --help --lang=c --lang=yacc\n\
for full help).";
static char auto_help [] =
"`auto' is not a real language, it indicates to use\n\
a default language for files base on file name suffix and file contents.";
static char none_help [] =
"`none' is not a real language, it indicates to only do\n\
regexp processing on files.";
static char no_lang_help [] =
"No detailed help available for this language.";
static language lang_names [] =
{
{ "ada", Ada_help, Ada_funcs, Ada_suffixes },
{ "asm", Asm_help, Asm_labels, Asm_suffixes },
{ "c", default_C_help, default_C_entries, default_C_suffixes },
{ "c++", Cplusplus_help, Cplusplus_entries, Cplusplus_suffixes },
{ "c*", no_lang_help, Cstar_entries, Cstar_suffixes },
{ "cobol", Cobol_help, Cobol_paragraphs, Cobol_suffixes },
{ "erlang", Erlang_help, Erlang_functions, Erlang_suffixes },
{ "forth", Forth_help, Forth_words, Forth_suffixes },
{ "fortran", Fortran_help, Fortran_functions, Fortran_suffixes },
{ "html", HTML_help, HTML_labels, HTML_suffixes },
{ "java", Cjava_help, Cjava_entries, Cjava_suffixes },
{ "lisp", Lisp_help, Lisp_functions, Lisp_suffixes },
{ "lua", Lua_help, Lua_functions, Lua_suffixes },
{ "makefile", Makefile_help,Makefile_targets,NULL,Makefile_filenames},
{ "objc", Objc_help, plain_C_entries, Objc_suffixes },
{ "pascal", Pascal_help, Pascal_functions, Pascal_suffixes },
{ "perl",Perl_help,Perl_functions,Perl_suffixes,NULL,Perl_interpreters},
{ "php", PHP_help, PHP_functions, PHP_suffixes },
{ "postscript",PS_help, PS_functions, PS_suffixes },
{ "proc", no_lang_help, plain_C_entries, plain_C_suffixes },
{ "prolog", Prolog_help, Prolog_functions, Prolog_suffixes },
{ "python", Python_help, Python_functions, Python_suffixes },
{ "scheme", Scheme_help, Scheme_functions, Scheme_suffixes },
{ "tex", TeX_help, TeX_commands, TeX_suffixes },
{ "texinfo", Texinfo_help, Texinfo_nodes, Texinfo_suffixes },
{ "yacc", Yacc_help,Yacc_entries,Yacc_suffixes,NULL,NULL,TRUE},
{ "auto", auto_help },
{ "none", none_help, just_read_file },
{ 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\" or \"template\" keyword\n\
switches to C++.");
puts ("Compressed files are supported using gzip and bzip2.\n\
\n\
For detailed help on a given language use, for example,\n\
etags --help --lang=ada.");
}
#ifndef EMACS_NAME
# define EMACS_NAME "standalone"
#endif
#ifndef VERSION
# define VERSION "version"
#endif
static void
print_version ()
{
printf ("%s (%s %s)\n", (CTAGS) ? "ctags" : "etags", EMACS_NAME, VERSION);
puts ("Copyright (C) 2007 Free Software Foundation, Inc.");
puts ("This program is distributed under the terms in ETAGS.README");
exit (EXIT_SUCCESS);
}
static void
print_help (argbuffer)
argument *argbuffer;
{
bool help_for_lang = FALSE;
for (; argbuffer->arg_type != at_end; argbuffer++)
if (argbuffer->arg_type == at_language)
{
if (help_for_lang)
puts ("");
puts (argbuffer->lang->help);
help_for_lang = TRUE;
}
if (help_for_lang)
exit (EXIT_SUCCESS);
printf ("Usage: %s [options] [[regex-option ...] file-name] ...\n\
\n\
These are the options accepted by %s.\n", progname, progname);
if (NO_LONG_OPTIONS)
puts ("WARNING: long option names do not work with this executable,\n\
as it is not linked with GNU getopt.");
else
puts ("You may use unambiguous abbreviations for the long option names.");
puts (" A - as file name means read names from stdin (one per line).\n\
Absolute names are stored in the output file as they are.\n\
Relative ones are stored relative to the output file's directory.\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 ("--no-members\n\
Do not create tag entries for members of structures\n\
in some languages.");
puts ("-r REGEXP, --regex=REGEXP or --regex=@regexfile\n\
Make a tag for each line matching a regular expression pattern\n\
in the following files. {LANGUAGE}REGEXP uses REGEXP for LANGUAGE\n\
files only. REGEXFILE is a file containing one REGEXP per line.\n\
REGEXP takes the form /TAGREGEXP/TAGNAME/MODS, where TAGNAME/ is\n\
optional. The TAGREGEXP pattern is anchored (as if preceded by ^).");
puts (" If TAGNAME/ is present, the tags created are named.\n\
For example Tcl named tags can be created with:\n\
--regex=\"/proc[ \\t]+\\([^ \\t]+\\)/\\1/.\".\n\
MODS are optional one-letter modifiers: `i' means to ignore case,\n\
`m' means to allow multi-line matches, `s' implies `m' and\n\
causes dot to match any character, including newline.");
puts ("-R, --no-regex\n\
Don't create tags from regexps for the following files.");
puts ("-I, --ignore-indentation\n\
In C and C++ do not assume that a closing brace in the first\n\
column is the final brace of a function or structure definition.");
puts ("-o FILE, --output=FILE\n\
Write the tags to FILE.");
puts ("--parse-stdin=NAME\n\
Read from standard input and record tags as belonging to file NAME.");
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.");
}
if (CTAGS)
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.");
if (CTAGS)
{
puts ("-v, --vgrind\n\
Print on the standard output an index of items intended for\n\
human consumption, similar to the output of vgrind. The index\n\
is sorted, and gives the page number of each item.");
# if PRINT_UNDOCUMENTED_OPTIONS_HELP
puts ("-w, --no-duplicates\n\
Do not create duplicate tag entries, for compatibility with\n\
traditional ctags.");
puts ("-w, --no-warn\n\
Suppress warning messages about duplicate tag entries.");
# endif
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.\n\
Followed by one or more `--language' options prints detailed\n\
help about tag generation for the specified languages.");
print_language_names ();
puts ("");
puts ("Report bugs to bug-gnu-emacs@gnu.org");
exit (EXIT_SUCCESS);
}
#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;
argument *argbuffer;
int current_arg, file_count;
linebuffer filename_lb;
bool help_asked = FALSE;
#ifdef VMS
bool got_err;
#endif
char *optstring;
int opt;
#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);
if (!CTAGS)
{
typedefs = typedefs_or_cplusplus = constantypedefs = TRUE;
globals = TRUE;
}
optstring = concat (NO_LONG_OPTIONS ? "" : "-",
"ac:Cf:Il:o:r:RSVhH",
(CTAGS) ? "BxdtTuvw" : "Di:");
while ((opt = getopt_long (argc, argv, optstring, longopts, NULL)) != EOF)
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 STDIN:
argbuffer[current_arg].arg_type = at_stdin;
argbuffer[current_arg].what = optarg;
++current_arg;
++file_count;
if (parsing_stdin)
fatal ("cannot parse standard input more than once", (char *)NULL);
parsing_stdin = TRUE;
break;
case 'a': append_to_tagfile = TRUE; break;
case 'C': cplusplus = TRUE; 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':
ignoreindent = 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;
case 'c':
optarg = concat (optarg, "i", "");
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 'V':
print_version ();
break;
case 'h':
case 'H':
help_asked = TRUE;
break;
case 'D': constantypedefs = FALSE; break;
case 'i': included_files[nincluded_files++] = optarg; break;
case 'B': searchar = '?'; break;
case 'd': constantypedefs = TRUE; break;
case 't': typedefs = TRUE; break;
case 'T': typedefs = typedefs_or_cplusplus = TRUE; break;
case 'u': update = TRUE; break;
case 'v': vgrind_style = TRUE;
case 'x': cxref_style = TRUE; break;
case 'w': no_warnings = TRUE; break;
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;
}
argbuffer[current_arg].arg_type = at_end;
if (help_asked)
print_help (argbuffer);
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, "-")
|| strneq (tagfile, "/dev/", 5))
tagfiledir = cwd;
else
tagfiledir = absolute_dirname (tagfile, cwd);
init ();
linebuffer_init (&lb);
linebuffer_init (&filename_lb);
linebuffer_init (&filebuf);
linebuffer_init (&token_name);
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++)
{
static language *lang;
char *this_file;
switch (argbuffer[i].arg_type)
{
case at_language:
lang = argbuffer[i].lang;
break;
case at_regexp:
analyse_regex (argbuffer[i].what);
break;
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, "-"))
{
if (parsing_stdin)
fatal ("cannot parse standard input AND read file names from it",
(char *)NULL);
while (readline_internal (&filename_lb, stdin) > 0)
process_file_name (filename_lb.buffer, lang);
}
else
process_file_name (this_file, lang);
#ifdef VMS
}
#endif
break;
case at_stdin:
this_file = argbuffer[i].what;
process_file (stdin, this_file, lang);
break;
}
}
free_regexps ();
free (lb.buffer);
free (filebuf.buffer);
free (token_name.buffer);
if (!CTAGS || cxref_style)
{
put_entries (nodehead);
free_tree (nodehead);
nodehead = NULL;
if (!CTAGS)
{
fdesc *fdp;
for (fdp = fdhead; fdp != NULL; fdp = fdp->next)
if (!fdp->written)
fprintf (tagf, "\f\n%s,0\n", fdp->taggedfname);
while (nincluded_files-- > 0)
fprintf (tagf, "\f\n%s,include\n", *included_files++);
if (fclose (tagf) == EOF)
pfatal (tagfile);
}
exit (EXIT_SUCCESS);
}
if (update)
{
char cmd[BUFSIZ];
for (i = 0; i < current_arg; ++i)
{
switch (argbuffer[i].arg_type)
{
case at_filename:
case at_stdin:
break;
default:
continue;
}
sprintf (cmd,
"mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
tagfile, argbuffer[i].what, tagfile);
if (system (cmd) != EXIT_SUCCESS)
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 (nodehead);
free_tree (nodehead);
nodehead = NULL;
if (fclose (tagf) == EOF)
pfatal (tagfile);
if (CTAGS)
if (append_to_tagfile || update)
{
char cmd[2*BUFSIZ+20];
sprintf (cmd, "sort -u -o %.*s %.*s", BUFSIZ, tagfile, BUFSIZ, tagfile);
exit (system (cmd));
}
return EXIT_SUCCESS;
}
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)
const 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, case_sensitive)
char *file;
bool case_sensitive;
{
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 ((case_sensitive)
? streq (*name, file)
: strcaseeq (*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 ((case_sensitive)
? streq (*ext, suffix)
: strcaseeq (*ext, suffix))
return lang;
return NULL;
}
static void
process_file_name (file, lang)
char *file;
language *lang;
{
struct stat stat_buf;
FILE *inf;
fdesc *fdp;
compressor *compr;
char *compressed_name, *uncompressed_name;
char *ext, *real_name;
int retval;
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);
}
for (fdp = fdhead; fdp != NULL; fdp = fdp->next)
{
assert (fdp->infname != NULL);
if (streq (uncompressed_name, fdp->infname))
goto cleanup;
}
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 cleanup;
}
}
if (!S_ISREG (stat_buf.st_mode))
{
error ("skipping %s: it is not a regular file.", real_name);
goto cleanup;
}
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 cleanup;
}
process_file (inf, uncompressed_name, lang);
if (real_name == compressed_name)
retval = pclose (inf);
else
retval = fclose (inf);
if (retval < 0)
pfatal (file);
cleanup:
if (compressed_name) free (compressed_name);
if (uncompressed_name) free (uncompressed_name);
last_node = NULL;
curfdp = NULL;
return;
}
static void
process_file (fh, fn, lang)
FILE *fh;
char *fn;
language *lang;
{
static const fdesc emptyfdesc;
fdesc *fdp;
fdp = xnew (1, fdesc);
*fdp = emptyfdesc;
fdp->next = fdhead;
fdp->infname = savestr (fn);
fdp->lang = lang;
fdp->infabsname = absolute_filename (fn, cwd);
fdp->infabsdir = absolute_dirname (fn, cwd);
if (filename_is_absolute (fn))
{
fdp->taggedfname = absolute_filename (fn, NULL);
}
else
{
fdp->taggedfname = relative_filename (fn, tagfiledir);
}
fdp->usecharno = TRUE;
fdp->prop = NULL;
fdp->written = FALSE;
fdhead = fdp;
curfdp = fdhead;
find_entries (fh);
if (!CTAGS
&& curfdp->usecharno
&& !curfdp->lang->metasource)
{
node *np, *prev;
prev = NULL;
for (np = nodehead; np != NULL; prev = np, np = np->left)
if (np->fdp == curfdp)
break;
if (np != NULL)
{
assert (np->left == NULL);
assert (fdhead == curfdp);
assert (last_node->fdp == curfdp);
put_entries (np);
free_tree (np);
if (prev == NULL)
nodehead = NULL;
else
prev->left = NULL;
}
}
}
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');
}
static void
find_entries (inf)
FILE *inf;
{
char *cp;
language *lang = curfdp->lang;
Lang_function *parser = NULL;
if (lang != NULL && lang->function != NULL)
{
parser = lang->function;
}
if (parser == NULL)
{
lang = get_language_from_filename (curfdp->infname, TRUE);
if (lang != NULL && lang->function != NULL)
{
curfdp->lang = lang;
parser = lang->function;
}
}
if (parser == NULL
&& 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)
{
curfdp->lang = lang;
parser = lang->function;
}
}
}
rewind (inf);
if (parser == NULL)
{
lang = get_language_from_filename (curfdp->infname, FALSE);
if (lang != NULL && lang->function != NULL)
{
curfdp->lang = lang;
parser = lang->function;
}
}
if (parser == NULL)
{
node *old_last_node = last_node;
curfdp->lang = get_language_from_langname ("fortran");
find_entries (inf);
if (old_last_node == last_node)
{
rewind (inf);
curfdp->lang = get_language_from_langname (cplusplus ? "c++" : "c");
find_entries (inf);
}
return;
}
if (!no_line_directive
&& curfdp->lang != NULL && curfdp->lang->metasource)
{
fdesc **fdpp = &fdhead;
while (*fdpp != NULL)
if (*fdpp != curfdp
&& streq ((*fdpp)->taggedfname, curfdp->taggedfname))
{
fdesc *badfdp = *fdpp;
invalidate_nodes (badfdp, &nodehead);
*fdpp = badfdp->next;
free_fdesc (badfdp);
}
else
fdpp = &(*fdpp)->next;
}
assert (parser != NULL);
linebuffer_setlen (&filebuf, 0);
lineno = 0;
charno = 0;
linecharno = 0;
parser (inf);
regex_tag_multiline ();
}
static void
make_tag (name, namelen, is_func, linestart, linelen, lno, cno)
char *name;
int namelen;
bool is_func;
char *linestart;
int linelen;
int lno;
long cno;
{
bool named = (name != NULL && namelen > 0);
if (!CTAGS && named)
{
int i;
register char *cp = name;
for (i = 0; i < namelen; i++)
if (notinname (*cp++))
break;
if (i == namelen)
{
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
pfnote (name, is_func, linestart, linelen, lno, cno)
char *name;
bool is_func;
char *linestart;
int linelen;
int lno;
long cno;
{
register node *np;
assert (name == NULL || name[0] != '\0');
if (CTAGS && name == NULL)
return;
np = xnew (1, node);
if (CTAGS && !cxref_style && streq (name, "main"))
{
register char *fp = etags_strrchr (curfdp->taggedfname, '/');
np->name = concat ("M", fp == NULL ? curfdp->taggedfname : fp + 1, "");
fp = etags_strrchr (np->name, '.');
if (fp != NULL && fp[1] != '\0' && fp[2] == '\0')
fp[0] = '\0';
}
else
np->name = name;
np->valid = TRUE;
np->been_warned = FALSE;
np->fdp = curfdp;
np->is_func = is_func;
np->lno = lno;
if (np->fdp->usecharno)
np->cno = cno ;
else
np->cno = invalidcharno;
np->left = np->right = NULL;
if (CTAGS && !cxref_style)
{
if (strlen (linestart) < 50)
np->regex = concat (linestart, "$", "");
else
np->regex = savenstr (linestart, 50);
}
else
np->regex = savenstr (linestart, linelen);
add_node (np, &nodehead);
}
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->regex);
free (np);
np = node_right;
}
}
static void
free_fdesc (fdp)
register fdesc *fdp;
{
if (fdp->infname != NULL) free (fdp->infname);
if (fdp->infabsname != NULL) free (fdp->infabsname);
if (fdp->infabsdir != NULL) free (fdp->infabsdir);
if (fdp->taggedfname != NULL) free (fdp->taggedfname);
if (fdp->prop != NULL) free (fdp->prop);
free (fdp);
}
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 && last_node->fdp == np->fdp)
{
assert (last_node->right == NULL);
last_node->right = np;
last_node = np;
}
else if (cur_node->fdp == np->fdp)
{
add_node (np, &cur_node->right);
}
else
add_node (np, &cur_node->left);
}
else
{
dif = strcmp (np->name, cur_node->name);
if (no_duplicates && !dif)
{
if (np->fdp == cur_node->fdp)
{
if (!no_warnings)
{
fprintf (stderr, "Duplicate entry in file %s, line %d: %s\n",
np->fdp->infname, 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->fdp->infname, cur_node->fdp->infname, np->name);
cur_node->been_warned = TRUE;
}
return;
}
add_node (np, dif < 0 ? &cur_node->left : &cur_node->right);
}
}
static void
invalidate_nodes (badfdp, npp)
fdesc *badfdp;
node **npp;
{
node *np = *npp;
if (np == NULL)
return;
if (CTAGS)
{
if (np->left != NULL)
invalidate_nodes (badfdp, &np->left);
if (np->fdp == badfdp)
np->valid = FALSE;
if (np->right != NULL)
invalidate_nodes (badfdp, &np->right);
}
else
{
assert (np->fdp != NULL);
if (np->fdp == badfdp)
{
*npp = np->left;
np->left = NULL;
free_tree (np);
invalidate_nodes (badfdp, npp);
}
else
invalidate_nodes (badfdp, &np->left);
}
}
static int total_size_of_entries __P((node *));
static int number_len __P((long));
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 = 0;
for (; np != NULL; np = np->right)
if (np->valid)
{
total += strlen (np->regex) + 1;
if (np->name != NULL)
total += strlen (np->name) + 1;
total += number_len ((long) np->lno) + 1;
if (np->cno != invalidcharno)
total += number_len (np->cno);
total += 1;
}
return total;
}
static void
put_entries (np)
register node *np;
{
register char *sp;
static fdesc *fdp = NULL;
if (np == NULL)
return;
if (CTAGS)
put_entries (np->left);
if (np->valid)
{
if (!CTAGS)
{
if (fdp != np->fdp)
{
fdp = np->fdp;
fprintf (tagf, "\f\n%s,%d\n",
fdp->taggedfname, total_size_of_entries (np));
fdp->written = TRUE;
}
fputs (np->regex, tagf);
fputc ('\177', tagf);
if (np->name != NULL)
{
fputs (np->name, tagf);
fputc ('\001', tagf);
}
fprintf (tagf, "%d,", np->lno);
if (np->cno != invalidcharno)
fprintf (tagf, "%ld", np->cno);
fputs ("\n", tagf);
}
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->fdp->taggedfname, (np->lno + 63) / 64);
else
fprintf (stdout, "%-16s %3d %-16s %s\n",
np->name, np->lno, np->fdp->taggedfname, np->regex);
}
else
{
fprintf (tagf, "%s\t%s\t", np->name, np->fdp->taggedfname);
if (np->is_func)
{
putc (searchar, tagf);
putc ('^', tagf);
for (sp = np->regex; *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);
if (!CTAGS)
put_entries (np->left);
}
#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_attribute,
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
};
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; };
#ifdef __GNUC__
__inline
#else
#ifdef __cplusplus
inline
#endif
#endif
static unsigned int
hash (str, len)
register const char *str;
register unsigned int len;
{
static unsigned char asso_values[] =
{
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 15,
14, 35, 35, 35, 35, 35, 35, 35, 14, 35,
35, 35, 35, 12, 13, 35, 35, 35, 35, 12,
35, 35, 35, 35, 35, 1, 35, 16, 35, 6,
23, 0, 0, 35, 22, 0, 35, 35, 5, 0,
0, 15, 1, 35, 6, 35, 8, 19, 35, 16,
4, 5, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
35, 35, 35, 35, 35, 35
};
register int hval = len;
switch (hval)
{
default:
hval += asso_values[(unsigned char)str[2]];
case 2:
hval += asso_values[(unsigned char)str[1]];
break;
}
return hval;
}
static struct C_stab_entry *
in_word_set (str, len)
register const char *str;
register unsigned int len;
{
enum
{
TOTAL_KEYWORDS = 32,
MIN_WORD_LENGTH = 2,
MAX_WORD_LENGTH = 15,
MIN_HASH_VALUE = 2,
MAX_HASH_VALUE = 34
};
static struct C_stab_entry wordlist[] =
{
{""}, {""},
{"if", 0, st_C_ignore},
{""},
{"@end", 0, st_C_objend},
{"union", 0, st_C_struct},
{"define", 0, st_C_define},
{"import", (C_JAVA & ~C_PLPL), st_C_ignore},
{"template", 0, st_C_template},
{"operator", C_PLPL, st_C_operator},
{"@interface", 0, st_C_objprot},
{"implements", (C_JAVA & ~C_PLPL), st_C_javastruct},
{"friend", C_PLPL, st_C_ignore},
{"typedef", 0, st_C_typedef},
{"return", 0, st_C_ignore},
{"@implementation",0, st_C_objimpl},
{"@protocol", 0, st_C_objprot},
{"interface", (C_JAVA & ~C_PLPL), st_C_struct},
{"extern", 0, st_C_extern},
{"extends", (C_JAVA & ~C_PLPL), st_C_javastruct},
{"struct", 0, st_C_struct},
{"domain", C_STAR, st_C_struct},
{"switch", 0, st_C_ignore},
{"enum", 0, st_C_enum},
{"for", 0, st_C_ignore},
{"namespace", C_PLPL, st_C_struct},
{"class", 0, st_C_class},
{"while", 0, st_C_ignore},
{"undef", 0, st_C_define},
{"package", (C_JAVA & ~C_PLPL), st_C_ignore},
{"__attribute__", 0, st_C_attribute},
{"SYSCALL", 0, st_C_gnumacro},
{"ENTRY", 0, st_C_gnumacro},
{"PSEUDO", 0, st_C_gnumacro},
{"DEFUN", 0, st_C_gnumacro}
};
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) && s[len] == '\0')
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;
}
static bool inattribute;
static enum
{
fvnone,
fdefunkey,
fdefunname,
foperator,
fvnameseen,
fstartlist,
finlist,
flistseen,
fignore,
vignore
} fvdef;
static bool fvextern;
static enum
{
tnone,
tkeyseen,
ttypeseen,
tinbody,
tend,
tignore
} typdef;
static enum
{
snone,
skeyseen,
stagseen,
scolonseen
} structdef;
static char *objtag = "<uninited>";
static enum
{
dnone,
dsharpseen,
ddefineseen,
dignorerest
} definedef;
static enum
{
onone,
oprotocol,
oimplementation,
otagseen,
oparenseen,
ocatseen,
oinbody,
omethodsign,
omethodtag,
omethodcolon,
omethodparm,
oignore
} objdef;
static struct tok
{
char *line;
int offset;
int length;
bool valid;
bool named;
int lineno;
long linepos;
} token;
static void pushclass_above __P((int, char *, int));
static void popclass_above __P((int));
static void write_classname __P((linebuffer *, char *qualifier));
static struct {
char **cname;
int *bracelev;
int nl;
int size;
} cstack;
#define nestlev (cstack.nl)
#define instruct (structdef == snone && nestlev > 0 \
&& bracelev == cstack.bracelev[nestlev-1] + 1)
static void
pushclass_above (bracelev, str, len)
int bracelev;
char *str;
int len;
{
int nl;
popclass_above (bracelev);
nl = cstack.nl;
if (nl >= cstack.size)
{
int size = cstack.size *= 2;
xrnew (cstack.cname, size, char *);
xrnew (cstack.bracelev, size, int);
}
assert (nl == 0 || cstack.bracelev[nl-1] < bracelev);
cstack.cname[nl] = (str == NULL) ? NULL : savenstr (str, len);
cstack.bracelev[nl] = bracelev;
cstack.nl = nl + 1;
}
static void
popclass_above (bracelev)
int bracelev;
{
int nl;
for (nl = cstack.nl - 1;
nl >= 0 && cstack.bracelev[nl] >= bracelev;
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, bracelev, parlev, is_func_or_var)
register char *str;
register int len;
register int c;
int *c_extp;
int bracelev;
int parlev;
bool *is_func_or_var;
{
static enum sym_type structtype;
static int structbracelev;
static enum sym_type toktype;
toktype = C_symtype (str, len, *c_extp);
if (toktype == st_C_attribute)
{
inattribute = TRUE;
return FALSE;
}
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_class:
case st_C_struct:
case st_C_enum:
typdef = ttypeseen;
}
break;
case ttypeseen:
if (structdef == snone && fvdef == fvnone)
{
fvdef = fvnameseen;
return TRUE;
}
break;
case tend:
switch (toktype)
{
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 ((*c_extp & C_AUTO)
&& bracelev == 0
&& 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;
structbracelev = bracelev;
if (fvdef == fvnameseen)
fvdef = fvnone;
}
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)
{
fvdef = fvnone;
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)
{
fvdef = fvnone;
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;
switch (fvdef)
{
case finlist:
case flistseen:
case fignore:
case vignore:
break;
default:
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 && bracelev > structbracelev)
return TRUE;
switch (fvdef)
{
case fdefunkey:
if (bracelev > 0)
break;
fvdef = fdefunname;
*is_func_or_var = TRUE;
return TRUE;
case fvnone:
switch (typdef)
{
case ttypeseen:
return FALSE;
case tnone:
if ((strneq (str, "asm", 3) && endtoken (str[3]))
|| (strneq (str, "__asm__", 7) && endtoken (str[7])))
{
fvdef = vignore;
return FALSE;
}
break;
}
case fvnameseen:
if (len >= 10 && strneq (str+len-10, "::operator", 10))
{
if (*c_extp & C_AUTO)
*c_extp = (*c_extp | C_PLPL) & ~C_AUTO;
fvdef = foperator;
*is_func_or_var = TRUE;
return TRUE;
}
if (bracelev > 0 && !instruct)
break;
fvdef = fvnameseen;
*is_func_or_var = TRUE;
return TRUE;
}
break;
}
return FALSE;
}
static 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 plainc ((c_ext & C_EXT) == C_PLAIN)
#define cplpl (c_ext & C_PLPL)
#define cjava ((c_ext & C_JAVA) == C_JAVA)
#define CNL_SAVE_DEFINEDEF() \
do { \
curlinepos = 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)
return;
if (token.valid)
make_tag (token_name.buffer, token_name.len, isfun, token.line,
token.offset+token.length+1, token.lineno, token.linepos);
else
make_tag (concat ("INVALID TOKEN:-->", token_name.buffer, ""),
token_name.len + 17, 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 bracelev;
int bracketlev;
int parlev;
int attrparlev;
int templatelev;
int typdefbracelev;
bool incomm, inquote, inchar, quotednl, midtoken;
bool yacc_rules;
struct tok savetoken = {0};
linebuffer_init (&lbs[0].lb);
linebuffer_init (&lbs[1].lb);
if (cstack.size == 0)
{
cstack.size = (DEBUG) ? 1 : 4;
cstack.nl = 0;
cstack.cname = xnew (cstack.size, char *);
cstack.bracelev = xnew (cstack.size, int);
}
tokoff = toklen = typdefbracelev = 0;
curndx = newndx = 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;
bracelev = bracketlev = parlev = attrparlev = templatelev = 0;
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 if (bracketlev > 0)
{
switch (c)
{
case ']':
if (--bracketlev > 0)
continue;
break;
case '\0':
CNL_SAVE_DEFINEDEF ();
break;
}
continue;
}
else switch (c)
{
case '"':
inquote = TRUE;
if (inattribute)
break;
switch (fvdef)
{
case fdefunkey:
case fstartlist:
case finlist:
case fignore:
case vignore:
break;
default:
fvextern = FALSE;
fvdef = fvnone;
}
continue;
case '\'':
inchar = TRUE;
if (inattribute)
break;
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;
bracelev = 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;
case '[':
bracketlev++;
continue;
}
if (typdef != tignore
&& definedef != dignorerest
&& fvdef != finlist
&& templatelev == 0
&& (definedef != dnone
|| structdef != scolonseen)
&& !inattribute)
{
if (midtoken)
{
if (endtoken (c))
{
if (c == ':' && *lp == ':' && begtoken (lp[1]))
{
if (c_ext & C_AUTO)
c_ext = (c_ext | C_PLPL) & ~C_AUTO;
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, bracelev, 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 (!plainc
&& 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;
}
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
&& bracelev > 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);
}
else
{
if (inattribute && fvdef == fignore)
{
fvdef = fvnone;
}
}
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:
if (plainc || declarations)
{
make_C_tag (TRUE);
fvdef = fignore;
}
break;
}
if (structdef == stagseen && !cjava)
{
popclass_above (bracelev);
structdef = snone;
}
break;
case dsharpseen:
savetoken = token;
break;
}
if (!yacc_rules || lp == newlb.buffer + 1)
{
tokoff = lp - 1 - newlb.buffer;
toklen = 1;
midtoken = TRUE;
}
continue;
}
}
switch (c)
{
case ':':
if (inattribute)
break;
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;
}
if (cplpl && fvdef == flistseen)
{
make_C_tag (TRUE);
fvdef = fignore;
break;
}
break;
case ';':
if (definedef != dnone || inattribute)
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 || cplpl)
fvdef = fvnone;
break;
case fvnameseen:
if ((globals && bracelev == 0 && (!fvextern || declarations))
|| (members && instruct))
make_C_tag (FALSE);
fvextern = FALSE;
fvdef = fvnone;
token.valid = FALSE;
break;
case flistseen:
if ((declarations
&& (cplpl || !instruct)
&& (typdef == tnone || (typdef != tignore && instruct)))
|| (members
&& plainc && instruct))
make_C_tag (TRUE);
default:
fvextern = FALSE;
fvdef = fvnone;
if (declarations
&& cplpl && structdef == stagseen)
make_C_tag (FALSE);
else
token.valid = FALSE;
}
default:
if (!instruct)
typdef = tnone;
}
if (structdef == stagseen)
structdef = snone;
break;
case ',':
if (definedef != dnone || inattribute)
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 (parlev == 0
&& ((globals
&& bracelev == 0
&& templatelev == 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 || inattribute)
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 && bracelev == 1)
|| (globals && bracelev == 0
&& (!fvextern || declarations)))
make_C_tag (FALSE);
default:
fvdef = fvnone;
}
break;
}
break;
case '(':
if (inattribute)
{
attrparlev++;
break;
}
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 (inattribute)
{
if (--attrparlev == 0)
inattribute = FALSE;
break;
}
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;
typdefbracelev = bracelev;
}
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 (bracelev == 0 && structdef == snone && nestlev == 0
&& typdef == tnone)
bracelev = -1;
}
break;
}
switch (structdef)
{
case skeyseen:
pushclass_above (bracelev, NULL, 0);
structdef = snone;
break;
case stagseen:
case scolonseen:
pushclass_above (bracelev,token.line+token.offset, token.length);
structdef = snone;
make_C_tag (FALSE);
break;
}
bracelev++;
break;
case '*':
if (definedef != dnone)
break;
if (fvdef == fstartlist)
{
fvdef = fvnone;
token.valid = FALSE;
}
break;
case '}':
if (definedef != dnone)
break;
if (!ignoreindent && lp == newlb.buffer + 1)
{
if (bracelev != 0)
token.valid = FALSE;
bracelev = 0;
parlev = 0;
}
else if (bracelev > 0)
bracelev--;
else
token.valid = FALSE;
popclass_above (bracelev);
structdef = snone;
if (typdef == tinbody && bracelev <= typdefbracelev)
{
assert (bracelev == typdefbracelev);
typdef = tend;
}
break;
case '=':
if (definedef != dnone)
break;
switch (fvdef)
{
case foperator:
case finlist:
case fignore:
case vignore:
break;
case fvnameseen:
if ((members && bracelev == 1)
|| (globals && bracelev == 0 && (!fvextern || declarations)))
make_C_tag (FALSE);
default:
fvdef = vignore;
}
break;
case '<':
if (cplpl
&& (structdef == stagseen || fvdef == fvnameseen))
{
templatelev++;
break;
}
goto resetfvdef;
case '>':
if (templatelev > 0)
{
templatelev--;
break;
}
goto resetfvdef;
case '+':
case '-':
if (objdef == oinbody && bracelev == 0)
{
objdef = omethodsign;
break;
}
resetfvdef:
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 (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 (; \
!feof (file_pointer) \
&& \
(readline (&line_buffer, file_pointer), \
char_pointer = line_buffer.buffer, \
TRUE); \
)
#define LOOKING_AT(cp, kw) \
((assert("" kw), TRUE) \
&& strneq ((cp), kw, sizeof(kw)-1) \
&& notinname ((cp)[sizeof(kw)-1]) \
&& ((cp) = skip_spaces((cp)+sizeof(kw)-1)))
#define LOOKING_AT_NOCASE(cp, kw) \
((assert("" kw), TRUE) \
&& strncaseeq ((cp), kw, sizeof(kw)-1) \
&& ((cp) += sizeof(kw)-1))
static void
just_read_file (inf)
FILE *inf;
{
register char *dummy;
LOOP_ON_INPUT_LINES (inf, lb, dummy)
continue;
}
static void F_takeprec __P((void));
static void F_getit __P((FILE *));
static void
F_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
F_getit (inf)
FILE *inf;
{
register char *cp;
dbp = skip_spaces (dbp);
if (*dbp == '\0')
{
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;
make_tag (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 (nocase_tail ("integer"))
F_takeprec ();
break;
case 'r':
if (nocase_tail ("real"))
F_takeprec ();
break;
case 'l':
if (nocase_tail ("logical"))
F_takeprec ();
break;
case 'c':
if (nocase_tail ("complex") || nocase_tail ("character"))
F_takeprec ();
break;
case 'd':
if (nocase_tail ("double"))
{
dbp = skip_spaces (dbp);
if (*dbp == '\0')
continue;
if (nocase_tail ("precision"))
break;
continue;
}
break;
}
dbp = skip_spaces (dbp);
if (*dbp == '\0')
continue;
switch (lowcase (*dbp))
{
case 'f':
if (nocase_tail ("function"))
F_getit (inf);
continue;
case 's':
if (nocase_tail ("subroutine"))
F_getit (inf);
continue;
case 'e':
if (nocase_tail ("entry"))
F_getit (inf);
continue;
case 'b':
if (nocase_tail ("blockdata") || nocase_tail ("block data"))
{
dbp = skip_spaces (dbp);
if (*dbp == '\0')
make_tag ("blockdata", 9, TRUE,
lb.buffer, dbp - lb.buffer, lineno, linecharno);
else
F_getit (inf);
}
continue;
}
}
}
static void Ada_getit __P((FILE *, char *));
static void
Ada_getit (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] == '-'))
{
readline (&lb, inf);
dbp = lb.buffer;
}
switch (lowcase(*dbp))
{
case 'b':
if (nocase_tail ("body"))
{
name_qualifier = "/b";
continue;
}
break;
case 't':
if (nocase_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;
make_tag (name, strlen (name), TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
free (name);
if (c == '"')
dbp = cp + 1;
return;
}
}
static void
Ada_funcs (inf)
FILE *inf;
{
bool inquote = FALSE;
bool skip_till_semicolumn = 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 (skip_till_semicolumn)
{
if (*dbp == ';')
skip_till_semicolumn = FALSE;
dbp++;
continue;
}
if (!begtoken (*dbp))
{
dbp++;
continue;
}
switch (lowcase(*dbp))
{
case 'f':
if (!packages_only && nocase_tail ("function"))
Ada_getit (inf, "/f");
else
break;
continue;
case 'p':
if (!packages_only && nocase_tail ("procedure"))
Ada_getit (inf, "/p");
else if (nocase_tail ("package"))
Ada_getit (inf, "/s");
else if (nocase_tail ("protected"))
Ada_getit (inf, "/t");
else
break;
continue;
case 'u':
if (typedefs && !packages_only && nocase_tail ("use"))
{
skip_till_semicolumn = TRUE;
continue;
}
case 't':
if (!packages_only && nocase_tail ("task"))
Ada_getit (inf, "/k");
else if (typedefs && !packages_only && nocase_tail ("type"))
{
Ada_getit (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))
make_tag (lb.buffer, cp - lb.buffer, TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
}
}
static void
Perl_functions (inf)
FILE *inf;
{
char *package = savestr ("main");
register char *cp;
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
skip_spaces(cp);
if (LOOKING_AT (cp, "package"))
{
free (package);
get_tag (cp, &package);
}
else if (LOOKING_AT (cp, "sub"))
{
char *pos;
char *sp = cp;
while (!notinname (*cp))
cp++;
if (cp == sp)
continue;
if ((pos = etags_strchr (sp, ':')) != NULL
&& pos < cp && pos[1] == ':')
make_tag (sp, cp - sp, TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
else
{
char savechar, *name;
savechar = *cp;
*cp = '\0';
name = concat (package, "::", sp);
*cp = savechar;
make_tag (name, strlen(name), TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
free (name);
}
}
else if (globals)
{
bool qual = LOOKING_AT (cp, "my") || LOOKING_AT (cp, "local");
char *varstart = cp;
if (qual
&& (*cp == '$' || *cp == '@' || *cp == '%'))
{
varstart += 1;
do
cp++;
while (ISALNUM (*cp) || *cp == '_');
}
else if (qual)
{
while (*cp != '\0' && *cp != ';' && *cp != '=' && *cp != ')')
cp++;
}
else
continue;
make_tag (varstart, cp - varstart, FALSE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
}
free (package);
}
static void
Python_functions (inf)
FILE *inf;
{
register char *cp;
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
cp = skip_spaces (cp);
if (LOOKING_AT (cp, "def") || LOOKING_AT (cp, "class"))
{
char *name = cp;
while (!notinname (*cp) && *cp != ':')
cp++;
make_tag (name, cp - name, TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
}
}
static void
PHP_functions (inf)
FILE *inf;
{
register char *cp, *name;
bool search_identifier = FALSE;
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
cp = skip_spaces (cp);
name = cp;
if (search_identifier
&& *cp != '\0')
{
while (!notinname (*cp))
cp++;
make_tag (name, cp - name, TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
search_identifier = FALSE;
}
else if (LOOKING_AT (cp, "function"))
{
if(*cp == '&')
cp = skip_spaces (cp+1);
if(*cp != '\0')
{
name = cp;
while (!notinname (*cp))
cp++;
make_tag (name, cp - name, TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
else
search_identifier = TRUE;
}
else if (LOOKING_AT (cp, "class"))
{
if (*cp != '\0')
{
name = cp;
while (*cp != '\0' && !iswhite (*cp))
cp++;
make_tag (name, cp - name, FALSE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
else
search_identifier = TRUE;
}
else if (strneq (cp, "define", 6)
&& (cp = skip_spaces (cp+6))
&& *cp++ == '('
&& (*cp == '"' || *cp == '\''))
{
char quote = *cp++;
name = cp;
while (*cp != quote && *cp != '\0')
cp++;
make_tag (name, cp - name, FALSE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
else if (members
&& LOOKING_AT (cp, "var")
&& *cp == '$')
{
name = cp;
while (!notinname(*cp))
cp++;
make_tag (name, cp - name, FALSE,
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++ == '.')
make_tag (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 == ':' || (globals && *bp == '='))
{
char * namestart = skip_spaces (lb.buffer);
while (--bp > namestart)
if (!notinname (*bp))
break;
make_tag (namestart, bp - namestart + 1, TRUE,
lb.buffer, bp - lb.buffer + 2, lineno, linecharno);
}
}
}
static void
Pascal_functions (inf)
FILE *inf;
{
linebuffer tline;
long save_lcno;
int save_lineno, namelen, taglen;
char c, *name;
bool
incomment,
inquote,
get_tagname,
found_tag,
inparms,
verify_tag;
save_lcno = save_lineno = namelen = taglen = 0;
name = NULL;
dbp = lb.buffer;
*dbp = '\0';
linebuffer_init (&tline);
incomment = inquote = FALSE;
found_tag = FALSE;
get_tagname = FALSE;
inparms = FALSE;
verify_tag = FALSE;
while (!feof (inf))
{
c = *dbp++;
if (c == '\0')
{
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 (nocase_tail ("extern"))
{
found_tag = FALSE;
verify_tag = FALSE;
}
}
else if (lowcase (*dbp) == 'f')
{
if (nocase_tail ("forward"))
{
found_tag = FALSE;
verify_tag = FALSE;
}
}
if (found_tag && verify_tag)
{
found_tag = FALSE;
verify_tag = FALSE;
make_tag (name, namelen, TRUE,
tline.buffer, taglen, save_lineno, save_lcno);
continue;
}
}
if (get_tagname)
{
char *cp;
if (*dbp == '\0')
continue;
for (cp = dbp + 1; *cp != '\0' && !endtoken (*cp); cp++)
continue;
linebuffer_setlen (&tline, lb.len);
strcpy (tline.buffer, lb.buffer);
save_lineno = lineno;
save_lcno = linecharno;
name = tline.buffer + (dbp - lb.buffer);
namelen = cp - dbp;
taglen = cp - lb.buffer + 1;
dbp = cp;
get_tagname = FALSE;
found_tag = TRUE;
continue;
}
else if (!incomment && !inquote && !found_tag)
{
switch (lowcase (c))
{
case 'p':
if (nocase_tail ("rocedure"))
get_tagname = TRUE;
continue;
case 'f':
if (nocase_tail ("unction"))
get_tagname = TRUE;
continue;
}
}
}
free (tline.buffer);
}
static void L_getit __P((void));
static void
L_getit ()
{
if (*dbp == '\'')
dbp++;
else if (*dbp == '(')
{
dbp++;
if (!LOOKING_AT (dbp, "quote") && !LOOKING_AT (dbp, "QUOTE"))
dbp = skip_spaces (dbp);
}
get_tag (dbp, NULL);
}
static void
Lisp_functions (inf)
FILE *inf;
{
LOOP_ON_INPUT_LINES (inf, lb, dbp)
{
if (dbp[0] != '(')
continue;
if (strneq (dbp+1, "def", 3) || strneq (dbp+1, "DEF", 3))
{
dbp = skip_non_spaces (dbp);
dbp = skip_spaces (dbp);
L_getit ();
}
else
{
do
dbp++;
while (!notinname (*dbp) && *dbp != ':');
if (*dbp == ':')
{
do
dbp++;
while (*dbp == ':');
if (strneq (dbp, "def", 3) || strneq (dbp, "DEF", 3))
{
dbp = skip_non_spaces (dbp);
dbp = skip_spaces (dbp);
L_getit ();
}
}
}
}
}
static void
Lua_functions (inf)
FILE *inf;
{
register char *bp;
LOOP_ON_INPUT_LINES (inf, lb, bp)
{
if (bp[0] != 'f' && bp[0] != 'l')
continue;
(void)LOOKING_AT (bp, "local");
if (LOOKING_AT (bp, "function"))
get_tag (bp, NULL);
}
}
static void
PS_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;
make_tag (bp, ep - bp, TRUE,
lb.buffer, ep - lb.buffer + 1, lineno, linecharno);
}
else if (LOOKING_AT (bp, "defineps"))
get_tag (bp, NULL);
}
}
static void
Forth_words (inf)
FILE *inf;
{
register char *bp;
LOOP_ON_INPUT_LINES (inf, lb, bp)
while ((bp = skip_spaces (bp))[0] != '\0')
if (bp[0] == '\\' && iswhite(bp[1]))
break;
else if (bp[0] == '(' && iswhite(bp[1]))
do
bp++;
while (*bp != ')' && *bp != '\0');
else if ((bp[0] == ':' && iswhite(bp[1]) && bp++)
|| LOOKING_AT_NOCASE (bp, "constant")
|| LOOKING_AT_NOCASE (bp, "code")
|| LOOKING_AT_NOCASE (bp, "create")
|| LOOKING_AT_NOCASE (bp, "defer")
|| LOOKING_AT_NOCASE (bp, "value")
|| LOOKING_AT_NOCASE (bp, "variable")
|| LOOKING_AT_NOCASE (bp, "buffer:")
|| LOOKING_AT_NOCASE (bp, "field"))
get_tag (skip_spaces (bp), NULL);
else
bp = skip_non_spaces (bp);
}
static void
Scheme_functions (inf)
FILE *inf;
{
register char *bp;
LOOP_ON_INPUT_LINES (inf, lb, bp)
{
if (strneq (bp, "(def", 4) || strneq (bp, "(DEF", 4))
{
bp = skip_non_spaces (bp+4);
while (notinname (*bp))
bp++;
get_tag (bp, NULL);
}
if (LOOKING_AT (bp, "(SET!") || LOOKING_AT (bp, "(set!"))
get_tag (bp, NULL);
}
}
static linebuffer *TEX_toktab = NULL;
static char *TEX_defenv = "\
:chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\
:part:appendix:entry:index:def\
:newcommand:renewcommand:newenvironment:renewenvironment";
static void TEX_mode __P((FILE *));
static void TEX_decode_env __P((char *, char *));
static char TEX_esc = '\\';
static char TEX_opgrp = '{';
static char TEX_clgrp = '}';
static void
TeX_commands (inf)
FILE *inf;
{
char *cp;
linebuffer *key;
TEX_mode (inf);
if (TEX_toktab == NULL)
TEX_decode_env ("TEXTAGS", TEX_defenv);
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
for (;;)
{
while (*cp++ != TEX_esc)
if (cp[-1] == '\0' || cp[-1] == '%')
goto tex_next_line;
for (key = TEX_toktab; key->buffer != NULL; key++)
if (strneq (cp, key->buffer, key->len))
{
register char *p;
int namelen, linelen;
bool opgrp = FALSE;
cp = skip_spaces (cp + key->len);
if (*cp == TEX_opgrp)
{
opgrp = TRUE;
cp++;
}
for (p = cp;
(!iswhite (*p) && *p != '#' &&
*p != TEX_opgrp && *p != TEX_clgrp);
p++)
continue;
namelen = p - cp;
linelen = lb.len;
if (!opgrp || *p == TEX_clgrp)
{
while (*p != '\0' && *p != TEX_opgrp && *p != TEX_clgrp)
p++;
linelen = p - lb.buffer + 1;
}
make_tag (cp, namelen, TRUE,
lb.buffer, linelen, lineno, linecharno);
goto tex_next_line;
}
}
tex_next_line:
;
}
}
#define TEX_LESC '\\'
#define TEX_SESC '!'
static void
TEX_mode (inf)
FILE *inf;
{
int c;
while ((c = getc (inf)) != EOF)
{
if (c == '%')
while (c != '\n' && c != EOF)
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 void
TEX_decode_env (evarname, defenv)
char *evarname;
char *defenv;
{
register char *env, *p;
int i, len;
env = getenv (evarname);
if (!env)
env = defenv;
else
{
char *oldenv = env;
env = concat (oldenv, defenv, "");
}
for (len = 1, p = env; p;)
if ((p = etags_strchr (p, ':')) && *++p != '\0')
len++;
TEX_toktab = xnew (len, linebuffer);
for (i = 0; *env != '\0';)
{
p = etags_strchr (env, ':');
if (!p)
p = env + strlen (env);
if (p - env > 0)
{
TEX_toktab[i].buffer = savenstr (env, p - env);
TEX_toktab[i].len = p - env;
i++;
}
if (*p)
env = p + 1;
else
{
TEX_toktab[i].buffer = NULL;
TEX_toktab[i].len = 0;
break;
}
}
}
static void
Texinfo_nodes (inf)
FILE * inf;
{
char *cp, *start;
LOOP_ON_INPUT_LINES (inf, lb, cp)
if (LOOKING_AT (cp, "@node"))
{
start = cp;
while (*cp != '\0' && *cp != ',')
cp++;
make_tag (start, cp - start, TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
}
static void
HTML_labels (inf)
FILE * inf;
{
bool getnext = FALSE;
bool skiptag = FALSE;
bool intag = FALSE;
bool inanchor = FALSE;
char *end;
linebuffer_setlen (&token_name, 0);
LOOP_ON_INPUT_LINES (inf, lb, dbp)
for (;;)
{
if (skiptag)
{
while (*dbp != '\0' && *dbp != '>')
dbp++;
if (*dbp == '>')
{
dbp += 1;
skiptag = FALSE;
continue;
}
break;
}
else if (intag)
{
while (*dbp != '\0' && *dbp != '>'
&& lowcase (*dbp) != 'n' && lowcase (*dbp) != 'i')
dbp++;
if (*dbp == '\0')
break;
if (*dbp == '>')
{
dbp += 1;
intag = FALSE;
continue;
}
if ((inanchor && LOOKING_AT_NOCASE (dbp, "name="))
|| LOOKING_AT_NOCASE (dbp, "id="))
{
bool quoted = (dbp[0] == '"');
if (quoted)
for (end = ++dbp; *end != '\0' && *end != '"'; end++)
continue;
else
for (end = dbp; *end != '\0' && intoken (*end); end++)
continue;
linebuffer_setlen (&token_name, end - dbp);
strncpy (token_name.buffer, dbp, end - dbp);
token_name.buffer[end - dbp] = '\0';
dbp = end;
intag = FALSE;
skiptag = TRUE;
getnext = TRUE;
continue;
}
dbp += 1;
}
else if (getnext)
{
dbp = skip_spaces (dbp);
if (*dbp == '\0')
break;
if (*dbp == '<')
{
intag = TRUE;
inanchor = (lowcase (dbp[1]) == 'a' && !intoken (dbp[2]));
continue;
}
for (end = dbp + 1; *end != '\0' && *end != '<'; end++)
continue;
make_tag (token_name.buffer, token_name.len, TRUE,
dbp, end - dbp, lineno, linecharno);
linebuffer_setlen (&token_name, 0);
getnext = FALSE;
break;
}
else
{
while (*dbp != '\0' && *dbp != '<')
dbp++;
if (*dbp == '\0')
break;
intag = TRUE;
if (lowcase (dbp[1]) == 'a' && !intoken (dbp[2]))
{
inanchor = TRUE;
continue;
}
else if (LOOKING_AT_NOCASE (dbp, "<title>")
|| LOOKING_AT_NOCASE (dbp, "<h1>")
|| LOOKING_AT_NOCASE (dbp, "<h2>")
|| LOOKING_AT_NOCASE (dbp, "<h3>"))
{
intag = FALSE;
getnext = TRUE;
continue;
}
dbp += 1;
}
}
}
static int prolog_pr __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_pr (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';
}
}
if (last != NULL)
free (last);
}
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;
readline (plb, inf);
}
while (!feof(inf));
}
static int
prolog_pr (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] == '(' && (pos += 1))
|| (s[pos] == ':' && s[pos + 1] == '-' && (pos += 2)))
&& (last == NULL
|| len != (int)strlen (last)
|| !strneq (s, last, len)))
{
make_tag (s, len, TRUE, s, pos, lineno, linecharno);
return len;
}
else
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++;
for (;;)
{
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 *));
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);
if (last != NULL)
{
free (last);
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';
}
}
if (last != NULL)
free (last);
}
static int
erlang_func (s, last)
char *s;
char *last;
{
int pos;
int len;
pos = erlang_atom (s);
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)))
{
make_tag (s, len, TRUE, s, pos, lineno, linecharno);
return len;
}
return 0;
}
static void
erlang_attribute (s)
char *s;
{
char *cp = s;
if ((LOOKING_AT (cp, "-define") || LOOKING_AT (cp, "-record"))
&& *cp++ == '(')
{
int len = erlang_atom (skip_spaces (cp));
if (len > 0)
make_tag (cp, len, TRUE, s, cp + len - s, lineno, linecharno);
}
return;
}
static int
erlang_atom (s)
char *s;
{
int pos = 0;
if (ISALPHA (s[pos]) || s[pos] == '_')
{
do
pos++;
while (ISALNUM (s[pos]) || s[pos] == '_');
}
else if (s[pos] == '\'')
{
for (pos++; s[pos] != '\''; pos++)
if (s[pos] == '\0'
|| (s[pos] == '\\' && s[++pos] == '\0'))
return 0;
pos++;
}
return pos;
}
static char *scan_separators __P((char *));
static void add_regex __P((char *, 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)
{
switch (*name)
{
case 'a': *copyto++ = '\007'; break;
case 'b': *copyto++ = '\b'; break;
case 'd': *copyto++ = 0177; break;
case 'e': *copyto++ = 033; break;
case 'f': *copyto++ = '\f'; break;
case 'n': *copyto++ = '\n'; break;
case 'r': *copyto++ = '\r'; break;
case 't': *copyto++ = '\t'; break;
case 'v': *copyto++ = '\v'; break;
default:
if (*name == sep)
*copyto++ = sep;
else
{
*copyto++ = '\\';
*copyto++ = *name;
}
break;
}
quoted = FALSE;
}
else if (*name == '\\')
quoted = TRUE;
else if (*name == sep)
break;
else
*copyto++ = *name;
}
if (*name != sep)
name = NULL;
*copyto = '\0';
return name;
}
static void
analyse_regex (regex_arg)
char *regex_arg;
{
if (regex_arg == NULL)
{
free_regexps ();
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;
}
linebuffer_init (®exbuf);
while (readline_internal (®exbuf, regexfp) > 0)
analyse_regex (regexbuf.buffer);
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, lang);
}
break;
default:
add_regex (regex_arg, NULL);
break;
}
}
static void
add_regex (regexp_pattern, lang)
char *regexp_pattern;
language *lang;
{
static struct re_pattern_buffer zeropattern;
char sep, *pat, *name, *modifiers;
const char *err;
struct re_pattern_buffer *patbuf;
regexp *rp;
bool
force_explicit_name = TRUE,
ignore_case = FALSE,
multi_line = FALSE,
single_line = FALSE;
if (strlen(regexp_pattern) < 3)
{
error ("null regexp", (char *)NULL);
return;
}
sep = regexp_pattern[0];
name = scan_separators (regexp_pattern);
if (name == NULL)
{
error ("%s: unterminated regexp", regexp_pattern);
return;
}
if (name[1] == sep)
{
error ("null name for regexp \"%s\"", regexp_pattern);
return;
}
modifiers = scan_separators (name);
if (modifiers == NULL)
{
modifiers = name;
name = "";
}
else
modifiers += 1;
for (; modifiers[0] != '\0'; modifiers++)
switch (modifiers[0])
{
case 'N':
if (modifiers == name)
error ("forcing explicit tag name but no name, ignoring", NULL);
force_explicit_name = TRUE;
break;
case 'i':
ignore_case = TRUE;
break;
case 's':
single_line = TRUE;
case 'm':
multi_line = TRUE;
need_filebuf = TRUE;
break;
default:
{
char wrongmod [2];
wrongmod[0] = modifiers[0];
wrongmod[1] = '\0';
error ("invalid regexp modifier `%s', ignoring", wrongmod);
}
break;
}
patbuf = xnew (1, struct re_pattern_buffer);
*patbuf = zeropattern;
if (ignore_case)
{
static char lc_trans[CHARS];
int i;
for (i = 0; i < CHARS; i++)
lc_trans[i] = lowcase (i);
patbuf->translate = lc_trans;
}
if (multi_line)
pat = concat ("^", regexp_pattern, "");
else
pat = regexp_pattern;
if (single_line)
re_set_syntax (RE_SYNTAX_EMACS | RE_DOT_NEWLINE);
else
re_set_syntax (RE_SYNTAX_EMACS);
err = re_compile_pattern (pat, strlen (regexp_pattern), patbuf);
if (multi_line)
free (pat);
if (err != NULL)
{
error ("%s while compiling pattern", err);
return;
}
rp = p_head;
p_head = xnew (1, regexp);
p_head->pattern = savestr (regexp_pattern);
p_head->p_next = rp;
p_head->lang = lang;
p_head->pat = patbuf;
p_head->name = savestr (name);
p_head->error_signaled = FALSE;
p_head->force_explicit_name = force_explicit_name;
p_head->ignore_case = ignore_case;
p_head->multi_line = multi_line;
}
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;
assert (size >= 0);
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);
assert (t - result == (int)strlen (result));
return result;
}
static void
free_regexps ()
{
regexp *rp;
while (p_head != NULL)
{
rp = p_head->p_next;
free (p_head->pattern);
free (p_head->name);
free (p_head);
p_head = rp;
}
return;
}
static void
regex_tag_multiline ()
{
char *buffer = filebuf.buffer;
regexp *rp;
char *name;
for (rp = p_head; rp != NULL; rp = rp->p_next)
{
int match = 0;
if (!rp->multi_line)
continue;
lineno = 1;
charno = 0;
linecharno = 0;
if (rp->lang != NULL && rp->lang != curfdp->lang)
continue;
while (match >= 0 && match < filebuf.len)
{
match = re_search (rp->pat, buffer, filebuf.len, charno,
filebuf.len - match, &rp->regs);
switch (match)
{
case -2:
if (!rp->error_signaled)
{
error ("regexp stack overflow while matching \"%s\"",
rp->pattern);
rp->error_signaled = TRUE;
}
break;
case -1:
break;
default:
if (match == rp->regs.end[0])
{
if (!rp->error_signaled)
{
error ("regexp matches the empty string: \"%s\"",
rp->pattern);
rp->error_signaled = TRUE;
}
match = -3;
break;
}
while (charno < rp->regs.end[0])
if (buffer[charno++] == '\n')
lineno++, linecharno = charno;
name = rp->name;
if (name[0] == '\0')
name = NULL;
else
name = substitute (buffer, rp->name, &rp->regs);
if (rp->force_explicit_name)
pfnote (name, TRUE, buffer + linecharno,
charno - linecharno + 1, lineno, linecharno);
else
make_tag (name, strlen (name), TRUE, buffer + linecharno,
charno - linecharno + 1, lineno, linecharno);
break;
}
}
}
}
static bool
nocase_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
get_tag (bp, namepp)
register char *bp;
char **namepp;
{
register char *cp = bp;
if (*bp != '\0')
{
for (cp = bp + 1; !notinname (*cp); cp++)
continue;
make_tag (bp, cp - bp, TRUE,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
}
if (namepp != NULL)
*namepp = savenstr (bp, cp - bp);
}
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;
for (;;)
{
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;
if (need_filebuf
&& chars_deleted > 0)
{
while (filebuf.size <= filebuf.len + lbp->len + 1)
{
filebuf.size *= 2;
xrnew (filebuf.buffer, filebuf.size, char);
}
strncpy (filebuf.buffer + filebuf.len, lbp->buffer, lbp->len);
filebuf.len += lbp->len;
filebuf.buffer[filebuf.len++] = '\n';
filebuf.buffer[filebuf.len] = '\0';
}
return lbp->len + chars_deleted;
}
static void
readline (lbp, stream)
linebuffer *lbp;
FILE *stream;
{
long result;
linecharno = charno;
result = readline_internal (lbp, stream);
lineno += 1;
charno += result;
if (!no_line_directive)
{
static bool discard_until_line_directive;
if (result > 12 && strneq (lbp->buffer, "#line ", 6))
{
unsigned int lno;
int start = 0;
if (sscanf (lbp->buffer, "#line %u \"%n", &lno, &start) >= 1
&& start > 0)
{
char *endp = lbp->buffer + start;
while ((endp = etags_strchr (endp, '"')) != NULL
&& endp[-1] == '\\')
endp++;
if (endp != NULL)
{
char *taggedabsname;
char *taggedfname;
char *name;
discard_until_line_directive = FALSE;
name = lbp->buffer + start;
*endp = '\0';
canonicalize_filename (name);
taggedabsname = absolute_filename (name, tagfiledir);
if (filename_is_absolute (name)
|| filename_is_absolute (curfdp->infname))
taggedfname = savestr (taggedabsname);
else
taggedfname = relative_filename (taggedabsname,tagfiledir);
if (streq (curfdp->taggedfname, taggedfname))
free (taggedfname);
else
{
fdesc *fdp;
for (fdp = fdhead; fdp != NULL; fdp = fdp->next)
if (streq (fdp->infname, curfdp->infname)
&& streq (fdp->taggedfname, taggedfname))
{
curfdp = fdp;
free (taggedfname);
break;
}
if (fdp == NULL)
for (fdp = fdhead; fdp != NULL; fdp = fdp->next)
if (streq (fdp->infabsname, taggedabsname))
{
discard_until_line_directive = TRUE;
free (taggedfname);
break;
}
if (fdp == NULL)
{
fdp = fdhead;
fdhead = xnew (1, fdesc);
*fdhead = *curfdp;
fdhead->next = fdp;
fdhead->infname = savestr (curfdp->infname);
fdhead->infabsname = savestr (curfdp->infabsname);
fdhead->infabsdir = savestr (curfdp->infabsdir);
fdhead->taggedfname = taggedfname;
fdhead->usecharno = FALSE;
fdhead->prop = NULL;
fdhead->written = FALSE;
curfdp = fdhead;
}
}
free (taggedabsname);
lineno = lno - 1;
readline (lbp, stream);
return;
}
}
}
if (discard_until_line_directive)
{
if (result > 0)
{
readline (lbp, stream);
return;
}
discard_until_line_directive = FALSE;
return;
}
}
{
int match;
regexp *rp;
char *name;
if (lbp->len > 0)
for (rp = p_head; rp != NULL; rp = rp->p_next)
{
if ((rp->lang != NULL && rp->lang != fdhead->lang)
|| rp->multi_line)
continue;
match = re_match (rp->pat, lbp->buffer, lbp->len, 0, &rp->regs);
switch (match)
{
case -2:
if (!rp->error_signaled)
{
error ("regexp stack overflow while matching \"%s\"",
rp->pattern);
rp->error_signaled = TRUE;
}
break;
case -1:
break;
case 0:
if (!rp->error_signaled)
{
error ("regexp matches the empty string: \"%s\"", rp->pattern);
rp->error_signaled = TRUE;
}
break;
default:
name = rp->name;
if (name[0] == '\0')
name = NULL;
else
name = substitute (lbp->buffer, rp->name, &rp->regs);
if (rp->force_explicit_name)
pfnote (name, TRUE, lbp->buffer, match, lineno, linecharno);
else
make_tag (name, strlen (name), TRUE,
lbp->buffer, match, lineno, linecharno);
break;
}
}
}
}
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 int
etags_strcasecmp (s1, s2)
register const char *s1;
register const char *s2;
{
while (*s1 != '\0'
&& (ISALPHA (*s1) && ISALPHA (*s2)
? lowcase (*s1) == lowcase (*s2)
: *s1 == *s2))
s1++, s2++;
return (ISALPHA (*s1) && ISALPHA (*s2)
? lowcase (*s1) - lowcase (*s2)
: *s1 - *s2);
}
static int
etags_strncasecmp (s1, s2, n)
register const char *s1;
register const char *s2;
register int n;
{
while (*s1 != '\0' && n-- > 0
&& (ISALPHA (*s1) && ISALPHA (*s2)
? lowcase (*s1) == lowcase (*s2)
: *s1 == *s2))
s1++, s2++;
if (n < 0)
return 0;
else
return (ISALPHA (*s1) && ISALPHA (*s2)
? lowcase (*s1) - lowcase (*s2)
: *s1 - *s2);
}
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 (EXIT_FAILURE);
}
static void
pfatal (s1)
char *s1;
{
perror (s1);
exit (EXIT_FAILURE);
}
static void
suggest_asking_for_help ()
{
fprintf (stderr, "\tTry `%s %s' for a complete list of options.\n",
progname, NO_LONG_OPTIONS ? "-h" : "--help");
exit (EXIT_FAILURE);
}
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;
linebuffer_init (&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')
{
free (res);
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_init (lbp)
linebuffer *lbp;
{
lbp->size = (DEBUG) ? 3 : 200;
lbp->buffer = xnew (lbp->size, char);
lbp->buffer[0] = '\0';
lbp->len = 0;
}
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;
}
static PTR
xmalloc (size)
unsigned int size;
{
PTR result = (PTR) malloc (size);
if (result == NULL)
fatal ("virtual memory exhausted", (char *)NULL);
return result;
}
static PTR
xrealloc (ptr, size)
char *ptr;
unsigned int size;
{
PTR result = (PTR) realloc (ptr, size);
if (result == NULL)
fatal ("virtual memory exhausted", (char *)NULL);
return result;
}