Commit | Line | Data |
---|---|---|
f2cc4248 RS |
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 distributed in the hope that it will be useful, | |
7 | but without any warranty. No author or distributor | |
8 | accepts responsibility to anyone for the consequences of using it | |
9 | or for whether it serves any particular purpose or works at all, | |
10 | unless he says so in writing. | |
11 | ||
12 | Everyone is granted permission to copy, modify and redistribute | |
13 | GNU Emacs, but only under the conditions described in the | |
14 | document "GNU Emacs copying permission notice". An exact copy | |
15 | of the document is supposed to have been given to you along with | |
16 | GNU Emacs so that you can know how you may redistribute it all. | |
17 | It should be in a file named COPYING. Among other things, the | |
18 | copyright notice and this notice must be preserved on all copies. */ | |
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 | ||
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 | ||
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 | \f | |
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 | ||
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 | \f | |
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 | ||
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 == ',') | |
156 | { | |
157 | minargs--; | |
158 | maxargs--; | |
159 | if (!space) | |
160 | putc (' ', out); | |
161 | if (minargs == 0 && maxargs > 0) | |
162 | fprintf (out, "&optional "); | |
163 | space = 1; | |
164 | continue; | |
165 | } | |
166 | else if (c == ' ' && space) | |
167 | continue; | |
168 | space = (c == ' '); | |
169 | putc (c, out); | |
170 | } | |
171 | putc ('\n', out); | |
172 | } | |
173 | \f | |
174 | /* Read through a c file. If a .o file is named, | |
175 | the corresponding .c file is read instead. | |
176 | Looks for DEFUN constructs such as are defined in ../src/lisp.h. | |
177 | Accepts any word starting DEF... so it finds DEFSIMPLE and DEFPRED. */ | |
178 | ||
179 | scan_c_file (filename) | |
180 | char *filename; | |
181 | { | |
182 | FILE *infile; | |
183 | register int c; | |
184 | register int commas; | |
185 | register int defunflag; | |
186 | register int defvarflag; | |
187 | int minargs, maxargs; | |
188 | ||
189 | if (filename[strlen (filename) - 1] == 'o') | |
190 | filename[strlen (filename) - 1] = 'c'; | |
191 | ||
192 | infile = fopen (filename, "r"); | |
193 | ||
194 | /* No error if non-ex input file */ | |
195 | if (infile == NULL) | |
196 | { | |
197 | perror (filename); | |
198 | return 0; | |
199 | } | |
200 | ||
201 | c = '\n'; | |
202 | while (!feof (infile)) | |
203 | { | |
204 | if (c != '\n') | |
205 | { | |
206 | c = getc (infile); | |
207 | continue; | |
208 | } | |
209 | c = getc (infile); | |
210 | if (c == ' ') | |
211 | { | |
212 | while (c == ' ') | |
213 | c = getc (infile); | |
214 | if (c != 'D') | |
215 | continue; | |
216 | c = getc (infile); | |
217 | if (c != 'E') | |
218 | continue; | |
219 | c = getc (infile); | |
220 | if (c != 'F') | |
221 | continue; | |
222 | c = getc (infile); | |
223 | if (c != 'V') | |
224 | continue; | |
225 | defvarflag = 1; | |
226 | defunflag = 0; | |
227 | c = getc (infile); | |
228 | } | |
229 | else if (c == 'D') | |
230 | { | |
231 | c = getc (infile); | |
232 | if (c != 'E') | |
233 | continue; | |
234 | c = getc (infile); | |
235 | if (c != 'F') | |
236 | continue; | |
237 | c = getc (infile); | |
238 | defunflag = c == 'U'; | |
239 | defvarflag = 0; | |
240 | } | |
241 | else continue; | |
242 | ||
243 | while (c != '(') | |
244 | { | |
245 | if (c < 0) | |
246 | goto eof; | |
247 | c = getc (infile); | |
248 | } | |
249 | ||
250 | c = getc (infile); | |
251 | if (c != '"') | |
252 | continue; | |
253 | c = read_c_string (infile, -1); | |
254 | ||
255 | if (defunflag) | |
256 | commas = 5; | |
257 | else if (defvarflag) | |
258 | commas = 1; | |
259 | else /* For DEFSIMPLE and DEFPRED */ | |
260 | commas = 2; | |
261 | ||
262 | while (commas) | |
263 | { | |
264 | if (c == ',') | |
265 | { | |
266 | commas--; | |
267 | if (defunflag && (commas == 1 || commas == 2)) | |
268 | { | |
269 | do | |
270 | c = getc (infile); | |
271 | while (c == ' ' || c == '\n' || c == '\t'); | |
272 | if (c < 0) | |
273 | goto eof; | |
274 | ungetc (c, infile); | |
275 | if (commas == 2) /* pick up minargs */ | |
276 | fscanf (infile, "%d", &minargs); | |
277 | else /* pick up maxargs */ | |
278 | if (c == 'M' || c == 'U') /* MANY || UNEVALLED */ | |
279 | maxargs = -1; | |
280 | else | |
281 | fscanf (infile, "%d", &maxargs); | |
282 | } | |
283 | } | |
284 | if (c < 0) | |
285 | goto eof; | |
286 | c = getc (infile); | |
287 | } | |
288 | while (c == ' ' || c == '\n' || c == '\t') | |
289 | c = getc (infile); | |
290 | if (c == '"') | |
291 | c = read_c_string (infile, 0); | |
292 | while (c != ',') | |
293 | c = getc (infile); | |
294 | c = getc (infile); | |
295 | while (c == ' ' || c == '\n' || c == '\t') | |
296 | c = getc (infile); | |
297 | ||
298 | if (c == '"') | |
299 | { | |
300 | putc (037, outfile); | |
301 | putc (defvarflag ? 'V' : 'F', outfile); | |
302 | fprintf (outfile, "%s\n", buf); | |
303 | read_c_string (infile, 1); | |
304 | if (defunflag) | |
305 | { | |
306 | char argbuf[1024], *p = argbuf; | |
307 | while (c != ')') | |
308 | { | |
309 | if (c < 0) | |
310 | goto eof; | |
311 | c = getc (infile); | |
312 | } | |
313 | /* Skip into arguments. */ | |
314 | while (c != '(') | |
315 | { | |
316 | if (c < 0) | |
317 | goto eof; | |
318 | c = getc (infile); | |
319 | } | |
320 | /* Copy arguments into ARGBUF. */ | |
321 | *p++ = c; | |
322 | do | |
323 | *p++ = c = getc (infile); | |
324 | while (c != ')'); | |
325 | *p = '\0'; | |
326 | /* Output them. */ | |
327 | fprintf (outfile, "\n\n"); | |
328 | write_c_args (outfile, argbuf, minargs, maxargs); | |
329 | } | |
330 | } | |
331 | } | |
332 | eof: | |
333 | fclose (infile); | |
334 | return 0; | |
335 | } | |
336 | \f | |
337 | /* Read a file of Lisp code, compiled or interpreted. | |
338 | Looks for | |
339 | (defun NAME ARGS DOCSTRING ...) | |
340 | (autoload 'NAME FILE DOCSTRING ...) | |
341 | (defvar NAME VALUE DOCSTRING) | |
342 | (defconst NAME VALUE DOCSTRING) | |
343 | starting in column zero. | |
344 | ARGS, FILE or VALUE is ignored. We do not know how to parse Lisp code | |
345 | so we use a kludge to skip them: | |
346 | In a function definition, the form of ARGS of FILE is known, and we | |
347 | can skip it. | |
348 | In a variable definition, we use a formatting convention: | |
349 | the DOCSTRING, if present, must be followed by a closeparen and a newline, | |
350 | and no newline must appear between the defvar or defconst and the docstring, | |
351 | The only source file that must follow this convention is loaddefs.el; | |
352 | aside from that, it is always the .elc file that we look at, and | |
353 | they are no problem because byte-compiler output follows this convention. | |
354 | The NAME and DOCSTRING are output. | |
355 | NAME is preceded by `F' for a function or `V' for a variable. | |
356 | An entry is output only if DOCSTRING has \ newline just after the opening " | |
357 | */ | |
358 | ||
359 | scan_lisp_file (filename) | |
360 | char *filename; | |
361 | { | |
362 | FILE *infile; | |
363 | register int c; | |
364 | register int commas; | |
365 | register char *p; | |
366 | int defvarflag; | |
367 | ||
368 | infile = fopen (filename, "r"); | |
369 | if (infile == NULL) | |
370 | { | |
371 | perror (filename); | |
372 | return 0; /* No error */ | |
373 | } | |
374 | ||
375 | c = '\n'; | |
376 | while (!feof (infile)) | |
377 | { | |
378 | if (c != '\n') | |
379 | { | |
380 | c = getc (infile); | |
381 | continue; | |
382 | } | |
383 | c = getc (infile); | |
384 | if (c != '(') | |
385 | continue; | |
386 | c = getc (infile); | |
387 | if (c == 'a') | |
388 | { | |
389 | c = getc (infile); | |
390 | if (c != 'u') | |
391 | continue; | |
392 | c = getc (infile); | |
393 | if (c != 't') | |
394 | continue; | |
395 | c = getc (infile); | |
396 | if (c != 'o') | |
397 | continue; | |
398 | c = getc (infile); | |
399 | if (c != 'l') | |
400 | continue; | |
401 | c = getc (infile); | |
402 | if (c != 'o') | |
403 | continue; | |
404 | c = getc (infile); | |
405 | if (c != 'a') | |
406 | continue; | |
407 | c = getc (infile); | |
408 | if (c != 'd') | |
409 | continue; | |
410 | ||
411 | c = getc (infile); | |
412 | while (c == ' ') | |
413 | c = getc (infile); | |
414 | ||
415 | if (c == '\'') | |
416 | { | |
417 | c = getc (infile); | |
418 | } | |
419 | else | |
420 | { | |
421 | if (c != '(') | |
422 | continue; | |
423 | c = getc (infile); | |
424 | if (c != 'q') | |
425 | continue; | |
426 | c = getc (infile); | |
427 | if (c != 'u') | |
428 | continue; | |
429 | c = getc (infile); | |
430 | if (c != 'o') | |
431 | continue; | |
432 | c = getc (infile); | |
433 | if (c != 't') | |
434 | continue; | |
435 | c = getc (infile); | |
436 | if (c != 'e') | |
437 | continue; | |
438 | c = getc (infile); | |
439 | if (c != ' ') | |
440 | continue; | |
441 | while (c == ' ') | |
442 | c = getc (infile); | |
443 | } | |
444 | ||
445 | p = buf; | |
446 | while (c != ' ' && c != ')') | |
447 | { | |
448 | if (c == EOF) | |
449 | return 1; | |
450 | if (c == '\\') | |
451 | c = getc (infile); | |
452 | *p++ = c; | |
453 | c = getc (infile); | |
454 | } | |
455 | *p = 0; | |
456 | ||
457 | while (c != '"') | |
458 | { | |
459 | if (c == EOF) | |
460 | return 1; | |
461 | c = getc (infile); | |
462 | } | |
463 | c = read_c_string (infile, 0); | |
464 | } | |
465 | else if (c == 'd') | |
466 | { | |
467 | c = getc (infile); | |
468 | if (c != 'e') | |
469 | continue; | |
470 | c = getc (infile); | |
471 | if (c != 'f') | |
472 | continue; | |
473 | c = getc (infile); | |
474 | if (c == 'u') | |
475 | { | |
476 | c = getc (infile); | |
477 | if (c != 'n') | |
478 | continue; | |
479 | defvarflag = 0; | |
480 | } | |
481 | else if (c == 'v') | |
482 | { | |
483 | c = getc (infile); | |
484 | if (c != 'a') | |
485 | continue; | |
486 | c = getc (infile); | |
487 | if (c != 'r') | |
488 | continue; | |
489 | defvarflag = 1; | |
490 | } | |
491 | else if (c == 'c') | |
492 | { | |
493 | c = getc (infile); | |
494 | if (c != 'o') | |
495 | continue; | |
496 | c = getc (infile); | |
497 | if (c != 'n') | |
498 | continue; | |
499 | c = getc (infile); | |
500 | if (c != 's') | |
501 | continue; | |
502 | c = getc (infile); | |
503 | if (c != 't') | |
504 | continue; | |
505 | defvarflag = 1; | |
506 | } | |
507 | else | |
508 | continue; | |
509 | ||
510 | /* Now we have seen "defun" or "defvar" or "defconst". */ | |
511 | ||
512 | while (c != ' ' && c != '\n' && c != '\t') | |
513 | c = getc (infile); | |
514 | ||
515 | while (c == ' ' || c == '\n' || c == '\t') | |
516 | c = getc (infile); | |
517 | ||
518 | /* Read and store name of function or variable being defined | |
519 | Discard backslashes that are for quoting. */ | |
520 | p = buf; | |
521 | while (c != ' ' && c != '\n' && c != '\t') | |
522 | { | |
523 | if (c == '\\') | |
524 | c = getc (infile); | |
525 | *p++ = c; | |
526 | c = getc (infile); | |
527 | } | |
528 | *p = 0; | |
529 | ||
530 | while (c == ' ' || c == '\n' || c == '\t') | |
531 | c = getc (infile); | |
532 | ||
533 | if (! defvarflag) | |
534 | { | |
535 | /* A function: */ | |
536 | /* Skip the arguments: either "nil" or a list in parens */ | |
537 | if (c == 'n') | |
538 | { | |
539 | while (c != ' ' && c != '\n' && c != '\t') | |
540 | c = getc (infile); | |
541 | } | |
542 | else | |
543 | { | |
544 | while (c != '(') | |
545 | c = getc (infile); | |
546 | while (c != ')') | |
547 | c = getc (infile); | |
548 | } | |
549 | c = getc (infile); | |
550 | } | |
551 | else | |
552 | { | |
553 | /* A variable: */ | |
554 | ||
555 | /* Skip until the first newline; remember | |
556 | the two previous characters. */ | |
557 | char c1 = 0, c2 = 0; | |
558 | ||
559 | while (c != '\n' && c >= 0) | |
560 | { | |
561 | c2 = c1; | |
562 | c1 = c; | |
563 | c = getc (infile); | |
564 | } | |
565 | ||
566 | /* If two previous characters were " and \, | |
567 | this is a doc string. Otherwise, there is none. */ | |
568 | if (c2 == '"' && c1 == '\\') | |
569 | { | |
570 | putc (037, outfile); | |
571 | putc ('V', outfile); | |
572 | fprintf (outfile, "%s\n", buf); | |
573 | read_c_string (infile, 1); | |
574 | } | |
575 | continue; | |
576 | } | |
577 | } | |
578 | else | |
579 | continue; | |
580 | ||
581 | /* Here for a function definition. | |
582 | We have skipped the file name or arguments | |
583 | and arrived at where the doc string is, | |
584 | if there is a doc string. */ | |
585 | ||
586 | /* Skip whitespace */ | |
587 | ||
588 | while (c == ' ' || c == '\n' || c == '\t') | |
589 | c = getc (infile); | |
590 | ||
591 | /* " followed by \ and newline means a doc string we should gobble */ | |
592 | if (c != '"') | |
593 | continue; | |
594 | c = getc (infile); | |
595 | if (c != '\\') | |
596 | continue; | |
597 | c = getc (infile); | |
598 | if (c != '\n') | |
599 | continue; | |
600 | ||
601 | putc (037, outfile); | |
602 | putc ('F', outfile); | |
603 | fprintf (outfile, "%s\n", buf); | |
604 | read_c_string (infile, 1); | |
605 | } | |
606 | fclose (infile); | |
607 | return 0; | |
608 | } |