-/* Process support for Windows NT port of GNU EMACS.
+/* Process support for GNU Emacs on the Microsoft W32 API.
Copyright (C) 1992, 1995 Free Software Foundation, Inc.
This file is part of GNU Emacs.
#include <windows.h>
#include "lisp.h"
-#include "nt.h"
+#include "w32.h"
#include "systime.h"
#include "syswait.h"
#include "process.h"
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;
+Lisp_Object Vw32_quote_process_args;
+
+/* Control whether create_child causes the process' window to be
+ hidden. The default is nil. */
+Lisp_Object Vw32_start_process_show_window;
+
+/* 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 W32 processes on both Windows 95 and NT as well. */
+Lisp_Object Vw32_pipe_read_delay;
+
+/* Control conversion of upper case file names to lower case.
+ nil means no, t means yes. */
+Lisp_Object Vw32_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[];
start.cb = sizeof (start);
#ifdef HAVE_NTGUI
- start.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
+ if (NILP (Vw32_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);
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;
}
-/* We pass our process ID to our children by setting up an environment
- variable in their environment. */
-char ppid_env_var_buffer[64];
+int
+w32_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 = w32_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;
+}
+
+int
+compare_env (const char **strp1, const char **strp2)
+{
+ const char *str1 = *strp1, *str2 = *strp2;
+
+ while (*str1 && *str2 && *str1 != '=' && *str2 != '=')
+ {
+ if (tolower (*str1) > tolower (*str2))
+ return 1;
+ else if (tolower (*str1) < tolower (*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. */
{
Lisp_Object program, full;
char *cmdline, *env, *parg, **targ;
- int arglen;
+ int arglen, numenv;
int pid;
child_process *cp;
-
+ int 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];
+ char *extra_env[] = {ppid_env_var_buffer, NULL};
+
/* 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 = w32_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
Additionally, zero-length args and args containing whitespace need
to be wrapped in double quotes. Args containing embedded double
quotes (as opposed to enclosing quotes, which we leave alone) are
- usually illegal (most Win32 programs do not implement escaping of
+ usually illegal (most W32 programs do not implement escaping of
double quotes - sad but true, at least for programs compiled with
MSVC), but we will escape quotes anyway for those programs that can
- handle it. The Win32 gcc library from Cygnus doubles quotes to
+ handle it. The W32 gcc library from Cygnus doubles quotes to
escape them, so we will use that convention.
Since I have no idea how large argv and envp are likely to be
if (*p == 0)
add_quotes = 1;
- if (!NILP (Vwin32_quote_process_args))
+ if (!NILP (Vw32_quote_process_args))
{
/* This is conditional because it sometimes causes more
problems than it solves, since argv arrays are not always
/* 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);
- targ = envp;
parg = env;
while (*targ)
{
parg += strlen (*targ++);
*parg++ = '\0';
}
- strcpy (parg, ppid_env_var_buffer);
- parg += strlen (ppid_env_var_buffer);
*parg++ = '\0';
*parg = '\0';
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))
+ /* Kill the process. On W32 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. Also, don't try to terminate DOS processes
+ (on Windows 95), 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 ("w32-has-winsock", Fw32_has_winsock, Sw32_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 ("w32-unload-winsock", Fw32_unload_winsock, Sw32_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 ()
{
- DEFVAR_LISP ("win32-quote-process-args", &Vwin32_quote_process_args,
+#ifdef HAVE_SOCKETS
+ defsubr (&Sw32_has_winsock);
+ defsubr (&Sw32_unload_winsock);
+#endif
+
+ DEFVAR_LISP ("w32-quote-process-args", &Vw32_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\
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;
+ Vw32_quote_process_args = Qnil;
+
+ DEFVAR_LISP ("w32-start-process-show-window",
+ &Vw32_start_process_show_window,
+ "When nil, processes started via start-process hide their windows.\n\
+When non-nil, they show their window in the method of their choice.");
+ Vw32_start_process_show_window = Qnil;
+
+ DEFVAR_INT ("w32-pipe-read-delay", &Vw32_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.");
+ Vw32_pipe_read_delay = 50;
+
+ DEFVAR_LISP ("w32-downcase-file-names", &Vw32_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.");
+ Vw32_downcase_file_names = Qnil;
}
/* end of ntproc.c */