* 1992 Joseph B. Wells improved C and C++ parsing.
* 1993 Francesco Potortì reorganised C and C++.
* 1994 Regexp tags by Tom Tromey.
- * 2001 Nested classes by Francesco Potortì (ideas by Mykola Dzyuba).
+ * 2001 Nested classes by Francesco Potortì (concept by Mykola Dzyuba).
+ * 2002 #line directives by Francesco Potortì.
*
* Francesco Potortì <pot@gnu.org> has maintained it since 1993.
*/
-char pot_etags_version[] = "@(#) pot revision number is 15.16";
+char pot_etags_version[] = "@(#) pot revision number is 16.4";
#define TRUE 1
#define FALSE 0
at_language, /* a language specification */
at_regexp, /* a regular expression */
at_icregexp, /* same, but with case ignored */
- at_filename /* a file name */
+ at_filename, /* a file name */
+ at_stdin /* read from stdin here */
} arg_type; /* argument type */
language *lang; /* language associated with the argument */
char *what; /* the argument itself */
static void init __P((void));
static void initbuffer __P((linebuffer *));
-static void process_file __P((char *, language *));
+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 bool globals; /* create tags for global variables */
static bool declarations; /* --declarations: tag them and extern in C&Co*/
static bool members; /* create tags for C member variables */
-static bool no_line_directive; /* ignore #line directives */
+static bool no_line_directive; /* ignore #line directives (undocumented) */
static bool update; /* -u: update tags */
static bool vgrind_style; /* -v: create vgrind style index output */
static bool no_warnings; /* -w: suppress warnings */
static bool noindentypedefs; /* -I: ignore indentation in C */
static bool packages_only; /* --packages-only: in Ada, only tag packages*/
+#define STDIN 0x1001 /* returned by getopt_long on --parse-stdin */
+static bool parsing_stdin; /* --parse-stdin used */
+
#ifdef ETAGS_REGEXPS
/* List of all regexps. */
static pattern *p_head;
{ "no-regex", no_argument, NULL, 'R' },
{ "ignore-case-regex", required_argument, NULL, 'c' },
#endif /* ETAGS_REGEXPS */
+ { "parse-stdin", required_argument, NULL, STDIN },
{ "version", no_argument, NULL, 'V' },
#if CTAGS /* Etags options */
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 ("--parse-stdin=NAME\n\
+ Read from standard input and record tags as belonging to file NAME.");
+
if (!CTAGS)
puts ("-a, --append\n\
Append tag entries to existing tags file.");
#ifdef VMS
bool got_err;
#endif
+ char *optstring;
+ int opt;
+
#ifdef DOS_NT
_fmode = O_BINARY; /* all of files are treated as binary files */
globals = TRUE;
}
- while (1)
- {
- int opt;
- char *optstring = "-";
-
+ optstring = "-";
#ifdef ETAGS_REGEXPS
- optstring = "-r:Rc:";
+ optstring = "-r:Rc:";
#endif /* ETAGS_REGEXPS */
-
#ifndef LONG_OPTIONS
- optstring = optstring + 1;
+ optstring = optstring + 1;
#endif /* LONG_OPTIONS */
+ optstring = concat (optstring,
+ "Cf:Il:o:SVhH",
+ (CTAGS) ? "BxdtTuvw" : "aDi:");
- optstring = concat (optstring,
- "Cf:Il:o:SVhH",
- (CTAGS) ? "BxdtTuvw" : "aDi:");
-
- opt = getopt_long (argc, argv, optstring, longopts, 0);
- if (opt == EOF)
+ while ((opt = getopt_long (argc, argv, optstring, longopts, 0)) != EOF)
+ switch (opt)
+ {
+ case 0:
+ /* If getopt returns 0, then it has already processed a
+ long-named option. We should do nothing. */
break;
- switch (opt)
- {
- case 0:
- /* If getopt returns 0, then it has already processed a
- long-named option. We should do nothing. */
- break;
+ case 1:
+ /* This means that a file name has been seen. Record it. */
+ argbuffer[current_arg].arg_type = at_filename;
+ argbuffer[current_arg].what = optarg;
+ ++current_arg;
+ ++file_count;
+ break;
- case 1:
- /* This means that a file name has been seen. Record it. */
- argbuffer[current_arg].arg_type = at_filename;
- argbuffer[current_arg].what = optarg;
- ++current_arg;
- ++file_count;
- break;
+ case STDIN:
+ /* Parse standard input. Idea by Vivek <vivek@etla.org>. */
+ argbuffer[current_arg].arg_type = at_stdin;
+ argbuffer[current_arg].what = optarg;
+ ++current_arg;
+ ++file_count;
+ parsing_stdin = TRUE;
+ break;
- /* Common options. */
- case 'C': cplusplus = TRUE; break;
- case 'f': /* for compatibility with old makefiles */
- 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': /* for backward compatibility */
- noindentypedefs = TRUE;
- break;
- case 'l':
+ /* Common options. */
+ case 'C': cplusplus = TRUE; break;
+ case 'f': /* for compatibility with old makefiles */
+ case 'o':
+ if (tagfile)
{
- language *lang = get_language_from_langname (optarg);
- if (lang != NULL)
- {
- argbuffer[current_arg].lang = lang;
- argbuffer[current_arg].arg_type = at_language;
- ++current_arg;
- }
+ error ("-o option may only be given once.", (char *)NULL);
+ suggest_asking_for_help ();
}
- break;
- case 'r':
- argbuffer[current_arg].arg_type = at_regexp;
- argbuffer[current_arg].what = optarg;
- ++current_arg;
- break;
- case 'R':
- argbuffer[current_arg].arg_type = at_regexp;
- argbuffer[current_arg].what = NULL;
- ++current_arg;
- break;
- case 'c':
- argbuffer[current_arg].arg_type = at_icregexp;
- argbuffer[current_arg].what = optarg;
- ++current_arg;
- break;
- case 'V':
- print_version ();
- break;
- case 'h':
- case 'H':
- print_help ();
- break;
-
- /* Etags options */
- case 'a': append_to_tagfile = TRUE; break;
- case 'D': constantypedefs = FALSE; break;
- case 'i': included_files[nincluded_files++] = optarg; break;
-
- /* Ctags options. */
- 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; /*FALLTHRU*/
- case 'x': cxref_style = TRUE; break;
- case 'w': no_warnings = TRUE; break;
- default:
- suggest_asking_for_help ();
+ tagfile = optarg;
+ break;
+ case 'I':
+ case 'S': /* for backward compatibility */
+ noindentypedefs = TRUE;
+ break;
+ case 'l':
+ {
+ language *lang = get_language_from_langname (optarg);
+ if (lang != NULL)
+ {
+ argbuffer[current_arg].lang = lang;
+ argbuffer[current_arg].arg_type = at_language;
+ ++current_arg;
+ }
}
- }
+ break;
+ case 'r':
+ argbuffer[current_arg].arg_type = at_regexp;
+ argbuffer[current_arg].what = optarg;
+ ++current_arg;
+ break;
+ case 'R':
+ argbuffer[current_arg].arg_type = at_regexp;
+ argbuffer[current_arg].what = NULL;
+ ++current_arg;
+ break;
+ case 'c':
+ argbuffer[current_arg].arg_type = at_icregexp;
+ argbuffer[current_arg].what = optarg;
+ ++current_arg;
+ break;
+ case 'V':
+ print_version ();
+ break;
+ case 'h':
+ case 'H':
+ print_help ();
+ break;
+
+ /* Etags options */
+ case 'a': append_to_tagfile = TRUE; break;
+ case 'D': constantypedefs = FALSE; break;
+ case 'i': included_files[nincluded_files++] = optarg; break;
+
+ /* Ctags options. */
+ 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; /*FALLTHRU*/
+ case 'x': cxref_style = TRUE; break;
+ case 'w': no_warnings = TRUE; break;
+ default:
+ suggest_asking_for_help ();
+ }
for (; optind < argc; ++optind)
{
/* Input file named "-" means read file names from stdin
(one per line) and use them. */
if (streq (this_file, "-"))
- while (readline_internal (&filename_lb, stdin) > 0)
- process_file (filename_lb.buffer, lang);
+ {
+ 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 (this_file, lang);
+ 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;
}
}
char cmd[BUFSIZ];
for (i = 0; i < current_arg; ++i)
{
- if (argbuffer[i].arg_type != at_filename)
- continue;
+ switch (argbuffer[i].arg_type)
+ {
+ case at_filename:
+ case at_stdin:
+ break;
+ default:
+ continue; /* the for loop */
+ }
sprintf (cmd,
"mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
tagfile, argbuffer[i].what, tagfile);
if (update)
{
- char cmd[BUFSIZ];
- sprintf (cmd, "sort %s -o %s", tagfile, tagfile);
+ char cmd[2*BUFSIZ+10];
+ sprintf (cmd, "sort -o %.*s %.*s", BUFSIZ, tagfile, BUFSIZ, tagfile);
exit (system (cmd));
}
return GOOD;
* This routine is called on each file argument.
*/
static void
-process_file (file, lang)
+process_file_name (file, lang)
char *file;
language *lang;
{
struct stat stat_buf;
FILE *inf;
- static const fdesc emptyfdesc;
fdesc *fdp;
compressor *compr;
char *compressed_name, *uncompressed_name;
char *ext, *real_name;
int retval;
-
canonicalize_filename (file);
if (streq (file, tagfile) && !streq (tagfile, "-"))
{
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;
+
/* Create a new input file description entry. */
fdp = xnew (1, fdesc);
*fdp = emptyfdesc;
fdp->next = fdhead;
- fdp->infname = savestr (uncompressed_name);
+ fdp->infname = savestr (fn);
fdp->lang = lang;
- fdp->infabsname = absolute_filename (uncompressed_name, cwd);
- fdp->infabsdir = absolute_dirname (uncompressed_name, cwd);
- if (filename_is_absolute (uncompressed_name))
+ fdp->infabsname = absolute_filename (fn, cwd);
+ fdp->infabsdir = absolute_dirname (fn, cwd);
+ if (filename_is_absolute (fn))
{
- /* file is an absolute file name. Canonicalize it. */
- fdp->taggedfname = absolute_filename (uncompressed_name, NULL);
+ /* An absolute file name. Canonicalize it. */
+ fdp->taggedfname = absolute_filename (fn, NULL);
}
else
{
- /* file is a file name relative to cwd. Make it relative
+ /* A file name relative to cwd. Make it relative
to the directory of the tags file. */
- fdp->taggedfname = relative_filename (uncompressed_name, tagfiledir);
+ fdp->taggedfname = relative_filename (fn, tagfiledir);
}
fdp->usecharno = TRUE; /* use char position when making tags */
fdp->prop = NULL;
fdhead = fdp;
curfdp = fdhead; /* the current file description */
- find_entries (inf);
-
- if (real_name == compressed_name)
- retval = pclose (inf);
- else
- retval = fclose (inf);
- if (retval < 0)
- pfatal (file);
+ find_entries (fh);
/* If not Ctags, and if this is not metasource and if it contained no #line
directives, we can write the tags and free all nodes pointing to
prev->left = NULL; /* delete the pointer to the sublist */
}
}
-
- cleanup:
- if (compressed_name) free (compressed_name);
- if (uncompressed_name) free (uncompressed_name);
- last_node = NULL;
- curfdp = NULL;
- return;
}
/*
break;
case dsharpseen:
savetoken = token;
+ break;
}
if (!yacc_rules || lp == newlb.buffer + 1)
{
&& typdef == tnone)
cblev = -1;
}
+ break;
}
switch (structdef)
{
/* Find tags in TeX and LaTeX input files. */
/* TEX_toktab is a table of TeX control sequences that define tags.
- Each TEX_tabent records one such control sequence.
- CONVERT THIS TO USE THE Stab TYPE!! */
-struct TEX_tabent
-{
- char *name;
- int len;
-};
+ * Each entry records one such control sequence.
+ *
+ * Original code from who knows whom.
+ * Ideas by:
+ * Stefan Monnier (2002)
+ */
-static struct TEX_tabent *TEX_toktab = NULL; /* Table with tag tokens */
+static linebuffer *TEX_toktab = NULL; /* Table with tag tokens */
/* Default set of control sequences to put into TEX_toktab.
The value of environment var TEXTAGS is prepended to this. */
-
static char *TEX_defenv = "\
:chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\
-:part:appendix:entry:index";
+:part:appendix:entry:index:def\
+:newcommand:renewcommand:newenvironment:renewenvironment";
static void TEX_mode __P((FILE *));
-static struct TEX_tabent *TEX_decode_env __P((char *, char *));
-static int TEX_Token __P((char *));
+static void TEX_decode_env __P((char *, char *));
static char TEX_esc = '\\';
static char TEX_opgrp = '{';
TeX_commands (inf)
FILE *inf;
{
- char *cp, *lasthit;
- register int i;
+ char *cp;
+ linebuffer *key;
/* Select either \ or ! as escape character. */
TEX_mode (inf);
/* Initialize token table once from environment. */
- if (!TEX_toktab)
- TEX_toktab = TEX_decode_env ("TEXTAGS", TEX_defenv);
+ if (TEX_toktab == NULL)
+ TEX_decode_env ("TEXTAGS", TEX_defenv);
LOOP_ON_INPUT_LINES (inf, lb, cp)
{
- lasthit = cp;
- /* Look at each esc in line. */
- while ((cp = etags_strchr (cp, TEX_esc)) != NULL)
+ /* Look at each TEX keyword in line. */
+ for (;;)
{
- if (*++cp == '\0')
- break;
- linecharno += cp - lasthit;
- lasthit = cp;
- i = TEX_Token (lasthit);
- if (i >= 0)
- {
- register char *p;
- for (lasthit += TEX_toktab[i].len;
- *lasthit == TEX_esc || *lasthit == TEX_opgrp;
- lasthit++)
- continue;
- for (p = lasthit;
- !iswhite (*p) && *p != TEX_opgrp && *p != TEX_clgrp;
- p++)
- continue;
- pfnote (savenstr (lasthit, p-lasthit), TRUE,
- lb.buffer, lb.len, lineno, linecharno);
- break; /* We only tag a line once */
- }
+ /* Look for a TEX escape. */
+ 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;
+ char *name;
+ int 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;
+ name = savenstr (cp, 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;
+ }
+ pfnote (name, TRUE, lb.buffer, linelen, lineno, linecharno);
+ goto tex_next_line; /* We only tag a line once */
+ }
}
+ tex_next_line:
+ ;
}
}
#define TEX_LESC '\\'
#define TEX_SESC '!'
-#define TEX_cmt '%'
/* Figure out whether TeX's escapechar is '\\' or '!' and set grouping
chars accordingly. */
while ((c = getc (inf)) != EOF)
{
/* Skip to next line if we hit the TeX comment char. */
- if (c == TEX_cmt)
+ if (c == '%')
while (c != '\n')
c = getc (inf);
else if (c == TEX_LESC || c == TEX_SESC )
/* Read environment and prepend it to the default string.
Build token table. */
-static struct TEX_tabent *
+static void
TEX_decode_env (evarname, defenv)
char *evarname;
char *defenv;
{
register char *env, *p;
-
- struct TEX_tabent *tab;
- int size, i;
+ int i, len;
/* Append default string to environment. */
env = getenv (evarname);
}
/* Allocate a token table */
- for (size = 1, p = env; p;)
+ for (len = 1, p = env; p;)
if ((p = etags_strchr (p, ':')) && *++p != '\0')
- size++;
- /* Add 1 to leave room for null terminator. */
- tab = xnew (size + 1, struct TEX_tabent);
+ len++;
+ TEX_toktab = xnew (len, linebuffer);
/* Unpack environment string into token table. Be careful about */
/* zero-length strings (leading ':', "::" and trailing ':') */
- for (i = 0; *env;)
+ for (i = 0; *env != '\0';)
{
p = etags_strchr (env, ':');
if (!p) /* End of environment string. */
p = env + strlen (env);
if (p - env > 0)
{ /* Only non-zero strings. */
- tab[i].name = savenstr (env, p - env);
- tab[i].len = strlen (tab[i].name);
+ TEX_toktab[i].buffer = savenstr (env, p - env);
+ TEX_toktab[i].len = p - env;
i++;
}
if (*p)
env = p + 1;
else
{
- tab[i].name = NULL; /* Mark end of table. */
- tab[i].len = 0;
+ TEX_toktab[i].buffer = NULL; /* Mark end of table. */
+ TEX_toktab[i].len = 0;
break;
}
}
- return tab;
-}
-
-/* If the text at CP matches one of the tag-defining TeX command names,
- return the pointer to the first occurrence of that command in TEX_toktab.
- Otherwise return -1.
- Keep the capital `T' in `token' for dumb truncating compilers
- (this distinguishes it from `TEX_toktab' */
-static int
-TEX_Token (cp)
- char *cp;
-{
- int i;
-
- for (i = 0; TEX_toktab[i].len > 0; i++)
- if (strneq (TEX_toktab[i].name, cp, TEX_toktab[i].len))
- return i;
- return -1;
}
\f
{
pos++;
- while (1)
+ for (;;)
{
if (s[pos] == '\'')
{
{
pos++;
- while (1)
+ for (;;)
{
if (s[pos] == '\'')
{
pend = p + lbp->size; /* Separate to avoid 386/IX compiler bug. */
- while (1)
+ for (;;)
{
register int c = getc (stream);
if (p == pend)