diff --git a/src/Makefile b/src/Makefile index 981e42a2b8..f06f6033be 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2106,8 +2106,8 @@ test1 \ test50 test51 test52 test53 test54 test55 test56 test57 test59 \ test60 test64 test66 test67 test68 test69 \ test70 test72 test73 test74 test75 test77 test78 test79 \ - test80 test82 test83 test84 test85 test86 test87 test88 \ - test90 test91 test94 test95 test97 test98 test99 \ + test80 test83 test84 test85 test86 test87 test88 \ + test91 test94 test95 test97 test98 test99 \ test100 test101 test103 test104 test107 test108: cd testdir; rm -f $@.out; $(MAKE) -f Makefile $@.out VIMPROG=../$(VIMTARGET) $(GUI_TESTARG) SCRIPTSOURCE=../$(SCRIPTSOURCE) @@ -2224,6 +2224,7 @@ test_arglist \ test_search \ test_searchpos \ test_set \ + test_sha256 \ test_signs \ test_smartindent \ test_sort \ @@ -2250,6 +2251,7 @@ test_arglist \ test_unlet \ test_usercommands \ test_utf8 \ + test_utf8_comparisons \ test_viminfo \ test_vimscript \ test_visual \ diff --git a/src/ex_cmds2.c b/src/ex_cmds2.c index c64aeabaa9..9afe8477d1 100644 --- a/src/ex_cmds2.c +++ b/src/ex_cmds2.c @@ -3358,19 +3358,6 @@ source_callback(char_u *fname, void *cookie UNUSED) (void)do_source(fname, FALSE, DOSO_NONE); } -/* - * Source the file "name" from all directories in 'runtimepath'. - * "name" can contain wildcards. - * When "flags" has DIP_ALL: source all files, otherwise only the first one. - * - * return FAIL when no file could be sourced, OK otherwise. - */ - int -source_runtime(char_u *name, int flags) -{ - return do_in_runtimepath(name, flags, source_callback, NULL); -} - /* * Find the file "name" in all directories in "path" and invoke * "callback(fname, cookie)". @@ -3508,18 +3495,19 @@ do_in_path( } /* - * Find "name" in 'runtimepath'. When found, invoke the callback function for + * Find "name" in "path". When found, invoke the callback function for * it: callback(fname, "cookie") * When "flags" has DIP_ALL repeat for all matches, otherwise only the first * one is used. * Returns OK when at least one match found, FAIL otherwise. * - * If "name" is NULL calls callback for each entry in runtimepath. Cookie is + * If "name" is NULL calls callback for each entry in "path". Cookie is * passed by reference in this case, setting it to NULL indicates that callback * has done its job. */ - int -do_in_runtimepath( + static int +do_in_path_and_pp( + char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), @@ -3532,7 +3520,7 @@ do_in_runtimepath( char *opt_dir = "pack/*/opt/*/%s"; if ((flags & DIP_NORTP) == 0) - done = do_in_path(p_rtp, name, flags, callback, cookie); + done = do_in_path(path, name, flags, callback, cookie); if ((done == FAIL || (flags & DIP_ALL)) && (flags & DIP_START)) { @@ -3559,6 +3547,42 @@ do_in_runtimepath( return done; } +/* + * Just like do_in_path_and_pp(), using 'runtimepath' for "path". + */ + int +do_in_runtimepath( + char_u *name, + int flags, + void (*callback)(char_u *fname, void *ck), + void *cookie) +{ + return do_in_path_and_pp(p_rtp, name, flags, callback, cookie); +} + +/* + * Source the file "name" from all directories in 'runtimepath'. + * "name" can contain wildcards. + * When "flags" has DIP_ALL: source all files, otherwise only the first one. + * + * return FAIL when no file could be sourced, OK otherwise. + */ + int +source_runtime(char_u *name, int flags) +{ + return source_in_path(p_rtp, name, flags); +} + +/* + * Just like source_runtime(), but use "path" instead of 'runtimepath'. + */ + int +source_in_path(char_u *path, char_u *name, int flags) +{ + return do_in_path_and_pp(path, name, flags, source_callback, NULL); +} + + /* * Expand wildcards in "pat" and invoke do_source() for each match. */ diff --git a/src/fileio.c b/src/fileio.c index 1a06f35825..c42afbfe88 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -3166,6 +3166,7 @@ buf_write( 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,433 +4345,491 @@ buf_write( #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. + * 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. */ - 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) + for (checking_conversion = TRUE; ; checking_conversion = FALSE) { /* - * 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. + * 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. */ - 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 +#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) { - errmsg = (char_u *)_("E212: Can't open file for writing"); - if (forceit && vim_strchr(p_cpo, CPO_FWRITE) == NULL - && perm >= 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 - /* 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; + 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 - if (!append) /* don't remove when appending */ - mch_remove(wfname); - continue; + { + 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) { + stat_T st; + /* - * 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 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 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 (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; - } + /* 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); + if (wfname != fname) + vim_free(wfname); #endif - goto fail; - } - errmsg = NULL; + 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); - } + /* 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 */ - } + 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) + if (*buf->b_p_key != NUL && !filtering) { - 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) + 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; /* write error: break loop */ - break; - } - nchars += bufsize; - s = buffer; - len = 0; + end = 0; + wb_flags |= FIO_ENCRYPTED; + vim_free(header); } - *s++ = NL; } +#endif } - if (++len == bufsize && end) - { - if (buf_write_bytes(&write_info) == FAIL) - { - end = 0; /* write error: break loop */ - break; - } - nchars += bufsize; - s = buffer; - len = 0; + errmsg = NULL; - ui_breakcheck(); - if (got_int) - { - end = 0; /* Interrupted, break loop */ - break; - } - } -#ifdef VMS + 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 /* - * 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. + * 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_fab_rfm == FAB$C_VFC - || ((buf->b_fab_rat & (FAB$M_FTN | FAB$M_CR)) != 0)) + if (buf->b_p_bomb && !write_bin && (!append || perm < 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 = make_bom(buffer, fenc); + if (write_info.bw_len > 0) { - write_info.bw_len = MIN(b2write, buf->b_fab_mrs); + /* 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; + end = 0; /* write error: break loop */ break; } - b2write -= MIN(b2write, buf->b_fab_mrs); - } - write_info.bw_len = bufsize; - nchars += len; - s = buffer; - len = 0; - } + nchars += bufsize; + s = buffer; + len = 0; +#ifdef FEAT_MBYTE + write_info.bw_start_lnum = lnum; #endif - } - if (len > 0 && end > 0) - { - write_info.bw_len = len; - if (buf_write_bytes(&write_info) == FAIL) - end = 0; /* write error */ - nchars += len; + } + /* 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 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) + /* If we started writing, finish writing. Also when an error was + * encountered. */ + if (!checking_conversion) { - errmsg = (char_u *)_("E667: Fsync failed"); - end = 0; - } +#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); + /* 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) + /* 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) { - 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); - } +# 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); + 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; - } + 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 */ + 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); + 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. - */ + /* + * 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) + if (!backup_copy) # endif - mch_set_acl(wfname, acl); + mch_set_acl(wfname, acl); #endif #ifdef FEAT_CRYPT - if (buf->b_cryptstate != NULL) - { - crypt_free_state(buf->b_cryptstate); - buf->b_cryptstate = NULL; - } + 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 (wfname != fname) { - if (eval_charconvert(enc_utf8 ? (char_u *)"utf-8" : p_enc, fenc, - wfname, fname) == FAIL) + /* + * The file was written to a temp file, now it needs to be + * converted with 'charconvert' to (overwrite) the output file. + */ + if (end != 0) { - write_info.bw_conv_error = TRUE; - 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); } - mch_remove(wfname); - vim_free(wfname); - } #endif + } if (end == 0) { + /* + * Error encountered. + */ if (errmsg == NULL) { #ifdef FEAT_MBYTE @@ -5694,6 +5753,10 @@ buf_write_bytes(struct bw_info *ip) } #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) { diff --git a/src/main.c b/src/main.c index 01d54d2dff..b903360202 100644 --- a/src/main.c +++ b/src/main.c @@ -472,18 +472,28 @@ vim_main2(void) */ if (p_lpl) { + char_u *rtp_copy = NULL; + /* First add all package directories to 'runtimepath', so that their * autoload directories can be found. Only if not done already with a - * :packloadall command. */ + * :packloadall command. + * Make a copy of 'runtimepath', so that source_runtime does not use + * the pack directories. */ if (!did_source_packages) + { + rtp_copy = vim_strsave(p_rtp); add_pack_start_dirs(); + } + source_in_path(rtp_copy == NULL ? p_rtp : rtp_copy, # ifdef VMS /* Somehow VMS doesn't handle the "**". */ - source_runtime((char_u *)"plugin/*.vim", DIP_ALL | DIP_NOAFTER); + (char_u *)"plugin/*.vim", # else - source_runtime((char_u *)"plugin/**/*.vim", DIP_ALL | DIP_NOAFTER); + (char_u *)"plugin/**/*.vim", # endif + DIP_ALL | DIP_NOAFTER); TIME_MSG("loading plugins"); + vim_free(rtp_copy); /* Only source "start" packages if not done already with a :packloadall * command. */ diff --git a/src/misc1.c b/src/misc1.c index 41972547c9..3802f577a1 100644 --- a/src/misc1.c +++ b/src/misc1.c @@ -3691,16 +3691,30 @@ vim_beep( { if (!((bo_flags & val) || (bo_flags & BO_ALL))) { - if (p_vb -#ifdef FEAT_GUI - /* While the GUI is starting up the termcap is set for the - * GUI but the output still goes to a terminal. */ - && !(gui.in_use && gui.starting) +#ifdef ELAPSED_FUNC + static int did_init = FALSE; + static ELAPSED_TYPE start_tv; + + /* Only beep once per half a second, otherwise a sequence of beeps + * would freeze Vim. */ + if (!did_init || ELAPSED_FUNC(start_tv) > 500) + { + did_init = TRUE; + ELAPSED_INIT(start_tv); +#endif + if (p_vb +#ifdef FEAT_GUI + /* While the GUI is starting up the termcap is set for + * the GUI but the output still goes to a terminal. */ + && !(gui.in_use && gui.starting) +#endif + ) + out_str_cf(T_VB); + else + out_char(BELL); +#ifdef ELAPSED_FUNC + } #endif - ) - out_str(T_VB); - else - out_char(BELL); } /* When 'verbose' is set and we are sourcing a script or executing a diff --git a/src/ops.c b/src/ops.c index 7cbc0c22cb..2aec5d4383 100644 --- a/src/ops.c +++ b/src/ops.c @@ -1636,7 +1636,9 @@ shift_delete_registers() free_yank_all(); /* free register nine */ for (n = 9; n > 1; --n) y_regs[n] = y_regs[n - 1]; - y_previous = y_current = &y_regs[1]; + y_current = &y_regs[1]; + if (!y_append) + y_previous = y_current; y_regs[1].y_array = NULL; /* set register one to empty */ } diff --git a/src/proto/ex_cmds2.pro b/src/proto/ex_cmds2.pro index e8d2814089..c8730393d3 100644 --- a/src/proto/ex_cmds2.pro +++ b/src/proto/ex_cmds2.pro @@ -69,9 +69,10 @@ void ex_argdelete(exarg_T *eap); void ex_listdo(exarg_T *eap); void ex_compiler(exarg_T *eap); void ex_runtime(exarg_T *eap); -int source_runtime(char_u *name, int flags); int do_in_path(char_u *path, char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie); int do_in_runtimepath(char_u *name, int flags, void (*callback)(char_u *fname, void *ck), void *cookie); +int source_runtime(char_u *name, int flags); +int source_in_path(char_u *path, char_u *name, int flags); void add_pack_start_dirs(void); void load_start_packages(void); void ex_packloadall(exarg_T *eap); diff --git a/src/proto/term.pro b/src/proto/term.pro index 7d9fc6ae71..9af725ff83 100644 --- a/src/proto/term.pro +++ b/src/proto/term.pro @@ -16,6 +16,7 @@ void out_flush_check(void); void out_trash(void); void out_char(unsigned c); void out_str_nf(char_u *s); +void out_str_cf(char_u *s); void out_str(char_u *s); void term_windgoto(int row, int col); void term_cursor_right(int i); diff --git a/src/term.c b/src/term.c index 4e9f91d5c6..7c25fae349 100644 --- a/src/term.c +++ b/src/term.c @@ -2519,6 +2519,75 @@ out_str_nf(char_u *s) } #endif +/* + * A conditional-flushing out_str, mainly for visualbell. + * Handles a delay internally, because termlib may not respect the delay or do + * it at the wrong time. + * Note: Only for terminal strings. + */ + void +out_str_cf(char_u *s) +{ + if (s != NULL && *s) + { + char_u *p; + +#ifdef FEAT_GUI + /* Don't use tputs() when GUI is used, ncurses crashes. */ + if (gui.in_use) + { + out_str_nf(s); + return; + } +#endif + if (out_pos > OUT_SIZE - 20) + out_flush(); +#ifdef HAVE_TGETENT + for (p = s; *s; ++s) + { + /* flush just before delay command */ + if (*s == '$' && *(s + 1) == '<') + { + char_u save_c = *s; + int duration = atoi((char *)s + 2); + + *s = NUL; + tputs((char *)p, 1, TPUTSFUNCAST out_char_nf); + *s = save_c; + out_flush(); +#ifdef ELAPSED_FUNC + /* Only sleep here if we can limit this happening in + * vim_beep(). */ + p = vim_strchr(s, '>'); + if (p == NULL || duration <= 0) + { + /* can't parse the time, don't sleep here */ + p = s; + } + else + { + ++p; + do_sleep(duration); + } +#else + /* Rely on the terminal library to sleep. */ + p = s; +#endif + break; + } + } + tputs((char *)p, 1, TPUTSFUNCAST out_char_nf); +#else + while (*s) + out_char_nf(*s++); +#endif + + /* For testing we write one string at a time. */ + if (p_wd) + out_flush(); + } +} + /* * out_str(s): Put a character string a byte at a time into the output buffer. * If HAVE_TGETENT is defined use the termcap parser. (jw) diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 1dedf64170..9a3cafd372 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -60,10 +60,8 @@ SCRIPTS_ALL = \ test77.out \ test79.out \ test80.out \ - test82.out \ test84.out \ test88.out \ - test90.out \ test91.out \ test94.out \ test95.out \ diff --git a/src/testdir/Make_vms.mms b/src/testdir/Make_vms.mms index 3392f07c24..5f124afc27 100644 --- a/src/testdir/Make_vms.mms +++ b/src/testdir/Make_vms.mms @@ -91,8 +91,8 @@ SCRIPT = test1.out test3.out test4.out test5.out \ test66.out test67.out test68.out test69.out \ test72.out test75.out \ test77a.out test78.out test79.out test80.out \ - test82.out test84.out test88.out \ - test90.out test91.out test94.out \ + test84.out test88.out \ + test91.out test94.out \ test95.out test98.out test99.out \ test103.out test104.out \ test107.out test108.out\ diff --git a/src/testdir/test82.in b/src/testdir/test82.in deleted file mode 100644 index 5ee9116685..0000000000 --- a/src/testdir/test82.in +++ /dev/null @@ -1,103 +0,0 @@ -Tests for case-insensitive UTF-8 comparisons (utf_strnicmp() in mbyte.c) -Also test "g~ap". - -STARTTEST -:so small.vim -:if !has("multi_byte") -: e! test.ok -: w! test.out -: qa! -:endif -:set enc=utf8 -ggdG: -: -:function! Ch(a, op, b, expected) -: if eval(printf('"%s" %s "%s"', a:a, a:op, a:b)) != a:expected -: call append(line('$'), printf('"%s" %s "%s" should return %d', a:a, a:op, a:b, a:expected)) -: else -: let b:passed += 1 -: endif -:endfunction -: -:function! Chk(a, b, result) -: if a:result == 0 -: call Ch(a:a, '==?', a:b, 1) -: call Ch(a:a, '!=?', a:b, 0) -: call Ch(a:a, '<=?', a:b, 1) -: call Ch(a:a, '>=?', a:b, 1) -: call Ch(a:a, '?', a:b, 0) -: elseif a:result > 0 -: call Ch(a:a, '==?', a:b, 0) -: call Ch(a:a, '!=?', a:b, 1) -: call Ch(a:a, '<=?', a:b, 0) -: call Ch(a:a, '>=?', a:b, 1) -: call Ch(a:a, '?', a:b, 1) -: else -: call Ch(a:a, '==?', a:b, 0) -: call Ch(a:a, '!=?', a:b, 1) -: call Ch(a:a, '<=?', a:b, 1) -: call Ch(a:a, '>=?', a:b, 0) -: call Ch(a:a, '?', a:b, 0) -: endif -:endfunction -: -:function! Check(a, b, result) -: call Chk(a:a, a:b, a:result) -: call Chk(a:b, a:a, -a:result) -:endfunction -: -:function! LT(a, b) -: call Check(a:a, a:b, -1) -:endfunction -: -:function! GT(a, b) -: call Check(a:a, a:b, 1) -:endfunction -: -:function! EQ(a, b) -: call Check(a:a, a:b, 0) -:endfunction -: -:let b:passed=0 -:call EQ('', '') -:call LT('', 'a') -:call EQ('abc', 'abc') -:call EQ('Abc', 'abC') -:call LT('ab', 'abc') -:call LT('AB', 'abc') -:call LT('ab', 'aBc') -:call EQ('\xd0\xb9\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd', '\xd0\xb9\xd0\xa6\xd0\xa3\xd0\xba\xd0\x95\xd0\xbd') -:call LT('\xd0\xb9\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd', '\xd0\xaf\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd') -:call EQ('\xe2\x84\xaa', 'k') -:call LT('\xe2\x84\xaa', 'kkkkkk') -:call EQ('\xe2\x84\xaa\xe2\x84\xaa\xe2\x84\xaa', 'kkk') -:call LT('kk', '\xe2\x84\xaa\xe2\x84\xaa\xe2\x84\xaa') -:call EQ('\xe2\x84\xaa\xe2\x84\xa6k\xe2\x84\xaak\xcf\x89', 'k\xcf\x89\xe2\x84\xaakk\xe2\x84\xa6') -:call EQ('Abc\x80', 'AbC\x80') -:call LT('Abc\x80', 'AbC\x81') -:call LT('Abc', 'AbC\x80') -:call LT('abc\x80DEF', 'abc\x80def') " case folding stops at the first bad character -:call LT('\xc3XYZ', '\xc3xyz') -:call EQ('\xef\xbc\xba', '\xef\xbd\x9a') " FF3A (upper), FF5A (lower) -:call GT('\xef\xbc\xba', '\xef\xbc\xff') " first string is ok and equals \xef\xbd\x9a after folding, second string is illegal and was left unchanged, then the strings were bytewise compared -:call LT('\xc3', '\xc3\x83') -:call EQ('\xc3\xa3xYz', '\xc3\x83XyZ') -:for n in range(0x60, 0xFF) | call LT(printf('xYz\x%.2X', n-1), printf('XyZ\x%.2X', n)) | endfor -:for n in range(0x80, 0xBF) | call EQ(printf('xYz\xc2\x%.2XUvW', n), printf('XyZ\xc2\x%.2XuVw', n)) | endfor -:for n in range(0xC0, 0xFF) | call LT(printf('xYz\xc2\x%.2XUvW', n), printf('XyZ\xc2\x%.2XuVw', n)) | endfor -:call append(0, printf('%d checks passed', b:passed)) -:" -:" test that g~ap changes one paragraph only. -:new -iabcd - -defggg0g~ap:let lns = getline(1,3) -:q! -:call append(line('$'), lns) -:" -:wq! test.out -ENDTEST - diff --git a/src/testdir/test82.ok b/src/testdir/test82.ok deleted file mode 100644 index 3f1866a0fb..0000000000 --- a/src/testdir/test82.ok +++ /dev/null @@ -1,5 +0,0 @@ -3732 checks passed - -ABCD - -defg diff --git a/src/testdir/test90.in b/src/testdir/test90.in deleted file mode 100644 index 27a87c617c..0000000000 --- a/src/testdir/test90.in +++ /dev/null @@ -1,53 +0,0 @@ -Tests for sha256() function. vim: set ft=vim et ts=2 sw=2 : - -STARTTEST -:so small.vim -:if !has('cryptv') || !exists('*sha256') - e! test.ok - wq! test.out -:endif -:" -:let testcase='test for empty string: ' -:if sha256("") ==# 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855' -: let res='ok' -:else -: let res='ng' -:endif -:$put =testcase.res -:" -:let testcase='test for 1 char: ' -:if sha256("a") ==# 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb' -: let res='ok' -:else -: let res='ng' -:endif -:$put =testcase.res -:" -:let testcase='test for 3 chars: ' -:if sha256("abc") ==# 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad' -: let res='ok' -:else -: let res='ng' -:endif -:$put =testcase.res -:" -:let testcase='test for contains meta char: ' -:if sha256("foo\nbar") ==# '807eff6267f3f926a21d234f7b0cf867a86f47e07a532f15e8cc39ed110ca776' -: let res='ok' -:else -: let res='ng' -:endif -:$put =testcase.res -:" -:let testcase='test for contains non-ascii char: ' -:if sha256("\xde\xad\xbe\xef") ==# '5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953' -: let res='ok' -:else -: let res='ng' -:endif -:$put =testcase.res -:" -:/^start:/,$wq! test.out -ENDTEST - -start: diff --git a/src/testdir/test90.ok b/src/testdir/test90.ok deleted file mode 100644 index 9a8e7fe961..0000000000 --- a/src/testdir/test90.ok +++ /dev/null @@ -1,6 +0,0 @@ -start: -test for empty string: ok -test for 1 char: ok -test for 3 chars: ok -test for contains meta char: ok -test for contains non-ascii char: ok diff --git a/src/testdir/test_put.vim b/src/testdir/test_put.vim index 38c812bc9c..18c7f4e64d 100644 --- a/src/testdir/test_put.vim +++ b/src/testdir/test_put.vim @@ -34,3 +34,14 @@ func Test_put_char_block2() bw! call setreg('a', a[0], a[1]) endfunc + +func Test_put_lines() + new + let a = [ getreg('a'), getregtype('a') ] + call setline(1, ['Line 1', 'Line2', 'Line 3', '']) + exe 'norm! gg"add"AddG""p' + call assert_equal(['Line 3', '', 'Line 1', 'Line2'], getline(1,'$')) + " clean up + bw! + call setreg('a', a[0], a[1]) +endfunc diff --git a/src/testdir/test_sha256.vim b/src/testdir/test_sha256.vim new file mode 100644 index 0000000000..dd4707977e --- /dev/null +++ b/src/testdir/test_sha256.vim @@ -0,0 +1,22 @@ +" Tests for the sha256() function. + +if !has('cryptv') || !exists('*sha256') + finish +endif + +function Test_sha256() + " test for empty string: + call assert_equal(sha256(""), 'e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855') + + "'test for 1 char: + call assert_equal(sha256("a"), 'ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb') + " + "test for 3 chars: + call assert_equal(sha256("abc"), 'ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad') + + " test for contains meta char: + call assert_equal(sha256("foo\nbar"), '807eff6267f3f926a21d234f7b0cf867a86f47e07a532f15e8cc39ed110ca776') + + " test for contains non-ascii char: + call assert_equal(sha256("\xde\xad\xbe\xef"), '5f78c33274e43fa9de5659265c1d917e25c03722dcb0b8d27db8d5feaa813953') +endfunction diff --git a/src/testdir/test_startup.vim b/src/testdir/test_startup.vim index 08a107e815..af2b3c1d4f 100644 --- a/src/testdir/test_startup.vim +++ b/src/testdir/test_startup.vim @@ -23,28 +23,34 @@ func Test_after_comes_later() \ 'set guioptions+=M', \ 'let $HOME = "/does/not/exist"', \ 'set loadplugins', - \ 'set rtp=Xhere,Xafter', + \ 'set rtp=Xhere,Xafter,Xanother', \ 'set packpath=Xhere,Xafter', \ 'set nomore', + \ 'let g:sequence = ""', \ ] let after = [ \ 'redir! > Xtestout', \ 'scriptnames', \ 'redir END', + \ 'redir! > Xsequence', + \ 'echo g:sequence', + \ 'redir END', \ 'quit', \ ] call mkdir('Xhere/plugin', 'p') - call writefile(['let done = 1'], 'Xhere/plugin/here.vim') + call writefile(['let g:sequence .= "here "'], 'Xhere/plugin/here.vim') + call mkdir('Xanother/plugin', 'p') + call writefile(['let g:sequence .= "another "'], 'Xanother/plugin/another.vim') call mkdir('Xhere/pack/foo/start/foobar/plugin', 'p') - call writefile(['let done = 1'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim') + call writefile(['let g:sequence .= "pack "'], 'Xhere/pack/foo/start/foobar/plugin/foo.vim') call mkdir('Xafter/plugin', 'p') - call writefile(['let done = 1'], 'Xafter/plugin/later.vim') + call writefile(['let g:sequence .= "after "'], 'Xafter/plugin/later.vim') if RunVim(before, after, '') let lines = readfile('Xtestout') - let expected = ['Xbefore.vim', 'here.vim', 'foo.vim', 'later.vim', 'Xafter.vim'] + let expected = ['Xbefore.vim', 'here.vim', 'another.vim', 'foo.vim', 'later.vim', 'Xafter.vim'] let found = [] for line in lines for one in expected @@ -56,8 +62,12 @@ func Test_after_comes_later() call assert_equal(expected, found) endif + call assert_equal('here another pack after', substitute(join(readfile('Xsequence', 1), ''), '\s\+$', '', '')) + call delete('Xtestout') + call delete('Xsequence') call delete('Xhere', 'rf') + call delete('Xanother', 'rf') call delete('Xafter', 'rf') endfunc diff --git a/src/testdir/test_syntax.vim b/src/testdir/test_syntax.vim index fef63418fd..33d7203e33 100644 --- a/src/testdir/test_syntax.vim +++ b/src/testdir/test_syntax.vim @@ -418,7 +418,7 @@ func Test_bg_detection() hi Normal ctermbg=15 call assert_equal('light', &bg) - " manually-set &bg takes precendence over auto-detection + " manually-set &bg takes precedence over auto-detection set bg=light hi Normal ctermbg=4 call assert_equal('light', &bg) @@ -461,7 +461,6 @@ func Test_syntax_hangs() bwipe! endfunc - func Test_conceal() if !has('conceal') return @@ -497,3 +496,27 @@ func Test_conceal() set conceallevel& bw! endfunc + +fun Test_synstack_synIDtrans() + new + setfiletype c + syntax on + call setline(1, ' /* A comment with a TODO */') + + call assert_equal([], synstack(1, 1)) + + norm f/ + call assert_equal(['cComment', 'cCommentStart'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) + call assert_equal(['Comment', 'Comment'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")')) + + norm fA + call assert_equal(['cComment'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) + call assert_equal(['Comment'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")')) + + norm fT + call assert_equal(['cComment', 'cTodo'], map(synstack(line("."), col(".")), 'synIDattr(v:val, "name")')) + call assert_equal(['Comment', 'Todo'], map(synstack(line("."), col(".")), 'synIDattr(synIDtrans(v:val), "name")')) + + syn clear + bw! +endfunc diff --git a/src/testdir/test_utf8_comparisons.vim b/src/testdir/test_utf8_comparisons.vim new file mode 100644 index 0000000000..576e86142f --- /dev/null +++ b/src/testdir/test_utf8_comparisons.vim @@ -0,0 +1,95 @@ +" Tests for case-insensitive UTF-8 comparisons (utf_strnicmp() in mbyte.c) +" Also test "g~ap". + +if !has("multi_byte") + finish +endif + +function! Ch(a, op, b, expected) + call assert_equal(eval(printf('"%s" %s "%s"', a:a, a:op, a:b)), a:expected, + \ printf('"%s" %s "%s" should return %d', a:a, a:op, a:b, a:expected)) +endfunction + +function! Chk(a, b, result) + if a:result == 0 + call Ch(a:a, '==?', a:b, 1) + call Ch(a:a, '!=?', a:b, 0) + call Ch(a:a, '<=?', a:b, 1) + call Ch(a:a, '>=?', a:b, 1) + call Ch(a:a, '?', a:b, 0) + elseif a:result > 0 + call Ch(a:a, '==?', a:b, 0) + call Ch(a:a, '!=?', a:b, 1) + call Ch(a:a, '<=?', a:b, 0) + call Ch(a:a, '>=?', a:b, 1) + call Ch(a:a, '?', a:b, 1) + else + call Ch(a:a, '==?', a:b, 0) + call Ch(a:a, '!=?', a:b, 1) + call Ch(a:a, '<=?', a:b, 1) + call Ch(a:a, '>=?', a:b, 0) + call Ch(a:a, '?', a:b, 0) + endif +endfunction + +function! Check(a, b, result) + call Chk(a:a, a:b, a:result) + call Chk(a:b, a:a, -a:result) +endfunction + +function! LT(a, b) + call Check(a:a, a:b, -1) +endfunction + +function! GT(a, b) + call Check(a:a, a:b, 1) +endfunction + +function! EQ(a, b) + call Check(a:a, a:b, 0) +endfunction + +function Test_comparisons() + call EQ('', '') + call LT('', 'a') + call EQ('abc', 'abc') + call EQ('Abc', 'abC') + call LT('ab', 'abc') + call LT('AB', 'abc') + call LT('ab', 'aBc') + call EQ('\xd0\xb9\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd', '\xd0\xb9\xd0\xa6\xd0\xa3\xd0\xba\xd0\x95\xd0\xbd') + call LT('\xd0\xb9\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd', '\xd0\xaf\xd1\x86\xd1\x83\xd0\xba\xd0\xb5\xd0\xbd') + call EQ('\xe2\x84\xaa', 'k') + call LT('\xe2\x84\xaa', 'kkkkkk') + call EQ('\xe2\x84\xaa\xe2\x84\xaa\xe2\x84\xaa', 'kkk') + call LT('kk', '\xe2\x84\xaa\xe2\x84\xaa\xe2\x84\xaa') + call EQ('\xe2\x84\xaa\xe2\x84\xa6k\xe2\x84\xaak\xcf\x89', 'k\xcf\x89\xe2\x84\xaakk\xe2\x84\xa6') + call EQ('Abc\x80', 'AbC\x80') + call LT('Abc\x80', 'AbC\x81') + call LT('Abc', 'AbC\x80') + call LT('abc\x80DEF', 'abc\x80def') " case folding stops at the first bad character + call LT('\xc3XYZ', '\xc3xyz') + call EQ('\xef\xbc\xba', '\xef\xbd\x9a') " FF3A (upper), FF5A (lower) + call GT('\xef\xbc\xba', '\xef\xbc\xff') " first string is ok and equals \xef\xbd\x9a after folding, second string is illegal and was left unchanged, then the strings were bytewise compared + call LT('\xc3', '\xc3\x83') + call EQ('\xc3\xa3xYz', '\xc3\x83XyZ') + for n in range(0x60, 0xFF) + call LT(printf('xYz\x%.2X', n-1), printf('XyZ\x%.2X', n)) + endfor + for n in range(0x80, 0xBF) + call EQ(printf('xYz\xc2\x%.2XUvW', n), printf('XyZ\xc2\x%.2XuVw', n)) + endfor + for n in range(0xC0, 0xFF) + call LT(printf('xYz\xc2\x%.2XUvW', n), printf('XyZ\xc2\x%.2XuVw', n)) + endfor +endfunction + +" test that g~ap changes one paragraph only. +function Test_gap() + new + call feedkeys("iabcd\n\ndefggg0g~ap", "tx") + call assert_equal(["ABCD", "", "defg"], getline(1,3)) +endfunction diff --git a/src/testdir/test_writefile.vim b/src/testdir/test_writefile.vim index 13c1a888d9..6768e3154a 100644 --- a/src/testdir/test_writefile.vim +++ b/src/testdir/test_writefile.vim @@ -31,3 +31,21 @@ func Test_writefile_fails_gently() 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 diff --git a/src/version.c b/src/version.c index 453a2bca9e..566527e49e 100644 --- a/src/version.c +++ b/src/version.c @@ -779,6 +779,18 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 685, +/**/ + 684, +/**/ + 683, +/**/ + 682, +/**/ + 681, +/**/ + 680, /**/ 679, /**/