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)); } }
Lisp_Object get_doc_string (Lisp_Object filepos, int unibyte, int definition) { char *from, *to; register int fd; register char *name; register char *p, *p1; EMACS_INT minsize; EMACS_INT offset, position; Lisp_Object file, tem; if (INTEGERP (filepos)) { file = Vdoc_file_name; position = XINT (filepos); } else if (CONSP (filepos)) { file = XCAR (filepos); position = XINT (XCDR (filepos)); } else return Qnil; if (position < 0) position = - position; 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); if (NILP (tem)) { minsize = SCHARS (Vdoc_directory); /* sizeof ("../etc/") == 8 */ if (minsize < 8) minsize = 8; name = (char *) alloca (minsize + SCHARS (file) + 8); strcpy (name, SSDATA (Vdoc_directory)); strcat (name, SSDATA (file)); } else { name = SSDATA (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. */ strcpy (name, "../etc/"); strcat (name, SSDATA (file)); fd = emacs_open (name, O_RDONLY, 0); } #endif if (fd < 0) error ("Cannot open doc string file \"%s\"", name); } /* 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 (0 > lseek (fd, position - offset, 0)) { emacs_close (fd); 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) { EMACS_INT space_left = (get_doc_string_buffer_size - (p - get_doc_string_buffer)); int nread; /* Allocate or grow the buffer if we need to. */ if (space_left == 0) { EMACS_INT in_buffer = p - get_doc_string_buffer; get_doc_string_buffer_size += 16 * 1024; get_doc_string_buffer = (char *) xrealloc (get_doc_string_buffer, get_doc_string_buffer_size + 1); p = get_doc_string_buffer + in_buffer; space_left = (get_doc_string_buffer_size - (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) { emacs_close (fd); error ("Read error on documentation 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; } emacs_close (fd); /* Sanity checking. */ if (CONSP (filepos)) { int test = 1; 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. */ EMACS_INT 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)); } }