/* Generate doc-string file for GNU Emacs from source files.
- Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2011
+ Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2012
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 */
+/* Defined to be emacs_main, sys_fopen, etc. in config.h. */
#undef main
#undef fopen
#undef chdir
#define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
#endif
-int scan_file (char *filename);
-int scan_lisp_file (const char *filename, const char *mode);
-int scan_c_file (char *filename, const char *mode);
-void fatal (const char *s1, const char *s2) NO_RETURN;
-void start_globals (void);
-void write_globals (void);
+/* 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 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
/* Print error message. `s1' is printf control string, `s2' is arg for it. */
/* VARARGS1 */
-void
+static void
error (const char *s1, const char *s2)
{
fprintf (stderr, "%s: ", progname);
/* Print error message and exit. */
/* VARARGS1 */
-void
+static _Noreturn void
fatal (const char *s1, const char *s2)
{
error (s1, s2);
/* Like malloc but get fatal error if memory is exhausted. */
-void *
+static void *
xmalloc (unsigned int size)
{
void *result = (void *) malloc (size);
/* Like realloc but get fatal error if memory is exhausted. */
-void *
+static void *
xrealloc (void *arg, unsigned int size)
{
void *result = (void *) realloc (arg, size);
}
/* Add a source file name boundary marker in the output file. */
-void
+static void
put_filename (char *filename)
{
char *tmp;
for (tmp = filename; *tmp; tmp++)
{
- if (IS_DIRECTORY_SEP(*tmp))
+ if (IS_DIRECTORY_SEP (*tmp))
filename = tmp + 1;
}
/* Read file FILENAME and output its doc strings to outfile. */
/* Return 1 if file is not found, 0 if it is found. */
-int
+static int
scan_file (char *filename)
{
return scan_c_file (filename, READ_TEXT);
}
-void
+static void
start_globals (void)
{
fprintf (outfile, "/* This file was auto-generated by make-docfile. */\n");
fprintf (outfile, "struct emacs_globals {\n");
}
\f
-char buf[128];
+static char input_buffer[128];
/* Some state during the execution of `read_c_string_or_comment'. */
struct rcsoc_state
/* Output CH to the file or buffer in STATE. Any pending newlines or
spaces are output first. */
-static INLINE void
+static inline void
put_char (int ch, struct rcsoc_state *state)
{
int out_ch;
at the beginning of a line will be removed, and *SAW_USAGE set to
true if any were encountered. */
-int
+static int
read_c_string_or_comment (FILE *infile, int printflag, int comment, int *saw_usage)
{
register int c;
struct rcsoc_state state;
state.in_file = infile;
- state.buf_ptr = (printflag < 0 ? buf : 0);
+ state.buf_ptr = (printflag < 0 ? input_buffer : 0);
state.out_file = (printflag > 0 ? outfile : 0);
state.pending_spaces = 0;
state.pending_newlines = 0;
/* 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. */
-void
+static void
write_c_args (FILE *out, char *func, char *buf, int minargs, int maxargs)
{
register char *p;
int in_ident = 0;
- char *ident_start;
+ char *ident_start IF_LINT (= NULL);
size_t ident_length = 0;
fprintf (out, "(fn");
maxargs--;
/* In C code, `default' is a reserved word, so we spell it
- `defalt'; unmangle that here. */
+ `defalt'; demangle that here. */
if (ident_length == 6 && strncmp (ident_start, "defalt", 6) == 0)
fprintf (out, "DEFAULT");
else
/* The types of globals. */
enum global_type
{
+ FUNCTION,
EMACS_INTEGER,
BOOLEAN,
LISP_OBJECT,
{
enum global_type type;
char *name;
+ int value;
};
/* All the variable names we saw while scanning C sources in `-g'
struct global *globals;
static void
-add_global (enum global_type type, char *name)
+add_global (enum global_type type, char *name, int value)
{
/* Ignore the one non-symbol that can occur. */
if (strcmp (name, "..."))
globals[num_globals - 1].type = type;
globals[num_globals - 1].name = name;
+ globals[num_globals - 1].value = value;
}
}
{
const struct global *ga = a;
const struct global *gb = b;
+
+ if (ga->type == FUNCTION)
+ {
+ if (gb->type != FUNCTION)
+ return 1;
+ }
+ else if (gb->type == FUNCTION)
+ return -1;
+
return strcmp (ga->name, gb->name);
}
-void
+static void
+close_emacs_globals (void)
+{
+ fprintf (outfile, "};\n");
+ fprintf (outfile, "extern struct emacs_globals globals;\n");
+}
+
+static void
write_globals (void)
{
- int i;
+ int i, seen_defun = 0;
qsort (globals, num_globals, sizeof (struct global), compare_globals);
for (i = 0; i < num_globals; ++i)
{
- char *type;
+ char const *type;
switch (globals[i].type)
{
case LISP_OBJECT:
type = "Lisp_Object";
break;
+ case FUNCTION:
+ if (!seen_defun)
+ {
+ close_emacs_globals ();
+ fprintf (outfile, "\n");
+ seen_defun = 1;
+ }
+ 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);
+ if (globals[i].type != FUNCTION)
+ {
+ fprintf (outfile, " %s f_%s;\n", type, globals[i].name);
+ fprintf (outfile, "#define %s globals.f_%s\n",
+ globals[i].name, globals[i].name);
+ }
+ else
+ {
+ /* It would be nice to have a cleaner way to deal with these
+ special hacks. */
+ if (strcmp (globals[i].name, "Fthrow") == 0
+ || strcmp (globals[i].name, "Ftop_level") == 0
+ || strcmp (globals[i].name, "Fkill_emacs") == 0)
+ fprintf (outfile, "_Noreturn ");
+ fprintf (outfile, "EXFUN (%s, ", globals[i].name);
+ if (globals[i].value == -1)
+ fprintf (outfile, "MANY");
+ else if (globals[i].value == -2)
+ fprintf (outfile, "UNEVALLED");
+ else
+ fprintf (outfile, "%d", globals[i].value);
+ fprintf (outfile, ");\n");
+ }
+
while (i + 1 < num_globals
&& !strcmp (globals[i].name, globals[i + 1].name))
++i;
}
- fprintf (outfile, "};\n");
- fprintf (outfile, "extern struct emacs_globals globals;\n");
+ if (!seen_defun)
+ close_emacs_globals ();
}
\f
Looks for DEFUN constructs such as are defined in ../src/lisp.h.
Accepts any word starting DEF... so it finds DEFSIMPLE and DEFPRED. */
-int
+static int
scan_c_file (char *filename, const char *mode)
{
FILE *infile;
register int c;
register int commas;
- register int defunflag;
- register int defvarperbufferflag;
- register int defvarflag;
int minargs, maxargs;
int extension = filename[strlen (filename) - 1];
- enum global_type type;
if (extension == 'o')
filename[strlen (filename) - 1] = 'c';
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);
while (!feof (infile))
{
int doc_keyword = 0;
+ int defunflag = 0;
+ int defvarperbufferflag = 0;
+ int defvarflag = 0;
+ enum global_type type = INVALID;
+ char *name;
if (c != '\n' && c != '\r')
{
continue;
defvarflag = 1;
- defunflag = 0;
c = getc (infile);
defvarperbufferflag = (c == 'P');
type = LISP_OBJECT;
else if (c == 'B')
type = BOOLEAN;
- else
- type = INVALID;
}
c = getc (infile);
continue;
c = getc (infile);
defunflag = c == 'U';
- defvarflag = 0;
- defvarperbufferflag = 0;
}
else continue;
- if (generate_globals && (!defvarflag || defvarperbufferflag
- || type == INVALID))
+ if (generate_globals
+ && (!defvarflag || defvarperbufferflag || type == INVALID)
+ && !defunflag)
continue;
while (c != '(')
if (generate_globals)
{
int i = 0;
- char *name;
/* Skip "," and whitespace. */
do
/* Read in the identifier. */
do
{
- buf[i++] = c;
+ input_buffer[i++] = c;
c = getc (infile);
}
- while (! (c == ',' || c == ' ' || c == '\t' ||
- c == '\n' || c == '\r'));
- buf[i] = '\0';
+ while (! (c == ',' || c == ' ' || c == '\t'
+ || c == '\n' || c == '\r'));
+ input_buffer[i] = '\0';
name = xmalloc (i + 1);
- memcpy (name, buf, i + 1);
- add_global (type, name);
- continue;
+ memcpy (name, input_buffer, i + 1);
+
+ if (!defunflag)
+ {
+ add_global (type, name, 0);
+ continue;
+ }
}
/* DEFVAR_LISP ("name", addr, "doc")
DEFVAR_LISP ("name", addr, doc: /\* doc *\/) */
if (defunflag)
- commas = 5;
+ commas = generate_globals ? 4 : 5;
else if (defvarperbufferflag)
- commas = 2;
+ 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)
+ {
+ add_global (FUNCTION, name, maxargs);
+ continue;
+ }
+
while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
c = getc (infile);
putc (037, outfile);
putc (defvarflag ? 'V' : 'F', outfile);
- fprintf (outfile, "%s\n", buf);
+ fprintf (outfile, "%s\n", 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
*p = '\0';
/* Output them. */
fprintf (outfile, "\n\n");
- write_c_args (outfile, buf, argbuf, minargs, maxargs);
+ write_c_args (outfile, input_buffer, argbuf, minargs, maxargs);
}
else if (defunflag && maxargs == -1 && !saw_usage)
/* The DOC should provide the usage form. */
- fprintf (stderr, "Missing `usage' for function `%s'.\n", buf);
+ fprintf (stderr, "Missing `usage' for function `%s'.\n",
+ input_buffer);
}
}
eof:
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 ".
*/
-void
+static void
skip_white (FILE *infile)
{
char c = ' ';
ungetc (c, infile);
}
-void
+static void
read_lisp_symbol (FILE *infile, char *buffer)
{
char c;
skip_white (infile);
}
-int
+static int
+search_lisp_doc_at_eol (FILE *infile)
+{
+ char 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 in %s (%s)\n",
+ buffer, filename);
+#endif
+ if (c != EOF)
+ ungetc (c, infile);
+ return 0;
+ }
+ return 1;
+}
+
+static int
scan_lisp_file (const char *filename, const char *mode)
{
FILE *infile;
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 */
}
}
+ /* defcustom can only occur in uncompiled Lisp files. */
else if (! strcmp (buffer, "defvar")
- || ! strcmp (buffer, "defconst"))
+ || ! 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);