]> code.delx.au - gnu-emacs/blobdiff - src/w32proc.c
Make gnutls-peer-status return even more data
[gnu-emacs] / src / w32proc.c
index 7d4fb9825fa55cae91eba3859f41823cda76ef27..09e0c0530a455b853b0fb50335c2787e6d4748f1 100644 (file)
@@ -1,5 +1,6 @@
 /* Process support for GNU Emacs on the Microsoft Windows API.
-   Copyright (C) 1992, 1995, 1999-2013 Free Software Foundation, Inc.
+
+Copyright (C) 1992, 1995, 1999-2014 Free Software Foundation, Inc.
 
 This file is part of GNU Emacs.
 
@@ -31,6 +32,7 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #include <signal.h>
 #include <sys/file.h>
 #include <mbstring.h>
+#include <locale.h>
 
 /* must include CRT headers *before* config.h */
 #include <config.h>
@@ -42,8 +44,9 @@ along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.  */
 #undef kill
 
 #include <windows.h>
-#ifdef __GNUC__
-/* This definition is missing from mingw32 headers. */
+#if defined(__GNUC__) && !defined(__MINGW64__)
+/* This definition is missing from mingw.org headers, but not MinGW64
+   headers. */
 extern BOOL WINAPI IsValidLocale (LCID, DWORD);
 #endif
 
@@ -564,7 +567,7 @@ init_timers (void)
 static int
 start_timer_thread (int which)
 {
-  DWORD exit_code;
+  DWORD exit_code, tid;
   HANDLE th;
   struct itimer_data *itimer =
     (which == ITIMER_REAL) ? &real_itimer : &prof_itimer;
@@ -602,7 +605,7 @@ start_timer_thread (int which)
      the way of threads we start to wait for subprocesses.  See also
      new_child below.  */
   itimer->timer_thread = CreateThread (NULL, 64 * 1024, timer_loop,
-                                      (void *)itimer, 0x00010000, NULL);
+                                      (void *)itimer, 0x00010000, &tid);
 
   if (!itimer->timer_thread)
     {
@@ -1065,7 +1068,7 @@ static char * process_dir;
 
 static BOOL
 create_child (char *exe, char *cmdline, char *env, int is_gui_app,
-             int * pPid, child_process *cp)
+             pid_t * pPid, child_process *cp)
 {
   STARTUPINFO start;
   SECURITY_ATTRIBUTES sec_attrs;
@@ -1075,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 ();
 
@@ -1113,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);
@@ -1592,7 +1605,7 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
       program = build_string (cmdname);
       full = Qnil;
       GCPRO1 (program);
-      openp (Vexec_path, program, Vexec_suffixes, &full, make_number (X_OK));
+      openp (Vexec_path, program, Vexec_suffixes, &full, make_number (X_OK), 0);
       UNGCPRO;
       if (NILP (full))
        {
@@ -1602,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);
@@ -1644,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");
        }
 
@@ -1770,12 +1792,12 @@ sys_spawnve (int mode, char *cmdname, char **argv, char **envp)
       if (need_quotes)
        {
          int escape_char_run = 0;
-         char * first;
-         char * last;
+         /* char * first; */
+         /* char * last; */
 
          p = *targ;
-         first = p;
-         last = p + strlen (p) - 1;
+         /* first = p; */
+         /* last = p + strlen (p) - 1; */
          *parg++ = '"';
 #if 0
          /* This version does not escape quotes if they occur at the
@@ -2247,10 +2269,9 @@ static BOOL CALLBACK
 find_child_console (HWND hwnd, LPARAM arg)
 {
   child_process * cp = (child_process *) arg;
-  DWORD thread_id;
   DWORD process_id;
 
-  thread_id = GetWindowThreadProcessId (hwnd, &process_id);
+  GetWindowThreadProcessId (hwnd, &process_id);
   if (process_id == cp->procinfo.dwProcessId)
     {
       char window_class[32];
@@ -2907,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);
@@ -2971,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);
@@ -3083,10 +3104,10 @@ The return value is a list of pairs of language id and layout id.  */)
     {
       while (--num_layouts >= 0)
        {
-         DWORD kl = (DWORD) layouts[num_layouts];
+         HKL kl = layouts[num_layouts];
 
-         obj = Fcons (Fcons (make_number (kl & 0xffff),
-                             make_number ((kl >> 16) & 0xffff)),
+         obj = Fcons (Fcons (make_number (LOWORD (kl)),
+                             make_number (HIWORD (kl))),
                       obj);
        }
     }
@@ -3101,10 +3122,10 @@ DEFUN ("w32-get-keyboard-layout", Fw32_get_keyboard_layout,
 The return value is the cons of the language id and the layout id.  */)
   (void)
 {
-  DWORD kl = (DWORD) GetKeyboardLayout (dwWindowsThreadId);
+  HKL kl = GetKeyboardLayout (dwWindowsThreadId);
 
-  return Fcons (make_number (kl & 0xffff),
-               make_number ((kl >> 16) & 0xffff));
+  return Fcons (make_number (LOWORD (kl)),
+               make_number (HIWORD (kl)));
 }
 
 
@@ -3115,14 +3136,14 @@ The keyboard layout setting affects interpretation of keyboard input.
 If successful, the new layout id is returned, otherwise nil.  */)
   (Lisp_Object layout)
 {
-  DWORD kl;
+  HKL kl;
 
   CHECK_CONS (layout);
   CHECK_NUMBER_CAR (layout);
   CHECK_NUMBER_CDR (layout);
 
 kl = (XINT (XCAR (layout)) & 0xffff)
-    | (XINT (XCDR (layout)) << 16);
kl = (HKL) ((XINT (XCAR (layout)) & 0xffff)
+            | (XINT (XCDR (layout)) << 16));
 
   /* Synchronize layout with input thread.  */
   if (dwWindowsThreadId)
@@ -3137,12 +3158,196 @@ If successful, the new layout id is returned, otherwise nil.  */)
            return Qnil;
        }
     }
-  else if (!ActivateKeyboardLayout ((HKL) kl, 0))
+  else if (!ActivateKeyboardLayout (kl, 0))
     return Qnil;
 
   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;
+}
+
 \f
 void
 syms_of_ntproc (void)
@@ -3253,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);
 }