/* Tags file maker to go with GNU Emacs
- Copyright (C) 1984, 1987, 1988, 1989 Free Software Foundation, Inc. and Ken Arnold
+ Copyright (C) 1984, 1987, 1988, 1989, 1992 Free Software Foundation, Inc. and Ken Arnold
This file is part of GNU Emacs.
#include <sys/types.h>
#include <sys/stat.h>
+#include "getopt.h"
+
#ifdef __GNUC__
#define alloca __builtin_alloca
#else
extern char *malloc (), *realloc ();
extern char *getenv ();
-extern char *index (), *rindex ();
extern char *strcpy (), *strncpy ();
extern int strcmp ();
-#ifdef hpux
-#define notdef
-#endif
+char *etags_index (), *etags_rindex ();
+char *savenstr ();
/* Define the symbol ETAGS to make the program "etags",
which makes emacs-style tag tables by default.
* for self-documentation only.
*/
#define LEVEL_OK_FOR_FUNCDEF() \
- (level==0 || c_ext && level==1 && structdef==sinbody)
+ (level==0 || (c_ext && level==1 && structdef==sinbody))
+
+/*
+ * next_token_is_func
+ * set this to TRUE, and the next token considered is called a function.
+ */
+logical next_token_is_func;
/* C extensions. Currently all listed extensions are C++ dialects, so
* `c_ext' is used as an abbreviation for `c_ext&C_PLPL'. If a non-C++
*intk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789", /* valid in-token chars */
*notgd = ",;"; /* non-valid after-function chars */
-int file_num; /* current file number */
int append_to_tagfile; /* -a: append to tags */
int emacs_tags_format; /* emacs style output (no -e option any more) */
/* The following three default to 1 for etags, but to 0 for ctags. */
/* Name this program was invoked with. */
char *progname;
+struct option longopts[] = {
+ { "append", no_argument, NULL, 'a' },
+ { "backward-search", no_argument, NULL, 'B' },
+ { "c++", no_argument, NULL, 'C' },
+ { "cxref", no_argument, NULL, 'x' },
+ { "defines", no_argument, NULL, 'd' },
+ { "forward-search", no_argument, NULL, 'F' },
+ { "help", no_argument, NULL, 'H' },
+ { "ignore-indentation", no_argument, NULL, 'S' },
+ { "include", required_argument, NULL, 'i' },
+ { "no-defines", no_argument, NULL, 'D' },
+ { "no-warn", no_argument, NULL, 'w' },
+ { "output", required_argument, NULL, 'o' },
+ { "typedefs", no_argument, NULL, 't' },
+ { "typedefs-and-c++", no_argument, NULL, 'T' },
+ { "update", no_argument, NULL, 'u' },
+ { "version", no_argument, NULL, 'V' },
+ { "vgrind", no_argument, NULL, 'v' },
+ { 0 }
+};
+
FILE *inf, /* ioptr for current input file */
*outf; /* ioptr for tags file */
struct linebuffer filename_lb; /* used to read in filenames */
\f
+void
+print_version ()
+{
+#ifdef CTAGS
+ printf ("CTAGS ");
+#ifdef ETAGS
+ printf ("and ");
+#endif
+#endif
+#ifdef ETAGS
+ printf ("ETAGS ");
+#endif
+ printf ("for Emacs version 19.0.\n");
+
+ exit (0);
+}
+
+void
+print_help ()
+{
+ printf ("These are the options accepted by %s. You may use unambiguous\n\
+abbreviations for the long option names.\n\n", progname);
+
+ fputs ("\
+-a, --append\n\
+ Append tag entries to existing tags file.\n\
+-C, --c++\n\
+ Treat files with `.c' and `.h' extensions as C++ code, not C\n\
+ code. Files with `.C', `.H', `.cxx', `.hxx', or `.cc'\n\
+ extensions are always assumed to be C++ code.\n\
+-d, --defines\n\
+ Create tag entries for #defines, too.", stdout);
+
+#ifdef ETAGS
+ fputs (" This is the default\n\
+ behavior.", stdout);
+#endif
+
+ fputs ("\n\
+-D, --no-defines\n\
+ Don't create tag entries for #defines.", stdout);
+
+#ifdef CTAGS
+ fputs (" This is the default\n\
+ behavior.", stdout);
+#endif
+
+ puts ("\n\
+-o FILE, --output=FILE\n\
+ Write the tags to FILE.\n\
+-S, --ignore-indentation\n\
+ Don't rely on indentation quite as much as normal. Currently,\n\
+ this means not to assume that a closing brace in the first\n\
+ column is the final brace of a function or structure\n\
+ definition.\n\
+-t, --typedefs\n\
+ Generate tag entries for typedefs. This is the default\n\
+ behavior.\n\
+-T, --typedefs-and-c++\n\
+ Generate tag entries for typedefs, struct/enum/union tags, and\n\
+ C++ member functions.");
+
+#ifdef ETAGS
+ 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.");
+#endif
+
+#ifdef CTAGS
+ puts ("-B, --backward-search\n\
+ Write the search commands for the tag entries using '?', the\n\
+ backward-search command.\n\
+-F, --forward-search\n\
+ Write the search commands for the tag entries using '/', the\n\
+ forward-search command.\n\
+-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.\n\
+-v, --vgrind\n\
+ Generates an index of items intended for human consumption,\n\
+ similar to the output of vgrind. The index is sorted, and\n\
+ gives the page number of each item.\n\
+-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.\n\
+-w, --no-warn\n\
+ Suppress warning messages about entries defined in multiple\n\
+ files.");
+#endif
+
+ puts ("-V, --version\n\
+ Print the version of the program.\n\
+-H, --help\n\
+ Print this help message.");
+
+ exit (0);
+}
+
+\f
void
main (argc, argv)
int argc;
{
char cmd[100];
int i;
- int outfflag = 0;
unsigned int nincluded_files = 0;
char **included_files = (char **) alloca (argc * sizeof (char *));
char *this_file;
if (emacs_tags_format)
typedefs = typedefs_and_cplusplus = constantypedefs = 1;
- for (; argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0'; argc--, argv++)
+ for (;;)
{
- for (i = 1; argv[1][i]; i++)
+ int opt;
+ opt = getopt_long (argc, argv, "aCdDo:StTi:BFuvxwVH", longopts, 0);
+
+ if (opt == EOF)
+ break;
+
+ switch (opt)
{
- switch (argv[1][i])
+ case '\0':
+ /* If getopt returns '\0', then it has already processed a
+ long-named option. We should do nothing. */
+ break;
+
+ /* Common options. */
+ case 'a':
+ append_to_tagfile++;
+ break;
+ case 'C':
+ cplusplus = 1;
+ break;
+ case 'd':
+ constantypedefs = 1;
+ break;
+ case 'D':
+ constantypedefs = 0;
+ break;
+ case 'o':
+ if (outfile)
{
- /* Common options. */
- case 'a':
- append_to_tagfile++;
- break;
- case 'C':
- cplusplus = 1;
- break;
- case 'd':
- constantypedefs = 1;
- break;
- case 'D':
- constantypedefs = 0;
- break;
- case 'o':
- if (outfflag)
- {
- fprintf (stderr,
- "%s: -o flag may only be given once\n", progname);
- goto usage;
- }
- outfflag++, argc--;
- argv++;
- if (argc <= 1 || argv[1][0] == '\0')
- {
- fprintf (stderr,
- "%s: -o flag must be followed by a filename\n",
- progname);
- goto usage;
- }
- outfile = argv[1];
- goto next_arg;
- case 'S':
- noindentypedefs++;
- break;
- case 't':
- typedefs++;
- break;
- case 'T':
- typedefs++;
- typedefs_and_cplusplus++;
- break;
-
- /* Etags options */
- case 'i':
- if (!emacs_tags_format)
- goto usage;
- --argc;
- ++argv;
- if (argc <= 1 || argv[1][0] == '\0')
- {
- fprintf (stderr,
- "%s: -i flag must be followed by a filename\n",
- progname);
- goto usage;
- }
- included_files[nincluded_files++] = argv[1];
- goto next_arg;
-
- /* Ctags options. */
- case 'B':
- searchar = '?';
- if (emacs_tags_format)
- goto usage;
- break;
- case 'F':
- searchar = '/';
- if (emacs_tags_format)
- goto usage;
- break;
- case 'u':
- update++;
- if (emacs_tags_format)
- goto usage;
- break;
- case 'v':
- vgrind_style++;
- /*FALLTHRU*/
- case 'x':
- cxref_style++;
- if (emacs_tags_format)
- goto usage;
- break;
- case 'w':
- no_warnings++;
- if (emacs_tags_format)
- goto usage;
- break;
-
- default:
+ fprintf (stderr,
+ "%s: -o flag may only be given once\n", progname);
goto usage;
}
+ outfile = optarg;
+ break;
+ case 'S':
+ noindentypedefs++;
+ break;
+ case 't':
+ typedefs++;
+ break;
+ case 'T':
+ typedefs++;
+ typedefs_and_cplusplus++;
+ break;
+ case 'V':
+ print_version ();
+ break;
+ case 'H':
+ print_help ();
+ break;
+
+ /* Etags options */
+ case 'i':
+ if (!emacs_tags_format)
+ goto usage;
+ included_files[nincluded_files++] = optarg;
+ break;
+
+ /* Ctags options. */
+ case 'B':
+ searchar = '?';
+ if (emacs_tags_format)
+ goto usage;
+ break;
+ case 'F':
+ searchar = '/';
+ if (emacs_tags_format)
+ goto usage;
+ break;
+ case 'u':
+ update++;
+ if (emacs_tags_format)
+ goto usage;
+ break;
+ case 'v':
+ vgrind_style++;
+ /*FALLTHRU*/
+ case 'x':
+ cxref_style++;
+ if (emacs_tags_format)
+ goto usage;
+ break;
+ case 'w':
+ no_warnings++;
+ if (emacs_tags_format)
+ goto usage;
+ break;
+
+ default:
+ goto usage;
}
- next_arg:;
}
- if (argc <= 1)
+ if (optind == argc)
{
+ fprintf (stderr, "%s: No input files specified.\n", progname);
+
usage:
- fprintf (stderr, "Usage:\n");
-#ifndef CTAGS
- fprintf (stderr, "\tetags [-aDiS] [-o tagsfile] file ...\n");
-#else
- fprintf (stderr, "\tctags [-aBdeFTStuwvx] [-o tagsfile] file ...\n");
-#endif
+ fprintf (stderr, "%s: Try '%s --help' for a complete list of options.\n",
+ progname, progname);
exit (BAD);
}
}
}
- file_num = 1;
#ifdef VMS
- for (argc--, argv++;
- (this_file = gfnames (&argc, &argv, &got_err)) != NULL; file_num++)
+ argc -= optind;
+ argv += optind;
+ while (gfnames (&argc, &argv, &got_err) != NULL)
{
if (got_err)
{
} /* solely to balance out the ifdef'd parens above */
#endif
#else
- for (; file_num < argc; file_num++)
+ for (; optind < argc; optind++)
{
- this_file = argv[file_num];
+ this_file = argv[optind];
if (1)
{
#endif
put_entries (head);
exit (GOOD);
}
- if (update) /* update cannot be set under VMS */
+ if (update)
{
- for (i = 1; i < argc; i++)
+ /* update cannot be set under VMS, so we may assume that argc
+ and argv have not been munged. */
+ for (i = optind; i < argc; i++)
{
sprintf (cmd,
"mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
struct stat stat_buf;
stat (file, &stat_buf);
- if (!(stat_buf.st_mode & S_IFREG) || !(stat_buf.st_mode & S_IFLNK))
+ if (!(stat_buf.st_mode & S_IFREG)
+#ifdef S_IFLNK
+ || !(stat_buf.st_mode & S_IFLNK)
+#endif
+ )
{
fprintf (stderr, "Skipping %s: it is not a regular file.\n", file);
return;
}
if (emacs_tags_format)
{
- char *cp = rindex (file, '/');
+ char *cp = etags_rindex (file, '/');
if (cp)
++cp;
else
return;
}
curfile = savestr (file);
- cp = rindex (file, '.');
+ cp = etags_rindex (file, '.');
header_file = (cp && (streq (cp + 1, "h")));
goto close_and_return;
}
/* if not a .c or .h or .y file, try fortran */
- else if (cp && ((cp[1] != 'c' && cp[1] != 'h' && cp[1] != 'y')
+ else if (cp && ((cp[1] != 'c'
+ && cp[1] != 'h'
+ && cp[1] != 'y')
|| (cp[1] != 0 && cp[2] != 0)))
{
if (PF_funcs (inf) != 0)
/* If ctags mode, change name "main" to M<thisfilename>. */
if (!emacs_tags_format && !cxref_style && streq (name, "main"))
{
- fp = rindex (curfile, '/');
+ fp = etags_rindex (curfile, '/');
name = concat ("M", fp == 0 ? curfile : fp + 1, "");
- fp = rindex (name, '.');
+ fp = etags_rindex (name, '.');
if (fp && fp[1] != '\0' && fp[2] == '\0')
*fp = 0;
rewritten = TRUE;
fprintf (stdout, "%s %s %d\n",
node->name, node->file, (node->lno + 63) / 64);
else
- fprintf (stdout, "%-16s%4d %-16s %s\n",
+ fprintf (stdout, "%-16s %3d %-16s %s\n",
node->name, node->lno, node->file, node->pat);
/* Output subentries that follow this one */
definedef = dnone;
gotone = midtoken = inquote = inchar = incomm = FALSE;
level = 0;
+ tydef = none;
+ next_token_is_func = 0;
C_create_stabs ();
c = *lp++;
if (c == '\\')
{
+ /* If we're at the end of the line, the next character is a
+ '\0'; don't skip it, because it's the thing that tells us
+ to read the next line. */
if (*lp == 0)
continue;
lp++;
}
else if (inquote)
{
- /*
- * Too dumb to know about \" not being magic, but
- * they usually occur in pairs anyway.
- */
if (c == '"')
inquote = FALSE;
- continue;
+ else if (c == '\\')
+ c = *lp++;
}
else if (inchar)
{
}
else if (c_ext && *lp == '/')
{
- c = 0; /* C++ comment: skip rest of line */
+ c = 0;
+ break;
}
continue;
case '#':
int level; /* IN */
{
reg char *lp = *lpp;
- /*
- * next_token_is_func
- * set this to TRUE, and the next token considered is called a function.
- */
- static logical next_token_is_func;
logical firsttok; /* TRUE if have seen first token in ()'s */
Stab_entry *tokse = stab_find (get_C_stab (c_ext), tokp->p, tokp->len);
enum sym_type toktype = stab_type (tokse);
goto goodone;
}
/* Detect GNUmacs's function-defining macros. */
- if (definedef == dnone && strneq (tokp->p, "DEF", 3))
+ if (definedef == dnone)
{
- next_token_is_func = TRUE;
- goto badone;
+ if (strneq (tokp->p, "DEF", 3)
+ || strneq (tokp->p, "ENTRY", 5)
+ || strneq (tokp->p, "SYSCALL", 7)
+ || strneq (tokp->p, "PSEUDO", 6))
+ {
+ next_token_is_func = TRUE;
+ goto badone;
+ }
+ else if (strneq (tokp->p, "EXFUN", 5))
+ {
+ next_token_is_func = FALSE;
+ goto badone;
+ }
}
if (next_token_is_func)
{
if (tail ("subroutine"))
getit ();
continue;
+ case 'e':
+ if (tail ("entry"))
+ getit ();
+ continue;
case 'p':
if (tail ("program"))
{
while (isspace (*dbp))
dbp++;
- if (*dbp == 0 || (!isalpha (*dbp)) && (*dbp != '_') && (*dbp != '$'))
+ if (*dbp == 0
+ || (!isalpha (*dbp)
+ && *dbp != '_'
+ && *dbp != '$'))
return;
for (cp = dbp + 1; *cp && (isalpha (*cp) || isdigit (*cp)
|| (*cp == '_') || (*cp == '$')); cp++)
TEX_toktab = TEX_decode_env ("TEXTAGS", TEX_defenv);
while (!feof (fi))
- {
+ { /* Scan each line in file */
lineno++;
linecharno = charno;
charno += readline (&lb, fi);
dbp = lb.buffer;
lasthit = dbp;
+ while (dbp = etags_index (dbp, TEX_esc)) /* Look at each escape in line */
+ {
+ register int i;
- while (!feof (fi))
- { /* Scan each line in file */
- lineno++;
- linecharno = charno;
- charno += readline (&lb, fi);
- dbp = lb.buffer;
+ if (!*(++dbp))
+ break;
+ linecharno += dbp - lasthit;
lasthit = dbp;
- while (dbp = index (dbp, TEX_esc)) /* Look at each escape in line */
+ i = TEX_Token (lasthit);
+ if (0 <= i)
{
- register int i;
-
- if (!*(++dbp))
- break;
- linecharno += dbp - lasthit;
- lasthit = dbp;
- i = TEX_Token (lasthit);
- if (0 <= i)
- {
- TEX_getit (lasthit, TEX_toktab[i].len);
- break; /* We only save a line once */
- }
+ TEX_getit (lasthit, TEX_toktab[i].len);
+ break; /* We only save a line once */
}
}
}
char *defenv;
{
register char *env, *p;
- extern char *savenstr (), *index ();
struct TEX_tabent *tab;
int size, i;
/* Allocate a token table */
for (size = 1, p = env; p;)
- if ((p = index (p, ':')) && *(++p))
+ if ((p = etags_index (p, ':')) && *(++p))
size++;
- tab = xnew (size, struct TEX_tabent);
+ /* Add 1 to leave room for null terminator. */
+ tab = xnew (size + 1, struct TEX_tabent);
/* Unpack environment string into token table. Be careful about */
/* zero-length strings (leading ':', "::" and trailing ':') */
for (i = 0; *env;)
{
- p = index (env, ':');
+ p = etags_index (env, ':');
if (!p) /* End of environment string. */
p = env + strlen (env);
if (p - env > 0)
}
/* If the text at CP matches one of the tag-defining TeX command names,
- return the index of that command in TEX_toktab.
+ return the etags_index of that command in TEX_toktab.
Otherwise return -1. */
/* Keep the capital `T' in `Token' for dumb truncating compilers
char *sub;
char *s;
{
- while (*s && (s = index (s, *sub)))
+ while (*s && (s = etags_index (s, *sub)))
if (prestr (sub, s))
return (TRUE);
else
return dp;
}
-#ifdef notdef
/*
* Return the ptr in sp at which the character c last
* appears; NULL if not found
*/
char *
-rindex (sp, c)
+etags_rindex (sp, c)
register char *sp, c;
{
register char *r;
return (r);
}
+
/*
* Return the ptr in sp at which the character c first
* appears; NULL if not found
*/
char *
-index (sp, c)
+etags_index (sp, c)
register char *sp, c;
{
do
return (NULL);
}
-#endif /* notdef */
-
/* Print error message and exit. */
/* VARARGS1 */