(C_entries): Cast result of xrealloc.
[bpt/emacs.git] / lib-src / make-docfile.c
index 2fb9af0..006600e 100644 (file)
@@ -1,5 +1,5 @@
 /* Generate doc-string file for GNU Emacs from source files.
-   Copyright (C) 1985, 1986, 1992 Free Software Foundation, Inc.
+   Copyright (C) 1985, 1986, 1992, 1993, 1994 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -32,15 +32,89 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  */
 
 #include <stdio.h>
-
+#ifdef MSDOS
+#include <fcntl.h>
+#endif /* MSDOS */
+#ifdef WINDOWSNT
+#include <stdlib.h>
+#include <fcntl.h>
+#include <direct.h>
+#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 ();
+
+/* 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.  */
+
+char *
+xmalloc (size)
+     unsigned int size;
+{
+  char *result = (char *) malloc (size);
+  if (result == NULL)
+    fatal ("virtual memory exhausted", 0);
+  return result;
+}
+\f
+int
 main (argc, argv)
      int argc;
      char **argv;
 {
   int i;
   int err_count = 0;
+  int first_infile;
+
+  progname = argv[0];
+
+  /* Don't put CRs in the DOC file.  */
+#ifdef MSDOS
+  _fmode = O_BINARY;
+  (stdout)->_flag &= ~_IOTEXT;
+  _setmode (fileno (stdout), O_BINARY);
+#endif /* MSDOS */
+#ifdef WINDOWSNT
+  _fmode = O_BINARY;
+  _setmode (fileno (stdout), O_BINARY);
+#endif /* WINDOWSNT */
 
   outfile = stdout;
 
@@ -62,26 +136,37 @@ main (argc, argv)
       i += 2;
     }
 
+  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 */
+  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);
+    return scan_lisp_file (filename, READ_BINARY);
   else if (!strcmp (filename + len - 3, ".el"))
-    return scan_lisp_file (filename);
+    return scan_lisp_file (filename, READ_TEXT);
   else
-    return scan_c_file (filename);
+    return scan_c_file (filename, READ_TEXT);
 }
 \f
 char buf[128];
@@ -93,6 +178,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;
@@ -127,10 +213,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);
     }
 
@@ -140,19 +223,24 @@ read_c_string (infile, printflag)
   return c;
 }
 \f
-/* 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 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++)
     {
@@ -171,6 +259,9 @@ write_c_args (out, buf, minargs, maxargs)
              in_ident = 1;
              ident_start = 1;
 
+             if (need_space)
+               putc (' ', out);
+
              if (minargs == 0 && maxargs > 0)
                fprintf (out, "&optional ");
              just_spaced = 1;
@@ -197,15 +288,21 @@ write_c_args (out, buf, minargs, maxargs)
                || ('0' <= p[6] && p[6] <= '9')
                || p[6] == '_'))
        {
-         fprintf (out, "default");
+         fprintf (out, "DEFAULT");
          p += 5;
          in_ident = 0;
          just_spaced = 0;
        }
       else if (c != ' ' || ! just_spaced)
-       putc (c, out);
+       {
+         if (c >= 'a' && c <= 'z')
+           /* Upcase the letter.  */
+           c += 'A' - 'a';
+         putc (c, out);
+       }
 
       just_spaced = (c == ' ');
+      need_space = 0;
     }
 }
 \f
@@ -214,8 +311,9 @@ 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;
@@ -224,11 +322,12 @@ scan_c_file (filename)
   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)
@@ -237,6 +336,9 @@ 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))
     {
@@ -385,7 +487,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);
            }
        }
     }
@@ -406,6 +508,11 @@ scan_c_file (filename)
   (defalias (quote NAME) #[... 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 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
@@ -460,14 +567,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);
@@ -477,8 +585,7 @@ scan_lisp_file (filename)
   c = '\n';
   while (!feof (infile))
     {
-      char buffer [BUFSIZ];
-      char *fillp = buffer;
+      char buffer[BUFSIZ];
       char type;
 
       if (c != '\n')
@@ -487,6 +594,46 @@ scan_lisp_file (filename)
          continue;
        }
       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 newline.  */
+             c = getc (infile);
+             while (c != '\n')
+               c = getc (infile);
+           }
+         continue;
+       }
+
       if (c != '(')
        continue;
 
@@ -544,23 +691,27 @@ scan_lisp_file (filename)
          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)
            {
-             c2 = c1;
-             c1 = c;
-             c = getc (infile);
-           }
+
+             /* Skip until the first newline; remember the two previous chars. */
+             while (c != '\n' && 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;
+               }
            }
        }
 
@@ -598,23 +749,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 until the first newline; remember the two previous chars. */
+             while (c != '\n' && 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;
+               }
            }
        }
 
@@ -659,18 +813,20 @@ 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')
+               {
 #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;
+               }
            }
        }
 
@@ -689,15 +845,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;