changeset 24:2354a499c504

Initial revision
author Richard M. Stallman <rms@gnu.org>
date Mon, 08 May 1989 21:10:27 +0000
parents b437d0778a66
children b446124cdf5a
files lib-src/make-docfile.c
diffstat 1 files changed, 608 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lib-src/make-docfile.c	Mon May 08 21:10:27 1989 +0000
@@ -0,0 +1,608 @@
+/* Generate doc-string file for GNU Emacs from source files.
+   Copyright (C) 1985, 1986 Free Software Foundation, Inc.
+
+This file is part of GNU Emacs.
+
+GNU Emacs is distributed in the hope that it will be useful,
+but without any warranty.  No author or distributor
+accepts responsibility to anyone for the consequences of using it
+or for whether it serves any particular purpose or works at all,
+unless he says so in writing.
+
+Everyone is granted permission to copy, modify and redistribute
+GNU Emacs, but only under the conditions described in the
+document "GNU Emacs copying permission notice".   An exact copy
+of the document is supposed to have been given to you along with
+GNU Emacs so that you can know how you may redistribute it all.
+It should be in a file named COPYING.  Among other things, the
+copyright notice and this notice must be preserved on all copies.  */
+
+/* The arguments given to this program are all the C and Lisp source files
+ of GNU Emacs.  .elc and .el and .c files are allowed.
+ A .o file can also be specified; the .c file it was made from is used.
+ This helps the makefile pass the correct list of files.
+
+ The results, which go to standard output or to a file
+ specified with -a or -o (-a to append, -o to start from nothing),
+ are entries containing function or variable names and their documentation.
+ Each entry starts with a ^_ character.
+ Then comes F for a function or V for a variable.
+ Then comes the function or variable name, terminated with a newline.
+ Then comes the documentation for that function or variable.
+ */
+
+#include <stdio.h>
+
+FILE *outfile;
+
+main (argc, argv)
+     int argc;
+     char **argv;
+{
+  int i;
+  int err_count = 0;
+
+  outfile = stdout;
+
+  /* If first two args are -o FILE, output to FILE.  */
+  i = 1;
+  if (argc > i + 1 && !strcmp (argv[i], "-o"))
+    {
+      outfile = fopen (argv[i + 1], "w");
+      i += 2;
+    }
+  if (argc > i + 1 && !strcmp (argv[i], "-a"))
+    {
+      outfile = fopen (argv[i + 1], "a");
+      i += 2;
+    }
+
+  for (; i < argc; i++)
+    err_count += scan_file (argv[i]);	/* err_count seems to be {mis,un}used */
+#ifndef VMS
+  exit (err_count);			/* see below - shane */
+#endif VMS
+}
+
+/* Read file FILENAME and output its doc strings to stdout.  */
+/* Return 1 if file is not found, 0 if it is found.  */
+
+scan_file (filename)
+     char *filename;
+{
+  int len = strlen (filename);
+  if (!strcmp (filename + len - 4, ".elc"))
+    return scan_lisp_file (filename);
+  else if (!strcmp (filename + len - 3, ".el"))
+    return scan_lisp_file (filename);
+  else
+    return scan_c_file (filename);
+}
+
+char buf[128];
+
+/* Skip a C string from INFILE,
+ and return the character that follows the closing ".
+ If printflag is positive, output string contents to stdout.
+ If it is negative, store contents in buf.
+ Convert escape sequences \n and \t to newline and tab;
+ discard \ followed by newline.  */
+
+read_c_string (infile, printflag)
+     FILE *infile;
+     int printflag;
+{
+  register int c;
+  char *p = buf;
+
+  c = getc (infile);
+  while (c != EOF)
+    {
+      while (c != '"' && c != EOF)
+	{
+	  if (c == '\\')
+	    {
+	      c = getc (infile);
+	      if (c == '\n')
+		{
+		  c = getc (infile);
+		  continue;
+		}
+	      if (c == 'n')
+		c = '\n';
+	      if (c == 't')
+		c = '\t';
+	    }
+	  if (printflag > 0)
+	    putc (c, outfile);
+	  else if (printflag < 0)
+	    *p++ = c;
+	  c = getc (infile);
+	}
+      c = getc (infile);
+      if (c != '"')
+	break;
+      if (printflag > 0)
+	putc (c, outfile);
+      else if (printflag < 0)
+	*p++ = c;
+      c = getc (infile);
+    }
+
+  if (printflag < 0)
+    *p = 0;
+
+  return c;
+}
+
+/* Write to file OUT the argument names of the function whose text is in BUF.
+   MINARGS and MAXARGS are the minimum and maximum number of arguments.  */
+
+write_c_args (out, buf, minargs, maxargs)
+     FILE *out;
+     char *buf;
+     int minargs, maxargs;
+{
+  register int c;
+  register char *p = buf;
+  int space = 0;
+
+  fprintf (out, "arguments:");
+
+  while (*p)
+    {
+      c = *p++;
+      if (c == ',')
+	{
+	  minargs--;
+	  maxargs--;
+	  if (!space)
+	    putc (' ', out);
+	  if (minargs == 0 && maxargs > 0)
+	    fprintf (out, "&optional ");
+	  space = 1;
+	  continue;
+	}
+      else if (c == ' ' && space)
+	continue;
+      space = (c == ' ');
+      putc (c, out);
+    }
+  putc ('\n', out);
+}
+
+/* Read through a c file.  If a .o file is named,
+   the corresponding .c file is read instead.
+   Looks for DEFUN constructs such as are defined in ../src/lisp.h.
+   Accepts any word starting DEF... so it finds DEFSIMPLE and DEFPRED.  */
+
+scan_c_file (filename)
+     char *filename;
+{
+  FILE *infile;
+  register int c;
+  register int commas;
+  register int defunflag;
+  register int defvarflag;
+  int minargs, maxargs;
+
+  if (filename[strlen (filename) - 1] == 'o')
+    filename[strlen (filename) - 1] = 'c';
+
+  infile = fopen (filename, "r");
+
+  /* No error if non-ex input file */
+  if (infile == NULL)
+    {
+      perror (filename);
+      return 0;
+    }
+
+  c = '\n';
+  while (!feof (infile))
+    {
+      if (c != '\n')
+	{
+	  c = getc (infile);
+	  continue;
+	}
+      c = getc (infile);
+      if (c == ' ')
+	{
+	  while (c == ' ')
+	    c = getc (infile);
+	  if (c != 'D')
+	    continue;
+	  c = getc (infile);
+	  if (c != 'E')
+	    continue;
+	  c = getc (infile);
+	  if (c != 'F')
+	    continue;
+	  c = getc (infile);
+	  if (c != 'V')
+	    continue;
+	  defvarflag = 1;
+	  defunflag = 0;
+	  c = getc (infile);
+	}
+      else if (c == 'D')
+	{
+	  c = getc (infile);
+	  if (c != 'E')
+	    continue;
+	  c = getc (infile);
+	  if (c != 'F')
+	    continue;
+	  c = getc (infile);
+	  defunflag = c == 'U';
+	  defvarflag = 0;
+	}
+      else continue;
+
+      while (c != '(')
+	{
+	  if (c < 0)
+	    goto eof;
+	  c = getc (infile);
+	}
+
+      c = getc (infile);
+      if (c != '"')
+	continue;
+      c = read_c_string (infile, -1);
+
+      if (defunflag)
+	commas = 5;
+      else if (defvarflag)
+	commas = 1;
+      else  /* For DEFSIMPLE and DEFPRED */
+	commas = 2;
+
+      while (commas)
+	{
+	  if (c == ',')
+	    {
+	      commas--;
+	      if (defunflag && (commas == 1 || commas == 2))
+		{
+		  do
+		    c = getc (infile);
+		  while (c == ' ' || c == '\n' || c == '\t');
+		  if (c < 0)
+		    goto eof;
+		  ungetc (c, infile);
+		  if (commas == 2) /* pick up minargs */
+		    fscanf (infile, "%d", &minargs);
+		  else /* pick up maxargs */
+		    if (c == 'M' || c == 'U') /* MANY || UNEVALLED */
+		      maxargs = -1;
+		    else
+		      fscanf (infile, "%d", &maxargs);
+		}
+	    }
+	  if (c < 0)
+	    goto eof;
+	  c = getc (infile);
+	}
+      while (c == ' ' || c == '\n' || c == '\t')
+	c = getc (infile);
+      if (c == '"')
+	c = read_c_string (infile, 0);
+      while (c != ',')
+	c = getc (infile);
+      c = getc (infile);
+      while (c == ' ' || c == '\n' || c == '\t')
+	c = getc (infile);
+
+      if (c == '"')
+	{
+	  putc (037, outfile);
+	  putc (defvarflag ? 'V' : 'F', outfile);
+	  fprintf (outfile, "%s\n", buf);
+	  read_c_string (infile, 1);
+	  if (defunflag)
+	    {
+	      char argbuf[1024], *p = argbuf;
+	      while (c != ')')
+		{
+		  if (c < 0)
+		    goto eof;
+		  c = getc (infile);
+		}
+	      /* Skip into arguments.  */
+	      while (c != '(')
+		{
+		  if (c < 0)
+		    goto eof;
+		  c = getc (infile);
+		}
+	      /* Copy arguments into ARGBUF.  */
+	      *p++ = c;
+	      do
+		*p++ = c = getc (infile);
+	      while (c != ')');
+	      *p = '\0';
+	      /* Output them.  */
+	      fprintf (outfile, "\n\n");
+	      write_c_args (outfile, argbuf, minargs, maxargs);
+	    }
+	}
+    }
+ eof:
+  fclose (infile);
+  return 0;
+}
+
+/* Read a file of Lisp code, compiled or interpreted.
+ Looks for
+  (defun NAME ARGS DOCSTRING ...)
+  (autoload 'NAME FILE DOCSTRING ...)
+  (defvar NAME VALUE DOCSTRING)
+  (defconst NAME VALUE DOCSTRING)
+ starting in column zero.
+ ARGS, FILE or VALUE is ignored.  We do not know how to parse Lisp code
+ so we use a kludge to skip them:
+  In a function definition, the form of ARGS of FILE is known, and we
+  can skip it.
+  In a variable definition, we use a formatting convention:
+  the DOCSTRING, if present, must be followed by a closeparen and a newline,
+  and no newline must appear between the defvar or defconst and the docstring,
+  The only source file that must follow this convention is loaddefs.el;
+  aside from that, it is always the .elc file that we look at, and
+  they are no problem because byte-compiler output follows this convention.
+ The NAME and DOCSTRING are output.
+ NAME is preceded by `F' for a function or `V' for a variable.
+ An entry is output only if DOCSTRING has \ newline just after the opening "
+ */
+
+scan_lisp_file (filename)
+     char *filename;
+{
+  FILE *infile;
+  register int c;
+  register int commas;
+  register char *p;
+  int defvarflag;
+
+  infile = fopen (filename, "r");
+  if (infile == NULL)
+    {
+      perror (filename);
+      return 0;				/* No error */
+    }
+
+  c = '\n';
+  while (!feof (infile))
+    {
+      if (c != '\n')
+	{
+	  c = getc (infile);
+	  continue;
+	}
+      c = getc (infile);
+      if (c != '(')
+	continue;
+      c = getc (infile);
+      if (c == 'a')
+	{
+	  c = getc (infile);
+	  if (c != 'u')
+	    continue;
+	  c = getc (infile);
+	  if (c != 't')
+	    continue;
+	  c = getc (infile);
+	  if (c != 'o')
+	    continue;
+	  c = getc (infile);
+	  if (c != 'l')
+	    continue;
+	  c = getc (infile);
+	  if (c != 'o')
+	    continue;
+	  c = getc (infile);
+	  if (c != 'a')
+	    continue;
+	  c = getc (infile);
+	  if (c != 'd')
+	    continue;
+
+	  c = getc (infile);
+	  while (c == ' ')
+	    c = getc (infile);
+
+	  if (c == '\'')
+	    {
+	      c = getc (infile);
+	    }
+	  else
+	    {
+	      if (c != '(')
+		continue;
+	      c = getc (infile);
+	      if (c != 'q')
+		continue;
+	      c = getc (infile);
+	      if (c != 'u')
+		continue;
+	      c = getc (infile);
+	      if (c != 'o')
+		continue;
+	      c = getc (infile);
+	      if (c != 't')
+		continue;
+	      c = getc (infile);
+	      if (c != 'e')
+		continue;
+	      c = getc (infile);
+	      if (c != ' ')
+		continue;
+	      while (c == ' ')
+		c = getc (infile);
+	    }
+
+	  p = buf;
+	  while (c != ' ' && c != ')')
+	    {
+	      if (c == EOF)
+		return 1;
+	      if (c == '\\')
+		c = getc (infile);
+	      *p++ = c;
+	      c = getc (infile);
+	    }
+	  *p = 0;
+
+	  while (c != '"')
+	    {
+	      if (c == EOF)
+		return 1;
+	      c = getc (infile);
+	    }
+	  c = read_c_string (infile, 0);
+	}
+      else if (c == 'd')
+	{
+	  c = getc (infile);
+	  if (c != 'e')
+	    continue;
+	  c = getc (infile);
+	  if (c != 'f')
+	    continue;
+	  c = getc (infile);
+	  if (c == 'u')
+	    {
+	      c = getc (infile);
+	      if (c != 'n')
+		continue;
+	      defvarflag = 0;
+	    }
+	  else if (c == 'v')
+	    {
+	      c = getc (infile);
+	      if (c != 'a')
+		continue;
+	      c = getc (infile);
+	      if (c != 'r')
+		continue;
+	      defvarflag = 1;
+	    }
+	  else if (c == 'c')
+	    {
+	      c = getc (infile);
+	      if (c != 'o')
+		continue;
+	      c = getc (infile);
+	      if (c != 'n')
+		continue;
+	      c = getc (infile);
+	      if (c != 's')
+		continue;
+	      c = getc (infile);
+	      if (c != 't')
+		continue;
+	      defvarflag = 1;
+	    }
+	  else
+	    continue;
+
+	  /* Now we have seen "defun" or "defvar" or "defconst".  */
+
+	  while (c != ' ' && c != '\n' && c != '\t')
+	    c = getc (infile);
+
+	  while (c == ' ' || c == '\n' || c == '\t')
+	    c = getc (infile);
+
+	  /* Read and store name of function or variable being defined
+	     Discard backslashes that are for quoting.  */
+	  p = buf;
+	  while (c != ' ' && c != '\n' && c != '\t')
+	    {
+	      if (c == '\\')
+		c = getc (infile);
+	      *p++ = c;
+	      c = getc (infile);
+	    }
+	  *p = 0;
+
+	  while (c == ' ' || c == '\n' || c == '\t')
+	    c = getc (infile);
+
+	  if (! defvarflag)
+	    {
+	      /* A function: */
+	      /* Skip the arguments: either "nil" or a list in parens */
+	      if (c == 'n')
+		{
+		  while (c != ' ' && c != '\n' && c != '\t')
+		    c = getc (infile);
+		}
+	      else
+		{
+		  while (c != '(')
+		    c = getc (infile);
+		  while (c != ')')
+		    c = getc (infile);
+		}
+	      c = getc (infile);
+	    }
+	  else
+	    {
+	      /* A variable:  */
+
+	      /* Skip until the first newline; remember
+		 the two previous characters.  */
+	      char c1 = 0, c2 = 0;
+
+	      while (c != '\n' && c >= 0)
+		{
+		  c2 = c1;
+		  c1 = c;
+		  c = getc (infile);
+		}
+
+	      /* If two previous characters were " and \,
+		 this is a doc string.  Otherwise, there is none.  */
+	      if (c2 == '"' && c1 == '\\')
+		{
+		  putc (037, outfile);
+		  putc ('V', outfile);
+		  fprintf (outfile, "%s\n", buf);
+		  read_c_string (infile, 1);
+		}
+	      continue;
+	    }
+	}
+      else
+	continue;
+
+      /* Here for a function definition.
+	 We have skipped the file name or arguments
+	 and arrived at where the doc string is,
+	 if there is a doc string.  */
+
+      /* Skip whitespace */
+
+      while (c == ' ' || c == '\n' || c == '\t')
+	c = getc (infile);
+
+      /* " followed by \ and newline means a doc string we should gobble */
+      if (c != '"')
+	continue;
+      c = getc (infile);
+      if (c != '\\')
+	continue;
+      c = getc (infile);
+      if (c != '\n')
+	continue;
+
+      putc (037, outfile);
+      putc ('F', outfile);
+      fprintf (outfile, "%s\n", buf);
+      read_c_string (infile, 1);
+    }
+  fclose (infile);
+  return 0;
+}