home *** CD-ROM | disk | FTP | other *** search
- /* 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 free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 1, or (at your option)
- any later version.
-
- GNU Emacs is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with GNU Emacs; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- /* 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 == 0 && maxargs > 0)
- {
- fprintf (out, "(&optional ");
- space = 1;
- continue;
- }
- else 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);
- c = 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 ...)
- (defmacro NAME ARGS DOCSTRING ...)
- (autoload (quote NAME) FILE DOCSTRING ...)
- (defvar NAME VALUE DOCSTRING)
- (defconst NAME VALUE DOCSTRING)
- (fset (quote NAME) (make-byte-code ... DOCSTRING ...))
- (fset (quote NAME) #[... DOCSTRING ...])
- starting in column zero.
- (quote NAME) may appear as 'NAME as well.
- For defun, defmacro, and autoload, we know how to skip over the arglist.
- For defvar, defconst, and fset we skip to the docstring with a klugey
- formatting convention: all docstrings must appear on the same line as the
- initial open-paren (the one in column zero) and must contain a backslash
- and a double-quote immediately after the initial double-quote. No newlines
- must appear between the beginning of the form and the first double-quote.
- 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 "
- */
-
- void
- skip_white (infile)
- FILE *infile;
- {
- char c = ' ';
- while (c == ' ' || c == '\t' || c == '\n')
- c = getc (infile);
- ungetc (c, infile);
- }
-
- void
- read_lisp_symbol (infile, buffer)
- FILE *infile;
- char *buffer;
- {
- char c;
- char *fillp = buffer;
-
- skip_white (infile);
- while (1)
- {
- c = getc (infile);
- if (c == '\\')
- *(++fillp) = getc (infile);
- else if (c == ' ' || c == '\t' || c == '\n' || c == '(' || c == ')')
- {
- ungetc (c, infile);
- *fillp = 0;
- break;
- }
- else
- *fillp++ = c;
- }
-
- if (! buffer[0])
- fprintf (stderr, "## expected a symbol, got '%c'\n", c);
-
- skip_white (infile);
- }
-
-
- scan_lisp_file (filename)
- char *filename;
- {
- FILE *infile;
- register int c;
-
- infile = fopen (filename, "r");
- if (infile == NULL)
- {
- perror (filename);
- return 0; /* No error */
- }
-
- c = '\n';
- while (!feof (infile))
- {
- char buffer [BUFSIZ];
- char *fillp = buffer;
- char type;
-
- if (c != '\n')
- {
- c = getc (infile);
- continue;
- }
- c = getc (infile);
- if (c != '(')
- continue;
-
- read_lisp_symbol (infile, buffer);
-
- if (! strcmp (buffer, "defun") ||
- ! strcmp (buffer, "defmacro"))
- {
- type = 'F';
- read_lisp_symbol (infile, buffer);
-
- /* Skip the arguments: either "nil" or a list in parens */
-
- c = getc (infile);
- if (c == 'n') /* nil */
- {
- if ((c = getc (infile)) != 'i' ||
- (c = getc (infile)) != 'l')
- {
- fprintf (stderr, "## unparsable arglist in %s (%s)\n",
- buffer, filename);
- continue;
- }
- }
- else if (c != '(')
- {
- fprintf (stderr, "## unparsable arglist in %s (%s)\n",
- buffer, filename);
- continue;
- }
- else
- while (c != ')')
- c = getc (infile);
- skip_white (infile);
-
- /* If the next three characters aren't `dquote bslash newline'
- then we're not reading a docstring.
- */
- if ((c = getc (infile)) != '"' ||
- (c = getc (infile)) != '\\' ||
- (c = getc (infile)) != '\n')
- {
- #ifdef DEBUG
- fprintf (stderr, "## non-docstring in %s (%s)\n",
- buffer, filename);
- #endif
- continue;
- }
- }
-
- else if (! strcmp (buffer, "defvar") ||
- ! strcmp (buffer, "defconst"))
- {
- char c1 = 0, c2 = 0;
- type = 'V';
- read_lisp_symbol (infile, buffer);
-
- /* Skip until the first newline; remember the two previous chars. */
- 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 != '\\')
- {
- #ifdef DEBUG
- fprintf (stderr, "## non-docstring in %s (%s)\n",
- buffer, filename);
- #endif
- continue;
- }
- }
-
- else if (! strcmp (buffer, "fset"))
- {
- char c1 = 0, c2 = 0;
- type = 'F';
-
- c = getc (infile);
- if (c == '\'')
- read_lisp_symbol (infile, buffer);
- else
- {
- if (c != '(')
- {
- fprintf (stderr, "## unparsable name in fset in %s\n",
- filename);
- continue;
- }
- read_lisp_symbol (infile, buffer);
- if (strcmp (buffer, "quote"))
- {
- fprintf (stderr, "## unparsable name in fset in %s\n",
- filename);
- continue;
- }
- read_lisp_symbol (infile, buffer);
- c = getc (infile);
- if (c != ')')
- {
- fprintf (stderr,
- "## unparsable quoted name in fset in %s\n",
- filename);
- continue;
- }
- }
-
- /* Skip until the first newline; remember the two previous chars. */
- 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 != '\\')
- {
- #ifdef DEBUG
- fprintf (stderr, "## non-docstring in %s (%s)\n",
- buffer, filename);
- #endif
- continue;
- }
- }
-
- else if (! strcmp (buffer, "autoload"))
- {
- type = 'F';
- c = getc (infile);
- if (c == '\'')
- read_lisp_symbol (infile, buffer);
- else
- {
- if (c != '(')
- {
- fprintf (stderr, "## unparsable name in autoload in %s\n",
- filename);
- continue;
- }
- read_lisp_symbol (infile, buffer);
- if (strcmp (buffer, "quote"))
- {
- fprintf (stderr, "## unparsable name in autoload in %s\n",
- filename);
- continue;
- }
- read_lisp_symbol (infile, buffer);
- c = getc (infile);
- if (c != ')')
- {
- fprintf (stderr,
- "## unparsable quoted name in autoload in %s\n",
- filename);
- continue;
- }
- }
- skip_white (infile);
- if ((c = getc (infile)) != '\"')
- {
- fprintf (stderr, "## autoload of %s unparsable (%s)\n",
- buffer, filename);
- continue;
- }
- read_c_string (infile, 0);
- skip_white (infile);
-
- /* If the next three characters aren't `dquote bslash newline'
- then we're not reading a docstring.
- */
- if ((c = getc (infile)) != '"' ||
- (c = getc (infile)) != '\\' ||
- (c = getc (infile)) != '\n')
- {
- #ifdef DEBUG
- fprintf (stderr, "## non-docstring in %s (%s)\n",
- buffer, filename);
- #endif
- continue;
- }
- }
-
- #ifdef DEBUG
- else if (! strcmp (buffer, "if") ||
- ! strcmp (buffer, "byte-code"))
- ;
- #endif
-
- else
- {
- #ifdef DEBUG
- fprintf (stderr, "## unrecognised top-level form, %s (%s)\n",
- buffer, filename);
- #endif
- continue;
- }
-
- /* At this point, there is a docstring that we should gobble.
- The opening quote (and leading backslash-newline) have already
- been read.
- */
- putc ('\n', outfile);
- putc (037, outfile);
- putc (type, outfile);
- fprintf (outfile, "%s\n", buffer);
- read_c_string (infile, 1);
- }
- fclose (infile);
- return 0;
- }
-