changeset 47081:9c058ffb550b

Two bug corrections and one new feature. (Ada_funcs): Do not tag "use type Xxxx;". New language HTML. (make_tag): Never generate null length tag names. (linebuffer_init): Renamed from initbuffer. All callers changed. (pattern): Structure renamed to `regexp', member regex renamed to pattern. (node_st): Member pat renamed to regex. (pattern); New member force_explicit_name, for future use. Now always set to true, cannot be reset. (add_regex, regex_tag_multiline, readline): Use it. (main): Free some global structures. (fdesc): New member `written'. (readline, process_file): Initialise it. (put_entries): Set it. (main): Use it to create entries for files without tags. (total_size_of_entries): Do not count invalid tags. (etags_strcasecmp): Like BSD's, for compatibility. (strcaseeq): Make it into a macro.
author Francesco Potortì <pot@gnu.org>
date Wed, 28 Aug 2002 10:35:51 +0000
parents 9fdb69291eb8
children ce5775569314
files lib-src/etags.c
diffstat 1 files changed, 348 insertions(+), 159 deletions(-) [+]
line wrap: on
line diff
--- a/lib-src/etags.c	Wed Aug 28 10:34:04 2002 +0000
+++ b/lib-src/etags.c	Wed Aug 28 10:35:51 2002 +0000
@@ -35,7 +35,7 @@
  *
  */
 
-char pot_etags_version[] = "@(#) pot revision number is 16.32";
+char pot_etags_version[] = "@(#) pot revision number is 16.42";
 
 #define	TRUE	1
 #define	FALSE	0
@@ -187,15 +187,17 @@
 #endif
 
 #define streq(s,t)	(assert((s)!=NULL || (t)!=NULL), !strcmp (s, t))
+#define strcaseeq(s,t)	(assert((s)!=NULL && (t)!=NULL), !etags_strcasecmp (s, t))
 #define strneq(s,t,n)	(assert((s)!=NULL || (t)!=NULL), !strncmp (s, t, n))
+#define strncaseeq(s,t,n) (assert((s)!=NULL && (t)!=NULL), !etags_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 */
-#define notinname(c)	(_nin[CHAR(c)]) /* c is not in a name */
-#define	begtoken(c)	(_btk[CHAR(c)]) /* c can start token */
-#define	intoken(c)	(_itk[CHAR(c)]) /* c can be in token */
-#define	endtoken(c)	(_etk[CHAR(c)]) /* c ends tokens */
+#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) */
 
 #define ISALNUM(c)	isalnum (CHAR(c))
 #define ISALPHA(c)	isalpha (CHAR(c))
@@ -254,6 +256,7 @@
   language *lang;		/* language of file */
   char *prop;			/* file properties to write in tagfile */
   bool usecharno;		/* etags tags shall contain char number */
+  bool written;			/* entry written in the tags file */
 } fdesc;
 
 typedef struct node_st
@@ -261,9 +264,9 @@
   struct node_st *left, *right;	/* left and right sons */
   fdesc *fdp;			/* description of file to whom tag belongs */
   char *name;			/* tag name */
-  char *pat;			/* search pattern */
+  char *regex;			/* search regexp */
   bool valid;			/* write this tag on the tag file */
-  bool is_func;			/* function tag: use pattern in CTAGS mode */
+  bool is_func;			/* function tag: use regexp in CTAGS mode */
   bool been_warned;		/* warning already given for duplicated tag */
   int lno;			/* line number tag is on */
   long cno;			/* character number line starts on */
@@ -298,18 +301,19 @@
 
 #ifdef ETAGS_REGEXPS
 /* Structure defining a regular expression. */
-typedef struct pattern
+typedef struct regexp
 {
-  struct pattern *p_next;
-  language *lang;
-  char *regex;
-  struct re_pattern_buffer *pat;
-  struct re_registers regs;
-  char *name_pattern;
-  bool error_signaled;
-  bool ignore_case;
-  bool multi_line;
-} pattern;
+  struct regexp *p_next;	/* pointer to next in list */
+  language *lang;		/* if set, use only for this language */
+  char *pattern;		/* the regexp pattern */
+  char *name;			/* tag name */
+  struct re_pattern_buffer *pat; /* the compiled pattern */
+  struct re_registers regs;	/* re registers */
+  bool error_signaled;		/* already signaled for this regexp */
+  bool force_explicit_name;	/* do not allow implict tag name */
+  bool ignore_case;		/* ignore case when matching */
+  bool multi_line;		/* do a multi-line match on the whole file */
+} regexp;
 #endif /* ETAGS_REGEXPS */
 
 
@@ -327,7 +331,7 @@
 static void Cstar_entries __P((FILE *));
 static void Erlang_functions __P((FILE *));
 static void Fortran_functions __P((FILE *));
-static void Yacc_entries __P((FILE *));
+static void HTML_labels __P((FILE *));
 static void Lisp_functions __P((FILE *));
 static void Makefile_targets __P((FILE *));
 static void Pascal_functions __P((FILE *));
@@ -339,6 +343,7 @@
 static void Scheme_functions __P((FILE *));
 static void TeX_commands __P((FILE *));
 static void Texinfo_nodes __P((FILE *));
+static void Yacc_entries __P((FILE *));
 static void just_read_file __P((FILE *));
 
 static void print_language_names __P((void));
@@ -357,7 +362,7 @@
 
 #ifdef ETAGS_REGEXPS
 static void analyse_regex __P((char *));
-static void free_patterns __P((void));
+static void free_regexps __P((void));
 static void regex_tag_multiline __P((void));
 #endif /* ETAGS_REGEXPS */
 static void error __P((const char *, const char *));
@@ -367,7 +372,6 @@
 static void add_node __P((node *, node **));
 
 static void init __P((void));
-static void initbuffer __P((linebuffer *));
 static void process_file_name __P((char *, language *));
 static void process_file __P((FILE *, char *, language *));
 static void find_entries __P((FILE *));
@@ -385,13 +389,15 @@
 static char *savestr __P((char *));
 static char *etags_strchr __P((const char *, int));
 static char *etags_strrchr __P((const char *, int));
-static bool strcaseeq __P((const char *, const char *));
+static int etags_strcasecmp __P((const char *, const char *));
+static int etags_strncasecmp __P((const char *, const char *, int));
 static char *etags_getcwd __P((void));
 static char *relative_filename __P((char *, char *));
 static char *absolute_filename __P((char *, char *));
 static char *absolute_dirname __P((char *, char *));
 static bool filename_is_absolute __P((char *f));
 static void canonicalize_filename __P((char *));
+static void linebuffer_init __P((linebuffer *));
 static void linebuffer_setlen __P((linebuffer *, int));
 static PTR xmalloc __P((unsigned int));
 static PTR xrealloc __P((char *, unsigned int));
@@ -419,6 +425,7 @@
 
 static linebuffer lb;		/* the 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];
@@ -459,7 +466,7 @@
 static bool parsing_stdin;	/* --parse-stdin used */
 
 #ifdef ETAGS_REGEXPS
-static pattern *p_head;		/* list of all regexps */
+static regexp *p_head;		/* list of all regexps */
 static bool need_filebuf;	/* some regexes are multi-line */
 #else
 # define need_filebuf FALSE
@@ -566,6 +573,9 @@
 static char *Fortran_suffixes [] =
   { "F", "f", "f90", "for", NULL };
 
+static char *HTML_suffixes [] =
+  { "htm", "html", "shtml", NULL };
+
 static char *Lisp_suffixes [] =
   { "cl", "clisp", "el", "l", "lisp", "LSP", "lsp", "ml", NULL };
 
@@ -629,6 +639,7 @@
   { "cobol",    FALSE, Cobol_paragraphs,     NULL, Cobol_suffixes,      NULL },
   { "erlang",   FALSE, Erlang_functions,     NULL, Erlang_suffixes,     NULL },
   { "fortran",  FALSE, Fortran_functions,    NULL, Fortran_suffixes,    NULL },
+  { "html",     FALSE, HTML_labels,          NULL, HTML_suffixes,       NULL },
   { "java",     FALSE, Cjava_entries,        NULL, Cjava_suffixes,      NULL },
   { "lisp",     FALSE, Lisp_functions,       NULL, Lisp_suffixes,       NULL },
   { "makefile", FALSE, Makefile_targets,     Makefile_filenames, NULL,  NULL },
@@ -1142,9 +1153,10 @@
 
   init ();			/* set up boolean "functions" */
 
-  initbuffer (&lb);
-  initbuffer (&filename_lb);
-  initbuffer (&filebuf);
+  linebuffer_init (&lb);
+  linebuffer_init (&filename_lb);
+  linebuffer_init (&filebuf);
+  linebuffer_init (&token_name);
 
   if (!CTAGS)
     {
@@ -1222,9 +1234,11 @@
     }
 
 #ifdef ETAGS_REGEXPS
-  free_patterns ();
+  free_regexps ();
 #endif /* ETAGS_REGEXPS */
+  free (lb.buffer);
   free (filebuf.buffer);
+  free (token_name.buffer);
 
   if (!CTAGS || cxref_style)
     {
@@ -1232,8 +1246,17 @@
       free_tree (nodehead);
       nodehead = NULL;
       if (!CTAGS)
-	while (nincluded_files-- > 0)
-	  fprintf (tagf, "\f\n%s,include\n", *included_files++);
+	{
+	  fdesc *fdp;
+
+	  /* Output file entries that have no tags. */
+	  for (fdp = fdhead; fdp != NULL; fdp = fdp->next)
+	    if (!fdp->written)
+	      fprintf (tagf, "\f\n%s,0\n", fdp->taggedfname);
+
+	  while (nincluded_files-- > 0)
+	    fprintf (tagf, "\f\n%s,include\n", *included_files++);
+	}
 
       if (fclose (tagf) == EOF)
 	pfatal (tagfile);
@@ -1562,6 +1585,7 @@
     }
   fdp->usecharno = TRUE;	/* use char position when making tags */
   fdp->prop = NULL;
+  fdp->written = FALSE;		/* not written on tags file yet */
 
   fdhead = fdp;
   curfdp = fdhead;		/* the current file description */
@@ -1761,7 +1785,7 @@
   assert (parser != NULL);
 
   /* Generic initialisations before reading from file. */
-  filebuf.len = 0;		/* reset the file buffer */
+  linebuffer_setlen (&filebuf, 0); /* reset the file buffer */
 
   /* Generic initialisations before parsing file with readline. */
   lineno = 0;		       /* reset global line number */
@@ -1809,9 +1833,11 @@
      int lno;			/* line number */
      long cno;			/* character number */
 {
-  bool named = TRUE;
-
-  if (!CTAGS && name != NULL && namelen > 0)
+  bool named = (name != NULL && namelen > 0);
+
+  if (!CTAGS && named)		/* maybe set named to false */
+    /* Let's try to make an implicit tag name, that is, create an unnamed tag
+       such that etags.el can guess a name from it. */
     {
       int i;
       register char *cp = name;
@@ -1885,12 +1911,12 @@
   if (CTAGS && !cxref_style)
     {
       if (strlen (linestart) < 50)
-	np->pat = concat (linestart, "$", "");
+	np->regex = concat (linestart, "$", "");
       else
-	np->pat = savenstr (linestart, 50);
+	np->regex = savenstr (linestart, 50);
     }
   else
-    np->pat = savenstr (linestart, linelen);
+    np->regex = savenstr (linestart, linelen);
 
   add_node (np, &nodehead);
 }
@@ -1909,7 +1935,7 @@
       free_tree (np->left);
       if (np->name != NULL)
 	free (np->name);
-      free (np->pat);
+      free (np->regex);
       free (np);
       np = node_right;
     }
@@ -2083,15 +2109,16 @@
   register int total = 0;
 
   for (; np != NULL; np = np->right)
-    {
-      total += strlen (np->pat) + 1;		/* pat\177 */
-      if (np->name != NULL)
-	total += strlen (np->name) + 1;		/* name\001 */
-      total += number_len ((long) np->lno) + 1;	/* lno, */
-      if (np->cno != invalidcharno)		/* cno */
-	total += number_len (np->cno);
-      total += 1;				/* newline */
-    }
+    if (np->valid)
+      {
+	total += strlen (np->regex) + 1;		/* pat\177 */
+	if (np->name != NULL)
+	  total += strlen (np->name) + 1;		/* name\001 */
+	total += number_len ((long) np->lno) + 1;	/* lno, */
+	if (np->cno != invalidcharno)			/* cno */
+	  total += number_len (np->cno);
+	total += 1;					/* newline */
+      }
 
   return total;
 }
@@ -2121,8 +2148,9 @@
 	      fdp = np->fdp;
 	      fprintf (tagf, "\f\n%s,%d\n",
 		       fdp->taggedfname, total_size_of_entries (np));
+	      fdp->written = TRUE;
 	    }
-	  fputs (np->pat, tagf);
+	  fputs (np->regex, tagf);
 	  fputc ('\177', tagf);
 	  if (np->name != NULL)
 	    {
@@ -2147,7 +2175,7 @@
 			 np->name, np->fdp->taggedfname, (np->lno + 63) / 64);
 	      else
 		fprintf (stdout, "%-16s %3d %-16s %s\n",
-			 np->name, np->lno, np->fdp->taggedfname, np->pat);
+			 np->name, np->lno, np->fdp->taggedfname, np->regex);
 	    }
 	  else
 	    {
@@ -2158,7 +2186,7 @@
 		  putc (searchar, tagf);
 		  putc ('^', tagf);
 
-		  for (sp = np->pat; *sp; sp++)
+		  for (sp = np->regex; *sp; sp++)
 		    {
 		      if (*sp == '\\' || *sp == searchar)
 			putc ('\\', tagf);
@@ -2550,7 +2578,6 @@
   long linepos;
   char *line;
 } token;			/* latest token read */
-static linebuffer token_name;	/* its name */
 
 /*
  * Variables and functions for dealing with nested structures.
@@ -3030,9 +3057,8 @@
   struct tok savetoken;	        /* token saved during preprocessor handling */
 
 
-  initbuffer (&token_name);
-  initbuffer (&lbs[0].lb);
-  initbuffer (&lbs[1].lb);
+  linebuffer_init (&lbs[0].lb);
+  linebuffer_init (&lbs[1].lb);
   if (cstack.size == 0)
     {
       cstack.size = (DEBUG) ? 1 : 4;
@@ -3765,7 +3791,6 @@
 
     } /* while not eof */
 
-  free (token_name.buffer);
   free (lbs[0].lb.buffer);
   free (lbs[1].lb.buffer);
 }
@@ -3981,7 +4006,7 @@
 /*
  * Ada parsing
  * Original code by
- * Philippe Waroquiers <philippe.waroquiers@eurocontrol.be> (1998)
+ * Philippe Waroquiers <philippe.waroquiers@eurocontrol.int> (1998)
  */
 
 static void Ada_getit __P((FILE *, char *));
@@ -4058,6 +4083,7 @@
      FILE *inf;
 {
   bool inquote = FALSE;
+  bool skip_till_semicolumn = FALSE;
 
   LOOP_ON_INPUT_LINES (inf, lb, dbp)
     {
@@ -4094,6 +4120,14 @@
 	      continue;
 	    }
 
+	  if (skip_till_semicolumn)
+	    {
+	      if (*dbp == ';')
+		skip_till_semicolumn = FALSE;
+	      dbp++;
+	      continue;         /* advance char */
+	    }
+
 	  /* Search for beginning of a token.  */
 	  if (!begtoken (*dbp))
 	    {
@@ -4120,6 +4154,16 @@
 	      else
 		break;		/* from switch */
 	      continue;		/* advance char */
+
+	    case 'u':
+	      if (typedefs && !packages_only && nocase_tail ("use"))
+		{
+		  /* when tagging types, avoid tagging  use type Pack.Typename;
+		     for this, we will skip everything till a ; */
+		  skip_till_semicolumn = TRUE;
+		  continue;     /* advance char */
+		}
+
 	    case 't':
 	      if (!packages_only && nocase_tail ("task"))
 		Ada_getit (inf, "/k");
@@ -4456,7 +4500,7 @@
   name = NULL;			/* keep compiler quiet */
   dbp = lb.buffer;
   *dbp = '\0';
-  initbuffer (&tline);
+  linebuffer_init (&tline);
 
   incomment = inquote = FALSE;
   found_tag = FALSE;		/* have a proc name; check if extern */
@@ -4542,7 +4586,7 @@
 	    }
 	  else if (lowcase (*dbp) == 'f')
 	    {
-	      if (nocase_tail ("forward")) /*  check for forward reference */
+	      if (nocase_tail ("forward")) /* check for forward reference */
 		{
 		  found_tag = FALSE;
 		  verify_tag = FALSE;
@@ -4925,6 +4969,127 @@
 }
 
 
+/* Similar to LOOKING_AT but does not use notinname, does not skip */
+#define LOOKING_AT_NOCASE(cp, kw)	/* kw is a constant string */	\
+  (strncaseeq ((cp), kw, sizeof(kw)-1)	/* cp points at kw */		\
+   && ((cp) += sizeof(kw)-1))		/* skip spaces */
+
+/*
+ * HTML support.
+ * Contents of <title>, <h1>, <h2>, <h3> are tags.
+ * Contents of <a name=xxx> are tags with name xxx.
+ *
+ * Francesco Potort́, 2002.
+ */
+static void
+HTML_labels (inf)
+     FILE * inf;
+{
+  bool getnext = FALSE;		/* next text outside of HTML tags is a tag */
+  bool ignoretag = FALSE;	/* skip to the end of the current HTML tag */
+  bool inanchor = FALSE;	/* inside an A HTML tag, looking for NAME= */
+  char *end;
+
+
+  linebuffer_setlen (&token_name, 0); /* no name in buffer */
+
+  LOOP_ON_INPUT_LINES (inf, lb, dbp)
+    {
+      for (;;)			/* loop on the same line */
+
+	if (ignoretag)		/* skip HTML tag */
+	  {
+	    while (*dbp != '\0' && *dbp != '>')
+	      dbp++;
+	    if (*dbp == '>')
+	      {
+		dbp += 1;
+		ignoretag = FALSE;
+		continue;	/* look on the same line */
+	      }
+	    break;		/* go to next line */
+	  }
+
+	else if (inanchor)	/* look for "name=" */
+	  {
+	    while (*dbp != '\0' && *dbp != '>' && lowcase (*dbp) != 'n')
+	      dbp++;
+	    if (*dbp == '\0')
+	      break;		/* go to next line */
+	    if (*dbp == '>')
+	      {
+		dbp += 1;
+		inanchor = FALSE;
+		continue;	/* look on the same line */
+	      }
+	    dbp += 1;
+	    if (LOOKING_AT_NOCASE (dbp, "ame="))
+	      {
+		bool quoted = (dbp[0] == '"');
+
+		if (quoted)
+		  for (end = ++dbp; *end != '\0' && *end != '"'; end++)
+		    continue;
+		else
+		  for (end = dbp; *end != '\0' && intoken (*end); end++)
+		    continue;
+		linebuffer_setlen (&token_name, end - dbp);
+		strncpy (token_name.buffer, dbp, end - dbp);
+		token_name.buffer[end - dbp] = '\0';
+
+		dbp = end;
+		inanchor = FALSE; /* we found what we looked for */
+		ignoretag = TRUE; /* skip to the end of the anchor */
+		getnext = TRUE;	/* then grab the text */
+		continue;	/* look on the same line */
+	      }
+	  }
+
+	else if (getnext)	/* grab next tokens and tag them */
+	  {
+	    dbp = skip_spaces (dbp);
+	    if (*dbp == '\0')
+	      break;		/* go to next line */
+	    if (*dbp == '<')
+	      {
+		if (lowcase (dbp[1]) == 'a' && !intoken (dbp[2]))
+		  inanchor = TRUE;
+		else
+		  ignoretag = TRUE;
+		continue;	/* look on the same line */
+	      }
+
+	    for (end = dbp + 1; *end != '\0' && *end != '<'; end++)
+	      continue;
+	    make_tag (token_name.buffer, token_name.len, TRUE,
+		      dbp, end - dbp, lineno, linecharno);
+	    linebuffer_setlen (&token_name, 0);	/* no name in buffer */
+	    getnext = FALSE;
+	    break;		/* go to next line */
+	  }
+
+	else			/* look for an interesting HTML tag */
+	  {
+	    while (*dbp != '\0' && *dbp != '<')
+	      dbp++;
+	    if (*dbp == '\0')
+	      break;		/* go to next line */
+	    dbp += 1;
+	    if (lowcase (dbp[0]) == 'a' && !intoken (dbp[1]))
+	      inanchor = TRUE;
+	    else if (LOOKING_AT_NOCASE (dbp, "title>")
+		     || LOOKING_AT_NOCASE (dbp, "h1>")
+		     || LOOKING_AT_NOCASE (dbp, "h2>")
+		     || LOOKING_AT_NOCASE (dbp, "h3>"))
+	      {
+		getnext = TRUE;
+		continue;	/* look on the same line */
+	      }
+	  }
+    }
+}
+
+
 /*
  * Prolog support
  *
@@ -5315,7 +5480,7 @@
 {
   if (regex_arg == NULL)
     {
-      free_patterns ();		/* --no-regex: remove existing regexps */
+      free_regexps ();		/* --no-regex: remove existing regexps */
       return;
     }
 
@@ -5343,7 +5508,7 @@
 	    pfatal (regexfile);
 	    return;
 	  }
-	initbuffer (&regexbuf);
+	linebuffer_init (&regexbuf);
 	while (readline_internal (&regexbuf, regexfp) > 0)
 	  analyse_regex (regexbuf.buffer);
 	free (regexbuf.buffer);
@@ -5379,8 +5544,8 @@
     }
 }
 
-/* Turn a name, which is an ed-style (but Emacs syntax) regular
-   expression, into a real regular expression by compiling it. */
+/* Separate the regexp pattern, compile it,
+   and care for optional name and modifiers. */
 static void
 add_regex (regexp_pattern, lang)
      char *regexp_pattern;
@@ -5390,8 +5555,12 @@
   char sep, *pat, *name, *modifiers;
   const char *err;
   struct re_pattern_buffer *patbuf;
-  pattern *pp;
-  bool ignore_case, multi_line, single_line;
+  regexp *rp;
+  bool
+    force_explicit_name = TRUE, /* do not use implicit tag names */
+    ignore_case = FALSE,	/* case is significant */
+    multi_line = FALSE,		/* matches are done one line at a time */
+    single_line = FALSE;	/* dot does not match newline */
 
 
   if (strlen(regexp_pattern) < 3)
@@ -5421,12 +5590,14 @@
     modifiers += 1;		/* skip separator */
 
   /* Parse regex modifiers. */
-  ignore_case = FALSE;		/* case is significant */
-  multi_line = FALSE;		/* matches are done one line at a time */
-  single_line = FALSE;		/* dot does not match newline */
   for (; modifiers[0] != '\0'; modifiers++)
     switch (modifiers[0])
       {
+      case 'N':
+	if (modifiers == name)
+	  error ("forcing explicit tag name but no name, ignoring", NULL);
+	force_explicit_name = TRUE;
+	break;
       case 'i':
 	ignore_case = TRUE;
 	break;
@@ -5477,14 +5648,15 @@
       return;
     }
 
-  pp = p_head;
-  p_head = xnew (1, pattern);
-  p_head->regex = savestr (regexp_pattern);
-  p_head->p_next = pp;
+  rp = p_head;
+  p_head = xnew (1, regexp);
+  p_head->pattern = savestr (regexp_pattern);
+  p_head->p_next = rp;
   p_head->lang = lang;
   p_head->pat = patbuf;
-  p_head->name_pattern = savestr (name);
+  p_head->name = savestr (name);
   p_head->error_signaled = FALSE;
+  p_head->force_explicit_name = force_explicit_name;
   p_head->ignore_case = ignore_case;
   p_head->multi_line = multi_line;
 }
@@ -5539,18 +5711,18 @@
   return result;
 }
 
-/* Deallocate all patterns. */
+/* Deallocate all regexps. */
 static void
-free_patterns ()
+free_regexps ()
 {
-  pattern *pp;
+  regexp *rp;
   while (p_head != NULL)
     {
-      pp = p_head->p_next;
-      free (p_head->regex);
-      free (p_head->name_pattern);
+      rp = p_head->p_next;
+      free (p_head->pattern);
+      free (p_head->name);
       free (p_head);
-      p_head = pp;
+      p_head = rp;
     }
   return;
 }
@@ -5566,13 +5738,14 @@
 regex_tag_multiline ()
 {
   char *buffer = filebuf.buffer;
-  pattern *pp;
-
-  for (pp = p_head; pp != NULL; pp = pp->p_next)
+  regexp *rp;
+  char *name;
+
+  for (rp = p_head; rp != NULL; rp = rp->p_next)
     {
       int match = 0;
 
-      if (!pp->multi_line)
+      if (!rp->multi_line)
 	continue;		/* skip normal regexps */
 
       /* Generic initialisations before parsing file from memory. */
@@ -5581,62 +5754,55 @@
       linecharno = 0;		/* reset global char number of line start */
 
       /* Only use generic regexps or those for the current language. */
-      if (pp->lang != NULL && pp->lang != curfdp->lang)
+      if (rp->lang != NULL && rp->lang != curfdp->lang)
 	continue;
 
       while (match >= 0 && match < filebuf.len)
 	{
-	  match = re_search (pp->pat, buffer, filebuf.len, charno,
-			     filebuf.len - match, &pp->regs);
+	  match = re_search (rp->pat, buffer, filebuf.len, charno,
+			     filebuf.len - match, &rp->regs);
 	  switch (match)
 	    {
 	    case -2:
 	      /* Some error. */
-	      if (!pp->error_signaled)
+	      if (!rp->error_signaled)
 		{
 		  error ("regexp stack overflow while matching \"%s\"",
-			 pp->regex);
-		  pp->error_signaled = TRUE;
+			 rp->pattern);
+		  rp->error_signaled = TRUE;
 		}
 	      break;
 	    case -1:
 	      /* No match. */
 	      break;
 	    default:
-	      if (match == pp->regs.end[0])
+	      if (match == rp->regs.end[0])
 		{
-		  if (!pp->error_signaled)
+		  if (!rp->error_signaled)
 		    {
 		      error ("regexp matches the empty string: \"%s\"",
-			     pp->regex);
-		      pp->error_signaled = TRUE;
+			     rp->pattern);
+		      rp->error_signaled = TRUE;
 		    }
 		  match = -3;	/* exit from while loop */
 		  break;
 		}
 
 	      /* Match occurred.  Construct a tag. */
-	      while (charno < pp->regs.end[0])
+	      while (charno < rp->regs.end[0])
 		if (buffer[charno++] == '\n')
 		  lineno++, linecharno = charno;
-	      if (pp->name_pattern[0] != '\0')
-		{
-		  /* Make a named tag.
-		     Do not use make_tag here, as it would make the name
-		     implicit if possible, while we want to respect the user's
-		     request to create an explicit tag name. */
-		  char *name = substitute (buffer,
-					   pp->name_pattern, &pp->regs);
-		  if (name != NULL)
-		    pfnote (name, TRUE, buffer + linecharno,
-			    charno - linecharno + 1, lineno, linecharno);
-		}
+	      name = rp->name;
+	      if (name[0] != '\0')
+		/* Make a named tag. */
+		name = substitute (buffer, rp->name, &rp->regs);
+	      if (rp->force_explicit_name)
+		/* Force explicit tag name, if a name is there. */
+		pfnote (name, TRUE, buffer + linecharno,
+			charno - linecharno + 1, lineno, linecharno);
 	      else
-		{
-		  /* Make an unnamed tag. */
-		  pfnote ((char *)NULL, TRUE, buffer + linecharno,
+		make_tag (name, strlen (name), TRUE, buffer + linecharno,
 			  charno - linecharno + 1, lineno, linecharno);
-		}
 	      break;
 	    }
 	}
@@ -5682,17 +5848,6 @@
     *namepp = savenstr (bp, cp - bp);
 }
 
-/* Initialize a linebuffer for use */
-static void
-initbuffer (lbp)
-     linebuffer *lbp;
-{
-  lbp->size = (DEBUG) ? 3 : 200;
-  lbp->buffer = xnew (lbp->size, char);
-  lbp->buffer[0] = '\0';
-  lbp->len = 0;
-}
-
 /*
  * Read a line of text from `stream' into `lbp', excluding the
  * newline or CR-NL, if any.  Return the number of characters read from
@@ -5889,6 +6044,8 @@
 			  fdhead->infabsdir = savestr (curfdp->infabsdir);
 			  fdhead->taggedfname = taggedfname;
 			  fdhead->usecharno = FALSE;
+			  fdhead->prop = NULL;
+			  fdhead->written = FALSE;
 			  curfdp = fdhead;
 			}
 		    }
@@ -5919,29 +6076,30 @@
 #ifdef ETAGS_REGEXPS
   {
     int match;
-    pattern *pp;
-
-    /* Match against relevant patterns. */
+    regexp *rp;
+    char *name;
+
+    /* Match against relevant regexps. */
     if (lbp->len > 0)
-      for (pp = p_head; pp != NULL; pp = pp->p_next)
+      for (rp = p_head; rp != NULL; rp = rp->p_next)
 	{
 	  /* Only use generic regexps or those for the current language.
 	     Also do not use multiline regexps, which is the job of
 	     regex_tag_multiline. */
-	  if ((pp->lang != NULL && pp->lang != fdhead->lang)
-	      || pp->multi_line)
+	  if ((rp->lang != NULL && rp->lang != fdhead->lang)
+	      || rp->multi_line)
 	    continue;
 
-	  match = re_match (pp->pat, lbp->buffer, lbp->len, 0, &pp->regs);
+	  match = re_match (rp->pat, lbp->buffer, lbp->len, 0, &rp->regs);
 	  switch (match)
 	    {
 	    case -2:
 	      /* Some error. */
-	      if (!pp->error_signaled)
+	      if (!rp->error_signaled)
 		{
 		  error ("regexp stack overflow while matching \"%s\"",
-			 pp->regex);
-		  pp->error_signaled = TRUE;
+			 rp->pattern);
+		  rp->error_signaled = TRUE;
 		}
 	      break;
 	    case -1:
@@ -5949,32 +6107,24 @@
 	      break;
 	    case 0:
 	      /* Empty string matched. */
-	      if (!pp->error_signaled)
+	      if (!rp->error_signaled)
 		{
-		  error ("regexp matches the empty string: \"%s\"",
-			 pp->regex);
-		  pp->error_signaled = TRUE;
+		  error ("regexp matches the empty string: \"%s\"", rp->pattern);
+		  rp->error_signaled = TRUE;
 		}
 	      break;
 	    default:
 	      /* Match occurred.  Construct a tag. */
-	      if (pp->name_pattern[0] != '\0')
-		{
-		  /* Make a named tag.
-		     Do not use make_tag here, as it would make the name
-		     implicit if possible, while we want to respect the user's
-		     request to create an explicit tag name. */
-		  char *name = substitute (lbp->buffer,
-					   pp->name_pattern, &pp->regs);
-		  if (name != NULL)
-		    pfnote (name, TRUE, lbp->buffer, match, lineno, linecharno);
-		}
+	      name = rp->name;
+	      if (name[0] != '\0')
+		/* Make a named tag. */
+		name = substitute (lbp->buffer, rp->name, &rp->regs);
+	      if (rp->force_explicit_name)
+		/* Force explicit tag name, if a name is there. */
+		pfnote (name, TRUE, lbp->buffer, match, lineno, linecharno);
 	      else
-		{
-		  /* Make an unnamed tag. */
-		  pfnote ((char *)NULL, TRUE,
+		make_tag (name, strlen (name), TRUE,
 			  lbp->buffer, match, lineno, linecharno);
-		}
 	      break;
 	    }
 	}
@@ -6053,13 +6203,12 @@
 }
 
 /*
- * Return TRUE if the two strings are equal, ignoring case for alphabetic
- * characters.
+ * Compare two strings, ignoring case for alphabetic characters.
  *
- * Analogous to BSD's strcasecmp, included for portability.
+ * Same as BSD's strcasecmp, included for portability.
  */
-static bool
-strcaseeq (s1, s2)
+static int
+etags_strcasecmp (s1, s2)
      register const char *s1;
      register const char *s2;
 {
@@ -6069,7 +6218,35 @@
 	     : *s1 == *s2))
     s1++, s2++;
 
-  return (*s1 == *s2);
+  return (ISALPHA (*s1) && ISALPHA (*s2)
+	  ? lowcase (*s1) - lowcase (*s2)
+	  : *s1 - *s2);
+}
+
+/*
+ * Compare two strings, ignoring case for alphabetic characters.
+ * Stop after a given number of characters
+ *
+ * Same as BSD's strncasecmp, included for portability.
+ */
+static int
+etags_strncasecmp (s1, s2, n)
+     register const char *s1;
+     register const char *s2;
+     register int n;
+{
+  while (*s1 != '\0' && n-- > 0
+	 && (ISALPHA (*s1) && ISALPHA (*s2)
+	     ? lowcase (*s1) == lowcase (*s2)
+	     : *s1 == *s2))
+    s1++, s2++;
+
+  if (n < 0)
+    return 0;
+  else
+    return (ISALPHA (*s1) && ISALPHA (*s2)
+	    ? lowcase (*s1) - lowcase (*s2)
+	    : *s1 - *s2);
 }
 
 /* Skip spaces, return new pointer. */
@@ -6190,7 +6367,7 @@
   linebuffer path;
   FILE *pipe;
 
-  initbuffer (&path);
+  linebuffer_init (&path);
   pipe = (FILE *) popen ("pwd 2>/dev/null", "r");
   if (pipe == NULL || readline_internal (&path, pipe) == 0)
     pfatal ("pwd");
@@ -6356,6 +6533,18 @@
 #endif
 }
 
+
+/* Initialize a linebuffer for use */
+static void
+linebuffer_init (lbp)
+     linebuffer *lbp;
+{
+  lbp->size = (DEBUG) ? 3 : 200;
+  lbp->buffer = xnew (lbp->size, char);
+  lbp->buffer[0] = '\0';
+  lbp->len = 0;
+}
+
 /* Set the minimum size of a string contained in a linebuffer. */
 static void
 linebuffer_setlen (lbp, toksize)
@@ -6370,7 +6559,7 @@
   lbp->len = toksize;
 }
 
-/* Like malloc but get fatal error if memory is exhausted.  */
+/* Like malloc but get fatal error if memory is exhausted. */
 static PTR
 xmalloc (size)
      unsigned int size;
@@ -6398,6 +6587,6 @@
  * indent-tabs-mode: t
  * tab-width: 8
  * fill-column: 79
- * c-font-lock-extra-types: ("FILE" "bool" "language" "linebuffer" "fdesc" "node" "pattern")
+ * c-font-lock-extra-types: ("FILE" "bool" "language" "linebuffer" "fdesc" "node" "regexp")
  * End:
  */