*** empty log message ***
[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
ba29c3c9
RS
84 them without actually fetching the doc string.)
85
86 If UNIBYTE is nonzero, always return the result as a unibyte string. */
700ea809
RS
87
88static Lisp_Object
ba29c3c9 89get_doc_string (filepos, unibyte)
700ea809 90 Lisp_Object filepos;
ba29c3c9 91 int unibyte;
778d47c7 92{
700ea809 93 char *from, *to;
778d47c7
RS
94 register int fd;
95 register char *name;
96 register char *p, *p1;
778d47c7 97 int minsize;
0fded513 98 int offset, position;
700ea809 99 Lisp_Object file, tem;
778d47c7 100
700ea809
RS
101 if (INTEGERP (filepos))
102 {
103 file = Vdoc_file_name;
104 position = XINT (filepos);
105 }
106 else if (CONSP (filepos))
107 {
108 file = XCONS (filepos)->car;
109 position = XINT (XCONS (filepos)->cdr);
0c00bc70
RS
110 if (position < 0)
111 position = - position;
700ea809
RS
112 }
113 else
778d47c7
RS
114 return Qnil;
115
700ea809
RS
116 if (!STRINGP (Vdoc_directory))
117 return Qnil;
118
119 if (!STRINGP (file))
120 return Qnil;
121
122 /* Put the file name in NAME as a C string.
123 If it is relative, combine it with Vdoc_directory. */
124
125 tem = Ffile_name_absolute_p (file);
126 if (NILP (tem))
127 {
128 minsize = XSTRING (Vdoc_directory)->size;
129 /* sizeof ("../etc/") == 8 */
130 if (minsize < 8)
131 minsize = 8;
132 name = (char *) alloca (minsize + XSTRING (file)->size + 8);
133 strcpy (name, XSTRING (Vdoc_directory)->data);
134 strcat (name, XSTRING (file)->data);
135 munge_doc_file_name (name);
136 }
137 else
138 {
a039f534 139 name = (char *) XSTRING (file)->data;
700ea809 140 }
c6045832
JB
141
142 fd = open (name, O_RDONLY, 0);
143 if (fd < 0)
778d47c7
RS
144 {
145#ifndef CANNOT_DUMP
146 if (!NILP (Vpurify_flag))
147 {
148 /* Preparing to dump; DOC file is probably not installed.
149 So check in ../etc. */
150 strcpy (name, "../etc/");
700ea809 151 strcat (name, XSTRING (file)->data);
778d47c7
RS
152 munge_doc_file_name (name);
153
154 fd = open (name, O_RDONLY, 0);
155 }
156#endif
778d47c7
RS
157 if (fd < 0)
158 error ("Cannot open doc string file \"%s\"", name);
159 }
160
0fded513
RS
161 /* Seek only to beginning of disk block. */
162 offset = position % (8 * 1024);
163 if (0 > lseek (fd, position - offset, 0))
c6045832
JB
164 {
165 close (fd);
166 error ("Position %ld out of range in doc string file \"%s\"",
700ea809 167 position, name);
c6045832 168 }
700ea809 169
912e8480
RS
170 /* Read the doc string into get_doc_string_buffer.
171 P points beyond the data just read. */
0fded513 172
912e8480 173 p = get_doc_string_buffer;
700ea809 174 while (1)
c6045832 175 {
912e8480
RS
176 int space_left = (get_doc_string_buffer_size
177 - (p - get_doc_string_buffer));
700ea809
RS
178 int nread;
179
0fded513 180 /* Allocate or grow the buffer if we need to. */
700ea809
RS
181 if (space_left == 0)
182 {
912e8480
RS
183 int in_buffer = p - get_doc_string_buffer;
184 get_doc_string_buffer_size += 16 * 1024;
185 get_doc_string_buffer
186 = (char *) xrealloc (get_doc_string_buffer,
187 get_doc_string_buffer_size + 1);
188 p = get_doc_string_buffer + in_buffer;
189 space_left = (get_doc_string_buffer_size
190 - (p - get_doc_string_buffer));
700ea809
RS
191 }
192
0fded513
RS
193 /* Read a disk block at a time.
194 If we read the same block last time, maybe skip this? */
700ea809
RS
195 if (space_left > 1024 * 8)
196 space_left = 1024 * 8;
197 nread = read (fd, p, space_left);
198 if (nread < 0)
199 {
200 close (fd);
201 error ("Read error on documentation file");
202 }
203 p[nread] = 0;
204 if (!nread)
c6045832 205 break;
912e8480 206 if (p == get_doc_string_buffer)
0fded513
RS
207 p1 = index (p + offset, '\037');
208 else
209 p1 = index (p, '\037');
c6045832
JB
210 if (p1)
211 {
212 *p1 = 0;
213 p = p1;
214 break;
215 }
700ea809 216 p += nread;
c6045832
JB
217 }
218 close (fd);
700ea809
RS
219
220 /* Scan the text and perform quoting with ^A (char code 1).
221 ^A^A becomes ^A, ^A0 becomes a null char, and ^A_ becomes a ^_. */
912e8480
RS
222 from = get_doc_string_buffer + offset;
223 to = get_doc_string_buffer + offset;
700ea809
RS
224 while (from != p)
225 {
226 if (*from == 1)
227 {
228 int c;
229
230 from++;
231 c = *from++;
232 if (c == 1)
233 *to++ = c;
234 else if (c == '0')
235 *to++ = 0;
236 else if (c == '_')
237 *to++ = 037;
238 else
239 error ("Invalid data in documentation file -- ^A followed by code 0%o", c);
240 }
241 else
242 *to++ = *from++;
243 }
244
ba29c3c9
RS
245 if (unibyte)
246 return make_unibyte_string (get_doc_string_buffer + offset,
247 to - (get_doc_string_buffer + offset));
248 else
249 return make_string (get_doc_string_buffer + offset,
250 to - (get_doc_string_buffer + offset));
700ea809
RS
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
257Lisp_Object
258read_doc_string (filepos)
259 Lisp_Object filepos;
260{
ba29c3c9 261 return Fread (get_doc_string (filepos, 1));
c6045832
JB
262}
263
ee04dc54 264DEFUN ("documentation", Fdocumentation, Sdocumentation, 1, 2, 0,
08564963 265 "Return the documentation string of FUNCTION.\n\
4acb738e 266Unless a non-nil second argument RAW is given, the\n\
ee04dc54 267string is passed through `substitute-command-keys'.")
502ddf23
JB
268 (function, raw)
269 Lisp_Object function, raw;
c6045832
JB
270{
271 Lisp_Object fun;
272 Lisp_Object funcar;
ee04dc54 273 Lisp_Object tem, doc;
c6045832 274
502ddf23 275 fun = Findirect_function (function);
c6045832 276
5b5f6883 277 if (SUBRP (fun))
c6045832 278 {
c6045832 279 if (XSUBR (fun)->doc == 0) return Qnil;
f0f787b8 280 if ((EMACS_INT) XSUBR (fun)->doc >= 0)
ee04dc54 281 doc = build_string (XSUBR (fun)->doc);
c6045832 282 else
ba29c3c9 283 doc = get_doc_string (make_number (- (EMACS_INT) XSUBR (fun)->doc), 0);
5b5f6883
KH
284 }
285 else if (COMPILEDP (fun))
286 {
f9b4aacf 287 if ((XVECTOR (fun)->size & PSEUDOVECTOR_SIZE_MASK) <= COMPILED_DOC_STRING)
c6045832
JB
288 return Qnil;
289 tem = XVECTOR (fun)->contents[COMPILED_DOC_STRING];
e6d12642 290 if (STRINGP (tem))
ee04dc54 291 doc = tem;
700ea809 292 else if (NATNUMP (tem) || CONSP (tem))
ba29c3c9 293 doc = get_doc_string (tem, 1);
ee04dc54
RM
294 else
295 return Qnil;
5b5f6883
KH
296 }
297 else if (STRINGP (fun) || VECTORP (fun))
298 {
c6045832 299 return build_string ("Keyboard macro.");
5b5f6883
KH
300 }
301 else if (CONSP (fun))
302 {
c6045832 303 funcar = Fcar (fun);
e6d12642 304 if (!SYMBOLP (funcar))
c6045832 305 return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
502ddf23 306 else if (EQ (funcar, Qkeymap))
c6045832
JB
307 return build_string ("Prefix command (definition is a keymap associating keystrokes with\n\
308subcommands.)");
502ddf23
JB
309 else if (EQ (funcar, Qlambda)
310 || EQ (funcar, Qautoload))
c6045832 311 {
ae44f7a4
RS
312 Lisp_Object tem1;
313 tem1 = Fcdr (Fcdr (fun));
314 tem = Fcar (tem1);
e6d12642 315 if (STRINGP (tem))
ee04dc54 316 doc = tem;
ae44f7a4
RS
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))
ba29c3c9 321 doc = get_doc_string (tem, 1);
ee04dc54
RM
322 else
323 return Qnil;
c6045832 324 }
502ddf23 325 else if (EQ (funcar, Qmocklisp))
c6045832 326 return Qnil;
502ddf23 327 else if (EQ (funcar, Qmacro))
ee04dc54 328 return Fdocumentation (Fcdr (fun), raw);
5b5f6883
KH
329 else
330 goto oops;
331 }
332 else
333 {
334 oops:
335 Fsignal (Qinvalid_function, Fcons (fun, Qnil));
c6045832 336 }
ee04dc54 337
956ace37 338 if (NILP (raw))
665d3046
JB
339 {
340 struct gcpro gcpro1;
341
342 GCPRO1 (doc);
343 doc = Fsubstitute_command_keys (doc);
344 UNGCPRO;
345 }
ee04dc54 346 return doc;
c6045832
JB
347}
348
b6c53774 349DEFUN ("documentation-property", Fdocumentation_property, Sdocumentation_property, 2, 3, 0,
c6045832 350 "Return the documentation string that is SYMBOL's PROP property.\n\
ee04dc54 351This is like `get', but it can refer to strings stored in the\n\
08564963 352`etc/DOC' file; and if the value is a string, it is passed through\n\
4acb738e 353`substitute-command-keys'. A non-nil third argument RAW avoids this\n\
956ace37 354translation.")
4acb738e
EN
355 (symbol, prop, raw)
356 Lisp_Object symbol, prop, raw;
c6045832
JB
357{
358 register Lisp_Object tem;
359
4acb738e 360 tem = Fget (symbol, prop);
e6d12642 361 if (INTEGERP (tem))
ba29c3c9 362 tem = get_doc_string (XINT (tem) > 0 ? tem : make_number (- XINT (tem)), 0);
700ea809 363 else if (CONSP (tem))
ba29c3c9 364 tem = get_doc_string (tem, 0);
e6d12642 365 if (NILP (raw) && STRINGP (tem))
992d176e
RS
366 return Fsubstitute_command_keys (tem);
367 return tem;
c6045832
JB
368}
369\f
283e1184
JB
370/* Scanning the DOC files and placing docstring offsets into functions. */
371
372static void
373store_function_docstring (fun, offset)
374 Lisp_Object fun;
e343d389
RS
375 /* Use EMACS_INT because we get this from pointer subtraction. */
376 EMACS_INT offset;
283e1184
JB
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. */
e6d12642 383 if (SUBRP (fun))
283e1184
JB
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));
e6d12642 395 if (CONSP (tem) && INTEGERP (XCONS (tem)->car))
2ee7863a 396 XSETFASTINT (XCONS (tem)->car, offset);
283e1184
JB
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. */
e6d12642 403 else if (COMPILEDP (fun))
283e1184
JB
404 {
405 /* This bytecode object must have a slot for the
406 docstring, since we've found a docstring for it. */
f9b4aacf 407 if ((XVECTOR (fun)->size & PSEUDOVECTOR_SIZE_MASK) > COMPILED_DOC_STRING)
2ee7863a 408 XSETFASTINT (XVECTOR (fun)->contents[COMPILED_DOC_STRING], offset);
283e1184
JB
409 }
410}
411
412
c6045832
JB
413DEFUN ("Snarf-documentation", Fsnarf_documentation, Ssnarf_documentation,
414 1, 1, 0,
415 "Used during Emacs initialization, before dumping runnable Emacs,\n\
08564963 416to find pointers to doc strings stored in `etc/DOC...' and\n\
c6045832
JB
417record them in function definitions.\n\
418One arg, FILENAME, a string which does not include a directory.\n\
08564963 419The file is found in `../etc' now; found in the `data-directory'\n\
c6045832
JB
420when 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
2bda628c
JB
433#ifndef CANNOT_DUMP
434 if (NILP (Vpurify_flag))
435 error ("Snarf-documentation can only be called in an undumped Emacs");
436#endif
437
c6045832
JB
438 CHECK_STRING (filename, 0);
439
440#ifndef CANNOT_DUMP
6367dc09 441 name = (char *) alloca (XSTRING (filename)->size + 14);
08564963 442 strcpy (name, "../etc/");
c6045832 443#else /* CANNOT_DUMP */
ba870521 444 CHECK_STRING (Vdoc_directory, 0);
c6045832 445 name = (char *) alloca (XSTRING (filename)->size +
ba870521
KH
446 XSTRING (Vdoc_directory)->size + 1);
447 strcpy (name, XSTRING (Vdoc_directory)->data);
c6045832
JB
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');
141199d1
RS
489 sym = oblookup (Vobarray, p + 2,
490 multibyte_chars_in_text (p + 2, end - p - 2),
491 end - p - 2);
e6d12642 492 if (SYMBOLP (sym))
c6045832
JB
493 {
494 /* Attach a docstring to a variable? */
495 if (p[1] == 'V')
496 {
497 /* Install file-position as variable-documentation property
498 and make it negative for a user-variable
499 (doc starts with a `*'). */
500 Fput (sym, Qvariable_documentation,
501 make_number ((pos + end + 1 - buf)
502 * (end[1] == '*' ? -1 : 1)));
503 }
504
283e1184 505 /* Attach a docstring to a function? */
c6045832 506 else if (p[1] == 'F')
283e1184
JB
507 store_function_docstring (sym, pos + end + 1 - buf);
508
509 else
510 error ("DOC file invalid at position %d", pos);
c6045832
JB
511 }
512 }
513 pos += end - buf;
514 filled -= end - buf;
515 bcopy (end, buf, filled);
516 }
517 close (fd);
518 return Qnil;
519}
520\f
521DEFUN ("substitute-command-keys", Fsubstitute_command_keys,
522 Ssubstitute_command_keys, 1, 1, 0,
523 "Substitute key descriptions for command names in STRING.\n\
524Return a new string which is STRING with substrings of the form \\=\\[COMMAND]\n\
525replaced by either: a keystroke sequence that will invoke COMMAND,\n\
526or \"M-x COMMAND\" if COMMAND is not on any keys.\n\
527Substrings of the form \\=\\{MAPVAR} are replaced by summaries\n\
528\(made by describe-bindings) of the value of MAPVAR, taken as a keymap.\n\
529Substrings of the form \\=\\<MAPVAR> specify to use the value of MAPVAR\n\
530as the keymap for future \\=\\[COMMAND] substrings.\n\
531\\=\\= quotes the following character and is discarded;\n\
532thus, \\=\\=\\=\\= puts \\=\\= into the output, and \\=\\=\\=\\[ puts \\=\\[ into the output.")
4acb738e
EN
533 (string)
534 Lisp_Object string;
c6045832
JB
535{
536 unsigned char *buf;
537 int changed = 0;
538 register unsigned char *strp;
539 register unsigned char *bufp;
540 int idx;
541 int bsize;
542 unsigned char *new;
665d3046 543 Lisp_Object tem;
c6045832
JB
544 Lisp_Object keymap;
545 unsigned char *start;
2d0aa229 546 int length, length_byte;
665d3046
JB
547 Lisp_Object name;
548 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
141199d1
RS
549 int multibyte;
550 int nchars;
c6045832 551
4acb738e 552 if (NILP (string))
c6045832
JB
553 return Qnil;
554
4acb738e 555 CHECK_STRING (string, 0);
665d3046
JB
556 tem = Qnil;
557 keymap = Qnil;
558 name = Qnil;
4acb738e 559 GCPRO4 (string, tem, keymap, name);
c6045832 560
141199d1
RS
561 multibyte = STRING_MULTIBYTE (string);
562 nchars = 0;
563
9a425dcb
RS
564 /* KEYMAP is either nil (which means search all the active keymaps)
565 or a specified local map (which means search just that and the
566 global map). If non-nil, it might come from Voverriding_local_map,
4acb738e 567 or from a \\<mapname> construct in STRING itself.. */
f73d1163
KH
568 keymap = current_kboard->Voverriding_terminal_local_map;
569 if (NILP (keymap))
570 keymap = Voverriding_local_map;
c6045832 571
fc932ac6 572 bsize = STRING_BYTES (XSTRING (string));
c6045832
JB
573 bufp = buf = (unsigned char *) xmalloc (bsize);
574
4acb738e 575 strp = (unsigned char *) XSTRING (string)->data;
fc932ac6 576 while (strp < XSTRING (string)->data + STRING_BYTES (XSTRING (string)))
c6045832
JB
577 {
578 if (strp[0] == '\\' && strp[1] == '=')
579 {
580 /* \= quotes the next character;
581 thus, to put in \[ without its special meaning, use \=\[. */
582 changed = 1;
141199d1
RS
583 strp += 2;
584 if (multibyte)
585 {
586 int len;
fc932ac6 587 int maxlen = XSTRING (string)->data + STRING_BYTES (XSTRING (string)) - strp;
141199d1
RS
588
589 STRING_CHAR_AND_LENGTH (strp, maxlen, len);
590 if (len == 1)
591 *bufp = *strp;
592 else
593 bcopy (strp, bufp, len);
594 strp += len;
595 bufp += len;
596 nchars++;
597 }
598 else
599 *bufp++ = *strp++, nchars++;
c6045832
JB
600 }
601 else if (strp[0] == '\\' && strp[1] == '[')
602 {
b6c53774
RS
603 Lisp_Object firstkey;
604
c6045832
JB
605 changed = 1;
606 strp += 2; /* skip \[ */
607 start = strp;
608
4acb738e 609 while ((strp - (unsigned char *) XSTRING (string)->data
fc932ac6 610 < STRING_BYTES (XSTRING (string)))
c6045832
JB
611 && *strp != ']')
612 strp++;
141199d1
RS
613 length_byte = strp - start;
614
c6045832
JB
615 strp++; /* skip ] */
616
617 /* Save STRP in IDX. */
4acb738e 618 idx = strp - (unsigned char *) XSTRING (string)->data;
141199d1 619 tem = Fintern (make_string (start, length_byte), Qnil);
9a425dcb 620 tem = Fwhere_is_internal (tem, keymap, Qt, Qnil);
c6045832 621
b6c53774
RS
622 /* Disregard menu bar bindings; it is positively annoying to
623 mention them when there's no menu bar, and it isn't terribly
624 useful even when there is a menu bar. */
ef586bbd
RS
625 if (!NILP (tem))
626 {
627 firstkey = Faref (tem, make_number (0));
628 if (EQ (firstkey, Qmenu_bar))
629 tem = Qnil;
630 }
b6c53774 631
265a9e55 632 if (NILP (tem)) /* but not on any keys */
c6045832
JB
633 {
634 new = (unsigned char *) xrealloc (buf, bsize += 4);
635 bufp += new - buf;
636 buf = new;
637 bcopy ("M-x ", bufp, 4);
638 bufp += 4;
141199d1
RS
639 nchars += 4;
640 if (multibyte)
641 length = multibyte_chars_in_text (start, length_byte);
642 else
643 length = length_byte;
c6045832
JB
644 goto subst;
645 }
646 else
647 { /* function is on a key */
648 tem = Fkey_description (tem);
649 goto subst_string;
650 }
651 }
652 /* \{foo} is replaced with a summary of the keymap (symbol-value foo).
653 \<foo> just sets the keymap used for \[cmd]. */
654 else if (strp[0] == '\\' && (strp[1] == '{' || strp[1] == '<'))
655 {
656 struct buffer *oldbuf;
c6045832
JB
657
658 changed = 1;
659 strp += 2; /* skip \{ or \< */
660 start = strp;
661
4acb738e
EN
662 while ((strp - (unsigned char *) XSTRING (string)->data
663 < XSTRING (string)->size)
c6045832
JB
664 && *strp != '}' && *strp != '>')
665 strp++;
141199d1
RS
666
667 length_byte = strp - start;
c6045832
JB
668 strp++; /* skip } or > */
669
670 /* Save STRP in IDX. */
4acb738e 671 idx = strp - (unsigned char *) XSTRING (string)->data;
c6045832
JB
672
673 /* Get the value of the keymap in TEM, or nil if undefined.
674 Do this while still in the user's current buffer
675 in case it is a local variable. */
141199d1 676 name = Fintern (make_string (start, length_byte), Qnil);
c6045832 677 tem = Fboundp (name);
265a9e55 678 if (! NILP (tem))
c6045832
JB
679 {
680 tem = Fsymbol_value (name);
265a9e55 681 if (! NILP (tem))
665d3046 682 tem = get_keymap_1 (tem, 0, 1);
c6045832
JB
683 }
684
685 /* Now switch to a temp buffer. */
686 oldbuf = current_buffer;
687 set_buffer_internal (XBUFFER (Vprin1_to_string_buffer));
688
265a9e55 689 if (NILP (tem))
c6045832
JB
690 {
691 name = Fsymbol_name (name);
692 insert_string ("\nUses keymap \"");
141199d1
RS
693 insert_from_string (name, 0, 0,
694 XSTRING (name)->size,
fc932ac6 695 STRING_BYTES (XSTRING (name)), 1);
c6045832
JB
696 insert_string ("\", which is not currently defined.\n");
697 if (start[-1] == '<') keymap = Qnil;
698 }
699 else if (start[-1] == '<')
700 keymap = tem;
701 else
7523d17c 702 describe_map_tree (tem, 1, Qnil, Qnil, (char *)0, 1, 0, 0);
c6045832
JB
703 tem = Fbuffer_string ();
704 Ferase_buffer ();
705 set_buffer_internal (oldbuf);
706
707 subst_string:
708 start = XSTRING (tem)->data;
709 length = XSTRING (tem)->size;
fc932ac6 710 length_byte = STRING_BYTES (XSTRING (tem));
c6045832 711 subst:
141199d1 712 new = (unsigned char *) xrealloc (buf, bsize += length_byte);
c6045832
JB
713 bufp += new - buf;
714 buf = new;
141199d1
RS
715 bcopy (start, bufp, length_byte);
716 bufp += length_byte;
717 nchars += length;
4acb738e
EN
718 /* Check STRING again in case gc relocated it. */
719 strp = (unsigned char *) XSTRING (string)->data + idx;
c6045832 720 }
141199d1
RS
721 else if (! multibyte) /* just copy other chars */
722 *bufp++ = *strp++, nchars++;
723 else
724 {
725 int len;
fc932ac6 726 int maxlen = XSTRING (string)->data + STRING_BYTES (XSTRING (string)) - strp;
141199d1
RS
727
728 STRING_CHAR_AND_LENGTH (strp, maxlen, len);
729 if (len == 1)
730 *bufp = *strp;
731 else
732 bcopy (strp, bufp, len);
733 strp += len;
734 bufp += len;
735 nchars++;
736 }
c6045832
JB
737 }
738
739 if (changed) /* don't bother if nothing substituted */
cc5bf9eb 740 tem = make_string_from_bytes (buf, nchars, bufp - buf);
c6045832 741 else
4acb738e 742 tem = string;
9ac0d9e0 743 xfree (buf);
665d3046 744 RETURN_UNGCPRO (tem);
c6045832
JB
745}
746\f
dfcf069d 747void
c6045832
JB
748syms_of_doc ()
749{
750 DEFVAR_LISP ("internal-doc-file-name", &Vdoc_file_name,
751 "Name of file containing documentation strings of built-in symbols.");
752 Vdoc_file_name = Qnil;
753
754 defsubr (&Sdocumentation);
755 defsubr (&Sdocumentation_property);
756 defsubr (&Ssnarf_documentation);
757 defsubr (&Ssubstitute_command_keys);
758}