xref: /vim-8.2.3635/src/bufwrite.c (revision db3b4464)
1473952e8SBram Moolenaar /* vi:set ts=8 sts=4 sw=4 noet:
2473952e8SBram Moolenaar  *
3473952e8SBram Moolenaar  * VIM - Vi IMproved	by Bram Moolenaar
4473952e8SBram Moolenaar  *
5473952e8SBram Moolenaar  * Do ":help uganda"  in Vim to read copying and usage conditions.
6473952e8SBram Moolenaar  * Do ":help credits" in Vim to see a list of people who contributed.
7473952e8SBram Moolenaar  * See README.txt for an overview of the Vim source code.
8473952e8SBram Moolenaar  */
9473952e8SBram Moolenaar 
10473952e8SBram Moolenaar /*
11473952e8SBram Moolenaar  * bufwrite.c: functions for writing a buffer
12473952e8SBram Moolenaar  */
13473952e8SBram Moolenaar 
14473952e8SBram Moolenaar #include "vim.h"
15473952e8SBram Moolenaar 
16473952e8SBram Moolenaar #if defined(HAVE_UTIME) && defined(HAVE_UTIME_H)
17473952e8SBram Moolenaar # include <utime.h>		// for struct utimbuf
18473952e8SBram Moolenaar #endif
19473952e8SBram Moolenaar 
20473952e8SBram Moolenaar #define SMALLBUFSIZE	256	// size of emergency write buffer
21473952e8SBram Moolenaar 
22473952e8SBram Moolenaar /*
23473952e8SBram Moolenaar  * Structure to pass arguments from buf_write() to buf_write_bytes().
24473952e8SBram Moolenaar  */
25473952e8SBram Moolenaar struct bw_info
26473952e8SBram Moolenaar {
27473952e8SBram Moolenaar     int		bw_fd;		// file descriptor
28473952e8SBram Moolenaar     char_u	*bw_buf;	// buffer with data to be written
29473952e8SBram Moolenaar     int		bw_len;		// length of data
30473952e8SBram Moolenaar     int		bw_flags;	// FIO_ flags
31473952e8SBram Moolenaar #ifdef FEAT_CRYPT
32473952e8SBram Moolenaar     buf_T	*bw_buffer;	// buffer being written
33f573c6e1SChristian Brabandt     int         bw_finish;      // finish encrypting
34473952e8SBram Moolenaar #endif
35473952e8SBram Moolenaar     char_u	bw_rest[CONV_RESTLEN]; // not converted bytes
36473952e8SBram Moolenaar     int		bw_restlen;	// nr of bytes in bw_rest[]
37473952e8SBram Moolenaar     int		bw_first;	// first write call
38473952e8SBram Moolenaar     char_u	*bw_conv_buf;	// buffer for writing converted chars
39473952e8SBram Moolenaar     size_t	bw_conv_buflen; // size of bw_conv_buf
40473952e8SBram Moolenaar     int		bw_conv_error;	// set for conversion error
41473952e8SBram Moolenaar     linenr_T	bw_conv_error_lnum;  // first line with error or zero
42473952e8SBram Moolenaar     linenr_T	bw_start_lnum;  // line number at start of buffer
43473952e8SBram Moolenaar #ifdef USE_ICONV
44473952e8SBram Moolenaar     iconv_t	bw_iconv_fd;	// descriptor for iconv() or -1
45473952e8SBram Moolenaar #endif
46473952e8SBram Moolenaar };
47473952e8SBram Moolenaar 
48473952e8SBram Moolenaar /*
49473952e8SBram Moolenaar  * Convert a Unicode character to bytes.
50473952e8SBram Moolenaar  * Return TRUE for an error, FALSE when it's OK.
51473952e8SBram Moolenaar  */
52473952e8SBram Moolenaar     static int
ucs2bytes(unsigned c,char_u ** pp,int flags)53473952e8SBram Moolenaar ucs2bytes(
54473952e8SBram Moolenaar     unsigned	c,		// in: character
55473952e8SBram Moolenaar     char_u	**pp,		// in/out: pointer to result
56473952e8SBram Moolenaar     int		flags)		// FIO_ flags
57473952e8SBram Moolenaar {
58473952e8SBram Moolenaar     char_u	*p = *pp;
59473952e8SBram Moolenaar     int		error = FALSE;
60473952e8SBram Moolenaar     int		cc;
61473952e8SBram Moolenaar 
62473952e8SBram Moolenaar 
63473952e8SBram Moolenaar     if (flags & FIO_UCS4)
64473952e8SBram Moolenaar     {
65473952e8SBram Moolenaar 	if (flags & FIO_ENDIAN_L)
66473952e8SBram Moolenaar 	{
67473952e8SBram Moolenaar 	    *p++ = c;
68473952e8SBram Moolenaar 	    *p++ = (c >> 8);
69473952e8SBram Moolenaar 	    *p++ = (c >> 16);
70473952e8SBram Moolenaar 	    *p++ = (c >> 24);
71473952e8SBram Moolenaar 	}
72473952e8SBram Moolenaar 	else
73473952e8SBram Moolenaar 	{
74473952e8SBram Moolenaar 	    *p++ = (c >> 24);
75473952e8SBram Moolenaar 	    *p++ = (c >> 16);
76473952e8SBram Moolenaar 	    *p++ = (c >> 8);
77473952e8SBram Moolenaar 	    *p++ = c;
78473952e8SBram Moolenaar 	}
79473952e8SBram Moolenaar     }
80473952e8SBram Moolenaar     else if (flags & (FIO_UCS2 | FIO_UTF16))
81473952e8SBram Moolenaar     {
82473952e8SBram Moolenaar 	if (c >= 0x10000)
83473952e8SBram Moolenaar 	{
84473952e8SBram Moolenaar 	    if (flags & FIO_UTF16)
85473952e8SBram Moolenaar 	    {
86473952e8SBram Moolenaar 		// Make two words, ten bits of the character in each.  First
87473952e8SBram Moolenaar 		// word is 0xd800 - 0xdbff, second one 0xdc00 - 0xdfff
88473952e8SBram Moolenaar 		c -= 0x10000;
89473952e8SBram Moolenaar 		if (c >= 0x100000)
90473952e8SBram Moolenaar 		    error = TRUE;
91473952e8SBram Moolenaar 		cc = ((c >> 10) & 0x3ff) + 0xd800;
92473952e8SBram Moolenaar 		if (flags & FIO_ENDIAN_L)
93473952e8SBram Moolenaar 		{
94473952e8SBram Moolenaar 		    *p++ = cc;
95473952e8SBram Moolenaar 		    *p++ = ((unsigned)cc >> 8);
96473952e8SBram Moolenaar 		}
97473952e8SBram Moolenaar 		else
98473952e8SBram Moolenaar 		{
99473952e8SBram Moolenaar 		    *p++ = ((unsigned)cc >> 8);
100473952e8SBram Moolenaar 		    *p++ = cc;
101473952e8SBram Moolenaar 		}
102473952e8SBram Moolenaar 		c = (c & 0x3ff) + 0xdc00;
103473952e8SBram Moolenaar 	    }
104473952e8SBram Moolenaar 	    else
105473952e8SBram Moolenaar 		error = TRUE;
106473952e8SBram Moolenaar 	}
107473952e8SBram Moolenaar 	if (flags & FIO_ENDIAN_L)
108473952e8SBram Moolenaar 	{
109473952e8SBram Moolenaar 	    *p++ = c;
110473952e8SBram Moolenaar 	    *p++ = (c >> 8);
111473952e8SBram Moolenaar 	}
112473952e8SBram Moolenaar 	else
113473952e8SBram Moolenaar 	{
114473952e8SBram Moolenaar 	    *p++ = (c >> 8);
115473952e8SBram Moolenaar 	    *p++ = c;
116473952e8SBram Moolenaar 	}
117473952e8SBram Moolenaar     }
118473952e8SBram Moolenaar     else    // Latin1
119473952e8SBram Moolenaar     {
120473952e8SBram Moolenaar 	if (c >= 0x100)
121473952e8SBram Moolenaar 	{
122473952e8SBram Moolenaar 	    error = TRUE;
123473952e8SBram Moolenaar 	    *p++ = 0xBF;
124473952e8SBram Moolenaar 	}
125473952e8SBram Moolenaar 	else
126473952e8SBram Moolenaar 	    *p++ = c;
127473952e8SBram Moolenaar     }
128473952e8SBram Moolenaar 
129473952e8SBram Moolenaar     *pp = p;
130473952e8SBram Moolenaar     return error;
131473952e8SBram Moolenaar }
132473952e8SBram Moolenaar 
133473952e8SBram Moolenaar /*
134473952e8SBram Moolenaar  * Call write() to write a number of bytes to the file.
135473952e8SBram Moolenaar  * Handles encryption and 'encoding' conversion.
136473952e8SBram Moolenaar  *
137473952e8SBram Moolenaar  * Return FAIL for failure, OK otherwise.
138473952e8SBram Moolenaar  */
139473952e8SBram Moolenaar     static int
buf_write_bytes(struct bw_info * ip)140473952e8SBram Moolenaar buf_write_bytes(struct bw_info *ip)
141473952e8SBram Moolenaar {
142473952e8SBram Moolenaar     int		wlen;
143473952e8SBram Moolenaar     char_u	*buf = ip->bw_buf;	// data to write
144473952e8SBram Moolenaar     int		len = ip->bw_len;	// length of data
145473952e8SBram Moolenaar     int		flags = ip->bw_flags;	// extra flags
146473952e8SBram Moolenaar 
147473952e8SBram Moolenaar     // Skip conversion when writing the crypt magic number or the BOM.
148473952e8SBram Moolenaar     if (!(flags & FIO_NOCONVERT))
149473952e8SBram Moolenaar     {
150473952e8SBram Moolenaar 	char_u		*p;
151473952e8SBram Moolenaar 	unsigned	c;
152473952e8SBram Moolenaar 	int		n;
153473952e8SBram Moolenaar 
154473952e8SBram Moolenaar 	if (flags & FIO_UTF8)
155473952e8SBram Moolenaar 	{
156473952e8SBram Moolenaar 	    // Convert latin1 in the buffer to UTF-8 in the file.
157473952e8SBram Moolenaar 	    p = ip->bw_conv_buf;	// translate to buffer
158473952e8SBram Moolenaar 	    for (wlen = 0; wlen < len; ++wlen)
159473952e8SBram Moolenaar 		p += utf_char2bytes(buf[wlen], p);
160473952e8SBram Moolenaar 	    buf = ip->bw_conv_buf;
161473952e8SBram Moolenaar 	    len = (int)(p - ip->bw_conv_buf);
162473952e8SBram Moolenaar 	}
163473952e8SBram Moolenaar 	else if (flags & (FIO_UCS4 | FIO_UTF16 | FIO_UCS2 | FIO_LATIN1))
164473952e8SBram Moolenaar 	{
165473952e8SBram Moolenaar 	    // Convert UTF-8 bytes in the buffer to UCS-2, UCS-4, UTF-16 or
166473952e8SBram Moolenaar 	    // Latin1 chars in the file.
167473952e8SBram Moolenaar 	    if (flags & FIO_LATIN1)
168473952e8SBram Moolenaar 		p = buf;	// translate in-place (can only get shorter)
169473952e8SBram Moolenaar 	    else
170473952e8SBram Moolenaar 		p = ip->bw_conv_buf;	// translate to buffer
171473952e8SBram Moolenaar 	    for (wlen = 0; wlen < len; wlen += n)
172473952e8SBram Moolenaar 	    {
173473952e8SBram Moolenaar 		if (wlen == 0 && ip->bw_restlen != 0)
174473952e8SBram Moolenaar 		{
175473952e8SBram Moolenaar 		    int		l;
176473952e8SBram Moolenaar 
177473952e8SBram Moolenaar 		    // Use remainder of previous call.  Append the start of
178473952e8SBram Moolenaar 		    // buf[] to get a full sequence.  Might still be too
179473952e8SBram Moolenaar 		    // short!
180473952e8SBram Moolenaar 		    l = CONV_RESTLEN - ip->bw_restlen;
181473952e8SBram Moolenaar 		    if (l > len)
182473952e8SBram Moolenaar 			l = len;
183473952e8SBram Moolenaar 		    mch_memmove(ip->bw_rest + ip->bw_restlen, buf, (size_t)l);
184473952e8SBram Moolenaar 		    n = utf_ptr2len_len(ip->bw_rest, ip->bw_restlen + l);
185473952e8SBram Moolenaar 		    if (n > ip->bw_restlen + len)
186473952e8SBram Moolenaar 		    {
187473952e8SBram Moolenaar 			// We have an incomplete byte sequence at the end to
188473952e8SBram Moolenaar 			// be written.  We can't convert it without the
189473952e8SBram Moolenaar 			// remaining bytes.  Keep them for the next call.
190473952e8SBram Moolenaar 			if (ip->bw_restlen + len > CONV_RESTLEN)
191473952e8SBram Moolenaar 			    return FAIL;
192473952e8SBram Moolenaar 			ip->bw_restlen += len;
193473952e8SBram Moolenaar 			break;
194473952e8SBram Moolenaar 		    }
195473952e8SBram Moolenaar 		    if (n > 1)
196473952e8SBram Moolenaar 			c = utf_ptr2char(ip->bw_rest);
197473952e8SBram Moolenaar 		    else
198473952e8SBram Moolenaar 			c = ip->bw_rest[0];
199473952e8SBram Moolenaar 		    if (n >= ip->bw_restlen)
200473952e8SBram Moolenaar 		    {
201473952e8SBram Moolenaar 			n -= ip->bw_restlen;
202473952e8SBram Moolenaar 			ip->bw_restlen = 0;
203473952e8SBram Moolenaar 		    }
204473952e8SBram Moolenaar 		    else
205473952e8SBram Moolenaar 		    {
206473952e8SBram Moolenaar 			ip->bw_restlen -= n;
207473952e8SBram Moolenaar 			mch_memmove(ip->bw_rest, ip->bw_rest + n,
208473952e8SBram Moolenaar 						      (size_t)ip->bw_restlen);
209473952e8SBram Moolenaar 			n = 0;
210473952e8SBram Moolenaar 		    }
211473952e8SBram Moolenaar 		}
212473952e8SBram Moolenaar 		else
213473952e8SBram Moolenaar 		{
214473952e8SBram Moolenaar 		    n = utf_ptr2len_len(buf + wlen, len - wlen);
215473952e8SBram Moolenaar 		    if (n > len - wlen)
216473952e8SBram Moolenaar 		    {
217473952e8SBram Moolenaar 			// We have an incomplete byte sequence at the end to
218473952e8SBram Moolenaar 			// be written.  We can't convert it without the
219473952e8SBram Moolenaar 			// remaining bytes.  Keep them for the next call.
220473952e8SBram Moolenaar 			if (len - wlen > CONV_RESTLEN)
221473952e8SBram Moolenaar 			    return FAIL;
222473952e8SBram Moolenaar 			ip->bw_restlen = len - wlen;
223473952e8SBram Moolenaar 			mch_memmove(ip->bw_rest, buf + wlen,
224473952e8SBram Moolenaar 						      (size_t)ip->bw_restlen);
225473952e8SBram Moolenaar 			break;
226473952e8SBram Moolenaar 		    }
227473952e8SBram Moolenaar 		    if (n > 1)
228473952e8SBram Moolenaar 			c = utf_ptr2char(buf + wlen);
229473952e8SBram Moolenaar 		    else
230473952e8SBram Moolenaar 			c = buf[wlen];
231473952e8SBram Moolenaar 		}
232473952e8SBram Moolenaar 
233473952e8SBram Moolenaar 		if (ucs2bytes(c, &p, flags) && !ip->bw_conv_error)
234473952e8SBram Moolenaar 		{
235473952e8SBram Moolenaar 		    ip->bw_conv_error = TRUE;
236473952e8SBram Moolenaar 		    ip->bw_conv_error_lnum = ip->bw_start_lnum;
237473952e8SBram Moolenaar 		}
238473952e8SBram Moolenaar 		if (c == NL)
239473952e8SBram Moolenaar 		    ++ip->bw_start_lnum;
240473952e8SBram Moolenaar 	    }
241473952e8SBram Moolenaar 	    if (flags & FIO_LATIN1)
242473952e8SBram Moolenaar 		len = (int)(p - buf);
243473952e8SBram Moolenaar 	    else
244473952e8SBram Moolenaar 	    {
245473952e8SBram Moolenaar 		buf = ip->bw_conv_buf;
246473952e8SBram Moolenaar 		len = (int)(p - ip->bw_conv_buf);
247473952e8SBram Moolenaar 	    }
248473952e8SBram Moolenaar 	}
249473952e8SBram Moolenaar 
250473952e8SBram Moolenaar #ifdef MSWIN
251473952e8SBram Moolenaar 	else if (flags & FIO_CODEPAGE)
252473952e8SBram Moolenaar 	{
253473952e8SBram Moolenaar 	    // Convert UTF-8 or codepage to UCS-2 and then to MS-Windows
254473952e8SBram Moolenaar 	    // codepage.
255473952e8SBram Moolenaar 	    char_u	*from;
256473952e8SBram Moolenaar 	    size_t	fromlen;
257473952e8SBram Moolenaar 	    char_u	*to;
258473952e8SBram Moolenaar 	    int		u8c;
259473952e8SBram Moolenaar 	    BOOL	bad = FALSE;
260473952e8SBram Moolenaar 	    int		needed;
261473952e8SBram Moolenaar 
262473952e8SBram Moolenaar 	    if (ip->bw_restlen > 0)
263473952e8SBram Moolenaar 	    {
264473952e8SBram Moolenaar 		// Need to concatenate the remainder of the previous call and
265473952e8SBram Moolenaar 		// the bytes of the current call.  Use the end of the
266473952e8SBram Moolenaar 		// conversion buffer for this.
267473952e8SBram Moolenaar 		fromlen = len + ip->bw_restlen;
268473952e8SBram Moolenaar 		from = ip->bw_conv_buf + ip->bw_conv_buflen - fromlen;
269473952e8SBram Moolenaar 		mch_memmove(from, ip->bw_rest, (size_t)ip->bw_restlen);
270473952e8SBram Moolenaar 		mch_memmove(from + ip->bw_restlen, buf, (size_t)len);
271473952e8SBram Moolenaar 	    }
272473952e8SBram Moolenaar 	    else
273473952e8SBram Moolenaar 	    {
274473952e8SBram Moolenaar 		from = buf;
275473952e8SBram Moolenaar 		fromlen = len;
276473952e8SBram Moolenaar 	    }
277473952e8SBram Moolenaar 
278473952e8SBram Moolenaar 	    to = ip->bw_conv_buf;
279473952e8SBram Moolenaar 	    if (enc_utf8)
280473952e8SBram Moolenaar 	    {
281473952e8SBram Moolenaar 		// Convert from UTF-8 to UCS-2, to the start of the buffer.
282473952e8SBram Moolenaar 		// The buffer has been allocated to be big enough.
283473952e8SBram Moolenaar 		while (fromlen > 0)
284473952e8SBram Moolenaar 		{
285473952e8SBram Moolenaar 		    n = (int)utf_ptr2len_len(from, (int)fromlen);
286473952e8SBram Moolenaar 		    if (n > (int)fromlen)	// incomplete byte sequence
287473952e8SBram Moolenaar 			break;
288473952e8SBram Moolenaar 		    u8c = utf_ptr2char(from);
289473952e8SBram Moolenaar 		    *to++ = (u8c & 0xff);
290473952e8SBram Moolenaar 		    *to++ = (u8c >> 8);
291473952e8SBram Moolenaar 		    fromlen -= n;
292473952e8SBram Moolenaar 		    from += n;
293473952e8SBram Moolenaar 		}
294473952e8SBram Moolenaar 
295473952e8SBram Moolenaar 		// Copy remainder to ip->bw_rest[] to be used for the next
296473952e8SBram Moolenaar 		// call.
297473952e8SBram Moolenaar 		if (fromlen > CONV_RESTLEN)
298473952e8SBram Moolenaar 		{
299473952e8SBram Moolenaar 		    // weird overlong sequence
300473952e8SBram Moolenaar 		    ip->bw_conv_error = TRUE;
301473952e8SBram Moolenaar 		    return FAIL;
302473952e8SBram Moolenaar 		}
303473952e8SBram Moolenaar 		mch_memmove(ip->bw_rest, from, fromlen);
304473952e8SBram Moolenaar 		ip->bw_restlen = (int)fromlen;
305473952e8SBram Moolenaar 	    }
306473952e8SBram Moolenaar 	    else
307473952e8SBram Moolenaar 	    {
308473952e8SBram Moolenaar 		// Convert from enc_codepage to UCS-2, to the start of the
309473952e8SBram Moolenaar 		// buffer.  The buffer has been allocated to be big enough.
310473952e8SBram Moolenaar 		ip->bw_restlen = 0;
311473952e8SBram Moolenaar 		needed = MultiByteToWideChar(enc_codepage,
312473952e8SBram Moolenaar 			     MB_ERR_INVALID_CHARS, (LPCSTR)from, (int)fromlen,
313473952e8SBram Moolenaar 								     NULL, 0);
314473952e8SBram Moolenaar 		if (needed == 0)
315473952e8SBram Moolenaar 		{
316473952e8SBram Moolenaar 		    // When conversion fails there may be a trailing byte.
317473952e8SBram Moolenaar 		    needed = MultiByteToWideChar(enc_codepage,
318473952e8SBram Moolenaar 			 MB_ERR_INVALID_CHARS, (LPCSTR)from, (int)fromlen - 1,
319473952e8SBram Moolenaar 								     NULL, 0);
320473952e8SBram Moolenaar 		    if (needed == 0)
321473952e8SBram Moolenaar 		    {
322473952e8SBram Moolenaar 			// Conversion doesn't work.
323473952e8SBram Moolenaar 			ip->bw_conv_error = TRUE;
324473952e8SBram Moolenaar 			return FAIL;
325473952e8SBram Moolenaar 		    }
326473952e8SBram Moolenaar 		    // Save the trailing byte for the next call.
327473952e8SBram Moolenaar 		    ip->bw_rest[0] = from[fromlen - 1];
328473952e8SBram Moolenaar 		    ip->bw_restlen = 1;
329473952e8SBram Moolenaar 		}
330473952e8SBram Moolenaar 		needed = MultiByteToWideChar(enc_codepage, MB_ERR_INVALID_CHARS,
331473952e8SBram Moolenaar 				(LPCSTR)from, (int)(fromlen - ip->bw_restlen),
332473952e8SBram Moolenaar 							  (LPWSTR)to, needed);
333473952e8SBram Moolenaar 		if (needed == 0)
334473952e8SBram Moolenaar 		{
335473952e8SBram Moolenaar 		    // Safety check: Conversion doesn't work.
336473952e8SBram Moolenaar 		    ip->bw_conv_error = TRUE;
337473952e8SBram Moolenaar 		    return FAIL;
338473952e8SBram Moolenaar 		}
339473952e8SBram Moolenaar 		to += needed * 2;
340473952e8SBram Moolenaar 	    }
341473952e8SBram Moolenaar 
342473952e8SBram Moolenaar 	    fromlen = to - ip->bw_conv_buf;
343473952e8SBram Moolenaar 	    buf = to;
344473952e8SBram Moolenaar # ifdef CP_UTF8	// VC 4.1 doesn't define CP_UTF8
345473952e8SBram Moolenaar 	    if (FIO_GET_CP(flags) == CP_UTF8)
346473952e8SBram Moolenaar 	    {
347473952e8SBram Moolenaar 		// Convert from UCS-2 to UTF-8, using the remainder of the
348473952e8SBram Moolenaar 		// conversion buffer.  Fails when out of space.
349473952e8SBram Moolenaar 		for (from = ip->bw_conv_buf; fromlen > 1; fromlen -= 2)
350473952e8SBram Moolenaar 		{
351473952e8SBram Moolenaar 		    u8c = *from++;
352473952e8SBram Moolenaar 		    u8c += (*from++ << 8);
353473952e8SBram Moolenaar 		    to += utf_char2bytes(u8c, to);
354473952e8SBram Moolenaar 		    if (to + 6 >= ip->bw_conv_buf + ip->bw_conv_buflen)
355473952e8SBram Moolenaar 		    {
356473952e8SBram Moolenaar 			ip->bw_conv_error = TRUE;
357473952e8SBram Moolenaar 			return FAIL;
358473952e8SBram Moolenaar 		    }
359473952e8SBram Moolenaar 		}
360473952e8SBram Moolenaar 		len = (int)(to - buf);
361473952e8SBram Moolenaar 	    }
362473952e8SBram Moolenaar 	    else
363473952e8SBram Moolenaar # endif
364473952e8SBram Moolenaar 	    {
365473952e8SBram Moolenaar 		// Convert from UCS-2 to the codepage, using the remainder of
366473952e8SBram Moolenaar 		// the conversion buffer.  If the conversion uses the default
367473952e8SBram Moolenaar 		// character "0", the data doesn't fit in this encoding, so
368473952e8SBram Moolenaar 		// fail.
369473952e8SBram Moolenaar 		len = WideCharToMultiByte(FIO_GET_CP(flags), 0,
370473952e8SBram Moolenaar 			(LPCWSTR)ip->bw_conv_buf, (int)fromlen / sizeof(WCHAR),
371473952e8SBram Moolenaar 			(LPSTR)to, (int)(ip->bw_conv_buflen - fromlen), 0,
372473952e8SBram Moolenaar 									&bad);
373473952e8SBram Moolenaar 		if (bad)
374473952e8SBram Moolenaar 		{
375473952e8SBram Moolenaar 		    ip->bw_conv_error = TRUE;
376473952e8SBram Moolenaar 		    return FAIL;
377473952e8SBram Moolenaar 		}
378473952e8SBram Moolenaar 	    }
379473952e8SBram Moolenaar 	}
380473952e8SBram Moolenaar #endif
381473952e8SBram Moolenaar 
382473952e8SBram Moolenaar #ifdef MACOS_CONVERT
383473952e8SBram Moolenaar 	else if (flags & FIO_MACROMAN)
384473952e8SBram Moolenaar 	{
385473952e8SBram Moolenaar 	    // Convert UTF-8 or latin1 to Apple MacRoman.
386473952e8SBram Moolenaar 	    char_u	*from;
387473952e8SBram Moolenaar 	    size_t	fromlen;
388473952e8SBram Moolenaar 
389473952e8SBram Moolenaar 	    if (ip->bw_restlen > 0)
390473952e8SBram Moolenaar 	    {
391473952e8SBram Moolenaar 		// Need to concatenate the remainder of the previous call and
392473952e8SBram Moolenaar 		// the bytes of the current call.  Use the end of the
393473952e8SBram Moolenaar 		// conversion buffer for this.
394473952e8SBram Moolenaar 		fromlen = len + ip->bw_restlen;
395473952e8SBram Moolenaar 		from = ip->bw_conv_buf + ip->bw_conv_buflen - fromlen;
396473952e8SBram Moolenaar 		mch_memmove(from, ip->bw_rest, (size_t)ip->bw_restlen);
397473952e8SBram Moolenaar 		mch_memmove(from + ip->bw_restlen, buf, (size_t)len);
398473952e8SBram Moolenaar 	    }
399473952e8SBram Moolenaar 	    else
400473952e8SBram Moolenaar 	    {
401473952e8SBram Moolenaar 		from = buf;
402473952e8SBram Moolenaar 		fromlen = len;
403473952e8SBram Moolenaar 	    }
404473952e8SBram Moolenaar 
405473952e8SBram Moolenaar 	    if (enc2macroman(from, fromlen,
406473952e8SBram Moolenaar 			ip->bw_conv_buf, &len, ip->bw_conv_buflen,
407473952e8SBram Moolenaar 			ip->bw_rest, &ip->bw_restlen) == FAIL)
408473952e8SBram Moolenaar 	    {
409473952e8SBram Moolenaar 		ip->bw_conv_error = TRUE;
410473952e8SBram Moolenaar 		return FAIL;
411473952e8SBram Moolenaar 	    }
412473952e8SBram Moolenaar 	    buf = ip->bw_conv_buf;
413473952e8SBram Moolenaar 	}
414473952e8SBram Moolenaar #endif
415473952e8SBram Moolenaar 
416473952e8SBram Moolenaar #ifdef USE_ICONV
417473952e8SBram Moolenaar 	if (ip->bw_iconv_fd != (iconv_t)-1)
418473952e8SBram Moolenaar 	{
419473952e8SBram Moolenaar 	    const char	*from;
420473952e8SBram Moolenaar 	    size_t	fromlen;
421473952e8SBram Moolenaar 	    char	*to;
422473952e8SBram Moolenaar 	    size_t	tolen;
423473952e8SBram Moolenaar 
424473952e8SBram Moolenaar 	    // Convert with iconv().
425473952e8SBram Moolenaar 	    if (ip->bw_restlen > 0)
426473952e8SBram Moolenaar 	    {
427473952e8SBram Moolenaar 		char *fp;
428473952e8SBram Moolenaar 
429473952e8SBram Moolenaar 		// Need to concatenate the remainder of the previous call and
430473952e8SBram Moolenaar 		// the bytes of the current call.  Use the end of the
431473952e8SBram Moolenaar 		// conversion buffer for this.
432473952e8SBram Moolenaar 		fromlen = len + ip->bw_restlen;
433473952e8SBram Moolenaar 		fp = (char *)ip->bw_conv_buf + ip->bw_conv_buflen - fromlen;
434473952e8SBram Moolenaar 		mch_memmove(fp, ip->bw_rest, (size_t)ip->bw_restlen);
435473952e8SBram Moolenaar 		mch_memmove(fp + ip->bw_restlen, buf, (size_t)len);
436473952e8SBram Moolenaar 		from = fp;
437473952e8SBram Moolenaar 		tolen = ip->bw_conv_buflen - fromlen;
438473952e8SBram Moolenaar 	    }
439473952e8SBram Moolenaar 	    else
440473952e8SBram Moolenaar 	    {
441473952e8SBram Moolenaar 		from = (const char *)buf;
442473952e8SBram Moolenaar 		fromlen = len;
443473952e8SBram Moolenaar 		tolen = ip->bw_conv_buflen;
444473952e8SBram Moolenaar 	    }
445473952e8SBram Moolenaar 	    to = (char *)ip->bw_conv_buf;
446473952e8SBram Moolenaar 
447473952e8SBram Moolenaar 	    if (ip->bw_first)
448473952e8SBram Moolenaar 	    {
449473952e8SBram Moolenaar 		size_t	save_len = tolen;
450473952e8SBram Moolenaar 
451473952e8SBram Moolenaar 		// output the initial shift state sequence
452473952e8SBram Moolenaar 		(void)iconv(ip->bw_iconv_fd, NULL, NULL, &to, &tolen);
453473952e8SBram Moolenaar 
454473952e8SBram Moolenaar 		// There is a bug in iconv() on Linux (which appears to be
455473952e8SBram Moolenaar 		// wide-spread) which sets "to" to NULL and messes up "tolen".
456473952e8SBram Moolenaar 		if (to == NULL)
457473952e8SBram Moolenaar 		{
458473952e8SBram Moolenaar 		    to = (char *)ip->bw_conv_buf;
459473952e8SBram Moolenaar 		    tolen = save_len;
460473952e8SBram Moolenaar 		}
461473952e8SBram Moolenaar 		ip->bw_first = FALSE;
462473952e8SBram Moolenaar 	    }
463473952e8SBram Moolenaar 
464473952e8SBram Moolenaar 	    // If iconv() has an error or there is not enough room, fail.
465473952e8SBram Moolenaar 	    if ((iconv(ip->bw_iconv_fd, (void *)&from, &fromlen, &to, &tolen)
466473952e8SBram Moolenaar 			== (size_t)-1 && ICONV_ERRNO != ICONV_EINVAL)
467473952e8SBram Moolenaar 						    || fromlen > CONV_RESTLEN)
468473952e8SBram Moolenaar 	    {
469473952e8SBram Moolenaar 		ip->bw_conv_error = TRUE;
470473952e8SBram Moolenaar 		return FAIL;
471473952e8SBram Moolenaar 	    }
472473952e8SBram Moolenaar 
473473952e8SBram Moolenaar 	    // copy remainder to ip->bw_rest[] to be used for the next call.
474473952e8SBram Moolenaar 	    if (fromlen > 0)
475473952e8SBram Moolenaar 		mch_memmove(ip->bw_rest, (void *)from, fromlen);
476473952e8SBram Moolenaar 	    ip->bw_restlen = (int)fromlen;
477473952e8SBram Moolenaar 
478473952e8SBram Moolenaar 	    buf = ip->bw_conv_buf;
479473952e8SBram Moolenaar 	    len = (int)((char_u *)to - ip->bw_conv_buf);
480473952e8SBram Moolenaar 	}
481473952e8SBram Moolenaar #endif
482473952e8SBram Moolenaar     }
483473952e8SBram Moolenaar 
484473952e8SBram Moolenaar     if (ip->bw_fd < 0)
485473952e8SBram Moolenaar 	// Only checking conversion, which is OK if we get here.
486473952e8SBram Moolenaar 	return OK;
487473952e8SBram Moolenaar 
488473952e8SBram Moolenaar #ifdef FEAT_CRYPT
489473952e8SBram Moolenaar     if (flags & FIO_ENCRYPTED)
490473952e8SBram Moolenaar     {
491473952e8SBram Moolenaar 	// Encrypt the data. Do it in-place if possible, otherwise use an
492473952e8SBram Moolenaar 	// allocated buffer.
493473952e8SBram Moolenaar # ifdef CRYPT_NOT_INPLACE
494473952e8SBram Moolenaar 	if (crypt_works_inplace(ip->bw_buffer->b_cryptstate))
495473952e8SBram Moolenaar 	{
496473952e8SBram Moolenaar # endif
49765aee0b7SBram Moolenaar 	    crypt_encode_inplace(ip->bw_buffer->b_cryptstate, buf, len,
49865aee0b7SBram Moolenaar 								ip->bw_finish);
499473952e8SBram Moolenaar # ifdef CRYPT_NOT_INPLACE
500473952e8SBram Moolenaar 	}
501473952e8SBram Moolenaar 	else
502473952e8SBram Moolenaar 	{
503473952e8SBram Moolenaar 	    char_u *outbuf;
504473952e8SBram Moolenaar 
50565aee0b7SBram Moolenaar 	    len = crypt_encode_alloc(curbuf->b_cryptstate, buf, len, &outbuf,
50665aee0b7SBram Moolenaar 								ip->bw_finish);
507473952e8SBram Moolenaar 	    if (len == 0)
508473952e8SBram Moolenaar 		return OK;  // Crypt layer is buffering, will flush later.
509473952e8SBram Moolenaar 	    wlen = write_eintr(ip->bw_fd, outbuf, len);
510473952e8SBram Moolenaar 	    vim_free(outbuf);
511473952e8SBram Moolenaar 	    return (wlen < len) ? FAIL : OK;
512473952e8SBram Moolenaar 	}
513473952e8SBram Moolenaar # endif
514473952e8SBram Moolenaar     }
515473952e8SBram Moolenaar #endif
516473952e8SBram Moolenaar 
517473952e8SBram Moolenaar     wlen = write_eintr(ip->bw_fd, buf, len);
518473952e8SBram Moolenaar     return (wlen < len) ? FAIL : OK;
519473952e8SBram Moolenaar }
520473952e8SBram Moolenaar 
521473952e8SBram Moolenaar /*
522473952e8SBram Moolenaar  * Check modification time of file, before writing to it.
523473952e8SBram Moolenaar  * The size isn't checked, because using a tool like "gzip" takes care of
524473952e8SBram Moolenaar  * using the same timestamp but can't set the size.
525473952e8SBram Moolenaar  */
526473952e8SBram Moolenaar     static int
check_mtime(buf_T * buf,stat_T * st)527473952e8SBram Moolenaar check_mtime(buf_T *buf, stat_T *st)
528473952e8SBram Moolenaar {
529473952e8SBram Moolenaar     if (buf->b_mtime_read != 0
5300a7984afSLeah Neukirchen 		  && time_differs(st, buf->b_mtime_read, buf->b_mtime_read_ns))
531473952e8SBram Moolenaar     {
532473952e8SBram Moolenaar 	msg_scroll = TRUE;	    // don't overwrite messages here
533473952e8SBram Moolenaar 	msg_silent = 0;		    // must give this prompt
534473952e8SBram Moolenaar 	// don't use emsg() here, don't want to flush the buffers
535473952e8SBram Moolenaar 	msg_attr(_("WARNING: The file has been changed since reading it!!!"),
536473952e8SBram Moolenaar 						       HL_ATTR(HLF_E));
537473952e8SBram Moolenaar 	if (ask_yesno((char_u *)_("Do you really want to write to it"),
538473952e8SBram Moolenaar 								 TRUE) == 'n')
539473952e8SBram Moolenaar 	    return FAIL;
540473952e8SBram Moolenaar 	msg_scroll = FALSE;	    // always overwrite the file message now
541473952e8SBram Moolenaar     }
542473952e8SBram Moolenaar     return OK;
543473952e8SBram Moolenaar }
544473952e8SBram Moolenaar 
545473952e8SBram Moolenaar /*
546473952e8SBram Moolenaar  * Generate a BOM in "buf[4]" for encoding "name".
547473952e8SBram Moolenaar  * Return the length of the BOM (zero when no BOM).
548473952e8SBram Moolenaar  */
549473952e8SBram Moolenaar     static int
make_bom(char_u * buf,char_u * name)550473952e8SBram Moolenaar make_bom(char_u *buf, char_u *name)
551473952e8SBram Moolenaar {
552473952e8SBram Moolenaar     int		flags;
553473952e8SBram Moolenaar     char_u	*p;
554473952e8SBram Moolenaar 
555473952e8SBram Moolenaar     flags = get_fio_flags(name);
556473952e8SBram Moolenaar 
557473952e8SBram Moolenaar     // Can't put a BOM in a non-Unicode file.
558473952e8SBram Moolenaar     if (flags == FIO_LATIN1 || flags == 0)
559473952e8SBram Moolenaar 	return 0;
560473952e8SBram Moolenaar 
561473952e8SBram Moolenaar     if (flags == FIO_UTF8)	// UTF-8
562473952e8SBram Moolenaar     {
563473952e8SBram Moolenaar 	buf[0] = 0xef;
564473952e8SBram Moolenaar 	buf[1] = 0xbb;
565473952e8SBram Moolenaar 	buf[2] = 0xbf;
566473952e8SBram Moolenaar 	return 3;
567473952e8SBram Moolenaar     }
568473952e8SBram Moolenaar     p = buf;
569473952e8SBram Moolenaar     (void)ucs2bytes(0xfeff, &p, flags);
570473952e8SBram Moolenaar     return (int)(p - buf);
571473952e8SBram Moolenaar }
572473952e8SBram Moolenaar 
573473952e8SBram Moolenaar #ifdef UNIX
574473952e8SBram Moolenaar     static void
set_file_time(char_u * fname,time_t atime,time_t mtime)575473952e8SBram Moolenaar set_file_time(
576473952e8SBram Moolenaar     char_u  *fname,
577473952e8SBram Moolenaar     time_t  atime,	    // access time
578473952e8SBram Moolenaar     time_t  mtime)	    // modification time
579473952e8SBram Moolenaar {
580473952e8SBram Moolenaar # if defined(HAVE_UTIME) && defined(HAVE_UTIME_H)
581473952e8SBram Moolenaar     struct utimbuf  buf;
582473952e8SBram Moolenaar 
583473952e8SBram Moolenaar     buf.actime	= atime;
584473952e8SBram Moolenaar     buf.modtime	= mtime;
585473952e8SBram Moolenaar     (void)utime((char *)fname, &buf);
586473952e8SBram Moolenaar # else
587473952e8SBram Moolenaar #  if defined(HAVE_UTIMES)
588473952e8SBram Moolenaar     struct timeval  tvp[2];
589473952e8SBram Moolenaar 
590473952e8SBram Moolenaar     tvp[0].tv_sec   = atime;
591473952e8SBram Moolenaar     tvp[0].tv_usec  = 0;
592473952e8SBram Moolenaar     tvp[1].tv_sec   = mtime;
593473952e8SBram Moolenaar     tvp[1].tv_usec  = 0;
594473952e8SBram Moolenaar #   ifdef NeXT
595473952e8SBram Moolenaar     (void)utimes((char *)fname, tvp);
596473952e8SBram Moolenaar #   else
597473952e8SBram Moolenaar     (void)utimes((char *)fname, (const struct timeval *)&tvp);
598473952e8SBram Moolenaar #   endif
599473952e8SBram Moolenaar #  endif
600473952e8SBram Moolenaar # endif
601473952e8SBram Moolenaar }
602473952e8SBram Moolenaar #endif // UNIX
603473952e8SBram Moolenaar 
604722e505dSBram Moolenaar     char *
new_file_message(void)605722e505dSBram Moolenaar new_file_message(void)
606722e505dSBram Moolenaar {
607722e505dSBram Moolenaar     return shortmess(SHM_NEW) ? _("[New]") : _("[New File]");
608722e505dSBram Moolenaar }
609722e505dSBram Moolenaar 
610473952e8SBram Moolenaar /*
611473952e8SBram Moolenaar  * buf_write() - write to file "fname" lines "start" through "end"
612473952e8SBram Moolenaar  *
613473952e8SBram Moolenaar  * We do our own buffering here because fwrite() is so slow.
614473952e8SBram Moolenaar  *
615473952e8SBram Moolenaar  * If "forceit" is true, we don't care for errors when attempting backups.
616473952e8SBram Moolenaar  * In case of an error everything possible is done to restore the original
617473952e8SBram Moolenaar  * file.  But when "forceit" is TRUE, we risk losing it.
618473952e8SBram Moolenaar  *
619473952e8SBram Moolenaar  * When "reset_changed" is TRUE and "append" == FALSE and "start" == 1 and
620473952e8SBram Moolenaar  * "end" == curbuf->b_ml.ml_line_count, reset curbuf->b_changed.
621473952e8SBram Moolenaar  *
622473952e8SBram Moolenaar  * This function must NOT use NameBuff (because it's called by autowrite()).
623473952e8SBram Moolenaar  *
624473952e8SBram Moolenaar  * return FAIL for failure, OK otherwise
625473952e8SBram Moolenaar  */
626473952e8SBram Moolenaar     int
buf_write(buf_T * buf,char_u * fname,char_u * sfname,linenr_T start,linenr_T end,exarg_T * eap,int append,int forceit,int reset_changed,int filtering)627473952e8SBram Moolenaar buf_write(
628473952e8SBram Moolenaar     buf_T	    *buf,
629473952e8SBram Moolenaar     char_u	    *fname,
630473952e8SBram Moolenaar     char_u	    *sfname,
631473952e8SBram Moolenaar     linenr_T	    start,
632473952e8SBram Moolenaar     linenr_T	    end,
633473952e8SBram Moolenaar     exarg_T	    *eap,		// for forced 'ff' and 'fenc', can be
634473952e8SBram Moolenaar 					// NULL!
635473952e8SBram Moolenaar     int		    append,		// append to the file
636473952e8SBram Moolenaar     int		    forceit,
637473952e8SBram Moolenaar     int		    reset_changed,
638473952e8SBram Moolenaar     int		    filtering)
639473952e8SBram Moolenaar {
640473952e8SBram Moolenaar     int		    fd;
641473952e8SBram Moolenaar     char_u	    *backup = NULL;
642473952e8SBram Moolenaar     int		    backup_copy = FALSE; // copy the original file?
643473952e8SBram Moolenaar     int		    dobackup;
644473952e8SBram Moolenaar     char_u	    *ffname;
645473952e8SBram Moolenaar     char_u	    *wfname = NULL;	// name of file to write to
646473952e8SBram Moolenaar     char_u	    *s;
647473952e8SBram Moolenaar     char_u	    *ptr;
648473952e8SBram Moolenaar     char_u	    c;
649473952e8SBram Moolenaar     int		    len;
650473952e8SBram Moolenaar     linenr_T	    lnum;
651473952e8SBram Moolenaar     long	    nchars;
652473952e8SBram Moolenaar     char_u	    *errmsg = NULL;
653473952e8SBram Moolenaar     int		    errmsg_allocated = FALSE;
654473952e8SBram Moolenaar     char_u	    *errnum = NULL;
655473952e8SBram Moolenaar     char_u	    *buffer;
656473952e8SBram Moolenaar     char_u	    smallbuf[SMALLBUFSIZE];
657473952e8SBram Moolenaar     char_u	    *backup_ext;
658473952e8SBram Moolenaar     int		    bufsize;
659473952e8SBram Moolenaar     long	    perm;		    // file permissions
660473952e8SBram Moolenaar     int		    retval = OK;
661473952e8SBram Moolenaar     int		    newfile = FALSE;	    // TRUE if file doesn't exist yet
662473952e8SBram Moolenaar     int		    msg_save = msg_scroll;
663473952e8SBram Moolenaar     int		    overwriting;	    // TRUE if writing over original
664473952e8SBram Moolenaar     int		    no_eol = FALSE;	    // no end-of-line written
665473952e8SBram Moolenaar     int		    device = FALSE;	    // writing to a device
666473952e8SBram Moolenaar     stat_T	    st_old;
667473952e8SBram Moolenaar     int		    prev_got_int = got_int;
668473952e8SBram Moolenaar     int		    checking_conversion;
669473952e8SBram Moolenaar     int		    file_readonly = FALSE;  // overwritten file is read-only
670473952e8SBram Moolenaar     static char	    *err_readonly = "is read-only (cannot override: \"W\" in 'cpoptions')";
671473952e8SBram Moolenaar #if defined(UNIX)			    // XXX fix me sometime?
672473952e8SBram Moolenaar     int		    made_writable = FALSE;  // 'w' bit has been set
673473952e8SBram Moolenaar #endif
674473952e8SBram Moolenaar 					// writing everything
675473952e8SBram Moolenaar     int		    whole = (start == 1 && end == buf->b_ml.ml_line_count);
676473952e8SBram Moolenaar     linenr_T	    old_line_count = buf->b_ml.ml_line_count;
677473952e8SBram Moolenaar     int		    attr;
678473952e8SBram Moolenaar     int		    fileformat;
679473952e8SBram Moolenaar     int		    write_bin;
680473952e8SBram Moolenaar     struct bw_info  write_info;		// info for buf_write_bytes()
681473952e8SBram Moolenaar     int		    converted = FALSE;
682473952e8SBram Moolenaar     int		    notconverted = FALSE;
683473952e8SBram Moolenaar     char_u	    *fenc;		// effective 'fileencoding'
684473952e8SBram Moolenaar     char_u	    *fenc_tofree = NULL; // allocated "fenc"
685473952e8SBram Moolenaar     int		    wb_flags = 0;
686473952e8SBram Moolenaar #ifdef HAVE_ACL
687473952e8SBram Moolenaar     vim_acl_T	    acl = NULL;		// ACL copied from original file to
688473952e8SBram Moolenaar 					// backup or new file
689473952e8SBram Moolenaar #endif
690473952e8SBram Moolenaar #ifdef FEAT_PERSISTENT_UNDO
691473952e8SBram Moolenaar     int		    write_undo_file = FALSE;
692473952e8SBram Moolenaar     context_sha256_T sha_ctx;
693473952e8SBram Moolenaar #endif
694473952e8SBram Moolenaar     unsigned int    bkc = get_bkc_value(buf);
695f4a1d1c0SBram Moolenaar     pos_T	    orig_start = buf->b_op_start;
696f4a1d1c0SBram Moolenaar     pos_T	    orig_end = buf->b_op_end;
697473952e8SBram Moolenaar 
698473952e8SBram Moolenaar     if (fname == NULL || *fname == NUL)	// safety check
699473952e8SBram Moolenaar 	return FAIL;
700473952e8SBram Moolenaar     if (buf->b_ml.ml_mfp == NULL)
701473952e8SBram Moolenaar     {
702473952e8SBram Moolenaar 	// This can happen during startup when there is a stray "w" in the
703473952e8SBram Moolenaar 	// vimrc file.
704473952e8SBram Moolenaar 	emsg(_(e_emptybuf));
705473952e8SBram Moolenaar 	return FAIL;
706473952e8SBram Moolenaar     }
707473952e8SBram Moolenaar 
708473952e8SBram Moolenaar     // Disallow writing from .exrc and .vimrc in current directory for
709473952e8SBram Moolenaar     // security reasons.
710473952e8SBram Moolenaar     if (check_secure())
711473952e8SBram Moolenaar 	return FAIL;
712473952e8SBram Moolenaar 
713473952e8SBram Moolenaar     // Avoid a crash for a long name.
714473952e8SBram Moolenaar     if (STRLEN(fname) >= MAXPATHL)
715473952e8SBram Moolenaar     {
716473952e8SBram Moolenaar 	emsg(_(e_longname));
717473952e8SBram Moolenaar 	return FAIL;
718473952e8SBram Moolenaar     }
719473952e8SBram Moolenaar 
720473952e8SBram Moolenaar     // must init bw_conv_buf and bw_iconv_fd before jumping to "fail"
721473952e8SBram Moolenaar     write_info.bw_conv_buf = NULL;
722473952e8SBram Moolenaar     write_info.bw_conv_error = FALSE;
723473952e8SBram Moolenaar     write_info.bw_conv_error_lnum = 0;
724473952e8SBram Moolenaar     write_info.bw_restlen = 0;
725473952e8SBram Moolenaar #ifdef USE_ICONV
726473952e8SBram Moolenaar     write_info.bw_iconv_fd = (iconv_t)-1;
727473952e8SBram Moolenaar #endif
728473952e8SBram Moolenaar #ifdef FEAT_CRYPT
729473952e8SBram Moolenaar     write_info.bw_buffer = buf;
730f573c6e1SChristian Brabandt     write_info.bw_finish = FALSE;
731473952e8SBram Moolenaar #endif
732473952e8SBram Moolenaar 
733473952e8SBram Moolenaar     // After writing a file changedtick changes but we don't want to display
734473952e8SBram Moolenaar     // the line.
735473952e8SBram Moolenaar     ex_no_reprint = TRUE;
736473952e8SBram Moolenaar 
737473952e8SBram Moolenaar     // If there is no file name yet, use the one for the written file.
738473952e8SBram Moolenaar     // BF_NOTEDITED is set to reflect this (in case the write fails).
739473952e8SBram Moolenaar     // Don't do this when the write is for a filter command.
740473952e8SBram Moolenaar     // Don't do this when appending.
741473952e8SBram Moolenaar     // Only do this when 'cpoptions' contains the 'F' flag.
742473952e8SBram Moolenaar     if (buf->b_ffname == NULL
743473952e8SBram Moolenaar 	    && reset_changed
744473952e8SBram Moolenaar 	    && whole
745473952e8SBram Moolenaar 	    && buf == curbuf
746473952e8SBram Moolenaar #ifdef FEAT_QUICKFIX
747473952e8SBram Moolenaar 	    && !bt_nofilename(buf)
748473952e8SBram Moolenaar #endif
749473952e8SBram Moolenaar 	    && !filtering
750473952e8SBram Moolenaar 	    && (!append || vim_strchr(p_cpo, CPO_FNAMEAPP) != NULL)
751473952e8SBram Moolenaar 	    && vim_strchr(p_cpo, CPO_FNAMEW) != NULL)
752473952e8SBram Moolenaar     {
753473952e8SBram Moolenaar 	if (set_rw_fname(fname, sfname) == FAIL)
754473952e8SBram Moolenaar 	    return FAIL;
755473952e8SBram Moolenaar 	buf = curbuf;	    // just in case autocmds made "buf" invalid
756473952e8SBram Moolenaar     }
757473952e8SBram Moolenaar 
758473952e8SBram Moolenaar     if (sfname == NULL)
759473952e8SBram Moolenaar 	sfname = fname;
760473952e8SBram Moolenaar     // For Unix: Use the short file name whenever possible.
761473952e8SBram Moolenaar     // Avoids problems with networks and when directory names are changed.
762473952e8SBram Moolenaar     // Don't do this for MS-DOS, a "cd" in a sub-shell may have moved us to
763473952e8SBram Moolenaar     // another directory, which we don't detect
764473952e8SBram Moolenaar     ffname = fname;			    // remember full fname
765473952e8SBram Moolenaar #ifdef UNIX
766473952e8SBram Moolenaar     fname = sfname;
767473952e8SBram Moolenaar #endif
768473952e8SBram Moolenaar 
769473952e8SBram Moolenaar     if (buf->b_ffname != NULL && fnamecmp(ffname, buf->b_ffname) == 0)
770473952e8SBram Moolenaar 	overwriting = TRUE;
771473952e8SBram Moolenaar     else
772473952e8SBram Moolenaar 	overwriting = FALSE;
773473952e8SBram Moolenaar 
774473952e8SBram Moolenaar     if (exiting)
775473952e8SBram Moolenaar 	settmode(TMODE_COOK);	    // when exiting allow typeahead now
776473952e8SBram Moolenaar 
777473952e8SBram Moolenaar     ++no_wait_return;		    // don't wait for return yet
778473952e8SBram Moolenaar 
779473952e8SBram Moolenaar     // Set '[ and '] marks to the lines to be written.
780473952e8SBram Moolenaar     buf->b_op_start.lnum = start;
781473952e8SBram Moolenaar     buf->b_op_start.col = 0;
782473952e8SBram Moolenaar     buf->b_op_end.lnum = end;
783473952e8SBram Moolenaar     buf->b_op_end.col = 0;
784473952e8SBram Moolenaar 
785473952e8SBram Moolenaar     {
786473952e8SBram Moolenaar 	aco_save_T	aco;
787473952e8SBram Moolenaar 	int		buf_ffname = FALSE;
788473952e8SBram Moolenaar 	int		buf_sfname = FALSE;
789473952e8SBram Moolenaar 	int		buf_fname_f = FALSE;
790473952e8SBram Moolenaar 	int		buf_fname_s = FALSE;
791473952e8SBram Moolenaar 	int		did_cmd = FALSE;
792473952e8SBram Moolenaar 	int		nofile_err = FALSE;
793473952e8SBram Moolenaar 	int		empty_memline = (buf->b_ml.ml_mfp == NULL);
794473952e8SBram Moolenaar 	bufref_T	bufref;
795473952e8SBram Moolenaar 
796473952e8SBram Moolenaar 	// Apply PRE autocommands.
797473952e8SBram Moolenaar 	// Set curbuf to the buffer to be written.
798473952e8SBram Moolenaar 	// Careful: The autocommands may call buf_write() recursively!
799473952e8SBram Moolenaar 	if (ffname == buf->b_ffname)
800473952e8SBram Moolenaar 	    buf_ffname = TRUE;
801473952e8SBram Moolenaar 	if (sfname == buf->b_sfname)
802473952e8SBram Moolenaar 	    buf_sfname = TRUE;
803473952e8SBram Moolenaar 	if (fname == buf->b_ffname)
804473952e8SBram Moolenaar 	    buf_fname_f = TRUE;
805473952e8SBram Moolenaar 	if (fname == buf->b_sfname)
806473952e8SBram Moolenaar 	    buf_fname_s = TRUE;
807473952e8SBram Moolenaar 
808473952e8SBram Moolenaar 	// set curwin/curbuf to buf and save a few things
809473952e8SBram Moolenaar 	aucmd_prepbuf(&aco, buf);
810473952e8SBram Moolenaar 	set_bufref(&bufref, buf);
811473952e8SBram Moolenaar 
812473952e8SBram Moolenaar 	if (append)
813473952e8SBram Moolenaar 	{
814473952e8SBram Moolenaar 	    if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEAPPENDCMD,
815473952e8SBram Moolenaar 					 sfname, sfname, FALSE, curbuf, eap)))
816473952e8SBram Moolenaar 	    {
817473952e8SBram Moolenaar #ifdef FEAT_QUICKFIX
818473952e8SBram Moolenaar 		if (overwriting && bt_nofilename(curbuf))
819473952e8SBram Moolenaar 		    nofile_err = TRUE;
820473952e8SBram Moolenaar 		else
821473952e8SBram Moolenaar #endif
822473952e8SBram Moolenaar 		    apply_autocmds_exarg(EVENT_FILEAPPENDPRE,
823473952e8SBram Moolenaar 					  sfname, sfname, FALSE, curbuf, eap);
824473952e8SBram Moolenaar 	    }
825473952e8SBram Moolenaar 	}
826473952e8SBram Moolenaar 	else if (filtering)
827473952e8SBram Moolenaar 	{
828473952e8SBram Moolenaar 	    apply_autocmds_exarg(EVENT_FILTERWRITEPRE,
829473952e8SBram Moolenaar 					    NULL, sfname, FALSE, curbuf, eap);
830473952e8SBram Moolenaar 	}
831473952e8SBram Moolenaar 	else if (reset_changed && whole)
832473952e8SBram Moolenaar 	{
833473952e8SBram Moolenaar 	    int was_changed = curbufIsChanged();
834473952e8SBram Moolenaar 
835473952e8SBram Moolenaar 	    did_cmd = apply_autocmds_exarg(EVENT_BUFWRITECMD,
836473952e8SBram Moolenaar 					  sfname, sfname, FALSE, curbuf, eap);
837473952e8SBram Moolenaar 	    if (did_cmd)
838473952e8SBram Moolenaar 	    {
839473952e8SBram Moolenaar 		if (was_changed && !curbufIsChanged())
840473952e8SBram Moolenaar 		{
841473952e8SBram Moolenaar 		    // Written everything correctly and BufWriteCmd has reset
842473952e8SBram Moolenaar 		    // 'modified': Correct the undo information so that an
843473952e8SBram Moolenaar 		    // undo now sets 'modified'.
844473952e8SBram Moolenaar 		    u_unchanged(curbuf);
845473952e8SBram Moolenaar 		    u_update_save_nr(curbuf);
846473952e8SBram Moolenaar 		}
847473952e8SBram Moolenaar 	    }
848473952e8SBram Moolenaar 	    else
849473952e8SBram Moolenaar 	    {
850473952e8SBram Moolenaar #ifdef FEAT_QUICKFIX
851473952e8SBram Moolenaar 		if (overwriting && bt_nofilename(curbuf))
852473952e8SBram Moolenaar 		    nofile_err = TRUE;
853473952e8SBram Moolenaar 		else
854473952e8SBram Moolenaar #endif
855473952e8SBram Moolenaar 		    apply_autocmds_exarg(EVENT_BUFWRITEPRE,
856473952e8SBram Moolenaar 					  sfname, sfname, FALSE, curbuf, eap);
857473952e8SBram Moolenaar 	    }
858473952e8SBram Moolenaar 	}
859473952e8SBram Moolenaar 	else
860473952e8SBram Moolenaar 	{
861473952e8SBram Moolenaar 	    if (!(did_cmd = apply_autocmds_exarg(EVENT_FILEWRITECMD,
862473952e8SBram Moolenaar 					 sfname, sfname, FALSE, curbuf, eap)))
863473952e8SBram Moolenaar 	    {
864473952e8SBram Moolenaar #ifdef FEAT_QUICKFIX
865473952e8SBram Moolenaar 		if (overwriting && bt_nofilename(curbuf))
866473952e8SBram Moolenaar 		    nofile_err = TRUE;
867473952e8SBram Moolenaar 		else
868473952e8SBram Moolenaar #endif
869473952e8SBram Moolenaar 		    apply_autocmds_exarg(EVENT_FILEWRITEPRE,
870473952e8SBram Moolenaar 					  sfname, sfname, FALSE, curbuf, eap);
871473952e8SBram Moolenaar 	    }
872473952e8SBram Moolenaar 	}
873473952e8SBram Moolenaar 
874473952e8SBram Moolenaar 	// restore curwin/curbuf and a few other things
875473952e8SBram Moolenaar 	aucmd_restbuf(&aco);
876473952e8SBram Moolenaar 
877473952e8SBram Moolenaar 	// In three situations we return here and don't write the file:
878473952e8SBram Moolenaar 	// 1. the autocommands deleted or unloaded the buffer.
879473952e8SBram Moolenaar 	// 2. The autocommands abort script processing.
880473952e8SBram Moolenaar 	// 3. If one of the "Cmd" autocommands was executed.
881473952e8SBram Moolenaar 	if (!bufref_valid(&bufref))
882473952e8SBram Moolenaar 	    buf = NULL;
883473952e8SBram Moolenaar 	if (buf == NULL || (buf->b_ml.ml_mfp == NULL && !empty_memline)
884473952e8SBram Moolenaar 				       || did_cmd || nofile_err
885473952e8SBram Moolenaar #ifdef FEAT_EVAL
886473952e8SBram Moolenaar 				       || aborting()
887473952e8SBram Moolenaar #endif
888473952e8SBram Moolenaar 				       )
889473952e8SBram Moolenaar 	{
890e1004401SBram Moolenaar 	    if (buf != NULL && (cmdmod.cmod_flags & CMOD_LOCKMARKS))
891f4a1d1c0SBram Moolenaar 	    {
892f4a1d1c0SBram Moolenaar 		// restore the original '[ and '] positions
893f4a1d1c0SBram Moolenaar 		buf->b_op_start = orig_start;
894f4a1d1c0SBram Moolenaar 		buf->b_op_end = orig_end;
895f4a1d1c0SBram Moolenaar 	    }
896f4a1d1c0SBram Moolenaar 
897473952e8SBram Moolenaar 	    --no_wait_return;
898473952e8SBram Moolenaar 	    msg_scroll = msg_save;
899473952e8SBram Moolenaar 	    if (nofile_err)
900473952e8SBram Moolenaar 		emsg(_("E676: No matching autocommands for acwrite buffer"));
901473952e8SBram Moolenaar 
902473952e8SBram Moolenaar 	    if (nofile_err
903473952e8SBram Moolenaar #ifdef FEAT_EVAL
904473952e8SBram Moolenaar 		    || aborting()
905473952e8SBram Moolenaar #endif
906473952e8SBram Moolenaar 		    )
907473952e8SBram Moolenaar 		// An aborting error, interrupt or exception in the
908473952e8SBram Moolenaar 		// autocommands.
909473952e8SBram Moolenaar 		return FAIL;
910473952e8SBram Moolenaar 	    if (did_cmd)
911473952e8SBram Moolenaar 	    {
912473952e8SBram Moolenaar 		if (buf == NULL)
913473952e8SBram Moolenaar 		    // The buffer was deleted.  We assume it was written
914473952e8SBram Moolenaar 		    // (can't retry anyway).
915473952e8SBram Moolenaar 		    return OK;
916473952e8SBram Moolenaar 		if (overwriting)
917473952e8SBram Moolenaar 		{
918473952e8SBram Moolenaar 		    // Assume the buffer was written, update the timestamp.
919473952e8SBram Moolenaar 		    ml_timestamp(buf);
920473952e8SBram Moolenaar 		    if (append)
921473952e8SBram Moolenaar 			buf->b_flags &= ~BF_NEW;
922473952e8SBram Moolenaar 		    else
923473952e8SBram Moolenaar 			buf->b_flags &= ~BF_WRITE_MASK;
924473952e8SBram Moolenaar 		}
925473952e8SBram Moolenaar 		if (reset_changed && buf->b_changed && !append
926473952e8SBram Moolenaar 			&& (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL))
927473952e8SBram Moolenaar 		    // Buffer still changed, the autocommands didn't work
928473952e8SBram Moolenaar 		    // properly.
929473952e8SBram Moolenaar 		    return FAIL;
930473952e8SBram Moolenaar 		return OK;
931473952e8SBram Moolenaar 	    }
932473952e8SBram Moolenaar #ifdef FEAT_EVAL
933473952e8SBram Moolenaar 	    if (!aborting())
934473952e8SBram Moolenaar #endif
935473952e8SBram Moolenaar 		emsg(_("E203: Autocommands deleted or unloaded buffer to be written"));
936473952e8SBram Moolenaar 	    return FAIL;
937473952e8SBram Moolenaar 	}
938473952e8SBram Moolenaar 
939473952e8SBram Moolenaar 	// The autocommands may have changed the number of lines in the file.
940473952e8SBram Moolenaar 	// When writing the whole file, adjust the end.
941473952e8SBram Moolenaar 	// When writing part of the file, assume that the autocommands only
942473952e8SBram Moolenaar 	// changed the number of lines that are to be written (tricky!).
943473952e8SBram Moolenaar 	if (buf->b_ml.ml_line_count != old_line_count)
944473952e8SBram Moolenaar 	{
945473952e8SBram Moolenaar 	    if (whole)						// write all
946473952e8SBram Moolenaar 		end = buf->b_ml.ml_line_count;
947473952e8SBram Moolenaar 	    else if (buf->b_ml.ml_line_count > old_line_count)	// more lines
948473952e8SBram Moolenaar 		end += buf->b_ml.ml_line_count - old_line_count;
949473952e8SBram Moolenaar 	    else						// less lines
950473952e8SBram Moolenaar 	    {
951473952e8SBram Moolenaar 		end -= old_line_count - buf->b_ml.ml_line_count;
952473952e8SBram Moolenaar 		if (end < start)
953473952e8SBram Moolenaar 		{
954473952e8SBram Moolenaar 		    --no_wait_return;
955473952e8SBram Moolenaar 		    msg_scroll = msg_save;
956473952e8SBram Moolenaar 		    emsg(_("E204: Autocommand changed number of lines in unexpected way"));
957473952e8SBram Moolenaar 		    return FAIL;
958473952e8SBram Moolenaar 		}
959473952e8SBram Moolenaar 	    }
960473952e8SBram Moolenaar 	}
961473952e8SBram Moolenaar 
962473952e8SBram Moolenaar 	// The autocommands may have changed the name of the buffer, which may
963473952e8SBram Moolenaar 	// be kept in fname, ffname and sfname.
964473952e8SBram Moolenaar 	if (buf_ffname)
965473952e8SBram Moolenaar 	    ffname = buf->b_ffname;
966473952e8SBram Moolenaar 	if (buf_sfname)
967473952e8SBram Moolenaar 	    sfname = buf->b_sfname;
968473952e8SBram Moolenaar 	if (buf_fname_f)
969473952e8SBram Moolenaar 	    fname = buf->b_ffname;
970473952e8SBram Moolenaar 	if (buf_fname_s)
971473952e8SBram Moolenaar 	    fname = buf->b_sfname;
972473952e8SBram Moolenaar     }
973473952e8SBram Moolenaar 
974e1004401SBram Moolenaar     if (cmdmod.cmod_flags & CMOD_LOCKMARKS)
975f4a1d1c0SBram Moolenaar     {
976f4a1d1c0SBram Moolenaar 	// restore the original '[ and '] positions
977f4a1d1c0SBram Moolenaar 	buf->b_op_start = orig_start;
978f4a1d1c0SBram Moolenaar 	buf->b_op_end = orig_end;
979f4a1d1c0SBram Moolenaar     }
980f4a1d1c0SBram Moolenaar 
981473952e8SBram Moolenaar #ifdef FEAT_NETBEANS_INTG
982473952e8SBram Moolenaar     if (netbeans_active() && isNetbeansBuffer(buf))
983473952e8SBram Moolenaar     {
984473952e8SBram Moolenaar 	if (whole)
985473952e8SBram Moolenaar 	{
986473952e8SBram Moolenaar 	    // b_changed can be 0 after an undo, but we still need to write
987473952e8SBram Moolenaar 	    // the buffer to NetBeans.
988473952e8SBram Moolenaar 	    if (buf->b_changed || isNetbeansModified(buf))
989473952e8SBram Moolenaar 	    {
990473952e8SBram Moolenaar 		--no_wait_return;		// may wait for return now
991473952e8SBram Moolenaar 		msg_scroll = msg_save;
992473952e8SBram Moolenaar 		netbeans_save_buffer(buf);	// no error checking...
993473952e8SBram Moolenaar 		return retval;
994473952e8SBram Moolenaar 	    }
995473952e8SBram Moolenaar 	    else
996473952e8SBram Moolenaar 	    {
997473952e8SBram Moolenaar 		errnum = (char_u *)"E656: ";
998473952e8SBram Moolenaar 		errmsg = (char_u *)_("NetBeans disallows writes of unmodified buffers");
999473952e8SBram Moolenaar 		buffer = NULL;
1000473952e8SBram Moolenaar 		goto fail;
1001473952e8SBram Moolenaar 	    }
1002473952e8SBram Moolenaar 	}
1003473952e8SBram Moolenaar 	else
1004473952e8SBram Moolenaar 	{
1005473952e8SBram Moolenaar 	    errnum = (char_u *)"E657: ";
1006473952e8SBram Moolenaar 	    errmsg = (char_u *)_("Partial writes disallowed for NetBeans buffers");
1007473952e8SBram Moolenaar 	    buffer = NULL;
1008473952e8SBram Moolenaar 	    goto fail;
1009473952e8SBram Moolenaar 	}
1010473952e8SBram Moolenaar     }
1011473952e8SBram Moolenaar #endif
1012473952e8SBram Moolenaar 
1013473952e8SBram Moolenaar     if (shortmess(SHM_OVER) && !exiting)
1014473952e8SBram Moolenaar 	msg_scroll = FALSE;	    // overwrite previous file message
1015473952e8SBram Moolenaar     else
1016473952e8SBram Moolenaar 	msg_scroll = TRUE;	    // don't overwrite previous file message
1017473952e8SBram Moolenaar     if (!filtering)
1018473952e8SBram Moolenaar 	filemess(buf,
1019473952e8SBram Moolenaar #ifndef UNIX
1020473952e8SBram Moolenaar 		sfname,
1021473952e8SBram Moolenaar #else
1022473952e8SBram Moolenaar 		fname,
1023473952e8SBram Moolenaar #endif
1024473952e8SBram Moolenaar 		    (char_u *)"", 0);	// show that we are busy
1025473952e8SBram Moolenaar     msg_scroll = FALSE;		    // always overwrite the file message now
1026473952e8SBram Moolenaar 
1027473952e8SBram Moolenaar     buffer = alloc(WRITEBUFSIZE);
1028473952e8SBram Moolenaar     if (buffer == NULL)		    // can't allocate big buffer, use small
1029473952e8SBram Moolenaar 				    // one (to be able to write when out of
1030473952e8SBram Moolenaar 				    // memory)
1031473952e8SBram Moolenaar     {
1032473952e8SBram Moolenaar 	buffer = smallbuf;
1033473952e8SBram Moolenaar 	bufsize = SMALLBUFSIZE;
1034473952e8SBram Moolenaar     }
1035473952e8SBram Moolenaar     else
1036473952e8SBram Moolenaar 	bufsize = WRITEBUFSIZE;
1037473952e8SBram Moolenaar 
1038473952e8SBram Moolenaar     // Get information about original file (if there is one).
1039473952e8SBram Moolenaar #if defined(UNIX)
1040473952e8SBram Moolenaar     st_old.st_dev = 0;
1041473952e8SBram Moolenaar     st_old.st_ino = 0;
1042473952e8SBram Moolenaar     perm = -1;
1043473952e8SBram Moolenaar     if (mch_stat((char *)fname, &st_old) < 0)
1044473952e8SBram Moolenaar 	newfile = TRUE;
1045473952e8SBram Moolenaar     else
1046473952e8SBram Moolenaar     {
1047473952e8SBram Moolenaar 	perm = st_old.st_mode;
1048473952e8SBram Moolenaar 	if (!S_ISREG(st_old.st_mode))		// not a file
1049473952e8SBram Moolenaar 	{
1050473952e8SBram Moolenaar 	    if (S_ISDIR(st_old.st_mode))
1051473952e8SBram Moolenaar 	    {
1052473952e8SBram Moolenaar 		errnum = (char_u *)"E502: ";
1053473952e8SBram Moolenaar 		errmsg = (char_u *)_("is a directory");
1054473952e8SBram Moolenaar 		goto fail;
1055473952e8SBram Moolenaar 	    }
1056473952e8SBram Moolenaar 	    if (mch_nodetype(fname) != NODE_WRITABLE)
1057473952e8SBram Moolenaar 	    {
1058473952e8SBram Moolenaar 		errnum = (char_u *)"E503: ";
1059473952e8SBram Moolenaar 		errmsg = (char_u *)_("is not a file or writable device");
1060473952e8SBram Moolenaar 		goto fail;
1061473952e8SBram Moolenaar 	    }
1062473952e8SBram Moolenaar 	    // It's a device of some kind (or a fifo) which we can write to
1063473952e8SBram Moolenaar 	    // but for which we can't make a backup.
1064473952e8SBram Moolenaar 	    device = TRUE;
1065473952e8SBram Moolenaar 	    newfile = TRUE;
1066473952e8SBram Moolenaar 	    perm = -1;
1067473952e8SBram Moolenaar 	}
1068473952e8SBram Moolenaar     }
1069473952e8SBram Moolenaar #else // !UNIX
1070473952e8SBram Moolenaar     // Check for a writable device name.
1071473952e8SBram Moolenaar     c = mch_nodetype(fname);
1072473952e8SBram Moolenaar     if (c == NODE_OTHER)
1073473952e8SBram Moolenaar     {
1074473952e8SBram Moolenaar 	errnum = (char_u *)"E503: ";
1075473952e8SBram Moolenaar 	errmsg = (char_u *)_("is not a file or writable device");
1076473952e8SBram Moolenaar 	goto fail;
1077473952e8SBram Moolenaar     }
1078473952e8SBram Moolenaar     if (c == NODE_WRITABLE)
1079473952e8SBram Moolenaar     {
1080473952e8SBram Moolenaar # if defined(MSWIN)
1081473952e8SBram Moolenaar 	// MS-Windows allows opening a device, but we will probably get stuck
1082473952e8SBram Moolenaar 	// trying to write to it.
1083473952e8SBram Moolenaar 	if (!p_odev)
1084473952e8SBram Moolenaar 	{
1085473952e8SBram Moolenaar 	    errnum = (char_u *)"E796: ";
1086473952e8SBram Moolenaar 	    errmsg = (char_u *)_("writing to device disabled with 'opendevice' option");
1087473952e8SBram Moolenaar 	    goto fail;
1088473952e8SBram Moolenaar 	}
1089473952e8SBram Moolenaar # endif
1090473952e8SBram Moolenaar 	device = TRUE;
1091473952e8SBram Moolenaar 	newfile = TRUE;
1092473952e8SBram Moolenaar 	perm = -1;
1093473952e8SBram Moolenaar     }
1094473952e8SBram Moolenaar     else
1095473952e8SBram Moolenaar     {
1096473952e8SBram Moolenaar 	perm = mch_getperm(fname);
1097473952e8SBram Moolenaar 	if (perm < 0)
1098473952e8SBram Moolenaar 	    newfile = TRUE;
1099473952e8SBram Moolenaar 	else if (mch_isdir(fname))
1100473952e8SBram Moolenaar 	{
1101473952e8SBram Moolenaar 	    errnum = (char_u *)"E502: ";
1102473952e8SBram Moolenaar 	    errmsg = (char_u *)_("is a directory");
1103473952e8SBram Moolenaar 	    goto fail;
1104473952e8SBram Moolenaar 	}
1105473952e8SBram Moolenaar 	if (overwriting)
1106473952e8SBram Moolenaar 	    (void)mch_stat((char *)fname, &st_old);
1107473952e8SBram Moolenaar     }
1108473952e8SBram Moolenaar #endif // !UNIX
1109473952e8SBram Moolenaar 
1110473952e8SBram Moolenaar     if (!device && !newfile)
1111473952e8SBram Moolenaar     {
1112473952e8SBram Moolenaar 	// Check if the file is really writable (when renaming the file to
1113473952e8SBram Moolenaar 	// make a backup we won't discover it later).
1114473952e8SBram Moolenaar 	file_readonly = check_file_readonly(fname, (int)perm);
1115473952e8SBram Moolenaar 
1116473952e8SBram Moolenaar 	if (!forceit && file_readonly)
1117473952e8SBram Moolenaar 	{
1118473952e8SBram Moolenaar 	    if (vim_strchr(p_cpo, CPO_FWRITE) != NULL)
1119473952e8SBram Moolenaar 	    {
1120473952e8SBram Moolenaar 		errnum = (char_u *)"E504: ";
1121473952e8SBram Moolenaar 		errmsg = (char_u *)_(err_readonly);
1122473952e8SBram Moolenaar 	    }
1123473952e8SBram Moolenaar 	    else
1124473952e8SBram Moolenaar 	    {
1125473952e8SBram Moolenaar 		errnum = (char_u *)"E505: ";
1126473952e8SBram Moolenaar 		errmsg = (char_u *)_("is read-only (add ! to override)");
1127473952e8SBram Moolenaar 	    }
1128473952e8SBram Moolenaar 	    goto fail;
1129473952e8SBram Moolenaar 	}
1130473952e8SBram Moolenaar 
1131473952e8SBram Moolenaar 	// Check if the timestamp hasn't changed since reading the file.
1132473952e8SBram Moolenaar 	if (overwriting)
1133473952e8SBram Moolenaar 	{
1134473952e8SBram Moolenaar 	    retval = check_mtime(buf, &st_old);
1135473952e8SBram Moolenaar 	    if (retval == FAIL)
1136473952e8SBram Moolenaar 		goto fail;
1137473952e8SBram Moolenaar 	}
1138473952e8SBram Moolenaar     }
1139473952e8SBram Moolenaar 
1140473952e8SBram Moolenaar #ifdef HAVE_ACL
1141473952e8SBram Moolenaar     // For systems that support ACL: get the ACL from the original file.
1142473952e8SBram Moolenaar     if (!newfile)
1143473952e8SBram Moolenaar 	acl = mch_get_acl(fname);
1144473952e8SBram Moolenaar #endif
1145473952e8SBram Moolenaar 
1146473952e8SBram Moolenaar     // If 'backupskip' is not empty, don't make a backup for some files.
1147473952e8SBram Moolenaar     dobackup = (p_wb || p_bk || *p_pm != NUL);
1148473952e8SBram Moolenaar #ifdef FEAT_WILDIGN
1149473952e8SBram Moolenaar     if (dobackup && *p_bsk != NUL && match_file_list(p_bsk, sfname, ffname))
1150473952e8SBram Moolenaar 	dobackup = FALSE;
1151473952e8SBram Moolenaar #endif
1152473952e8SBram Moolenaar 
1153473952e8SBram Moolenaar     // Save the value of got_int and reset it.  We don't want a previous
1154473952e8SBram Moolenaar     // interruption cancel writing, only hitting CTRL-C while writing should
1155473952e8SBram Moolenaar     // abort it.
1156473952e8SBram Moolenaar     prev_got_int = got_int;
1157473952e8SBram Moolenaar     got_int = FALSE;
1158473952e8SBram Moolenaar 
1159473952e8SBram Moolenaar     // Mark the buffer as 'being saved' to prevent changed buffer warnings
1160473952e8SBram Moolenaar     buf->b_saving = TRUE;
1161473952e8SBram Moolenaar 
1162473952e8SBram Moolenaar     // If we are not appending or filtering, the file exists, and the
1163473952e8SBram Moolenaar     // 'writebackup', 'backup' or 'patchmode' option is set, need a backup.
1164473952e8SBram Moolenaar     // When 'patchmode' is set also make a backup when appending.
1165473952e8SBram Moolenaar     //
1166473952e8SBram Moolenaar     // Do not make any backup, if 'writebackup' and 'backup' are both switched
1167473952e8SBram Moolenaar     // off.  This helps when editing large files on almost-full disks.
1168473952e8SBram Moolenaar     if (!(append && *p_pm == NUL) && !filtering && perm >= 0 && dobackup)
1169473952e8SBram Moolenaar     {
1170473952e8SBram Moolenaar #if defined(UNIX) || defined(MSWIN)
1171473952e8SBram Moolenaar 	stat_T	    st;
1172473952e8SBram Moolenaar #endif
1173473952e8SBram Moolenaar 
1174473952e8SBram Moolenaar 	if ((bkc & BKC_YES) || append)	// "yes"
1175473952e8SBram Moolenaar 	    backup_copy = TRUE;
1176473952e8SBram Moolenaar #if defined(UNIX) || defined(MSWIN)
1177473952e8SBram Moolenaar 	else if ((bkc & BKC_AUTO))	// "auto"
1178473952e8SBram Moolenaar 	{
1179473952e8SBram Moolenaar 	    int		i;
1180473952e8SBram Moolenaar 
1181473952e8SBram Moolenaar # ifdef UNIX
1182473952e8SBram Moolenaar 	    // Don't rename the file when:
1183473952e8SBram Moolenaar 	    // - it's a hard link
1184473952e8SBram Moolenaar 	    // - it's a symbolic link
1185473952e8SBram Moolenaar 	    // - we don't have write permission in the directory
1186473952e8SBram Moolenaar 	    // - we can't set the owner/group of the new file
1187473952e8SBram Moolenaar 	    if (st_old.st_nlink > 1
1188473952e8SBram Moolenaar 		    || mch_lstat((char *)fname, &st) < 0
1189473952e8SBram Moolenaar 		    || st.st_dev != st_old.st_dev
1190473952e8SBram Moolenaar 		    || st.st_ino != st_old.st_ino
1191473952e8SBram Moolenaar #  ifndef HAVE_FCHOWN
1192473952e8SBram Moolenaar 		    || st.st_uid != st_old.st_uid
1193473952e8SBram Moolenaar 		    || st.st_gid != st_old.st_gid
1194473952e8SBram Moolenaar #  endif
1195473952e8SBram Moolenaar 		    )
1196473952e8SBram Moolenaar 		backup_copy = TRUE;
1197473952e8SBram Moolenaar 	    else
1198473952e8SBram Moolenaar # else
1199473952e8SBram Moolenaar #  ifdef MSWIN
1200473952e8SBram Moolenaar 	    // On NTFS file systems hard links are possible.
1201473952e8SBram Moolenaar 	    if (mch_is_linked(fname))
1202473952e8SBram Moolenaar 		backup_copy = TRUE;
1203473952e8SBram Moolenaar 	    else
1204473952e8SBram Moolenaar #  endif
1205473952e8SBram Moolenaar # endif
1206473952e8SBram Moolenaar 	    {
1207473952e8SBram Moolenaar 		// Check if we can create a file and set the owner/group to
1208473952e8SBram Moolenaar 		// the ones from the original file.
1209473952e8SBram Moolenaar 		// First find a file name that doesn't exist yet (use some
1210473952e8SBram Moolenaar 		// arbitrary numbers).
1211473952e8SBram Moolenaar 		STRCPY(IObuff, fname);
1212473952e8SBram Moolenaar 		for (i = 4913; ; i += 123)
1213473952e8SBram Moolenaar 		{
1214473952e8SBram Moolenaar 		    sprintf((char *)gettail(IObuff), "%d", i);
1215473952e8SBram Moolenaar 		    if (mch_lstat((char *)IObuff, &st) < 0)
1216473952e8SBram Moolenaar 			break;
1217473952e8SBram Moolenaar 		}
1218473952e8SBram Moolenaar 		fd = mch_open((char *)IObuff,
1219473952e8SBram Moolenaar 				    O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, perm);
1220473952e8SBram Moolenaar 		if (fd < 0)	// can't write in directory
1221473952e8SBram Moolenaar 		    backup_copy = TRUE;
1222473952e8SBram Moolenaar 		else
1223473952e8SBram Moolenaar 		{
1224473952e8SBram Moolenaar # ifdef UNIX
1225473952e8SBram Moolenaar #  ifdef HAVE_FCHOWN
1226473952e8SBram Moolenaar 		    vim_ignored = fchown(fd, st_old.st_uid, st_old.st_gid);
1227473952e8SBram Moolenaar #  endif
1228473952e8SBram Moolenaar 		    if (mch_stat((char *)IObuff, &st) < 0
1229473952e8SBram Moolenaar 			    || st.st_uid != st_old.st_uid
1230473952e8SBram Moolenaar 			    || st.st_gid != st_old.st_gid
1231473952e8SBram Moolenaar 			    || (long)st.st_mode != perm)
1232473952e8SBram Moolenaar 			backup_copy = TRUE;
1233473952e8SBram Moolenaar # endif
1234473952e8SBram Moolenaar 		    // Close the file before removing it, on MS-Windows we
1235473952e8SBram Moolenaar 		    // can't delete an open file.
1236473952e8SBram Moolenaar 		    close(fd);
1237473952e8SBram Moolenaar 		    mch_remove(IObuff);
1238473952e8SBram Moolenaar # ifdef MSWIN
1239473952e8SBram Moolenaar 		    // MS-Windows may trigger a virus scanner to open the
1240473952e8SBram Moolenaar 		    // file, we can't delete it then.  Keep trying for half a
1241473952e8SBram Moolenaar 		    // second.
1242473952e8SBram Moolenaar 		    {
1243473952e8SBram Moolenaar 			int try;
1244473952e8SBram Moolenaar 
1245473952e8SBram Moolenaar 			for (try = 0; try < 10; ++try)
1246473952e8SBram Moolenaar 			{
1247473952e8SBram Moolenaar 			    if (mch_lstat((char *)IObuff, &st) < 0)
1248473952e8SBram Moolenaar 				break;
1249473952e8SBram Moolenaar 			    ui_delay(50L, TRUE);  // wait 50 msec
1250473952e8SBram Moolenaar 			    mch_remove(IObuff);
1251473952e8SBram Moolenaar 			}
1252473952e8SBram Moolenaar 		    }
1253473952e8SBram Moolenaar # endif
1254473952e8SBram Moolenaar 		}
1255473952e8SBram Moolenaar 	    }
1256473952e8SBram Moolenaar 	}
1257473952e8SBram Moolenaar 
1258473952e8SBram Moolenaar 	// Break symlinks and/or hardlinks if we've been asked to.
1259473952e8SBram Moolenaar 	if ((bkc & BKC_BREAKSYMLINK) || (bkc & BKC_BREAKHARDLINK))
1260473952e8SBram Moolenaar 	{
1261473952e8SBram Moolenaar # ifdef UNIX
1262473952e8SBram Moolenaar 	    int	lstat_res;
1263473952e8SBram Moolenaar 
1264473952e8SBram Moolenaar 	    lstat_res = mch_lstat((char *)fname, &st);
1265473952e8SBram Moolenaar 
1266473952e8SBram Moolenaar 	    // Symlinks.
1267473952e8SBram Moolenaar 	    if ((bkc & BKC_BREAKSYMLINK)
1268473952e8SBram Moolenaar 		    && lstat_res == 0
1269473952e8SBram Moolenaar 		    && st.st_ino != st_old.st_ino)
1270473952e8SBram Moolenaar 		backup_copy = FALSE;
1271473952e8SBram Moolenaar 
1272473952e8SBram Moolenaar 	    // Hardlinks.
1273473952e8SBram Moolenaar 	    if ((bkc & BKC_BREAKHARDLINK)
1274473952e8SBram Moolenaar 		    && st_old.st_nlink > 1
1275473952e8SBram Moolenaar 		    && (lstat_res != 0 || st.st_ino == st_old.st_ino))
1276473952e8SBram Moolenaar 		backup_copy = FALSE;
1277473952e8SBram Moolenaar # else
1278473952e8SBram Moolenaar #  if defined(MSWIN)
1279473952e8SBram Moolenaar 	    // Symlinks.
1280473952e8SBram Moolenaar 	    if ((bkc & BKC_BREAKSYMLINK) && mch_is_symbolic_link(fname))
1281473952e8SBram Moolenaar 		backup_copy = FALSE;
1282473952e8SBram Moolenaar 
1283473952e8SBram Moolenaar 	    // Hardlinks.
1284473952e8SBram Moolenaar 	    if ((bkc & BKC_BREAKHARDLINK) && mch_is_hard_link(fname))
1285473952e8SBram Moolenaar 		backup_copy = FALSE;
1286473952e8SBram Moolenaar #  endif
1287473952e8SBram Moolenaar # endif
1288473952e8SBram Moolenaar 	}
1289473952e8SBram Moolenaar 
1290473952e8SBram Moolenaar #endif
1291473952e8SBram Moolenaar 
1292473952e8SBram Moolenaar 	// make sure we have a valid backup extension to use
1293473952e8SBram Moolenaar 	if (*p_bex == NUL)
1294473952e8SBram Moolenaar 	    backup_ext = (char_u *)".bak";
1295473952e8SBram Moolenaar 	else
1296473952e8SBram Moolenaar 	    backup_ext = p_bex;
1297473952e8SBram Moolenaar 
1298473952e8SBram Moolenaar 	if (backup_copy
1299473952e8SBram Moolenaar 		&& (fd = mch_open((char *)fname, O_RDONLY | O_EXTRA, 0)) >= 0)
1300473952e8SBram Moolenaar 	{
1301473952e8SBram Moolenaar 	    int		bfd;
1302473952e8SBram Moolenaar 	    char_u	*copybuf, *wp;
1303473952e8SBram Moolenaar 	    int		some_error = FALSE;
1304473952e8SBram Moolenaar 	    stat_T	st_new;
1305473952e8SBram Moolenaar 	    char_u	*dirp;
1306473952e8SBram Moolenaar 	    char_u	*rootname;
1307473952e8SBram Moolenaar #if defined(UNIX) || defined(MSWIN)
1308473952e8SBram Moolenaar 	    char_u      *p;
1309473952e8SBram Moolenaar #endif
1310473952e8SBram Moolenaar #if defined(UNIX)
1311473952e8SBram Moolenaar 	    int		did_set_shortname;
1312473952e8SBram Moolenaar 	    mode_t	umask_save;
1313473952e8SBram Moolenaar #endif
1314473952e8SBram Moolenaar 
1315473952e8SBram Moolenaar 	    copybuf = alloc(WRITEBUFSIZE + 1);
1316473952e8SBram Moolenaar 	    if (copybuf == NULL)
1317473952e8SBram Moolenaar 	    {
1318473952e8SBram Moolenaar 		some_error = TRUE;	    // out of memory
1319473952e8SBram Moolenaar 		goto nobackup;
1320473952e8SBram Moolenaar 	    }
1321473952e8SBram Moolenaar 
1322473952e8SBram Moolenaar 	    // Try to make the backup in each directory in the 'bdir' option.
1323473952e8SBram Moolenaar 	    //
1324473952e8SBram Moolenaar 	    // Unix semantics has it, that we may have a writable file,
1325473952e8SBram Moolenaar 	    // that cannot be recreated with a simple open(..., O_CREAT, ) e.g:
1326473952e8SBram Moolenaar 	    //  - the directory is not writable,
1327473952e8SBram Moolenaar 	    //  - the file may be a symbolic link,
1328473952e8SBram Moolenaar 	    //  - the file may belong to another user/group, etc.
1329473952e8SBram Moolenaar 	    //
1330473952e8SBram Moolenaar 	    // For these reasons, the existing writable file must be truncated
1331473952e8SBram Moolenaar 	    // and reused. Creation of a backup COPY will be attempted.
1332473952e8SBram Moolenaar 	    dirp = p_bdir;
1333473952e8SBram Moolenaar 	    while (*dirp)
1334473952e8SBram Moolenaar 	    {
1335473952e8SBram Moolenaar #ifdef UNIX
1336473952e8SBram Moolenaar 		st_new.st_ino = 0;
1337473952e8SBram Moolenaar 		st_new.st_dev = 0;
1338473952e8SBram Moolenaar 		st_new.st_gid = 0;
1339473952e8SBram Moolenaar #endif
1340473952e8SBram Moolenaar 
1341473952e8SBram Moolenaar 		// Isolate one directory name, using an entry in 'bdir'.
1342473952e8SBram Moolenaar 		(void)copy_option_part(&dirp, copybuf, WRITEBUFSIZE, ",");
1343473952e8SBram Moolenaar 
1344473952e8SBram Moolenaar #if defined(UNIX) || defined(MSWIN)
1345473952e8SBram Moolenaar 		p = copybuf + STRLEN(copybuf);
1346473952e8SBram Moolenaar 		if (after_pathsep(copybuf, p) && p[-1] == p[-2])
1347473952e8SBram Moolenaar 		    // Ends with '//', use full path
1348473952e8SBram Moolenaar 		    if ((p = make_percent_swname(copybuf, fname)) != NULL)
1349473952e8SBram Moolenaar 		    {
1350473952e8SBram Moolenaar 			backup = modname(p, backup_ext, FALSE);
1351473952e8SBram Moolenaar 			vim_free(p);
1352473952e8SBram Moolenaar 		    }
1353473952e8SBram Moolenaar #endif
1354473952e8SBram Moolenaar 		rootname = get_file_in_dir(fname, copybuf);
1355473952e8SBram Moolenaar 		if (rootname == NULL)
1356473952e8SBram Moolenaar 		{
1357473952e8SBram Moolenaar 		    some_error = TRUE;	    // out of memory
1358473952e8SBram Moolenaar 		    goto nobackup;
1359473952e8SBram Moolenaar 		}
1360473952e8SBram Moolenaar 
1361473952e8SBram Moolenaar #if defined(UNIX)
1362473952e8SBram Moolenaar 		did_set_shortname = FALSE;
1363473952e8SBram Moolenaar #endif
1364473952e8SBram Moolenaar 
1365473952e8SBram Moolenaar 		// May try twice if 'shortname' not set.
1366473952e8SBram Moolenaar 		for (;;)
1367473952e8SBram Moolenaar 		{
1368473952e8SBram Moolenaar 		    // Make the backup file name.
1369473952e8SBram Moolenaar 		    if (backup == NULL)
1370473952e8SBram Moolenaar 			backup = buf_modname((buf->b_p_sn || buf->b_shortname),
1371473952e8SBram Moolenaar 						 rootname, backup_ext, FALSE);
1372473952e8SBram Moolenaar 		    if (backup == NULL)
1373473952e8SBram Moolenaar 		    {
1374473952e8SBram Moolenaar 			vim_free(rootname);
1375473952e8SBram Moolenaar 			some_error = TRUE;		// out of memory
1376473952e8SBram Moolenaar 			goto nobackup;
1377473952e8SBram Moolenaar 		    }
1378473952e8SBram Moolenaar 
1379473952e8SBram Moolenaar 		    // Check if backup file already exists.
1380473952e8SBram Moolenaar 		    if (mch_stat((char *)backup, &st_new) >= 0)
1381473952e8SBram Moolenaar 		    {
1382473952e8SBram Moolenaar #ifdef UNIX
1383473952e8SBram Moolenaar 			// Check if backup file is same as original file.
1384473952e8SBram Moolenaar 			// May happen when modname() gave the same file back.
1385473952e8SBram Moolenaar 			// E.g. silly link, or file name-length reached.
1386473952e8SBram Moolenaar 			// If we don't check here, we either ruin the file
1387473952e8SBram Moolenaar 			// when copying or erase it after writing. jw.
1388473952e8SBram Moolenaar 			if (st_new.st_dev == st_old.st_dev
1389473952e8SBram Moolenaar 					    && st_new.st_ino == st_old.st_ino)
1390473952e8SBram Moolenaar 			{
1391473952e8SBram Moolenaar 			    VIM_CLEAR(backup);	// no backup file to delete
1392473952e8SBram Moolenaar 			    // may try again with 'shortname' set
1393473952e8SBram Moolenaar 			    if (!(buf->b_shortname || buf->b_p_sn))
1394473952e8SBram Moolenaar 			    {
1395473952e8SBram Moolenaar 				buf->b_shortname = TRUE;
1396473952e8SBram Moolenaar 				did_set_shortname = TRUE;
1397473952e8SBram Moolenaar 				continue;
1398473952e8SBram Moolenaar 			    }
1399473952e8SBram Moolenaar 				// setting shortname didn't help
1400473952e8SBram Moolenaar 			    if (did_set_shortname)
1401473952e8SBram Moolenaar 				buf->b_shortname = FALSE;
1402473952e8SBram Moolenaar 			    break;
1403473952e8SBram Moolenaar 			}
1404473952e8SBram Moolenaar #endif
1405473952e8SBram Moolenaar 
1406473952e8SBram Moolenaar 			// If we are not going to keep the backup file, don't
1407473952e8SBram Moolenaar 			// delete an existing one, try to use another name.
1408473952e8SBram Moolenaar 			// Change one character, just before the extension.
1409473952e8SBram Moolenaar 			if (!p_bk)
1410473952e8SBram Moolenaar 			{
1411473952e8SBram Moolenaar 			    wp = backup + STRLEN(backup) - 1
1412473952e8SBram Moolenaar 							 - STRLEN(backup_ext);
1413473952e8SBram Moolenaar 			    if (wp < backup)	// empty file name ???
1414473952e8SBram Moolenaar 				wp = backup;
1415473952e8SBram Moolenaar 			    *wp = 'z';
1416473952e8SBram Moolenaar 			    while (*wp > 'a'
1417473952e8SBram Moolenaar 				    && mch_stat((char *)backup, &st_new) >= 0)
1418473952e8SBram Moolenaar 				--*wp;
1419473952e8SBram Moolenaar 			    // They all exist??? Must be something wrong.
1420473952e8SBram Moolenaar 			    if (*wp == 'a')
1421473952e8SBram Moolenaar 				VIM_CLEAR(backup);
1422473952e8SBram Moolenaar 			}
1423473952e8SBram Moolenaar 		    }
1424473952e8SBram Moolenaar 		    break;
1425473952e8SBram Moolenaar 		}
1426473952e8SBram Moolenaar 		vim_free(rootname);
1427473952e8SBram Moolenaar 
1428473952e8SBram Moolenaar 		// Try to create the backup file
1429473952e8SBram Moolenaar 		if (backup != NULL)
1430473952e8SBram Moolenaar 		{
1431473952e8SBram Moolenaar 		    // remove old backup, if present
1432473952e8SBram Moolenaar 		    mch_remove(backup);
1433473952e8SBram Moolenaar 		    // Open with O_EXCL to avoid the file being created while
1434473952e8SBram Moolenaar 		    // we were sleeping (symlink hacker attack?). Reset umask
1435473952e8SBram Moolenaar 		    // if possible to avoid mch_setperm() below.
1436473952e8SBram Moolenaar #ifdef UNIX
1437473952e8SBram Moolenaar 		    umask_save = umask(0);
1438473952e8SBram Moolenaar #endif
1439473952e8SBram Moolenaar 		    bfd = mch_open((char *)backup,
1440473952e8SBram Moolenaar 				O_WRONLY|O_CREAT|O_EXTRA|O_EXCL|O_NOFOLLOW,
1441473952e8SBram Moolenaar 								 perm & 0777);
1442473952e8SBram Moolenaar #ifdef UNIX
1443473952e8SBram Moolenaar 		    (void)umask(umask_save);
1444473952e8SBram Moolenaar #endif
1445473952e8SBram Moolenaar 		    if (bfd < 0)
1446473952e8SBram Moolenaar 			VIM_CLEAR(backup);
1447473952e8SBram Moolenaar 		    else
1448473952e8SBram Moolenaar 		    {
1449473952e8SBram Moolenaar 			// Set file protection same as original file, but
1450473952e8SBram Moolenaar 			// strip s-bit.  Only needed if umask() wasn't used
1451473952e8SBram Moolenaar 			// above.
1452473952e8SBram Moolenaar #ifndef UNIX
1453473952e8SBram Moolenaar 			(void)mch_setperm(backup, perm & 0777);
1454473952e8SBram Moolenaar #else
1455473952e8SBram Moolenaar 			// Try to set the group of the backup same as the
1456473952e8SBram Moolenaar 			// original file. If this fails, set the protection
1457473952e8SBram Moolenaar 			// bits for the group same as the protection bits for
1458473952e8SBram Moolenaar 			// others.
1459473952e8SBram Moolenaar 			if (st_new.st_gid != st_old.st_gid
1460473952e8SBram Moolenaar # ifdef HAVE_FCHOWN  // sequent-ptx lacks fchown()
1461473952e8SBram Moolenaar 				&& fchown(bfd, (uid_t)-1, st_old.st_gid) != 0
1462473952e8SBram Moolenaar # endif
1463473952e8SBram Moolenaar 						)
1464473952e8SBram Moolenaar 			    mch_setperm(backup,
1465473952e8SBram Moolenaar 					  (perm & 0707) | ((perm & 07) << 3));
1466473952e8SBram Moolenaar # if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
1467473952e8SBram Moolenaar 			mch_copy_sec(fname, backup);
1468473952e8SBram Moolenaar # endif
1469473952e8SBram Moolenaar #endif
1470473952e8SBram Moolenaar 
1471473952e8SBram Moolenaar 			// copy the file.
1472473952e8SBram Moolenaar 			write_info.bw_fd = bfd;
1473473952e8SBram Moolenaar 			write_info.bw_buf = copybuf;
1474473952e8SBram Moolenaar 			write_info.bw_flags = FIO_NOCONVERT;
1475473952e8SBram Moolenaar 			while ((write_info.bw_len = read_eintr(fd, copybuf,
1476473952e8SBram Moolenaar 							    WRITEBUFSIZE)) > 0)
1477473952e8SBram Moolenaar 			{
1478473952e8SBram Moolenaar 			    if (buf_write_bytes(&write_info) == FAIL)
1479473952e8SBram Moolenaar 			    {
1480473952e8SBram Moolenaar 				errmsg = (char_u *)_("E506: Can't write to backup file (add ! to override)");
1481473952e8SBram Moolenaar 				break;
1482473952e8SBram Moolenaar 			    }
1483473952e8SBram Moolenaar 			    ui_breakcheck();
1484473952e8SBram Moolenaar 			    if (got_int)
1485473952e8SBram Moolenaar 			    {
1486473952e8SBram Moolenaar 				errmsg = (char_u *)_(e_interr);
1487473952e8SBram Moolenaar 				break;
1488473952e8SBram Moolenaar 			    }
1489473952e8SBram Moolenaar 			}
1490473952e8SBram Moolenaar 
1491473952e8SBram Moolenaar 			if (close(bfd) < 0 && errmsg == NULL)
1492473952e8SBram Moolenaar 			    errmsg = (char_u *)_("E507: Close error for backup file (add ! to override)");
1493473952e8SBram Moolenaar 			if (write_info.bw_len < 0)
1494473952e8SBram Moolenaar 			    errmsg = (char_u *)_("E508: Can't read file for backup (add ! to override)");
1495473952e8SBram Moolenaar #ifdef UNIX
1496473952e8SBram Moolenaar 			set_file_time(backup, st_old.st_atime, st_old.st_mtime);
1497473952e8SBram Moolenaar #endif
1498473952e8SBram Moolenaar #ifdef HAVE_ACL
1499473952e8SBram Moolenaar 			mch_set_acl(backup, acl);
1500473952e8SBram Moolenaar #endif
1501473952e8SBram Moolenaar #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
1502473952e8SBram Moolenaar 			mch_copy_sec(fname, backup);
1503473952e8SBram Moolenaar #endif
15047781ebe5SBram Moolenaar #ifdef MSWIN
15057781ebe5SBram Moolenaar 			(void)mch_copy_file_attribute(fname, backup);
15067781ebe5SBram Moolenaar #endif
1507473952e8SBram Moolenaar 			break;
1508473952e8SBram Moolenaar 		    }
1509473952e8SBram Moolenaar 		}
1510473952e8SBram Moolenaar 	    }
1511473952e8SBram Moolenaar     nobackup:
1512473952e8SBram Moolenaar 	    close(fd);		// ignore errors for closing read file
1513473952e8SBram Moolenaar 	    vim_free(copybuf);
1514473952e8SBram Moolenaar 
1515473952e8SBram Moolenaar 	    if (backup == NULL && errmsg == NULL)
1516473952e8SBram Moolenaar 		errmsg = (char_u *)_("E509: Cannot create backup file (add ! to override)");
1517473952e8SBram Moolenaar 	    // ignore errors when forceit is TRUE
1518473952e8SBram Moolenaar 	    if ((some_error || errmsg != NULL) && !forceit)
1519473952e8SBram Moolenaar 	    {
1520473952e8SBram Moolenaar 		retval = FAIL;
1521473952e8SBram Moolenaar 		goto fail;
1522473952e8SBram Moolenaar 	    }
1523473952e8SBram Moolenaar 	    errmsg = NULL;
1524473952e8SBram Moolenaar 	}
1525473952e8SBram Moolenaar 	else
1526473952e8SBram Moolenaar 	{
1527473952e8SBram Moolenaar 	    char_u	*dirp;
1528473952e8SBram Moolenaar 	    char_u	*p;
1529473952e8SBram Moolenaar 	    char_u	*rootname;
1530473952e8SBram Moolenaar 
1531473952e8SBram Moolenaar 	    // Make a backup by renaming the original file.
1532473952e8SBram Moolenaar 
1533473952e8SBram Moolenaar 	    // If 'cpoptions' includes the "W" flag, we don't want to
1534473952e8SBram Moolenaar 	    // overwrite a read-only file.  But rename may be possible
1535473952e8SBram Moolenaar 	    // anyway, thus we need an extra check here.
1536473952e8SBram Moolenaar 	    if (file_readonly && vim_strchr(p_cpo, CPO_FWRITE) != NULL)
1537473952e8SBram Moolenaar 	    {
1538473952e8SBram Moolenaar 		errnum = (char_u *)"E504: ";
1539473952e8SBram Moolenaar 		errmsg = (char_u *)_(err_readonly);
1540473952e8SBram Moolenaar 		goto fail;
1541473952e8SBram Moolenaar 	    }
1542473952e8SBram Moolenaar 
1543473952e8SBram Moolenaar 	    // Form the backup file name - change path/fo.o.h to
1544473952e8SBram Moolenaar 	    // path/fo.o.h.bak Try all directories in 'backupdir', first one
1545473952e8SBram Moolenaar 	    // that works is used.
1546473952e8SBram Moolenaar 	    dirp = p_bdir;
1547473952e8SBram Moolenaar 	    while (*dirp)
1548473952e8SBram Moolenaar 	    {
1549473952e8SBram Moolenaar 		// Isolate one directory name and make the backup file name.
1550473952e8SBram Moolenaar 		(void)copy_option_part(&dirp, IObuff, IOSIZE, ",");
1551473952e8SBram Moolenaar 
1552473952e8SBram Moolenaar #if defined(UNIX) || defined(MSWIN)
1553473952e8SBram Moolenaar 		p = IObuff + STRLEN(IObuff);
1554473952e8SBram Moolenaar 		if (after_pathsep(IObuff, p) && p[-1] == p[-2])
1555473952e8SBram Moolenaar 		    // path ends with '//', use full path
1556473952e8SBram Moolenaar 		    if ((p = make_percent_swname(IObuff, fname)) != NULL)
1557473952e8SBram Moolenaar 		    {
1558473952e8SBram Moolenaar 			backup = modname(p, backup_ext, FALSE);
1559473952e8SBram Moolenaar 			vim_free(p);
1560473952e8SBram Moolenaar 		    }
1561473952e8SBram Moolenaar #endif
1562473952e8SBram Moolenaar 		if (backup == NULL)
1563473952e8SBram Moolenaar 		{
1564473952e8SBram Moolenaar 		    rootname = get_file_in_dir(fname, IObuff);
1565473952e8SBram Moolenaar 		    if (rootname == NULL)
1566473952e8SBram Moolenaar 			backup = NULL;
1567473952e8SBram Moolenaar 		    else
1568473952e8SBram Moolenaar 		    {
1569473952e8SBram Moolenaar 			backup = buf_modname(
1570473952e8SBram Moolenaar 				(buf->b_p_sn || buf->b_shortname),
1571473952e8SBram Moolenaar 						rootname, backup_ext, FALSE);
1572473952e8SBram Moolenaar 			vim_free(rootname);
1573473952e8SBram Moolenaar 		    }
1574473952e8SBram Moolenaar 		}
1575473952e8SBram Moolenaar 
1576473952e8SBram Moolenaar 		if (backup != NULL)
1577473952e8SBram Moolenaar 		{
1578473952e8SBram Moolenaar 		    // If we are not going to keep the backup file, don't
1579473952e8SBram Moolenaar 		    // delete an existing one, try to use another name.
1580473952e8SBram Moolenaar 		    // Change one character, just before the extension.
1581473952e8SBram Moolenaar 		    if (!p_bk && mch_getperm(backup) >= 0)
1582473952e8SBram Moolenaar 		    {
1583473952e8SBram Moolenaar 			p = backup + STRLEN(backup) - 1 - STRLEN(backup_ext);
1584473952e8SBram Moolenaar 			if (p < backup)	// empty file name ???
1585473952e8SBram Moolenaar 			    p = backup;
1586473952e8SBram Moolenaar 			*p = 'z';
1587473952e8SBram Moolenaar 			while (*p > 'a' && mch_getperm(backup) >= 0)
1588473952e8SBram Moolenaar 			    --*p;
1589473952e8SBram Moolenaar 			// They all exist??? Must be something wrong!
1590473952e8SBram Moolenaar 			if (*p == 'a')
1591473952e8SBram Moolenaar 			    VIM_CLEAR(backup);
1592473952e8SBram Moolenaar 		    }
1593473952e8SBram Moolenaar 		}
1594473952e8SBram Moolenaar 		if (backup != NULL)
1595473952e8SBram Moolenaar 		{
1596473952e8SBram Moolenaar 		    // Delete any existing backup and move the current version
1597473952e8SBram Moolenaar 		    // to the backup.	For safety, we don't remove the backup
1598473952e8SBram Moolenaar 		    // until the write has finished successfully. And if the
1599473952e8SBram Moolenaar 		    // 'backup' option is set, leave it around.
1600473952e8SBram Moolenaar 
1601473952e8SBram Moolenaar 		    // If the renaming of the original file to the backup file
1602473952e8SBram Moolenaar 		    // works, quit here.
1603473952e8SBram Moolenaar 		    if (vim_rename(fname, backup) == 0)
1604473952e8SBram Moolenaar 			break;
1605473952e8SBram Moolenaar 
1606473952e8SBram Moolenaar 		    VIM_CLEAR(backup);   // don't do the rename below
1607473952e8SBram Moolenaar 		}
1608473952e8SBram Moolenaar 	    }
1609473952e8SBram Moolenaar 	    if (backup == NULL && !forceit)
1610473952e8SBram Moolenaar 	    {
1611473952e8SBram Moolenaar 		errmsg = (char_u *)_("E510: Can't make backup file (add ! to override)");
1612473952e8SBram Moolenaar 		goto fail;
1613473952e8SBram Moolenaar 	    }
1614473952e8SBram Moolenaar 	}
1615473952e8SBram Moolenaar     }
1616473952e8SBram Moolenaar 
1617473952e8SBram Moolenaar #if defined(UNIX)
1618473952e8SBram Moolenaar     // When using ":w!" and the file was read-only: make it writable
1619473952e8SBram Moolenaar     if (forceit && perm >= 0 && !(perm & 0200) && st_old.st_uid == getuid()
1620473952e8SBram Moolenaar 				     && vim_strchr(p_cpo, CPO_FWRITE) == NULL)
1621473952e8SBram Moolenaar     {
1622473952e8SBram Moolenaar 	perm |= 0200;
1623473952e8SBram Moolenaar 	(void)mch_setperm(fname, perm);
1624473952e8SBram Moolenaar 	made_writable = TRUE;
1625473952e8SBram Moolenaar     }
1626473952e8SBram Moolenaar #endif
1627473952e8SBram Moolenaar 
1628473952e8SBram Moolenaar     // When using ":w!" and writing to the current file, 'readonly' makes no
1629473952e8SBram Moolenaar     // sense, reset it, unless 'Z' appears in 'cpoptions'.
1630473952e8SBram Moolenaar     if (forceit && overwriting && vim_strchr(p_cpo, CPO_KEEPRO) == NULL)
1631473952e8SBram Moolenaar     {
1632473952e8SBram Moolenaar 	buf->b_p_ro = FALSE;
1633473952e8SBram Moolenaar #ifdef FEAT_TITLE
1634473952e8SBram Moolenaar 	need_maketitle = TRUE;	    // set window title later
1635473952e8SBram Moolenaar #endif
1636473952e8SBram Moolenaar 	status_redraw_all();	    // redraw status lines later
1637473952e8SBram Moolenaar     }
1638473952e8SBram Moolenaar 
1639473952e8SBram Moolenaar     if (end > buf->b_ml.ml_line_count)
1640473952e8SBram Moolenaar 	end = buf->b_ml.ml_line_count;
1641473952e8SBram Moolenaar     if (buf->b_ml.ml_flags & ML_EMPTY)
1642473952e8SBram Moolenaar 	start = end + 1;
1643473952e8SBram Moolenaar 
1644473952e8SBram Moolenaar     // If the original file is being overwritten, there is a small chance that
1645473952e8SBram Moolenaar     // we crash in the middle of writing. Therefore the file is preserved now.
1646473952e8SBram Moolenaar     // This makes all block numbers positive so that recovery does not need
1647473952e8SBram Moolenaar     // the original file.
1648473952e8SBram Moolenaar     // Don't do this if there is a backup file and we are exiting.
1649473952e8SBram Moolenaar     if (reset_changed && !newfile && overwriting
1650473952e8SBram Moolenaar 					      && !(exiting && backup != NULL))
1651473952e8SBram Moolenaar     {
1652473952e8SBram Moolenaar 	ml_preserve(buf, FALSE);
1653473952e8SBram Moolenaar 	if (got_int)
1654473952e8SBram Moolenaar 	{
1655473952e8SBram Moolenaar 	    errmsg = (char_u *)_(e_interr);
1656473952e8SBram Moolenaar 	    goto restore_backup;
1657473952e8SBram Moolenaar 	}
1658473952e8SBram Moolenaar     }
1659473952e8SBram Moolenaar 
1660473952e8SBram Moolenaar #ifdef VMS
1661473952e8SBram Moolenaar     vms_remove_version(fname); // remove version
1662473952e8SBram Moolenaar #endif
1663473952e8SBram Moolenaar     // Default: write the file directly.  May write to a temp file for
1664473952e8SBram Moolenaar     // multi-byte conversion.
1665473952e8SBram Moolenaar     wfname = fname;
1666473952e8SBram Moolenaar 
1667473952e8SBram Moolenaar     // Check for forced 'fileencoding' from "++opt=val" argument.
1668473952e8SBram Moolenaar     if (eap != NULL && eap->force_enc != 0)
1669473952e8SBram Moolenaar     {
1670473952e8SBram Moolenaar 	fenc = eap->cmd + eap->force_enc;
1671473952e8SBram Moolenaar 	fenc = enc_canonize(fenc);
1672473952e8SBram Moolenaar 	fenc_tofree = fenc;
1673473952e8SBram Moolenaar     }
1674473952e8SBram Moolenaar     else
1675473952e8SBram Moolenaar 	fenc = buf->b_p_fenc;
1676473952e8SBram Moolenaar 
1677473952e8SBram Moolenaar     // Check if the file needs to be converted.
1678473952e8SBram Moolenaar     converted = need_conversion(fenc);
1679473952e8SBram Moolenaar 
1680473952e8SBram Moolenaar     // Check if UTF-8 to UCS-2/4 or Latin1 conversion needs to be done.  Or
1681473952e8SBram Moolenaar     // Latin1 to Unicode conversion.  This is handled in buf_write_bytes().
1682473952e8SBram Moolenaar     // Prepare the flags for it and allocate bw_conv_buf when needed.
1683473952e8SBram Moolenaar     if (converted && (enc_utf8 || STRCMP(p_enc, "latin1") == 0))
1684473952e8SBram Moolenaar     {
1685473952e8SBram Moolenaar 	wb_flags = get_fio_flags(fenc);
1686473952e8SBram Moolenaar 	if (wb_flags & (FIO_UCS2 | FIO_UCS4 | FIO_UTF16 | FIO_UTF8))
1687473952e8SBram Moolenaar 	{
1688473952e8SBram Moolenaar 	    // Need to allocate a buffer to translate into.
1689473952e8SBram Moolenaar 	    if (wb_flags & (FIO_UCS2 | FIO_UTF16 | FIO_UTF8))
1690473952e8SBram Moolenaar 		write_info.bw_conv_buflen = bufsize * 2;
1691473952e8SBram Moolenaar 	    else // FIO_UCS4
1692473952e8SBram Moolenaar 		write_info.bw_conv_buflen = bufsize * 4;
1693473952e8SBram Moolenaar 	    write_info.bw_conv_buf = alloc(write_info.bw_conv_buflen);
1694473952e8SBram Moolenaar 	    if (write_info.bw_conv_buf == NULL)
1695473952e8SBram Moolenaar 		end = 0;
1696473952e8SBram Moolenaar 	}
1697473952e8SBram Moolenaar     }
1698473952e8SBram Moolenaar 
1699473952e8SBram Moolenaar #ifdef MSWIN
1700473952e8SBram Moolenaar     if (converted && wb_flags == 0 && (wb_flags = get_win_fio_flags(fenc)) != 0)
1701473952e8SBram Moolenaar     {
1702473952e8SBram Moolenaar 	// Convert UTF-8 -> UCS-2 and UCS-2 -> DBCS.  Worst-case * 4:
1703473952e8SBram Moolenaar 	write_info.bw_conv_buflen = bufsize * 4;
1704473952e8SBram Moolenaar 	write_info.bw_conv_buf = alloc(write_info.bw_conv_buflen);
1705473952e8SBram Moolenaar 	if (write_info.bw_conv_buf == NULL)
1706473952e8SBram Moolenaar 	    end = 0;
1707473952e8SBram Moolenaar     }
1708473952e8SBram Moolenaar #endif
1709473952e8SBram Moolenaar 
1710473952e8SBram Moolenaar #ifdef MACOS_CONVERT
1711473952e8SBram Moolenaar     if (converted && wb_flags == 0 && (wb_flags = get_mac_fio_flags(fenc)) != 0)
1712473952e8SBram Moolenaar     {
1713473952e8SBram Moolenaar 	write_info.bw_conv_buflen = bufsize * 3;
1714473952e8SBram Moolenaar 	write_info.bw_conv_buf = alloc(write_info.bw_conv_buflen);
1715473952e8SBram Moolenaar 	if (write_info.bw_conv_buf == NULL)
1716473952e8SBram Moolenaar 	    end = 0;
1717473952e8SBram Moolenaar     }
1718473952e8SBram Moolenaar #endif
1719473952e8SBram Moolenaar 
1720473952e8SBram Moolenaar #if defined(FEAT_EVAL) || defined(USE_ICONV)
1721473952e8SBram Moolenaar     if (converted && wb_flags == 0)
1722473952e8SBram Moolenaar     {
1723473952e8SBram Moolenaar # ifdef USE_ICONV
1724473952e8SBram Moolenaar 	// Use iconv() conversion when conversion is needed and it's not done
1725473952e8SBram Moolenaar 	// internally.
1726473952e8SBram Moolenaar 	write_info.bw_iconv_fd = (iconv_t)my_iconv_open(fenc,
1727473952e8SBram Moolenaar 					enc_utf8 ? (char_u *)"utf-8" : p_enc);
1728473952e8SBram Moolenaar 	if (write_info.bw_iconv_fd != (iconv_t)-1)
1729473952e8SBram Moolenaar 	{
1730473952e8SBram Moolenaar 	    // We're going to use iconv(), allocate a buffer to convert in.
1731473952e8SBram Moolenaar 	    write_info.bw_conv_buflen = bufsize * ICONV_MULT;
1732473952e8SBram Moolenaar 	    write_info.bw_conv_buf = alloc(write_info.bw_conv_buflen);
1733473952e8SBram Moolenaar 	    if (write_info.bw_conv_buf == NULL)
1734473952e8SBram Moolenaar 		end = 0;
1735473952e8SBram Moolenaar 	    write_info.bw_first = TRUE;
1736473952e8SBram Moolenaar 	}
1737473952e8SBram Moolenaar #  ifdef FEAT_EVAL
1738473952e8SBram Moolenaar 	else
1739473952e8SBram Moolenaar #  endif
1740473952e8SBram Moolenaar # endif
1741473952e8SBram Moolenaar 
1742473952e8SBram Moolenaar # ifdef FEAT_EVAL
1743473952e8SBram Moolenaar 	    // When the file needs to be converted with 'charconvert' after
1744473952e8SBram Moolenaar 	    // writing, write to a temp file instead and let the conversion
1745473952e8SBram Moolenaar 	    // overwrite the original file.
1746473952e8SBram Moolenaar 	    if (*p_ccv != NUL)
1747473952e8SBram Moolenaar 	    {
1748473952e8SBram Moolenaar 		wfname = vim_tempname('w', FALSE);
1749473952e8SBram Moolenaar 		if (wfname == NULL)	// Can't write without a tempfile!
1750473952e8SBram Moolenaar 		{
1751473952e8SBram Moolenaar 		    errmsg = (char_u *)_("E214: Can't find temp file for writing");
1752473952e8SBram Moolenaar 		    goto restore_backup;
1753473952e8SBram Moolenaar 		}
1754473952e8SBram Moolenaar 	    }
1755473952e8SBram Moolenaar # endif
1756473952e8SBram Moolenaar     }
1757473952e8SBram Moolenaar #endif
1758473952e8SBram Moolenaar     if (converted && wb_flags == 0
1759473952e8SBram Moolenaar #ifdef USE_ICONV
1760473952e8SBram Moolenaar 	    && write_info.bw_iconv_fd == (iconv_t)-1
1761473952e8SBram Moolenaar # endif
1762473952e8SBram Moolenaar # ifdef FEAT_EVAL
1763473952e8SBram Moolenaar 	    && wfname == fname
1764473952e8SBram Moolenaar # endif
1765473952e8SBram Moolenaar 	    )
1766473952e8SBram Moolenaar     {
1767473952e8SBram Moolenaar 	if (!forceit)
1768473952e8SBram Moolenaar 	{
1769473952e8SBram Moolenaar 	    errmsg = (char_u *)_("E213: Cannot convert (add ! to write without conversion)");
1770473952e8SBram Moolenaar 	    goto restore_backup;
1771473952e8SBram Moolenaar 	}
1772473952e8SBram Moolenaar 	notconverted = TRUE;
1773473952e8SBram Moolenaar     }
1774473952e8SBram Moolenaar 
1775473952e8SBram Moolenaar     // If conversion is taking place, we may first pretend to write and check
1776473952e8SBram Moolenaar     // for conversion errors.  Then loop again to write for real.
1777473952e8SBram Moolenaar     // When not doing conversion this writes for real right away.
1778473952e8SBram Moolenaar     for (checking_conversion = TRUE; ; checking_conversion = FALSE)
1779473952e8SBram Moolenaar     {
1780473952e8SBram Moolenaar 	// There is no need to check conversion when:
1781473952e8SBram Moolenaar 	// - there is no conversion
1782473952e8SBram Moolenaar 	// - we make a backup file, that can be restored in case of conversion
1783473952e8SBram Moolenaar 	//   failure.
1784473952e8SBram Moolenaar 	if (!converted || dobackup)
1785473952e8SBram Moolenaar 	    checking_conversion = FALSE;
1786473952e8SBram Moolenaar 
1787473952e8SBram Moolenaar 	if (checking_conversion)
1788473952e8SBram Moolenaar 	{
1789473952e8SBram Moolenaar 	    // Make sure we don't write anything.
1790473952e8SBram Moolenaar 	    fd = -1;
1791473952e8SBram Moolenaar 	    write_info.bw_fd = fd;
1792473952e8SBram Moolenaar 	}
1793473952e8SBram Moolenaar 	else
1794473952e8SBram Moolenaar 	{
1795473952e8SBram Moolenaar #ifdef HAVE_FTRUNCATE
1796473952e8SBram Moolenaar # define TRUNC_ON_OPEN 0
1797473952e8SBram Moolenaar #else
1798473952e8SBram Moolenaar # define TRUNC_ON_OPEN O_TRUNC
1799473952e8SBram Moolenaar #endif
1800473952e8SBram Moolenaar 	    // Open the file "wfname" for writing.
1801473952e8SBram Moolenaar 	    // We may try to open the file twice: If we can't write to the file
1802473952e8SBram Moolenaar 	    // and forceit is TRUE we delete the existing file and try to
1803473952e8SBram Moolenaar 	    // create a new one. If this still fails we may have lost the
1804473952e8SBram Moolenaar 	    // original file!  (this may happen when the user reached his
1805473952e8SBram Moolenaar 	    // quotum for number of files).
1806473952e8SBram Moolenaar 	    // Appending will fail if the file does not exist and forceit is
1807473952e8SBram Moolenaar 	    // FALSE.
1808473952e8SBram Moolenaar 	    while ((fd = mch_open((char *)wfname, O_WRONLY | O_EXTRA | (append
1809473952e8SBram Moolenaar 				? (forceit ? (O_APPEND | O_CREAT) : O_APPEND)
1810473952e8SBram Moolenaar 				: (O_CREAT | TRUNC_ON_OPEN))
1811473952e8SBram Moolenaar 				, perm < 0 ? 0666 : (perm & 0777))) < 0)
1812473952e8SBram Moolenaar 	    {
1813473952e8SBram Moolenaar 		// A forced write will try to create a new file if the old one
1814473952e8SBram Moolenaar 		// is still readonly. This may also happen when the directory
1815473952e8SBram Moolenaar 		// is read-only. In that case the mch_remove() will fail.
1816473952e8SBram Moolenaar 		if (errmsg == NULL)
1817473952e8SBram Moolenaar 		{
1818473952e8SBram Moolenaar #ifdef UNIX
1819473952e8SBram Moolenaar 		    stat_T	st;
1820473952e8SBram Moolenaar 
1821473952e8SBram Moolenaar 		    // Don't delete the file when it's a hard or symbolic link.
1822473952e8SBram Moolenaar 		    if ((!newfile && st_old.st_nlink > 1)
1823473952e8SBram Moolenaar 			    || (mch_lstat((char *)fname, &st) == 0
1824473952e8SBram Moolenaar 				&& (st.st_dev != st_old.st_dev
1825473952e8SBram Moolenaar 				    || st.st_ino != st_old.st_ino)))
1826473952e8SBram Moolenaar 			errmsg = (char_u *)_("E166: Can't open linked file for writing");
1827473952e8SBram Moolenaar 		    else
1828473952e8SBram Moolenaar #endif
1829473952e8SBram Moolenaar 		    {
1830473952e8SBram Moolenaar 			errmsg = (char_u *)_("E212: Can't open file for writing");
1831473952e8SBram Moolenaar 			if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL
1832473952e8SBram Moolenaar 								  && perm >= 0)
1833473952e8SBram Moolenaar 			{
1834473952e8SBram Moolenaar #ifdef UNIX
1835473952e8SBram Moolenaar 			    // we write to the file, thus it should be marked
1836473952e8SBram Moolenaar 			    // writable after all
1837473952e8SBram Moolenaar 			    if (!(perm & 0200))
1838473952e8SBram Moolenaar 				made_writable = TRUE;
1839473952e8SBram Moolenaar 			    perm |= 0200;
1840473952e8SBram Moolenaar 			    if (st_old.st_uid != getuid()
1841473952e8SBram Moolenaar 						  || st_old.st_gid != getgid())
1842473952e8SBram Moolenaar 				perm &= 0777;
1843473952e8SBram Moolenaar #endif
1844473952e8SBram Moolenaar 			    if (!append)  // don't remove when appending
1845473952e8SBram Moolenaar 				mch_remove(wfname);
1846473952e8SBram Moolenaar 			    continue;
1847473952e8SBram Moolenaar 			}
1848473952e8SBram Moolenaar 		    }
1849473952e8SBram Moolenaar 		}
1850473952e8SBram Moolenaar 
1851473952e8SBram Moolenaar restore_backup:
1852473952e8SBram Moolenaar 		{
1853473952e8SBram Moolenaar 		    stat_T	st;
1854473952e8SBram Moolenaar 
1855473952e8SBram Moolenaar 		    // If we failed to open the file, we don't need a backup.
1856473952e8SBram Moolenaar 		    // Throw it away.  If we moved or removed the original file
1857473952e8SBram Moolenaar 		    // try to put the backup in its place.
1858473952e8SBram Moolenaar 		    if (backup != NULL && wfname == fname)
1859473952e8SBram Moolenaar 		    {
1860473952e8SBram Moolenaar 			if (backup_copy)
1861473952e8SBram Moolenaar 			{
1862473952e8SBram Moolenaar 			    // There is a small chance that we removed the
1863473952e8SBram Moolenaar 			    // original, try to move the copy in its place.
1864473952e8SBram Moolenaar 			    // This may not work if the vim_rename() fails.
1865473952e8SBram Moolenaar 			    // In that case we leave the copy around.
1866473952e8SBram Moolenaar 
1867473952e8SBram Moolenaar 			    // If file does not exist, put the copy in its
1868473952e8SBram Moolenaar 			    // place
1869473952e8SBram Moolenaar 			    if (mch_stat((char *)fname, &st) < 0)
1870473952e8SBram Moolenaar 				vim_rename(backup, fname);
1871473952e8SBram Moolenaar 			    // if original file does exist throw away the copy
1872473952e8SBram Moolenaar 			    if (mch_stat((char *)fname, &st) >= 0)
1873473952e8SBram Moolenaar 				mch_remove(backup);
1874473952e8SBram Moolenaar 			}
1875473952e8SBram Moolenaar 			else
1876473952e8SBram Moolenaar 			{
1877473952e8SBram Moolenaar 			    // try to put the original file back
1878473952e8SBram Moolenaar 			    vim_rename(backup, fname);
1879473952e8SBram Moolenaar 			}
1880473952e8SBram Moolenaar 		    }
1881473952e8SBram Moolenaar 
1882473952e8SBram Moolenaar 		    // if original file no longer exists give an extra warning
1883473952e8SBram Moolenaar 		    if (!newfile && mch_stat((char *)fname, &st) < 0)
1884473952e8SBram Moolenaar 			end = 0;
1885473952e8SBram Moolenaar 		}
1886473952e8SBram Moolenaar 
1887473952e8SBram Moolenaar 		if (wfname != fname)
1888473952e8SBram Moolenaar 		    vim_free(wfname);
1889473952e8SBram Moolenaar 		goto fail;
1890473952e8SBram Moolenaar 	    }
1891473952e8SBram Moolenaar 	    write_info.bw_fd = fd;
1892473952e8SBram Moolenaar 
1893473952e8SBram Moolenaar #if defined(UNIX)
1894473952e8SBram Moolenaar 	    {
1895473952e8SBram Moolenaar 		stat_T	st;
1896473952e8SBram Moolenaar 
1897473952e8SBram Moolenaar 		// Double check we are writing the intended file before making
1898473952e8SBram Moolenaar 		// any changes.
1899473952e8SBram Moolenaar 		if (overwriting
1900473952e8SBram Moolenaar 			&& (!dobackup || backup_copy)
1901473952e8SBram Moolenaar 			&& fname == wfname
1902473952e8SBram Moolenaar 			&& perm >= 0
1903473952e8SBram Moolenaar 			&& mch_fstat(fd, &st) == 0
1904473952e8SBram Moolenaar 			&& st.st_ino != st_old.st_ino)
1905473952e8SBram Moolenaar 		{
1906473952e8SBram Moolenaar 		    close(fd);
1907473952e8SBram Moolenaar 		    errmsg = (char_u *)_("E949: File changed while writing");
1908473952e8SBram Moolenaar 		    goto fail;
1909473952e8SBram Moolenaar 		}
1910473952e8SBram Moolenaar 	    }
1911473952e8SBram Moolenaar #endif
1912473952e8SBram Moolenaar #ifdef HAVE_FTRUNCATE
1913473952e8SBram Moolenaar 	    if (!append)
1914473952e8SBram Moolenaar 		vim_ignored = ftruncate(fd, (off_t)0);
1915473952e8SBram Moolenaar #endif
1916473952e8SBram Moolenaar 
1917473952e8SBram Moolenaar #if defined(MSWIN)
1918473952e8SBram Moolenaar 	    if (backup != NULL && overwriting && !append)
1919473952e8SBram Moolenaar 		(void)mch_copy_file_attribute(backup, wfname);
1920473952e8SBram Moolenaar 
1921473952e8SBram Moolenaar 	    if (!overwriting && !append)
1922473952e8SBram Moolenaar 	    {
1923473952e8SBram Moolenaar 		if (buf->b_ffname != NULL)
1924473952e8SBram Moolenaar 		    (void)mch_copy_file_attribute(buf->b_ffname, wfname);
1925473952e8SBram Moolenaar 		// Should copy resource fork
1926473952e8SBram Moolenaar 	    }
1927473952e8SBram Moolenaar #endif
1928473952e8SBram Moolenaar 
1929473952e8SBram Moolenaar #ifdef FEAT_CRYPT
1930473952e8SBram Moolenaar 	    if (*buf->b_p_key != NUL && !filtering)
1931473952e8SBram Moolenaar 	    {
1932473952e8SBram Moolenaar 		char_u		*header;
1933473952e8SBram Moolenaar 		int		header_len;
1934473952e8SBram Moolenaar 
1935473952e8SBram Moolenaar 		buf->b_cryptstate = crypt_create_for_writing(
1936473952e8SBram Moolenaar 						      crypt_get_method_nr(buf),
1937473952e8SBram Moolenaar 					   buf->b_p_key, &header, &header_len);
1938473952e8SBram Moolenaar 		if (buf->b_cryptstate == NULL || header == NULL)
1939473952e8SBram Moolenaar 		    end = 0;
1940473952e8SBram Moolenaar 		else
1941473952e8SBram Moolenaar 		{
1942473952e8SBram Moolenaar 		    // Write magic number, so that Vim knows how this file is
1943473952e8SBram Moolenaar 		    // encrypted when reading it back.
1944473952e8SBram Moolenaar 		    write_info.bw_buf = header;
1945473952e8SBram Moolenaar 		    write_info.bw_len = header_len;
1946473952e8SBram Moolenaar 		    write_info.bw_flags = FIO_NOCONVERT;
1947473952e8SBram Moolenaar 		    if (buf_write_bytes(&write_info) == FAIL)
1948473952e8SBram Moolenaar 			end = 0;
1949473952e8SBram Moolenaar 		    wb_flags |= FIO_ENCRYPTED;
1950473952e8SBram Moolenaar 		    vim_free(header);
1951473952e8SBram Moolenaar 		}
1952473952e8SBram Moolenaar 	    }
1953473952e8SBram Moolenaar #endif
1954473952e8SBram Moolenaar 	}
1955473952e8SBram Moolenaar 	errmsg = NULL;
1956473952e8SBram Moolenaar 
1957473952e8SBram Moolenaar 	write_info.bw_buf = buffer;
1958473952e8SBram Moolenaar 	nchars = 0;
1959473952e8SBram Moolenaar 
1960473952e8SBram Moolenaar 	// use "++bin", "++nobin" or 'binary'
1961473952e8SBram Moolenaar 	if (eap != NULL && eap->force_bin != 0)
1962473952e8SBram Moolenaar 	    write_bin = (eap->force_bin == FORCE_BIN);
1963473952e8SBram Moolenaar 	else
1964473952e8SBram Moolenaar 	    write_bin = buf->b_p_bin;
1965473952e8SBram Moolenaar 
1966473952e8SBram Moolenaar 	// The BOM is written just after the encryption magic number.
1967473952e8SBram Moolenaar 	// Skip it when appending and the file already existed, the BOM only
1968473952e8SBram Moolenaar 	// makes sense at the start of the file.
1969473952e8SBram Moolenaar 	if (buf->b_p_bomb && !write_bin && (!append || perm < 0))
1970473952e8SBram Moolenaar 	{
1971473952e8SBram Moolenaar 	    write_info.bw_len = make_bom(buffer, fenc);
1972473952e8SBram Moolenaar 	    if (write_info.bw_len > 0)
1973473952e8SBram Moolenaar 	    {
1974473952e8SBram Moolenaar 		// don't convert, do encryption
1975473952e8SBram Moolenaar 		write_info.bw_flags = FIO_NOCONVERT | wb_flags;
1976473952e8SBram Moolenaar 		if (buf_write_bytes(&write_info) == FAIL)
1977473952e8SBram Moolenaar 		    end = 0;
1978473952e8SBram Moolenaar 		else
1979473952e8SBram Moolenaar 		    nchars += write_info.bw_len;
1980473952e8SBram Moolenaar 	    }
1981473952e8SBram Moolenaar 	}
1982473952e8SBram Moolenaar 	write_info.bw_start_lnum = start;
1983473952e8SBram Moolenaar 
1984473952e8SBram Moolenaar #ifdef FEAT_PERSISTENT_UNDO
1985473952e8SBram Moolenaar 	write_undo_file = (buf->b_p_udf
1986473952e8SBram Moolenaar 			    && overwriting
1987473952e8SBram Moolenaar 			    && !append
1988473952e8SBram Moolenaar 			    && !filtering
198965aee0b7SBram Moolenaar # ifdef CRYPT_NOT_INPLACE
199065aee0b7SBram Moolenaar 			    // writing undo file requires
199165aee0b7SBram Moolenaar 			    // crypt_encode_inplace()
19928a4c812eSChristian Brabandt 			    && (buf->b_cryptstate == NULL
19938a4c812eSChristian Brabandt 				|| crypt_works_inplace(buf->b_cryptstate))
199465aee0b7SBram Moolenaar # endif
1995473952e8SBram Moolenaar 			    && reset_changed
1996473952e8SBram Moolenaar 			    && !checking_conversion);
19978a4c812eSChristian Brabandt # ifdef CRYPT_NOT_INPLACE
19988a4c812eSChristian Brabandt 	// remove undo file if encrypting it is not possible
19998a4c812eSChristian Brabandt 	if (buf->b_p_udf
20008a4c812eSChristian Brabandt 		&& overwriting
20018a4c812eSChristian Brabandt 		&& !append
20028a4c812eSChristian Brabandt 		&& !filtering
20038a4c812eSChristian Brabandt 		&& !checking_conversion
20048a4c812eSChristian Brabandt 		&& buf->b_cryptstate != NULL
20058a4c812eSChristian Brabandt 		&& !crypt_works_inplace(buf->b_cryptstate))
20068a4c812eSChristian Brabandt 	    u_undofile_reset_and_delete(buf);
20078a4c812eSChristian Brabandt # endif
2008473952e8SBram Moolenaar 	if (write_undo_file)
2009473952e8SBram Moolenaar 	    // Prepare for computing the hash value of the text.
2010473952e8SBram Moolenaar 	    sha256_start(&sha_ctx);
2011473952e8SBram Moolenaar #endif
2012473952e8SBram Moolenaar 
2013473952e8SBram Moolenaar 	write_info.bw_len = bufsize;
2014473952e8SBram Moolenaar 	write_info.bw_flags = wb_flags;
2015473952e8SBram Moolenaar 	fileformat = get_fileformat_force(buf, eap);
2016473952e8SBram Moolenaar 	s = buffer;
2017473952e8SBram Moolenaar 	len = 0;
2018473952e8SBram Moolenaar 	for (lnum = start; lnum <= end; ++lnum)
2019473952e8SBram Moolenaar 	{
2020473952e8SBram Moolenaar 	    // The next while loop is done once for each character written.
2021473952e8SBram Moolenaar 	    // Keep it fast!
2022473952e8SBram Moolenaar 	    ptr = ml_get_buf(buf, lnum, FALSE) - 1;
2023473952e8SBram Moolenaar #ifdef FEAT_PERSISTENT_UNDO
2024473952e8SBram Moolenaar 	    if (write_undo_file)
2025473952e8SBram Moolenaar 		sha256_update(&sha_ctx, ptr + 1,
2026473952e8SBram Moolenaar 					      (UINT32_T)(STRLEN(ptr + 1) + 1));
2027473952e8SBram Moolenaar #endif
2028473952e8SBram Moolenaar 	    while ((c = *++ptr) != NUL)
2029473952e8SBram Moolenaar 	    {
2030473952e8SBram Moolenaar 		if (c == NL)
2031473952e8SBram Moolenaar 		    *s = NUL;		// replace newlines with NULs
2032473952e8SBram Moolenaar 		else if (c == CAR && fileformat == EOL_MAC)
2033473952e8SBram Moolenaar 		    *s = NL;		// Mac: replace CRs with NLs
2034473952e8SBram Moolenaar 		else
2035473952e8SBram Moolenaar 		    *s = c;
2036473952e8SBram Moolenaar 		++s;
2037473952e8SBram Moolenaar 		if (++len != bufsize)
2038473952e8SBram Moolenaar 		    continue;
2039f573c6e1SChristian Brabandt #ifdef FEAT_CRYPT
2040f573c6e1SChristian Brabandt 		if (write_info.bw_fd > 0 && lnum == end
2041f573c6e1SChristian Brabandt 			&& (write_info.bw_flags & FIO_ENCRYPTED)
2042f573c6e1SChristian Brabandt 			&& *buf->b_p_key != NUL && !filtering
2043f573c6e1SChristian Brabandt 			&& *ptr == NUL)
2044f573c6e1SChristian Brabandt 		    write_info.bw_finish = TRUE;
2045f573c6e1SChristian Brabandt  #endif
2046473952e8SBram Moolenaar 		if (buf_write_bytes(&write_info) == FAIL)
2047473952e8SBram Moolenaar 		{
2048473952e8SBram Moolenaar 		    end = 0;		// write error: break loop
2049473952e8SBram Moolenaar 		    break;
2050473952e8SBram Moolenaar 		}
2051473952e8SBram Moolenaar 		nchars += bufsize;
2052473952e8SBram Moolenaar 		s = buffer;
2053473952e8SBram Moolenaar 		len = 0;
2054473952e8SBram Moolenaar 		write_info.bw_start_lnum = lnum;
2055473952e8SBram Moolenaar 	    }
2056473952e8SBram Moolenaar 	    // write failed or last line has no EOL: stop here
2057473952e8SBram Moolenaar 	    if (end == 0
2058473952e8SBram Moolenaar 		    || (lnum == end
2059473952e8SBram Moolenaar 			&& (write_bin || !buf->b_p_fixeol)
2060b3c8b1d2SBram Moolenaar 			&& ((write_bin && lnum == buf->b_no_eol_lnum)
2061473952e8SBram Moolenaar 			    || (lnum == buf->b_ml.ml_line_count
2062473952e8SBram Moolenaar 							   && !buf->b_p_eol))))
2063473952e8SBram Moolenaar 	    {
2064473952e8SBram Moolenaar 		++lnum;			// written the line, count it
2065473952e8SBram Moolenaar 		no_eol = TRUE;
2066473952e8SBram Moolenaar 		break;
2067473952e8SBram Moolenaar 	    }
2068473952e8SBram Moolenaar 	    if (fileformat == EOL_UNIX)
2069473952e8SBram Moolenaar 		*s++ = NL;
2070473952e8SBram Moolenaar 	    else
2071473952e8SBram Moolenaar 	    {
2072473952e8SBram Moolenaar 		*s++ = CAR;		    // EOL_MAC or EOL_DOS: write CR
2073473952e8SBram Moolenaar 		if (fileformat == EOL_DOS)  // write CR-NL
2074473952e8SBram Moolenaar 		{
2075473952e8SBram Moolenaar 		    if (++len == bufsize)
2076473952e8SBram Moolenaar 		    {
2077473952e8SBram Moolenaar 			if (buf_write_bytes(&write_info) == FAIL)
2078473952e8SBram Moolenaar 			{
2079473952e8SBram Moolenaar 			    end = 0;	// write error: break loop
2080473952e8SBram Moolenaar 			    break;
2081473952e8SBram Moolenaar 			}
2082473952e8SBram Moolenaar 			nchars += bufsize;
2083473952e8SBram Moolenaar 			s = buffer;
2084473952e8SBram Moolenaar 			len = 0;
2085473952e8SBram Moolenaar 		    }
2086473952e8SBram Moolenaar 		    *s++ = NL;
2087473952e8SBram Moolenaar 		}
2088473952e8SBram Moolenaar 	    }
2089473952e8SBram Moolenaar 	    if (++len == bufsize && end)
2090473952e8SBram Moolenaar 	    {
2091473952e8SBram Moolenaar 		if (buf_write_bytes(&write_info) == FAIL)
2092473952e8SBram Moolenaar 		{
2093473952e8SBram Moolenaar 		    end = 0;		// write error: break loop
2094473952e8SBram Moolenaar 		    break;
2095473952e8SBram Moolenaar 		}
2096473952e8SBram Moolenaar 		nchars += bufsize;
2097473952e8SBram Moolenaar 		s = buffer;
2098473952e8SBram Moolenaar 		len = 0;
2099473952e8SBram Moolenaar 
2100473952e8SBram Moolenaar 		ui_breakcheck();
2101473952e8SBram Moolenaar 		if (got_int)
2102473952e8SBram Moolenaar 		{
2103473952e8SBram Moolenaar 		    end = 0;		// Interrupted, break loop
2104473952e8SBram Moolenaar 		    break;
2105473952e8SBram Moolenaar 		}
2106473952e8SBram Moolenaar 	    }
2107473952e8SBram Moolenaar #ifdef VMS
2108473952e8SBram Moolenaar 	    // On VMS there is a problem: newlines get added when writing
2109473952e8SBram Moolenaar 	    // blocks at a time. Fix it by writing a line at a time.
2110473952e8SBram Moolenaar 	    // This is much slower!
2111473952e8SBram Moolenaar 	    // Explanation: VAX/DECC RTL insists that records in some RMS
2112473952e8SBram Moolenaar 	    // structures end with a newline (carriage return) character, and
2113473952e8SBram Moolenaar 	    // if they don't it adds one.
2114473952e8SBram Moolenaar 	    // With other RMS structures it works perfect without this fix.
211595f0b6e5SBram Moolenaar # ifndef MIN
211695f0b6e5SBram Moolenaar // Older DECC compiler for VAX doesn't define MIN()
211795f0b6e5SBram Moolenaar #  define MIN(a, b) ((a) < (b) ? (a) : (b))
211895f0b6e5SBram Moolenaar # endif
2119473952e8SBram Moolenaar 	    if (buf->b_fab_rfm == FAB$C_VFC
2120473952e8SBram Moolenaar 		    || ((buf->b_fab_rat & (FAB$M_FTN | FAB$M_CR)) != 0))
2121473952e8SBram Moolenaar 	    {
2122473952e8SBram Moolenaar 		int b2write;
2123473952e8SBram Moolenaar 
2124473952e8SBram Moolenaar 		buf->b_fab_mrs = (buf->b_fab_mrs == 0
2125473952e8SBram Moolenaar 			? MIN(4096, bufsize)
2126473952e8SBram Moolenaar 			: MIN(buf->b_fab_mrs, bufsize));
2127473952e8SBram Moolenaar 
2128473952e8SBram Moolenaar 		b2write = len;
2129473952e8SBram Moolenaar 		while (b2write > 0)
2130473952e8SBram Moolenaar 		{
2131473952e8SBram Moolenaar 		    write_info.bw_len = MIN(b2write, buf->b_fab_mrs);
2132473952e8SBram Moolenaar 		    if (buf_write_bytes(&write_info) == FAIL)
2133473952e8SBram Moolenaar 		    {
2134473952e8SBram Moolenaar 			end = 0;
2135473952e8SBram Moolenaar 			break;
2136473952e8SBram Moolenaar 		    }
2137473952e8SBram Moolenaar 		    b2write -= MIN(b2write, buf->b_fab_mrs);
2138473952e8SBram Moolenaar 		}
2139473952e8SBram Moolenaar 		write_info.bw_len = bufsize;
2140473952e8SBram Moolenaar 		nchars += len;
2141473952e8SBram Moolenaar 		s = buffer;
2142473952e8SBram Moolenaar 		len = 0;
2143473952e8SBram Moolenaar 	    }
2144473952e8SBram Moolenaar #endif
2145473952e8SBram Moolenaar 	}
2146473952e8SBram Moolenaar 	if (len > 0 && end > 0)
2147473952e8SBram Moolenaar 	{
2148473952e8SBram Moolenaar 	    write_info.bw_len = len;
2149f573c6e1SChristian Brabandt #ifdef FEAT_CRYPT
2150f573c6e1SChristian Brabandt 	    if (write_info.bw_fd > 0 && lnum >= end
2151f573c6e1SChristian Brabandt 		    && (write_info.bw_flags & FIO_ENCRYPTED)
2152f573c6e1SChristian Brabandt 		    && *buf->b_p_key != NUL && !filtering)
2153f573c6e1SChristian Brabandt 		write_info.bw_finish = TRUE;
2154f573c6e1SChristian Brabandt  #endif
2155473952e8SBram Moolenaar 	    if (buf_write_bytes(&write_info) == FAIL)
2156473952e8SBram Moolenaar 		end = 0;		    // write error
2157473952e8SBram Moolenaar 	    nchars += len;
2158473952e8SBram Moolenaar 	}
2159473952e8SBram Moolenaar 
2160473952e8SBram Moolenaar 	// Stop when writing done or an error was encountered.
2161473952e8SBram Moolenaar 	if (!checking_conversion || end == 0)
2162473952e8SBram Moolenaar 	    break;
2163473952e8SBram Moolenaar 
2164473952e8SBram Moolenaar 	// If no error happened until now, writing should be ok, so loop to
2165473952e8SBram Moolenaar 	// really write the buffer.
2166473952e8SBram Moolenaar     }
2167473952e8SBram Moolenaar 
2168473952e8SBram Moolenaar     // If we started writing, finish writing. Also when an error was
2169473952e8SBram Moolenaar     // encountered.
2170473952e8SBram Moolenaar     if (!checking_conversion)
2171473952e8SBram Moolenaar     {
2172473952e8SBram Moolenaar #if defined(UNIX) && defined(HAVE_FSYNC)
21738e7d6223SBram Moolenaar 	// On many journaling file systems there is a bug that causes both the
2174473952e8SBram Moolenaar 	// original and the backup file to be lost when halting the system
2175473952e8SBram Moolenaar 	// right after writing the file.  That's because only the meta-data is
2176473952e8SBram Moolenaar 	// journalled.  Syncing the file slows down the system, but assures it
2177473952e8SBram Moolenaar 	// has been written to disk and we don't lose it.
2178473952e8SBram Moolenaar 	// For a device do try the fsync() but don't complain if it does not
2179473952e8SBram Moolenaar 	// work (could be a pipe).
2180473952e8SBram Moolenaar 	// If the 'fsync' option is FALSE, don't fsync().  Useful for laptops.
2181473952e8SBram Moolenaar 	if (p_fs && vim_fsync(fd) != 0 && !device)
2182473952e8SBram Moolenaar 	{
2183473952e8SBram Moolenaar 	    errmsg = (char_u *)_(e_fsync);
2184473952e8SBram Moolenaar 	    end = 0;
2185473952e8SBram Moolenaar 	}
2186473952e8SBram Moolenaar #endif
2187473952e8SBram Moolenaar 
2188473952e8SBram Moolenaar #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
2189473952e8SBram Moolenaar 	// Probably need to set the security context.
2190473952e8SBram Moolenaar 	if (!backup_copy)
2191473952e8SBram Moolenaar 	    mch_copy_sec(backup, wfname);
2192473952e8SBram Moolenaar #endif
2193473952e8SBram Moolenaar 
2194473952e8SBram Moolenaar #ifdef UNIX
2195473952e8SBram Moolenaar 	// When creating a new file, set its owner/group to that of the
2196473952e8SBram Moolenaar 	// original file.  Get the new device and inode number.
2197473952e8SBram Moolenaar 	if (backup != NULL && !backup_copy)
2198473952e8SBram Moolenaar 	{
2199473952e8SBram Moolenaar # ifdef HAVE_FCHOWN
2200473952e8SBram Moolenaar 	    stat_T	st;
2201473952e8SBram Moolenaar 
2202473952e8SBram Moolenaar 	    // Don't change the owner when it's already OK, some systems remove
2203473952e8SBram Moolenaar 	    // permission or ACL stuff.
2204473952e8SBram Moolenaar 	    if (mch_stat((char *)wfname, &st) < 0
2205473952e8SBram Moolenaar 		    || st.st_uid != st_old.st_uid
2206473952e8SBram Moolenaar 		    || st.st_gid != st_old.st_gid)
2207473952e8SBram Moolenaar 	    {
2208473952e8SBram Moolenaar 		// changing owner might not be possible
2209473952e8SBram Moolenaar 		vim_ignored = fchown(fd, st_old.st_uid, -1);
2210473952e8SBram Moolenaar 		// if changing group fails clear the group permissions
2211473952e8SBram Moolenaar 		if (fchown(fd, -1, st_old.st_gid) == -1 && perm > 0)
2212473952e8SBram Moolenaar 		    perm &= ~070;
2213473952e8SBram Moolenaar 	    }
2214473952e8SBram Moolenaar # endif
2215473952e8SBram Moolenaar 	    buf_setino(buf);
2216473952e8SBram Moolenaar 	}
2217473952e8SBram Moolenaar 	else if (!buf->b_dev_valid)
2218473952e8SBram Moolenaar 	    // Set the inode when creating a new file.
2219473952e8SBram Moolenaar 	    buf_setino(buf);
2220473952e8SBram Moolenaar #endif
2221473952e8SBram Moolenaar 
2222473952e8SBram Moolenaar #ifdef UNIX
2223473952e8SBram Moolenaar 	if (made_writable)
2224473952e8SBram Moolenaar 	    perm &= ~0200;	// reset 'w' bit for security reasons
2225473952e8SBram Moolenaar #endif
2226473952e8SBram Moolenaar #ifdef HAVE_FCHMOD
2227473952e8SBram Moolenaar 	// set permission of new file same as old file
2228473952e8SBram Moolenaar 	if (perm >= 0)
2229473952e8SBram Moolenaar 	    (void)mch_fsetperm(fd, perm);
2230473952e8SBram Moolenaar #endif
2231473952e8SBram Moolenaar 	if (close(fd) != 0)
2232473952e8SBram Moolenaar 	{
2233473952e8SBram Moolenaar 	    errmsg = (char_u *)_("E512: Close failed");
2234473952e8SBram Moolenaar 	    end = 0;
2235473952e8SBram Moolenaar 	}
2236473952e8SBram Moolenaar 
2237473952e8SBram Moolenaar #ifndef HAVE_FCHMOD
2238473952e8SBram Moolenaar 	// set permission of new file same as old file
2239473952e8SBram Moolenaar 	if (perm >= 0)
2240473952e8SBram Moolenaar 	    (void)mch_setperm(wfname, perm);
2241473952e8SBram Moolenaar #endif
2242473952e8SBram Moolenaar #ifdef HAVE_ACL
2243473952e8SBram Moolenaar 	// Probably need to set the ACL before changing the user (can't set the
2244473952e8SBram Moolenaar 	// ACL on a file the user doesn't own).
2245473952e8SBram Moolenaar 	// On Solaris, with ZFS and the aclmode property set to "discard" (the
2246473952e8SBram Moolenaar 	// default), chmod() discards all part of a file's ACL that don't
2247473952e8SBram Moolenaar 	// represent the mode of the file.  It's non-trivial for us to discover
2248473952e8SBram Moolenaar 	// whether we're in that situation, so we simply always re-set the ACL.
2249473952e8SBram Moolenaar # ifndef HAVE_SOLARIS_ZFS_ACL
2250473952e8SBram Moolenaar 	if (!backup_copy)
2251473952e8SBram Moolenaar # endif
2252473952e8SBram Moolenaar 	    mch_set_acl(wfname, acl);
2253473952e8SBram Moolenaar #endif
2254473952e8SBram Moolenaar #ifdef FEAT_CRYPT
2255473952e8SBram Moolenaar 	if (buf->b_cryptstate != NULL)
2256473952e8SBram Moolenaar 	{
2257473952e8SBram Moolenaar 	    crypt_free_state(buf->b_cryptstate);
2258473952e8SBram Moolenaar 	    buf->b_cryptstate = NULL;
2259473952e8SBram Moolenaar 	}
2260473952e8SBram Moolenaar #endif
2261473952e8SBram Moolenaar 
2262473952e8SBram Moolenaar #if defined(FEAT_EVAL)
2263473952e8SBram Moolenaar 	if (wfname != fname)
2264473952e8SBram Moolenaar 	{
2265473952e8SBram Moolenaar 	    // The file was written to a temp file, now it needs to be
2266473952e8SBram Moolenaar 	    // converted with 'charconvert' to (overwrite) the output file.
2267473952e8SBram Moolenaar 	    if (end != 0)
2268473952e8SBram Moolenaar 	    {
2269473952e8SBram Moolenaar 		if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc,
2270473952e8SBram Moolenaar 						  fenc, wfname, fname) == FAIL)
2271473952e8SBram Moolenaar 		{
2272473952e8SBram Moolenaar 		    write_info.bw_conv_error = TRUE;
2273473952e8SBram Moolenaar 		    end = 0;
2274473952e8SBram Moolenaar 		}
2275473952e8SBram Moolenaar 	    }
2276473952e8SBram Moolenaar 	    mch_remove(wfname);
2277473952e8SBram Moolenaar 	    vim_free(wfname);
2278473952e8SBram Moolenaar 	}
2279473952e8SBram Moolenaar #endif
2280473952e8SBram Moolenaar     }
2281473952e8SBram Moolenaar 
2282473952e8SBram Moolenaar     if (end == 0)
2283473952e8SBram Moolenaar     {
2284473952e8SBram Moolenaar 	// Error encountered.
2285473952e8SBram Moolenaar 	if (errmsg == NULL)
2286473952e8SBram Moolenaar 	{
2287473952e8SBram Moolenaar 	    if (write_info.bw_conv_error)
2288473952e8SBram Moolenaar 	    {
2289473952e8SBram Moolenaar 		if (write_info.bw_conv_error_lnum == 0)
2290473952e8SBram Moolenaar 		    errmsg = (char_u *)_("E513: write error, conversion failed (make 'fenc' empty to override)");
2291473952e8SBram Moolenaar 		else
2292473952e8SBram Moolenaar 		{
2293473952e8SBram Moolenaar 		    errmsg_allocated = TRUE;
2294473952e8SBram Moolenaar 		    errmsg = alloc(300);
2295473952e8SBram Moolenaar 		    vim_snprintf((char *)errmsg, 300, _("E513: write error, conversion failed in line %ld (make 'fenc' empty to override)"),
2296473952e8SBram Moolenaar 					 (long)write_info.bw_conv_error_lnum);
2297473952e8SBram Moolenaar 		}
2298473952e8SBram Moolenaar 	    }
2299473952e8SBram Moolenaar 	    else if (got_int)
2300473952e8SBram Moolenaar 		errmsg = (char_u *)_(e_interr);
2301473952e8SBram Moolenaar 	    else
2302473952e8SBram Moolenaar 		errmsg = (char_u *)_("E514: write error (file system full?)");
2303473952e8SBram Moolenaar 	}
2304473952e8SBram Moolenaar 
2305473952e8SBram Moolenaar 	// If we have a backup file, try to put it in place of the new file,
2306473952e8SBram Moolenaar 	// because the new file is probably corrupt.  This avoids losing the
2307473952e8SBram Moolenaar 	// original file when trying to make a backup when writing the file a
2308473952e8SBram Moolenaar 	// second time.
2309473952e8SBram Moolenaar 	// When "backup_copy" is set we need to copy the backup over the new
2310473952e8SBram Moolenaar 	// file.  Otherwise rename the backup file.
2311473952e8SBram Moolenaar 	// If this is OK, don't give the extra warning message.
2312473952e8SBram Moolenaar 	if (backup != NULL)
2313473952e8SBram Moolenaar 	{
2314473952e8SBram Moolenaar 	    if (backup_copy)
2315473952e8SBram Moolenaar 	    {
2316473952e8SBram Moolenaar 		// This may take a while, if we were interrupted let the user
2317473952e8SBram Moolenaar 		// know we got the message.
2318473952e8SBram Moolenaar 		if (got_int)
2319473952e8SBram Moolenaar 		{
2320473952e8SBram Moolenaar 		    msg(_(e_interr));
2321473952e8SBram Moolenaar 		    out_flush();
2322473952e8SBram Moolenaar 		}
2323473952e8SBram Moolenaar 		if ((fd = mch_open((char *)backup, O_RDONLY | O_EXTRA, 0)) >= 0)
2324473952e8SBram Moolenaar 		{
2325473952e8SBram Moolenaar 		    if ((write_info.bw_fd = mch_open((char *)fname,
2326473952e8SBram Moolenaar 				    O_WRONLY | O_CREAT | O_TRUNC | O_EXTRA,
2327473952e8SBram Moolenaar 							   perm & 0777)) >= 0)
2328473952e8SBram Moolenaar 		    {
2329473952e8SBram Moolenaar 			// copy the file.
2330473952e8SBram Moolenaar 			write_info.bw_buf = smallbuf;
2331473952e8SBram Moolenaar 			write_info.bw_flags = FIO_NOCONVERT;
2332473952e8SBram Moolenaar 			while ((write_info.bw_len = read_eintr(fd, smallbuf,
2333473952e8SBram Moolenaar 						      SMALLBUFSIZE)) > 0)
2334473952e8SBram Moolenaar 			    if (buf_write_bytes(&write_info) == FAIL)
2335473952e8SBram Moolenaar 				break;
2336473952e8SBram Moolenaar 
2337473952e8SBram Moolenaar 			if (close(write_info.bw_fd) >= 0
2338473952e8SBram Moolenaar 						   && write_info.bw_len == 0)
2339473952e8SBram Moolenaar 			    end = 1;		// success
2340473952e8SBram Moolenaar 		    }
2341473952e8SBram Moolenaar 		    close(fd);	// ignore errors for closing read file
2342473952e8SBram Moolenaar 		}
2343473952e8SBram Moolenaar 	    }
2344473952e8SBram Moolenaar 	    else
2345473952e8SBram Moolenaar 	    {
2346473952e8SBram Moolenaar 		if (vim_rename(backup, fname) == 0)
2347473952e8SBram Moolenaar 		    end = 1;
2348473952e8SBram Moolenaar 	    }
2349473952e8SBram Moolenaar 	}
2350473952e8SBram Moolenaar 	goto fail;
2351473952e8SBram Moolenaar     }
2352473952e8SBram Moolenaar 
2353473952e8SBram Moolenaar     lnum -= start;	    // compute number of written lines
2354473952e8SBram Moolenaar     --no_wait_return;	    // may wait for return now
2355473952e8SBram Moolenaar 
2356473952e8SBram Moolenaar #if !(defined(UNIX) || defined(VMS))
2357473952e8SBram Moolenaar     fname = sfname;	    // use shortname now, for the messages
2358473952e8SBram Moolenaar #endif
2359473952e8SBram Moolenaar     if (!filtering)
2360473952e8SBram Moolenaar     {
2361473952e8SBram Moolenaar 	msg_add_fname(buf, fname);	// put fname in IObuff with quotes
2362473952e8SBram Moolenaar 	c = FALSE;
2363473952e8SBram Moolenaar 	if (write_info.bw_conv_error)
2364473952e8SBram Moolenaar 	{
2365473952e8SBram Moolenaar 	    STRCAT(IObuff, _(" CONVERSION ERROR"));
2366473952e8SBram Moolenaar 	    c = TRUE;
2367473952e8SBram Moolenaar 	    if (write_info.bw_conv_error_lnum != 0)
2368473952e8SBram Moolenaar 		vim_snprintf_add((char *)IObuff, IOSIZE, _(" in line %ld;"),
2369473952e8SBram Moolenaar 			(long)write_info.bw_conv_error_lnum);
2370473952e8SBram Moolenaar 	}
2371473952e8SBram Moolenaar 	else if (notconverted)
2372473952e8SBram Moolenaar 	{
2373473952e8SBram Moolenaar 	    STRCAT(IObuff, _("[NOT converted]"));
2374473952e8SBram Moolenaar 	    c = TRUE;
2375473952e8SBram Moolenaar 	}
2376473952e8SBram Moolenaar 	else if (converted)
2377473952e8SBram Moolenaar 	{
2378473952e8SBram Moolenaar 	    STRCAT(IObuff, _("[converted]"));
2379473952e8SBram Moolenaar 	    c = TRUE;
2380473952e8SBram Moolenaar 	}
2381473952e8SBram Moolenaar 	if (device)
2382473952e8SBram Moolenaar 	{
2383473952e8SBram Moolenaar 	    STRCAT(IObuff, _("[Device]"));
2384473952e8SBram Moolenaar 	    c = TRUE;
2385473952e8SBram Moolenaar 	}
2386473952e8SBram Moolenaar 	else if (newfile)
2387473952e8SBram Moolenaar 	{
2388722e505dSBram Moolenaar 	    STRCAT(IObuff, new_file_message());
2389473952e8SBram Moolenaar 	    c = TRUE;
2390473952e8SBram Moolenaar 	}
2391473952e8SBram Moolenaar 	if (no_eol)
2392473952e8SBram Moolenaar 	{
2393473952e8SBram Moolenaar 	    msg_add_eol();
2394473952e8SBram Moolenaar 	    c = TRUE;
2395473952e8SBram Moolenaar 	}
2396473952e8SBram Moolenaar 	// may add [unix/dos/mac]
2397473952e8SBram Moolenaar 	if (msg_add_fileformat(fileformat))
2398473952e8SBram Moolenaar 	    c = TRUE;
2399473952e8SBram Moolenaar #ifdef FEAT_CRYPT
2400473952e8SBram Moolenaar 	if (wb_flags & FIO_ENCRYPTED)
2401473952e8SBram Moolenaar 	{
2402473952e8SBram Moolenaar 	    crypt_append_msg(buf);
2403473952e8SBram Moolenaar 	    c = TRUE;
2404473952e8SBram Moolenaar 	}
2405473952e8SBram Moolenaar #endif
2406473952e8SBram Moolenaar 	msg_add_lines(c, (long)lnum, nchars);	// add line/char count
2407473952e8SBram Moolenaar 	if (!shortmess(SHM_WRITE))
2408473952e8SBram Moolenaar 	{
2409473952e8SBram Moolenaar 	    if (append)
2410473952e8SBram Moolenaar 		STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [a]") : _(" appended"));
2411473952e8SBram Moolenaar 	    else
2412473952e8SBram Moolenaar 		STRCAT(IObuff, shortmess(SHM_WRI) ? _(" [w]") : _(" written"));
2413473952e8SBram Moolenaar 	}
2414473952e8SBram Moolenaar 
2415473952e8SBram Moolenaar 	set_keep_msg((char_u *)msg_trunc_attr((char *)IObuff, FALSE, 0), 0);
2416473952e8SBram Moolenaar     }
2417473952e8SBram Moolenaar 
2418473952e8SBram Moolenaar     // When written everything correctly: reset 'modified'.  Unless not
2419473952e8SBram Moolenaar     // writing to the original file and '+' is not in 'cpoptions'.
2420473952e8SBram Moolenaar     if (reset_changed && whole && !append
2421473952e8SBram Moolenaar 	    && !write_info.bw_conv_error
2422473952e8SBram Moolenaar 	    && (overwriting || vim_strchr(p_cpo, CPO_PLUS) != NULL))
2423473952e8SBram Moolenaar     {
2424473952e8SBram Moolenaar 	unchanged(buf, TRUE, FALSE);
2425*db3b4464SChristian Brabandt 	// b:changedtick may be incremented in unchanged() but that should not
2426*db3b4464SChristian Brabandt 	// trigger a TextChanged event.
2427473952e8SBram Moolenaar 	if (buf->b_last_changedtick + 1 == CHANGEDTICK(buf))
2428473952e8SBram Moolenaar 	    buf->b_last_changedtick = CHANGEDTICK(buf);
2429473952e8SBram Moolenaar 	u_unchanged(buf);
2430473952e8SBram Moolenaar 	u_update_save_nr(buf);
2431473952e8SBram Moolenaar     }
2432473952e8SBram Moolenaar 
2433473952e8SBram Moolenaar     // If written to the current file, update the timestamp of the swap file
2434473952e8SBram Moolenaar     // and reset the BF_WRITE_MASK flags. Also sets buf->b_mtime.
2435473952e8SBram Moolenaar     if (overwriting)
2436473952e8SBram Moolenaar     {
2437473952e8SBram Moolenaar 	ml_timestamp(buf);
2438473952e8SBram Moolenaar 	if (append)
2439473952e8SBram Moolenaar 	    buf->b_flags &= ~BF_NEW;
2440473952e8SBram Moolenaar 	else
2441473952e8SBram Moolenaar 	    buf->b_flags &= ~BF_WRITE_MASK;
2442473952e8SBram Moolenaar     }
2443473952e8SBram Moolenaar 
2444473952e8SBram Moolenaar     // If we kept a backup until now, and we are in patch mode, then we make
2445473952e8SBram Moolenaar     // the backup file our 'original' file.
2446473952e8SBram Moolenaar     if (*p_pm && dobackup)
2447473952e8SBram Moolenaar     {
2448473952e8SBram Moolenaar 	char *org = (char *)buf_modname((buf->b_p_sn || buf->b_shortname),
2449473952e8SBram Moolenaar 							  fname, p_pm, FALSE);
2450473952e8SBram Moolenaar 
2451473952e8SBram Moolenaar 	if (backup != NULL)
2452473952e8SBram Moolenaar 	{
2453473952e8SBram Moolenaar 	    stat_T	st;
2454473952e8SBram Moolenaar 
2455473952e8SBram Moolenaar 	    // If the original file does not exist yet
2456473952e8SBram Moolenaar 	    // the current backup file becomes the original file
2457473952e8SBram Moolenaar 	    if (org == NULL)
2458473952e8SBram Moolenaar 		emsg(_("E205: Patchmode: can't save original file"));
2459473952e8SBram Moolenaar 	    else if (mch_stat(org, &st) < 0)
2460473952e8SBram Moolenaar 	    {
2461473952e8SBram Moolenaar 		vim_rename(backup, (char_u *)org);
2462473952e8SBram Moolenaar 		VIM_CLEAR(backup);	    // don't delete the file
2463473952e8SBram Moolenaar #ifdef UNIX
2464473952e8SBram Moolenaar 		set_file_time((char_u *)org, st_old.st_atime, st_old.st_mtime);
2465473952e8SBram Moolenaar #endif
2466473952e8SBram Moolenaar 	    }
2467473952e8SBram Moolenaar 	}
2468473952e8SBram Moolenaar 	// If there is no backup file, remember that a (new) file was
2469473952e8SBram Moolenaar 	// created.
2470473952e8SBram Moolenaar 	else
2471473952e8SBram Moolenaar 	{
2472473952e8SBram Moolenaar 	    int empty_fd;
2473473952e8SBram Moolenaar 
2474473952e8SBram Moolenaar 	    if (org == NULL
2475473952e8SBram Moolenaar 		    || (empty_fd = mch_open(org,
2476473952e8SBram Moolenaar 				      O_CREAT | O_EXTRA | O_EXCL | O_NOFOLLOW,
2477473952e8SBram Moolenaar 					perm < 0 ? 0666 : (perm & 0777))) < 0)
2478473952e8SBram Moolenaar 	      emsg(_("E206: patchmode: can't touch empty original file"));
2479473952e8SBram Moolenaar 	    else
2480473952e8SBram Moolenaar 	      close(empty_fd);
2481473952e8SBram Moolenaar 	}
2482473952e8SBram Moolenaar 	if (org != NULL)
2483473952e8SBram Moolenaar 	{
2484473952e8SBram Moolenaar 	    mch_setperm((char_u *)org, mch_getperm(fname) & 0777);
2485473952e8SBram Moolenaar 	    vim_free(org);
2486473952e8SBram Moolenaar 	}
2487473952e8SBram Moolenaar     }
2488473952e8SBram Moolenaar 
2489473952e8SBram Moolenaar     // Remove the backup unless 'backup' option is set or there was a
2490473952e8SBram Moolenaar     // conversion error.
2491473952e8SBram Moolenaar     if (!p_bk && backup != NULL && !write_info.bw_conv_error
2492473952e8SBram Moolenaar 	    && mch_remove(backup) != 0)
2493473952e8SBram Moolenaar 	emsg(_("E207: Can't delete backup file"));
2494473952e8SBram Moolenaar 
2495473952e8SBram Moolenaar     goto nofail;
2496473952e8SBram Moolenaar 
2497473952e8SBram Moolenaar     // Finish up.  We get here either after failure or success.
2498473952e8SBram Moolenaar fail:
2499473952e8SBram Moolenaar     --no_wait_return;		// may wait for return now
2500473952e8SBram Moolenaar nofail:
2501473952e8SBram Moolenaar 
2502473952e8SBram Moolenaar     // Done saving, we accept changed buffer warnings again
2503473952e8SBram Moolenaar     buf->b_saving = FALSE;
2504473952e8SBram Moolenaar 
2505473952e8SBram Moolenaar     vim_free(backup);
2506473952e8SBram Moolenaar     if (buffer != smallbuf)
2507473952e8SBram Moolenaar 	vim_free(buffer);
2508473952e8SBram Moolenaar     vim_free(fenc_tofree);
2509473952e8SBram Moolenaar     vim_free(write_info.bw_conv_buf);
2510473952e8SBram Moolenaar #ifdef USE_ICONV
2511473952e8SBram Moolenaar     if (write_info.bw_iconv_fd != (iconv_t)-1)
2512473952e8SBram Moolenaar     {
2513473952e8SBram Moolenaar 	iconv_close(write_info.bw_iconv_fd);
2514473952e8SBram Moolenaar 	write_info.bw_iconv_fd = (iconv_t)-1;
2515473952e8SBram Moolenaar     }
2516473952e8SBram Moolenaar #endif
2517473952e8SBram Moolenaar #ifdef HAVE_ACL
2518473952e8SBram Moolenaar     mch_free_acl(acl);
2519473952e8SBram Moolenaar #endif
2520473952e8SBram Moolenaar 
2521473952e8SBram Moolenaar     if (errmsg != NULL)
2522473952e8SBram Moolenaar     {
2523473952e8SBram Moolenaar 	int numlen = errnum != NULL ? (int)STRLEN(errnum) : 0;
2524473952e8SBram Moolenaar 
2525473952e8SBram Moolenaar 	attr = HL_ATTR(HLF_E);	// set highlight for error messages
2526473952e8SBram Moolenaar 	msg_add_fname(buf,
2527473952e8SBram Moolenaar #ifndef UNIX
2528473952e8SBram Moolenaar 		sfname
2529473952e8SBram Moolenaar #else
2530473952e8SBram Moolenaar 		fname
2531473952e8SBram Moolenaar #endif
2532473952e8SBram Moolenaar 		     );		// put file name in IObuff with quotes
2533473952e8SBram Moolenaar 	if (STRLEN(IObuff) + STRLEN(errmsg) + numlen >= IOSIZE)
2534473952e8SBram Moolenaar 	    IObuff[IOSIZE - STRLEN(errmsg) - numlen - 1] = NUL;
2535473952e8SBram Moolenaar 	// If the error message has the form "is ...", put the error number in
2536473952e8SBram Moolenaar 	// front of the file name.
2537473952e8SBram Moolenaar 	if (errnum != NULL)
2538473952e8SBram Moolenaar 	{
2539473952e8SBram Moolenaar 	    STRMOVE(IObuff + numlen, IObuff);
2540473952e8SBram Moolenaar 	    mch_memmove(IObuff, errnum, (size_t)numlen);
2541473952e8SBram Moolenaar 	}
2542473952e8SBram Moolenaar 	STRCAT(IObuff, errmsg);
2543473952e8SBram Moolenaar 	emsg((char *)IObuff);
2544473952e8SBram Moolenaar 	if (errmsg_allocated)
2545473952e8SBram Moolenaar 	    vim_free(errmsg);
2546473952e8SBram Moolenaar 
2547473952e8SBram Moolenaar 	retval = FAIL;
2548473952e8SBram Moolenaar 	if (end == 0)
2549473952e8SBram Moolenaar 	{
2550473952e8SBram Moolenaar 	    msg_puts_attr(_("\nWARNING: Original file may be lost or damaged\n"),
2551473952e8SBram Moolenaar 		    attr | MSG_HIST);
2552473952e8SBram Moolenaar 	    msg_puts_attr(_("don't quit the editor until the file is successfully written!"),
2553473952e8SBram Moolenaar 		    attr | MSG_HIST);
2554473952e8SBram Moolenaar 
2555473952e8SBram Moolenaar 	    // Update the timestamp to avoid an "overwrite changed file"
2556473952e8SBram Moolenaar 	    // prompt when writing again.
2557473952e8SBram Moolenaar 	    if (mch_stat((char *)fname, &st_old) >= 0)
2558473952e8SBram Moolenaar 	    {
2559473952e8SBram Moolenaar 		buf_store_time(buf, &st_old, fname);
2560473952e8SBram Moolenaar 		buf->b_mtime_read = buf->b_mtime;
25610a7984afSLeah Neukirchen 		buf->b_mtime_read_ns = buf->b_mtime_ns;
2562473952e8SBram Moolenaar 	    }
2563473952e8SBram Moolenaar 	}
2564473952e8SBram Moolenaar     }
2565473952e8SBram Moolenaar     msg_scroll = msg_save;
2566473952e8SBram Moolenaar 
2567473952e8SBram Moolenaar #ifdef FEAT_PERSISTENT_UNDO
2568473952e8SBram Moolenaar     // When writing the whole file and 'undofile' is set, also write the undo
2569473952e8SBram Moolenaar     // file.
2570473952e8SBram Moolenaar     if (retval == OK && write_undo_file)
2571473952e8SBram Moolenaar     {
2572473952e8SBram Moolenaar 	char_u	    hash[UNDO_HASH_SIZE];
2573473952e8SBram Moolenaar 
2574473952e8SBram Moolenaar 	sha256_finish(&sha_ctx, hash);
2575473952e8SBram Moolenaar 	u_write_undo(NULL, FALSE, buf, hash);
2576473952e8SBram Moolenaar     }
2577473952e8SBram Moolenaar #endif
2578473952e8SBram Moolenaar 
2579473952e8SBram Moolenaar #ifdef FEAT_EVAL
2580473952e8SBram Moolenaar     if (!should_abort(retval))
2581473952e8SBram Moolenaar #else
2582473952e8SBram Moolenaar     if (!got_int)
2583473952e8SBram Moolenaar #endif
2584473952e8SBram Moolenaar     {
2585473952e8SBram Moolenaar 	aco_save_T	aco;
2586473952e8SBram Moolenaar 
2587473952e8SBram Moolenaar 	curbuf->b_no_eol_lnum = 0;  // in case it was set by the previous read
2588473952e8SBram Moolenaar 
2589473952e8SBram Moolenaar 	// Apply POST autocommands.
2590473952e8SBram Moolenaar 	// Careful: The autocommands may call buf_write() recursively!
2591473952e8SBram Moolenaar 	aucmd_prepbuf(&aco, buf);
2592473952e8SBram Moolenaar 
2593473952e8SBram Moolenaar 	if (append)
2594473952e8SBram Moolenaar 	    apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname,
2595473952e8SBram Moolenaar 							  FALSE, curbuf, eap);
2596473952e8SBram Moolenaar 	else if (filtering)
2597473952e8SBram Moolenaar 	    apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname,
2598473952e8SBram Moolenaar 							  FALSE, curbuf, eap);
2599473952e8SBram Moolenaar 	else if (reset_changed && whole)
2600473952e8SBram Moolenaar 	    apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname,
2601473952e8SBram Moolenaar 							  FALSE, curbuf, eap);
2602473952e8SBram Moolenaar 	else
2603473952e8SBram Moolenaar 	    apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname,
2604473952e8SBram Moolenaar 							  FALSE, curbuf, eap);
2605473952e8SBram Moolenaar 
2606473952e8SBram Moolenaar 	// restore curwin/curbuf and a few other things
2607473952e8SBram Moolenaar 	aucmd_restbuf(&aco);
2608473952e8SBram Moolenaar 
2609473952e8SBram Moolenaar #ifdef FEAT_EVAL
2610473952e8SBram Moolenaar 	if (aborting())	    // autocmds may abort script processing
2611473952e8SBram Moolenaar 	    retval = FALSE;
2612473952e8SBram Moolenaar #endif
2613473952e8SBram Moolenaar     }
2614473952e8SBram Moolenaar 
26158e6be343SBram Moolenaar #ifdef FEAT_VIMINFO
26168e6be343SBram Moolenaar     // Make sure marks will be written out to the viminfo file later, even when
26178e6be343SBram Moolenaar     // the file is new.
26188e6be343SBram Moolenaar     curbuf->b_marks_read = TRUE;
26198e6be343SBram Moolenaar #endif
26208e6be343SBram Moolenaar 
2621473952e8SBram Moolenaar     got_int |= prev_got_int;
2622473952e8SBram Moolenaar 
2623473952e8SBram Moolenaar     return retval;
2624473952e8SBram Moolenaar }
2625