/* Process support for GNU Emacs on the Microsoft Windows API.
-Copyright (C) 1992, 1995, 1999-2015 Free Software Foundation, Inc.
+Copyright (C) 1992, 1995, 1999-2016 Free Software Foundation, Inc.
This file is part of GNU Emacs.
GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation, either version 3 of the License, or
-(at your option) any later version.
+the Free Software Foundation, either version 3 of the License, or (at
+your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
#include <ctype.h>
#include <io.h>
#include <fcntl.h>
+#include <unistd.h>
#include <signal.h>
#include <sys/file.h>
#include <mbstring.h>
#include "w32.h"
#include "w32common.h"
#include "w32heap.h"
-#include "systime.h"
-#include "syswait.h"
-#include "process.h"
+#include "syswait.h" /* for WNOHANG */
#include "syssignal.h"
#include "w32term.h"
-#include "dispextern.h" /* for xstrcasecmp */
#include "coding.h"
#define RVA_TO_PTR(var,section,filedata) \
etc.
Both these arrays reference each other: there's a member of
- child_process structure that records the file corresponding
+ child_process structure that records the corresponding file
descriptor, and there's a member of filedesc structure that holds a
pointer to the corresponding child_process.
thread" that will watch the output of the subprocess/stream and its
status. (If no vacant slot can be found, new_child returns a
failure indication to its caller, and the higher-level Emacs
- primitive will then fail with EMFILE or EAGAIN.)
+ primitive that called it will then fail with EMFILE or EAGAIN.)
The reader thread started by new_child communicates with the main
(a.k.a. "Lisp") thread via two event objects and a status, all of
them recorded by the members of the child_process structure in
child_procs[]. The event objects serve as semaphores between the
- reader thread and the 'select' emulation in sys_select, as follows:
+ reader thread and the 'pselect' emulation in sys_select, as follows:
. Initially, the reader thread is waiting for the char_consumed
event to become signaled by sys_select, which is an indication
When the subprocess exits or the network/serial stream is closed,
the reader thread sets the status accordingly and exits. It also
- exits when the main thread sets the ststus to STATUS_READ_ERROR
- and/or the char_avail and char_consumed event handles are NULL;
+ exits when the main thread sets the status to STATUS_READ_ERROR
+ and/or the char_avail and char_consumed event handles become NULL;
this is how delete_child, called by Emacs when a subprocess or a
stream is terminated, terminates the reader thread as part of
deleting the child_process object.
If file descriptor zero (stdin) doesn't have its bit set in the
'rfds' argument to sys_select, the function always watches for
- keyboard interrupts, to be able to return when the user presses
- C-g.
+ keyboard interrupts, to be able to interrupt the wait and return
+ when the user presses C-g.
Having collected the handles to watch, sys_select calls
WaitForMultipleObjects to wait for any one of them to become
/* Implementation note: This function works with file names encoded in
the current ANSI codepage. */
-static void
+static int
w32_executable_type (char * filename,
int * is_dos_app,
int * is_cygnus_app,
+ int * is_msys_app,
int * is_gui_app)
{
file_data executable;
char * p;
+ int retval = 0;
/* Default values in case we can't tell for sure. */
*is_dos_app = FALSE;
*is_cygnus_app = FALSE;
+ *is_msys_app = FALSE;
*is_gui_app = FALSE;
if (!open_input_file (&executable, filename))
- return;
+ return -1;
p = strrchr (filename, '.');
extension, which is defined in the registry. */
p = egetenv ("COMSPEC");
if (p)
- w32_executable_type (p, is_dos_app, is_cygnus_app, is_gui_app);
+ retval = w32_executable_type (p, is_dos_app, is_cygnus_app, is_msys_app,
+ is_gui_app);
}
else
{
#endif
if (data_dir)
{
- /* Look for cygwin.dll in DLL import list. */
+ /* Look for Cygwin DLL in the DLL import list. */
IMAGE_DATA_DIRECTORY import_dir =
data_dir[IMAGE_DIRECTORY_ENTRY_IMPORT];
- IMAGE_IMPORT_DESCRIPTOR * imports;
- IMAGE_SECTION_HEADER * section;
-
- section = rva_to_section (import_dir.VirtualAddress, nt_header);
- imports = RVA_TO_PTR (import_dir.VirtualAddress, section,
- executable);
+ IMAGE_IMPORT_DESCRIPTOR * imports =
+ RVA_TO_PTR (import_dir.VirtualAddress,
+ rva_to_section (import_dir.VirtualAddress,
+ nt_header),
+ executable);
for ( ; imports->Name; imports++)
{
+ IMAGE_SECTION_HEADER * section =
+ rva_to_section (imports->Name, nt_header);
char * dllname = RVA_TO_PTR (imports->Name, section,
executable);
- /* The exact name of the cygwin dll has changed with
- various releases, but hopefully this will be reasonably
- future proof. */
+ /* The exact name of the Cygwin DLL has changed with
+ various releases, but hopefully this will be
+ reasonably future-proof. */
if (strncmp (dllname, "cygwin", 6) == 0)
{
*is_cygnus_app = TRUE;
break;
}
+ else if (strncmp (dllname, "msys-", 5) == 0)
+ {
+ /* This catches both MSYS 1.x and MSYS2
+ executables (the DLL name is msys-1.0.dll and
+ msys-2.0.dll, respectively). There doesn't
+ seem to be a reason to distinguish between
+ the two, for now. */
+ *is_msys_app = TRUE;
+ break;
+ }
}
}
}
unwind:
close_file_data (&executable);
+ return retval;
}
static int
int arglen, numenv;
pid_t pid;
child_process *cp;
- int is_dos_app, is_cygnus_app, is_gui_app;
+ int is_dos_app, is_cygnus_app, is_msys_app, is_gui_app;
int do_quoting = 0;
/* We pass our process ID to our children by setting up an environment
variable in their environment. */
argument being split into two or more. Arguments with wildcards
are also quoted, for consistency with posix platforms, where wildcards
are not expanded if we run the program directly without a shell.
- Some extra whitespace characters need quoting in Cygwin programs,
+ Some extra whitespace characters need quoting in Cygwin/MSYS programs,
so this list is conditionally modified below. */
char *sepchars = " \t*?";
- /* This is for native w32 apps; modified below for Cygwin apps. */
+ /* This is for native w32 apps; modified below for Cygwin/MSUS apps. */
char escape_char = '\\';
char cmdname_a[MAX_PATH];
absolute. So we double-check this here, just in case. */
if (faccessat (AT_FDCWD, cmdname, X_OK, AT_EACCESS) != 0)
{
- struct gcpro gcpro1;
-
program = build_string (cmdname);
full = Qnil;
- GCPRO1 (program);
openp (Vexec_path, program, Vexec_suffixes, &full, make_number (X_OK), 0);
- UNGCPRO;
if (NILP (full))
{
errno = EINVAL;
return -1;
}
program = ENCODE_FILE (full);
- cmdname = SDATA (program);
+ cmdname = SSDATA (program);
}
else
{
/* We explicitly require that the command's file name be encodable
in the current ANSI codepage, because we will be invoking it via
the ANSI APIs. */
- if (_mbspbrk (cmdname_a, "?"))
+ if (_mbspbrk ((unsigned char *)cmdname_a, (const unsigned char *)"?"))
{
errno = ENOENT;
return -1;
cmdname = cmdname_a;
argv[0] = cmdname;
- /* Determine whether program is a 16-bit DOS executable, or a 32-bit Windows
- executable that is implicitly linked to the Cygnus dll (implying it
- was compiled with the Cygnus GNU toolchain and hence relies on
- cygwin.dll to parse the command line - we use this to decide how to
- escape quote chars in command line args that must be quoted).
+ /* Determine whether program is a 16-bit DOS executable, or a 32-bit
+ Windows executable that is implicitly linked to the Cygnus or
+ MSYS dll (implying it was compiled with the Cygnus/MSYS GNU
+ toolchain and hence relies on cygwin.dll or MSYS DLL to parse the
+ command line - we use this to decide how to escape quote chars in
+ command line args that must be quoted).
Also determine whether it is a GUI app, so that we don't hide its
initial window unless specifically requested. */
- w32_executable_type (cmdname, &is_dos_app, &is_cygnus_app, &is_gui_app);
+ w32_executable_type (cmdname, &is_dos_app, &is_cygnus_app, &is_msys_app,
+ &is_gui_app);
/* On Windows 95, if cmdname is a DOS app, we invoke a helper
application to start it by specifying the helper app as cmdname,
cmdname = alloca (MAX_PATH);
if (egetenv ("CMDPROXY"))
- strcpy (cmdname, egetenv ("CMDPROXY"));
+ {
+ /* Implementation note: since process-environment, where
+ 'egetenv' looks, is encoded in the system codepage, we
+ don't need to encode the cmdproxy file name if we get it
+ from the environment. */
+ strcpy (cmdname, egetenv ("CMDPROXY"));
+ }
else
- strcpy (lispstpcpy (cmdname, Vinvocation_directory), "cmdproxy.exe");
+ {
+ char *q = lispstpcpy (cmdname,
+ /* exec-directory needs to be encoded. */
+ ansi_encode_filename (Vexec_directory));
+ /* If we are run from the source tree, use cmdproxy.exe from
+ the same source tree. */
+ for (p = q - 2; p > cmdname; p = CharPrevA (cmdname, p))
+ if (*p == '/')
+ break;
+ if (*p == '/' && xstrcasecmp (p, "/lib-src/") == 0)
+ q = stpcpy (p, "/nt/");
+ strcpy (q, "cmdproxy.exe");
+ }
/* Can't use unixtodos_filename here, since that needs its file
name argument encoded in UTF-8. */
if (INTEGERP (Vw32_quote_process_args))
escape_char = XINT (Vw32_quote_process_args);
else
- escape_char = is_cygnus_app ? '"' : '\\';
+ escape_char = (is_cygnus_app || is_msys_app) ? '"' : '\\';
}
- /* Cygwin apps needs quoting a bit more often. */
+ /* Cygwin/MSYS apps need quoting a bit more often. */
if (escape_char == '"')
sepchars = "\r\n\t\f '";
for ( ; *p; p++)
{
if (escape_char == '"' && *p == '\\')
- /* If it's a Cygwin app, \ needs to be escaped. */
+ /* If it's a Cygwin/MSYS app, \ needs to be escaped. */
arglen++;
else if (*p == '"')
{
return pid;
}
-/* Emulate the select call
+/* Emulate the select call.
Wait for available input on any of the given rfds, or timeout if
- a timeout is given and no input is detected
- wfds and efds are not supported and must be NULL.
+ a timeout is given and no input is detected. wfds are supported
+ only for asynchronous 'connect' calls. efds are not supported
+ and must be NULL.
For simplicity, we detect the death of child processes here and
synchronously call the SIGCHLD handler. Since it is possible for
cp = fd_info[i].cp;
if (FD_ISSET (i, &owfds)
&& cp
- && (fd_info[i].flags && FILE_CONNECT) == 0)
+ && (fd_info[i].flags & FILE_CONNECT) == 0)
{
DebPrint (("sys_select: fd %d is in wfds, but FILE_CONNECT is reset!\n", i));
cp = NULL;
filename = Fexpand_file_name (filename, Qnil);
/* luckily, this returns the short version of each element in the path. */
- if (w32_get_short_filename (SDATA (ENCODE_FILE (filename)),
+ if (w32_get_short_filename (SSDATA (ENCODE_FILE (filename)),
shortname, MAX_PATH) == 0)
return Qnil;
/* first expand it. */
filename = Fexpand_file_name (filename, Qnil);
- if (!w32_get_long_filename (SDATA (ENCODE_FILE (filename)), longname,
+ if (!w32_get_long_filename (SSDATA (ENCODE_FILE (filename)), longname,
MAX_UTF8_PATH))
return Qnil;
return result;
}
+DEFUN ("w32-application-type", Fw32_application_type,
+ Sw32_application_type, 1, 1, 0,
+ doc: /* Return the type of an MS-Windows PROGRAM.
+
+Knowing the type of an executable could be useful for formatting
+file names passed to it or for quoting its command-line arguments.
+
+PROGRAM should specify an executable file, including the extension.
+
+The value is one of the following:
+
+`dos' -- a DOS .com program or some other non-PE executable
+`cygwin' -- a Cygwin program that depends on Cygwin DLL
+`msys' -- an MSYS 1.x or MSYS2 program
+`w32-native' -- a native Windows application
+`unknown' -- a file that doesn't exist, or cannot be open, or whose
+ name is not encodable in the current ANSI codepage.
+
+Note that for .bat and .cmd batch files the function returns the type
+of their command interpreter, as specified by the \"COMSPEC\"
+environment variable.
+
+This function returns `unknown' for programs whose file names
+include characters not supported by the current ANSI codepage, as
+such programs cannot be invoked by Emacs anyway. */)
+ (Lisp_Object program)
+{
+ int is_dos_app, is_cygwin_app, is_msys_app, dummy;
+ Lisp_Object encoded_progname;
+ char *progname, progname_a[MAX_PATH];
+
+ program = Fexpand_file_name (program, Qnil);
+ encoded_progname = ENCODE_FILE (program);
+ progname = SSDATA (encoded_progname);
+ unixtodos_filename (progname);
+ filename_to_ansi (progname, progname_a);
+ /* Reject file names that cannot be encoded in the current ANSI
+ codepage. */
+ if (_mbspbrk ((unsigned char *)progname_a, (const unsigned char *)"?"))
+ return Qunknown;
+
+ if (w32_executable_type (progname_a, &is_dos_app, &is_cygwin_app,
+ &is_msys_app, &dummy) != 0)
+ return Qunknown;
+ if (is_dos_app)
+ return Qdos;
+ if (is_cygwin_app)
+ return Qcygwin;
+ if (is_msys_app)
+ return Qmsys;
+ return Qw32_native;
+}
+
#ifdef HAVE_LANGINFO_CODESET
/* Emulation of nl_langinfo. Used in fns.c:Flocale_info. */
char *
(Lisp_Object cp)
{
CHARSETINFO info;
- DWORD dwcp;
+ DWORD_PTR dwcp;
CHECK_NUMBER (cp);
if (!IsValidCodePage (XINT (cp)))
return Qnil;
- /* Going through a temporary DWORD variable avoids compiler warning
+ /* Going through a temporary DWORD_PTR variable avoids compiler warning
about cast to pointer from integer of different size, when
- building --with-wide-int. */
+ building --with-wide-int or building for 64bit. */
dwcp = XINT (cp);
if (TranslateCharsetInfo ((DWORD *) dwcp, &info, TCI_SRCCODEPAGE))
return make_number (info.ciCharset);
return found_lcid;
}
-#ifndef _NSLCMPERROR
-# define _NSLCMPERROR INT_MAX
+#ifndef _NLSCMPERROR
+# define _NLSCMPERROR INT_MAX
#endif
#ifndef LINGUISTIC_IGNORECASE
# define LINGUISTIC_IGNORECASE 0x00000010
#endif
+typedef int (WINAPI *CompareStringW_Proc)
+ (LCID, DWORD, LPCWSTR, int, LPCWSTR, int);
+
int
w32_compare_strings (const char *s1, const char *s2, char *locname,
int ignore_case)
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);
+ static CompareStringW_Proc pCompareStringW;
DWORD flags = 0;
USE_SAFE_ALLOCA;
{
if (os_subtype == OS_9X)
{
- pCompareStringW = GetProcAddress (LoadLibrary ("Unicows.dll"),
- "CompareStringW");
+ pCompareStringW =
+ (CompareStringW_Proc) GetProcAddress (LoadLibrary ("Unicows.dll"),
+ "CompareStringW");
if (!pCompareStringW)
{
errno = EINVAL;
/* This return value is compatible with wcscoll and
other MS CRT functions. */
- return _NSLCMPERROR;
+ return _NLSCMPERROR;
}
}
else
else
{
errno = EINVAL;
- return _NSLCMPERROR;
+ return _NLSCMPERROR;
}
needed = pMultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, s2, -1, NULL, 0);
{
SAFE_FREE ();
errno = EINVAL;
- return _NSLCMPERROR;
+ return _NLSCMPERROR;
}
if (locname)
if (!val)
{
errno = EINVAL;
- return _NSLCMPERROR;
+ return _NLSCMPERROR;
}
return val - 2;
}
{
DEFSYM (Qhigh, "high");
DEFSYM (Qlow, "low");
+ DEFSYM (Qcygwin, "cygwin");
+ DEFSYM (Qmsys, "msys");
+ DEFSYM (Qw32_native, "w32-native");
defsubr (&Sw32_has_winsock);
defsubr (&Sw32_unload_winsock);
defsubr (&Sw32_short_file_name);
defsubr (&Sw32_long_file_name);
defsubr (&Sw32_set_process_priority);
+ defsubr (&Sw32_application_type);
defsubr (&Sw32_get_locale_info);
defsubr (&Sw32_get_current_locale_id);
defsubr (&Sw32_get_default_locale_id);
process temporarily). A value of zero disables waiting entirely. */);
w32_pipe_read_delay = 50;
+ DEFVAR_INT ("w32-pipe-buffer-size", w32_pipe_buffer_size,
+ doc: /* Size of buffer for pipes created to communicate with subprocesses.
+The size is in bytes, and must be non-negative. The default is zero,
+which lets the OS use its default size, usually 4KB (4096 bytes).
+Any negative value means to use the default value of zero. */);
+ w32_pipe_buffer_size = 0;
+
DEFVAR_LISP ("w32-downcase-file-names", Vw32_downcase_file_names,
doc: /* Non-nil means convert all-upper case file names to lower case.
This applies when performing completions and file name expansion.