Patch 8.0.0685
Problem:    When making backups is disabled and conversion with iconv fails
            the written file is truncated. (Luo Chen)
Solution:   First try converting the file and write the file only when it did
            not fail. (partly by Christian Brabandt)
Files:      src/fileio.c, src/testdir/test_writefile.vim


*** ../vim-8.0.0684/src/fileio.c        2017-04-07 19:50:08.691049319 +0200
--- src/fileio.c        2017-06-27 21:23:26.649445699 +0200
***************
*** 3166,3171 ****
--- 3166,3172 ----
      int                   device = FALSE;         /* writing to a device */
      stat_T        st_old;
      int                   prev_got_int = got_int;
+     int                   checking_conversion;
      int                   file_readonly = FALSE;  /* overwritten file is 
read-only */
      static char           *err_readonly = "is read-only (cannot override: 
\"W\" in 'cpoptions')";
  #if defined(UNIX)                         /*XXX fix me sometime? */
***************
*** 4344,4776 ****
  #endif
  
      /*
!      * Open the file "wfname" for writing.
!      * We may try to open the file twice: If we can't write to the
!      * file and forceit is TRUE we delete the existing file and try to create
!      * a new one. If this still fails we may have lost the original file!
!      * (this may happen when the user reached his quotum for number of files).
!      * Appending will fail if the file does not exist and forceit is FALSE.
!      */
!     while ((fd = mch_open((char *)wfname, O_WRONLY | O_EXTRA | (append
!                       ? (forceit ? (O_APPEND | O_CREAT) : O_APPEND)
!                       : (O_CREAT | O_TRUNC))
!                       , perm < 0 ? 0666 : (perm & 0777))) < 0)
      {
        /*
!        * A forced write will try to create a new file if the old one is
!        * still readonly. This may also happen when the directory is
!        * read-only. In that case the mch_remove() will fail.
         */
!       if (errmsg == NULL)
!       {
! #ifdef UNIX
!           stat_T      st;
! 
!           /* Don't delete the file when it's a hard or symbolic link. */
!           if ((!newfile && st_old.st_nlink > 1)
!                   || (mch_lstat((char *)fname, &st) == 0
!                       && (st.st_dev != st_old.st_dev
!                           || st.st_ino != st_old.st_ino)))
!               errmsg = (char_u *)_("E166: Can't open linked file for 
writing");
!           else
  #endif
            {
!               errmsg = (char_u *)_("E212: Can't open file for writing");
!               if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL
!                                                                && perm >= 0)
                {
  #ifdef UNIX
!                   /* we write to the file, thus it should be marked
!                      writable after all */
!                   if (!(perm & 0200))
!                       made_writable = TRUE;
!                   perm |= 0200;
!                   if (st_old.st_uid != getuid() || st_old.st_gid != getgid())
!                       perm &= 0777;
  #endif
!                   if (!append)            /* don't remove when appending */
!                       mch_remove(wfname);
!                   continue;
                }
-           }
-       }
  
  restore_backup:
-       {
-           stat_T      st;
- 
-           /*
-            * If we failed to open the file, we don't need a backup. Throw it
-            * away.  If we moved or removed the original file try to put the
-            * backup in its place.
-            */
-           if (backup != NULL && wfname == fname)
-           {
-               if (backup_copy)
                {
                    /*
!                    * There is a small chance that we removed the original,
!                    * try to move the copy in its place.
!                    * This may not work if the vim_rename() fails.
!                    * In that case we leave the copy around.
                     */
!                   /* If file does not exist, put the copy in its place */
!                   if (mch_stat((char *)fname, &st) < 0)
!                       vim_rename(backup, fname);
!                   /* if original file does exist throw away the copy */
!                   if (mch_stat((char *)fname, &st) >= 0)
!                       mch_remove(backup);
!               }
!               else
!               {
!                   /* try to put the original file back */
!                   vim_rename(backup, fname);
!               }
!           }
  
!           /* if original file no longer exists give an extra warning */
!           if (!newfile && mch_stat((char *)fname, &st) < 0)
!               end = 0;
!       }
  
  #ifdef FEAT_MBYTE
!       if (wfname != fname)
!           vim_free(wfname);
  #endif
!       goto fail;
!     }
!     errmsg = NULL;
  
  #if defined(MACOS_CLASSIC) || defined(WIN3264)
!     /* TODO: Is it need for MACOS_X? (Dany) */
!     /*
!      * On macintosh copy the original files attributes (i.e. the backup)
!      * This is done in order to preserve the resource fork and the
!      * Finder attribute (label, comments, custom icons, file creator)
!      */
!     if (backup != NULL && overwriting && !append)
!     {
!       if (backup_copy)
!           (void)mch_copy_file_attribute(wfname, backup);
!       else
!           (void)mch_copy_file_attribute(backup, wfname);
!     }
  
!     if (!overwriting && !append)
!     {
!       if (buf->b_ffname != NULL)
!           (void)mch_copy_file_attribute(buf->b_ffname, wfname);
!       /* Should copy resource fork */
!     }
  #endif
  
-     write_info.bw_fd = fd;
- 
  #ifdef FEAT_CRYPT
!     if (*buf->b_p_key != NUL && !filtering)
!     {
!       char_u          *header;
!       int             header_len;
  
!       buf->b_cryptstate = crypt_create_for_writing(crypt_get_method_nr(buf),
!                                         buf->b_p_key, &header, &header_len);
!       if (buf->b_cryptstate == NULL || header == NULL)
!           end = 0;
!       else
!       {
!           /* Write magic number, so that Vim knows how this file is
!            * encrypted when reading it back. */
!           write_info.bw_buf = header;
!           write_info.bw_len = header_len;
!           write_info.bw_flags = FIO_NOCONVERT;
!           if (buf_write_bytes(&write_info) == FAIL)
!               end = 0;
!           wb_flags |= FIO_ENCRYPTED;
!           vim_free(header);
!       }
!     }
  #endif
  
!     write_info.bw_buf = buffer;
!     nchars = 0;
  
!     /* use "++bin", "++nobin" or 'binary' */
!     if (eap != NULL && eap->force_bin != 0)
!       write_bin = (eap->force_bin == FORCE_BIN);
!     else
!       write_bin = buf->b_p_bin;
  
  #ifdef FEAT_MBYTE
!     /*
!      * The BOM is written just after the encryption magic number.
!      * Skip it when appending and the file already existed, the BOM only makes
!      * sense at the start of the file.
!      */
!     if (buf->b_p_bomb && !write_bin && (!append || perm < 0))
!     {
!       write_info.bw_len = make_bom(buffer, fenc);
!       if (write_info.bw_len > 0)
        {
!           /* don't convert, do encryption */
!           write_info.bw_flags = FIO_NOCONVERT | wb_flags;
!           if (buf_write_bytes(&write_info) == FAIL)
!               end = 0;
!           else
!               nchars += write_info.bw_len;
        }
!     }
!     write_info.bw_start_lnum = start;
  #endif
  
  #ifdef FEAT_PERSISTENT_UNDO
!     write_undo_file = (buf->b_p_udf && overwriting && !append
!                                             && !filtering && reset_changed);
!     if (write_undo_file)
!       /* Prepare for computing the hash value of the text. */
!       sha256_start(&sha_ctx);
  #endif
  
!     write_info.bw_len = bufsize;
  #ifdef HAS_BW_FLAGS
!     write_info.bw_flags = wb_flags;
  #endif
!     fileformat = get_fileformat_force(buf, eap);
!     s = buffer;
!     len = 0;
!     for (lnum = start; lnum <= end; ++lnum)
!     {
!       /*
!        * The next while loop is done once for each character written.
!        * Keep it fast!
!        */
!       ptr = ml_get_buf(buf, lnum, FALSE) - 1;
  #ifdef FEAT_PERSISTENT_UNDO
!       if (write_undo_file)
!           sha256_update(&sha_ctx, ptr + 1, (UINT32_T)(STRLEN(ptr + 1) + 1));
  #endif
!       while ((c = *++ptr) != NUL)
!       {
!           if (c == NL)
!               *s = NUL;               /* replace newlines with NULs */
!           else if (c == CAR && fileformat == EOL_MAC)
!               *s = NL;                /* Mac: replace CRs with NLs */
!           else
!               *s = c;
!           ++s;
!           if (++len != bufsize)
!               continue;
!           if (buf_write_bytes(&write_info) == FAIL)
            {
!               end = 0;                /* write error: break loop */
!               break;
!           }
!           nchars += bufsize;
!           s = buffer;
!           len = 0;
  #ifdef FEAT_MBYTE
!           write_info.bw_start_lnum = lnum;
  #endif
!       }
!       /* write failed or last line has no EOL: stop here */
!       if (end == 0
!               || (lnum == end
!                   && (write_bin || !buf->b_p_fixeol)
!                   && (lnum == buf->b_no_eol_lnum
!                       || (lnum == buf->b_ml.ml_line_count && !buf->b_p_eol))))
!       {
!           ++lnum;                     /* written the line, count it */
!           no_eol = TRUE;
!           break;
!       }
!       if (fileformat == EOL_UNIX)
!           *s++ = NL;
!       else
!       {
!           *s++ = CAR;                 /* EOL_MAC or EOL_DOS: write CR */
!           if (fileformat == EOL_DOS)  /* write CR-NL */
            {
!               if (++len == bufsize)
                {
!                   if (buf_write_bytes(&write_info) == FAIL)
                    {
!                       end = 0;        /* write error: break loop */
!                       break;
                    }
!                   nchars += bufsize;
!                   s = buffer;
!                   len = 0;
                }
-               *s++ = NL;
            }
!       }
!       if (++len == bufsize && end)
!       {
!           if (buf_write_bytes(&write_info) == FAIL)
            {
!               end = 0;                /* write error: break loop */
!               break;
!           }
!           nchars += bufsize;
!           s = buffer;
!           len = 0;
  
!           ui_breakcheck();
!           if (got_int)
!           {
!               end = 0;                /* Interrupted, break loop */
!               break;
            }
-       }
  #ifdef VMS
!       /*
!        * On VMS there is a problem: newlines get added when writing blocks
!        * at a time. Fix it by writing a line at a time.
!        * This is much slower!
!        * Explanation: VAX/DECC RTL insists that records in some RMS
!        * structures end with a newline (carriage return) character, and if
!        * they don't it adds one.
!        * With other RMS structures it works perfect without this fix.
!        */
!       if (buf->b_fab_rfm == FAB$C_VFC
!               || ((buf->b_fab_rat & (FAB$M_FTN | FAB$M_CR)) != 0))
!       {
!           int b2write;
  
!           buf->b_fab_mrs = (buf->b_fab_mrs == 0
!                   ? MIN(4096, bufsize)
!                   : MIN(buf->b_fab_mrs, bufsize));
  
!           b2write = len;
!           while (b2write > 0)
!           {
!               write_info.bw_len = MIN(b2write, buf->b_fab_mrs);
!               if (buf_write_bytes(&write_info) == FAIL)
                {
!                   end = 0;
!                   break;
                }
!               b2write -= MIN(b2write, buf->b_fab_mrs);
            }
!           write_info.bw_len = bufsize;
            nchars += len;
-           s = buffer;
-           len = 0;
        }
! #endif
!     }
!     if (len > 0 && end > 0)
!     {
!       write_info.bw_len = len;
!       if (buf_write_bytes(&write_info) == FAIL)
!           end = 0;                /* write error */
!       nchars += len;
      }
  
! #if defined(UNIX) && defined(HAVE_FSYNC)
!     /* On many journalling file systems there is a bug that causes both the
!      * original and the backup file to be lost when halting the system right
!      * after writing the file.  That's because only the meta-data is
!      * journalled.  Syncing the file slows down the system, but assures it has
!      * been written to disk and we don't lose it.
!      * For a device do try the fsync() but don't complain if it does not work
!      * (could be a pipe).
!      * If the 'fsync' option is FALSE, don't fsync().  Useful for laptops. */
!     if (p_fs && fsync(fd) != 0 && !device)
      {
!       errmsg = (char_u *)_("E667: Fsync failed");
!       end = 0;
!     }
  #endif
  
  #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
!     /* Probably need to set the security context. */
!     if (!backup_copy)
!       mch_copy_sec(backup, wfname);
  #endif
  
  #ifdef UNIX
!     /* When creating a new file, set its owner/group to that of the original
!      * file.  Get the new device and inode number. */
!     if (backup != NULL && !backup_copy)
!     {
  # ifdef HAVE_FCHOWN
!       stat_T  st;
  
!       /* don't change the owner when it's already OK, some systems remove
!        * permission or ACL stuff */
!       if (mch_stat((char *)wfname, &st) < 0
!               || st.st_uid != st_old.st_uid
!               || st.st_gid != st_old.st_gid)
!       {
!           ignored = fchown(fd, st_old.st_uid, st_old.st_gid);
!           if (perm >= 0)      /* set permission again, may have changed */
!               (void)mch_setperm(wfname, perm);
!       }
  # endif
!       buf_setino(buf);
!     }
!     else if (!buf->b_dev_valid)
!       /* Set the inode when creating a new file. */
!       buf_setino(buf);
  #endif
  
!     if (close(fd) != 0)
!     {
!       errmsg = (char_u *)_("E512: Close failed");
!       end = 0;
!     }
  
  #ifdef UNIX
!     if (made_writable)
!       perm &= ~0200;          /* reset 'w' bit for security reasons */
  #endif
!     if (perm >= 0)            /* set perm. of new file same as old file */
!       (void)mch_setperm(wfname, perm);
  #ifdef HAVE_ACL
!     /*
!      * Probably need to set the ACL before changing the user (can't set the
!      * ACL on a file the user doesn't own).
!      * On Solaris, with ZFS and the aclmode property set to "discard" (the
!      * default), chmod() discards all part of a file's ACL that don't 
represent
!      * the mode of the file.  It's non-trivial for us to discover whether 
we're
!      * in that situation, so we simply always re-set the ACL.
!      */
  # ifndef HAVE_SOLARIS_ZFS_ACL
!     if (!backup_copy)
  # endif
!       mch_set_acl(wfname, acl);
  #endif
  #ifdef FEAT_CRYPT
!     if (buf->b_cryptstate != NULL)
!     {
!       crypt_free_state(buf->b_cryptstate);
!       buf->b_cryptstate = NULL;
!     }
  #endif
  
  #if defined(FEAT_MBYTE) && defined(FEAT_EVAL)
!     if (wfname != fname)
!     {
!       /*
!        * The file was written to a temp file, now it needs to be converted
!        * with 'charconvert' to (overwrite) the output file.
!        */
!       if (end != 0)
        {
!           if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc,
!                                                      wfname, fname) == FAIL)
            {
!               write_info.bw_conv_error = TRUE;
!               end = 0;
            }
        }
-       mch_remove(wfname);
-       vim_free(wfname);
-     }
  #endif
  
      if (end == 0)
      {
        if (errmsg == NULL)
        {
  #ifdef FEAT_MBYTE
--- 4345,4835 ----
  #endif
  
      /*
!      * If conversion is taking place, we may first pretend to write and check
!      * for conversion errors.  Then loop again to write for real.
!      * When not doing conversion this writes for real right away.
!      */
!     for (checking_conversion = TRUE; ; checking_conversion = FALSE)
      {
        /*
!        * There is no need to check conversion when:
!        * - there is no conversion
!        * - we make a backup file, that can be restored in case of conversion
!        *   failure.
         */
! #ifdef FEAT_MBYTE
!       if (!converted || dobackup)
  #endif
+           checking_conversion = FALSE;
+ 
+       if (checking_conversion)
+       {
+           /* Make sure we don't write anything. */
+           fd = -1;
+           write_info.bw_fd = fd;
+       }
+       else
+       {
+           /*
+            * Open the file "wfname" for writing.
+            * We may try to open the file twice: If we can't write to the file
+            * and forceit is TRUE we delete the existing file and try to
+            * create a new one. If this still fails we may have lost the
+            * original file!  (this may happen when the user reached his
+            * quotum for number of files).
+            * Appending will fail if the file does not exist and forceit is
+            * FALSE.
+            */
+           while ((fd = mch_open((char *)wfname, O_WRONLY | O_EXTRA | (append
+                               ? (forceit ? (O_APPEND | O_CREAT) : O_APPEND)
+                               : (O_CREAT | O_TRUNC))
+                               , perm < 0 ? 0666 : (perm & 0777))) < 0)
            {
!               /*
!                * A forced write will try to create a new file if the old one
!                * is still readonly. This may also happen when the directory
!                * is read-only. In that case the mch_remove() will fail.
!                */
!               if (errmsg == NULL)
                {
  #ifdef UNIX
!                   stat_T      st;
! 
!                   /* Don't delete the file when it's a hard or symbolic link.
!                    */
!                   if ((!newfile && st_old.st_nlink > 1)
!                           || (mch_lstat((char *)fname, &st) == 0
!                               && (st.st_dev != st_old.st_dev
!                                   || st.st_ino != st_old.st_ino)))
!                       errmsg = (char_u *)_("E166: Can't open linked file for 
writing");
!                   else
  #endif
!                   {
!                       errmsg = (char_u *)_("E212: Can't open file for 
writing");
!                       if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL
!                                                                 && perm >= 0)
!                       {
! #ifdef UNIX
!                           /* we write to the file, thus it should be marked
!                              writable after all */
!                           if (!(perm & 0200))
!                               made_writable = TRUE;
!                           perm |= 0200;
!                           if (st_old.st_uid != getuid()
!                                                 || st_old.st_gid != getgid())
!                               perm &= 0777;
! #endif
!                           if (!append)  /* don't remove when appending */
!                               mch_remove(wfname);
!                           continue;
!                       }
!                   }
                }
  
  restore_backup:
                {
+                   stat_T      st;
+ 
                    /*
!                    * If we failed to open the file, we don't need a backup.
!                    * Throw it away.  If we moved or removed the original file
!                    * try to put the backup in its place.
                     */
!                   if (backup != NULL && wfname == fname)
!                   {
!                       if (backup_copy)
!                       {
!                           /*
!                            * There is a small chance that we removed the
!                            * original, try to move the copy in its place.
!                            * This may not work if the vim_rename() fails.
!                            * In that case we leave the copy around.
!                            */
!                           /* If file does not exist, put the copy in its
!                            * place */
!                           if (mch_stat((char *)fname, &st) < 0)
!                               vim_rename(backup, fname);
!                           /* if original file does exist throw away the copy
!                            */
!                           if (mch_stat((char *)fname, &st) >= 0)
!                               mch_remove(backup);
!                       }
!                       else
!                       {
!                           /* try to put the original file back */
!                           vim_rename(backup, fname);
!                       }
!                   }
  
!                   /* if original file no longer exists give an extra warning
!                    */
!                   if (!newfile && mch_stat((char *)fname, &st) < 0)
!                       end = 0;
!               }
  
  #ifdef FEAT_MBYTE
!               if (wfname != fname)
!                   vim_free(wfname);
  #endif
!               goto fail;
!           }
!           write_info.bw_fd = fd;
  
  #if defined(MACOS_CLASSIC) || defined(WIN3264)
!           /* TODO: Is it need for MACOS_X? (Dany) */
!           /*
!            * On macintosh copy the original files attributes (i.e. the backup)
!            * This is done in order to preserve the resource fork and the
!            * Finder attribute (label, comments, custom icons, file creator)
!            */
!           if (backup != NULL && overwriting && !append)
!           {
!               if (backup_copy)
!                   (void)mch_copy_file_attribute(wfname, backup);
!               else
!                   (void)mch_copy_file_attribute(backup, wfname);
!           }
  
!           if (!overwriting && !append)
!           {
!               if (buf->b_ffname != NULL)
!                   (void)mch_copy_file_attribute(buf->b_ffname, wfname);
!               /* Should copy resource fork */
!           }
  #endif
  
  #ifdef FEAT_CRYPT
!           if (*buf->b_p_key != NUL && !filtering)
!           {
!               char_u          *header;
!               int             header_len;
  
!               buf->b_cryptstate = crypt_create_for_writing(
!                                                     crypt_get_method_nr(buf),
!                                          buf->b_p_key, &header, &header_len);
!               if (buf->b_cryptstate == NULL || header == NULL)
!                   end = 0;
!               else
!               {
!                   /* Write magic number, so that Vim knows how this file is
!                    * encrypted when reading it back. */
!                   write_info.bw_buf = header;
!                   write_info.bw_len = header_len;
!                   write_info.bw_flags = FIO_NOCONVERT;
!                   if (buf_write_bytes(&write_info) == FAIL)
!                       end = 0;
!                   wb_flags |= FIO_ENCRYPTED;
!                   vim_free(header);
!               }
!           }
  #endif
+       }
+       errmsg = NULL;
  
!       write_info.bw_buf = buffer;
!       nchars = 0;
  
!       /* use "++bin", "++nobin" or 'binary' */
!       if (eap != NULL && eap->force_bin != 0)
!           write_bin = (eap->force_bin == FORCE_BIN);
!       else
!           write_bin = buf->b_p_bin;
  
  #ifdef FEAT_MBYTE
!       /*
!        * The BOM is written just after the encryption magic number.
!        * Skip it when appending and the file already existed, the BOM only
!        * makes sense at the start of the file.
!        */
!       if (buf->b_p_bomb && !write_bin && (!append || perm < 0))
        {
!           write_info.bw_len = make_bom(buffer, fenc);
!           if (write_info.bw_len > 0)
!           {
!               /* don't convert, do encryption */
!               write_info.bw_flags = FIO_NOCONVERT | wb_flags;
!               if (buf_write_bytes(&write_info) == FAIL)
!                   end = 0;
!               else
!                   nchars += write_info.bw_len;
!           }
        }
!       write_info.bw_start_lnum = start;
  #endif
  
  #ifdef FEAT_PERSISTENT_UNDO
!       write_undo_file = (buf->b_p_udf
!                           && overwriting
!                           && !append
!                           && !filtering
!                           && reset_changed
!                           && !checking_conversion);
!       if (write_undo_file)
!           /* Prepare for computing the hash value of the text. */
!           sha256_start(&sha_ctx);
  #endif
  
!       write_info.bw_len = bufsize;
  #ifdef HAS_BW_FLAGS
!       write_info.bw_flags = wb_flags;
  #endif
!       fileformat = get_fileformat_force(buf, eap);
!       s = buffer;
!       len = 0;
!       for (lnum = start; lnum <= end; ++lnum)
!       {
!           /*
!            * The next while loop is done once for each character written.
!            * Keep it fast!
!            */
!           ptr = ml_get_buf(buf, lnum, FALSE) - 1;
  #ifdef FEAT_PERSISTENT_UNDO
!           if (write_undo_file)
!               sha256_update(&sha_ctx, ptr + 1,
!                                             (UINT32_T)(STRLEN(ptr + 1) + 1));
  #endif
!           while ((c = *++ptr) != NUL)
            {
!               if (c == NL)
!                   *s = NUL;           /* replace newlines with NULs */
!               else if (c == CAR && fileformat == EOL_MAC)
!                   *s = NL;            /* Mac: replace CRs with NLs */
!               else
!                   *s = c;
!               ++s;
!               if (++len != bufsize)
!                   continue;
!               if (buf_write_bytes(&write_info) == FAIL)
!               {
!                   end = 0;            /* write error: break loop */
!                   break;
!               }
!               nchars += bufsize;
!               s = buffer;
!               len = 0;
  #ifdef FEAT_MBYTE
!               write_info.bw_start_lnum = lnum;
  #endif
!           }
!           /* write failed or last line has no EOL: stop here */
!           if (end == 0
!                   || (lnum == end
!                       && (write_bin || !buf->b_p_fixeol)
!                       && (lnum == buf->b_no_eol_lnum
!                           || (lnum == buf->b_ml.ml_line_count
!                                                          && !buf->b_p_eol))))
!           {
!               ++lnum;                 /* written the line, count it */
!               no_eol = TRUE;
!               break;
!           }
!           if (fileformat == EOL_UNIX)
!               *s++ = NL;
!           else
            {
!               *s++ = CAR;                 /* EOL_MAC or EOL_DOS: write CR */
!               if (fileformat == EOL_DOS)  /* write CR-NL */
                {
!                   if (++len == bufsize)
                    {
!                       if (buf_write_bytes(&write_info) == FAIL)
!                       {
!                           end = 0;    /* write error: break loop */
!                           break;
!                       }
!                       nchars += bufsize;
!                       s = buffer;
!                       len = 0;
                    }
!                   *s++ = NL;
                }
            }
!           if (++len == bufsize && end)
            {
!               if (buf_write_bytes(&write_info) == FAIL)
!               {
!                   end = 0;            /* write error: break loop */
!                   break;
!               }
!               nchars += bufsize;
!               s = buffer;
!               len = 0;
  
!               ui_breakcheck();
!               if (got_int)
!               {
!                   end = 0;            /* Interrupted, break loop */
!                   break;
!               }
            }
  #ifdef VMS
!           /*
!            * On VMS there is a problem: newlines get added when writing
!            * blocks at a time. Fix it by writing a line at a time.
!            * This is much slower!
!            * Explanation: VAX/DECC RTL insists that records in some RMS
!            * structures end with a newline (carriage return) character, and
!            * if they don't it adds one.
!            * With other RMS structures it works perfect without this fix.
!            */
!           if (buf->b_fab_rfm == FAB$C_VFC
!                   || ((buf->b_fab_rat & (FAB$M_FTN | FAB$M_CR)) != 0))
!           {
!               int b2write;
  
!               buf->b_fab_mrs = (buf->b_fab_mrs == 0
!                       ? MIN(4096, bufsize)
!                       : MIN(buf->b_fab_mrs, bufsize));
  
!               b2write = len;
!               while (b2write > 0)
                {
!                   write_info.bw_len = MIN(b2write, buf->b_fab_mrs);
!                   if (buf_write_bytes(&write_info) == FAIL)
!                   {
!                       end = 0;
!                       break;
!                   }
!                   b2write -= MIN(b2write, buf->b_fab_mrs);
                }
!               write_info.bw_len = bufsize;
!               nchars += len;
!               s = buffer;
!               len = 0;
            }
! #endif
!       }
!       if (len > 0 && end > 0)
!       {
!           write_info.bw_len = len;
!           if (buf_write_bytes(&write_info) == FAIL)
!               end = 0;                    /* write error */
            nchars += len;
        }
! 
!       /* Stop when writing done or an error was encountered. */
!       if (!checking_conversion || end == 0)
!           break;
! 
!       /* If no error happened until now, writing should be ok, so loop to
!        * really write the buffer. */
      }
  
!     /* If we started writing, finish writing. Also when an error was
!      * encountered. */
!     if (!checking_conversion)
      {
! #if defined(UNIX) && defined(HAVE_FSYNC)
!       /*
!        * On many journalling file systems there is a bug that causes both the
!        * original and the backup file to be lost when halting the system
!        * right after writing the file.  That's because only the meta-data is
!        * journalled.  Syncing the file slows down the system, but assures it
!        * has been written to disk and we don't lose it.
!        * For a device do try the fsync() but don't complain if it does not
!        * work (could be a pipe).
!        * If the 'fsync' option is FALSE, don't fsync().  Useful for laptops.
!        */
!       if (p_fs && fsync(fd) != 0 && !device)
!       {
!           errmsg = (char_u *)_("E667: Fsync failed");
!           end = 0;
!       }
  #endif
  
  #if defined(HAVE_SELINUX) || defined(HAVE_SMACK)
!       /* Probably need to set the security context. */
!       if (!backup_copy)
!           mch_copy_sec(backup, wfname);
  #endif
  
  #ifdef UNIX
!       /* When creating a new file, set its owner/group to that of the
!        * original file.  Get the new device and inode number. */
!       if (backup != NULL && !backup_copy)
!       {
  # ifdef HAVE_FCHOWN
!           stat_T      st;
  
!           /* don't change the owner when it's already OK, some systems remove
!            * permission or ACL stuff */
!           if (mch_stat((char *)wfname, &st) < 0
!                   || st.st_uid != st_old.st_uid
!                   || st.st_gid != st_old.st_gid)
!           {
!               ignored = fchown(fd, st_old.st_uid, st_old.st_gid);
!               if (perm >= 0)  /* set permission again, may have changed */
!                   (void)mch_setperm(wfname, perm);
!           }
  # endif
!           buf_setino(buf);
!       }
!       else if (!buf->b_dev_valid)
!           /* Set the inode when creating a new file. */
!           buf_setino(buf);
  #endif
  
!       if (close(fd) != 0)
!       {
!           errmsg = (char_u *)_("E512: Close failed");
!           end = 0;
!       }
  
  #ifdef UNIX
!       if (made_writable)
!           perm &= ~0200;      /* reset 'w' bit for security reasons */
  #endif
!       if (perm >= 0)          /* set perm. of new file same as old file */
!           (void)mch_setperm(wfname, perm);
  #ifdef HAVE_ACL
!       /*
!        * Probably need to set the ACL before changing the user (can't set the
!        * ACL on a file the user doesn't own).
!        * On Solaris, with ZFS and the aclmode property set to "discard" (the
!        * default), chmod() discards all part of a file's ACL that don't
!        * represent the mode of the file.  It's non-trivial for us to discover
!        * whether we're in that situation, so we simply always re-set the ACL.
!        */
  # ifndef HAVE_SOLARIS_ZFS_ACL
!       if (!backup_copy)
  # endif
!           mch_set_acl(wfname, acl);
  #endif
  #ifdef FEAT_CRYPT
!       if (buf->b_cryptstate != NULL)
!       {
!           crypt_free_state(buf->b_cryptstate);
!           buf->b_cryptstate = NULL;
!       }
  #endif
  
  #if defined(FEAT_MBYTE) && defined(FEAT_EVAL)
!       if (wfname != fname)
        {
!           /*
!            * The file was written to a temp file, now it needs to be
!            * converted with 'charconvert' to (overwrite) the output file.
!            */
!           if (end != 0)
            {
!               if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc,
!                                                 fenc, wfname, fname) == FAIL)
!               {
!                   write_info.bw_conv_error = TRUE;
!                   end = 0;
!               }
            }
+           mch_remove(wfname);
+           vim_free(wfname);
        }
  #endif
+     }
  
      if (end == 0)
      {
+       /*
+        * Error encountered.
+        */
        if (errmsg == NULL)
        {
  #ifdef FEAT_MBYTE
***************
*** 5690,5695 ****
--- 5749,5758 ----
      }
  #endif /* FEAT_MBYTE */
  
+     if (ip->bw_fd < 0)
+       /* Only checking conversion, which is OK if we get here. */
+       return OK;
+ 
  #ifdef FEAT_CRYPT
      if (flags & FIO_ENCRYPTED)
      {
*** ../vim-8.0.0684/src/testdir/test_writefile.vim      2017-06-13 
19:38:33.301791799 +0200
--- src/testdir/test_writefile.vim      2017-06-27 21:22:55.749684062 +0200
***************
*** 31,33 ****
--- 31,51 ----
  
    call assert_fails('call writefile([], [])', 'E730:')
  endfunc
+ 
+ func Test_writefile_fails_conversion()
+   if !has('multi_byte') || !has('iconv')
+     return
+   endif
+   set nobackup nowritebackup
+   new
+   let contents = ["line one", "line two"]
+   call writefile(contents, 'Xfile')
+   edit Xfile
+   call setline(1, ["first line", "cannot convert \u010b", "third line"])
+   call assert_fails('write ++enc=cp932')
+   call assert_equal(contents, readfile('Xfile'))
+ 
+   call delete('Xfile')
+   bwipe!
+   set backup& writebackup&
+ endfunc
*** ../vim-8.0.0684/src/version.c       2017-06-27 18:28:52.558196805 +0200
--- src/version.c       2017-06-27 22:11:05.711318535 +0200
***************
*** 766,767 ****
--- 766,769 ----
  {   /* Add new patch number below this line */
+ /**/
+     685,
  /**/

-- 
>From "know your smileys":
 =):-)  Uncle Sam

 /// Bram Moolenaar -- [email protected] -- http://www.Moolenaar.net   \\\
///        sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\
\\\  an exciting new programming language -- http://www.Zimbu.org        ///
 \\\            help me help AIDS victims -- http://ICCF-Holland.org    ///

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Raspunde prin e-mail lui