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 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 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 527473952e8SBram Moolenaar check_mtime(buf_T *buf, stat_T *st) 528473952e8SBram Moolenaar { 529473952e8SBram Moolenaar if (buf->b_mtime_read != 0 530473952e8SBram Moolenaar && time_differs((long)st->st_mtime, buf->b_mtime_read)) 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 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 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 * 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 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() 1992*8a4c812eSChristian Brabandt && (buf->b_cryptstate == NULL 1993*8a4c812eSChristian Brabandt || crypt_works_inplace(buf->b_cryptstate)) 199465aee0b7SBram Moolenaar # endif 1995473952e8SBram Moolenaar && reset_changed 1996473952e8SBram Moolenaar && !checking_conversion); 1997*8a4c812eSChristian Brabandt # ifdef CRYPT_NOT_INPLACE 1998*8a4c812eSChristian Brabandt // remove undo file if encrypting it is not possible 1999*8a4c812eSChristian Brabandt if (buf->b_p_udf 2000*8a4c812eSChristian Brabandt && overwriting 2001*8a4c812eSChristian Brabandt && !append 2002*8a4c812eSChristian Brabandt && !filtering 2003*8a4c812eSChristian Brabandt && !checking_conversion 2004*8a4c812eSChristian Brabandt && buf->b_cryptstate != NULL 2005*8a4c812eSChristian Brabandt && !crypt_works_inplace(buf->b_cryptstate)) 2006*8a4c812eSChristian Brabandt u_undofile_reset_and_delete(buf); 2007*8a4c812eSChristian 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); 2425473952e8SBram Moolenaar // b:changedtick is may be incremented in unchanged() but that 2426473952e8SBram Moolenaar // should not 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; 2561473952e8SBram Moolenaar } 2562473952e8SBram Moolenaar } 2563473952e8SBram Moolenaar } 2564473952e8SBram Moolenaar msg_scroll = msg_save; 2565473952e8SBram Moolenaar 2566473952e8SBram Moolenaar #ifdef FEAT_PERSISTENT_UNDO 2567473952e8SBram Moolenaar // When writing the whole file and 'undofile' is set, also write the undo 2568473952e8SBram Moolenaar // file. 2569473952e8SBram Moolenaar if (retval == OK && write_undo_file) 2570473952e8SBram Moolenaar { 2571473952e8SBram Moolenaar char_u hash[UNDO_HASH_SIZE]; 2572473952e8SBram Moolenaar 2573473952e8SBram Moolenaar sha256_finish(&sha_ctx, hash); 2574473952e8SBram Moolenaar u_write_undo(NULL, FALSE, buf, hash); 2575473952e8SBram Moolenaar } 2576473952e8SBram Moolenaar #endif 2577473952e8SBram Moolenaar 2578473952e8SBram Moolenaar #ifdef FEAT_EVAL 2579473952e8SBram Moolenaar if (!should_abort(retval)) 2580473952e8SBram Moolenaar #else 2581473952e8SBram Moolenaar if (!got_int) 2582473952e8SBram Moolenaar #endif 2583473952e8SBram Moolenaar { 2584473952e8SBram Moolenaar aco_save_T aco; 2585473952e8SBram Moolenaar 2586473952e8SBram Moolenaar curbuf->b_no_eol_lnum = 0; // in case it was set by the previous read 2587473952e8SBram Moolenaar 2588473952e8SBram Moolenaar // Apply POST autocommands. 2589473952e8SBram Moolenaar // Careful: The autocommands may call buf_write() recursively! 2590473952e8SBram Moolenaar aucmd_prepbuf(&aco, buf); 2591473952e8SBram Moolenaar 2592473952e8SBram Moolenaar if (append) 2593473952e8SBram Moolenaar apply_autocmds_exarg(EVENT_FILEAPPENDPOST, fname, fname, 2594473952e8SBram Moolenaar FALSE, curbuf, eap); 2595473952e8SBram Moolenaar else if (filtering) 2596473952e8SBram Moolenaar apply_autocmds_exarg(EVENT_FILTERWRITEPOST, NULL, fname, 2597473952e8SBram Moolenaar FALSE, curbuf, eap); 2598473952e8SBram Moolenaar else if (reset_changed && whole) 2599473952e8SBram Moolenaar apply_autocmds_exarg(EVENT_BUFWRITEPOST, fname, fname, 2600473952e8SBram Moolenaar FALSE, curbuf, eap); 2601473952e8SBram Moolenaar else 2602473952e8SBram Moolenaar apply_autocmds_exarg(EVENT_FILEWRITEPOST, fname, fname, 2603473952e8SBram Moolenaar FALSE, curbuf, eap); 2604473952e8SBram Moolenaar 2605473952e8SBram Moolenaar // restore curwin/curbuf and a few other things 2606473952e8SBram Moolenaar aucmd_restbuf(&aco); 2607473952e8SBram Moolenaar 2608473952e8SBram Moolenaar #ifdef FEAT_EVAL 2609473952e8SBram Moolenaar if (aborting()) // autocmds may abort script processing 2610473952e8SBram Moolenaar retval = FALSE; 2611473952e8SBram Moolenaar #endif 2612473952e8SBram Moolenaar } 2613473952e8SBram Moolenaar 26148e6be343SBram Moolenaar #ifdef FEAT_VIMINFO 26158e6be343SBram Moolenaar // Make sure marks will be written out to the viminfo file later, even when 26168e6be343SBram Moolenaar // the file is new. 26178e6be343SBram Moolenaar curbuf->b_marks_read = TRUE; 26188e6be343SBram Moolenaar #endif 26198e6be343SBram Moolenaar 2620473952e8SBram Moolenaar got_int |= prev_got_int; 2621473952e8SBram Moolenaar 2622473952e8SBram Moolenaar return retval; 2623473952e8SBram Moolenaar } 2624