#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <io.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/file.h>
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#undef signal
#undef wait
#undef spawnve
#undef select
#undef kill
#include <windows.h>
#ifdef __GNUC__
extern BOOL WINAPI IsValidLocale(LCID, DWORD);
#endif
#ifdef HAVE_LANGINFO_CODESET
#include <nl_types.h>
#include <langinfo.h>
#endif
#include "lisp.h"
#include "w32.h"
#include "w32heap.h"
#include "systime.h"
#include "syswait.h"
#include "process.h"
#include "syssignal.h"
#include "w32term.h"
#define RVA_TO_PTR(var,section,filedata) \
((void *)((section)->PointerToRawData \
+ ((DWORD)(var) - (section)->VirtualAddress) \
+ (filedata).file_base))
Lisp_Object Vw32_quote_process_args;
Lisp_Object Vw32_start_process_show_window;
Lisp_Object Vw32_start_process_share_console;
Lisp_Object Vw32_start_process_inherit_error_mode;
int w32_pipe_read_delay;
Lisp_Object Vw32_downcase_file_names;
Lisp_Object Vw32_generate_fake_inodes;
Lisp_Object Vw32_get_true_file_attributes;
Lisp_Object Qhigh, Qlow;
#ifdef EMACSDEBUG
void _DebPrint (const char *fmt, ...)
{
char buf[1024];
va_list args;
va_start (args, fmt);
vsprintf (buf, fmt, args);
va_end (args);
OutputDebugString (buf);
}
#endif
typedef void (_CALLBACK_ *signal_handler)(int);
static signal_handler sig_handlers[NSIG];
signal_handler
sys_signal (int sig, signal_handler handler)
{
signal_handler old;
if (sig != SIGCHLD)
{
errno = EINVAL;
return SIG_ERR;
}
old = sig_handlers[sig];
sig_handlers[sig] = handler;
return old;
}
#define _P_NOWAIT 1
int child_proc_count = 0;
child_process child_procs[ MAX_CHILDREN ];
child_process *dead_child = NULL;
DWORD WINAPI reader_thread (void *arg);
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 Initialise;
if (child_proc_count == MAX_CHILDREN)
return NULL;
cp = &child_procs[child_proc_count++];
Initialise:
memset (cp, 0, sizeof(*cp));
cp->fd = -1;
cp->pid = -1;
cp->procinfo.hProcess = NULL;
cp->status = STATUS_READ_ERROR;
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;
for (i = 0; i < MAXDESC; i++)
if (fd_info[i].cp == cp)
abort ();
if (!CHILD_ACTIVE (cp))
return;
if (cp->thrd)
{
DWORD rc;
if (GetExitCodeThread (cp->thrd, &rc) && rc == STILL_ACTIVE)
{
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;
}
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;
}
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;
}
DWORD WINAPI
reader_thread (void *arg)
{
child_process *cp;
cp = (child_process *)arg;
if (cp == NULL
|| WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
return 1;
for (;;)
{
int rc;
if (fd_info[cp->fd].flags & FILE_LISTEN)
rc = _sys_wait_accept (cp->fd);
else
rc = _sys_read_ahead (cp->fd);
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 (rc == STATUS_READ_FAILED)
break;
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;
}
static char * process_dir;
static BOOL
create_child (char *exe, char *cmdline, char *env, int is_gui_app,
int * pPid, child_process *cp)
{
STARTUPINFO start;
SECURITY_ATTRIBUTES sec_attrs;
#if 0
SECURITY_DESCRIPTOR sec_desc;
#endif
DWORD flags;
char dir[ MAXPATHLEN ];
if (cp == NULL) abort ();
memset (&start, 0, sizeof (start));
start.cb = sizeof (start);
#ifdef HAVE_NTGUI
if (NILP (Vw32_start_process_show_window) && !is_gui_app)
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
#if 0
if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
goto EH_Fail;
if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
goto EH_Fail;
#endif
sec_attrs.nLength = sizeof (sec_attrs);
sec_attrs.lpSecurityDescriptor = NULL ;
sec_attrs.bInheritHandle = FALSE;
strcpy (dir, process_dir);
unixtodos_filename (dir);
flags = (!NILP (Vw32_start_process_share_console)
? CREATE_NEW_PROCESS_GROUP
: CREATE_NEW_CONSOLE);
if (NILP (Vw32_start_process_inherit_error_mode))
flags |= CREATE_DEFAULT_ERROR_MODE;
if (!CreateProcess (exe, cmdline, &sec_attrs, NULL, TRUE,
flags, env, dir, &start, &cp->procinfo))
goto EH_Fail;
cp->pid = (int) cp->procinfo.dwProcessId;
if (cp->pid < 0)
cp->pid = -cp->pid;
cp->pid = cp->pid & INTMASK;
*pPid = cp->pid;
return TRUE;
EH_Fail:
DebPrint (("create_child.CreateProcess failed: %ld\n", GetLastError()););
return FALSE;
}
void
register_child (int pid, int fd)
{
child_process *cp;
cp = find_child_pid (pid);
if (cp == NULL)
{
DebPrint (("register_child unable to find pid %lu\n", pid));
return;
}
#ifdef FULL_DEBUG
DebPrint (("register_child registered fd %d with pid %lu\n", fd, pid));
#endif
cp->fd = fd;
cp->status = STATUS_READ_ACKNOWLEDGED;
if (fd_info[fd].cp != NULL)
{
DebPrint (("register_child: fd_info[%d] apparently in use!\n", fd));
abort ();
}
fd_info[fd].cp = cp;
}
static void
reap_subprocess (child_process *cp)
{
if (cp->procinfo.hProcess)
{
#ifdef FULL_DEBUG
if (WaitForSingleObject (cp->procinfo.hProcess, 0) != WAIT_OBJECT_0)
DebPrint (("reap_subprocess: child fpr fd %d has not died yet!", cp->fd));
#endif
CloseHandle (cp->procinfo.hProcess);
cp->procinfo.hProcess = NULL;
CloseHandle (cp->procinfo.hThread);
cp->procinfo.hThread = NULL;
}
if (cp->fd == -1)
delete_child (cp);
}
int
sys_wait (int *status)
{
DWORD active, retval;
int nh;
int pid;
child_process *cp, *cps[MAX_CHILDREN];
HANDLE wait_hnd[MAX_CHILDREN];
nh = 0;
if (dead_child != NULL)
{
wait_hnd[nh] = dead_child->procinfo.hProcess;
cps[nh] = dead_child;
if (!wait_hnd[nh]) abort ();
nh++;
active = 0;
goto get_result;
}
else
{
for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
if (CHILD_ACTIVE (cp) && cp->procinfo.hProcess
&& (cp->fd < 0 || (fd_info[cp->fd].flags & FILE_AT_EOF) != 0))
{
wait_hnd[nh] = cp->procinfo.hProcess;
cps[nh] = cp;
nh++;
}
}
if (nh == 0)
{
errno = ECHILD;
return -1;
}
do
{
QUIT;
active = WaitForMultipleObjects (nh, wait_hnd, FALSE, 1000);
} while (active == WAIT_TIMEOUT);
if (active == WAIT_FAILED)
{
errno = EBADF;
return -1;
}
else if (active >= WAIT_OBJECT_0
&& active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
{
active -= WAIT_OBJECT_0;
}
else if (active >= WAIT_ABANDONED_0
&& active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS)
{
active -= WAIT_ABANDONED_0;
}
else
abort ();
get_result:
if (!GetExitCodeProcess (wait_hnd[active], &retval))
{
DebPrint (("Wait.GetExitCodeProcess failed with %lu\n",
GetLastError ()));
retval = 1;
}
if (retval == STILL_ACTIVE)
{
DebPrint (("Wait.WaitForMultipleObjects returned an active process\n"));
errno = EINVAL;
return -1;
}
if (retval == STATUS_CONTROL_C_EXIT)
retval = SIGINT;
else
retval <<= 8;
cp = cps[active];
pid = cp->pid;
#ifdef FULL_DEBUG
DebPrint (("Wait signaled with process pid %d\n", cp->pid));
#endif
if (status)
{
*status = retval;
}
else if (synch_process_alive)
{
synch_process_alive = 0;
if (WIFEXITED (retval))
synch_process_retcode = WRETCODE (retval);
else if (WIFSIGNALED (retval))
{
int code = WTERMSIG (retval);
char *signame;
synchronize_system_messages_locale ();
signame = strsignal (code);
if (signame == 0)
signame = "unknown";
synch_process_death = signame;
}
reap_subprocess (cp);
}
reap_subprocess (cp);
return pid;
}
void
w32_executable_type (char * filename, int * is_dos_app, int * is_cygnus_app, int * is_gui_app)
{
file_data executable;
char * p;
*is_dos_app = FALSE;
*is_cygnus_app = FALSE;
*is_gui_app = FALSE;
if (!open_input_file (&executable, filename))
return;
p = strrchr (filename, '.');
if (p && stricmp (p, ".com") == 0)
*is_dos_app = TRUE;
else if (p && (stricmp (p, ".bat") == 0
|| stricmp (p, ".cmd") == 0))
{
p = egetenv ("COMSPEC");
if (p)
w32_executable_type (p, is_dos_app, is_cygnus_app, is_gui_app);
}
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)
{
*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)
{
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 (strncmp (dllname, "cygwin", 6) == 0)
{
*is_cygnus_app = TRUE;
break;
}
}
*is_gui_app = (nt_header->OptionalHeader.Subsystem == IMAGE_SUBSYSTEM_WINDOWS_GUI);
}
}
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 (toupper (*str1) > toupper (*str2))
return 1;
else if (toupper (*str1) < toupper (*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;
}
int
sys_spawnve (int mode, char *cmdname, char **argv, char **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, is_gui_app;
int do_quoting = 0;
char escape_char;
char ppid_env_var_buffer[64];
char *extra_env[] = {ppid_env_var_buffer, NULL};
char *sepchars = " \t";
if (mode != _P_NOWAIT)
{
errno = EINVAL;
return -1;
}
program = make_string (cmdname, strlen (cmdname));
if (NILP (Ffile_executable_p (program)))
{
struct gcpro gcpro1;
full = Qnil;
GCPRO1 (program);
openp (Vexec_path, program, Vexec_suffixes, &full, make_number (X_OK));
UNGCPRO;
if (NILP (full))
{
errno = EINVAL;
return -1;
}
program = full;
}
cmdname = SDATA (program);
unixtodos_filename (cmdname);
argv[0] = cmdname;
w32_executable_type (cmdname, &is_dos_app, &is_cygnus_app, &is_gui_app);
if (is_dos_app)
{
cmdname = alloca (MAXPATHLEN);
if (egetenv ("CMDPROXY"))
strcpy (cmdname, egetenv ("CMDPROXY"));
else
{
strcpy (cmdname, SDATA (Vinvocation_directory));
strcat (cmdname, "cmdproxy.exe");
}
unixtodos_filename (cmdname);
}
if (!NILP (Vw32_quote_process_args))
{
do_quoting = 1;
if (INTEGERP (Vw32_quote_process_args))
escape_char = XINT (Vw32_quote_process_args);
else
escape_char = is_cygnus_app ? '"' : '\\';
}
if (escape_char == '"')
sepchars = "\r\n\t\f '";
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 (escape_char == '"' && *p == '\\')
arglen++;
else if (*p == '"')
{
arglen++;
need_quotes = 1;
if (escape_char_run > 0)
{
arglen += escape_char_run;
}
}
else if (strchr (sepchars, *p) != NULL)
{
need_quotes = 1;
}
if (*p == escape_char && escape_char != '"')
escape_char_run++;
else
escape_char_run = 0;
}
if (need_quotes)
{
arglen += 2;
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 ((strchr (sepchars, *p) != NULL) || *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
while (*p)
{
if (*p == '"' && p > first && p < last)
*parg++ = escape_char;
*parg++ = *p++;
}
#else
for ( ; *p; p++)
{
if (*p == '"')
{
while (escape_char_run > 0)
{
*parg++ = escape_char;
escape_char_run--;
}
*parg++ = escape_char;
}
else if (escape_char == '"' && *p == '\\')
*parg++ = '\\';
*parg++ = *p;
if (*p == escape_char && escape_char != '"')
escape_char_run++;
else
escape_char_run = 0;
}
while (escape_char_run > 0)
{
*parg++ = escape_char;
escape_char_run--;
}
#endif
*parg++ = '"';
}
else
{
strcpy (parg, *targ);
parg += strlen (*targ);
}
*parg++ = ' ';
targ++;
}
*--parg = '\0';
arglen = 1;
targ = envp;
numenv = 1;
while (*targ)
{
arglen += strlen (*targ++) + 1;
numenv++;
}
sprintf (ppid_env_var_buffer, "EM_PARENT_PROCESS_ID=%d",
GetCurrentProcessId ());
arglen += strlen (ppid_env_var_buffer) + 1;
numenv++;
targ = (char **) alloca (numenv * sizeof (char *));
merge_and_sort_env (envp, extra_env, targ);
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;
}
if (!create_child (cmdname, cmdline, env, is_gui_app, &pid, cp))
{
delete_child (cp);
errno = ENOEXEC;
return -1;
}
return pid;
}
extern HANDLE keyboard_handle;
extern HANDLE interrupt_handle;
extern int proc_buffered_char[];
int
sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
EMACS_TIME *timeout)
{
SELECT_TYPE orfds;
DWORD timeout_ms, start_time;
int i, nh, nc, nr;
DWORD active;
child_process *cp, *cps[MAX_CHILDREN];
HANDLE wait_hnd[MAXDESC + MAX_CHILDREN];
int fdindex[MAXDESC];
timeout_ms = timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFINITE;
if (rfds == NULL && wfds == NULL && efds == NULL && timeout != NULL)
{
Sleep (timeout_ms);
return 0;
}
if (rfds == NULL || wfds != NULL || efds != NULL)
{
errno = EINVAL;
return -1;
}
orfds = *rfds;
FD_ZERO (rfds);
nr = 0;
wait_hnd[0] = interrupt_handle;
fdindex[0] = -1;
nh = 1;
for (i = 0; i < nfds; i++)
if (FD_ISSET (i, &orfds))
{
if (i == 0)
{
if (keyboard_handle)
{
wait_hnd[nh] = keyboard_handle;
fdindex[nh] = i;
nh++;
}
if (detect_input_pending ())
{
FD_SET (i, rfds);
return 1;
}
}
else
{
cp = fd_info[i].cp;
if (cp)
{
int current_status = cp->status;
if (current_status == STATUS_READ_ACKNOWLEDGED)
{
cp->fd = i;
cp->status = STATUS_READ_READY;
if (!SetEvent (cp->char_consumed))
DebPrint (("nt_select.SetEvent failed with "
"%lu for fd %ld\n", GetLastError (), i));
}
#ifdef CHECK_INTERLOCK
current_status = cp->status;
if (WaitForSingleObject (cp->char_avail, 0) == WAIT_OBJECT_0)
{
current_status = cp->status;
if (current_status != STATUS_READ_SUCCEEDED
&& current_status != STATUS_READ_FAILED)
DebPrint (("char_avail set, but read not completed: status %d\n",
current_status));
}
else
{
if (current_status != STATUS_READ_READY
&& current_status != STATUS_READ_IN_PROGRESS
&& current_status != STATUS_READ_SUCCEEDED
&& current_status != STATUS_READ_FAILED)
DebPrint (("char_avail reset, but read status is bad: %d\n",
current_status));
}
#endif
wait_hnd[nh] = cp->char_avail;
fdindex[nh] = i;
if (!wait_hnd[nh]) abort ();
nh++;
#ifdef FULL_DEBUG
DebPrint (("select waiting on child %d fd %d\n",
cp-child_procs, i));
#endif
}
else
{
DebPrint (("sys_select: fd %ld is invalid! ignoring\n", i));
}
}
}
count_children:
nc = 0;
for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
if (CHILD_ACTIVE (cp) && cp->procinfo.hProcess
&& (cp->fd < 0
|| (fd_info[cp->fd].flags & FILE_SEND_SIGCHLD) == 0
|| (fd_info[cp->fd].flags & FILE_AT_EOF) != 0)
)
{
wait_hnd[nh + nc] = cp->procinfo.hProcess;
cps[nc] = cp;
nc++;
}
if (nh + nc == 0)
{
if (timeout)
Sleep (timeout_ms);
return 0;
}
start_time = GetTickCount ();
if (FD_ISSET (0, &orfds))
active = MsgWaitForMultipleObjects (nh + nc, wait_hnd, FALSE, timeout_ms,
QS_ALLINPUT);
else
active = WaitForMultipleObjects (nh + nc, wait_hnd, FALSE, timeout_ms);
if (active == WAIT_FAILED)
{
DebPrint (("select.WaitForMultipleObjects (%d, %lu) failed with %lu\n",
nh + nc, timeout_ms, GetLastError ()));
errno = EINTR;
return -1;
}
else if (active == WAIT_TIMEOUT)
{
return 0;
}
else if (active >= WAIT_OBJECT_0
&& active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
{
active -= WAIT_OBJECT_0;
}
else if (active >= WAIT_ABANDONED_0
&& active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS)
{
active -= WAIT_ABANDONED_0;
}
else
abort ();
do
{
if (active == nh + nc)
{
drain_message_queue ();
}
else if (active >= nh)
{
cp = cps[active - nh];
if (cp->fd >= 0 && (fd_info[cp->fd].flags & FILE_AT_EOF) == 0)
fd_info[cp->fd].flags |= FILE_SEND_SIGCHLD;
else if (sig_handlers[SIGCHLD] != SIG_DFL &&
sig_handlers[SIGCHLD] != SIG_IGN)
{
#ifdef FULL_DEBUG
DebPrint (("select calling SIGCHLD handler for pid %d\n",
cp->pid));
#endif
dead_child = cp;
sig_handlers[SIGCHLD] (SIGCHLD);
dead_child = NULL;
}
}
else if (fdindex[active] == -1)
{
errno = EINTR;
return -1;
}
else if (fdindex[active] == 0)
{
FD_SET (0, rfds);
nr++;
}
else
{
FD_SET (fdindex[active], rfds);
nr++;
}
while (++active < nh + nc)
if (WaitForSingleObject (wait_hnd[active], 0) == WAIT_OBJECT_0)
break;
} while (active < nh + nc);
if (nr == 0)
{
DWORD elapsed = GetTickCount () - start_time;
if (timeout_ms > elapsed)
{
if (timeout_ms != INFINITE)
timeout_ms -= elapsed;
goto count_children;
}
}
return nr;
}
static BOOL CALLBACK
find_child_console (HWND hwnd, LPARAM arg)
{
child_process * cp = (child_process *) arg;
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;
}
}
return TRUE;
}
int
sys_kill (int pid, int sig)
{
child_process *cp;
HANDLE proc_hand;
int need_to_free = 0;
int rc = 0;
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;
EnumWindows (find_child_console, (LPARAM) cp);
}
if (sig == SIGINT || sig == SIGQUIT)
{
if (NILP (Vw32_start_process_share_console) && cp && cp->hwnd)
{
BYTE control_scan_code = (BYTE) MapVirtualKey (VK_CONTROL, 0);
BYTE vk_break_code = (sig == SIGINT) ? 'C' : VK_CANCEL;
BYTE break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
HWND foreground_window;
if (break_scan_code == 0)
{
vk_break_code = 'C';
break_scan_code = (BYTE) MapVirtualKey (vk_break_code, 0);
}
foreground_window = GetForegroundWindow ();
if (foreground_window)
{
DWORD foreground_thread, child_thread;
foreground_thread =
GetWindowThreadProcessId (foreground_window, NULL);
if (foreground_thread == GetCurrentThreadId ()
|| !AttachThreadInput (GetCurrentThreadId (),
foreground_thread, TRUE))
foreground_thread = 0;
child_thread = GetWindowThreadProcessId (cp->hwnd, NULL);
if (child_thread == GetCurrentThreadId ()
|| !AttachThreadInput (GetCurrentThreadId (),
child_thread, TRUE))
child_thread = 0;
if (SetForegroundWindow (cp->hwnd))
{
keybd_event (VK_CONTROL, control_scan_code, 0, 0);
keybd_event (vk_break_code, break_scan_code,
(vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY), 0);
keybd_event (vk_break_code, break_scan_code,
(vk_break_code == 'C' ? 0 : KEYEVENTF_EXTENDEDKEY)
| KEYEVENTF_KEYUP, 0);
keybd_event (VK_CONTROL, control_scan_code,
KEYEVENTF_KEYUP, 0);
Sleep (100);
SetForegroundWindow (foreground_window);
}
if (foreground_thread)
AttachThreadInput (GetCurrentThreadId (),
foreground_thread, FALSE);
if (child_thread)
AttachThreadInput (GetCurrentThreadId (),
child_thread, FALSE);
}
}
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 (Vw32_start_process_share_console) && cp && cp->hwnd)
{
#if 1
if (os_subtype == OS_WIN95)
{
#if 0
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);
}
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;
}
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);
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);
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])
{
CloseHandle (GetStdHandle (STD_INPUT_HANDLE));
CloseHandle (GetStdHandle (STD_OUTPUT_HANDLE));
CloseHandle (GetStdHandle (STD_ERROR_HANDLE));
SetStdHandle (STD_INPUT_HANDLE, handles[0]);
SetStdHandle (STD_OUTPUT_HANDLE, handles[1]);
SetStdHandle (STD_ERROR_HANDLE, handles[2]);
}
void
set_process_dir (char * dir)
{
process_dir = dir;
}
#ifdef HAVE_SOCKETS
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,
doc: )
(load_now)
Lisp_Object load_now;
{
int have_winsock;
have_winsock = init_winsock (!NILP (load_now));
if (have_winsock)
{
if (winsock_lib != NULL)
{
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,
doc: )
()
{
return term_winsock () ? Qt : Qnil;
}
#endif
#define CORRECT_DIR_SEPS(s) \
do { if ('/' == DIRECTORY_SEP) dostounix_filename (s); \
else unixtodos_filename (s); \
} while (0)
DEFUN ("w32-short-file-name", Fw32_short_file_name, Sw32_short_file_name, 1, 1, 0,
doc: )
(filename)
Lisp_Object filename;
{
char shortname[MAX_PATH];
CHECK_STRING (filename);
filename = Fexpand_file_name (filename, Qnil);
if (GetShortPathName (SDATA (filename), shortname, MAX_PATH) == 0)
return Qnil;
CORRECT_DIR_SEPS (shortname);
return build_string (shortname);
}
DEFUN ("w32-long-file-name", Fw32_long_file_name, Sw32_long_file_name,
1, 1, 0,
doc: )
(filename)
Lisp_Object filename;
{
char longname[ MAX_PATH ];
CHECK_STRING (filename);
filename = Fexpand_file_name (filename, Qnil);
if (!w32_get_long_filename (SDATA (filename), longname, MAX_PATH))
return Qnil;
CORRECT_DIR_SEPS (longname);
return build_string (longname);
}
DEFUN ("w32-set-process-priority", Fw32_set_process_priority,
Sw32_set_process_priority, 2, 2, 0,
doc: )
(process, priority)
Lisp_Object 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_NUMBER (process);
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;
}
#ifdef HAVE_LANGINFO_CODESET
char *nl_langinfo (nl_item item)
{
static const LCTYPE w32item[] = {
LOCALE_IDEFAULTANSICODEPAGE,
LOCALE_SDAYNAME1, LOCALE_SDAYNAME2, LOCALE_SDAYNAME3,
LOCALE_SDAYNAME4, LOCALE_SDAYNAME5, LOCALE_SDAYNAME6, LOCALE_SDAYNAME7,
LOCALE_SMONTHNAME1, LOCALE_SMONTHNAME2, LOCALE_SMONTHNAME3,
LOCALE_SMONTHNAME4, LOCALE_SMONTHNAME5, LOCALE_SMONTHNAME6,
LOCALE_SMONTHNAME7, LOCALE_SMONTHNAME8, LOCALE_SMONTHNAME9,
LOCALE_SMONTHNAME10, LOCALE_SMONTHNAME11, LOCALE_SMONTHNAME12
};
static char *nl_langinfo_buf = NULL;
static int nl_langinfo_len = 0;
if (nl_langinfo_len <= 0)
nl_langinfo_buf = xmalloc (nl_langinfo_len = 1);
if (item < 0 || item >= _NL_NUM)
nl_langinfo_buf[0] = 0;
else
{
LCID cloc = GetThreadLocale ();
int need_len = GetLocaleInfo (cloc, w32item[item] | LOCALE_USE_CP_ACP,
NULL, 0);
if (need_len <= 0)
nl_langinfo_buf[0] = 0;
else
{
if (item == CODESET)
{
need_len += 2;
if (need_len < 8)
need_len = 8;
}
if (nl_langinfo_len <= need_len)
nl_langinfo_buf = xrealloc (nl_langinfo_buf,
nl_langinfo_len = need_len);
if (!GetLocaleInfo (cloc, w32item[item] | LOCALE_USE_CP_ACP,
nl_langinfo_buf, nl_langinfo_len))
nl_langinfo_buf[0] = 0;
else if (item == CODESET)
{
if (strcmp (nl_langinfo_buf, "0") == 0
|| strcmp (nl_langinfo_buf, "1") == 0)
sprintf (nl_langinfo_buf, "cp%u", GetACP ());
else
{
memmove (nl_langinfo_buf + 2, nl_langinfo_buf,
strlen (nl_langinfo_buf) + 1);
nl_langinfo_buf[0] = 'c';
nl_langinfo_buf[1] = 'p';
}
}
}
}
return nl_langinfo_buf;
}
#endif
DEFUN ("w32-get-locale-info", Fw32_get_locale_info,
Sw32_get_locale_info, 1, 2, 0,
doc: )
(lcid, longform)
Lisp_Object lcid, longform;
{
int got_abbrev;
int got_full;
char abbrev_name[32] = { 0 };
char full_name[256] = { 0 };
CHECK_NUMBER (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 if (EQ (longform, Qt))
{
got_full = GetLocaleInfo (XINT (lcid),
LOCALE_SLANGUAGE | LOCALE_USE_CP_ACP,
full_name, sizeof (full_name));
if (got_full)
return build_string (full_name);
}
else if (NUMBERP (longform))
{
got_full = GetLocaleInfo (XINT (lcid),
XINT (longform),
full_name, sizeof (full_name));
if (got_full)
return make_unibyte_string (full_name, got_full);
}
return Qnil;
}
DEFUN ("w32-get-current-locale-id", Fw32_get_current_locale_id,
Sw32_get_current_locale_id, 0, 0, 0,
doc: )
()
{
return make_number (GetThreadLocale ());
}
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;
}
Lisp_Object Vw32_valid_locale_ids;
BOOL CALLBACK enum_locale_fn (LPTSTR localeNum)
{
DWORD id = int_from_hex (localeNum);
Vw32_valid_locale_ids = Fcons (make_number (id), Vw32_valid_locale_ids);
return TRUE;
}
DEFUN ("w32-get-valid-locale-ids", Fw32_get_valid_locale_ids,
Sw32_get_valid_locale_ids, 0, 0, 0,
doc: )
()
{
Vw32_valid_locale_ids = Qnil;
EnumSystemLocales (enum_locale_fn, LCID_SUPPORTED);
Vw32_valid_locale_ids = Fnreverse (Vw32_valid_locale_ids);
return Vw32_valid_locale_ids;
}
DEFUN ("w32-get-default-locale-id", Fw32_get_default_locale_id, Sw32_get_default_locale_id, 0, 1, 0,
doc: )
(userp)
Lisp_Object userp;
{
if (NILP (userp))
return make_number (GetSystemDefaultLCID ());
return make_number (GetUserDefaultLCID ());
}
DEFUN ("w32-set-current-locale", Fw32_set_current_locale, Sw32_set_current_locale, 1, 1, 0,
doc: )
(lcid)
Lisp_Object lcid;
{
CHECK_NUMBER (lcid);
if (!IsValidLocale (XINT (lcid), LCID_SUPPORTED))
return Qnil;
if (!SetThreadLocale (XINT (lcid)))
return Qnil;
if (dwWindowsThreadId)
PostThreadMessage (dwWindowsThreadId, WM_EMACS_SETLOCALE, XINT (lcid), 0);
return make_number (GetThreadLocale ());
}
Lisp_Object Vw32_valid_codepages;
BOOL CALLBACK enum_codepage_fn (LPTSTR codepageNum)
{
DWORD id = atoi (codepageNum);
Vw32_valid_codepages = Fcons (make_number (id), Vw32_valid_codepages);
return TRUE;
}
DEFUN ("w32-get-valid-codepages", Fw32_get_valid_codepages,
Sw32_get_valid_codepages, 0, 0, 0,
doc: )
()
{
Vw32_valid_codepages = Qnil;
EnumSystemCodePages (enum_codepage_fn, CP_SUPPORTED);
Vw32_valid_codepages = Fnreverse (Vw32_valid_codepages);
return Vw32_valid_codepages;
}
DEFUN ("w32-get-console-codepage", Fw32_get_console_codepage,
Sw32_get_console_codepage, 0, 0, 0,
doc: )
()
{
return make_number (GetConsoleCP ());
}
DEFUN ("w32-set-console-codepage", Fw32_set_console_codepage,
Sw32_set_console_codepage, 1, 1, 0,
doc: )
(cp)
Lisp_Object cp;
{
CHECK_NUMBER (cp);
if (!IsValidCodePage (XINT (cp)))
return Qnil;
if (!SetConsoleCP (XINT (cp)))
return Qnil;
return make_number (GetConsoleCP ());
}
DEFUN ("w32-get-console-output-codepage", Fw32_get_console_output_codepage,
Sw32_get_console_output_codepage, 0, 0, 0,
doc: )
()
{
return make_number (GetConsoleOutputCP ());
}
DEFUN ("w32-set-console-output-codepage", Fw32_set_console_output_codepage,
Sw32_set_console_output_codepage, 1, 1, 0,
doc: )
(cp)
Lisp_Object cp;
{
CHECK_NUMBER (cp);
if (!IsValidCodePage (XINT (cp)))
return Qnil;
if (!SetConsoleOutputCP (XINT (cp)))
return Qnil;
return make_number (GetConsoleOutputCP ());
}
DEFUN ("w32-get-codepage-charset", Fw32_get_codepage_charset,
Sw32_get_codepage_charset, 1, 1, 0,
doc: )
(cp)
Lisp_Object cp;
{
CHARSETINFO info;
CHECK_NUMBER (cp);
if (!IsValidCodePage (XINT (cp)))
return Qnil;
if (TranslateCharsetInfo ((DWORD *) XINT (cp), &info, TCI_SRCCODEPAGE))
return make_number (info.ciCharset);
return Qnil;
}
DEFUN ("w32-get-valid-keyboard-layouts", Fw32_get_valid_keyboard_layouts,
Sw32_get_valid_keyboard_layouts, 0, 0, 0,
doc: )
()
{
int num_layouts = GetKeyboardLayoutList (0, NULL);
HKL * layouts = (HKL *) alloca (num_layouts * sizeof (HKL));
Lisp_Object obj = Qnil;
if (GetKeyboardLayoutList (num_layouts, layouts) == num_layouts)
{
while (--num_layouts >= 0)
{
DWORD kl = (DWORD) layouts[num_layouts];
obj = Fcons (Fcons (make_number (kl & 0xffff),
make_number ((kl >> 16) & 0xffff)),
obj);
}
}
return obj;
}
DEFUN ("w32-get-keyboard-layout", Fw32_get_keyboard_layout,
Sw32_get_keyboard_layout, 0, 0, 0,
doc: )
()
{
DWORD kl = (DWORD) GetKeyboardLayout (dwWindowsThreadId);
return Fcons (make_number (kl & 0xffff),
make_number ((kl >> 16) & 0xffff));
}
DEFUN ("w32-set-keyboard-layout", Fw32_set_keyboard_layout,
Sw32_set_keyboard_layout, 1, 1, 0,
doc: )
(layout)
Lisp_Object layout;
{
DWORD kl;
CHECK_CONS (layout);
CHECK_NUMBER_CAR (layout);
CHECK_NUMBER_CDR (layout);
kl = (XINT (XCAR (layout)) & 0xffff)
| (XINT (XCDR (layout)) << 16);
if (dwWindowsThreadId)
{
if (PostThreadMessage (dwWindowsThreadId, WM_EMACS_SETKEYBOARDLAYOUT,
(WPARAM) kl, 0))
{
MSG msg;
GetMessage (&msg, NULL, WM_EMACS_DONE, WM_EMACS_DONE);
if (msg.wParam == 0)
return Qnil;
}
}
else if (!ActivateKeyboardLayout ((HKL) kl, 0))
return Qnil;
return Fw32_get_keyboard_layout ();
}
syms_of_ntproc ()
{
Qhigh = intern ("high");
Qlow = intern ("low");
staticpro (&Qhigh);
staticpro (&Qlow);
#ifdef HAVE_SOCKETS
defsubr (&Sw32_has_winsock);
defsubr (&Sw32_unload_winsock);
#endif
defsubr (&Sw32_short_file_name);
defsubr (&Sw32_long_file_name);
defsubr (&Sw32_set_process_priority);
defsubr (&Sw32_get_locale_info);
defsubr (&Sw32_get_current_locale_id);
defsubr (&Sw32_get_default_locale_id);
defsubr (&Sw32_get_valid_locale_ids);
defsubr (&Sw32_set_current_locale);
defsubr (&Sw32_get_console_codepage);
defsubr (&Sw32_set_console_codepage);
defsubr (&Sw32_get_console_output_codepage);
defsubr (&Sw32_set_console_output_codepage);
defsubr (&Sw32_get_valid_codepages);
defsubr (&Sw32_get_codepage_charset);
defsubr (&Sw32_get_valid_keyboard_layouts);
defsubr (&Sw32_get_keyboard_layout);
defsubr (&Sw32_set_keyboard_layout);
DEFVAR_LISP ("w32-quote-process-args", &Vw32_quote_process_args,
doc: );
Vw32_quote_process_args = Qt;
DEFVAR_LISP ("w32-start-process-show-window",
&Vw32_start_process_show_window,
doc: );
Vw32_start_process_show_window = Qnil;
DEFVAR_LISP ("w32-start-process-share-console",
&Vw32_start_process_share_console,
doc: );
Vw32_start_process_share_console = Qnil;
DEFVAR_LISP ("w32-start-process-inherit-error-mode",
&Vw32_start_process_inherit_error_mode,
doc: );
Vw32_start_process_inherit_error_mode = Qt;
DEFVAR_INT ("w32-pipe-read-delay", &w32_pipe_read_delay,
doc: );
w32_pipe_read_delay = 50;
DEFVAR_LISP ("w32-downcase-file-names", &Vw32_downcase_file_names,
doc: );
Vw32_downcase_file_names = Qnil;
#if 0
DEFVAR_LISP ("w32-generate-fake-inodes", &Vw32_generate_fake_inodes,
doc: );
Vw32_generate_fake_inodes = Qnil;
#endif
DEFVAR_LISP ("w32-get-true-file-attributes", &Vw32_get_true_file_attributes,
doc: );
Vw32_get_true_file_attributes = Qt;
staticpro (&Vw32_valid_locale_ids);
staticpro (&Vw32_valid_codepages);
}