xref: /csrg-svn/contrib/emacs-18.57/src/doc.c (revision 60297)
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