static void store_function_docstring (Lisp_Object obj, ptrdiff_t offset) { /* Don't use indirect_function here, or defaliases will apply their docstrings to the base functions (Bug#2603). */ Lisp_Object fun = SYMBOLP (obj) ? XSYMBOL (obj)->function : obj; /* The type determines where the docstring is stored. */ /* Lisp_Subrs have a slot for it. */ if (SUBRP (fun)) { intptr_t negative_offset = - offset; XSUBR (fun)->doc = (char *) negative_offset; } /* If it's a lisp form, stick it in the form. */ else if (CONSP (fun)) { Lisp_Object tem; tem = XCAR (fun); if (EQ (tem, Qlambda) || EQ (tem, Qautoload) || (EQ (tem, Qclosure) && (fun = XCDR (fun), 1))) { tem = Fcdr (Fcdr (fun)); if (CONSP (tem) && INTEGERP (XCAR (tem))) /* FIXME: This modifies typically pure hash-cons'd data, so its correctness is quite delicate. */ XSETCAR (tem, make_number (offset)); } else if (EQ (tem, Qmacro)) store_function_docstring (XCDR (fun), offset); } /* Bytecode objects sometimes have slots for it. */ else if (COMPILEDP (fun)) { /* This bytecode object must have a slot for the docstring, since we've found a docstring for it. */ if ((ASIZE (fun) & PSEUDOVECTOR_SIZE_MASK) > COMPILED_DOC_STRING) ASET (fun, COMPILED_DOC_STRING, make_number (offset)); else { AUTO_STRING (format, "No docstring slot for %s"); CALLN (Fmessage, format, (SYMBOLP (obj) ? SYMBOL_NAME (obj) : build_string ("<anonymous>"))); } } }
static void single_menu_item (Lisp_Object key, Lisp_Object item, Lisp_Object dummy, void *skp_v) { Lisp_Object map, item_string, enabled; bool res; struct skp *skp = skp_v; /* Parse the menu item and leave the result in item_properties. */ res = parse_menu_item (item, 0); if (!res) return; /* Not a menu item. */ map = AREF (item_properties, ITEM_PROPERTY_MAP); enabled = AREF (item_properties, ITEM_PROPERTY_ENABLE); item_string = AREF (item_properties, ITEM_PROPERTY_NAME); if (!NILP (map) && SREF (item_string, 0) == '@') { if (!NILP (enabled)) /* An enabled separate pane. Remember this to handle it later. */ skp->pending_maps = Fcons (Fcons (map, Fcons (item_string, key)), skp->pending_maps); return; } /* Simulate radio buttons and toggle boxes by putting a prefix in front of them. */ if (!have_boxes ()) { char const *prefix = 0; Lisp_Object type = AREF (item_properties, ITEM_PROPERTY_TYPE); if (!NILP (type)) { Lisp_Object selected = AREF (item_properties, ITEM_PROPERTY_SELECTED); if (skp->notbuttons) /* The first button. Line up previous items in this menu. */ { int idx = skp->notbuttons; /* Index for first item this menu. */ int submenu = 0; Lisp_Object tem; while (idx < menu_items_used) { tem = AREF (menu_items, idx + MENU_ITEMS_ITEM_NAME); if (NILP (tem)) { idx++; submenu++; /* Skip sub menu. */ } else if (EQ (tem, Qlambda)) { idx++; submenu--; /* End sub menu. */ } else if (EQ (tem, Qt)) idx += 3; /* Skip new pane marker. */ else if (EQ (tem, Qquote)) idx++; /* Skip a left, right divider. */ else { if (!submenu && SREF (tem, 0) != '\0' && SREF (tem, 0) != '-') { AUTO_STRING (spaces, " "); ASET (menu_items, idx + MENU_ITEMS_ITEM_NAME, concat2 (spaces, tem)); } idx += MENU_ITEMS_ITEM_LENGTH; } } skp->notbuttons = 0; } /* Calculate prefix, if any, for this item. */ if (EQ (type, QCtoggle)) prefix = NILP (selected) ? "[ ] " : "[X] "; else if (EQ (type, QCradio)) prefix = NILP (selected) ? "( ) " : "(*) "; } /* Not a button. If we have earlier buttons, then we need a prefix. */ else if (!skp->notbuttons && SREF (item_string, 0) != '\0' && SREF (item_string, 0) != '-') prefix = " "; if (prefix) { AUTO_STRING (prefix_obj, prefix); item_string = concat2 (prefix_obj, item_string); } } if ((FRAME_TERMCAP_P (XFRAME (Vmenu_updating_frame)) || FRAME_MSDOS_P (XFRAME (Vmenu_updating_frame))) && !NILP (map)) /* Indicate visually that this is a submenu. */ { AUTO_STRING (space_gt, " >"); item_string = concat2 (item_string, space_gt); } push_menu_item (item_string, enabled, key, AREF (item_properties, ITEM_PROPERTY_DEF), AREF (item_properties, ITEM_PROPERTY_KEYEQ), AREF (item_properties, ITEM_PROPERTY_TYPE), AREF (item_properties, ITEM_PROPERTY_SELECTED), AREF (item_properties, ITEM_PROPERTY_HELP)); #if defined (USE_X_TOOLKIT) || defined (USE_GTK) || defined (HAVE_NS) || defined (HAVE_NTGUI) /* Display a submenu using the toolkit. */ if (FRAME_WINDOW_P (XFRAME (Vmenu_updating_frame)) && ! (NILP (map) || NILP (enabled))) { push_submenu_start (); single_keymap_panes (map, Qnil, key, skp->maxdepth - 1); push_submenu_end (); } #endif }
Lisp_Object get_doc_string (Lisp_Object filepos, bool unibyte, bool definition) { char *from, *to, *name, *p, *p1; int fd; int offset; EMACS_INT position; Lisp_Object file, tem, pos; ptrdiff_t count; USE_SAFE_ALLOCA; if (INTEGERP (filepos)) { file = Vdoc_file_name; pos = filepos; } else if (CONSP (filepos)) { file = XCAR (filepos); pos = XCDR (filepos); } else return Qnil; position = eabs (XINT (pos)); if (!STRINGP (Vdoc_directory)) return Qnil; if (!STRINGP (file)) return Qnil; /* Put the file name in NAME as a C string. If it is relative, combine it with Vdoc_directory. */ tem = Ffile_name_absolute_p (file); file = ENCODE_FILE (file); Lisp_Object docdir = NILP (tem) ? ENCODE_FILE (Vdoc_directory) : empty_unibyte_string; ptrdiff_t docdir_sizemax = SBYTES (docdir) + 1; #ifndef CANNOT_DUMP docdir_sizemax = max (docdir_sizemax, sizeof sibling_etc); #endif name = SAFE_ALLOCA (docdir_sizemax + SBYTES (file)); lispstpcpy (lispstpcpy (name, docdir), file); fd = emacs_open (name, O_RDONLY, 0); if (fd < 0) { #ifndef CANNOT_DUMP if (!NILP (Vpurify_flag)) { /* Preparing to dump; DOC file is probably not installed. So check in ../etc. */ lispstpcpy (stpcpy (name, sibling_etc), file); fd = emacs_open (name, O_RDONLY, 0); } #endif if (fd < 0) { if (errno == EMFILE || errno == ENFILE) report_file_error ("Read error on documentation file", file); SAFE_FREE (); AUTO_STRING (cannot_open, "Cannot open doc string file \""); AUTO_STRING (quote_nl, "\"\n"); return concat3 (cannot_open, file, quote_nl); } } count = SPECPDL_INDEX (); record_unwind_protect_int (close_file_unwind, fd); /* Seek only to beginning of disk block. */ /* Make sure we read at least 1024 bytes before `position' so we can check the leading text for consistency. */ offset = min (position, max (1024, position % (8 * 1024))); if (TYPE_MAXIMUM (off_t) < position || lseek (fd, position - offset, 0) < 0) error ("Position %"pI"d out of range in doc string file \"%s\"", position, name); /* Read the doc string into get_doc_string_buffer. P points beyond the data just read. */ p = get_doc_string_buffer; while (1) { ptrdiff_t space_left = (get_doc_string_buffer_size - 1 - (p - get_doc_string_buffer)); int nread; /* Allocate or grow the buffer if we need to. */ if (space_left <= 0) { ptrdiff_t in_buffer = p - get_doc_string_buffer; get_doc_string_buffer = xpalloc (get_doc_string_buffer, &get_doc_string_buffer_size, 16 * 1024, -1, 1); p = get_doc_string_buffer + in_buffer; space_left = (get_doc_string_buffer_size - 1 - (p - get_doc_string_buffer)); } /* Read a disk block at a time. If we read the same block last time, maybe skip this? */ if (space_left > 1024 * 8) space_left = 1024 * 8; nread = emacs_read (fd, p, space_left); if (nread < 0) report_file_error ("Read error on documentation file", file); p[nread] = 0; if (!nread) break; if (p == get_doc_string_buffer) p1 = strchr (p + offset, '\037'); else p1 = strchr (p, '\037'); if (p1) { *p1 = 0; p = p1; break; } p += nread; } unbind_to (count, Qnil); SAFE_FREE (); /* Sanity checking. */ if (CONSP (filepos)) { int test = 1; /* A dynamic docstring should be either at the very beginning of a "#@ comment" or right after a dynamic docstring delimiter (in case we pack several such docstrings within the same comment). */ if (get_doc_string_buffer[offset - test] != '\037') { if (get_doc_string_buffer[offset - test++] != ' ') return Qnil; while (get_doc_string_buffer[offset - test] >= '0' && get_doc_string_buffer[offset - test] <= '9') test++; if (get_doc_string_buffer[offset - test++] != '@' || get_doc_string_buffer[offset - test] != '#') return Qnil; } } else { int test = 1; if (get_doc_string_buffer[offset - test++] != '\n') return Qnil; while (get_doc_string_buffer[offset - test] > ' ') test++; if (get_doc_string_buffer[offset - test] != '\037') return Qnil; } /* Scan the text and perform quoting with ^A (char code 1). ^A^A becomes ^A, ^A0 becomes a null char, and ^A_ becomes a ^_. */ from = get_doc_string_buffer + offset; to = get_doc_string_buffer + offset; while (from != p) { if (*from == 1) { int c; from++; c = *from++; if (c == 1) *to++ = c; else if (c == '0') *to++ = 0; else if (c == '_') *to++ = 037; else { unsigned char uc = c; error ("\ Invalid data in documentation file -- %c followed by code %03o", 1, uc); } } else *to++ = *from++; } /* If DEFINITION, read from this buffer the same way we would read bytes from a file. */ if (definition) { read_bytecode_pointer = (unsigned char *) get_doc_string_buffer + offset; return Fread (Qlambda); } if (unibyte) return make_unibyte_string (get_doc_string_buffer + offset, to - (get_doc_string_buffer + offset)); else { /* The data determines whether the string is multibyte. */ ptrdiff_t nchars = multibyte_chars_in_text (((unsigned char *) get_doc_string_buffer + offset), to - (get_doc_string_buffer + offset)); return make_string_from_bytes (get_doc_string_buffer + offset, nchars, to - (get_doc_string_buffer + offset)); } }