emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

master 460f35e96d: Revert part of 59732a83c8 to fix bug#52969


From: Stefan Monnier
Subject: master 460f35e96d: Revert part of 59732a83c8 to fix bug#52969
Date: Mon, 3 Jan 2022 15:04:23 -0500 (EST)

branch: master
commit 460f35e96df1c39ce2ba0f424b36365a2f9e9825
Author: Stefan Monnier <monnier@iro.umontreal.ca>
Commit: Stefan Monnier <monnier@iro.umontreal.ca>

    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 33ed5ec0c9..199f1dbbcc 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 */
diff --git a/src/Makefile.in b/src/Makefile.in
index 6379582660..83210b1317 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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]