(customize-face-other-window): Make it work similarly.
[bpt/emacs.git] / src / doc.c
CommitLineData
c6045832 1/* Record indices of function doc strings stored in a file.
32d1b897 2 Copyright (C) 1985, 86,93,94,95,97,98,99, 2000 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"
8feddab4 43#include "keymap.h"
c6045832 44
8892f40b
GM
45#ifdef HAVE_INDEX
46extern char *index P_ ((const char *, int));
a847af86
GM
47#endif
48
512ca171 49Lisp_Object Vdoc_file_name;
c6045832 50
9191c8ae
GM
51Lisp_Object Qfunction_documentation;
52
9a425dcb
RS
53extern Lisp_Object Voverriding_local_map;
54
778d47c7
RS
55/* For VMS versions with limited file name syntax,
56 convert the name to something VMS will allow. */
57static void
58munge_doc_file_name (name)
59 char *name;
c6045832 60{
c6045832
JB
61#ifdef VMS
62#ifndef VMS4_4
63 /* For VMS versions with limited file name syntax,
64 convert the name to something VMS will allow. */
65 p = name;
66 while (*p)
67 {
68 if (*p == '-')
69 *p = '_';
70 p++;
71 }
72#endif /* not VMS4_4 */
73#ifdef VMS4_4
74 strcpy (name, sys_translate_unix (name));
75#endif /* VMS4_4 */
76#endif /* VMS */
778d47c7
RS
77}
78
912e8480
RS
79/* Buffer used for reading from documentation file. */
80static char *get_doc_string_buffer;
81static int get_doc_string_buffer_size;
82
32caae30 83static unsigned char *read_bytecode_pointer;
87afdd65 84Lisp_Object Fsnarf_documentation P_ ((Lisp_Object));
32caae30
RS
85
86/* readchar in lread.c calls back here to fetch the next byte.
87 If UNREADFLAG is 1, we unread a byte. */
88
89int
90read_bytecode_char (unreadflag)
33711cc9 91 int unreadflag;
32caae30
RS
92{
93 if (unreadflag)
94 {
95 read_bytecode_pointer--;
96 return 0;
97 }
98 return *read_bytecode_pointer++;
99}
100
700ea809
RS
101/* Extract a doc string from a file. FILEPOS says where to get it.
102 If it is an integer, use that position in the standard DOC-... file.
103 If it is (FILE . INTEGER), use FILE as the file name
0c00bc70
RS
104 and INTEGER as the position in that file.
105 But if INTEGER is negative, make it positive.
106 (A negative integer is used for user variables, so we can distinguish
ba29c3c9
RS
107 them without actually fetching the doc string.)
108
a154a4ef
SM
109 If the location does not point to the beginning of a docstring
110 (e.g. because the file has been modified and the location is stale),
111 return nil.
112
e96179b3
RS
113 If UNIBYTE is nonzero, always make a unibyte string.
114
f1df0d67
RS
115 If DEFINITION is nonzero, assume this is for reading
116 a dynamic function definition; convert the bytestring
117 and the constants vector with appropriate byte handling,
118 and return a cons cell. */
700ea809 119
0c3debbc 120Lisp_Object
e96179b3 121get_doc_string (filepos, unibyte, definition)
700ea809 122 Lisp_Object filepos;
e96179b3 123 int unibyte, definition;
778d47c7 124{
700ea809 125 char *from, *to;
778d47c7
RS
126 register int fd;
127 register char *name;
128 register char *p, *p1;
778d47c7 129 int minsize;
0fded513 130 int offset, position;
700ea809 131 Lisp_Object file, tem;
778d47c7 132
700ea809
RS
133 if (INTEGERP (filepos))
134 {
135 file = Vdoc_file_name;
136 position = XINT (filepos);
137 }
138 else if (CONSP (filepos))
139 {
03699b14
KR
140 file = XCAR (filepos);
141 position = XINT (XCDR (filepos));
700ea809
RS
142 }
143 else
778d47c7
RS
144 return Qnil;
145
87afdd65
SM
146 if (position < 0)
147 position = - position;
148
700ea809
RS
149 if (!STRINGP (Vdoc_directory))
150 return Qnil;
151
152 if (!STRINGP (file))
153 return Qnil;
154
155 /* Put the file name in NAME as a C string.
156 If it is relative, combine it with Vdoc_directory. */
157
158 tem = Ffile_name_absolute_p (file);
159 if (NILP (tem))
160 {
161 minsize = XSTRING (Vdoc_directory)->size;
162 /* sizeof ("../etc/") == 8 */
163 if (minsize < 8)
164 minsize = 8;
165 name = (char *) alloca (minsize + XSTRING (file)->size + 8);
166 strcpy (name, XSTRING (Vdoc_directory)->data);
167 strcat (name, XSTRING (file)->data);
168 munge_doc_file_name (name);
169 }
170 else
171 {
a039f534 172 name = (char *) XSTRING (file)->data;
700ea809 173 }
c6045832 174
68c45bf0 175 fd = emacs_open (name, O_RDONLY, 0);
c6045832 176 if (fd < 0)
778d47c7
RS
177 {
178#ifndef CANNOT_DUMP
179 if (!NILP (Vpurify_flag))
180 {
181 /* Preparing to dump; DOC file is probably not installed.
182 So check in ../etc. */
183 strcpy (name, "../etc/");
700ea809 184 strcat (name, XSTRING (file)->data);
778d47c7
RS
185 munge_doc_file_name (name);
186
68c45bf0 187 fd = emacs_open (name, O_RDONLY, 0);
778d47c7
RS
188 }
189#endif
778d47c7
RS
190 if (fd < 0)
191 error ("Cannot open doc string file \"%s\"", name);
192 }
193
0fded513 194 /* Seek only to beginning of disk block. */
a154a4ef
SM
195 /* Make sure we read at least 1024 bytes before `position'
196 so we can check the leading text for consistency. */
197 offset = min (position, max (1024, position % (8 * 1024)));
0fded513 198 if (0 > lseek (fd, position - offset, 0))
c6045832 199 {
68c45bf0 200 emacs_close (fd);
c6045832 201 error ("Position %ld out of range in doc string file \"%s\"",
700ea809 202 position, name);
c6045832 203 }
700ea809 204
912e8480
RS
205 /* Read the doc string into get_doc_string_buffer.
206 P points beyond the data just read. */
0fded513 207
912e8480 208 p = get_doc_string_buffer;
700ea809 209 while (1)
c6045832 210 {
912e8480
RS
211 int space_left = (get_doc_string_buffer_size
212 - (p - get_doc_string_buffer));
700ea809
RS
213 int nread;
214
0fded513 215 /* Allocate or grow the buffer if we need to. */
700ea809
RS
216 if (space_left == 0)
217 {
912e8480
RS
218 int in_buffer = p - get_doc_string_buffer;
219 get_doc_string_buffer_size += 16 * 1024;
220 get_doc_string_buffer
221 = (char *) xrealloc (get_doc_string_buffer,
222 get_doc_string_buffer_size + 1);
223 p = get_doc_string_buffer + in_buffer;
224 space_left = (get_doc_string_buffer_size
225 - (p - get_doc_string_buffer));
700ea809
RS
226 }
227
0fded513
RS
228 /* Read a disk block at a time.
229 If we read the same block last time, maybe skip this? */
700ea809
RS
230 if (space_left > 1024 * 8)
231 space_left = 1024 * 8;
68c45bf0 232 nread = emacs_read (fd, p, space_left);
700ea809
RS
233 if (nread < 0)
234 {
68c45bf0 235 emacs_close (fd);
700ea809
RS
236 error ("Read error on documentation file");
237 }
238 p[nread] = 0;
239 if (!nread)
c6045832 240 break;
912e8480 241 if (p == get_doc_string_buffer)
a847af86 242 p1 = (char *) index (p + offset, '\037');
0fded513 243 else
a847af86 244 p1 = (char *) index (p, '\037');
c6045832
JB
245 if (p1)
246 {
247 *p1 = 0;
248 p = p1;
249 break;
250 }
700ea809 251 p += nread;
c6045832 252 }
68c45bf0 253 emacs_close (fd);
700ea809 254
a154a4ef
SM
255 /* Sanity checking. */
256 if (CONSP (filepos))
257 {
258 int test = 1;
259 if (get_doc_string_buffer[offset - test++] != ' ')
260 return Qnil;
261 while (get_doc_string_buffer[offset - test] >= '0'
262 && get_doc_string_buffer[offset - test] <= '9')
263 test++;
264 if (get_doc_string_buffer[offset - test++] != '@'
265 || get_doc_string_buffer[offset - test] != '#')
266 return Qnil;
267 }
268 else
269 {
270 int test = 1;
271 if (get_doc_string_buffer[offset - test++] != '\n')
272 return Qnil;
273 while (get_doc_string_buffer[offset - test] > ' ')
274 test++;
275 if (get_doc_string_buffer[offset - test] != '\037')
276 return Qnil;
277 }
278
700ea809
RS
279 /* Scan the text and perform quoting with ^A (char code 1).
280 ^A^A becomes ^A, ^A0 becomes a null char, and ^A_ becomes a ^_. */
912e8480
RS
281 from = get_doc_string_buffer + offset;
282 to = get_doc_string_buffer + offset;
700ea809
RS
283 while (from != p)
284 {
285 if (*from == 1)
286 {
287 int c;
288
289 from++;
290 c = *from++;
291 if (c == 1)
292 *to++ = c;
293 else if (c == '0')
294 *to++ = 0;
295 else if (c == '_')
296 *to++ = 037;
297 else
298 error ("Invalid data in documentation file -- ^A followed by code 0%o", c);
299 }
300 else
301 *to++ = *from++;
302 }
303
32caae30
RS
304 /* If DEFINITION, read from this buffer
305 the same way we would read bytes from a file. */
f1df0d67
RS
306 if (definition)
307 {
32caae30
RS
308 read_bytecode_pointer = get_doc_string_buffer + offset;
309 return Fread (Qlambda);
f1df0d67
RS
310 }
311
e96179b3
RS
312 if (unibyte)
313 return make_unibyte_string (get_doc_string_buffer + offset,
314 to - (get_doc_string_buffer + offset));
315 else
fb2fdea7
RS
316 {
317 /* Let the data determine whether the string is multibyte,
318 even if Emacs is running in --unibyte mode. */
319 int nchars = multibyte_chars_in_text (get_doc_string_buffer + offset,
320 to - (get_doc_string_buffer + offset));
321 return make_string_from_bytes (get_doc_string_buffer + offset,
322 nchars,
323 to - (get_doc_string_buffer + offset));
324 }
700ea809
RS
325}
326
327/* Get a string from position FILEPOS and pass it through the Lisp reader.
328 We use this for fetching the bytecode string and constants vector
329 of a compiled function from the .elc file. */
330
331Lisp_Object
332read_doc_string (filepos)
333 Lisp_Object filepos;
334{
e96179b3 335 return get_doc_string (filepos, 0, 1);
c6045832
JB
336}
337
e5aa79fa 338static int
a154a4ef 339reread_doc_file (file)
04fb8283 340 Lisp_Object file;
a154a4ef
SM
341{
342 Lisp_Object reply, prompt[3];
343 struct gcpro gcpro1;
344 GCPRO1 (file);
345 prompt[0] = build_string ("File ");
346 prompt[1] = NILP (file) ? Vdoc_file_name : file;
347 prompt[2] = build_string (" is out-of-sync. Reload? ");
348 reply = Fy_or_n_p (Fconcat (3, prompt));
349 UNGCPRO;
350 if (NILP (reply))
e5aa79fa 351 return 0;
a154a4ef
SM
352
353 if (NILP (file))
354 Fsnarf_documentation (Vdoc_file_name);
355 else
356 Fload (file, Qt, Qt, Qt, Qnil);
e5aa79fa
SM
357
358 return 1;
a154a4ef
SM
359}
360
ee04dc54 361DEFUN ("documentation", Fdocumentation, Sdocumentation, 1, 2, 0,
335c5470
PJ
362 doc: /* Return the documentation string of FUNCTION.
363Unless a non-nil second argument RAW is given, the
364string is passed through `substitute-command-keys'. */)
365 (function, raw)
502ddf23 366 Lisp_Object function, raw;
c6045832
JB
367{
368 Lisp_Object fun;
369 Lisp_Object funcar;
ee04dc54 370 Lisp_Object tem, doc;
e5aa79fa
SM
371 int try_reload = 1;
372
373 documentation:
c6045832 374
8d17fe0b
GM
375 doc = Qnil;
376
9191c8ae
GM
377 if (SYMBOLP (function)
378 && (tem = Fget (function, Qfunction_documentation),
379 !NILP (tem)))
380 return Fdocumentation_property (function, Qfunction_documentation, raw);
381
502ddf23 382 fun = Findirect_function (function);
5b5f6883 383 if (SUBRP (fun))
c6045832 384 {
9191c8ae
GM
385 if (XSUBR (fun)->doc == 0)
386 return Qnil;
387 else if ((EMACS_INT) XSUBR (fun)->doc >= 0)
ee04dc54 388 doc = build_string (XSUBR (fun)->doc);
c6045832 389 else
87afdd65 390 doc = make_number ((EMACS_INT) XSUBR (fun)->doc);
5b5f6883
KH
391 }
392 else if (COMPILEDP (fun))
393 {
87afdd65 394 if ((ASIZE (fun) & PSEUDOVECTOR_SIZE_MASK) <= COMPILED_DOC_STRING)
c6045832 395 return Qnil;
87afdd65 396 tem = AREF (fun, COMPILED_DOC_STRING);
e6d12642 397 if (STRINGP (tem))
ee04dc54 398 doc = tem;
700ea809 399 else if (NATNUMP (tem) || CONSP (tem))
87afdd65 400 doc = tem;
ee04dc54
RM
401 else
402 return Qnil;
5b5f6883
KH
403 }
404 else if (STRINGP (fun) || VECTORP (fun))
405 {
c6045832 406 return build_string ("Keyboard macro.");
5b5f6883
KH
407 }
408 else if (CONSP (fun))
409 {
c6045832 410 funcar = Fcar (fun);
e6d12642 411 if (!SYMBOLP (funcar))
c6045832 412 return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
502ddf23 413 else if (EQ (funcar, Qkeymap))
a3cec380 414 return build_string ("Prefix command (definition is a keymap associating keystrokes with commands).");
502ddf23
JB
415 else if (EQ (funcar, Qlambda)
416 || EQ (funcar, Qautoload))
c6045832 417 {
ae44f7a4
RS
418 Lisp_Object tem1;
419 tem1 = Fcdr (Fcdr (fun));
420 tem = Fcar (tem1);
e6d12642 421 if (STRINGP (tem))
ee04dc54 422 doc = tem;
ae44f7a4
RS
423 /* Handle a doc reference--but these never come last
424 in the function body, so reject them if they are last. */
87afdd65
SM
425 else if ((NATNUMP (tem) || (CONSP (tem) && INTEGERP (XCDR (tem))))
426 && !NILP (XCDR (tem1)))
427 doc = tem;
ee04dc54
RM
428 else
429 return Qnil;
c6045832 430 }
502ddf23 431 else if (EQ (funcar, Qmacro))
ee04dc54 432 return Fdocumentation (Fcdr (fun), raw);
5b5f6883
KH
433 else
434 goto oops;
435 }
436 else
437 {
438 oops:
439 Fsignal (Qinvalid_function, Fcons (fun, Qnil));
c6045832 440 }
ee04dc54 441
e5aa79fa
SM
442 /* If DOC is 0, it's typically because of a dumped file missing
443 from the DOC file (bug in src/Makefile.in). */
71cdb109
SM
444 if (EQ (doc, make_number (0)))
445 doc = Qnil;
68d8a8e1 446 if (INTEGERP (doc) || CONSP (doc))
a154a4ef
SM
447 {
448 Lisp_Object tem;
449 tem = get_doc_string (doc, 0, 0);
e5aa79fa 450 if (NILP (tem) && try_reload)
a154a4ef
SM
451 {
452 /* The file is newer, we need to reset the pointers. */
453 struct gcpro gcpro1, gcpro2;
454 GCPRO2 (function, raw);
e5aa79fa 455 try_reload = reread_doc_file (Fcar_safe (doc));
a154a4ef 456 UNGCPRO;
e5aa79fa
SM
457 if (try_reload)
458 {
459 try_reload = 0;
460 goto documentation;
461 }
a154a4ef
SM
462 }
463 else
464 doc = tem;
465 }
87afdd65 466
956ace37 467 if (NILP (raw))
441d75e5 468 doc = Fsubstitute_command_keys (doc);
ee04dc54 469 return doc;
c6045832
JB
470}
471
f6ee1260
GM
472DEFUN ("documentation-property", Fdocumentation_property,
473 Sdocumentation_property, 2, 3, 0,
335c5470
PJ
474 doc: /* Return the documentation string that is SYMBOL's PROP property.
475Third argument RAW omitted or nil means pass the result through
476`substitute-command-keys' if it is a string.
477
478This differs from `get' in that it can refer to strings stored in the
479`etc/DOC' file; and that it evaluates documentation properties that
480aren't strings. */)
4acb738e
EN
481 (symbol, prop, raw)
482 Lisp_Object symbol, prop, raw;
c6045832 483{
e5aa79fa 484 int try_reload = 1;
2f0b74ea 485 Lisp_Object tem;
c6045832 486
e5aa79fa
SM
487 documentation_property:
488
4acb738e 489 tem = Fget (symbol, prop);
44766095 490 if (EQ (tem, make_number (0)))
71cdb109 491 tem = Qnil;
68d8a8e1 492 if (INTEGERP (tem) || (CONSP (tem) && INTEGERP (XCDR (tem))))
a154a4ef
SM
493 {
494 Lisp_Object doc = tem;
495 tem = get_doc_string (tem, 0, 0);
e5aa79fa 496 if (NILP (tem) && try_reload)
a154a4ef
SM
497 {
498 /* The file is newer, we need to reset the pointers. */
499 struct gcpro gcpro1, gcpro2, gcpro3;
500 GCPRO3 (symbol, prop, raw);
e5aa79fa 501 try_reload = reread_doc_file (Fcar_safe (doc));
a154a4ef 502 UNGCPRO;
e5aa79fa
SM
503 if (try_reload)
504 {
505 try_reload = 0;
506 goto documentation_property;
507 }
a154a4ef
SM
508 }
509 }
f6ee1260
GM
510 else if (!STRINGP (tem))
511 /* Feval protects its argument. */
512 tem = Feval (tem);
513
e6d12642 514 if (NILP (raw) && STRINGP (tem))
bbd7d5d3 515 tem = Fsubstitute_command_keys (tem);
992d176e 516 return tem;
c6045832
JB
517}
518\f
283e1184
JB
519/* Scanning the DOC files and placing docstring offsets into functions. */
520
521static void
522store_function_docstring (fun, offset)
523 Lisp_Object fun;
e343d389
RS
524 /* Use EMACS_INT because we get this from pointer subtraction. */
525 EMACS_INT offset;
283e1184
JB
526{
527 fun = indirect_function (fun);
528
529 /* The type determines where the docstring is stored. */
530
531 /* Lisp_Subrs have a slot for it. */
e6d12642 532 if (SUBRP (fun))
283e1184
JB
533 XSUBR (fun)->doc = (char *) - offset;
534
535 /* If it's a lisp form, stick it in the form. */
536 else if (CONSP (fun))
537 {
538 Lisp_Object tem;
539
03699b14 540 tem = XCAR (fun);
283e1184
JB
541 if (EQ (tem, Qlambda) || EQ (tem, Qautoload))
542 {
543 tem = Fcdr (Fcdr (fun));
03699b14 544 if (CONSP (tem) && INTEGERP (XCAR (tem)))
f3fbd155 545 XSETCARFASTINT (tem, offset);
283e1184
JB
546 }
547 else if (EQ (tem, Qmacro))
03699b14 548 store_function_docstring (XCDR (fun), offset);
283e1184
JB
549 }
550
551 /* Bytecode objects sometimes have slots for it. */
e6d12642 552 else if (COMPILEDP (fun))
283e1184
JB
553 {
554 /* This bytecode object must have a slot for the
555 docstring, since we've found a docstring for it. */
87afdd65
SM
556 if ((ASIZE (fun) & PSEUDOVECTOR_SIZE_MASK) > COMPILED_DOC_STRING)
557 XSETFASTINT (AREF (fun, COMPILED_DOC_STRING), offset);
283e1184
JB
558 }
559}
560
561
c6045832 562DEFUN ("Snarf-documentation", Fsnarf_documentation, Ssnarf_documentation,
335c5470 563 1, 1, 0,
f4e25f94
RS
564 doc: /* Used during Emacs initialization to scan the `etc/DOC...' file.
565This searches the `etc/DOC...' file for doc strings and
566records them in function and variable definitions.
567The function takes one argument, FILENAME, a string;
568it specifies the file name (without a directory) of the DOC file.
569That file is found in `../etc' now; later, when the dumped Emacs is run,
570the same file name is found in the `data-directory'. */)
335c5470 571 (filename)
c6045832
JB
572 Lisp_Object filename;
573{
574 int fd;
575 char buf[1024 + 1];
576 register int filled;
577 register int pos;
578 register char *p, *end;
24109c98 579 Lisp_Object sym;
c6045832 580 char *name;
c6045832 581
b7826503 582 CHECK_STRING (filename);
c6045832 583
a154a4ef 584 if
c6045832 585#ifndef CANNOT_DUMP
a154a4ef 586 (!NILP (Vpurify_flag))
c6045832 587#else /* CANNOT_DUMP */
a154a4ef 588 (0)
c6045832 589#endif /* CANNOT_DUMP */
a154a4ef
SM
590 {
591 name = (char *) alloca (XSTRING (filename)->size + 14);
592 strcpy (name, "../etc/");
593 }
594 else
595 {
596 CHECK_STRING (Vdoc_directory);
597 name = (char *) alloca (XSTRING (filename)->size
598 + XSTRING (Vdoc_directory)->size + 1);
599 strcpy (name, XSTRING (Vdoc_directory)->data);
600 }
c6045832
JB
601 strcat (name, XSTRING (filename)->data); /*** Add this line ***/
602#ifdef VMS
603#ifndef VMS4_4
604 /* For VMS versions with limited file name syntax,
605 convert the name to something VMS will allow. */
606 p = name;
607 while (*p)
608 {
609 if (*p == '-')
610 *p = '_';
611 p++;
612 }
613#endif /* not VMS4_4 */
614#ifdef VMS4_4
615 strcpy (name, sys_translate_unix (name));
616#endif /* VMS4_4 */
617#endif /* VMS */
618
68c45bf0 619 fd = emacs_open (name, O_RDONLY, 0);
c6045832
JB
620 if (fd < 0)
621 report_file_error ("Opening doc string file",
622 Fcons (build_string (name), Qnil));
623 Vdoc_file_name = filename;
624 filled = 0;
625 pos = 0;
626 while (1)
627 {
628 if (filled < 512)
68c45bf0 629 filled += emacs_read (fd, &buf[filled], sizeof buf - 1 - filled);
c6045832
JB
630 if (!filled)
631 break;
632
633 buf[filled] = 0;
634 p = buf;
635 end = buf + (filled < 512 ? filled : filled - 128);
636 while (p != end && *p != '\037') p++;
637 /* p points to ^_Ffunctionname\n or ^_Vvarname\n. */
638 if (p != end)
639 {
a847af86 640 end = (char *) index (p, '\n');
141199d1
RS
641 sym = oblookup (Vobarray, p + 2,
642 multibyte_chars_in_text (p + 2, end - p - 2),
643 end - p - 2);
e6d12642 644 if (SYMBOLP (sym))
c6045832
JB
645 {
646 /* Attach a docstring to a variable? */
647 if (p[1] == 'V')
648 {
649 /* Install file-position as variable-documentation property
650 and make it negative for a user-variable
651 (doc starts with a `*'). */
652 Fput (sym, Qvariable_documentation,
653 make_number ((pos + end + 1 - buf)
654 * (end[1] == '*' ? -1 : 1)));
655 }
656
283e1184 657 /* Attach a docstring to a function? */
c6045832 658 else if (p[1] == 'F')
283e1184
JB
659 store_function_docstring (sym, pos + end + 1 - buf);
660
661 else
662 error ("DOC file invalid at position %d", pos);
c6045832
JB
663 }
664 }
665 pos += end - buf;
666 filled -= end - buf;
667 bcopy (end, buf, filled);
668 }
68c45bf0 669 emacs_close (fd);
c6045832
JB
670 return Qnil;
671}
672\f
673DEFUN ("substitute-command-keys", Fsubstitute_command_keys,
335c5470
PJ
674 Ssubstitute_command_keys, 1, 1, 0,
675 doc: /* Substitute key descriptions for command names in STRING.
676Return a new string which is STRING with substrings of the form \\=\\[COMMAND]
677replaced by either: a keystroke sequence that will invoke COMMAND,
678or "M-x COMMAND" if COMMAND is not on any keys.
679Substrings of the form \\=\\{MAPVAR} are replaced by summaries
680\(made by describe-bindings) of the value of MAPVAR, taken as a keymap.
681Substrings of the form \\=\\<MAPVAR> specify to use the value of MAPVAR
682as the keymap for future \\=\\[COMMAND] substrings.
683\\=\\= quotes the following character and is discarded;
684thus, \\=\\=\\=\\= puts \\=\\= into the output, and \\=\\=\\=\\[ puts \\=\\[ into the output. */)
685 (string)
4acb738e 686 Lisp_Object string;
c6045832
JB
687{
688 unsigned char *buf;
689 int changed = 0;
690 register unsigned char *strp;
691 register unsigned char *bufp;
692 int idx;
693 int bsize;
665d3046 694 Lisp_Object tem;
c6045832
JB
695 Lisp_Object keymap;
696 unsigned char *start;
2d0aa229 697 int length, length_byte;
665d3046
JB
698 Lisp_Object name;
699 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
141199d1
RS
700 int multibyte;
701 int nchars;
c6045832 702
4acb738e 703 if (NILP (string))
c6045832
JB
704 return Qnil;
705
b7826503 706 CHECK_STRING (string);
665d3046
JB
707 tem = Qnil;
708 keymap = Qnil;
709 name = Qnil;
4acb738e 710 GCPRO4 (string, tem, keymap, name);
c6045832 711
141199d1
RS
712 multibyte = STRING_MULTIBYTE (string);
713 nchars = 0;
714
9a425dcb
RS
715 /* KEYMAP is either nil (which means search all the active keymaps)
716 or a specified local map (which means search just that and the
717 global map). If non-nil, it might come from Voverriding_local_map,
4acb738e 718 or from a \\<mapname> construct in STRING itself.. */
f73d1163
KH
719 keymap = current_kboard->Voverriding_terminal_local_map;
720 if (NILP (keymap))
721 keymap = Voverriding_local_map;
c6045832 722
fc932ac6 723 bsize = STRING_BYTES (XSTRING (string));
c6045832
JB
724 bufp = buf = (unsigned char *) xmalloc (bsize);
725
4acb738e 726 strp = (unsigned char *) XSTRING (string)->data;
fc932ac6 727 while (strp < XSTRING (string)->data + STRING_BYTES (XSTRING (string)))
c6045832
JB
728 {
729 if (strp[0] == '\\' && strp[1] == '=')
730 {
731 /* \= quotes the next character;
732 thus, to put in \[ without its special meaning, use \=\[. */
733 changed = 1;
141199d1
RS
734 strp += 2;
735 if (multibyte)
736 {
737 int len;
fc932ac6 738 int maxlen = XSTRING (string)->data + STRING_BYTES (XSTRING (string)) - strp;
141199d1
RS
739
740 STRING_CHAR_AND_LENGTH (strp, maxlen, len);
741 if (len == 1)
742 *bufp = *strp;
743 else
744 bcopy (strp, bufp, len);
745 strp += len;
746 bufp += len;
747 nchars++;
748 }
749 else
750 *bufp++ = *strp++, nchars++;
c6045832
JB
751 }
752 else if (strp[0] == '\\' && strp[1] == '[')
753 {
b6c53774 754 Lisp_Object firstkey;
11f9d6e1 755 int start_idx;
b6c53774 756
c6045832
JB
757 changed = 1;
758 strp += 2; /* skip \[ */
759 start = strp;
11f9d6e1 760 start_idx = start - XSTRING (string)->data;
c6045832 761
4acb738e 762 while ((strp - (unsigned char *) XSTRING (string)->data
fc932ac6 763 < STRING_BYTES (XSTRING (string)))
c6045832
JB
764 && *strp != ']')
765 strp++;
141199d1
RS
766 length_byte = strp - start;
767
c6045832
JB
768 strp++; /* skip ] */
769
770 /* Save STRP in IDX. */
4acb738e 771 idx = strp - (unsigned char *) XSTRING (string)->data;
141199d1 772 tem = Fintern (make_string (start, length_byte), Qnil);
11f9d6e1
GM
773
774 /* Note the Fwhere_is_internal can GC, so we have to take
775 relocation of string contents into account. */
4857ef58 776 tem = Fwhere_is_internal (tem, keymap, Qt, Qnil, Qnil);
11f9d6e1
GM
777 strp = XSTRING (string)->data + idx;
778 start = XSTRING (string)->data + start_idx;
c6045832 779
b6c53774
RS
780 /* Disregard menu bar bindings; it is positively annoying to
781 mention them when there's no menu bar, and it isn't terribly
782 useful even when there is a menu bar. */
ef586bbd
RS
783 if (!NILP (tem))
784 {
785 firstkey = Faref (tem, make_number (0));
786 if (EQ (firstkey, Qmenu_bar))
787 tem = Qnil;
788 }
b6c53774 789
265a9e55 790 if (NILP (tem)) /* but not on any keys */
c6045832 791 {
8d17fe0b
GM
792 int offset = bufp - buf;
793 buf = (unsigned char *) xrealloc (buf, bsize += 4);
794 bufp = buf + offset;
c6045832
JB
795 bcopy ("M-x ", bufp, 4);
796 bufp += 4;
141199d1
RS
797 nchars += 4;
798 if (multibyte)
799 length = multibyte_chars_in_text (start, length_byte);
800 else
801 length = length_byte;
c6045832
JB
802 goto subst;
803 }
804 else
805 { /* function is on a key */
806 tem = Fkey_description (tem);
807 goto subst_string;
808 }
809 }
810 /* \{foo} is replaced with a summary of the keymap (symbol-value foo).
811 \<foo> just sets the keymap used for \[cmd]. */
812 else if (strp[0] == '\\' && (strp[1] == '{' || strp[1] == '<'))
813 {
814 struct buffer *oldbuf;
11f9d6e1 815 int start_idx;
c6045832
JB
816
817 changed = 1;
818 strp += 2; /* skip \{ or \< */
819 start = strp;
11f9d6e1 820 start_idx = start - XSTRING (string)->data;
c6045832 821
4acb738e
EN
822 while ((strp - (unsigned char *) XSTRING (string)->data
823 < XSTRING (string)->size)
c6045832
JB
824 && *strp != '}' && *strp != '>')
825 strp++;
141199d1
RS
826
827 length_byte = strp - start;
c6045832
JB
828 strp++; /* skip } or > */
829
830 /* Save STRP in IDX. */
4acb738e 831 idx = strp - (unsigned char *) XSTRING (string)->data;
c6045832
JB
832
833 /* Get the value of the keymap in TEM, or nil if undefined.
834 Do this while still in the user's current buffer
835 in case it is a local variable. */
141199d1 836 name = Fintern (make_string (start, length_byte), Qnil);
c6045832 837 tem = Fboundp (name);
265a9e55 838 if (! NILP (tem))
c6045832
JB
839 {
840 tem = Fsymbol_value (name);
265a9e55 841 if (! NILP (tem))
11f9d6e1 842 {
02067692
SM
843 tem = get_keymap (tem, 0, 1);
844 /* Note that get_keymap can GC. */
11f9d6e1
GM
845 strp = XSTRING (string)->data + idx;
846 start = XSTRING (string)->data + start_idx;
847 }
c6045832
JB
848 }
849
850 /* Now switch to a temp buffer. */
851 oldbuf = current_buffer;
852 set_buffer_internal (XBUFFER (Vprin1_to_string_buffer));
853
265a9e55 854 if (NILP (tem))
c6045832
JB
855 {
856 name = Fsymbol_name (name);
857 insert_string ("\nUses keymap \"");
141199d1
RS
858 insert_from_string (name, 0, 0,
859 XSTRING (name)->size,
fc932ac6 860 STRING_BYTES (XSTRING (name)), 1);
c6045832
JB
861 insert_string ("\", which is not currently defined.\n");
862 if (start[-1] == '<') keymap = Qnil;
863 }
864 else if (start[-1] == '<')
865 keymap = tem;
866 else
7523d17c 867 describe_map_tree (tem, 1, Qnil, Qnil, (char *)0, 1, 0, 0);
c6045832
JB
868 tem = Fbuffer_string ();
869 Ferase_buffer ();
870 set_buffer_internal (oldbuf);
871
872 subst_string:
873 start = XSTRING (tem)->data;
874 length = XSTRING (tem)->size;
fc932ac6 875 length_byte = STRING_BYTES (XSTRING (tem));
c6045832 876 subst:
8d17fe0b
GM
877 {
878 int offset = bufp - buf;
879 buf = (unsigned char *) xrealloc (buf, bsize += length_byte);
880 bufp = buf + offset;
881 bcopy (start, bufp, length_byte);
882 bufp += length_byte;
883 nchars += length;
884 /* Check STRING again in case gc relocated it. */
885 strp = (unsigned char *) XSTRING (string)->data + idx;
886 }
c6045832 887 }
141199d1
RS
888 else if (! multibyte) /* just copy other chars */
889 *bufp++ = *strp++, nchars++;
890 else
891 {
892 int len;
fc932ac6 893 int maxlen = XSTRING (string)->data + STRING_BYTES (XSTRING (string)) - strp;
141199d1
RS
894
895 STRING_CHAR_AND_LENGTH (strp, maxlen, len);
896 if (len == 1)
897 *bufp = *strp;
898 else
899 bcopy (strp, bufp, len);
900 strp += len;
901 bufp += len;
902 nchars++;
903 }
c6045832
JB
904 }
905
906 if (changed) /* don't bother if nothing substituted */
cc5bf9eb 907 tem = make_string_from_bytes (buf, nchars, bufp - buf);
c6045832 908 else
4acb738e 909 tem = string;
9ac0d9e0 910 xfree (buf);
665d3046 911 RETURN_UNGCPRO (tem);
c6045832
JB
912}
913\f
dfcf069d 914void
c6045832
JB
915syms_of_doc ()
916{
9191c8ae
GM
917 Qfunction_documentation = intern ("function-documentation");
918 staticpro (&Qfunction_documentation);
919
c6045832 920 DEFVAR_LISP ("internal-doc-file-name", &Vdoc_file_name,
335c5470 921 doc: /* Name of file containing documentation strings of built-in symbols. */);
c6045832
JB
922 Vdoc_file_name = Qnil;
923
924 defsubr (&Sdocumentation);
925 defsubr (&Sdocumentation_property);
926 defsubr (&Ssnarf_documentation);
927 defsubr (&Ssubstitute_command_keys);
928}