Function: insert-file-contents
insert-file-contents is a function defined in fileio.c.
Signature
(insert-file-contents FILENAME &optional VISIT BEG END REPLACE)
Documentation
Insert contents of file FILENAME after point.
Returns list of absolute file name and number of characters inserted. If second argument VISIT is non-nil, the buffer's visited filename and last save file modtime are set, and it is marked unmodified. Signal an error if FILENAME cannot be read or is a directory. If visiting and the file does not exist, visiting is completed before the error is signaled.
The optional third and fourth arguments BEG and END specify what portion of the file to insert. These arguments count bytes in the file, not characters in the buffer. If VISIT is non-nil, BEG and END must be nil.
When inserting data from a special file (e.g., /dev/urandom), you can't specify VISIT or BEG, and END should be specified to avoid inserting unlimited data into the buffer from some special files which otherwise could supply infinite amounts of data.
If optional fifth argument REPLACE is non-nil and FILENAME names a regular file, replace the current buffer contents (in the accessible portion) with the file's contents. This is better than simply deleting and inserting the whole thing because (1) it preserves some marker positions (in unchanged portions at the start and end of the buffer) and (2) it puts less data in the undo list. When REPLACE is non-nil, the second element of the return value is the number of characters that replace the previous buffer contents.
If FILENAME is not a regular file and REPLACE is if-regular, erase
the accessible portion of the buffer and insert the new contents. Any
other non-nil value of REPLACE will signal an error if FILENAME is not
a regular file.
This function does code conversion according to the value of
coding-system-for-read or file-coding-system-alist, and sets the
variable last-coding-system-used to the coding system actually used.
In addition, this function decodes the inserted text from known formats
by calling format-decode, which see.
Other relevant functions are documented in the file group.
Probably introduced at or before Emacs version 1.6.
Shortdoc
;; file
(insert-file-contents "/tmp/foo")
e.g. => ("/tmp/foo" 6)
Source Code
// Defined in /usr/src/emacs/src/fileio.c
// Skipping highlighting due to helpful-max-highlight.
{
/* A good read blocksize for insert-file-contents.
It is for reading a big chunk of a file into memory,
as opposed to coreutils IO_BUFSIZE which is for 'cat'-like stream reads.
If too small, insert-file-contents has more syscall overhead.
If too large, insert-file-contents might take too long respond to a quit.
1 MiB should be reasonable even for older, slower devices circa 2025. */
enum { INSERT_READ_SIZE_MAX = min (1024 * 1024, SYS_BUFSIZE_MAX) };
struct timespec mtime;
emacs_fd fd;
ptrdiff_t inserted = 0;
int unprocessed;
specpdl_ref count = SPECPDL_INDEX ();
Lisp_Object handler, val, insval, orig_filename, old_undo;
Lisp_Object p;
off_t total = 0;
bool regular;
int save_errno = 0;
char read_buf[min (+MAX_ALLOCA, +INSERT_READ_SIZE_MAX)];
struct coding_system coding;
bool replace_handled = false;
bool set_coding_system = false;
Lisp_Object coding_system;
/* errno if read error, 0 if OK so far, negative if quit. */
int read_quit = 0;
/* If the undo log only contains the insertion, there's no point
keeping it. It's typically when we first fill a file-buffer. */
bool empty_undo_list_p
= (!NILP (visit) && NILP (BVAR (current_buffer, undo_list))
&& BEG == Z);
Lisp_Object old_Vdeactivate_mark = Vdeactivate_mark;
bool we_locked_file = false;
Lisp_Object window_markers = Qnil;
/* same_at_start and same_at_end count bytes, because file access counts
bytes and BEG and END count bytes. */
ptrdiff_t same_at_start = BEGV_BYTE;
ptrdiff_t same_at_end = ZV_BYTE;
/* SAME_AT_END_CHARPOS counts characters, because
restore_window_points needs the old character count. */
ptrdiff_t same_at_end_charpos = ZV;
/* The size reported by fstat, or -1 if the file was not found or its
size is meaningless. */
off_t st_size = -1;
if (current_buffer->base_buffer && ! NILP (visit))
error ("Cannot do file visiting in an indirect buffer");
if (!NILP (BVAR (current_buffer, read_only)))
Fbarf_if_buffer_read_only (Qnil);
val = Qnil;
p = Qnil;
old_undo = Qnil;
CHECK_STRING (filename);
filename = Fexpand_file_name (filename, Qnil);
orig_filename = filename;
/* The value Qnil means that the coding system is not yet
decided. */
coding_system = Qnil;
/* If the file name has special constructs in it,
call the corresponding file name handler. */
handler = Ffind_file_name_handler (filename, Qinsert_file_contents);
if (!NILP (handler))
{
val = calln (handler, Qinsert_file_contents, filename,
visit, beg, end, replace);
if (CONSP (val) && CONSP (XCDR (val))
&& RANGED_FIXNUMP (0, XCAR (XCDR (val)), ZV - PT))
inserted = XFIXNUM (XCAR (XCDR (val)));
goto handled;
}
if (!NILP (visit))
{
if (!NILP (beg) || !NILP (end))
error ("Attempt to visit less than an entire file");
if (BEG < Z && NILP (replace))
error ("Cannot do file visiting in a non-empty buffer");
}
off_t beg_offset = !NILP (beg) ? file_offset (beg) : 0;
off_t end_offset = !NILP (end) ? file_offset (end) : TYPE_MAXIMUM (off_t);
/* END_OFFSET should never be less than BEG_OFFSET,
so that code can assume that END_OFFSET - BEG_OFFSET is nonnegative.
So set END_OFFSET = max (END_OFFSET, BEG_OFFSET)
whenever setting either variable,
unless it is already known that BEG_OFFSET <= END_OFFSET. */
end_offset = max (end_offset, beg_offset);
filename = ENCODE_FILE (filename);
fd = emacs_fd_open (SSDATA (filename), O_RDONLY, 0);
if (!emacs_fd_valid_p (fd))
{
save_errno = errno;
if (NILP (visit))
report_file_error ("Opening input file", orig_filename);
mtime = time_error_value (save_errno);
if (!NILP (Vcoding_system_for_read))
{
/* Don't let invalid values into buffer-file-coding-system. */
CHECK_CODING_SYSTEM (Vcoding_system_for_read);
Fset (Qbuffer_file_coding_system, Vcoding_system_for_read);
}
eassert (inserted == 0);
goto notfound;
}
specpdl_ref fd_index = SPECPDL_INDEX ();
record_unwind_protect_ptr (close_file_unwind_emacs_fd, &fd);
/* Replacement should preserve point as it preserves markers. */
if (!NILP (replace))
{
window_markers = get_window_points_and_markers ();
record_unwind_protect (restore_point_unwind,
XCAR (XCAR (window_markers)));
}
{
struct stat st;
if (emacs_fd_fstat (fd, &st) < 0)
report_file_error ("Input file status", orig_filename);
/* Normally there is no need for an S_ISDIR test here,
as the first 'read' syscall will fail with EISDIR.
However, for backwards compatibility to traditional Unix,
POSIX allows 'read' to succeed on directories.
So do an explicit S_ISDIR test now, so that callers can rely on
this function rejecting directories on all platforms. */
if (S_ISDIR (st.st_mode))
report_file_errno ("Read error", orig_filename, EISDIR);
regular = S_ISREG (st.st_mode) != 0;
bool memory_object = S_TYPEISSHM (&st) || S_TYPEISTMO (&st);
if (regular | memory_object)
{
st_size = st.st_size;
/* On some ancient platforms, a regular file with a negative size means
the actual file size is greater than the maximum off_t value.
Fail now rather than doing a lot of work and exhausting memory. */
if (st_size < 0)
buffer_overflow ();
}
mtime = (memory_object
? make_timespec (0, UNKNOWN_MODTIME_NSECS)
: get_stat_mtime (&st));
}
/* The REPLACE code will need to be changed in order to work on
named pipes, and it's probably just not worth it. So we should
at least signal an error. */
if (!regular)
{
if (!NILP (visit) || !NILP (replace))
{
if (!NILP (visit) || !EQ (replace, Qif_regular))
xsignal2 (Qfile_error,
build_string ("not a regular file"), orig_filename);
else
/* Set REPLACE to Qunbound, indicating that we are trying
to replace the buffer contents with that of a
non-regular file. */
replace = Qunbound;
}
/* Forbid specifying BEG unless the file is regular, as per
the doc string. */
if (!NILP (beg))
xsignal2 (Qfile_error,
build_string ("can use a start position"
" only in a regular file"),
orig_filename);
}
/* Check now whether the buffer will become too large,
in the likely case where the file's length is not changing.
This saves a lot of needless work before a buffer overflow.
If LIKELY_END is nonnegative, it is likely where we will stop reading.
We could read more (or less), if the file grows (or shrinks). */
off_t likely_end = min (end_offset, st_size);
if (beg_offset < likely_end)
{
ptrdiff_t buf_bytes
= Z_BYTE - (!NILP (replace) ? ZV_BYTE - BEGV_BYTE : 0);
ptrdiff_t buf_growth_max = BUF_BYTES_MAX - buf_bytes;
off_t likely_growth = likely_end - beg_offset;
if (buf_growth_max < likely_growth)
buffer_overflow ();
}
/* Prevent redisplay optimizations. */
current_buffer->clip_changed = true;
/* The current input position if known, -1 otherwise. */
off_t curpos = -1;
if (EQ (Vcoding_system_for_read, Qauto_save_coding))
{
coding_system = coding_inherit_eol_type (Qutf_8_emacs, Qunix);
setup_coding_system (coding_system, &coding);
/* Ensure we set Vlast_coding_system_used. */
set_coding_system = true;
}
else if (BEG < Z)
{
/* Decide the coding system to use for reading the file now
because we can't use an optimized method for handling
`coding:' tag if the current buffer is not empty. */
if (!NILP (Vcoding_system_for_read))
coding_system = Vcoding_system_for_read;
else
{
/* Don't try looking inside a file for a coding system
specification if it is not a regular file. */
if (regular && !NILP (Vset_auto_coding_function))
{
if (!NILP (beg))
curpos = xlseek (fd, beg_offset, SEEK_SET, orig_filename);
else
{
beg_offset = curpos = xlseek (fd, 0, SEEK_CUR, orig_filename);
end_offset = max (end_offset, beg_offset);
}
/* Find a coding system specified in the heading two
lines or in the tailing several lines of the file.
Assume that the 1 KiB and 3 KiB for heading
and tailing respectively are sufficient for this
purpose. Because the file may be in /proc,
do not use st_size or report any SEEK_END failure. */
static_assert (4 * 1024 < sizeof read_buf);
ptrdiff_t
trial = min (end_offset - beg_offset, 4 * 1024),
nread = trial ? emacs_full_read (fd, read_buf, trial) : 0;
curpos += nread;
if (nread == 4 * 1024 && curpos < end_offset)
{
curpos = emacs_fd_lseek (fd, - 3 * 1024, SEEK_END);
if (curpos < 0)
curpos = beg_offset + nread;
else
{
off_t tailbeg = (curpos <= beg_offset + nread
? beg_offset + nread
: min (curpos, end_offset - 3 * 1024));
if (tailbeg != curpos)
curpos = xlseek (fd, tailbeg, SEEK_SET, orig_filename);
}
/* When appending the last 3 KiB, read extra bytes
without trusting SEEK_END, as the file may be growing.
Although this may yield more than 4 KiB of data total,
and the trailing data may not be from file end if
the file is growing, it is good enough. */
ptrdiff_t trial = min (end_offset - curpos,
sizeof read_buf - 1024);
nread = emacs_full_read (fd, read_buf + 1024, trial);
if (0 <= nread)
{
curpos += nread;
nread += 1024;
}
}
if (nread < 0)
report_file_error ("Read error", orig_filename);
else if (nread > 0)
{
AUTO_STRING (name, " *code-converting-work*");
struct buffer *prev = current_buffer;
Lisp_Object workbuf;
struct buffer *buf;
record_unwind_current_buffer ();
workbuf = Fget_buffer_create (name, Qt);
buf = XBUFFER (workbuf);
delete_all_overlays (buf);
bset_directory (buf, BVAR (current_buffer, directory));
bset_read_only (buf, Qnil);
bset_filename (buf, Qnil);
bset_undo_list (buf, Qt);
eassert (buf->overlays == NULL);
set_buffer_internal (buf);
Ferase_buffer ();
bset_enable_multibyte_characters (buf, Qnil);
insert_1_both ((char *) read_buf, nread, nread, 0, 0, 0);
TEMP_SET_PT_BOTH (BEG, BEG_BYTE);
coding_system = calln (Vset_auto_coding_function,
filename, make_fixnum (nread));
set_buffer_internal (prev);
/* Discard the unwind protect for recovering the
current buffer. */
specpdl_ptr--;
}
}
if (NILP (coding_system))
{
/* If we have not yet decided a coding system, check
file-coding-system-alist. */
coding_system = CALLN (Ffind_operation_coding_system,
Qinsert_file_contents, orig_filename,
visit, beg, end, replace);
if (CONSP (coding_system))
coding_system = XCAR (coding_system);
}
}
if (NILP (coding_system))
coding_system = Qundecided;
else
CHECK_CODING_SYSTEM (coding_system);
if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
/* We must suppress all character code conversion except for
end-of-line conversion. */
coding_system = raw_text_coding_system (coding_system);
setup_coding_system (coding_system, &coding);
/* Ensure we set Vlast_coding_system_used. */
set_coding_system = true;
}
/* If requested, replace the accessible part of the buffer
with the file contents. Avoid replacing text at the
beginning or end of the buffer that matches the file contents;
that preserves markers pointing to the unchanged parts.
Here we implement this feature in an optimized way
for the case where code conversion is NOT needed.
The following if-statement handles the case of conversion
in a less optimal way.
If the code conversion is "automatic" then we try using this
method and hope for the best.
But if we discover the need for conversion, we give up on this method
and let the following if-statement handle the replace job. */
if ((!NILP (replace)
&& !BASE_EQ (replace, Qunbound))
&& BEGV < ZV
&& (NILP (coding_system)
|| ! CODING_REQUIRE_DECODING (&coding)))
{
/* There is still a possibility we will find the need to do code
conversion. If that happens, set this variable to
give up on handling REPLACE in the optimized way. */
bool giveup_match_end = false;
if (beg_offset != curpos)
{
if (!NILP (beg) || 0 <= curpos)
curpos = xlseek (fd, beg_offset, SEEK_SET, orig_filename);
else
{
beg_offset = curpos = xlseek (fd, 0, SEEK_CUR, orig_filename);
end_offset = max (end_offset, beg_offset);
}
}
/* Count how many chars at the start of the file
match the text at the beginning of the buffer. */
while (true)
{
ptrdiff_t trial = min (end_offset - curpos, sizeof read_buf);
ptrdiff_t nread = trial ? emacs_fd_read (fd, read_buf, trial) : 0;
if (nread < 0)
report_file_error ("Read error", orig_filename);
else if (nread == 0)
{
/* Data inserted from the file match the buffer's leading bytes,
so there's no need to replace anything. */
emacs_fd_close (fd);
clear_unwind_protect (fd_index);
/* Truncate the buffer to the size of the file. */
del_range_byte (same_at_start, same_at_end);
goto handled;
}
curpos += nread;
if (CODING_REQUIRE_DETECTION (&coding))
{
coding_system = detect_coding_system ((unsigned char *) read_buf,
nread, nread, 1, 0,
coding_system);
setup_coding_system (coding_system, &coding);
}
if (CODING_REQUIRE_DECODING (&coding))
/* We found that the file should be decoded somehow.
Let's give up here. */
{
giveup_match_end = true;
break;
}
ptrdiff_t bufpos = 0;
ptrdiff_t bufposlim = min (nread, same_at_end - same_at_start);
while (bufpos < bufposlim
&& FETCH_BYTE (same_at_start) == read_buf[bufpos])
same_at_start++, bufpos++;
/* If we found a discrepancy, stop the scan. */
if (bufpos != nread)
break;
}
/* Find the end position, which is end_offset if given,
the file's end otherwise. */
off_t endpos;
if (!giveup_match_end)
{
endpos = end_offset;
if (endpos == TYPE_MAXIMUM (off_t))
{
endpos = emacs_fd_lseek (fd, 0, SEEK_END);
if (endpos < 0)
endpos = curpos;
/* Check that read reports EOF soon, to catch platforms
where SEEK_END fails or reports too-small offsets. */
ptrdiff_t n = emacs_full_read (fd, read_buf, sizeof read_buf);
if (n < 0)
report_file_error ("Read error", orig_filename);
curpos = endpos += n;
/* Give up if the file extends past the test read. */
giveup_match_end = n == sizeof read_buf;
if (!giveup_match_end)
{
/* Shrink the file's head if the file shrank to
be smaller than its head. */
off_t offset_from_beg = endpos - beg_offset;
if (offset_from_beg < same_at_start - BEGV_BYTE)
{
/* Give up if the file shrank to less than BEG. */
giveup_match_end = offset_from_beg < 0;
if (!giveup_match_end)
same_at_start = offset_from_beg + BEGV_BYTE;
}
}
}
}
/* Count how many bytes in the file's end match the buffer's end.
However, don't waste time if decoding is necessary. */
while (!giveup_match_end)
{
ptrdiff_t nread, bufpos, trial;
/* How much can we scan in the next step? Compare with poslim
to prevent overlap of the matching head with the matching tail.
The 'same_at_start_pos' limit prevents overlap in the buffer's
head and tail, and the 'file_overlap_pos' limit prevents
overlap in the inserted file's head and tail. */
off_t same_at_start_pos = beg_offset + (same_at_start - BEGV_BYTE);
off_t file_overlap_pos = endpos - (same_at_end - same_at_start);
off_t poslim = max (same_at_start_pos, file_overlap_pos);
/* Do not scan more than sizeof read_buf at a time, and stop
the scan if it can go no more. */
trial = min (curpos - poslim, sizeof read_buf);
if (trial == 0)
break;
curpos = xlseek (fd, -trial, SEEK_CUR, orig_filename);
nread = emacs_full_read (fd, read_buf, trial);
curpos += nread;
if (nread < trial)
{
if (nread < 0)
report_file_error ("Read error", orig_filename);
/* The file unexpectedly shrank. */
giveup_match_end = true;
break;
}
/* Scan this bufferful from the end, comparing with
the Emacs buffer. */
bufpos = nread;
while (bufpos > 0
&& FETCH_BYTE (same_at_end - 1) == read_buf[bufpos - 1])
same_at_end--, bufpos--;
/* If we found a discrepancy, stop the scan.
Otherwise loop around and scan the preceding bufferful. */
if (bufpos != 0)
{
/* If this discrepancy is because of code conversion,
we cannot use this method; giveup and try the other. */
if (same_at_end > same_at_start
&& FETCH_BYTE (same_at_end - 1) >= 0200
&& ! NILP (BVAR (current_buffer, enable_multibyte_characters))
&& (CODING_MAY_REQUIRE_DECODING (&coding)))
giveup_match_end = true;
break;
}
}
if (! giveup_match_end)
{
ptrdiff_t temp;
specpdl_ref this_count = SPECPDL_INDEX ();
/* We win! We can handle REPLACE the optimized way. */
/* Extend the start of non-matching text area to multibyte
character boundary. */
if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
while (same_at_start > BEGV_BYTE
&& ! CHAR_HEAD_P (FETCH_BYTE (same_at_start)))
same_at_start--;
/* Extend the end of non-matching text area to multibyte
character boundary. */
if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
while (same_at_end < ZV_BYTE
&& ! CHAR_HEAD_P (FETCH_BYTE (same_at_end)))
same_at_end++;
same_at_end_charpos = BYTE_TO_CHAR (same_at_end);
/* Arrange to read only the nonmatching middle part of the file. */
beg_offset += same_at_start - BEGV_BYTE;
end_offset = endpos - (ZV_BYTE - same_at_end);
eassert (beg_offset <= end_offset);
if (!NILP (visit) && BEG == BEGV && Z == ZV)
/* This binding is to avoid ask-user-about-supersession-threat
being called in insert_from_buffer or del_range_bytes (via
prepare_to_modify_buffer).
Such a prompt makes no sense if we're VISITing the file,
since the insertion makes the buffer *more* like the file
rather than the reverse.
AFAICT we could avoid ask-user-about-supersession-threat by
setting current_buffer->modtime earlier, but we could still
end up calling ask-user-about-supersession-threat if the file
is modified while we read it, so we bind buffer-file-name
instead. */
specbind (Qbuffer_file_name, Qnil);
del_range_byte (same_at_start, same_at_end);
/* Insert from the file at the proper position. */
temp = BYTE_TO_CHAR (same_at_start);
SET_PT_BOTH (temp, same_at_start);
unbind_to (this_count, Qnil);
/* If display currently starts at beginning of line,
keep it that way. */
if (XBUFFER (XWINDOW (selected_window)->contents) == current_buffer)
XWINDOW (selected_window)->start_at_line_beg = !NILP (Fbolp ());
replace_handled = true;
}
}
/* If requested, replace the accessible part of the buffer
with the file contents. Avoid replacing text at the
beginning or end of the buffer that matches the file contents;
that preserves markers pointing to the unchanged parts.
Here we implement this feature for the case where code conversion
is needed, in a simple way that needs a lot of memory.
The preceding if-statement handles the case of no conversion
in a more optimized way. */
if ((!NILP (replace)
&& !BASE_EQ (replace, Qunbound))
&& ! replace_handled && BEGV < ZV)
{
ptrdiff_t same_at_start_charpos;
ptrdiff_t inserted_chars;
ptrdiff_t bufpos;
unsigned char *decoded;
ptrdiff_t temp;
ptrdiff_t this;
specpdl_ref this_count = SPECPDL_INDEX ();
bool multibyte
= ! NILP (BVAR (current_buffer, enable_multibyte_characters));
Lisp_Object conversion_buffer;
conversion_buffer = code_conversion_save (1, multibyte);
/* First read the whole file, performing code conversion into
CONVERSION_BUFFER. */
if (beg_offset != curpos)
{
if (!NILP (beg) || 0 <= curpos)
curpos = xlseek (fd, beg_offset, SEEK_SET, orig_filename);
else
{
beg_offset = curpos = xlseek (fd, 0, SEEK_CUR, orig_filename);
end_offset = max (end_offset, beg_offset);
}
}
inserted = 0; /* Bytes put into CONVERSION_BUFFER so far. */
unprocessed = 0; /* Bytes not processed in previous loop. */
while (true)
{
/* Read one buffer a time, to allow
quitting while reading a huge file. */
ptrdiff_t trial = min (end_offset - curpos,
sizeof read_buf - unprocessed);
this = trial ? emacs_fd_read (fd, read_buf + unprocessed, trial) : 0;
if (this < 0)
report_file_error ("Read error", orig_filename);
if (this == 0)
break;
curpos += this;
BUF_TEMP_SET_PT (XBUFFER (conversion_buffer),
BUF_Z (XBUFFER (conversion_buffer)));
decode_coding_c_string (&coding, (unsigned char *) read_buf,
unprocessed + this, conversion_buffer);
unprocessed = coding.carryover_bytes;
if (coding.carryover_bytes > 0)
memcpy (read_buf, coding.carryover, unprocessed);
}
emacs_fd_close (fd);
clear_unwind_protect (fd_index);
if (unprocessed > 0)
{
BUF_TEMP_SET_PT (XBUFFER (conversion_buffer),
BUF_Z (XBUFFER (conversion_buffer)));
coding.mode |= CODING_MODE_LAST_BLOCK;
decode_coding_c_string (&coding, (unsigned char *) read_buf,
unprocessed, conversion_buffer);
coding.mode &= ~CODING_MODE_LAST_BLOCK;
}
coding_system = CODING_ID_NAME (coding.id);
set_coding_system = true;
maybe_move_gap (XBUFFER (conversion_buffer));
decoded = BUF_BEG_ADDR (XBUFFER (conversion_buffer));
inserted = (BUF_Z_BYTE (XBUFFER (conversion_buffer))
- BUF_BEG_BYTE (XBUFFER (conversion_buffer)));
/* Compare the beginning of the converted string with the buffer
text. */
bufpos = 0;
ptrdiff_t bufposlim = min (inserted, same_at_end - same_at_start);
while (bufpos < bufposlim
&& FETCH_BYTE (same_at_start) == decoded[bufpos])
same_at_start++, bufpos++;
/* If the file matches the head of buffer completely,
there's no need to replace anything. */
if (bufpos == inserted)
{
/* Truncate the buffer to the size of the file. */
if (same_at_start != same_at_end)
{
if (!NILP (visit) && BEG == BEGV && Z == ZV)
/* See previous specbind for the reason behind this. */
specbind (Qbuffer_file_name, Qnil);
del_range_byte (same_at_start, same_at_end);
}
inserted = 0;
unbind_to (this_count, Qnil);
goto handled;
}
/* Extend the start of non-matching text area to the previous
multibyte character boundary. */
if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
while (same_at_start > BEGV_BYTE
&& ! CHAR_HEAD_P (FETCH_BYTE (same_at_start)))
same_at_start--;
/* Scan this bufferful from the end, comparing with the Emacs
buffer. Compare with bufposlim to prevent overlap of the
matching head with the matching tail. The 'same_at_start -
BEGV_BYTE' limit prevents overlap in the buffer's head and
tail, and the 'inserted - (same_at_end - same_at_start)' limit
prevents overlap in the inserted file's head and tail. */
bufposlim = max (same_at_start - BEGV_BYTE,
inserted - (same_at_end - same_at_start));
bufpos = inserted;
while (bufposlim < bufpos
&& FETCH_BYTE (same_at_end - 1) == decoded[bufpos - 1])
same_at_end--, bufpos--;
/* Extend the end of non-matching text area to the next
multibyte character boundary. */
if (! NILP (BVAR (current_buffer, enable_multibyte_characters)))
while (same_at_end < ZV_BYTE
&& ! CHAR_HEAD_P (FETCH_BYTE (same_at_end)))
same_at_end++;
same_at_end_charpos = BYTE_TO_CHAR (same_at_end);
/* If display currently starts at beginning of line,
keep it that way. */
if (XBUFFER (XWINDOW (selected_window)->contents) == current_buffer)
XWINDOW (selected_window)->start_at_line_beg = !NILP (Fbolp ());
/* Replace the chars that we need to replace,
and update INSERTED to equal the number of bytes
we are taking from the decoded string. */
inserted -= (ZV_BYTE - same_at_end) + (same_at_start - BEGV_BYTE);
if (!NILP (visit) && BEG == BEGV && Z == ZV)
/* See previous specbind for the reason behind this. */
specbind (Qbuffer_file_name, Qnil);
if (same_at_end != same_at_start)
{
del_range_byte (same_at_start, same_at_end);
temp = GPT;
eassert (same_at_start == GPT_BYTE);
same_at_start = GPT_BYTE;
}
else
{
temp = same_at_end_charpos;
}
/* Insert from the file at the proper position. */
SET_PT_BOTH (temp, same_at_start);
same_at_start_charpos
= buf_bytepos_to_charpos (XBUFFER (conversion_buffer),
same_at_start - BEGV_BYTE
+ BUF_BEG_BYTE (XBUFFER (conversion_buffer)));
eassert (same_at_start_charpos == temp - (BEGV - BEG));
inserted_chars
= (buf_bytepos_to_charpos (XBUFFER (conversion_buffer),
same_at_start + inserted - BEGV_BYTE
+ BUF_BEG_BYTE (XBUFFER (conversion_buffer)))
- same_at_start_charpos);
insert_from_buffer (XBUFFER (conversion_buffer),
same_at_start_charpos, inserted_chars, 0);
/* Set `inserted' to the number of inserted characters. */
inserted = PT - temp;
/* Set point before the inserted characters. */
SET_PT_BOTH (temp, same_at_start);
unbind_to (this_count, Qnil);
goto handled;
}
if (beg_offset != curpos)
{
if (!NILP (beg) || 0 <= curpos)
xlseek (fd, beg_offset, SEEK_SET, orig_filename);
else if (end_offset != TYPE_MAXIMUM (off_t))
{
curpos = emacs_fd_lseek (fd, 0, SEEK_CUR);
beg_offset = max (0, curpos);
end_offset = max (end_offset, beg_offset);
}
}
/* curpos effectively goes out of scope now, as it is no longer needed,
so do not bother to update curpos from now on. */
total = end_offset - beg_offset;
if (NILP (visit) && total > 0)
{
if (!NILP (BVAR (current_buffer, file_truename))
/* Make binding buffer-file-name to nil effective. */
&& !NILP (BVAR (current_buffer, filename))
&& SAVE_MODIFF >= MODIFF)
we_locked_file = true;
prepare_to_modify_buffer (PT, PT, NULL);
}
/* If REPLACE is Qunbound, buffer contents are being replaced with
text read from a FIFO or a device. Erase the entire accessible
portion of the buffer. */
if (BASE_EQ (replace, Qunbound))
del_range (BEGV, ZV);
move_gap_both (PT, PT_BYTE);
/* Ensure the gap is at least one byte larger than needed for the
estimated insertion, so that in the usual case we read
without reallocating. */
off_t inserted_estimate = likely_end - beg_offset;
if (GAP_SIZE <= inserted_estimate)
{
ptrdiff_t growth;
if (ckd_sub (&growth, inserted_estimate, GAP_SIZE - 1))
buffer_overflow ();
make_gap (growth);
}
/* Total bytes inserted. */
inserted = 0;
/* Here, we don't do code conversion in the loop. It is done by
decode_coding_gap after all data are read into the buffer. */
{
ptrdiff_t gap_size = GAP_SIZE;
while (inserted < total)
{
char *buf;
ptrdiff_t bufsize, this;
if (gap_size < total - inserted && gap_size < sizeof read_buf)
{
buf = read_buf;
bufsize = sizeof read_buf;
}
else
{
buf = (char *) BEG_ADDR + PT_BYTE - BEG_BYTE + inserted;
bufsize = min (gap_size, INSERT_READ_SIZE_MAX);
}
bufsize = min (bufsize, total - inserted);
if (!regular)
{
Lisp_Object nbytes;
intmax_t number;
/* Read from the file, capturing `quit'. When an
error occurs, end the loop, and arrange for a quit
to be signaled after decoding the text we read.
This way, we do not lose any data read. */
union read_non_regular data
= {{fd, buf == read_buf ? buf : NULL, inserted, bufsize}};
nbytes = internal_condition_case_1
(read_non_regular, make_pointer_integer (&data),
Qerror, read_non_regular_quit);
if (NILP (nbytes))
{
read_quit = -1;
break;
}
integer_to_intmax (nbytes, &number);
this = number;
}
else
/* Allow quitting out of the actual I/O. We don't make text
part of the buffer until all the reading is done, so a
C-g here doesn't do any harm: though the data read are discarded,
the original data is still in the input file. */
{
this = emacs_fd_read (fd, buf, bufsize);
if (this < 0)
this = -errno;
}
if (this <= 0)
{
read_quit = -this;
break;
}
if (buf == read_buf)
{
if (gap_size < this)
{
/* Size estimate was low. Make the gap at least 50% larger,
and big enough so that the next loop iteration will
use the gap directly instead of copying via read_buf. */
make_gap (max (GAP_SIZE >> 1,
this - gap_size + sizeof read_buf));
gap_size = GAP_SIZE - inserted;
}
memcpy (BEG_ADDR + PT_BYTE - BEG_BYTE + inserted,
read_buf, this);
}
gap_size -= this;
inserted += this;
}
}
/* Now we have either read all the file data into the gap,
or stop reading on I/O error or quit. If nothing was
read, undo marking the buffer modified. */
if (inserted == 0)
{
if (we_locked_file)
Funlock_file (BVAR (current_buffer, file_truename));
Vdeactivate_mark = old_Vdeactivate_mark;
}
else
Fset (Qdeactivate_mark, Qt);
emacs_fd_close (fd);
clear_unwind_protect (fd_index);
if (0 < read_quit)
report_file_errno ("Read error", orig_filename, read_quit);
notfound:
if (NILP (coding_system))
{
/* The coding system is not yet decided. Decide it by an
optimized method for handling `coding:' tag.
Note that we can get here only if the buffer was empty
before the insertion. */
eassert (Z == BEG);
if (!NILP (Vcoding_system_for_read))
coding_system = Vcoding_system_for_read;
else
{
/* Since we are sure that the current buffer was empty
before the insertion, we can toggle
enable-multibyte-characters directly here without taking
care of marker adjustment. By this way, we can run Lisp
program safely before decoding the inserted text. */
Lisp_Object multibyte
= BVAR (current_buffer, enable_multibyte_characters);
Lisp_Object unwind_data
= Fcons (multibyte,
Fcons (BVAR (current_buffer, undo_list),
Fcurrent_buffer ()));
specpdl_ref count1 = SPECPDL_INDEX ();
bset_enable_multibyte_characters (current_buffer, Qnil);
bset_undo_list (current_buffer, Qt);
record_unwind_protect (decide_coding_unwind, unwind_data);
/* Make the text read part of the buffer. */
insert_from_gap_1 (inserted, inserted, false);
if (inserted > 0 && ! NILP (Vset_auto_coding_function))
{
coding_system = calln (Vset_auto_coding_function,
filename, make_fixnum (inserted));
}
if (NILP (coding_system))
{
/* If the coding system is not yet decided, check
file-coding-system-alist. */
coding_system = CALLN (Ffind_operation_coding_system,
Qinsert_file_contents, orig_filename,
visit, beg, end, Qnil);
if (CONSP (coding_system))
coding_system = XCAR (coding_system);
}
/* Move the text back to the gap. */
unbind_to (count1, Qnil);
inserted = XFIXNUM (XCAR (unwind_data));
}
if (NILP (coding_system))
coding_system = Qundecided;
else
CHECK_CODING_SYSTEM (coding_system);
if (NILP (BVAR (current_buffer, enable_multibyte_characters)))
/* We must suppress all character code conversion except for
end-of-line conversion. */
coding_system = raw_text_coding_system (coding_system);
setup_coding_system (coding_system, &coding);
/* Ensure we set Vlast_coding_system_used. */
set_coding_system = true;
}
if (!NILP (visit))
{
/* When we visit a file by raw-text, we change the buffer to
unibyte. */
if (CODING_FOR_UNIBYTE (&coding)
/* Can't do this if part of the buffer might be preserved. */
&& NILP (replace))
{
/* Visiting a file with these coding system makes the buffer
unibyte. */
if (inserted > 0)
bset_enable_multibyte_characters (current_buffer, Qnil);
else
Fset_buffer_multibyte (Qnil);
}
}
eassert (PT == GPT);
coding.dst_multibyte
= !NILP (BVAR (current_buffer, enable_multibyte_characters));
if (CODING_MAY_REQUIRE_DECODING (&coding)
&& (inserted > 0 || CODING_REQUIRE_FLUSHING (&coding)))
{
/* Now we have all the new bytes at the beginning of the gap,
but `decode_coding_gap` can't have them at the beginning of the gap,
so we need to move them. */
memmove (GAP_END_ADDR - inserted, GPT_ADDR, inserted);
decode_coding_gap (&coding, inserted);
inserted = coding.produced_char;
coding_system = CODING_ID_NAME (coding.id);
}
else if (inserted > 0)
{
/* Make the text read part of the buffer. */
eassert (NILP (BVAR (current_buffer, enable_multibyte_characters)));
insert_from_gap_1 (inserted, inserted, false);
invalidate_buffer_caches (current_buffer, PT, PT + inserted);
adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted,
inserted);
}
/* Call after-change hooks for the inserted text, aside from the case
of normal visiting (not with REPLACE), which is done in a new buffer
"before" the buffer is changed. */
if (inserted > 0 && total > 0
&& (NILP (visit) || !NILP (replace)))
{
signal_after_change (PT, 0, inserted);
update_compositions (PT, PT, CHECK_BORDER);
}
/* Now INSERTED is measured in characters. */
handled:
if (inserted > 0)
restore_window_points (window_markers, inserted,
BYTE_TO_CHAR (same_at_start),
same_at_end_charpos);
if (!NILP (visit))
{
if (empty_undo_list_p)
bset_undo_list (current_buffer, Qnil);
if (NILP (handler))
{
current_buffer->modtime = mtime;
current_buffer->modtime_size = st_size;
bset_filename (current_buffer, orig_filename);
}
SAVE_MODIFF = MODIFF;
BUF_AUTOSAVE_MODIFF (current_buffer) = MODIFF;
XSETFASTINT (BVAR (current_buffer, save_length), Z - BEG);
if (NILP (handler))
{
if (!NILP (BVAR (current_buffer, file_truename)))
Funlock_file (BVAR (current_buffer, file_truename));
Funlock_file (filename);
}
}
if (set_coding_system)
Vlast_coding_system_used = coding_system;
if (! NILP (Ffboundp (Qafter_insert_file_set_coding)))
{
insval = calln (Qafter_insert_file_set_coding, make_fixnum (inserted),
visit);
if (! NILP (insval))
{
if (! RANGED_FIXNUMP (0, insval, ZV - PT))
wrong_type_argument (Qinserted_chars, insval);
inserted = XFIXNAT (insval);
}
}
/* Decode file format. Don't do this if Qformat_decode is not
bound, which can happen when called early during loadup. */
if (inserted > 0 && !NILP (Ffboundp (Qformat_decode)))
{
/* Don't run point motion or modification hooks when decoding. */
specpdl_ref count1 = SPECPDL_INDEX ();
ptrdiff_t old_inserted = inserted;
specbind (Qinhibit_point_motion_hooks, Qt);
specbind (Qinhibit_modification_hooks, Qt);
/* Save old undo list and don't record undo for decoding. */
old_undo = BVAR (current_buffer, undo_list);
bset_undo_list (current_buffer, Qt);
if (NILP (replace))
{
insval = calln (Qformat_decode,
Qnil, make_fixnum (inserted), visit);
if (! RANGED_FIXNUMP (0, insval, ZV - PT))
wrong_type_argument (Qinserted_chars, insval);
inserted = XFIXNAT (insval);
}
else
{
/* If REPLACE is non-nil and we succeeded in not replacing the
beginning or end of the buffer text with the file's contents,
call format-decode with `point' positioned at the beginning
of the buffer and `inserted' equaling the number of
characters in the buffer. Otherwise, format-decode might
fail to correctly analyze the beginning or end of the buffer.
Hence we temporarily save `point' and `inserted' here and
restore `point' iff format-decode did not insert or delete
any text. Otherwise we leave `point' at point-min. */
ptrdiff_t opoint = PT;
ptrdiff_t opoint_byte = PT_BYTE;
ptrdiff_t oinserted = ZV - BEGV;
modiff_count ochars_modiff = CHARS_MODIFF;
TEMP_SET_PT_BOTH (BEGV, BEGV_BYTE);
insval = calln (Qformat_decode,
Qnil, make_fixnum (oinserted), visit);
if (! RANGED_FIXNUMP (0, insval, ZV - PT))
wrong_type_argument (Qinserted_chars, insval);
if (ochars_modiff == CHARS_MODIFF)
/* format_decode didn't modify buffer's characters => move
point back to position before inserted text and leave
value of inserted alone. */
SET_PT_BOTH (opoint, opoint_byte);
else
/* format_decode modified buffer's characters => consider
entire buffer changed and leave point at point-min. */
inserted = XFIXNAT (insval);
}
/* For consistency with format-decode call these now iff inserted > 0
(martin 2007-06-28). */
p = Vafter_insert_file_functions;
FOR_EACH_TAIL (p)
{
if (NILP (replace))
{
insval = calln (XCAR (p), make_fixnum (inserted));
if (!NILP (insval))
{
if (! RANGED_FIXNUMP (0, insval, ZV - PT))
wrong_type_argument (Qinserted_chars, insval);
inserted = XFIXNAT (insval);
}
}
else
{
/* For the rationale of this see the comment on
format-decode above. */
ptrdiff_t opoint = PT;
ptrdiff_t opoint_byte = PT_BYTE;
ptrdiff_t oinserted = ZV - BEGV;
modiff_count ochars_modiff = CHARS_MODIFF;
TEMP_SET_PT_BOTH (BEGV, BEGV_BYTE);
insval = calln (XCAR (p), make_fixnum (oinserted));
if (!NILP (insval))
{
if (! RANGED_FIXNUMP (0, insval, ZV - PT))
wrong_type_argument (Qinserted_chars, insval);
if (ochars_modiff == CHARS_MODIFF)
/* after_insert_file_functions didn't modify
buffer's characters => move point back to
position before inserted text and leave value of
inserted alone. */
SET_PT_BOTH (opoint, opoint_byte);
else
/* after_insert_file_functions did modify buffer's
characters => consider entire buffer changed and
leave point at point-min. */
inserted = XFIXNAT (insval);
}
}
}
if (!empty_undo_list_p)
{
bset_undo_list (current_buffer, old_undo);
if (CONSP (old_undo) && inserted != old_inserted)
{
/* Adjust the last undo record for the size change during
the format conversion. */
Lisp_Object tem = XCAR (old_undo);
if (CONSP (tem) && FIXNUMP (XCAR (tem))
&& FIXNUMP (XCDR (tem))
&& XFIXNUM (XCDR (tem)) == PT + old_inserted)
XSETCDR (tem, make_fixnum (PT + inserted));
}
}
else
/* If undo_list was Qt before, keep it that way.
Otherwise start with an empty undo_list. */
bset_undo_list (current_buffer, EQ (old_undo, Qt) ? Qt : Qnil);
unbind_to (count1, Qnil);
}
if (save_errno != 0)
{
/* Signal an error if visiting a file that could not be opened. */
eassert (!NILP (visit) && NILP (handler));
report_file_errno ("Opening input file", orig_filename, save_errno);
}
/* We made a lot of deletions and insertions above, so invalidate
the newline cache for the entire region of the inserted
characters. */
if (current_buffer->base_buffer && current_buffer->base_buffer->newline_cache)
invalidate_region_cache (current_buffer->base_buffer,
current_buffer->base_buffer->newline_cache,
PT - BEG, Z - PT - inserted);
else if (current_buffer->newline_cache)
invalidate_region_cache (current_buffer,
current_buffer->newline_cache,
PT - BEG, Z - PT - inserted);
if (read_quit)
quit ();
/* Retval needs to be dealt with in all cases consistently. */
if (NILP (val))
val = list2 (orig_filename, make_fixnum (inserted));
return unbind_to (count, val);
}