summaryrefslogtreecommitdiff
path: root/lib-src/make-docfile.c
diff options
context:
space:
mode:
authorStefan Monnier <monnier@iro.umontreal.ca>2022-01-03 15:04:12 -0500
committerStefan Monnier <monnier@iro.umontreal.ca>2022-01-03 15:04:12 -0500
commit460f35e96df1c39ce2ba0f424b36365a2f9e9825 (patch)
tree28398a961fccf9538114174b32d96f5dd918043a /lib-src/make-docfile.c
parentab5ee3e29e916d4009b301841e9780aad564a6a0 (diff)
downloademacs-460f35e96df1c39ce2ba0f424b36365a2f9e9825.tar.gz
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.
Diffstat (limited to 'lib-src/make-docfile.c')
-rw-r--r--lib-src/make-docfile.c362
1 files changed, 359 insertions, 3 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 <https://www.gnu.org/licenses/>. */
-/* 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 <https://www.gnu.org/licenses/>. */
#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 */