diff --git a/.hgtags b/.hgtags index e3f2e100fa..d112ae08aa 100644 --- a/.hgtags +++ b/.hgtags @@ -2730,3 +2730,12 @@ f6247eaf4e1d556f782321890d725663f74babe6 v7-4-004 3640cf4c0d4b6e5687bb7a31678fab70c88ed94b v7-4-005 2374a05efe20287d55bd824689a41becc7662505 v7-4-006 4fe1dfc7014e57b4beb5a01c9e94357265d19a92 v7-4-007 +b04bdb2c5fce70a278d26c477debb65a388da0ca v7-4-008 +8b5d80861c5e0403ea9f54ddddce2752a463c8a5 v7-4-009 +bb358cc41d920983629ace62bcf26decbf06cab4 v7-4-010 +54e66395831c1a58b4a9804e7884e505842157e8 v7-4-011 +8e28c23e482c5b3c8296d8022271822886793456 v7-4-012 +07737d3aa81725672796cbc9a010d63414ab6fea v7-4-013 +9801d06e7b4ccdcd02cf40bee34eaaada0ca0409 v7-4-014 +a7478f9f2551e95bff138cd658f7a86ced804ab1 v7-4-015 +8d5cd0ec3e7183a289f9bac41d3981307cdc1fac v7-4-016 diff --git a/src/eval.c b/src/eval.c index 8669e7f999..998c286565 100644 --- a/src/eval.c +++ b/src/eval.c @@ -12143,6 +12143,9 @@ f_has(argvars, rettv) #ifndef CASE_INSENSITIVE_FILENAME "fname_case", #endif +#ifdef HAVE_ACL + "acl", +#endif #ifdef FEAT_ARABIC "arabic", #endif @@ -12558,7 +12561,12 @@ f_has(argvars, rettv) "xfontset", #endif #ifdef FEAT_XPM_W32 - "xpm_w32", + "xpm", + "xpm_w32", /* for backward compatibility */ +#else +# if defined(HAVE_XPM) + "xpm", +# endif #endif #ifdef USE_XSMP "xsmp", @@ -14312,18 +14320,23 @@ f_mkdir(argvars, rettv) return; dir = get_tv_string_buf(&argvars[0], buf); - if (*gettail(dir) == NUL) - /* remove trailing slashes */ - *gettail_sep(dir) = NUL; - - if (argvars[1].v_type != VAR_UNKNOWN) + if (*dir == NUL) + rettv->vval.v_number = FAIL; + else { - if (argvars[2].v_type != VAR_UNKNOWN) - prot = get_tv_number_chk(&argvars[2], NULL); - if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) - mkdir_recurse(dir, prot); + if (*gettail(dir) == NUL) + /* remove trailing slashes */ + *gettail_sep(dir) = NUL; + + if (argvars[1].v_type != VAR_UNKNOWN) + { + if (argvars[2].v_type != VAR_UNKNOWN) + prot = get_tv_number_chk(&argvars[2], NULL); + if (prot != -1 && STRCMP(get_tv_string(&argvars[1]), "p") == 0) + mkdir_recurse(dir, prot); + } + rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot); } - rettv->vval.v_number = prot == -1 ? FAIL : vim_mkdir_emsg(dir, prot); } #endif diff --git a/src/fileio.c b/src/fileio.c index 89977d2deb..4fc1aef71c 100644 --- a/src/fileio.c +++ b/src/fileio.c @@ -428,13 +428,13 @@ readfile(fname, sfname, from, lines_to_skip, lines_to_read, eap, flags) } } -#ifdef UNIX - /* - * On Unix it is possible to read a directory, so we have to - * check for it before the mch_open(). - */ if (!read_stdin && !read_buffer) { +#ifdef UNIX + /* + * On Unix it is possible to read a directory, so we have to + * check for it before the mch_open(). + */ perm = mch_getperm(fname); if (perm >= 0 && !S_ISREG(perm) /* not a regular file ... */ # ifdef S_ISFIFO @@ -457,8 +457,8 @@ readfile(fname, sfname, from, lines_to_skip, lines_to_read, eap, flags) msg_scroll = msg_save; return FAIL; } - -# if defined(MSDOS) || defined(MSWIN) || defined(OS2) +#endif +#if defined(MSDOS) || defined(MSWIN) || defined(OS2) /* * MS-Windows allows opening a device, but we will probably get stuck * trying to read it. @@ -470,9 +470,8 @@ readfile(fname, sfname, from, lines_to_skip, lines_to_read, eap, flags) msg_scroll = msg_save; return FAIL; } -# endif - } #endif + } /* Set default or forced 'fileformat' and 'binary'. */ set_file_options(set_options, eap); @@ -2926,9 +2925,14 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask) int *did_ask; /* flag: whether already asked for key */ { int method = crypt_method_from_magic((char *)ptr, *sizep); + int b_p_ro = curbuf->b_p_ro; if (method >= 0) { + /* Mark the buffer as read-only until the decryption has taken place. + * Avoids accidentally overwriting the file with garbage. */ + curbuf->b_p_ro = TRUE; + set_crypt_method(curbuf, method); if (method > 0) (void)blowfish_self_test(); @@ -2977,6 +2981,8 @@ check_for_cryptkey(cryptkey, ptr, sizep, filesizep, newfile, fname, did_ask) *sizep -= CRYPT_MAGIC_LEN + salt_len + seed_len; mch_memmove(ptr, ptr + CRYPT_MAGIC_LEN + salt_len + seed_len, (size_t)*sizep); + /* Restore the read-only flag. */ + curbuf->b_p_ro = b_p_ro; } } /* When starting to edit a new file which does not have encryption, clear diff --git a/src/os_mswin.c b/src/os_mswin.c index 96d3448c33..67b7960978 100644 --- a/src/os_mswin.c +++ b/src/os_mswin.c @@ -456,7 +456,14 @@ mch_FullName( int mch_isFullName(char_u *fname) { +#ifdef FEAT_MBYTE + /* WinNT and later can use _MAX_PATH wide characters for a pathname, which + * means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is + * UTF-8. */ + char szName[_MAX_PATH * 3 + 1]; +#else char szName[_MAX_PATH + 1]; +#endif /* A name like "d:/foo" and "//server/share" is absolute */ if ((fname[0] && fname[1] == ':' && (fname[2] == '/' || fname[2] == '\\')) @@ -464,7 +471,7 @@ mch_isFullName(char_u *fname) return TRUE; /* A name that can't be made absolute probably isn't absolute. */ - if (mch_FullName(fname, szName, _MAX_PATH, FALSE) == FAIL) + if (mch_FullName(fname, szName, sizeof(szName) - 1, FALSE) == FAIL) return FALSE; return pathcmp(fname, szName, -1) == 0; @@ -498,10 +505,17 @@ slash_adjust(p) int vim_stat(const char *name, struct stat *stp) { +#ifdef FEAT_MBYTE + /* WinNT and later can use _MAX_PATH wide characters for a pathname, which + * means that the maximum pathname is _MAX_PATH * 3 bytes when 'enc' is + * UTF-8. */ + char buf[_MAX_PATH * 3 + 1]; +#else char buf[_MAX_PATH + 1]; +#endif char *p; - vim_strncpy((char_u *)buf, (char_u *)name, _MAX_PATH); + vim_strncpy((char_u *)buf, (char_u *)name, sizeof(buf) - 1); p = buf + strlen(buf); if (p > buf) mb_ptr_back(buf, p); @@ -1761,9 +1775,13 @@ mch_resolve_shortcut(char_u *fname) IPersistFile *ppf = NULL; OLECHAR wsz[MAX_PATH]; WIN32_FIND_DATA ffd; // we get those free of charge - TCHAR buf[MAX_PATH]; // could have simply reused 'wsz'... + CHAR buf[MAX_PATH]; // could have simply reused 'wsz'... char_u *rfname = NULL; int len; +# ifdef FEAT_MBYTE + IShellLinkW *pslw = NULL; + WIN32_FIND_DATAW ffdw; // we get those free of charge +# endif /* Check if the file name ends in ".lnk". Avoid calling * CoCreateInstance(), it's quite slow. */ @@ -1775,18 +1793,62 @@ mch_resolve_shortcut(char_u *fname) CoInitialize(NULL); +# ifdef FEAT_MBYTE + if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) + { + // create a link manager object and request its interface + hr = CoCreateInstance( + &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + &IID_IShellLinkW, (void**)&pslw); + if (hr == S_OK) + { + WCHAR *p = enc_to_utf16(fname, NULL); + + if (p != NULL) + { + // Get a pointer to the IPersistFile interface. + hr = pslw->lpVtbl->QueryInterface( + pslw, &IID_IPersistFile, (void**)&ppf); + if (hr != S_OK) + goto shortcut_errorw; + + // "load" the name and resolve the link + hr = ppf->lpVtbl->Load(ppf, p, STGM_READ); + if (hr != S_OK) + goto shortcut_errorw; +# if 0 // This makes Vim wait a long time if the target does not exist. + hr = pslw->lpVtbl->Resolve(pslw, NULL, SLR_NO_UI); + if (hr != S_OK) + goto shortcut_errorw; +# endif + + // Get the path to the link target. + ZeroMemory(wsz, MAX_PATH * sizeof(WCHAR)); + hr = pslw->lpVtbl->GetPath(pslw, wsz, MAX_PATH, &ffdw, 0); + if (hr == S_OK && wsz[0] != NUL) + rfname = utf16_to_enc(wsz, NULL); + +shortcut_errorw: + vim_free(p); + if (hr == S_OK) + goto shortcut_end; + } + } + /* Retry with non-wide function (for Windows 98). */ + } +# endif // create a link manager object and request its interface hr = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, &IID_IShellLink, (void**)&psl); if (hr != S_OK) - goto shortcut_error; + goto shortcut_end; // Get a pointer to the IPersistFile interface. hr = psl->lpVtbl->QueryInterface( psl, &IID_IPersistFile, (void**)&ppf); if (hr != S_OK) - goto shortcut_error; + goto shortcut_end; // full path string must be in Unicode. MultiByteToWideChar(CP_ACP, 0, fname, -1, wsz, MAX_PATH); @@ -1794,12 +1856,12 @@ mch_resolve_shortcut(char_u *fname) // "load" the name and resolve the link hr = ppf->lpVtbl->Load(ppf, wsz, STGM_READ); if (hr != S_OK) - goto shortcut_error; -#if 0 // This makes Vim wait a long time if the target doesn't exist. + goto shortcut_end; +# if 0 // This makes Vim wait a long time if the target doesn't exist. hr = psl->lpVtbl->Resolve(psl, NULL, SLR_NO_UI); if (hr != S_OK) - goto shortcut_error; -#endif + goto shortcut_end; +# endif // Get the path to the link target. ZeroMemory(buf, MAX_PATH); @@ -1807,12 +1869,16 @@ mch_resolve_shortcut(char_u *fname) if (hr == S_OK && buf[0] != NUL) rfname = vim_strsave(buf); -shortcut_error: +shortcut_end: // Release all interface pointers (both belong to the same object) if (ppf != NULL) ppf->lpVtbl->Release(ppf); if (psl != NULL) psl->lpVtbl->Release(psl); +# ifdef FEAT_MBYTE + if (pslw != NULL) + pslw->lpVtbl->Release(pslw); +# endif CoUninitialize(); return rfname; diff --git a/src/os_win32.c b/src/os_win32.c index 4013353cf3..f36dfb3db9 100644 --- a/src/os_win32.c +++ b/src/os_win32.c @@ -2500,9 +2500,125 @@ mch_check_win( } +#ifdef FEAT_MBYTE +/* + * fname_casew(): Wide version of fname_case(). Set the case of the file name, + * if it already exists. When "len" is > 0, also expand short to long + * filenames. + * Return FAIL if wide functions are not available, OK otherwise. + * NOTE: much of this is identical to fname_case(), keep in sync! + */ + static int +fname_casew( + WCHAR *name, + int len) +{ + WCHAR szTrueName[_MAX_PATH + 2]; + WCHAR szTrueNameTemp[_MAX_PATH + 2]; + WCHAR *ptrue, *ptruePrev; + WCHAR *porig, *porigPrev; + int flen; + WIN32_FIND_DATAW fb; + HANDLE hFind; + int c; + int slen; + + flen = (int)wcslen(name); + if (flen > _MAX_PATH) + return OK; + + /* slash_adjust(name) not needed, already adjusted by fname_case(). */ + + /* Build the new name in szTrueName[] one component at a time. */ + porig = name; + ptrue = szTrueName; + + if (iswalpha(porig[0]) && porig[1] == L':') + { + /* copy leading drive letter */ + *ptrue++ = *porig++; + *ptrue++ = *porig++; + *ptrue = NUL; /* in case nothing follows */ + } + + while (*porig != NUL) + { + /* copy \ characters */ + while (*porig == psepc) + *ptrue++ = *porig++; + + ptruePrev = ptrue; + porigPrev = porig; + while (*porig != NUL && *porig != psepc) + { + *ptrue++ = *porig++; + } + *ptrue = NUL; + + /* To avoid a slow failure append "\*" when searching a directory, + * server or network share. */ + wcscpy(szTrueNameTemp, szTrueName); + slen = (int)wcslen(szTrueNameTemp); + if (*porig == psepc && slen + 2 < _MAX_PATH) + wcscpy(szTrueNameTemp + slen, L"\\*"); + + /* Skip "", "." and "..". */ + if (ptrue > ptruePrev + && (ptruePrev[0] != L'.' + || (ptruePrev[1] != NUL + && (ptruePrev[1] != L'.' || ptruePrev[2] != NUL))) + && (hFind = FindFirstFileW(szTrueNameTemp, &fb)) + != INVALID_HANDLE_VALUE) + { + c = *porig; + *porig = NUL; + + /* Only use the match when it's the same name (ignoring case) or + * expansion is allowed and there is a match with the short name + * and there is enough room. */ + if (_wcsicoll(porigPrev, fb.cFileName) == 0 + || (len > 0 + && (_wcsicoll(porigPrev, fb.cAlternateFileName) == 0 + && (int)(ptruePrev - szTrueName) + + (int)wcslen(fb.cFileName) < len))) + { + wcscpy(ptruePrev, fb.cFileName); + + /* Look for exact match and prefer it if found. Must be a + * long name, otherwise there would be only one match. */ + while (FindNextFileW(hFind, &fb)) + { + if (*fb.cAlternateFileName != NUL + && (wcscoll(porigPrev, fb.cFileName) == 0 + || (len > 0 + && (_wcsicoll(porigPrev, + fb.cAlternateFileName) == 0 + && (int)(ptruePrev - szTrueName) + + (int)wcslen(fb.cFileName) < len)))) + { + wcscpy(ptruePrev, fb.cFileName); + break; + } + } + } + FindClose(hFind); + *porig = c; + ptrue = ptruePrev + wcslen(ptruePrev); + } + else if (hFind == INVALID_HANDLE_VALUE + && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + return FAIL; + } + + wcscpy(name, szTrueName); + return OK; +} +#endif + /* * fname_case(): Set the case of the file name, if it already exists. * When "len" is > 0, also expand short to long filenames. + * NOTE: much of this is identical to fname_casew(), keep in sync! */ void fname_case( @@ -2520,11 +2636,44 @@ fname_case( int slen; flen = (int)STRLEN(name); - if (flen == 0 || flen > _MAX_PATH) + if (flen == 0) return; slash_adjust(name); +#ifdef FEAT_MBYTE + if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) + { + WCHAR *p = enc_to_utf16(name, NULL); + + if (p != NULL) + { + char_u *q; + WCHAR buf[_MAX_PATH + 2]; + + wcscpy(buf, p); + vim_free(p); + + if (fname_casew(buf, (len > 0) ? _MAX_PATH : 0) == OK) + { + q = utf16_to_enc(buf, NULL); + if (q != NULL) + { + vim_strncpy(name, q, (len > 0) ? len - 1 : flen); + vim_free(q); + return; + } + } + } + /* Retry with non-wide function (for Windows 98). */ + } +#endif + + /* If 'enc' is utf-8, flen can be larger than _MAX_PATH. + * So we should check this after calling wide function. */ + if (flen > _MAX_PATH) + return; + /* Build the new name in szTrueName[] one component at a time. */ porig = name; ptrue = szTrueName; @@ -3107,6 +3256,9 @@ mch_nodetype(char_u *name) { HANDLE hFile; int type; +#ifdef FEAT_MBYTE + WCHAR *wn = NULL; +#endif /* We can't open a file with a name "\\.\con" or "\\.\prn" and trying to * read from it later will cause Vim to hang. Thus return NODE_WRITABLE @@ -3114,14 +3266,41 @@ mch_nodetype(char_u *name) if (STRNCMP(name, "\\\\.\\", 4) == 0) return NODE_WRITABLE; - hFile = CreateFile(name, /* file name */ - GENERIC_WRITE, /* access mode */ - 0, /* share mode */ - NULL, /* security descriptor */ - OPEN_EXISTING, /* creation disposition */ - 0, /* file attributes */ - NULL); /* handle to template file */ +#ifdef FEAT_MBYTE + if (enc_codepage >= 0 && (int)GetACP() != enc_codepage) + { + wn = enc_to_utf16(name, NULL); + if (wn != NULL) + { + hFile = CreateFileW(wn, /* file name */ + GENERIC_WRITE, /* access mode */ + 0, /* share mode */ + NULL, /* security descriptor */ + OPEN_EXISTING, /* creation disposition */ + 0, /* file attributes */ + NULL); /* handle to template file */ + if (hFile == INVALID_HANDLE_VALUE + && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED) + { + /* Retry with non-wide function (for Windows 98). */ + vim_free(wn); + wn = NULL; + } + } + } + if (wn == NULL) +#endif + hFile = CreateFile(name, /* file name */ + GENERIC_WRITE, /* access mode */ + 0, /* share mode */ + NULL, /* security descriptor */ + OPEN_EXISTING, /* creation disposition */ + 0, /* file attributes */ + NULL); /* handle to template file */ +#ifdef FEAT_MBYTE + vim_free(wn); +#endif if (hFile == INVALID_HANDLE_VALUE) return NODE_NORMAL; diff --git a/src/regexp.c b/src/regexp.c index 06bbb4a540..df884e25bd 100644 --- a/src/regexp.c +++ b/src/regexp.c @@ -4311,8 +4311,8 @@ regmatch(scan) */ for (;;) { - /* Some patterns may cause a long time to match, even though they are not - * illegal. E.g., "\([a-z]\+\)\+Q". Allow breaking them with CTRL-C. */ + /* Some patterns may take a long time to match, e.g., "\([a-z]\+\)\+Q". + * Allow interrupting them with CTRL-C. */ fast_breakcheck(); #ifdef DEBUG diff --git a/src/regexp_nfa.c b/src/regexp_nfa.c index 57539f4e53..5288eb6d3f 100644 --- a/src/regexp_nfa.c +++ b/src/regexp_nfa.c @@ -5089,6 +5089,12 @@ nfa_regmatch(prog, start, submatch, m) return FALSE; } #endif + /* Some patterns may take a long time to match, especially when using + * recursive_regmatch(). Allow interrupting them with CTRL-C. */ + fast_breakcheck(); + if (got_int) + return FALSE; + nfa_match = FALSE; /* Allocate memory for the lists of nodes. */ diff --git a/src/version.c b/src/version.c index 08ae64c8f9..74f361d770 100644 --- a/src/version.c +++ b/src/version.c @@ -60,6 +60,11 @@ static void version_msg __ARGS((char *s)); static char *(features[]) = { +#ifdef HAVE_ACL + "+acl", +#else + "-acl", +#endif #ifdef AMIGA /* only for Amiga systems */ # ifdef FEAT_ARP "+ARP", @@ -736,12 +741,36 @@ static char *(features[]) = # else "-xpm_w32", # endif +#else +# ifdef HAVE_XPM + "+xpm", +# else + "-xpm", +# endif #endif NULL }; static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 16, +/**/ + 15, +/**/ + 14, +/**/ + 13, +/**/ + 12, +/**/ + 11, +/**/ + 10, +/**/ + 9, +/**/ + 8, /**/ 7, /**/