(enum arg_type): New label at_stdin.
[bpt/emacs.git] / lib-src / etags.c
index 0ba1ccf..8b5e85c 100644 (file)
@@ -28,12 +28,13 @@ Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
  * 1992 Joseph B. Wells improved C and C++ parsing.
  * 1993        Francesco Potortì reorganised C and C++.
  * 1994        Regexp tags by Tom Tromey.
- * 2001 Nested classes by Francesco Potortì (ideas by Mykola Dzyuba).
+ * 2001 Nested classes by Francesco Potortì (concept by Mykola Dzyuba).
+ * 2002 #line directives by Francesco Potortì.
  *
  *     Francesco Potortì <pot@gnu.org> has maintained it since 1993.
  */
 
-char pot_etags_version[] = "@(#) pot revision number is 15.16";
+char pot_etags_version[] = "@(#) pot revision number is 16.4";
 
 #define        TRUE    1
 #define        FALSE   0
@@ -288,7 +289,8 @@ typedef struct
     at_language,               /* a language specification */
     at_regexp,                 /* a regular expression */
     at_icregexp,               /* same, but with case ignored */
-    at_filename                        /* a file name */
+    at_filename,               /* a file name */
+    at_stdin                   /* read from stdin here */
   } arg_type;                  /* argument type */
   language *lang;              /* language associated with the argument */
   char *what;                  /* the argument itself */
@@ -365,7 +367,8 @@ static void add_node __P((node *, node **));
 
 static void init __P((void));
 static void initbuffer __P((linebuffer *));
-static void process_file __P((char *, language *));
+static void process_file_name __P((char *, language *));
+static void process_file __P((FILE *, char *, language *));
 static void find_entries __P((FILE *));
 static void free_tree __P((node *));
 static void free_fdesc __P((fdesc *));
@@ -441,7 +444,7 @@ static bool constantypedefs;        /* -d: create tags for C #define, enum */
 static bool globals;           /* create tags for global variables */
 static bool declarations;      /* --declarations: tag them and extern in C&Co*/
 static bool members;           /* create tags for C member variables */
-static bool no_line_directive; /* ignore #line directives */
+static bool no_line_directive; /* ignore #line directives (undocumented) */
 static bool update;            /* -u: update tags */
 static bool vgrind_style;      /* -v: create vgrind style index output */
 static bool no_warnings;       /* -w: suppress warnings */
@@ -450,6 +453,9 @@ static bool cplusplus;              /* .[hc] means C++, not C */
 static bool noindentypedefs;   /* -I: ignore indentation in C */
 static bool packages_only;     /* --packages-only: in Ada, only tag packages*/
 
+#define STDIN 0x1001           /* returned by getopt_long on --parse-stdin */
+static bool parsing_stdin;     /* --parse-stdin used */
+
 #ifdef ETAGS_REGEXPS
 /* List of all regexps. */
 static pattern *p_head;
@@ -479,6 +485,7 @@ static struct option longopts[] =
   { "no-regex",                  no_argument,       NULL,               'R'   },
   { "ignore-case-regex",  required_argument, NULL,              'c'   },
 #endif /* ETAGS_REGEXPS */
+  { "parse-stdin",        required_argument, NULL,               STDIN },
   { "version",           no_argument,       NULL,               'V'   },
 
 #if CTAGS /* Etags options */
@@ -704,6 +711,9 @@ linked with GNU getopt.");
 Absolute names are stored in the output file as they are.\n\
 Relative ones are stored relative to the output file's directory.\n");
 
+  puts ("--parse-stdin=NAME\n\
+        Read from standard input and record tags as belonging to file NAME.");
+
   if (!CTAGS)
     puts ("-a, --append\n\
         Append tag entries to existing tags file.");
@@ -969,6 +979,9 @@ main (argc, argv)
 #ifdef VMS
   bool got_err;
 #endif
+ char *optstring;
+ int opt;
+
 
 #ifdef DOS_NT
   _fmode = O_BINARY;   /* all of files are treated as binary files */
@@ -1003,109 +1016,108 @@ main (argc, argv)
       globals = TRUE;
     }
 
-  while (1)
-    {
-      int opt;
-      char *optstring = "-";
-
+  optstring = "-";
 #ifdef ETAGS_REGEXPS
-      optstring = "-r:Rc:";
+  optstring = "-r:Rc:";
 #endif /* ETAGS_REGEXPS */
-
 #ifndef LONG_OPTIONS
-      optstring = optstring + 1;
+  optstring = optstring + 1;
 #endif /* LONG_OPTIONS */
+  optstring = concat (optstring,
+                     "Cf:Il:o:SVhH",
+                     (CTAGS) ? "BxdtTuvw" : "aDi:");
 
-      optstring = concat (optstring,
-                         "Cf:Il:o:SVhH",
-                         (CTAGS) ? "BxdtTuvw" : "aDi:");
-
-      opt = getopt_long (argc, argv, optstring, longopts, 0);
-      if (opt == EOF)
+  while ((opt = getopt_long (argc, argv, optstring, longopts, 0)) != EOF)
+    switch (opt)
+      {
+      case 0:
+       /* If getopt returns 0, then it has already processed a
+          long-named option.  We should do nothing.  */
        break;
 
-      switch (opt)
-       {
-       case 0:
-         /* If getopt returns 0, then it has already processed a
-            long-named option.  We should do nothing.  */
-         break;
+      case 1:
+       /* This means that a file name has been seen.  Record it. */
+       argbuffer[current_arg].arg_type = at_filename;
+       argbuffer[current_arg].what     = optarg;
+       ++current_arg;
+       ++file_count;
+       break;
 
-       case 1:
-         /* This means that a file name has been seen.  Record it. */
-         argbuffer[current_arg].arg_type = at_filename;
-         argbuffer[current_arg].what = optarg;
-         ++current_arg;
-         ++file_count;
-         break;
+      case STDIN:
+       /* Parse standard input.  Idea by Vivek <vivek@etla.org>. */
+       argbuffer[current_arg].arg_type = at_stdin;
+       argbuffer[current_arg].what     = optarg;
+       ++current_arg;
+       ++file_count;
+       parsing_stdin = TRUE;
+       break;
 
-         /* Common options. */
-       case 'C': cplusplus = TRUE;             break;
-       case 'f':               /* for compatibility with old makefiles */
-       case 'o':
-         if (tagfile)
-           {
-             error ("-o option may only be given once.", (char *)NULL);
-             suggest_asking_for_help ();
-           }
-         tagfile = optarg;
-         break;
-       case 'I':
-       case 'S':               /* for backward compatibility */
-         noindentypedefs = TRUE;
-         break;
-       case 'l':
+       /* Common options. */
+      case 'C': cplusplus = TRUE;              break;
+      case 'f':                /* for compatibility with old makefiles */
+      case 'o':
+       if (tagfile)
          {
-           language *lang = get_language_from_langname (optarg);
-           if (lang != NULL)
-             {
-               argbuffer[current_arg].lang = lang;
-               argbuffer[current_arg].arg_type = at_language;
-               ++current_arg;
-             }
+           error ("-o option may only be given once.", (char *)NULL);
+           suggest_asking_for_help ();
          }
-         break;
-       case 'r':
-         argbuffer[current_arg].arg_type = at_regexp;
-         argbuffer[current_arg].what = optarg;
-         ++current_arg;
-         break;
-       case 'R':
-         argbuffer[current_arg].arg_type = at_regexp;
-         argbuffer[current_arg].what = NULL;
-         ++current_arg;
-         break;
-        case 'c':
-         argbuffer[current_arg].arg_type = at_icregexp;
-         argbuffer[current_arg].what = optarg;
-         ++current_arg;
-         break;
-       case 'V':
-         print_version ();
-         break;
-       case 'h':
-       case 'H':
-         print_help ();
-         break;
-
-         /* Etags options */
-       case 'a': append_to_tagfile = TRUE;                     break;
-       case 'D': constantypedefs = FALSE;                      break;
-       case 'i': included_files[nincluded_files++] = optarg;   break;
-
-         /* Ctags options. */
-       case 'B': searchar = '?';                               break;
-       case 'd': constantypedefs = TRUE;                       break;
-       case 't': typedefs = TRUE;                              break;
-       case 'T': typedefs = typedefs_or_cplusplus = TRUE;      break;
-       case 'u': update = TRUE;                                break;
-       case 'v': vgrind_style = TRUE;                    /*FALLTHRU*/
-       case 'x': cxref_style = TRUE;                           break;
-       case 'w': no_warnings = TRUE;                           break;
-       default:
-         suggest_asking_for_help ();
+       tagfile = optarg;
+       break;
+      case 'I':
+      case 'S':                /* for backward compatibility */
+       noindentypedefs = TRUE;
+       break;
+      case 'l':
+       {
+         language *lang = get_language_from_langname (optarg);
+         if (lang != NULL)
+           {
+             argbuffer[current_arg].lang = lang;
+             argbuffer[current_arg].arg_type = at_language;
+             ++current_arg;
+           }
        }
-    }
+       break;
+      case 'r':
+       argbuffer[current_arg].arg_type = at_regexp;
+       argbuffer[current_arg].what = optarg;
+       ++current_arg;
+       break;
+      case 'R':
+       argbuffer[current_arg].arg_type = at_regexp;
+       argbuffer[current_arg].what = NULL;
+       ++current_arg;
+       break;
+      case 'c':
+       argbuffer[current_arg].arg_type = at_icregexp;
+       argbuffer[current_arg].what = optarg;
+       ++current_arg;
+       break;
+      case 'V':
+       print_version ();
+       break;
+      case 'h':
+      case 'H':
+       print_help ();
+       break;
+
+       /* Etags options */
+      case 'a': append_to_tagfile = TRUE;                      break;
+      case 'D': constantypedefs = FALSE;                       break;
+      case 'i': included_files[nincluded_files++] = optarg;    break;
+
+       /* Ctags options. */
+      case 'B': searchar = '?';                                        break;
+      case 'd': constantypedefs = TRUE;                                break;
+      case 't': typedefs = TRUE;                               break;
+      case 'T': typedefs = typedefs_or_cplusplus = TRUE;       break;
+      case 'u': update = TRUE;                                 break;
+      case 'v': vgrind_style = TRUE;                     /*FALLTHRU*/
+      case 'x': cxref_style = TRUE;                            break;
+      case 'w': no_warnings = TRUE;                            break;
+      default:
+       suggest_asking_for_help ();
+      }
 
   for (; optind < argc; ++optind)
     {
@@ -1198,14 +1210,23 @@ main (argc, argv)
              /* Input file named "-" means read file names from stdin
                 (one per line) and use them. */
              if (streq (this_file, "-"))
-               while (readline_internal (&filename_lb, stdin) > 0)
-                 process_file (filename_lb.buffer, lang);
+               {
+                 if (parsing_stdin)
+                   fatal ("cannot parse standard input AND read file names from it",
+                          (char *)NULL);
+                 while (readline_internal (&filename_lb, stdin) > 0)
+                   process_file_name (filename_lb.buffer, lang);
+               }
              else
-               process_file (this_file, lang);
+               process_file_name (this_file, lang);
 #ifdef VMS
            }
 #endif
          break;
+        case at_stdin:
+          this_file = argbuffer[i].what;
+          process_file (stdin, this_file, lang);
+          break;
        }
     }
 
@@ -1232,8 +1253,14 @@ main (argc, argv)
       char cmd[BUFSIZ];
       for (i = 0; i < current_arg; ++i)
        {
-         if (argbuffer[i].arg_type != at_filename)
-           continue;
+         switch (argbuffer[i].arg_type)
+           {
+           case at_filename:
+           case at_stdin:
+             break;
+           default:
+             continue;         /* the for loop */
+           }
          sprintf (cmd,
                   "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
                   tagfile, argbuffer[i].what, tagfile);
@@ -1254,8 +1281,8 @@ main (argc, argv)
 
   if (update)
     {
-      char cmd[BUFSIZ];
-      sprintf (cmd, "sort %s -o %s", tagfile, tagfile);
+      char cmd[2*BUFSIZ+10];
+      sprintf (cmd, "sort -o %.*s %.*s", BUFSIZ, tagfile, BUFSIZ, tagfile);
       exit (system (cmd));
     }
   return GOOD;
@@ -1390,20 +1417,18 @@ get_language_from_filename (file, case_sensitive)
  * This routine is called on each file argument.
  */
 static void
-process_file (file, lang)
+process_file_name (file, lang)
      char *file;
      language *lang;
 {
   struct stat stat_buf;
   FILE *inf;
-  static const fdesc emptyfdesc;
   fdesc *fdp;
   compressor *compr;
   char *compressed_name, *uncompressed_name;
   char *ext, *real_name;
   int retval;
 
-
   canonicalize_filename (file);
   if (streq (file, tagfile) && !streq (tagfile, "-"))
     {
@@ -1498,24 +1523,50 @@ process_file (file, lang)
       goto cleanup;
     }
 
+  process_file (inf, uncompressed_name, lang);
+
+  if (real_name == compressed_name)
+    retval = pclose (inf);
+  else
+    retval = fclose (inf);
+  if (retval < 0)
+    pfatal (file);
+
+ cleanup:
+  if (compressed_name) free (compressed_name);
+  if (uncompressed_name) free (uncompressed_name);
+  last_node = NULL;
+  curfdp = NULL;
+  return;
+}
+
+static void
+process_file (fh, fn, lang)
+     FILE *fh;
+     char *fn;
+     language *lang;
+{
+  static const fdesc emptyfdesc;
+  fdesc *fdp;
+
   /* Create a new input file description entry. */
   fdp = xnew (1, fdesc);
   *fdp = emptyfdesc;
   fdp->next = fdhead;
-  fdp->infname = savestr (uncompressed_name);
+  fdp->infname = savestr (fn);
   fdp->lang = lang;
-  fdp->infabsname = absolute_filename (uncompressed_name, cwd);
-  fdp->infabsdir = absolute_dirname (uncompressed_name, cwd);
-  if (filename_is_absolute (uncompressed_name))
+  fdp->infabsname = absolute_filename (fn, cwd);
+  fdp->infabsdir = absolute_dirname (fn, cwd);
+  if (filename_is_absolute (fn))
     {
-      /* file is an absolute file name.  Canonicalize it. */
-      fdp->taggedfname = absolute_filename (uncompressed_name, NULL);
+      /* An absolute file name.  Canonicalize it. */
+      fdp->taggedfname = absolute_filename (fn, NULL);
     }
   else
     {
-      /* file is a file name relative to cwd.  Make it relative
+      /* A file name relative to cwd.  Make it relative
         to the directory of the tags file. */
-      fdp->taggedfname = relative_filename (uncompressed_name, tagfiledir);
+      fdp->taggedfname = relative_filename (fn, tagfiledir);
     }
   fdp->usecharno = TRUE;       /* use char position when making tags */
   fdp->prop = NULL;
@@ -1523,14 +1574,7 @@ process_file (file, lang)
   fdhead = fdp;
   curfdp = fdhead;             /* the current file description */
 
-  find_entries (inf);
-
-  if (real_name == compressed_name)
-    retval = pclose (inf);
-  else
-    retval = fclose (inf);
-  if (retval < 0)
-    pfatal (file);
+  find_entries (fh);
 
   /* If not Ctags, and if this is not metasource and if it contained no #line
      directives, we can write the tags and free all nodes pointing to
@@ -1565,13 +1609,6 @@ process_file (file, lang)
            prev->left = NULL;  /* delete the pointer to the sublist */
        }
     }
-
- cleanup:
-  if (compressed_name) free (compressed_name);
-  if (uncompressed_name) free (uncompressed_name);
-  last_node = NULL;
-  curfdp = NULL;
-  return;
 }
 
 /*
@@ -3351,6 +3388,7 @@ C_entries (c_ext, inf)
                  break;
                case dsharpseen:
                  savetoken = token;
+                 break;
                }
              if (!yacc_rules || lp == newlb.buffer + 1)
                {
@@ -3615,6 +3653,7 @@ C_entries (c_ext, inf)
                      && typdef == tnone)
                    cblev = -1;
                }
+             break;
            }
          switch (structdef)
            {
@@ -4695,26 +4734,24 @@ Scheme_functions (inf)
 /* Find tags in TeX and LaTeX input files.  */
 
 /* TEX_toktab is a table of TeX control sequences that define tags.
-   Each TEX_tabent records one such control sequence.
-   CONVERT THIS TO USE THE Stab TYPE!! */
-struct TEX_tabent
-{
-  char *name;
-  int len;
-};
+ * Each entry records one such control sequence.
+ *
+ * Original code from who knows whom.
+ * Ideas by:
+ *   Stefan Monnier (2002)
+ */
 
-static struct TEX_tabent *TEX_toktab = NULL; /* Table with tag tokens */
+static linebuffer *TEX_toktab = NULL; /* Table with tag tokens */
 
 /* Default set of control sequences to put into TEX_toktab.
    The value of environment var TEXTAGS is prepended to this.  */
-
 static char *TEX_defenv = "\
 :chapter:section:subsection:subsubsection:eqno:label:ref:cite:bibitem\
-:part:appendix:entry:index";
+:part:appendix:entry:index:def\
+:newcommand:renewcommand:newenvironment:renewenvironment";
 
 static void TEX_mode __P((FILE *));
-static struct TEX_tabent *TEX_decode_env __P((char *, char *));
-static int TEX_Token __P((char *));
+static void TEX_decode_env __P((char *, char *));
 
 static char TEX_esc = '\\';
 static char TEX_opgrp = '{';
@@ -4727,49 +4764,64 @@ static void
 TeX_commands (inf)
      FILE *inf;
 {
-  char *cp, *lasthit;
-  register int i;
+  char *cp;
+  linebuffer *key;
 
   /* Select either \ or ! as escape character.  */
   TEX_mode (inf);
 
   /* Initialize token table once from environment. */
-  if (!TEX_toktab)
-    TEX_toktab = TEX_decode_env ("TEXTAGS", TEX_defenv);
+  if (TEX_toktab == NULL)
+    TEX_decode_env ("TEXTAGS", TEX_defenv);
 
   LOOP_ON_INPUT_LINES (inf, lb, cp)
     {
-      lasthit = cp;
-      /* Look at each esc in line. */
-      while ((cp = etags_strchr (cp, TEX_esc)) != NULL)
+      /* Look at each TEX keyword in line. */
+      for (;;)
        {
-         if (*++cp == '\0')
-           break;
-         linecharno += cp - lasthit;
-         lasthit = cp;
-         i = TEX_Token (lasthit);
-         if (i >= 0)
-           {
-             register char *p;
-             for (lasthit += TEX_toktab[i].len;
-                  *lasthit == TEX_esc || *lasthit == TEX_opgrp;
-                  lasthit++)
-               continue;
-             for (p = lasthit;
-                  !iswhite (*p) && *p != TEX_opgrp && *p != TEX_clgrp;
-                  p++)
-               continue;
-             pfnote (savenstr (lasthit, p-lasthit), TRUE,
-                     lb.buffer, lb.len, lineno, linecharno);
-             break;            /* We only tag a line once */
-           }
+         /* Look for a TEX escape. */
+         while (*cp++ != TEX_esc)
+           if (cp[-1] == '\0' || cp[-1] == '%')
+             goto tex_next_line;
+
+         for (key = TEX_toktab; key->buffer != NULL; key++)
+           if (strneq (cp, key->buffer, key->len))
+             {
+               register char *p;
+               char *name;
+               int linelen;
+               bool opgrp = FALSE;
+
+               cp = skip_spaces (cp + key->len);
+               if (*cp == TEX_opgrp)
+                 {
+                   opgrp = TRUE;
+                   cp++;
+                 }
+               for (p = cp;
+                    (!iswhite (*p) && *p != '#' &&
+                     *p != TEX_opgrp && *p != TEX_clgrp);
+                    p++)
+                 continue;
+               name = savenstr (cp, p-cp);
+               linelen = lb.len;
+               if (!opgrp || *p == TEX_clgrp)
+                 {
+                   while (*p != '\0' && *p != TEX_opgrp && *p != TEX_clgrp)
+                     *p++;
+                   linelen = p - lb.buffer + 1;
+                 }
+               pfnote (name, TRUE, lb.buffer, linelen, lineno, linecharno);
+               goto tex_next_line; /* We only tag a line once */
+             }
        }
+    tex_next_line:
+      ;
     }
 }
 
 #define TEX_LESC '\\'
 #define TEX_SESC '!'
-#define TEX_cmt  '%'
 
 /* Figure out whether TeX's escapechar is '\\' or '!' and set grouping
    chars accordingly. */
@@ -4782,7 +4834,7 @@ TEX_mode (inf)
   while ((c = getc (inf)) != EOF)
     {
       /* Skip to next line if we hit the TeX comment char. */
-      if (c == TEX_cmt)
+      if (c == '%')
        while (c != '\n')
          c = getc (inf);
       else if (c == TEX_LESC || c == TEX_SESC )
@@ -4808,15 +4860,13 @@ TEX_mode (inf)
 
 /* Read environment and prepend it to the default string.
    Build token table. */
-static struct TEX_tabent *
+static void
 TEX_decode_env (evarname, defenv)
      char *evarname;
      char *defenv;
 {
   register char *env, *p;
-
-  struct TEX_tabent *tab;
-  int size, i;
+  int i, len;
 
   /* Append default string to environment. */
   env = getenv (evarname);
@@ -4829,52 +4879,33 @@ TEX_decode_env (evarname, defenv)
     }
 
   /* Allocate a token table */
-  for (size = 1, p = env; p;)
+  for (len = 1, p = env; p;)
     if ((p = etags_strchr (p, ':')) && *++p != '\0')
-      size++;
-  /* Add 1 to leave room for null terminator.  */
-  tab = xnew (size + 1, struct TEX_tabent);
+      len++;
+  TEX_toktab = xnew (len, linebuffer);
 
   /* Unpack environment string into token table. Be careful about */
   /* zero-length strings (leading ':', "::" and trailing ':') */
-  for (i = 0; *env;)
+  for (i = 0; *env != '\0';)
     {
       p = etags_strchr (env, ':');
       if (!p)                  /* End of environment string. */
        p = env + strlen (env);
       if (p - env > 0)
        {                       /* Only non-zero strings. */
-         tab[i].name = savenstr (env, p - env);
-         tab[i].len = strlen (tab[i].name);
+         TEX_toktab[i].buffer = savenstr (env, p - env);
+         TEX_toktab[i].len = p - env;
          i++;
        }
       if (*p)
        env = p + 1;
       else
        {
-         tab[i].name = NULL;   /* Mark end of table. */
-         tab[i].len = 0;
+         TEX_toktab[i].buffer = NULL; /* Mark end of table. */
+         TEX_toktab[i].len = 0;
          break;
        }
     }
-  return tab;
-}
-
-/* If the text at CP matches one of the tag-defining TeX command names,
-   return the pointer to the first occurrence of that command in TEX_toktab.
-   Otherwise return -1.
-   Keep the capital `T' in `token' for dumb truncating compilers
-   (this distinguishes it from `TEX_toktab' */
-static int
-TEX_Token (cp)
-     char *cp;
-{
-  int i;
-
-  for (i = 0; TEX_toktab[i].len > 0; i++)
-    if (strneq (TEX_toktab[i].name, cp, TEX_toktab[i].len))
-      return i;
-  return -1;
 }
 
 \f
@@ -5034,7 +5065,7 @@ prolog_atom (s, pos)
     {
       pos++;
 
-      while (1)
+      for (;;)
        {
          if (s[pos] == '\'')
            {
@@ -5213,7 +5244,7 @@ erlang_atom (s, pos)
     {
       pos++;
 
-      while (1)
+      for (;;)
        {
          if (s[pos] == '\'')
            {
@@ -5547,7 +5578,7 @@ readline_internal (lbp, stream)
 
   pend = p + lbp->size;                /* Separate to avoid 386/IX compiler bug.  */
 
-  while (1)
+  for (;;)
     {
       register int c = getc (stream);
       if (p == pend)