* Francesco Potorti` reorganised C and C++ based on work by Joe Wells.
* Regexp tags by Tom Tromey.
*
- * Francesco Potorti` (pot@cnuce.cnr.it) is the current maintainer.
+ * Francesco Potorti` (F.Potorti@cnuce.cnr.it) is the current maintainer.
*/
-char pot_etags_version[] = "@(#) pot revision number is 11.53";
+char pot_etags_version[] = "@(#) pot revision number is 11.59";
#define TRUE 1
#define FALSE 0
#endif
#ifdef MSDOS
-#include <fcntl.h>
-#include <sys/param.h>
+# include <fcntl.h>
+# include <sys/param.h>
#endif /* MSDOS */
#ifdef WINDOWSNT
-#include <stdlib.h>
-#include <fcntl.h>
-#include <string.h>
-#define MAXPATHLEN _MAX_PATH
+# include <stdlib.h>
+# include <fcntl.h>
+# include <string.h>
+# define MAXPATHLEN _MAX_PATH
#endif
#ifdef HAVE_CONFIG_H
-#include <config.h>
-/* On some systems, Emacs defines static as nothing for the sake
- of unexec. We don't want that here since we don't use unexec. */
-#undef static
+# include <config.h>
+ /* On some systems, Emacs defines static as nothing for the sake
+ of unexec. We don't want that here since we don't use unexec. */
+# undef static
#endif
#include <stdio.h>
#include <getopt.h>
#ifdef ETAGS_REGEXPS
-#include <regex.h>
+# include <regex.h>
#endif /* ETAGS_REGEXPS */
/* Define CTAGS to make the program "ctags" compatible with the usual one.
/* Exit codes for success and failure. */
#ifdef VMS
-#define GOOD 1
-#define BAD 0
+# define GOOD 1
+# define BAD 0
#else
-#define GOOD 0
-#define BAD 1
+# define GOOD 0
+# define BAD 1
#endif
/* C extensions. */
#define endtoken(arg) (_etk[arg]) /* T if char ends tokens */
#ifdef DOS_NT
-# define absolutefn(fn) (fn[0] == '/' || (isalpha (fn[0]) && fn[1] == ':'))
+# define absolutefn(fn) (fn[0] == '/' \
+ || (isalpha (fn[0]) && fn[1] == ':' && fn[2] == '/'))
#else
# define absolutefn(fn) (fn[0] == '/')
#endif
Lang_function C_entries;
Lang_function Cplusplus_entries;
Lang_function Cstar_entries;
+Lang_function Erlang_functions;
Lang_function Fortran_functions;
Lang_function Yacc_entries;
Lang_function Lisp_functions;
void plain_C_entries ();
void Cplusplus_entries ();
void Cstar_entries ();
+void Erlang_functions ();
void Fortran_functions ();
void Yacc_entries ();
void Lisp_functions ();
char *Cstar_suffixes [] =
{ "cs", "hs", NULL };
+char *Erlang_suffixes [] =
+ { "erl", "hrl", NULL };
+
char *Fortran_suffixes [] =
{ "F", "f", "f90", "for", NULL };
{ "c", default_C_entries, default_C_suffixes, NULL },
{ "c++", Cplusplus_entries, Cplusplus_suffixes, NULL },
{ "c*", Cstar_entries, Cstar_suffixes, NULL },
+ { "erlang", Erlang_functions, Erlang_suffixes, NULL },
{ "fortran", Fortran_functions, Fortran_suffixes, NULL },
{ "lisp", Lisp_functions, Lisp_suffixes, NULL },
{ "pascal", Pascal_functions, Pascal_suffixes, NULL },
{
printf ("These are the options accepted by %s. You may use unambiguous\n\
abbreviations for the long option names. A - as file name means read\n\
-names from stdin.\n\n", progname);
+names from stdin.", progname);
+ if (!CTAGS)
+ printf (" Absolute names are stored in the output file as they\n\
+are. Relative ones are stored relative to the output file's directory.");
+ puts ("\n");
puts ("-a, --append\n\
Append tag entries to existing tags file.");
}
if (tagfile == NULL)
- {
- tagfile = CTAGS ? "tags" : "TAGS";
- }
+ tagfile = CTAGS ? "tags" : "TAGS";
cwd = etags_getcwd (); /* the current working directory */
- strcat (cwd, "/");
+ if (cwd[strlen(cwd)-1] != '/')
+ strcat (cwd, "/");
if (streq (tagfile, "-"))
- {
- tagfiledir = cwd;
- }
+ tagfiledir = cwd;
else
- {
- tagfiledir = absolute_dirname (tagfile, cwd);
- }
+ tagfiledir = absolute_dirname (tagfile, cwd);
init (); /* set up boolean "functions" */
return -1;
}
\f
-/* Support for Prolog. */
+/*
+ * Prolog support (rewritten) by Anders Lindgren, Mar. 96
+ *
+ * Assumes that the predicate starts at column 0.
+ * Only the first clause of a predicate is added.
+ */
+void
+Prolog_functions (inf)
+ FILE *inf;
+{
+ int prolog_pred ();
+ void prolog_skip_comment ();
+
+ char * last;
+ int len;
+ int allocated;
+
+ allocated = 0;
+ len = 0;
+ last = NULL;
+
+ lineno = 0;
+ linecharno = 0;
+ charno = 0;
+
+ while (!feof (inf))
+ {
+ lineno++;
+ linecharno += charno;
+ charno = readline (&lb, inf);
+ dbp = lb.buffer;
+ if (dbp[0] == '\0') /* Empty line */
+ continue;
+ else if (isspace (dbp[0])) /* Not a predicate */
+ continue;
+ else if (dbp[0] == '/' && dbp[1] == '*') /* comment. */
+ prolog_skip_comment (&lb, inf, &lineno, &linecharno);
+ else if (len = prolog_pred (dbp, last))
+ {
+ /* Predicate. Store the function name so that we only
+ * generates a tag for the first clause. */
+ if (last == NULL)
+ last = xnew(len + 1, char);
+ else if (len + 1 > allocated)
+ last = (char *) xrealloc(last, len + 1);
+ allocated = len + 1;
+ strncpy (last, dbp, len);
+ last[len] = '\0';
+ }
+ }
+}
+
-/* Whole head (not only functor, but also arguments)
- is gotten in compound term. */
void
-prolog_getit (s)
+prolog_skip_comment (plb, inf)
+ struct linebuffer *plb;
+ FILE *inf;
+{
+ char *cp;
+
+ do
+ {
+ for (cp = plb->buffer; *cp != '\0'; cp++)
+ if (cp[0] == '*' && cp[1] == '/')
+ return;
+ lineno++;
+ linecharno += readline (plb, inf);
+ }
+ while (!feof(inf));
+}
+
+/*
+ * A predicate definition is added if it matches:
+ * <beginning of line><Prolog Atom><whitespace>(
+ *
+ * It is added to the tags database if it doesn't match the
+ * name of the previous clause header.
+ *
+ * Return the size of the name of the predicate, or 0 if no header
+ * was found.
+ */
+int
+prolog_pred (s, last)
char *s;
+ char *last; /* Name of last clause. */
{
- char *save_s;
- int insquote, npar;
+ int prolog_atom();
+ int prolog_white();
- save_s = s;
- insquote = FALSE;
- npar = 0;
- while (1)
+ int pos;
+ int len;
+
+ pos = prolog_atom(s, 0);
+ if (pos < 1)
+ return 0;
+
+ len = pos;
+ pos += prolog_white(s, pos);
+
+ if ((s[pos] == '(') || (s[pos] == '.'))
{
- if (s[0] == '\0') /* syntax error. */
- return;
- else if (insquote && s[0] == '\'' && s[1] == '\'')
- s += 2;
- else if (s[0] == '\'')
+ if (s[pos] == '(')
+ pos++;
+
+ /* Save only the first clause. */
+ if ((last == NULL) ||
+ (len != strlen(last)) ||
+ (strncmp(s, last, len) != 0))
{
- insquote = !insquote;
- s++;
+ pfnote ((CTAGS) ? savenstr (s, len) : NULL, TRUE,
+ s, pos, lineno, linecharno);
+ return len;
}
- else if (!insquote && s[0] == '(')
+ }
+ return 0;
+}
+
+/*
+ * Consume a Prolog atom.
+ * Return the number of bytes consumed, or -1 if there was an error.
+ *
+ * A prolog atom, in this context, could be one of:
+ * - An alphanumeric sequence, starting with a lower case letter.
+ * - A quoted arbitrary string. Single quotes can escape themselves.
+ * Backslash quotes everything.
+ */
+int
+prolog_atom (s, pos)
+ char *s;
+ int pos;
+{
+ int origpos;
+
+ origpos = pos;
+
+ if (islower(s[pos]) || (s[pos] == '_'))
+ {
+ /* The atom is unquoted. */
+ pos++;
+ while (isalnum(s[pos]) || (s[pos] == '_'))
{
- npar++;
- s++;
+ pos++;
}
- else if (!insquote && s[0] == ')')
+ return pos - origpos;
+ }
+ else if (s[pos] == '\'')
+ {
+ pos++;
+
+ while (1)
{
- npar--;
- s++;
- if (npar == 0)
- break;
- else if (npar < 0) /* syntax error. */
- return;
- }
- else if (!insquote && s[0] == '.'
- && (isspace (s[1]) || s[1] == '\0'))
- { /* fullstop. */
- if (npar != 0) /* syntax error. */
- return;
- s++;
- break;
+ if (s[pos] == '\'')
+ {
+ pos++;
+ if (s[pos] != '\'')
+ break;
+ pos++; /* A double quote */
+ }
+ else if (s[pos] == '\0')
+ /* Multiline quoted atoms are ignored. */
+ return -1;
+ else if (s[pos] == '\\')
+ {
+ if (s[pos+1] == '\0')
+ return -1;
+ pos += 2;
+ }
+ else
+ pos++;
}
- else
- s++;
+ return pos - origpos;
}
- pfnote ((CTAGS) ? savenstr (save_s, s-save_s) : NULL, TRUE,
- save_s, s-save_s, lineno, linecharno);
+ else
+ return -1;
}
-/* It is assumed that prolog predicate starts from column 0. */
+/* Consume whitespace. Return the number of bytes eaten. */
+int
+prolog_white (s, pos)
+ char *s;
+ int pos;
+{
+ int origpos;
+
+ origpos = pos;
+
+ while (isspace(s[pos]))
+ pos++;
+
+ return pos - origpos;
+}
+\f
+/*
+ * Support for Erlang -- Anders Lindgren, Feb 1996.
+ *
+ * Generates tags for functions, defines, and records.
+ *
+ * Assumes that Erlang functions start at column 0.
+ */
void
-Prolog_functions (inf)
+Erlang_functions (inf)
FILE *inf;
{
- void skip_comment (), prolog_getit ();
+ int erlang_func ();
+ void erlang_attribute ();
+
+ char * last;
+ int len;
+ int allocated;
+
+ allocated = 0;
+ len = 0;
+ last = NULL;
+
+ lineno = 0;
+ linecharno = 0;
+ charno = 0;
- lineno = linecharno = charno = 0;
while (!feof (inf))
{
lineno++;
linecharno += charno;
- charno = readline (&lb, inf) + 1; /* 1 for newline. */
+ charno = readline (&lb, inf);
dbp = lb.buffer;
- if (isspace (dbp[0])) /* not predicate header. */
+ if (dbp[0] == '\0') /* Empty line */
continue;
- else if (dbp[0] == '%') /* comment. */
+ else if (isspace (dbp[0])) /* Not function nor attribute */
continue;
- else if (dbp[0] == '/' && dbp[1] == '*') /* comment. */
- skip_comment (&lb, inf, &lineno, &linecharno);
- else /* found. */
- prolog_getit (dbp);
+ else if (dbp[0] == '%') /* comment */
+ continue;
+ else if (dbp[0] == '"') /* Sometimes, strings start in column one */
+ continue;
+ else if (dbp[0] == '-') /* attribute, e.g. "-define" */
+ {
+ erlang_attribute(dbp);
+ last = NULL;
+ }
+ else if (len = erlang_func (dbp, last))
+ {
+ /*
+ * Function. Store the function name so that we only
+ * generates a tag for the first clause.
+ */
+ if (last == NULL)
+ last = xnew(len + 1, char);
+ else if (len + 1 > allocated)
+ last = (char *) xrealloc(last, len + 1);
+ allocated = len + 1;
+ strncpy (last, dbp, len);
+ last[len] = '\0';
+ }
+ }
+}
+
+
+/*
+ * A function definition is added if it matches:
+ * <beginning of line><Erlang Atom><whitespace>(
+ *
+ * It is added to the tags database if it doesn't match the
+ * name of the previous clause header.
+ *
+ * Return the size of the name of the function, or 0 if no function
+ * was found.
+ */
+int
+erlang_func (s, last)
+ char *s;
+ char *last; /* Name of last clause. */
+{
+ int erlang_atom ();
+ int erlang_white ();
+
+ int pos;
+ int len;
+
+ pos = erlang_atom(s, 0);
+ if (pos < 1)
+ return 0;
+
+ len = pos;
+ pos += erlang_white(s, pos);
+
+ if (s[pos++] == '(')
+ {
+ /* Save only the first clause. */
+ if ((last == NULL) ||
+ (len != strlen(last)) ||
+ (strncmp(s, last, len) != 0))
+ {
+ pfnote ((CTAGS) ? savenstr (s, len) : NULL, TRUE,
+ s, pos, lineno, linecharno);
+ return len;
+ }
}
+ return 0;
}
+
+/*
+ * Handle attributes. Currently, tags are generated for defines
+ * and records.
+ *
+ * They are on the form:
+ * -define(foo, bar).
+ * -define(Foo(M, N), M+N).
+ * -record(graph, {vtab = notable, cyclic = true}).
+ */
void
-skip_comment (plb, inf, plineno, plinecharno)
- struct linebuffer *plb;
- FILE *inf;
- int *plineno; /* result */
- long *plinecharno; /* result */
+erlang_attribute (s)
+ char *s;
{
- char *cp;
+ int erlang_atom ();
+ int erlang_white ();
- do
+ int pos;
+ int len;
+
+ if ((strncmp(s, "-define", 7) == 0) ||
+ (strncmp(s, "-record", 7) == 0))
{
- for (cp = plb->buffer; *cp != '\0'; cp++)
- if (cp[0] == '*' && cp[1] == '/')
- return;
- (*plineno)++;
- *plinecharno += readline (plb, inf) + 1; /* 1 for newline. */
+ pos = 7;
+ pos += erlang_white(s, pos);
+
+ if (s[pos++] == '(')
+ {
+ pos += erlang_white(s, pos);
+
+ if (len = erlang_atom(s, pos))
+ {
+ pfnote ((CTAGS) ? savenstr (& s[pos], len) : NULL, TRUE,
+ s, pos + len, lineno, linecharno);
+ }
+ }
}
- while (!feof(inf));
+ return;
+}
+
+
+/*
+ * Consume an Erlang atom (or variable).
+ * Return the number of bytes consumed, or -1 if there was an error.
+ */
+int
+erlang_atom (s, pos)
+ char *s;
+ int pos;
+{
+ int origpos;
+
+ origpos = pos;
+
+ if (isalpha (s[pos]) || s[pos] == '_')
+ {
+ /* The atom is unquoted. */
+ pos++;
+ while (isalnum (s[pos]) || s[pos] == '_')
+ pos++;
+ return pos - origpos;
+ }
+ else if (s[pos] == '\'')
+ {
+ pos++;
+
+ while (1)
+ {
+ if (s[pos] == '\'')
+ {
+ pos++;
+ break;
+ }
+ else if (s[pos] == '\0')
+ /* Multiline quoted atoms are ignored. */
+ return -1;
+ else if (s[pos] == '\\')
+ {
+ if (s[pos+1] == '\0')
+ return -1;
+ pos += 2;
+ }
+ else
+ pos++;
+ }
+ return pos - origpos;
+ }
+ else
+ return -1;
+}
+
+/* Consume whitespace. Return the number of bytes eaten */
+int
+erlang_white (s, pos)
+ char *s;
+ int pos;
+{
+ int origpos;
+
+ origpos = pos;
+
+ while (isspace (s[pos]))
+ pos++;
+
+ return pos - origpos;
}
\f
#ifdef ETAGS_REGEXPS
}
if (c == EOF)
{
+ *p = '\0';
chars_deleted = 0;
break;
}
just_read_file (inf)
FILE *inf;
{
+ lineno = 0;
+ charno = 0;
+
while (!feof (inf))
{
++lineno;