X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/4fda400e58339cdf962b5679bf05ebe62c6189c5..eb0f65b4fbbea60100b53cb40a1d7138d47ad0d2:/lib-src/make-docfile.c diff --git a/lib-src/make-docfile.c b/lib-src/make-docfile.c index 884b6c1001..bada8df9f7 100644 --- a/lib-src/make-docfile.c +++ b/lib-src/make-docfile.c @@ -1,6 +1,6 @@ /* Generate doc-string file for GNU Emacs from source files. -Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2014 Free Software +Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2015 Free Software Foundation, Inc. This file is part of GNU Emacs. @@ -36,6 +36,7 @@ along with GNU Emacs. If not, see . */ #include +#include #include #include /* config.h unconditionally includes this anyway */ @@ -63,6 +64,7 @@ along with GNU Emacs. If not, see . */ 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 int scan_c_stream (FILE *infile); static void start_globals (void); static void write_globals (void); @@ -106,6 +108,17 @@ xmalloc (unsigned int size) 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 * @@ -123,7 +136,6 @@ main (int argc, char **argv) { int i; int err_count = 0; - int first_infile; progname = argv[0]; @@ -167,16 +179,21 @@ main (int argc, char **argv) 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) @@ -528,13 +545,15 @@ write_c_args (char *func, char *buf, int minargs, int maxargs) } /* The types of globals. These are sorted roughly in decreasing alignment - order to avoid allocation gaps, except that functions are last. */ + order to avoid allocation gaps, except that symbols and functions + are last. */ enum global_type { INVALID, LISP_OBJECT, EMACS_INTEGER, BOOLEAN, + SYMBOL, FUNCTION }; @@ -543,17 +562,25 @@ struct global { enum global_type type; char *name; - int value; + 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, int value) +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, "...")) @@ -574,8 +601,14 @@ add_global (enum global_type type, char *name, int value) globals[num_globals - 1].type = type; globals[num_globals - 1].name = name; - globals[num_globals - 1].value = value; + 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 @@ -587,21 +620,58 @@ compare_globals (const void *a, const void *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 (void) +close_emacs_globals (int num_symbols) { - puts ("};"); - puts ("extern struct emacs_globals globals;"); + 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, seen_defun = 0; + 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 = 0; @@ -617,12 +687,13 @@ write_globals (void) case LISP_OBJECT: type = "Lisp_Object"; break; + case SYMBOL: case FUNCTION: if (!seen_defun) { - close_emacs_globals (); + close_emacs_globals (num_symbols); putchar ('\n'); - seen_defun = 1; + seen_defun = true; } break; default: @@ -635,50 +706,50 @@ write_globals (void) printf ("#define %s globals.f_%s\n", globals[i].name, globals[i].name); } + else if (globals[i].type == SYMBOL) + printf (("#define i%s %d\n" + "DEFINE_LISP_SYMBOL (%s)\n"), + globals[i].name, symnum++, 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 - || strcmp (globals[i].name, "Fexit_recursive_edit") == 0 - || strcmp (globals[i].name, "Fabort_recursive_edit") == 0) + if (globals[i].flags & DEFUN_noreturn) fputs ("_Noreturn ", stdout); printf ("EXFUN (%s, ", globals[i].name); - if (globals[i].value == -1) + if (globals[i].v.value == -1) fputs ("MANY", stdout); - else if (globals[i].value == -2) + else if (globals[i].v.value == -2) fputs ("UNEVALLED", stdout); else - printf ("%d", globals[i].value); + printf ("%d", globals[i].v.value); putchar (')'); - /* It would be nice to have a cleaner way to deal with these - special hacks, too. */ - if (strcmp (globals[i].name, "Fbyteorder") == 0 - || strcmp (globals[i].name, "Ftool_bar_height") == 0 - || strcmp (globals[i].name, "Fmax_char") == 0 - || strcmp (globals[i].name, "Fidentity") == 0) + if (globals[i].flags & DEFUN_const) fputs (" ATTRIBUTE_CONST", stdout); puts (";"); } - - while (i + 1 < num_globals - && !strcmp (globals[i].name, globals[i + 1].name)) - { - if (globals[i].type == FUNCTION - && globals[i].value != globals[i + 1].value) - error ("function '%s' defined twice with differing signatures", - globals[i].name); - ++i; - } } if (!seen_defun) - close_emacs_globals (); + 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); + puts ("};"); + puts ("#endif"); + + puts ("#define Qnil builtin_lisp_symbol (0)"); + puts ("#if DEFINE_NON_NIL_Q_SYMBOL_MACROS"); + num_symbols = 0; + for (int i = 0; i < num_globals; i++) + if (globals[i].type == SYMBOL && num_symbols++ != 0) + printf ("# define %s builtin_lisp_symbol (%d)\n", + globals[i].name, num_symbols - 1); + puts ("#endif"); } @@ -691,9 +762,6 @@ static int 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') @@ -719,8 +787,32 @@ scan_c_file (char *filename, const char *mode) /* 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; @@ -749,37 +841,53 @@ scan_c_file (char *filename, const char *mode) 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') { @@ -796,7 +904,7 @@ scan_c_file (char *filename, const char *mode) if (generate_globals && (!defvarflag || defvarperbufferflag || type == INVALID) - && !defunflag) + && !defunflag && type != SYMBOL) continue; while (c != '(') @@ -806,15 +914,19 @@ scan_c_file (char *filename, const char *mode) 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 const *svalue = 0; /* Skip "," and whitespace. */ do @@ -826,6 +938,8 @@ scan_c_file (char *filename, const char *mode) /* Read in the identifier. */ do { + if (c < 0) + goto eof; input_buffer[i++] = c; c = getc (infile); } @@ -836,13 +950,27 @@ scan_c_file (char *filename, const char *mode) name = xmalloc (i + 1); memcpy (name, input_buffer, i + 1); + 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); + 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 *\/) */ @@ -895,7 +1023,63 @@ scan_c_file (char *filename, const char *mode) if (generate_globals) { - add_global (FUNCTION, name, maxargs); + 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; }