diff options
| author | Stefan Monnier | 2019-07-09 17:04:07 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2019-07-09 17:04:24 -0400 |
| commit | fec111c9ee7a248ac49ba1d6d62d3ef9473b7af9 (patch) | |
| tree | c88f33a33fbd4bba52c7b0e8310dcdfc99591065 /src | |
| parent | 4c619758b2806ee6607af7b1d56943e547001e7a (diff) | |
| download | emacs-fec111c9ee7a248ac49ba1d6d62d3ef9473b7af9.tar.gz emacs-fec111c9ee7a248ac49ba1d6d62d3ef9473b7af9.zip | |
* 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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/fileio.c | 119 |
1 files changed, 89 insertions, 30 deletions
diff --git a/src/fileio.c b/src/fileio.c index cce49c43b2e..614c0f989da 100644 --- a/src/fileio.c +++ b/src/fileio.c | |||
| @@ -3474,6 +3474,67 @@ otherwise, if FILE2 does not exist, the answer is t. */) | |||
| 3474 | 3474 | ||
| 3475 | enum { READ_BUF_SIZE = MAX_ALLOCA }; | 3475 | enum { READ_BUF_SIZE = MAX_ALLOCA }; |
| 3476 | 3476 | ||
| 3477 | /* This function is called after Lisp functions to decide a coding | ||
| 3478 | system are called, or when they cause an error. Before they are | ||
| 3479 | called, the current buffer is set unibyte and it contains only a | ||
| 3480 | newly inserted text (thus the buffer was empty before the | ||
| 3481 | insertion). | ||
| 3482 | |||
| 3483 | The functions may set markers, overlays, text properties, or even | ||
| 3484 | alter the buffer contents, change the current buffer. | ||
| 3485 | |||
| 3486 | Here, we reset all those changes by: | ||
| 3487 | o set back the current buffer. | ||
| 3488 | o move all markers and overlays to BEG. | ||
| 3489 | o remove all text properties. | ||
| 3490 | o set back the buffer multibyteness. */ | ||
| 3491 | |||
| 3492 | static void | ||
| 3493 | decide_coding_unwind (Lisp_Object unwind_data) | ||
| 3494 | { | ||
| 3495 | Lisp_Object multibyte = XCAR (unwind_data); | ||
| 3496 | Lisp_Object tmp = XCDR (unwind_data); | ||
| 3497 | Lisp_Object undo_list = XCAR (tmp); | ||
| 3498 | Lisp_Object buffer = XCDR (tmp); | ||
| 3499 | |||
| 3500 | set_buffer_internal (XBUFFER (buffer)); | ||
| 3501 | |||
| 3502 | /* We're about to "delete" the text by moving it back into the gap. | ||
| 3503 | So move markers that set-auto-coding might have created to BEG, | ||
| 3504 | just in case. */ | ||
| 3505 | adjust_markers_for_delete (BEG, BEG_BYTE, Z, Z_BYTE); | ||
| 3506 | adjust_overlays_for_delete (BEG, Z - BEG); | ||
| 3507 | set_buffer_intervals (current_buffer, NULL); | ||
| 3508 | TEMP_SET_PT_BOTH (BEG, BEG_BYTE); | ||
| 3509 | |||
| 3510 | /* In case of a non-local exit from set_auto_coding_function, in order not | ||
| 3511 | to end up with potentially invalid byte sequences in a multibyte buffer, | ||
| 3512 | we have the following options: | ||
| 3513 | 1- decode the bytes in some arbitrary coding-system. | ||
| 3514 | 2- erase the buffer. | ||
| 3515 | 3- leave the buffer unibyte (which is actually the same as option (1) | ||
| 3516 | where the coding-system is `raw-text-unix`). | ||
| 3517 | Here we choose 2. */ | ||
| 3518 | |||
| 3519 | /* Move the bytes back to (the beginning of) the gap. | ||
| 3520 | In general this may have to move all the bytes, but here | ||
| 3521 | this can't move more bytes than were moved during the execution | ||
| 3522 | of Vset_auto_coding_function, which is normally 0 (because it | ||
| 3523 | normally doesn't modify the buffer). */ | ||
| 3524 | move_gap_both (Z, Z_BYTE); | ||
| 3525 | ptrdiff_t inserted = Z_BYTE - BEG_BYTE; | ||
| 3526 | GAP_SIZE += inserted; | ||
| 3527 | ZV = Z = GPT = BEG; | ||
| 3528 | ZV_BYTE = Z_BYTE = GPT_BYTE = BEG_BYTE; | ||
| 3529 | |||
| 3530 | /* Pass the new `inserted` back. */ | ||
| 3531 | XSETCAR (unwind_data, make_fixnum (inserted)); | ||
| 3532 | |||
| 3533 | /* Now we are safe to change the buffer's multibyteness directly. */ | ||
| 3534 | bset_enable_multibyte_characters (current_buffer, multibyte); | ||
| 3535 | bset_undo_list (current_buffer, undo_list); | ||
| 3536 | } | ||
| 3537 | |||
| 3477 | /* Read from a non-regular file. Return the number of bytes read. */ | 3538 | /* Read from a non-regular file. Return the number of bytes read. */ |
| 3478 | 3539 | ||
| 3479 | union read_non_regular | 3540 | union read_non_regular |
| @@ -3720,6 +3781,7 @@ by calling `format-decode', which see. */) | |||
| 3720 | CHECK_CODING_SYSTEM (Vcoding_system_for_read); | 3781 | CHECK_CODING_SYSTEM (Vcoding_system_for_read); |
| 3721 | Fset (Qbuffer_file_coding_system, Vcoding_system_for_read); | 3782 | Fset (Qbuffer_file_coding_system, Vcoding_system_for_read); |
| 3722 | } | 3783 | } |
| 3784 | eassert (inserted == 0); | ||
| 3723 | goto notfound; | 3785 | goto notfound; |
| 3724 | } | 3786 | } |
| 3725 | 3787 | ||
| @@ -3746,7 +3808,10 @@ by calling `format-decode', which see. */) | |||
| 3746 | not_regular = 1; | 3808 | not_regular = 1; |
| 3747 | 3809 | ||
| 3748 | if (! NILP (visit)) | 3810 | if (! NILP (visit)) |
| 3749 | goto notfound; | 3811 | { |
| 3812 | eassert (inserted == 0); | ||
| 3813 | goto notfound; | ||
| 3814 | } | ||
| 3750 | 3815 | ||
| 3751 | if (! NILP (replace) || ! NILP (beg) || ! NILP (end)) | 3816 | if (! NILP (replace) || ! NILP (beg) || ! NILP (end)) |
| 3752 | xsignal2 (Qfile_error, | 3817 | xsignal2 (Qfile_error, |
| @@ -4414,9 +4479,6 @@ by calling `format-decode', which see. */) | |||
| 4414 | if (how_much < 0) | 4479 | if (how_much < 0) |
| 4415 | report_file_error ("Read error", orig_filename); | 4480 | report_file_error ("Read error", orig_filename); |
| 4416 | 4481 | ||
| 4417 | /* Make the text read part of the buffer. */ | ||
| 4418 | insert_from_gap_1 (inserted, inserted, false); | ||
| 4419 | |||
| 4420 | notfound: | 4482 | notfound: |
| 4421 | 4483 | ||
| 4422 | if (NILP (coding_system)) | 4484 | if (NILP (coding_system)) |
| @@ -4426,6 +4488,7 @@ by calling `format-decode', which see. */) | |||
| 4426 | 4488 | ||
| 4427 | Note that we can get here only if the buffer was empty | 4489 | Note that we can get here only if the buffer was empty |
| 4428 | before the insertion. */ | 4490 | before the insertion. */ |
| 4491 | eassert (Z == BEG); | ||
| 4429 | 4492 | ||
| 4430 | if (!NILP (Vcoding_system_for_read)) | 4493 | if (!NILP (Vcoding_system_for_read)) |
| 4431 | coding_system = Vcoding_system_for_read; | 4494 | coding_system = Vcoding_system_for_read; |
| @@ -4438,12 +4501,18 @@ by calling `format-decode', which see. */) | |||
| 4438 | program safely before decoding the inserted text. */ | 4501 | program safely before decoding the inserted text. */ |
| 4439 | Lisp_Object multibyte | 4502 | Lisp_Object multibyte |
| 4440 | = BVAR (current_buffer, enable_multibyte_characters); | 4503 | = BVAR (current_buffer, enable_multibyte_characters); |
| 4441 | Lisp_Object undo_list = BVAR (current_buffer, undo_list); | 4504 | Lisp_Object unwind_data |
| 4505 | = Fcons (multibyte, | ||
| 4506 | Fcons (BVAR (current_buffer, undo_list), | ||
| 4507 | Fcurrent_buffer ())); | ||
| 4442 | ptrdiff_t count1 = SPECPDL_INDEX (); | 4508 | ptrdiff_t count1 = SPECPDL_INDEX (); |
| 4443 | 4509 | ||
| 4444 | bset_enable_multibyte_characters (current_buffer, Qnil); | 4510 | bset_enable_multibyte_characters (current_buffer, Qnil); |
| 4445 | bset_undo_list (current_buffer, Qt); | 4511 | bset_undo_list (current_buffer, Qt); |
| 4446 | record_unwind_protect (restore_buffer, Fcurrent_buffer ()); | 4512 | record_unwind_protect (decide_coding_unwind, unwind_data); |
| 4513 | |||
| 4514 | /* Make the text read part of the buffer. */ | ||
| 4515 | insert_from_gap_1 (inserted, inserted, false); | ||
| 4447 | 4516 | ||
| 4448 | if (inserted > 0 && ! NILP (Vset_auto_coding_function)) | 4517 | if (inserted > 0 && ! NILP (Vset_auto_coding_function)) |
| 4449 | { | 4518 | { |
| @@ -4461,24 +4530,9 @@ by calling `format-decode', which see. */) | |||
| 4461 | if (CONSP (coding_system)) | 4530 | if (CONSP (coding_system)) |
| 4462 | coding_system = XCAR (coding_system); | 4531 | coding_system = XCAR (coding_system); |
| 4463 | } | 4532 | } |
| 4533 | /* Move the text back to the gap. */ | ||
| 4464 | unbind_to (count1, Qnil); | 4534 | unbind_to (count1, Qnil); |
| 4465 | /* We're about to "delete" the text by moving it back into the gap | 4535 | inserted = XFIXNUM (XCAR (unwind_data)); |
| 4466 | (right before calling decode_coding_gap). | ||
| 4467 | So move markers that set-auto-coding might have created to BEG, | ||
| 4468 | just in case. */ | ||
| 4469 | adjust_markers_for_delete (BEG, BEG_BYTE, Z, Z_BYTE); | ||
| 4470 | adjust_overlays_for_delete (BEG, Z - BEG); | ||
| 4471 | set_buffer_intervals (current_buffer, NULL); | ||
| 4472 | TEMP_SET_PT_BOTH (BEG, BEG_BYTE); | ||
| 4473 | |||
| 4474 | /* Change the buffer's multibyteness directly. We used to do this | ||
| 4475 | from within unbind_to, but it was unsafe since the bytes | ||
| 4476 | may contain invalid sequences for a multibyte buffer (which is OK | ||
| 4477 | here since we'll decode them before anyone else gets to see | ||
| 4478 | them, but is dangerous when we're doing a non-local exit). */ | ||
| 4479 | bset_enable_multibyte_characters (current_buffer, multibyte); | ||
| 4480 | bset_undo_list (current_buffer, undo_list); | ||
| 4481 | inserted = Z_BYTE - BEG_BYTE; | ||
| 4482 | } | 4536 | } |
| 4483 | 4537 | ||
| 4484 | if (NILP (coding_system)) | 4538 | if (NILP (coding_system)) |
| @@ -4512,22 +4566,27 @@ by calling `format-decode', which see. */) | |||
| 4512 | } | 4566 | } |
| 4513 | } | 4567 | } |
| 4514 | 4568 | ||
| 4515 | coding.dst_multibyte = ! NILP (BVAR (current_buffer, enable_multibyte_characters)); | 4569 | eassert (PT == GPT); |
| 4570 | |||
| 4571 | coding.dst_multibyte | ||
| 4572 | = !NILP (BVAR (current_buffer, enable_multibyte_characters)); | ||
| 4516 | if (CODING_MAY_REQUIRE_DECODING (&coding) | 4573 | if (CODING_MAY_REQUIRE_DECODING (&coding) |
| 4517 | && (inserted > 0 || CODING_REQUIRE_FLUSHING (&coding))) | 4574 | && (inserted > 0 || CODING_REQUIRE_FLUSHING (&coding))) |
| 4518 | { | 4575 | { |
| 4519 | move_gap_both (PT, PT_BYTE); | 4576 | /* Now we have all the new bytes at the beginning of the gap, |
| 4520 | GAP_SIZE += inserted; | 4577 | but `decode_coding_gap` can't have them at the beginning of the gap, |
| 4521 | ZV_BYTE -= inserted; | 4578 | so we need to move them. */ |
| 4522 | Z_BYTE -= inserted; | 4579 | memmove (GAP_END_ADDR - inserted, GPT_ADDR, inserted); |
| 4523 | ZV -= inserted; | ||
| 4524 | Z -= inserted; | ||
| 4525 | decode_coding_gap (&coding, inserted); | 4580 | decode_coding_gap (&coding, inserted); |
| 4526 | inserted = coding.produced_char; | 4581 | inserted = coding.produced_char; |
| 4527 | coding_system = CODING_ID_NAME (coding.id); | 4582 | coding_system = CODING_ID_NAME (coding.id); |
| 4528 | } | 4583 | } |
| 4529 | else if (inserted > 0) | 4584 | else if (inserted > 0) |
| 4530 | { | 4585 | { |
| 4586 | /* Make the text read part of the buffer. */ | ||
| 4587 | eassert (NILP (BVAR (current_buffer, enable_multibyte_characters))); | ||
| 4588 | insert_from_gap_1 (inserted, inserted, false); | ||
| 4589 | |||
| 4531 | invalidate_buffer_caches (current_buffer, PT, PT + inserted); | 4590 | invalidate_buffer_caches (current_buffer, PT, PT + inserted); |
| 4532 | adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted, | 4591 | adjust_after_insert (PT, PT_BYTE, PT + inserted, PT_BYTE + inserted, |
| 4533 | inserted); | 4592 | inserted); |