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