X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/f125a9e8b5f2894df9d731f6479afd86d60d93fe..ef53d75e82b52e5948807e3af892fb3f3e8f98cc:/lib-src/make-docfile.c diff --git a/lib-src/make-docfile.c b/lib-src/make-docfile.c index 037e00e680..793c5e4211 100644 --- a/lib-src/make-docfile.c +++ b/lib-src/make-docfile.c @@ -1,5 +1,6 @@ /* Generate doc-string file for GNU Emacs from source files. - Copyright (C) 1985, 1986, 1992 Free Software Foundation, Inc. + Copyright (C) 1985, 86, 92, 93, 94, 97, 1999, 2000, 2001 + Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -15,7 +16,8 @@ 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 Free Software Foundation, Inc., 59 Temple Place - Suite 330, +Boston, MA 02111-1307, 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. @@ -31,19 +33,115 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ Then comes the documentation for that function or variable. */ +#define NO_SHORTNAMES /* Tell config not to load remap.h */ +#include + +/* defined to be emacs_main, sys_fopen, etc. in config.h */ +#undef main +#undef fopen +#undef chdir + #include +#ifdef MSDOS +#include +#endif /* MSDOS */ +#ifdef WINDOWSNT +#include +#include +#include +#endif /* WINDOWSNT */ + +#ifdef DOS_NT +#define READ_TEXT "rt" +#define READ_BINARY "rb" +#else /* not DOS_NT */ +#define READ_TEXT "r" +#define READ_BINARY "r" +#endif /* not DOS_NT */ + +int scan_file (); +int scan_lisp_file (); +int scan_c_file (); + +#ifdef MSDOS +/* s/msdos.h defines this as sys_chdir, but we're not linking with the + file where that function is defined. */ +#undef chdir +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif +/* Stdio stream for output to the DOC file. */ FILE *outfile; +/* Name this program was invoked with. */ +char *progname; + +/* Print error message. `s1' is printf control string, `s2' is arg for it. */ + +/* VARARGS1 */ +void +error (s1, s2) + char *s1, *s2; +{ + fprintf (stderr, "%s: ", progname); + fprintf (stderr, s1, s2); + fprintf (stderr, "\n"); +} + +/* Print error message and exit. */ + +/* VARARGS1 */ +void +fatal (s1, s2) + char *s1, *s2; +{ + error (s1, s2); + exit (1); +} + +/* Like malloc but get fatal error if memory is exhausted. */ + +long * +xmalloc (size) + unsigned int size; +{ + long *result = (long *) malloc (size); + if (result == NULL) + fatal ("virtual memory exhausted", 0); + return result; +} + +int main (argc, argv) int argc; char **argv; { int i; int err_count = 0; + int first_infile; + + progname = argv[0]; outfile = stdout; + /* Don't put CRs in the DOC file. */ +#ifdef MSDOS + _fmode = O_BINARY; +#if 0 /* Suspicion is that this causes hanging. + So instead we require people to use -o on MSDOS. */ + (stdout)->_flag &= ~_IOTEXT; + _setmode (fileno (stdout), O_BINARY); +#endif + outfile = 0; +#endif /* MSDOS */ +#ifdef WINDOWSNT + _fmode = O_BINARY; + _setmode (fileno (stdout), O_BINARY); +#endif /* WINDOWSNT */ + /* If first two args are -o FILE, output to FILE. */ i = 1; if (argc > i + 1 && !strcmp (argv[i], "-o")) @@ -56,27 +154,46 @@ main (argc, argv) outfile = fopen (argv[i + 1], "a"); i += 2; } + if (argc > i + 1 && !strcmp (argv[i], "-d")) + { + chdir (argv[i + 1]); + i += 2; + } + + if (outfile == 0) + fatal ("No output file specified", ""); + first_infile = i; for (; i < argc; i++) - err_count += scan_file (argv[i]); /* err_count seems to be {mis,un}used */ + { + int j; + /* Don't process one file twice. */ + for (j = first_infile; j < i; j++) + if (! strcmp (argv[i], argv[j])) + break; + if (j == i) + err_count += scan_file (argv[i]); + } #ifndef VMS - exit (err_count); /* see below - shane */ -#endif VMS + exit (err_count > 0); +#endif /* VMS */ + return err_count > 0; } /* Read file FILENAME and output its doc strings to outfile. */ /* Return 1 if file is not found, 0 if it is found. */ +int 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); + if (len > 4 && !strcmp (filename + len - 4, ".elc")) + return scan_lisp_file (filename, READ_BINARY); + else if (len > 3 && !strcmp (filename + len - 3, ".el")) + return scan_lisp_file (filename, READ_TEXT); else - return scan_c_file (filename); + return scan_c_file (filename, READ_TEXT); } char buf[128]; @@ -88,6 +205,7 @@ char buf[128]; Convert escape sequences \n and \t to newline and tab; discard \ followed by newline. */ +int read_c_string (infile, printflag) FILE *infile; int printflag; @@ -103,7 +221,7 @@ read_c_string (infile, printflag) if (c == '\\') { c = getc (infile); - if (c == '\n') + if (c == '\n' || c == '\r') { c = getc (infile); continue; @@ -122,10 +240,7 @@ read_c_string (infile, printflag) c = getc (infile); if (c != '"') break; - if (printflag > 0) - putc (c, outfile); - else if (printflag < 0) - *p++ = c; + /* If we had a "", concatenate the two strings. */ c = getc (infile); } @@ -135,46 +250,89 @@ read_c_string (infile, printflag) return c; } -/* Write to file OUT the argument names of the function whose text is in BUF. +/* Write to file OUT the argument names of function FUNC, whose text is in BUF. MINARGS and MAXARGS are the minimum and maximum number of arguments. */ -write_c_args (out, buf, minargs, maxargs) +void +write_c_args (out, func, buf, minargs, maxargs) FILE *out; - char *buf; + char *func, *buf; int minargs, maxargs; { register char *p; - int space = 0; + int in_ident = 0; + int just_spaced = 0; + int need_space = 1; + + fprintf (out, "(%s", func); - fprintf (out, "arguments: "); + if (*buf == '(') + ++buf; for (p = buf; *p; p++) { - if (*p == ',' || p == buf) + char c = *p; + int ident_start = 0; + + /* Notice when we start printing a new identifier. */ + if ((('A' <= c && c <= 'Z') + || ('a' <= c && c <= 'z') + || ('0' <= c && c <= '9') + || c == '_') + != in_ident) { - if (!space) - putc (' ', out); - if (minargs == 0 && maxargs > 0) - fprintf (out, "&optional "); - space = 1; + if (!in_ident) + { + in_ident = 1; + ident_start = 1; - minargs--; - maxargs--; + if (need_space) + putc (' ', out); - continue; + if (minargs == 0 && maxargs > 0) + fprintf (out, "&optional "); + just_spaced = 1; + + minargs--; + maxargs--; + } + else + in_ident = 0; } - else if (*p == ' ' && space) - continue; - space = (*p == ' '); - /* Print the C arguments as they would appear in Elisp; - print underscores as hyphens. */ - if (*p == '_') - putc ('-', out); - else - putc (*p, out); + /* Print the C argument list as it would appear in lisp: + print underscores as hyphens, and print commas and newlines + as spaces. Collapse adjacent spaces into one. */ + if (c == '_') + c = '-'; + else if (c == ',' || c == '\n') + c = ' '; + + /* In C code, `default' is a reserved word, so we spell it + `defalt'; unmangle that here. */ + if (ident_start + && strncmp (p, "defalt", 6) == 0 + && ! (('A' <= p[6] && p[6] <= 'Z') + || ('a' <= p[6] && p[6] <= 'z') + || ('0' <= p[6] && p[6] <= '9') + || p[6] == '_')) + { + fprintf (out, "DEFAULT"); + p += 5; + in_ident = 0; + just_spaced = 0; + } + else if (c != ' ' || !just_spaced) + { + if (c >= 'a' && c <= 'z') + /* Upcase the letter. */ + c += 'A' - 'a'; + putc (c, out); + } + + just_spaced = c == ' '; + need_space = 0; } - putc ('\n', out); } /* Read through a c file. If a .o file is named, @@ -182,20 +340,23 @@ write_c_args (out, buf, minargs, maxargs) 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; +int +scan_c_file (filename, mode) + char *filename, *mode; { FILE *infile; register int c; register int commas; register int defunflag; + register int defvarperbufferflag; register int defvarflag; int minargs, maxargs; + int extension = filename[strlen (filename) - 1]; - if (filename[strlen (filename) - 1] == 'o') + if (extension == 'o') filename[strlen (filename) - 1] = 'c'; - infile = fopen (filename, "r"); + infile = fopen (filename, mode); /* No error if non-ex input file */ if (infile == NULL) @@ -204,10 +365,13 @@ scan_c_file (filename) return 0; } + /* Reset extension to be able to detect duplicate files. */ + filename[strlen (filename) - 1] = extension; + c = '\n'; while (!feof (infile)) { - if (c != '\n') + if (c != '\n' && c != '\r') { c = getc (infile); continue; @@ -228,8 +392,22 @@ scan_c_file (filename) c = getc (infile); if (c != 'V') continue; + c = getc (infile); + if (c != 'A') + continue; + c = getc (infile); + if (c != 'R') + continue; + c = getc (infile); + if (c != '_') + continue; + defvarflag = 1; defunflag = 0; + + c = getc (infile); + defvarperbufferflag = (c == 'P'); + c = getc (infile); } else if (c == 'D') @@ -260,6 +438,8 @@ scan_c_file (filename) if (defunflag) commas = 5; + else if (defvarperbufferflag) + commas = 2; else if (defvarflag) commas = 1; else /* For DEFSIMPLE and DEFPRED */ @@ -274,7 +454,7 @@ scan_c_file (filename) { do c = getc (infile); - while (c == ' ' || c == '\n' || c == '\t'); + while (c == ' ' || c == '\n' || c == '\r' || c == '\t'); if (c < 0) goto eof; ungetc (c, infile); @@ -291,14 +471,14 @@ scan_c_file (filename) goto eof; c = getc (infile); } - while (c == ' ' || c == '\n' || c == '\t') + while (c == ' ' || c == '\n' || c == '\r' || 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') + while (c == ' ' || c == '\n' || c == '\r' || c == '\t') c = getc (infile); if (c == '"') @@ -336,7 +516,7 @@ scan_c_file (filename) *p = '\0'; /* Output them. */ fprintf (outfile, "\n\n"); - write_c_args (outfile, argbuf, minargs, maxargs); + write_c_args (outfile, buf, argbuf, minargs, maxargs); } } } @@ -349,20 +529,31 @@ scan_c_file (filename) Looks for (defun NAME ARGS DOCSTRING ...) (defmacro NAME ARGS DOCSTRING ...) + (defsubst 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 ...]) + (defalias (quote NAME) #[... DOCSTRING ...]) + (custom-declare-variable (quote NAME) VALUE 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 + + We also look for #@LENGTH CONTENTS^_ at the beginning of the line. + When we find that, we save it for the following defining-form, + and we use that instead of reading a doc string within that defining-form. + + For defvar, defconst, and fset we skip to the docstring with a kludgy 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 + and a newline 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 + For defun, defmacro, and autoload, we know how to skip over the + arglist, but the doc string must still have a backslash and newline + immediately after the double quote. + The only source files that must follow this convention are preloaded + uncompiled ones like loaddefs.el and bindings.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. @@ -375,7 +566,7 @@ skip_white (infile) FILE *infile; { char c = ' '; - while (c == ' ' || c == '\t' || c == '\n') + while (c == ' ' || c == '\t' || c == '\n' || c == '\r') c = getc (infile); ungetc (c, infile); } @@ -394,7 +585,7 @@ read_lisp_symbol (infile, buffer) c = getc (infile); if (c == '\\') *(++fillp) = getc (infile); - else if (c == ' ' || c == '\t' || c == '\n' || c == '(' || c == ')') + else if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '(' || c == ')') { ungetc (c, infile); *fillp = 0; @@ -410,14 +601,15 @@ read_lisp_symbol (infile, buffer) skip_white (infile); } - -scan_lisp_file (filename) - char *filename; +int +scan_lisp_file (filename, mode) + char *filename, *mode; { FILE *infile; register int c; + char *saved_string = 0; - infile = fopen (filename, "r"); + infile = fopen (filename, mode); if (infile == NULL) { perror (filename); @@ -427,23 +619,68 @@ scan_lisp_file (filename) c = '\n'; while (!feof (infile)) { - char buffer [BUFSIZ]; - char *fillp = buffer; + char buffer[BUFSIZ]; char type; - if (c != '\n') + /* If not at end of line, skip till we get to one. */ + if (c != '\n' && c != '\r') { c = getc (infile); continue; } - c = getc (infile); + /* Skip the line break. */ + while (c == '\n' || c == '\r') + c = getc (infile); + /* Detect a dynamic doc string and save it for the next expression. */ + if (c == '#') + { + c = getc (infile); + if (c == '@') + { + int length = 0; + int i; + + /* Read the length. */ + while ((c = getc (infile), + c >= '0' && c <= '9')) + { + length *= 10; + length += c - '0'; + } + + /* The next character is a space that is counted in the length + but not part of the doc string. + We already read it, so just ignore it. */ + length--; + + /* Read in the contents. */ + if (saved_string != 0) + free (saved_string); + saved_string = (char *) malloc (length); + for (i = 0; i < length; i++) + saved_string[i] = getc (infile); + /* The last character is a ^_. + That is needed in the .elc file + but it is redundant in DOC. So get rid of it here. */ + saved_string[length - 1] = 0; + /* Skip the line break. */ + while (c == '\n' && c == '\r') + c = getc (infile); + /* Skip the following line. */ + while (c != '\n' && c != '\r') + c = getc (infile); + } + continue; + } + if (c != '(') continue; read_lisp_symbol (infile, buffer); - if (! strcmp (buffer, "defun") || - ! strcmp (buffer, "defmacro")) + if (! strcmp (buffer, "defun") + || ! strcmp (buffer, "defmacro") + || ! strcmp (buffer, "defsubst")) { type = 'F'; read_lisp_symbol (infile, buffer); @@ -453,8 +690,8 @@ scan_lisp_file (filename) c = getc (infile); if (c == 'n') /* nil */ { - if ((c = getc (infile)) != 'i' || - (c = getc (infile)) != 'l') + if ((c = getc (infile)) != 'i' + || (c = getc (infile)) != 'l') { fprintf (stderr, "## unparsable arglist in %s (%s)\n", buffer, filename); @@ -475,9 +712,9 @@ scan_lisp_file (filename) /* 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') + if ((c = getc (infile)) != '"' + || (c = getc (infile)) != '\\' + || ((c = getc (infile)) != '\n' && c != '\r')) { #ifdef DEBUG fprintf (stderr, "## non-docstring in %s (%s)\n", @@ -487,34 +724,97 @@ scan_lisp_file (filename) } } - else if (! strcmp (buffer, "defvar") || - ! strcmp (buffer, "defconst")) + 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) + if (saved_string == 0) + { + + /* Skip until the end of line; remember two previous chars. */ + while (c != '\n' && c != '\r' && 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, "custom-declare-variable")) + { + char c1 = 0, c2 = 0; + type = 'V'; + + c = getc (infile); + if (c == '\'') + read_lisp_symbol (infile, buffer); + else { - c2 = c1; - c1 = c; + if (c != '(') + { + fprintf (stderr, + "## unparsable name in custom-declare-variable in %s\n", + filename); + continue; + } + read_lisp_symbol (infile, buffer); + if (strcmp (buffer, "quote")) + { + fprintf (stderr, + "## unparsable name in custom-declare-variable in %s\n", + filename); + continue; + } + read_lisp_symbol (infile, buffer); c = getc (infile); + if (c != ')') + { + fprintf (stderr, + "## unparsable quoted name in custom-declare-variable in %s\n", + filename); + continue; + } } - - /* If two previous characters were " and \, - this is a doc string. Otherwise, there is none. */ - if (c2 != '"' || c1 != '\\') + + if (saved_string == 0) { + /* Skip to end of line; remember the two previous chars. */ + while (c != '\n' && c != '\r' && 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); + fprintf (stderr, "## non-docstring in %s (%s)\n", + buffer, filename); #endif - continue; + continue; + } } } - else if (! strcmp (buffer, "fset")) + else if (! strcmp (buffer, "fset") || ! strcmp (buffer, "defalias")) { char c1 = 0, c2 = 0; type = 'F'; @@ -548,23 +848,26 @@ scan_lisp_file (filename) } } - /* Skip until the first newline; remember the two previous chars. */ - while (c != '\n' && c >= 0) + if (saved_string == 0) { - c2 = c1; - c1 = c; - c = getc (infile); - } + /* Skip to end of line; remember the two previous chars. */ + while (c != '\n' && c != '\r' && 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 != '\\') - { + /* 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); + fprintf (stderr, "## non-docstring in %s (%s)\n", + buffer, filename); #endif - continue; + continue; + } } } @@ -609,24 +912,26 @@ scan_lisp_file (filename) 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') + if (saved_string == 0) { + /* 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' && c != '\r')) + { #ifdef DEBUG - fprintf (stderr, "## non-docstring in %s (%s)\n", - buffer, filename); + fprintf (stderr, "## non-docstring in %s (%s)\n", + buffer, filename); #endif - continue; + continue; + } } } #ifdef DEBUG - else if (! strcmp (buffer, "if") || - ! strcmp (buffer, "byte-code")) + else if (! strcmp (buffer, "if") + || ! strcmp (buffer, "byte-code")) ; #endif @@ -639,15 +944,25 @@ scan_lisp_file (filename) 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); + /* At this point, we should either use the previous + dynamic doc string in saved_string + or gobble a doc string from the input file. + + In the latter case, the opening quote (and leading + backslash-newline) have already been read. */ + putc (037, outfile); putc (type, outfile); fprintf (outfile, "%s\n", buffer); - read_c_string (infile, 1); + if (saved_string) + { + fputs (saved_string, outfile); + /* Don't use one dynamic doc string twice. */ + free (saved_string); + saved_string = 0; + } + else + read_c_string (infile, 1); } fclose (infile); return 0;