*** empty log message ***
[bpt/emacs.git] / lib-src / etags.c
index fed3f58..4921e31 100644 (file)
@@ -1,5 +1,5 @@
 /* Tags file maker to go with GNU Emacs
-   Copyright (C) 1984, 1987, 1988, 1989 Free Software Foundation, Inc. and Ken Arnold
+   Copyright (C) 1984, 1987, 1988, 1989, 1992 Free Software Foundation, Inc. and Ken Arnold
 
 This file is part of GNU Emacs.
 
@@ -31,6 +31,8 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include <sys/types.h>
 #include <sys/stat.h>
 
+#include "getopt.h"
+
 #ifdef __GNUC__
 #define        alloca  __builtin_alloca
 #else
@@ -43,13 +45,11 @@ extern char *alloca ();
 
 extern char *malloc (), *realloc ();
 extern char *getenv ();
-extern char *index (), *rindex ();
 extern char *strcpy (), *strncpy ();
 extern int strcmp ();
 
-#ifdef hpux
-#define notdef
-#endif
+char *etags_index (), *etags_rindex ();
+char *savenstr ();
 
 /* Define the symbol ETAGS to make the program "etags",
  which makes emacs-style tag tables by default.
@@ -408,7 +408,13 @@ DEFINEST definedef;
  * for self-documentation only.
  */
 #define LEVEL_OK_FOR_FUNCDEF()                                 \
-       (level==0 || c_ext && level==1 && structdef==sinbody)
+       (level==0 || (c_ext && level==1 && structdef==sinbody))
+
+/*
+ * next_token_is_func
+ *     set this to TRUE, and the next token considered is called a function.
+ */
+logical next_token_is_func;
 
 /* C extensions.  Currently all listed extensions are C++ dialects, so
  * `c_ext' is used as an abbreviation for `c_ext&C_PLPL'.  If a non-C++
@@ -436,7 +442,6 @@ char *curfile,                      /* current input file name              */
  *intk = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789",   /* valid in-token chars                 */
  *notgd = ",;";                        /* non-valid after-function chars       */
 
-int file_num;                  /* current file number                  */
 int append_to_tagfile;         /* -a: append to tags */
 int emacs_tags_format;         /* emacs style output (no -e option any more) */
 /* The following three default to 1 for etags, but to 0 for ctags.  */
@@ -458,6 +463,27 @@ int noindentypedefs;               /* -S: ignore indentation in C */
 /* Name this program was invoked with.  */
 char *progname;
 
+struct option longopts[] = {
+  { "append",                  no_argument,       NULL, 'a' },
+  { "backward-search",         no_argument,       NULL, 'B' }, 
+  { "c++",                     no_argument,       NULL, 'C' },
+  { "cxref",                   no_argument,       NULL, 'x' },
+  { "defines",                 no_argument,       NULL, 'd' },
+  { "forward-search",          no_argument,       NULL, 'F' }, 
+  { "help",                    no_argument,       NULL, 'H' },
+  { "ignore-indentation",      no_argument,       NULL, 'S' },
+  { "include",                 required_argument, NULL, 'i' },
+  { "no-defines",              no_argument,       NULL, 'D' },
+  { "no-warn",                 no_argument,       NULL, 'w' },
+  { "output",                  required_argument, NULL, 'o' },
+  { "typedefs",                        no_argument,       NULL, 't' },
+  { "typedefs-and-c++",                no_argument,       NULL, 'T' },
+  { "update",                  no_argument,       NULL, 'u' }, 
+  { "version",                 no_argument,       NULL, 'V' },
+  { "vgrind",                  no_argument,       NULL, 'v' }, 
+  { 0 }
+};
+
 FILE *inf,                     /* ioptr for current input file         */
  *outf;                                /* ioptr for tags file                  */
 
@@ -480,6 +506,112 @@ struct linebuffer lb1;            /* sometimes, a previous line in which a token lies */
 struct linebuffer filename_lb; /* used to read in filenames */
 \f
 
+void
+print_version ()
+{
+#ifdef CTAGS
+  printf ("CTAGS ");
+#ifdef ETAGS
+  printf ("and ");
+#endif
+#endif
+#ifdef ETAGS
+  printf ("ETAGS ");
+#endif
+  printf ("for Emacs version 19.0.\n");
+
+  exit (0);
+}
+
+void
+print_help ()
+{
+  printf ("These are the options accepted by %s.  You may use unambiguous\n\
+abbreviations for the long option names.\n\n", progname);
+
+  fputs ("\
+-a, --append\n\
+        Append tag entries to existing tags file.\n\
+-C, --c++\n\
+        Treat files with `.c' and `.h' extensions as C++ code, not C\n\
+        code.  Files with `.C', `.H', `.cxx', `.hxx', or `.cc'\n\
+        extensions are always assumed to be C++ code.\n\
+-d, --defines\n\
+        Create tag entries for #defines, too.", stdout);
+
+#ifdef ETAGS
+  fputs ("  This is the default\n\
+        behavior.", stdout);
+#endif
+
+  fputs ("\n\
+-D, --no-defines\n\
+        Don't create tag entries for #defines.", stdout);
+
+#ifdef CTAGS
+  fputs ("  This is the default\n\
+        behavior.", stdout);
+#endif
+
+  puts ("\n\
+-o FILE, --output=FILE\n\
+        Write the tags to FILE.\n\
+-S, --ignore-indentation\n\
+        Don't rely on indentation quite as much as normal.  Currently,\n\
+        this means not to assume that a closing brace in the first\n\
+        column is the final brace of a function or structure\n\
+        definition.\n\
+-t, --typedefs\n\
+        Generate tag entries for typedefs.  This is the default\n\
+        behavior.\n\
+-T, --typedefs-and-c++\n\
+        Generate tag entries for typedefs, struct/enum/union tags, and\n\
+        C++ member functions.");
+
+#ifdef ETAGS
+  puts ("-i FILE, --include=FILE\n\
+        Include a note in tag file indicating that, when searching for\n\
+        a tag, one should also consult the tags file FILE after\n\
+        checking the current file.");
+#endif
+
+#ifdef CTAGS
+  puts ("-B, --backward-search\n\
+        Write the search commands for the tag entries using '?', the\n\
+        backward-search command.\n\
+-F, --forward-search\n\
+        Write the search commands for the tag entries using '/', the\n\
+        forward-search command.\n\
+-u, --update\n\
+        Update the tag entries for the given files, leaving tag\n\
+        entries for other files in place.  Currently, this is\n\
+        implemented by deleting the existing entries for the given\n\
+        files and then rewriting the new entries at the end of the\n\
+        tags file.  It is often faster to simply rebuild the entire\n\
+        tag file than to use this.\n\
+-v, --vgrind\n\
+        Generates an index of items intended for human consumption,\n\
+        similar to the output of vgrind.  The index is sorted, and\n\
+        gives the page number of each item.\n\
+-x, --cxref\n\
+        Like --vgrind, but in the style of cxref, rather than vgrind.\n\
+        The output uses line numbers instead of page numbers, but\n\
+        beyond that the differences are cosmetic; try both to see\n\
+        which you like.\n\
+-w, --no-warn\n\
+        Suppress warning messages about entries defined in multiple\n\
+        files.");
+#endif
+
+  puts ("-V, --version\n\
+        Print the version of the program.\n\
+-H, --help\n\
+        Print this help message.");
+
+  exit (0);
+}
+
+\f
 void
 main (argc, argv)
      int argc;
@@ -487,7 +619,6 @@ main (argc, argv)
 {
   char cmd[100];
   int i;
-  int outfflag = 0;
   unsigned int nincluded_files = 0;
   char **included_files = (char **) alloca (argc * sizeof (char *));
   char *this_file;
@@ -513,116 +644,109 @@ main (argc, argv)
   if (emacs_tags_format)
     typedefs = typedefs_and_cplusplus = constantypedefs = 1;
 
-  for (; argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0'; argc--, argv++)
+  for (;;)
     {
-      for (i = 1; argv[1][i]; i++)
+      int opt;
+      opt = getopt_long (argc, argv, "aCdDo:StTi:BFuvxwVH", longopts, 0);
+
+      if (opt == EOF)
+       break;
+
+      switch (opt)
        {
-         switch (argv[1][i])
+       case '\0':
+         /* If getopt returns '\0', then it has already processed a
+            long-named option.  We should do nothing.  */
+         break;
+
+         /* Common options. */
+       case 'a':
+         append_to_tagfile++;
+         break;
+       case 'C':
+         cplusplus = 1;
+         break;
+       case 'd':
+         constantypedefs = 1;
+         break;
+       case 'D':
+         constantypedefs = 0;
+         break;
+       case 'o':
+         if (outfile)
            {
-             /* Common options. */
-           case 'a':
-             append_to_tagfile++;
-             break;
-           case 'C':
-             cplusplus = 1;
-             break;
-           case 'd':
-             constantypedefs = 1;
-             break;
-           case 'D':
-             constantypedefs = 0;
-             break;
-           case 'o':
-             if (outfflag)
-               {
-                 fprintf (stderr,
-                          "%s: -o flag may only be given once\n", progname);
-                 goto usage;
-               }
-             outfflag++, argc--;
-             argv++;
-             if (argc <= 1 || argv[1][0] == '\0')
-               {
-                 fprintf (stderr,
-                          "%s: -o flag must be followed by a filename\n",
-                          progname);
-                 goto usage;
-               }
-             outfile = argv[1];
-             goto next_arg;
-           case 'S':
-             noindentypedefs++;
-             break;
-           case 't':
-             typedefs++;
-             break;
-           case 'T':
-             typedefs++;
-             typedefs_and_cplusplus++;
-             break;
-
-             /* Etags options */
-           case 'i':
-             if (!emacs_tags_format)
-               goto usage;
-             --argc;
-             ++argv;
-             if (argc <= 1 || argv[1][0] == '\0')
-               {
-                 fprintf (stderr,
-                          "%s: -i flag must be followed by a filename\n",
-                          progname);
-                 goto usage;
-               }
-             included_files[nincluded_files++] = argv[1];
-             goto next_arg;
-
-             /* Ctags options. */
-           case 'B':
-             searchar = '?';
-             if (emacs_tags_format)
-               goto usage;
-             break;
-           case 'F':
-             searchar = '/';
-             if (emacs_tags_format)
-               goto usage;
-             break;
-           case 'u':
-             update++;
-             if (emacs_tags_format)
-               goto usage;
-             break;
-           case 'v':
-             vgrind_style++;
-             /*FALLTHRU*/
-           case 'x':
-             cxref_style++;
-             if (emacs_tags_format)
-               goto usage;
-             break;
-           case 'w':
-             no_warnings++;
-             if (emacs_tags_format)
-               goto usage;
-             break;
-
-           default:
+             fprintf (stderr,
+                      "%s: -o flag may only be given once\n", progname);
              goto usage;
            }
+         outfile = optarg;
+         break;
+       case 'S':
+         noindentypedefs++;
+         break;
+       case 't':
+         typedefs++;
+         break;
+       case 'T':
+         typedefs++;
+         typedefs_and_cplusplus++;
+         break;
+       case 'V':
+         print_version ();
+         break;
+       case 'H':
+         print_help ();
+         break;
+
+         /* Etags options */
+       case 'i':
+         if (!emacs_tags_format)
+           goto usage;
+         included_files[nincluded_files++] = optarg;
+         break;
+
+         /* Ctags options. */
+       case 'B':
+         searchar = '?';
+         if (emacs_tags_format)
+           goto usage;
+         break;
+       case 'F':
+         searchar = '/';
+         if (emacs_tags_format)
+           goto usage;
+         break;
+       case 'u':
+         update++;
+         if (emacs_tags_format)
+           goto usage;
+         break;
+       case 'v':
+         vgrind_style++;
+         /*FALLTHRU*/
+       case 'x':
+         cxref_style++;
+         if (emacs_tags_format)
+           goto usage;
+         break;
+       case 'w':
+         no_warnings++;
+         if (emacs_tags_format)
+           goto usage;
+         break;
+
+       default:
+         goto usage;
        }
-    next_arg:;
     }
 
-  if (argc <= 1)
+  if (optind == argc)
     {
+      fprintf (stderr, "%s: No input files specified.\n", progname);
+
     usage:
-      fprintf (stderr, "Usage:\n");
-#ifndef CTAGS
-      fprintf (stderr, "\tetags [-aDiS] [-o tagsfile] file ...\n");
-#else
-      fprintf (stderr, "\tctags [-aBdeFTStuwvx] [-o tagsfile] file ...\n");
-#endif
+      fprintf (stderr, "%s: Try '%s --help' for a complete list of options.\n",
+              progname, progname);
       exit (BAD);
     }
 
@@ -652,10 +776,10 @@ main (argc, argv)
        }
     }
 
-  file_num = 1;
 #ifdef VMS
-  for (argc--, argv++;
-       (this_file = gfnames (&argc, &argv, &got_err)) != NULL; file_num++)
+  argc -= optind;
+  argv += optind;
+  while (gfnames (&argc, &argv, &got_err) != NULL)
     {
       if (got_err)
        {
@@ -670,9 +794,9 @@ main (argc, argv)
     }                          /* solely to balance out the ifdef'd parens above */
 #endif
 #else
-  for (; file_num < argc; file_num++)
+  for (; optind < argc; optind++)
     {
-      this_file = argv[file_num];
+      this_file = argv[optind];
       if (1)
        {
 #endif
@@ -706,9 +830,11 @@ main (argc, argv)
       put_entries (head);
       exit (GOOD);
     }
-  if (update)                  /* update cannot be set under VMS */
+  if (update)
     {
-      for (i = 1; i < argc; i++)
+      /* update cannot be set under VMS, so we may assume that argc
+        and argv have not been munged.  */
+      for (i = optind; i < argc; i++)
        {
          sprintf (cmd,
                   "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
@@ -744,7 +870,11 @@ process_file (file)
   struct stat stat_buf;
 
   stat (file, &stat_buf);
-  if (!(stat_buf.st_mode & S_IFREG) || !(stat_buf.st_mode & S_IFLNK))
+  if (!(stat_buf.st_mode & S_IFREG)
+#ifdef S_IFLNK
+      || !(stat_buf.st_mode & S_IFLNK)
+#endif
+      )
     {
       fprintf (stderr, "Skipping %s: it is not a regular file.\n", file);
       return;
@@ -757,7 +887,7 @@ process_file (file)
     }
   if (emacs_tags_format)
     {
-      char *cp = rindex (file, '/');
+      char *cp = etags_rindex (file, '/');
       if (cp)
        ++cp;
       else
@@ -828,7 +958,7 @@ find_entries (file)
       return;
     }
   curfile = savestr (file);
-  cp = rindex (file, '.');
+  cp = etags_rindex (file, '.');
 
   header_file = (cp && (streq (cp + 1, "h")));
 
@@ -905,7 +1035,9 @@ find_entries (file)
       goto close_and_return;
     }
   /* if not a .c or .h or .y file, try fortran */
-  else if (cp && ((cp[1] != 'c' && cp[1] != 'h' && cp[1] != 'y')
+  else if (cp && ((cp[1] != 'c'
+                  && cp[1] != 'h'
+                  && cp[1] != 'y')
                  || (cp[1] != 0 && cp[2] != 0)))
     {
       if (PF_funcs (inf) != 0)
@@ -969,9 +1101,9 @@ pfnote (name, is_func, rewritten, linestart, linelen, lno, cno)
   /* If ctags mode, change name "main" to M<thisfilename>. */
   if (!emacs_tags_format && !cxref_style && streq (name, "main"))
     {
-      fp = rindex (curfile, '/');
+      fp = etags_rindex (curfile, '/');
       name = concat ("M", fp == 0 ? curfile : fp + 1, "");
-      fp = rindex (name, '.');
+      fp = etags_rindex (name, '.');
       if (fp && fp[1] != '\0' && fp[2] == '\0')
        *fp = 0;
       rewritten = TRUE;
@@ -1153,7 +1285,7 @@ put_entries (node)
     fprintf (stdout, "%s %s %d\n",
             node->name, node->file, (node->lno + 63) / 64);
   else
-    fprintf (stdout, "%-16s%4d %-16s %s\n",
+    fprintf (stdout, "%-16s %3d %-16s %s\n",
             node->name, node->lno, node->file, node->pat);
 
   /* Output subentries that follow this one */
@@ -1308,6 +1440,8 @@ C_entries (c_ext)
   definedef = dnone;
   gotone = midtoken = inquote = inchar = incomm = FALSE;
   level = 0;
+  tydef = none;
+  next_token_is_func = 0;
 
   C_create_stabs ();
 
@@ -1316,6 +1450,9 @@ C_entries (c_ext)
       c = *lp++;
       if (c == '\\')
        {
+         /* If we're at the end of the line, the next character is a
+            '\0'; don't skip it, because it's the thing that tells us
+            to read the next line.  */
          if (*lp == 0)
            continue;
          lp++;
@@ -1331,13 +1468,10 @@ C_entries (c_ext)
        }
       else if (inquote)
        {
-         /*
-         * Too dumb to know about \" not being magic, but
-         * they usually occur in pairs anyway.
-         */
          if (c == '"')
            inquote = FALSE;
-         continue;
+         else if (c == '\\')
+           c = *lp++;
        }
       else if (inchar)
        {
@@ -1362,7 +1496,8 @@ C_entries (c_ext)
              }
            else if (c_ext && *lp == '/')
              {
-               c = 0;          /* C++ comment: skip rest of line */
+               c = 0;
+               break;
              }
            continue;
          case '#':
@@ -1569,11 +1704,6 @@ consider_token (c, lpp, tokp, is_func, c_ext, level)
      int level;                        /* IN */
 {
   reg char *lp = *lpp;
-  /*
-   * next_token_is_func
-   *   set this to TRUE, and the next token considered is called a function.
-   */
-  static logical next_token_is_func;
   logical firsttok;            /* TRUE if have seen first token in ()'s */
   Stab_entry *tokse = stab_find (get_C_stab (c_ext), tokp->p, tokp->len);
   enum sym_type toktype = stab_type (tokse);
@@ -1758,10 +1888,21 @@ consider_token (c, lpp, tokp, is_func, c_ext, level)
       goto goodone;
     }
   /* Detect GNUmacs's function-defining macros. */
-  if (definedef == dnone && strneq (tokp->p, "DEF", 3))
+  if (definedef == dnone)
     {
-      next_token_is_func = TRUE;
-      goto badone;
+      if (strneq (tokp->p, "DEF", 3)
+         || strneq (tokp->p, "ENTRY", 5)
+         || strneq (tokp->p, "SYSCALL", 7)
+         || strneq (tokp->p, "PSEUDO", 6))
+       {
+         next_token_is_func = TRUE;
+         goto badone;
+       }
+      else if (strneq (tokp->p, "EXFUN", 5))
+       {
+         next_token_is_func = FALSE;
+         goto badone;
+       }
     }
   if (next_token_is_func)
     {
@@ -1891,6 +2032,10 @@ PF_funcs (fi)
          if (tail ("subroutine"))
            getit ();
          continue;
+       case 'e':
+         if (tail ("entry"))
+           getit ();
+         continue;
        case 'p':
          if (tail ("program"))
            {
@@ -1950,7 +2095,10 @@ getit ()
 
   while (isspace (*dbp))
     dbp++;
-  if (*dbp == 0 || (!isalpha (*dbp)) && (*dbp != '_') && (*dbp != '$'))
+  if (*dbp == 0
+      || (!isalpha (*dbp)
+         && *dbp != '_'
+         && *dbp != '$'))
     return;
   for (cp = dbp + 1; *cp && (isalpha (*cp) || isdigit (*cp)
                             || (*cp == '_') || (*cp == '$')); cp++)
@@ -2398,34 +2546,25 @@ TEX_funcs (fi)
     TEX_toktab = TEX_decode_env ("TEXTAGS", TEX_defenv);
 
   while (!feof (fi))
-    {
+    {                  /* Scan each line in file */
       lineno++;
       linecharno = charno;
       charno += readline (&lb, fi);
       dbp = lb.buffer;
       lasthit = dbp;
+      while (dbp = etags_index (dbp, TEX_esc)) /* Look at each escape in line */
+       {
+         register int i;
 
-      while (!feof (fi))
-       {                       /* Scan each line in file */
-         lineno++;
-         linecharno = charno;
-         charno += readline (&lb, fi);
-         dbp = lb.buffer;
+         if (!*(++dbp))
+           break;
+         linecharno += dbp - lasthit;
          lasthit = dbp;
-         while (dbp = index (dbp, TEX_esc))    /* Look at each escape in line */
+         i = TEX_Token (lasthit);
+         if (0 <= i)
            {
-             register int i;
-
-             if (!*(++dbp))
-               break;
-             linecharno += dbp - lasthit;
-             lasthit = dbp;
-             i = TEX_Token (lasthit);
-             if (0 <= i)
-               {
-                 TEX_getit (lasthit, TEX_toktab[i].len);
-                 break;        /* We only save a line once */
-               }
+             TEX_getit (lasthit, TEX_toktab[i].len);
+             break;    /* We only save a line once */
            }
        }
     }
@@ -2478,7 +2617,6 @@ TEX_decode_env (evarname, defenv)
      char *defenv;
 {
   register char *env, *p;
-  extern char *savenstr (), *index ();
 
   struct TEX_tabent *tab;
   int size, i;
@@ -2492,15 +2630,16 @@ TEX_decode_env (evarname, defenv)
 
   /* Allocate a token table */
   for (size = 1, p = env; p;)
-    if ((p = index (p, ':')) && *(++p))
+    if ((p = etags_index (p, ':')) && *(++p))
       size++;
-  tab = xnew (size, struct TEX_tabent);
+  /* Add 1 to leave room for null terminator.  */
+  tab = xnew (size + 1, struct TEX_tabent);
 
   /* Unpack environment string into token table. Be careful about */
   /* zero-length strings (leading ':', "::" and trailing ':') */
   for (i = 0; *env;)
     {
-      p = index (env, ':');
+      p = etags_index (env, ':');
       if (!p)                  /* End of environment string. */
        p = env + strlen (env);
       if (p - env > 0)
@@ -2547,7 +2686,7 @@ TEX_getit (name, len)
 }
 
 /* If the text at CP matches one of the tag-defining TeX command names,
-   return the index of that command in TEX_toktab.
+   return the etags_index of that command in TEX_toktab.
    Otherwise return -1.  */
 
 /* Keep the capital `T' in `Token' for dumb truncating compilers
@@ -2670,7 +2809,7 @@ substr (sub, s)
      char *sub;
      char *s;
 {
-  while (*s && (s = index (s, *sub)))
+  while (*s && (s = etags_index (s, *sub)))
     if (prestr (sub, s))
       return (TRUE);
     else
@@ -2763,7 +2902,6 @@ savenstr (cp, len)
   return dp;
 }
 
-#ifdef notdef
 /*
  * Return the ptr in sp at which the character c last
  * appears; NULL if not found
@@ -2772,7 +2910,7 @@ savenstr (cp, len)
  */
 
 char *
-rindex (sp, c)
+etags_rindex (sp, c)
      register char *sp, c;
 {
   register char *r;
@@ -2786,6 +2924,7 @@ rindex (sp, c)
   return (r);
 }
 
+
 /*
  * Return the ptr in sp at which the character c first
  * appears; NULL if not found
@@ -2794,7 +2933,7 @@ rindex (sp, c)
  */
 
 char *
-index (sp, c)
+etags_index (sp, c)
      register char *sp, c;
 {
   do
@@ -2805,8 +2944,6 @@ index (sp, c)
   return (NULL);
 }
 
-#endif /* notdef */
-
 /* Print error message and exit.  */
 
 /* VARARGS1 */