(put_char): New function.
[bpt/emacs.git] / lib-src / make-docfile.c
index f647c21..8a74de4 100644 (file)
@@ -1,5 +1,6 @@
 /* Generate doc-string file for GNU Emacs from source files.
-   Copyright (C) 1985, 1986, 1992, 1993, 1994 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.
@@ -32,7 +34,12 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  */
 
 #define NO_SHORTNAMES   /* Tell config not to load remap.h */
-#include <../src/config.h>
+#include <config.h>
+
+/* defined to be emacs_main, sys_fopen, etc. in config.h */
+#undef main
+#undef fopen
+#undef chdir
 
 #include <stdio.h>
 #ifdef MSDOS
@@ -62,6 +69,10 @@ int scan_c_file ();
 #undef chdir
 #endif
 
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
 /* Stdio stream for output to the DOC file.  */
 FILE *outfile;
 
@@ -187,30 +198,74 @@ scan_file (filename)
 \f
 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 outfile.
- If it is negative, store contents in buf.
- Convert escape sequences \n and \t to newline and tab;
- discard \ followed by newline.  */
+/* Add CH to either outfile, if PRINTFLAG is positive, or to the buffer
+   whose end is pointed to by BUFP, if PRINTFLAG is negative.
+   If the counters pointed to by PENDING_NEWLINES and PENDING_SPACES are
+   non-zero, that many newlines and spaces are output before CH, and
+   the counters are zeroed.  */
+
+static INLINE void
+put_char (ch, printflag, bufp, pending_newlines, pending_spaces)
+     int ch, printflag;
+     char **bufp;
+     unsigned *pending_newlines, *pending_spaces;
+{
+  int out_ch;
+  do
+    {
+      if (*pending_newlines > 0)
+       {
+         (*pending_newlines)--;
+         out_ch = '\n';
+       }
+      else if (*pending_spaces > 0)
+       {
+         (*pending_spaces)--;
+         out_ch = ' ';
+       }
+      else
+       out_ch = ch;
+
+      if (printflag > 0)
+       putc (out_ch, outfile);
+      else if (printflag < 0)
+       *(*bufp)++ = out_ch;
+    }
+  while (out_ch != ch);
+}
+
+/* Skip a C string or C-style comment from INFILE, and return the
+   character that follows.  COMMENT non-zero means skip a comment.  If
+   PRINTFLAG is positive, output string contents to outfile.  If it is
+   negative, store contents in buf.  Convert escape sequences \n and
+   \t to newline and tab; discard \ followed by newline.  */
 
 int
-read_c_string (infile, printflag)
+read_c_string_or_comment (infile, printflag, comment)
      FILE *infile;
      int printflag;
 {
   register int c;
+  unsigned pending_spaces = 0, pending_newlines = 0;
   char *p = buf;
 
-  c = getc (infile);
+  if (comment)
+    {
+      while ((c = getc (infile)) != EOF
+            && (c == '\n' || c == '\r' || c == '\t' || c == ' '))
+       ;
+    }
+  else
+    c = getc (infile);
+  
   while (c != EOF)
     {
-      while (c != '"' && c != EOF)
+      while (c != EOF && (comment ? c != '*' : c != '"'))
        {
          if (c == '\\')
            {
              c = getc (infile);
-             if (c == '\n')
+             if (c == '\n' || c == '\r')
                {
                  c = getc (infile);
                  continue;
@@ -220,24 +275,50 @@ read_c_string (infile, printflag)
              if (c == 't')
                c = '\t';
            }
-         if (printflag > 0)
-           putc (c, outfile);
-         else if (printflag < 0)
-           *p++ = c;
+         
+         if (c == ' ')
+           pending_spaces++;
+         else if (c == '\n')
+           {
+             pending_newlines++;
+             pending_spaces = 0;
+           }
+         else
+           put_char (c, printflag, &p, &pending_newlines, &pending_spaces);
+
          c = getc (infile);
        }
-      c = getc (infile);
-      if (c != '"')
-       break;
-      /* If we had a "", concatenate the two strings.  */
-      c = getc (infile);
-    }
 
+      if (c != EOF)
+       c = getc (infile);
+
+      if (comment)
+       {
+         if (c == '/')
+           {
+             c = getc (infile);
+             break;
+           }
+         
+         put_char ('*', printflag, &p, &pending_newlines, &pending_spaces);
+       }
+      else
+       {
+         if (c != '"')
+           break;
+      
+         /* If we had a "", concatenate the two strings.  */
+         c = getc (infile);
+       }
+    }
+  
   if (printflag < 0)
     *p = 0;
 
   return c;
 }
+
+
 \f
 /* 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.  */
@@ -290,10 +371,12 @@ write_c_args (out, func, buf, minargs, maxargs)
        }
 
       /* Print the C argument list as it would appear in lisp:
-        print underscores as hyphens, and print commas as spaces.
-        Collapse adjacent spaces into one.  */
-      if (c == '_') c = '-';
-      if (c == ',') c = ' ';
+        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.  */
@@ -309,7 +392,7 @@ write_c_args (out, func, buf, minargs, maxargs)
          in_ident = 0;
          just_spaced = 0;
        }
-      else if (c != ' ' || ! just_spaced)
+      else if (c != ' ' || !just_spaced)
        {
          if (c >= 'a' && c <= 'z')
            /* Upcase the letter.  */
@@ -317,7 +400,7 @@ write_c_args (out, func, buf, minargs, maxargs)
          putc (c, out);
        }
 
-      just_spaced = (c == ' ');
+      just_spaced = c == ' ';
       need_space = 0;
     }
 }
@@ -358,7 +441,9 @@ scan_c_file (filename, mode)
   c = '\n';
   while (!feof (infile))
     {
-      if (c != '\n')
+      int doc_keyword = 0;
+
+      if (c != '\n' && c != '\r')
        {
          c = getc (infile);
          continue;
@@ -418,10 +503,15 @@ scan_c_file (filename, mode)
          c = getc (infile);
        }
 
+      /* Lisp variable or function name.  */
       c = getc (infile);
       if (c != '"')
        continue;
-      c = read_c_string (infile, -1);
+      c = read_c_string_or_comment (infile, -1, 0);
+
+      /* DEFVAR_LISP ("name", addr, "doc")
+        DEFVAR_LISP ("name", addr /\* doc *\/)
+        DEFVAR_LISP ("name", addr, doc: /\* doc *\/)  */
 
       if (defunflag)
        commas = 5;
@@ -437,11 +527,12 @@ scan_c_file (filename, mode)
          if (c == ',')
            {
              commas--;
+
              if (defunflag && (commas == 1 || commas == 2))
                {
                  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);
@@ -454,40 +545,74 @@ scan_c_file (filename, mode)
                      fscanf (infile, "%d", &maxargs);
                }
            }
-         if (c < 0)
+
+         if (c == EOF)
            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')
+       c = read_c_string_or_comment (infile, 0, 0);
+      
+      while (c != EOF && c != ',' && c != '/')
        c = getc (infile);
+      if (c == ',')
+       {
+         c = getc (infile);
+         while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
+           c = getc (infile);
+         while ((c >= 'a' && c <= 'z') || (c >= 'Z' && c <= 'Z'))
+           c = getc (infile);
+         if (c == ':')
+           {
+             doc_keyword = 1;
+             c = getc (infile);
+             while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
+               c = getc (infile);
+           }
+       }
 
-      if (c == '"')
+      if (c == '"'
+         || (c == '/'
+             && (c = getc (infile),
+                 ungetc (c, infile),
+                 c == '*')))
        {
+         int comment = c != '"';
+         
          putc (037, outfile);
          putc (defvarflag ? 'V' : 'F', outfile);
          fprintf (outfile, "%s\n", buf);
-         c = read_c_string (infile, 1);
+
+         if (comment)
+           getc (infile);      /* Skip past `*' */
+         c = read_c_string_or_comment (infile, 1, comment);
 
          /* If this is a defun, find the arguments and print them.  If
             this function takes MANY or UNEVALLED args, then the C source
             won't give the names of the arguments, so we shouldn't bother
-            trying to find them.  */
+            trying to find them.
+
+            Various doc-string styles:
+             0: DEFUN (..., "DOC") (args)            [!comment]
+             1: DEFUN (..., /\* DOC *\/ (args))      [comment && !doc_keyword]
+             2: DEFUN (..., doc: /\* DOC *\/) (args) [comment && doc_keyword]
+         */
          if (defunflag && maxargs != -1)
            {
              char argbuf[1024], *p = argbuf;
-             while (c != ')')
-               {
-                 if (c < 0)
-                   goto eof;
-                 c = getc (infile);
-               }
+
+             if (!comment || doc_keyword)
+               while (c != ')')
+                 {
+                   if (c < 0)
+                     goto eof;
+                   c = getc (infile);
+                 }
+             
              /* Skip into arguments.  */
              while (c != '(')
                {
@@ -516,12 +641,14 @@ scan_c_file (filename, mode)
  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.
 
@@ -529,13 +656,16 @@ scan_c_file (filename, mode)
  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 defun, defmacro, and autoload, we know how to skip over the arglist.
  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.
@@ -548,7 +678,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);
 }
@@ -567,7 +697,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;
@@ -604,12 +734,15 @@ scan_lisp_file (filename, mode)
       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 == '#')
        {
@@ -642,9 +775,11 @@ scan_lisp_file (filename, mode)
                 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 newline.  */
-             c = getc (infile);
-             while (c != '\n')
+             /* 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;
@@ -655,8 +790,9 @@ scan_lisp_file (filename, mode)
 
       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);
@@ -666,8 +802,8 @@ scan_lisp_file (filename, mode)
          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);
@@ -688,9 +824,9 @@ scan_lisp_file (filename, mode)
          /* 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",
@@ -700,8 +836,8 @@ scan_lisp_file (filename, mode)
            }
        }
 
-      else if (! strcmp (buffer, "defvar") ||
-              ! strcmp (buffer, "defconst"))
+      else if (! strcmp (buffer, "defvar")
+              || ! strcmp (buffer, "defconst"))
        {
          char c1 = 0, c2 = 0;
          type = 'V';
@@ -710,8 +846,67 @@ scan_lisp_file (filename, mode)
          if (saved_string == 0)
            {
 
-             /* Skip until the first newline; remember the two previous chars. */
-             while (c != '\n' && c >= 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
+           {
+             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 (saved_string == 0)
+           {
+             /* Skip to end of line; remember the two previous chars.  */
+             while (c != '\n' && c != '\r' && c >= 0)
                {
                  c2 = c1;
                  c1 = c;
@@ -767,8 +962,8 @@ scan_lisp_file (filename, mode)
 
          if (saved_string == 0)
            {
-             /* Skip until the first newline; remember the two previous chars. */
-             while (c != '\n' && c >= 0)
+             /* Skip to end of line; remember the two previous chars.  */
+             while (c != '\n' && c != '\r' && c >= 0)
                {
                  c2 = c1;
                  c1 = c;
@@ -826,16 +1021,16 @@ scan_lisp_file (filename, mode)
                       buffer, filename);
              continue;
            }
-         read_c_string (infile, 0);
+         read_c_string_or_comment (infile, 0, 0);
          skip_white (infile);
 
          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')
+             if ((c = getc (infile)) != '"'
+                 || (c = getc (infile)) != '\\'
+                 || ((c = getc (infile)) != '\n' && c != '\r'))
                {
 #ifdef DEBUG
                  fprintf (stderr, "## non-docstring in %s (%s)\n",
@@ -847,8 +1042,8 @@ scan_lisp_file (filename, mode)
        }
 
 #ifdef DEBUG
-      else if (! strcmp (buffer, "if") ||
-              ! strcmp (buffer, "byte-code"))
+      else if (! strcmp (buffer, "if")
+              || ! strcmp (buffer, "byte-code"))
        ;
 #endif
 
@@ -879,7 +1074,7 @@ scan_lisp_file (filename, mode)
          saved_string = 0;
        }
       else
-       read_c_string (infile, 1);
+       read_c_string_or_comment (infile, 1, 0);
     }
   fclose (infile);
   return 0;