#include "syswait.h"
#include "process.h"
+/* 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;
+
+/* 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 conversion of upper case file names to lower case.
+ nil means no, t means yes. */
+Lisp_Object Vwin32_downcase_file_names;
+
+/* Keep track of whether we have already started a DOS program. */
+BOOL dos_process_running;
+
#ifndef SYS_SIGLIST_DECLARED
extern char *sys_siglist[];
#endif
cp->procinfo.hProcess = NULL;
CloseHandle (cp->procinfo.hThread);
cp->procinfo.hThread = NULL;
+
+ /* If this was a DOS process, indicate that it is now safe to
+ start a new one. */
+ if (cp->is_dos_process)
+ dos_process_running = FALSE;
}
/* For asynchronous children, the child_proc resources will be freed
return pid;
}
+int
+win32_is_dos_binary (char * filename)
+{
+ IMAGE_DOS_HEADER dos_header;
+ DWORD signature;
+ int fd;
+ int is_dos_binary = FALSE;
+
+ fd = open (filename, O_RDONLY | O_BINARY, 0);
+ if (fd >= 0)
+ {
+ char * p = strrchr (filename, '.');
+
+ /* We can only identify DOS .com programs from the extension. */
+ if (p && stricmp (p, ".com") == 0)
+ is_dos_binary = TRUE;
+ else if (p && stricmp (p, ".bat") == 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. */
+ p = getenv ("COMSPEC");
+ if (p)
+ is_dos_binary = win32_is_dos_binary (p);
+ }
+ 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. */
+ if (read (fd, &dos_header, sizeof (dos_header)) == sizeof (dos_header)
+ && dos_header.e_magic == IMAGE_DOS_SIGNATURE
+ && lseek (fd, dos_header.e_lfanew, SEEK_SET) != -1)
+ {
+ if (read (fd, &signature, sizeof (signature)) != sizeof (signature)
+ || (signature != IMAGE_NT_SIGNATURE &&
+ LOWORD (signature) != IMAGE_OS2_SIGNATURE))
+ is_dos_binary = TRUE;
+ }
+ }
+ close (fd);
+ }
+
+ return is_dos_binary;
+}
+
/* We pass our process ID to our children by setting up an environment
variable in their environment. */
char ppid_env_var_buffer[64];
int arglen;
int pid;
child_process *cp;
+ int is_dos_binary;
/* We don't care about the other modes */
if (mode != _P_NOWAIT)
strcpy (cmdname = alloca (strlen (cmdname) + 1), argv[0]);
unixtodos_filename (cmdname);
argv[0] = cmdname;
+
+ /* Check if program is a DOS executable, and if so whether we are
+ allowed to start it. */
+ is_dos_binary = win32_is_dos_binary (cmdname);
+ if (is_dos_binary && dos_process_running)
+ {
+ errno = EAGAIN;
+ return -1;
+ }
/* 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
if (*p == 0)
add_quotes = 1;
-#if 0
- /* Unfortunately, this causes more problems than it solves,
- because argv arrays are not always carefully constructed.
- grep, for instance, passes the whole command line as one
- argument, so it becomes impossible to pass a regexp which
- contains spaces. */
- for ( ; *p; p++)
- if (*p == ' ' || *p == '\t' || *p == '"')
- add_quotes = 1;
-#endif
+
+ if (!NILP (Vwin32_quote_process_args))
+ {
+ /* This is conditional because it sometimes causes more
+ problems than it solves, since argv arrays are not always
+ carefully constructed. M-x grep, for instance, passes the
+ whole command line as one argument, so it becomes
+ impossible to pass a regexp which contains spaces. */
+ for ( ; *p; p++)
+ if (*p == ' ' || *p == '\t' || *p == '"')
+ add_quotes = 1;
+ }
if (add_quotes)
{
char * first;
errno = ENOEXEC;
return -1;
}
+
+ if (is_dos_binary)
+ {
+ cp->is_dos_process = TRUE;
+ dos_process_running = TRUE;
+ }
return pid;
}
else
{
/* 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. */
- if (!TerminateProcess (proc_hand, 0xff))
+ so it doesn't work very well for shells which is why it's not
+ used in every case. Also, don't try to terminate DOS processes
+ (on Win95), because this will hang Emacs. */
+ if (!(cp && cp->is_dos_process)
+ && !TerminateProcess (proc_hand, 0xff))
{
DebPrint (("sys_kill.TerminateProcess returned %d "
"for pid %lu\n", GetLastError (), pid));
SetStdHandle (STD_ERROR_HANDLE, handles[2]);
}
+#ifdef HAVE_SOCKETS
+
+/* To avoid problems with winsock implementations that work over dial-up
+ connections causing or requiring a connection to exist while Emacs is
+ running, Emacs no longer automatically loads winsock on startup if it
+ is present. Instead, it will be loaded when open-network-stream is
+ first called.
+
+ To allow full control over when winsock is loaded, we provide these
+ two functions to dynamically load and unload winsock. This allows
+ dial-up users to only be connected when they actually need to use
+ socket services. */
+
+/* From nt.c */
+extern HANDLE winsock_lib;
+extern BOOL term_winsock (void);
+extern BOOL init_winsock (int load_now);
+
+extern Lisp_Object Vsystem_name;
+
+DEFUN ("win32-has-winsock", Fwin32_has_winsock, Swin32_has_winsock, 0, 1, 0,
+ "Test for presence of the Windows socket library `winsock'.\n\
+Returns non-nil if winsock support is present, nil otherwise.\n\
+\n\
+If the optional argument LOAD-NOW is non-nil, the winsock library is\n\
+also loaded immediately if not already loaded. If winsock is loaded,\n\
+the winsock local hostname is returned (since this may be different from\n\
+the value of `system-name' and should supplant it), otherwise t is\n\
+returned to indicate winsock support is present.")
+ (load_now)
+ Lisp_Object load_now;
+{
+ int have_winsock;
+
+ have_winsock = init_winsock (!NILP (load_now));
+ if (have_winsock)
+ {
+ if (winsock_lib != NULL)
+ {
+ /* Return new value for system-name. The best way to do this
+ is to call init_system_name, saving and restoring the
+ original value to avoid side-effects. */
+ Lisp_Object orig_hostname = Vsystem_name;
+ Lisp_Object hostname;
+
+ init_system_name ();
+ hostname = Vsystem_name;
+ Vsystem_name = orig_hostname;
+ return hostname;
+ }
+ return Qt;
+ }
+ return Qnil;
+}
+
+DEFUN ("win32-unload-winsock", Fwin32_unload_winsock, Swin32_unload_winsock,
+ 0, 0, 0,
+ "Unload the Windows socket library `winsock' if loaded.\n\
+This is provided to allow dial-up socket connections to be disconnected\n\
+when no longer needed. Returns nil without unloading winsock if any\n\
+socket connections still exist.")
+ ()
+{
+ return term_winsock () ? Qt : Qnil;
+}
+
+#endif /* HAVE_SOCKETS */
+
+\f
+syms_of_ntproc ()
+{
+#ifdef HAVE_SOCKETS
+ defsubr (&Swin32_has_winsock);
+ defsubr (&Swin32_unload_winsock);
+#endif
+
+ DEFVAR_LISP ("win32-quote-process-args", &Vwin32_quote_process_args,
+ "Non-nil enables quoting of process arguments to ensure correct parsing.\n\
+Because Windows does not directly pass argv arrays to child processes,\n\
+programs have to reconstruct the argv array by parsing the command\n\
+line string. For an argument to contain a space, it must be enclosed\n\
+in double quotes or it will be parsed as multiple arguments.\n\
+\n\
+However, the argument list to call-process is not always correctly\n\
+constructed (or arguments have already been quoted), so enabling this\n\
+option may cause unexpected behavior.");
+ Vwin32_quote_process_args = Qnil;
+
+ DEFVAR_INT ("win32-pipe-read-delay", &Vwin32_pipe_read_delay,
+ "Forced delay before reading subprocess output.\n\
+This is done to improve the buffering of subprocess output, by\n\
+avoiding the inefficiency of frequently reading small amounts of data.\n\
+\n\
+If positive, the value is the number of milliseconds to sleep before\n\
+reading the subprocess output. If negative, the magnitude is the number\n\
+of time slices to wait (effectively boosting the priority of the child\n\
+process temporarily). A value of zero disables waiting entirely.");
+ Vwin32_pipe_read_delay = 50;
+
+ DEFVAR_LISP ("win32-downcase-file-names", &Vwin32_downcase_file_names,
+ "Non-nil means convert all-upper case file names to lower case.\n\
+This applies when performing completions and file name expansion.");
+ Vwin32_downcase_file_names = Qnil;
+}
/* end of ntproc.c */