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. 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.
{
struct stat st;
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;
ptrdiff_t total = 0;
bool regular = true;
int save_errno = 0;
char read_buf[READ_BUF_SIZE];
struct coding_system coding;
bool replace_handled = false;
bool set_coding_system = false;
Lisp_Object coding_system;
/* Negative if read error, 0 if OK so far, positive if quit. */
ptrdiff_t 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;
bool seekable = true;
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;
orig_filename = Qnil;
old_undo = Qnil;
CHECK_STRING (filename);
filename = Fexpand_file_name (filename, Qnil);
/* 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 = call6 (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) : -1;
orig_filename = filename;
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);
st.st_size = -1;
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)));
}
if (emacs_fd_fstat (fd, &st) != 0)
report_file_error ("Input file status", orig_filename);
mtime = 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 (!(S_ISREG (st.st_mode) || S_ISDIR (st.st_mode)))
{
regular = false;
if (!NILP (replace))
{
if (!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 together with a special file, as per
the doc string. */
if (!NILP (beg))
xsignal2 (Qfile_error,
build_string ("cannot use a start position in a non-seekable file/device"),
orig_filename);
/* Now ascertain if this file is seekable, by detecting if
seeking leads to -1 being returned. */
seekable
= emacs_fd_lseek (fd, 0, SEEK_CUR) != (off_t) -1;
}
if (end_offset < 0)
{
if (!regular)
end_offset = TYPE_MAXIMUM (off_t);
else
{
end_offset = st.st_size;
/* A negative size can happen on a platform that allows file
sizes greater than the maximum off_t value. */
if (end_offset < 0)
buffer_overflow ();
/* The file size returned from fstat may be zero, but data
may be readable nonetheless, for example when this is a
file in the /proc filesystem. */
if (end_offset == 0)
end_offset = READ_BUF_SIZE;
}
}
/* 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 (regular)
{
/* The likely offset where we will stop reading. We could read
more (or less), if the file grows (or shrinks) as we read it. */
off_t likely_end = min (end_offset, st.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;
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))
{
/* Find a coding system specified in the heading two
lines or in the tailing several lines of the file.
We assume that the 1K-byte and 3K-byte for heading
and tailing respectively are sufficient for this
purpose. */
int nread;
if (st.st_size <= (1024 * 4))
nread = emacs_fd_read (fd, read_buf, 1024 * 4);
else
{
nread = emacs_fd_read (fd, read_buf, 1024);
if (nread == 1024)
{
int ntail;
if (emacs_fd_lseek (fd, st.st_size - 1024 * 3, SEEK_CUR) < 0)
report_file_error ("Setting file position",
orig_filename);
ntail = emacs_fd_read (fd, read_buf + nread, 1024 * 3);
nread = ntail < 0 ? ntail : nread + ntail;
}
}
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 = call2 (Vset_auto_coding_function,
filename, make_fixnum (nread));
set_buffer_internal (prev);
/* Discard the unwind protect for recovering the
current buffer. */
specpdl_ptr--;
/* Rewind the file for the actual read done later. */
if (emacs_fd_lseek (fd, 0, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
}
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)))
{
ptrdiff_t overlap;
/* 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 != 0)
{
if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
/* Count how many chars at the start of the file
match the text at the beginning of the buffer. */
while (true)
{
int nread = emacs_fd_read (fd, read_buf, sizeof read_buf);
if (nread < 0)
report_file_error ("Read error", orig_filename);
else if (nread == 0)
break;
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;
}
int bufpos = 0;
while (bufpos < nread && same_at_start < ZV_BYTE
&& FETCH_BYTE (same_at_start) == read_buf[bufpos])
same_at_start++, bufpos++;
/* If we found a discrepancy, stop the scan.
Otherwise loop around and scan the next bufferful. */
if (bufpos != nread)
break;
}
/* If the file matches the buffer completely,
there's no need to replace anything. */
if (same_at_start - BEGV_BYTE == end_offset - beg_offset)
{
emacs_fd_close (fd);
clear_unwind_protect (fd_index);
/* Truncate the buffer to the size of the file. */
del_range_1 (same_at_start, same_at_end, 0, 0);
goto handled;
}
/* Count how many chars at the end of the file
match the text at the end of the buffer. But, if we have
already found that decoding is necessary, don't waste time. */
while (!giveup_match_end)
{
int total_read, nread, bufpos, trial;
off_t curpos;
/* At what file position are we now scanning? */
curpos = end_offset - (ZV_BYTE - same_at_end);
/* If the entire file matches the buffer tail, stop the scan. */
if (curpos == 0)
break;
/* How much can we scan in the next step? */
trial = min (curpos, sizeof read_buf);
if (emacs_fd_lseek (fd, curpos - trial, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
total_read = nread = 0;
while (total_read < trial)
{
nread = emacs_fd_read (fd, read_buf + total_read,
trial - total_read);
if (nread < 0)
report_file_error ("Read error", orig_filename);
else if (nread == 0)
break;
total_read += nread;
}
/* Scan this bufferful from the end, comparing with
the Emacs buffer. */
bufpos = total_read;
/* Compare with same_at_start to avoid counting some buffer text
as matching both at the file's beginning and at the end. */
while (bufpos > 0 && same_at_end > same_at_start
&& 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 (nread == 0)
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++;
/* Don't try to reuse the same piece of text twice. */
overlap = (same_at_start - BEGV_BYTE
- (same_at_end
+ (! NILP (end) ? end_offset : st.st_size) - ZV_BYTE));
if (overlap > 0)
same_at_end += overlap;
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 -= ZV_BYTE - same_at_end;
/* 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).
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 overlap;
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 (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
inserted = 0; /* Bytes put into CONVERSION_BUFFER so far. */
unprocessed = 0; /* Bytes not processed in previous loop. */
while (true)
{
/* Read at most READ_BUF_SIZE bytes at a time, to allow
quitting while reading a huge file. */
this = emacs_fd_read (fd, read_buf + unprocessed,
READ_BUF_SIZE - unprocessed);
if (this <= 0)
break;
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);
}
if (this < 0)
report_file_error ("Read error", orig_filename);
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;
while (bufpos < inserted && same_at_start < same_at_end
&& 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)
{
/* 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. */
bufpos = inserted;
/* Compare with same_at_start to avoid counting some buffer text
as matching both at the file's beginning and at the end. */
while (bufpos > 0 && same_at_end > same_at_start
&& 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++;
/* Don't try to reuse the same piece of text twice. */
overlap = same_at_start - BEGV_BYTE - (same_at_end + inserted - ZV_BYTE);
if (overlap > 0)
same_at_end += overlap;
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);
/* 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 (seekable || !NILP (end))
total = end_offset - beg_offset;
else
/* For a special file, all we can do is guess. */
total = READ_BUF_SIZE;
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 file size, so that in the usual case we read to EOF
without reallocating. */
if (GAP_SIZE <= total)
make_gap (total - GAP_SIZE + 1);
if (beg_offset != 0 || (!NILP (replace)
&& !BASE_EQ (replace, Qunbound)))
{
if (emacs_fd_lseek (fd, beg_offset, SEEK_SET) < 0)
report_file_error ("Setting file position", orig_filename);
}
/* 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 (NILP (end) || inserted < total)
{
ptrdiff_t this;
if (gap_size == 0)
{
/* The size estimate was wrong. Make the gap 50% larger. */
make_gap (GAP_SIZE >> 1);
gap_size = GAP_SIZE - inserted;
}
/* 'try' is reserved in some compilers (Microsoft C). */
ptrdiff_t trytry = min (gap_size, READ_BUF_SIZE);
if (seekable || !NILP (end))
trytry = min (trytry, total - inserted);
if (!seekable && NILP (end))
{
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. */
union read_non_regular data = {{fd, inserted, trytry}};
nbytes = internal_condition_case_1
(read_non_regular, make_pointer_integer (&data),
Qerror, read_non_regular_quit);
if (NILP (nbytes))
{
read_quit = 1;
break;
}
if (!integer_to_intmax (nbytes, &number)
&& number > PTRDIFF_MAX)
buffer_overflow ();
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. */
this = emacs_fd_read (fd,
((char *) BEG_ADDR + PT_BYTE - BEG_BYTE
+ inserted),
trytry);
if (this <= 0)
{
read_quit = this;
break;
}
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 (read_quit < 0)
report_file_error ("Read error", orig_filename);
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 = call2 (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.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 !defined HAVE_ANDROID || defined ANDROID_STUBIFY
/* Under Android, modtime and st.st_size can be valid even if FD
is not a regular file. */
if (!regular)
xsignal2 (Qfile_error,
build_string ("not a regular file"), orig_filename);
#endif /* !defined HAVE_ANDROID || defined ANDROID_STUBIFY */
}
if (set_coding_system)
Vlast_coding_system_used = coding_system;
if (! NILP (Ffboundp (Qafter_insert_file_set_coding)))
{
insval = call2 (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 = call3 (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 = call3 (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 = call1 (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 = call1 (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);
}