160252Shibler /* Record indices of function doc strings stored in a file.
260252Shibler Copyright (C) 1985, 1986 Free Software Foundation, Inc.
360252Shibler
460252Shibler This file is part of GNU Emacs.
560252Shibler
660252Shibler GNU Emacs is free software; you can redistribute it and/or modify
760252Shibler it under the terms of the GNU General Public License as published by
860252Shibler the Free Software Foundation; either version 1, or (at your option)
960252Shibler any later version.
1060252Shibler
1160252Shibler GNU Emacs is distributed in the hope that it will be useful,
1260252Shibler but WITHOUT ANY WARRANTY; without even the implied warranty of
1360252Shibler MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1460252Shibler GNU General Public License for more details.
1560252Shibler
1660252Shibler You should have received a copy of the GNU General Public License
1760252Shibler along with GNU Emacs; see the file COPYING. If not, write to
1860252Shibler the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
1960252Shibler
2060252Shibler
2160252Shibler #include "config.h"
2260252Shibler #include "lisp.h"
2360252Shibler #include "buffer.h"
2460252Shibler
2560252Shibler #include <sys/types.h>
2660252Shibler #include <sys/file.h> /* Must be after sys/types.h for USG and BSD4_1*/
2760252Shibler
2860252Shibler #ifdef USG5
2960252Shibler #include <fcntl.h>
3060252Shibler #endif
3160252Shibler
3260252Shibler #ifndef O_RDONLY
3360252Shibler #define O_RDONLY 0
3460252Shibler #endif
3560252Shibler
3660252Shibler Lisp_Object Vdoc_file_name;
3760252Shibler
3860252Shibler Lisp_Object
get_doc_string(filepos)3960252Shibler get_doc_string (filepos)
4060252Shibler long filepos;
4160252Shibler {
4260252Shibler char buf[512 * 32 + 1];
4360252Shibler register int fd;
4460252Shibler register char *name;
4560252Shibler register char *p, *p1;
4660252Shibler register int count;
4760252Shibler extern char *index ();
4860252Shibler
4960252Shibler if (XTYPE (Vexec_directory) != Lisp_String
5060252Shibler || XTYPE (Vdoc_file_name) != Lisp_String)
5160252Shibler return Qnil;
5260252Shibler
5360252Shibler name = (char *) alloca (XSTRING (Vexec_directory)->size
5460252Shibler + XSTRING (Vdoc_file_name)->size + 8);
5560252Shibler strcpy (name, XSTRING (Vexec_directory)->data);
5660252Shibler strcat (name, XSTRING (Vdoc_file_name)->data);
5760252Shibler #ifdef VMS
5860252Shibler #ifndef VMS4_4
5960252Shibler /* For VMS versions with limited file name syntax,
6060252Shibler convert the name to something VMS will allow. */
6160252Shibler p = name;
6260252Shibler while (*p)
6360252Shibler {
6460252Shibler if (*p == '-')
6560252Shibler *p = '_';
6660252Shibler p++;
6760252Shibler }
6860252Shibler #endif /* not VMS4_4 */
6960252Shibler #ifdef VMS4_4
7060252Shibler strcpy (name, sys_translate_unix (name));
7160252Shibler #endif /* VMS4_4 */
7260252Shibler #endif /* VMS */
7360252Shibler
7460252Shibler fd = open (name, O_RDONLY, 0);
7560252Shibler if (fd < 0)
7660252Shibler error ("Cannot open doc string file \"%s\"", name);
77*60297Shibler if (0 > lseek (fd, (off_t) filepos, 0))
7860252Shibler {
7960252Shibler close (fd);
8060252Shibler error ("Position %ld out of range in doc string file \"%s\"",
8160252Shibler filepos, name);
8260252Shibler }
8360252Shibler p = buf;
8460252Shibler while (p != buf + sizeof buf - 1)
8560252Shibler {
8660252Shibler count = read (fd, p, 512);
8760252Shibler p[count] = 0;
8860252Shibler if (!count)
8960252Shibler break;
9060252Shibler p1 = index (p, '\037');
9160252Shibler if (p1)
9260252Shibler {
9360252Shibler *p1 = 0;
9460252Shibler p = p1;
9560252Shibler break;
9660252Shibler }
9760252Shibler p += count;
9860252Shibler }
9960252Shibler close (fd);
10060252Shibler return make_string (buf, p - buf);
10160252Shibler }
10260252Shibler
10360252Shibler DEFUN ("documentation", Fdocumentation, Sdocumentation, 1, 1, 0,
10460252Shibler "Return the documentation string of FUNCTION.")
10560252Shibler (fun1)
10660252Shibler Lisp_Object fun1;
10760252Shibler {
10860252Shibler Lisp_Object fun;
10960252Shibler Lisp_Object funcar;
11060252Shibler Lisp_Object tem;
11160252Shibler
11260252Shibler fun = fun1;
11360252Shibler while (XTYPE (fun) == Lisp_Symbol)
11460252Shibler fun = Fsymbol_function (fun);
11560252Shibler if (XTYPE (fun) == Lisp_Subr)
11660252Shibler {
11760252Shibler if (XSUBR (fun)->doc == 0) return Qnil;
11860252Shibler if ((int) XSUBR (fun)->doc >= 0)
11960252Shibler return Fsubstitute_command_keys (build_string (XSUBR (fun)->doc));
12060252Shibler return Fsubstitute_command_keys (get_doc_string (- (int) XSUBR (fun)->doc));
12160252Shibler }
12260252Shibler if (XTYPE (fun) == Lisp_Vector)
12360252Shibler return build_string ("Prefix command (definition is a Lisp vector of subcommands).");
12460252Shibler if (XTYPE (fun) == Lisp_String)
12560252Shibler return build_string ("Keyboard macro.");
12660252Shibler if (!CONSP (fun))
12760252Shibler return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
12860252Shibler funcar = Fcar (fun);
12960252Shibler if (XTYPE (funcar) != Lisp_Symbol)
13060252Shibler return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
13160252Shibler if (XSYMBOL (funcar) == XSYMBOL (Qkeymap))
13260252Shibler return build_string ("Prefix command (definition is a list whose cdr is an alist of subcommands.)");
13360252Shibler if (XSYMBOL (funcar) == XSYMBOL (Qlambda)
13460252Shibler || XSYMBOL (funcar) == XSYMBOL (Qautoload))
13560252Shibler {
13660252Shibler tem = Fcar (Fcdr (Fcdr (fun)));
13760252Shibler if (XTYPE (tem) == Lisp_String)
13860252Shibler return Fsubstitute_command_keys (tem);
13960252Shibler if (XTYPE (tem) == Lisp_Int && XINT (tem) >= 0)
14060252Shibler return Fsubstitute_command_keys (get_doc_string (XFASTINT (tem)));
14160252Shibler return Qnil;
14260252Shibler }
14360252Shibler if (XSYMBOL (funcar) == XSYMBOL (Qmocklisp))
14460252Shibler return Qnil;
14560252Shibler if (XSYMBOL (funcar) == XSYMBOL (Qmacro))
14660252Shibler return Fdocumentation (Fcdr (fun));
14760252Shibler else
14860252Shibler return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
14960252Shibler }
15060252Shibler
15160252Shibler DEFUN ("documentation-property", Fdocumentation_property,
15260252Shibler Sdocumentation_property, 2, 2, 0,
15360252Shibler "Return the documentation string that is SYMBOL's PROP property.\n\
15460252Shibler This differs from using `get' only in that it can refer to strings\n\
15560252Shibler stored in the etc/DOC file.")
15660252Shibler (sym, prop)
15760252Shibler Lisp_Object sym, prop;
15860252Shibler {
15960252Shibler register Lisp_Object tem;
16060252Shibler
16160252Shibler tem = Fget (sym, prop);
16260252Shibler if (XTYPE (tem) == Lisp_Int)
16360252Shibler tem = get_doc_string (XINT (tem) > 0 ? XINT (tem) : - XINT (tem));
16460252Shibler return Fsubstitute_command_keys (tem);
16560252Shibler }
16660252Shibler
16760252Shibler DEFUN ("Snarf-documentation", Fsnarf_documentation, Ssnarf_documentation,
16860252Shibler 1, 1, 0,
16960252Shibler "Used during Emacs initialization, before dumping runnable Emacs,\n\
17060252Shibler to find pointers to doc strings stored in etc/DOC... and\n\
17160252Shibler record them in function definitions.\n\
17260252Shibler One arg, FILENAME, a string which does not include a directory.\n\
17360252Shibler The file is found in ../etc now; found in the exec-directory\n\
17460252Shibler when doc strings are referred to later in the dumped Emacs.")
17560252Shibler (filename)
17660252Shibler Lisp_Object filename;
17760252Shibler {
17860252Shibler int fd;
17960252Shibler char buf[1024 + 1];
18060252Shibler register int filled;
18160252Shibler register int pos;
18260252Shibler register char *p, *end;
18360252Shibler Lisp_Object sym, fun, tem;
18460252Shibler char *name;
18560252Shibler extern char *index ();
18660252Shibler
18760252Shibler CHECK_STRING (filename, 0);
18860252Shibler
18960252Shibler #ifndef CANNOT_DUMP
19060252Shibler name = (char *) alloca (XSTRING (filename)->size + 8);
19160252Shibler strcpy (name, "../etc/");
19260252Shibler #else /* CANNOT_DUMP */
19360252Shibler CHECK_STRING (Vexec_directory, 0);
19460252Shibler name = (char *) alloca (XSTRING (filename)->size +
19560252Shibler XSTRING (Vexec_directory)->size + 1);
19660252Shibler strcpy (name, XSTRING (Vexec_directory)->data);
19760252Shibler #endif /* CANNOT_DUMP */
19860252Shibler strcat (name, XSTRING (filename)->data); /*** Add this line ***/
19960252Shibler #ifdef VMS
20060252Shibler #ifndef VMS4_4
20160252Shibler /* For VMS versions with limited file name syntax,
20260252Shibler convert the name to something VMS will allow. */
20360252Shibler p = name;
20460252Shibler while (*p)
20560252Shibler {
20660252Shibler if (*p == '-')
20760252Shibler *p = '_';
20860252Shibler p++;
20960252Shibler }
21060252Shibler #endif /* not VMS4_4 */
21160252Shibler #ifdef VMS4_4
21260252Shibler strcpy (name, sys_translate_unix (name));
21360252Shibler #endif /* VMS4_4 */
21460252Shibler #endif /* VMS */
21560252Shibler
21660252Shibler fd = open (name, O_RDONLY, 0);
21760252Shibler if (fd < 0)
21860252Shibler report_file_error ("Opening doc string file",
21960252Shibler Fcons (build_string (name), Qnil));
22060252Shibler Vdoc_file_name = filename;
22160252Shibler filled = 0;
22260252Shibler pos = 0;
22360252Shibler while (1)
22460252Shibler {
22560252Shibler if (filled < 512)
22660252Shibler filled += read (fd, &buf[filled], sizeof buf - 1 - filled);
22760252Shibler if (!filled)
22860252Shibler break;
22960252Shibler
23060252Shibler buf[filled] = 0;
23160252Shibler p = buf;
23260252Shibler end = buf + (filled < 512 ? filled : filled - 128);
23360252Shibler while (p != end && *p != '\037') p++;
23460252Shibler /* p points to ^_Ffunctionname\n or ^_Vvarname\n. */
23560252Shibler if (p != end)
23660252Shibler {
23760252Shibler end = index (p, '\n');
23860252Shibler sym = oblookup (Vobarray, p + 2, end - p - 2);
23960252Shibler if (XTYPE (sym) == Lisp_Symbol)
24060252Shibler {
24160252Shibler if (p[1] == 'V')
24260252Shibler {
24360252Shibler /* Install file-position as variable-documentation property
24460252Shibler and make it negative for a user-variable
24560252Shibler (doc starts with a `*'). */
24660252Shibler Fput (sym, Qvariable_documentation,
24760252Shibler make_number ((pos + end + 1 - buf)
24860252Shibler * (end[1] == '*' ? -1 : 1)));
24960252Shibler }
25060252Shibler else if (p[1] == 'F')
25160252Shibler {
25260252Shibler fun = XSYMBOL (sym)->function;
25360252Shibler if (XTYPE (fun) == Lisp_Subr)
25460252Shibler XSUBR (fun)->doc = (char *) - (pos + end + 1 - buf);
25560252Shibler else if (CONSP (fun))
25660252Shibler {
25760252Shibler tem = XCONS (fun)->car;
25860252Shibler if (EQ (tem, Qlambda) || EQ (tem, Qautoload))
25960252Shibler {
26060252Shibler tem = Fcdr (Fcdr (fun));
26160252Shibler if (CONSP (tem) &&
26260252Shibler XTYPE (XCONS (tem)->car) == Lisp_Int)
26360252Shibler XFASTINT (XCONS (tem)->car) = (pos + end + 1 - buf);
26460252Shibler }
26560252Shibler }
26660252Shibler }
26760252Shibler else error ("DOC file invalid at position %d", pos);
26860252Shibler }
26960252Shibler }
27060252Shibler pos += end - buf;
27160252Shibler filled -= end - buf;
27260252Shibler bcopy (end, buf, filled);
27360252Shibler }
27460252Shibler close (fd);
27560252Shibler return Qnil;
27660252Shibler }
27760252Shibler
27860252Shibler DEFUN ("substitute-command-keys", Fsubstitute_command_keys,
27960252Shibler Ssubstitute_command_keys, 1, 1, 0,
28060252Shibler "Return the STRING with substrings of the form \\=\\[COMMAND]\n\
28160252Shibler replaced by either: a keystroke sequence that will invoke COMMAND,\n\
28260252Shibler or \"M-x COMMAND\" if COMMAND is not on any keys.\n\
28360252Shibler Substrings of the form \\=\\{MAPVAR} are replaced by summaries\n\
28460252Shibler \(made by describe-bindings) of the value of MAPVAR, taken as a keymap.\n\
28560252Shibler Substrings of the form \\=\\<MAPVAR> specify to use the value of MAPVAR\n\
28660252Shibler as the keymap for future \\=\\[COMMAND] substrings.\n\
28760252Shibler \\=\\= quotes the following character and is discarded;\n\
28860252Shibler thus, \\=\\=\\=\\= puts \\=\\= into the output, and \\=\\=\\=\\[ puts \\=\\[ into the output.")
28960252Shibler (str)
29060252Shibler Lisp_Object str;
29160252Shibler {
29260252Shibler unsigned char *buf;
29360252Shibler int changed = 0;
29460252Shibler register unsigned char *strp;
29560252Shibler register unsigned char *bufp;
29660252Shibler int idx;
29760252Shibler int bsize;
29860252Shibler unsigned char *new;
29960252Shibler register Lisp_Object tem;
30060252Shibler Lisp_Object keymap;
30160252Shibler unsigned char *start;
30260252Shibler int length;
30360252Shibler struct gcpro gcpro1;
30460252Shibler
30560252Shibler if (NULL (str))
30660252Shibler return Qnil;
30760252Shibler
30860252Shibler CHECK_STRING (str, 0);
30960252Shibler GCPRO1 (str);
31060252Shibler
31160252Shibler keymap = current_buffer->keymap;
31260252Shibler
31360252Shibler bsize = XSTRING (str)->size;
31460252Shibler bufp = buf = (unsigned char *) xmalloc (bsize);
31560252Shibler
31660252Shibler strp = (unsigned char *) XSTRING (str)->data;
31760252Shibler while (strp - (unsigned char *) XSTRING (str)->data < XSTRING (str)->size)
31860252Shibler {
31960252Shibler if (strp[0] == '\\' && strp[1] == '=')
32060252Shibler {
32160252Shibler /* \= quotes the next character;
32260252Shibler thus, to put in \[ without its special meaning, use \=\[. */
32360252Shibler changed = 1;
32460252Shibler *bufp++ = strp[2];
32560252Shibler strp += 3;
32660252Shibler }
32760252Shibler else if (strp[0] == '\\' && strp[1] == '[')
32860252Shibler {
32960252Shibler changed = 1;
33060252Shibler strp += 2; /* skip \[ */
33160252Shibler start = strp;
33260252Shibler
33360252Shibler while (strp - (unsigned char *) XSTRING (str)->data < XSTRING (str)->size
33460252Shibler && *strp != ']')
33560252Shibler strp++;
33660252Shibler length = strp - start;
33760252Shibler strp++; /* skip ] */
33860252Shibler
33960252Shibler /* Save STRP in IDX. */
34060252Shibler idx = strp - (unsigned char *) XSTRING (str)->data;
34160252Shibler tem = Fintern (make_string (start, length), Qnil);
34260252Shibler tem = Fwhere_is_internal (tem, keymap, Qt);
34360252Shibler
34460252Shibler if (NULL (tem)) /* but not on any keys */
34560252Shibler {
34660252Shibler new = (unsigned char *) xrealloc (buf, bsize += 4);
34760252Shibler bufp += new - buf;
34860252Shibler buf = new;
34960252Shibler bcopy ("M-x ", bufp, 4);
35060252Shibler bufp += 4;
35160252Shibler goto subst;
35260252Shibler }
35360252Shibler else
35460252Shibler { /* function is on a key */
35560252Shibler tem = Fkey_description (tem);
35660252Shibler goto subst_string;
35760252Shibler }
35860252Shibler }
35960252Shibler /* \{foo} is replaced with a summary of the keymap (symeval foo).
36060252Shibler \<foo> just sets the keymap used for \[cmd]. */
36160252Shibler else if (strp[0] == '\\' && (strp[1] == '{' || strp[1] == '<'))
36260252Shibler {
36360252Shibler struct buffer *oldbuf;
36460252Shibler Lisp_Object name;
36560252Shibler
36660252Shibler changed = 1;
36760252Shibler strp += 2; /* skip \{ or \< */
36860252Shibler start = strp;
36960252Shibler
37060252Shibler while (strp - (unsigned char *) XSTRING (str)->data < XSTRING (str)->size
37160252Shibler && *strp != '}' && *strp != '>')
37260252Shibler strp++;
37360252Shibler length = strp - start;
37460252Shibler strp++; /* skip } or > */
37560252Shibler
37660252Shibler /* Save STRP in IDX. */
37760252Shibler idx = strp - (unsigned char *) XSTRING (str)->data;
37860252Shibler
37960252Shibler oldbuf = current_buffer;
38060252Shibler set_buffer_internal (XBUFFER (Vprin1_to_string_buffer));
38160252Shibler name = Fintern (make_string (start, length), Qnil);
38260252Shibler if ((tem = (Fboundp (name)), NULL (tem)) ||
38360252Shibler (tem = (Fsymbol_value (name)), NULL (tem)) ||
38460252Shibler (tem = (get_keymap_1 (tem, 0)), NULL (tem)))
38560252Shibler {
38660252Shibler name = Fsymbol_name (name);
38760252Shibler InsStr ("\nUses keymap \"");
38860252Shibler insert (XSTRING (name)->data, XSTRING (name)->size);
38960252Shibler InsStr ("\", which is not currently defined.\n");
39060252Shibler if (start[-1] == '<') keymap = Qnil;
39160252Shibler }
39260252Shibler else if (start[-1] == '<')
39360252Shibler keymap = tem;
39460252Shibler else
39560252Shibler describe_map_tree (tem, 1, Qnil);
39660252Shibler tem = Fbuffer_string ();
39760252Shibler Ferase_buffer ();
39860252Shibler set_buffer_internal (oldbuf);
39960252Shibler
40060252Shibler subst_string:
40160252Shibler start = XSTRING (tem)->data;
40260252Shibler length = XSTRING (tem)->size;
40360252Shibler subst:
40460252Shibler new = (unsigned char *) xrealloc (buf, bsize += length);
40560252Shibler bufp += new - buf;
40660252Shibler buf = new;
40760252Shibler bcopy (start, bufp, length);
40860252Shibler bufp += length;
40960252Shibler /* Check STR again in case gc relocated it. */
41060252Shibler strp = (unsigned char *) XSTRING (str)->data + idx;
41160252Shibler }
41260252Shibler else /* just copy other chars */
41360252Shibler *bufp++ = *strp++;
41460252Shibler }
41560252Shibler
41660252Shibler if (changed) /* don't bother if nothing substituted */
41760252Shibler tem = make_string (buf, bufp - buf);
41860252Shibler else
41960252Shibler tem = str;
42060252Shibler UNGCPRO;
42160252Shibler free (buf);
42260252Shibler return tem;
42360252Shibler }
42460252Shibler
syms_of_doc()42560252Shibler syms_of_doc ()
42660252Shibler {
42760252Shibler staticpro (&Vdoc_file_name);
42860252Shibler Vdoc_file_name = Qnil;
42960252Shibler
43060252Shibler defsubr (&Sdocumentation);
43160252Shibler defsubr (&Sdocumentation_property);
43260252Shibler defsubr (&Ssnarf_documentation);
43360252Shibler defsubr (&Ssubstitute_command_keys);
43460252Shibler }
435