1 /* Record indices of function doc strings stored in a file.
2 Copyright (C) 1985-1986, 1993-1995, 1997-2011
3 Free Software Foundation, Inc.
5 This file is part of GNU Emacs.
7 GNU Emacs is free software: you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation, either version 3 of the License, or
10 (at your option) any later version.
12 GNU Emacs is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
23 #include <sys/types.h>
24 #include <sys/file.h> /* Must be after sys/types.h for USG*/
33 #include "character.h"
37 Lisp_Object Qfunction_documentation
;
39 /* Buffer used for reading from documentation file. */
40 static char *get_doc_string_buffer
;
41 static int get_doc_string_buffer_size
;
43 static unsigned char *read_bytecode_pointer
;
44 Lisp_Object
Fsnarf_documentation (Lisp_Object
);
46 /* readchar in lread.c calls back here to fetch the next byte.
47 If UNREADFLAG is 1, we unread a byte. */
50 read_bytecode_char (int unreadflag
)
54 read_bytecode_pointer
--;
57 return *read_bytecode_pointer
++;
60 /* Extract a doc string from a file. FILEPOS says where to get it.
61 If it is an integer, use that position in the standard DOC-... file.
62 If it is (FILE . INTEGER), use FILE as the file name
63 and INTEGER as the position in that file.
64 But if INTEGER is negative, make it positive.
65 (A negative integer is used for user variables, so we can distinguish
66 them without actually fetching the doc string.)
68 If the location does not point to the beginning of a docstring
69 (e.g. because the file has been modified and the location is stale),
72 If UNIBYTE is nonzero, always make a unibyte string.
74 If DEFINITION is nonzero, assume this is for reading
75 a dynamic function definition; convert the bytestring
76 and the constants vector with appropriate byte handling,
77 and return a cons cell. */
80 get_doc_string (Lisp_Object filepos
, int unibyte
, int definition
)
85 register char *p
, *p1
;
87 EMACS_INT offset
, position
;
88 Lisp_Object file
, tem
;
90 if (INTEGERP (filepos
))
92 file
= Vdoc_file_name
;
93 position
= XINT (filepos
);
95 else if (CONSP (filepos
))
97 file
= XCAR (filepos
);
98 position
= XINT (XCDR (filepos
));
104 position
= - position
;
106 if (!STRINGP (Vdoc_directory
))
112 /* Put the file name in NAME as a C string.
113 If it is relative, combine it with Vdoc_directory. */
115 tem
= Ffile_name_absolute_p (file
);
118 minsize
= SCHARS (Vdoc_directory
);
119 /* sizeof ("../etc/") == 8 */
122 name
= (char *) alloca (minsize
+ SCHARS (file
) + 8);
123 strcpy (name
, SSDATA (Vdoc_directory
));
124 strcat (name
, SSDATA (file
));
128 name
= SSDATA (file
);
131 fd
= emacs_open (name
, O_RDONLY
, 0);
135 if (!NILP (Vpurify_flag
))
137 /* Preparing to dump; DOC file is probably not installed.
138 So check in ../etc. */
139 strcpy (name
, "../etc/");
140 strcat (name
, SSDATA (file
));
142 fd
= emacs_open (name
, O_RDONLY
, 0);
146 error ("Cannot open doc string file \"%s\"", name
);
149 /* Seek only to beginning of disk block. */
150 /* Make sure we read at least 1024 bytes before `position'
151 so we can check the leading text for consistency. */
152 offset
= min (position
, max (1024, position
% (8 * 1024)));
153 if (0 > lseek (fd
, position
- offset
, 0))
156 error ("Position %ld out of range in doc string file \"%s\"",
160 /* Read the doc string into get_doc_string_buffer.
161 P points beyond the data just read. */
163 p
= get_doc_string_buffer
;
166 EMACS_INT space_left
= (get_doc_string_buffer_size
167 - (p
- get_doc_string_buffer
));
170 /* Allocate or grow the buffer if we need to. */
173 EMACS_INT in_buffer
= p
- get_doc_string_buffer
;
174 get_doc_string_buffer_size
+= 16 * 1024;
175 get_doc_string_buffer
176 = (char *) xrealloc (get_doc_string_buffer
,
177 get_doc_string_buffer_size
+ 1);
178 p
= get_doc_string_buffer
+ in_buffer
;
179 space_left
= (get_doc_string_buffer_size
180 - (p
- get_doc_string_buffer
));
183 /* Read a disk block at a time.
184 If we read the same block last time, maybe skip this? */
185 if (space_left
> 1024 * 8)
186 space_left
= 1024 * 8;
187 nread
= emacs_read (fd
, p
, space_left
);
191 error ("Read error on documentation file");
196 if (p
== get_doc_string_buffer
)
197 p1
= strchr (p
+ offset
, '\037');
199 p1
= strchr (p
, '\037');
210 /* Sanity checking. */
214 if (get_doc_string_buffer
[offset
- test
++] != ' ')
216 while (get_doc_string_buffer
[offset
- test
] >= '0'
217 && get_doc_string_buffer
[offset
- test
] <= '9')
219 if (get_doc_string_buffer
[offset
- test
++] != '@'
220 || get_doc_string_buffer
[offset
- test
] != '#')
226 if (get_doc_string_buffer
[offset
- test
++] != '\n')
228 while (get_doc_string_buffer
[offset
- test
] > ' ')
230 if (get_doc_string_buffer
[offset
- test
] != '\037')
234 /* Scan the text and perform quoting with ^A (char code 1).
235 ^A^A becomes ^A, ^A0 becomes a null char, and ^A_ becomes a ^_. */
236 from
= get_doc_string_buffer
+ offset
;
237 to
= get_doc_string_buffer
+ offset
;
253 error ("Invalid data in documentation file -- ^A followed by code 0%o", c
);
259 /* If DEFINITION, read from this buffer
260 the same way we would read bytes from a file. */
263 read_bytecode_pointer
= (unsigned char *) get_doc_string_buffer
+ offset
;
264 return Fread (Qlambda
);
268 return make_unibyte_string (get_doc_string_buffer
+ offset
,
269 to
- (get_doc_string_buffer
+ offset
));
272 /* The data determines whether the string is multibyte. */
274 multibyte_chars_in_text (((unsigned char *) get_doc_string_buffer
276 to
- (get_doc_string_buffer
+ offset
));
277 return make_string_from_bytes (get_doc_string_buffer
+ offset
,
279 to
- (get_doc_string_buffer
+ offset
));
283 /* Get a string from position FILEPOS and pass it through the Lisp reader.
284 We use this for fetching the bytecode string and constants vector
285 of a compiled function from the .elc file. */
288 read_doc_string (Lisp_Object filepos
)
290 return get_doc_string (filepos
, 0, 1);
294 reread_doc_file (Lisp_Object file
)
297 Lisp_Object reply
, prompt
[3];
300 prompt
[0] = build_string ("File ");
301 prompt
[1] = NILP (file
) ? Vdoc_file_name
: file
;
302 prompt
[2] = build_string (" is out of sync. Reload? ");
303 reply
= Fy_or_n_p (Fconcat (3, prompt
));
310 Fsnarf_documentation (Vdoc_file_name
);
312 Fload (file
, Qt
, Qt
, Qt
, Qnil
);
317 DEFUN ("documentation", Fdocumentation
, Sdocumentation
, 1, 2, 0,
318 doc
: /* Return the documentation string of FUNCTION.
319 Unless a non-nil second argument RAW is given, the
320 string is passed through `substitute-command-keys'. */)
321 (Lisp_Object function
, Lisp_Object raw
)
325 Lisp_Object tem
, doc
;
332 if (SYMBOLP (function
)
333 && (tem
= Fget (function
, Qfunction_documentation
),
335 return Fdocumentation_property (function
, Qfunction_documentation
, raw
);
337 fun
= Findirect_function (function
, Qnil
);
340 if (XSUBR (fun
)->doc
== 0)
342 else if ((EMACS_INT
) XSUBR (fun
)->doc
>= 0)
343 doc
= build_string (XSUBR (fun
)->doc
);
345 doc
= make_number ((EMACS_INT
) XSUBR (fun
)->doc
);
347 else if (COMPILEDP (fun
))
349 if ((ASIZE (fun
) & PSEUDOVECTOR_SIZE_MASK
) <= COMPILED_DOC_STRING
)
351 tem
= AREF (fun
, COMPILED_DOC_STRING
);
354 else if (NATNUMP (tem
) || CONSP (tem
))
359 else if (STRINGP (fun
) || VECTORP (fun
))
361 return build_string ("Keyboard macro.");
363 else if (CONSP (fun
))
366 if (!SYMBOLP (funcar
))
367 xsignal1 (Qinvalid_function
, fun
);
368 else if (EQ (funcar
, Qkeymap
))
369 return build_string ("Prefix command (definition is a keymap associating keystrokes with commands).");
370 else if (EQ (funcar
, Qlambda
)
371 || EQ (funcar
, Qautoload
))
374 tem1
= Fcdr (Fcdr (fun
));
378 /* Handle a doc reference--but these never come last
379 in the function body, so reject them if they are last. */
380 else if ((NATNUMP (tem
) || (CONSP (tem
) && INTEGERP (XCDR (tem
))))
381 && !NILP (XCDR (tem1
)))
386 else if (EQ (funcar
, Qmacro
))
387 return Fdocumentation (Fcdr (fun
), raw
);
394 xsignal1 (Qinvalid_function
, fun
);
397 /* Check for an advised function. Its doc string
398 has an `ad-advice-info' text property. */
401 Lisp_Object innerfunc
;
402 innerfunc
= Fget_text_property (make_number (0),
403 intern ("ad-advice-info"),
405 if (! NILP (innerfunc
))
406 doc
= call1 (intern ("ad-make-advised-docstring"), innerfunc
);
409 /* If DOC is 0, it's typically because of a dumped file missing
410 from the DOC file (bug in src/Makefile.in). */
411 if (EQ (doc
, make_number (0)))
413 if (INTEGERP (doc
) || CONSP (doc
))
416 tem
= get_doc_string (doc
, 0, 0);
417 if (NILP (tem
) && try_reload
)
419 /* The file is newer, we need to reset the pointers. */
420 struct gcpro gcpro1
, gcpro2
;
421 GCPRO2 (function
, raw
);
422 try_reload
= reread_doc_file (Fcar_safe (doc
));
435 doc
= Fsubstitute_command_keys (doc
);
439 DEFUN ("documentation-property", Fdocumentation_property
,
440 Sdocumentation_property
, 2, 3, 0,
441 doc
: /* Return the documentation string that is SYMBOL's PROP property.
442 Third argument RAW omitted or nil means pass the result through
443 `substitute-command-keys' if it is a string.
445 This differs from `get' in that it can refer to strings stored in the
446 `etc/DOC' file; and that it evaluates documentation properties that
448 (Lisp_Object symbol
, Lisp_Object prop
, Lisp_Object raw
)
453 documentation_property
:
455 tem
= Fget (symbol
, prop
);
456 if (EQ (tem
, make_number (0)))
458 if (INTEGERP (tem
) || (CONSP (tem
) && INTEGERP (XCDR (tem
))))
460 Lisp_Object doc
= tem
;
461 tem
= get_doc_string (tem
, 0, 0);
462 if (NILP (tem
) && try_reload
)
464 /* The file is newer, we need to reset the pointers. */
465 struct gcpro gcpro1
, gcpro2
, gcpro3
;
466 GCPRO3 (symbol
, prop
, raw
);
467 try_reload
= reread_doc_file (Fcar_safe (doc
));
472 goto documentation_property
;
476 else if (!STRINGP (tem
))
477 /* Feval protects its argument. */
480 if (NILP (raw
) && STRINGP (tem
))
481 tem
= Fsubstitute_command_keys (tem
);
485 /* Scanning the DOC files and placing docstring offsets into functions. */
488 store_function_docstring (Lisp_Object fun
, EMACS_INT offset
)
489 /* Use EMACS_INT because we get offset from pointer subtraction. */
491 fun
= indirect_function (fun
);
493 /* The type determines where the docstring is stored. */
495 /* Lisp_Subrs have a slot for it. */
497 XSUBR (fun
)->doc
= (char *) - offset
;
499 /* If it's a lisp form, stick it in the form. */
500 else if (CONSP (fun
))
505 if (EQ (tem
, Qlambda
) || EQ (tem
, Qautoload
))
507 tem
= Fcdr (Fcdr (fun
));
508 if (CONSP (tem
) && INTEGERP (XCAR (tem
)))
509 XSETCAR (tem
, make_number (offset
));
511 else if (EQ (tem
, Qmacro
))
512 store_function_docstring (XCDR (fun
), offset
);
515 /* Bytecode objects sometimes have slots for it. */
516 else if (COMPILEDP (fun
))
518 /* This bytecode object must have a slot for the
519 docstring, since we've found a docstring for it. */
520 if ((ASIZE (fun
) & PSEUDOVECTOR_SIZE_MASK
) > COMPILED_DOC_STRING
)
521 ASET (fun
, COMPILED_DOC_STRING
, make_number (offset
));
525 static const char buildobj
[] = BUILDOBJ
;
527 DEFUN ("Snarf-documentation", Fsnarf_documentation
, Ssnarf_documentation
,
529 doc
: /* Used during Emacs initialization to scan the `etc/DOC...' file.
530 This searches the `etc/DOC...' file for doc strings and
531 records them in function and variable definitions.
532 The function takes one argument, FILENAME, a string;
533 it specifies the file name (without a directory) of the DOC file.
534 That file is found in `../etc' now; later, when the dumped Emacs is run,
535 the same file name is found in the `doc-directory'. */)
536 (Lisp_Object filename
)
540 register EMACS_INT filled
;
541 register EMACS_INT pos
;
542 register char *p
, *end
;
547 CHECK_STRING (filename
);
551 (!NILP (Vpurify_flag
))
552 #else /* CANNOT_DUMP */
554 #endif /* CANNOT_DUMP */
556 name
= (char *) alloca (SCHARS (filename
) + 14);
557 strcpy (name
, "../etc/");
561 CHECK_STRING (Vdoc_directory
);
562 name
= (char *) alloca (SCHARS (filename
)
563 + SCHARS (Vdoc_directory
) + 1);
564 strcpy (name
, SSDATA (Vdoc_directory
));
566 strcat (name
, SSDATA (filename
)); /*** Add this line ***/
568 /* Vbuild_files is nil when temacs is run, and non-nil after that. */
569 if (NILP (Vbuild_files
))
571 const char *beg
, *end
;
573 for (beg
= buildobj
; *beg
; beg
= end
)
577 while (*beg
&& isspace (*beg
)) ++beg
;
579 for (end
= beg
; *end
&& ! isspace (*end
); ++end
)
580 if (*end
== '/') beg
= end
+1; /* skip directory part */
583 if (len
> 4 && end
[-4] == '.' && end
[-3] == 'o')
584 len
-= 2; /* Just take .o if it ends in .obj */
587 Vbuild_files
= Fcons (make_string (beg
, len
), Vbuild_files
);
589 Vbuild_files
= Fpurecopy (Vbuild_files
);
592 fd
= emacs_open (name
, O_RDONLY
, 0);
594 report_file_error ("Opening doc string file",
595 Fcons (build_string (name
), Qnil
));
596 Vdoc_file_name
= filename
;
602 filled
+= emacs_read (fd
, &buf
[filled
], sizeof buf
- 1 - filled
);
608 end
= buf
+ (filled
< 512 ? filled
: filled
- 128);
609 while (p
!= end
&& *p
!= '\037') p
++;
610 /* p points to ^_Ffunctionname\n or ^_Vvarname\n or ^_Sfilename\n. */
613 end
= strchr (p
, '\n');
615 /* See if this is a file name, and if it is a file in build-files. */
619 if (end
- p
> 4 && end
[-2] == '.'
620 && (end
[-1] == 'o' || end
[-1] == 'c'))
622 EMACS_INT len
= end
- p
- 2;
623 char *fromfile
= alloca (len
+ 1);
624 strncpy (fromfile
, &p
[2], len
);
626 if (fromfile
[len
-1] == 'c')
627 fromfile
[len
-1] = 'o';
629 skip_file
= NILP (Fmember (build_string (fromfile
),
634 sym
= oblookup (Vobarray
, p
+ 2,
635 multibyte_chars_in_text ((unsigned char *) p
+ 2,
638 /* Check skip_file so that when a function is defined several
639 times in different files (typically, once in xterm, once in
640 w32term, ...), we only pay attention to the one that
642 if (! skip_file
&& SYMBOLP (sym
))
644 /* Attach a docstring to a variable? */
647 /* Install file-position as variable-documentation property
648 and make it negative for a user-variable
649 (doc starts with a `*'). */
650 Fput (sym
, Qvariable_documentation
,
651 make_number ((pos
+ end
+ 1 - buf
)
652 * (end
[1] == '*' ? -1 : 1)));
655 /* Attach a docstring to a function? */
656 else if (p
[1] == 'F')
657 store_function_docstring (sym
, pos
+ end
+ 1 - buf
);
659 else if (p
[1] == 'S')
660 ; /* Just a source file name boundary marker. Ignore it. */
663 error ("DOC file invalid at position %d", pos
);
668 memmove (buf
, end
, filled
);
674 DEFUN ("substitute-command-keys", Fsubstitute_command_keys
,
675 Ssubstitute_command_keys
, 1, 1, 0,
676 doc
: /* Substitute key descriptions for command names in STRING.
677 Substrings of the form \\=\\[COMMAND] replaced by either: a keystroke
678 sequence that will invoke COMMAND, or "M-x COMMAND" if COMMAND is not
680 Substrings of the form \\=\\{MAPVAR} are replaced by summaries
681 \(made by `describe-bindings') of the value of MAPVAR, taken as a keymap.
682 Substrings of the form \\=\\<MAPVAR> specify to use the value of MAPVAR
683 as the keymap for future \\=\\[COMMAND] substrings.
684 \\=\\= quotes the following character and is discarded;
685 thus, \\=\\=\\=\\= puts \\=\\= into the output, and \\=\\=\\=\\[ puts \\=\\[ into the output.
687 Returns original STRING if no substitutions were made. Otherwise,
688 a new string, without any text properties, is returned. */)
693 register unsigned char *strp
;
699 unsigned char *start
;
700 EMACS_INT length
, length_byte
;
702 struct gcpro gcpro1
, gcpro2
, gcpro3
, gcpro4
;
709 CHECK_STRING (string
);
713 GCPRO4 (string
, tem
, keymap
, name
);
715 multibyte
= STRING_MULTIBYTE (string
);
718 /* KEYMAP is either nil (which means search all the active keymaps)
719 or a specified local map (which means search just that and the
720 global map). If non-nil, it might come from Voverriding_local_map,
721 or from a \\<mapname> construct in STRING itself.. */
722 keymap
= KVAR (current_kboard
, Voverriding_terminal_local_map
);
724 keymap
= Voverriding_local_map
;
726 bsize
= SBYTES (string
);
727 bufp
= buf
= (char *) xmalloc (bsize
);
729 strp
= SDATA (string
);
730 while (strp
< SDATA (string
) + SBYTES (string
))
732 if (strp
[0] == '\\' && strp
[1] == '=')
734 /* \= quotes the next character;
735 thus, to put in \[ without its special meaning, use \=\[. */
742 STRING_CHAR_AND_LENGTH (strp
, len
);
746 memcpy (bufp
, strp
, len
);
752 *bufp
++ = *strp
++, nchars
++;
754 else if (strp
[0] == '\\' && strp
[1] == '[')
757 int follow_remap
= 1;
760 strp
+= 2; /* skip \[ */
762 start_idx
= start
- SDATA (string
);
764 while ((strp
- SDATA (string
)
768 length_byte
= strp
- start
;
772 /* Save STRP in IDX. */
773 idx
= strp
- SDATA (string
);
774 name
= Fintern (make_string ((char *) start
, length_byte
), Qnil
);
777 tem
= Fwhere_is_internal (name
, keymap
, Qt
, Qnil
, Qnil
);
779 if (VECTORP (tem
) && XVECTOR (tem
)->size
> 1
780 && EQ (AREF (tem
, 0), Qremap
) && SYMBOLP (AREF (tem
, 1))
783 name
= AREF (tem
, 1);
788 /* Note the Fwhere_is_internal can GC, so we have to take
789 relocation of string contents into account. */
790 strp
= SDATA (string
) + idx
;
791 start
= SDATA (string
) + start_idx
;
793 if (NILP (tem
)) /* but not on any keys */
795 EMACS_INT offset
= bufp
- buf
;
796 buf
= (char *) xrealloc (buf
, bsize
+= 4);
798 memcpy (bufp
, "M-x ", 4);
802 length
= multibyte_chars_in_text (start
, length_byte
);
804 length
= length_byte
;
808 { /* function is on a key */
809 tem
= Fkey_description (tem
, Qnil
);
813 /* \{foo} is replaced with a summary of the keymap (symbol-value foo).
814 \<foo> just sets the keymap used for \[cmd]. */
815 else if (strp
[0] == '\\' && (strp
[1] == '{' || strp
[1] == '<'))
817 struct buffer
*oldbuf
;
819 /* This is for computing the SHADOWS arg for describe_map_tree. */
820 Lisp_Object active_maps
= Fcurrent_active_maps (Qnil
, Qnil
);
821 Lisp_Object earlier_maps
;
824 strp
+= 2; /* skip \{ or \< */
826 start_idx
= start
- SDATA (string
);
828 while ((strp
- SDATA (string
) < SBYTES (string
))
829 && *strp
!= '}' && *strp
!= '>')
832 length_byte
= strp
- start
;
833 strp
++; /* skip } or > */
835 /* Save STRP in IDX. */
836 idx
= strp
- SDATA (string
);
838 /* Get the value of the keymap in TEM, or nil if undefined.
839 Do this while still in the user's current buffer
840 in case it is a local variable. */
841 name
= Fintern (make_string ((char *) start
, length_byte
), Qnil
);
842 tem
= Fboundp (name
);
845 tem
= Fsymbol_value (name
);
848 tem
= get_keymap (tem
, 0, 1);
849 /* Note that get_keymap can GC. */
850 strp
= SDATA (string
) + idx
;
851 start
= SDATA (string
) + start_idx
;
855 /* Now switch to a temp buffer. */
856 oldbuf
= current_buffer
;
857 set_buffer_internal (XBUFFER (Vprin1_to_string_buffer
));
861 name
= Fsymbol_name (name
);
862 insert_string ("\nUses keymap \"");
863 insert_from_string (name
, 0, 0,
866 insert_string ("\", which is not currently defined.\n");
867 if (start
[-1] == '<') keymap
= Qnil
;
869 else if (start
[-1] == '<')
873 /* Get the list of active keymaps that precede this one.
874 If this one's not active, get nil. */
875 earlier_maps
= Fcdr (Fmemq (tem
, Freverse (active_maps
)));
876 describe_map_tree (tem
, 1, Fnreverse (earlier_maps
),
877 Qnil
, (char *)0, 1, 0, 0, 1);
879 tem
= Fbuffer_string ();
881 set_buffer_internal (oldbuf
);
885 length
= SCHARS (tem
);
886 length_byte
= SBYTES (tem
);
889 EMACS_INT offset
= bufp
- buf
;
890 buf
= (char *) xrealloc (buf
, bsize
+= length_byte
);
892 memcpy (bufp
, start
, length_byte
);
895 /* Check STRING again in case gc relocated it. */
896 strp
= SDATA (string
) + idx
;
899 else if (! multibyte
) /* just copy other chars */
900 *bufp
++ = *strp
++, nchars
++;
905 STRING_CHAR_AND_LENGTH (strp
, len
);
909 memcpy (bufp
, strp
, len
);
916 if (changed
) /* don't bother if nothing substituted */
917 tem
= make_string_from_bytes (buf
, nchars
, bufp
- buf
);
921 RETURN_UNGCPRO (tem
);
927 Qfunction_documentation
= intern_c_string ("function-documentation");
928 staticpro (&Qfunction_documentation
);
930 DEFVAR_LISP ("internal-doc-file-name", Vdoc_file_name
,
931 doc
: /* Name of file containing documentation strings of built-in symbols. */);
932 Vdoc_file_name
= Qnil
;
934 DEFVAR_LISP ("build-files", Vbuild_files
,
935 doc
: /* A list of files used to build this Emacs binary. */);
938 defsubr (&Sdocumentation
);
939 defsubr (&Sdocumentation_property
);
940 defsubr (&Ssnarf_documentation
);
941 defsubr (&Ssubstitute_command_keys
);