Mercurial > hg > xemacs-beta
comparison src/doc.c @ 428:3ecd8885ac67 r21-2-22
Import from CVS: tag r21-2-22
author | cvs |
---|---|
date | Mon, 13 Aug 2007 11:28:15 +0200 |
parents | |
children | 8de8e3f6228a |
comparison
equal
deleted
inserted
replaced
427:0a0253eac470 | 428:3ecd8885ac67 |
---|---|
1 /* Record indices of function doc strings stored in a file. | |
2 Copyright (C) 1985, 1986, 1992, 1993, 1994, 1995 | |
3 Free Software Foundation, Inc. | |
4 | |
5 This file is part of XEmacs. | |
6 | |
7 XEmacs is free software; you can redistribute it and/or modify it | |
8 under the terms of the GNU General Public License as published by the | |
9 Free Software Foundation; either version 2, or (at your option) any | |
10 later version. | |
11 | |
12 XEmacs is distributed in the hope that it will be useful, but WITHOUT | |
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
15 for more details. | |
16 | |
17 You should have received a copy of the GNU General Public License | |
18 along with XEmacs; see the file COPYING. If not, write to | |
19 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | |
20 Boston, MA 02111-1307, USA. */ | |
21 | |
22 /* Synched up with: FSF 19.30. */ | |
23 | |
24 /* This file has been Mule-ized except as noted. */ | |
25 | |
26 #include <config.h> | |
27 #include "lisp.h" | |
28 | |
29 #include "buffer.h" | |
30 #include "bytecode.h" | |
31 #include "insdel.h" | |
32 #include "keymap.h" | |
33 #include "sysfile.h" | |
34 | |
35 Lisp_Object Vinternal_doc_file_name; | |
36 | |
37 Lisp_Object QSsubstitute; | |
38 | |
39 /* Read and return doc string from open file descriptor FD | |
40 at position POSITION. Does not close the file. Returns | |
41 string; or if error, returns a cons holding the error | |
42 data to pass to Fsignal. NAME_NONRELOC and NAME_RELOC | |
43 are only used for the error messages. */ | |
44 | |
45 Lisp_Object | |
46 unparesseuxify_doc_string (int fd, EMACS_INT position, | |
47 char *name_nonreloc, Lisp_Object name_reloc) | |
48 { | |
49 char buf[512 * 32 + 1]; | |
50 char *buffer = buf; | |
51 int buffer_size = sizeof (buf); | |
52 char *from, *to; | |
53 REGISTER char *p = buffer; | |
54 Lisp_Object return_me; | |
55 | |
56 if (0 > lseek (fd, position, 0)) | |
57 { | |
58 if (name_nonreloc) | |
59 name_reloc = build_string (name_nonreloc); | |
60 return_me = list3 (build_string | |
61 ("Position out of range in doc string file"), | |
62 name_reloc, make_int (position)); | |
63 goto done; | |
64 } | |
65 | |
66 /* Read the doc string into a buffer. | |
67 Use the fixed buffer BUF if it is big enough; otherwise allocate one. | |
68 We store the buffer in use in BUFFER and its size in BUFFER_SIZE. */ | |
69 | |
70 while (1) | |
71 { | |
72 int space_left = buffer_size - (p - buffer); | |
73 int nread; | |
74 | |
75 /* Switch to a bigger buffer if we need one. */ | |
76 if (space_left == 0) | |
77 { | |
78 char * old_buffer = buffer; | |
79 if (buffer == buf) { | |
80 buffer = (char *) xmalloc (buffer_size *= 2); | |
81 memcpy (buffer, old_buffer, p - old_buffer); | |
82 } else { | |
83 buffer = (char *) xrealloc (buffer, buffer_size *= 2); | |
84 } | |
85 p += buffer - old_buffer; | |
86 space_left = buffer_size - (p - buffer); | |
87 } | |
88 | |
89 /* Don't read too much at one go. */ | |
90 if (space_left > 1024 * 8) | |
91 space_left = 1024 * 8; | |
92 nread = read (fd, p, space_left); | |
93 if (nread < 0) | |
94 { | |
95 return_me = list1 (build_string | |
96 ("Read error on documentation file")); | |
97 goto done; | |
98 } | |
99 p[nread] = 0; | |
100 if (!nread) | |
101 break; | |
102 { | |
103 char *p1 = strchr (p, '\037'); /* End of doc string marker */ | |
104 if (p1) | |
105 { | |
106 *p1 = 0; | |
107 p = p1; | |
108 break; | |
109 } | |
110 } | |
111 p += nread; | |
112 } | |
113 | |
114 /* Scan the text and remove quoting with ^A (char code 1). | |
115 ^A^A becomes ^A, ^A0 becomes a null char, and ^A_ becomes a ^_. */ | |
116 from = to = buffer; | |
117 while (from < p) | |
118 { | |
119 if (*from != 1 /*^A*/) | |
120 *to++ = *from++; | |
121 else | |
122 { | |
123 int c = *(++from); | |
124 | |
125 from++; | |
126 switch (c) | |
127 { | |
128 case 1: *to++ = c; break; | |
129 case '0': *to++ = '\0'; break; | |
130 case '_': *to++ = '\037'; break; | |
131 default: | |
132 return_me = list2 (build_string | |
133 ("Invalid data in documentation file -- ^A followed by weird code"), | |
134 make_int (c)); | |
135 goto done; | |
136 } | |
137 } | |
138 } | |
139 | |
140 /* #### mrb: following STILL completely broken */ | |
141 return_me = make_ext_string ((Bufbyte *) buffer, to - buffer, FORMAT_BINARY); | |
142 | |
143 done: | |
144 if (buffer != buf) /* We must have allocated buffer above */ | |
145 xfree (buffer); | |
146 return return_me; | |
147 } | |
148 | |
149 #define string_join(dest, s1, s2) \ | |
150 memcpy ((void *) dest, (void *) XSTRING_DATA (s1), XSTRING_LENGTH (s1)); \ | |
151 memcpy ((void *) ((Bufbyte *) dest + XSTRING_LENGTH (s1)), \ | |
152 (void *) XSTRING_DATA (s2), XSTRING_LENGTH (s2)); \ | |
153 dest[XSTRING_LENGTH (s1) + XSTRING_LENGTH (s2)] = '\0' | |
154 | |
155 /* Extract a doc string from a file. FILEPOS says where to get it. | |
156 (This could actually be byte code instructions/constants instead | |
157 of a doc string.) | |
158 If it is an integer, use that position in the standard DOC file. | |
159 If it is (FILE . INTEGER), use FILE as the file name | |
160 and INTEGER as the position in that file. | |
161 But if INTEGER is negative, make it positive. | |
162 (A negative integer is used for user variables, so we can distinguish | |
163 them without actually fetching the doc string.) */ | |
164 | |
165 static Lisp_Object | |
166 get_doc_string (Lisp_Object filepos) | |
167 { | |
168 /* !!#### This function has not been Mule-ized */ | |
169 REGISTER int fd; | |
170 REGISTER char *name_nonreloc = 0; | |
171 int minsize; | |
172 EMACS_INT position; | |
173 Lisp_Object file, tem; | |
174 Lisp_Object name_reloc = Qnil; | |
175 | |
176 if (INTP (filepos)) | |
177 { | |
178 file = Vinternal_doc_file_name; | |
179 position = XINT (filepos); | |
180 } | |
181 else if (CONSP (filepos) && INTP (XCDR (filepos))) | |
182 { | |
183 file = XCAR (filepos); | |
184 position = XINT (XCDR (filepos)); | |
185 if (position < 0) | |
186 position = - position; | |
187 } | |
188 else | |
189 return Qnil; | |
190 | |
191 if (!STRINGP (file)) | |
192 return Qnil; | |
193 | |
194 /* Put the file name in NAME as a C string. | |
195 If it is relative, combine it with Vdoc_directory. */ | |
196 | |
197 tem = Ffile_name_absolute_p (file); | |
198 if (NILP (tem)) | |
199 { | |
200 /* XEmacs: Move this check here. OK if called during loadup to | |
201 load byte code instructions. */ | |
202 if (!STRINGP (Vdoc_directory)) | |
203 return Qnil; | |
204 | |
205 minsize = XSTRING_LENGTH (Vdoc_directory); | |
206 /* sizeof ("../lib-src/") == 12 */ | |
207 if (minsize < 12) | |
208 minsize = 12; | |
209 name_nonreloc = (char *) alloca (minsize + XSTRING_LENGTH (file) + 8); | |
210 string_join (name_nonreloc, Vdoc_directory, file); | |
211 } | |
212 else | |
213 name_reloc = file; | |
214 | |
215 fd = open (name_nonreloc ? name_nonreloc : | |
216 (char *) XSTRING_DATA (name_reloc), O_RDONLY | OPEN_BINARY, 0); | |
217 if (fd < 0) | |
218 { | |
219 #ifndef CANNOT_DUMP | |
220 if (purify_flag) | |
221 { | |
222 /* sizeof ("../lib-src/") == 12 */ | |
223 name_nonreloc = (char *) alloca (12 + XSTRING_LENGTH (file) + 8); | |
224 /* Preparing to dump; DOC file is probably not installed. | |
225 So check in ../lib-src. */ | |
226 strcpy (name_nonreloc, "../lib-src/"); | |
227 strcat (name_nonreloc, (char *) XSTRING_DATA (file)); | |
228 | |
229 fd = open (name_nonreloc, O_RDONLY | OPEN_BINARY, 0); | |
230 } | |
231 #endif /* CANNOT_DUMP */ | |
232 | |
233 if (fd < 0) | |
234 error ("Cannot open doc string file \"%s\"", | |
235 name_nonreloc ? name_nonreloc : | |
236 (char *) XSTRING_DATA (name_reloc)); | |
237 } | |
238 | |
239 tem = unparesseuxify_doc_string (fd, position, name_nonreloc, name_reloc); | |
240 close (fd); | |
241 | |
242 if (!STRINGP (tem)) | |
243 signal_error (Qerror, tem); | |
244 | |
245 return tem; | |
246 } | |
247 | |
248 /* Get a string from position FILEPOS and pass it through the Lisp reader. | |
249 We use this for fetching the bytecode string and constants vector | |
250 of a compiled function from the .elc file. */ | |
251 | |
252 Lisp_Object | |
253 read_doc_string (Lisp_Object filepos) | |
254 { | |
255 Lisp_Object string = get_doc_string (filepos); | |
256 | |
257 if (!STRINGP (string)) | |
258 signal_simple_error ("loading bytecode failed to return string", string); | |
259 return Fread (string); | |
260 } | |
261 | |
262 DEFUN ("documentation", Fdocumentation, 1, 2, 0, /* | |
263 Return the documentation string of FUNCTION. | |
264 Unless a non-nil second argument is given, the | |
265 string is passed through `substitute-command-keys'. | |
266 */ | |
267 (function, raw)) | |
268 { | |
269 /* This function can GC */ | |
270 Lisp_Object fun; | |
271 Lisp_Object doc; | |
272 | |
273 fun = Findirect_function (function); | |
274 | |
275 if (SUBRP (fun)) | |
276 { | |
277 if (XSUBR (fun)->doc == 0) | |
278 return Qnil; | |
279 if ((EMACS_INT) XSUBR (fun)->doc >= 0) | |
280 doc = build_string (XSUBR (fun)->doc); | |
281 else | |
282 doc = get_doc_string (make_int (- (EMACS_INT) XSUBR (fun)->doc)); | |
283 } | |
284 else if (COMPILED_FUNCTIONP (fun)) | |
285 { | |
286 Lisp_Object tem; | |
287 struct Lisp_Compiled_Function *f = XCOMPILED_FUNCTION (fun); | |
288 if (! (f->flags.documentationp)) | |
289 return Qnil; | |
290 tem = compiled_function_documentation (f); | |
291 if (STRINGP (tem)) | |
292 doc = tem; | |
293 else if (NATNUMP (tem) || CONSP (tem)) | |
294 doc = get_doc_string (tem); | |
295 else | |
296 return Qnil; | |
297 } | |
298 else if (KEYMAPP (fun)) | |
299 return build_translated_string ("Prefix command (definition is a keymap of subcommands)."); | |
300 else if (STRINGP (fun) || VECTORP (fun)) | |
301 return build_translated_string ("Keyboard macro."); | |
302 else if (CONSP (fun)) | |
303 { | |
304 Lisp_Object funcar = Fcar (fun); | |
305 | |
306 if (!SYMBOLP (funcar)) | |
307 return Fsignal (Qinvalid_function, list1 (fun)); | |
308 else if (EQ (funcar, Qlambda) | |
309 || EQ (funcar, Qautoload)) | |
310 { | |
311 Lisp_Object tem, tem1; | |
312 tem1 = Fcdr (Fcdr (fun)); | |
313 tem = Fcar (tem1); | |
314 if (STRINGP (tem)) | |
315 doc = tem; | |
316 /* Handle a doc reference--but these never come last | |
317 in the function body, so reject them if they are last. */ | |
318 else if ((NATNUMP (tem) || CONSP (tem)) | |
319 && ! NILP (XCDR (tem1))) | |
320 doc = get_doc_string (tem); | |
321 else | |
322 return Qnil; | |
323 } | |
324 else if (EQ (funcar, Qmacro)) | |
325 return Fdocumentation (Fcdr (fun), raw); | |
326 else | |
327 goto oops; | |
328 } | |
329 else | |
330 { | |
331 oops: | |
332 return Fsignal (Qinvalid_function, list1 (fun)); | |
333 } | |
334 | |
335 if (NILP (raw)) | |
336 { | |
337 struct gcpro gcpro1; | |
338 #ifdef I18N3 | |
339 Lisp_Object domain = Qnil; | |
340 if (COMPILED_FUNCTIONP (fun)) | |
341 domain = compiled_function_domain (XCOMPILED_FUNCTION (fun)); | |
342 if (NILP (domain)) | |
343 doc = Fgettext (doc); | |
344 else | |
345 doc = Fdgettext (domain, doc); | |
346 #endif | |
347 | |
348 GCPRO1 (doc); | |
349 doc = Fsubstitute_command_keys (doc); | |
350 UNGCPRO; | |
351 } | |
352 return doc; | |
353 } | |
354 | |
355 DEFUN ("documentation-property", Fdocumentation_property, 2, 3, 0, /* | |
356 Return the documentation string that is SYMBOL's PROP property. | |
357 This is like `get', but it can refer to strings stored in the | |
358 `doc-directory/DOC' file; and if the value is a string, it is passed | |
359 through `substitute-command-keys'. A non-nil third argument avoids this | |
360 translation. | |
361 */ | |
362 (sym, prop, raw)) | |
363 { | |
364 /* This function can GC */ | |
365 REGISTER Lisp_Object doc = Qnil; | |
366 #ifdef I18N3 | |
367 REGISTER Lisp_Object domain; | |
368 #endif | |
369 struct gcpro gcpro1; | |
370 | |
371 GCPRO1 (doc); | |
372 | |
373 doc = Fget (sym, prop, Qnil); | |
374 if (INTP (doc)) | |
375 doc = get_doc_string (XINT (doc) > 0 ? doc : make_int (- XINT (doc))); | |
376 else if (CONSP (doc)) | |
377 doc = get_doc_string (doc); | |
378 #ifdef I18N3 | |
379 if (!NILP (doc)) | |
380 { | |
381 domain = Fget (sym, Qvariable_domain, Qnil); | |
382 if (NILP (domain)) | |
383 doc = Fgettext (doc); | |
384 else | |
385 doc = Fdgettext (domain, doc); | |
386 } | |
387 #endif | |
388 if (NILP (raw) && STRINGP (doc)) | |
389 doc = Fsubstitute_command_keys (doc); | |
390 UNGCPRO; | |
391 return doc; | |
392 } | |
393 | |
394 static void | |
395 weird_doc (Lisp_Object sym, CONST char *weirdness, CONST char *type, int pos) | |
396 { | |
397 if (!strcmp (weirdness, GETTEXT ("duplicate"))) return; | |
398 message ("Note: Strange doc (%s) for %s %s @ %d", | |
399 weirdness, type, string_data (XSYMBOL (sym)->name), pos); | |
400 } | |
401 | |
402 | |
403 DEFUN ("Snarf-documentation", Fsnarf_documentation, 1, 1, 0, /* | |
404 Used during Emacs initialization, before dumping runnable Emacs, | |
405 to find pointers to doc strings stored in `.../lib-src/DOC' and | |
406 record them in function definitions. | |
407 One arg, FILENAME, a string which does not include a directory. | |
408 The file is written to `../lib-src', and later found in `exec-directory' | |
409 when doc strings are referred to in the dumped Emacs. | |
410 */ | |
411 (filename)) | |
412 { | |
413 /* !!#### This function has not been Mule-ized */ | |
414 int fd; | |
415 char buf[1024 + 1]; | |
416 REGISTER int filled; | |
417 REGISTER int pos; | |
418 REGISTER char *p, *end; | |
419 Lisp_Object sym, fun, tem; | |
420 char *name; | |
421 | |
422 #ifndef CANNOT_DUMP | |
423 if (!purify_flag) | |
424 error ("Snarf-documentation can only be called in an undumped Emacs"); | |
425 #endif | |
426 | |
427 CHECK_STRING (filename); | |
428 | |
429 #ifdef CANNOT_DUMP | |
430 if (!NILP(Vdoc_directory)) | |
431 { | |
432 CHECK_STRING (Vdoc_directory); | |
433 name = (char *) alloca (XSTRING_LENGTH (filename) | |
434 + XSTRING_LENGTH (Vdoc_directory) | |
435 + 1); | |
436 strcpy (name, (char *) XSTRING_DATA (Vdoc_directory)); | |
437 } | |
438 else | |
439 #endif /* CANNOT_DUMP */ | |
440 { | |
441 name = (char *) alloca (XSTRING_LENGTH (filename) + 14); | |
442 strcpy (name, "../lib-src/"); | |
443 } | |
444 | |
445 strcat (name, (char *) XSTRING_DATA (filename)); | |
446 | |
447 fd = open (name, O_RDONLY | OPEN_BINARY, 0); | |
448 if (fd < 0) | |
449 report_file_error ("Opening doc string file", | |
450 Fcons (build_string (name), Qnil)); | |
451 Vinternal_doc_file_name = filename; | |
452 filled = 0; | |
453 pos = 0; | |
454 while (1) | |
455 { | |
456 if (filled < 512) | |
457 filled += read (fd, &buf[filled], sizeof buf - 1 - filled); | |
458 if (!filled) | |
459 break; | |
460 | |
461 buf[filled] = 0; | |
462 p = buf; | |
463 end = buf + (filled < 512 ? filled : filled - 128); | |
464 while (p != end && *p != '\037') p++; | |
465 /* p points to ^_Ffunctionname\n or ^_Vvarname\n. */ | |
466 if (p != end) | |
467 { | |
468 end = strchr (p, '\n'); | |
469 sym = oblookup (Vobarray, (Bufbyte *) p + 2, end - p - 2); | |
470 if (SYMBOLP (sym)) | |
471 { | |
472 Lisp_Object offset = make_int (pos + end + 1 - buf); | |
473 /* Attach a docstring to a variable */ | |
474 if (p[1] == 'V') | |
475 { | |
476 /* Install file-position as variable-documentation property | |
477 and make it negative for a user-variable | |
478 (doc starts with a `*'). */ | |
479 Lisp_Object old = Fget (sym, Qvariable_documentation, Qzero); | |
480 if (!ZEROP (old)) | |
481 { | |
482 weird_doc (sym, GETTEXT ("duplicate"), | |
483 GETTEXT ("variable"), pos); | |
484 /* In the case of duplicate doc file entries, always | |
485 take the later one. But if the doc is not an int | |
486 (a string, say) leave it alone. */ | |
487 if (!INTP (old)) | |
488 goto weird; | |
489 } | |
490 Fput (sym, Qvariable_documentation, | |
491 ((end[1] == '*') | |
492 ? make_int (- XINT (offset)) | |
493 : offset)); | |
494 } | |
495 /* Attach a docstring to a function. | |
496 The type determines where the docstring is stored. */ | |
497 else if (p[1] == 'F') | |
498 { | |
499 fun = indirect_function (sym,0); | |
500 | |
501 if (CONSP (fun) && EQ (XCAR (fun), Qmacro)) | |
502 fun = XCDR (fun); | |
503 | |
504 if (UNBOUNDP (fun)) | |
505 { | |
506 /* May have been #if'ed out or something */ | |
507 weird_doc (sym, GETTEXT ("not fboundp"), | |
508 GETTEXT ("function"), pos); | |
509 goto weird; | |
510 } | |
511 else if (SUBRP (fun)) | |
512 { | |
513 /* Lisp_Subrs have a slot for it. */ | |
514 if (XSUBR (fun)->doc) | |
515 { | |
516 weird_doc (sym, GETTEXT ("duplicate"), | |
517 GETTEXT ("subr"), pos); | |
518 goto weird; | |
519 } | |
520 XSUBR (fun)->doc = (char *) (- XINT (offset)); | |
521 } | |
522 else if (CONSP (fun)) | |
523 { | |
524 /* If it's a lisp form, stick it in the form. */ | |
525 tem = XCAR (fun); | |
526 if (EQ (tem, Qlambda) || EQ (tem, Qautoload)) | |
527 { | |
528 tem = Fcdr (Fcdr (fun)); | |
529 if (CONSP (tem) && | |
530 INTP (XCAR (tem))) | |
531 { | |
532 Lisp_Object old = XCAR (tem); | |
533 if (!ZEROP (old)) | |
534 { | |
535 weird_doc (sym, GETTEXT ("duplicate"), | |
536 (EQ (tem, Qlambda) | |
537 ? GETTEXT ("lambda") | |
538 : GETTEXT ("autoload")), | |
539 pos); | |
540 /* In the case of duplicate doc file entries, | |
541 always take the later one. But if the doc | |
542 is not an int (a string, say) leave it | |
543 alone. */ | |
544 if (!INTP (old)) | |
545 goto weird; | |
546 } | |
547 XCAR (tem) = offset; | |
548 } | |
549 else if (!CONSP (tem)) | |
550 { | |
551 weird_doc (sym, GETTEXT ("!CONSP(tem)"), | |
552 GETTEXT ("function"), pos); | |
553 goto cont; | |
554 } | |
555 else | |
556 { | |
557 /* DOC string is a string not integer 0 */ | |
558 #if 0 | |
559 weird_doc (sym, GETTEXT ("!INTP(XCAR(tem))"), | |
560 GETTEXT ("function"), pos); | |
561 #endif | |
562 goto cont; | |
563 } | |
564 } | |
565 else | |
566 { | |
567 weird_doc (sym, GETTEXT ("not lambda or autoload"), | |
568 GETTEXT ("function"), pos); | |
569 goto cont; | |
570 } | |
571 } | |
572 else if (COMPILED_FUNCTIONP (fun)) | |
573 { | |
574 /* Compiled-Function objects sometimes have | |
575 slots for it. */ | |
576 struct Lisp_Compiled_Function *f = | |
577 XCOMPILED_FUNCTION (fun); | |
578 | |
579 /* This compiled-function object must have a | |
580 slot for the docstring, since we've found a | |
581 docstring for it. Unless there were multiple | |
582 definitions of it, and the latter one didn't | |
583 have any doc, which is a legal if slightly | |
584 bogus situation, so don't blow up. */ | |
585 | |
586 if (! (f->flags.documentationp)) | |
587 { | |
588 weird_doc (sym, GETTEXT ("no doc slot"), | |
589 GETTEXT ("bytecode"), pos); | |
590 goto weird; | |
591 } | |
592 else | |
593 { | |
594 Lisp_Object old = | |
595 compiled_function_documentation (f); | |
596 if (!ZEROP (old)) | |
597 { | |
598 weird_doc (sym, GETTEXT ("duplicate"), | |
599 GETTEXT ("bytecode"), pos); | |
600 /* In the case of duplicate doc file entries, | |
601 always take the later one. But if the doc is | |
602 not an int (a string, say) leave it alone. */ | |
603 if (!INTP (old)) | |
604 goto weird; | |
605 } | |
606 set_compiled_function_documentation (f, offset); | |
607 } | |
608 } | |
609 else | |
610 { | |
611 /* Otherwise the function is undefined or | |
612 otherwise weird. Ignore it. */ | |
613 weird_doc (sym, GETTEXT ("weird function"), | |
614 GETTEXT ("function"), pos); | |
615 goto weird; | |
616 } | |
617 } | |
618 else | |
619 { | |
620 /* lose: */ | |
621 error ("DOC file invalid at position %d", pos); | |
622 weird: | |
623 /* goto lose */; | |
624 } | |
625 } | |
626 } | |
627 cont: | |
628 pos += end - buf; | |
629 filled -= end - buf; | |
630 memmove (buf, end, filled); | |
631 } | |
632 close (fd); | |
633 return Qnil; | |
634 } | |
635 | |
636 | |
637 #if 1 /* Don't warn about functions whose doc was lost because they were | |
638 wrapped by advice-freeze.el... */ | |
639 static int | |
640 kludgily_ignore_lost_doc_p (Lisp_Object sym) | |
641 { | |
642 # define kludge_prefix "ad-Orig-" | |
643 struct Lisp_String *name = XSYMBOL (sym)->name; | |
644 return (string_length (name) > (Bytecount) (sizeof (kludge_prefix)) && | |
645 !strncmp ((char *) string_data (name), kludge_prefix, | |
646 sizeof (kludge_prefix) - 1)); | |
647 # undef kludge_prefix | |
648 } | |
649 #else | |
650 # define kludgily_ignore_lost_doc_p(sym) 0 | |
651 #endif | |
652 | |
653 | |
654 static int | |
655 verify_doc_mapper (Lisp_Object sym, void *arg) | |
656 { | |
657 Lisp_Object closure = *(Lisp_Object *)arg; | |
658 | |
659 if (!NILP (Ffboundp (sym))) | |
660 { | |
661 int doc = 0; | |
662 Lisp_Object fun = XSYMBOL (sym)->function; | |
663 if (CONSP (fun) && | |
664 EQ (XCAR (fun), Qmacro)) | |
665 fun = XCDR (fun); | |
666 | |
667 if (SUBRP (fun)) | |
668 doc = (EMACS_INT) XSUBR (fun)->doc; | |
669 else if (SYMBOLP (fun)) | |
670 doc = -1; | |
671 else if (KEYMAPP (fun)) | |
672 doc = -1; | |
673 else if (CONSP (fun)) | |
674 { | |
675 Lisp_Object tem = XCAR (fun); | |
676 if (EQ (tem, Qlambda) || EQ (tem, Qautoload)) | |
677 { | |
678 doc = -1; | |
679 tem = Fcdr (Fcdr (fun)); | |
680 if (CONSP (tem) && | |
681 INTP (XCAR (tem))) | |
682 doc = XINT (XCAR (tem)); | |
683 } | |
684 } | |
685 else if (COMPILED_FUNCTIONP (fun)) | |
686 { | |
687 struct Lisp_Compiled_Function *f = XCOMPILED_FUNCTION (fun); | |
688 if (! (f->flags.documentationp)) | |
689 doc = -1; | |
690 else | |
691 { | |
692 Lisp_Object tem = compiled_function_documentation (f); | |
693 if (INTP (tem)) | |
694 doc = XINT (tem); | |
695 } | |
696 } | |
697 | |
698 if (doc == 0 && !kludgily_ignore_lost_doc_p (sym)) | |
699 { | |
700 message ("Warning: doc lost for function %s.", | |
701 string_data (XSYMBOL (sym)->name)); | |
702 XCDR (closure) = Qt; | |
703 } | |
704 } | |
705 if (!NILP (Fboundp (sym))) | |
706 { | |
707 Lisp_Object doc = Fget (sym, Qvariable_documentation, Qnil); | |
708 if (ZEROP (doc)) | |
709 { | |
710 message ("Warning: doc lost for variable %s.", | |
711 string_data (XSYMBOL (sym)->name)); | |
712 XCDR (closure) = Qt; | |
713 } | |
714 } | |
715 return 0; /* Never stop */ | |
716 } | |
717 | |
718 DEFUN ("Verify-documentation", Fverify_documentation, 0, 0, 0, /* | |
719 Used to make sure everything went well with Snarf-documentation. | |
720 Writes to stderr if not. | |
721 */ | |
722 ()) | |
723 { | |
724 Lisp_Object closure = Fcons (Qnil, Qnil); | |
725 struct gcpro gcpro1; | |
726 GCPRO1 (closure); | |
727 map_obarray (Vobarray, verify_doc_mapper, &closure); | |
728 if (!NILP (Fcdr (closure))) | |
729 message ("\n" | |
730 "This is usually because some files were preloaded by loaddefs.el or\n" | |
731 "site-load.el, but were not passed to make-docfile by Makefile.\n"); | |
732 UNGCPRO; | |
733 return NILP (Fcdr (closure)) ? Qt : Qnil; | |
734 } | |
735 | |
736 | |
737 DEFUN ("substitute-command-keys", Fsubstitute_command_keys, 1, 1, 0, /* | |
738 Substitute key descriptions for command names in STRING. | |
739 Return a new string which is STRING with substrings of the form \\=\\[COMMAND] | |
740 replaced by either: a keystroke sequence that will invoke COMMAND, | |
741 or "M-x COMMAND" if COMMAND is not on any keys. | |
742 Substrings of the form \\=\\{MAPVAR} are replaced by summaries | |
743 \(made by describe-bindings) of the value of MAPVAR, taken as a keymap. | |
744 Substrings of the form \\=\\<MAPVAR> specify to use the value of MAPVAR | |
745 as the keymap for future \\=\\[COMMAND] substrings. | |
746 \\=\\= quotes the following character and is discarded; | |
747 thus, \\=\\=\\=\\= puts \\=\\= into the output, and \\=\\=\\=\\[ puts \\=\\[ into the output. | |
748 */ | |
749 (str)) | |
750 { | |
751 /* This function can GC */ | |
752 Bufbyte *buf; | |
753 int changed = 0; | |
754 REGISTER Bufbyte *strdata; | |
755 REGISTER Bufbyte *bufp; | |
756 Bytecount strlength; | |
757 Bytecount idx; | |
758 Bytecount bsize; | |
759 Bufbyte *new; | |
760 Lisp_Object tem; | |
761 Lisp_Object keymap; | |
762 Bufbyte *start; | |
763 Bytecount length; | |
764 Lisp_Object name; | |
765 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4; | |
766 | |
767 if (NILP (str)) | |
768 return Qnil; | |
769 | |
770 CHECK_STRING (str); | |
771 tem = Qnil; | |
772 keymap = Qnil; | |
773 name = Qnil; | |
774 GCPRO4 (str, tem, keymap, name); | |
775 | |
776 /* There is the possibility that the string is not destined for a | |
777 translating stream, and it could be argued that we should do the | |
778 same thing here as in Fformat(), but there are very few times | |
779 when this will be the case and many calls to this function | |
780 would have to have `gettext' calls added. (I18N3) */ | |
781 str = LISP_GETTEXT (str); | |
782 | |
783 /* KEYMAP is either nil (which means search all the active keymaps) | |
784 or a specified local map (which means search just that and the | |
785 global map). If non-nil, it might come from Voverriding_local_map, | |
786 or from a \\<mapname> construct in STR itself.. */ | |
787 #if 0 /* FSFmacs */ | |
788 /* This is really weird and garbagey. If keymap is nil and there's | |
789 an overriding-local-map, `where-is-internal' will correctly note | |
790 this, so there's no reason to do it here. Maybe FSFmacs | |
791 `where-is-internal' is broken. */ | |
792 /* | |
793 keymap = current_kboard->Voverriding_terminal_local_map; | |
794 if (NILP (keymap)) | |
795 keymap = Voverriding_local_map; | |
796 */ | |
797 #endif | |
798 | |
799 strlength = XSTRING_LENGTH (str); | |
800 bsize = 1 + strlength; | |
801 buf = (Bufbyte *) xmalloc (bsize); | |
802 bufp = buf; | |
803 | |
804 /* Have to reset strdata every time GC might be called */ | |
805 strdata = XSTRING_DATA (str); | |
806 for (idx = 0; idx < strlength; ) | |
807 { | |
808 Bufbyte *strp = strdata + idx; | |
809 | |
810 if (strp[0] != '\\') | |
811 { | |
812 /* just copy other chars */ | |
813 /* As it happens, this will work with Mule even if the | |
814 character quoted is multi-byte; the remaining multi-byte | |
815 characters will just be copied by this loop. */ | |
816 *bufp++ = *strp; | |
817 idx++; | |
818 } | |
819 else switch (strp[1]) | |
820 { | |
821 default: | |
822 { | |
823 /* just copy unknown escape sequences */ | |
824 *bufp++ = *strp; | |
825 idx++; | |
826 break; | |
827 } | |
828 case '=': | |
829 { | |
830 /* \= quotes the next character; | |
831 thus, to put in \[ without its special meaning, use \=\[. */ | |
832 /* As it happens, this will work with Mule even if the | |
833 character quoted is multi-byte; the remaining multi-byte | |
834 characters will just be copied by this loop. */ | |
835 changed = 1; | |
836 *bufp++ = strp[2]; | |
837 idx += 3; | |
838 break; | |
839 } | |
840 case '[': | |
841 { | |
842 changed = 1; | |
843 idx += 2; /* skip \[ */ | |
844 strp += 2; | |
845 start = strp; | |
846 | |
847 while ((idx < strlength) | |
848 && *strp != ']') | |
849 { | |
850 strp++; | |
851 idx++; | |
852 } | |
853 length = strp - start; | |
854 idx++; /* skip ] */ | |
855 | |
856 tem = Fintern (make_string (start, length), Qnil); | |
857 tem = Fwhere_is_internal (tem, keymap, Qt, Qnil, Qnil); | |
858 | |
859 #if 0 /* FSFmacs */ | |
860 /* Disregard menu bar bindings; it is positively annoying to | |
861 mention them when there's no menu bar, and it isn't terribly | |
862 useful even when there is a menu bar. */ | |
863 if (!NILP (tem)) | |
864 { | |
865 firstkey = Faref (tem, Qzero); | |
866 if (EQ (firstkey, Qmenu_bar)) | |
867 tem = Qnil; | |
868 } | |
869 #endif | |
870 | |
871 if (NILP (tem)) /* but not on any keys */ | |
872 { | |
873 new = (Bufbyte *) xrealloc (buf, bsize += 4); | |
874 bufp += new - buf; | |
875 buf = new; | |
876 memcpy (bufp, "M-x ", 4); | |
877 bufp += 4; | |
878 goto subst; | |
879 } | |
880 else | |
881 { /* function is on a key */ | |
882 tem = Fkey_description (tem); | |
883 goto subst_string; | |
884 } | |
885 } | |
886 case '{': | |
887 case '<': | |
888 { | |
889 /* ### jump to label `subst_string|subst' crosses | |
890 initialization of `buffer|_buf' */ | |
891 Lisp_Object buffer; | |
892 struct buffer *buf_; | |
893 | |
894 buffer = Fget_buffer_create (QSsubstitute); | |
895 buf_ = XBUFFER (buffer); | |
896 | |
897 Fbuffer_disable_undo (buffer); | |
898 Ferase_buffer (buffer); | |
899 | |
900 /* \{foo} is replaced with a summary of keymap (symbol-value foo). | |
901 \<foo> just sets the keymap used for \[cmd]. */ | |
902 changed = 1; | |
903 idx += 2; /* skip \{ or \< */ | |
904 strp += 2; | |
905 start = strp; | |
906 | |
907 while ((idx < strlength) | |
908 && *strp != '}' && *strp != '>') | |
909 { | |
910 strp++; | |
911 idx++; | |
912 } | |
913 length = strp - start; | |
914 idx++; /* skip } or > */ | |
915 | |
916 /* Get the value of the keymap in TEM, or nil if undefined. | |
917 Do this while still in the user's current buffer | |
918 in case it is a local variable. */ | |
919 name = Fintern (make_string (start, length), Qnil); | |
920 tem = Fboundp (name); | |
921 if (! NILP (tem)) | |
922 { | |
923 tem = Fsymbol_value (name); | |
924 if (! NILP (tem)) | |
925 tem = get_keymap (tem, 0, 1); | |
926 } | |
927 | |
928 if (NILP (tem)) | |
929 { | |
930 char boof[255], *b = boof; | |
931 *b++ = '\n'; | |
932 /* #### This sprintf() is potentially dangerous! */ | |
933 sprintf (b, GETTEXT ( | |
934 "Uses keymap \"%s\", which is not currently defined."), | |
935 (char *) XSTRING_DATA (Fsymbol_name (name))); | |
936 b += strlen (b); | |
937 *b++ = '\n'; | |
938 *b++ = 0; | |
939 buffer_insert_c_string (buf_, boof); | |
940 | |
941 if (start[-1] == '<') keymap = Qnil; | |
942 } | |
943 else if (start[-1] == '<') | |
944 keymap = tem; | |
945 else | |
946 describe_map_tree (tem, 1, Qnil, Qnil, 0, buffer); | |
947 | |
948 tem = make_string_from_buffer (buf_, BUF_BEG (buf_), | |
949 BUF_Z (buf_) - BUF_BEG (buf_)); | |
950 Ferase_buffer (buffer); | |
951 goto subst_string; | |
952 | |
953 subst_string: | |
954 start = XSTRING_DATA (tem); | |
955 length = XSTRING_LENGTH (tem); | |
956 subst: | |
957 bsize += length; | |
958 new = (Bufbyte *) xrealloc (buf, bsize); | |
959 bufp += new - buf; | |
960 buf = new; | |
961 memcpy (bufp, start, length); | |
962 bufp += length; | |
963 | |
964 /* Reset STRDATA in case gc relocated it. */ | |
965 strdata = XSTRING_DATA (str); | |
966 | |
967 break; | |
968 } | |
969 } | |
970 } | |
971 | |
972 if (changed) /* don't bother if nothing substituted */ | |
973 tem = make_string (buf, bufp - buf); | |
974 else | |
975 tem = str; | |
976 xfree (buf); | |
977 UNGCPRO; | |
978 return tem; | |
979 } | |
980 | |
981 | |
982 /************************************************************************/ | |
983 /* initialization */ | |
984 /************************************************************************/ | |
985 | |
986 void | |
987 syms_of_doc (void) | |
988 { | |
989 DEFSUBR (Fdocumentation); | |
990 DEFSUBR (Fdocumentation_property); | |
991 DEFSUBR (Fsnarf_documentation); | |
992 DEFSUBR (Fverify_documentation); | |
993 DEFSUBR (Fsubstitute_command_keys); | |
994 } | |
995 | |
996 void | |
997 vars_of_doc (void) | |
998 { | |
999 DEFVAR_LISP ("internal-doc-file-name", &Vinternal_doc_file_name /* | |
1000 Name of file containing documentation strings of built-in symbols. | |
1001 */ ); | |
1002 Vinternal_doc_file_name = Qnil; | |
1003 | |
1004 QSsubstitute = build_string (" *substitute*"); | |
1005 staticpro (&QSsubstitute); | |
1006 } |