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