X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/2db38a6f98c2abb42b746064ce97417cccc27e68..40d83b412f584cc02e68d4eac8fd5e6eb769e2fe:/lib-src/make-docfile.c diff --git a/lib-src/make-docfile.c b/lib-src/make-docfile.c index af1928c63f..f900ea42e9 100644 --- a/lib-src/make-docfile.c +++ b/lib-src/make-docfile.c @@ -1,6 +1,5 @@ /* Generate doc-string file for GNU Emacs from source files. - Copyright (C) 1985, 1986, 1992, 1993, 1994, 1997, 1999, 2000, 2001, - 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 + Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2011 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -42,11 +41,11 @@ along with GNU Emacs. If not, see . */ #undef chdir #include +#include #ifdef MSDOS #include #endif /* MSDOS */ #ifdef WINDOWSNT -#include #include #include #endif /* WINDOWSNT */ @@ -67,9 +66,12 @@ along with GNU Emacs. If not, see . */ #define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP) #endif -int scan_file (); -int scan_lisp_file (); -int scan_c_file (); +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 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 @@ -77,9 +79,7 @@ int scan_c_file (); #undef chdir #endif -#ifdef HAVE_UNISTD_H #include -#endif /* Stdio stream for output to the DOC file. */ FILE *outfile; @@ -87,12 +87,14 @@ FILE *outfile; /* Name this program was invoked with. */ char *progname; +/* Nonzero if this invocation is generating globals.h. */ +int generate_globals; + /* Print error message. `s1' is printf control string, `s2' is arg for it. */ /* VARARGS1 */ -void -error (s1, s2) - char *s1, *s2; +static void +error (const char *s1, const char *s2) { fprintf (stderr, "%s: ", progname); fprintf (stderr, s1, s2); @@ -102,9 +104,8 @@ error (s1, s2) /* Print error message and exit. */ /* VARARGS1 */ -void -fatal (s1, s2) - char *s1, *s2; +static void +fatal (const char *s1, const char *s2) { error (s1, s2); exit (EXIT_FAILURE); @@ -112,20 +113,29 @@ fatal (s1, s2) /* Like malloc but get fatal error if memory is exhausted. */ -void * -xmalloc (size) - unsigned int size; +static void * +xmalloc (unsigned int size) { void *result = (void *) malloc (size); if (result == NULL) fatal ("virtual memory exhausted", 0); return result; } + +/* Like realloc but get fatal error if memory is exhausted. */ + +static void * +xrealloc (void *arg, unsigned int size) +{ + void *result = (void *) realloc (arg, size); + if (result == NULL) + fatal ("virtual memory exhausted", 0); + return result; +} + int -main (argc, argv) - int argc; - char **argv; +main (int argc, char **argv) { int i; int err_count = 0; @@ -164,13 +174,25 @@ main (argc, argv) } if (argc > i + 1 && !strcmp (argv[i], "-d")) { - chdir (argv[i + 1]); + if (chdir (argv[i + 1]) != 0) + { + perror (argv[i + 1]); + return EXIT_FAILURE; + } i += 2; } + if (argc > i && !strcmp (argv[i], "-g")) + { + generate_globals = 1; + ++i; + } if (outfile == 0) fatal ("No output file specified", ""); + if (generate_globals) + start_globals (); + first_infile = i; for (; i < argc; i++) { @@ -182,13 +204,16 @@ main (argc, argv) if (j == i) err_count += scan_file (argv[i]); } + + if (err_count == 0 && generate_globals) + write_globals (); + return (err_count > 0 ? EXIT_FAILURE : EXIT_SUCCESS); } /* Add a source file name boundary marker in the output file. */ -void -put_filename (filename) - char *filename; +static void +put_filename (char *filename) { char *tmp; @@ -206,13 +231,14 @@ put_filename (filename) /* 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; +static int +scan_file (char *filename) { - int len = strlen (filename); - put_filename (filename); + size_t len = strlen (filename); + + if (!generate_globals) + put_filename (filename); if (len > 4 && !strcmp (filename + len - 4, ".elc")) return scan_lisp_file (filename, READ_BINARY); else if (len > 3 && !strcmp (filename + len - 3, ".el")) @@ -220,8 +246,16 @@ scan_file (filename) else return scan_c_file (filename, READ_TEXT); } + +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"); +} -char buf[128]; +static char input_buffer[128]; /* Some state during the execution of `read_c_string_or_comment'. */ struct rcsoc_state @@ -239,11 +273,11 @@ struct rcsoc_state /* A keyword we look for at the beginning of lines. If found, it is not copied, and SAW_KEYWORD is set to true. */ - char *keyword; - /* The current point we've reached in an occurance of KEYWORD in + const char *keyword; + /* The current point we've reached in an occurrence of KEYWORD in the input stream. */ - char *cur_keyword_ptr; - /* Set to true if we saw an occurance of KEYWORD. */ + const char *cur_keyword_ptr; + /* Set to true if we saw an occurrence of KEYWORD. */ int saw_keyword; }; @@ -251,9 +285,7 @@ struct rcsoc_state spaces are output first. */ static INLINE void -put_char (ch, state) - int ch; - struct rcsoc_state *state; +put_char (int ch, struct rcsoc_state *state) { int out_ch; do @@ -286,9 +318,7 @@ put_char (ch, state) keyword, but were in fact not. */ static void -scan_keyword_or_put_char (ch, state) - int ch; - struct rcsoc_state *state; +scan_keyword_or_put_char (int ch, struct rcsoc_state *state) { if (state->keyword && *state->cur_keyword_ptr == ch @@ -336,7 +366,7 @@ scan_keyword_or_put_char (ch, state) keyword, but it was a false alarm. Output the part we scanned. */ { - char *p; + const char *p; for (p = state->keyword; p < state->cur_keyword_ptr; p++) put_char (*p, state); @@ -354,22 +384,18 @@ scan_keyword_or_put_char (ch, state) 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. - If SAW_USAGE is non-zero, then any occurances of the string `usage:' + If SAW_USAGE is non-zero, then any occurrences of the string `usage:' at the beginning of a line will be removed, and *SAW_USAGE set to true if any were encountered. */ -int -read_c_string_or_comment (infile, printflag, comment, saw_usage) - FILE *infile; - int printflag; - int *saw_usage; - int comment; +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; @@ -450,16 +476,13 @@ read_c_string_or_comment (infile, printflag, comment, saw_usage) /* 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 -write_c_args (out, func, buf, minargs, maxargs) - FILE *out; - char *func, *buf; - int minargs, maxargs; +static void +write_c_args (FILE *out, char *func, char *buf, int minargs, int maxargs) { register char *p; int in_ident = 0; - int just_spaced = 0; - int need_space = 1; + char *ident_start; + size_t ident_length = 0; fprintf (out, "(fn"); @@ -469,9 +492,8 @@ write_c_args (out, func, buf, minargs, maxargs) for (p = buf; *p; p++) { char c = *p; - int ident_start = 0; - /* Notice when we start printing a new identifier. */ + /* Notice when a new identifier starts. */ if ((('A' <= c && c <= 'Z') || ('a' <= c && c <= 'z') || ('0' <= c && c <= '9') @@ -481,72 +503,161 @@ write_c_args (out, func, buf, minargs, maxargs) if (!in_ident) { in_ident = 1; - ident_start = 1; + ident_start = p; + } + else + { + in_ident = 0; + ident_length = p - ident_start; + } + } + + /* Found the end of an argument, write out the last seen + identifier. */ + if (c == ',' || c == ')') + { + if (ident_length == 0) + { + error ("empty arg list for `%s' should be (void), not ()", func); + continue; + } - if (need_space) - putc (' ', out); + if (strncmp (ident_start, "void", ident_length) == 0) + continue; - if (minargs == 0 && maxargs > 0) - fprintf (out, "&optional "); - just_spaced = 1; + putc (' ', out); - minargs--; - maxargs--; - } + if (minargs == 0 && maxargs > 0) + fprintf (out, "&optional "); + + minargs--; + maxargs--; + + /* In C code, `default' is a reserved word, so we spell it + `defalt'; unmangle that here. */ + if (ident_length == 6 && strncmp (ident_start, "defalt", 6) == 0) + fprintf (out, "DEFAULT"); else - in_ident = 0; + while (ident_length-- > 0) + { + c = *ident_start++; + if (c >= 'a' && c <= 'z') + /* Upcase the letter. */ + c += 'A' - 'a'; + else if (c == '_') + /* Print underscore as hyphen. */ + c = '-'; + putc (c, out); + } } + } + + putc (')', out); +} + +/* The types of globals. */ +enum global_type +{ + EMACS_INTEGER, + BOOLEAN, + LISP_OBJECT, + INVALID +}; + +/* A single global. */ +struct global +{ + enum global_type type; + char *name; +}; - /* Print the C argument list as it would appear in lisp: - 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. */ - if (ident_start - && strncmp (p, "defalt", 6) == 0 - && ! (('A' <= p[6] && p[6] <= 'Z') - || ('a' <= p[6] && p[6] <= 'z') - || ('0' <= p[6] && p[6] <= '9') - || p[6] == '_')) +/* 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) +{ + /* Ignore the one non-symbol that can occur. */ + if (strcmp (name, "...")) + { + ++num_globals; + + if (num_globals_allocated == 0) + { + num_globals_allocated = 100; + globals = xmalloc (num_globals_allocated * sizeof (struct global)); + } + else if (num_globals == num_globals_allocated) { - fprintf (out, "DEFAULT"); - p += 5; - in_ident = 0; - just_spaced = 0; + num_globals_allocated *= 2; + globals = xrealloc (globals, + num_globals_allocated * sizeof (struct global)); } - else if (c != ' ' || !just_spaced) + + globals[num_globals - 1].type = type; + globals[num_globals - 1].name = name; + } +} + +static int +compare_globals (const void *a, const void *b) +{ + const struct global *ga = a; + const struct global *gb = b; + return strcmp (ga->name, gb->name); +} + +static void +write_globals (void) +{ + int i; + qsort (globals, num_globals, sizeof (struct global), compare_globals); + for (i = 0; i < num_globals; ++i) + { + char const *type; + + switch (globals[i].type) { - if (c >= 'a' && c <= 'z') - /* Upcase the letter. */ - c += 'A' - 'a'; - putc (c, out); + case EMACS_INTEGER: + type = "EMACS_INT"; + break; + case BOOLEAN: + type = "int"; + break; + case LISP_OBJECT: + type = "Lisp_Object"; + break; + default: + fatal ("not a recognized DEFVAR_", 0); } - just_spaced = c == ' '; - need_space = 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; } + + fprintf (outfile, "};\n"); + fprintf (outfile, "extern struct emacs_globals globals;\n"); } + /* Read through a c file. If a .o file is named, the corresponding .c or .m file is read instead. Looks for DEFUN constructs such as are defined in ../src/lisp.h. Accepts any word starting DEF... so it finds DEFSIMPLE and DEFPRED. */ -int -scan_c_file (filename, mode) - char *filename, *mode; +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]; @@ -578,6 +689,10 @@ scan_c_file (filename, mode) while (!feof (infile)) { int doc_keyword = 0; + int defunflag = 0; + int defvarperbufferflag = 0; + int defvarflag = 0; + enum global_type type = INVALID; if (c != '\n' && c != '\r') { @@ -611,12 +726,24 @@ scan_c_file (filename, mode) continue; defvarflag = 1; - defunflag = 0; 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 if (c == 'D') { @@ -628,11 +755,13 @@ scan_c_file (filename, mode) continue; c = getc (infile); defunflag = c == 'U'; - defvarflag = 0; - defvarperbufferflag = 0; } else continue; + if (generate_globals && (!defvarflag || defvarperbufferflag + || type == INVALID)) + continue; + while (c != '(') { if (c < 0) @@ -646,6 +775,34 @@ scan_c_file (filename, mode) continue; c = read_c_string_or_comment (infile, -1, 0, 0); + if (generate_globals) + { + int i = 0; + char *name; + + /* Skip "," and whitespace. */ + do + { + c = getc (infile); + } + while (c == ',' || c == ' ' || c == '\t' || c == '\n' || c == '\r'); + + /* Read in the identifier. */ + do + { + input_buffer[i++] = c; + c = getc (infile); + } + 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; + } + /* DEFVAR_LISP ("name", addr, "doc") DEFVAR_LISP ("name", addr /\* doc *\/) DEFVAR_LISP ("name", addr, doc: /\* doc *\/) */ @@ -653,7 +810,7 @@ scan_c_file (filename, mode) if (defunflag) commas = 5; else if (defvarperbufferflag) - commas = 2; + commas = 3; else if (defvarflag) commas = 1; else /* For DEFSIMPLE and DEFPRED */ @@ -667,6 +824,7 @@ scan_c_file (filename, mode) if (defunflag && (commas == 1 || commas == 2)) { + int scanned = 0; do c = getc (infile); while (c == ' ' || c == '\n' || c == '\r' || c == '\t'); @@ -674,12 +832,14 @@ scan_c_file (filename, mode) goto eof; ungetc (c, infile); if (commas == 2) /* pick up minargs */ - fscanf (infile, "%d", &minargs); + scanned = fscanf (infile, "%d", &minargs); else /* pick up maxargs */ if (c == 'M' || c == 'U') /* MANY || UNEVALLED */ maxargs = -1; else - fscanf (infile, "%d", &maxargs); + scanned = fscanf (infile, "%d", &maxargs); + if (scanned < 0) + goto eof; } } @@ -723,7 +883,7 @@ scan_c_file (filename, mode) 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 `*' */ @@ -766,11 +926,12 @@ scan_c_file (filename, mode) *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: @@ -814,9 +975,8 @@ scan_c_file (filename, mode) An entry is output only if DOCSTRING has \ newline just after the opening " */ -void -skip_white (infile) - FILE *infile; +static void +skip_white (FILE *infile) { char c = ' '; while (c == ' ' || c == '\t' || c == '\n' || c == '\r') @@ -824,10 +984,8 @@ skip_white (infile) ungetc (c, infile); } -void -read_lisp_symbol (infile, buffer) - FILE *infile; - char *buffer; +static void +read_lisp_symbol (FILE *infile, char *buffer) { char c; char *fillp = buffer; @@ -854,14 +1012,16 @@ read_lisp_symbol (infile, buffer) skip_white (infile); } -int -scan_lisp_file (filename, mode) - char *filename, *mode; +static int +scan_lisp_file (const char *filename, const char *mode) { FILE *infile; register int c; char *saved_string = 0; + if (generate_globals) + fatal ("scanning lisp file when -g specified", 0); + infile = fopen (filename, mode); if (infile == NULL) { @@ -890,8 +1050,8 @@ scan_lisp_file (filename, mode) c = getc (infile); if (c == '@') { - int length = 0; - int i; + size_t length = 0; + size_t i; /* Read the length. */ while ((c = getc (infile), @@ -901,6 +1061,12 @@ scan_lisp_file (filename, mode) length += c - '0'; } + if (length <= 1) + fatal ("invalid dynamic doc string length", ""); + + if (c != ' ') + fatal ("space not found after dynamic doc string length", ""); + /* 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. */ @@ -916,7 +1082,7 @@ scan_lisp_file (filename, mode) but it is redundant in DOC. So get rid of it here. */ saved_string[length - 1] = 0; /* Skip the line break. */ - while (c == '\n' && c == '\r') + while (c == '\n' || c == '\r') c = getc (infile); /* Skip the following line. */ while (c != '\n' && c != '\r') @@ -1222,7 +1388,5 @@ scan_lisp_file (filename, mode) return 0; } -/* arch-tag: f7203aaf-991a-4238-acb5-601db56f2894 - (do not change this comment) */ /* make-docfile.c ends here */