X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/36cf8493aff99b652b2ad8c9e4d55a18688e8484..ad67503f50b8d2c443e63cb16863b39ad8345567:/src/w32proc.c diff --git a/src/w32proc.c b/src/w32proc.c index 426a656f56..09e0c0530a 100644 --- a/src/w32proc.c +++ b/src/w32proc.c @@ -32,6 +32,7 @@ along with GNU Emacs. If not, see . */ #include #include #include +#include /* must include CRT headers *before* config.h */ #include @@ -1077,6 +1078,7 @@ create_child (char *exe, char *cmdline, char *env, int is_gui_app, DWORD flags; char dir[ MAX_PATH ]; char *p; + const char *ext; if (cp == NULL) emacs_abort (); @@ -1115,6 +1117,15 @@ create_child (char *exe, char *cmdline, char *env, int is_gui_app, if (*p == '/') *p = '\\'; + /* CreateProcess handles batch files as exe specially. This special + handling fails when both the batch file and arguments are quoted. + We pass NULL as exe to avoid the special handling. */ + if (exe && cmdline[0] == '"' && + (ext = strrchr (exe, '.')) && + (xstrcasecmp (ext, ".bat") == 0 + || xstrcasecmp (ext, ".cmd") == 0)) + exe = NULL; + flags = (!NILP (Vw32_start_process_share_console) ? CREATE_NEW_PROCESS_GROUP : CREATE_NEW_CONSOLE); @@ -1604,6 +1615,15 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp) program = ENCODE_FILE (full); cmdname = SDATA (program); } + else + { + char *p = alloca (strlen (cmdname) + 1); + + /* Don't change the command name we were passed by our caller + (unixtodos_filename below will destructively mirror forward + slashes). */ + cmdname = strcpy (p, cmdname); + } /* make sure argv[0] and cmdname are both in DOS format */ unixtodos_filename (cmdname); @@ -1646,7 +1666,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp) strcpy (cmdname, egetenv ("CMDPROXY")); else { - strcpy (cmdname, SDATA (Vinvocation_directory)); + lispstpcpy (cmdname, Vinvocation_directory); strcat (cmdname, "cmdproxy.exe"); } @@ -2908,7 +2928,7 @@ int_from_hex (char * s) function isn't given a context pointer. */ Lisp_Object Vw32_valid_locale_ids; -static BOOL CALLBACK +static BOOL CALLBACK ALIGN_STACK enum_locale_fn (LPTSTR localeNum) { DWORD id = int_from_hex (localeNum); @@ -2972,7 +2992,7 @@ If successful, the new locale id is returned, otherwise nil. */) function isn't given a context pointer. */ Lisp_Object Vw32_valid_codepages; -static BOOL CALLBACK +static BOOL CALLBACK ALIGN_STACK enum_codepage_fn (LPTSTR codepageNum) { DWORD id = atoi (codepageNum); @@ -3144,6 +3164,190 @@ If successful, the new layout id is returned, otherwise nil. */) return Fw32_get_keyboard_layout (); } +/* Two variables to interface between get_lcid and the EnumLocales + callback function below. */ +#ifndef LOCALE_NAME_MAX_LENGTH +# define LOCALE_NAME_MAX_LENGTH 85 +#endif +static LCID found_lcid; +static char lname[3 * LOCALE_NAME_MAX_LENGTH + 1 + 1]; + +/* Callback function for EnumLocales. */ +static BOOL CALLBACK +get_lcid_callback (LPTSTR locale_num_str) +{ + char *endp; + char locval[2 * LOCALE_NAME_MAX_LENGTH + 1 + 1]; + LCID try_lcid = strtoul (locale_num_str, &endp, 16); + + if (GetLocaleInfo (try_lcid, LOCALE_SABBREVLANGNAME, + locval, LOCALE_NAME_MAX_LENGTH)) + { + /* This is for when they only specify the language, as in "ENU". */ + if (stricmp (locval, lname) == 0) + { + found_lcid = try_lcid; + return FALSE; + } + strcat (locval, "_"); + if (GetLocaleInfo (try_lcid, LOCALE_SABBREVCTRYNAME, + locval + strlen (locval), LOCALE_NAME_MAX_LENGTH)) + { + size_t locval_len = strlen (locval); + + if (strnicmp (locval, lname, locval_len) == 0 + && (lname[locval_len] == '.' + || lname[locval_len] == '\0')) + { + found_lcid = try_lcid; + return FALSE; + } + } + } + return TRUE; +} + +/* Return the Locale ID (LCID) number given the locale's name, a + string, in LOCALE_NAME. This works by enumerating all the locales + supported by the system, until we find one whose name matches + LOCALE_NAME. */ +static LCID +get_lcid (const char *locale_name) +{ + /* A simple cache. */ + static LCID last_lcid; + static char last_locale[1000]; + + /* The code below is not thread-safe, as it uses static variables. + But this function is called only from the Lisp thread. */ + if (last_lcid > 0 && strcmp (locale_name, last_locale) == 0) + return last_lcid; + + strncpy (lname, locale_name, sizeof (lname) - 1); + lname[sizeof (lname) - 1] = '\0'; + found_lcid = 0; + EnumSystemLocales (get_lcid_callback, LCID_SUPPORTED); + if (found_lcid > 0) + { + last_lcid = found_lcid; + strcpy (last_locale, locale_name); + } + return found_lcid; +} + +#ifndef _NSLCMPERROR +# define _NSLCMPERROR INT_MAX +#endif +#ifndef LINGUISTIC_IGNORECASE +# define LINGUISTIC_IGNORECASE 0x00000010 +#endif + +int +w32_compare_strings (const char *s1, const char *s2, char *locname, + int ignore_case) +{ + LCID lcid = GetThreadLocale (); + wchar_t *string1_w, *string2_w; + int val, needed; + extern BOOL g_b_init_compare_string_w; + static int (WINAPI *pCompareStringW)(LCID, DWORD, LPCWSTR, int, LPCWSTR, int); + DWORD flags = 0; + + USE_SAFE_ALLOCA; + + /* The LCID machinery doesn't seem to support the "C" locale, so we + need to do that by hand. */ + if (locname + && ((locname[0] == 'C' && (locname[1] == '\0' || locname[1] == '.')) + || strcmp (locname, "POSIX") == 0)) + return (ignore_case ? stricmp (s1, s2) : strcmp (s1, s2)); + + if (!g_b_init_compare_string_w) + { + if (os_subtype == OS_9X) + { + pCompareStringW = GetProcAddress (LoadLibrary ("Unicows.dll"), + "CompareStringW"); + if (!pCompareStringW) + { + errno = EINVAL; + /* This return value is compatible with wcscoll and + other MS CRT functions. */ + return _NSLCMPERROR; + } + } + else + pCompareStringW = CompareStringW; + + g_b_init_compare_string_w = 1; + } + + needed = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, s1, -1, NULL, 0); + if (needed > 0) + { + SAFE_NALLOCA (string1_w, 1, needed + 1); + pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, s1, -1, + string1_w, needed); + } + else + { + errno = EINVAL; + return _NSLCMPERROR; + } + + needed = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, s2, -1, NULL, 0); + if (needed > 0) + { + SAFE_NALLOCA (string2_w, 1, needed + 1); + pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, s2, -1, + string2_w, needed); + } + else + { + SAFE_FREE (); + errno = EINVAL; + return _NSLCMPERROR; + } + + if (locname) + { + /* Convert locale name string to LCID. We don't want to use + LocaleNameToLCID because (a) it is only available since + Vista, and (b) it doesn't accept locale names returned by + 'setlocale' and 'GetLocaleInfo'. */ + LCID new_lcid = get_lcid (locname); + + if (new_lcid > 0) + lcid = new_lcid; + else + error ("Invalid locale %s: Invalid argument", locname); + } + + if (ignore_case) + { + /* NORM_IGNORECASE ignores any tertiary distinction, not just + case variants. LINGUISTIC_IGNORECASE is more selective, and + is sensitive to the locale's language, but it is not + available before Vista. */ + if (w32_major_version >= 6) + flags |= LINGUISTIC_IGNORECASE; + else + flags |= NORM_IGNORECASE; + } + /* This approximates what glibc collation functions do when the + locale's codeset is UTF-8. */ + if (!NILP (Vw32_collate_ignore_punctuation)) + flags |= NORM_IGNORESYMBOLS; + val = pCompareStringW (lcid, flags, string1_w, -1, string2_w, -1); + SAFE_FREE (); + if (!val) + { + errno = EINVAL; + return _NSLCMPERROR; + } + return val - 2; +} + void syms_of_ntproc (void) @@ -3254,6 +3458,20 @@ Any other non-nil value means do this even on remote and removable drives where the performance impact may be noticeable even on modern hardware. */); Vw32_get_true_file_attributes = Qlocal; + DEFVAR_LISP ("w32-collate-ignore-punctuation", + Vw32_collate_ignore_punctuation, + doc: /* Non-nil causes string collation functions ignore punctuation on MS-Windows. +On Posix platforms, `string-collate-lessp' and `string-collate-equalp' +ignore punctuation characters when they compare strings, if the +locale's codeset is UTF-8, as in \"en_US.UTF-8\". Binding this option +to a non-nil value will achieve a similar effect on MS-Windows, where +locales with UTF-8 codeset are not supported. + +Note that setting this to non-nil will also ignore blanks and symbols +in the strings. So do NOT use this option when comparing file names +for equality, only when you need to sort them. */); + Vw32_collate_ignore_punctuation = Qnil; + staticpro (&Vw32_valid_locale_ids); staticpro (&Vw32_valid_codepages); }