Commit c6d46f5f authored by Jim Blandy's avatar Jim Blandy
Browse files

Initial revision

parent cd645247
/* Tags file maker to go with GNU Emacs
Copyright (C) 1984, 1987, 1988, 1989 Free Software Foundation, Inc. and Ken Arnold
This file is part of GNU Emacs.
GNU Emacs is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Emacs; see the file COPYING. If not, write to
the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
/*
* Authors:
* Ctags originally by Ken Arnold.
* FORTRAN added by Jim Kleckner.
* Ed Pelegri-Llopart added C typedefs.
* Gnu Emacs TAGS format and modifications by RMS?
* Sam Kendall added C++.
*/
#include <stdio.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
extern char *malloc (), *realloc ();
extern char *getenv ();
extern char *index (), *rindex ();
extern char *strcpy (), *strncpy ();
extern int strcmp ();
#ifdef hpux
#define notdef
#endif
/* Define the symbol ETAGS to make the program "etags",
which makes emacs-style tag tables by default.
Define CTAGS to make the program "ctags" compatible with the usual one.
Define neither one to get behavior that depends
on the name with which the program is invoked
(but we don't normally compile it that way). */
#if !defined(ETAGS) && !defined(CTAGS)
/* If neither is defined, program can be run as either. */
#define ETAGS
#define CTAGS
#endif
/* On VMS, CTAGS is not useful, so always do ETAGS. */
#ifdef VMS
#ifndef ETAGS
#define ETAGS
#endif
#endif
/* Exit codes for success and failure. */
#ifdef VMS
#define GOOD (1)
#define BAD (0)
#else
#define GOOD (0)
#define BAD (1)
#endif
/*
* The FILEPOS abstract type, which represents a position in a file,
* plus the following accessor functions:
*
* long GET_CHARNO (pos)
* returns absolute char number.
* long GET_COOKIE (pos)
* returns ftell () cookie.
* void SET_FILEPOS (pos, fp, charno)
* FILE *fp; long charno;
* sets `pos' from the current file
* position of `fp' and from `charno',
* which must be the absolute character
* number corresponding to the current
* position of `fp'.
*
* The `pos' parameter is an lvalue expression of type FILEPOS.
* Parameters to the accessor functions are evaluated 0 or more times,
* and so must have no side effects.
*
* FILEPOS objects can also be assigned and passed to and from
* functions in the normal C manner.
*
* Implementation notes: the `+ 0' is to enforce rvalue-ness.
*/
#ifdef VMS
typedef struct
{
long cookie;
long charno;
} FILEPOS;
#define GET_CHARNO(pos) ((pos).charno + 0)
#define GET_COOKIE(pos) ((pos).cookie + 0)
#define SET_FILEPOS(pos, fp, cno) \
((void) ((pos).cookie = ftell (fp), (pos).charno = (cno)))
#else
#ifndef DEBUG
/* UNIX real implementation */
typedef long FILEPOS;
#define GET_CHARNO(pos) ((pos) + 0)
#define GET_COOKIE(pos) GET_CHARNO (pos)
#define SET_FILEPOS(pos, fp, cno) ((void) ((pos) = (cno)))
#else
/* UNIX debugging implementation */
typedef struct
{
long charno;
} FILEPOS;
#define GET_CHARNO(pos) ((pos).charno + 0)
#define GET_COOKIE(pos) GET_CHARNO (pos)
#define SET_FILEPOS(pos, fp, cno) \
((void) ((pos).charno = (cno), \
(cno) != ftell (fp) ? (error ("SET_FILEPOS inconsistency"), 0) \
: 0))
#endif
#endif
#define streq(s, t) (strcmp (s, t) == 0)
#define strneq(s, t, n) (strncmp (s, t, n) == 0)
#define reg register
#define logical char
#define TRUE 1
#define FALSE 0
#define iswhite(arg) (_wht[arg]) /* T if char is white */
#define begtoken(arg) (_btk[arg]) /* T if char can start token */
#define intoken(arg) (_itk[arg]) /* T if char can be in token */
#define endtoken(arg) (_etk[arg]) /* T if char ends tokens */
#define isgood(arg) (_gd[arg]) /* T if char can be after ')' */
#define max(I1,I2) ((I1) > (I2) ? (I1) : (I2))
struct nd_st
{ /* sorting structure */
char *name; /* function or type name */
char *file; /* file name */
logical is_func; /* use pattern or line no */
logical rewritten; /* list name separately */
logical been_warned; /* set if noticed dup */
int lno; /* line number tag is on */
long cno; /* character number line starts on */
char *pat; /* search pattern */
struct nd_st *left, *right; /* left and right sons */
};
long ftell ();
typedef struct nd_st NODE;
logical gotone, /* found a func already on line */
/* boolean "func" (see init) */
header_file, /* TRUE if .h file, FALSE o.w. */
_wht[0177], _etk[0177], _itk[0177], _btk[0177], _gd[0177];
char *concat ();
char *savenstr ();
char *savestr ();
char *xmalloc ();
char *xrealloc ();
int L_isdef ();
int PF_funcs ();
int total_size_of_entries ();
logical consider_token ();
logical tail ();
long readline ();
void Asm_funcs ();
void C_entries ();
void L_funcs ();
void L_getit ();
void PAS_funcs ();
void Scheme_funcs ();
void TEX_funcs ();
void add_node ();
void error ();
void fatal ();
void find_entries ();
void free_tree ();
void getit ();
void getline ();
void init ();
void initbuffer ();
void initbuffer ();
void pfnote ();
void process_file ();
void put_entries ();
void takeprec ();
/*
* MACRO
* xnew -- allocate storage
*
* SYNOPSIS
* Type *xnew (int n, Type);
*/
#define xnew(n, Type) ((Type *) xmalloc ((n) * sizeof (Type)))
/*
* Symbol table stuff.
*
* Should probably be implemented with hash table; linked list for now.
*/
enum sym_type
{
st_none, st_C_struct, st_C_enum, st_C_define, st_C_typedef, st_C_typespec
};
struct stab_entry
{
char *sym;
int symlen;
enum sym_type type;
struct stab_entry *next;
};
typedef struct stab_entry Stab_entry;
typedef Stab_entry *Stab;
/*
* NAME
* Stab, Stab_entry, stab_create, stab_search, stab_find -- symbol table
*
* SYNOPSIS
* Types: Stab, Stab_entry, enum sym_type
*
* Stab * stab_create ()
*
* Stab_entry * stab_find (stab, sym)
* Stab *stab;
* char *sym;
*
* Stab_entry * stab_search (stab, sym)
* Stab *stab;
* char *sym;
*
* DESCRIPTION
* stab_create creates a Stab, a symbol table object, and returns a
* pointer to it. stab_find finds a symbol in a Stab; it returns a
* pointer to the Stab_entry if found, otherwise NULL. stab_search
* is like stab_find, except that it creates a new Stab_entry,
* initialized with type = st_none, if one did not exist already
* (it never returns NULL).
*
* A Stab_entry is a structure that contains at least the following
* members:
*
* char *name; // must not be modified
* enum sym_type type; // should be set
*
* The type field is initially set to st_none; it should be set to
* something else by the caller of stab_search. Other possible values
* of an enum sym_type can be added.
*/
Stab *
stab_create ()
{
Stab *sp;
sp = xnew (1, Stab);
*sp = NULL; /* a Stab starts out as a null Stab_entry* */
return sp;
}
Stab_entry *
stab_find (stab, sym, symlen)
Stab *stab;
register char *sym;
register int symlen;
{
register Stab_entry *se;
for (se = *stab; se != NULL; se = se->next)
{
if (se->symlen == symlen && strneq (se->sym, sym, symlen))
return se;
}
return NULL;
}
Stab_entry *
stab_search (stab, sym, symlen)
register Stab *stab;
char *sym;
int symlen;
{
register Stab_entry *se;
se = stab_find (stab, sym, symlen);
if (se == NULL)
{
/* make a new one */
se = xnew (1, Stab_entry);
se->sym = savenstr (sym, symlen);
se->symlen = symlen;
se->type = st_none;
se->next = *stab;
*stab = se;
}
return se;
}
/*
* NAME
* stab_type -- type of a symbol table entry
*
* SYNOPSIS
* enum sym_type stab_type (Stab_entry *se);
*
* WARNING
* May evaluate its argument more than once.
*/
#define stab_type(se) ((se)==NULL ? st_none : (se)->type)
typedef int LINENO;
typedef struct
{
char *p;
int len;
FILEPOS linestart;
LINENO lineno;
logical rewritten;
} TOKEN;
/* typedefs are recognized using a simple finite automaton.
* tydef is its state variable.
*/
typedef enum
{
none, begin, middle, end
} TYST;
TYST tydef = none;
/* struct tags for C++ are recognized using another simple
* finite automaton. `structdef' is its state variable.
* This machinery is only invoked for C++; otherwise structdef
* should remain snone. However, this machinery can easily be
* adapted to find structure tags in normal C code.
*/
typedef enum
{
snone, /* nothing seen yet */
skeyseen, /* struct-like keyword seen */
stagseen, /* struct-like tag seen */
scolonseen, /* colon seen after struct-like tag */
sinbody /* in a class body: recognize member func defs */
} STRUCTST;
STRUCTST structdef = snone;
/*
* When structdef is stagseen, scolonseen, or sinbody, structtag is the
* struct tag, and structkey is the preceding struct-like keyword.
*/
char structtag[512];
Stab_entry *structkey;
/*
* Yet another little state machine to deal with preprocessor lines.
*/
typedef enum
{
dnone, /* nothing seen */
dsharpseen, /* '#' seen as first char on line */
ddefineseen, /* '#' and 'define' seen */
dignorerest /* ignore rest of line */
} DEFINEST;
DEFINEST definedef;
/*
* LEVEL_OK_FOR_FUNCDEF allows C++ function definition within class body.
* Currently tydef and structdef stuff (typedefs and struct definitions) are
* only noticed when level==0, but that may change.
*
* Note that this macro may only be evaluated inside C_entries(). It is
* for self-documentation only.
*/
#define LEVEL_OK_FOR_FUNCDEF() \
(level==0 || c_ext && level==1 && structdef==sinbody)
/* 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++
* dialect is added, this must change.
*/
#define C_PLPL 0x1 /* C++ */
#define C_STAR 0x3 /* C* */
char searchar = '/'; /* use /.../ searches */
LINENO lineno; /* line number of current line */
long charno; /* current character number */
FILEPOS linepos; /* start of line (C only) */
FILEPOS prev_linepos; /* start of previous line (C only) */
long linecharno; /* charno of start of line; not used by C, but
* by every other language.
*/
char *curfile, /* current input file name */
*outfile, /* output file */
*white = " \f\t\n", /* white chars */
*endtk = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?", /* token ending chars */
*begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$", /* token starting chars */
*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. */
int typedefs; /* -t: create tags for typedefs */
int typedefs_and_cplusplus; /* -T: create tags for typedefs, level */
/* 0 struct/enum/union decls, and C++ */
/* member functions */
int constantypedefs; /* -d: create tags for C #define and enum */
/* constants. Default under etags. Enum */
/* constants not implemented. */
/* -D: opposite of -d. Default under ctags. */
int update; /* -u: update tags */
int vgrind_style; /* -v: create vgrind style index output */
int no_warnings; /* -w: suppress warnings */
int cxref_style; /* -x: create cxref style output */
int cplusplus; /* .[hc] means C++, not C */
int noindentypedefs; /* -S: ignore indentation in C */
int files_are_tag_tables; /* -i: treat all spec'd files as */
/* included sub-tag-tables. */
/* Name this program was invoked with. */
char *progname;
FILE *inf, /* ioptr for current input file */
*outf; /* ioptr for tags file */
NODE *head; /* the head of the binary tree of tags */
int permit_duplicates = 1; /* Nonzero means allow duplicate tags. */
/* A `struct linebuffer' is a structure which holds a line of text.
`readline' reads a line from a stream into a linebuffer
and works regardless of the length of the line. */
struct linebuffer
{
long size;
char *buffer;
};
struct linebuffer lb; /* the current line */
struct linebuffer lb1; /* sometimes, a previous line in which a token lies */
struct linebuffer filename_lb; /* used to read in filenames */
void
main (argc, argv)
int argc;
char *argv[];
{
char cmd[100];
int i;
int outfflag = 0;
char *this_file;
#ifdef VMS
char got_err;
extern char *gfnames ();
extern char *massage_name ();
#endif
progname = argv[0];
#ifndef CTAGS
emacs_tags_format = 1;
#else
emacs_tags_format = 0;
#endif
/*
* If etags, always find typedefs and structure tags. Why not?
* Also default is to find macro constants.
*/
if (emacs_tags_format)
typedefs = typedefs_and_cplusplus = constantypedefs = 1;
for (; argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0'; argc--, argv++)
{
for (i = 1; argv[1][i]; i++)
{
switch (argv[1][i])
{
/* 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':
files_are_tag_tables++;
if (!emacs_tags_format)
goto usage;
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)
{
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
exit (BAD);
}
if (outfile == 0)
{
outfile = emacs_tags_format ? "TAGS" : "tags";
}
init (); /* set up boolean "functions" */
initbuffer (&lb);
initbuffer (&lb1);
initbuffer (&filename_lb);
/*
* loop through files finding functions
*/
if (emacs_tags_format)
{
if (streq (outfile, "-"))
outf = stdout;
else
outf = fopen (outfile, append_to_tagfile ? "a" : "w");
if (!outf)
{
perror (outfile);
exit (1);
}
}
file_num = 1;
#ifdef VMS
for (argc--, argv++;
(this_file = gfnames (&argc, &argv, &got_err)) != NULL; file_num++)
{
if (got_err)
{
error ("Can't find file %s\n", this_file);
argc--, argv++;
}
else
{
this_file = massage_name (this_file);
#if 0
}