]> code.delx.au - gnu-emacs/blobdiff - lib-src/make-docfile.c
Ibuffer change marks
[gnu-emacs] / lib-src / make-docfile.c
index f74b3d516d12af34795b502018979cdd79bf1338..f463f13699bfb1960e3f1439df927582a6ff3837 100644 (file)
@@ -1,14 +1,14 @@
 /* Generate doc-string file for GNU Emacs from source files.
 
-Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2015 Free Software
+Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2016 Free Software
 Foundation, Inc.
 
 This file is part of GNU Emacs.
 
 GNU Emacs is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
 
 GNU Emacs is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -36,6 +36,10 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include <config.h>
 
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stddef.h>
+#include <stdint.h>
 #include <stdio.h>
 #include <stdlib.h>   /* config.h unconditionally includes this anyway */
 
@@ -47,6 +51,8 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #endif /* WINDOWSNT */
 
 #include <binary-io.h>
+#include <intprops.h>
+#include <min-max.h>
 
 #ifdef DOS_NT
 /* Defined to be sys_chdir in ms-w32.h, but only #ifdef emacs, so this
@@ -60,60 +66,79 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #define IS_SLASH(c)  ((c) == '/')
 #endif /* not DOS_NT */
 
-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 scan_file (char *filename);
+static void scan_lisp_file (const char *filename, const char *mode);
+static void scan_c_file (char *filename, const char *mode);
+static void scan_c_stream (FILE *infile);
 static void start_globals (void);
 static void write_globals (void);
 
 #include <unistd.h>
 
 /* Name this program was invoked with.  */
-char *progname;
+static char *progname;
 
-/* Nonzero if this invocation is generating globals.h.  */
-int generate_globals;
+/* True if this invocation is generating globals.h.  */
+static bool generate_globals;
 
-/* Print error message.  `s1' is printf control string, `s2' is arg for it.  */
+/* Print error message.  Args are like vprintf.  */
 
-/* VARARGS1 */
-static void
-error (const char *s1, const char *s2)
+static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
+verror (char const *m, va_list ap)
 {
   fprintf (stderr, "%s: ", progname);
-  fprintf (stderr, s1, s2);
+  vfprintf (stderr, m, ap);
   fprintf (stderr, "\n");
 }
 
-/* Print error message and exit.  */
+/* Print error message.  Args are like printf.  */
 
-/* VARARGS1 */
-static _Noreturn void
-fatal (const char *s1, const char *s2)
+static void ATTRIBUTE_FORMAT_PRINTF (1, 2)
+error (char const *m, ...)
 {
-  error (s1, s2);
+  va_list ap;
+  va_start (ap, m);
+  verror (m, ap);
+  va_end (ap);
+}
+
+/* Print error message and exit.  Args are like printf.  */
+
+static _Noreturn void ATTRIBUTE_FORMAT_PRINTF (1, 2)
+fatal (char const *m, ...)
+{
+  va_list ap;
+  va_start (ap, m);
+  verror (m, ap);
+  va_end (ap);
   exit (EXIT_FAILURE);
 }
 
+static _Noreturn void
+memory_exhausted (void)
+{
+  fatal ("virtual memory exhausted");
+}
+
 /* Like malloc but get fatal error if memory is exhausted.  */
 
 static void *
-xmalloc (unsigned int size)
+xmalloc (ptrdiff_t size)
 {
-  void *result = (void *) malloc (size);
+  void *result = malloc (size);
   if (result == NULL)
-    fatal ("virtual memory exhausted", 0);
+    memory_exhausted ();
   return result;
 }
 
 /* Like realloc but get fatal error if memory is exhausted.  */
 
 static void *
-xrealloc (void *arg, unsigned int size)
+xrealloc (void *arg, ptrdiff_t size)
 {
-  void *result = (void *) realloc (arg, size);
+  void *result = realloc (arg, size);
   if (result == NULL)
-    fatal ("virtual memory exhausted", 0);
+    memory_exhausted ();
   return result;
 }
 
@@ -122,8 +147,6 @@ int
 main (int argc, char **argv)
 {
   int i;
-  int err_count = 0;
-  int first_infile;
 
   progname = argv[0];
 
@@ -158,7 +181,7 @@ main (int argc, char **argv)
     }
   if (argc > i && !strcmp (argv[i], "-g"))
     {
-      generate_globals = 1;
+      generate_globals = true;
       ++i;
     }
 
@@ -167,22 +190,30 @@ 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)
+           scan_file (argv[i]);
+       }
     }
 
-  if (err_count == 0 && generate_globals)
+  if (generate_globals)
     write_globals ();
 
-  return (err_count > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
+  if (ferror (stdout) || fclose (stdout) != 0)
+    fatal ("write error");
+
+  return EXIT_SUCCESS;
 }
 
 /* Add a source file name boundary marker in the output file.  */
@@ -201,22 +232,21 @@ put_filename (char *filename)
 }
 
 /* Read file FILENAME and output its doc strings to stdout.
-   Return 1 if file is not found, 0 if it is found.  */
+   Return true if file is found, false otherwise.  */
 
-static int
+static void
 scan_file (char *filename)
 {
-
-  size_t len = strlen (filename);
+  ptrdiff_t len = strlen (filename);
 
   if (!generate_globals)
     put_filename (filename);
   if (len > 4 && !strcmp (filename + len - 4, ".elc"))
-    return scan_lisp_file (filename, "rb");
+    scan_lisp_file (filename, "rb");
   else if (len > 3 && !strcmp (filename + len - 3, ".el"))
-    return scan_lisp_file (filename, "r");
+    scan_lisp_file (filename, "r");
   else
-    return scan_c_file (filename, "r");
+    scan_c_file (filename, "r");
 }
 
 static void
@@ -233,7 +263,7 @@ static char input_buffer[128];
 struct rcsoc_state
 {
   /* A count of spaces and newlines that have been read, but not output.  */
-  unsigned pending_spaces, pending_newlines;
+  intmax_t pending_spaces, pending_newlines;
 
   /* Where we're reading from.  */
   FILE *in_file;
@@ -250,16 +280,16 @@ struct rcsoc_state
      the input stream.  */
   const char *cur_keyword_ptr;
   /* Set to true if we saw an occurrence of KEYWORD.  */
-  int saw_keyword;
+  bool saw_keyword;
 };
 
 /* Output CH to the file or buffer in STATE.  Any pending newlines or
    spaces are output first.  */
 
 static void
-put_char (int ch, struct rcsoc_state *state)
+put_char (char ch, struct rcsoc_state *state)
 {
-  int out_ch;
+  char out_ch;
   do
     {
       if (state->pending_newlines > 0)
@@ -290,7 +320,7 @@ put_char (int ch, struct rcsoc_state *state)
    keyword, but were in fact not.  */
 
 static void
-scan_keyword_or_put_char (int ch, struct rcsoc_state *state)
+scan_keyword_or_put_char (char ch, struct rcsoc_state *state)
 {
   if (state->keyword
       && *state->cur_keyword_ptr == ch
@@ -302,7 +332,7 @@ scan_keyword_or_put_char (int ch, struct rcsoc_state *state)
       if (*++state->cur_keyword_ptr == '\0')
        /* Saw the whole keyword.  Set SAW_KEYWORD flag to true.  */
        {
-         state->saw_keyword = 1;
+         state->saw_keyword = true;
 
          /* Reset the scanning pointer.  */
          state->cur_keyword_ptr = state->keyword;
@@ -313,22 +343,29 @@ scan_keyword_or_put_char (int ch, struct rcsoc_state *state)
 
          /* Skip any whitespace between the keyword and the
             usage string.  */
+         int c;
          do
-           ch = getc (state->in_file);
-         while (ch == ' ' || ch == '\n');
+           c = getc (state->in_file);
+         while (c == ' ' || c == '\n');
 
          /* Output the open-paren we just read.  */
-         put_char (ch, state);
+         if (c != '(')
+           fatal ("Missing '(' after keyword");
+         put_char (c, state);
 
          /* Skip the function name and replace it with `fn'.  */
          do
-           ch = getc (state->in_file);
-         while (ch != ' ' && ch != ')');
+           {
+             c = getc (state->in_file);
+             if (c == EOF)
+               fatal ("Unexpected EOF after keyword");
+           }
+         while (c != ' ' && c != ')');
          put_char ('f', state);
          put_char ('n', state);
 
          /* Put back the last character.  */
-         ungetc (ch, state->in_file);
+         ungetc (c, state->in_file);
        }
     }
   else
@@ -352,18 +389,19 @@ scan_keyword_or_put_char (int ch, struct rcsoc_state *state)
 
 
 /* Skip a C string or C-style comment from INFILE, and return the
-   character that follows.  COMMENT non-zero means skip a comment.  If
+   byte that follows, or EOF.  COMMENT means skip a comment.  If
    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:'
+   If SAW_USAGE is non-null, 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.  */
 
 static int
-read_c_string_or_comment (FILE *infile, int printflag, int comment, int *saw_usage)
+read_c_string_or_comment (FILE *infile, int printflag, bool comment,
+                         bool *saw_usage)
 {
-  register int c;
+  int c;
   struct rcsoc_state state;
 
   state.in_file = infile;
@@ -373,7 +411,7 @@ read_c_string_or_comment (FILE *infile, int printflag, int comment, int *saw_usa
   state.pending_newlines = 0;
   state.keyword = (saw_usage ? "usage:" : 0);
   state.cur_keyword_ptr = state.keyword;
-  state.saw_keyword = 0;
+  state.saw_keyword = false;
 
   c = getc (infile);
   if (comment)
@@ -451,10 +489,10 @@ read_c_string_or_comment (FILE *infile, int printflag, int comment, int *saw_usa
 static void
 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;
+  char *p;
+  bool in_ident = false;
+  char *ident_start UNINIT;
+  ptrdiff_t ident_length = 0;
 
   fputs ("(fn", stdout);
 
@@ -474,12 +512,12 @@ write_c_args (char *func, char *buf, int minargs, int maxargs)
        {
          if (!in_ident)
            {
-             in_ident = 1;
+             in_ident = true;
              ident_start = p;
            }
          else
            {
-             in_ident = 0;
+             in_ident = false;
              ident_length = p - ident_start;
            }
        }
@@ -490,7 +528,7 @@ write_c_args (char *func, char *buf, int minargs, int maxargs)
        {
          if (ident_length == 0)
            {
-             error ("empty arg list for `%s' should be (void), not ()", func);
+             error ("empty arg list for '%s' should be (void), not ()", func);
              continue;
            }
 
@@ -528,13 +566,15 @@ write_c_args (char *func, char *buf, int minargs, int maxargs)
 }
 \f
 /* 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,39 +583,57 @@ 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 ptrdiff_t num_globals;
+static ptrdiff_t num_globals_allocated;
+static 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 const *name, int value,
+           char const *svalue)
 {
   /* Ignore the one non-symbol that can occur.  */
   if (strcmp (name, "..."))
     {
-      ++num_globals;
-
-      if (num_globals_allocated == 0)
+      if (num_globals == num_globals_allocated)
        {
-         num_globals_allocated = 100;
-         globals = xmalloc (num_globals_allocated * sizeof (struct global));
-       }
-      else if (num_globals == num_globals_allocated)
-       {
-         num_globals_allocated *= 2;
-         globals = xrealloc (globals,
-                             num_globals_allocated * sizeof (struct global));
+         ptrdiff_t num_globals_max = (min (PTRDIFF_MAX, SIZE_MAX)
+                                      / sizeof *globals);
+         if (num_globals_allocated == num_globals_max)
+           memory_exhausted ();
+         if (num_globals_allocated < num_globals_max / 2)
+           num_globals_allocated = 2 * num_globals_allocated + 1;
+         else
+           num_globals_allocated = num_globals_max;
+         globals = xrealloc (globals, num_globals_allocated * sizeof *globals);
        }
 
+      ++num_globals;
+
+      ptrdiff_t namesize = strlen (name) + 1;
+      char *buf = xmalloc (namesize + (svalue ? strlen (svalue) + 1 : 0));
       globals[num_globals - 1].type = type;
-      globals[num_globals - 1].name = name;
-      globals[num_globals - 1].value = value;
+      globals[num_globals - 1].name = strcpy (buf, name);
+      if (svalue)
+       globals[num_globals - 1].v.svalue = strcpy (buf + namesize, 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 +645,59 @@ 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 (ptrdiff_t 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[%td];\n"),
+         num_symbols);
 }
 
 static void
 write_globals (void)
 {
-  int i, seen_defun = 0;
+  ptrdiff_t i, j;
+  bool seen_defun = false;
+  ptrdiff_t symnum = 0;
+  ptrdiff_t 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);
+         free (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,16 +713,17 @@ 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:
-         fatal ("not a recognized DEFVAR_", 0);
+         fatal ("not a recognized DEFVAR_");
        }
 
       if (type)
@@ -635,51 +732,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 %td\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, "Fframe_windows_min_size") == 0
-             || strcmp (globals[i].name, "Fidentity") == 0
-             || strcmp (globals[i].name, "Fmax_char") == 0
-             || strcmp (globals[i].name, "Ftool_bar_height") == 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 (ptrdiff_t 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 (ptrdiff_t i = 0; i < num_globals; i++)
+    if (globals[i].type == SYMBOL && num_symbols++ != 0)
+      printf ("# define %s builtin_lisp_symbol (%td)\n",
+             globals[i].name, num_symbols - 1);
+  puts ("#endif");
 }
 
 \f
@@ -688,14 +784,11 @@ write_globals (void)
    Looks for DEFUN constructs such as are defined in ../src/lisp.h.
    Accepts any word starting DEF... so it finds DEFSIMPLE and DEFPRED.  */
 
-static int
+static void
 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];
+  char extension = filename[strlen (filename) - 1];
 
   if (extension == 'o')
     filename[strlen (filename) - 1] = 'c';
@@ -711,25 +804,49 @@ scan_c_file (char *filename, const char *mode)
         filename[strlen (filename) - 1] = 'c'; /* Don't confuse people.  */
     }
 
-  /* No error if non-ex input file.  */
   if (infile == NULL)
     {
       perror (filename);
-      return 0;
+      exit (EXIT_FAILURE);
     }
 
   /* Reset extension to be able to detect duplicate files.  */
   filename[strlen (filename) - 1] = extension;
+  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 void
+scan_c_stream (FILE *infile)
+{
+  int commas, minargs, maxargs;
+  int c = '\n';
 
-  c = '\n';
   while (!feof (infile))
     {
-      int doc_keyword = 0;
-      int defunflag = 0;
-      int defvarperbufferflag = 0;
-      int defvarflag = 0;
+      bool doc_keyword = false;
+      bool defunflag = false;
+      bool defvarperbufferflag = false;
+      bool defvarflag = false;
       enum global_type type = INVALID;
-      char *name IF_LINT (= 0);
+      static char *name;
+      static ptrdiff_t name_size;
 
       if (c != '\n' && c != '\r')
        {
@@ -750,37 +867,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 = true;
+
+             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')
        {
@@ -797,7 +930,7 @@ scan_c_file (char *filename, const char *mode)
 
       if (generate_globals
          && (!defvarflag || defvarperbufferflag || type == INVALID)
-         && !defunflag)
+         && !defunflag && type != SYMBOL)
        continue;
 
       while (c != '(')
@@ -807,15 +940,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, false, 0);
+       }
 
       if (generate_globals)
        {
-         int i = 0;
+         ptrdiff_t i = 0;
+         char const *svalue = 0;
 
          /* Skip "," and whitespace.  */
          do
@@ -827,6 +964,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);
            }
@@ -834,16 +973,39 @@ scan_c_file (char *filename, const char *mode)
                    || c == '\n' || c == '\r'));
          input_buffer[i] = '\0';
 
-         name = xmalloc (i + 1);
+         if (name_size <= i)
+           {
+             free (name);
+             name_size = i + 1;
+             ptrdiff_t doubled;
+             if (! INT_MULTIPLY_WRAPV (name_size, 2, &doubled)
+                 && doubled <= SIZE_MAX)
+               name_size = doubled;
+             name = xmalloc (name_size);
+           }
          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, false, 0);
+             svalue = 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 *\/)  */
@@ -896,7 +1058,65 @@ 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);
+         if (!g)
+           continue;
+
+         /* 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;
        }
 
@@ -904,7 +1124,7 @@ scan_c_file (char *filename, const char *mode)
        c = getc (infile);
 
       if (c == '"')
-       c = read_c_string_or_comment (infile, 0, 0, 0);
+       c = read_c_string_or_comment (infile, 0, false, 0);
 
       while (c != EOF && c != ',' && c != '/')
        c = getc (infile);
@@ -917,7 +1137,7 @@ scan_c_file (char *filename, const char *mode)
            c = getc (infile);
          if (c == ':')
            {
-             doc_keyword = 1;
+             doc_keyword = true;
              c = getc (infile);
              while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
                c = getc (infile);
@@ -930,8 +1150,8 @@ scan_c_file (char *filename, const char *mode)
                  ungetc (c, infile),
                  c == '*')))
        {
-         int comment = c != '"';
-         int saw_usage;
+         bool comment = c != '"';
+         bool saw_usage;
 
          printf ("\037%c%s\n", defvarflag ? 'V' : 'F', input_buffer);
 
@@ -980,13 +1200,13 @@ scan_c_file (char *filename, const char *mode)
            }
          else if (defunflag && maxargs == -1 && !saw_usage)
            /* The DOC should provide the usage form.  */
-           fprintf (stderr, "Missing `usage' for function `%s'.\n",
+           fprintf (stderr, "Missing 'usage' for function '%s'.\n",
                     input_buffer);
        }
     }
  eof:
-  fclose (infile);
-  return 0;
+  if (ferror (infile) || fclose (infile) != 0)
+    fatal ("read error");
 }
 \f
 /* Read a file of Lisp code, compiled or interpreted.
@@ -1062,7 +1282,7 @@ read_lisp_symbol (FILE *infile, char *buffer)
   skip_white (infile);
 }
 
-static int
+static bool
 search_lisp_doc_at_eol (FILE *infile)
 {
   int c = 0, c1 = 0, c2 = 0;
@@ -1082,20 +1302,19 @@ search_lisp_doc_at_eol (FILE *infile)
 #ifdef DEBUG
       fprintf (stderr, "## non-docstring found\n");
 #endif
-      if (c != EOF)
-       ungetc (c, infile);
-      return 0;
+      ungetc (c, infile);
+      return false;
     }
-  return 1;
+  return true;
 }
 
 #define DEF_ELISP_FILE(fn)  { #fn, sizeof(#fn) - 1 }
 
-static int
+static void
 scan_lisp_file (const char *filename, const char *mode)
 {
   FILE *infile;
-  register int c;
+  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
@@ -1103,7 +1322,7 @@ scan_lisp_file (const char *filename, const char *mode)
      byte compiler when it produces the .elc files.  */
   static struct {
     const char *fn;
-    size_t fl;
+    int fl;
   } const uncompiled[] = {
     DEF_ELISP_FILE (loaddefs.el),
     DEF_ELISP_FILE (loadup.el),
@@ -1111,22 +1330,22 @@ scan_lisp_file (const char *filename, const char *mode)
     DEF_ELISP_FILE (cp51932.el),
     DEF_ELISP_FILE (eucjp-ms.el)
   };
-  int i, match;
-  size_t flen = strlen (filename);
+  int i;
+  int flen = strlen (filename);
 
   if (generate_globals)
-    fatal ("scanning lisp file when -g specified", 0);
+    fatal ("scanning lisp file when -g specified");
   if (flen > 3 && !strcmp (filename + flen - 3, ".el"))
     {
-      for (i = 0, match = 0; i < sizeof (uncompiled) / sizeof (uncompiled[0]);
-          i++)
+      bool match = false;
+      for (i = 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;
+             match = true;
              break;
            }
        }
@@ -1138,7 +1357,7 @@ scan_lisp_file (const char *filename, const char *mode)
   if (infile == NULL)
     {
       perror (filename);
-      return 0;                                /* No error.  */
+      exit (EXIT_FAILURE);
     }
 
   c = '\n';
@@ -1162,22 +1381,24 @@ scan_lisp_file (const char *filename, const char *mode)
          c = getc (infile);
          if (c == '@')
            {
-             size_t length = 0;
-             size_t i;
+             ptrdiff_t length = 0;
+             ptrdiff_t i;
 
              /* Read the length.  */
              while ((c = getc (infile),
                      c >= '0' && c <= '9'))
                {
-                 length *= 10;
-                 length += c - '0';
+                 if (INT_MULTIPLY_WRAPV (length, 10, &length)
+                     || INT_ADD_WRAPV (length, c - '0', &length)
+                     || SIZE_MAX < length)
+                   memory_exhausted ();
                }
 
              if (length <= 1)
-               fatal ("invalid dynamic doc string length", "");
+               fatal ("invalid dynamic doc string length");
 
              if (c != ' ')
-               fatal ("space not found after dynamic doc string length", "");
+               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.
@@ -1186,7 +1407,7 @@ scan_lisp_file (const char *filename, const char *mode)
 
              /* Read in the contents.  */
              free (saved_string);
-             saved_string = (char *) xmalloc (length);
+             saved_string = xmalloc (length);
              for (i = 0; i < length; i++)
                saved_string[i] = getc (infile);
              /* The last character is a ^_.
@@ -1385,7 +1606,7 @@ scan_lisp_file (const char *filename, const char *mode)
                       buffer, filename);
              continue;
            }
-         read_c_string_or_comment (infile, 0, 0, 0);
+         read_c_string_or_comment (infile, 0, false, 0);
 
          if (saved_string == 0)
            if (!search_lisp_doc_at_eol (infile))
@@ -1421,10 +1642,11 @@ scan_lisp_file (const char *filename, const char *mode)
          saved_string = 0;
        }
       else
-       read_c_string_or_comment (infile, 1, 0, 0);
+       read_c_string_or_comment (infile, 1, false, 0);
     }
-  fclose (infile);
-  return 0;
+  free (saved_string);
+  if (ferror (infile) || fclose (infile) != 0)
+    fatal ("%s: read error", filename);
 }