/* 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.
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.
*/
#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
#undef chdir
#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
/* Stdio stream for output to the DOC file. */
FILE *outfile;
progname = argv[0];
+ outfile = stdout;
+
/* Don't put CRs in the DOC file. */
#ifdef MSDOS
_fmode = O_BINARY;
+#if 0 /* Suspicion is that this causes hanging.
+ So instead we require people to use -o on MSDOS. */
(stdout)->_flag &= ~_IOTEXT;
_setmode (fileno (stdout), O_BINARY);
+#endif
+ outfile = 0;
#endif /* MSDOS */
#ifdef WINDOWSNT
_fmode = O_BINARY;
_setmode (fileno (stdout), O_BINARY);
#endif /* WINDOWSNT */
- outfile = stdout;
-
/* If first two args are -o FILE, output to FILE. */
i = 1;
if (argc > i + 1 && !strcmp (argv[i], "-o"))
i += 2;
}
+ if (outfile == 0)
+ fatal ("No output file specified", "");
+
first_infile = i;
for (; i < argc; i++)
{
char *filename;
{
int len = strlen (filename);
- if (!strcmp (filename + len - 4, ".elc"))
+ if (len > 4 && !strcmp (filename + len - 4, ".elc"))
return scan_lisp_file (filename, READ_BINARY);
- else if (!strcmp (filename + len - 3, ".el"))
+ else if (len > 3 && !strcmp (filename + len - 3, ".el"))
return scan_lisp_file (filename, READ_TEXT);
else
return scan_c_file (filename, READ_TEXT);
\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;
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. */
}
/* 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. */
in_ident = 0;
just_spaced = 0;
}
- else if (c != ' ' || ! just_spaced)
+ else if (c != ' ' || !just_spaced)
{
if (c >= 'a' && c <= 'z')
/* Upcase the letter. */
putc (c, out);
}
- just_spaced = (c == ' ');
+ just_spaced = c == ' ';
need_space = 0;
}
}
c = '\n';
while (!feof (infile))
{
- if (c != '\n')
+ int doc_keyword = 0;
+
+ if (c != '\n' && c != '\r')
{
c = getc (infile);
continue;
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;
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);
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 != '(')
{
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.
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.
FILE *infile;
{
char c = ' ';
- while (c == ' ' || c == '\t' || c == '\n')
+ while (c == ' ' || c == '\t' || c == '\n' || c == '\r')
c = getc (infile);
ungetc (c, infile);
}
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;
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 == '#')
{
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;
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);
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);
/* 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",
}
}
- else if (! strcmp (buffer, "defvar") ||
- ! strcmp (buffer, "defconst"))
+ else if (! strcmp (buffer, "defvar")
+ || ! strcmp (buffer, "defconst"))
{
char c1 = 0, c2 = 0;
type = 'V';
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;
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;
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",
}
#ifdef DEBUG
- else if (! strcmp (buffer, "if") ||
- ! strcmp (buffer, "byte-code"))
+ else if (! strcmp (buffer, "if")
+ || ! strcmp (buffer, "byte-code"))
;
#endif
saved_string = 0;
}
else
- read_c_string (infile, 1);
+ read_c_string_or_comment (infile, 1, 0);
}
fclose (infile);
return 0;