--- /dev/null
+/* Process support for Windows NT port of XEMACS.
+ Copyright (C) 1992, 1995 Free Software Foundation, Inc.
+
+This file is part of XEmacs.
+
+XEmacs 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 2, or (at your option) any
+later version.
+
+XEmacs is distributed in the hope that it will be useful, but WITHOUT
+ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with XEmacs; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.
+
+ Drew Bliss Oct 14, 1993
+ Adapted from alarm.c by Tim Fleehart */
+
+/* Adapted for XEmacs by David Hobley <david@spook-le0.cia.com.au> */
+/* Synced with FSF Emacs 19.34.6 by Marc Paquette <marcpa@cam.org> */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <io.h>
+#include <fcntl.h>
+#include <signal.h>
+
+/* must include CRT headers *before* config.h */
+/* ### I don't believe it - martin */
+#include <config.h>
+#undef signal
+#undef wait
+#undef spawnve
+#undef select
+#undef kill
+
+#include <windows.h>
+#include <sys/socket.h>
+
+#include "lisp.h"
+#include "sysproc.h"
+#include "nt.h"
+#include "ntheap.h" /* From 19.34.6 */
+#include "systime.h"
+#include "syssignal.h"
+#include "syswait.h"
+#include "process.h"
+/*#include "w32term.h"*/ /* From 19.34.6: sync in ? --marcpa */
+
+/* #### I'm not going to play with shit. */
+#pragma warning (disable:4013 4024 4090)
+
+/* Control whether spawnve quotes arguments as necessary to ensure
+ correct parsing by child process. Because not all uses of spawnve
+ are careful about constructing argv arrays, we make this behaviour
+ conditional (off by default). */
+Lisp_Object Vwin32_quote_process_args;
+
+/* Control whether create_child causes the process' window to be
+ hidden. The default is nil. */
+Lisp_Object Vwin32_start_process_show_window;
+
+/* Control whether create_child causes the process to inherit Emacs'
+ console window, or be given a new one of its own. The default is
+ nil, to allow multiple DOS programs to run on Win95. Having separate
+ consoles also allows Emacs to cleanly terminate process groups. */
+Lisp_Object Vwin32_start_process_share_console;
+
+/* Time to sleep before reading from a subprocess output pipe - this
+ avoids the inefficiency of frequently reading small amounts of data.
+ This is primarily necessary for handling DOS processes on Windows 95,
+ but is useful for Win32 processes on both Win95 and NT as well. */
+Lisp_Object Vwin32_pipe_read_delay;
+
+/* Control whether stat() attempts to generate fake but hopefully
+ "accurate" inode values, by hashing the absolute truenames of files.
+ This should detect aliasing between long and short names, but still
+ allows the possibility of hash collisions. */
+Lisp_Object Vwin32_generate_fake_inodes;
+
+Lisp_Object Qhigh, Qlow;
+
+#ifndef DEBUG_XEMACS
+__inline
+#endif
+void _DebPrint (const char *fmt, ...)
+{
+#ifdef DEBUG_XEMACS
+ char buf[1024];
+ va_list args;
+
+ va_start (args, fmt);
+ vsprintf (buf, fmt, args);
+ va_end (args);
+ OutputDebugString (buf);
+#endif
+}
+
+/* sys_signal moved to nt.c. It's now called msw_signal... */
+
+/* Defined in <process.h> which conflicts with the local copy */
+#define _P_NOWAIT 1
+
+/* Child process management list. */
+int child_proc_count = 0;
+child_process child_procs[ MAX_CHILDREN ];
+child_process *dead_child = NULL;
+
+DWORD WINAPI reader_thread (void *arg);
+
+/* Find an unused process slot. */
+child_process *
+new_child (void)
+{
+ child_process *cp;
+ DWORD id;
+
+ for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
+ if (!CHILD_ACTIVE (cp))
+ goto Initialize;
+ if (child_proc_count == MAX_CHILDREN)
+ return NULL;
+ cp = &child_procs[child_proc_count++];
+
+ Initialize:
+ xzero (*cp);
+ cp->fd = -1;
+ cp->pid = -1;
+ cp->procinfo.hProcess = NULL;
+ cp->status = STATUS_READ_ERROR;
+
+ /* use manual reset event so that select() will function properly */
+ cp->char_avail = CreateEvent (NULL, TRUE, FALSE, NULL);
+ if (cp->char_avail)
+ {
+ cp->char_consumed = CreateEvent (NULL, FALSE, FALSE, NULL);
+ if (cp->char_consumed)
+ {
+ cp->thrd = CreateThread (NULL, 1024, reader_thread, cp, 0, &id);
+ if (cp->thrd)
+ return cp;
+ }
+ }
+ delete_child (cp);
+ return NULL;
+}
+
+void
+delete_child (child_process *cp)
+{
+ int i;
+
+ /* Should not be deleting a child that is still needed. */
+ for (i = 0; i < MAXDESC; i++)
+ if (fd_info[i].cp == cp)
+ abort ();
+
+ if (!CHILD_ACTIVE (cp))
+ return;
+
+ /* reap thread if necessary */
+ if (cp->thrd)
+ {
+ DWORD rc;
+
+ if (GetExitCodeThread (cp->thrd, &rc) && rc == STILL_ACTIVE)
+ {
+ /* let the thread exit cleanly if possible */
+ cp->status = STATUS_READ_ERROR;
+ SetEvent (cp->char_consumed);
+ if (WaitForSingleObject (cp->thrd, 1000) != WAIT_OBJECT_0)
+ {
+ DebPrint (("delete_child.WaitForSingleObject (thread) failed "
+ "with %lu for fd %ld\n", GetLastError (), cp->fd));
+ TerminateThread (cp->thrd, 0);
+ }
+ }
+ CloseHandle (cp->thrd);
+ cp->thrd = NULL;
+ }
+ if (cp->char_avail)
+ {
+ CloseHandle (cp->char_avail);
+ cp->char_avail = NULL;
+ }
+ if (cp->char_consumed)
+ {
+ CloseHandle (cp->char_consumed);
+ cp->char_consumed = NULL;
+ }
+
+ /* update child_proc_count (highest numbered slot in use plus one) */
+ if (cp == child_procs + child_proc_count - 1)
+ {
+ for (i = child_proc_count-1; i >= 0; i--)
+ if (CHILD_ACTIVE (&child_procs[i]))
+ {
+ child_proc_count = i + 1;
+ break;
+ }
+ }
+ if (i < 0)
+ child_proc_count = 0;
+}
+
+/* Find a child by pid. */
+static child_process *
+find_child_pid (DWORD pid)
+{
+ child_process *cp;
+
+ for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
+ if (CHILD_ACTIVE (cp) && pid == cp->pid)
+ return cp;
+ return NULL;
+}
+
+
+/* Thread proc for child process and socket reader threads. Each thread
+ is normally blocked until woken by select() to check for input by
+ reading one char. When the read completes, char_avail is signalled
+ to wake up the select emulator and the thread blocks itself again. */
+DWORD WINAPI
+reader_thread (void *arg)
+{
+ child_process *cp;
+
+ /* Our identity */
+ cp = (child_process *)arg;
+
+ /* We have to wait for the go-ahead before we can start */
+ if (cp == NULL ||
+ WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
+ return 1;
+
+ for (;;)
+ {
+ int rc;
+
+ rc = _sys_read_ahead (cp->fd);
+
+ /* The name char_avail is a misnomer - it really just means the
+ read-ahead has completed, whether successfully or not. */
+ if (!SetEvent (cp->char_avail))
+ {
+ DebPrint (("reader_thread.SetEvent failed with %lu for fd %ld\n",
+ GetLastError (), cp->fd));
+ return 1;
+ }
+
+ if (rc == STATUS_READ_ERROR)
+ return 1;
+
+ /* If the read died, the child has died so let the thread die */
+ if (rc == STATUS_READ_FAILED)
+ break;
+
+ /* Wait until our input is acknowledged before reading again */
+ if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
+ {
+ DebPrint (("reader_thread.WaitForSingleObject failed with "
+ "%lu for fd %ld\n", GetLastError (), cp->fd));
+ break;
+ }
+ }
+ return 0;
+}
+
+/* To avoid Emacs changing directory, we just record here the directory
+ the new process should start in. This is set just before calling
+ sys_spawnve, and is not generally valid at any other time. */
+static const char * process_dir;
+
+static BOOL
+create_child (char *exe, char *cmdline, char *env,
+ int * pPid, child_process *cp)
+{
+ STARTUPINFO start;
+ SECURITY_ATTRIBUTES sec_attrs;
+ SECURITY_DESCRIPTOR sec_desc;
+ char dir[ MAXPATHLEN ];
+
+ if (cp == NULL) abort ();
+
+ xzero (start);
+ start.cb = sizeof (start);
+
+#ifdef HAVE_NTGUI
+ if (NILP (Vwin32_start_process_show_window))
+ start.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ else
+ start.dwFlags = STARTF_USESTDHANDLES;
+ start.wShowWindow = SW_HIDE;
+
+ start.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
+ start.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
+ start.hStdError = GetStdHandle (STD_ERROR_HANDLE);
+#endif /* HAVE_NTGUI */
+
+ /* Explicitly specify no security */
+ if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
+ goto EH_Fail;
+ if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
+ goto EH_Fail;
+ sec_attrs.nLength = sizeof (sec_attrs);
+ sec_attrs.lpSecurityDescriptor = &sec_desc;
+ sec_attrs.bInheritHandle = FALSE;
+
+ strcpy (dir, process_dir);
+ unixtodos_filename (dir);
+
+ if (!CreateProcess (exe, cmdline, &sec_attrs, NULL, TRUE,
+ (!NILP (Vwin32_start_process_share_console)
+ ? CREATE_NEW_PROCESS_GROUP
+ : CREATE_NEW_CONSOLE),
+ env, dir,
+ &start, &cp->procinfo))
+ goto EH_Fail;
+
+ cp->pid = (int) cp->procinfo.dwProcessId;
+
+ /* Hack for Windows 95, which assigns large (ie negative) pids */
+ if (cp->pid < 0)
+ cp->pid = -cp->pid;
+
+ /* pid must fit in a Lisp_Int */
+#ifdef USE_UNION_TYPE
+ cp->pid = (cp->pid & ((1U << VALBITS) - 1));
+#else
+ cp->pid = (cp->pid & VALMASK);
+#endif
+
+ *pPid = cp->pid;
+
+ return TRUE;
+
+ EH_Fail:
+ DebPrint (("create_child.CreateProcess failed: %ld\n", GetLastError()););
+ return FALSE;
+}
+
+void
+win32_executable_type (char * filename, int * is_dos_app, int * is_cygnus_app)
+{
+ file_data executable;
+ char * p;
+
+ /* Default values in case we can't tell for sure. */
+ *is_dos_app = FALSE;
+ *is_cygnus_app = FALSE;
+
+ if (!open_input_file (&executable, filename))
+ return;
+
+ p = strrchr (filename, '.');
+
+ /* We can only identify DOS .com programs from the extension. */
+ if (p && stricmp (p, ".com") == 0)
+ *is_dos_app = TRUE;
+ else if (p && (stricmp (p, ".bat") == 0 ||
+ stricmp (p, ".cmd") == 0))
+ {
+ /* A DOS shell script - it appears that CreateProcess is happy to
+ accept this (somewhat surprisingly); presumably it looks at
+ COMSPEC to determine what executable to actually invoke.
+ Therefore, we have to do the same here as well. */
+ /* Actually, I think it uses the program association for that
+ extension, which is defined in the registry. */
+ p = egetenv ("COMSPEC");
+ if (p)
+ win32_executable_type (p, is_dos_app, is_cygnus_app);
+ }
+ else
+ {
+ /* Look for DOS .exe signature - if found, we must also check that
+ it isn't really a 16- or 32-bit Windows exe, since both formats
+ start with a DOS program stub. Note that 16-bit Windows
+ executables use the OS/2 1.x format. */
+
+ IMAGE_DOS_HEADER * dos_header;
+ IMAGE_NT_HEADERS * nt_header;
+
+ dos_header = (PIMAGE_DOS_HEADER) executable.file_base;
+ if (dos_header->e_magic != IMAGE_DOS_SIGNATURE)
+ goto unwind;
+
+ nt_header = (PIMAGE_NT_HEADERS) ((char *) dos_header + dos_header->e_lfanew);
+
+ if ((char *) nt_header > (char *) dos_header + executable.size)
+ {
+ /* Some dos headers (pkunzip) have bogus e_lfanew fields. */
+ *is_dos_app = TRUE;
+ }
+ else if (nt_header->Signature != IMAGE_NT_SIGNATURE &&
+ LOWORD (nt_header->Signature) != IMAGE_OS2_SIGNATURE)
+ {
+ *is_dos_app = TRUE;
+ }
+ else if (nt_header->Signature == IMAGE_NT_SIGNATURE)
+ {
+ /* Look for cygwin.dll in DLL import list. */
+ IMAGE_DATA_DIRECTORY import_dir =
+ nt_header->OptionalHeader.DataDirectory[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);
+
+ for ( ; imports->Name; imports++)
+ {
+ char * dllname = RVA_TO_PTR (imports->Name, section, executable);
+
+ if (strcmp (dllname, "cygwin.dll") == 0)
+ {
+ *is_cygnus_app = TRUE;
+ break;
+ }
+ }
+ }
+ }
+
+unwind:
+ close_file_data (&executable);
+}
+
+int
+compare_env (const char **strp1, const char **strp2)
+{
+ const char *str1 = *strp1, *str2 = *strp2;
+
+ while (*str1 && *str2 && *str1 != '=' && *str2 != '=')
+ {
+ if ((*str1) > (*str2))
+ return 1;
+ else if ((*str1) < (*str2))
+ return -1;
+ str1++, str2++;
+ }
+
+ if (*str1 == '=' && *str2 == '=')
+ return 0;
+ else if (*str1 == '=')
+ return -1;
+ else
+ return 1;
+}
+
+void
+merge_and_sort_env (char **envp1, char **envp2, char **new_envp)
+{
+ char **optr, **nptr;
+ int num;
+
+ nptr = new_envp;
+ optr = envp1;
+ while (*optr)
+ *nptr++ = *optr++;
+ num = optr - envp1;
+
+ optr = envp2;
+ while (*optr)
+ *nptr++ = *optr++;
+ num += optr - envp2;
+
+ qsort (new_envp, num, sizeof (char *), compare_env);
+
+ *nptr = NULL;
+}
+
+/* When a new child process is created we need to register it in our list,
+ so intercept spawn requests. */
+int
+sys_spawnve (int mode, CONST char *cmdname,
+ CONST char * CONST *argv, CONST char *CONST *envp)
+{
+ Lisp_Object program, full;
+ char *cmdline, *env, *parg, **targ;
+ int arglen, numenv;
+ int pid;
+ child_process *cp;
+ int is_dos_app, is_cygnus_app;
+ int do_quoting = 0;
+ char escape_char;
+ /* We pass our process ID to our children by setting up an environment
+ variable in their environment. */
+ char ppid_env_var_buffer[64];
+ char *extra_env[] = {ppid_env_var_buffer, NULL};
+ struct gcpro gcpro1;
+
+ /* We don't care about the other modes */
+ if (mode != _P_NOWAIT)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* Handle executable names without an executable suffix. */
+ program = make_string (cmdname, strlen (cmdname));
+ GCPRO1 (program);
+ if (NILP (Ffile_executable_p (program)))
+ {
+ full = Qnil;
+ locate_file (Vexec_path, program, EXEC_SUFFIXES, &full, 1);
+ if (NILP (full))
+ {
+ UNGCPRO;
+ errno = EINVAL;
+ return -1;
+ }
+ cmdname = XSTRING_DATA (full);
+ /* #### KLUDGE */
+ *(char**)(argv[0]) = cmdname;
+ }
+ UNGCPRO;
+
+ /* make sure argv[0] and cmdname are both in DOS format */
+ strcpy (cmdname = alloca (strlen (cmdname) + 1), argv[0]);
+ unixtodos_filename (cmdname);
+ /* #### KLUDGE */
+ *(char**)(argv[0]) = cmdname;
+
+ /* Determine whether program is a 16-bit DOS executable, or a Win32
+ 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). */
+ win32_executable_type (cmdname, &is_dos_app, &is_cygnus_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,
+ while leaving the real app name as argv[0]. */
+ if (is_dos_app)
+ {
+ cmdname = alloca (MAXPATHLEN);
+ if (egetenv ("CMDPROXY"))
+ strcpy (cmdname, egetenv ("CMDPROXY"));
+ else
+ {
+ strcpy (cmdname, XSTRING_DATA (Vinvocation_directory));
+ strcat (cmdname, "cmdproxy.exe");
+ }
+ unixtodos_filename (cmdname);
+ }
+
+ /* we have to do some conjuring here to put argv and envp into the
+ form CreateProcess wants... argv needs to be a space separated/null
+ terminated list of parameters, and envp is a null
+ separated/double-null terminated list of parameters.
+
+ Additionally, zero-length args and args containing whitespace or
+ quote chars need to be wrapped in double quotes - for this to work,
+ embedded quotes need to be escaped as well. The aim is to ensure
+ the child process reconstructs the argv array we start with
+ exactly, so we treat quotes at the beginning and end of arguments
+ as embedded quotes.
+
+ The Win32 GNU-based library from Cygnus doubles quotes to escape
+ them, while MSVC uses backslash for escaping. (Actually the MSVC
+ startup code does attempt to recognise doubled quotes and accept
+ them, but gets it wrong and ends up requiring three quotes to get a
+ single embedded quote!) So by default we decide whether to use
+ quote or backslash as the escape character based on whether the
+ binary is apparently a Cygnus compiled app.
+
+ Note that using backslash to escape embedded quotes requires
+ additional special handling if an embedded quote is already
+ preceeded by backslash, or if an arg requiring quoting ends with
+ backslash. In such cases, the run of escape characters needs to be
+ doubled. For consistency, we apply this special handling as long
+ as the escape character is not quote.
+
+ Since we have no idea how large argv and envp are likely to be we
+ figure out list lengths on the fly and allocate them. */
+
+ if (!NILP (Vwin32_quote_process_args))
+ {
+ do_quoting = 1;
+ /* Override escape char by binding win32-quote-process-args to
+ desired character, or use t for auto-selection. */
+ if (INTP (Vwin32_quote_process_args))
+ escape_char = XINT (Vwin32_quote_process_args);
+ else
+ escape_char = is_cygnus_app ? '"' : '\\';
+ }
+
+ /* do argv... */
+ arglen = 0;
+ targ = argv;
+ while (*targ)
+ {
+ char * p = *targ;
+ int need_quotes = 0;
+ int escape_char_run = 0;
+
+ if (*p == 0)
+ need_quotes = 1;
+ for ( ; *p; p++)
+ {
+ if (*p == '"')
+ {
+ /* allow for embedded quotes to be escaped */
+ arglen++;
+ need_quotes = 1;
+ /* handle the case where the embedded quote is already escaped */
+ if (escape_char_run > 0)
+ {
+ /* To preserve the arg exactly, we need to double the
+ preceding escape characters (plus adding one to
+ escape the quote character itself). */
+ arglen += escape_char_run;
+ }
+ }
+ else if (*p == ' ' || *p == '\t')
+ {
+ need_quotes = 1;
+ }
+
+ if (*p == escape_char && escape_char != '"')
+ escape_char_run++;
+ else
+ escape_char_run = 0;
+ }
+ if (need_quotes)
+ {
+ arglen += 2;
+ /* handle the case where the arg ends with an escape char - we
+ must not let the enclosing quote be escaped. */
+ if (escape_char_run > 0)
+ arglen += escape_char_run;
+ }
+ arglen += strlen (*targ++) + 1;
+ }
+ cmdline = alloca (arglen);
+ targ = argv;
+ parg = cmdline;
+ while (*targ)
+ {
+ char * p = *targ;
+ int need_quotes = 0;
+
+ if (*p == 0)
+ need_quotes = 1;
+
+ if (do_quoting)
+ {
+ for ( ; *p; p++)
+ if (*p == ' ' || *p == '\t' || *p == '"')
+ need_quotes = 1;
+ }
+ if (need_quotes)
+ {
+ int escape_char_run = 0;
+ char * first;
+ char * last;
+
+ p = *targ;
+ first = p;
+ last = p + strlen (p) - 1;
+ *parg++ = '"';
+#if 0
+ /* This version does not escape quotes if they occur at the
+ beginning or end of the arg - this could lead to incorrect
+ behaviour when the arg itself represents a command line
+ containing quoted args. I believe this was originally done
+ as a hack to make some things work, before
+ `win32-quote-process-args' was added. */
+ while (*p)
+ {
+ if (*p == '"' && p > first && p < last)
+ *parg++ = escape_char; /* escape embedded quotes */
+ *parg++ = *p++;
+ }
+#else
+ for ( ; *p; p++)
+ {
+ if (*p == '"')
+ {
+ /* double preceding escape chars if any */
+ while (escape_char_run > 0)
+ {
+ *parg++ = escape_char;
+ escape_char_run--;
+ }
+ /* escape all quote chars, even at beginning or end */
+ *parg++ = escape_char;
+ }
+ *parg++ = *p;
+
+ if (*p == escape_char && escape_char != '"')
+ escape_char_run++;
+ else
+ escape_char_run = 0;
+ }
+ /* double escape chars before enclosing quote */
+ while (escape_char_run > 0)
+ {
+ *parg++ = escape_char;
+ escape_char_run--;
+ }
+#endif
+ *parg++ = '"';
+ }
+ else
+ {
+ strcpy (parg, *targ);
+ parg += strlen (*targ);
+ }
+ *parg++ = ' ';
+ targ++;
+ }
+ *--parg = '\0';
+
+ /* and envp... */
+ arglen = 1;
+ targ = envp;
+ numenv = 1; /* for end null */
+ while (*targ)
+ {
+ arglen += strlen (*targ++) + 1;
+ numenv++;
+ }
+ /* extra env vars... */
+ sprintf (ppid_env_var_buffer, "__PARENT_PROCESS_ID=%d",
+ GetCurrentProcessId ());
+ arglen += strlen (ppid_env_var_buffer) + 1;
+ numenv++;
+
+ /* merge env passed in and extra env into one, and sort it. */
+ targ = (char **) alloca (numenv * sizeof (char *));
+ merge_and_sort_env (envp, extra_env, targ);
+
+ /* concatenate env entries. */
+ env = alloca (arglen);
+ parg = env;
+ while (*targ)
+ {
+ strcpy (parg, *targ);
+ parg += strlen (*targ++);
+ *parg++ = '\0';
+ }
+ *parg++ = '\0';
+ *parg = '\0';
+
+ cp = new_child ();
+ if (cp == NULL)
+ {
+ errno = EAGAIN;
+ return -1;
+ }
+
+ /* Now create the process. */
+ if (!create_child (cmdname, cmdline, env, &pid, cp))
+ {
+ delete_child (cp);
+ errno = ENOEXEC;
+ return -1;
+ }
+
+ return pid;
+}
+
+/* Substitute for certain kill () operations */
+
+static BOOL CALLBACK
+find_child_console (HWND hwnd, child_process * cp)
+{
+ DWORD thread_id;
+ DWORD process_id;
+
+ thread_id = GetWindowThreadProcessId (hwnd, &process_id);
+ if (process_id == cp->procinfo.dwProcessId)
+ {
+ char window_class[32];
+
+ GetClassName (hwnd, window_class, sizeof (window_class));
+ if (strcmp (window_class,
+ (os_subtype == OS_WIN95)
+ ? "tty"
+ : "ConsoleWindowClass") == 0)
+ {
+ cp->hwnd = hwnd;
+ return FALSE;
+ }
+ }
+ /* keep looking */
+ return TRUE;
+}
+
+int
+sys_kill (int pid, int sig)
+{
+ child_process *cp;
+ HANDLE proc_hand;
+ int need_to_free = 0;
+ int rc = 0;
+
+ /* Only handle signals that will result in the process dying */
+ if (sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+
+ cp = find_child_pid (pid);
+ if (cp == NULL)
+ {
+ proc_hand = OpenProcess (PROCESS_TERMINATE, 0, pid);
+ if (proc_hand == NULL)
+ {
+ errno = EPERM;
+ return -1;
+ }
+ need_to_free = 1;
+ }
+ else
+ {
+ proc_hand = cp->procinfo.hProcess;
+ pid = cp->procinfo.dwProcessId;
+
+ /* Try to locate console window for process. */
+ EnumWindows (find_child_console, (LPARAM) cp);
+ }
+
+ if (sig == SIGINT)
+ {
+ if (NILP (Vwin32_start_process_share_console) && cp && cp->hwnd)
+ {
+ BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0);
+ BYTE vk_break_code = VK_CANCEL;
+ BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
+ HWND foreground_window;
+
+ if (break_scan_code == 0)
+ {
+ /* Fake Ctrl-C if we can't manage Ctrl-Break. */
+ vk_break_code = 'C';
+ break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
+ }
+
+ foreground_window = GetForegroundWindow ();
+ if (foreground_window && SetForegroundWindow (cp->hwnd))
+ {
+ /* Generate keystrokes as if user had typed Ctrl-Break or Ctrl-C. */
+ keybd_event (VK_CONTROL, control_scan_code, 0, 0);
+ keybd_event (vk_break_code, break_scan_code, 0, 0);
+ keybd_event (vk_break_code, break_scan_code, KEYEVENTF_KEYUP, 0);
+ keybd_event (VK_CONTROL, control_scan_code, KEYEVENTF_KEYUP, 0);
+
+ /* Sleep for a bit to give time for Emacs frame to respond
+ to focus change events (if Emacs was active app). */
+ Sleep (10);
+
+ SetForegroundWindow (foreground_window);
+ }
+ }
+ /* Ctrl-Break is NT equivalent of SIGINT. */
+ else if (!GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid))
+ {
+ DebPrint (("sys_kill.GenerateConsoleCtrlEvent return %d "
+ "for pid %lu\n", GetLastError (), pid));
+ errno = EINVAL;
+ rc = -1;
+ }
+ }
+ else
+ {
+ if (NILP (Vwin32_start_process_share_console) && cp && cp->hwnd)
+ {
+#if 1
+ if (os_subtype == OS_WIN95)
+ {
+/*
+ Another possibility is to try terminating the VDM out-right by
+ calling the Shell VxD (id 0x17) V86 interface, function #4
+ "SHELL_Destroy_VM", ie.
+
+ mov edx,4
+ mov ebx,vm_handle
+ call shellapi
+
+ First need to determine the current VM handle, and then arrange for
+ the shellapi call to be made from the system vm (by using
+ Switch_VM_and_callback).
+
+ Could try to invoke DestroyVM through CallVxD.
+
+*/
+#if 0
+ /* On Win95, posting WM_QUIT causes the 16-bit subsystem
+ to hang when cmdproxy is used in conjunction with
+ command.com for an interactive shell. Posting
+ WM_CLOSE pops up a dialog that, when Yes is selected,
+ does the same thing. TerminateProcess is also less
+ than ideal in that subprocesses tend to stick around
+ until the machine is shutdown, but at least it
+ doesn't freeze the 16-bit subsystem. */
+ PostMessage (cp->hwnd, WM_QUIT, 0xff, 0);
+#endif
+ if (!TerminateProcess (proc_hand, 0xff))
+ {
+ DebPrint (("sys_kill.TerminateProcess returned %d "
+ "for pid %lu\n", GetLastError (), pid));
+ errno = EINVAL;
+ rc = -1;
+ }
+ }
+ else
+#endif
+ PostMessage (cp->hwnd, WM_CLOSE, 0, 0);
+ }
+ /* Kill the process. On Win32 this doesn't kill child processes
+ so it doesn't work very well for shells which is why it's not
+ used in every case. */
+ else if (!TerminateProcess (proc_hand, 0xff))
+ {
+ DebPrint (("sys_kill.TerminateProcess returned %d "
+ "for pid %lu\n", GetLastError (), pid));
+ errno = EINVAL;
+ rc = -1;
+ }
+ }
+
+ if (need_to_free)
+ CloseHandle (proc_hand);
+
+ return rc;
+}
+
+#if 0
+/* Sync with FSF Emacs 19.34.6 note: ifdef'ed out in XEmacs */
+extern int report_file_error (CONST char *, Lisp_Object);
+#endif
+/* The following two routines are used to manipulate stdin, stdout, and
+ stderr of our child processes.
+
+ Assuming that in, out, and err are *not* inheritable, we make them
+ stdin, stdout, and stderr of the child as follows:
+
+ - Save the parent's current standard handles.
+ - Set the std handles to inheritable duplicates of the ones being passed in.
+ (Note that _get_osfhandle() is an io.h procedure that retrieves the
+ NT file handle for a crt file descriptor.)
+ - Spawn the child, which inherits in, out, and err as stdin,
+ stdout, and stderr. (see Spawnve)
+ - Close the std handles passed to the child.
+ - Reset the parent's standard handles to the saved handles.
+ (see reset_standard_handles)
+ We assume that the caller closes in, out, and err after calling us. */
+
+void
+prepare_standard_handles (int in, int out, int err, HANDLE handles[3])
+{
+ HANDLE parent;
+ HANDLE newstdin, newstdout, newstderr;
+
+ parent = GetCurrentProcess ();
+
+ handles[0] = GetStdHandle (STD_INPUT_HANDLE);
+ handles[1] = GetStdHandle (STD_OUTPUT_HANDLE);
+ handles[2] = GetStdHandle (STD_ERROR_HANDLE);
+
+ /* make inheritable copies of the new handles */
+ if (!DuplicateHandle (parent,
+ (HANDLE) _get_osfhandle (in),
+ parent,
+ &newstdin,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS))
+ report_file_error ("Duplicating input handle for child", Qnil);
+
+ if (!DuplicateHandle (parent,
+ (HANDLE) _get_osfhandle (out),
+ parent,
+ &newstdout,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS))
+ report_file_error ("Duplicating output handle for child", Qnil);
+
+ if (!DuplicateHandle (parent,
+ (HANDLE) _get_osfhandle (err),
+ parent,
+ &newstderr,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS))
+ report_file_error ("Duplicating error handle for child", Qnil);
+
+ /* and store them as our std handles */
+ if (!SetStdHandle (STD_INPUT_HANDLE, newstdin))
+ report_file_error ("Changing stdin handle", Qnil);
+
+ if (!SetStdHandle (STD_OUTPUT_HANDLE, newstdout))
+ report_file_error ("Changing stdout handle", Qnil);
+
+ if (!SetStdHandle (STD_ERROR_HANDLE, newstderr))
+ report_file_error ("Changing stderr handle", Qnil);
+}
+
+void
+reset_standard_handles (int in, int out, int err, HANDLE handles[3])
+{
+ /* close the duplicated handles passed to the child */
+ CloseHandle (GetStdHandle (STD_INPUT_HANDLE));
+ CloseHandle (GetStdHandle (STD_OUTPUT_HANDLE));
+ CloseHandle (GetStdHandle (STD_ERROR_HANDLE));
+
+ /* now restore parent's saved std handles */
+ SetStdHandle (STD_INPUT_HANDLE, handles[0]);
+ SetStdHandle (STD_OUTPUT_HANDLE, handles[1]);
+ SetStdHandle (STD_ERROR_HANDLE, handles[2]);
+}
+
+void
+set_process_dir (const char * dir)
+{
+ process_dir = dir;
+}
+\f
+/* Some miscellaneous functions that are Windows specific, but not GUI
+ specific (ie. are applicable in terminal or batch mode as well). */
+
+/* lifted from fileio.c */
+#define CORRECT_DIR_SEPS(s) \
+ do { if ('/' == DIRECTORY_SEP) dostounix_filename (s); \
+ else unixtodos_filename (s); \
+ } while (0)
+
+DEFUN ("win32-short-file-name", Fwin32_short_file_name, 1, 1, "", /*
+ Return the short file name version (8.3) of the full path of FILENAME.
+If FILENAME does not exist, return nil.
+All path elements in FILENAME are converted to their short names.
+*/
+ (filename))
+{
+ char shortname[MAX_PATH];
+
+ CHECK_STRING (filename);
+
+ /* first expand it. */
+ filename = Fexpand_file_name (filename, Qnil);
+
+ /* luckily, this returns the short version of each element in the path. */
+ if (GetShortPathName (XSTRING_DATA (filename), shortname, MAX_PATH) == 0)
+ return Qnil;
+
+ CORRECT_DIR_SEPS (shortname);
+
+ return build_string (shortname);
+}
+
+
+DEFUN ("win32-long-file-name", Fwin32_long_file_name, 1, 1, "", /*
+ Return the long file name version of the full path of FILENAME.
+If FILENAME does not exist, return nil.
+All path elements in FILENAME are converted to their long names.
+*/
+ (filename))
+{
+ char longname[ MAX_PATH ];
+
+ CHECK_STRING (filename);
+
+ /* first expand it. */
+ filename = Fexpand_file_name (filename, Qnil);
+
+ if (!win32_get_long_filename (XSTRING_DATA (filename), longname, MAX_PATH))
+ return Qnil;
+
+ CORRECT_DIR_SEPS (longname);
+
+ return build_string (longname);
+}
+
+DEFUN ("win32-set-process-priority", Fwin32_set_process_priority, 2, 2, "", /*
+ Set the priority of PROCESS to PRIORITY.
+If PROCESS is nil, the priority of Emacs is changed, otherwise the
+priority of the process whose pid is PROCESS is changed.
+PRIORITY should be one of the symbols high, normal, or low;
+any other symbol will be interpreted as normal.
+
+If successful, the return value is t, otherwise nil.
+*/
+ (process, priority))
+{
+ HANDLE proc_handle = GetCurrentProcess ();
+ DWORD priority_class = NORMAL_PRIORITY_CLASS;
+ Lisp_Object result = Qnil;
+
+ CHECK_SYMBOL (priority);
+
+ if (!NILP (process))
+ {
+ DWORD pid;
+ child_process *cp;
+
+ CHECK_INT (process);
+
+ /* Allow pid to be an internally generated one, or one obtained
+ externally. This is necessary because real pids on Win95 are
+ negative. */
+
+ pid = XINT (process);
+ cp = find_child_pid (pid);
+ if (cp != NULL)
+ pid = cp->procinfo.dwProcessId;
+
+ proc_handle = OpenProcess (PROCESS_SET_INFORMATION, FALSE, pid);
+ }
+
+ if (EQ (priority, Qhigh))
+ priority_class = HIGH_PRIORITY_CLASS;
+ else if (EQ (priority, Qlow))
+ priority_class = IDLE_PRIORITY_CLASS;
+
+ if (proc_handle != NULL)
+ {
+ if (SetPriorityClass (proc_handle, priority_class))
+ result = Qt;
+ if (!NILP (process))
+ CloseHandle (proc_handle);
+ }
+
+ return result;
+}
+
+
+DEFUN ("win32-get-locale-info", Fwin32_get_locale_info, 1, 2, "", /*
+ "Return information about the Windows locale LCID.
+By default, return a three letter locale code which encodes the default
+language as the first two characters, and the country or regionial variant
+as the third letter. For example, ENU refers to `English (United States)',
+while ENC means `English (Canadian)'.
+
+If the optional argument LONGFORM is non-nil, the long form of the locale
+name is returned, e.g. `English (United States)' instead.
+
+If LCID (a 16-bit number) is not a valid locale, the result is nil.
+*/
+ (lcid, longform))
+{
+ int got_abbrev;
+ int got_full;
+ char abbrev_name[32] = { 0 };
+ char full_name[256] = { 0 };
+
+ CHECK_INT (lcid);
+
+ if (!IsValidLocale (XINT (lcid), LCID_SUPPORTED))
+ return Qnil;
+
+ if (NILP (longform))
+ {
+ got_abbrev = GetLocaleInfo (XINT (lcid),
+ LOCALE_SABBREVLANGNAME | LOCALE_USE_CP_ACP,
+ abbrev_name, sizeof (abbrev_name));
+ if (got_abbrev)
+ return build_string (abbrev_name);
+ }
+ else
+ {
+ got_full = GetLocaleInfo (XINT (lcid),
+ LOCALE_SLANGUAGE | LOCALE_USE_CP_ACP,
+ full_name, sizeof (full_name));
+ if (got_full)
+ return build_string (full_name);
+ }
+
+ return Qnil;
+}
+
+
+DEFUN ("win32-get-current-locale-id", Fwin32_get_current_locale_id, 0, 0, "", /*
+ "Return Windows locale id for current locale setting.
+This is a numerical value; use `win32-get-locale-info' to convert to a
+human-readable form.
+*/
+ ())
+{
+ return make_int (GetThreadLocale ());
+}
+
+
+DEFUN ("win32-get-default-locale-id", Fwin32_get_default_locale_id, 0, 1, "", /*
+ "Return Windows locale id for default locale setting.
+By default, the system default locale setting is returned; if the optional
+parameter USERP is non-nil, the user default locale setting is returned.
+This is a numerical value; use `win32-get-locale-info' to convert to a
+human-readable form.
+*/
+ (userp))
+{
+ if (NILP (userp))
+ return make_int (GetSystemDefaultLCID ());
+ return make_int (GetUserDefaultLCID ());
+}
+
+DWORD int_from_hex (char * s)
+{
+ DWORD val = 0;
+ static char hex[] = "0123456789abcdefABCDEF";
+ char * p;
+
+ while (*s && (p = strchr(hex, *s)) != NULL)
+ {
+ unsigned digit = p - hex;
+ if (digit > 15)
+ digit -= 6;
+ val = val * 16 + digit;
+ s++;
+ }
+ return val;
+}
+
+/* We need to build a global list, since the EnumSystemLocale callback
+ function isn't given a context pointer. */
+Lisp_Object Vwin32_valid_locale_ids;
+
+BOOL CALLBACK enum_locale_fn (LPTSTR localeNum)
+{
+ DWORD id = int_from_hex (localeNum);
+ Vwin32_valid_locale_ids = Fcons (make_int (id), Vwin32_valid_locale_ids);
+ return TRUE;
+}
+
+DEFUN ("win32-get-valid-locale-ids", Fwin32_get_valid_locale_ids, 0, 0, "", /*
+ Return list of all valid Windows locale ids.
+Each id is a numerical value; use `win32-get-locale-info' to convert to a
+human-readable form.
+*/
+ ())
+{
+ Vwin32_valid_locale_ids = Qnil;
+
+ EnumSystemLocales (enum_locale_fn, LCID_SUPPORTED);
+
+ Vwin32_valid_locale_ids = Fnreverse (Vwin32_valid_locale_ids);
+ return Vwin32_valid_locale_ids;
+}
+
+
+DEFUN ("win32-set-current-locale", Fwin32_set_current_locale, 1, 1, "", /*
+ Make Windows locale LCID be the current locale setting for Emacs.
+If successful, the new locale id is returned, otherwise nil.
+*/
+ (lcid))
+{
+ CHECK_INT (lcid);
+
+ if (!IsValidLocale (XINT (lcid), LCID_SUPPORTED))
+ return Qnil;
+
+ if (!SetThreadLocale (XINT (lcid)))
+ return Qnil;
+
+/* Sync with FSF Emacs 19.34.6 note: dwWinThreadId declared in
+ w32term.h and defined in w32fns.c, both of which are not in current
+ XEmacs. ### Check what we lose by ifdef'ing out these. --marcpa */
+#if 0
+ /* Need to set input thread locale if present. */
+ if (dwWinThreadId)
+ /* Reply is not needed. */
+ PostThreadMessage (dwWinThreadId, WM_EMACS_SETLOCALE, XINT (lcid), 0);
+#endif
+
+ return make_int (GetThreadLocale ());
+}
+
+\f
+void
+syms_of_ntproc ()
+{
+ Qhigh = intern ("high");
+ Qlow = intern ("low");
+
+ DEFSUBR (Fwin32_short_file_name);
+ DEFSUBR (Fwin32_long_file_name);
+ DEFSUBR (Fwin32_set_process_priority);
+ DEFSUBR (Fwin32_get_locale_info);
+ DEFSUBR (Fwin32_get_current_locale_id);
+ DEFSUBR (Fwin32_get_default_locale_id);
+ DEFSUBR (Fwin32_get_valid_locale_ids);
+ DEFSUBR (Fwin32_set_current_locale);
+
+ DEFVAR_LISP ("win32-quote-process-args", &Vwin32_quote_process_args /*
+ Non-nil enables quoting of process arguments to ensure correct parsing.
+Because Windows does not directly pass argv arrays to child processes,
+programs have to reconstruct the argv array by parsing the command
+line string. For an argument to contain a space, it must be enclosed
+in double quotes or it will be parsed as multiple arguments.
+
+If the value is a character, that character will be used to escape any
+quote characters that appear, otherwise a suitable escape character
+will be chosen based on the type of the program.
+*/ );
+ Vwin32_quote_process_args = Qt;
+
+ DEFVAR_LISP ("win32-start-process-show-window",
+ &Vwin32_start_process_show_window /*
+ When nil, processes started via start-process hide their windows.
+When non-nil, they show their window in the method of their choice.
+*/ );
+ Vwin32_start_process_show_window = Qnil;
+
+ DEFVAR_LISP ("win32-start-process-share-console",
+ &Vwin32_start_process_share_console /*
+ When nil, processes started via start-process are given a new console.
+When non-nil, they share the Emacs console; this has the limitation of
+allowing only only DOS subprocess to run at a time (whether started directly
+or indirectly by Emacs), and preventing Emacs from cleanly terminating the
+subprocess group, but may allow Emacs to interrupt a subprocess that doesn't
+otherwise respond to interrupts from Emacs.
+*/ );
+ Vwin32_start_process_share_console = Qnil;
+
+ DEFVAR_LISP ("win32-pipe-read-delay", &Vwin32_pipe_read_delay /*
+ Forced delay before reading subprocess output.
+This is done to improve the buffering of subprocess output, by
+avoiding the inefficiency of frequently reading small amounts of data.
+
+If positive, the value is the number of milliseconds to sleep before
+reading the subprocess output. If negative, the magnitude is the number
+of time slices to wait (effectively boosting the priority of the child
+process temporarily). A value of zero disables waiting entirely.
+*/ );
+ Vwin32_pipe_read_delay = make_int (50);
+
+#if 0
+ DEFVAR_LISP ("win32-generate-fake-inodes", &Vwin32_generate_fake_inodes /*
+ "Non-nil means attempt to fake realistic inode values.
+This works by hashing the truename of files, and should detect
+aliasing between long and short (8.3 DOS) names, but can have
+false positives because of hash collisions. Note that determing
+the truename of a file can be slow.
+*/ );
+ Vwin32_generate_fake_inodes = Qnil;
+#endif
+}
+/* end of ntproc.c */