1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2024-12-16 09:50:25 +00:00

* src/fileio.c: Fix bug#36431

(decide_coding_unwind): Re-introduce.  Move text back to the gap.
Return the new `inserted` via the unwind_data.
(Finsert_file_contents): Use it.
Make sure `inserted` is always 0 when we jump straight to `notfound`.
Don't insert the text in the buffer until we know it's properly decoded
for the byteness of the buffer.

* test/src/fileio-tests.el (fileio-tests--insert-file-interrupt):
Allow insert-file-contents to return an empty buffer in case of
non-local exit in set-auto-coding-function.
This commit is contained in:
Stefan Monnier 2019-07-09 17:04:07 -04:00
parent 4c619758b2
commit fec111c9ee
2 changed files with 95 additions and 35 deletions

View File

@ -3474,6 +3474,67 @@ otherwise, if FILE2 does not exist, the answer is t. */)
enum { READ_BUF_SIZE = MAX_ALLOCA };
/* This function is called after Lisp functions to decide a coding
system are called, or when they cause an error. Before they are
called, the current buffer is set unibyte and it contains only a
newly inserted text (thus the buffer was empty before the
insertion).
The functions may set markers, overlays, text properties, or even
alter the buffer contents, change the current buffer.
Here, we reset all those changes by:
o set back the current buffer.
o move all markers and overlays to BEG.
o remove all text properties.
o set back the buffer multibyteness. */
static void
decide_coding_unwind (Lisp_Object unwind_data)
{
Lisp_Object multibyte = XCAR (unwind_data);
Lisp_Object tmp = XCDR (unwind_data);
Lisp_Object undo_list = XCAR (tmp);
Lisp_Object buffer = XCDR (tmp);
set_buffer_internal (XBUFFER (buffer));
/* We're about to "delete" the text by moving it back into the gap.
So move markers that set-auto-coding might have created to BEG,
just in case. */
adjust_markers_for_delete (BEG, BEG_BYTE, Z, Z_BYTE);
adjust_overlays_for_delete (BEG, Z - BEG);
set_buffer_intervals (current_buffer, NULL);
TEMP_SET_PT_BOTH (BEG, BEG_BYTE);
/* In case of a non-local exit from set_auto_coding_function, in order not
to end up with potentially invalid byte sequences in a multibyte buffer,
we have the following options:
1- decode the bytes in some arbitrary coding-system.
2- erase the buffer.
3- leave the buffer unibyte (which is actually the same as option (1)
where the coding-system is `raw-text-unix`).
Here we choose 2. */
/* Move the bytes back to (the beginning of) the gap.
In general this may have to move all the bytes, but here
this can't move more bytes than were moved during the execution
of Vset_auto_coding_function, which is normally 0 (because it
normally doesn't modify the buffer). */
move_gap_both (Z, Z_BYTE);
ptrdiff_t inserted = Z_BYTE - BEG_BYTE;
GAP_SIZE += inserted;
ZV = Z = GPT = BEG;
ZV_BYTE = Z_BYTE = GPT_BYTE = BEG_BYTE;
/* Pass the new `inserted` back. */
XSETCAR (unwind_data, make_fixnum (inserted));
/* Now we are safe to change the buffer's multibyteness directly. */
bset_enable_multibyte_characters (current_buffer, multibyte);
bset_undo_list (current_buffer, undo_list);
}
/* Read from a non-regular file. Return the number of bytes read. */
union read_non_regular
@ -3720,6 +3781,7 @@ by calling `format-decode', which see. */)
CHECK_CODING_SYSTEM (Vcoding_system_for_read);
Fset (Qbuffer_file_coding_system, Vcoding_system_for_read);
}
eassert (inserted == 0);
goto notfound;
}
@ -3746,7 +3808,10 @@ by calling `format-decode', which see. */)
not_regular = 1;
if (! NILP (visit))
goto notfound;
{
eassert (inserted == 0);
goto notfound;
}
if (! NILP (replace) || ! NILP (beg) || ! NILP (end))
xsignal2 (Qfile_error,
@ -4414,9 +4479,6 @@ by calling `format-decode', which see. */)
if (how_much < 0)
report_file_error ("Read error", orig_filename);
/* Make the text read part of the buffer. */
insert_from_gap_1 (inserted, inserted, false);
notfound:
if (NILP (coding_system))
@ -4426,6 +4488,7 @@ by calling `format-decode', which see. */)
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;
@ -4438,12 +4501,18 @@ by calling `format-decode', which see. */)
program safely before decoding the inserted text. */
Lisp_Object multibyte
= BVAR (current_buffer, enable_multibyte_characters);
Lisp_Object undo_list = BVAR (current_buffer, undo_list);
Lisp_Object unwind_data
= Fcons (multibyte,
Fcons (BVAR (current_buffer, undo_list),
Fcurrent_buffer ()));
ptrdiff_t count1 = SPECPDL_INDEX ();
bset_enable_multibyte_characters (current_buffer, Qnil);
bset_undo_list (current_buffer, Qt);
record_unwind_protect (restore_buffer, Fcurrent_buffer ());
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))
{
@ -4461,24 +4530,9 @@ by calling `format-decode', which see. */)
if (CONSP (coding_system))
coding_system = XCAR (coding_system);
}
/* Move the text back to the gap. */
unbind_to (count1, Qnil);
/* We're about to "delete" the text by moving it back into the gap
(right before calling decode_coding_gap).
So move markers that set-auto-coding might have created to BEG,
just in case. */
adjust_markers_for_delete (BEG, BEG_BYTE, Z, Z_BYTE);
adjust_overlays_for_delete (BEG, Z - BEG);
set_buffer_intervals (current_buffer, NULL);
TEMP_SET_PT_BOTH (BEG, BEG_BYTE);
/* Change the buffer's multibyteness directly. We used to do this
from within unbind_to, but it was unsafe since the bytes
may contain invalid sequences for a multibyte buffer (which is OK
here since we'll decode them before anyone else gets to see
them, but is dangerous when we're doing a non-local exit). */
bset_enable_multibyte_characters (current_buffer, multibyte);
bset_undo_list (current_buffer, undo_list);
inserted = Z_BYTE - BEG_BYTE;
inserted = XFIXNUM (XCAR (unwind_data));
}
if (NILP (coding_system))
@ -4512,22 +4566,27 @@ by calling `format-decode', which see. */)
}
}
coding.dst_multibyte = ! NILP (BVAR (current_buffer, enable_multibyte_characters));
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)))
{
move_gap_both (PT, PT_BYTE);
GAP_SIZE += inserted;
ZV_BYTE -= inserted;
Z_BYTE -= inserted;
ZV -= inserted;
Z -= inserted;
/* 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);

View File

@ -120,11 +120,12 @@ Also check that an encoding error can appear in a symlink."
(let ((set-auto-coding-function (lambda (&rest _) (throw 'toto nil))))
(insert-file-contents f)))
(goto-char (point-min))
(forward-line 1)
(let ((c1 (char-after)))
(forward-char 1)
(should (equal c1 (char-before)))
(should (equal c1 (char-after))))))
(unless (eobp)
(forward-line 1)
(let ((c1 (char-after)))
(forward-char 1)
(should (equal c1 (char-before)))
(should (equal c1 (char-after)))))))
(if f (delete-file f)))))
(ert-deftest fileio-tests--relative-default-directory ()