Hello, list. I discovered this bug a few months back and I'm finally getting around to posting it. Enjoy.
-Kevin BUG: Final newline incorrectly removed from files Vim incorrectly strips the final newline from files due to a misuse of the write_no_eol_lnum global variable. This variable can be set during the reading of one file (in fileio.c readfile) and used during the writing of an unrelated file (in fileio.c buf_write), triggering the bug. This script demonstrates the problem: ======== BEGIN SCRIPT ======== #!/bin/sh echo "File with an end-of-line." > eol echo -n "File without an end-of-line." > no-eol cp eol eol-backup vim -u NONE -N -b eol -c "sp no-eol|wincmd p|w|qa" if ! cmp -s eol eol-backup; then echo 'Vim stripped the newline!' fi ======== END SCRIPT ======== Note that this bug is only triggered when the no-newline file has the same number of lines as the file being written. I don't think I fully understand the usage of write_no_eol_lnum, but my best guess to fix this is to reset it to 0 after the autocommands are executed near the end of readfile. In general it seems like the best practice would be to limit the range over which write_no_eol_lnum is non-zero to be only what is necessary. The following first-pass patch is based an that. Things that may still be missing include: * Places where write_no_eol_lnum is reset to 0 "in case it was set by the previous read" may not be needed anymore, but this isn't clear. Nested calls due to autocommand execution might still need this (or might be broken regardless of what is done with this global variable). * write_no_eol_lnum is still set to non-zero in os_unix.c. I don't know why, or if it's necessary. In any case, if it is set to non-zero it seems that it *should* be reset to zero once it is no longer needed. diff -r df6b12c84b23 src/fileio.c --- a/src/fileio.c Wed Oct 27 18:36:36 2010 +0200 +++ b/src/fileio.c Mon Nov 01 11:24:25 2010 -0700 @@ -2605,13 +2605,6 @@ check_marks_read(); #endif - /* - * Trick: We remember if the last line of the read didn't have - * an eol for when writing it again. This is required for - * ":autocmd FileReadPost *.gz set bin|'[,']!gunzip" to work. - */ - write_no_eol_lnum = read_no_eol_lnum; - /* When reloading a buffer put the cursor at the first line that is * different. */ if (flags & READ_KEEP_UNDO) @@ -2630,6 +2623,13 @@ } #endif + /* + * Trick: We remember if the last line of the read didn't have + * an eol for when writing it again. This is required for + * ":autocmd FileReadPost *.gz set bin|'[,']!gunzip" to work. + */ + write_no_eol_lnum = read_no_eol_lnum; + #ifdef FEAT_AUTOCMD if (!read_stdin && !read_buffer) { @@ -2665,6 +2665,9 @@ } #endif + /* don't affect future writes */ + write_no_eol_lnum = 0; + if (recoverymode && error) return FAIL; return OK; -- You received this message from the "vim_dev" maillist. Do not top-post! Type your reply below the text you are replying to. For more information, visit http://www.vim.org/maillist.php