From 460f35e96df1c39ce2ba0f424b36365a2f9e9825 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 3 Jan 2022 15:04:12 -0500 Subject: [PATCH] Revert part of 59732a83c8 to fix bug#52969 While we don't need to put docstrings of .elc files into etc/DOC, we still need to put those of `loaddefs.el` there since we don't have a "dynamic docstring" feature for the non-compiled files and keeping the actual docstrings in the heap would be prohibitive. * src/Makefile.in ($(etc)/DOC): Scan `lisp/loaddefs.el` still. * lib-src/make-docfile.c (scan_lisp_file): New function. (scan_file): Use it. (skip_white, read_lisp_symbol, search_lisp_doc_at_eol): New functions. --- lib-src/make-docfile.c | 362 ++++++++++++++++++++++++++++++++++++++++- src/Makefile.in | 4 +- 2 files changed, 362 insertions(+), 4 deletions(-) diff --git a/lib-src/make-docfile.c b/lib-src/make-docfile.c index 33ed5ec0c92..199f1dbbcc7 100644 --- a/lib-src/make-docfile.c +++ b/lib-src/make-docfile.c @@ -19,8 +19,8 @@ You should have received a copy of the GNU General Public License along with GNU Emacs. If not, see . */ -/* The arguments given to this program are all the C files - of GNU Emacs. .c files are allowed. +/* The arguments given to this program are all the C and some Lisp source files + of GNU Emacs. .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. Option -d DIR means change to DIR before looking for files. @@ -65,6 +65,7 @@ along with GNU Emacs. If not, see . */ #endif /* not DOS_NT */ static void scan_file (char *filename); +static void scan_lisp_file (const char *filename, const char *mode); static void scan_c_file (char *filename, const char *mode); static void scan_c_stream (FILE *infile); static void start_globals (void); @@ -234,9 +235,14 @@ put_filename (char *filename) static void scan_file (char *filename) { + ptrdiff_t len = strlen (filename); + if (!generate_globals) put_filename (filename); - scan_c_file (filename, "r"); + if (len > 3 && !strcmp (filename + len - 3, ".el")) + scan_lisp_file (filename, "r"); + else + scan_c_file (filename, "r"); } static void @@ -1214,4 +1220,354 @@ scan_c_stream (FILE *infile) fatal ("read error"); } +/* Read a file of Lisp source code. + 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. + + 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 newline immediately after the initial double-quote. No newlines + must appear between the beginning of the form and the first double-quote. + 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 follow this convention are autoload-generated + files like loaddefs.el; + 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 ". + */ + +static void +skip_white (FILE *infile) +{ + int c; + do + c = getc (infile); + while (c_isspace (c)); + + ungetc (c, infile); +} + +static void +read_lisp_symbol (FILE *infile, char *buffer) +{ + int c; + char *fillp = buffer; + + skip_white (infile); + while (true) + { + c = getc (infile); + if (c == '\\') + { + c = getc (infile); + if (c < 0) + return; + *fillp++ = c; + } + else if (c_isspace (c) || c == '(' || c == ')' || c < 0) + { + ungetc (c, infile); + *fillp = 0; + break; + } + else + *fillp++ = c; + } + + if (! buffer[0]) + fprintf (stderr, "## expected a symbol, got '%c'\n", c); + + skip_white (infile); +} + +static bool +search_lisp_doc_at_eol (FILE *infile) +{ + int c = 0, c1 = 0, c2 = 0; + + /* Skip until the end of line; remember two previous chars. */ + while (c != '\n' && c != '\r' && c != EOF) + { + 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 found\n"); +#endif + ungetc (c, infile); + return false; + } + return true; +} + +static void +scan_lisp_file (const char *filename, const char *mode) +{ + FILE *infile; + int c; + int i; + int flen = strlen (filename); + + if (generate_globals) + fatal ("scanning lisp file when -g specified"); + + infile = fopen (filename, mode); + if (infile == NULL) + { + perror (filename); + exit (EXIT_FAILURE); + } + + c = '\n'; + while (!feof (infile)) + { + char buffer[BUFSIZ]; + char type; + + /* If not at end of line, skip till we get to one. */ + if (c != '\n' && c != '\r') + { + c = getc (infile); + continue; + } + /* Skip the line break. */ + while (c == '\n' || c == '\r') + c = getc (infile); + + if (c != '(') + continue; + + read_lisp_symbol (infile, buffer); + + if (! strcmp (buffer, "defun") + || ! strcmp (buffer, "defmacro") + || ! strcmp (buffer, "defsubst")) + { + 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 < 0)) + 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' && c != '\r')) + { +#ifdef DEBUG + fprintf (stderr, "## non-docstring in %s (%s)\n", + buffer, filename); +#endif + continue; + } + } + + else if (! strcmp (buffer, "defvar") + || ! strcmp (buffer, "defconst") + || ! strcmp (buffer, "defcustom")) + { + type = 'V'; + read_lisp_symbol (infile, buffer); + + if (!search_lisp_doc_at_eol (infile)) + continue; + } + + else if (! strcmp (buffer, "custom-declare-variable") + || ! strcmp (buffer, "defvaralias") + ) + { + type = 'V'; + + c = getc (infile); + if (c == '\'') + read_lisp_symbol (infile, buffer); + else + { + 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 (!search_lisp_doc_at_eol (infile)) + continue; + } + + else if (! strcmp (buffer, "fset") || ! strcmp (buffer, "defalias")) + { + 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; + } + } + + if (!search_lisp_doc_at_eol (infile)) + 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); + c = getc (infile); + if (c != '\"') + { + fprintf (stderr, "## autoload of %s unparsable (%s)\n", + buffer, filename); + continue; + } + read_c_string_or_comment (infile, 0, false, 0); + + if (!search_lisp_doc_at_eol (infile)) + continue; + } + +#ifdef DEBUG + else if (! strcmp (buffer, "if") + || ! strcmp (buffer, "byte-code")) + continue; +#endif + + else + { +#ifdef DEBUG + fprintf (stderr, "## unrecognized top-level form, %s (%s)\n", + buffer, filename); +#endif + continue; + } + + /* At this point, we should gobble a doc string from the input file. + The opening quote (and leading backslash-newline) + have already been read. */ + + printf ("\037%c%s\n", type, buffer); + read_c_string_or_comment (infile, 1, false, 0); + } + if (ferror (infile) || fclose (infile) != 0) + fatal ("%s: read error", filename); +} + + /* make-docfile.c ends here */ diff --git a/src/Makefile.in b/src/Makefile.in index 6379582660c..83210b1317a 100644 --- a/src/Makefile.in +++ b/src/Makefile.in @@ -642,11 +642,13 @@ endif ## for the first time, this prevents any variation between configurations ## in the contents of the DOC file. ## -$(etc)/DOC: $(libsrc)/make-docfile$(EXEEXT) $(doc_obj) +$(etc)/DOC: $(libsrc)/make-docfile$(EXEEXT) $(doc_obj) $(lispsource)/loaddefs.el $(AM_V_GEN)$(MKDIR_P) $(etc) $(AM_V_at)rm -f $(etc)/DOC $(AM_V_at)$(libsrc)/make-docfile -d $(srcdir) \ $(SOME_MACHINE_OBJECTS) $(doc_obj) > $(etc)/DOC + $(AM_V_at)$(libsrc)/make-docfile -a $(etc)/DOC -d $(lispsource) \ + loaddefs.el $(libsrc)/make-docfile$(EXEEXT) $(libsrc)/make-fingerprint$(EXEEXT): \ $(lib)/libgnu.a -- 2.39.2