root/lib-src/make-docfile.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ATTRIBUTE_FORMAT_PRINTF
  2. ATTRIBUTE_FORMAT_PRINTF
  3. ATTRIBUTE_FORMAT_PRINTF
  4. memory_exhausted
  5. xmalloc
  6. xrealloc
  7. main
  8. put_filename
  9. scan_file
  10. start_globals
  11. put_char
  12. scan_keyword_or_put_char
  13. read_c_string_or_comment
  14. write_c_args
  15. add_global
  16. compare_globals
  17. close_emacs_globals
  18. write_globals
  19. scan_c_file
  20. stream_match
  21. scan_c_stream

     1 /* Generate doc-string file for GNU Emacs from source files.
     2 
     3 Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2023 Free Software
     4 Foundation, Inc.
     5 
     6 This file is part of GNU Emacs.
     7 
     8 GNU Emacs is free software: you can redistribute it and/or modify
     9 it under the terms of the GNU General Public License as published by
    10 the Free Software Foundation, either version 3 of the License, or (at
    11 your option) any later version.
    12 
    13 GNU Emacs is distributed in the hope that it will be useful,
    14 but WITHOUT ANY WARRANTY; without even the implied warranty of
    15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    16 GNU General Public License for more details.
    17 
    18 You should have received a copy of the GNU General Public License
    19 along with GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.  */
    20 
    21 
    22 /* The arguments given to this program are all the C files
    23  of GNU Emacs.  .c files are allowed.
    24  A .o file can also be specified; the .c file it was made from is used.
    25  This helps the makefile pass the correct list of files.
    26  Option -d DIR means change to DIR before looking for files.
    27 
    28  The results, which go to standard output or to a file
    29  specified with -a or -o (-a to append, -o to start from nothing),
    30  are entries containing function or variable names and their documentation.
    31  Each entry starts with a ^_ character.
    32  Then comes F for a function or V for a variable.
    33  Then comes the function or variable name, terminated with a newline.
    34  Then comes the documentation for that function or variable.
    35  */
    36 
    37 #include <config.h>
    38 
    39 #include <stdarg.h>
    40 #include <stddef.h>
    41 #include <stdint.h>
    42 #include <stdlib.h>
    43 #include <string.h>
    44 
    45 #include <attribute.h>
    46 #include <binary-io.h>
    47 #include <c-ctype.h>
    48 #include <intprops.h>
    49 #include <min-max.h>
    50 #include <unlocked-io.h>
    51 
    52 #ifdef WINDOWSNT
    53 /* Defined to be sys_fopen in ms-w32.h, but only #ifdef emacs, so this
    54    is really just insurance.  */
    55 #undef fopen
    56 #include <direct.h>
    57 #endif /* WINDOWSNT */
    58 
    59 #ifdef DOS_NT
    60 /* Defined to be sys_chdir in ms-w32.h, but only #ifdef emacs, so this
    61    is really just insurance.
    62 
    63    Similarly, msdos defines this as sys_chdir, but we're not linking with the
    64    file where that function is defined.  */
    65 #undef chdir
    66 #endif /* not DOS_NT */
    67 
    68 static void scan_file (char *filename);
    69 static void scan_c_file (char *filename, const char *mode);
    70 static void scan_c_stream (FILE *infile);
    71 static void start_globals (void);
    72 static void write_globals (void);
    73 
    74 #include <unistd.h>
    75 
    76 /* Name this program was invoked with.  */
    77 static char *progname;
    78 
    79 /* True if this invocation is generating globals.h.  */
    80 static bool generate_globals;
    81 
    82 /* Print error message.  Args are like vprintf.  */
    83 
    84 static void ATTRIBUTE_FORMAT_PRINTF (1, 0)
    85 verror (char const *m, va_list ap)
    86 {
    87   fprintf (stderr, "%s: ", progname);
    88   vfprintf (stderr, m, ap);
    89   fprintf (stderr, "\n");
    90 }
    91 
    92 /* Print error message.  Args are like printf.  */
    93 
    94 static void ATTRIBUTE_FORMAT_PRINTF (1, 2)
    95 error (char const *m, ...)
    96 {
    97   va_list ap;
    98   va_start (ap, m);
    99   verror (m, ap);
   100   va_end (ap);
   101 }
   102 
   103 /* Print error message and exit.  Args are like printf.  */
   104 
   105 static _Noreturn void ATTRIBUTE_FORMAT_PRINTF (1, 2)
   106 fatal (char const *m, ...)
   107 {
   108   va_list ap;
   109   va_start (ap, m);
   110   verror (m, ap);
   111   va_end (ap);
   112   exit (EXIT_FAILURE);
   113 }
   114 
   115 static _Noreturn void
   116 memory_exhausted (void)
   117 {
   118   fatal ("virtual memory exhausted");
   119 }
   120 
   121 /* Like malloc but get fatal error if memory is exhausted.  */
   122 
   123 static void * ATTRIBUTE_MALLOC
   124 xmalloc (ptrdiff_t size)
   125 {
   126   void *result = malloc (size);
   127   if (result == NULL)
   128     memory_exhausted ();
   129   return result;
   130 }
   131 
   132 /* Like realloc but get fatal error if memory is exhausted.  */
   133 
   134 static void *
   135 xrealloc (void *arg, ptrdiff_t size)
   136 {
   137   void *result = realloc (arg, size);
   138   if (result == NULL)
   139     memory_exhausted ();
   140   return result;
   141 }
   142 
   143 
   144 int
   145 main (int argc, char **argv)
   146 {
   147   int i;
   148 
   149   progname = argv[0];
   150 
   151   /* If first two args are -o FILE, output to FILE.  */
   152   i = 1;
   153   if (argc > i + 1 && !strcmp (argv[i], "-o"))
   154     {
   155       if (! freopen (argv[i + 1], "w", stdout))
   156         {
   157           perror (argv[i + 1]);
   158           return EXIT_FAILURE;
   159         }
   160       i += 2;
   161     }
   162   if (argc > i + 1 && !strcmp (argv[i], "-a"))
   163     {
   164       if (! freopen (argv[i + 1], "a", stdout))
   165         {
   166           perror (argv[i + 1]);
   167           return EXIT_FAILURE;
   168         }
   169       i += 2;
   170     }
   171   if (argc > i + 1 && !strcmp (argv[i], "-d"))
   172     {
   173       if (chdir (argv[i + 1]) != 0)
   174         {
   175           perror (argv[i + 1]);
   176           return EXIT_FAILURE;
   177         }
   178       i += 2;
   179     }
   180   if (argc > i && !strcmp (argv[i], "-g"))
   181     {
   182       generate_globals = true;
   183       ++i;
   184     }
   185 
   186   set_binary_mode (fileno (stdout), O_BINARY);
   187 
   188   if (generate_globals)
   189     start_globals ();
   190 
   191   if (argc <= i)
   192     scan_c_stream (stdin);
   193   else
   194     {
   195       int first_infile = i;
   196       for (; i < argc; i++)
   197         {
   198           int j;
   199           /* Don't process one file twice.  */
   200           for (j = first_infile; j < i; j++)
   201             if (strcmp (argv[i], argv[j]) == 0)
   202               break;
   203           if (j == i)
   204             scan_file (argv[i]);
   205         }
   206     }
   207 
   208   if (generate_globals)
   209     write_globals ();
   210 
   211   if (ferror (stdout) || fclose (stdout) != 0)
   212     fatal ("write error");
   213 
   214   return EXIT_SUCCESS;
   215 }
   216 
   217 /* Add a source file name boundary marker in the output file.  */
   218 static void
   219 put_filename (char *filename)
   220 {
   221   char *tmp;
   222 
   223   for (tmp = filename; *tmp; tmp++)
   224     {
   225       if (IS_DIRECTORY_SEP (*tmp))
   226         filename = tmp + 1;
   227     }
   228 
   229   printf ("\037S%s\n", filename);
   230 }
   231 
   232 /* Read file FILENAME and output its doc strings to stdout.
   233    Return true if file is found, false otherwise.  */
   234 
   235 static void
   236 scan_file (char *filename)
   237 {
   238   if (!generate_globals)
   239     put_filename (filename);
   240   scan_c_file (filename, "r");
   241 }
   242 
   243 static void
   244 start_globals (void)
   245 {
   246   puts ("/* This file was auto-generated by make-docfile.  */");
   247   puts ("/* DO NOT EDIT.  */");
   248   puts ("struct emacs_globals {");
   249 }
   250 
   251 static char input_buffer[128];
   252 
   253 /* Some state during the execution of `read_c_string_or_comment'.  */
   254 struct rcsoc_state
   255 {
   256   /* A count of spaces and newlines that have been read, but not output.  */
   257   intmax_t pending_spaces, pending_newlines;
   258 
   259   /* Where we're reading from.  */
   260   FILE *in_file;
   261 
   262   /* If non-zero, a buffer into which to copy characters.  */
   263   char *buf_ptr;
   264   /* If non-zero, a file into which to copy characters.  */
   265   FILE *out_file;
   266 
   267   /* A keyword we look for at the beginning of lines.  If found, it is
   268      not copied, and SAW_KEYWORD is set to true.  */
   269   const char *keyword;
   270   /* The current point we've reached in an occurrence of KEYWORD in
   271      the input stream.  */
   272   const char *cur_keyword_ptr;
   273   /* Set to true if we saw an occurrence of KEYWORD.  */
   274   bool saw_keyword;
   275 };
   276 
   277 /* Output CH to the file or buffer in STATE.  Any pending newlines or
   278    spaces are output first.  */
   279 
   280 static void
   281 put_char (char ch, struct rcsoc_state *state)
   282 {
   283   char out_ch;
   284   do
   285     {
   286       if (state->pending_newlines > 0)
   287         {
   288           state->pending_newlines--;
   289           out_ch = '\n';
   290         }
   291       else if (state->pending_spaces > 0)
   292         {
   293           state->pending_spaces--;
   294           out_ch = ' ';
   295         }
   296       else
   297         out_ch = ch;
   298 
   299       if (state->out_file)
   300         putc (out_ch, state->out_file);
   301       if (state->buf_ptr)
   302         *state->buf_ptr++ = out_ch;
   303     }
   304   while (out_ch != ch);
   305 }
   306 
   307 /* If in the middle of scanning a keyword, continue scanning with
   308    character CH, otherwise output CH to the file or buffer in STATE.
   309    Any pending newlines or spaces are output first, as well as any
   310    previously scanned characters that were thought to be part of a
   311    keyword, but were in fact not.  */
   312 
   313 static void
   314 scan_keyword_or_put_char (char ch, struct rcsoc_state *state)
   315 {
   316   if (state->keyword
   317       && *state->cur_keyword_ptr == ch
   318       && (state->cur_keyword_ptr > state->keyword
   319           || state->pending_newlines > 0))
   320     /* We might be looking at STATE->keyword at some point.
   321        Keep looking until we know for sure.  */
   322     {
   323       if (*++state->cur_keyword_ptr == '\0')
   324         /* Saw the whole keyword.  Set SAW_KEYWORD flag to true.  */
   325         {
   326           state->saw_keyword = true;
   327 
   328           /* Reset the scanning pointer.  */
   329           state->cur_keyword_ptr = state->keyword;
   330 
   331           /* Canonicalize whitespace preceding a usage string.  */
   332           state->pending_newlines = 2;
   333           state->pending_spaces = 0;
   334 
   335           /* Skip any spaces and newlines between the keyword and the
   336              usage string.  */
   337           int c;
   338           do
   339             c = getc (state->in_file);
   340           while (c == ' ' || c == '\n');
   341 
   342           /* Output the open-paren we just read.  */
   343           if (c != '(')
   344             fatal ("Missing '(' after keyword");
   345           put_char (c, state);
   346 
   347           /* Skip the function name and replace it with `fn'.  */
   348           do
   349             {
   350               c = getc (state->in_file);
   351               if (c == EOF)
   352                 fatal ("Unexpected EOF after keyword");
   353             }
   354           while (c != ' ' && c != ')');
   355 
   356           put_char ('f', state);
   357           put_char ('n', state);
   358 
   359           /* Put back the last character.  */
   360           ungetc (c, state->in_file);
   361         }
   362     }
   363   else
   364     {
   365       if (state->keyword && state->cur_keyword_ptr > state->keyword)
   366         /* We scanned the beginning of a potential usage
   367            keyword, but it was a false alarm.  Output the
   368            part we scanned.  */
   369         {
   370           const char *p;
   371 
   372           for (p = state->keyword; p < state->cur_keyword_ptr; p++)
   373             put_char (*p, state);
   374 
   375           state->cur_keyword_ptr = state->keyword;
   376         }
   377 
   378       put_char (ch, state);
   379     }
   380 }
   381 
   382 
   383 /* Skip a C string or C-style comment from INFILE, and return the
   384    byte that follows, or EOF.  COMMENT means skip a comment.  If
   385    PRINTFLAG is positive, output string contents to stdout.  If it is
   386    negative, store contents in buf.  Convert escape sequences \n and
   387    \t to newline and tab; discard \ followed by newline.
   388    If SAW_USAGE is non-null, then any occurrences of the string "usage:"
   389    at the beginning of a line will be removed, and *SAW_USAGE set to
   390    true if any were encountered.  */
   391 
   392 static int
   393 read_c_string_or_comment (FILE *infile, int printflag, bool comment,
   394                           bool *saw_usage)
   395 {
   396   int c;
   397   struct rcsoc_state state;
   398 
   399   state.in_file = infile;
   400   state.buf_ptr = (printflag < 0 ? input_buffer : 0);
   401   state.out_file = (printflag > 0 ? stdout : 0);
   402   state.pending_spaces = 0;
   403   state.pending_newlines = 0;
   404   state.keyword = (saw_usage ? "usage:" : 0);
   405   state.cur_keyword_ptr = state.keyword;
   406   state.saw_keyword = false;
   407 
   408   c = getc (infile);
   409   if (comment)
   410     while (c_isspace (c))
   411       c = getc (infile);
   412 
   413   while (c != EOF)
   414     {
   415       while (c != EOF && (comment ? c != '*' : c != '"'))
   416         {
   417           if (c == '\\')
   418             {
   419               c = getc (infile);
   420               switch (c)
   421                 {
   422                 case '\n': case '\r':
   423                   c = getc (infile);
   424                   continue;
   425                 case 'n': c = '\n'; break;
   426                 case 't': c = '\t'; break;
   427                 }
   428             }
   429 
   430           if (c == ' ')
   431             state.pending_spaces++;
   432           else if (c == '\n')
   433             {
   434               state.pending_newlines++;
   435               state.pending_spaces = 0;
   436             }
   437           else
   438             scan_keyword_or_put_char (c, &state);
   439 
   440           c = getc (infile);
   441         }
   442 
   443       if (c != EOF)
   444         c = getc (infile);
   445 
   446       if (comment)
   447         {
   448           if (c == '/')
   449             {
   450               c = getc (infile);
   451               break;
   452             }
   453 
   454           scan_keyword_or_put_char ('*', &state);
   455         }
   456       else
   457         {
   458           if (c != '"')
   459             break;
   460 
   461           /* If we had a "", concatenate the two strings.  */
   462           c = getc (infile);
   463         }
   464     }
   465 
   466   if (printflag < 0)
   467     *state.buf_ptr = 0;
   468 
   469   if (saw_usage)
   470     *saw_usage = state.saw_keyword;
   471 
   472   return c;
   473 }
   474 
   475 
   476 
   477 /* Write to stdout the argument names of function FUNC, whose text is in BUF.
   478    MINARGS and MAXARGS are the minimum and maximum number of arguments.  */
   479 
   480 static void
   481 write_c_args (char *func, char *buf, int minargs, int maxargs)
   482 {
   483   char *p;
   484   bool in_ident = false;
   485   char *ident_start UNINIT;
   486   ptrdiff_t ident_length = 0;
   487 
   488   fputs ("(fn", stdout);
   489 
   490   if (*buf == '(')
   491     ++buf;
   492 
   493   for (p = buf; *p; p++)
   494     {
   495       char c = *p;
   496 
   497       /* Notice when a new identifier starts.  */
   498       if ((c_isalnum (c) || c == '_')
   499           != in_ident)
   500         {
   501           if (!in_ident)
   502             {
   503               in_ident = true;
   504               ident_start = p;
   505             }
   506           else
   507             {
   508               in_ident = false;
   509               ident_length = p - ident_start;
   510             }
   511         }
   512 
   513       /* Found the end of an argument, write out the last seen
   514          identifier.  */
   515       if (c == ',' || c == ')')
   516         {
   517           if (ident_length == 0)
   518             {
   519               error ("empty arg list for '%s' should be (void), not ()", func);
   520               continue;
   521             }
   522 
   523           if (strncmp (ident_start, "void", ident_length) == 0)
   524             continue;
   525 
   526           putchar (' ');
   527 
   528           if (minargs == 0 && maxargs > 0)
   529             fputs ("&optional ", stdout);
   530 
   531           minargs--;
   532           maxargs--;
   533 
   534           /* In C code, `default' is a reserved word, so we spell it
   535              `defalt'; demangle that here.  */
   536           if (ident_length == 6 && memcmp (ident_start, "defalt", 6) == 0)
   537             fputs ("DEFAULT", stdout);
   538           else
   539             while (ident_length-- > 0)
   540               {
   541                 c = c_toupper (*ident_start++);
   542                 if (c == '_')
   543                   /* Print underscore as hyphen.  */
   544                   c = '-';
   545                 putchar (c);
   546               }
   547         }
   548     }
   549 
   550   putchar (')');
   551 }
   552 
   553 /* The types of globals.  These are sorted roughly in decreasing alignment
   554    order to avoid allocation gaps, except that symbols and functions
   555    are last.  */
   556 enum global_type
   557 {
   558   INVALID,
   559   LISP_OBJECT,
   560   EMACS_INTEGER,
   561   BOOLEAN,
   562   SYMBOL,
   563   FUNCTION
   564 };
   565 
   566 /* A single global.  */
   567 struct global
   568 {
   569   enum global_type type;
   570   char *name;
   571   int flags;
   572   union
   573   {
   574     int value;
   575     char const *svalue;
   576   } v;
   577 };
   578 
   579 /* Bit values for FLAGS field from the above.  Applied for DEFUNs only.  */
   580 enum { DEFUN_noreturn = 1, DEFUN_const = 2, DEFUN_noinline = 4 };
   581 
   582 /* All the variable names we saw while scanning C sources in `-g'
   583    mode.  */
   584 static ptrdiff_t num_globals;
   585 static ptrdiff_t num_globals_allocated;
   586 static struct global *globals;
   587 
   588 static struct global *
   589 add_global (enum global_type type, char const *name, int value,
   590             char const *svalue)
   591 {
   592   /* Ignore the one non-symbol that can occur.  */
   593   if (strcmp (name, "..."))
   594     {
   595       if (num_globals == num_globals_allocated)
   596         {
   597           ptrdiff_t num_globals_max = (min (PTRDIFF_MAX, SIZE_MAX)
   598                                        / sizeof *globals);
   599           if (num_globals_allocated == num_globals_max)
   600             memory_exhausted ();
   601           if (num_globals_allocated < num_globals_max / 2)
   602             num_globals_allocated = 2 * num_globals_allocated + 1;
   603           else
   604             num_globals_allocated = num_globals_max;
   605           globals = xrealloc (globals, num_globals_allocated * sizeof *globals);
   606         }
   607 
   608       ++num_globals;
   609 
   610       ptrdiff_t namesize = strlen (name) + 1;
   611       char *buf = xmalloc (namesize + (svalue ? strlen (svalue) + 1 : 0));
   612       globals[num_globals - 1].type = type;
   613       globals[num_globals - 1].name = strcpy (buf, name);
   614       if (svalue)
   615         globals[num_globals - 1].v.svalue = strcpy (buf + namesize, svalue);
   616       else
   617         globals[num_globals - 1].v.value = value;
   618       globals[num_globals - 1].flags = 0;
   619       return globals + num_globals - 1;
   620     }
   621   return NULL;
   622 }
   623 
   624 static int
   625 compare_globals (const void *a, const void *b)
   626 {
   627   const struct global *ga = a;
   628   const struct global *gb = b;
   629 
   630   if (ga->type != gb->type)
   631     return ga->type - gb->type;
   632 
   633   /* Consider "nil" to be the least, so that iQnil is zero.  That
   634      way, Qnil's internal representation is zero, which is a bit faster.
   635      Similarly, consider "t" to be the second-least, and so forth.  */
   636   if (ga->type == SYMBOL)
   637     {
   638       /* Common symbols in decreasing popularity order.  */
   639       static char const commonsym[][8]
   640         = { "nil", "t", "unbound", "error", "lambda" };
   641       int ncommonsym = sizeof commonsym / sizeof *commonsym;
   642       int ai = ncommonsym, bi = ncommonsym;
   643       for (int i = 0; i < ncommonsym; i++)
   644         {
   645           if (ga->name[0] == 'Q' && strcmp (ga->name + 1, commonsym[i]) == 0)
   646             ai = i;
   647           if (gb->name[0] == 'Q' && strcmp (gb->name + 1, commonsym[i]) == 0)
   648             bi = i;
   649         }
   650       if (! (ai == ncommonsym && bi == ncommonsym))
   651         return ai - bi;
   652     }
   653 
   654   return strcmp (ga->name, gb->name);
   655 }
   656 
   657 static void
   658 close_emacs_globals (ptrdiff_t num_symbols)
   659 {
   660   printf (("};\n"
   661            "extern struct emacs_globals globals;\n"
   662            "\n"
   663            "#ifndef DEFINE_SYMBOLS\n"
   664            "extern\n"
   665            "#endif\n"
   666            "struct Lisp_Symbol lispsym[%td];\n"),
   667           num_symbols);
   668 }
   669 
   670 static void
   671 write_globals (void)
   672 {
   673   ptrdiff_t i, j;
   674   bool seen_defun = false;
   675   ptrdiff_t symnum = 0;
   676   ptrdiff_t num_symbols = 0;
   677   qsort (globals, num_globals, sizeof (struct global), compare_globals);
   678 
   679   j = 0;
   680   for (i = 0; i < num_globals; i++)
   681     {
   682       while (i + 1 < num_globals
   683              && strcmp (globals[i].name, globals[i + 1].name) == 0)
   684         {
   685           if (globals[i].type == FUNCTION
   686               && globals[i].v.value != globals[i + 1].v.value)
   687             error ("function '%s' defined twice with differing signatures",
   688                    globals[i].name);
   689           free (globals[i].name);
   690           i++;
   691         }
   692       num_symbols += globals[i].type == SYMBOL;
   693       globals[j++] = globals[i];
   694     }
   695   num_globals = j;
   696 
   697   for (i = 0; i < num_globals; ++i)
   698     {
   699       char const *type = 0;
   700 
   701       switch (globals[i].type)
   702         {
   703         case EMACS_INTEGER:
   704           type = "intmax_t";
   705           break;
   706         case BOOLEAN:
   707           type = "bool";
   708           break;
   709         case LISP_OBJECT:
   710           type = "Lisp_Object";
   711           break;
   712         case SYMBOL:
   713         case FUNCTION:
   714           if (!seen_defun)
   715             {
   716               close_emacs_globals (num_symbols);
   717               putchar ('\n');
   718               seen_defun = true;
   719             }
   720           break;
   721         default:
   722           fatal ("not a recognized DEFVAR_");
   723         }
   724 
   725       if (type)
   726         {
   727           printf ("  %s f_%s;\n", type, globals[i].name);
   728           printf ("#define %s globals.f_%s\n",
   729                   globals[i].name, globals[i].name);
   730         }
   731       else if (globals[i].type == SYMBOL)
   732         printf (("#define i%s %td\n"
   733                  "DEFINE_LISP_SYMBOL (%s)\n"),
   734                 globals[i].name, symnum++, globals[i].name);
   735       else
   736         {
   737           if (globals[i].flags & DEFUN_noreturn)
   738             fputs ("_Noreturn ", stdout);
   739           if (globals[i].flags & DEFUN_noinline)
   740             fputs ("NO_INLINE ", stdout);
   741 
   742           printf ("EXFUN (%s, ", globals[i].name);
   743           if (globals[i].v.value == -1)
   744             fputs ("MANY", stdout);
   745           else if (globals[i].v.value == -2)
   746             fputs ("UNEVALLED", stdout);
   747           else
   748             printf ("%d", globals[i].v.value);
   749           putchar (')');
   750 
   751           if (globals[i].flags & DEFUN_noreturn)
   752             fputs (" ATTRIBUTE_COLD", stdout);
   753           if (globals[i].flags & DEFUN_const)
   754             fputs (" ATTRIBUTE_CONST", stdout);
   755 
   756           puts (";");
   757         }
   758     }
   759 
   760   if (!seen_defun)
   761     close_emacs_globals (num_symbols);
   762 
   763   puts ("#ifdef DEFINE_SYMBOLS");
   764   puts ("static char const *const defsym_name[] = {");
   765   for (ptrdiff_t i = 0; i < num_globals; i++)
   766     if (globals[i].type == SYMBOL)
   767       printf ("\t\"%s\",\n", globals[i].v.svalue);
   768   puts ("};");
   769   puts ("#endif");
   770 
   771   puts ("#define Qnil builtin_lisp_symbol (0)");
   772   puts ("#if DEFINE_NON_NIL_Q_SYMBOL_MACROS");
   773   num_symbols = 0;
   774   for (ptrdiff_t i = 0; i < num_globals; i++)
   775     if (globals[i].type == SYMBOL && num_symbols++ != 0)
   776       printf ("# define %s builtin_lisp_symbol (%td)\n",
   777               globals[i].name, num_symbols - 1);
   778   puts ("#endif");
   779 }
   780 
   781 
   782 /* Read through a c file.  If a .o file is named,
   783    the corresponding .c or .m file is read instead.
   784    Looks for DEFUN constructs such as are defined in ../src/lisp.h.
   785    Accepts any word starting DEF... so it finds DEFSIMPLE and DEFPRED.  */
   786 
   787 static void
   788 scan_c_file (char *filename, const char *mode)
   789 {
   790   FILE *infile;
   791   char extension = filename[strlen (filename) - 1];
   792 
   793   if (extension == 'o')
   794     filename[strlen (filename) - 1] = 'c';
   795 
   796   infile = fopen (filename, mode);
   797 
   798   if (infile == NULL && extension == 'o')
   799     {
   800       /* Try .m.  */
   801       filename[strlen (filename) - 1] = 'm';
   802       infile = fopen (filename, mode);
   803       if (infile == NULL)
   804         filename[strlen (filename) - 1] = 'c'; /* Don't confuse people.  */
   805     }
   806 
   807   if (infile == NULL)
   808     {
   809       perror (filename);
   810       exit (EXIT_FAILURE);
   811     }
   812 
   813   /* Reset extension to be able to detect duplicate files.  */
   814   filename[strlen (filename) - 1] = extension;
   815   scan_c_stream (infile);
   816 }
   817 
   818 /* Return 1 if next input from INFILE is equal to P, -1 if EOF,
   819    0 if input doesn't match.  */
   820 
   821 static int
   822 stream_match (FILE *infile, const char *p)
   823 {
   824   for (; *p; p++)
   825     {
   826       int c = getc (infile);
   827       if (c == EOF)
   828        return -1;
   829       if (c != *p)
   830        return 0;
   831     }
   832   return 1;
   833 }
   834 
   835 static void
   836 scan_c_stream (FILE *infile)
   837 {
   838   int commas, minargs, maxargs;
   839   int c = '\n';
   840 
   841   while (!feof (infile))
   842     {
   843       bool doc_keyword = false;
   844       bool defunflag = false;
   845       bool defvarperbufferflag = false;
   846       bool defvarflag = false;
   847       enum global_type type = INVALID;
   848       static char name[sizeof input_buffer];
   849 
   850       if (c != '\n' && c != '\r')
   851         {
   852           c = getc (infile);
   853           continue;
   854         }
   855       c = getc (infile);
   856       if (c == ' ')
   857         {
   858           while (c == ' ')
   859             c = getc (infile);
   860           if (c != 'D')
   861             continue;
   862           c = getc (infile);
   863           if (c != 'E')
   864             continue;
   865           c = getc (infile);
   866           if (c != 'F')
   867             continue;
   868           c = getc (infile);
   869           if (c == 'S')
   870             {
   871               c = getc (infile);
   872               if (c != 'Y')
   873                 continue;
   874               c = getc (infile);
   875               if (c != 'M')
   876                 continue;
   877               c = getc (infile);
   878               if (c != ' ' && c != '\t' && c != '(')
   879                 continue;
   880               type = SYMBOL;
   881             }
   882           else if (c == 'V')
   883             {
   884               c = getc (infile);
   885               if (c != 'A')
   886                 continue;
   887               c = getc (infile);
   888               if (c != 'R')
   889                 continue;
   890               c = getc (infile);
   891               if (c != '_')
   892                 continue;
   893 
   894               defvarflag = true;
   895 
   896               c = getc (infile);
   897               defvarperbufferflag = (c == 'P');
   898               if (generate_globals)
   899                 {
   900                   if (c == 'I')
   901                     type = EMACS_INTEGER;
   902                   else if (c == 'L')
   903                     type = LISP_OBJECT;
   904                   else if (c == 'B')
   905                     type = BOOLEAN;
   906                 }
   907 
   908               c = getc (infile);
   909               /* We need to distinguish between DEFVAR_BOOL and
   910                  DEFVAR_BUFFER_DEFAULTS.  */
   911               if (generate_globals && type == BOOLEAN && c != 'O')
   912                 type = INVALID;
   913             }
   914           else
   915             continue;
   916         }
   917       else if (c == 'D')
   918         {
   919           c = getc (infile);
   920           if (c != 'E')
   921             continue;
   922           c = getc (infile);
   923           if (c != 'F')
   924             continue;
   925           c = getc (infile);
   926           defunflag = c == 'U';
   927         }
   928       else continue;
   929 
   930       if (generate_globals
   931           && (!defvarflag || defvarperbufferflag || type == INVALID)
   932           && !defunflag && type != SYMBOL)
   933         continue;
   934 
   935       while (c != '(')
   936         {
   937           if (c < 0)
   938             goto eof;
   939           c = getc (infile);
   940         }
   941 
   942       if (type != SYMBOL)
   943         {
   944           /* Lisp variable or function name.  */
   945           c = getc (infile);
   946           if (c != '"')
   947             continue;
   948           c = read_c_string_or_comment (infile, -1, false, 0);
   949         }
   950 
   951       if (generate_globals)
   952         {
   953           ptrdiff_t i = 0;
   954           char const *svalue = 0;
   955 
   956           /* Skip "," and whitespace.  */
   957           do
   958             {
   959               c = getc (infile);
   960             }
   961           while (c == ',' || c_isspace (c));
   962 
   963           /* Read in the identifier.  */
   964           do
   965             {
   966               if (c < 0)
   967                 goto eof;
   968               input_buffer[i++] = c;
   969               if (sizeof input_buffer <= i)
   970                 fatal ("identifier too long");
   971               c = getc (infile);
   972             }
   973           while (! (c == ',' || c_isspace (c)));
   974 
   975           input_buffer[i] = '\0';
   976           memcpy (name, input_buffer, i + 1);
   977 
   978           if (type == SYMBOL)
   979             {
   980               do
   981                 c = getc (infile);
   982               while (c_isspace (c));
   983 
   984               if (c != '"')
   985                 continue;
   986               c = read_c_string_or_comment (infile, -1, false, 0);
   987               svalue = input_buffer;
   988             }
   989 
   990           if (!defunflag)
   991             {
   992               add_global (type, name, 0, svalue);
   993               continue;
   994             }
   995         }
   996 
   997       if (type == SYMBOL)
   998         continue;
   999 
  1000       /* DEFVAR_LISP ("name", addr, "doc")
  1001          DEFVAR_LISP ("name", addr /\* doc *\/)
  1002          DEFVAR_LISP ("name", addr, doc: /\* doc *\/)  */
  1003 
  1004       if (defunflag)
  1005         commas = generate_globals ? 4 : 5;
  1006       else if (defvarperbufferflag)
  1007         commas = 3;
  1008       else if (defvarflag)
  1009         commas = 1;
  1010       else  /* For DEFSIMPLE and DEFPRED.  */
  1011         commas = 2;
  1012 
  1013       while (commas)
  1014         {
  1015           if (c == ',')
  1016             {
  1017               commas--;
  1018 
  1019               if (defunflag && (commas == 1 || commas == 2))
  1020                 {
  1021                   int scanned = 0;
  1022                   do
  1023                     c = getc (infile);
  1024                   while (c_isspace (c));
  1025 
  1026                   if (c < 0)
  1027                     goto eof;
  1028                   ungetc (c, infile);
  1029                   if (commas == 2) /* Pick up minargs.  */
  1030                     scanned = fscanf (infile, "%d", &minargs);
  1031                   else /* Pick up maxargs.  */
  1032                     if (c == 'M' || c == 'U') /* MANY || UNEVALLED */
  1033                       {
  1034                         if (generate_globals)
  1035                           maxargs = (c == 'M') ? -1 : -2;
  1036                         else
  1037                           maxargs = -1;
  1038                       }
  1039                     else
  1040                       scanned = fscanf (infile, "%d", &maxargs);
  1041                   if (scanned < 0)
  1042                     goto eof;
  1043                 }
  1044             }
  1045 
  1046           if (c == EOF)
  1047             goto eof;
  1048           c = getc (infile);
  1049         }
  1050 
  1051       if (generate_globals)
  1052         {
  1053           struct global *g = add_global (FUNCTION, name, maxargs, 0);
  1054           if (!g)
  1055             continue;
  1056 
  1057           /* The following code tries to recognize function attributes
  1058              specified after the docstring, e.g.:
  1059 
  1060              DEFUN ("foo", Ffoo, Sfoo, X, Y, Z,
  1061                    doc: /\* doc *\/
  1062                    attributes: attribute1 attribute2 ...)
  1063                (Lisp_Object arg...)
  1064 
  1065              Now only `const', `noinline' and `noreturn' attributes
  1066              are used.  */
  1067 
  1068           /* Advance to the end of docstring.  */
  1069           c = getc (infile);
  1070           if (c == EOF)
  1071             goto eof;
  1072           int d = getc (infile);
  1073           if (d == EOF)
  1074             goto eof;
  1075           while (true)
  1076             {
  1077               if (c == '*' && d == '/')
  1078                 break;
  1079               c = d, d = getc (infile);
  1080               if (d == EOF)
  1081                 goto eof;
  1082             }
  1083           /* Skip spaces, if any.  */
  1084           do
  1085             {
  1086               c = getc (infile);
  1087               if (c == EOF)
  1088                 goto eof;
  1089             }
  1090           while (c_isspace (c));
  1091 
  1092           /* Check for 'attributes:' token.  */
  1093           if (c == 'a' && stream_match (infile, "ttributes:"))
  1094             {
  1095               char *p = input_buffer;
  1096               /* Collect attributes up to ')'.  */
  1097               while (true)
  1098                 {
  1099                   c = getc (infile);
  1100                   if (c == EOF)
  1101                     goto eof;
  1102                   if (c == ')')
  1103                     break;
  1104                   if (p - input_buffer > sizeof (input_buffer))
  1105                     abort ();
  1106                   *p++ = c;
  1107                 }
  1108               *p = 0;
  1109               if (strstr (input_buffer, "noreturn"))
  1110                 g->flags |= DEFUN_noreturn;
  1111               if (strstr (input_buffer, "const"))
  1112                 g->flags |= DEFUN_const;
  1113 
  1114               /* Although the noinline attribute is no longer used,
  1115                  leave its support in, in case it's needed later.  */
  1116               if (strstr (input_buffer, "noinline"))
  1117                 g->flags |= DEFUN_noinline;
  1118             }
  1119           continue;
  1120         }
  1121 
  1122       while (c_isspace (c))
  1123         c = getc (infile);
  1124 
  1125       if (c == '"')
  1126         c = read_c_string_or_comment (infile, 0, false, 0);
  1127 
  1128       while (c != EOF && c != ',' && c != '/')
  1129         c = getc (infile);
  1130       if (c == ',')
  1131         {
  1132           do
  1133             c = getc (infile);
  1134           while (c_isspace (c));
  1135 
  1136           while (c_isalpha (c))
  1137             c = getc (infile);
  1138           if (c == ':')
  1139             {
  1140               doc_keyword = true;
  1141               do
  1142                 c = getc (infile);
  1143               while (c_isspace (c));
  1144             }
  1145         }
  1146 
  1147       if (c == '"'
  1148           || (c == '/'
  1149               && (c = getc (infile),
  1150                   ungetc (c, infile),
  1151                   c == '*')))
  1152         {
  1153           bool comment = c != '"';
  1154           bool saw_usage;
  1155 
  1156           printf ("\037%c%s\n", defvarflag ? 'V' : 'F', input_buffer);
  1157 
  1158           if (comment)
  1159             getc (infile);      /* Skip past `*'.  */
  1160           c = read_c_string_or_comment (infile, 1, comment, &saw_usage);
  1161 
  1162           /* If this is a defun, find the arguments and print them.  If
  1163              this function takes MANY or UNEVALLED args, then the C source
  1164              won't give the names of the arguments, so we shouldn't bother
  1165              trying to find them.
  1166 
  1167              Various doc-string styles:
  1168               0: DEFUN (..., "DOC") (args)            [!comment]
  1169               1: DEFUN (..., /\* DOC *\/ (args))      [comment && !doc_keyword]
  1170               2: DEFUN (..., doc: /\* DOC *\/) (args) [comment && doc_keyword]
  1171           */
  1172           if (defunflag && maxargs != -1 && !saw_usage)
  1173             {
  1174               char argbuf[1024], *p = argbuf;
  1175 
  1176               if (!comment || doc_keyword)
  1177                 while (c != ')')
  1178                   {
  1179                     if (c < 0)
  1180                       goto eof;
  1181                     c = getc (infile);
  1182                   }
  1183 
  1184               /* Skip into arguments.  */
  1185               while (c != '(')
  1186                 {
  1187                   if (c < 0)
  1188                     goto eof;
  1189                   c = getc (infile);
  1190                 }
  1191               /* Copy arguments into ARGBUF.  */
  1192               *p++ = c;
  1193               do
  1194                 {
  1195                   c = getc (infile);
  1196                   if (c < 0)
  1197                     goto eof;
  1198                   *p++ = c;
  1199                 }
  1200               while (c != ')');
  1201 
  1202               *p = '\0';
  1203               /* Output them.  */
  1204               fputs ("\n\n", stdout);
  1205               write_c_args (input_buffer, argbuf, minargs, maxargs);
  1206             }
  1207           else if (defunflag && maxargs == -1 && !saw_usage)
  1208             /* The DOC should provide the usage form.  */
  1209             fprintf (stderr, "Missing 'usage' for function '%s'.\n",
  1210                      input_buffer);
  1211         }
  1212     }
  1213  eof:
  1214   if (ferror (infile) || fclose (infile) != 0)
  1215     fatal ("read error");
  1216 }
  1217 
  1218 /* make-docfile.c ends here */

/* [<][>][^][v][top][bottom][index][help] */