/* Generate doc-string file for GNU Emacs from source files.
- Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2012
- Free Software Foundation, Inc.
+
+Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2015 Free Software
+Foundation, Inc.
This file is part of GNU Emacs.
#include <config.h>
-/* defined to be emacs_main, sys_fopen, etc. in config.h */
-#undef main
-#undef fopen
-#undef chdir
-
+#include <stdbool.h>
#include <stdio.h>
-#include <stdlib.h>
-#ifdef MSDOS
-#include <fcntl.h>
-#endif /* MSDOS */
+#include <stdlib.h> /* config.h unconditionally includes this anyway */
+
#ifdef WINDOWSNT
-#include <fcntl.h>
+/* Defined to be sys_fopen in ms-w32.h, but only #ifdef emacs, so this
+ is really just insurance. */
+#undef fopen
#include <direct.h>
#endif /* WINDOWSNT */
+#include <binary-io.h>
+
#ifdef DOS_NT
-#define READ_TEXT "rt"
-#define READ_BINARY "rb"
+/* Defined to be sys_chdir in ms-w32.h, but only #ifdef emacs, so this
+ is really just insurance.
+
+ Similarly, msdos defines this as sys_chdir, but we're not linking with the
+ file where that function is defined. */
+#undef chdir
+#define IS_SLASH(c) ((c) == '/' || (c) == '\\' || (c) == ':')
#else /* not DOS_NT */
-#define READ_TEXT "r"
-#define READ_BINARY "r"
+#define IS_SLASH(c) ((c) == '/')
#endif /* not DOS_NT */
-#ifndef DIRECTORY_SEP
-#define DIRECTORY_SEP '/'
-#endif
-
-#ifndef IS_DIRECTORY_SEP
-#define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
-#endif
-
-/* Use this to suppress gcc's `...may be used before initialized' warnings. */
-#ifdef lint
-# define IF_LINT(Code) Code
-#else
-# define IF_LINT(Code) /* empty */
-#endif
-
static int scan_file (char *filename);
static int scan_lisp_file (const char *filename, const char *mode);
static int scan_c_file (char *filename, const char *mode);
-static void fatal (const char *s1, const char *s2) NO_RETURN;
+static int scan_c_stream (FILE *infile);
static void start_globals (void);
static void write_globals (void);
-#ifdef MSDOS
-/* s/msdos.h defines this as sys_chdir, but we're not linking with the
- file where that function is defined. */
-#undef chdir
-#endif
-
#include <unistd.h>
-/* Stdio stream for output to the DOC file. */
-FILE *outfile;
-
/* Name this program was invoked with. */
char *progname;
/* Print error message and exit. */
/* VARARGS1 */
-static void
+static _Noreturn void
fatal (const char *s1, const char *s2)
{
error (s1, s2);
return result;
}
+/* Like strdup, but get fatal error if memory is exhausted. */
+
+static char *
+xstrdup (char *s)
+{
+ char *result = strdup (s);
+ if (! result)
+ fatal ("virtual memory exhausted", 0);
+ return result;
+}
+
/* Like realloc but get fatal error if memory is exhausted. */
static void *
{
int i;
int err_count = 0;
- int first_infile;
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 */
-
/* If first two args are -o FILE, output to FILE. */
i = 1;
if (argc > i + 1 && !strcmp (argv[i], "-o"))
{
- outfile = fopen (argv[i + 1], "w");
+ if (! freopen (argv[i + 1], "w", stdout))
+ {
+ perror (argv[i + 1]);
+ return EXIT_FAILURE;
+ }
i += 2;
}
if (argc > i + 1 && !strcmp (argv[i], "-a"))
{
- outfile = fopen (argv[i + 1], "a");
+ if (! freopen (argv[i + 1], "a", stdout))
+ {
+ perror (argv[i + 1]);
+ return EXIT_FAILURE;
+ }
i += 2;
}
if (argc > i + 1 && !strcmp (argv[i], "-d"))
++i;
}
- if (outfile == 0)
- fatal ("No output file specified", "");
+ set_binary_mode (fileno (stdout), O_BINARY);
if (generate_globals)
start_globals ();
- first_infile = i;
- for (; i < argc; i++)
+ if (argc <= i)
+ scan_c_stream (stdin);
+ else
{
- 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]);
+ int first_infile = i;
+ for (; i < argc; i++)
+ {
+ int j;
+ /* Don't process one file twice. */
+ for (j = first_infile; j < i; j++)
+ if (strcmp (argv[i], argv[j]) == 0)
+ break;
+ if (j == i)
+ err_count += scan_file (argv[i]);
+ }
}
if (err_count == 0 && generate_globals)
for (tmp = filename; *tmp; tmp++)
{
- if (IS_DIRECTORY_SEP(*tmp))
+ if (IS_DIRECTORY_SEP (*tmp))
filename = tmp + 1;
}
- putc (037, outfile);
- putc ('S', outfile);
- fprintf (outfile, "%s\n", filename);
+ printf ("\037S%s\n", filename);
}
-/* Read file FILENAME and output its doc strings to outfile. */
-/* Return 1 if file is not found, 0 if it is found. */
+/* Read file FILENAME and output its doc strings to stdout.
+ Return 1 if file is not found, 0 if it is found. */
static int
scan_file (char *filename)
if (!generate_globals)
put_filename (filename);
if (len > 4 && !strcmp (filename + len - 4, ".elc"))
- return scan_lisp_file (filename, READ_BINARY);
+ return scan_lisp_file (filename, "rb");
else if (len > 3 && !strcmp (filename + len - 3, ".el"))
- return scan_lisp_file (filename, READ_TEXT);
+ return scan_lisp_file (filename, "r");
else
- return scan_c_file (filename, READ_TEXT);
+ return scan_c_file (filename, "r");
}
static void
start_globals (void)
{
- fprintf (outfile, "/* This file was auto-generated by make-docfile. */\n");
- fprintf (outfile, "/* DO NOT EDIT. */\n");
- fprintf (outfile, "struct emacs_globals {\n");
+ puts ("/* This file was auto-generated by make-docfile. */");
+ puts ("/* DO NOT EDIT. */");
+ puts ("struct emacs_globals {");
}
\f
static char input_buffer[128];
/* Output CH to the file or buffer in STATE. Any pending newlines or
spaces are output first. */
-static inline void
+static void
put_char (int ch, struct rcsoc_state *state)
{
int out_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
+ PRINTFLAG is positive, output string contents to stdout. If it is
negative, store contents in buf. Convert escape sequences \n and
\t to newline and tab; discard \ followed by newline.
If SAW_USAGE is non-zero, then any occurrences of the string `usage:'
state.in_file = infile;
state.buf_ptr = (printflag < 0 ? input_buffer : 0);
- state.out_file = (printflag > 0 ? outfile : 0);
+ state.out_file = (printflag > 0 ? stdout : 0);
state.pending_spaces = 0;
state.pending_newlines = 0;
state.keyword = (saw_usage ? "usage:" : 0);
\f
-/* Write to file OUT the argument names of function FUNC, whose text is in BUF.
+/* Write to stdout the argument names of function FUNC, whose text is in BUF.
MINARGS and MAXARGS are the minimum and maximum number of arguments. */
static void
-write_c_args (FILE *out, char *func, char *buf, int minargs, int maxargs)
+write_c_args (char *func, char *buf, int minargs, int maxargs)
{
register char *p;
int in_ident = 0;
char *ident_start IF_LINT (= NULL);
size_t ident_length = 0;
- fprintf (out, "(fn");
+ fputs ("(fn", stdout);
if (*buf == '(')
++buf;
if (strncmp (ident_start, "void", ident_length) == 0)
continue;
- putc (' ', out);
+ putchar (' ');
if (minargs == 0 && maxargs > 0)
- fprintf (out, "&optional ");
+ fputs ("&optional ", stdout);
minargs--;
maxargs--;
/* In C code, `default' is a reserved word, so we spell it
`defalt'; demangle that here. */
- if (ident_length == 6 && strncmp (ident_start, "defalt", 6) == 0)
- fprintf (out, "DEFAULT");
+ if (ident_length == 6 && memcmp (ident_start, "defalt", 6) == 0)
+ fputs ("DEFAULT", stdout);
else
while (ident_length-- > 0)
{
else if (c == '_')
/* Print underscore as hyphen. */
c = '-';
- putc (c, out);
+ putchar (c);
}
}
}
- putc (')', out);
+ putchar (')');
}
\f
-/* The types of globals. */
+/* The types of globals. These are sorted roughly in decreasing alignment
+ order to avoid allocation gaps, except that symbols and functions
+ are last. */
enum global_type
{
+ INVALID,
+ LISP_OBJECT,
EMACS_INTEGER,
BOOLEAN,
- LISP_OBJECT,
- INVALID
+ SYMBOL,
+ FUNCTION
};
/* A single global. */
{
enum global_type type;
char *name;
+ int flags;
+ union
+ {
+ int value;
+ char const *svalue;
+ } v;
};
+/* Bit values for FLAGS field from the above. Applied for DEFUNs only. */
+enum { DEFUN_noreturn = 1, DEFUN_const = 2 };
+
/* All the variable names we saw while scanning C sources in `-g'
mode. */
int num_globals;
int num_globals_allocated;
struct global *globals;
-static void
-add_global (enum global_type type, char *name)
+static struct global *
+add_global (enum global_type type, char *name, int value, char const *svalue)
{
/* Ignore the one non-symbol that can occur. */
if (strcmp (name, "..."))
globals[num_globals - 1].type = type;
globals[num_globals - 1].name = name;
+ if (svalue)
+ globals[num_globals - 1].v.svalue = svalue;
+ else
+ globals[num_globals - 1].v.value = value;
+ globals[num_globals - 1].flags = 0;
+ return globals + num_globals - 1;
}
+ return NULL;
}
static int
{
const struct global *ga = a;
const struct global *gb = b;
+
+ if (ga->type != gb->type)
+ return ga->type - gb->type;
+
+ /* Consider "nil" to be the least, so that iQnil is zero. That
+ way, Qnil's internal representation is zero, which is a bit faster. */
+ if (ga->type == SYMBOL)
+ {
+ bool a_nil = strcmp (ga->name, "Qnil") == 0;
+ bool b_nil = strcmp (gb->name, "Qnil") == 0;
+ if (a_nil | b_nil)
+ return b_nil - a_nil;
+ }
+
return strcmp (ga->name, gb->name);
}
+static void
+close_emacs_globals (int num_symbols)
+{
+ printf (("};\n"
+ "extern struct emacs_globals globals;\n"
+ "\n"
+ "#ifndef DEFINE_SYMBOLS\n"
+ "extern\n"
+ "#endif\n"
+ "struct Lisp_Symbol alignas (GCALIGNMENT) lispsym[%d];\n"),
+ num_symbols);
+}
+
static void
write_globals (void)
{
- int i;
+ int i, j;
+ bool seen_defun = false;
+ int symnum = 0;
+ int num_symbols = 0;
qsort (globals, num_globals, sizeof (struct global), compare_globals);
+
+ j = 0;
+ for (i = 0; i < num_globals; i++)
+ {
+ while (i + 1 < num_globals
+ && strcmp (globals[i].name, globals[i + 1].name) == 0)
+ {
+ if (globals[i].type == FUNCTION
+ && globals[i].v.value != globals[i + 1].v.value)
+ error ("function '%s' defined twice with differing signatures",
+ globals[i].name);
+ i++;
+ }
+ num_symbols += globals[i].type == SYMBOL;
+ globals[j++] = globals[i];
+ }
+ num_globals = j;
+
for (i = 0; i < num_globals; ++i)
{
- char const *type;
+ char const *type = 0;
switch (globals[i].type)
{
type = "EMACS_INT";
break;
case BOOLEAN:
- type = "int";
+ type = "bool";
break;
case LISP_OBJECT:
type = "Lisp_Object";
break;
+ case SYMBOL:
+ case FUNCTION:
+ if (!seen_defun)
+ {
+ close_emacs_globals (num_symbols);
+ putchar ('\n');
+ seen_defun = true;
+ }
+ break;
default:
fatal ("not a recognized DEFVAR_", 0);
}
- fprintf (outfile, " %s f_%s;\n", type, globals[i].name);
- fprintf (outfile, "#define %s globals.f_%s\n",
- globals[i].name, globals[i].name);
- while (i + 1 < num_globals
- && !strcmp (globals[i].name, globals[i + 1].name))
- ++i;
+ if (type)
+ {
+ printf (" %s f_%s;\n", type, globals[i].name);
+ printf ("#define %s globals.f_%s\n",
+ globals[i].name, globals[i].name);
+ }
+ else if (globals[i].type == SYMBOL)
+ printf (("DEFINE_LISP_SYMBOL_BEGIN (%s)\n"
+ "#define i%s %d\n"
+ "#define %s builtin_lisp_symbol (i%s)\n"
+ "DEFINE_LISP_SYMBOL_END (%s)\n\n"),
+ globals[i].name, globals[i].name, symnum++,
+ globals[i].name, globals[i].name, globals[i].name);
+ else
+ {
+ if (globals[i].flags & DEFUN_noreturn)
+ fputs ("_Noreturn ", stdout);
+
+ printf ("EXFUN (%s, ", globals[i].name);
+ if (globals[i].v.value == -1)
+ fputs ("MANY", stdout);
+ else if (globals[i].v.value == -2)
+ fputs ("UNEVALLED", stdout);
+ else
+ printf ("%d", globals[i].v.value);
+ putchar (')');
+
+ if (globals[i].flags & DEFUN_const)
+ fputs (" ATTRIBUTE_CONST", stdout);
+ else if (strcmp (globals[i].name, "Fnext_read_file_uses_dialog_p")
+ == 0)
+ {
+ /* It would be nice to have a cleaner way to deal with this
+ special hack. */
+ fputs (("\n"
+ "#if ! (defined USE_GTK || defined USE_MOTIF \\\n"
+ " || defined HAVE_NS || defined HAVE_NTGUI)\n"
+ "\tATTRIBUTE_CONST\n"
+ "#endif\n"),
+ stdout);
+ }
+
+ puts (";");
+ }
}
- fprintf (outfile, "};\n");
- fprintf (outfile, "extern struct emacs_globals globals;\n");
+ if (!seen_defun)
+ close_emacs_globals (num_symbols);
+
+ puts ("#ifdef DEFINE_SYMBOLS");
+ puts ("static char const *const defsym_name[] = {");
+ for (int i = 0; i < num_globals; i++)
+ {
+ if (globals[i].type == SYMBOL)
+ printf ("\t\"%s\",\n", globals[i].v.svalue);
+ while (i + 1 < num_globals
+ && strcmp (globals[i].name, globals[i + 1].name) == 0)
+ i++;
+ }
+ puts ("};");
+ puts ("#endif");
}
\f
scan_c_file (char *filename, const char *mode)
{
FILE *infile;
- register int c;
- register int commas;
- int minargs, maxargs;
int extension = filename[strlen (filename) - 1];
if (extension == 'o')
if (infile == NULL && extension == 'o')
{
- /* try .m */
+ /* Try .m. */
filename[strlen (filename) - 1] = 'm';
infile = fopen (filename, mode);
if (infile == NULL)
- filename[strlen (filename) - 1] = 'c'; /* don't confuse people */
+ filename[strlen (filename) - 1] = 'c'; /* Don't confuse people. */
}
- /* No error if non-ex input file */
+ /* No error if non-ex input file. */
if (infile == NULL)
{
perror (filename);
/* Reset extension to be able to detect duplicate files. */
filename[strlen (filename) - 1] = extension;
+ return scan_c_stream (infile);
+}
+
+/* Return 1 if next input from INFILE is equal to P, -1 if EOF,
+ 0 if input doesn't match. */
+
+static int
+stream_match (FILE *infile, const char *p)
+{
+ for (; *p; p++)
+ {
+ int c = getc (infile);
+ if (c == EOF)
+ return -1;
+ if (c != *p)
+ return 0;
+ }
+ return 1;
+}
+
+static int
+scan_c_stream (FILE *infile)
+{
+ int commas, minargs, maxargs;
+ int c = '\n';
- c = '\n';
while (!feof (infile))
{
int doc_keyword = 0;
int defvarperbufferflag = 0;
int defvarflag = 0;
enum global_type type = INVALID;
+ char *name IF_LINT (= 0);
if (c != '\n' && c != '\r')
{
if (c != 'F')
continue;
c = getc (infile);
- if (c != 'V')
- continue;
- c = getc (infile);
- if (c != 'A')
- continue;
- c = getc (infile);
- if (c != 'R')
- continue;
- c = getc (infile);
- if (c != '_')
- continue;
-
- defvarflag = 1;
-
- c = getc (infile);
- defvarperbufferflag = (c == 'P');
- if (generate_globals)
+ if (c == 'S')
{
- if (c == 'I')
- type = EMACS_INTEGER;
- else if (c == 'L')
- type = LISP_OBJECT;
- else if (c == 'B')
- type = BOOLEAN;
+ c = getc (infile);
+ if (c != 'Y')
+ continue;
+ c = getc (infile);
+ if (c != 'M')
+ continue;
+ c = getc (infile);
+ if (c != ' ' && c != '\t' && c != '(')
+ continue;
+ type = SYMBOL;
}
+ else if (c == 'V')
+ {
+ c = getc (infile);
+ if (c != 'A')
+ continue;
+ c = getc (infile);
+ if (c != 'R')
+ continue;
+ c = getc (infile);
+ if (c != '_')
+ continue;
- c = getc (infile);
- /* We need to distinguish between DEFVAR_BOOL and
- DEFVAR_BUFFER_DEFAULTS. */
- if (generate_globals && type == BOOLEAN && c != 'O')
- type = INVALID;
+ defvarflag = 1;
+
+ c = getc (infile);
+ defvarperbufferflag = (c == 'P');
+ if (generate_globals)
+ {
+ if (c == 'I')
+ type = EMACS_INTEGER;
+ else if (c == 'L')
+ type = LISP_OBJECT;
+ else if (c == 'B')
+ type = BOOLEAN;
+ }
+
+ c = getc (infile);
+ /* We need to distinguish between DEFVAR_BOOL and
+ DEFVAR_BUFFER_DEFAULTS. */
+ if (generate_globals && type == BOOLEAN && c != 'O')
+ type = INVALID;
+ }
+ else
+ continue;
}
else if (c == 'D')
{
}
else continue;
- if (generate_globals && (!defvarflag || defvarperbufferflag
- || type == INVALID))
+ if (generate_globals
+ && (!defvarflag || defvarperbufferflag || type == INVALID)
+ && !defunflag && type != SYMBOL)
continue;
while (c != '(')
c = getc (infile);
}
- /* Lisp variable or function name. */
- c = getc (infile);
- if (c != '"')
- continue;
- c = read_c_string_or_comment (infile, -1, 0, 0);
+ if (type != SYMBOL)
+ {
+ /* Lisp variable or function name. */
+ c = getc (infile);
+ if (c != '"')
+ continue;
+ c = read_c_string_or_comment (infile, -1, 0, 0);
+ }
if (generate_globals)
{
int i = 0;
- char *name;
+ char const *svalue = 0;
/* Skip "," and whitespace. */
do
/* Read in the identifier. */
do
{
+ if (c < 0)
+ goto eof;
input_buffer[i++] = c;
c = getc (infile);
}
- while (! (c == ',' || c == ' ' || c == '\t' ||
- c == '\n' || c == '\r'));
+ while (! (c == ',' || c == ' ' || c == '\t'
+ || c == '\n' || c == '\r'));
input_buffer[i] = '\0';
name = xmalloc (i + 1);
memcpy (name, input_buffer, i + 1);
- add_global (type, name);
- continue;
+
+ if (type == SYMBOL)
+ {
+ do
+ c = getc (infile);
+ while (c == ' ' || c == '\t' || c == '\n' || c == '\r');
+ if (c != '"')
+ continue;
+ c = read_c_string_or_comment (infile, -1, 0, 0);
+ svalue = xstrdup (input_buffer);
+ }
+
+ if (!defunflag)
+ {
+ add_global (type, name, 0, svalue);
+ continue;
+ }
}
+ if (type == SYMBOL)
+ continue;
+
/* DEFVAR_LISP ("name", addr, "doc")
DEFVAR_LISP ("name", addr /\* doc *\/)
DEFVAR_LISP ("name", addr, doc: /\* doc *\/) */
if (defunflag)
- commas = 5;
+ commas = generate_globals ? 4 : 5;
else if (defvarperbufferflag)
commas = 3;
else if (defvarflag)
commas = 1;
- else /* For DEFSIMPLE and DEFPRED */
+ else /* For DEFSIMPLE and DEFPRED. */
commas = 2;
while (commas)
if (c < 0)
goto eof;
ungetc (c, infile);
- if (commas == 2) /* pick up minargs */
+ if (commas == 2) /* Pick up minargs. */
scanned = fscanf (infile, "%d", &minargs);
- else /* pick up maxargs */
+ else /* Pick up maxargs. */
if (c == 'M' || c == 'U') /* MANY || UNEVALLED */
- maxargs = -1;
+ {
+ if (generate_globals)
+ maxargs = (c == 'M') ? -1 : -2;
+ else
+ maxargs = -1;
+ }
else
scanned = fscanf (infile, "%d", &maxargs);
if (scanned < 0)
c = getc (infile);
}
+ if (generate_globals)
+ {
+ struct global *g = add_global (FUNCTION, name, maxargs, 0);
+
+ /* The following code tries to recognize function attributes
+ specified after the docstring, e.g.:
+
+ DEFUN ("foo", Ffoo, Sfoo, X, Y, Z,
+ doc: /\* doc *\/
+ attributes: attribute1 attribute2 ...)
+ (Lisp_Object arg...)
+
+ Now only 'noreturn' and 'const' attributes are used. */
+
+ /* Advance to the end of docstring. */
+ c = getc (infile);
+ if (c == EOF)
+ goto eof;
+ int d = getc (infile);
+ if (d == EOF)
+ goto eof;
+ while (1)
+ {
+ if (c == '*' && d == '/')
+ break;
+ c = d, d = getc (infile);
+ if (d == EOF)
+ goto eof;
+ }
+ /* Skip spaces, if any. */
+ do
+ {
+ c = getc (infile);
+ if (c == EOF)
+ goto eof;
+ }
+ while (c == ' ' || c == '\n' || c == '\r' || c == '\t');
+ /* Check for 'attributes:' token. */
+ if (c == 'a' && stream_match (infile, "ttributes:"))
+ {
+ char *p = input_buffer;
+ /* Collect attributes up to ')'. */
+ while (1)
+ {
+ c = getc (infile);
+ if (c == EOF)
+ goto eof;
+ if (c == ')')
+ break;
+ if (p - input_buffer > sizeof (input_buffer))
+ abort ();
+ *p++ = c;
+ }
+ *p = 0;
+ if (strstr (input_buffer, "noreturn"))
+ g->flags |= DEFUN_noreturn;
+ if (strstr (input_buffer, "const"))
+ g->flags |= DEFUN_const;
+ }
+ continue;
+ }
+
while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
c = getc (infile);
int comment = c != '"';
int saw_usage;
- putc (037, outfile);
- putc (defvarflag ? 'V' : 'F', outfile);
- fprintf (outfile, "%s\n", input_buffer);
+ printf ("\037%c%s\n", defvarflag ? 'V' : 'F', input_buffer);
if (comment)
- getc (infile); /* Skip past `*' */
+ getc (infile); /* Skip past `*'. */
c = read_c_string_or_comment (infile, 1, comment, &saw_usage);
/* If this is a defun, find the arguments and print them. If
while (c != ')');
*p = '\0';
/* Output them. */
- fprintf (outfile, "\n\n");
- write_c_args (outfile, input_buffer, argbuf, minargs, maxargs);
+ fputs ("\n\n", stdout);
+ write_c_args (input_buffer, argbuf, minargs, maxargs);
}
else if (defunflag && maxargs == -1 && !saw_usage)
/* The DOC should provide the usage form. */
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.
+ uncompiled ones like loaddefs.el; aside from that, it is always the .elc
+ file that we should look at, and they are no problem because byte-compiler
+ output follows this convention.
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 "
+ An entry is output only if DOCSTRING has \ newline just after the opening ".
*/
static void
skip_white (infile);
}
+static int
+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
+ if (c != EOF)
+ ungetc (c, infile);
+ return 0;
+ }
+ return 1;
+}
+
+#define DEF_ELISP_FILE(fn) { #fn, sizeof(#fn) - 1 }
+
static int
scan_lisp_file (const char *filename, const char *mode)
{
FILE *infile;
register int c;
char *saved_string = 0;
+ /* These are the only files that are loaded uncompiled, and must
+ follow the conventions of the doc strings expected by this
+ function. These conventions are automatically followed by the
+ byte compiler when it produces the .elc files. */
+ static struct {
+ const char *fn;
+ size_t fl;
+ } const uncompiled[] = {
+ DEF_ELISP_FILE (loaddefs.el),
+ DEF_ELISP_FILE (loadup.el),
+ DEF_ELISP_FILE (charprop.el),
+ DEF_ELISP_FILE (cp51932.el),
+ DEF_ELISP_FILE (eucjp-ms.el)
+ };
+ int i, match;
+ size_t flen = strlen (filename);
if (generate_globals)
fatal ("scanning lisp file when -g specified", 0);
+ if (flen > 3 && !strcmp (filename + flen - 3, ".el"))
+ {
+ for (i = 0, match = 0; i < sizeof (uncompiled) / sizeof (uncompiled[0]);
+ i++)
+ {
+ if (uncompiled[i].fl <= flen
+ && !strcmp (filename + flen - uncompiled[i].fl, uncompiled[i].fn)
+ && (flen == uncompiled[i].fl
+ || IS_SLASH (filename[flen - uncompiled[i].fl - 1])))
+ {
+ match = 1;
+ break;
+ }
+ }
+ if (!match)
+ fatal ("uncompiled lisp file %s is not supported", filename);
+ }
infile = fopen (filename, mode);
if (infile == NULL)
{
perror (filename);
- return 0; /* No error */
+ return 0; /* No error. */
}
c = '\n';
type = 'F';
read_lisp_symbol (infile, buffer);
- /* Skip the arguments: either "nil" or a list in parens */
+ /* Skip the arguments: either "nil" or a list in parens. */
c = getc (infile);
if (c == 'n') /* nil */
|| ! strcmp (buffer, "defconst")
|| ! strcmp (buffer, "defcustom"))
{
- char c1 = 0, c2 = 0;
type = 'V';
read_lisp_symbol (infile, buffer);
if (saved_string == 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;
- }
- }
+ if (!search_lisp_doc_at_eol (infile))
+ continue;
}
else if (! strcmp (buffer, "custom-declare-variable")
|| ! strcmp (buffer, "defvaralias")
)
{
- char c1 = 0, c2 = 0;
type = 'V';
c = getc (infile);
}
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;
- 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;
- }
- }
+ if (!search_lisp_doc_at_eol (infile))
+ continue;
}
else if (! strcmp (buffer, "fset") || ! strcmp (buffer, "defalias"))
{
- char c1 = 0, c2 = 0;
type = 'F';
c = getc (infile);
}
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;
- 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;
- }
- }
+ if (!search_lisp_doc_at_eol (infile))
+ continue;
}
else if (! strcmp (buffer, "autoload"))
continue;
}
read_c_string_or_comment (infile, 0, 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' && c != '\r'))
- {
-#ifdef DEBUG
- fprintf (stderr, "## non-docstring in %s (%s)\n",
- buffer, filename);
-#endif
- continue;
- }
- }
+ if (!search_lisp_doc_at_eol (infile))
+ continue;
}
#ifdef DEBUG
else if (! strcmp (buffer, "if")
|| ! strcmp (buffer, "byte-code"))
- ;
+ continue;
#endif
else
continue;
}
- /* 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. */
+ /* 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);
+ printf ("\037%c%s\n", type, buffer);
if (saved_string)
{
- fputs (saved_string, outfile);
+ fputs (saved_string, stdout);
/* Don't use one dynamic doc string twice. */
free (saved_string);
saved_string = 0;