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