summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBram Moolenaar <Bram@vim.org>2019-09-28 16:30:04 +0200
committerBram Moolenaar <Bram@vim.org>2019-09-28 16:30:04 +0200
commit473952e85286eb9c6098801f1819981ba61ad153 (patch)
tree853c22efb3c9b723e336b560e756da38db610021
parent9be0e0b9d31e42d0074527a7789836087475142a (diff)
patch 8.1.2094: the fileio.c file is too bigv8.1.2094
Problem: The fileio.c file is too big. Solution: Move buf_write() to bufwrite.c. (Yegappan Lakshmanan, closes #4990)
-rw-r--r--Filelist2
-rw-r--r--src/Make_cyg_ming.mak1
-rw-r--r--src/Make_morph.mak1
-rw-r--r--src/Make_mvc.mak4
-rw-r--r--src/Make_vms.mms242
-rw-r--r--src/Makefile10
-rw-r--r--src/README.md1
-rw-r--r--src/bufwrite.c2559
-rw-r--r--src/fileio.c2780
-rw-r--r--src/option.c4
-rw-r--r--src/proto.h1
-rw-r--r--src/proto/bufwrite.pro4
-rw-r--r--src/proto/fileio.pro10
-rw-r--r--src/structs.h29
-rw-r--r--src/version.c2
15 files changed, 2850 insertions, 2800 deletions
diff --git a/Filelist b/Filelist
index 2d05b4a3af..dfc06245e5 100644
--- a/Filelist
+++ b/Filelist
@@ -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