[project @ 1996-01-08 20:28:12 by partain]
[ghc-hetmet.git] / glafp-utils / etags / etags.c
diff --git a/glafp-utils/etags/etags.c b/glafp-utils/etags/etags.c
new file mode 100644 (file)
index 0000000..b24592d
--- /dev/null
@@ -0,0 +1,1762 @@
+/* Tags file maker to go with GNUmacs
+   Copyright (C) 1984, 1987, 1988 Free Software Foundation, Inc. and Ken Arnold
+
+                          NO WARRANTY
+
+  BECAUSE THIS PROGRAM IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY
+NO WARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT
+WHEN OTHERWISE STATED IN WRITING, FREE SOFTWARE FOUNDATION, INC,
+RICHARD M. STALLMAN AND/OR OTHER PARTIES PROVIDE THIS PROGRAM "AS IS"
+WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
+BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY
+AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE PROGRAM PROVE
+DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+ IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL RICHARD M.
+STALLMAN, THE FREE SOFTWARE FOUNDATION, INC., AND/OR ANY OTHER PARTY
+WHO MAY MODIFY AND REDISTRIBUTE THIS PROGRAM AS PERMITTED BELOW, BE
+LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST MONIES, OR
+OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR
+DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD PARTIES OR
+A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS) THIS
+PROGRAM, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES, OR FOR ANY CLAIM BY ANY OTHER PARTY.
+
+               GENERAL PUBLIC LICENSE TO COPY
+
+  1. You may copy and distribute verbatim copies of this source file
+as you receive it, in any medium, provided that you conspicuously
+and appropriately publish on each copy a valid copyright notice
+"Copyright (C) 1986 Free Software Foundation"; and include
+following the copyright notice a verbatim copy of the above disclaimer
+of warranty and of this License.
+
+  2. You may modify your copy or copies of this source file or
+any portion of it, and copy and distribute such modifications under
+the terms of Paragraph 1 above, provided that you also do the following:
+
+    a) cause the modified files to carry prominent notices stating
+    that you changed the files and the date of any change; and
+
+    b) cause the whole of any work that you distribute or publish,
+    that in whole or in part contains or is a derivative of this
+    program or any part thereof, to be licensed at no charge to all
+    third parties on terms identical to those contained in this
+    License Agreement (except that you may choose to grant more extensive
+    warranty protection to some or all third parties, at your option).
+
+    c) You may charge a distribution fee for the physical act of
+    transferring a copy, and you may at your option offer warranty
+    protection in exchange for a fee.
+
+Mere aggregation of another unrelated program with this program (or its
+derivative) on a volume of a storage or distribution medium does not bring
+the other program under the scope of these terms.
+
+  3. You may copy and distribute this program (or a portion or derivative
+of it, under Paragraph 2) in object code or executable form under the terms
+of Paragraphs 1 and 2 above provided that you also do one of the following:
+
+    a) accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    b) accompany it with a written offer, valid for at least three
+    years, to give any third party free (except for a nominal
+    shipping charge) a complete machine-readable copy of the
+    corresponding source code, to be distributed under the terms of
+    Paragraphs 1 and 2 above; or,
+
+    c) accompany it with the information you received as to where the
+    corresponding source code may be obtained.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form alone.)
+
+For an executable file, complete source code means all the source code for
+all modules it contains; but, as a special exception, it need not include
+source code for modules which are standard libraries that accompany the
+operating system on which the executable file runs.
+
+  4. You may not copy, sublicense, distribute or transfer this program
+except as expressly provided under this License Agreement.  Any attempt
+otherwise to copy, sublicense, distribute or transfer this program is void and
+your rights to use the program under this License agreement shall be
+automatically terminated.  However, parties who have received computer
+software programs from you with this License Agreement will not have
+their licenses terminated so long as such parties remain in full compliance.
+
+In other words, you are welcome to use, share and improve this program.
+You are forbidden to forbid anyone else to use, share and improve
+what you give them.   Help stamp out software-hoarding!  */
+
+#include <stdio.h>
+#include <ctype.h>
+
+/* Define the symbol ETAGS to make the program "etags",
+ which makes emacs-style tag tables by default.
+ Define CTAGS to make the program "ctags" compatible with the usual one.
+ Define neither one to get behavior that depends
+ on the name with which the program is invoked
+ (but we don't normally compile it that way).  */
+
+/* On VMS, CTAGS is not useful, so always do ETAGS.  */
+#ifdef VMS
+#ifndef ETAGS
+#define ETAGS
+#endif
+#endif
+
+/* Exit codes for success and failure.  */
+
+#ifdef VMS
+#define        GOOD    (1)
+#define BAD    (0)
+#else
+#define        GOOD    (0)
+#define        BAD     (1)
+#endif
+
+#define        reg     register
+#define        logical char
+
+#ifndef TRUE
+#define        TRUE    (1)
+#endif
+
+#ifndef FALSE
+#define        FALSE   (0)
+#endif
+
+#define        iswhite(arg)    (_wht[arg])     /* T if char is white           */
+#define        begtoken(arg)   (_btk[arg])     /* T if char can start token    */
+#define        intoken(arg)    (_itk[arg])     /* T if char can be in token    */
+#define        endtoken(arg)   (_etk[arg])     /* T if char ends tokens        */
+#define        isgood(arg)     (_gd[arg])      /* T if char can be after ')'   */
+
+#define        max(I1,I2)      (I1 > I2 ? I1 : I2)
+
+/* cause token checking for typedef, struct, union, enum to distinguish
+   keywords from identifier-prefixes (e.g. struct vs struct_tag).  */
+#define istoken(s, tok, len) (!strncmp(s,tok,len) && endtoken(*((s)+(len))))
+
+struct nd_st {                 /* sorting structure                    */
+       char    *name;                  /* function or type name        */
+       char    *file;                  /* file name                    */
+       logical f;                      /* use pattern or line no       */
+       int     lno;                    /* line number tag is on        */
+       long    cno;                    /* character number line starts on */
+       char    *pat;                   /* search pattern               */
+       logical been_warned;            /* set if noticed dup           */
+       struct  nd_st   *left,*right;   /* left and right sons          */
+};
+
+long   ftell();
+typedef        struct  nd_st   NODE;
+
+int number; /* tokens found so far on line starting with # (including #) */
+logical gotone,                                /* found a func already on line */
+                                       /* boolean "func" (see init)    */
+       _wht[0177],_etk[0177],_itk[0177],_btk[0177],_gd[0177];
+
+       /* typedefs are recognized using a simple finite automata,
+        * tydef is its state variable.
+        */
+typedef enum {none, begin, tag_ok, middle, end } TYST;
+
+TYST tydef = none;
+
+char   searchar = '/';                 /* use /.../ searches           */
+
+int    lineno;                 /* line number of current line */
+long   charno;                 /* current character number */
+long   linecharno;             /* character number of start of line */
+
+char    *curfile,              /* current input file name              */
+       *outfile= 0,            /* output file                          */
+       *white  = " \f\t\n",    /* white chars                          */
+       *endtk  = " \t\n\"'#()[]{}=-+%*/&|^~!<>;,.:?",
+                               /* token ending chars                   */
+       *begtk  = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$",
+                               /* token starting chars                 */
+       *intk   = "ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz$0123456789",
+                               /* valid in-token chars                 */
+       *notgd  = ",;";         /* non-valid after-function chars       */
+
+int    file_num = 0;           /* current file number                  */
+int    aflag = 0;              /* -a: append to tags */
+int    tflag = 0;              /* -t: create tags for typedefs */
+int    uflag = 0;              /* -u: update tags */
+int    wflag = 0;              /* -w: suppress warnings */
+int    vflag = 0;              /* -v: create vgrind style index output */
+int    xflag = 0;              /* -x: create cxref style output */
+int    eflag = 0;              /* -e: emacs style output */
+
+/* Name this program was invoked with.  */
+char *progname;
+
+FILE   *inf,                   /* ioptr for current input file         */
+       *outf;                  /* ioptr for tags file                  */
+
+NODE   *head;                  /* the head of the sorted binary tree   */
+
+char *savestr();
+char *savenstr ();
+char *rindex();
+char *index();
+char *concat ();
+void initbuffer ();
+long readline ();
+
+/* A `struct linebuffer' is a structure which holds a line of text.
+ `readline' reads a line from a stream into a linebuffer
+ and works regardless of the length of the line.  */
+
+struct linebuffer
+  {
+    long size;
+    char *buffer;
+  };
+
+struct linebuffer lb, lb1;
+\f
+#if 0  /* VMS now provides the `system' function.  */
+#ifdef VMS
+
+#include <descrip.h>
+
+void
+system (buf)
+     char *buf;
+{
+  struct dsc$descriptor_s command =
+    {
+      strlen(buf), DSC$K_DTYPE_T, DSC$K_CLASS_S, buf
+    };
+
+  LIB$SPAWN(&command);
+}
+#endif /* VMS */
+#endif /* 0 */
+\f
+main(ac,av)
+     int       ac;
+     char      *av[];
+{
+  char cmd[100];
+  int i;
+  int fflag = 0;
+  char *this_file;
+#ifdef VMS
+  char got_err;
+
+  extern char *gfnames();
+  extern char *massage_name();
+#endif
+
+  progname = av[0];
+
+#ifdef ETAGS
+  eflag = 1;
+#else
+#ifdef CTAGS
+  eflag = 0;
+#else
+  {
+    char *subname = rindex (progname, '/');
+    if (subname++ == NULL)
+      subname = progname;
+    eflag = ! strcmp(subname, "ctags");
+  }
+#endif
+#endif
+
+  while (ac > 1 && av[1][0] == '-')
+    {
+      for (i=1; av[1][i]; i++)
+       {
+         switch(av[1][i])
+           {
+#ifndef VMS  /* These options are useful only with ctags,
+               and VMS can't input them, so just omit them.  */
+           case 'B':
+             searchar='?';
+             eflag = 0;
+             break;
+           case 'F':
+             searchar='/';
+             eflag = 0;
+             break;
+#endif
+           case 'a':
+             aflag++;
+             break;
+           case 'e':
+             eflag++;
+             break;
+           case 'f':
+             if (fflag > 0)
+               {
+                 fprintf(stderr,
+                         "%s: -f flag may only be given once\n", progname);
+                 goto usage;
+               }
+             fflag++, ac--; av++;
+             if (ac <= 1 || av[1][0] == '\0')
+               {
+                 fprintf(stderr,
+                         "%s: -f flag must be followed by a filename\n",
+                         progname);
+                 goto usage;
+               }
+             outfile = av[1];
+             goto end_loop;
+           case 't':
+             tflag++;
+             break;
+#ifndef VMS
+           case 'u':
+             uflag++;
+             eflag = 0;
+             break;
+#endif
+           case 'w':
+             wflag++;
+             break;
+           case 'v':
+             vflag++;
+             xflag++;
+             eflag = 0;
+             break;
+           case 'x':
+             xflag++;
+             eflag = 0;
+             break;
+           default:
+             goto usage;
+           }
+       }
+    end_loop: ;
+      ac--; av++;
+    }
+
+  if (ac <= 1)
+    {
+    usage:
+#ifdef VMS
+      fprintf (stderr, "Usage: %s [-aetwvx] [-f outfile] file ...\n", progname);
+#else
+      fprintf (stderr, "Usage: %s [-BFaetuwvx] [-f outfile] file ...\n", progname);
+#endif
+      exit(BAD);
+    }
+
+  if (outfile == 0)
+    {
+      outfile = eflag ? "TAGS" : "tags";
+    }
+
+  init();                      /* set up boolean "functions"           */
+
+  initbuffer (&lb);
+  initbuffer (&lb1);
+  /*
+   * loop through files finding functions
+   */
+  if (eflag)
+    {
+      outf = fopen (outfile, aflag ? "a" : "w");
+      if (!outf)
+       {
+         fprintf (stderr, "%s: ", progname);
+         perror (outfile);
+         exit (BAD);
+       }
+    }
+
+  file_num = 1;
+#ifdef VMS
+  for (ac--, av++;
+       (this_file = gfnames (&ac, &av, &got_err)) != NULL; file_num++)
+    {
+      if (got_err)
+       {
+         error("Can't find file %s\n", this_file);
+         ac--, av++;
+       }
+      else
+       {
+         this_file = massage_name (this_file);
+#else     
+  for (; file_num < ac; file_num++)
+    {
+      this_file = av[file_num];
+      if (1)
+       {
+#endif
+         find_entries (this_file);
+         if (eflag)
+           {
+             fprintf (outf, "\f\n%s,%d\n",
+                      this_file, total_size_of_entries (head));
+             put_entries (head);
+             free_tree (head);
+             head = NULL;
+           }
+       }
+    }
+
+  if (eflag)
+    {
+      fclose (outf);
+      exit (GOOD);
+    }
+
+  if (xflag)
+    {
+      put_entries(head);
+      exit(GOOD);
+    }
+  if (uflag)
+    {
+      for (i=1; i<ac; i++)
+       {
+         sprintf(cmd,
+                 "mv %s OTAGS;fgrep -v '\t%s\t' OTAGS >%s;rm OTAGS",
+                 outfile, av[i], outfile);
+         system(cmd);
+       }
+      aflag++;
+    }
+  outf = fopen(outfile, aflag ? "a" : "w");
+  if (outf == NULL)
+    {
+      fprintf (stderr, "%s: ", outfile);
+      perror(outfile);
+      exit(BAD);
+    }
+  put_entries(head);
+  fclose(outf);
+#ifndef VMS
+  if (uflag)
+    {
+      sprintf(cmd, "sort %s -o %s", outfile, outfile);
+      system(cmd);
+    }
+#endif
+  exit(GOOD);
+}
+
+/*
+ * This routine sets up the boolean psuedo-functions which work
+ * by seting boolean flags dependent upon the corresponding character
+ * Every char which is NOT in that string is not a white char.  Therefore,
+ * all of the array "_wht" is set to FALSE, and then the elements
+ * subscripted by the chars in "white" are set to TRUE.  Thus "_wht"
+ * of a char is TRUE if it is the string "white", else FALSE.
+ */
+init()
+{
+
+  reg char *sp;
+  reg int i;
+
+  for (i = 0; i < 0177; i++)
+    {
+      _wht[i] = _etk[i] = _itk[i] = _btk[i] = FALSE;
+      _gd[i] = TRUE;
+    }
+  for (sp = white; *sp; sp++)
+    _wht[*sp] = TRUE;
+  for (sp = endtk; *sp; sp++)
+    _etk[*sp] = TRUE;
+  for (sp = intk; *sp; sp++)
+    _itk[*sp] = TRUE;
+  for (sp = begtk; *sp; sp++)
+    _btk[*sp] = TRUE;
+  for (sp = notgd; *sp; sp++)
+    _gd[*sp] = FALSE;
+  _wht[0] = _wht['\n'];
+  _etk[0] = _etk['\n'];
+  _btk[0] = _btk['\n'];
+  _itk[0] = _itk['\n'];
+  _gd[0] = _gd['\n'];
+}
+
+/*
+ * This routine opens the specified file and calls the function
+ * which finds the function and type definitions.
+ */
+find_entries (file)
+     char *file;
+{
+  char *cp;
+
+  if ((inf=fopen(file,"r")) == NULL)
+    {
+      fprintf (stderr, "%s: ", progname);
+      perror(file);
+      return;
+    }
+  curfile = savestr(file);
+  cp = rindex(file, '.');
+  /* .tex, .aux or .bbl implies LaTeX source code */
+  if (cp && (!strcmp (cp + 1, "tex") || !strcmp (cp + 1, "aux")
+            || !strcmp (cp + 1, "bbl")))
+    {
+      TEX_funcs(inf);
+      fclose(inf);
+      return;
+    }
+  /* .l or .el or .lisp (or .cl or .clisp or ...) implies lisp source code */
+  if (cp && (!strcmp (cp + 1, "l") ||
+            !strcmp (cp + 1, "el") ||
+            !strcmp (cp + 1, "lsp") ||
+            !strcmp (cp + 1, "lisp") ||
+            !strcmp (cp + 1, "cl") ||
+            !strcmp (cp + 1, "clisp")))
+    {
+      L_funcs(inf);
+      fclose(inf);
+      return;
+    }
+  /* .scm or .sm or .scheme implies scheme source code */
+  if (cp && (!strcmp (cp + 1, "sm")
+            || !strcmp (cp + 1, "scm")
+            || !strcmp (cp + 1, "scheme")
+            || !strcmp (cp + 1, "t")
+            || !strcmp (cp + 1, "sch")
+            || !strcmp (cp + 1, "SM")
+            || !strcmp (cp + 1, "SCM")
+             /* The `SCM' or `scm' prefix with a version number */
+             || (cp[-1] == 'm' && cp[-2] == 'c' && cp[-3] == 's')
+             || (cp[-1] == 'M' && cp[-2] == 'C' && cp[-3] == 'S')))
+    {
+      Scheme_funcs(inf);
+      fclose(inf);
+      return;
+    }
+  /* .M implies Mcode source code */
+  if (cp && !strcmp (cp + 1, "M"))
+    {
+      Mcode_funcs(inf);
+      fclose(inf);
+      return;
+    }
+
+  /* if not a .c or .h or .y file, try fortran */
+  if (cp && (cp[1] != 'c' && cp[1] != 'h' && cp[1] != 'y')
+      && cp[2] == '\0')
+    {
+      if (PF_funcs(inf) != 0)
+       {
+         fclose(inf);
+         return;
+       }
+      rewind(inf);     /* no fortran tags found, try C */
+    }
+  C_entries();
+  fclose(inf);
+}
+\f
+/* Record a tag on the current line.
+  name is the tag name,
+  f is nonzero to use a pattern, zero to use line number instead. */
+
+pfnote (name, f, linestart, linelen, lno, cno)
+     char *name;
+     logical f;                        /* f == TRUE when function */
+     char *linestart;
+     int linelen;
+     int lno;
+     long cno;
+{
+  register char *fp;
+  register NODE *np;
+  char *altname;
+  char tem[51];
+
+  if ((np = (NODE *) malloc (sizeof (NODE))) == NULL)
+    {
+      fprintf(stderr, "%s: too many entries to sort\n", progname);
+      put_entries(head);
+      free_tree(head);
+      head = NULL;
+      np = (NODE *) xmalloc(sizeof (NODE));
+    }
+  /* Change name "main" to M<thisfilename>. */
+  if (!eflag && !xflag && !strcmp(name, "main"))
+    {
+      fp = rindex(curfile, '/');
+      if (fp == 0)
+       fp = curfile;
+      else
+       fp++;
+      altname = concat ("M", fp, "");
+      fp = rindex(altname, '.');
+      if (fp && fp[2] == 0)
+       *fp = 0;
+      name = altname;
+    }
+  np->name = savestr(name);
+  np->file = curfile;
+  np->f = f;
+  np->lno = lno;
+  np->cno = cno;
+  np->left = np->right = 0;
+  if (eflag)
+    {
+      linestart[linelen] = 0;
+    }
+  else if (xflag == 0)
+    {
+      sprintf (tem, strlen (linestart) < 50 ? "%s$" : "%.50s", linestart);
+      linestart = tem;
+    }
+  np->pat = savestr (linestart);
+  if (head == NULL)
+    head = np;
+  else
+    add_node(np, head);
+}
+
+free_tree(node)
+     NODE *node;
+{
+  while (node)
+    {
+      free_tree(node->right);
+      free(node);
+      node = node->left;
+    }
+}
+
+add_node(node, cur_node)
+     NODE *node,*cur_node;
+{
+  register int dif;
+
+  dif = strcmp(node->name, cur_node->name);
+
+  /* If this tag name matches an existing one, then
+     unless -e was given, do not add the node, but maybe print a warning */
+  if (!eflag && !dif)
+    {
+      if (node->file == cur_node->file)
+       {
+         if (!wflag)
+           {
+             fprintf(stderr,"%s: Duplicate entry in file %s, line %d: %s\n",
+                     progname, node->file,lineno,node->name);
+             fprintf(stderr,"Second entry ignored\n");
+           }
+         return;
+       }
+      if (!cur_node->been_warned)
+       if (!wflag)
+         fprintf(stderr,"%s: Duplicate entry in files %s and %s: %s (Warning only)\n",
+                 progname, node->file, cur_node->file, node->name);
+      cur_node->been_warned = TRUE;
+      return;
+    } 
+
+  /* Actually add the node */
+  if (dif < 0) 
+    {
+      if (cur_node->left != NULL)
+       add_node(node,cur_node->left);
+      else
+       cur_node->left = node;
+      return;
+    }
+  if (cur_node->right != NULL)
+    add_node(node,cur_node->right);
+  else
+    cur_node->right = node;
+}
+\f
+put_entries(node)
+     reg NODE *node;
+{
+  reg char *sp;
+
+  if (node == NULL)
+    return;
+
+  /* Output subentries that precede this one */
+  put_entries (node->left);
+
+  /* Output this entry */
+
+  if (eflag)
+    {
+      fprintf (outf, "%s%c%d,%d\n",
+              node->pat, 0177, node->lno, node->cno);
+    }
+  else if (!xflag)
+    {
+      fprintf (outf, "%s\t%s\t",
+              node->name, node->file);
+
+      if (node->f)
+       {               /* a function */
+         putc (searchar, outf);
+         putc ('^', outf);
+
+         for (sp = node->pat; *sp; sp++)
+           {
+             if (*sp == '\\' || *sp == searchar)
+               putc ('\\', outf);
+             putc (*sp, outf);
+           }
+         putc (searchar, outf);
+       }
+      else
+       {               /* a typedef; text pattern inadequate */
+         fprintf (outf, "%d", node->lno);
+       }
+      putc ('\n', outf);
+    }
+  else if (vflag)
+    fprintf (stdout, "%s %s %d\n",
+            node->name, node->file, (node->lno+63)/64);
+  else
+    fprintf (stdout, "%-16s%4d %-16s %s\n",
+            node->name, node->lno, node->file, node->pat);
+
+  /* Output subentries that follow this one */
+  put_entries (node->right);
+}
+
+/* Return total number of characters that put_entries will output for
+ the nodes in the subtree of the specified node.
+ Works only if eflag is set, but called only in that case.  */
+
+total_size_of_entries(node)
+     reg NODE *node;
+{
+  reg int total = 0;
+  reg long num;
+
+  if (node == NULL)
+    return 0;
+
+  /* Count subentries that precede this one */
+  total = total_size_of_entries (node->left);
+
+  /* Count subentries that follow this one */
+  total += total_size_of_entries (node->right);
+
+  /* Count this entry */
+
+  total += strlen (node->pat) + 3;
+
+  num = node->lno;
+  while (num)
+    {
+      total++;
+      num /= 10;
+    }
+
+  num = node->cno;
+  if (!num) total++;
+  while (num)
+    {
+      total++;
+      num /= 10;
+    }
+  return total;
+}
+\f
+/*
+ * This routine finds functions and typedefs in C syntax and adds them
+ * to the list.
+ */
+#ifdef VMS
+long vmslinecharno;
+#define VMS_SET_LINECHARNO     (vmslinecharno = ftell(inf))
+#else
+#define VMS_SET_LINECHARNO
+#endif
+
+#define CNL_SAVE_NUMBER \
+{ \
+  VMS_SET_LINECHARNO; \
+  linecharno = charno; lineno++; \
+  charno += 1 + readline (&lb, inf); \
+  lp = lb.buffer; \
+}
+
+#define CNL \
+{ \
+  CNL_SAVE_NUMBER; \
+  number = 0; \
+}
+
+C_entries ()
+{
+  register int c;
+  register char *token, *tp, *lp;
+  logical incomm, inquote, inchar, midtoken;
+  int level;
+  char tok[BUFSIZ];
+
+  lineno = 0;
+  charno = 0;
+  lp = lb.buffer;
+  *lp = 0;
+
+  number = 0;
+  gotone = midtoken = inquote = inchar = incomm = FALSE;
+  level = 0;
+
+  while (!feof (inf))
+    {
+      c = *lp++;
+      if (c == 0)
+       {
+         CNL;
+         gotone = FALSE;
+       }
+      if (c == '\\')
+       {
+         c = *lp++;
+         if (c == 0)
+           CNL_SAVE_NUMBER;
+         c = ' ';
+       } 
+      else if (incomm)
+       {
+         if (c == '*')
+           {
+             while ((c = *lp++) == '*')
+               continue;
+             if (c == 0)
+               CNL;
+             if (c == '/')
+               incomm = FALSE;
+           }
+       }
+      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 (inchar)
+       {
+         if (c == '\'')
+           inchar = FALSE;
+         continue;
+       }
+      else switch (c)
+       {
+       case '"':
+         inquote = TRUE;
+         continue;
+       case '\'':
+         inchar = TRUE;
+         continue;
+       case '/':
+         if (*lp == '*')
+           {
+             lp++;
+             incomm = TRUE;
+           }
+         continue;
+       case '#':
+         if (lp == lb.buffer + 1)
+           number = 1;
+         continue;
+       case '{':
+         if (tydef == tag_ok)
+           {
+             tydef=middle;
+           }
+         level++;
+         continue;
+       case '}':
+         if (lp == lb.buffer + 1)
+           level = 0;  /* reset */
+         else
+           level--;
+         if (!level && tydef==middle)
+           {
+             tydef=end;
+           }
+         continue;
+       }
+      if (!level && !inquote && !incomm && gotone == FALSE)
+       {
+         if (midtoken)
+           {
+             if (endtoken(c))
+               {
+                 int f;
+                 char *buf = lb.buffer;
+                 int endpos = lp - lb.buffer;
+                 char *lp1 = lp;
+                 int line = lineno;
+                 long linestart = linecharno;
+#ifdef VMS
+                 long vmslinestart = vmslinecharno;
+#endif
+                 int tem = consider_token (&lp1, token, &f, level);
+                 lp = lp1;
+                 if (tem)
+                   {
+                     if (linestart != linecharno)
+                       {
+#ifdef VMS
+                         getline (vmslinestart);
+#else
+                         getline (linestart);
+#endif
+                         strncpy (tok, token + (lb1.buffer - buf),
+                                  tp-token+1);
+                         tok[tp-token+1] = 0;
+                         pfnote(tok, f, lb1.buffer, endpos, line, linestart);
+                       }
+                     else
+                       {
+                         strncpy (tok, token, tp-token+1);
+                         tok[tp-token+1] = 0;
+                         pfnote(tok, f, lb.buffer, endpos, line, linestart);
+                       }
+                     gotone = f;       /* function */
+                   }
+                 midtoken = FALSE;
+                 token = lp - 1;
+               }
+             else if (intoken(c))
+               tp++;
+           }
+         else if (begtoken(c))
+           {
+             token = tp = lp - 1;
+             midtoken = TRUE;
+           }
+       }
+      if (c == ';'  &&  tydef==end)    /* clean with typedefs */
+       tydef=none;
+    }
+}
+
+/*
+ * This routine  checks to see if the current token is
+ * at the start of a function, or corresponds to a typedef
+ * It updates the input line * so that the '(' will be
+ * in it when it returns.
+ */
+consider_token (lpp, token, f, level)
+     char **lpp, *token;
+     int *f, level;
+{
+  reg char *lp = *lpp;
+  reg char c;
+  static logical next_token_is_func;
+  logical firsttok;    /* T if have seen first token in ()'s */
+  int bad, win;
+
+  *f = 1;                      /* a function */
+  c = lp[-1];
+  bad = FALSE;
+  if (!number)
+    {          /* space is not allowed in macro defs   */
+      while (iswhite(c))
+       {
+         c = *lp++;
+         if (c == 0)
+           {
+             if (feof (inf))
+               break;
+             CNL;
+           }
+       }
+    }
+  else
+    {
+      /* the following tries to make it so that a #define a b(c)       */
+      /* doesn't count as a define of b.                               */
+
+      number++;
+      if (number >= 4  || (number==2 && strncmp (token, "define", 6)==0))
+       {
+         /* Force the next symbol to be recognised, even if it is #define a b(c)! */
+         if(number == 2) next_token_is_func = 1;
+         else
+           gotone = TRUE;
+       badone:
+         bad = TRUE;
+         goto ret;
+       }
+    }
+  /* check for the typedef cases               */
+  if (tflag && istoken(token, "typedef", 7))
+    {
+      tydef=begin;
+      goto badone;
+    }
+  if (tydef==begin && (istoken(token, "struct", 6) ||
+                      istoken(token, "union", 5) || istoken(token, "enum", 4)))
+  {
+    tydef=tag_ok;      
+    goto badone;
+  }
+  if (tydef==tag_ok)
+    {
+      tydef=middle;
+      goto badone;
+    }
+  if (tydef==begin)            /* e.g. typedef ->int<- */
+    {
+      tydef=end;
+      goto badone;
+    }
+  if (tydef==middle && level == 0) /* e.g. typedef struct tag ->struct_t<- */
+    {
+      tydef=end;
+    }
+  if (tydef==end)
+    {
+      *f = 0;
+      win = 1;
+      goto ret;
+    }
+  /* Detect GNUmacs's function-defining macros. */
+  if (!number && !strncmp (token, "DEF", 3))
+        
+    {
+      next_token_is_func = 1;
+      goto badone;
+    }
+  if (next_token_is_func)
+    {
+      next_token_is_func = 0;
+      win = 1;
+      goto ret;
+    }
+  if (c != '(')
+    goto badone;
+  firsttok = FALSE;
+  while ((c = *lp++) != ')')
+    {
+      if (c == 0)
+       {
+         if (feof (inf))
+           break;
+         CNL;
+       }
+      /*
+       * This line used to confuse ctags:
+       *       int     (*oldhup)();
+       * This fixes it. A nonwhite char before the first
+       * token, other than a / (in case of a comment in there)
+       * makes this not a declaration.
+       */
+      if (begtoken(c) || c=='/') firsttok++;
+      else if (!iswhite(c) && !firsttok) goto badone;
+    }
+  while (iswhite (c = *lp++))
+    {
+      if (c == 0)
+       {
+         if (feof (inf))
+           break;
+         CNL;
+       }
+    }
+  win = isgood (c);
+ret:
+  *lpp = lp - 1;
+  return !bad && win;
+}
+
+getline (atchar)
+     long atchar;
+{
+  long saveftell = ftell (inf);
+
+  fseek (inf, atchar, 0);
+  readline (&lb1, inf);
+  fseek (inf, saveftell, 0);
+}
+\f
+/* Fortran parsing */
+
+char   *dbp;
+int    pfcnt;
+
+PF_funcs(fi)
+     FILE *fi;
+{
+  lineno = 0;
+  charno = 0;
+  pfcnt = 0;
+
+  while (!feof (fi))
+    {
+      lineno++;
+      linecharno = charno;
+      charno += readline (&lb, fi) + 1;
+      dbp = lb.buffer;
+      if (*dbp == '%') dbp++ ; /* Ratfor escape to fortran */
+      while (isspace(*dbp))
+       dbp++;
+      if (*dbp == 0)
+       continue;
+      switch (*dbp |' ')
+       {
+       case 'i':
+         if (tail("integer"))
+           takeprec();
+         break;
+       case 'r':
+         if (tail("real"))
+           takeprec();
+         break;
+       case 'l':
+         if (tail("logical"))
+           takeprec();
+         break;
+       case 'c':
+         if (tail("complex") || tail("character"))
+           takeprec();
+         break;
+       case 'd':
+         if (tail("double"))
+           {
+             while (isspace(*dbp))
+               dbp++;
+             if (*dbp == 0)
+               continue;
+             if (tail("precision"))
+               break;
+             continue;
+           }
+         break;
+       }
+      while (isspace(*dbp))
+       dbp++;
+      if (*dbp == 0)
+       continue;
+      switch (*dbp|' ')
+       {
+       case 'f':
+         if (tail("function"))
+           getit();
+         continue;
+       case 's':
+         if (tail("subroutine"))
+           getit();
+         continue;
+       case 'p':
+         if (tail("program"))
+           {
+             getit();
+             continue;
+           }
+         if (tail("procedure"))
+           getit();
+         continue;
+       }
+    }
+  return (pfcnt);
+}
+
+tail(cp)
+     char *cp;
+{
+  register int len = 0;
+
+  while (*cp && (*cp&~' ') == ((*(dbp+len))&~' '))
+    cp++, len++;
+  if (*cp == 0)
+    {
+      dbp += len;
+      return (1);
+    }
+  return (0);
+}
+
+takeprec()
+{
+  while (isspace(*dbp))
+    dbp++;
+  if (*dbp != '*')
+    return;
+  dbp++;
+  while (isspace(*dbp))
+    dbp++;
+  if (!isdigit(*dbp))
+    {
+      --dbp;           /* force failure */
+      return;
+    }
+  do
+    dbp++;
+  while (isdigit(*dbp));
+}
+
+getit()
+{
+  register char *cp;
+  char c;
+  char nambuf[BUFSIZ];
+
+  while (isspace(*dbp))
+    dbp++;
+  if (*dbp == 0 || !isalpha(*dbp))
+    return;
+  for (cp = dbp+1; *cp && (isalpha(*cp) || isdigit(*cp)); cp++)
+    continue;
+  c = cp[0];
+  cp[0] = 0;
+  strcpy(nambuf, dbp);
+  cp[0] = c;
+  pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+  pfcnt++;
+}
+\f
+/*
+ * lisp tag functions
+ * just look for (def or (DEF
+ */
+
+L_funcs (fi)
+     FILE *fi;
+{
+  lineno = 0;
+  charno = 0;
+  pfcnt = 0;
+
+  while (!feof (fi))
+    {
+      lineno++;
+      linecharno = charno;
+      charno += readline (&lb, fi) + 1;
+      dbp = lb.buffer;
+      if (dbp[0] == '(' && 
+         (dbp[1] == 'D' || dbp[1] == 'd') &&
+           (dbp[2] == 'E' || dbp[2] == 'e') &&
+             (dbp[3] == 'F' || dbp[3] == 'f'))
+       {
+         while (!isspace(*dbp)) dbp++;
+         while (isspace(*dbp)) dbp++;
+         L_getit();
+       }
+    }
+}
+
+L_getit()
+{
+  register char *cp;
+  char c;
+  char nambuf[BUFSIZ];
+
+  if (*dbp == 0) return;
+  for (cp = dbp+1; *cp && *cp != '(' && *cp != ' '; cp++)
+    continue;
+  c = cp[0];
+  cp[0] = 0;
+  strcpy(nambuf, dbp);
+  cp[0] = c;
+  pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+  pfcnt++;
+}
+\f
+/*
+ * Scheme tag functions
+ * look for (def... xyzzy
+ * look for (def... (xyzzy
+ * look for (def ... ((...(xyzzy ....
+ * look for (set! xyzzy
+ */
+
+static get_scheme ();
+Scheme_funcs (fi)
+     FILE *fi;
+{
+  lineno = 0;
+  charno = 0;
+  pfcnt = 0;
+
+  while (!feof (fi))
+    {
+      lineno++;
+      linecharno = charno;
+      charno += readline (&lb, fi) + 1;
+      dbp = lb.buffer;
+      if (dbp[0] == '(' && 
+         (dbp[1] == 'D' || dbp[1] == 'd') &&
+           (dbp[2] == 'E' || dbp[2] == 'e') &&
+             (dbp[3] == 'F' || dbp[3] == 'f'))
+       {
+         while (!isspace(*dbp)) dbp++;
+          /* Skip over open parens and white space */
+          while (*dbp && (isspace(*dbp) || *dbp == '(')) dbp++;
+         get_scheme ();
+       }
+      if (dbp[0] == '(' && 
+         (dbp[1] == 'S' || dbp[1] == 's') &&
+           (dbp[2] == 'E' || dbp[2] == 'e') &&
+             (dbp[3] == 'T' || dbp[3] == 't') &&
+                (dbp[4] == '!' || dbp[4] == '!') &&
+                  (isspace(dbp[5])))
+       {
+         while (!isspace(*dbp)) dbp++;
+          /* Skip over white space */
+          while (isspace(*dbp)) dbp++;
+         get_scheme ();
+       }
+    }
+}
+
+static
+get_scheme()
+{
+  register char *cp;
+  char c;
+  char nambuf[BUFSIZ];
+
+  if (*dbp == 0) return;
+  /* Go till you get to white space or a syntactic break */
+  for (cp = dbp+1; *cp && *cp != '(' && *cp != ')' && !isspace(*cp); cp++)
+    continue;
+  /* Null terminate the string there. */
+  c = cp[0];
+  cp[0] = 0;
+  /* Copy the string */
+  strcpy(nambuf, dbp);
+  /* Unterminate the string */
+  cp[0] = c;
+  /* Announce the change */
+  pfnote(nambuf, TRUE, lb.buffer, cp - lb.buffer + 1, lineno, linecharno);
+  pfcnt++;
+}
+static get_mcode ();
+Mcode_funcs (fi)
+     FILE *fi;
+{
+  lineno = 0;
+  charno = 0;
+  pfcnt = 0;
+
+  while (!feof (fi))
+    {
+      lineno++;
+      linecharno = charno;
+      charno += readline (&lb, fi) + 1;
+      dbp = lb.buffer;
+
+      while (*dbp != 0)
+       {
+         /* Skip over white space */
+         while (isspace(*dbp)) dbp++;
+         get_mcode();
+       }
+    }
+}
+
+static
+get_mcode()
+{
+  register char *cp;
+  char c;
+  char nambuf[BUFSIZ];
+
+  if (*dbp == 0) return;
+  /* Go till you get to white space or a syntactic break */
+  for (cp = dbp; *cp && *cp != ':' && *cp != ';' && !isspace(*cp); cp++)
+    continue;
+
+  if(*cp == ':')
+    {
+      /* Null terminate the string there. */
+      c = cp[0];
+      cp[0] = 0;
+      /* Copy the string */
+      strcpy(nambuf, dbp);
+      /* Unterminate the string */
+      cp[0] = c;
+      /* Announce the change */
+      pfnote(nambuf, TRUE, lb.buffer, strlen(lb.buffer), lineno, linecharno);
+      pfcnt++;
+      *dbp = 0;
+    }
+  if (*cp == ';')
+    *dbp = 0;
+  else
+    dbp = cp;
+}
+\f
+/* 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.  */
+
+struct TEX_tabent
+{
+  char *name;
+  int len;
+};
+
+struct TEX_tabent *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:typeout";
+
+struct TEX_tabent *TEX_decode_env (); 
+
+static char TEX_esc = '\\';
+static char TEX_opgrp = '{';
+static char TEX_clgrp = '}';
+
+/*
+ * TeX/LaTeX scanning loop.
+ */
+
+TEX_funcs (fi)
+    FILE *fi;
+{
+  char *lasthit;
+
+  lineno = 0;
+  charno = 0;
+  pfcnt = 0;
+
+  /* Select either \ or ! as escape character.  */
+  TEX_mode (fi);
+
+  /* Initialize token table once from environment. */
+  if (!TEX_toktab)
+    TEX_toktab = TEX_decode_env ("TEXTAGS", TEX_defenv);
+
+  while (!feof (fi))
+    {
+      lineno++;
+      linecharno = charno;
+      charno += readline (&lb, fi) + 1;
+      dbp = lb.buffer;
+      lasthit = dbp;
+
+      while (!feof (fi))
+       {       /* Scan each line in file */
+         lineno++;
+         linecharno = charno;
+         charno += readline (&lb, fi) + 1;
+         dbp = lb.buffer;
+         lasthit = dbp;
+         while (dbp = index (dbp, TEX_esc)) /* Look at each escape in line */
+           {
+             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 */
+               }
+           }
+       }
+    }
+}
+
+#define TEX_LESC '\\'
+#define TEX_SESC '!'
+
+/* Figure out whether TeX's escapechar is '\\' or '!' and set grouping */
+/* chars accordingly. */
+
+TEX_mode (f)
+     FILE *f;
+{
+  int c;
+
+  while ((c = getc (f)) != EOF)
+    if (c == TEX_LESC || c == TEX_SESC)
+      break;
+
+  if (c == TEX_LESC)
+    {
+      TEX_esc = TEX_LESC;
+      TEX_opgrp = '{';
+      TEX_clgrp = '}';
+    } 
+  else
+    {
+      TEX_esc = TEX_SESC;
+      TEX_opgrp = '<';
+      TEX_clgrp = '>';
+    }
+  rewind (f);
+}
+
+/* Read environment and prepend it to the default string. */
+/* Build token table. */
+
+struct TEX_tabent *
+TEX_decode_env (evarname, defenv)
+     char *evarname;
+     char *defenv;
+{
+  register char *env, *p;
+  extern char *savenstr (), *index ();
+
+  struct TEX_tabent *tab;
+  int size, i;
+
+  /* Append deafult string to environment. */
+  env = (char *) getenv (evarname);
+  if (!env)
+    env = defenv;
+  else
+    env = concat (env, defenv, "");
+
+  /* Allocate a token table */
+  for (size = 1, p=env; p;)
+    if ((p = index (p, ':')) && *(++p))
+      size++;
+  tab = (struct TEX_tabent *) xmalloc (size * sizeof (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, ':');
+      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);
+         i++;
+       }
+      if (*p)
+       env = p + 1;
+      else
+       {
+         tab[i].name = NULL;   /* Mark end of table. */
+         tab[i].len = 0;
+         break;
+       }
+    }
+  return tab;
+}
+
+/* Record a tag defined by a TeX command of length LEN and starting at NAME.
+   The name being defined actually starts at (NAME + LEN + 1).
+   But we seem to include the TeX command in the tag name.  */
+
+TEX_getit (name, len)
+    char *name;
+    int len;
+{
+  char *p = name + len;
+  char nambuf[BUFSIZ];
+
+  if (*name == 0) return;
+
+  /* Let tag name extend to next group close (or end of line) */
+  while (*p && *p != TEX_clgrp)
+    p++;
+  strncpy (nambuf, name, p - name);
+  nambuf[p - name] = 0;
+
+  pfnote (nambuf, TRUE, lb.buffer, strlen (lb.buffer), lineno, linecharno);
+  pfcnt++;
+}
+
+/* If the text at CP matches one of the tag-defining TeX command names,
+   return the index 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' */
+TEX_Token (cp)
+    char *cp;
+{
+  int i;
+
+  for (i = 0; TEX_toktab[i].len > 0; i++)
+    if (strncmp (TEX_toktab[i].name, cp, TEX_toktab[i].len) == 0)
+      return i;
+  return -1;
+}
+\f
+/* Initialize a linebuffer for use */
+
+void
+initbuffer (linebuffer)
+     struct linebuffer *linebuffer;
+{
+  linebuffer->size = 200;
+  linebuffer->buffer = (char *) xmalloc (200);
+}
+
+/* Read a line of text from `stream' into `linebuffer'.
+ Return the length of the line.  */
+
+long
+readline (linebuffer, stream)
+     struct linebuffer *linebuffer;
+     register FILE *stream;
+{
+  char *buffer = linebuffer->buffer;
+  register char *p = linebuffer->buffer;
+  register char *pend = p + linebuffer->size;
+
+  while (1)
+    {
+      int c = getc (stream);
+      if (p == pend)
+       {
+         linebuffer->size *= 2;
+         buffer = (char *) xrealloc (buffer, linebuffer->size);
+         p += buffer - linebuffer->buffer;
+         pend = buffer + linebuffer->size;
+         linebuffer->buffer = buffer;
+       }
+      if (c < 0 || c == '\n')
+       {
+         *p = 0;
+         break;
+       }
+      *p++ = c;
+    }
+
+  return p - buffer;
+}
+\f
+char *
+savestr(cp)
+     char *cp;
+{
+  return savenstr (cp, strlen (cp));
+}
+
+char *
+savenstr(cp, len)
+    char *cp;
+    int len;
+{
+  register char *dp;
+
+  dp = (char *) xmalloc (len + 1);
+  strncpy (dp, cp, len);
+  dp[len] = '\0';
+  return dp;
+}
+
+/*
+ * Return the ptr in sp at which the character c last
+ * appears; NULL if not found
+ *
+ * Identical to v7 rindex, included for portability.
+ */
+
+char *
+rindex(sp, c)
+     register char *sp, c;
+{
+  register char *r;
+
+  r = NULL;
+  do
+    {
+      if (*sp == c)
+       r = sp;
+    } while (*sp++);
+  return(r);
+}
+
+/*
+ * Return the ptr in sp at which the character c first
+ * appears; NULL if not found
+ *
+ * Identical to v7 index, included for portability.
+ */
+
+char *
+index(sp, c)
+     register char *sp, c;
+{
+  do
+    {
+      if (*sp == c)
+       return (sp);
+    } while (*sp++);
+  return (NULL);
+}
+
+/* Print error message and exit.  */
+
+fatal (s1, s2)
+     char *s1, *s2;
+{
+  error (s1, s2);
+  exit (BAD);
+}
+
+/* Print error message.  `s1' is printf control string, `s2' is arg for it. */
+
+error (s1, s2)
+     char *s1, *s2;
+{
+  fprintf (stderr, "%s: ", progname);
+  fprintf (stderr, s1, s2);
+  fprintf (stderr, "\n");
+}
+
+/* Return a newly-allocated string whose contents concatenate those of s1, s2, s3.  */
+
+char *
+concat (s1, s2, s3)
+     char *s1, *s2, *s3;
+{
+  int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
+  char *result = (char *) xmalloc (len1 + len2 + len3 + 1);
+
+  strcpy (result, s1);
+  strcpy (result + len1, s2);
+  strcpy (result + len1 + len2, s3);
+  *(result + len1 + len2 + len3) = 0;
+
+  return result;
+}
+
+/* Like malloc but get fatal error if memory is exhausted.  */
+
+int
+xmalloc (size)
+     int size;
+{
+  int result = malloc (size);
+  if (!result)
+    fatal ("virtual memory exhausted", 0);
+  return result;
+}
+
+int
+xrealloc (ptr, size)
+     char *ptr;
+     int size;
+{
+  int result = realloc (ptr, size);
+  if (!result)
+    fatal ("virtual memory exhausted");
+  return result;
+}