Fix -Wimplicit warnings.
[bpt/emacs.git] / src / doc.c
1 /* Record indices of function doc strings stored in a file.
2 Copyright (C) 1985, 86, 93, 94, 95, 97, 1998 Free Software Foundation, Inc.
3
4 This file is part of GNU Emacs.
5
6 GNU Emacs is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2, or (at your option)
9 any later version.
10
11 GNU Emacs is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with GNU Emacs; see the file COPYING. If not, write to
18 the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA. */
20
21
22 #include <config.h>
23
24 #include <sys/types.h>
25 #include <sys/file.h> /* Must be after sys/types.h for USG and BSD4_1*/
26
27 #ifdef USG5
28 #include <fcntl.h>
29 #endif
30
31 #ifdef HAVE_UNISTD_H
32 #include <unistd.h>
33 #endif
34
35 #ifndef O_RDONLY
36 #define O_RDONLY 0
37 #endif
38
39 #include "lisp.h"
40 #include "buffer.h"
41 #include "keyboard.h"
42 #include "charset.h"
43
44 Lisp_Object Vdoc_file_name;
45
46 extern char *index ();
47
48 extern Lisp_Object Voverriding_local_map;
49
50 /* For VMS versions with limited file name syntax,
51 convert the name to something VMS will allow. */
52 static void
53 munge_doc_file_name (name)
54 char *name;
55 {
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 */
72 }
73
74 /* Buffer used for reading from documentation file. */
75 static char *get_doc_string_buffer;
76 static int get_doc_string_buffer_size;
77
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
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.) */
85
86 static Lisp_Object
87 get_doc_string (filepos)
88 Lisp_Object filepos;
89 {
90 char *from, *to;
91 register int fd;
92 register char *name;
93 register char *p, *p1;
94 int minsize;
95 int offset, position;
96 Lisp_Object file, tem;
97
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);
107 if (position < 0)
108 position = - position;
109 }
110 else
111 return Qnil;
112
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 {
136 name = (char *) XSTRING (file)->data;
137 }
138
139 fd = open (name, O_RDONLY, 0);
140 if (fd < 0)
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/");
148 strcat (name, XSTRING (file)->data);
149 munge_doc_file_name (name);
150
151 fd = open (name, O_RDONLY, 0);
152 }
153 #endif
154 if (fd < 0)
155 error ("Cannot open doc string file \"%s\"", name);
156 }
157
158 /* Seek only to beginning of disk block. */
159 offset = position % (8 * 1024);
160 if (0 > lseek (fd, position - offset, 0))
161 {
162 close (fd);
163 error ("Position %ld out of range in doc string file \"%s\"",
164 position, name);
165 }
166
167 /* Read the doc string into get_doc_string_buffer.
168 P points beyond the data just read. */
169
170 p = get_doc_string_buffer;
171 while (1)
172 {
173 int space_left = (get_doc_string_buffer_size
174 - (p - get_doc_string_buffer));
175 int nread;
176
177 /* Allocate or grow the buffer if we need to. */
178 if (space_left == 0)
179 {
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));
188 }
189
190 /* Read a disk block at a time.
191 If we read the same block last time, maybe skip this? */
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)
202 break;
203 if (p == get_doc_string_buffer)
204 p1 = index (p + offset, '\037');
205 else
206 p1 = index (p, '\037');
207 if (p1)
208 {
209 *p1 = 0;
210 p = p1;
211 break;
212 }
213 p += nread;
214 }
215 close (fd);
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 ^_. */
219 from = get_doc_string_buffer + offset;
220 to = get_doc_string_buffer + offset;
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
242 return make_string (get_doc_string_buffer + offset,
243 to - (get_doc_string_buffer + offset));
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
250 Lisp_Object
251 read_doc_string (filepos)
252 Lisp_Object filepos;
253 {
254 return Fread (get_doc_string (filepos));
255 }
256
257 DEFUN ("documentation", Fdocumentation, Sdocumentation, 1, 2, 0,
258 "Return the documentation string of FUNCTION.\n\
259 Unless a non-nil second argument RAW is given, the\n\
260 string is passed through `substitute-command-keys'.")
261 (function, raw)
262 Lisp_Object function, raw;
263 {
264 Lisp_Object fun;
265 Lisp_Object funcar;
266 Lisp_Object tem, doc;
267
268 fun = Findirect_function (function);
269
270 if (SUBRP (fun))
271 {
272 if (XSUBR (fun)->doc == 0) return Qnil;
273 if ((EMACS_INT) XSUBR (fun)->doc >= 0)
274 doc = build_string (XSUBR (fun)->doc);
275 else
276 doc = get_doc_string (make_number (- (EMACS_INT) XSUBR (fun)->doc));
277 }
278 else if (COMPILEDP (fun))
279 {
280 if ((XVECTOR (fun)->size & PSEUDOVECTOR_SIZE_MASK) <= COMPILED_DOC_STRING)
281 return Qnil;
282 tem = XVECTOR (fun)->contents[COMPILED_DOC_STRING];
283 if (STRINGP (tem))
284 doc = tem;
285 else if (NATNUMP (tem) || CONSP (tem))
286 doc = get_doc_string (tem);
287 else
288 return Qnil;
289 }
290 else if (STRINGP (fun) || VECTORP (fun))
291 {
292 return build_string ("Keyboard macro.");
293 }
294 else if (CONSP (fun))
295 {
296 funcar = Fcar (fun);
297 if (!SYMBOLP (funcar))
298 return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
299 else if (EQ (funcar, Qkeymap))
300 return build_string ("Prefix command (definition is a keymap associating keystrokes with\n\
301 subcommands.)");
302 else if (EQ (funcar, Qlambda)
303 || EQ (funcar, Qautoload))
304 {
305 Lisp_Object tem1;
306 tem1 = Fcdr (Fcdr (fun));
307 tem = Fcar (tem1);
308 if (STRINGP (tem))
309 doc = tem;
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))
314 doc = get_doc_string (tem);
315 else
316 return Qnil;
317 }
318 else if (EQ (funcar, Qmocklisp))
319 return Qnil;
320 else if (EQ (funcar, Qmacro))
321 return Fdocumentation (Fcdr (fun), raw);
322 else
323 goto oops;
324 }
325 else
326 {
327 oops:
328 Fsignal (Qinvalid_function, Fcons (fun, Qnil));
329 }
330
331 if (NILP (raw))
332 {
333 struct gcpro gcpro1;
334
335 GCPRO1 (doc);
336 doc = Fsubstitute_command_keys (doc);
337 UNGCPRO;
338 }
339 return doc;
340 }
341
342 DEFUN ("documentation-property", Fdocumentation_property, Sdocumentation_property, 2, 3, 0,
343 "Return the documentation string that is SYMBOL's PROP property.\n\
344 This is like `get', but it can refer to strings stored in the\n\
345 `etc/DOC' file; and if the value is a string, it is passed through\n\
346 `substitute-command-keys'. A non-nil third argument RAW avoids this\n\
347 translation.")
348 (symbol, prop, raw)
349 Lisp_Object symbol, prop, raw;
350 {
351 register Lisp_Object tem;
352
353 tem = Fget (symbol, prop);
354 if (INTEGERP (tem))
355 tem = get_doc_string (XINT (tem) > 0 ? tem : make_number (- XINT (tem)));
356 else if (CONSP (tem))
357 tem = get_doc_string (tem);
358 if (NILP (raw) && STRINGP (tem))
359 return Fsubstitute_command_keys (tem);
360 return tem;
361 }
362 \f
363 /* Scanning the DOC files and placing docstring offsets into functions. */
364
365 static void
366 store_function_docstring (fun, offset)
367 Lisp_Object fun;
368 /* Use EMACS_INT because we get this from pointer subtraction. */
369 EMACS_INT offset;
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. */
376 if (SUBRP (fun))
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));
388 if (CONSP (tem) && INTEGERP (XCONS (tem)->car))
389 XSETFASTINT (XCONS (tem)->car, offset);
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. */
396 else if (COMPILEDP (fun))
397 {
398 /* This bytecode object must have a slot for the
399 docstring, since we've found a docstring for it. */
400 if ((XVECTOR (fun)->size & PSEUDOVECTOR_SIZE_MASK) > COMPILED_DOC_STRING)
401 XSETFASTINT (XVECTOR (fun)->contents[COMPILED_DOC_STRING], offset);
402 }
403 }
404
405
406 DEFUN ("Snarf-documentation", Fsnarf_documentation, Ssnarf_documentation,
407 1, 1, 0,
408 "Used during Emacs initialization, before dumping runnable Emacs,\n\
409 to find pointers to doc strings stored in `etc/DOC...' and\n\
410 record them in function definitions.\n\
411 One arg, FILENAME, a string which does not include a directory.\n\
412 The file is found in `../etc' now; found in the `data-directory'\n\
413 when 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
426 #ifndef CANNOT_DUMP
427 if (NILP (Vpurify_flag))
428 error ("Snarf-documentation can only be called in an undumped Emacs");
429 #endif
430
431 CHECK_STRING (filename, 0);
432
433 #ifndef CANNOT_DUMP
434 name = (char *) alloca (XSTRING (filename)->size + 14);
435 strcpy (name, "../etc/");
436 #else /* CANNOT_DUMP */
437 CHECK_STRING (Vdoc_directory, 0);
438 name = (char *) alloca (XSTRING (filename)->size +
439 XSTRING (Vdoc_directory)->size + 1);
440 strcpy (name, XSTRING (Vdoc_directory)->data);
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');
482 sym = oblookup (Vobarray, p + 2,
483 multibyte_chars_in_text (p + 2, end - p - 2),
484 end - p - 2);
485 if (SYMBOLP (sym))
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
498 /* Attach a docstring to a function? */
499 else if (p[1] == 'F')
500 store_function_docstring (sym, pos + end + 1 - buf);
501
502 else
503 error ("DOC file invalid at position %d", pos);
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
514 DEFUN ("substitute-command-keys", Fsubstitute_command_keys,
515 Ssubstitute_command_keys, 1, 1, 0,
516 "Substitute key descriptions for command names in STRING.\n\
517 Return a new string which is STRING with substrings of the form \\=\\[COMMAND]\n\
518 replaced by either: a keystroke sequence that will invoke COMMAND,\n\
519 or \"M-x COMMAND\" if COMMAND is not on any keys.\n\
520 Substrings of the form \\=\\{MAPVAR} are replaced by summaries\n\
521 \(made by describe-bindings) of the value of MAPVAR, taken as a keymap.\n\
522 Substrings of the form \\=\\<MAPVAR> specify to use the value of MAPVAR\n\
523 as the keymap for future \\=\\[COMMAND] substrings.\n\
524 \\=\\= quotes the following character and is discarded;\n\
525 thus, \\=\\=\\=\\= puts \\=\\= into the output, and \\=\\=\\=\\[ puts \\=\\[ into the output.")
526 (string)
527 Lisp_Object string;
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;
536 Lisp_Object tem;
537 Lisp_Object keymap;
538 unsigned char *start;
539 int length, length_byte;
540 Lisp_Object name;
541 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
542 int multibyte;
543 int nchars;
544
545 if (NILP (string))
546 return Qnil;
547
548 CHECK_STRING (string, 0);
549 tem = Qnil;
550 keymap = Qnil;
551 name = Qnil;
552 GCPRO4 (string, tem, keymap, name);
553
554 multibyte = STRING_MULTIBYTE (string);
555 nchars = 0;
556
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,
560 or from a \\<mapname> construct in STRING itself.. */
561 keymap = current_kboard->Voverriding_terminal_local_map;
562 if (NILP (keymap))
563 keymap = Voverriding_local_map;
564
565 bsize = STRING_BYTES (XSTRING (string));
566 bufp = buf = (unsigned char *) xmalloc (bsize);
567
568 strp = (unsigned char *) XSTRING (string)->data;
569 while (strp < XSTRING (string)->data + STRING_BYTES (XSTRING (string)))
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;
576 strp += 2;
577 if (multibyte)
578 {
579 int len;
580 int maxlen = XSTRING (string)->data + STRING_BYTES (XSTRING (string)) - strp;
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++;
593 }
594 else if (strp[0] == '\\' && strp[1] == '[')
595 {
596 Lisp_Object firstkey;
597
598 changed = 1;
599 strp += 2; /* skip \[ */
600 start = strp;
601
602 while ((strp - (unsigned char *) XSTRING (string)->data
603 < STRING_BYTES (XSTRING (string)))
604 && *strp != ']')
605 strp++;
606 length_byte = strp - start;
607
608 strp++; /* skip ] */
609
610 /* Save STRP in IDX. */
611 idx = strp - (unsigned char *) XSTRING (string)->data;
612 tem = Fintern (make_string (start, length_byte), Qnil);
613 tem = Fwhere_is_internal (tem, keymap, Qt, Qnil);
614
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. */
618 if (!NILP (tem))
619 {
620 firstkey = Faref (tem, make_number (0));
621 if (EQ (firstkey, Qmenu_bar))
622 tem = Qnil;
623 }
624
625 if (NILP (tem)) /* but not on any keys */
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;
632 nchars += 4;
633 if (multibyte)
634 length = multibyte_chars_in_text (start, length_byte);
635 else
636 length = length_byte;
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;
650
651 changed = 1;
652 strp += 2; /* skip \{ or \< */
653 start = strp;
654
655 while ((strp - (unsigned char *) XSTRING (string)->data
656 < XSTRING (string)->size)
657 && *strp != '}' && *strp != '>')
658 strp++;
659
660 length_byte = strp - start;
661 strp++; /* skip } or > */
662
663 /* Save STRP in IDX. */
664 idx = strp - (unsigned char *) XSTRING (string)->data;
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. */
669 name = Fintern (make_string (start, length_byte), Qnil);
670 tem = Fboundp (name);
671 if (! NILP (tem))
672 {
673 tem = Fsymbol_value (name);
674 if (! NILP (tem))
675 tem = get_keymap_1 (tem, 0, 1);
676 }
677
678 /* Now switch to a temp buffer. */
679 oldbuf = current_buffer;
680 set_buffer_internal (XBUFFER (Vprin1_to_string_buffer));
681
682 if (NILP (tem))
683 {
684 name = Fsymbol_name (name);
685 insert_string ("\nUses keymap \"");
686 insert_from_string (name, 0, 0,
687 XSTRING (name)->size,
688 STRING_BYTES (XSTRING (name)), 1);
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
695 describe_map_tree (tem, 1, Qnil, Qnil, (char *)0, 1, 0, 0);
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;
703 length_byte = STRING_BYTES (XSTRING (tem));
704 subst:
705 new = (unsigned char *) xrealloc (buf, bsize += length_byte);
706 bufp += new - buf;
707 buf = new;
708 bcopy (start, bufp, length_byte);
709 bufp += length_byte;
710 nchars += length;
711 /* Check STRING again in case gc relocated it. */
712 strp = (unsigned char *) XSTRING (string)->data + idx;
713 }
714 else if (! multibyte) /* just copy other chars */
715 *bufp++ = *strp++, nchars++;
716 else
717 {
718 int len;
719 int maxlen = XSTRING (string)->data + STRING_BYTES (XSTRING (string)) - strp;
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 }
730 }
731
732 if (changed) /* don't bother if nothing substituted */
733 tem = make_string_from_bytes (buf, nchars, bufp - buf);
734 else
735 tem = string;
736 xfree (buf);
737 RETURN_UNGCPRO (tem);
738 }
739 \f
740 void
741 syms_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 }