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