xref: /csrg-svn/contrib/emacs-18.57/src/doc.c (revision 60252)
1*60252Shibler /* Record indices of function doc strings stored in a file.
2*60252Shibler    Copyright (C) 1985, 1986 Free Software Foundation, Inc.
3*60252Shibler 
4*60252Shibler This file is part of GNU Emacs.
5*60252Shibler 
6*60252Shibler GNU Emacs is free software; you can redistribute it and/or modify
7*60252Shibler it under the terms of the GNU General Public License as published by
8*60252Shibler the Free Software Foundation; either version 1, or (at your option)
9*60252Shibler any later version.
10*60252Shibler 
11*60252Shibler GNU Emacs is distributed in the hope that it will be useful,
12*60252Shibler but WITHOUT ANY WARRANTY; without even the implied warranty of
13*60252Shibler MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*60252Shibler GNU General Public License for more details.
15*60252Shibler 
16*60252Shibler You should have received a copy of the GNU General Public License
17*60252Shibler along with GNU Emacs; see the file COPYING.  If not, write to
18*60252Shibler the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
19*60252Shibler 
20*60252Shibler 
21*60252Shibler #include "config.h"
22*60252Shibler #include "lisp.h"
23*60252Shibler #include "buffer.h"
24*60252Shibler 
25*60252Shibler #include <sys/types.h>
26*60252Shibler #include <sys/file.h>	/* Must be after sys/types.h for USG and BSD4_1*/
27*60252Shibler 
28*60252Shibler #ifdef USG5
29*60252Shibler #include <fcntl.h>
30*60252Shibler #endif
31*60252Shibler 
32*60252Shibler #ifndef O_RDONLY
33*60252Shibler #define O_RDONLY 0
34*60252Shibler #endif
35*60252Shibler 
36*60252Shibler Lisp_Object Vdoc_file_name;
37*60252Shibler 
38*60252Shibler Lisp_Object
39*60252Shibler get_doc_string (filepos)
40*60252Shibler      long filepos;
41*60252Shibler {
42*60252Shibler   char buf[512 * 32 + 1];
43*60252Shibler   register int fd;
44*60252Shibler   register char *name;
45*60252Shibler   register char *p, *p1;
46*60252Shibler   register int count;
47*60252Shibler   extern char *index ();
48*60252Shibler 
49*60252Shibler   if (XTYPE (Vexec_directory) != Lisp_String
50*60252Shibler       || XTYPE (Vdoc_file_name) != Lisp_String)
51*60252Shibler     return Qnil;
52*60252Shibler 
53*60252Shibler   name = (char *) alloca (XSTRING (Vexec_directory)->size
54*60252Shibler 			  + XSTRING (Vdoc_file_name)->size + 8);
55*60252Shibler   strcpy (name, XSTRING (Vexec_directory)->data);
56*60252Shibler   strcat (name, XSTRING (Vdoc_file_name)->data);
57*60252Shibler #ifdef VMS
58*60252Shibler #ifndef VMS4_4
59*60252Shibler   /* For VMS versions with limited file name syntax,
60*60252Shibler      convert the name to something VMS will allow.  */
61*60252Shibler   p = name;
62*60252Shibler   while (*p)
63*60252Shibler     {
64*60252Shibler       if (*p == '-')
65*60252Shibler 	*p = '_';
66*60252Shibler       p++;
67*60252Shibler     }
68*60252Shibler #endif /* not VMS4_4 */
69*60252Shibler #ifdef VMS4_4
70*60252Shibler   strcpy (name, sys_translate_unix (name));
71*60252Shibler #endif /* VMS4_4 */
72*60252Shibler #endif /* VMS */
73*60252Shibler 
74*60252Shibler   fd = open (name, O_RDONLY, 0);
75*60252Shibler   if (fd < 0)
76*60252Shibler     error ("Cannot open doc string file \"%s\"", name);
77*60252Shibler   if (0 > lseek (fd, filepos, 0))
78*60252Shibler     {
79*60252Shibler       close (fd);
80*60252Shibler       error ("Position %ld out of range in doc string file \"%s\"",
81*60252Shibler 	     filepos, name);
82*60252Shibler     }
83*60252Shibler   p = buf;
84*60252Shibler   while (p != buf + sizeof buf - 1)
85*60252Shibler     {
86*60252Shibler       count = read (fd, p, 512);
87*60252Shibler       p[count] = 0;
88*60252Shibler       if (!count)
89*60252Shibler 	break;
90*60252Shibler       p1 = index (p, '\037');
91*60252Shibler       if (p1)
92*60252Shibler 	{
93*60252Shibler 	  *p1 = 0;
94*60252Shibler 	  p = p1;
95*60252Shibler 	  break;
96*60252Shibler 	}
97*60252Shibler       p += count;
98*60252Shibler     }
99*60252Shibler   close (fd);
100*60252Shibler   return make_string (buf, p - buf);
101*60252Shibler }
102*60252Shibler 
103*60252Shibler DEFUN ("documentation", Fdocumentation, Sdocumentation, 1, 1, 0,
104*60252Shibler   "Return the documentation string of FUNCTION.")
105*60252Shibler   (fun1)
106*60252Shibler      Lisp_Object fun1;
107*60252Shibler {
108*60252Shibler   Lisp_Object fun;
109*60252Shibler   Lisp_Object funcar;
110*60252Shibler   Lisp_Object tem;
111*60252Shibler 
112*60252Shibler   fun = fun1;
113*60252Shibler   while (XTYPE (fun) == Lisp_Symbol)
114*60252Shibler     fun = Fsymbol_function (fun);
115*60252Shibler   if (XTYPE (fun) == Lisp_Subr)
116*60252Shibler     {
117*60252Shibler       if (XSUBR (fun)->doc == 0) return Qnil;
118*60252Shibler       if ((int) XSUBR (fun)->doc >= 0)
119*60252Shibler 	return Fsubstitute_command_keys (build_string (XSUBR (fun)->doc));
120*60252Shibler       return Fsubstitute_command_keys (get_doc_string (- (int) XSUBR (fun)->doc));
121*60252Shibler     }
122*60252Shibler   if (XTYPE (fun) == Lisp_Vector)
123*60252Shibler     return build_string ("Prefix command (definition is a Lisp vector of subcommands).");
124*60252Shibler   if (XTYPE (fun) == Lisp_String)
125*60252Shibler     return build_string ("Keyboard macro.");
126*60252Shibler   if (!CONSP (fun))
127*60252Shibler     return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
128*60252Shibler   funcar = Fcar (fun);
129*60252Shibler   if (XTYPE (funcar) != Lisp_Symbol)
130*60252Shibler     return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
131*60252Shibler   if (XSYMBOL (funcar) == XSYMBOL (Qkeymap))
132*60252Shibler     return build_string ("Prefix command (definition is a list whose cdr is an alist of subcommands.)");
133*60252Shibler   if (XSYMBOL (funcar) == XSYMBOL (Qlambda)
134*60252Shibler       || XSYMBOL (funcar) == XSYMBOL (Qautoload))
135*60252Shibler     {
136*60252Shibler       tem = Fcar (Fcdr (Fcdr (fun)));
137*60252Shibler       if (XTYPE (tem) == Lisp_String)
138*60252Shibler 	return Fsubstitute_command_keys (tem);
139*60252Shibler       if (XTYPE (tem) == Lisp_Int && XINT (tem) >= 0)
140*60252Shibler 	return Fsubstitute_command_keys (get_doc_string (XFASTINT (tem)));
141*60252Shibler       return Qnil;
142*60252Shibler     }
143*60252Shibler   if (XSYMBOL (funcar) == XSYMBOL (Qmocklisp))
144*60252Shibler     return Qnil;
145*60252Shibler   if (XSYMBOL (funcar) == XSYMBOL (Qmacro))
146*60252Shibler     return Fdocumentation (Fcdr (fun));
147*60252Shibler   else
148*60252Shibler     return Fsignal (Qinvalid_function, Fcons (fun, Qnil));
149*60252Shibler }
150*60252Shibler 
151*60252Shibler DEFUN ("documentation-property", Fdocumentation_property,
152*60252Shibler        Sdocumentation_property, 2, 2, 0,
153*60252Shibler   "Return the documentation string that is SYMBOL's PROP property.\n\
154*60252Shibler This differs from using `get' only in that it can refer to strings\n\
155*60252Shibler stored in the etc/DOC file.")
156*60252Shibler   (sym, prop)
157*60252Shibler      Lisp_Object sym, prop;
158*60252Shibler {
159*60252Shibler   register Lisp_Object tem;
160*60252Shibler 
161*60252Shibler   tem = Fget (sym, prop);
162*60252Shibler   if (XTYPE (tem) == Lisp_Int)
163*60252Shibler     tem = get_doc_string (XINT (tem) > 0 ? XINT (tem) : - XINT (tem));
164*60252Shibler   return Fsubstitute_command_keys (tem);
165*60252Shibler }
166*60252Shibler 
167*60252Shibler DEFUN ("Snarf-documentation", Fsnarf_documentation, Ssnarf_documentation,
168*60252Shibler   1, 1, 0,
169*60252Shibler   "Used during Emacs initialization, before dumping runnable Emacs,\n\
170*60252Shibler to find pointers to doc strings stored in etc/DOC... and\n\
171*60252Shibler record them in function definitions.\n\
172*60252Shibler One arg, FILENAME, a string which does not include a directory.\n\
173*60252Shibler The file is found in ../etc now; found in the exec-directory\n\
174*60252Shibler when doc strings are referred to later in the dumped Emacs.")
175*60252Shibler   (filename)
176*60252Shibler      Lisp_Object filename;
177*60252Shibler {
178*60252Shibler   int fd;
179*60252Shibler   char buf[1024 + 1];
180*60252Shibler   register int filled;
181*60252Shibler   register int pos;
182*60252Shibler   register char *p, *end;
183*60252Shibler   Lisp_Object sym, fun, tem;
184*60252Shibler   char *name;
185*60252Shibler   extern char *index ();
186*60252Shibler 
187*60252Shibler   CHECK_STRING (filename, 0);
188*60252Shibler 
189*60252Shibler #ifndef CANNOT_DUMP
190*60252Shibler   name = (char *) alloca (XSTRING (filename)->size + 8);
191*60252Shibler   strcpy (name, "../etc/");
192*60252Shibler #else /* CANNOT_DUMP */
193*60252Shibler   CHECK_STRING (Vexec_directory, 0);
194*60252Shibler   name = (char *) alloca (XSTRING (filename)->size +
195*60252Shibler 			  XSTRING (Vexec_directory)->size + 1);
196*60252Shibler   strcpy (name, XSTRING (Vexec_directory)->data);
197*60252Shibler #endif /* CANNOT_DUMP */
198*60252Shibler   strcat (name, XSTRING (filename)->data); 	/*** Add this line ***/
199*60252Shibler #ifdef VMS
200*60252Shibler #ifndef VMS4_4
201*60252Shibler   /* For VMS versions with limited file name syntax,
202*60252Shibler      convert the name to something VMS will allow.  */
203*60252Shibler   p = name;
204*60252Shibler   while (*p)
205*60252Shibler     {
206*60252Shibler       if (*p == '-')
207*60252Shibler 	*p = '_';
208*60252Shibler       p++;
209*60252Shibler     }
210*60252Shibler #endif /* not VMS4_4 */
211*60252Shibler #ifdef VMS4_4
212*60252Shibler   strcpy (name, sys_translate_unix (name));
213*60252Shibler #endif /* VMS4_4 */
214*60252Shibler #endif /* VMS */
215*60252Shibler 
216*60252Shibler   fd = open (name, O_RDONLY, 0);
217*60252Shibler   if (fd < 0)
218*60252Shibler     report_file_error ("Opening doc string file",
219*60252Shibler 		       Fcons (build_string (name), Qnil));
220*60252Shibler   Vdoc_file_name = filename;
221*60252Shibler   filled = 0;
222*60252Shibler   pos = 0;
223*60252Shibler   while (1)
224*60252Shibler     {
225*60252Shibler       if (filled < 512)
226*60252Shibler 	filled += read (fd, &buf[filled], sizeof buf - 1 - filled);
227*60252Shibler       if (!filled)
228*60252Shibler 	break;
229*60252Shibler 
230*60252Shibler       buf[filled] = 0;
231*60252Shibler       p = buf;
232*60252Shibler       end = buf + (filled < 512 ? filled : filled - 128);
233*60252Shibler       while (p != end && *p != '\037') p++;
234*60252Shibler       /* p points to ^_Ffunctionname\n or ^_Vvarname\n.  */
235*60252Shibler       if (p != end)
236*60252Shibler 	{
237*60252Shibler 	  end = index (p, '\n');
238*60252Shibler 	  sym = oblookup (Vobarray, p + 2, end - p - 2);
239*60252Shibler 	  if (XTYPE (sym) == Lisp_Symbol)
240*60252Shibler 	    {
241*60252Shibler 	      if (p[1] == 'V')
242*60252Shibler 		{
243*60252Shibler 		  /* Install file-position as variable-documentation property
244*60252Shibler 		     and make it negative for a user-variable
245*60252Shibler 		     (doc starts with a `*').  */
246*60252Shibler 		  Fput (sym, Qvariable_documentation,
247*60252Shibler 			make_number ((pos + end + 1 - buf)
248*60252Shibler 				     * (end[1] == '*' ? -1 : 1)));
249*60252Shibler 		}
250*60252Shibler 	      else if (p[1] == 'F')
251*60252Shibler 		{
252*60252Shibler 		  fun = XSYMBOL (sym)->function;
253*60252Shibler 		  if (XTYPE (fun) == Lisp_Subr)
254*60252Shibler 		    XSUBR (fun)->doc = (char *) - (pos + end + 1 - buf);
255*60252Shibler 		  else if (CONSP (fun))
256*60252Shibler 		    {
257*60252Shibler 		      tem = XCONS (fun)->car;
258*60252Shibler 		      if (EQ (tem, Qlambda) || EQ (tem, Qautoload))
259*60252Shibler 			{
260*60252Shibler 			  tem = Fcdr (Fcdr (fun));
261*60252Shibler 			  if (CONSP (tem) &&
262*60252Shibler 			      XTYPE (XCONS (tem)->car) == Lisp_Int)
263*60252Shibler 			    XFASTINT (XCONS (tem)->car) = (pos + end + 1 - buf);
264*60252Shibler 			}
265*60252Shibler 		    }
266*60252Shibler 		}
267*60252Shibler 	      else error ("DOC file invalid at position %d", pos);
268*60252Shibler 	    }
269*60252Shibler 	}
270*60252Shibler       pos += end - buf;
271*60252Shibler       filled -= end - buf;
272*60252Shibler       bcopy (end, buf, filled);
273*60252Shibler     }
274*60252Shibler   close (fd);
275*60252Shibler   return Qnil;
276*60252Shibler }
277*60252Shibler 
278*60252Shibler DEFUN ("substitute-command-keys", Fsubstitute_command_keys,
279*60252Shibler   Ssubstitute_command_keys, 1, 1, 0,
280*60252Shibler   "Return the STRING with substrings of the form \\=\\[COMMAND]\n\
281*60252Shibler replaced by either:  a keystroke sequence that will invoke COMMAND,\n\
282*60252Shibler or \"M-x COMMAND\" if COMMAND is not on any keys.\n\
283*60252Shibler Substrings of the form \\=\\{MAPVAR} are replaced by summaries\n\
284*60252Shibler \(made by describe-bindings) of the value of MAPVAR, taken as a keymap.\n\
285*60252Shibler Substrings of the form \\=\\<MAPVAR> specify to use the value of MAPVAR\n\
286*60252Shibler as the keymap for future \\=\\[COMMAND] substrings.\n\
287*60252Shibler \\=\\= quotes the following character and is discarded;\n\
288*60252Shibler thus, \\=\\=\\=\\= puts \\=\\= into the output, and \\=\\=\\=\\[ puts \\=\\[ into the output.")
289*60252Shibler   (str)
290*60252Shibler      Lisp_Object str;
291*60252Shibler {
292*60252Shibler   unsigned char *buf;
293*60252Shibler   int changed = 0;
294*60252Shibler   register unsigned char *strp;
295*60252Shibler   register unsigned char *bufp;
296*60252Shibler   int idx;
297*60252Shibler   int bsize;
298*60252Shibler   unsigned char *new;
299*60252Shibler   register Lisp_Object tem;
300*60252Shibler   Lisp_Object keymap;
301*60252Shibler   unsigned char *start;
302*60252Shibler   int length;
303*60252Shibler   struct gcpro gcpro1;
304*60252Shibler 
305*60252Shibler   if (NULL (str))
306*60252Shibler     return Qnil;
307*60252Shibler 
308*60252Shibler   CHECK_STRING (str, 0);
309*60252Shibler   GCPRO1 (str);
310*60252Shibler 
311*60252Shibler   keymap = current_buffer->keymap;
312*60252Shibler 
313*60252Shibler   bsize = XSTRING (str)->size;
314*60252Shibler   bufp = buf = (unsigned char *) xmalloc (bsize);
315*60252Shibler 
316*60252Shibler   strp = (unsigned char *) XSTRING (str)->data;
317*60252Shibler   while (strp - (unsigned char *) XSTRING (str)->data < XSTRING (str)->size)
318*60252Shibler     {
319*60252Shibler       if (strp[0] == '\\' && strp[1] == '=')
320*60252Shibler 	{
321*60252Shibler 	  /* \= quotes the next character;
322*60252Shibler 	     thus, to put in \[ without its special meaning, use \=\[.  */
323*60252Shibler 	  changed = 1;
324*60252Shibler 	  *bufp++ = strp[2];
325*60252Shibler 	  strp += 3;
326*60252Shibler 	}
327*60252Shibler       else if (strp[0] == '\\' && strp[1] == '[')
328*60252Shibler 	{
329*60252Shibler 	  changed = 1;
330*60252Shibler 	  strp += 2;		/* skip \[ */
331*60252Shibler 	  start = strp;
332*60252Shibler 
333*60252Shibler 	  while (strp - (unsigned char *) XSTRING (str)->data < XSTRING (str)->size
334*60252Shibler 		 && *strp != ']')
335*60252Shibler 	    strp++;
336*60252Shibler 	  length = strp - start;
337*60252Shibler 	  strp++;		/* skip ] */
338*60252Shibler 
339*60252Shibler 	  /* Save STRP in IDX.  */
340*60252Shibler 	  idx = strp - (unsigned char *) XSTRING (str)->data;
341*60252Shibler 	  tem = Fintern (make_string (start, length), Qnil);
342*60252Shibler 	  tem = Fwhere_is_internal (tem, keymap, Qt);
343*60252Shibler 
344*60252Shibler 	  if (NULL (tem))	/* but not on any keys */
345*60252Shibler 	    {
346*60252Shibler 	      new = (unsigned char *) xrealloc (buf, bsize += 4);
347*60252Shibler 	      bufp += new - buf;
348*60252Shibler 	      buf = new;
349*60252Shibler 	      bcopy ("M-x ", bufp, 4);
350*60252Shibler 	      bufp += 4;
351*60252Shibler 	      goto subst;
352*60252Shibler 	    }
353*60252Shibler 	  else
354*60252Shibler 	    {			/* function is on a key */
355*60252Shibler 	      tem = Fkey_description (tem);
356*60252Shibler 	      goto subst_string;
357*60252Shibler 	    }
358*60252Shibler 	}
359*60252Shibler       /* \{foo} is replaced with a summary of the keymap (symeval foo).
360*60252Shibler 	 \<foo> just sets the keymap used for \[cmd].  */
361*60252Shibler       else if (strp[0] == '\\' && (strp[1] == '{' || strp[1] == '<'))
362*60252Shibler 	{
363*60252Shibler 	  struct buffer *oldbuf;
364*60252Shibler 	  Lisp_Object name;
365*60252Shibler 
366*60252Shibler 	  changed = 1;
367*60252Shibler 	  strp += 2;		/* skip \{ or \< */
368*60252Shibler 	  start = strp;
369*60252Shibler 
370*60252Shibler 	  while (strp - (unsigned char *) XSTRING (str)->data < XSTRING (str)->size
371*60252Shibler 		 && *strp != '}' && *strp != '>')
372*60252Shibler 	    strp++;
373*60252Shibler 	  length = strp - start;
374*60252Shibler 	  strp++;			/* skip } or > */
375*60252Shibler 
376*60252Shibler 	  /* Save STRP in IDX.  */
377*60252Shibler 	  idx = strp - (unsigned char *) XSTRING (str)->data;
378*60252Shibler 
379*60252Shibler 	  oldbuf = current_buffer;
380*60252Shibler 	  set_buffer_internal (XBUFFER (Vprin1_to_string_buffer));
381*60252Shibler 	  name = Fintern (make_string (start, length), Qnil);
382*60252Shibler 	  if ((tem = (Fboundp (name)), NULL (tem)) ||
383*60252Shibler 	      (tem = (Fsymbol_value (name)), NULL (tem)) ||
384*60252Shibler 	      (tem = (get_keymap_1 (tem, 0)), NULL (tem)))
385*60252Shibler 	    {
386*60252Shibler 	      name = Fsymbol_name (name);
387*60252Shibler 	      InsStr ("\nUses keymap \"");
388*60252Shibler 	      insert (XSTRING (name)->data, XSTRING (name)->size);
389*60252Shibler 	      InsStr ("\", which is not currently defined.\n");
390*60252Shibler 	      if (start[-1] == '<') keymap = Qnil;
391*60252Shibler 	    }
392*60252Shibler 	  else if (start[-1] == '<')
393*60252Shibler 	    keymap = tem;
394*60252Shibler 	  else
395*60252Shibler 	    describe_map_tree (tem, 1, Qnil);
396*60252Shibler 	  tem = Fbuffer_string ();
397*60252Shibler 	  Ferase_buffer ();
398*60252Shibler 	  set_buffer_internal (oldbuf);
399*60252Shibler 
400*60252Shibler 	subst_string:
401*60252Shibler 	  start = XSTRING (tem)->data;
402*60252Shibler 	  length = XSTRING (tem)->size;
403*60252Shibler 	subst:
404*60252Shibler 	  new = (unsigned char *) xrealloc (buf, bsize += length);
405*60252Shibler 	  bufp += new - buf;
406*60252Shibler 	  buf = new;
407*60252Shibler 	  bcopy (start, bufp, length);
408*60252Shibler 	  bufp += length;
409*60252Shibler 	  /* Check STR again in case gc relocated it.  */
410*60252Shibler 	  strp = (unsigned char *) XSTRING (str)->data + idx;
411*60252Shibler 	}
412*60252Shibler       else			/* just copy other chars */
413*60252Shibler 	*bufp++ = *strp++;
414*60252Shibler      }
415*60252Shibler 
416*60252Shibler   if (changed)			/* don't bother if nothing substituted */
417*60252Shibler     tem = make_string (buf, bufp - buf);
418*60252Shibler   else
419*60252Shibler     tem = str;
420*60252Shibler   UNGCPRO;
421*60252Shibler   free (buf);
422*60252Shibler   return tem;
423*60252Shibler }
424*60252Shibler 
425*60252Shibler syms_of_doc ()
426*60252Shibler {
427*60252Shibler   staticpro (&Vdoc_file_name);
428*60252Shibler   Vdoc_file_name = Qnil;
429*60252Shibler 
430*60252Shibler   defsubr (&Sdocumentation);
431*60252Shibler   defsubr (&Sdocumentation_property);
432*60252Shibler   defsubr (&Ssnarf_documentation);
433*60252Shibler   defsubr (&Ssubstitute_command_keys);
434*60252Shibler }
435