* 1994 Line-by-line regexp tags by Tom Tromey.
* 2001 Nested classes by Francesco Potortì (concept by Mykola Dzyuba).
* 2002 #line directives by Francesco Potortì.
- *
- * Francesco Potortì <pot@gnu.org> has maintained and improved it since 1993.
+ * Francesco Potortì maintained and improved it for many years
+ starting in 1993.
*/
/*
# undef HAVE_NTGUI
# undef DOS_NT
# define DOS_NT
+# define O_CLOEXEC O_NOINHERIT
#endif /* WINDOWSNT */
+#include <limits.h>
#include <unistd.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <sysstdio.h>
-#include <ctype.h>
#include <errno.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+#include <fcntl.h>
#include <binary-io.h>
+#include <c-ctype.h>
#include <c-strcase.h>
#include <assert.h>
#define strneq(s,t,n) (assert ((s)!=NULL || (t)!=NULL), !strncmp (s, t, n))
#define strncaseeq(s,t,n) (assert ((s)!=NULL && (t)!=NULL), !c_strncasecmp (s, t, n))
-#define CHARS 256 /* 2^sizeof(char) */
-#define CHAR(x) ((unsigned int)(x) & (CHARS - 1))
-#define iswhite(c) (_wht[CHAR (c)]) /* c is white (see white) */
-#define notinname(c) (_nin[CHAR (c)]) /* c is not in a name (see nonam) */
-#define begtoken(c) (_btk[CHAR (c)]) /* c can start token (see begtk) */
-#define intoken(c) (_itk[CHAR (c)]) /* c can be in token (see midtk) */
-#define endtoken(c) (_etk[CHAR (c)]) /* c ends tokens (see endtk) */
+/* C is not in a name. */
+static bool
+notinname (unsigned char c)
+{
+ /* Look at make_tag before modifying! */
+ static bool const table[UCHAR_MAX + 1] = {
+ ['\0']=1, ['\t']=1, ['\n']=1, ['\f']=1, ['\r']=1, [' ']=1,
+ ['(']=1, [')']=1, [',']=1, [';']=1, ['=']=1
+ };
+ return table[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))
+/* C can start a token. */
+static bool
+begtoken (unsigned char c)
+{
+ static bool const table[UCHAR_MAX + 1] = {
+ ['$']=1, ['@']=1,
+ ['A']=1, ['B']=1, ['C']=1, ['D']=1, ['E']=1, ['F']=1, ['G']=1, ['H']=1,
+ ['I']=1, ['J']=1, ['K']=1, ['L']=1, ['M']=1, ['N']=1, ['O']=1, ['P']=1,
+ ['Q']=1, ['R']=1, ['S']=1, ['T']=1, ['U']=1, ['V']=1, ['W']=1, ['X']=1,
+ ['Y']=1, ['Z']=1,
+ ['_']=1,
+ ['a']=1, ['b']=1, ['c']=1, ['d']=1, ['e']=1, ['f']=1, ['g']=1, ['h']=1,
+ ['i']=1, ['j']=1, ['k']=1, ['l']=1, ['m']=1, ['n']=1, ['o']=1, ['p']=1,
+ ['q']=1, ['r']=1, ['s']=1, ['t']=1, ['u']=1, ['v']=1, ['w']=1, ['x']=1,
+ ['y']=1, ['z']=1,
+ ['~']=1
+ };
+ return table[c];
+}
-#define lowcase(c) tolower (CHAR (c))
+/* C can be in the middle of a token. */
+static bool
+intoken (unsigned char c)
+{
+ static bool const table[UCHAR_MAX + 1] = {
+ ['$']=1,
+ ['0']=1, ['1']=1, ['2']=1, ['3']=1, ['4']=1,
+ ['5']=1, ['6']=1, ['7']=1, ['8']=1, ['9']=1,
+ ['A']=1, ['B']=1, ['C']=1, ['D']=1, ['E']=1, ['F']=1, ['G']=1, ['H']=1,
+ ['I']=1, ['J']=1, ['K']=1, ['L']=1, ['M']=1, ['N']=1, ['O']=1, ['P']=1,
+ ['Q']=1, ['R']=1, ['S']=1, ['T']=1, ['U']=1, ['V']=1, ['W']=1, ['X']=1,
+ ['Y']=1, ['Z']=1,
+ ['_']=1,
+ ['a']=1, ['b']=1, ['c']=1, ['d']=1, ['e']=1, ['f']=1, ['g']=1, ['h']=1,
+ ['i']=1, ['j']=1, ['k']=1, ['l']=1, ['m']=1, ['n']=1, ['o']=1, ['p']=1,
+ ['q']=1, ['r']=1, ['s']=1, ['t']=1, ['u']=1, ['v']=1, ['w']=1, ['x']=1,
+ ['y']=1, ['z']=1
+ };
+ return table[c];
+}
+/* C can end a token. */
+static bool
+endtoken (unsigned char c)
+{
+ static bool const table[UCHAR_MAX + 1] = {
+ ['\0']=1, ['\t']=1, ['\n']=1, ['\r']=1, [' ']=1,
+ ['!']=1, ['"']=1, ['#']=1, ['%']=1, ['&']=1, ['\'']=1, ['(']=1, [')']=1,
+ ['*']=1, ['+']=1, [',']=1, ['-']=1, ['.']=1, ['/']=1, [':']=1, [';']=1,
+ ['<']=1, ['=']=1, ['>']=1, ['?']=1, ['[']=1, [']']=1, ['^']=1,
+ ['{']=1, ['|']=1, ['}']=1, ['~']=1
+ };
+ return table[c];
+}
/*
* xnew, xrnew -- allocate, reallocate storage
* SYNOPSIS: Type *xnew (int n, Type);
* void xrnew (OldPointer, int n, Type);
*/
-#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 xnew(n, Type) ((Type *) xmalloc ((n) * sizeof (Type)))
+#define xrnew(op, n, Type) ((op) = (Type *) xrealloc (op, (n) * sizeof (Type)))
typedef void Lang_function (FILE *);
static language *get_language_from_langname (const char *);
static void readline (linebuffer *, FILE *);
-static long readline_internal (linebuffer *, FILE *);
+static long readline_internal (linebuffer *, FILE *, char const *);
static bool nocase_tail (const char *);
static void get_tag (char *, char **);
static _Noreturn void pfatal (const char *);
static void add_node (node *, node **);
-static void init (void);
static void process_file_name (char *, language *);
static void process_file (FILE *, char *, language *);
static void find_entries (FILE *);
static char *absolute_dirname (char *, char *);
static bool filename_is_absolute (char *f);
static void canonicalize_filename (char *);
+static char *etags_mktmp (void);
static void linebuffer_init (linebuffer *);
static void linebuffer_setlen (linebuffer *, int);
static void *xmalloc (size_t);
-static void *xrealloc (char *, size_t);
+static void *xrealloc (void *, size_t);
\f
static char searchar = '/'; /* use /.../ searches */
static fdesc *fdhead; /* head of file description list */
static fdesc *curfdp; /* current file description */
+static char *infilename; /* current input file name */
static int lineno; /* line number of current line */
static long charno; /* current character number */
static long linecharno; /* charno of start of current line */
static linebuffer filebuf; /* a buffer containing the whole file */
static linebuffer token_name; /* a buffer containing a tag name */
-/* boolean "functions" (see init) */
-static bool _wht[CHARS], _nin[CHARS], _itk[CHARS], _btk[CHARS], _etk[CHARS];
-static const char
- /* white chars */
- *white = " \f\t\n\r\v",
- /* not in a name */
- *nonam = " \f\t\n\r()=,;", /* look at make_tag before modifying! */
- /* token ending chars */
- *endtk = " \t\n\r\"'#()[]{}=-+%*/&|^~!<>;,.:?",
- /* token starting chars */
- *begtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$~@",
- /* valid in-token chars */
- *midtk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789";
-
static bool append_to_tagfile; /* -a: append to tags */
/* The next five default to true in C and derived languages. */
static bool typedefs; /* -t: create tags for C and Ada typedefs */
static bool cplusplus; /* .[hc] means C++, not C (undocumented) */
static bool ignoreindent; /* -I: ignore indentation in C */
static int packages_only; /* --packages-only: in Ada, only tag packages*/
+static int class_qualify; /* -Q: produce class-qualified tags in C++/Java */
/* STDIN is defined in LynxOS system headers */
#ifdef STDIN
{ "members", no_argument, &members, 1 },
{ "no-members", no_argument, &members, 0 },
{ "output", required_argument, NULL, 'o' },
+ { "class-qualify", no_argument, &class_qualify, 'Q' },
{ "regex", required_argument, NULL, 'r' },
{ "no-regex", no_argument, NULL, 'R' },
{ "ignore-case-regex", required_argument, NULL, 'c' },
Do not create tag entries for members of structures\n\
in some languages.");
+ puts ("-Q, --class-qualify\n\
+ Qualify tag names with their class name in C++, ObjC, and Java.\n\
+ This produces tag names of the form \"class::member\" for C++,\n\
+ \"class(category)\" for Objective C, and \"class.member\" for Java.\n\
+ For Objective C, this also produces class methods qualified with\n\
+ their arguments, as in \"foo:bar:baz:more\".");
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\
/* When the optstring begins with a '-' getopt_long does not rearrange the
non-options arguments to be at the end, but leaves them alone. */
- optstring = concat ("-ac:Cf:Il:o:r:RSVhH",
+ optstring = concat ("-ac:Cf:Il:o:Qr:RSVhH",
(CTAGS) ? "BxdtTuvw" : "Di:",
"");
case 'H':
help_asked = true;
break;
+ case 'Q':
+ class_qualify = 1;
+ break;
/* Etags options */
case 'D': constantypedefs = false; break;
tagfiledir = absolute_dirname (tagfile, cwd);
}
- init (); /* set up boolean "functions" */
-
linebuffer_init (&lb);
linebuffer_init (&filename_lb);
linebuffer_init (&filebuf);
if (parsing_stdin)
fatal ("cannot parse standard input AND read file names from it",
(char *)NULL);
- while (readline_internal (&filename_lb, stdin) > 0)
+ while (readline_internal (&filename_lb, stdin, "-") > 0)
process_file_name (filename_lb.buffer, lang);
}
else
static void
process_file_name (char *file, language *lang)
{
- struct stat stat_buf;
FILE *inf;
fdesc *fdp;
compressor *compr;
char *compressed_name, *uncompressed_name;
- char *ext, *real_name;
+ char *ext, *real_name, *tmp_name;
int retval;
canonicalize_filename (file);
error ("skipping inclusion of %s in self.", file);
return;
}
- if ((compr = get_compressor_from_suffix (file, &ext)) == NULL)
+ compr = get_compressor_from_suffix (file, &ext);
+ if (compr)
{
- compressed_name = NULL;
- real_name = uncompressed_name = savestr (file);
+ compressed_name = file;
+ uncompressed_name = savenstr (file, ext - file);
}
else
{
- real_name = compressed_name = savestr (file);
- uncompressed_name = savenstr (file, ext - file);
+ compressed_name = NULL;
+ uncompressed_name = file;
}
/* If the canonicalized uncompressed name
goto cleanup;
}
- if (stat (real_name, &stat_buf) != 0)
+ inf = fopen (file, "r" FOPEN_BINARY);
+ if (inf)
+ real_name = file;
+ else
{
- /* Reset real_name and try with a different name. */
- real_name = NULL;
- if (compressed_name != NULL) /* try with the given suffix */
+ int file_errno = errno;
+ if (compressed_name)
{
- if (stat (uncompressed_name, &stat_buf) == 0)
+ /* Try with the given suffix. */
+ inf = fopen (uncompressed_name, "r" FOPEN_BINARY);
+ if (inf)
real_name = uncompressed_name;
}
- else /* try all possible suffixes */
+ else
{
+ /* Try all possible suffixes. */
for (compr = compressors; compr->suffix != NULL; compr++)
{
compressed_name = concat (file, ".", compr->suffix);
- if (stat (compressed_name, &stat_buf) != 0)
+ inf = fopen (compressed_name, "r" FOPEN_BINARY);
+ if (inf)
{
- if (MSDOS)
+ real_name = compressed_name;
+ break;
+ }
+ if (MSDOS)
+ {
+ char *suf = compressed_name + strlen (file);
+ size_t suflen = strlen (compr->suffix) + 1;
+ for ( ; suf[1]; suf++, suflen--)
{
- char *suf = compressed_name + strlen (file);
- size_t suflen = strlen (compr->suffix) + 1;
- for ( ; suf[1]; suf++, suflen--)
+ memmove (suf, suf + 1, suflen);
+ inf = fopen (compressed_name, "r" FOPEN_BINARY);
+ if (inf)
{
- memmove (suf, suf + 1, suflen);
- if (stat (compressed_name, &stat_buf) == 0)
- {
- real_name = compressed_name;
- break;
- }
+ real_name = compressed_name;
+ break;
}
- if (real_name != NULL)
- break;
- } /* MSDOS */
- free (compressed_name);
- compressed_name = NULL;
- }
- else
- {
- real_name = compressed_name;
- break;
+ }
+ if (inf)
+ break;
}
+ free (compressed_name);
+ compressed_name = NULL;
}
}
- if (real_name == NULL)
+ if (! inf)
{
+ errno = file_errno;
perror (file);
goto cleanup;
}
- } /* try with a different name */
-
- 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 = popen (cmd, "r" FOPEN_BINARY);
- free (cmd);
- }
- else
- inf = fopen (real_name, "r" FOPEN_BINARY);
- if (inf == NULL)
- {
- perror (real_name);
- goto cleanup;
+ fclose (inf);
+ tmp_name = etags_mktmp ();
+ if (!tmp_name)
+ inf = NULL;
+ else
+ {
+#if MSDOS || defined (DOS_NT)
+ char *cmd1 = concat (compr->command, " \"", real_name);
+ char *cmd = concat (cmd1, "\" > ", tmp_name);
+#else
+ char *cmd1 = concat (compr->command, " '", real_name);
+ char *cmd = concat (cmd1, "' > ", tmp_name);
+#endif
+ free (cmd1);
+ int tmp_errno;
+ if (system (cmd) == -1)
+ {
+ inf = NULL;
+ tmp_errno = EINVAL;
+ }
+ else
+ {
+ inf = fopen (tmp_name, "r" FOPEN_BINARY);
+ tmp_errno = errno;
+ }
+ free (cmd);
+ errno = tmp_errno;
+ }
+
+ if (!inf)
+ {
+ perror (real_name);
+ goto cleanup;
+ }
}
process_file (inf, uncompressed_name, lang);
+ retval = fclose (inf);
if (real_name == compressed_name)
- retval = pclose (inf);
- else
- retval = fclose (inf);
+ {
+ remove (tmp_name);
+ free (tmp_name);
+ }
if (retval < 0)
pfatal (file);
cleanup:
- free (compressed_name);
- free (uncompressed_name);
+ if (compressed_name != file)
+ free (compressed_name);
+ if (uncompressed_name != file)
+ free (uncompressed_name);
last_node = NULL;
curfdp = NULL;
return;
static const fdesc emptyfdesc;
fdesc *fdp;
+ infilename = fn;
/* Create a new input file description entry. */
fdp = xnew (1, fdesc);
*fdp = emptyfdesc;
}
}
-/*
- * This routine sets up the boolean pseudo-functions which work
- * by setting boolean flags dependent upon the corresponding character.
- * Every char which is NOT in that string is not a white char. Therefore,
- * all of the array "_wht" is set to false, and then the elements
- * subscripted by the chars in "white" are set to true. Thus "_wht"
- * of a char is true if it is the string "white", else false.
- */
static void
-init (void)
+reset_input (FILE *inf)
{
- const char *sp;
- 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');
+ if (fseek (inf, 0, SEEK_SET) != 0)
+ perror (infilename);
}
/*
/* Else look for sharp-bang as the first two characters. */
if (parser == NULL
- && readline_internal (&lb, inf) > 0
+ && readline_internal (&lb, inf, infilename) > 0
&& lb.len >= 2
&& lb.buffer[0] == '#'
&& lb.buffer[1] == '!')
}
}
- /* We rewind here, even if inf may be a pipe. We fail if the
- length of the first line is longer than the pipe block size,
- which is unlikely. */
- rewind (inf);
+ reset_input (inf);
/* Else try to guess the language given the case insensitive file name. */
if (parser == NULL)
if (old_last_node == last_node)
/* No Fortran entries found. Try C. */
{
- /* We do not tag if rewind fails.
- Only the file name will be recorded in the tags file. */
- rewind (inf);
+ reset_input (inf);
curfdp->lang = get_language_from_langname (cplusplus ? "c++" : "c");
find_entries (inf);
}
case omethodparm:
if (parlev == 0)
{
- int oldlen = token_name.len;
- fvdef = fvnone;
objdef = omethodtag;
- linebuffer_setlen (&token_name, oldlen + len);
- memcpy (token_name.buffer + oldlen, str, len);
- token_name.buffer[oldlen + len] = '\0';
+ if (class_qualify)
+ {
+ int oldlen = token_name.len;
+ fvdef = fvnone;
+ linebuffer_setlen (&token_name, oldlen + len);
+ memcpy (token_name.buffer + oldlen, str, len);
+ token_name.buffer[oldlen + len] = '\0';
+ }
return true;
}
return false;
case st_none:
if (constantypedefs
&& structdef == snone
- && structtype == st_C_enum && bracelev > structbracelev)
+ && structtype == st_C_enum && bracelev > structbracelev
+ /* Don't tag tokens in expressions that assign values to enum
+ constants. */
+ && fvdef != vignore)
return true; /* enum constant */
switch (fvdef)
{
#define CNL() \
do { \
- CNL_SAVE_DEFINEDEF(); \
+ CNL_SAVE_DEFINEDEF (); \
if (savetoken.valid) \
{ \
token = savetoken; \
token.valid = false;
}
+static bool
+perhaps_more_input (FILE *inf)
+{
+ return !feof (inf) && !ferror (inf);
+}
+
/*
* C_entries ()
{ qualifier = "::"; qlen = 2; }
- while (!feof (inf))
+ while (perhaps_more_input (inf))
{
c = *lp++;
if (c == '\\')
followed by an end of comment, this is a preprocessor
token. */
for (cp = newlb.buffer; cp < lp-1; cp++)
- if (!iswhite (*cp))
+ if (!c_isspace (*cp))
{
if (*cp == '*' && cp[1] == '/')
{
cpptoken = false;
}
if (cpptoken)
- definedef = dsharpseen;
+ {
+ definedef = dsharpseen;
+ /* This is needed for tagging enum values: when there are
+ preprocessor conditionals inside the enum, we need to
+ reset the value of fvdef so that the next enum value is
+ tagged even though the one before it did not end in a
+ comma. */
+ if (fvdef == vignore && instruct && parlev == 0)
+ {
+ if (strneq (cp, "#if", 3) || strneq (cp, "#el", 3))
+ fvdef = fvnone;
+ }
+ }
} /* if (definedef == dnone) */
continue;
case '[':
if (*lp != '\0')
lp += 1;
while (*lp != '\0'
- && !iswhite (*lp) && *lp != '(')
+ && !c_isspace (*lp) && *lp != '(')
lp += 1;
c = *lp++;
toklen += lp - oldlp;
&& nestlev > 0 && definedef == dnone)
/* in struct body */
{
- int len;
- write_classname (&token_name, qualifier);
- len = token_name.len;
- linebuffer_setlen (&token_name, len+qlen+toklen);
- sprintf (token_name.buffer + len, "%s%.*s",
- qualifier, toklen, newlb.buffer + tokoff);
+ if (class_qualify)
+ {
+ int len;
+ write_classname (&token_name, qualifier);
+ len = token_name.len;
+ linebuffer_setlen (&token_name,
+ len + qlen + toklen);
+ sprintf (token_name.buffer + len, "%s%.*s",
+ qualifier, toklen,
+ newlb.buffer + tokoff);
+ }
+ else
+ {
+ linebuffer_setlen (&token_name, toklen);
+ sprintf (token_name.buffer, "%.*s",
+ toklen, newlb.buffer + tokoff);
+ }
token.named = true;
}
else if (objdef == ocatseen)
/* Objective C category */
{
- int len = strlen (objtag) + 2 + toklen;
- linebuffer_setlen (&token_name, len);
- sprintf (token_name.buffer, "%s(%.*s)",
- objtag, toklen, newlb.buffer + tokoff);
+ if (class_qualify)
+ {
+ int len = strlen (objtag) + 2 + toklen;
+ linebuffer_setlen (&token_name, len);
+ sprintf (token_name.buffer, "%s(%.*s)",
+ objtag, toklen,
+ newlb.buffer + tokoff);
+ }
+ else
+ {
+ linebuffer_setlen (&token_name, toklen);
+ sprintf (token_name.buffer, "%.*s",
+ toklen, newlb.buffer + tokoff);
+ }
token.named = true;
}
else if (objdef == omethodtag
case omethodtag:
case omethodparm:
objdef = omethodcolon;
- int toklen = token_name.len;
- linebuffer_setlen (&token_name, toklen + 1);
- strcpy (token_name.buffer + toklen, ":");
+ if (class_qualify)
+ {
+ int toklen = token_name.len;
+ linebuffer_setlen (&token_name, toklen + 1);
+ strcpy (token_name.buffer + toklen, ":");
+ }
break;
}
if (structdef == stagseen)
case fstartlist:
case finlist:
case fignore:
+ break;
case vignore:
+ if (instruct && parlev == 0)
+ fvdef = fvnone;
break;
case fdefunname:
fvdef = fignore;
switch (fvdef)
{
case flistseen:
+ if (cplpl && !class_qualify)
+ {
+ /* Remove class and namespace qualifiers from the token,
+ leaving only the method/member name. */
+ char *cc, *uqname = token_name.buffer;
+ char *tok_end = token_name.buffer + token_name.len;
+
+ for (cc = token_name.buffer; cc < tok_end; cc++)
+ {
+ if (*cc == ':' && cc[1] == ':')
+ {
+ uqname = cc + 2;
+ cc++;
+ }
+ }
+ if (uqname > token_name.buffer)
+ {
+ int uqlen = strlen (uqname);
+ linebuffer_setlen (&token_name, uqlen);
+ memmove (token_name.buffer, uqname, uqlen + 1);
+ }
+ }
make_C_tag (true); /* a function */
/* FALLTHRU */
case fignore:
\f
/* Useful macros. */
#define LOOP_ON_INPUT_LINES(file_pointer, line_buffer, char_pointer) \
- for (; /* loop initialization */ \
- !feof (file_pointer) /* loop test */ \
- && /* instructions at start of loop */ \
- (readline (&line_buffer, file_pointer), \
- char_pointer = line_buffer.buffer, \
- true); \
- )
+ while (perhaps_more_input (file_pointer) \
+ && (readline (&(line_buffer), file_pointer), \
+ (char_pointer) = (line_buffer).buffer, \
+ true)) \
#define LOOKING_AT(cp, kw) /* kw is the keyword, a literal string */ \
((assert ("" kw), true) /* syntax error if not a literal string */ \
static void
just_read_file (FILE *inf)
{
- while (!feof (inf))
+ while (perhaps_more_input (inf))
readline (&lb, inf);
}
dbp += 3;
return;
}
- if (!ISDIGIT (*dbp))
+ if (!c_isdigit (*dbp))
{
--dbp; /* force failure */
return;
}
do
dbp++;
- while (ISDIGIT (*dbp));
+ while (c_isdigit (*dbp));
}
static void
dbp += 6;
dbp = skip_spaces (dbp);
}
- if (!ISALPHA (*dbp) && *dbp != '_' && *dbp != '$')
+ if (!c_isalpha (*dbp) && *dbp != '_' && *dbp != '$')
return;
for (cp = dbp + 1; *cp != '\0' && intoken (*cp); cp++)
continue;
if (LOOKING_AT_NOCASE (dbp, "elemental"))
dbp = skip_spaces (dbp);
- switch (lowcase (*dbp))
+ switch (c_tolower (*dbp))
{
case 'i':
if (nocase_tail ("integer"))
dbp = skip_spaces (dbp);
if (*dbp == '\0')
continue;
- switch (lowcase (*dbp))
+ switch (c_tolower (*dbp))
{
case 'f':
if (nocase_tail ("function"))
char *name;
char c;
- while (!feof (inf))
+ while (perhaps_more_input (inf))
{
dbp = skip_spaces (dbp);
if (*dbp == '\0'
readline (&lb, inf);
dbp = lb.buffer;
}
- switch (lowcase (*dbp))
+ switch (c_tolower (*dbp))
{
case 'b':
if (nocase_tail ("body"))
{
dbp = skip_spaces (dbp);
for (cp = dbp;
- (*cp != '\0'
- && (ISALPHA (*cp) || ISDIGIT (*cp) || *cp == '_' || *cp == '.'));
+ c_isalnum (*cp) || *cp == '_' || *cp == '.';
cp++)
continue;
if (cp == dbp)
}
/* We are at the beginning of a token. */
- switch (lowcase (*dbp))
+ switch (c_tolower (*dbp))
{
case 'f':
if (!packages_only && nocase_tail ("function"))
{
/* If first char is alphabetic or one of [_.$], test for colon
following identifier. */
- if (ISALPHA (*cp) || *cp == '_' || *cp == '.' || *cp == '$')
+ if (c_isalpha (*cp) || *cp == '_' || *cp == '.' || *cp == '$')
{
/* Read past label. */
cp++;
- while (ISALNUM (*cp) || *cp == '_' || *cp == '.' || *cp == '$')
+ while (c_isalnum (*cp) || *cp == '_' || *cp == '.' || *cp == '$')
cp++;
- if (*cp == ':' || iswhite (*cp))
+ if (*cp == ':' || c_isspace (*cp))
/* Found end of label, so copy it and add it to the table. */
make_tag (lb.buffer, cp - lb.buffer, true,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
cp++;
if (cp == sp)
continue; /* nothing found */
- if ((pos = strchr (sp, ':')) != NULL
- && pos < cp && pos[1] == ':')
+ pos = strchr (sp, ':');
+ if (pos && pos < cp && pos[1] == ':')
/* The name is already qualified. */
make_tag (sp, cp - sp, true,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
varstart += 1;
do
cp++;
- while (ISALNUM (*cp) || *cp == '_');
+ while (c_isalnum (*cp) || *cp == '_');
}
else if (qual)
{
if (*cp != '\0')
{
name = cp;
- while (*cp != '\0' && !iswhite (*cp))
+ while (*cp != '\0' && !c_isspace (*cp))
cp++;
make_tag (name, cp - name, false,
lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
bp += 8;
/* If eoln, compiler option or comment ignore whole line. */
- if (bp[-1] != ' ' || !ISALNUM (bp[0]))
+ if (bp[-1] != ' ' || !c_isalnum (bp[0]))
continue;
- for (ep = bp; ISALNUM (*ep) || *ep == '-'; ep++)
+ for (ep = bp; c_isalnum (*ep) || *ep == '-'; ep++)
continue;
if (*ep++ == '.')
make_tag (bp, ep - bp, true,
verify_tag = false; /* check if "extern" is ahead */
- while (!feof (inf)) /* long main loop to get next char */
+ while (perhaps_more_input (inf)) /* long main loop to get next char */
{
c = *dbp++;
if (c == '\0') /* if end of line */
/* Check if this is an "extern" declaration. */
if (*dbp == '\0')
continue;
- if (lowcase (*dbp) == 'e')
+ if (c_tolower (*dbp) == 'e')
{
if (nocase_tail ("extern")) /* superfluous, really! */
{
verify_tag = false;
}
}
- else if (lowcase (*dbp) == 'f')
+ else if (c_tolower (*dbp) == 'f')
{
if (nocase_tail ("forward")) /* check for forward reference */
{
else if (!incomment && !inquote && !found_tag)
{
/* Check for proc/fn keywords. */
- switch (lowcase (c))
+ switch (c_tolower (c))
{
case 'p':
if (nocase_tail ("rocedure")) /* c = 'p', dbp has advanced */
LOOP_ON_INPUT_LINES (inf, lb, bp)
while ((bp = skip_spaces (bp))[0] != '\0')
- if (bp[0] == '\\' && iswhite (bp[1]))
+ if (bp[0] == '\\' && c_isspace (bp[1]))
break; /* read next line */
- else if (bp[0] == '(' && iswhite (bp[1]))
+ else if (bp[0] == '(' && c_isspace (bp[1]))
do /* skip to ) or eol */
bp++;
while (*bp != ')' && *bp != '\0');
- else if ((bp[0] == ':' && iswhite (bp[1]) && bp++)
+ else if ((bp[0] == ':' && c_isspace (bp[1]) && bp++)
|| LOOKING_AT_NOCASE (bp, "constant")
|| LOOKING_AT_NOCASE (bp, "code")
|| LOOKING_AT_NOCASE (bp, "create")
:part:appendix:entry:index:def\
:newcommand:renewcommand:newenvironment:renewenvironment";
-static void TEX_mode (FILE *);
static void TEX_decode_env (const char *, const char *);
-static char TEX_esc = '\\';
-static char TEX_opgrp = '{';
-static char TEX_clgrp = '}';
-
/*
* TeX/LaTeX scanning loop.
*/
char *cp;
linebuffer *key;
- /* Select either \ or ! as escape character. */
- TEX_mode (inf);
+ char TEX_esc = '\0';
+ char TEX_opgrp, TEX_clgrp;
/* Initialize token table once from environment. */
if (TEX_toktab == NULL)
for (;;)
{
/* Look for a TEX escape. */
- while (*cp++ != TEX_esc)
- if (cp[-1] == '\0' || cp[-1] == '%')
- goto tex_next_line;
+ while (true)
+ {
+ char c = *cp++;
+ if (c == '\0' || c == '%')
+ goto tex_next_line;
+
+ /* Select either \ or ! as escape character, whichever comes
+ first outside a comment. */
+ if (!TEX_esc)
+ switch (c)
+ {
+ case '\\':
+ TEX_esc = c;
+ TEX_opgrp = '{';
+ TEX_clgrp = '}';
+ break;
+
+ case '!':
+ TEX_esc = c;
+ TEX_opgrp = '<';
+ TEX_clgrp = '>';
+ break;
+ }
+
+ if (c == TEX_esc)
+ break;
+ }
for (key = TEX_toktab; key->buffer != NULL; key++)
if (strneq (cp, key->buffer, key->len))
cp++;
}
for (p = cp;
- (!iswhite (*p) && *p != '#' &&
+ (!c_isspace (*p) && *p != '#' &&
*p != TEX_opgrp && *p != TEX_clgrp);
p++)
continue;
}
}
-#define TEX_LESC '\\'
-#define TEX_SESC '!'
-
-/* Figure out whether TeX's escapechar is '\\' or '!' and set grouping
- chars accordingly. */
-static void
-TEX_mode (FILE *inf)
-{
- int c;
-
- while ((c = getc (inf)) != EOF)
- {
- /* Skip to next line if we hit the TeX comment char. */
- 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 = '>';
- }
- /* If the input file is compressed, inf is a pipe, and rewind may fail.
- No attempt is made to correct the situation. */
- rewind (inf);
-}
-
/* Read environment and prepend it to the default string.
Build token table. */
static void
env = concat (env, defenv, "");
/* Allocate a token table */
- for (len = 1, p = env; p;)
- if ((p = strchr (p, ':')) && *++p != '\0')
+ for (len = 1, p = env; (p = strchr (p, ':')); )
+ if (*++p)
len++;
TEX_toktab = xnew (len, linebuffer);
else if (intag) /* look for "name=" or "id=" */
{
while (*dbp != '\0' && *dbp != '>'
- && lowcase (*dbp) != 'n' && lowcase (*dbp) != 'i')
+ && c_tolower (*dbp) != 'n' && c_tolower (*dbp) != 'i')
dbp++;
if (*dbp == '\0')
break; /* go to next line */
if (*dbp == '<')
{
intag = true;
- inanchor = (lowcase (dbp[1]) == 'a' && !intoken (dbp[2]));
+ inanchor = (c_tolower (dbp[1]) == 'a' && !intoken (dbp[2]));
continue; /* look on the same line */
}
if (*dbp == '\0')
break; /* go to next line */
intag = true;
- if (lowcase (dbp[1]) == 'a' && !intoken (dbp[2]))
+ if (c_tolower (dbp[1]) == 'a' && !intoken (dbp[2]))
{
inanchor = true;
continue; /* look on the same line */
{
if (cp[0] == '\0') /* Empty line */
continue;
- else if (iswhite (cp[0])) /* Not a predicate */
+ else if (c_isspace (cp[0])) /* Not a predicate */
continue;
else if (cp[0] == '/' && cp[1] == '*') /* comment. */
prolog_skip_comment (&lb, inf);
return;
readline (plb, inf);
}
- while (!feof (inf));
+ while (perhaps_more_input (inf));
}
/*
origpos = pos;
- if (ISLOWER (s[pos]) || (s[pos] == '_'))
+ if (c_islower (s[pos]) || s[pos] == '_')
{
/* The atom is unquoted. */
pos++;
- while (ISALNUM (s[pos]) || (s[pos] == '_'))
+ while (c_isalnum (s[pos]) || s[pos] == '_')
{
pos++;
}
{
if (cp[0] == '\0') /* Empty line */
continue;
- else if (iswhite (cp[0])) /* Not function nor attribute */
+ else if (c_isspace (cp[0])) /* Not function nor attribute */
continue;
else if (cp[0] == '%') /* comment */
continue;
{
int pos = 0;
- if (ISALPHA (s[pos]) || s[pos] == '_')
+ if (c_isalpha (s[pos]) || s[pos] == '_')
{
/* The atom is unquoted. */
do
pos++;
- while (ISALNUM (s[pos]) || s[pos] == '_');
+ while (c_isalnum (s[pos]) || s[pos] == '_');
}
else if (s[pos] == '\'')
{
if (regexfp == NULL)
pfatal (regexfile);
linebuffer_init (®exbuf);
- while (readline_internal (®exbuf, regexfp) > 0)
+ while (readline_internal (®exbuf, regexfp, regexfile) > 0)
analyze_regex (regexbuf.buffer);
free (regexbuf.buffer);
- fclose (regexfp);
+ if (fclose (regexfp) != 0)
+ pfatal (regexfile);
}
break;
*patbuf = zeropattern;
if (ignore_case)
{
- static char lc_trans[CHARS];
+ static char lc_trans[UCHAR_MAX + 1];
int i;
- for (i = 0; i < CHARS; i++)
- lc_trans[i] = lowcase (i);
+ for (i = 0; i < UCHAR_MAX + 1; i++)
+ lc_trans[i] = c_tolower (i);
patbuf->translate = lc_trans; /* translation table to fold case */
}
for (t = strchr (out, '\\');
t != NULL;
t = strchr (t + 2, '\\'))
- if (ISDIGIT (t[1]))
+ if (c_isdigit (t[1]))
{
dig = t[1] - '0';
diglen = regs->end[dig] - regs->start[dig];
result = xnew (size + 1, char);
for (t = result; *out != '\0'; out++)
- if (*out == '\\' && ISDIGIT (*++out))
+ if (*out == '\\' && c_isdigit (*++out))
{
dig = *out - '0';
diglen = regs->end[dig] - regs->start[dig];
static bool
nocase_tail (const char *cp)
{
- register int len = 0;
+ int len = 0;
- while (*cp != '\0' && lowcase (*cp) == lowcase (dbp[len]))
+ while (*cp != '\0' && c_tolower (*cp) == c_tolower (dbp[len]))
cp++, len++;
if (*cp == '\0' && !intoken (dbp[len]))
{
* appended to `filebuf'.
*/
static long
-readline_internal (linebuffer *lbp, register FILE *stream)
+readline_internal (linebuffer *lbp, FILE *stream, char const *filename)
{
char *buffer = lbp->buffer;
- register char *p = lbp->buffer;
- register char *pend;
+ char *p = lbp->buffer;
+ char *pend;
int chars_deleted;
pend = p + lbp->size; /* Separate to avoid 386/IX compiler bug. */
}
if (c == EOF)
{
+ if (ferror (stream))
+ perror (filename);
*p = '\0';
chars_deleted = 0;
break;
if (p > buffer && p[-1] == '\r')
{
p -= 1;
-#ifdef DOS_NT
- /* Assume CRLF->LF translation will be performed by Emacs
- when loading this file, so CRs won't appear in the buffer.
- It would be cleaner to compensate within Emacs;
- however, Emacs does not know how many CRs were deleted
- before any given point in the file. */
- chars_deleted = 1;
-#else
chars_deleted = 2;
-#endif
}
else
{
long result;
linecharno = charno; /* update global char number of line start */
- result = readline_internal (lbp, stream); /* read line */
+ result = readline_internal (lbp, stream, infilename); /* read line */
lineno += 1; /* increment global line number */
charno += result; /* increment global char number */
static char *
skip_spaces (char *cp)
{
- while (iswhite (*cp))
+ while (c_isspace (*cp))
cp++;
return cp;
}
static char *
skip_non_spaces (char *cp)
{
- while (*cp != '\0' && !iswhite (*cp))
+ while (*cp != '\0' && !c_isspace (*cp))
cp++;
return cp;
}
return path;
}
+/* Return a newly allocated string containing a name of a temporary file. */
+static char *
+etags_mktmp (void)
+{
+ const char *tmpdir = getenv ("TMPDIR");
+ const char *slash = "/";
+
+#if MSDOS || defined (DOS_NT)
+ if (!tmpdir)
+ tmpdir = getenv ("TEMP");
+ if (!tmpdir)
+ tmpdir = getenv ("TMP");
+ if (!tmpdir)
+ tmpdir = ".";
+ if (tmpdir[strlen (tmpdir) - 1] == '/'
+ || tmpdir[strlen (tmpdir) - 1] == '\\')
+ slash = "";
+#else
+ if (!tmpdir)
+ tmpdir = "/tmp";
+ if (tmpdir[strlen (tmpdir) - 1] == '/')
+ slash = "";
+#endif
+
+ char *templt = concat (tmpdir, slash, "etXXXXXX");
+ int fd = mkostemp (templt, O_CLOEXEC);
+ if (fd < 0 || close (fd) != 0)
+ {
+ int temp_errno = errno;
+ free (templt);
+ errno = temp_errno;
+ templt = NULL;
+ }
+
+#if defined (DOS_NT)
+ /* The file name will be used in shell redirection, so it needs to have
+ DOS-style backslashes, or else the Windows shell will barf. */
+ char *p;
+ for (p = templt; *p; p++)
+ if (*p == '/')
+ *p = '\\';
+#endif
+
+ return templt;
+}
+
/* Return a newly allocated string containing the file name of FILE
relative to the absolute directory DIR (which should end with a slash). */
static char *
{
return (fn[0] == '/'
#ifdef DOS_NT
- || (ISALPHA (fn[0]) && fn[1] == ':' && fn[2] == '/')
+ || (c_isalpha (fn[0]) && fn[1] == ':' && fn[2] == '/')
#endif
);
}
canonicalize_filename (register char *fn)
{
register char* cp;
- char sep = '/';
#ifdef DOS_NT
/* Canonicalize drive letter case. */
-# define ISUPPER(c) isupper (CHAR (c))
- if (fn[0] != '\0' && fn[1] == ':' && ISUPPER (fn[0]))
- fn[0] = lowcase (fn[0]);
+ if (c_isupper (fn[0]) && fn[1] == ':')
+ fn[0] = c_tolower (fn[0]);
- sep = '\\';
-#endif
+ /* Collapse multiple forward- and back-slashes into a single forward
+ slash. */
+ for (cp = fn; *cp != '\0'; cp++, fn++)
+ if (*cp == '/' || *cp == '\\')
+ {
+ *fn = '/';
+ while (cp[1] == '/' || cp[1] == '\\')
+ cp++;
+ }
+ else
+ *fn = *cp;
+
+#else /* !DOS_NT */
- /* Collapse multiple separators into a single slash. */
+ /* Collapse multiple slashes into a single slash. */
for (cp = fn; *cp != '\0'; cp++, fn++)
- if (*cp == sep)
+ if (*cp == '/')
{
*fn = '/';
- while (cp[1] == sep)
+ while (cp[1] == '/')
cp++;
}
else
*fn = *cp;
+
+#endif /* !DOS_NT */
+
*fn = '\0';
}
}
static void *
-xrealloc (char *ptr, size_t size)
+xrealloc (void *ptr, size_t size)
{
void *result = realloc (ptr, size);
if (result == NULL)