make-docfile.c 31.7 KB
Newer Older
Richard M. Stallman's avatar
Richard M. Stallman committed
1
/* Generate doc-string file for GNU Emacs from source files.
2
   Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2011
Glenn Morris's avatar
Glenn Morris committed
3
                 Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
4 5 6

This file is part of GNU Emacs.

7
GNU Emacs is free software: you can redistribute it and/or modify
Joseph Arceneaux's avatar
Joseph Arceneaux committed
8
it under the terms of the GNU General Public License as published by
9 10
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Joseph Arceneaux's avatar
Joseph Arceneaux committed
11

Richard M. Stallman's avatar
Richard M. Stallman committed
12
GNU Emacs is distributed in the hope that it will be useful,
Joseph Arceneaux's avatar
Joseph Arceneaux committed
13 14 15
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
Richard M. Stallman's avatar
Richard M. Stallman committed
16

Joseph Arceneaux's avatar
Joseph Arceneaux committed
17
You should have received a copy of the GNU General Public License
18 19
along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */

Richard M. Stallman's avatar
Richard M. Stallman committed
20 21 22 23 24

/* The arguments given to this program are all the C and Lisp source files
 of GNU Emacs.  .elc and .el and .c files are allowed.
 A .o file can also be specified; the .c file it was made from is used.
 This helps the makefile pass the correct list of files.
25
 Option -d DIR means change to DIR before looking for files.
Richard M. Stallman's avatar
Richard M. Stallman committed
26 27 28 29 30 31 32 33 34 35

 The results, which go to standard output or to a file
 specified with -a or -o (-a to append, -o to start from nothing),
 are entries containing function or variable names and their documentation.
 Each entry starts with a ^_ character.
 Then comes F for a function or V for a variable.
 Then comes the function or variable name, terminated with a newline.
 Then comes the documentation for that function or variable.
 */

36 37 38 39 40 41
#include <config.h>

/* defined to be emacs_main, sys_fopen, etc. in config.h */
#undef main
#undef fopen
#undef chdir
Richard M. Stallman's avatar
Richard M. Stallman committed
42

Richard M. Stallman's avatar
Richard M. Stallman committed
43
#include <stdio.h>
Tom Tromey's avatar
Tom Tromey committed
44
#include <stdlib.h>
45 46 47
#ifdef MSDOS
#include <fcntl.h>
#endif /* MSDOS */
48 49 50 51
#ifdef WINDOWSNT
#include <fcntl.h>
#include <direct.h>
#endif /* WINDOWSNT */
52

53
#ifdef DOS_NT
54 55
#define READ_TEXT "rt"
#define READ_BINARY "rb"
56
#else  /* not DOS_NT */
57 58
#define READ_TEXT "r"
#define READ_BINARY "r"
59
#endif /* not DOS_NT */
Richard M. Stallman's avatar
Richard M. Stallman committed
60

61 62 63 64
#ifndef DIRECTORY_SEP
#define DIRECTORY_SEP '/'
#endif

Eli Zaretskii's avatar
Eli Zaretskii committed
65
#ifndef IS_DIRECTORY_SEP
66
#define IS_DIRECTORY_SEP(_c_) ((_c_) == DIRECTORY_SEP)
Eli Zaretskii's avatar
Eli Zaretskii committed
67 68
#endif

69
int scan_file (char *filename);
70 71 72
int scan_lisp_file (const char *filename, const char *mode);
int scan_c_file (char *filename, const char *mode);
void fatal (const char *s1, const char *s2) NO_RETURN;
Tom Tromey's avatar
Tom Tromey committed
73 74
void start_globals (void);
void write_globals (void);
75

Karl Heuer's avatar
Karl Heuer committed
76 77 78 79 80 81
#ifdef MSDOS
/* s/msdos.h defines this as sys_chdir, but we're not linking with the
   file where that function is defined.  */
#undef chdir
#endif

Andreas Schwab's avatar
Andreas Schwab committed
82 83
#include <unistd.h>

84
/* Stdio stream for output to the DOC file.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
85 86
FILE *outfile;

87 88 89
/* Name this program was invoked with.  */
char *progname;

Tom Tromey's avatar
Tom Tromey committed
90 91 92
/* Nonzero if this invocation is generating globals.h.  */
int generate_globals;

Pavel Janík's avatar
Pavel Janík committed
93
/* Print error message.  `s1' is printf control string, `s2' is arg for it.  */
94 95 96

/* VARARGS1 */
void
97
error (const char *s1, const char *s2)
98 99 100 101 102 103 104 105 106 107
{
  fprintf (stderr, "%s: ", progname);
  fprintf (stderr, s1, s2);
  fprintf (stderr, "\n");
}

/* Print error message and exit.  */

/* VARARGS1 */
void
108
fatal (const char *s1, const char *s2)
109 110
{
  error (s1, s2);
111
  exit (EXIT_FAILURE);
112 113 114 115
}

/* Like malloc but get fatal error if memory is exhausted.  */

Stefan Monnier's avatar
Stefan Monnier committed
116
void *
117
xmalloc (unsigned int size)
118
{
Stefan Monnier's avatar
Stefan Monnier committed
119
  void *result = (void *) malloc (size);
120 121 122 123
  if (result == NULL)
    fatal ("virtual memory exhausted", 0);
  return result;
}
Tom Tromey's avatar
Tom Tromey committed
124 125 126 127 128 129 130 131 132 133 134 135

/* Like realloc but get fatal error if memory is exhausted.  */

void *
xrealloc (void *arg, unsigned int size)
{
  void *result = (void *) realloc (arg, size);
  if (result == NULL)
    fatal ("virtual memory exhausted", 0);
  return result;
}

136

137
int
138
main (int argc, char **argv)
Richard M. Stallman's avatar
Richard M. Stallman committed
139 140 141
{
  int i;
  int err_count = 0;
142
  int first_infile;
Richard M. Stallman's avatar
Richard M. Stallman committed
143

144 145
  progname = argv[0];

146 147
  outfile = stdout;

148
  /* Don't put CRs in the DOC file.  */
149
#ifdef MSDOS
150
  _fmode = O_BINARY;
151 152
#if 0  /* Suspicion is that this causes hanging.
	  So instead we require people to use -o on MSDOS.  */
153 154
  (stdout)->_flag &= ~_IOTEXT;
  _setmode (fileno (stdout), O_BINARY);
155 156
#endif
  outfile = 0;
157
#endif /* MSDOS */
158 159 160 161 162
#ifdef WINDOWSNT
  _fmode = O_BINARY;
  _setmode (fileno (stdout), O_BINARY);
#endif /* WINDOWSNT */

Richard M. Stallman's avatar
Richard M. Stallman committed
163 164 165 166 167 168 169 170 171 172 173 174
  /* If first two args are -o FILE, output to FILE.  */
  i = 1;
  if (argc > i + 1 && !strcmp (argv[i], "-o"))
    {
      outfile = fopen (argv[i + 1], "w");
      i += 2;
    }
  if (argc > i + 1 && !strcmp (argv[i], "-a"))
    {
      outfile = fopen (argv[i + 1], "a");
      i += 2;
    }
175 176
  if (argc > i + 1 && !strcmp (argv[i], "-d"))
    {
177 178 179 180 181
      if (chdir (argv[i + 1]) != 0)
	{
	  perror (argv[i + 1]);
	  return EXIT_FAILURE;
	}
182 183
      i += 2;
    }
Tom Tromey's avatar
Tom Tromey committed
184 185 186 187 188
  if (argc > i && !strcmp (argv[i], "-g"))
    {
      generate_globals = 1;
      ++i;
    }
Richard M. Stallman's avatar
Richard M. Stallman committed
189

190 191 192
  if (outfile == 0)
    fatal ("No output file specified", "");

Tom Tromey's avatar
Tom Tromey committed
193 194 195
  if (generate_globals)
    start_globals ();

196
  first_infile = i;
Richard M. Stallman's avatar
Richard M. Stallman committed
197
  for (; i < argc; i++)
198 199 200 201 202 203 204 205 206
    {
      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]);
    }
Tom Tromey's avatar
Tom Tromey committed
207 208 209 210

  if (err_count == 0 && generate_globals)
    write_globals ();

211
  return (err_count > 0 ? EXIT_FAILURE : EXIT_SUCCESS);
Richard M. Stallman's avatar
Richard M. Stallman committed
212 213
}

Stefan Monnier's avatar
Stefan Monnier committed
214 215
/* Add a source file name boundary marker in the output file.  */
void
216
put_filename (char *filename)
Stefan Monnier's avatar
Stefan Monnier committed
217
{
Eli Zaretskii's avatar
Eli Zaretskii committed
218 219 220 221 222 223 224
  char *tmp;

  for (tmp = filename; *tmp; tmp++)
    {
      if (IS_DIRECTORY_SEP(*tmp))
	filename = tmp + 1;
    }
Stefan Monnier's avatar
Stefan Monnier committed
225 226 227 228 229 230

  putc (037, outfile);
  putc ('S', outfile);
  fprintf (outfile, "%s\n", filename);
}

Jim Blandy's avatar
Jim Blandy committed
231
/* Read file FILENAME and output its doc strings to outfile.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
232 233
/* Return 1 if file is not found, 0 if it is found.  */

234
int
235
scan_file (char *filename)
Richard M. Stallman's avatar
Richard M. Stallman committed
236
{
Dan Nicolaescu's avatar
Dan Nicolaescu committed
237 238

  size_t len = strlen (filename);
Stefan Monnier's avatar
Stefan Monnier committed
239

Tom Tromey's avatar
Tom Tromey committed
240 241
  if (!generate_globals)
    put_filename (filename);
242
  if (len > 4 && !strcmp (filename + len - 4, ".elc"))
243
    return scan_lisp_file (filename, READ_BINARY);
244
  else if (len > 3 && !strcmp (filename + len - 3, ".el"))
245
    return scan_lisp_file (filename, READ_TEXT);
Richard M. Stallman's avatar
Richard M. Stallman committed
246
  else
247
    return scan_c_file (filename, READ_TEXT);
Richard M. Stallman's avatar
Richard M. Stallman committed
248
}
Tom Tromey's avatar
Tom Tromey committed
249 250 251 252 253 254 255 256

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");
}
Richard M. Stallman's avatar
Richard M. Stallman committed
257 258 259

char buf[128];

Miles Bader's avatar
Miles Bader committed
260 261 262
/* Some state during the execution of `read_c_string_or_comment'.  */
struct rcsoc_state
{
Pavel Janík's avatar
Pavel Janík committed
263
  /* A count of spaces and newlines that have been read, but not output.  */
Miles Bader's avatar
Miles Bader committed
264 265 266 267 268 269 270 271 272 273 274 275
  unsigned pending_spaces, pending_newlines;

  /* Where we're reading from.  */
  FILE *in_file;

  /* If non-zero, a buffer into which to copy characters.  */
  char *buf_ptr;
  /* If non-zero, a file into which to copy characters.  */
  FILE *out_file;

  /* A keyword we look for at the beginning of lines.  If found, it is
     not copied, and SAW_KEYWORD is set to true.  */
276
  const char *keyword;
Juanma Barranquero's avatar
Juanma Barranquero committed
277
  /* The current point we've reached in an occurrence of KEYWORD in
Miles Bader's avatar
Miles Bader committed
278
     the input stream.  */
279
  const char *cur_keyword_ptr;
Juanma Barranquero's avatar
Juanma Barranquero committed
280
  /* Set to true if we saw an occurrence of KEYWORD.  */
Miles Bader's avatar
Miles Bader committed
281 282 283 284 285
  int saw_keyword;
};

/* Output CH to the file or buffer in STATE.  Any pending newlines or
   spaces are output first.  */
Miles Bader's avatar
Miles Bader committed
286 287

static INLINE void
288
put_char (int ch, struct rcsoc_state *state)
Miles Bader's avatar
Miles Bader committed
289 290 291 292
{
  int out_ch;
  do
    {
Miles Bader's avatar
Miles Bader committed
293
      if (state->pending_newlines > 0)
Miles Bader's avatar
Miles Bader committed
294
	{
Miles Bader's avatar
Miles Bader committed
295
	  state->pending_newlines--;
Miles Bader's avatar
Miles Bader committed
296 297
	  out_ch = '\n';
	}
Miles Bader's avatar
Miles Bader committed
298
      else if (state->pending_spaces > 0)
Miles Bader's avatar
Miles Bader committed
299
	{
Miles Bader's avatar
Miles Bader committed
300
	  state->pending_spaces--;
Miles Bader's avatar
Miles Bader committed
301 302 303 304 305
	  out_ch = ' ';
	}
      else
	out_ch = ch;

Miles Bader's avatar
Miles Bader committed
306 307 308 309
      if (state->out_file)
	putc (out_ch, state->out_file);
      if (state->buf_ptr)
	*state->buf_ptr++ = out_ch;
Miles Bader's avatar
Miles Bader committed
310 311 312 313
    }
  while (out_ch != ch);
}

Miles Bader's avatar
Miles Bader committed
314 315 316 317 318 319 320
/* If in the middle of scanning a keyword, continue scanning with
   character CH, otherwise output CH to the file or buffer in STATE.
   Any pending newlines or spaces are output first, as well as any
   previously scanned characters that were thought to be part of a
   keyword, but were in fact not.  */

static void
321
scan_keyword_or_put_char (int ch, struct rcsoc_state *state)
Miles Bader's avatar
Miles Bader committed
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
{
  if (state->keyword
      && *state->cur_keyword_ptr == ch
      && (state->cur_keyword_ptr > state->keyword
	  || state->pending_newlines > 0))
    /* We might be looking at STATE->keyword at some point.
       Keep looking until we know for sure.  */
    {
      if (*++state->cur_keyword_ptr == '\0')
	/* Saw the whole keyword.  Set SAW_KEYWORD flag to true.  */
	{
	  state->saw_keyword = 1;

	  /* Reset the scanning pointer.  */
	  state->cur_keyword_ptr = state->keyword;

Pavel Janík's avatar
Pavel Janík committed
338
	  /* Canonicalize whitespace preceding a usage string.  */
Miles Bader's avatar
Miles Bader committed
339 340 341 342 343 344 345 346 347
	  state->pending_newlines = 2;
	  state->pending_spaces = 0;

	  /* Skip any whitespace between the keyword and the
	     usage string.  */
	  do
	    ch = getc (state->in_file);
	  while (ch == ' ' || ch == '\n');

348 349 350 351 352 353 354 355 356
	  /* Output the open-paren we just read.  */
	  put_char (ch, state);

	  /* Skip the function name and replace it with `fn'.  */
	  do
	    ch = getc (state->in_file);
	  while (ch != ' ' && ch != ')');
	  put_char ('f', state);
	  put_char ('n', state);
357

358
	  /* Put back the last character.  */
Miles Bader's avatar
Miles Bader committed
359 360 361 362 363 364 365 366 367 368
	  ungetc (ch, state->in_file);
	}
    }
  else
    {
      if (state->keyword && state->cur_keyword_ptr > state->keyword)
	/* We scanned the beginning of a potential usage
	   keyword, but it was a false alarm.  Output the
	   part we scanned.  */
	{
369
	  const char *p;
Miles Bader's avatar
Miles Bader committed
370 371 372 373 374 375 376 377 378 379 380 381

	  for (p = state->keyword; p < state->cur_keyword_ptr; p++)
	    put_char (*p, state);

	  state->cur_keyword_ptr = state->keyword;
	}

      put_char (ch, state);
    }
}


382 383 384 385
/* Skip a C string or C-style comment from INFILE, and return the
   character that follows.  COMMENT non-zero means skip a comment.  If
   PRINTFLAG is positive, output string contents to outfile.  If it is
   negative, store contents in buf.  Convert escape sequences \n and
386
   \t to newline and tab; discard \ followed by newline.
Juanma Barranquero's avatar
Juanma Barranquero committed
387
   If SAW_USAGE is non-zero, then any occurrences of the string `usage:'
388 389
   at the beginning of a line will be removed, and *SAW_USAGE set to
   true if any were encountered.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
390

391
int
392
read_c_string_or_comment (FILE *infile, int printflag, int comment, int *saw_usage)
Richard M. Stallman's avatar
Richard M. Stallman committed
393 394
{
  register int c;
Miles Bader's avatar
Miles Bader committed
395 396 397 398 399 400 401 402 403 404 405 406
  struct rcsoc_state state;

  state.in_file = infile;
  state.buf_ptr = (printflag < 0 ? buf : 0);
  state.out_file = (printflag > 0 ? outfile : 0);
  state.pending_spaces = 0;
  state.pending_newlines = 0;
  state.keyword = (saw_usage ? "usage:" : 0);
  state.cur_keyword_ptr = state.keyword;
  state.saw_keyword = 0;

  c = getc (infile);
407
  if (comment)
Miles Bader's avatar
Miles Bader committed
408 409
    while (c == '\n' || c == '\r' || c == '\t' || c == ' ')
      c = getc (infile);
410

Richard M. Stallman's avatar
Richard M. Stallman committed
411 412
  while (c != EOF)
    {
413
      while (c != EOF && (comment ? c != '*' : c != '"'))
Richard M. Stallman's avatar
Richard M. Stallman committed
414 415 416 417
	{
	  if (c == '\\')
	    {
	      c = getc (infile);
418
	      if (c == '\n' || c == '\r')
Richard M. Stallman's avatar
Richard M. Stallman committed
419 420 421 422 423 424 425 426 427
		{
		  c = getc (infile);
		  continue;
		}
	      if (c == 'n')
		c = '\n';
	      if (c == 't')
		c = '\t';
	    }
Pavel Janík's avatar
Pavel Janík committed
428

Miles Bader's avatar
Miles Bader committed
429
	  if (c == ' ')
Miles Bader's avatar
Miles Bader committed
430
	    state.pending_spaces++;
Miles Bader's avatar
Miles Bader committed
431 432
	  else if (c == '\n')
	    {
Miles Bader's avatar
Miles Bader committed
433 434
	      state.pending_newlines++;
	      state.pending_spaces = 0;
Miles Bader's avatar
Miles Bader committed
435 436
	    }
	  else
Miles Bader's avatar
Miles Bader committed
437
	    scan_keyword_or_put_char (c, &state);
Miles Bader's avatar
Miles Bader committed
438

Richard M. Stallman's avatar
Richard M. Stallman committed
439 440
	  c = getc (infile);
	}
441

442 443
      if (c != EOF)
	c = getc (infile);
Richard M. Stallman's avatar
Richard M. Stallman committed
444

445 446 447 448 449 450 451
      if (comment)
	{
	  if (c == '/')
	    {
	      c = getc (infile);
	      break;
	    }
Pavel Janík's avatar
Pavel Janík committed
452

Miles Bader's avatar
Miles Bader committed
453
	  scan_keyword_or_put_char ('*', &state);
454 455 456 457 458
	}
      else
	{
	  if (c != '"')
	    break;
Pavel Janík's avatar
Pavel Janík committed
459

460 461 462 463
	  /* If we had a "", concatenate the two strings.  */
	  c = getc (infile);
	}
    }
Pavel Janík's avatar
Pavel Janík committed
464

Richard M. Stallman's avatar
Richard M. Stallman committed
465
  if (printflag < 0)
Miles Bader's avatar
Miles Bader committed
466 467 468 469
    *state.buf_ptr = 0;

  if (saw_usage)
    *saw_usage = state.saw_keyword;
Richard M. Stallman's avatar
Richard M. Stallman committed
470 471 472

  return c;
}
473 474


Richard M. Stallman's avatar
Richard M. Stallman committed
475

476
/* Write to file OUT the argument names of function FUNC, whose text is in BUF.
Richard M. Stallman's avatar
Richard M. Stallman committed
477 478
   MINARGS and MAXARGS are the minimum and maximum number of arguments.  */

479
void
480
write_c_args (FILE *out, char *func, char *buf, int minargs, int maxargs)
Richard M. Stallman's avatar
Richard M. Stallman committed
481
{
Jim Blandy's avatar
Jim Blandy committed
482
  register char *p;
Jim Blandy's avatar
Jim Blandy committed
483
  int in_ident = 0;
484
  char *ident_start;
Dan Nicolaescu's avatar
Dan Nicolaescu committed
485
  size_t ident_length = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
486

487
  fprintf (out, "(fn");
488 489 490

  if (*buf == '(')
    ++buf;
Richard M. Stallman's avatar
Richard M. Stallman committed
491

Jim Blandy's avatar
Jim Blandy committed
492
  for (p = buf; *p; p++)
Richard M. Stallman's avatar
Richard M. Stallman committed
493
    {
494
      char c = *p;
Jim Blandy's avatar
Jim Blandy committed
495

496
      /* Notice when a new identifier starts.  */
Jim Blandy's avatar
Jim Blandy committed
497 498 499 500 501
      if ((('A' <= c && c <= 'Z')
	   || ('a' <= c && c <= 'z')
	   || ('0' <= c && c <= '9')
	   || c == '_')
	  != in_ident)
Richard M. Stallman's avatar
Richard M. Stallman committed
502
	{
Jim Blandy's avatar
Jim Blandy committed
503 504 505
	  if (!in_ident)
	    {
	      in_ident = 1;
506
	      ident_start = p;
Jim Blandy's avatar
Jim Blandy committed
507 508
	    }
	  else
509 510 511 512
	    {
	      in_ident = 0;
	      ident_length = p - ident_start;
	    }
Richard M. Stallman's avatar
Richard M. Stallman committed
513
	}
Jim Blandy's avatar
Jim Blandy committed
514

515 516 517
      /* Found the end of an argument, write out the last seen
	 identifier.  */
      if (c == ',' || c == ')')
518
	{
519 520 521 522 523 524
	  if (ident_length == 0)
	    {
	      error ("empty arg list for `%s' should be (void), not ()", func);
	      continue;
	    }

525 526 527 528 529 530 531
	  if (strncmp (ident_start, "void", ident_length) == 0)
	    continue;

	  putc (' ', out);

	  if (minargs == 0 && maxargs > 0)
	    fprintf (out, "&optional ");
Jim Blandy's avatar
Jim Blandy committed
532

533 534 535 536 537
	  minargs--;
	  maxargs--;

	  /* In C code, `default' is a reserved word, so we spell it
	     `defalt'; unmangle that here.  */
538
	  if (ident_length == 6 && strncmp (ident_start, "defalt", 6) == 0)
539 540 541 542 543 544 545 546 547 548 549 550 551 552
	    fprintf (out, "DEFAULT");
	  else
	    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);
	      }
	}
Richard M. Stallman's avatar
Richard M. Stallman committed
553
    }
554 555

  putc (')', out);
Richard M. Stallman's avatar
Richard M. Stallman committed
556
}
Tom Tromey's avatar
Tom Tromey committed
557 558 559 560

/* The types of globals.  */
enum global_type
{
561
  EMACS_INTEGER,
Tom Tromey's avatar
Tom Tromey committed
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623
  BOOLEAN,
  LISP_OBJECT,
  INVALID
};

/* A single global.  */
struct global
{
  enum global_type type;
  char *name;
};

/* 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)
	{
	  num_globals_allocated *= 2;
	  globals = xrealloc (globals,
			      num_globals_allocated * sizeof (struct global));
	}

      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);
}

void
write_globals (void)
{
  int i;
  qsort (globals, num_globals, sizeof (struct global), compare_globals);
  for (i = 0; i < num_globals; ++i)
    {
      char *type;

      switch (globals[i].type)
	{
624
	case EMACS_INTEGER:
Tom Tromey's avatar
Tom Tromey committed
625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648
	  type = "EMACS_INT";
	  break;
	case BOOLEAN:
	  type = "int";
	  break;
	case LISP_OBJECT:
	  type = "Lisp_Object";
	  break;
	default:
	  fatal ("not a recognized DEFVAR_", 0);
	}

      fprintf (outfile, "  %s f_%s;\n", type, globals[i].name);
      fprintf (outfile, "#define %s globals.f_%s\n",
	       globals[i].name, globals[i].name);
      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");
}

Richard M. Stallman's avatar
Richard M. Stallman committed
649 650

/* Read through a c file.  If a .o file is named,
651
   the corresponding .c or .m file is read instead.
Richard M. Stallman's avatar
Richard M. Stallman committed
652 653 654
   Looks for DEFUN constructs such as are defined in ../src/lisp.h.
   Accepts any word starting DEF... so it finds DEFSIMPLE and DEFPRED.  */

655
int
656
scan_c_file (char *filename, const char *mode)
Richard M. Stallman's avatar
Richard M. Stallman committed
657 658 659 660 661
{
  FILE *infile;
  register int c;
  register int commas;
  register int defunflag;
662
  register int defvarperbufferflag;
Richard M. Stallman's avatar
Richard M. Stallman committed
663 664
  register int defvarflag;
  int minargs, maxargs;
665
  int extension = filename[strlen (filename) - 1];
Tom Tromey's avatar
Tom Tromey committed
666
  enum global_type type;
Richard M. Stallman's avatar
Richard M. Stallman committed
667

668
  if (extension == 'o')
Richard M. Stallman's avatar
Richard M. Stallman committed
669 670
    filename[strlen (filename) - 1] = 'c';

671
  infile = fopen (filename, mode);
Richard M. Stallman's avatar
Richard M. Stallman committed
672

673 674 675 676 677 678 679 680 681
  if (infile == NULL && extension == 'o')
    {
      /* try .m */
      filename[strlen (filename) - 1] = 'm';
      infile = fopen (filename, mode);
      if (infile == NULL)
        filename[strlen (filename) - 1] = 'c'; /* don't confuse people */
    }

Richard M. Stallman's avatar
Richard M. Stallman committed
682 683 684 685 686 687 688
  /* No error if non-ex input file */
  if (infile == NULL)
    {
      perror (filename);
      return 0;
    }

Pavel Janík's avatar
Pavel Janík committed
689
  /* Reset extension to be able to detect duplicate files.  */
690 691
  filename[strlen (filename) - 1] = extension;

Richard M. Stallman's avatar
Richard M. Stallman committed
692 693 694
  c = '\n';
  while (!feof (infile))
    {
695 696
      int doc_keyword = 0;

697
      if (c != '\n' && c != '\r')
Richard M. Stallman's avatar
Richard M. Stallman committed
698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717
	{
	  c = getc (infile);
	  continue;
	}
      c = getc (infile);
      if (c == ' ')
	{
	  while (c == ' ')
	    c = getc (infile);
	  if (c != 'D')
	    continue;
	  c = getc (infile);
	  if (c != 'E')
	    continue;
	  c = getc (infile);
	  if (c != 'F')
	    continue;
	  c = getc (infile);
	  if (c != 'V')
	    continue;
718 719 720 721 722 723 724 725 726 727
	  c = getc (infile);
	  if (c != 'A')
	    continue;
	  c = getc (infile);
	  if (c != 'R')
	    continue;
	  c = getc (infile);
	  if (c != '_')
	    continue;

Richard M. Stallman's avatar
Richard M. Stallman committed
728 729
	  defvarflag = 1;
	  defunflag = 0;
730 731 732

	  c = getc (infile);
	  defvarperbufferflag = (c == 'P');
Tom Tromey's avatar
Tom Tromey committed
733 734 735
	  if (generate_globals)
	    {
	      if (c == 'I')
736
		type = EMACS_INTEGER;
Tom Tromey's avatar
Tom Tromey committed
737 738 739 740 741 742 743
	      else if (c == 'L')
		type = LISP_OBJECT;
	      else if (c == 'B')
		type = BOOLEAN;
	      else
		type = INVALID;
	    }
744

Richard M. Stallman's avatar
Richard M. Stallman committed
745
	  c = getc (infile);
Tom Tromey's avatar
Tom Tromey committed
746 747 748 749
	  /* We need to distinguish between DEFVAR_BOOL and
	     DEFVAR_BUFFER_DEFAULTS.  */
	  if (generate_globals && type == BOOLEAN && c != 'O')
	    type = INVALID;
Richard M. Stallman's avatar
Richard M. Stallman committed
750 751 752 753 754 755 756 757 758 759 760 761
	}
      else if (c == 'D')
	{
	  c = getc (infile);
	  if (c != 'E')
	    continue;
	  c = getc (infile);
	  if (c != 'F')
	    continue;
	  c = getc (infile);
	  defunflag = c == 'U';
	  defvarflag = 0;
762
	  defvarperbufferflag = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
763 764 765
	}
      else continue;

Tom Tromey's avatar
Tom Tromey committed
766 767 768 769
      if (generate_globals && (!defvarflag || defvarperbufferflag
			       || type == INVALID))
	continue;

Richard M. Stallman's avatar
Richard M. Stallman committed
770 771 772 773 774 775 776
      while (c != '(')
	{
	  if (c < 0)
	    goto eof;
	  c = getc (infile);
	}

777
      /* Lisp variable or function name.  */
Richard M. Stallman's avatar
Richard M. Stallman committed
778 779 780
      c = getc (infile);
      if (c != '"')
	continue;
781
      c = read_c_string_or_comment (infile, -1, 0, 0);
782

Tom Tromey's avatar
Tom Tromey committed
783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810
      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
	    {
	      buf[i++] = c;
	      c = getc (infile);
	    }
	  while (! (c == ',' || c == ' ' || c == '\t' ||
		    c == '\n' || c == '\r'));
	  buf[i] = '\0';

	  name = xmalloc (i + 1);
	  memcpy (name, buf, i + 1);
	  add_global (type, name);
	  continue;
	}

811 812 813
      /* DEFVAR_LISP ("name", addr, "doc")
	 DEFVAR_LISP ("name", addr /\* doc *\/)
	 DEFVAR_LISP ("name", addr, doc: /\* doc *\/)  */
Richard M. Stallman's avatar
Richard M. Stallman committed
814 815 816

      if (defunflag)
	commas = 5;
817 818
      else if (defvarperbufferflag)
	commas = 2;
Richard M. Stallman's avatar
Richard M. Stallman committed
819 820 821 822 823 824 825 826 827 828
      else if (defvarflag)
	commas = 1;
      else  /* For DEFSIMPLE and DEFPRED */
	commas = 2;

      while (commas)
	{
	  if (c == ',')
	    {
	      commas--;
829

Richard M. Stallman's avatar
Richard M. Stallman committed
830 831
	      if (defunflag && (commas == 1 || commas == 2))
		{
832
		  int scanned = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
833 834
		  do
		    c = getc (infile);
835
		  while (c == ' ' || c == '\n' || c == '\r' || c == '\t');
Richard M. Stallman's avatar
Richard M. Stallman committed
836 837 838 839
		  if (c < 0)
		    goto eof;
		  ungetc (c, infile);
		  if (commas == 2) /* pick up minargs */
840
		    scanned = fscanf (infile, "%d", &minargs);
Richard M. Stallman's avatar
Richard M. Stallman committed
841 842 843 844
		  else /* pick up maxargs */
		    if (c == 'M' || c == 'U') /* MANY || UNEVALLED */
		      maxargs = -1;
		    else
845 846 847
		      scanned = fscanf (infile, "%d", &maxargs);
		  if (scanned < 0)
		    goto eof;
Richard M. Stallman's avatar
Richard M. Stallman committed
848 849
		}
	    }
850 851

	  if (c == EOF)
Richard M. Stallman's avatar
Richard M. Stallman committed
852 853 854
	    goto eof;
	  c = getc (infile);
	}
855

856
      while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
Richard M. Stallman's avatar
Richard M. Stallman committed
857
	c = getc (infile);
Pavel Janík's avatar
Pavel Janík committed
858

Richard M. Stallman's avatar
Richard M. Stallman committed
859
      if (c == '"')
860
	c = read_c_string_or_comment (infile, 0, 0, 0);
Pavel Janík's avatar
Pavel Janík committed
861

862
      while (c != EOF && c != ',' && c != '/')
Richard M. Stallman's avatar
Richard M. Stallman committed
863
	c = getc (infile);
864 865
      if (c == ',')
	{
866 867 868 869 870 871 872 873 874 875 876 877
	  c = getc (infile);
	  while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
	    c = getc (infile);
	  while ((c >= 'a' && c <= 'z') || (c >= 'Z' && c <= 'Z'))
	    c = getc (infile);
	  if (c == ':')
	    {
	      doc_keyword = 1;
	      c = getc (infile);
	      while (c == ' ' || c == '\n' || c == '\r' || c == '\t')
		c = getc (infile);
	    }
878
	}
Richard M. Stallman's avatar
Richard M. Stallman committed
879

880 881 882 883 884
      if (c == '"'
	  || (c == '/'
	      && (c = getc (infile),
		  ungetc (c, infile),
		  c == '*')))
Richard M. Stallman's avatar
Richard M. Stallman committed
885
	{
886
	  int comment = c != '"';
887
	  int saw_usage;
Pavel Janík's avatar
Pavel Janík committed
888

Richard M. Stallman's avatar
Richard M. Stallman committed
889 890 891
	  putc (037, outfile);
	  putc (defvarflag ? 'V' : 'F', outfile);
	  fprintf (outfile, "%s\n", buf);
892 893 894

	  if (comment)
	    getc (infile); 	/* Skip past `*' */
895
	  c = read_c_string_or_comment (infile, 1, comment, &saw_usage);
Jim Blandy's avatar
Jim Blandy committed
896 897 898 899

	  /* If this is a defun, find the arguments and print them.  If
	     this function takes MANY or UNEVALLED args, then the C source
	     won't give the names of the arguments, so we shouldn't bother
900 901
	     trying to find them.

902 903 904 905 906
	     Various doc-string styles:
	      0: DEFUN (..., "DOC") (args)            [!comment]
	      1: DEFUN (..., /\* DOC *\/ (args))      [comment && !doc_keyword]
	      2: DEFUN (..., doc: /\* DOC *\/) (args) [comment && doc_keyword]
	  */
907
	  if (defunflag && maxargs != -1 && !saw_usage)
Richard M. Stallman's avatar
Richard M. Stallman committed
908 909
	    {
	      char argbuf[1024], *p = argbuf;
910

911
	      if (!comment || doc_keyword)
912 913 914 915 916 917
		while (c != ')')
		  {
		    if (c < 0)
		      goto eof;
		    c = getc (infile);
		  }
Pavel Janík's avatar
Pavel Janík committed
918

Richard M. Stallman's avatar
Richard M. Stallman committed
919 920 921 922 923 924 925 926 927 928 929 930 931 932 933
	      /* Skip into arguments.  */
	      while (c != '(')
		{
		  if (c < 0)
		    goto eof;
		  c = getc (infile);
		}
	      /* Copy arguments into ARGBUF.  */
	      *p++ = c;
	      do
		*p++ = c = getc (infile);
	      while (c != ')');
	      *p = '\0';
	      /* Output them.  */
	      fprintf (outfile, "\n\n");
934
	      write_c_args (outfile, buf, argbuf, minargs, maxargs);
Richard M. Stallman's avatar
Richard M. Stallman committed
935
	    }
936 937 938
	  else if (defunflag && maxargs == -1 && !saw_usage)
	    /* The DOC should provide the usage form.  */
	    fprintf (stderr, "Missing `usage' for function `%s'.\n", buf);
Richard M. Stallman's avatar
Richard M. Stallman committed
939 940 941 942 943 944 945 946 947 948
	}
    }
 eof:
  fclose (infile);
  return 0;
}

/* Read a file of Lisp code, compiled or interpreted.
 Looks for
  (defun NAME ARGS DOCSTRING ...)
Jim Blandy's avatar
Jim Blandy committed
949
  (defmacro NAME ARGS DOCSTRING ...)
950
  (defsubst NAME ARGS DOCSTRING ...)
Jim Blandy's avatar
Jim Blandy committed
951
  (autoload (quote NAME) FILE DOCSTRING ...)
Richard M. Stallman's avatar
Richard M. Stallman committed
952 953
  (defvar NAME VALUE DOCSTRING)
  (defconst NAME VALUE DOCSTRING)
Jim Blandy's avatar
Jim Blandy committed
954 955
  (fset (quote NAME) (make-byte-code ... DOCSTRING ...))
  (fset (quote NAME) #[... DOCSTRING ...])
956
  (defalias (quote NAME) #[... DOCSTRING ...])
957
  (custom-declare-variable (quote NAME) VALUE DOCSTRING ...)
Richard M. Stallman's avatar
Richard M. Stallman committed
958
 starting in column zero.
Jim Blandy's avatar
Jim Blandy committed
959
 (quote NAME) may appear as 'NAME as well.
960 961 962 963 964

 We also look for #@LENGTH CONTENTS^_ at the beginning of the line.
 When we find that, we save it for the following defining-form,
 and we use that instead of reading a doc string within that defining-form.

Pavel Janík's avatar
Pavel Janík committed
965
 For defvar, defconst, and fset we skip to the docstring with a kludgy
Jim Blandy's avatar
Jim Blandy committed
966
 formatting convention: all docstrings must appear on the same line as the
Pavel Janík's avatar
Pavel Janík committed
967
 initial open-paren (the one in column zero) and must contain a backslash
968
 and a newline immediately after the initial double-quote.  No newlines
Jim Blandy's avatar
Jim Blandy committed
969
 must appear between the beginning of the form and the first double-quote.
970 971
 For defun, defmacro, and autoload, we know how to skip over the
 arglist, but the doc string must still have a backslash and newline
Pavel Janík's avatar
Pavel Janík committed
972
 immediately after the double quote.
973 974
 The only source files that must follow this convention are preloaded
 uncompiled ones like loaddefs.el and bindings.el; aside
Jim Blandy's avatar
Jim Blandy committed
975 976
 from that, it is always the .elc file that we look at, and they are no
 problem because byte-compiler output follows this convention.
Richard M. Stallman's avatar
Richard M. Stallman committed
977 978 979 980 981
 The NAME and DOCSTRING are output.
 NAME is preceded by `F' for a function or `V' for a variable.
 An entry is output only if DOCSTRING has \ newline just after the opening "
 */

Jim Blandy's avatar
Jim Blandy committed
982
void
983
skip_white (FILE *infile)
Jim Blandy's avatar
Jim Blandy committed
984 985
{
  char c = ' ';
986
  while (c == ' ' || c == '\t' || c == '\n' || c == '\r')
Jim Blandy's avatar
Jim Blandy committed
987 988 989 990 991
    c = getc (infile);
  ungetc (c, infile);
}

void
992
read_lisp_symbol (FILE *infile, char *buffer)
Jim Blandy's avatar
Jim Blandy committed
993 994 995 996 997 998 999 1000 1001 1002
{
  char c;
  char *fillp = buffer;

  skip_white (infile);
  while (1)
    {
      c = getc (infile);
      if (c == '\\')
	*(++fillp) = getc (infile);
1003
      else if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '(' || c == ')')
Jim Blandy's avatar
Jim Blandy committed
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014
	{
	  ungetc (c, infile);
	  *fillp = 0;
	  break;
	}
      else
	*fillp++ = c;
    }

  if (! buffer[0])
    fprintf (stderr, "## expected a symbol, got '%c'\n", c);
Pavel Janík's avatar
Pavel Janík committed
1015

Jim Blandy's avatar
Jim Blandy committed
1016 1017 1018
  skip_white (infile);
}

1019
int
1020
scan_lisp_file (const char *filename, const char *mode)
Richard M. Stallman's avatar
Richard M. Stallman committed
1021 1022 1023
{
  FILE *infile;
  register int c;
1024
  char *saved_string = 0;
Richard M. Stallman's avatar
Richard M. Stallman committed
1025

Tom Tromey's avatar
Tom Tromey committed
1026 1027 1028
  if (generate_globals)
    fatal ("scanning lisp file when -g specified", 0);

1029
  infile = fopen (filename, mode);
Richard M. Stallman's avatar
Richard M. Stallman committed
1030 1031 1032 1033 1034 1035 1036 1037 1038
  if (infile == NULL)
    {
      perror (filename);
      return 0;				/* No error */
    }

  c = '\n';
  while (!feof (infile))
    {
1039
      char buffer[BUFSIZ];
Jim Blandy's avatar
Jim Blandy committed
1040 1041
      char type;

1042
      /* If not at end of line, skip till we get to one.  */
1043
      if (c != '\n' && c != '\r')
Richard M. Stallman's avatar
Richard M. Stallman committed
1044 1045 1046 1047
	{
	  c = getc (infile);
	  continue;
	}
1048
      /* Skip the line break.  */
1049
      while (c == '\n' || c == '\r')
1050
	c = getc (infile);
1051 1052 1053 1054 1055 1056
      /* Detect a dynamic doc string and save it for the next expression.  */
      if (c == '#')
	{
	  c = getc (infile);
	  if (c == '@')
	    {
1057 1058
	      size_t length = 0;
	      size_t i;
1059 1060 1061 1062 1063 1064 1065 1066 1067

	      /* Read the length.  */
	      while ((c = getc (infile),
		      c >= '0' && c <= '9'))
		{
		  length *= 10;
		  length += c - '0';
		}

1068 1069 1070 1071 1072 1073
	      if (length <= 1)
		fatal ("invalid dynamic doc string length", "");

	      if (c != ' ')
		fatal ("space not found after dynamic doc string length", "");

1074 1075 1076 1077 1078 1079
	      /* 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.  */
	      length--;

	      /* Read in the contents.  */
1080
	      free (saved_string);
1081
	      saved_string = (char *) xmalloc (length);
1082 1083 1084 1085 1086 1087
	      for (i = 0; i < length; i++)
		saved_string[i] = getc (infile);
	      /* The last character is a ^_.
		 That is needed in the .elc file
		 but it is redundant in DOC.  So get rid of it here.  */
	      saved_string[length - 1] = 0;
1088
	      /* Skip the line break.  */
1089
	      while (c == '\n' || c == '\r')