1 /* Generate doc-string file for GNU Emacs from source files.
2    Copyright (C) 1985, 1986 Free Software Foundation, Inc.
3 
4 This file is part of GNU Emacs.
5 
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 1, or (at your option)
9 any later version.
10 
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15 
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING.  If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19 
20 /* The arguments given to this program are all the C and Lisp source files
21  of GNU Emacs.  .elc and .el and .c files are allowed.
22  A .o file can also be specified; the .c file it was made from is used.
23  This helps the makefile pass the correct list of files.
24 
25  The results, which go to standard output or to a file
26  specified with -a or -o (-a to append, -o to start from nothing),
27  are entries containing function or variable names and their documentation.
28  Each entry starts with a ^_ character.
29  Then comes F for a function or V for a variable.
30  Then comes the function or variable name, terminated with a newline.
31  Then comes the documentation for that function or variable.
32  */
33 
34 #include <stdio.h>
35 
36 FILE *outfile;
37 
main(argc,argv)38 main (argc, argv)
39      int argc;
40      char **argv;
41 {
42   int i;
43   int err_count = 0;
44 
45   outfile = stdout;
46 
47   /* If first two args are -o FILE, output to FILE.  */
48   i = 1;
49   if (argc > i + 1 && !strcmp (argv[i], "-o"))
50     {
51       outfile = fopen (argv[i + 1], "w");
52       i += 2;
53     }
54   if (argc > i + 1 && !strcmp (argv[i], "-a"))
55     {
56       outfile = fopen (argv[i + 1], "a");
57       i += 2;
58     }
59 
60   for (; i < argc; i++)
61     err_count += scan_file (argv[i]);	/* err_count seems to be {mis,un}used */
62 #ifndef VMS
63   exit (err_count);			/* see below - shane */
64 #endif VMS
65 }
66 
67 /* Read file FILENAME and output its doc strings to stdout.  */
68 /* Return 1 if file is not found, 0 if it is found.  */
69 
scan_file(filename)70 scan_file (filename)
71      char *filename;
72 {
73   int len = strlen (filename);
74   if (!strcmp (filename + len - 4, ".elc"))
75     return scan_lisp_file (filename);
76   else if (!strcmp (filename + len - 3, ".el"))
77     return scan_lisp_file (filename);
78   else
79     return scan_c_file (filename);
80 }
81 
82 char buf[128];
83 
84 /* Skip a C string from INFILE,
85  and return the character that follows the closing ".
86  If printflag is positive, output string contents to stdout.
87  If it is negative, store contents in buf.
88  Convert escape sequences \n and \t to newline and tab;
89  discard \ followed by newline.  */
90 
read_c_string(infile,printflag)91 read_c_string (infile, printflag)
92      FILE *infile;
93      int printflag;
94 {
95   register int c;
96   char *p = buf;
97 
98   c = getc (infile);
99   while (c != EOF)
100     {
101       while (c != '"' && c != EOF)
102 	{
103 	  if (c == '\\')
104 	    {
105 	      c = getc (infile);
106 	      if (c == '\n')
107 		{
108 		  c = getc (infile);
109 		  continue;
110 		}
111 	      if (c == 'n')
112 		c = '\n';
113 	      if (c == 't')
114 		c = '\t';
115 	    }
116 	  if (printflag > 0)
117 	    putc (c, outfile);
118 	  else if (printflag < 0)
119 	    *p++ = c;
120 	  c = getc (infile);
121 	}
122       c = getc (infile);
123       if (c != '"')
124 	break;
125       if (printflag > 0)
126 	putc (c, outfile);
127       else if (printflag < 0)
128 	*p++ = c;
129       c = getc (infile);
130     }
131 
132   if (printflag < 0)
133     *p = 0;
134 
135   return c;
136 }
137 
138 /* Write to file OUT the argument names of the function whose text is in BUF.
139    MINARGS and MAXARGS are the minimum and maximum number of arguments.  */
140 
write_c_args(out,buf,minargs,maxargs)141 write_c_args (out, buf, minargs, maxargs)
142      FILE *out;
143      char *buf;
144      int minargs, maxargs;
145 {
146   register int c;
147   register char *p = buf;
148   int space = 0;
149 
150   fprintf (out, "arguments:");
151 
152   while (*p)
153     {
154       c = *p++;
155       if (c == '(' && minargs == 0 && maxargs > 0)
156 	{
157 	  fprintf (out, "(&optional ");
158 	  space = 1;
159 	  continue;
160 	}
161       else if (c == ',')
162 	{
163 	  minargs--;
164 	  maxargs--;
165 	  if (!space)
166 	    putc (' ', out);
167 	  if (minargs == 0 && maxargs > 0)
168 	    fprintf (out, "&optional ");
169 	  space = 1;
170 	  continue;
171 	}
172       else if (c == ' ' && space)
173 	continue;
174       space = (c == ' ');
175       putc (c, out);
176     }
177   putc ('\n', out);
178 }
179 
180 /* Read through a c file.  If a .o file is named,
181    the corresponding .c file is read instead.
182    Looks for DEFUN constructs such as are defined in ../src/lisp.h.
183    Accepts any word starting DEF... so it finds DEFSIMPLE and DEFPRED.  */
184 
scan_c_file(filename)185 scan_c_file (filename)
186      char *filename;
187 {
188   FILE *infile;
189   register int c;
190   register int commas;
191   register int defunflag;
192   register int defvarflag;
193   int minargs, maxargs;
194 
195   if (filename[strlen (filename) - 1] == 'o')
196     filename[strlen (filename) - 1] = 'c';
197 
198   infile = fopen (filename, "r");
199 
200   /* No error if non-ex input file */
201   if (infile == NULL)
202     {
203       perror (filename);
204       return 0;
205     }
206 
207   c = '\n';
208   while (!feof (infile))
209     {
210       if (c != '\n')
211 	{
212 	  c = getc (infile);
213 	  continue;
214 	}
215       c = getc (infile);
216       if (c == ' ')
217 	{
218 	  while (c == ' ')
219 	    c = getc (infile);
220 	  if (c != 'D')
221 	    continue;
222 	  c = getc (infile);
223 	  if (c != 'E')
224 	    continue;
225 	  c = getc (infile);
226 	  if (c != 'F')
227 	    continue;
228 	  c = getc (infile);
229 	  if (c != 'V')
230 	    continue;
231 	  defvarflag = 1;
232 	  defunflag = 0;
233 	  c = getc (infile);
234 	}
235       else if (c == 'D')
236 	{
237 	  c = getc (infile);
238 	  if (c != 'E')
239 	    continue;
240 	  c = getc (infile);
241 	  if (c != 'F')
242 	    continue;
243 	  c = getc (infile);
244 	  defunflag = c == 'U';
245 	  defvarflag = 0;
246 	}
247       else continue;
248 
249       while (c != '(')
250 	{
251 	  if (c < 0)
252 	    goto eof;
253 	  c = getc (infile);
254 	}
255 
256       c = getc (infile);
257       if (c != '"')
258 	continue;
259       c = read_c_string (infile, -1);
260 
261       if (defunflag)
262 	commas = 5;
263       else if (defvarflag)
264 	commas = 1;
265       else  /* For DEFSIMPLE and DEFPRED */
266 	commas = 2;
267 
268       while (commas)
269 	{
270 	  if (c == ',')
271 	    {
272 	      commas--;
273 	      if (defunflag && (commas == 1 || commas == 2))
274 		{
275 		  do
276 		    c = getc (infile);
277 		  while (c == ' ' || c == '\n' || c == '\t');
278 		  if (c < 0)
279 		    goto eof;
280 		  ungetc (c, infile);
281 		  if (commas == 2) /* pick up minargs */
282 		    fscanf (infile, "%d", &minargs);
283 		  else /* pick up maxargs */
284 		    if (c == 'M' || c == 'U') /* MANY || UNEVALLED */
285 		      maxargs = -1;
286 		    else
287 		      fscanf (infile, "%d", &maxargs);
288 		}
289 	    }
290 	  if (c < 0)
291 	    goto eof;
292 	  c = getc (infile);
293 	}
294       while (c == ' ' || c == '\n' || c == '\t')
295 	c = getc (infile);
296       if (c == '"')
297 	c = read_c_string (infile, 0);
298       while (c != ',')
299 	c = getc (infile);
300       c = getc (infile);
301       while (c == ' ' || c == '\n' || c == '\t')
302 	c = getc (infile);
303 
304       if (c == '"')
305 	{
306 	  putc (037, outfile);
307 	  putc (defvarflag ? 'V' : 'F', outfile);
308 	  fprintf (outfile, "%s\n", buf);
309 	  c = read_c_string (infile, 1);
310 	  if (defunflag)
311 	    {
312 	      char argbuf[1024], *p = argbuf;
313 	      while (c != ')')
314 		{
315 		  if (c < 0)
316 		    goto eof;
317 		  c = getc (infile);
318 		}
319 	      /* Skip into arguments.  */
320 	      while (c != '(')
321 		{
322 		  if (c < 0)
323 		    goto eof;
324 		  c = getc (infile);
325 		}
326 	      /* Copy arguments into ARGBUF.  */
327 	      *p++ = c;
328 	      do
329 		*p++ = c = getc (infile);
330 	      while (c != ')');
331 	      *p = '\0';
332 	      /* Output them.  */
333 	      fprintf (outfile, "\n\n");
334 	      write_c_args (outfile, argbuf, minargs, maxargs);
335 	    }
336 	}
337     }
338  eof:
339   fclose (infile);
340   return 0;
341 }
342 
343 /* Read a file of Lisp code, compiled or interpreted.
344  Looks for
345   (defun NAME ARGS DOCSTRING ...)
346   (defmacro NAME ARGS DOCSTRING ...)
347   (autoload (quote NAME) FILE DOCSTRING ...)
348   (defvar NAME VALUE DOCSTRING)
349   (defconst NAME VALUE DOCSTRING)
350   (fset (quote NAME) (make-byte-code ... DOCSTRING ...))
351   (fset (quote NAME) #[... DOCSTRING ...])
352  starting in column zero.
353  (quote NAME) may appear as 'NAME as well.
354  For defun, defmacro, and autoload, we know how to skip over the arglist.
355  For defvar, defconst, and fset we skip to the docstring with a klugey
356  formatting convention: all docstrings must appear on the same line as the
357  initial open-paren (the one in column zero) and must contain a backslash
358  and a double-quote immediately after the initial double-quote.  No newlines
359  must appear between the beginning of the form and the first double-quote.
360  The only source file that must follow this convention is loaddefs.el; aside
361  from that, it is always the .elc file that we look at, and they are no
362  problem because byte-compiler output follows this convention.
363  The NAME and DOCSTRING are output.
364  NAME is preceded by `F' for a function or `V' for a variable.
365  An entry is output only if DOCSTRING has \ newline just after the opening "
366  */
367 
368 void
skip_white(infile)369 skip_white (infile)
370      FILE *infile;
371 {
372   char c = ' ';
373   while (c == ' ' || c == '\t' || c == '\n')
374     c = getc (infile);
375   ungetc (c, infile);
376 }
377 
378 void
read_lisp_symbol(infile,buffer)379 read_lisp_symbol (infile, buffer)
380      FILE *infile;
381      char *buffer;
382 {
383   char c;
384   char *fillp = buffer;
385 
386   skip_white (infile);
387   while (1)
388     {
389       c = getc (infile);
390       if (c == '\\')
391 	*(++fillp) = getc (infile);
392       else if (c == ' ' || c == '\t' || c == '\n' || c == '(' || c == ')')
393 	{
394 	  ungetc (c, infile);
395 	  *fillp = 0;
396 	  break;
397 	}
398       else
399 	*fillp++ = c;
400     }
401 
402   if (! buffer[0])
403     fprintf (stderr, "## expected a symbol, got '%c'\n", c);
404 
405   skip_white (infile);
406 }
407 
408 
scan_lisp_file(filename)409 scan_lisp_file (filename)
410      char *filename;
411 {
412   FILE *infile;
413   register int c;
414 
415   infile = fopen (filename, "r");
416   if (infile == NULL)
417     {
418       perror (filename);
419       return 0;				/* No error */
420     }
421 
422   c = '\n';
423   while (!feof (infile))
424     {
425       char buffer [BUFSIZ];
426       char *fillp = buffer;
427       char type;
428 
429       if (c != '\n')
430 	{
431 	  c = getc (infile);
432 	  continue;
433 	}
434       c = getc (infile);
435       if (c != '(')
436 	continue;
437 
438       read_lisp_symbol (infile, buffer);
439 
440       if (! strcmp (buffer, "defun") ||
441 	  ! strcmp (buffer, "defmacro"))
442 	{
443 	  type = 'F';
444 	  read_lisp_symbol (infile, buffer);
445 
446 	  /* Skip the arguments: either "nil" or a list in parens */
447 
448 	  c = getc (infile);
449 	  if (c == 'n') /* nil */
450 	    {
451 	      if ((c = getc (infile)) != 'i' ||
452 		  (c = getc (infile)) != 'l')
453 		{
454 		  fprintf (stderr, "## unparsable arglist in %s (%s)\n",
455 			   buffer, filename);
456 		  continue;
457 		}
458 	    }
459 	  else if (c != '(')
460 	    {
461 	      fprintf (stderr, "## unparsable arglist in %s (%s)\n",
462 		       buffer, filename);
463 	      continue;
464 	    }
465 	  else
466 	    while (c != ')')
467 	      c = getc (infile);
468 	  skip_white (infile);
469 
470 	  /* If the next three characters aren't `dquote bslash newline'
471 	     then we're not reading a docstring.
472 	   */
473 	  if ((c = getc (infile)) != '"' ||
474 	      (c = getc (infile)) != '\\' ||
475 	      (c = getc (infile)) != '\n')
476 	    {
477 #ifdef DEBUG
478 	      fprintf (stderr, "## non-docstring in %s (%s)\n",
479 		       buffer, filename);
480 #endif
481 	      continue;
482 	    }
483 	}
484 
485       else if (! strcmp (buffer, "defvar") ||
486 	       ! strcmp (buffer, "defconst"))
487 	{
488 	  char c1 = 0, c2 = 0;
489 	  type = 'V';
490 	  read_lisp_symbol (infile, buffer);
491 
492 	  /* Skip until the first newline; remember the two previous chars. */
493 	  while (c != '\n' && c >= 0)
494 	    {
495 	      c2 = c1;
496 	      c1 = c;
497 	      c = getc (infile);
498 	    }
499 
500 	  /* If two previous characters were " and \,
501 	     this is a doc string.  Otherwise, there is none.  */
502 	  if (c2 != '"' || c1 != '\\')
503 	    {
504 #ifdef DEBUG
505 	      fprintf (stderr, "## non-docstring in %s (%s)\n",
506 		       buffer, filename);
507 #endif
508 	      continue;
509 	    }
510 	}
511 
512       else if (! strcmp (buffer, "fset"))
513 	{
514 	  char c1 = 0, c2 = 0;
515 	  type = 'F';
516 
517 	  c = getc (infile);
518 	  if (c == '\'')
519 	    read_lisp_symbol (infile, buffer);
520 	  else
521 	    {
522 	      if (c != '(')
523 		{
524 		  fprintf (stderr, "## unparsable name in fset in %s\n",
525 			   filename);
526 		  continue;
527 		}
528 	      read_lisp_symbol (infile, buffer);
529 	      if (strcmp (buffer, "quote"))
530 		{
531 		  fprintf (stderr, "## unparsable name in fset in %s\n",
532 			   filename);
533 		  continue;
534 		}
535 	      read_lisp_symbol (infile, buffer);
536 	      c = getc (infile);
537 	      if (c != ')')
538 		{
539 		  fprintf (stderr,
540 			   "## unparsable quoted name in fset in %s\n",
541 			   filename);
542 		  continue;
543 		}
544 	    }
545 
546 	  /* Skip until the first newline; remember the two previous chars. */
547 	  while (c != '\n' && c >= 0)
548 	    {
549 	      c2 = c1;
550 	      c1 = c;
551 	      c = getc (infile);
552 	    }
553 
554 	  /* If two previous characters were " and \,
555 	     this is a doc string.  Otherwise, there is none.  */
556 	  if (c2 != '"' || c1 != '\\')
557 	    {
558 #ifdef DEBUG
559 	      fprintf (stderr, "## non-docstring in %s (%s)\n",
560 		       buffer, filename);
561 #endif
562 	      continue;
563 	    }
564 	}
565 
566       else if (! strcmp (buffer, "autoload"))
567 	{
568 	  type = 'F';
569 	  c = getc (infile);
570 	  if (c == '\'')
571 	    read_lisp_symbol (infile, buffer);
572 	  else
573 	    {
574 	      if (c != '(')
575 		{
576 		  fprintf (stderr, "## unparsable name in autoload in %s\n",
577 			   filename);
578 		  continue;
579 		}
580 	      read_lisp_symbol (infile, buffer);
581 	      if (strcmp (buffer, "quote"))
582 		{
583 		  fprintf (stderr, "## unparsable name in autoload in %s\n",
584 			   filename);
585 		  continue;
586 		}
587 	      read_lisp_symbol (infile, buffer);
588 	      c = getc (infile);
589 	      if (c != ')')
590 		{
591 		  fprintf (stderr,
592 			   "## unparsable quoted name in autoload in %s\n",
593 			   filename);
594 		  continue;
595 		}
596 	    }
597 	  skip_white (infile);
598 	  if ((c = getc (infile)) != '\"')
599 	    {
600 	      fprintf (stderr, "## autoload of %s unparsable (%s)\n",
601 		       buffer, filename);
602 	      continue;
603 	    }
604 	  read_c_string (infile, 0);
605 	  skip_white (infile);
606 
607 	  /* If the next three characters aren't `dquote bslash newline'
608 	     then we're not reading a docstring.
609 	   */
610 	  if ((c = getc (infile)) != '"' ||
611 	      (c = getc (infile)) != '\\' ||
612 	      (c = getc (infile)) != '\n')
613 	    {
614 #ifdef DEBUG
615 	      fprintf (stderr, "## non-docstring in %s (%s)\n",
616 		       buffer, filename);
617 #endif
618 	      continue;
619 	    }
620 	}
621 
622 #ifdef DEBUG
623       else if (! strcmp (buffer, "if") ||
624 	       ! strcmp (buffer, "byte-code"))
625 	;
626 #endif
627 
628       else
629 	{
630 #ifdef DEBUG
631 	  fprintf (stderr, "## unrecognised top-level form, %s (%s)\n",
632 		   buffer, filename);
633 #endif
634 	  continue;
635 	}
636 
637       /* At this point, there is a docstring that we should gobble.
638 	 The opening quote (and leading backslash-newline) have already
639 	 been read.
640        */
641       putc ('\n', outfile);
642       putc (037, outfile);
643       putc (type, outfile);
644       fprintf (outfile, "%s\n", buffer);
645       read_c_string (infile, 1);
646     }
647   fclose (infile);
648   return 0;
649 }
650