/* 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.
*/
#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;
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];
Convert escape sequences \n and \t to newline and tab;
discard \ followed by newline. */
+int
read_c_string (infile, printflag)
FILE *infile;
int 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);
}
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++)
{
in_ident = 1;
ident_start = 1;
+ if (need_space)
+ putc (' ', out);
+
if (minargs == 0 && maxargs > 0)
fprintf (out, "&optional ");
just_spaced = 1;
|| ('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
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;
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)
return 0;
}
+ /* Reset extension to be able to detect duplicate files. */
+ filename[strlen (filename) - 1] = extension;
+
c = '\n';
while (!feof (infile))
{
*p = '\0';
/* Output them. */
fprintf (outfile, "\n\n");
- write_c_args (outfile, argbuf, minargs, maxargs);
+ write_c_args (outfile, buf, argbuf, minargs, maxargs);
}
}
}
(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
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);
c = '\n';
while (!feof (infile))
{
- char buffer [BUFSIZ];
- char *fillp = buffer;
+ char buffer[BUFSIZ];
char type;
if (c != '\n')
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;
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;
+ }
}
}
}
}
- /* 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;
+ }
}
}
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;
+ }
}
}
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;