Commit | Line | Data |
---|---|---|
f2cc4248 | 1 | /* Generate doc-string file for GNU Emacs from source files. |
9374581a GM |
2 | |
3 | Copyright (C) 1985-1986, 1992-1994, 1997, 1999-2012 | |
4 | Free Software Foundation, Inc. | |
f2cc4248 RS |
5 | |
6 | This file is part of GNU Emacs. | |
7 | ||
294981c7 | 8 | GNU Emacs is free software: you can redistribute it and/or modify |
93320c23 | 9 | it under the terms of the GNU General Public License as published by |
294981c7 GM |
10 | the Free Software Foundation, either version 3 of the License, or |
11 | (at your option) any later version. | |
93320c23 | 12 | |
f2cc4248 | 13 | GNU Emacs is distributed in the hope that it will be useful, |
93320c23 JA |
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. | |
f2cc4248 | 17 | |
93320c23 | 18 | You should have received a copy of the GNU General Public License |
294981c7 GM |
19 | along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */ |
20 | ||
f2cc4248 RS |
21 | |
22 | /* The arguments given to this program are all the C and Lisp source files | |
23 | of GNU Emacs. .elc and .el and .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. | |
08a39b83 | 26 | Option -d DIR means change to DIR before looking for files. |
f2cc4248 RS |
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 | ||
433d333d RS |
37 | #include <config.h> |
38 | ||
f2cc4248 | 39 | #include <stdio.h> |
9748df5d | 40 | #include <stdlib.h> /* config.h unconditionally includes this anyway */ |
e3938952 RS |
41 | #ifdef MSDOS |
42 | #include <fcntl.h> | |
43 | #endif /* MSDOS */ | |
86b0513a | 44 | #ifdef WINDOWSNT |
9748df5d GM |
45 | /* Defined to be sys_fopen in ms-w32.h, but only #ifdef emacs, so this |
46 | is really just insurance. */ | |
47 | #undef fopen | |
86b0513a RS |
48 | #include <fcntl.h> |
49 | #include <direct.h> | |
50 | #endif /* WINDOWSNT */ | |
e3938952 | 51 | |
86b0513a | 52 | #ifdef DOS_NT |
9748df5d GM |
53 | /* Defined to be sys_chdir in ms-w32.h, but only #ifdef emacs, so this |
54 | is really just insurance. | |
55 | ||
56 | Similarly, msdos defines this as sys_chdir, but we're not linking with the | |
57 | file where that function is defined. */ | |
58 | #undef chdir | |
e3938952 RS |
59 | #define READ_TEXT "rt" |
60 | #define READ_BINARY "rb" | |
86b0513a | 61 | #else /* not DOS_NT */ |
e3938952 RS |
62 | #define READ_TEXT "r" |
63 | #define READ_BINARY "r" | |
86b0513a | 64 | #endif /* not DOS_NT */ |
f2cc4248 | 65 | |
b23b5a5b PE |
66 | static int scan_file (char *filename); |
67 | static int scan_lisp_file (const char *filename, const char *mode); | |
68 | static int scan_c_file (char *filename, const char *mode); | |
b23b5a5b PE |
69 | static void start_globals (void); |
70 | static void write_globals (void); | |
340ff9de | 71 | |
a0613c61 | 72 | #include <unistd.h> |
a0613c61 | 73 | |
b5ff43cc | 74 | /* Stdio stream for output to the DOC file. */ |
f2cc4248 RS |
75 | FILE *outfile; |
76 | ||
b5ff43cc RS |
77 | /* Name this program was invoked with. */ |
78 | char *progname; | |
79 | ||
00b3c7ac TT |
80 | /* Nonzero if this invocation is generating globals.h. */ |
81 | int generate_globals; | |
82 | ||
fb2d3129 | 83 | /* Print error message. `s1' is printf control string, `s2' is arg for it. */ |
b5ff43cc RS |
84 | |
85 | /* VARARGS1 */ | |
b23b5a5b | 86 | static void |
988e88ab | 87 | error (const char *s1, const char *s2) |
b5ff43cc RS |
88 | { |
89 | fprintf (stderr, "%s: ", progname); | |
90 | fprintf (stderr, s1, s2); | |
91 | fprintf (stderr, "\n"); | |
92 | } | |
93 | ||
94 | /* Print error message and exit. */ | |
95 | ||
96 | /* VARARGS1 */ | |
845ca893 | 97 | static _Noreturn void |
988e88ab | 98 | fatal (const char *s1, const char *s2) |
b5ff43cc RS |
99 | { |
100 | error (s1, s2); | |
65396510 | 101 | exit (EXIT_FAILURE); |
b5ff43cc RS |
102 | } |
103 | ||
104 | /* Like malloc but get fatal error if memory is exhausted. */ | |
105 | ||
b23b5a5b | 106 | static void * |
873fbd0b | 107 | xmalloc (unsigned int size) |
b5ff43cc | 108 | { |
d5d66b7e | 109 | void *result = (void *) malloc (size); |
b5ff43cc RS |
110 | if (result == NULL) |
111 | fatal ("virtual memory exhausted", 0); | |
112 | return result; | |
113 | } | |
00b3c7ac TT |
114 | |
115 | /* Like realloc but get fatal error if memory is exhausted. */ | |
116 | ||
b23b5a5b | 117 | static void * |
00b3c7ac TT |
118 | xrealloc (void *arg, unsigned int size) |
119 | { | |
120 | void *result = (void *) realloc (arg, size); | |
121 | if (result == NULL) | |
122 | fatal ("virtual memory exhausted", 0); | |
123 | return result; | |
124 | } | |
125 | ||
b5ff43cc | 126 | \f |
340ff9de | 127 | int |
873fbd0b | 128 | main (int argc, char **argv) |
f2cc4248 RS |
129 | { |
130 | int i; | |
131 | int err_count = 0; | |
a27897c9 | 132 | int first_infile; |
f2cc4248 | 133 | |
b5ff43cc RS |
134 | progname = argv[0]; |
135 | ||
4e043ed3 RS |
136 | outfile = stdout; |
137 | ||
86b0513a | 138 | /* Don't put CRs in the DOC file. */ |
e3938952 | 139 | #ifdef MSDOS |
5281dea4 | 140 | _fmode = O_BINARY; |
4e043ed3 RS |
141 | #if 0 /* Suspicion is that this causes hanging. |
142 | So instead we require people to use -o on MSDOS. */ | |
e3938952 RS |
143 | (stdout)->_flag &= ~_IOTEXT; |
144 | _setmode (fileno (stdout), O_BINARY); | |
4e043ed3 RS |
145 | #endif |
146 | outfile = 0; | |
e3938952 | 147 | #endif /* MSDOS */ |
86b0513a RS |
148 | #ifdef WINDOWSNT |
149 | _fmode = O_BINARY; | |
150 | _setmode (fileno (stdout), O_BINARY); | |
151 | #endif /* WINDOWSNT */ | |
152 | ||
f2cc4248 RS |
153 | /* If first two args are -o FILE, output to FILE. */ |
154 | i = 1; | |
155 | if (argc > i + 1 && !strcmp (argv[i], "-o")) | |
156 | { | |
157 | outfile = fopen (argv[i + 1], "w"); | |
158 | i += 2; | |
159 | } | |
160 | if (argc > i + 1 && !strcmp (argv[i], "-a")) | |
161 | { | |
162 | outfile = fopen (argv[i + 1], "a"); | |
163 | i += 2; | |
164 | } | |
d2d92f7a JB |
165 | if (argc > i + 1 && !strcmp (argv[i], "-d")) |
166 | { | |
9055082e PE |
167 | if (chdir (argv[i + 1]) != 0) |
168 | { | |
169 | perror (argv[i + 1]); | |
170 | return EXIT_FAILURE; | |
171 | } | |
d2d92f7a JB |
172 | i += 2; |
173 | } | |
00b3c7ac TT |
174 | if (argc > i && !strcmp (argv[i], "-g")) |
175 | { | |
176 | generate_globals = 1; | |
177 | ++i; | |
178 | } | |
f2cc4248 | 179 | |
4e043ed3 RS |
180 | if (outfile == 0) |
181 | fatal ("No output file specified", ""); | |
182 | ||
00b3c7ac TT |
183 | if (generate_globals) |
184 | start_globals (); | |
185 | ||
a27897c9 | 186 | first_infile = i; |
f2cc4248 | 187 | for (; i < argc; i++) |
a27897c9 RS |
188 | { |
189 | int j; | |
190 | /* Don't process one file twice. */ | |
191 | for (j = first_infile; j < i; j++) | |
192 | if (! strcmp (argv[i], argv[j])) | |
193 | break; | |
194 | if (j == i) | |
195 | err_count += scan_file (argv[i]); | |
196 | } | |
00b3c7ac TT |
197 | |
198 | if (err_count == 0 && generate_globals) | |
199 | write_globals (); | |
200 | ||
08a39b83 | 201 | return (err_count > 0 ? EXIT_FAILURE : EXIT_SUCCESS); |
f2cc4248 RS |
202 | } |
203 | ||
d5d66b7e | 204 | /* Add a source file name boundary marker in the output file. */ |
b23b5a5b | 205 | static void |
873fbd0b | 206 | put_filename (char *filename) |
d5d66b7e | 207 | { |
b09c5608 EZ |
208 | char *tmp; |
209 | ||
210 | for (tmp = filename; *tmp; tmp++) | |
211 | { | |
61b108cc | 212 | if (IS_DIRECTORY_SEP (*tmp)) |
b09c5608 EZ |
213 | filename = tmp + 1; |
214 | } | |
d5d66b7e SM |
215 | |
216 | putc (037, outfile); | |
217 | putc ('S', outfile); | |
218 | fprintf (outfile, "%s\n", filename); | |
219 | } | |
220 | ||
a8a7afbe | 221 | /* Read file FILENAME and output its doc strings to outfile. */ |
f2cc4248 RS |
222 | /* Return 1 if file is not found, 0 if it is found. */ |
223 | ||
b23b5a5b | 224 | static int |
873fbd0b | 225 | scan_file (char *filename) |
f2cc4248 | 226 | { |
728a982d DN |
227 | |
228 | size_t len = strlen (filename); | |
d5d66b7e | 229 | |
00b3c7ac TT |
230 | if (!generate_globals) |
231 | put_filename (filename); | |
dc61cb9d | 232 | if (len > 4 && !strcmp (filename + len - 4, ".elc")) |
e3938952 | 233 | return scan_lisp_file (filename, READ_BINARY); |
dc61cb9d | 234 | else if (len > 3 && !strcmp (filename + len - 3, ".el")) |
e3938952 | 235 | return scan_lisp_file (filename, READ_TEXT); |
f2cc4248 | 236 | else |
e3938952 | 237 | return scan_c_file (filename, READ_TEXT); |
f2cc4248 | 238 | } |
00b3c7ac | 239 | |
b23b5a5b | 240 | static void |
00b3c7ac TT |
241 | start_globals (void) |
242 | { | |
243 | fprintf (outfile, "/* This file was auto-generated by make-docfile. */\n"); | |
244 | fprintf (outfile, "/* DO NOT EDIT. */\n"); | |
245 | fprintf (outfile, "struct emacs_globals {\n"); | |
246 | } | |
f2cc4248 | 247 | \f |
4df52042 | 248 | static char input_buffer[128]; |
f2cc4248 | 249 | |
52d8c529 MB |
250 | /* Some state during the execution of `read_c_string_or_comment'. */ |
251 | struct rcsoc_state | |
252 | { | |
fb2d3129 | 253 | /* A count of spaces and newlines that have been read, but not output. */ |
52d8c529 MB |
254 | unsigned pending_spaces, pending_newlines; |
255 | ||
256 | /* Where we're reading from. */ | |
257 | FILE *in_file; | |
258 | ||
259 | /* If non-zero, a buffer into which to copy characters. */ | |
260 | char *buf_ptr; | |
261 | /* If non-zero, a file into which to copy characters. */ | |
262 | FILE *out_file; | |
263 | ||
264 | /* A keyword we look for at the beginning of lines. If found, it is | |
265 | not copied, and SAW_KEYWORD is set to true. */ | |
988e88ab | 266 | const char *keyword; |
33972e80 | 267 | /* The current point we've reached in an occurrence of KEYWORD in |
52d8c529 | 268 | the input stream. */ |
988e88ab | 269 | const char *cur_keyword_ptr; |
33972e80 | 270 | /* Set to true if we saw an occurrence of KEYWORD. */ |
52d8c529 MB |
271 | int saw_keyword; |
272 | }; | |
273 | ||
274 | /* Output CH to the file or buffer in STATE. Any pending newlines or | |
275 | spaces are output first. */ | |
0c82822c | 276 | |
55d4c1b2 | 277 | static inline void |
873fbd0b | 278 | put_char (int ch, struct rcsoc_state *state) |
0c82822c MB |
279 | { |
280 | int out_ch; | |
281 | do | |
282 | { | |
52d8c529 | 283 | if (state->pending_newlines > 0) |
0c82822c | 284 | { |
52d8c529 | 285 | state->pending_newlines--; |
0c82822c MB |
286 | out_ch = '\n'; |
287 | } | |
52d8c529 | 288 | else if (state->pending_spaces > 0) |
0c82822c | 289 | { |
52d8c529 | 290 | state->pending_spaces--; |
0c82822c MB |
291 | out_ch = ' '; |
292 | } | |
293 | else | |
294 | out_ch = ch; | |
295 | ||
52d8c529 MB |
296 | if (state->out_file) |
297 | putc (out_ch, state->out_file); | |
298 | if (state->buf_ptr) | |
299 | *state->buf_ptr++ = out_ch; | |
0c82822c MB |
300 | } |
301 | while (out_ch != ch); | |
302 | } | |
303 | ||
52d8c529 MB |
304 | /* If in the middle of scanning a keyword, continue scanning with |
305 | character CH, otherwise output CH to the file or buffer in STATE. | |
306 | Any pending newlines or spaces are output first, as well as any | |
307 | previously scanned characters that were thought to be part of a | |
308 | keyword, but were in fact not. */ | |
309 | ||
310 | static void | |
873fbd0b | 311 | scan_keyword_or_put_char (int ch, struct rcsoc_state *state) |
52d8c529 MB |
312 | { |
313 | if (state->keyword | |
314 | && *state->cur_keyword_ptr == ch | |
315 | && (state->cur_keyword_ptr > state->keyword | |
316 | || state->pending_newlines > 0)) | |
317 | /* We might be looking at STATE->keyword at some point. | |
318 | Keep looking until we know for sure. */ | |
319 | { | |
320 | if (*++state->cur_keyword_ptr == '\0') | |
321 | /* Saw the whole keyword. Set SAW_KEYWORD flag to true. */ | |
322 | { | |
323 | state->saw_keyword = 1; | |
324 | ||
325 | /* Reset the scanning pointer. */ | |
326 | state->cur_keyword_ptr = state->keyword; | |
327 | ||
fb2d3129 | 328 | /* Canonicalize whitespace preceding a usage string. */ |
52d8c529 MB |
329 | state->pending_newlines = 2; |
330 | state->pending_spaces = 0; | |
331 | ||
332 | /* Skip any whitespace between the keyword and the | |
333 | usage string. */ | |
334 | do | |
335 | ch = getc (state->in_file); | |
336 | while (ch == ' ' || ch == '\n'); | |
337 | ||
56cf5162 SM |
338 | /* Output the open-paren we just read. */ |
339 | put_char (ch, state); | |
340 | ||
341 | /* Skip the function name and replace it with `fn'. */ | |
342 | do | |
343 | ch = getc (state->in_file); | |
344 | while (ch != ' ' && ch != ')'); | |
345 | put_char ('f', state); | |
346 | put_char ('n', state); | |
177c0ea7 | 347 | |
56cf5162 | 348 | /* Put back the last character. */ |
52d8c529 MB |
349 | ungetc (ch, state->in_file); |
350 | } | |
351 | } | |
352 | else | |
353 | { | |
354 | if (state->keyword && state->cur_keyword_ptr > state->keyword) | |
355 | /* We scanned the beginning of a potential usage | |
356 | keyword, but it was a false alarm. Output the | |
357 | part we scanned. */ | |
358 | { | |
988e88ab | 359 | const char *p; |
52d8c529 MB |
360 | |
361 | for (p = state->keyword; p < state->cur_keyword_ptr; p++) | |
362 | put_char (*p, state); | |
363 | ||
364 | state->cur_keyword_ptr = state->keyword; | |
365 | } | |
366 | ||
367 | put_char (ch, state); | |
368 | } | |
369 | } | |
370 | ||
371 | ||
74c55c82 GM |
372 | /* Skip a C string or C-style comment from INFILE, and return the |
373 | character that follows. COMMENT non-zero means skip a comment. If | |
374 | PRINTFLAG is positive, output string contents to outfile. If it is | |
375 | negative, store contents in buf. Convert escape sequences \n and | |
d097ad57 | 376 | \t to newline and tab; discard \ followed by newline. |
33972e80 | 377 | If SAW_USAGE is non-zero, then any occurrences of the string `usage:' |
d097ad57 MB |
378 | at the beginning of a line will be removed, and *SAW_USAGE set to |
379 | true if any were encountered. */ | |
f2cc4248 | 380 | |
b23b5a5b | 381 | static int |
873fbd0b | 382 | read_c_string_or_comment (FILE *infile, int printflag, int comment, int *saw_usage) |
f2cc4248 RS |
383 | { |
384 | register int c; | |
52d8c529 MB |
385 | struct rcsoc_state state; |
386 | ||
387 | state.in_file = infile; | |
4df52042 | 388 | state.buf_ptr = (printflag < 0 ? input_buffer : 0); |
52d8c529 MB |
389 | state.out_file = (printflag > 0 ? outfile : 0); |
390 | state.pending_spaces = 0; | |
391 | state.pending_newlines = 0; | |
392 | state.keyword = (saw_usage ? "usage:" : 0); | |
393 | state.cur_keyword_ptr = state.keyword; | |
394 | state.saw_keyword = 0; | |
395 | ||
396 | c = getc (infile); | |
74c55c82 | 397 | if (comment) |
52d8c529 MB |
398 | while (c == '\n' || c == '\r' || c == '\t' || c == ' ') |
399 | c = getc (infile); | |
d097ad57 | 400 | |
f2cc4248 RS |
401 | while (c != EOF) |
402 | { | |
74c55c82 | 403 | while (c != EOF && (comment ? c != '*' : c != '"')) |
f2cc4248 RS |
404 | { |
405 | if (c == '\\') | |
406 | { | |
407 | c = getc (infile); | |
433d333d | 408 | if (c == '\n' || c == '\r') |
f2cc4248 RS |
409 | { |
410 | c = getc (infile); | |
411 | continue; | |
412 | } | |
413 | if (c == 'n') | |
414 | c = '\n'; | |
415 | if (c == 't') | |
416 | c = '\t'; | |
417 | } | |
a00e9335 | 418 | |
0c82822c | 419 | if (c == ' ') |
52d8c529 | 420 | state.pending_spaces++; |
0c82822c MB |
421 | else if (c == '\n') |
422 | { | |
52d8c529 MB |
423 | state.pending_newlines++; |
424 | state.pending_spaces = 0; | |
0c82822c MB |
425 | } |
426 | else | |
52d8c529 | 427 | scan_keyword_or_put_char (c, &state); |
0c82822c | 428 | |
f2cc4248 RS |
429 | c = getc (infile); |
430 | } | |
74c55c82 | 431 | |
7dfd439c GM |
432 | if (c != EOF) |
433 | c = getc (infile); | |
f2cc4248 | 434 | |
74c55c82 GM |
435 | if (comment) |
436 | { | |
437 | if (c == '/') | |
438 | { | |
439 | c = getc (infile); | |
440 | break; | |
441 | } | |
a00e9335 | 442 | |
52d8c529 | 443 | scan_keyword_or_put_char ('*', &state); |
74c55c82 GM |
444 | } |
445 | else | |
446 | { | |
447 | if (c != '"') | |
448 | break; | |
a00e9335 | 449 | |
74c55c82 GM |
450 | /* If we had a "", concatenate the two strings. */ |
451 | c = getc (infile); | |
452 | } | |
453 | } | |
a00e9335 | 454 | |
f2cc4248 | 455 | if (printflag < 0) |
52d8c529 MB |
456 | *state.buf_ptr = 0; |
457 | ||
458 | if (saw_usage) | |
459 | *saw_usage = state.saw_keyword; | |
f2cc4248 RS |
460 | |
461 | return c; | |
462 | } | |
74c55c82 GM |
463 | |
464 | ||
f2cc4248 | 465 | \f |
069ad9ea | 466 | /* Write to file OUT the argument names of function FUNC, whose text is in BUF. |
f2cc4248 RS |
467 | MINARGS and MAXARGS are the minimum and maximum number of arguments. */ |
468 | ||
b23b5a5b | 469 | static void |
873fbd0b | 470 | write_c_args (FILE *out, char *func, char *buf, int minargs, int maxargs) |
f2cc4248 | 471 | { |
f125a9e8 | 472 | register char *p; |
30e4c427 | 473 | int in_ident = 0; |
8e48d7bc | 474 | char *ident_start IF_LINT (= NULL); |
728a982d | 475 | size_t ident_length = 0; |
f2cc4248 | 476 | |
56cf5162 | 477 | fprintf (out, "(fn"); |
069ad9ea RM |
478 | |
479 | if (*buf == '(') | |
480 | ++buf; | |
f2cc4248 | 481 | |
f125a9e8 | 482 | for (p = buf; *p; p++) |
f2cc4248 | 483 | { |
91a7f76d | 484 | char c = *p; |
30e4c427 | 485 | |
91a7f76d | 486 | /* Notice when a new identifier starts. */ |
30e4c427 JB |
487 | if ((('A' <= c && c <= 'Z') |
488 | || ('a' <= c && c <= 'z') | |
489 | || ('0' <= c && c <= '9') | |
490 | || c == '_') | |
491 | != in_ident) | |
f2cc4248 | 492 | { |
30e4c427 JB |
493 | if (!in_ident) |
494 | { | |
495 | in_ident = 1; | |
91a7f76d | 496 | ident_start = p; |
30e4c427 JB |
497 | } |
498 | else | |
91a7f76d AS |
499 | { |
500 | in_ident = 0; | |
501 | ident_length = p - ident_start; | |
502 | } | |
f2cc4248 | 503 | } |
30e4c427 | 504 | |
91a7f76d AS |
505 | /* Found the end of an argument, write out the last seen |
506 | identifier. */ | |
507 | if (c == ',' || c == ')') | |
3941a179 | 508 | { |
a94a477d JB |
509 | if (ident_length == 0) |
510 | { | |
511 | error ("empty arg list for `%s' should be (void), not ()", func); | |
512 | continue; | |
513 | } | |
514 | ||
91a7f76d AS |
515 | if (strncmp (ident_start, "void", ident_length) == 0) |
516 | continue; | |
517 | ||
518 | putc (' ', out); | |
519 | ||
520 | if (minargs == 0 && maxargs > 0) | |
521 | fprintf (out, "&optional "); | |
30e4c427 | 522 | |
91a7f76d AS |
523 | minargs--; |
524 | maxargs--; | |
525 | ||
526 | /* In C code, `default' is a reserved word, so we spell it | |
22bcf204 | 527 | `defalt'; demangle that here. */ |
e99a530f | 528 | if (ident_length == 6 && memcmp (ident_start, "defalt", 6) == 0) |
91a7f76d AS |
529 | fprintf (out, "DEFAULT"); |
530 | else | |
531 | while (ident_length-- > 0) | |
532 | { | |
533 | c = *ident_start++; | |
534 | if (c >= 'a' && c <= 'z') | |
535 | /* Upcase the letter. */ | |
536 | c += 'A' - 'a'; | |
537 | else if (c == '_') | |
538 | /* Print underscore as hyphen. */ | |
539 | c = '-'; | |
540 | putc (c, out); | |
541 | } | |
542 | } | |
f2cc4248 | 543 | } |
91a7f76d AS |
544 | |
545 | putc (')', out); | |
f2cc4248 RS |
546 | } |
547 | \f | |
f5d9e83a PE |
548 | /* The types of globals. These are sorted roughly in decreasing alignment |
549 | order to avoid allocation gaps, except that functions are last. */ | |
00b3c7ac TT |
550 | enum global_type |
551 | { | |
f5d9e83a PE |
552 | INVALID, |
553 | LISP_OBJECT, | |
64df8c10 | 554 | EMACS_INTEGER, |
00b3c7ac | 555 | BOOLEAN, |
f5d9e83a | 556 | FUNCTION, |
00b3c7ac TT |
557 | }; |
558 | ||
559 | /* A single global. */ | |
560 | struct global | |
561 | { | |
562 | enum global_type type; | |
563 | char *name; | |
404dbd37 | 564 | int value; |
00b3c7ac TT |
565 | }; |
566 | ||
567 | /* All the variable names we saw while scanning C sources in `-g' | |
568 | mode. */ | |
569 | int num_globals; | |
570 | int num_globals_allocated; | |
571 | struct global *globals; | |
572 | ||
573 | static void | |
404dbd37 | 574 | add_global (enum global_type type, char *name, int value) |
00b3c7ac TT |
575 | { |
576 | /* Ignore the one non-symbol that can occur. */ | |
577 | if (strcmp (name, "...")) | |
578 | { | |
579 | ++num_globals; | |
580 | ||
581 | if (num_globals_allocated == 0) | |
582 | { | |
583 | num_globals_allocated = 100; | |
584 | globals = xmalloc (num_globals_allocated * sizeof (struct global)); | |
585 | } | |
586 | else if (num_globals == num_globals_allocated) | |
587 | { | |
588 | num_globals_allocated *= 2; | |
589 | globals = xrealloc (globals, | |
590 | num_globals_allocated * sizeof (struct global)); | |
591 | } | |
592 | ||
593 | globals[num_globals - 1].type = type; | |
594 | globals[num_globals - 1].name = name; | |
404dbd37 | 595 | globals[num_globals - 1].value = value; |
00b3c7ac TT |
596 | } |
597 | } | |
598 | ||
599 | static int | |
600 | compare_globals (const void *a, const void *b) | |
601 | { | |
602 | const struct global *ga = a; | |
603 | const struct global *gb = b; | |
404dbd37 | 604 | |
f5d9e83a PE |
605 | if (ga->type != gb->type) |
606 | return ga->type - gb->type; | |
404dbd37 | 607 | |
00b3c7ac TT |
608 | return strcmp (ga->name, gb->name); |
609 | } | |
610 | ||
404dbd37 TT |
611 | static void |
612 | close_emacs_globals (void) | |
613 | { | |
614 | fprintf (outfile, "};\n"); | |
615 | fprintf (outfile, "extern struct emacs_globals globals;\n"); | |
616 | } | |
617 | ||
b23b5a5b | 618 | static void |
00b3c7ac TT |
619 | write_globals (void) |
620 | { | |
404dbd37 | 621 | int i, seen_defun = 0; |
00b3c7ac TT |
622 | qsort (globals, num_globals, sizeof (struct global), compare_globals); |
623 | for (i = 0; i < num_globals; ++i) | |
624 | { | |
564ff1f2 | 625 | char const *type; |
00b3c7ac TT |
626 | |
627 | switch (globals[i].type) | |
628 | { | |
64df8c10 | 629 | case EMACS_INTEGER: |
00b3c7ac TT |
630 | type = "EMACS_INT"; |
631 | break; | |
632 | case BOOLEAN: | |
f5d9e83a | 633 | type = "bool"; |
00b3c7ac TT |
634 | break; |
635 | case LISP_OBJECT: | |
636 | type = "Lisp_Object"; | |
637 | break; | |
404dbd37 TT |
638 | case FUNCTION: |
639 | if (!seen_defun) | |
640 | { | |
641 | close_emacs_globals (); | |
642 | fprintf (outfile, "\n"); | |
643 | seen_defun = 1; | |
644 | } | |
645 | break; | |
00b3c7ac TT |
646 | default: |
647 | fatal ("not a recognized DEFVAR_", 0); | |
648 | } | |
649 | ||
404dbd37 TT |
650 | if (globals[i].type != FUNCTION) |
651 | { | |
652 | fprintf (outfile, " %s f_%s;\n", type, globals[i].name); | |
653 | fprintf (outfile, "#define %s globals.f_%s\n", | |
654 | globals[i].name, globals[i].name); | |
655 | } | |
656 | else | |
657 | { | |
658 | /* It would be nice to have a cleaner way to deal with these | |
659 | special hacks. */ | |
660 | if (strcmp (globals[i].name, "Fthrow") == 0 | |
661 | || strcmp (globals[i].name, "Ftop_level") == 0 | |
662 | || strcmp (globals[i].name, "Fkill_emacs") == 0) | |
663 | fprintf (outfile, "_Noreturn "); | |
664 | fprintf (outfile, "EXFUN (%s, ", globals[i].name); | |
665 | if (globals[i].value == -1) | |
666 | fprintf (outfile, "MANY"); | |
667 | else if (globals[i].value == -2) | |
668 | fprintf (outfile, "UNEVALLED"); | |
669 | else | |
670 | fprintf (outfile, "%d", globals[i].value); | |
671 | fprintf (outfile, ");\n"); | |
672 | } | |
673 | ||
00b3c7ac TT |
674 | while (i + 1 < num_globals |
675 | && !strcmp (globals[i].name, globals[i + 1].name)) | |
9c32bf45 AS |
676 | { |
677 | if (globals[i].type == FUNCTION | |
678 | && globals[i].value != globals[i + 1].value) | |
679 | error ("function '%s' defined twice with differing signatures", | |
680 | globals[i].name); | |
681 | ++i; | |
682 | } | |
00b3c7ac TT |
683 | } |
684 | ||
404dbd37 TT |
685 | if (!seen_defun) |
686 | close_emacs_globals (); | |
00b3c7ac TT |
687 | } |
688 | ||
689 | \f | |
f2cc4248 | 690 | /* Read through a c file. If a .o file is named, |
edfda783 | 691 | the corresponding .c or .m file is read instead. |
f2cc4248 RS |
692 | Looks for DEFUN constructs such as are defined in ../src/lisp.h. |
693 | Accepts any word starting DEF... so it finds DEFSIMPLE and DEFPRED. */ | |
694 | ||
b23b5a5b | 695 | static int |
988e88ab | 696 | scan_c_file (char *filename, const char *mode) |
f2cc4248 RS |
697 | { |
698 | FILE *infile; | |
699 | register int c; | |
700 | register int commas; | |
f2cc4248 | 701 | int minargs, maxargs; |
1feb8ae1 | 702 | int extension = filename[strlen (filename) - 1]; |
f2cc4248 | 703 | |
1feb8ae1 | 704 | if (extension == 'o') |
f2cc4248 RS |
705 | filename[strlen (filename) - 1] = 'c'; |
706 | ||
e3938952 | 707 | infile = fopen (filename, mode); |
f2cc4248 | 708 | |
edfda783 AR |
709 | if (infile == NULL && extension == 'o') |
710 | { | |
61b108cc | 711 | /* Try .m. */ |
edfda783 AR |
712 | filename[strlen (filename) - 1] = 'm'; |
713 | infile = fopen (filename, mode); | |
714 | if (infile == NULL) | |
61b108cc | 715 | filename[strlen (filename) - 1] = 'c'; /* Don't confuse people. */ |
edfda783 AR |
716 | } |
717 | ||
61b108cc | 718 | /* No error if non-ex input file. */ |
f2cc4248 RS |
719 | if (infile == NULL) |
720 | { | |
721 | perror (filename); | |
722 | return 0; | |
723 | } | |
724 | ||
fb2d3129 | 725 | /* Reset extension to be able to detect duplicate files. */ |
1feb8ae1 RS |
726 | filename[strlen (filename) - 1] = extension; |
727 | ||
f2cc4248 RS |
728 | c = '\n'; |
729 | while (!feof (infile)) | |
730 | { | |
a5979c0e | 731 | int doc_keyword = 0; |
8dfa32f6 PE |
732 | int defunflag = 0; |
733 | int defvarperbufferflag = 0; | |
734 | int defvarflag = 0; | |
735 | enum global_type type = INVALID; | |
9c46aab9 | 736 | char *name IF_LINT (= 0); |
a5979c0e | 737 | |
433d333d | 738 | if (c != '\n' && c != '\r') |
f2cc4248 RS |
739 | { |
740 | c = getc (infile); | |
741 | continue; | |
742 | } | |
743 | c = getc (infile); | |
744 | if (c == ' ') | |
745 | { | |
746 | while (c == ' ') | |
747 | c = getc (infile); | |
748 | if (c != 'D') | |
749 | continue; | |
750 | c = getc (infile); | |
751 | if (c != 'E') | |
752 | continue; | |
753 | c = getc (infile); | |
754 | if (c != 'F') | |
755 | continue; | |
756 | c = getc (infile); | |
757 | if (c != 'V') | |
758 | continue; | |
84128dee JB |
759 | c = getc (infile); |
760 | if (c != 'A') | |
761 | continue; | |
762 | c = getc (infile); | |
763 | if (c != 'R') | |
764 | continue; | |
765 | c = getc (infile); | |
766 | if (c != '_') | |
767 | continue; | |
768 | ||
f2cc4248 | 769 | defvarflag = 1; |
84128dee JB |
770 | |
771 | c = getc (infile); | |
772 | defvarperbufferflag = (c == 'P'); | |
00b3c7ac TT |
773 | if (generate_globals) |
774 | { | |
775 | if (c == 'I') | |
64df8c10 | 776 | type = EMACS_INTEGER; |
00b3c7ac TT |
777 | else if (c == 'L') |
778 | type = LISP_OBJECT; | |
779 | else if (c == 'B') | |
780 | type = BOOLEAN; | |
00b3c7ac | 781 | } |
84128dee | 782 | |
f2cc4248 | 783 | c = getc (infile); |
00b3c7ac TT |
784 | /* We need to distinguish between DEFVAR_BOOL and |
785 | DEFVAR_BUFFER_DEFAULTS. */ | |
786 | if (generate_globals && type == BOOLEAN && c != 'O') | |
787 | type = INVALID; | |
f2cc4248 RS |
788 | } |
789 | else if (c == 'D') | |
790 | { | |
791 | c = getc (infile); | |
792 | if (c != 'E') | |
793 | continue; | |
794 | c = getc (infile); | |
795 | if (c != 'F') | |
796 | continue; | |
797 | c = getc (infile); | |
798 | defunflag = c == 'U'; | |
f2cc4248 RS |
799 | } |
800 | else continue; | |
801 | ||
404dbd37 TT |
802 | if (generate_globals |
803 | && (!defvarflag || defvarperbufferflag || type == INVALID) | |
804 | && !defunflag) | |
00b3c7ac TT |
805 | continue; |
806 | ||
f2cc4248 RS |
807 | while (c != '(') |
808 | { | |
809 | if (c < 0) | |
810 | goto eof; | |
811 | c = getc (infile); | |
812 | } | |
813 | ||
74c55c82 | 814 | /* Lisp variable or function name. */ |
f2cc4248 RS |
815 | c = getc (infile); |
816 | if (c != '"') | |
817 | continue; | |
d097ad57 | 818 | c = read_c_string_or_comment (infile, -1, 0, 0); |
74c55c82 | 819 | |
00b3c7ac TT |
820 | if (generate_globals) |
821 | { | |
822 | int i = 0; | |
00b3c7ac TT |
823 | |
824 | /* Skip "," and whitespace. */ | |
825 | do | |
826 | { | |
827 | c = getc (infile); | |
828 | } | |
829 | while (c == ',' || c == ' ' || c == '\t' || c == '\n' || c == '\r'); | |
830 | ||
831 | /* Read in the identifier. */ | |
832 | do | |
833 | { | |
4df52042 | 834 | input_buffer[i++] = c; |
00b3c7ac TT |
835 | c = getc (infile); |
836 | } | |
61b108cc SM |
837 | while (! (c == ',' || c == ' ' || c == '\t' |
838 | || c == '\n' || c == '\r')); | |
4df52042 | 839 | input_buffer[i] = '\0'; |
00b3c7ac TT |
840 | |
841 | name = xmalloc (i + 1); | |
4df52042 | 842 | memcpy (name, input_buffer, i + 1); |
404dbd37 TT |
843 | |
844 | if (!defunflag) | |
845 | { | |
846 | add_global (type, name, 0); | |
847 | continue; | |
848 | } | |
00b3c7ac TT |
849 | } |
850 | ||
a5979c0e MB |
851 | /* DEFVAR_LISP ("name", addr, "doc") |
852 | DEFVAR_LISP ("name", addr /\* doc *\/) | |
853 | DEFVAR_LISP ("name", addr, doc: /\* doc *\/) */ | |
f2cc4248 RS |
854 | |
855 | if (defunflag) | |
404dbd37 | 856 | commas = generate_globals ? 4 : 5; |
84128dee | 857 | else if (defvarperbufferflag) |
6ca1c3b4 | 858 | commas = 3; |
f2cc4248 RS |
859 | else if (defvarflag) |
860 | commas = 1; | |
61b108cc | 861 | else /* For DEFSIMPLE and DEFPRED. */ |
f2cc4248 RS |
862 | commas = 2; |
863 | ||
864 | while (commas) | |
865 | { | |
866 | if (c == ',') | |
867 | { | |
868 | commas--; | |
74c55c82 | 869 | |
f2cc4248 RS |
870 | if (defunflag && (commas == 1 || commas == 2)) |
871 | { | |
9055082e | 872 | int scanned = 0; |
f2cc4248 RS |
873 | do |
874 | c = getc (infile); | |
433d333d | 875 | while (c == ' ' || c == '\n' || c == '\r' || c == '\t'); |
f2cc4248 RS |
876 | if (c < 0) |
877 | goto eof; | |
878 | ungetc (c, infile); | |
61b108cc | 879 | if (commas == 2) /* Pick up minargs. */ |
9055082e | 880 | scanned = fscanf (infile, "%d", &minargs); |
61b108cc | 881 | else /* Pick up maxargs. */ |
f2cc4248 | 882 | if (c == 'M' || c == 'U') /* MANY || UNEVALLED */ |
404dbd37 TT |
883 | { |
884 | if (generate_globals) | |
885 | maxargs = (c == 'M') ? -1 : -2; | |
886 | else | |
887 | maxargs = -1; | |
888 | } | |
f2cc4248 | 889 | else |
9055082e PE |
890 | scanned = fscanf (infile, "%d", &maxargs); |
891 | if (scanned < 0) | |
892 | goto eof; | |
f2cc4248 RS |
893 | } |
894 | } | |
74c55c82 GM |
895 | |
896 | if (c == EOF) | |
f2cc4248 RS |
897 | goto eof; |
898 | c = getc (infile); | |
899 | } | |
a5979c0e | 900 | |
404dbd37 TT |
901 | if (generate_globals) |
902 | { | |
903 | add_global (FUNCTION, name, maxargs); | |
904 | continue; | |
905 | } | |
906 | ||
433d333d | 907 | while (c == ' ' || c == '\n' || c == '\r' || c == '\t') |
f2cc4248 | 908 | c = getc (infile); |
a00e9335 | 909 | |
f2cc4248 | 910 | if (c == '"') |
d097ad57 | 911 | c = read_c_string_or_comment (infile, 0, 0, 0); |
a00e9335 | 912 | |
74c55c82 | 913 | while (c != EOF && c != ',' && c != '/') |
f2cc4248 | 914 | c = getc (infile); |
74c55c82 GM |
915 | if (c == ',') |
916 | { | |
a5979c0e MB |
917 | c = getc (infile); |
918 | while (c == ' ' || c == '\n' || c == '\r' || c == '\t') | |
919 | c = getc (infile); | |
920 | while ((c >= 'a' && c <= 'z') || (c >= 'Z' && c <= 'Z')) | |
921 | c = getc (infile); | |
922 | if (c == ':') | |
923 | { | |
924 | doc_keyword = 1; | |
925 | c = getc (infile); | |
926 | while (c == ' ' || c == '\n' || c == '\r' || c == '\t') | |
927 | c = getc (infile); | |
928 | } | |
74c55c82 | 929 | } |
f2cc4248 | 930 | |
74c55c82 GM |
931 | if (c == '"' |
932 | || (c == '/' | |
933 | && (c = getc (infile), | |
934 | ungetc (c, infile), | |
935 | c == '*'))) | |
f2cc4248 | 936 | { |
74c55c82 | 937 | int comment = c != '"'; |
d097ad57 | 938 | int saw_usage; |
a00e9335 | 939 | |
f2cc4248 RS |
940 | putc (037, outfile); |
941 | putc (defvarflag ? 'V' : 'F', outfile); | |
4df52042 | 942 | fprintf (outfile, "%s\n", input_buffer); |
74c55c82 GM |
943 | |
944 | if (comment) | |
61b108cc | 945 | getc (infile); /* Skip past `*'. */ |
d097ad57 | 946 | c = read_c_string_or_comment (infile, 1, comment, &saw_usage); |
772e2009 JB |
947 | |
948 | /* If this is a defun, find the arguments and print them. If | |
949 | this function takes MANY or UNEVALLED args, then the C source | |
950 | won't give the names of the arguments, so we shouldn't bother | |
74c55c82 GM |
951 | trying to find them. |
952 | ||
a5979c0e MB |
953 | Various doc-string styles: |
954 | 0: DEFUN (..., "DOC") (args) [!comment] | |
955 | 1: DEFUN (..., /\* DOC *\/ (args)) [comment && !doc_keyword] | |
956 | 2: DEFUN (..., doc: /\* DOC *\/) (args) [comment && doc_keyword] | |
957 | */ | |
d097ad57 | 958 | if (defunflag && maxargs != -1 && !saw_usage) |
f2cc4248 RS |
959 | { |
960 | char argbuf[1024], *p = argbuf; | |
74c55c82 | 961 | |
a5979c0e | 962 | if (!comment || doc_keyword) |
74c55c82 GM |
963 | while (c != ')') |
964 | { | |
965 | if (c < 0) | |
966 | goto eof; | |
967 | c = getc (infile); | |
968 | } | |
a00e9335 | 969 | |
f2cc4248 RS |
970 | /* Skip into arguments. */ |
971 | while (c != '(') | |
972 | { | |
973 | if (c < 0) | |
974 | goto eof; | |
975 | c = getc (infile); | |
976 | } | |
977 | /* Copy arguments into ARGBUF. */ | |
978 | *p++ = c; | |
979 | do | |
980 | *p++ = c = getc (infile); | |
981 | while (c != ')'); | |
982 | *p = '\0'; | |
983 | /* Output them. */ | |
984 | fprintf (outfile, "\n\n"); | |
4df52042 | 985 | write_c_args (outfile, input_buffer, argbuf, minargs, maxargs); |
f2cc4248 | 986 | } |
1e042160 SM |
987 | else if (defunflag && maxargs == -1 && !saw_usage) |
988 | /* The DOC should provide the usage form. */ | |
4df52042 PE |
989 | fprintf (stderr, "Missing `usage' for function `%s'.\n", |
990 | input_buffer); | |
f2cc4248 RS |
991 | } |
992 | } | |
993 | eof: | |
994 | fclose (infile); | |
995 | return 0; | |
996 | } | |
997 | \f | |
998 | /* Read a file of Lisp code, compiled or interpreted. | |
999 | Looks for | |
1000 | (defun NAME ARGS DOCSTRING ...) | |
23d6b5a6 | 1001 | (defmacro NAME ARGS DOCSTRING ...) |
34e778a6 | 1002 | (defsubst NAME ARGS DOCSTRING ...) |
23d6b5a6 | 1003 | (autoload (quote NAME) FILE DOCSTRING ...) |
f2cc4248 RS |
1004 | (defvar NAME VALUE DOCSTRING) |
1005 | (defconst NAME VALUE DOCSTRING) | |
23d6b5a6 JB |
1006 | (fset (quote NAME) (make-byte-code ... DOCSTRING ...)) |
1007 | (fset (quote NAME) #[... DOCSTRING ...]) | |
2d6e2619 | 1008 | (defalias (quote NAME) #[... DOCSTRING ...]) |
3fe77f98 | 1009 | (custom-declare-variable (quote NAME) VALUE DOCSTRING ...) |
f2cc4248 | 1010 | starting in column zero. |
23d6b5a6 | 1011 | (quote NAME) may appear as 'NAME as well. |
b5ff43cc RS |
1012 | |
1013 | We also look for #@LENGTH CONTENTS^_ at the beginning of the line. | |
1014 | When we find that, we save it for the following defining-form, | |
1015 | and we use that instead of reading a doc string within that defining-form. | |
1016 | ||
a00e9335 | 1017 | For defvar, defconst, and fset we skip to the docstring with a kludgy |
23d6b5a6 | 1018 | formatting convention: all docstrings must appear on the same line as the |
a00e9335 | 1019 | initial open-paren (the one in column zero) and must contain a backslash |
b0f08a24 | 1020 | and a newline immediately after the initial double-quote. No newlines |
23d6b5a6 | 1021 | must appear between the beginning of the form and the first double-quote. |
b0f08a24 DL |
1022 | For defun, defmacro, and autoload, we know how to skip over the |
1023 | arglist, but the doc string must still have a backslash and newline | |
a00e9335 | 1024 | immediately after the double quote. |
b0f08a24 DL |
1025 | The only source files that must follow this convention are preloaded |
1026 | uncompiled ones like loaddefs.el and bindings.el; aside | |
23d6b5a6 JB |
1027 | from that, it is always the .elc file that we look at, and they are no |
1028 | problem because byte-compiler output follows this convention. | |
f2cc4248 RS |
1029 | The NAME and DOCSTRING are output. |
1030 | NAME is preceded by `F' for a function or `V' for a variable. | |
61b108cc | 1031 | An entry is output only if DOCSTRING has \ newline just after the opening ". |
f2cc4248 RS |
1032 | */ |
1033 | ||
b23b5a5b | 1034 | static void |
873fbd0b | 1035 | skip_white (FILE *infile) |
23d6b5a6 JB |
1036 | { |
1037 | char c = ' '; | |
433d333d | 1038 | while (c == ' ' || c == '\t' || c == '\n' || c == '\r') |
23d6b5a6 JB |
1039 | c = getc (infile); |
1040 | ungetc (c, infile); | |
1041 | } | |
1042 | ||
b23b5a5b | 1043 | static void |
873fbd0b | 1044 | read_lisp_symbol (FILE *infile, char *buffer) |
23d6b5a6 JB |
1045 | { |
1046 | char c; | |
1047 | char *fillp = buffer; | |
1048 | ||
1049 | skip_white (infile); | |
1050 | while (1) | |
1051 | { | |
1052 | c = getc (infile); | |
1053 | if (c == '\\') | |
1054 | *(++fillp) = getc (infile); | |
433d333d | 1055 | else if (c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '(' || c == ')') |
23d6b5a6 JB |
1056 | { |
1057 | ungetc (c, infile); | |
1058 | *fillp = 0; | |
1059 | break; | |
1060 | } | |
1061 | else | |
1062 | *fillp++ = c; | |
1063 | } | |
1064 | ||
1065 | if (! buffer[0]) | |
1066 | fprintf (stderr, "## expected a symbol, got '%c'\n", c); | |
a00e9335 | 1067 | |
23d6b5a6 JB |
1068 | skip_white (infile); |
1069 | } | |
1070 | ||
61b108cc SM |
1071 | static int |
1072 | search_lisp_doc_at_eol (FILE *infile) | |
1073 | { | |
1074 | char c = 0, c1 = 0, c2 = 0; | |
1075 | ||
1076 | /* Skip until the end of line; remember two previous chars. */ | |
7cb70fd7 | 1077 | while (c != '\n' && c != '\r' && c != EOF) |
61b108cc SM |
1078 | { |
1079 | c2 = c1; | |
1080 | c1 = c; | |
1081 | c = getc (infile); | |
1082 | } | |
1083 | ||
1084 | /* If two previous characters were " and \, | |
1085 | this is a doc string. Otherwise, there is none. */ | |
1086 | if (c2 != '"' || c1 != '\\') | |
1087 | { | |
1088 | #ifdef DEBUG | |
1089 | fprintf (stderr, "## non-docstring in %s (%s)\n", | |
1090 | buffer, filename); | |
1091 | #endif | |
7cb70fd7 AS |
1092 | if (c != EOF) |
1093 | ungetc (c, infile); | |
61b108cc SM |
1094 | return 0; |
1095 | } | |
1096 | return 1; | |
1097 | } | |
1098 | ||
b23b5a5b | 1099 | static int |
988e88ab | 1100 | scan_lisp_file (const char *filename, const char *mode) |
f2cc4248 RS |
1101 | { |
1102 | FILE *infile; | |
1103 | register int c; | |
b5ff43cc | 1104 | char *saved_string = 0; |
f2cc4248 | 1105 | |
00b3c7ac TT |
1106 | if (generate_globals) |
1107 | fatal ("scanning lisp file when -g specified", 0); | |
1108 | ||
e3938952 | 1109 | infile = fopen (filename, mode); |
f2cc4248 RS |
1110 | if (infile == NULL) |
1111 | { | |
1112 | perror (filename); | |
61b108cc | 1113 | return 0; /* No error. */ |
f2cc4248 RS |
1114 | } |
1115 | ||
1116 | c = '\n'; | |
1117 | while (!feof (infile)) | |
1118 | { | |
b5ff43cc | 1119 | char buffer[BUFSIZ]; |
23d6b5a6 JB |
1120 | char type; |
1121 | ||
66f54605 | 1122 | /* If not at end of line, skip till we get to one. */ |
433d333d | 1123 | if (c != '\n' && c != '\r') |
f2cc4248 RS |
1124 | { |
1125 | c = getc (infile); | |
1126 | continue; | |
1127 | } | |
66f54605 | 1128 | /* Skip the line break. */ |
7e6972e9 | 1129 | while (c == '\n' || c == '\r') |
66f54605 | 1130 | c = getc (infile); |
b5ff43cc RS |
1131 | /* Detect a dynamic doc string and save it for the next expression. */ |
1132 | if (c == '#') | |
1133 | { | |
1134 | c = getc (infile); | |
1135 | if (c == '@') | |
1136 | { | |
8aec9916 JM |
1137 | size_t length = 0; |
1138 | size_t i; | |
b5ff43cc RS |
1139 | |
1140 | /* Read the length. */ | |
1141 | while ((c = getc (infile), | |
1142 | c >= '0' && c <= '9')) | |
1143 | { | |
1144 | length *= 10; | |
1145 | length += c - '0'; | |
1146 | } | |
1147 | ||
8aec9916 JM |
1148 | if (length <= 1) |
1149 | fatal ("invalid dynamic doc string length", ""); | |
1150 | ||
1151 | if (c != ' ') | |
1152 | fatal ("space not found after dynamic doc string length", ""); | |
1153 | ||
b5ff43cc RS |
1154 | /* The next character is a space that is counted in the length |
1155 | but not part of the doc string. | |
1156 | We already read it, so just ignore it. */ | |
1157 | length--; | |
1158 | ||
1159 | /* Read in the contents. */ | |
e0f59195 | 1160 | free (saved_string); |
938ebc4f | 1161 | saved_string = (char *) xmalloc (length); |
b5ff43cc RS |
1162 | for (i = 0; i < length; i++) |
1163 | saved_string[i] = getc (infile); | |
1164 | /* The last character is a ^_. | |
1165 | That is needed in the .elc file | |
1166 | but it is redundant in DOC. So get rid of it here. */ | |
1167 | saved_string[length - 1] = 0; | |
66f54605 | 1168 | /* Skip the line break. */ |
8aec9916 | 1169 | while (c == '\n' || c == '\r') |
66f54605 PR |
1170 | c = getc (infile); |
1171 | /* Skip the following line. */ | |
433d333d | 1172 | while (c != '\n' && c != '\r') |
b5ff43cc RS |
1173 | c = getc (infile); |
1174 | } | |
1175 | continue; | |
1176 | } | |
1177 | ||
f2cc4248 RS |
1178 | if (c != '(') |
1179 | continue; | |
a8a7afbe | 1180 | |
23d6b5a6 JB |
1181 | read_lisp_symbol (infile, buffer); |
1182 | ||
66f54605 | 1183 | if (! strcmp (buffer, "defun") |
34e778a6 AS |
1184 | || ! strcmp (buffer, "defmacro") |
1185 | || ! strcmp (buffer, "defsubst")) | |
f2cc4248 | 1186 | { |
23d6b5a6 JB |
1187 | type = 'F'; |
1188 | read_lisp_symbol (infile, buffer); | |
f2cc4248 | 1189 | |
61b108cc | 1190 | /* Skip the arguments: either "nil" or a list in parens. */ |
f2cc4248 | 1191 | |
23d6b5a6 JB |
1192 | c = getc (infile); |
1193 | if (c == 'n') /* nil */ | |
f2cc4248 | 1194 | { |
66f54605 PR |
1195 | if ((c = getc (infile)) != 'i' |
1196 | || (c = getc (infile)) != 'l') | |
23d6b5a6 JB |
1197 | { |
1198 | fprintf (stderr, "## unparsable arglist in %s (%s)\n", | |
1199 | buffer, filename); | |
1200 | continue; | |
1201 | } | |
f2cc4248 | 1202 | } |
23d6b5a6 | 1203 | else if (c != '(') |
f2cc4248 | 1204 | { |
23d6b5a6 JB |
1205 | fprintf (stderr, "## unparsable arglist in %s (%s)\n", |
1206 | buffer, filename); | |
1207 | continue; | |
f2cc4248 | 1208 | } |
23d6b5a6 JB |
1209 | else |
1210 | while (c != ')') | |
f2cc4248 | 1211 | c = getc (infile); |
23d6b5a6 JB |
1212 | skip_white (infile); |
1213 | ||
1214 | /* If the next three characters aren't `dquote bslash newline' | |
1215 | then we're not reading a docstring. | |
1216 | */ | |
66f54605 PR |
1217 | if ((c = getc (infile)) != '"' |
1218 | || (c = getc (infile)) != '\\' | |
1219 | || ((c = getc (infile)) != '\n' && c != '\r')) | |
f2cc4248 | 1220 | { |
23d6b5a6 JB |
1221 | #ifdef DEBUG |
1222 | fprintf (stderr, "## non-docstring in %s (%s)\n", | |
1223 | buffer, filename); | |
1224 | #endif | |
1225 | continue; | |
f2cc4248 | 1226 | } |
f2cc4248 | 1227 | } |
a8a7afbe | 1228 | |
420b63ad | 1229 | /* defcustom can only occur in uncompiled Lisp files. */ |
66f54605 | 1230 | else if (! strcmp (buffer, "defvar") |
420b63ad GM |
1231 | || ! strcmp (buffer, "defconst") |
1232 | || ! strcmp (buffer, "defcustom")) | |
f2cc4248 | 1233 | { |
23d6b5a6 JB |
1234 | type = 'V'; |
1235 | read_lisp_symbol (infile, buffer); | |
a8a7afbe | 1236 | |
b5ff43cc | 1237 | if (saved_string == 0) |
61b108cc SM |
1238 | if (!search_lisp_doc_at_eol (infile)) |
1239 | continue; | |
23d6b5a6 JB |
1240 | } |
1241 | ||
f6195dfb DN |
1242 | else if (! strcmp (buffer, "custom-declare-variable") |
1243 | || ! strcmp (buffer, "defvaralias") | |
1244 | ) | |
3fe77f98 | 1245 | { |
3fe77f98 RS |
1246 | type = 'V'; |
1247 | ||
1248 | c = getc (infile); | |
1249 | if (c == '\'') | |
1250 | read_lisp_symbol (infile, buffer); | |
1251 | else | |
1252 | { | |
1253 | if (c != '(') | |
1254 | { | |
1255 | fprintf (stderr, | |
1256 | "## unparsable name in custom-declare-variable in %s\n", | |
1257 | filename); | |
1258 | continue; | |
1259 | } | |
1260 | read_lisp_symbol (infile, buffer); | |
1261 | if (strcmp (buffer, "quote")) | |
1262 | { | |
1263 | fprintf (stderr, | |
1264 | "## unparsable name in custom-declare-variable in %s\n", | |
1265 | filename); | |
1266 | continue; | |
1267 | } | |
1268 | read_lisp_symbol (infile, buffer); | |
1269 | c = getc (infile); | |
1270 | if (c != ')') | |
1271 | { | |
1272 | fprintf (stderr, | |
1273 | "## unparsable quoted name in custom-declare-variable in %s\n", | |
1274 | filename); | |
1275 | continue; | |
1276 | } | |
1277 | } | |
1278 | ||
1279 | if (saved_string == 0) | |
61b108cc SM |
1280 | if (!search_lisp_doc_at_eol (infile)) |
1281 | continue; | |
3fe77f98 RS |
1282 | } |
1283 | ||
2d6e2619 | 1284 | else if (! strcmp (buffer, "fset") || ! strcmp (buffer, "defalias")) |
23d6b5a6 | 1285 | { |
23d6b5a6 | 1286 | type = 'F'; |
a8a7afbe | 1287 | |
23d6b5a6 JB |
1288 | c = getc (infile); |
1289 | if (c == '\'') | |
1290 | read_lisp_symbol (infile, buffer); | |
1291 | else | |
f2cc4248 | 1292 | { |
23d6b5a6 JB |
1293 | if (c != '(') |
1294 | { | |
1295 | fprintf (stderr, "## unparsable name in fset in %s\n", | |
1296 | filename); | |
1297 | continue; | |
1298 | } | |
1299 | read_lisp_symbol (infile, buffer); | |
1300 | if (strcmp (buffer, "quote")) | |
1301 | { | |
1302 | fprintf (stderr, "## unparsable name in fset in %s\n", | |
1303 | filename); | |
1304 | continue; | |
1305 | } | |
1306 | read_lisp_symbol (infile, buffer); | |
f2cc4248 | 1307 | c = getc (infile); |
23d6b5a6 JB |
1308 | if (c != ')') |
1309 | { | |
1310 | fprintf (stderr, | |
1311 | "## unparsable quoted name in fset in %s\n", | |
1312 | filename); | |
1313 | continue; | |
1314 | } | |
f2cc4248 | 1315 | } |
f2cc4248 | 1316 | |
b5ff43cc | 1317 | if (saved_string == 0) |
61b108cc SM |
1318 | if (!search_lisp_doc_at_eol (infile)) |
1319 | continue; | |
23d6b5a6 | 1320 | } |
f2cc4248 | 1321 | |
23d6b5a6 JB |
1322 | else if (! strcmp (buffer, "autoload")) |
1323 | { | |
1324 | type = 'F'; | |
1325 | c = getc (infile); | |
1326 | if (c == '\'') | |
1327 | read_lisp_symbol (infile, buffer); | |
1328 | else | |
f2cc4248 | 1329 | { |
23d6b5a6 | 1330 | if (c != '(') |
f2cc4248 | 1331 | { |
23d6b5a6 JB |
1332 | fprintf (stderr, "## unparsable name in autoload in %s\n", |
1333 | filename); | |
1334 | continue; | |
f2cc4248 | 1335 | } |
23d6b5a6 JB |
1336 | read_lisp_symbol (infile, buffer); |
1337 | if (strcmp (buffer, "quote")) | |
f2cc4248 | 1338 | { |
23d6b5a6 JB |
1339 | fprintf (stderr, "## unparsable name in autoload in %s\n", |
1340 | filename); | |
1341 | continue; | |
f2cc4248 | 1342 | } |
23d6b5a6 | 1343 | read_lisp_symbol (infile, buffer); |
f2cc4248 | 1344 | c = getc (infile); |
23d6b5a6 | 1345 | if (c != ')') |
f2cc4248 | 1346 | { |
23d6b5a6 JB |
1347 | fprintf (stderr, |
1348 | "## unparsable quoted name in autoload in %s\n", | |
1349 | filename); | |
1350 | continue; | |
f2cc4248 | 1351 | } |
23d6b5a6 JB |
1352 | } |
1353 | skip_white (infile); | |
1354 | if ((c = getc (infile)) != '\"') | |
1355 | { | |
1356 | fprintf (stderr, "## autoload of %s unparsable (%s)\n", | |
1357 | buffer, filename); | |
f2cc4248 RS |
1358 | continue; |
1359 | } | |
d097ad57 | 1360 | read_c_string_or_comment (infile, 0, 0, 0); |
23d6b5a6 | 1361 | |
b5ff43cc | 1362 | if (saved_string == 0) |
61b108cc SM |
1363 | if (!search_lisp_doc_at_eol (infile)) |
1364 | continue; | |
a8a7afbe | 1365 | } |
f2cc4248 | 1366 | |
23d6b5a6 | 1367 | #ifdef DEBUG |
66f54605 PR |
1368 | else if (! strcmp (buffer, "if") |
1369 | || ! strcmp (buffer, "byte-code")) | |
a706a3bc | 1370 | continue; |
23d6b5a6 | 1371 | #endif |
f2cc4248 | 1372 | |
23d6b5a6 JB |
1373 | else |
1374 | { | |
1375 | #ifdef DEBUG | |
bcfce2c7 | 1376 | fprintf (stderr, "## unrecognized top-level form, %s (%s)\n", |
23d6b5a6 JB |
1377 | buffer, filename); |
1378 | #endif | |
1379 | continue; | |
1380 | } | |
f2cc4248 | 1381 | |
61b108cc SM |
1382 | /* At this point, we should either use the previous dynamic doc string in |
1383 | saved_string or gobble a doc string from the input file. | |
1384 | In the latter case, the opening quote (and leading backslash-newline) | |
1385 | have already been read. */ | |
b5ff43cc | 1386 | |
f2cc4248 | 1387 | putc (037, outfile); |
23d6b5a6 JB |
1388 | putc (type, outfile); |
1389 | fprintf (outfile, "%s\n", buffer); | |
b5ff43cc RS |
1390 | if (saved_string) |
1391 | { | |
1392 | fputs (saved_string, outfile); | |
1393 | /* Don't use one dynamic doc string twice. */ | |
1394 | free (saved_string); | |
1395 | saved_string = 0; | |
1396 | } | |
1397 | else | |
d097ad57 | 1398 | read_c_string_or_comment (infile, 1, 0, 0); |
f2cc4248 RS |
1399 | } |
1400 | fclose (infile); | |
1401 | return 0; | |
1402 | } | |
ab5796a9 | 1403 | |
65396510 TTN |
1404 | |
1405 | /* make-docfile.c ends here */ |