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