diff src/ntproc.c @ 428:3ecd8885ac67 r21-2-22

Import from CVS: tag r21-2-22
author cvs
date Mon, 13 Aug 2007 11:28:15 +0200
parents
children 3a7e78e1142d
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/ntproc.c	Mon Aug 13 11:28:15 2007 +0200
@@ -0,0 +1,1517 @@
+/* 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>
+#ifdef HAVE_A_OUT_H
+#include <a.out.h>
+#endif
+#include "lisp.h"
+#include "sysproc.h"
+#include "nt.h"
+#include "ntheap.h" /* From 19.34.6 */
+#include "systime.h"
+#include "syssignal.h"
+#include "sysfile.h"
+#include "syswait.h"
+#include "buffer.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 behavior
+   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;
+
+extern Lisp_Object Vlisp_EXEC_SUFFIXES;
+
+#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;
+  if (cp->procinfo.hProcess)
+    CloseHandle(cp->procinfo.hProcess);
+  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;
+}
+
+/* Function to do blocking read of one byte, needed to implement
+   select.  It is only allowed on sockets and pipes. */
+static int
+_sys_read_ahead (int fd)
+{
+  child_process * cp;
+  int rc = 0;
+
+  if (fd < 0 || fd >= MAXDESC)
+    return STATUS_READ_ERROR;
+
+  cp = fd_info[fd].cp;
+
+  if (cp == NULL || cp->fd != fd || cp->status != STATUS_READ_READY)
+    return STATUS_READ_ERROR;
+
+  if ((fd_info[fd].flags & (FILE_PIPE | FILE_SOCKET)) == 0
+      || (fd_info[fd].flags & FILE_READ) == 0)
+    {
+      /* fd is not a pipe or socket */
+      abort ();
+    }
+  
+  cp->status = STATUS_READ_IN_PROGRESS;
+  
+  if (fd_info[fd].flags & FILE_PIPE)
+    {
+      rc = _read (fd, &cp->chr, sizeof (char));
+
+      /* Give subprocess time to buffer some more output for us before
+	 reporting that input is available; we need this because Win95
+	 connects DOS programs to pipes by making the pipe appear to be
+	 the normal console stdout - as a result most DOS programs will
+	 write to stdout without buffering, ie.  one character at a
+	 time.  Even some Win32 programs do this - "dir" in a command
+	 shell on NT is very slow if we don't do this. */
+      if (rc > 0)
+	{
+	  int wait = XINT (Vwin32_pipe_read_delay);
+
+	  if (wait > 0)
+	    Sleep (wait);
+	  else if (wait < 0)
+	    while (++wait <= 0)
+	      /* Yield remainder of our time slice, effectively giving a
+		 temporary priority boost to the child process. */
+	      Sleep (0);
+	}
+    }
+
+  if (rc == sizeof (char))
+    cp->status = STATUS_READ_SUCCEEDED;
+  else
+    cp->status = STATUS_READ_FAILED;
+
+  return cp->status;
+}
+
+/* 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;
+  
+  /* <matts@tibco.com> - I think the test below is wrong - we don't
+     want to wait for someone to signal char_consumed, as we haven't
+     read anything for them to consume yet! */
+
+  /*
+  if (cp == NULL ||
+      WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
+  */
+
+  if (cp == NULL)
+  {
+      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)
+      {
+        /* We are finished, so clean up handles and set to NULL so
+           that CHILD_ACTIVE will see what is going on */
+        if (cp->char_avail) {
+          CloseHandle (cp->char_avail);
+          cp->char_avail = NULL;
+        }
+        if (cp->thrd) {
+          CloseHandle (cp->thrd);
+          cp->thrd = NULL;
+        }
+        if (cp->char_consumed) {
+          CloseHandle(cp->char_consumed);
+          cp->char_consumed = NULL;
+        }
+        if (cp->procinfo.hProcess)
+        {
+          CloseHandle (cp->procinfo.hProcess);
+          cp->procinfo.hProcess=NULL;
+        }
+        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;
+        }
+    }
+  /* We are finished, so clean up handles and set to NULL so that
+     CHILD_ACTIVE will see what is going on */
+  if (cp->char_avail) {
+    CloseHandle (cp->char_avail);
+    cp->char_avail = NULL;
+  }
+  if (cp->thrd) {
+    CloseHandle (cp->thrd);
+    cp->thrd = NULL;
+  }
+  if (cp->char_consumed) {
+    CloseHandle(cp->char_consumed);
+    cp->char_consumed = NULL;
+  }
+  if (cp->procinfo.hProcess)
+  {
+    CloseHandle (cp->procinfo.hProcess);
+    cp->procinfo.hProcess=NULL;
+  }
+  
+  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 (CONST 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;
+
+  CloseHandle (cp->procinfo.hThread);
+  CloseHandle (cp->procinfo.hProcess);
+  cp->procinfo.hThread=NULL;
+  cp->procinfo.hProcess=NULL;
+
+  /* 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;
+}
+
+#ifndef __MINGW32__
+/* Return pointer to section header for section containing the given
+   relative virtual address. */
+static IMAGE_SECTION_HEADER *
+rva_to_section (DWORD rva, IMAGE_NT_HEADERS * nt_header)
+{
+  PIMAGE_SECTION_HEADER section;
+  int i;
+
+  section = IMAGE_FIRST_SECTION (nt_header);
+
+  for (i = 0; i < nt_header->FileHeader.NumberOfSections; i++)
+    {
+      if (rva >= section->VirtualAddress
+	  && rva < section->VirtualAddress + section->SizeOfRawData)
+	return section;
+      section++;
+    }
+  return NULL;
+}
+#endif
+
+void
+win32_executable_type (CONST 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. */
+
+#ifdef __MINGW32__
+	  /* mingw32 doesn't have enough headers to detect cygwin
+             apps, just do what we can. */
+	  FILHDR * exe_header;
+
+	  exe_header = (FILHDR*) executable.file_base;
+	  if (exe_header->e_magic != DOSMAGIC)
+	    goto unwind;
+
+	  if ((char *) exe_header->e_lfanew > (char *) executable.size)
+	    {
+	      /* Some dos headers (pkunzip) have bogus e_lfanew fields.  */
+	      *is_dos_app = TRUE;
+	    } 
+	  else if (exe_header->nt_signature != NT_SIGNATURE)
+	    {
+	      *is_dos_app = TRUE;
+	    }
+#else
+	  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;
+		    }
+		}
+	    }
+#endif
+	}
+
+ unwind:
+      close_file_data (&executable);
+}
+
+int
+compare_env (const void *strp1, const void *strp2)
+{
+  const char *str1 = *(const char**)strp1, *str2 = *(const char**)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 = 0;
+  /* 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, Vlisp_EXEC_SUFFIXES, &full, 1);
+      if (NILP (full))
+	{
+	  UNGCPRO;
+	  errno = EINVAL;
+	  return -1;
+	}
+      GET_C_STRING_FILENAME_DATA_ALLOCA (full, cmdname);
+    }
+  else
+    {
+      (char*)cmdname = alloca (strlen (argv[0]) + 1);
+      strcpy ((char*)cmdname, argv[0]);
+    }
+  UNGCPRO;
+
+  /* make sure argv[0] and cmdname are both in DOS format */
+  unixtodos_filename ((char*)cmdname);
+  /* #### KLUDGE */
+  ((CONST 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 ((char*)cmdname, egetenv ("CMDPROXY"));
+      else
+    {
+	  strcpy ((char*)cmdname, XSTRING_DATA (Vinvocation_directory));
+	  strcat ((char*)cmdname, "cmdproxy.exe");
+	}
+      unixtodos_filename ((char*)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 recognize 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
+     preceded 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 = (char**)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 = (char**)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
+	     behavior 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 = (char**)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 ((char**)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 ((WNDENUMPROC)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;
+}
+
+/* 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 regional 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 ());
+}
+
+
+void
+syms_of_ntproc ()
+{
+  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);
+}
+
+
+void
+vars_of_ntproc (void)
+{
+  defsymbol (&Qhigh, "high");
+  defsymbol (&Qlow, "low");
+
+  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 = Qt;
+
+  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 determining
+the truename of a file can be slow.
+*/ );
+  Vwin32_generate_fake_inodes = Qnil;
+#endif
+}
+
+/* end of ntproc.c */