]> code.delx.au - gnu-emacs/blob - src/doc.c
(detect_coding_mask): Initilize local variable C.
[gnu-emacs] / 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 If UNIBYTE is nonzero, always return the result as a unibyte string. */
87
88 Lisp_Object
89 get_doc_string (filepos, unibyte)
90 Lisp_Object filepos;
91 int unibyte;
92 {
93 char *from, *to;
94 register int fd;
95 register char *name;
96 register char *p, *p1;
97 int minsize;
98 int offset, position;
99 Lisp_Object file, tem;
100
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);
110 if (position < 0)
111 position = - position;
112 }
113 else
114 return Qnil;
115
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 {
139 name = (char *) XSTRING (file)->data;
140 }
141
142 fd = open (name, O_RDONLY, 0);
143 if (fd < 0)
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/");
151 strcat (name, XSTRING (file)->data);
152 munge_doc_file_name (name);
153
154 fd = open (name, O_RDONLY, 0);
155 }
156 #endif
157 if (fd < 0)
158 error ("Cannot open doc string file \"%s\"", name);
159 }
160
161 /* Seek only to beginning of disk block. */
162 offset = position % (8 * 1024);
163 if (0 > lseek (fd, position - offset, 0))
164 {
165 close (fd);
166 error ("Position %ld out of range in doc string file \"%s\"",
167 position, name);
168 }
169
170 /* Read the doc string into get_doc_string_buffer.
171 P points beyond the data just read. */
172
173 p = get_doc_string_buffer;
174 while (1)
175 {
176 int space_left = (get_doc_string_buffer_size
177 - (p - get_doc_string_buffer));
178 int nread;
179
180 /* Allocate or grow the buffer if we need to. */
181 if (space_left == 0)
182 {
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));
191 }
192
193 /* Read a disk block at a time.
194 If we read the same block last time, maybe skip this? */
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)
205 break;
206 if (p == get_doc_string_buffer)
207 p1 = index (p + offset, '\037');
208 else
209 p1 = index (p, '\037');
210 if (p1)
211 {
212 *p1 = 0;
213 p = p1;
214 break;
215 }
216 p += nread;
217 }
218 close (fd);
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 ^_. */
222 from = get_doc_string_buffer + offset;
223 to = get_doc_string_buffer + offset;
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
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));
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
257 Lisp_Object
258 read_doc_string (filepos)
259 Lisp_Object filepos;
260 {
261 return Fread (get_doc_string (filepos, 1));
262 }
263
264 DEFUN ("documentation", Fdocumentation, Sdocumentation, 1, 2, 0,
265 "Return the documentation string of FUNCTION.\n\
266 Unless a non-nil second argument RAW is given, the\n\
267 string is passed through `substitute-command-keys'.")
268 (function, raw)
269 Lisp_Object function, raw;
270 {
271 Lisp_Object fun;
272 Lisp_Object funcar;
273 Lisp_Object tem, doc;
274
275 fun = Findirect_function (function);
276
277 if (SUBRP (fun))
278 {
279 if (XSUBR (fun)->doc == 0) return Qnil;
280 if ((EMACS_INT) XSUBR (fun)->doc >= 0)
281 doc = build_string (XSUBR (fun)->doc);
282 else
283 doc = get_doc_string (make_number (- (EMACS_INT) XSUBR (fun)->doc), 0);
284 }
285 else if (COMPILEDP (fun))
286 {
287 if ((XVECTOR (fun)->size & PSEUDOVECTOR_SIZE_MASK) <= COMPILED_DOC_STRING)
288 return Qnil;
289 tem = XVECTOR (fun)->contents[COMPILED_DOC_STRING];
290 if (STRINGP (tem))
291 doc = tem;
292 else if (NATNUMP (tem) || CONSP (tem))
293 doc = get_doc_string (tem, 1);
294 else
295 return Qnil;
296 }
297 else if (STRINGP (fun) || VECTORP (fun))
298 {
299 return build_string ("Keyboard macro.");
300 }
301 else if (CONSP (fun))
302 {
303 funcar = Fcar (fun);
304 if (!SYMBOLP (funcar))
305 return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
306 else if (EQ (funcar, Qkeymap))
307 return build_string ("Prefix command (definition is a keymap associating keystrokes with\n\
308 subcommands.)");
309 else if (EQ (funcar, Qlambda)
310 || EQ (funcar, Qautoload))
311 {
312 Lisp_Object tem1;
313 tem1 = Fcdr (Fcdr (fun));
314 tem = Fcar (tem1);
315 if (STRINGP (tem))
316 doc = tem;
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))
321 doc = get_doc_string (tem, 1);
322 else
323 return Qnil;
324 }
325 else if (EQ (funcar, Qmocklisp))
326 return Qnil;
327 else if (EQ (funcar, Qmacro))
328 return Fdocumentation (Fcdr (fun), raw);
329 else
330 goto oops;
331 }
332 else
333 {
334 oops:
335 Fsignal (Qinvalid_function, Fcons (fun, Qnil));
336 }
337
338 if (NILP (raw))
339 {
340 struct gcpro gcpro1;
341
342 GCPRO1 (doc);
343 doc = Fsubstitute_command_keys (doc);
344 UNGCPRO;
345 }
346 return doc;
347 }
348
349 DEFUN ("documentation-property", Fdocumentation_property, Sdocumentation_property, 2, 3, 0,
350 "Return the documentation string that is SYMBOL's PROP property.\n\
351 This is like `get', but it can refer to strings stored in the\n\
352 `etc/DOC' file; and if the value is a string, it is passed through\n\
353 `substitute-command-keys'. A non-nil third argument RAW avoids this\n\
354 translation.")
355 (symbol, prop, raw)
356 Lisp_Object symbol, prop, raw;
357 {
358 register Lisp_Object tem;
359
360 tem = Fget (symbol, prop);
361 if (INTEGERP (tem))
362 tem = get_doc_string (XINT (tem) > 0 ? tem : make_number (- XINT (tem)), 0);
363 else if (CONSP (tem))
364 tem = get_doc_string (tem, 0);
365 if (NILP (raw) && STRINGP (tem))
366 return Fsubstitute_command_keys (tem);
367 return tem;
368 }
369 \f
370 /* Scanning the DOC files and placing docstring offsets into functions. */
371
372 static void
373 store_function_docstring (fun, offset)
374 Lisp_Object fun;
375 /* Use EMACS_INT because we get this from pointer subtraction. */
376 EMACS_INT offset;
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. */
383 if (SUBRP (fun))
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));
395 if (CONSP (tem) && INTEGERP (XCONS (tem)->car))
396 XSETFASTINT (XCONS (tem)->car, offset);
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. */
403 else if (COMPILEDP (fun))
404 {
405 /* This bytecode object must have a slot for the
406 docstring, since we've found a docstring for it. */
407 if ((XVECTOR (fun)->size & PSEUDOVECTOR_SIZE_MASK) > COMPILED_DOC_STRING)
408 XSETFASTINT (XVECTOR (fun)->contents[COMPILED_DOC_STRING], offset);
409 }
410 }
411
412
413 DEFUN ("Snarf-documentation", Fsnarf_documentation, Ssnarf_documentation,
414 1, 1, 0,
415 "Used during Emacs initialization, before dumping runnable Emacs,\n\
416 to find pointers to doc strings stored in `etc/DOC...' and\n\
417 record them in function definitions.\n\
418 One arg, FILENAME, a string which does not include a directory.\n\
419 The file is found in `../etc' now; found in the `data-directory'\n\
420 when 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
433 #ifndef CANNOT_DUMP
434 if (NILP (Vpurify_flag))
435 error ("Snarf-documentation can only be called in an undumped Emacs");
436 #endif
437
438 CHECK_STRING (filename, 0);
439
440 #ifndef CANNOT_DUMP
441 name = (char *) alloca (XSTRING (filename)->size + 14);
442 strcpy (name, "../etc/");
443 #else /* CANNOT_DUMP */
444 CHECK_STRING (Vdoc_directory, 0);
445 name = (char *) alloca (XSTRING (filename)->size +
446 XSTRING (Vdoc_directory)->size + 1);
447 strcpy (name, XSTRING (Vdoc_directory)->data);
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');
489 sym = oblookup (Vobarray, p + 2,
490 multibyte_chars_in_text (p + 2, end - p - 2),
491 end - p - 2);
492 if (SYMBOLP (sym))
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
505 /* Attach a docstring to a function? */
506 else if (p[1] == 'F')
507 store_function_docstring (sym, pos + end + 1 - buf);
508
509 else
510 error ("DOC file invalid at position %d", pos);
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
521 DEFUN ("substitute-command-keys", Fsubstitute_command_keys,
522 Ssubstitute_command_keys, 1, 1, 0,
523 "Substitute key descriptions for command names in STRING.\n\
524 Return a new string which is STRING with substrings of the form \\=\\[COMMAND]\n\
525 replaced by either: a keystroke sequence that will invoke COMMAND,\n\
526 or \"M-x COMMAND\" if COMMAND is not on any keys.\n\
527 Substrings of the form \\=\\{MAPVAR} are replaced by summaries\n\
528 \(made by describe-bindings) of the value of MAPVAR, taken as a keymap.\n\
529 Substrings of the form \\=\\<MAPVAR> specify to use the value of MAPVAR\n\
530 as the keymap for future \\=\\[COMMAND] substrings.\n\
531 \\=\\= quotes the following character and is discarded;\n\
532 thus, \\=\\=\\=\\= puts \\=\\= into the output, and \\=\\=\\=\\[ puts \\=\\[ into the output.")
533 (string)
534 Lisp_Object string;
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;
543 Lisp_Object tem;
544 Lisp_Object keymap;
545 unsigned char *start;
546 int length, length_byte;
547 Lisp_Object name;
548 struct gcpro gcpro1, gcpro2, gcpro3, gcpro4;
549 int multibyte;
550 int nchars;
551
552 if (NILP (string))
553 return Qnil;
554
555 CHECK_STRING (string, 0);
556 tem = Qnil;
557 keymap = Qnil;
558 name = Qnil;
559 GCPRO4 (string, tem, keymap, name);
560
561 multibyte = STRING_MULTIBYTE (string);
562 nchars = 0;
563
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,
567 or from a \\<mapname> construct in STRING itself.. */
568 keymap = current_kboard->Voverriding_terminal_local_map;
569 if (NILP (keymap))
570 keymap = Voverriding_local_map;
571
572 bsize = STRING_BYTES (XSTRING (string));
573 bufp = buf = (unsigned char *) xmalloc (bsize);
574
575 strp = (unsigned char *) XSTRING (string)->data;
576 while (strp < XSTRING (string)->data + STRING_BYTES (XSTRING (string)))
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;
583 strp += 2;
584 if (multibyte)
585 {
586 int len;
587 int maxlen = XSTRING (string)->data + STRING_BYTES (XSTRING (string)) - strp;
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++;
600 }
601 else if (strp[0] == '\\' && strp[1] == '[')
602 {
603 Lisp_Object firstkey;
604
605 changed = 1;
606 strp += 2; /* skip \[ */
607 start = strp;
608
609 while ((strp - (unsigned char *) XSTRING (string)->data
610 < STRING_BYTES (XSTRING (string)))
611 && *strp != ']')
612 strp++;
613 length_byte = strp - start;
614
615 strp++; /* skip ] */
616
617 /* Save STRP in IDX. */
618 idx = strp - (unsigned char *) XSTRING (string)->data;
619 tem = Fintern (make_string (start, length_byte), Qnil);
620 tem = Fwhere_is_internal (tem, keymap, Qt, Qnil);
621
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. */
625 if (!NILP (tem))
626 {
627 firstkey = Faref (tem, make_number (0));
628 if (EQ (firstkey, Qmenu_bar))
629 tem = Qnil;
630 }
631
632 if (NILP (tem)) /* but not on any keys */
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;
639 nchars += 4;
640 if (multibyte)
641 length = multibyte_chars_in_text (start, length_byte);
642 else
643 length = length_byte;
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;
657
658 changed = 1;
659 strp += 2; /* skip \{ or \< */
660 start = strp;
661
662 while ((strp - (unsigned char *) XSTRING (string)->data
663 < XSTRING (string)->size)
664 && *strp != '}' && *strp != '>')
665 strp++;
666
667 length_byte = strp - start;
668 strp++; /* skip } or > */
669
670 /* Save STRP in IDX. */
671 idx = strp - (unsigned char *) XSTRING (string)->data;
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. */
676 name = Fintern (make_string (start, length_byte), Qnil);
677 tem = Fboundp (name);
678 if (! NILP (tem))
679 {
680 tem = Fsymbol_value (name);
681 if (! NILP (tem))
682 tem = get_keymap_1 (tem, 0, 1);
683 }
684
685 /* Now switch to a temp buffer. */
686 oldbuf = current_buffer;
687 set_buffer_internal (XBUFFER (Vprin1_to_string_buffer));
688
689 if (NILP (tem))
690 {
691 name = Fsymbol_name (name);
692 insert_string ("\nUses keymap \"");
693 insert_from_string (name, 0, 0,
694 XSTRING (name)->size,
695 STRING_BYTES (XSTRING (name)), 1);
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
702 describe_map_tree (tem, 1, Qnil, Qnil, (char *)0, 1, 0, 0);
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;
710 length_byte = STRING_BYTES (XSTRING (tem));
711 subst:
712 new = (unsigned char *) xrealloc (buf, bsize += length_byte);
713 bufp += new - buf;
714 buf = new;
715 bcopy (start, bufp, length_byte);
716 bufp += length_byte;
717 nchars += length;
718 /* Check STRING again in case gc relocated it. */
719 strp = (unsigned char *) XSTRING (string)->data + idx;
720 }
721 else if (! multibyte) /* just copy other chars */
722 *bufp++ = *strp++, nchars++;
723 else
724 {
725 int len;
726 int maxlen = XSTRING (string)->data + STRING_BYTES (XSTRING (string)) - strp;
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 }
737 }
738
739 if (changed) /* don't bother if nothing substituted */
740 tem = make_string_from_bytes (buf, nchars, bufp - buf);
741 else
742 tem = string;
743 xfree (buf);
744 RETURN_UNGCPRO (tem);
745 }
746 \f
747 void
748 syms_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 }