diff options
-rw-r--r-- | Filelist | 2 | ||||
-rw-r--r-- | src/Make_cyg_ming.mak | 1 | ||||
-rw-r--r-- | src/Make_morph.mak | 1 | ||||
-rw-r--r-- | src/Make_mvc.mak | 4 | ||||
-rw-r--r-- | src/Make_vms.mms | 242 | ||||
-rw-r--r-- | src/Makefile | 10 | ||||
-rw-r--r-- | src/README.md | 1 | ||||
-rw-r--r-- | src/bufwrite.c | 2559 | ||||
-rw-r--r-- | src/fileio.c | 2780 | ||||
-rw-r--r-- | src/option.c | 4 | ||||
-rw-r--r-- | src/proto.h | 1 | ||||
-rw-r--r-- | src/proto/bufwrite.pro | 4 | ||||
-rw-r--r-- | src/proto/fileio.pro | 10 | ||||
-rw-r--r-- | src/structs.h | 29 | ||||
-rw-r--r-- | src/version.c | 2 |
15 files changed, 2850 insertions, 2800 deletions
@@ -21,6 +21,7 @@ SRC_ALL = \ src/blob.c \ src/blowfish.c \ src/buffer.c \ + src/bufwrite.c \ src/change.c \ src/channel.c \ src/charset.c \ @@ -186,6 +187,7 @@ SRC_ALL = \ src/proto/blob.pro \ src/proto/blowfish.pro \ src/proto/buffer.pro \ + src/proto/bufwrite.pro \ src/proto/change.pro \ src/proto/channel.pro \ src/proto/charset.pro \ diff --git a/src/Make_cyg_ming.mak b/src/Make_cyg_ming.mak index 01f6233bfb..503761c54d 100644 --- a/src/Make_cyg_ming.mak +++ b/src/Make_cyg_ming.mak @@ -709,6 +709,7 @@ OBJ = \ $(OUTDIR)/blob.o \ $(OUTDIR)/blowfish.o \ $(OUTDIR)/buffer.o \ + $(OUTDIR)/bufwrite.o \ $(OUTDIR)/change.o \ $(OUTDIR)/charset.o \ $(OUTDIR)/cmdexpand.o \ diff --git a/src/Make_morph.mak b/src/Make_morph.mak index f0d9cf8144..247a5788fe 100644 --- a/src/Make_morph.mak +++ b/src/Make_morph.mak @@ -29,6 +29,7 @@ SRC = arabic.c \ autocmd.c \ blowfish.c \ buffer.c \ + bufwrite.c \ change.c \ charset.c \ cmdexpand.c \ diff --git a/src/Make_mvc.mak b/src/Make_mvc.mak index 762b349a7f..e853ae9b17 100644 --- a/src/Make_mvc.mak +++ b/src/Make_mvc.mak @@ -716,6 +716,7 @@ OBJ = \ $(OUTDIR)\blob.obj \ $(OUTDIR)\blowfish.obj \ $(OUTDIR)\buffer.obj \ + $(OUTDIR)\bufwrite.obj \ $(OUTDIR)\change.obj \ $(OUTDIR)\charset.obj \ $(OUTDIR)\cmdexpand.obj \ @@ -1457,6 +1458,8 @@ $(OUTDIR)/blowfish.obj: $(OUTDIR) blowfish.c $(INCL) $(OUTDIR)/buffer.obj: $(OUTDIR) buffer.c $(INCL) +$(OUTDIR)/bufwrite.obj: $(OUTDIR) bufwrite.c $(INCL) + $(OUTDIR)/change.obj: $(OUTDIR) change.c $(INCL) $(OUTDIR)/charset.obj: $(OUTDIR) charset.c $(INCL) @@ -1788,6 +1791,7 @@ proto.h: \ proto/blob.pro \ proto/blowfish.pro \ proto/buffer.pro \ + proto/bufwrite.pro \ proto/change.pro \ proto/charset.pro \ proto/cmdexpand.pro \ diff --git a/src/Make_vms.mms b/src/Make_vms.mms index 6053f9b873..6046b5a235 100644 --- a/src/Make_vms.mms +++ b/src/Make_vms.mms @@ -2,7 +2,7 @@ # Makefile for Vim on OpenVMS # # Maintainer: Zoltan Arpadffy <arpadffy@polarhome.com> -# Last change: 2019 Sep 27 +# Last change: 2019 Sep 28 # # This has script been tested on VMS 6.2 to 8.2 on DEC Alpha, VAX and IA64 # with MMS and MMK @@ -307,48 +307,210 @@ ALL_CFLAGS_VER = /def=($(MODEL_DEF)$(DEFS)$(DEBUG_DEF)$(PERL_DEF)$(PYTHON_DEF) - ALL_LIBS = $(LIBS) $(GUI_LIB_DIR) $(GUI_LIB) \ $(PERL_LIB) $(PYTHON_LIB) $(TCL_LIB) $(RUBY_LIB) -SRC = arabic.c arglist.c autocmd.c beval.c blob.c blowfish.c buffer.c \ - change.c charset.c cmdexpand.c cmdhist.c crypt.c crypt_zip.c \ - debugger.c dict.c diff.c digraph.c drawline.c drawscreen.c edit.c \ - eval.c evalbuffer.c evalfunc.c \ - evalvars.c evalwindow.c ex_cmds.c ex_cmds2.c ex_docmd.c ex_eval.c \ +SRC = \ + arabic.c \ + arglist.c \ + autocmd.c \ + beval.c \ + blob.c \ + blowfish.c \ + buffer.c \ + bufwrite.c \ + change.c \ + charset.c \ + cmdexpand.c \ + cmdhist.c \ + crypt.c \ + crypt_zip.c \ + debugger.c \ + dict.c \ + diff.c \ + digraph.c \ + drawline.c \ + drawscreen.c \ + edit.c \ + eval.c \ + evalbuffer.c \ + evalfunc.c \ + evalvars.c \ + evalwindow.c \ + ex_cmds.c \ + ex_cmds2.c \ + ex_docmd.c \ + ex_eval.c \ ex_getln.c \ - if_cscope.c if_xcmdsrv.c fileio.c filepath.c, findfile.c fold.c \ - getchar.c hardcopy.c hashtab.c highlight.c \ - indent.c insexpand.c json.c list.c main.c map.c mark.c menu.c mbyte.c \ - memfile.c memline.c message.c misc1.c misc2.c mouse.c move.c normal.c \ + fileio.c \ + filepath.c, \ + findfile.c \ + fold.c \ + getchar.c \ + hardcopy.c \ + hashtab.c \ + highlight.c \ + if_cscope.c \ + if_xcmdsrv.c \ + indent.c \ + insexpand.c \ + json.c \ + list.c \ + main.c \ + map.c \ + mark.c \ + mbyte.c \ + memfile.c \ + memline.c \ + menu.c \ + message.c \ + misc1.c \ + misc2.c \ + mouse.c \ + move.c \ + normal.c \ ops.c \ - option.c optionstr.c popupmenu.c popupwin.c profiler.c quickfix.c \ - regexp.c register.c scriptfile.c \ - search.c session.c sha256.c sign.c spell.c spellfile.c spellsuggest.c \ - syntax.c tag.c \ - term.c termlib.c testing.c textprop.c ui.c undo.c usercmd.c \ - userfunc.c version.c viminfo.c screen.c window.c os_unix.c os_vms.c \ + option.c \ + optionstr.c \ + os_unix.c \ + os_vms.c \ pathdef.c \ - $(GUI_SRC) $(PERL_SRC) $(PYTHON_SRC) $(TCL_SRC) \ - $(RUBY_SRC) $(HANGULIN_SRC) $(MZSCH_SRC) $(XDIFF_SRC) - -OBJ = arabic.obj arglist.obj autocmd.obj beval.obj blob.obj blowfish.obj \ - buffer.obj change.obj charset.obj cmdexpand.obj cmdhist.obj \ - crypt.obj crypt_zip.obj debugger.obj dict.obj diff.obj digraph.obj \ - drawline.obj drawscreen.obj edit.obj eval.obj evalbuffer.obj \ - evalfunc.obj evalvars.obj evalwindow.obj ex_cmds.obj ex_cmds2.obj \ - ex_docmd.obj ex_eval.obj ex_getln.obj if_cscope.obj if_xcmdsrv.obj \ - fileio.obj filepath.obj \ - findfile.obj fold.obj getchar.obj hardcopy.obj hashtab.obj \ - highlight.obj indent.obj insexpand.obj json.obj list.obj main.obj \ - map.obj mark.obj menu.obj memfile.obj memline.obj message.obj \ - misc1.obj misc2.obj mouse.obj move.obj mbyte.obj normal.obj ops.obj \ + popupmenu.c \ + popupwin.c \ + profiler.c \ + quickfix.c \ + regexp.c \ + register.c \ + screen.c \ + scriptfile.c \ + search.c \ + session.c \ + sha256.c \ + sign.c \ + spell.c \ + spellfile.c \ + spellsuggest.c \ + syntax.c \ + tag.c \ + term.c \ + termlib.c \ + testing.c \ + textprop.c \ + ui.c \ + undo.c \ + usercmd.c \ + userfunc.c \ + version.c \ + viminfo.c \ + window.c \ + $(GUI_SRC) \ + $(PERL_SRC) \ + $(PYTHON_SRC) \ + $(TCL_SRC) \ + $(RUBY_SRC) \ + $(HANGULIN_SRC) \ + $(MZSCH_SRC) \ + $(XDIFF_SRC) + +OBJ = \ + arabic.obj \ + arglist.obj \ + autocmd.obj \ + beval.obj \ + blob.obj \ + blowfish.obj \ + buffer.obj \ + bufwrite.obj \ + change.obj \ + charset.obj \ + cmdexpand.obj \ + cmdhist.obj \ + crypt.obj \ + crypt_zip.obj \ + debugger.obj \ + dict.obj \ + diff.obj \ + digraph.obj \ + drawline.obj \ + drawscreen.obj \ + edit.obj \ + eval.obj \ + evalbuffer.obj \ + evalfunc.obj \ + evalvars.obj \ + evalwindow.obj \ + ex_cmds.obj \ + ex_cmds2.obj \ + ex_docmd.obj \ + ex_eval.obj \ + ex_getln.obj \ + fileio.obj \ + filepath.obj \ + findfile.obj \ + fold.obj \ + getchar.obj \ + hardcopy.obj \ + hashtab.obj \ + highlight.obj \ + if_cscope.obj \ + if_mzsch.obj \ + if_xcmdsrv.obj \ + indent.obj \ + insexpand.obj \ + json.obj \ + list.obj \ + main.obj \ + map.obj \ + mark.obj \ + mbyte.obj \ + memfile.obj \ + memline.obj \ + menu.obj \ + message.obj \ + misc1.obj \ + misc2.obj \ + mouse.obj \ + move.obj \ + normal.obj \ + ops.obj \ option.obj \ - optionstr.obj popupmenu.obj popupwin.obj profiler.obj quickfix.obj \ - regexp.obj register.obj scriptfile.obj \ - search.obj session.obj sha256.obj sign.obj spell.obj spellfile.obj \ - spellsuggest.obj syntax.obj tag.obj term.obj termlib.obj testing.obj \ + optionstr.obj \ + os_unix.obj \ + os_vms.obj \ + pathdef.obj \ + popupmenu.obj \ + popupwin.obj \ + profiler.obj \ + quickfix.obj \ + regexp.obj \ + register.obj \ + screen.obj \ + scriptfile.obj \ + search.obj \ + session.obj \ + sha256.obj \ + sign.obj \ + spell.obj \ + spellfile.obj \ + spellsuggest.obj \ + syntax.obj \ + tag.obj \ + term.obj \ + termlib.obj \ + testing.obj \ textprop.obj \ - ui.obj undo.obj usercmd.obj userfunc.obj screen.obj version.obj \ - viminfo.obj window.obj os_unix.obj os_vms.obj pathdef.obj if_mzsch.obj \ - $(GUI_OBJ) $(PERL_OBJ) $(PYTHON_OBJ) $(TCL_OBJ) \ - $(RUBY_OBJ) $(HANGULIN_OBJ) $(MZSCH_OBJ) $(XDIFF_OBJ) + ui.obj \ + undo.obj \ + usercmd.obj \ + userfunc.obj \ + version.obj \ + viminfo.obj \ + window.obj \ + $(GUI_OBJ) \ + $(PERL_OBJ) \ + $(PYTHON_OBJ) \ + $(TCL_OBJ) \ + $(RUBY_OBJ) \ + $(HANGULIN_OBJ) \ + $(MZSCH_OBJ) \ + $(XDIFF_OBJ) # Default target is making the executable all : [.auto]config.h mmk_compat motif_env gtk_env perl_env python_env tcl_env ruby_env $(TARGET) @@ -526,6 +688,10 @@ buffer.obj : buffer.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ globals.h version.h +bufwrite.obj : bufwrite.c vim.h [.auto]config.h feature.h os_unix.h \ + ascii.h keymap.h term.h macros.h structs.h regexp.h \ + gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ + globals.h version.h change.obj : change.c vim.h [.auto]config.h feature.h os_unix.h \ ascii.h keymap.h term.h macros.h structs.h regexp.h \ gui.h beval.h [.proto]gui_beval.pro option.h ex_cmds.h proto.h \ diff --git a/src/Makefile b/src/Makefile index 6e676f170d..cae0e5ff9e 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1668,6 +1668,7 @@ BASIC_SRC = \ version.c \ viminfo.c \ window.c \ + bufwrite.c \ $(OS_EXTRA_SRC) SRC = $(BASIC_SRC) \ @@ -1804,6 +1805,7 @@ OBJ_COMMON = \ objects/version.o \ objects/viminfo.o \ objects/window.o \ + objects/bufwrite.o \ $(GUI_OBJ) \ $(TERM_OBJ) \ $(LUA_OBJ) \ @@ -1966,6 +1968,7 @@ PRO_AUTO = \ version.pro \ viminfo.pro \ window.pro \ + bufwrite.pro \ beval.pro \ gui_beval.pro \ netbeans.pro \ @@ -3432,6 +3435,9 @@ objects/viminfo.o: viminfo.c objects/window.o: window.c $(CCC) -o $@ window.c +objects/bufwrite.o: bufwrite.c + $(CCC) -o $@ bufwrite.c + objects/netbeans.o: netbeans.c $(CCC) -o $@ netbeans.c @@ -3949,6 +3955,10 @@ objects/window.o: window.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ proto.h globals.h +objects/bufwrite.o: bufwrite.c vim.h protodef.h auto/config.h feature.h os_unix.h \ + auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ + proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ + proto.h globals.h objects/gui.o: gui.c vim.h protodef.h auto/config.h feature.h os_unix.h \ auto/osdef.h ascii.h keymap.h term.h macros.h option.h beval.h \ proto/gui_beval.pro structs.h regexp.h gui.h alloc.h ex_cmds.h spell.h \ diff --git a/src/README.md b/src/README.md index 8d21b5f679..8d20140851 100644 --- a/src/README.md +++ b/src/README.md @@ -27,6 +27,7 @@ arglist.c | handling argument list autocmd.c | autocommands blob.c | blob data type buffer.c | manipulating buffers (loaded files) +bufwrite.c | writing a buffer to file change.c | handling changes to text cmdexpand.c | command-line completion cmdhist.c | command-line history diff --git a/src/bufwrite.c b/src/bufwrite.c new file mode 100644 index 0000000000..13091a336e --- /dev/null +++ b/src/bufwrite.c @@ -0,0 +1,2559 @@ +/* vi:set ts=8 sts=4 sw=4 noet: + * + * VIM - Vi IMproved by Bram Moolenaar + * + * Do ":help uganda" in Vim to read copying and usage conditions. + * Do ":help credits" in Vim to see a list of people who contributed. + * See README.txt for an overview of the Vim source code. + */ + +/* + * bufwrite.c: functions for writing a buffer + */ + +#include "vim.h" + +#if defined(HAVE_UTIME) && defined(HAVE_UTIME_H) +# include <utime.h> // for struct utimbuf +#endif + +#define SMALLBUFSIZE 256 // size of emergency write buffer + +/* + * Structure to pass arguments from buf_write() to buf_write_bytes(). + */ +struct bw_info +{ + int bw_fd; // file descriptor + char_u *bw_buf; // buffer with data to be written + int bw_len; // length of data + int bw_flags; // FIO_ flags +#ifdef FEAT_CRYPT + buf_T *bw_buffer; // buffer being written +#endif + char_u bw_rest[CONV_RESTLEN]; // not converted bytes + int bw_restlen; // nr of bytes in bw_rest[] + int bw_first; // first write call + char_u *bw_conv_buf; // buffer for writing converted chars + size_t bw_conv_buflen; // size of bw_conv_buf + int bw_conv_error; // set for conversion error + linenr_T bw_conv_error_lnum; // first line with error or zero + linenr_T bw_start_lnum; // line number at start of buffer +#ifdef USE_ICONV + iconv_t bw_iconv_fd; // descriptor for iconv() or -1 +#endif +}; + +/* + * Convert a Unicode character to bytes. + * Return TRUE for an error, FALSE when it's OK. + */ + static int +ucs2bytes( + unsigned c, // in: character + char_u **pp, // in/out: pointer to result + int flags) // FIO_ flags +{ + char_u *p = *pp; + int error = FALSE; + int cc; + + + if (flags & FIO_UCS4) + { + if (flags & FIO_ENDIAN_L) + { + *p++ = c; + *p++ = (c >> 8); + *p++ = (c >> 16); + *p++ = (c >> 24); + } + else + { + *p++ = (c >> 24); + *p++ = (c >> 16); + *p++ = (c >> 8); + *p++ = c; + } + } + else if (flags & (FIO_UCS2 | FIO_UTF16)) + { + if (c >= 0x10000) + { + if (flags & FIO_UTF16) + { + // Make two words, ten bits of the character in each. First + // word is 0xd800 - 0xdbff, second one 0xdc00 - 0xdfff + c -= 0x10000; + if (c >= 0x100000) + error = TRUE; + cc = ((c >> 10) & 0x3ff) + 0xd800; + if (flags & FIO_ENDIAN_L) + { + *p++ = cc; + *p++ = ((unsigned)cc >> 8); + } + else + { + *p++ = ((unsigned)cc >> 8); + *p++ = cc; + } + c = (c & 0x3ff) + 0xdc00; + } + else + error = TRUE; + } + if (flags & FIO_ENDIAN_L) + { + *p++ = c; + *p++ = (c >> 8); + } + else + { + *p++ = (c >> 8); + *p++ = c; + } + } + else // Latin1 + { + if (c >= 0x100) + { + error = TRUE; + *p++ = 0xBF; + } + else + *p++ = c; + } + + *pp = p; + return error; +} + +/* + * Call write() to write a number of bytes to the file. + * Handles encryption and 'encoding' conversion. + * + * Return FAIL for failure, OK otherwise. + */ + static int +buf_write_bytes(struct bw_info *ip) +{ + int wlen; + char_u *buf = ip->bw_buf; // data to write + int len = ip->bw_len; // length of data + int flags = ip->bw_flags; // extra flags + + // Skip conversion when writing the crypt magic number or the BOM. + if (!(flags & FIO_NOCONVERT)) + { + char_u *p; + unsigned c; + int n; + + if (flags & FIO_UTF8) + { + // Convert latin1 in the buffer to UTF-8 in the file. + p = ip->bw_conv_buf; // translate to buffer + for (wlen = 0; wlen < len; ++wlen) + p += utf_char2bytes(buf[wlen], p); + buf = ip->bw_conv_buf; + len = (int)(p - ip->bw_conv_buf); + } + else if (flags & (FIO_UCS4 | FIO_UTF16 | FIO_UCS2 | FIO_LATIN1)) + { + // Convert UTF-8 bytes in the buffer to UCS-2, UCS-4, UTF-16 or + // Latin1 chars in the file. + if (flags & FIO_LATIN1) + p = buf; // translate in-place (can only get shorter) + else + p = ip->bw_conv_buf; // translate to buffer + for (wlen = 0; wlen < len; wlen += n) + { + if (wlen == 0 && ip->bw_restlen != 0) + { + int l; + + // Use remainder of previous call. Append the start of + // buf[] to get a full sequence. Might still be too + // short! + l = CONV_RESTLEN - ip->bw_restlen; + if (l > len) + l = len; + mch_memmove(ip->bw_rest + ip->bw_restlen, buf, (size_t)l); + n = utf_ptr2len_len(ip->bw_rest, ip->bw_restlen + l); + if (n > ip->bw_restlen + len) + { + // We have an incomplete byte sequence at the end to + // be written. We can't convert it without the + // remaining bytes. Keep them for the next call. + if (ip->bw_restlen + len > CONV_RESTLEN) + return FAIL; + ip->bw_restlen += len; + break; + } + if (n > 1) + c = utf_ptr2char(ip->bw_rest); + else + c = ip->bw_rest[0]; + if (n >= ip->bw_restlen) + { + n -= ip->bw_restlen; + ip->bw_restlen = 0; + } + else + { + ip->bw_restlen -= n; + mch_memmove(ip->bw_rest, ip->bw_rest + n, + (size_t)ip->bw_restlen); + n = 0; + } + } + else + { + n = utf_ptr2len_len(buf + wlen, len - wlen); + if (n > len - wlen) + { + // We have an incomplete byte sequence at the end to + // be written. We can't convert it without the + // remaining bytes. Keep them for the next call. + if (len - wlen > CONV_RESTLEN) + return FAIL; + ip->bw_restlen = len - wlen; + mch_memmove(ip->bw_rest, buf + wlen, + (size_t)ip->bw_restlen); + break; + } + if (n > 1) + c = utf_ptr2char(buf + wlen); + else + c = buf[wlen]; + } + + if (ucs2bytes(c, &p, flags) && !ip->bw_conv_error) + { + ip->bw_conv_error = TRUE; + ip->bw_conv_error_lnum = ip->bw_start_lnum; + } + if (c == NL) + ++ip->bw_start_lnum; + } + if (flags & FIO_LATIN1) + len = (int)(p - buf); + else + { + buf = ip->bw_conv_buf; + len = (int)(p - ip->bw_conv_buf); + } + } + +#ifdef MSWIN + else if (flags & FIO_CODEPAGE) + { + // Convert UTF-8 or codepage to UCS-2 and then to MS-Windows + // codepage. + char_u *from; + size_t fromlen; + char_u *to; + int u8c; + BOOL bad = FALSE; + int needed; + + if (ip->bw_restlen > 0) + { + // Need to concatenate the remainder of the previous call and + // the bytes of the current call. Use the end of the + // conversion buffer for this. + fromlen = len + ip->bw_restlen; + from = ip->bw_conv_buf + ip->bw_conv_buflen - fromlen; + mch_memmove(from, ip->bw_rest, (size_t)ip->bw_restlen); + mch_memmove(from + ip->bw_restlen, buf, (size_t)len); + } + else + { + from = buf; + fromlen = len; + } + + to = ip->bw_conv_buf; + if (enc_utf8) + { + // Convert from UTF-8 to UCS-2, to the start of the buffer. + // The buffer has been allocated to be big enough. + while (fromlen > 0) + { + n = (int)utf_ptr2len_len(from, (int)fromlen); + if (n > (int)fromlen) // incomplete byte sequence + break; + u8c = utf_ptr2char(from); + *to++ = (u8c & 0xff); + *to++ = (u8c >> 8); + fromlen -= n; + from += n; + } + + // Copy remainder to ip->bw_rest[] to be used for the next + // call. + if (fromlen > CONV_RESTLEN) + { + // weird overlong sequence + ip->bw_conv_error = TRUE; + return FAIL; + } + mch_memmove(ip->bw_rest, from, fromlen); + ip->bw_restlen = (int)fromlen; + } + else + { + // Convert from enc_codepage to UCS-2, to the start of the + // buffer. The buffer has been allocated to be big enough. + ip->bw_restlen = 0; + needed = MultiByteToWideChar(enc_codepage, + MB_ERR_INVALID_CHARS, (LPCSTR)from, (int)fromlen, + NULL, 0); + if (needed == 0) + { + // When conversion fails there may be a trailing byte. + needed = MultiByteToWideChar(enc_codepage, + MB_ERR_INVALID_CHARS, (LPCSTR)from, (int)fromlen - 1, + NULL, 0); + if (needed == 0) + { + // Conversion doesn't work. + ip->bw_conv_error = TRUE; + return FAIL; + } + // Save the trailing byte for the next call. + ip->bw_rest[0] = from[fromlen - 1]; + ip->bw_restlen = 1; + } + needed = MultiByteToWideChar(enc_codepage, MB_ERR_INVALID_CHARS, + (LPCSTR)from, (int)(fromlen - ip->bw_restlen), + (LPWSTR)to, needed); + if (needed == 0) + { + // Safety check: Conversion doesn't work. + ip->bw_conv_error = TRUE; + return FAIL; + } + to += needed * 2; + } + + fromlen = to - ip->bw_conv_buf; + buf = to; +# ifdef CP_UTF8 // VC 4.1 doesn't define CP_UTF8 + if (FIO_GET_CP(flags) == CP_UTF8) + { + // Convert from UCS-2 to UTF-8, using the remainder of the + // conversion buffer. Fails when out of space. + for (from = ip->bw_conv_buf; fromlen > 1; fromlen -= 2) + { + u8c = *from++; + u8c += (*from++ << 8); + to += utf_char2bytes(u8c, to); + if (to + 6 >= ip->bw_conv_buf + ip->bw_conv_buflen) + { + ip->bw_conv_error = TRUE; + return FAIL; + } + } + len = (int)(to - buf); + } + else +# endif + { + // Convert from UCS-2 to the codepage, using the remainder of + // the conversion buffer. If the conversion uses the default + // character "0", the data doesn't fit in this encoding, so + // fail. + len = WideCharToMultiByte(FIO_GET_CP(flags), 0, + (LPCWSTR)ip->bw_conv_buf, (int)fromlen / sizeof(WCHAR), + (LPSTR)to, (int)(ip->bw_conv_buflen - fromlen), 0, + &bad); + if (bad) + { + ip->bw_conv_error = TRUE; + return FAIL; + } + } + } +#endif + +#ifdef MACOS_CONVERT + else if (flags & FIO_MACROMAN) + { + // Convert UTF-8 or latin1 to Apple MacRoman. + char_u *from; + size_t fromlen; + + if (ip->bw_restlen > 0) + { + // Need to concatenate the remainder of the previous call and + // the bytes of the current call. Use the end of the + // conversion buffer for this. + fromlen = len + ip->bw_restlen; + from = ip->bw_conv_buf + ip->bw_conv_buflen - fromlen; + mch_memmove(from, ip->bw_rest, (size_t)ip->bw_restlen); + mch_memmove(from + ip->bw_restlen, buf, (size_t)len); + } + else + { + from = buf; + fromlen = len; + } + + if (enc2macroman(from, fromlen, + ip->bw_conv_buf, &len, ip->bw_conv_buflen, + ip->bw_rest, &ip->bw_restlen) == FAIL) + { + ip->bw_conv_error = TRUE; + return FAIL; + } + buf = ip->bw_conv_buf; + } +#endif + +#ifdef USE_ICONV + if (ip->bw_iconv_fd != (iconv_t)-1) + { + const char *from; + size_t fromlen; + char *to; + size_t tolen; + + // Convert with iconv(). + if (ip->bw_restlen > 0) + { + char *fp; + + // Need to concatenate the remainder of the previous call and + // the bytes of the current call. Use the end of the + // conversion buffer for this. + fromlen = len + ip->bw_restlen; + fp = (char *)ip->bw_conv_buf + ip->bw_conv_buflen - fromlen; + mch_memmove(fp, ip->bw_rest, (size_t)ip->bw_restlen); + mch_memmove(fp + ip->bw_restlen, buf, (size_t)len); + from = fp; + tolen = ip->bw_conv_buflen - fromlen; + } + else + { + from = (const char *)buf; + fromlen = len; + tolen = ip->bw_conv_buflen; + } + to = (char *)ip->bw_conv_buf; + + if (ip->bw_first) + { + size_t save_len = tolen; + + // output the initial shift state sequence + (void)iconv(ip->bw_iconv_fd, NULL, NULL, &to, &tolen); + + // There is a bug in iconv() on Linux (which appears to be + // wide-spread) which sets "to" to NULL and messes up "tolen". + if (to == NULL) + { + to = (char *)ip->bw_conv_buf; + tolen = save_len; + } + ip->bw_first = FALSE; + } + + // If iconv() has an error or there is not enough room, fail. + if ((iconv(ip->bw_iconv_fd, (void *)&from, &fromlen, &to, &tolen) + == (size_t)-1 && ICONV_ERRNO != ICONV_EINVAL) + || fromlen > CONV_RESTLEN) + { + ip->bw_conv_error = TRUE; + return FAIL; + } + + // copy remainder to ip->bw_rest[] to be used for the next call. + if (fromlen > 0) + mch_memmove(ip->bw_rest, (void *)from, fromlen); + ip->bw_restlen = (int)fromlen; + + buf = ip->bw_conv_buf; + len = (int)((char_u *)to - ip->bw_conv_buf); + } +#endif + } + + if (ip->bw_fd < 0) + // Only checking conversion, which is OK if we get here. + return OK; + +#ifdef FEAT_CRYPT + if (flags & FIO_ENCRYPTED) + { + // Encrypt the data. Do it in-place if possible, otherwise use an + // allocated buffer. +# ifdef CRYPT_NOT_INPLACE + if (crypt_works_inplace(ip->bw_buffer->b_cryptstate)) + { +# endif + crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len); +# ifdef CRYPT_NOT_INPLACE + } + else + { + char_u *outbuf; + + len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf); + if (len == 0) + return OK; // Crypt layer is buffering, will flush later. + wlen = write_eintr(ip->bw_fd, outbuf, len); + vim_free(outbuf); + return (wlen < len) ? FAIL : OK; + } +# endif + } +#endif + + wlen = write_eintr(ip->bw_fd, buf, len); + return (wlen < len) ? FAIL : OK; +} + +/* + * Check modification time of file, before writing to it. + * The size isn't checked, because using a tool like "gzip" takes care of + * using the same timestamp but can't set the size. + */ + static int +check_mtime(buf_T *buf, stat_T *st) +{ + if (buf->b_mtime_read != 0 + && time_differs((long)st->st_mtime, buf->b_mtime_read)) + { + msg_scroll = TRUE; // don't overwrite messages here + msg_silent = 0; // must give this prompt + // don't use emsg() here, don't want to flush the buffers + msg_attr(_("WARNING: The file has been changed since reading it!!!"), + HL_ATTR(HLF_E)); + if (ask_yesno((char_u *)_("Do you really want to write to it"), + TRUE) == 'n') + return FAIL; + msg_scroll = FALSE; // always overwrite the file message now + } + return OK; +} + +/* + * Generate a BOM in "buf[4]" for encoding "name". + * Return the length of the BOM (zero when no BOM). + */ + static int +make_bom(char_u *buf, char_u *name) +{ + int flags; + char_u *p; + + flags = get_fio_flags(name); + + // Can't put a BOM in a non-Unicode file. + if (flags == FIO_LATIN1 || flags == 0) + return 0; + + if (flags == FIO_UTF8) // UTF-8 + { + buf[0] = 0xef; + buf[1] = 0xbb; + buf[2] = 0xbf; + return 3; + } + p = buf; + (void)ucs2bytes(0xfeff, &p, flags); + return (int)(p - buf); +} + +#ifdef UNIX + static void +set_file_time( + char_u *fname, + time_t atime, // access time + time_t mtime) // modification time +{ +# if defined(HAVE_UTIME) && defined(HAVE_UTIME_H) + struct utimbuf buf; + + buf.actime = atime; + buf.modtime = mtime; + (void)utime((char *)fname, &buf); +# else +# if defined(HAVE_UTIMES) + struct timeval tvp[2]; + + tvp[0].tv_sec = atime; + tvp[0].tv_usec = 0; + tvp[1].tv_sec = mtime; + tvp[1].tv_usec = 0; +# ifdef NeXT + (void)utimes((char *)fname, tvp); +# else + (void)utimes((char *)fname, (const struct timeval *)&tvp); +# endif +# endif +# endif +} +#endif // UNIX + +/* + * buf_write() - write to file "fname" lines "start" through "end" + * + * We do our own buffering here because fwrite() is so slow. + * + * If "forceit" is true, we don't care for errors when attempting backups. + * In case of an error everything possible is done to restore the original + * file. But when "forceit" is TRUE, we risk losing it. + * + * When "reset_changed" is TRUE and "append" == FALSE and "start" == 1 and + * "end" == curbuf->b_ml.ml_line_count, reset curbuf->b_changed. + * + * This function must NOT use NameBuff (because it's called by autowrite()). + * + * return FAIL for failure, OK otherwise + */ + int +buf_write( + buf_T *buf, + char_u *fname, + char_u *sfname, + linenr_T start, + linenr_T end, + exarg_T *eap, // for forced 'ff' and 'fenc', can be + // NULL! + int append, // append to the file + int forceit, + int reset_changed, + int filtering) +{ + int fd; + char_u *backup = NULL; + int backup_copy = FALSE; // copy the original file? + int dobackup; + char_u *ffname; + char_u *wfname = NULL; // name of file to write to + char_u *s; + char_u *ptr; + char_u c; + int len; + linenr_T lnum; + long nchars; + char_u *errmsg = NULL; + int errmsg_allocated = FALSE; + char_u *errnum = NULL; + char_u *buffer; + char_u smallbuf[SMALLBUFSIZE]; + char_u *backup_ext; + int bufsize; + long perm; // file permissions + int retval = OK; + int newfile = FALSE; // TRUE if file doesn't exist yet + int msg_save = msg_scroll; + int overwriting; // TRUE if writing over original + int no_eol = FALSE; // no end-of-line written + int device = FALSE; // writing to a device + stat_T st_old; + int prev_got_int = got_int; + int checking_conversion; + int file_readonly = FALSE; // overwritten file is read-only + static char *err_readonly = "is read-only (cannot override: \"W\" in 'cpoptions')"; +#if defined(UNIX) // XXX fix me sometime? + int made_writable = FALSE; // 'w' bit has been set +#endif + // writing everything + int whole = (start == 1 && end == buf->b_ml.ml_line_count); + linenr_T old_line_count = buf->b_ml.ml_line_count; + int attr; + int fileformat; + int write_bin; + struct bw_info write_info; // info for buf_write_bytes() + int converted = FALSE; + int notconverted = FALSE; + char_u *fenc; // effective 'fileencoding' + char_u *fenc_tofree = NULL; // allocated "fenc" + int wb_flags = 0; +#ifdef HAVE_ACL + vim_acl_T acl = NULL; // ACL copied from original file to + // backup or new file +#endif +#ifdef FEAT_PERSISTENT_UNDO + int write_undo_file = FALSE; + context_sha256_T sha_ctx; +#endif + unsigned int bkc = get_bkc_value(buf); + + if (fname == NULL || *fname == NUL) // safety check + return FAIL; + if (buf->b_ml.ml_mfp == NULL) + { + // This can happen during startup when there is a stray "w" in the |