]> code.delx.au - gnu-emacs/blob - src/doc.c
(compilation-handle-exit): New function, broken out of compilation-sentinel.
[gnu-emacs] / src / doc.c
1 /* Record indices of function doc strings stored in a file.
2 Copyright (C) 1985, 1986, 1993, 1994, 1995 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
19
20
21 #include <config.h>
22
23 #include <sys/types.h>
24 #include <sys/file.h> /* Must be after sys/types.h for USG and BSD4_1*/
25
26 #ifdef USG5
27 #include <fcntl.h>
28 #endif
29
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33
34 #ifndef O_RDONLY
35 #define O_RDONLY 0
36 #endif
37
38 #include "lisp.h"
39 #include "buffer.h"
40 #include "keyboard.h"
41
42 Lisp_Object Vdoc_file_name;
43
44 extern char *index ();
45
46 extern Lisp_Object Voverriding_local_map;
47
48 /* For VMS versions with limited file name syntax,
49 convert the name to something VMS will allow. */
50 static void
51 munge_doc_file_name (name)
52 char *name;
53 {
54 #ifdef VMS
55 #ifndef VMS4_4
56 /* For VMS versions with limited file name syntax,
57 convert the name to something VMS will allow. */
58 p = name;
59 while (*p)
60 {
61 if (*p == '-')
62 *p = '_';
63 p++;
64 }
65 #endif /* not VMS4_4 */
66 #ifdef VMS4_4
67 strcpy (name, sys_translate_unix (name));
68 #endif /* VMS4_4 */
69 #endif /* VMS */
70 }
71
72 /* Extract a doc string from a file. FILEPOS says where to get it.
73 If it is an integer, use that position in the standard DOC-... file.
74 If it is (FILE . INTEGER), use FILE as the file name
75 and INTEGER as the position in that file.
76 But if INTEGER is negative, make it positive.
77 (A negative integer is used for user variables, so we can distinguish
78 them without actually fetching the doc string.) */
79
80 static Lisp_Object
81 get_doc_string (filepos)
82 Lisp_Object filepos;
83 {
84 char buf[512 * 32 + 1];
85 char *buffer;
86 int buffer_size;
87 int free_it;
88 char *from, *to;
89 register int fd;
90 register char *name;
91 register char *p, *p1;
92 int minsize;
93 int position;
94 Lisp_Object file, tem;
95
96 if (INTEGERP (filepos))
97 {
98 file = Vdoc_file_name;
99 position = XINT (filepos);
100 }
101 else if (CONSP (filepos))
102 {
103 file = XCONS (filepos)->car;
104 position = XINT (XCONS (filepos)->cdr);
105 if (position < 0)
106 position = - position;
107 }
108 else
109 return Qnil;
110
111 if (!STRINGP (Vdoc_directory))
112 return Qnil;
113
114 if (!STRINGP (file))
115 return Qnil;
116
117 /* Put the file name in NAME as a C string.
118 If it is relative, combine it with Vdoc_directory. */
119
120 tem = Ffile_name_absolute_p (file);
121 if (NILP (tem))
122 {
123 minsize = XSTRING (Vdoc_directory)->size;
124 /* sizeof ("../etc/") == 8 */
125 if (minsize < 8)
126 minsize = 8;
127 name = (char *) alloca (minsize + XSTRING (file)->size + 8);
128 strcpy (name, XSTRING (Vdoc_directory)->data);
129 strcat (name, XSTRING (file)->data);
130 munge_doc_file_name (name);
131 }
132 else
133 {
134 name = (char *) XSTRING (file)->data;
135 }
136
137 fd = open (name, O_RDONLY, 0);
138 if (fd < 0)
139 {
140 #ifndef CANNOT_DUMP
141 if (!NILP (Vpurify_flag))
142 {
143 /* Preparing to dump; DOC file is probably not installed.
144 So check in ../etc. */
145 strcpy (name, "../etc/");
146 strcat (name, XSTRING (file)->data);
147 munge_doc_file_name (name);
148
149 fd = open (name, O_RDONLY, 0);
150 }
151 #endif
152
153 if (fd < 0)
154 error ("Cannot open doc string file \"%s\"", name);
155 }
156
157 if (0 > lseek (fd, position, 0))
158 {
159 close (fd);
160 error ("Position %ld out of range in doc string file \"%s\"",
161 position, name);
162 }
163
164 /* Read the doc string into a buffer.
165 Use the fixed buffer BUF if it is big enough;
166 otherwise allocate one and set FREE_IT.
167 We store the buffer in use in BUFFER and its size in BUFFER_SIZE. */
168
169 buffer = buf;
170 buffer_size = sizeof buf;
171 free_it = 0;
172 p = buf;
173 while (1)
174 {
175 int space_left = buffer_size - (p - buffer);
176 int nread;
177
178 /* Switch to a bigger buffer if we need one. */
179 if (space_left == 0)
180 {
181 if (free_it)
182 {
183 int offset = p - buffer;
184 buffer = (char *) xrealloc (buffer,
185 buffer_size *= 2);
186 p = buffer + offset;
187 }
188 else
189 {
190 buffer = (char *) xmalloc (buffer_size *= 2);
191 bcopy (buf, buffer, p - buf);
192 p = buffer + (p - buf);
193 }
194 free_it = 1;
195 space_left = buffer_size - (p - buffer);
196 }
197
198 /* Don't read too too much at one go. */
199 if (space_left > 1024 * 8)
200 space_left = 1024 * 8;
201 nread = read (fd, p, space_left);
202 if (nread < 0)
203 {
204 close (fd);
205 error ("Read error on documentation file");
206 }
207 p[nread] = 0;
208 if (!nread)
209 break;
210 p1 = index (p, '\037');
211 if (p1)
212 {
213 *p1 = 0;
214 p = p1;
215 break;
216 }
217 p += nread;
218 }
219 close (fd);
220
221 /* Scan the text and perform quoting with ^A (char code 1).
222 ^A^A becomes ^A, ^A0 becomes a null char, and ^A_ becomes a ^_. */
223 from = buffer;
224 to = buffer;
225 while (from != p)
226 {
227 if (*from == 1)
228 {
229 int c;
230
231 from++;
232 c = *from++;
233 if (c == 1)
234 *to++ = c;
235 else if (c == '0')
236 *to++ = 0;
237 else if (c == '_')
238 *to++ = 037;
239 else
240 error ("Invalid data in documentation file -- ^A followed by code 0%o", c);
241 }
242 else
243 *to++ = *from++;
244 }
245
246 tem = make_string (buffer, to - buffer);
247 if (free_it)
248 free (buffer);
249
250 return tem;
251 }
252
253 /* Get a string from position FILEPOS and pass it through the Lisp reader.
254 We use this for fetching the bytecode string and constants vector
255 of a compiled function from the .elc file. */
256
257 Lisp_Object
258 read_doc_string (filepos)
259 Lisp_Object filepos;
260 {
261 return Fread (get_doc_string (filepos));
262 }
263
264 DEFUN ("documentation", Fdocumentation, Sdocumentation, 1, 2, 0,
265 "Return the documentation string of FUNCTION.\n\
266 Unless a non-nil second argument is given, the\n\
267 string is passed through `substitute-command-keys'.")
268 (function, raw)
269 Lisp_Object function, raw;
270 {
271 Lisp_Object fun;
272 Lisp_Object funcar;
273 Lisp_Object tem, doc;
274
275 fun = Findirect_function (function);
276
277 if (SUBRP (fun))
278 {
279 if (XSUBR (fun)->doc == 0) return Qnil;
280 if ((EMACS_INT) XSUBR (fun)->doc >= 0)
281 doc = build_string (XSUBR (fun)->doc);
282 else
283 doc = get_doc_string (make_number (- (EMACS_INT) XSUBR (fun)->doc));
284 }
285 else if (COMPILEDP (fun))
286 {
287 if ((XVECTOR (fun)->size & PSEUDOVECTOR_SIZE_MASK) <= COMPILED_DOC_STRING)
288 return Qnil;
289 tem = XVECTOR (fun)->contents[COMPILED_DOC_STRING];
290 if (STRINGP (tem))
291 doc = tem;
292 else if (NATNUMP (tem) || CONSP (tem))
293 doc = get_doc_string (tem);
294 else
295 return Qnil;
296 }
297 else if (STRINGP (fun) || VECTORP (fun))
298 {
299 return build_string ("Keyboard macro.");
300 }
301 else if (CONSP (fun))
302 {
303 funcar = Fcar (fun);
304 if (!SYMBOLP (funcar))
305 return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
306 else if (EQ (funcar, Qkeymap))
307 return build_string ("Prefix command (definition is a keymap associating keystrokes with\n\
308 subcommands.)");
309 else if (EQ (funcar, Qlambda)
310 || EQ (funcar, Qautoload))
311 {
312 Lisp_Object tem1;
313 tem1 = Fcdr (Fcdr (fun));
314 tem = Fcar (tem1);
315 if (STRINGP (tem))
316 doc = tem;
317 /* Handle a doc reference--but these never come last
318 in the function body, so reject them if they are last. */
319 else if ((NATNUMP (tem) || CONSP (tem))
320 && ! NILP (XCONS (tem1)->cdr))
321 doc = get_doc_string (tem);
322 else
323 return Qnil;
324 }
325 else if (EQ (funcar, Qmocklisp))
326 return Qnil;
327 else if (EQ (funcar, Qmacro))
328 return Fdocumentation (Fcdr (fun), raw);
329 else
330 goto oops;
331 }
332 else
333 {
334 oops:
335 Fsignal (Qinvalid_function, Fcons (fun, Qnil));
336 }
337
338 if (NILP (raw))
339 {
340 struct gcpro gcpro1;
341
342 GCPRO1 (doc);
343 doc = Fsubstitute_command_keys (doc);
344 UNGCPRO;
345 }
346 return doc;
347 }
348
349 DEFUN ("documentation-property", Fdocumentation_property, Sdocumentation_property, 2, 3, 0,
350 "Return the documentation string that is SYMBOL's PROP property.\n\
351 This is like `get', but it can refer to strings stored in the\n\
352 `etc/DOC' file; and if the value is a string, it is passed through\n\
353 `substitute-command-keys'. A non-nil third argument avoids this\n\
354 translation.")
355 (sym, prop, raw)
356 Lisp_Object sym, prop, raw;
357 {
358 register Lisp_Object tem;
359
360 tem = Fget (sym, prop);
361 if (INTEGERP (tem))
362 tem = get_doc_string (XINT (tem) > 0 ? tem : make_number (- XINT (tem)));
363 else if (CONSP (tem))
364 tem = get_doc_string (tem);
365 if (NILP (raw) && STRINGP (tem))
366 return Fsubstitute_command_keys (tem);
367 return tem;
368 }
369 \f
370 /* Scanning the DOC files and placing docstring offsets into functions. */
371
372 static void
373 store_function_docstring (fun, offset)
374 Lisp_Object fun;
375 /* Use EMACS_INT because we get this from pointer subtraction. */
376 EMACS_INT offset;
377 {
378 fun = indirect_function (fun);
379
380 /* The type determines where the docstring is stored. */
381
382 /* Lisp_Subrs have a slot for it. */
383 if (SUBRP (fun))
384 XSUBR (fun)->doc = (char *) - offset;
385
386 /* If it's a lisp form, stick it in the form. */
387 else if (CONSP (fun))
388 {
389 Lisp_Object tem;
390
391 tem = XCONS (fun)->car;
392 if (EQ (tem, Qlambda) || EQ (tem, Qautoload))
393 {
394 tem = Fcdr (Fcdr (fun));
395 if (CONSP (tem) && INTEGERP (XCONS (tem)->car))
396 XSETFASTINT (XCONS (tem)->car, offset);
397 }
398 else if (EQ (tem, Qmacro))
399 store_function_docstring (XCONS (fun)->cdr, offset);
400 }
401
402 /* Bytecode objects sometimes have slots for it. */
403 else if (COMPILEDP (fun))
404 {
405 /* This bytecode object must have a slot for the
406 docstring, since we've found a docstring for it. */
407 if ((XVECTOR (fun)->size & PSEUDOVECTOR_SIZE_MASK) > COMPILED_DOC_STRING)
408 XSETFASTINT (XVECTOR (fun)->contents[COMPILED_DOC_STRING], offset);
409 }
410 }
411
412
413 DEFUN ("Snarf-documentation", Fsnarf_documentation, Ssnarf_documentation,
414 1, 1, 0,
415 "Used during Emacs initialization, before dumping runnable Emacs,\n\
416 to find pointers to doc strings stored in `etc/DOC...' and\n\
417 record them in function definitions.\n\
418 One arg, FILENAME, a string which does not include a directory.\n\
419 The file is found in `../etc' now; found in the `data-directory'\n\
420 when doc strings are referred to later in the dumped Emacs.")
421 (filename)
422 Lisp_Object filename;
423 {
424 int fd;
425 char buf[1024 + 1];
426 register int filled;
427 register int pos;
428 register char *p, *end;
429 Lisp_Object sym, fun, tem;
430 char *name;
431 extern char *index ();
432
433 #ifndef CANNOT_DUMP
434 if (NILP (Vpurify_flag))
435 error ("Snarf-documentation can only be called in an undumped Emacs");
436 #endif
437
438 CHECK_STRING (filename, 0);
439
440 #ifndef CANNOT_DUMP
441 name = (char *) alloca (XSTRING (filename)->size + 14);
442 strcpy (name, "../etc/");
443 #else /* CANNOT_DUMP */
444 CHECK_STRING (Vdoc_directory, 0);
445 name = (char *) alloca (XSTRING (filename)->size +
446 XSTRING (Vdoc_directory)->size + 1);
447 strcpy (name, XSTRING (Vdoc_directory)->data);
448 #endif /* CANNOT_DUMP */
449 strcat (name, XSTRING (filename)->data); /*** Add this line ***/
450 #ifdef VMS
451 #ifndef VMS4_4
452 /* For VMS versions with limited file name syntax,
453 convert the name to something VMS will allow. */
454 p = name;
455 while (*p)
456 {
457 if (*p == '-')
458 *p = '_';
459 p++;
460 }
461 #endif /* not VMS4_4 */
462 #ifdef VMS4_4
463 strcpy (name, sys_translate_unix (name));
464 #endif /* VMS4_4 */
465 #endif /* VMS */
466
467 fd = open (name, O_RDONLY, 0);
468 if (fd < 0)
469 report_file_error ("Opening doc string file",
470 Fcons (build_string (name), Qnil));
471 Vdoc_file_name = filename;
472 filled = 0;
473 pos = 0;
474 while (1)
475 {
476 if (filled < 512)
477 filled += read (fd, &buf[filled], sizeof buf - 1 - filled);
478 if (!filled)
479 break;
480
481 buf[filled] = 0;
482 p = buf;
483 end = buf + (filled < 512 ? filled : filled - 128);
484 while (p != end && *p != '\037') p++;
485 /* p points to ^_Ffunctionname\n or ^_Vvarname\n. */
486 if (p != end)
487 {
488 end = index (p, '\n');
489 sym = oblookup (Vobarray, p + 2, end - p - 2);
490 if (SYMBOLP (sym))
491 {
492 /* Attach a docstring to a variable? */
493 if (p[1] == 'V')
494 {
495 /* Install file-position as variable-documentation property
496 and make it negative for a user-variable
497 (doc starts with a `*'). */
498 Fput (sym, Qvariable_documentation,
499 make_number ((pos + end + 1 - buf)
500 * (end[1] == '*' ? -1 : 1)));
501 }
502
503 /* Attach a docstring to a function? */
504 else if (p[1] == 'F')
505 store_function_docstring (sym, pos + end + 1 - buf);
506
507 else
508 error ("DOC file invalid at position %d", pos);
509 }
510 }
511 pos += end - buf;
512 filled -= end - buf;
513 bcopy (end, buf, filled);
514 }
515 close (fd);
516 return Qnil;
517 }
518 \f
519 DEFUN ("substitute-command-keys", Fsubstitute_command_keys,
520 Ssubstitute_command_keys, 1, 1, 0,
521 "Substitute key descriptions for command names in STRING.\n\
522 Return a new string which is STRING with substrings of the form \\=\\[COMMAND]\n\
523 replaced by either: a keystroke sequence that will invoke COMMAND,\n\
524 or \"M-x COMMAND\" if COMMAND is not on any keys.\n\
525 Substrings of the form \\=\\{MAPVAR} are replaced by summaries\n\
526 \(made by describe-bindings) of the value of MAPVAR, taken as a keymap.\n\
527 Substrings of the form \\=\\<MAPVAR> specify to use the value of MAPVAR\n\
528 as the keymap for future \\=\\[COMMAND] substrings.\n\
529 \\=\\= quotes the following character and is discarded;\n\
530 thus, \\=\\=\\=\\= puts \\=\\= into the output, and \\=\\=\\=\\[ puts \\=\\[ into the output.")
531 (str)
532 Lisp_Object str;
533 {
534 unsigned char *buf;
535 int changed = 0;
536 register unsigned char *strp;
537 register unsigned char *bufp;
538 int idx;
539 int bsize;
540 unsigned char *new;
541 Lisp_Object tem;
542 Lisp_Object keymap;
543 unsigned char *start;
544 int length;
545 Lisp_Object name;
546 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
547
548 if (NILP (str))
549 return Qnil;
550
551 CHECK_STRING (str, 0);
552 tem = Qnil;
553 keymap = Qnil;
554 name = Qnil;
555 GCPRO4 (str, tem, keymap, name);
556
557 /* KEYMAP is either nil (which means search all the active keymaps)
558 or a specified local map (which means search just that and the
559 global map). If non-nil, it might come from Voverriding_local_map,
560 or from a \\<mapname> construct in STR itself.. */
561 keymap = current_kboard->Voverriding_terminal_local_map;
562 if (NILP (keymap))
563 keymap = Voverriding_local_map;
564
565 bsize = XSTRING (str)->size;
566 bufp = buf = (unsigned char *) xmalloc (bsize);
567
568 strp = (unsigned char *) XSTRING (str)->data;
569 while (strp < (unsigned char *) XSTRING (str)->data + XSTRING (str)->size)
570 {
571 if (strp[0] == '\\' && strp[1] == '=')
572 {
573 /* \= quotes the next character;
574 thus, to put in \[ without its special meaning, use \=\[. */
575 changed = 1;
576 *bufp++ = strp[2];
577 strp += 3;
578 }
579 else if (strp[0] == '\\' && strp[1] == '[')
580 {
581 Lisp_Object firstkey;
582
583 changed = 1;
584 strp += 2; /* skip \[ */
585 start = strp;
586
587 while ((strp - (unsigned char *) XSTRING (str)->data
588 < XSTRING (str)->size)
589 && *strp != ']')
590 strp++;
591 length = strp - start;
592 strp++; /* skip ] */
593
594 /* Save STRP in IDX. */
595 idx = strp - (unsigned char *) XSTRING (str)->data;
596 tem = Fintern (make_string (start, length), Qnil);
597 tem = Fwhere_is_internal (tem, keymap, Qt, Qnil);
598
599 /* Disregard menu bar bindings; it is positively annoying to
600 mention them when there's no menu bar, and it isn't terribly
601 useful even when there is a menu bar. */
602 if (!NILP (tem))
603 {
604 firstkey = Faref (tem, make_number (0));
605 if (EQ (firstkey, Qmenu_bar))
606 tem = Qnil;
607 }
608
609 if (NILP (tem)) /* but not on any keys */
610 {
611 new = (unsigned char *) xrealloc (buf, bsize += 4);
612 bufp += new - buf;
613 buf = new;
614 bcopy ("M-x ", bufp, 4);
615 bufp += 4;
616 goto subst;
617 }
618 else
619 { /* function is on a key */
620 tem = Fkey_description (tem);
621 goto subst_string;
622 }
623 }
624 /* \{foo} is replaced with a summary of the keymap (symbol-value foo).
625 \<foo> just sets the keymap used for \[cmd]. */
626 else if (strp[0] == '\\' && (strp[1] == '{' || strp[1] == '<'))
627 {
628 struct buffer *oldbuf;
629
630 changed = 1;
631 strp += 2; /* skip \{ or \< */
632 start = strp;
633
634 while ((strp - (unsigned char *) XSTRING (str)->data
635 < XSTRING (str)->size)
636 && *strp != '}' && *strp != '>')
637 strp++;
638 length = strp - start;
639 strp++; /* skip } or > */
640
641 /* Save STRP in IDX. */
642 idx = strp - (unsigned char *) XSTRING (str)->data;
643
644 /* Get the value of the keymap in TEM, or nil if undefined.
645 Do this while still in the user's current buffer
646 in case it is a local variable. */
647 name = Fintern (make_string (start, length), Qnil);
648 tem = Fboundp (name);
649 if (! NILP (tem))
650 {
651 tem = Fsymbol_value (name);
652 if (! NILP (tem))
653 tem = get_keymap_1 (tem, 0, 1);
654 }
655
656 /* Now switch to a temp buffer. */
657 oldbuf = current_buffer;
658 set_buffer_internal (XBUFFER (Vprin1_to_string_buffer));
659
660 if (NILP (tem))
661 {
662 name = Fsymbol_name (name);
663 insert_string ("\nUses keymap \"");
664 insert_from_string (name, 0, XSTRING (name)->size, 1);
665 insert_string ("\", which is not currently defined.\n");
666 if (start[-1] == '<') keymap = Qnil;
667 }
668 else if (start[-1] == '<')
669 keymap = tem;
670 else
671 describe_map_tree (tem, 1, Qnil, Qnil, (char *)0, 1, 0);
672 tem = Fbuffer_string ();
673 Ferase_buffer ();
674 set_buffer_internal (oldbuf);
675
676 subst_string:
677 start = XSTRING (tem)->data;
678 length = XSTRING (tem)->size;
679 subst:
680 new = (unsigned char *) xrealloc (buf, bsize += length);
681 bufp += new - buf;
682 buf = new;
683 bcopy (start, bufp, length);
684 bufp += length;
685 /* Check STR again in case gc relocated it. */
686 strp = (unsigned char *) XSTRING (str)->data + idx;
687 }
688 else /* just copy other chars */
689 *bufp++ = *strp++;
690 }
691
692 if (changed) /* don't bother if nothing substituted */
693 tem = make_string (buf, bufp - buf);
694 else
695 tem = str;
696 xfree (buf);
697 RETURN_UNGCPRO (tem);
698 }
699 \f
700 syms_of_doc ()
701 {
702 DEFVAR_LISP ("internal-doc-file-name", &Vdoc_file_name,
703 "Name of file containing documentation strings of built-in symbols.");
704 Vdoc_file_name = Qnil;
705
706 defsubr (&Sdocumentation);
707 defsubr (&Sdocumentation_property);
708 defsubr (&Ssnarf_documentation);
709 defsubr (&Ssubstitute_command_keys);
710 }