changeset 35665:2ce207b252dc

Many changes to the parsing capabilities of etags. The -C or -c++ options are now mostly obsolete. Can delve at arbitrary deeps into structures of C-like languages.
author Francesco Potortì <pot@gnu.org>
date Sun, 28 Jan 2001 16:14:04 +0000
parents 16e83e3dab3d
children b85d466802a5
files etc/NEWS lib-src/ChangeLog lib-src/etags.1 lib-src/etags.c
diffstat 4 files changed, 451 insertions(+), 260 deletions(-) [+]
line wrap: on
line diff
--- a/etc/NEWS	Sun Jan 28 15:47:04 2001 +0000
+++ b/etc/NEWS	Sun Jan 28 16:14:04 2001 +0000
@@ -1460,6 +1460,12 @@
 *** In C++, tags are created for "operator".  The tags have the form
 "operator+", without spaces between the keyword and the operator.
 
+*** You shouldn't generally need any more the -C or -c++ option: etags
+automatically detects a C++ file when it meets the `class' keyword.
+
+*** Etags now is able to delve at arbitrary deeps into nested structures in
+C-like languages.  Previously, it was limited to one or two brace levels.
+
 *** New language Ada: tags are functions, procedures, packages, tasks, and
 types.
 
--- a/lib-src/ChangeLog	Sun Jan 28 15:47:04 2001 +0000
+++ b/lib-src/ChangeLog	Sun Jan 28 16:14:04 2001 +0000
@@ -1,10 +1,33 @@
+2001-01-28  Francesco Potorti`  <pot@gnu.org>
+
+	* etags.c: Be capable to parse nested struct-like structures.
+	(structdef, structtag): Struct state machine revisited.
+	(struct tok): Revisited.
+	(cstack, nestlev, instruct): New struct and macros.
+	(pushclass_above, popclass_above, write_classname): New functions
+	for dealing with nested class names, inspired by Mykola Dzyuba.
+	(consider_token, make_C_tag, C_entries): Many changes for dealing
+	with arbitrarily nested structures.
+	(etags_getcwd): #if MSDOS, not #ifdef MSDOS!
+	(C_entries): Consider templates in C++.
+	(sym_type): New constant st_C_class for detecting "class" also in
+	C mode.
+	(C_AUTO): New macro for automatic detection of C++.
+	(consider_token): Automatic set C++ mode.
+	(C_entries): New security check for yacc.
+	(print_language_names, print_help): Mention the autodetect
+	feature, do not show help for the -C option, now mostly	useless.
+	(C_entries): Tag C++ forward declarations if --declarations.
+	(C_entries): Don't be fooled by things like XDEFUN.
+	(consider_token): Discard asm pseudo function.
+
 2001-01-27  Eli Zaretskii  <eliz@is.elta.co.il>
 
 	* etags.c: Add a coding: tag.
 
 2001-01-26  Gerd Moellmann  <gerd@gnu.org>
 
-	* ebrowse.c (matching_regexp_buffer, matching_regexp_end_buf): 
+	* ebrowse.c (matching_regexp_buffer, matching_regexp_end_buf):
 	New variables.
 	(matching_regexp): Use them instead of static variables in
 	function scope.
--- a/lib-src/etags.1	Sun Jan 28 15:47:04 2001 +0000
+++ b/lib-src/etags.1	Sun Jan 28 16:14:04 2001 +0000
@@ -16,7 +16,7 @@
 .if n .br
 .B [\|\-o \fItagfile\fP\|] [\|\-r \fIregexp\fP\|]
 .br
-.B [\|\-\-append\|] [\|\-\-c++\|] [\|\-\-no\-defines\|]
+.B [\|\-\-append\|] [\|\-\-no\-defines\|]
 .B [\|\-\-no\-globals\|] [\|\-\-include=\fIfile\fP\|]
 .B [\|\-\-ignore\-indentation\|] [\|\-\-language=\fIlanguage\fP\|]
 .B [\|\-\-members\|] [\|\-\-output=\fItagfile\fP\|]
@@ -29,7 +29,7 @@
 .if n .br
 .B [\|\-o \fItagfile\fP\|] [\|\-r \fIregexp\fP\|]
 .br
-.B [\|\-\-append\|] [\|\-\-backward\-search\|] [\|\-\-c++\|]
+.B [\|\-\-append\|] [\|\-\-backward\-search\|]
 .B [\|\-\-cxref\|] [\|\-\-defines\|] [\|\-\-forward\-search\|]
 .B [\|\-\-globals\|] [\|\-\-ignore\-indentation\|]
 .B [\|\-\-language=\fIlanguage\fP\|] [\|\-\-members\|]
@@ -81,11 +81,6 @@
 through files.
 Only \fBctags\fP accepts this option.
 .TP
-.B \-C, \-\-c++
-Treat files with `\|.c\|' and `\|.h\|' extensions as C++ code, not C
-code.  Files with `\|.C\|', `\|.H\|', `\|.cxx\|', `\|.hxx\|', or
-`\|.cc\|' extensions are always assumed to be C++ code.
-.TP
 .B \-\-declarations
 In C and derived languages, create tags for function declarations,
 and create tags for extern variables unless \-\-no\-globals is used.
--- a/lib-src/etags.c	Sun Jan 28 15:47:04 2001 +0000
+++ b/lib-src/etags.c	Sun Jan 28 16:14:04 2001 +0000
@@ -32,7 +32,7 @@
  *	Francesco Potort́ <pot@gnu.org> has maintained it since 1993.
  */
 
-char pot_etags_version[] = "@(#) pot number is $Revision: 2.79 $";
+char pot_etags_version[] = "@(#) pot revision number is 14.11";
 
 #define	TRUE	1
 #define	FALSE	0
@@ -156,11 +156,13 @@
 #endif
 
 /* C extensions. */
+#define C_EXT	0x00fff		/* C extensions */
+#define C_PLAIN 0x00000		/* C */
 #define C_PLPL	0x00001		/* C++ */
 #define C_STAR	0x00003		/* C* */
 #define C_JAVA	0x00005		/* JAVA */
+#define C_AUTO  0x01000		/* C, switch to C++ if `class' is met */
 #define YACC	0x10000		/* yacc file */
-#define PUREC   (!(c_ext & ~YACC)) /* no extensions (apart from possibly yacc) */
 
 #define streq(s,t)	(assert((s)!=NULL || (t)!=NULL), !strcmp (s, t))
 #define strneq(s,t,n)	(assert((s)!=NULL || (t)!=NULL), !strncmp (s, t, n))
@@ -601,6 +603,7 @@
 the first line of the file is read for a sharp-bang (#!) sequence\n\
 followed by the name of an interpreter.  If no such sequence is found,\n\
 Fortran is tried first; if no tags are found, C is tried next.\n\
+When parsing any C file, a \"class\" keyword switches to C++.\n\
 Compressed files are supported using gzip and bzip2.");
 }
 
@@ -649,8 +652,13 @@
         Write the search commands for the tag entries using '?', the\n\
         backward-search command instead of '/', the forward-search command.");
 
+  /* This option is mostly obsolete, because etags can now automatically
+     detect C++.  Retained for backward compatibility and for debugging and
+     experimentation.  In principle, we could want to tag as C++ even
+     before any "class" keyword.
   puts ("-C, --c++\n\
         Treat files whose name suffix defaults to C language as C++ files.");
+  */
 
   puts ("--declarations\n\
 	In C and derived languages, create tags for function declarations,");
@@ -934,13 +942,14 @@
 
   /*
    * If etags, always find typedefs and structure tags.  Why not?
-   * Also default is to find macro constants, enum constants and
+   * Also default to find macro constants, enum constants and
    * global variables.
    */
   if (!CTAGS)
     {
       typedefs = typedefs_or_cplusplus = constantypedefs = TRUE;
       globals = TRUE;
+      declarations = FALSE;
       members = FALSE;
     }
 
@@ -1440,7 +1449,7 @@
   if (real_name == compressed_name)
     {
       char *cmd = concat (compr->command, " ", real_name);
-      inf = popen (cmd, "r");
+      inf = (FILE *) popen (cmd, "r");
       free (cmd);
     }
   else
@@ -1922,6 +1931,7 @@
   st_C_ignore,
   st_C_javastruct,
   st_C_operator,
+  st_C_class,
   st_C_struct, st_C_extern, st_C_enum, st_C_define, st_C_typedef, st_C_typespec
 };
 
@@ -1949,7 +1959,7 @@
 extends,  	C_JAVA,	st_C_javastruct
 implements,  	C_JAVA,	st_C_javastruct
 interface,	C_JAVA, st_C_struct
-class,  	C_PLPL,	st_C_struct
+class,  	0,	st_C_class
 namespace,	C_PLPL,	st_C_struct
 domain, 	C_STAR,	st_C_struct
 union,  	0,	st_C_struct
@@ -1986,7 +1996,8 @@
 #EXFUN,		0,	st_C_gnumacro
 #DEFVAR_,	0,	st_C_gnumacro
 %]
-and replace lines between %< and %> with its output. */
+and replace lines between %< and %> with its output,
+then make in_word_set static. */
 /*%<*/
 /* C code produced by gperf version 2.7.1 (19981006 egcs) */
 /* Command-line: gperf -c -k 1,3 -o -p -r -t  */
@@ -1996,8 +2007,8 @@
 #define MIN_WORD_LENGTH 2
 #define MAX_WORD_LENGTH 15
 #define MIN_HASH_VALUE 13
-#define MAX_HASH_VALUE 123
-/* maximum key range = 111, duplicates = 0 */
+#define MAX_HASH_VALUE 121
+/* maximum key range = 109, duplicates = 0 */
 
 #ifdef __GNUC__
 __inline
@@ -2009,32 +2020,32 @@
 {
   static unsigned char asso_values[] =
     {
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124,   3, 124, 124, 124,  43,   6,
-       11, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-       11, 124, 124,  58,   7, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124,  57,   7,  42,
-        4,  14,  52,   0, 124,  53, 124, 124,  29,  11,
-        6,  35,  32, 124,  29,  34,  59,  58,  51,  24,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124, 124, 124, 124, 124,
-      124, 124, 124, 124, 124, 124
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122,  57, 122, 122, 122,  55,   6,
+       60, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+       51, 122, 122,  10,   2, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122,   2,  52,  59,
+       49,  38,  56,  41, 122,  22, 122, 122,   9,  32,
+       33,  60,  26, 122,   1,  28,  46,  59,  44,  51,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122, 122, 122, 122, 122,
+      122, 122, 122, 122, 122, 122
     };
   register int hval = len;
 
@@ -2054,7 +2065,7 @@
 #ifdef __GNUC__
 __inline
 #endif
-static struct C_stab_entry *
+struct C_stab_entry *
 in_word_set (str, len)
      register const char *str;
      register unsigned int len;
@@ -2063,77 +2074,76 @@
     {
       {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
       {""}, {""}, {""}, {""},
-      {"@end",		0,	st_C_objend},
-      {""}, {""}, {""}, {""},
       {"ENTRY",		0,	st_C_gnumacro},
-      {"@interface",	0,	st_C_objprot},
+      {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
       {""},
-      {"domain", 	C_STAR,	st_C_struct},
-      {""},
-      {"PSEUDO",		0,	st_C_gnumacro},
+      {"if",		0,	st_C_ignore},
       {""}, {""},
-      {"namespace",	C_PLPL,	st_C_struct},
-      {""}, {""},
-      {"@implementation",0,	st_C_objimpl},
+      {"SYSCALL",	0,	st_C_gnumacro},
+      {""}, {""}, {""}, {""}, {""}, {""}, {""},
+      {"struct", 	0,	st_C_struct},
+      {"static",  	0,	st_C_typespec},
       {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""}, {""},
       {"long",    	0,	st_C_typespec},
-      {"signed",  	0,	st_C_typespec},
-      {"@protocol",	0,	st_C_objprot},
-      {""}, {""}, {""}, {""},
-      {"bool",		C_PLPL,	st_C_typespec},
-      {""}, {""}, {""}, {""}, {""}, {""},
-      {"const",   	0,	st_C_typespec},
-      {"explicit",	C_PLPL,	st_C_typespec},
-      {"if",		0,	st_C_ignore},
+      {""}, {""}, {""}, {""}, {""},
+      {"auto",    	0,	st_C_typespec},
+      {"return",		0,	st_C_ignore},
+      {"import",		C_JAVA,	st_C_ignore},
+      {""},
+      {"switch",		0,	st_C_ignore},
       {""},
-      {"operator",	C_PLPL, st_C_operator},
+      {"implements",  	C_JAVA,	st_C_javastruct},
+      {""},
+      {"for",		0,	st_C_ignore},
+      {"volatile",	0,	st_C_typespec},
+      {""},
+      {"PSEUDO",		0,	st_C_gnumacro},
       {""},
-      {"DEFUN",		0,	st_C_gnumacro},
+      {"char",    	0,	st_C_typespec},
+      {"class",  	0,	st_C_class},
+      {"@protocol",	0,	st_C_objprot},
       {""}, {""},
-      {"define",  	0,	st_C_define},
-      {""}, {""}, {""}, {""}, {""},
-      {"double",  	0,	st_C_typespec},
-      {"struct", 	0,	st_C_struct},
-      {""}, {""}, {""}, {""},
-      {"short",   	0,	st_C_typespec},
+      {"void",    	0,	st_C_typespec},
+      {"int",     	0,	st_C_typespec},
+      {"explicit",	C_PLPL,	st_C_typespec},
+      {""},
+      {"namespace",	C_PLPL,	st_C_struct},
+      {"signed",  	0,	st_C_typespec},
       {""},
-      {"enum",    	0,	st_C_enum},
+      {"interface",	C_JAVA, st_C_struct},
+      {"while",		0,	st_C_ignore},
+      {"typedef", 	0,	st_C_typedef},
+      {"typename",	C_PLPL,	st_C_typespec},
+      {""}, {""}, {""},
+      {"friend",		C_PLPL,	st_C_ignore},
       {"mutable",	C_PLPL,	st_C_typespec},
-      {""},
+      {"union",  	0,	st_C_struct},
+      {"domain", 	C_STAR,	st_C_struct},
+      {""}, {""},
       {"extern",  	0,	st_C_extern},
       {"extends",  	C_JAVA,	st_C_javastruct},
       {"package",	C_JAVA,	st_C_ignore},
-      {"while",		0,	st_C_ignore},
-      {""},
-      {"for",		0,	st_C_ignore},
-      {""}, {""}, {""},
-      {"volatile",	0,	st_C_typespec},
-      {""}, {""},
-      {"import",		C_JAVA,	st_C_ignore},
-      {"float",   	0,	st_C_typespec},
-      {"switch",		0,	st_C_ignore},
-      {"return",		0,	st_C_ignore},
-      {"implements",  	C_JAVA,	st_C_javastruct},
-      {""},
-      {"static",  	0,	st_C_typespec},
-      {"typedef", 	0,	st_C_typedef},
-      {"typename",	C_PLPL,	st_C_typespec},
+      {"short",   	0,	st_C_typespec},
+      {"@end",		0,	st_C_objend},
       {"unsigned",	0,	st_C_typespec},
+      {""},
+      {"const",   	0,	st_C_typespec},
       {""}, {""},
-      {"char",    	0,	st_C_typespec},
-      {"class",  	C_PLPL,	st_C_struct},
-      {""}, {""}, {""},
-      {"void",    	0,	st_C_typespec},
+      {"@interface",	0,	st_C_objprot},
+      {"enum",    	0,	st_C_enum},
       {""}, {""},
-      {"friend",		C_PLPL,	st_C_ignore},
+      {"@implementation",0,	st_C_objimpl},
+      {""},
+      {"operator",	C_PLPL, st_C_operator},
+      {""}, {""}, {""}, {""},
+      {"define",  	0,	st_C_define},
+      {""}, {""},
+      {"double",  	0,	st_C_typespec},
+      {""},
+      {"bool",		C_PLPL,	st_C_typespec},
       {""}, {""}, {""},
-      {"int",     	0,	st_C_typespec},
-      {"union",  	0,	st_C_struct},
-      {""}, {""}, {""},
-      {"auto",    	0,	st_C_typespec},
-      {"interface",	C_JAVA, st_C_struct},
-      {""},
-      {"SYSCALL",	0,	st_C_gnumacro}
+      {"DEFUN",		0,	st_C_gnumacro},
+      {"float",   	0,	st_C_typespec}
     };
 
   if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH)
@@ -2207,20 +2217,15 @@
  */
 enum
 {
-  snone,			/* nothing seen yet */
+  snone,			/* nothing seen yet,
+				   or in struct body if cblev > 0 */
   skeyseen,			/* struct-like keyword seen */
   stagseen,			/* struct-like tag seen */
-  scolonseen,			/* colon seen after struct-like tag */
-  sinbody			/* in struct body: recognize member func defs*/
+  sintemplate,			/* inside template (ignore) */
+  scolonseen			/* colon seen after struct-like tag */
 } structdef;
 
 /*
- * When structdef is stagseen, scolonseen, or sinbody, structtype is the
- * type of the preceding struct-like keyword.
- */
-char *structtag = "<uninited>";
-
-/*
  * When objdef is different from onone, objtag is the name of the class.
  */
 char *objtag = "<uninited>";
@@ -2265,14 +2270,110 @@
 {
   bool valid;
   bool named;
-  int linelen;
+  int offset;
+  int length;
   int lineno;
   long linepos;
   char *line;
 } token;			/* latest token read */
 linebuffer token_name;		/* its name */
 
-static bool consider_token P_((char *, int, int, int, int, int, bool *));
+/*
+ * Variables and functions for dealing with nested structures.
+ * Idea by Mykola Dzyuba <mdzyuba@yahoo.com> (2001)
+ */
+static void pushclass_above P_((int, char *, int));
+static void popclass_above P_((int));
+static void write_classname P_((linebuffer *, char *qualifier));
+
+struct {
+  char **cname;			/* nested class names */
+  int *cblev;			/* nested class curly brace level */
+  int nl;			/* class nesting level (elements used) */
+  int size;			/* length of the array */
+} cstack;			/* stack for nested declaration tags */
+/* Current struct nesting depth (namespace, class, struct, union, enum). */
+#define nestlev		(cstack.nl)
+/* After struct keyword or in struct body, not inside an nested function. */
+#define instruct	(structdef == snone && nestlev > 0			\
+			 && cblev == cstack.cblev[nestlev-1] + 1)
+
+static void
+pushclass_above (cblev, str, len)
+     int cblev;
+     char *str;
+     int len;
+{
+  int nl;
+
+  popclass_above (cblev);
+  nl = cstack.nl;
+  if (nl >= cstack.size)
+    {
+      int size = cstack.size *= 2;
+      xrnew (cstack.cname, size, char *);
+      xrnew (cstack.cblev, size, int);
+    }
+  assert (nl == 0 || cstack.cblev[nl-1] < cblev);
+  cstack.cname[nl] = (str == NULL) ? NULL : savenstr (str, len);
+  cstack.cblev[nl] = cblev;
+  cstack.nl = nl + 1;
+}
+
+static void
+popclass_above (cblev)
+     int cblev;
+{
+  int nl;
+
+  for (nl = cstack.nl - 1;
+       nl >= 0 && cstack.cblev[nl] >= cblev;
+       nl--)
+    {
+      if (cstack.cname[nl] != NULL)
+	free (cstack.cname[nl]);
+      cstack.nl = nl;
+    }
+}
+
+static void
+write_classname (cn, qualifier)
+     linebuffer *cn;
+     char *qualifier;
+{
+  int i, len;
+  int qlen = strlen (qualifier);
+
+  if (cstack.nl == 0 || cstack.cname[0] == NULL)
+    {
+      len = 0;
+      cn->len = 0;
+      cn->buffer[0] = '\0';
+    }
+  else
+    {
+      len = strlen (cstack.cname[0]);
+      linebuffer_setlen (cn, len);
+      strcpy (cn->buffer, cstack.cname[0]);
+    }
+  for (i = 1; i < cstack.nl; i++)
+    {
+      char *s;
+      int slen;
+
+      s = cstack.cname[i];
+      if (s == NULL)
+	continue;
+      slen = strlen (s);
+      len += slen + qlen;
+      linebuffer_setlen (cn, len);
+      strncat (cn->buffer, qualifier, qlen);
+      strncat (cn->buffer, s, slen);
+    }
+}
+
+
+static bool consider_token P_((char *, int, int, int *, int, int, bool *));
 static void make_C_tag P_((bool));
 
 /*
@@ -2282,7 +2383,7 @@
  * 	is a struct/union/enum tag, or #define, or an enum constant.
  *
  *	*IS_FUNC gets TRUE iff the token is a function or #define macro
- *	with args.  C_EXT is which language we are looking at.
+ *	with args.  C_EXTP points to which language we are looking at.
  *
  * Globals
  *	fvdef			IN OUT
@@ -2293,22 +2394,24 @@
  */
 
 static bool
-consider_token (str, len, c, c_ext, cblev, parlev, is_func_or_var)
+consider_token (str, len, c, c_extp, cblev, parlev, is_func_or_var)
      register char *str;	/* IN: token pointer */
      register int len;		/* IN: token length */
      register int c;		/* IN: first char after the token */
-     int c_ext;			/* IN: C extensions mask */
+     int *c_extp;		/* IN, OUT: C extensions mask */
      int cblev;			/* IN: curly brace level */
      int parlev;		/* IN: parenthesis level */
      bool *is_func_or_var;	/* OUT: function or variable found */
 {
   /* When structdef is stagseen, scolonseen, or snone with cblev > 0,
-     structtype is the type of the preceding struct-like keyword. */
+     structtype is the type of the preceding struct-like keyword, and
+     structcblev is the curly brace level where it has been seen. */
   static enum sym_type structtype;
+  static int structcblev;
   static enum sym_type toktype;
 
 
-  toktype = C_symtype (str, len, c_ext);
+  toktype = C_symtype (str, len, *c_extp);
 
   /*
    * Advance the definedef state machine.
@@ -2370,17 +2473,25 @@
 	{
 	case st_none:
 	case st_C_typespec:
+	case st_C_class:
 	case st_C_struct:
 	case st_C_enum:
 	  typdef = ttypeseen;
 	  break;
 	}
-      /* Do not return here, so the structdef stuff has a chance. */
+      break;
+    case ttypeseen:
+      if (structdef == snone && fvdef == fvnone)
+	{
+	  fvdef = fvnameseen;
+	  return TRUE;
+	}
       break;
     case tend:
       switch (toktype)
 	{
 	case st_C_typespec:
+	case st_C_class:
 	case st_C_struct:
 	case st_C_enum:
 	  return FALSE;
@@ -2389,11 +2500,6 @@
     }
 
   /*
-   * This structdef business is currently only invoked when cblev==0.
-   * It should be recursively invoked whatever the curly brace level,
-   * and a stack of states kept, to allow for definitions of structs
-   * within structs.
-   *
    * This structdef business is NOT invoked when we are ctags and the
    * file is plain C.  This is because a struct tag may have the same
    * name as another tag, and this loses with ctags.
@@ -2404,25 +2510,29 @@
       if (structdef == stagseen)
         structdef = scolonseen;
       return FALSE;
+    case st_C_class:
+      if (cblev == 0
+	  && (*c_extp & C_AUTO)	/* automatic detection of C++ language */
+	  && definedef == dnone && structdef == snone
+	  && typdef == tnone && fvdef == fvnone)
+	*c_extp = (*c_extp | C_PLPL) & ~C_AUTO;
+      /* FALLTHRU */
     case st_C_struct:
     case st_C_enum:
-      if (typdef == tkeyseen
-	  || (typedefs_or_cplusplus && cblev == 0 && structdef == snone))
+      if (parlev == 0
+	  && fvdef != vignore
+	  && (typdef == tkeyseen
+	      || (typedefs_or_cplusplus && structdef == snone)))
 	{
 	  structdef = skeyseen;
 	  structtype = toktype;
+	  structcblev = cblev;
 	}
       return FALSE;
     }
 
   if (structdef == skeyseen)
     {
-      /* Save the tag for struct/union/class, for functions and variables
-	 that may be defined inside. */
-      if (structtype == st_C_struct)
-	structtag = savenstr (str, len);
-      else
-	structtag = "<enum>";
       structdef = stagseen;
       return TRUE;
     }
@@ -2517,21 +2627,33 @@
       *is_func_or_var = TRUE;
       return TRUE;
     case st_none:
-      if ((c_ext & C_PLPL) && strneq (str+len-10, "::operator", 10))
-	{
-	  fvdef = foperator;
-	  *is_func_or_var = TRUE;
-	  return TRUE;
-	}
-      if (constantypedefs && structdef == sinbody && structtype == st_C_enum)
-	return TRUE;
+      if (constantypedefs
+	  && structdef == snone
+	  && structtype == st_C_enum && cblev > structcblev)
+	return TRUE;		/* enum constant */
       switch (fvdef)
 	{
 	case fdefunkey:
+	  if (cblev > 0)
+	    break;
 	  fvdef = fdefunname;	/* GNU macro */
 	  *is_func_or_var = TRUE;
 	  return TRUE;
 	case fvnone:
+	  if ((strneq (str, "asm", 3) && endtoken (str[3]))
+	      || (strneq (str, "__asm__", 7) && endtoken (str[7])))
+	    {
+	      fvdef = vignore;
+	      return FALSE;
+	    }
+	  if ((*c_extp & C_PLPL) && strneq (str+len-10, "::operator", 10))
+	    {
+	      fvdef = foperator;
+	      *is_func_or_var = TRUE;
+	      return TRUE;
+	    }
+	  if (cblev > 0 && !instruct)
+	    break;
 	  fvdef = fvnameseen;	/* function or variable */
 	  *is_func_or_var = TRUE;
 	  return TRUE;
@@ -2600,17 +2722,20 @@
 	  char *name = NULL;
 
 	  if (CTAGS || token.named)
+	    name = savestr (token_name.buffer);
+	  if (DEBUG && !token.valid)
 	    {
-	      name = savestr (token_name.buffer);
-	      if (!token.valid)
+	      if (token.named)
 		name = concat (name, "##invalid##", "");
+	      else
+		name = savestr ("##invalid##");
 	    }
-	  pfnote (name, isfun,
-		  token.line, token.linelen, token.lineno, token.linepos);
+	  pfnote (name, isfun, token.line,
+		  token.offset+token.length+1, token.lineno, token.linepos);
 	}
       else
-	new_pfnote (token_name.buffer, token_name.len, isfun,
-		    token.line, token.linelen, token.lineno, token.linepos);
+	new_pfnote (token_name.buffer, token_name.len, isfun, token.line,
+		    token.offset+token.length+1, token.lineno, token.linepos);
       token.valid = FALSE;
     }
 }
@@ -2636,6 +2761,7 @@
   int qlen;			/* length of qualifier */
   int cblev;			/* current curly brace level */
   int parlev;			/* current parenthesis level */
+  int typdefcblev;		/* cblev where a typedef struct body begun */
   bool incomm, inquote, inchar, quotednl, midtoken;
   bool cplpl, cjava;
   bool yacc_rules;		/* in the rules part of a yacc file */
@@ -2645,6 +2771,13 @@
   initbuffer (&token_name);
   initbuffer (&lbs[0].lb);
   initbuffer (&lbs[1].lb);
+  if (cstack.size == 0)
+    {
+      cstack.size = (DEBUG) ? 1 : 4;
+      cstack.nl = 0;
+      cstack.cname = xnew (cstack.size, char *);
+      cstack.cblev = xnew (cstack.size, int);
+    }
 
   tokoff = toklen = 0;		/* keep compiler quiet */
   curndx = newndx = 0;
@@ -2741,6 +2874,7 @@
 	    switch (fvdef)
 	      {
 	      case fdefunkey:
+	      case fstartlist:
 	      case finlist:
 	      case fignore:
 	      case vignore:
@@ -2775,7 +2909,7 @@
 	  case '%':
 	    if ((c_ext & YACC) && *lp == '%')
 	      {
-		/* entering or exiting rules section in yacc file */
+		/* Entering or exiting rules section in yacc file. */
 		lp++;
 		definedef = dnone; fvdef = fvnone; fvextern = FALSE;
 		typdef = tnone; structdef = snone;
@@ -2814,15 +2948,13 @@
 	  } /* switch (c) */
 
 
-      /* Consider token only if some complicated conditions are satisfied. */
+      /* Consider token only if some involved conditions are satisfied. */
       if (typdef != tignore
 	  && definedef != dignorerest
 	  && fvdef != finlist
+	  && structdef != sintemplate
 	  && (definedef != dnone
-	      || (cblev == 0 && structdef != scolonseen)
-	      || (cblev == 1 && cplpl && structdef == sinbody)
-	      || (PUREC && structdef == sinbody))
-	  )
+	      || structdef != scolonseen))
 	{
 	  if (midtoken)
 	    {
@@ -2846,7 +2978,7 @@
 
 		      if (yacc_rules
 			  || consider_token (newlb.buffer + tokoff, toklen, c,
-					     c_ext, cblev, parlev, &funorvar))
+					     &c_ext, cblev, parlev, &funorvar))
 			{
 			  if (fvdef == foperator)
 			    {
@@ -2861,15 +2993,13 @@
 			      toklen += lp - oldlp;
 			    }
 			  token.named = FALSE;
-			  if (!PUREC
-			      && funorvar
-			      && definedef == dnone
-			      && structdef == sinbody)
-			    /* function or var defined in C++ class body */
+			  if ((c_ext & C_EXT)	/* not pure C */
+			      && nestlev > 0 && definedef == dnone)
+			    /* in struct body */
 			    {
-			      int len = strlen (structtag) + qlen + toklen;
-			      linebuffer_setlen (&token_name, len);
-			      strcpy (token_name.buffer, structtag);
+                              write_classname (&token_name, qualifier);
+			      linebuffer_setlen (&token_name,
+						 token_name.len+qlen+toklen);
 			      strcat (token_name.buffer, qualifier);
 			      strncat (token_name.buffer,
 				       newlb.buffer + tokoff, toklen);
@@ -2894,6 +3024,7 @@
 			      token.named = TRUE;
 			    }
 			  else if (fvdef == fdefunname)
+			    /* GNU DEFUN and similar macros */
 			    {
 			      bool defun = (newlb.buffer[tokoff] == 'F');
 			      int off = tokoff;
@@ -2931,10 +3062,12 @@
 						 && definedef == dignorerest)
 					     || (funorvar
 						 && definedef == dnone
-						 && structdef == sinbody));
+						 && structdef == snone
+						 && cblev > 0));
 			    }
 			  token.lineno = lineno;
-			  token.linelen = tokoff + toklen + 1;
+			  token.offset = tokoff;
+			  token.length = toklen;
 			  token.line = newlb.buffer;
 			  token.linepos = newlinepos;
 			  token.valid = TRUE;
@@ -2944,12 +3077,15 @@
 				  || fvdef == foperator
 				  || structdef == stagseen
 				  || typdef == tend
+				  || typdef == ttypeseen
 				  || objdef != onone))
 			    {
 			      if (current_lb_is_new)
 				switch_line_buffers ();
 			    }
-			  else
+			  else if (definedef != dnone
+				   || fvdef == fdefunname
+				   || instruct)
 			    make_C_tag (funorvar);
 			}
 		      midtoken = FALSE;
@@ -2981,7 +3117,10 @@
 		      break;
 		    }
 		  if (structdef == stagseen && !cjava)
-		    structdef = snone;
+		    {
+		      popclass_above (cblev);
+		      structdef = snone;
+		    }
 		  break;
 		case dsharpseen:
 		  savetoken = token;
@@ -3002,6 +3141,11 @@
       switch (c)
 	{
 	case ':':
+	  if (yacc_rules && token.offset == 0 && token.valid)
+	    {
+	      make_C_tag (FALSE); /* a yacc function */
+	      break;
+	    }
 	  if (definedef != dnone)
 	    break;
 	  switch (objdef)
@@ -3019,60 +3163,57 @@
 	    }
 	  if (structdef == stagseen)
 	    structdef = scolonseen;
-	  else
-	    switch (fvdef)
-	      {
-	      case fvnameseen:
-		if (yacc_rules)
-		  {
-		    make_C_tag (FALSE); /* a yacc function */
-		    fvdef = fignore;
-		  }
-		break;
-	      case fstartlist:
-		fvextern = FALSE;
-		fvdef = fvnone;
-		break;
-	      }
 	  break;
 	case ';':
 	  if (definedef != dnone)
 	    break;
-	  switch (fvdef)
+	  switch (typdef)
 	    {
-	    case fignore:
+	    case tend:
+	    case ttypeseen:
+	      make_C_tag (FALSE); /* a typedef */
+	      typdef = tnone;
+	      fvdef = fvnone;
 	      break;
-	    case fvnameseen:
-	      if ((members && cblev == 1)
-		  || (globals && cblev == 0 && (!fvextern || declarations)))
-		make_C_tag (FALSE); /* a variable */
-	      fvextern = FALSE;
-	      fvdef = fvnone;
-	      token.valid = FALSE;
-	      break;
-	    case flistseen:
-	      if ((declarations && typdef == tnone && cblev == 0)
-		  || (members && cblev == 1))
-		make_C_tag (TRUE); /* a function declaration */
+	    case tnone:
+	    case tinbody:
+	    case tignore:
+	      switch (fvdef)
+		{
+		case fignore:
+		  if (typdef == tignore)
+		    fvdef = fvnone;
+		  break;
+		case fvnameseen:
+		  if ((globals && cblev == 0 && (!fvextern || declarations))
+		      || (members && instruct))
+		    make_C_tag (FALSE); /* a variable */
+		  fvextern = FALSE;
+		  fvdef = fvnone;
+		  token.valid = FALSE;
+		  break;
+		case flistseen:
+		  if ((declarations && typdef == tnone && !instruct)
+		      || (members && typdef != tignore && instruct))
+		    make_C_tag (TRUE);  /* a function declaration */
+		  /* FALLTHRU */
+		default:
+		  fvextern = FALSE;
+		  fvdef = fvnone;
+		  if (declarations
+		      && structdef == stagseen && (c_ext & C_PLPL))
+		    make_C_tag (FALSE);	/* forward declaration */
+		  else
+		    /* The following instruction invalidates the token.
+		       Probably the token should be invalidated in all other
+		       cases where some state machine is reset prematurely. */
+		    token.valid = FALSE;
+		} /* switch (fvdef) */
 	      /* FALLTHRU */
 	    default:
-	      fvextern = FALSE;
-	      fvdef = fvnone;
-	      if (typdef != tend)
-		/* The following instruction invalidates the token.
-		   Probably the token should be invalidated in all other
-		   cases where some state machine is reset prematurely. */
-		token.valid = FALSE;
+	      if (!instruct)
+		typdef = tnone;
 	    }
-	  if (cblev == 0)
-	    switch (typdef)
-	      {
-	      case tend:
-		make_C_tag (FALSE); /* a typedef */
-		/* FALLTHRU */
-	      default:
-		typdef = tnone;
-	      }
 	  if (structdef == stagseen)
 	    structdef = snone;
 	  break;
@@ -3091,6 +3232,7 @@
 	    {
 	    case fdefunkey:
 	    case foperator:
+	    case fstartlist:
 	    case finlist:
 	    case fignore:
 	    case vignore:
@@ -3098,18 +3240,22 @@
 	    case fdefunname:
 	      fvdef = fignore;
 	      break;
+	    case fvnameseen:	/* a variable */
+	      if ((globals && cblev == 0 && (!fvextern || declarations))
+		  || (members && instruct))
+		make_C_tag (FALSE);
+	      break;
 	    case flistseen:	/* a function */
-	      if (!declarations)
+	      if ((declarations && typdef == tnone && !instruct)
+		  || (members && typdef != tignore && instruct))
 		{
-		  fvdef = fvnone;
-		  break;
+		  make_C_tag (TRUE); /* a function declaration */
+		  fvdef = fvnameseen;
 		}
-	      /* FALLTHRU */
-	    case fvnameseen:	/* a variable */
-	      if ((members && structdef == sinbody && cblev == 1)
-		  || (globals && cblev == 0 && (!fvextern || declarations)))
-		make_C_tag (FALSE);
-	      /* FALLTHRU */
+	      else if (!declarations)
+		fvdef = fvnone;
+	      token.valid = FALSE;
+	      break;
 	    default:
 	      fvdef = fvnone;
 	    }
@@ -3119,29 +3265,35 @@
 	case '[':
 	  if (definedef != dnone)
 	    break;
-	  if (cblev == 0 && typdef == tend)
+	  if (structdef == stagseen)
+	    structdef = snone;
+	  switch (typdef)
 	    {
+	    case ttypeseen:
+	    case tend:
 	      typdef = tignore;
 	      make_C_tag (FALSE);	/* a typedef */
 	      break;
-	    }
-	  switch (fvdef)
-	    {
-	    case foperator:
-	    case finlist:
-	    case fignore:
-	    case vignore:
+	    case tnone:
+	    case tinbody:
+	      switch (fvdef)
+		{
+		case foperator:
+		case finlist:
+		case fignore:
+		case vignore:
+		  break;
+		case fvnameseen:
+		  if ((members && cblev == 1)
+		      || (globals && cblev == 0
+			  && (!fvextern || declarations)))
+		    make_C_tag (FALSE); /* a variable */
+		  /* FALLTHRU */
+		default:
+		  fvdef = fvnone;
+		}
 	      break;
-	    case fvnameseen:
-	      if ((members && cblev == 1)
-		  || (globals && cblev == 0 && (!fvextern || declarations)))
-		make_C_tag (FALSE); /* a variable */
-	      /* FALLTHRU */
-	    default:
-	      fvdef = fvnone;
 	    }
-	  if (structdef == stagseen)
-	    structdef = snone;
 	  break;
 	case '(':
 	  if (definedef != dnone)
@@ -3152,14 +3304,15 @@
 	    {
 	    case fvnameseen:
 	      if (typdef == ttypeseen
-		  && token.valid
 		  && *lp != '*'
-		  && structdef != sinbody)
+		  && !instruct)
 		{
 		  /* This handles constructs like:
 		     typedef void OperatorFun (int fun); */
 		  make_C_tag (FALSE);
 		  typdef = tignore;
+		  fvdef = fignore;
+		  break;
 		}
 	      /* FALLTHRU */
 	    case foperator:
@@ -3188,7 +3341,9 @@
 		  fvdef = flistseen;
 		  break;
 		}
-	      if (cblev == 0 && (typdef == tend))
+	      if (!instruct
+		  && (typdef == tend
+		      || typdef == ttypeseen))
 		{
 		  typdef = tignore;
 		  make_C_tag (FALSE); /* a typedef */
@@ -3201,7 +3356,10 @@
 	  if (definedef != dnone)
 	    break;
 	  if (typdef == ttypeseen)
-	    typdef = tinbody;
+	    {
+	      typdefcblev = cblev;
+	      typdef = tinbody;
+	    }
 	  switch (fvdef)
 	    {
 	    case flistseen:
@@ -3224,20 +3382,22 @@
 		  break;
 		default:
 		  /* Neutralize `extern "C" {' grot. */
-		  if (cblev == 0 && structdef == snone && typdef == tnone)
+		  if (cblev == 0 && structdef == snone && nestlev == 0
+		      && typdef == tnone)
 		    cblev = -1;
 		}
 	    }
 	  switch (structdef)
 	    {
 	    case skeyseen:	   /* unnamed struct */
-	      structdef = sinbody;
-	      structtag = "_anonymous_";
+	      pushclass_above (cblev, NULL, 0);
+	      structdef = snone;
 	      break;
-	    case stagseen:
-	    case scolonseen:	/* named struct */
-	      structdef = sinbody;
-	      make_C_tag (FALSE);  /* a struct */
+	    case stagseen:	   /* named struct or enum */
+	    case scolonseen:	   /* a class */
+	      pushclass_above (cblev, token.line+token.offset, token.length);
+	      structdef = snone;
+	      make_C_tag (FALSE);  /* a struct or enum */
 	      break;
 	    }
 	  cblev++;
@@ -3258,20 +3418,12 @@
 	    }
 	  else if (cblev > 0)
 	    cblev--;
-	  if (cblev == 0)
+	  popclass_above (cblev);
+	  structdef = snone;
+	  if (typdef == tinbody && cblev <= typdefcblev)
 	    {
-	      if (typdef == tinbody)
-		typdef = tend;
-	      /* Memory leakage here: the string pointed by structtag is
-	         never released, because I fear to miss something and
-	         break things while freeing the area.  The amount of
-	         memory leaked here is the sum of the lengths of the
-	         struct tags.
-	      if (structdef == sinbody)
-		free (structtag); */
-
-	      structdef = snone;
-	      structtag = "<error>";
+	      assert (cblev == typdefcblev);
+	      typdef = tend;
 	    }
 	  break;
 	case '=':
@@ -3293,6 +3445,20 @@
 	      fvdef = vignore;
 	    }
 	  break;
+	case '<':
+	  if (cplpl && structdef == stagseen)
+	    {
+	      structdef = sintemplate;
+	      break;
+	    }
+	  goto resetfvdef;
+	case '>':
+	  if (structdef == sintemplate)
+	    {
+	      structdef = stagseen;
+	      break;
+	    }
+	  goto resetfvdef;
 	case '+':
 	case '-':
 	  if (objdef == oinbody && cblev == 0)
@@ -3301,8 +3467,9 @@
 	      break;
 	    }
 	  /* FALLTHRU */
+	resetfvdef:
 	case '#': case '~': case '&': case '%': case '/': case '|':
-	case '^': case '!': case '<': case '>': case '.': case '?': case ']':
+	case '^': case '!': case '.': case '?': case ']':
 	  if (definedef != dnone)
 	    break;
 	  /* These surely cannot follow a function tag in C. */
@@ -3346,10 +3513,10 @@
 default_C_entries (inf)
      FILE *inf;
 {
-  C_entries (cplusplus ? C_PLPL : 0, inf);
+  C_entries (cplusplus ? C_PLPL : C_AUTO, inf);
 }
 
-/* Always do plain ANSI C. */
+/* Always do plain C. */
 static void
 plain_C_entries (inf)
      FILE *inf;
@@ -5071,6 +5238,7 @@
     }
   return;
 }
+#endif /* ETAGS_REGEXPS */
 
 
 static void
@@ -5090,7 +5258,6 @@
 	  lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
 }
 
-#endif /* ETAGS_REGEXPS */
 /* Initialize a linebuffer for use */
 static void
 initbuffer (lbp)
@@ -5405,7 +5572,7 @@
   return path;
 
 #else /* not HAVE_GETCWD */
-#ifdef MSDOS
+#if MSDOS
 
   char *p, path[MAXPATHLEN + 1]; /* Fixed size is safe on MSDOS.  */