(cal-tex-cursor-filofax-week): Renamed from cal-tex-cursor-week6.
[emacs.git] / src / w32proc.c
blob8e8d986f38c6ceb5f679bc4f09c5e7d16cdb6884
1 /* Process support for Windows NT port of GNU EMACS.
2 Copyright (C) 1992, 1995 Free Software Foundation, Inc.
4 This file is part of GNU Emacs.
6 GNU Emacs is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any later
9 version.
11 GNU Emacs is distributed in the hope that it will be useful, but WITHOUT
12 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 more details.
16 You should have received a copy of the GNU General Public License along
17 with GNU Emacs; see the file COPYING. If not, write to the Free Software
18 Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
20 Drew Bliss Oct 14, 1993
21 Adapted from alarm.c by Tim Fleehart
24 #include <config.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <errno.h>
29 #include <io.h>
30 #include <signal.h>
32 #include <windows.h>
34 #include "lisp.h"
35 #include "nt.h"
36 #include "systime.h"
38 /* #define FULL_DEBUG */
40 typedef void (_CALLBACK_ *signal_handler)(int);
42 /* Defined in process.h which conflicts with the local copy */
43 #define _P_NOWAIT 1
45 typedef struct _child_process
47 int fd;
48 HANDLE char_avail;
49 HANDLE char_consumed;
50 char chr;
51 BOOL status;
52 HANDLE process;
53 DWORD pid;
54 HANDLE thrd;
55 } child_process;
57 #define MAX_CHILDREN MAXDESC
59 #ifdef EMACSDEBUG
60 void _CRTAPI1
61 _DebPrint (char *fmt, ...)
63 char buf[256];
64 va_list args;
66 va_start (args, fmt);
67 vsprintf (buf, fmt, args);
68 va_end (args);
69 OutputDebugString (buf);
71 #endif
73 /* Child process management list. */
74 static int child_proc_count = 0;
75 static child_process child_procs[MAX_CHILDREN];
76 static child_process *dead_child = NULL;
78 #define CHILD_ACTIVE(cp) ((cp)->process != NULL)
79 #define DEACTIVATE_CHILD(cp) ((cp)->process = NULL)
81 /* Signal handlers...SIG_DFL == 0 so this is initialized correctly. */
82 static signal_handler sig_handlers[NSIG];
84 /* Fake signal implementation to record the SIGCHLD handler. */
85 signal_handler
86 win32_signal (int sig, signal_handler handler)
88 signal_handler old;
90 if (sig != SIGCHLD)
92 errno = EINVAL;
93 return SIG_ERR;
95 old = sig_handlers[sig];
96 sig_handlers[sig] = handler;
97 return old;
100 /* Find an unused process slot. */
101 static child_process *
102 new_child (void)
104 child_process *cp;
106 if (child_proc_count == MAX_CHILDREN)
107 return NULL;
109 for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
110 if (!CHILD_ACTIVE (cp))
111 return cp;
112 return &child_procs[child_proc_count++];
115 /* Find a child by pid. */
116 static child_process *
117 find_child_pid (DWORD pid)
119 child_process *cp;
121 for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
122 if (CHILD_ACTIVE (cp) && pid == cp->pid)
123 return cp;
124 return NULL;
127 /* Find a child by fd. */
128 static child_process *
129 find_child_fd (int fd)
131 child_process *cp;
133 for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
134 if (CHILD_ACTIVE (cp) && fd == cp->fd)
135 return cp;
136 return NULL;
139 /* Thread proc for child process reader threads
140 The threads just sit in a loop waiting for input
141 When they detect input, they signal the char_avail input to
142 wake up the select emulator
143 When the select emulator processes their input, it pulses
144 char_consumed so that the reader thread goes back to reading. */
145 DWORD WINAPI
146 reader_thread (void *arg)
148 child_process *cp;
150 /* Our identity */
151 cp = (child_process *)arg;
153 /* We have to wait for the go-ahead before we can start */
154 if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
155 return 0;
156 /* If something went wrong, quit */
157 if (!cp->status)
158 return 0;
160 for (;;)
162 /* Use read to get CRLF translation */
163 if (read (cp->fd, &cp->chr, sizeof (char)) == sizeof (char))
165 cp->status = TRUE;
167 else
169 #ifdef FULL_DEBUG
170 DebPrint (("reader_thread.read failed with %lu for fd %ld\n",
171 GetLastError (), cp->fd));
172 #endif
173 cp->status = FALSE;
176 if (!SetEvent (cp->char_avail))
178 DebPrint (("reader_thread.SetEvent failed with %lu for fd %ld\n",
179 GetLastError (), cp->fd));
180 break;
183 /* If the read died, the child has died so let the thread die */
184 if (!cp->status)
185 break;
187 /* Wait until our input is acknowledged before reading again */
188 if (WaitForSingleObject (cp->char_consumed, INFINITE) != WAIT_OBJECT_0)
190 DebPrint (("reader_thread.WaitForSingleObject failed with "
191 "%lu for fd %ld\n", GetLastError (), cp->fd));
192 break;
195 return 0;
198 static BOOL
199 create_child (char *exe, char *cmdline, char *env,
200 PROCESS_INFORMATION *info)
202 child_process *cp;
203 DWORD id;
204 STARTUPINFO start;
205 SECURITY_ATTRIBUTES sec_attrs;
206 SECURITY_DESCRIPTOR sec_desc;
208 cp = new_child ();
209 if (cp == NULL)
210 goto EH_Fail;
212 cp->fd = -1;
214 cp->char_avail = CreateEvent (NULL, FALSE, FALSE, NULL);
215 if (cp->char_avail == NULL)
216 goto EH_Fail;
218 cp->char_consumed = CreateEvent (NULL, FALSE, FALSE, NULL);
219 if (cp->char_consumed == NULL)
220 goto EH_char_avail;
222 cp->thrd = CreateThread (NULL, 1024, reader_thread, cp, 0, &id);
223 if (cp->thrd == NULL)
224 goto EH_char_consumed;
226 memset (&start, 0, sizeof (start));
227 start.cb = sizeof (start);
229 #ifdef HAVE_NTGUI
230 start.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
231 start.wShowWindow = SW_HIDE;
233 start.hStdInput = GetStdHandle (STD_INPUT_HANDLE);
234 start.hStdOutput = GetStdHandle (STD_OUTPUT_HANDLE);
235 start.hStdError = GetStdHandle (STD_ERROR_HANDLE);
236 #endif /* HAVE_NTGUI */
238 /* Explicitly specify no security */
239 if (!InitializeSecurityDescriptor (&sec_desc, SECURITY_DESCRIPTOR_REVISION))
240 goto EH_thrd;
241 if (!SetSecurityDescriptorDacl (&sec_desc, TRUE, NULL, FALSE))
242 goto EH_thrd;
243 sec_attrs.nLength = sizeof (sec_attrs);
244 sec_attrs.lpSecurityDescriptor = &sec_desc;
245 sec_attrs.bInheritHandle = FALSE;
247 if (!CreateProcess (exe, cmdline, &sec_attrs, NULL, TRUE,
248 CREATE_NEW_PROCESS_GROUP, env, NULL,
249 &start, info))
250 goto EH_thrd;
251 cp->process = info->hProcess;
252 cp->pid = info->dwProcessId;
254 return TRUE;
256 EH_thrd:
257 id = GetLastError ();
259 cp->status = FALSE;
260 SetEvent (cp->char_consumed);
261 EH_char_consumed:
262 CloseHandle (cp->char_consumed);
263 EH_char_avail:
264 CloseHandle (cp->char_avail);
265 EH_Fail:
266 return FALSE;
269 /* create_child doesn't know what emacs' file handle will be for waiting
270 on output from the child, so we need to make this additional call
271 to register the handle with the process
272 This way the select emulator knows how to match file handles with
273 entries in child_procs. */
274 void
275 register_child (int pid, int fd)
277 child_process *cp;
279 cp = find_child_pid (pid);
280 if (cp == NULL)
282 DebPrint (("register_child unable to find pid %lu\n", pid));
283 return;
286 #ifdef FULL_DEBUG
287 DebPrint (("register_child registered fd %d with pid %lu\n", fd, pid));
288 #endif
290 cp->fd = fd;
291 cp->status = TRUE;
293 /* Tell the reader thread to start */
294 if (!SetEvent (cp->char_consumed))
296 DebPrint (("register_child.SetEvent failed with %lu for fd %ld\n",
297 GetLastError (), cp->fd));
301 /* When a process dies its pipe will break so the reader thread will
302 signal failure to the select emulator.
303 The select emulator then calls this routine to clean up.
304 Since the thread signaled failure we can assume it is exiting. */
305 static void
306 remove_child (child_process *cp)
308 /* Reap the thread */
309 if (WaitForSingleObject (cp->thrd, INFINITE) != WAIT_OBJECT_0)
311 DebPrint (("remove_child.WaitForSingleObject (thread) failed "
312 "with %lu for fd %ld\n", GetLastError (), cp->fd));
314 CloseHandle (cp->thrd);
315 CloseHandle (cp->char_consumed);
316 CloseHandle (cp->char_avail);
318 /* Reap the process */
319 if (WaitForSingleObject (cp->process, INFINITE) != WAIT_OBJECT_0)
321 DebPrint (("remove_child.WaitForSingleObject (process) failed "
322 "with %lu for fd %ld\n", GetLastError (), cp->fd));
324 CloseHandle (cp->process);
326 DEACTIVATE_CHILD (cp);
329 /* Wait for any of our existing child processes to die
330 When it does, close its handle
331 Return the pid and fill in the status if non-NULL. */
333 /* From callproc.c */
334 extern int synch_process_alive;
335 extern int synch_process_retcode;
337 int
338 win32_wait (int *status)
340 DWORD active, retval;
341 int nh;
342 child_process *cp, *cps[MAX_CHILDREN];
343 HANDLE wait_hnd[MAX_CHILDREN];
345 nh = 0;
346 if (dead_child != NULL)
348 /* We want to wait for a specific child */
349 wait_hnd[nh] = dead_child->process;
350 cps[nh] = dead_child;
351 nh++;
353 else
355 for (cp = child_procs+(child_proc_count-1); cp >= child_procs; cp--)
356 if (CHILD_ACTIVE (cp))
358 wait_hnd[nh] = cp->process;
359 cps[nh] = cp;
360 nh++;
364 if (nh == 0)
366 /* Nothing to wait on, so fail */
367 errno = ECHILD;
368 return -1;
371 active = WaitForMultipleObjects (nh, wait_hnd, FALSE, INFINITE);
372 if (active == WAIT_FAILED)
374 errno = EBADF;
375 return -1;
377 else if (active == WAIT_TIMEOUT)
379 /* Should never happen */
380 errno = EINVAL;
381 return -1;
383 else if (active >= WAIT_OBJECT_0 &&
384 active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
386 active -= WAIT_OBJECT_0;
388 else if (active >= WAIT_ABANDONED_0 &&
389 active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS)
391 active -= WAIT_ABANDONED_0;
394 if (!GetExitCodeProcess (wait_hnd[active], &retval))
396 DebPrint (("Wait.GetExitCodeProcess failed with %lu\n",
397 GetLastError ()));
398 retval = 1;
400 if (retval == STILL_ACTIVE)
402 /* Should never happen */
403 DebPrint (("Wait.WaitForMultipleObjects returned an active process\n"));
404 errno = EINVAL;
405 return -1;
408 /* Massage the exit code from the process to match the format expected
409 by the WIFSTOPPED et al macros in syswait.h. Only WIFSIGNALLED and
410 WIFEXITED are supported; WIFSTOPPED doesn't make sense under NT. */
412 if (retval == STATUS_CONTROL_C_EXIT)
413 retval = SIGINT;
414 else
415 retval <<= 8;
417 cp = cps[active];
419 if (status)
421 *status = retval;
423 else if (synch_process_alive)
425 synch_process_alive = 0;
426 synch_process_retcode = retval;
428 TerminateThread (cp->thrd, 0);
429 CloseHandle (cp->thrd);
430 CloseHandle (cp->char_consumed);
431 CloseHandle (cp->char_avail);
432 CloseHandle (cp->process);
433 DEACTIVATE_CHILD (cp);
436 return cp->pid;
439 /* We pass our process ID to our children by setting up an environment
440 variable in their environment. */
441 char ppid_env_var_buffer[64];
443 /* When a new child process is created we need to register it in our list,
444 so intercept spawn requests. */
445 int
446 win32_spawnve (int mode, char *cmdname, char **argv, char **envp)
448 Lisp_Object program, full;
449 char *cmdline, *env, *parg, **targ;
450 int arglen;
451 PROCESS_INFORMATION pi;
453 /* Handle executable names without an executable suffix. */
454 program = make_string (cmdname, strlen (cmdname));
455 if (NILP (Ffile_executable_p (program)))
457 struct gcpro gcpro1;
459 full = Qnil;
460 GCPRO1 (program);
461 openp (Vexec_path, program, EXEC_SUFFIXES, &full, 1);
462 UNGCPRO;
463 if (NILP (full))
465 errno = EINVAL;
466 return -1;
468 cmdname = XSTRING (full)->data;
469 argv[0] = cmdname;
472 if (child_proc_count == MAX_CHILDREN)
474 errno = EAGAIN;
475 return -1;
478 /* We don't care about the other modes */
479 if (mode != _P_NOWAIT)
481 errno = EINVAL;
482 return -1;
485 /* we have to do some conjuring here to put argv and envp into the
486 form CreateProcess wants... argv needs to be a space separated/null
487 terminated list of parameters, and envp is a null
488 separated/double-null terminated list of parameters.
490 Since I have no idea how large argv and envp are likely to be
491 we figure out list lengths on the fly and allocate them. */
493 /* do argv... */
494 arglen = 0;
495 targ = argv;
496 while (*targ)
498 arglen += strlen (*targ++) + 1;
500 cmdline = malloc (arglen);
501 if (cmdline == NULL)
503 errno = ENOMEM;
504 goto EH_Fail;
506 targ = argv;
507 parg = cmdline;
508 while (*targ)
510 strcpy (parg, *targ);
511 parg += strlen (*targ++);
512 *parg++ = ' ';
514 *--parg = '\0';
516 /* and envp... */
517 arglen = 1;
518 targ = envp;
519 while (*targ)
521 arglen += strlen (*targ++) + 1;
523 sprintf (ppid_env_var_buffer, "__PARENT_PROCESS_ID=%d",
524 GetCurrentProcessId ());
525 arglen += strlen (ppid_env_var_buffer) + 1;
527 env = malloc (arglen);
528 if (env == NULL)
530 errno = ENOMEM;
531 goto EH_cmdline;
533 targ = envp;
534 parg = env;
535 while (*targ)
537 strcpy (parg, *targ);
538 parg += strlen (*targ++);
539 *parg++ = '\0';
541 strcpy (parg, ppid_env_var_buffer);
542 parg += strlen (ppid_env_var_buffer);
543 *parg++ = '\0';
544 *parg = '\0';
546 /* Now create the process. */
547 if (!create_child (cmdname, cmdline, env, &pi))
549 errno = ENOEXEC;
550 goto EH_env;
553 return pi.dwProcessId;
555 EH_env:
556 free (env);
557 EH_cmdline:
558 free (cmdline);
559 EH_Fail:
560 return -1;
563 /* Emulate the select call
564 Wait for available input on any of the given rfds, or timeout if
565 a timeout is given and no input is detected
566 wfds and efds are not supported and must be NULL. */
568 /* From ntterm.c */
569 extern HANDLE keyboard_handle;
570 /* From process.c */
571 extern int proc_buffered_char[];
573 int
574 sys_select (int nfds, SELECT_TYPE *rfds, SELECT_TYPE *wfds, SELECT_TYPE *efds,
575 EMACS_TIME *timeout)
577 SELECT_TYPE orfds;
578 DWORD timeout_ms;
579 int i, nh, nr;
580 DWORD active;
581 child_process *cp, *cps[MAX_CHILDREN + 1];
582 HANDLE wait_hnd[MAX_CHILDREN + 1];
583 #ifdef HAVE_NTGUI1
584 BOOL keyboardwait = FALSE ;
585 #endif /* HAVE_NTGUI */
587 /* If the descriptor sets are NULL but timeout isn't, then just Sleep. */
588 if (rfds == NULL && wfds == NULL && efds == NULL && timeout != NULL)
590 #ifdef HAVE_TIMEVAL
591 Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
592 #else
593 Sleep ((*timeout) * 1000);
594 #endif
595 return 0;
598 /* Otherwise, we only handle rfds, so fail otherwise. */
599 if (rfds == NULL || wfds != NULL || efds != NULL)
601 errno = EINVAL;
602 return -1;
605 orfds = *rfds;
606 FD_ZERO (rfds);
607 nr = 0;
609 /* Build a list of handles to wait on. */
610 nh = 0;
611 for (i = 0; i < nfds; i++)
612 if (FD_ISSET (i, &orfds))
614 if (i == 0)
616 #ifdef HAVE_NTGUI1
617 keyboardwait = TRUE ;
618 #else
619 /* Handle stdin specially */
620 wait_hnd[nh] = keyboard_handle;
621 cps[nh] = NULL;
622 nh++;
623 #endif /* HAVE_NTGUI */
625 /* Check for any emacs-generated input in the queue since
626 it won't be detected in the wait */
627 if (detect_input_pending ())
629 FD_SET (i, rfds);
630 nr++;
633 else
635 /* Child process input */
636 cp = find_child_fd (i);
637 if (cp)
639 #ifdef FULL_DEBUG
640 DebPrint (("select waiting on child %d fd %d\n",
641 cp-child_procs, i));
642 #endif
643 wait_hnd[nh] = cp->char_avail;
644 cps[nh] = cp;
645 nh++;
647 else
649 /* Unable to find something to wait on for this fd, fail */
650 DebPrint (("select unable to find child process "
651 "for fd %ld\n", i));
652 nh = 0;
653 break;
658 /* Never do this in win32 since we will not get paint messages */
660 #ifndef HAVE_NTGUI1
661 /* Nothing to look for, so we didn't find anything */
662 if (nh == 0)
664 if (timeout)
665 #ifdef HAVE_TIMEVAL
666 Sleep (timeout->tv_sec * 1000 + timeout->tv_usec / 1000);
667 #else
668 Sleep ((*timeout) * 1000);
669 #endif
670 return 0;
672 #endif /* !HAVE_NTGUI */
674 /* Check for immediate return without waiting */
675 if (nr > 0)
676 return nr;
679 Wait for input
680 If a child process dies while this is waiting, its pipe will break
681 so the reader thread will signal an error condition, thus, the wait
682 will wake up
684 #ifdef HAVE_TIMEVAL
685 timeout_ms = timeout ? (timeout->tv_sec * 1000 + timeout->tv_usec / 1000) : INFINITE;
686 #else
687 timeout_ms = timeout ? *timeout*1000 : INFINITE;
688 #endif
689 #ifdef HAVE_NTGUI1
690 active = MsgWaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms,QS_ALLINPUT);
691 #else
692 active = WaitForMultipleObjects (nh, wait_hnd, FALSE, timeout_ms);
693 #endif /* HAVE_NTGUI */
694 if (active == WAIT_FAILED)
696 DebPrint (("select.WaitForMultipleObjects (%d, %lu) failed with %lu\n",
697 nh, timeout_ms, GetLastError ()));
698 /* Is there a better error? */
699 errno = EBADF;
700 return -1;
702 else if (active == WAIT_TIMEOUT)
704 return 0;
706 #ifdef HAVE_NTGUI1
707 else if (active == WAIT_OBJECT_0 + nh)
709 /* Keyboard input available */
710 FD_SET (0, rfds);
712 /* This shouldn't be necessary, but apparently just setting the input
713 fd is not good enough for emacs */
714 // read_input_waiting ();
716 return (1) ;
718 #endif /* HAVE_NTGUI */
719 else if (active >= WAIT_OBJECT_0 &&
720 active < WAIT_OBJECT_0+MAXIMUM_WAIT_OBJECTS)
722 active -= WAIT_OBJECT_0;
724 else if (active >= WAIT_ABANDONED_0 &&
725 active < WAIT_ABANDONED_0+MAXIMUM_WAIT_OBJECTS)
727 active -= WAIT_ABANDONED_0;
730 if (cps[active] == NULL)
732 /* Keyboard input available */
733 FD_SET (0, rfds);
734 nr++;
736 /* This shouldn't be necessary, but apparently just setting the input
737 fd is not good enough for emacs */
738 read_input_waiting ();
740 else
742 /* Child process */
743 cp = cps[active];
745 /* If status is FALSE the read failed so don't report input */
746 if (cp->status)
748 FD_SET (cp->fd, rfds);
749 proc_buffered_char[cp->fd] = cp->chr;
750 nr++;
752 else
754 /* The SIGCHLD handler will do a Wait so we know it won't
755 return until the process is dead
756 We force Wait to only wait for this process to avoid it
757 picking up other children that happen to be dead but that
758 we haven't noticed yet
759 SIG_DFL for SIGCHLD is ignore? */
760 if (sig_handlers[SIGCHLD] != SIG_DFL &&
761 sig_handlers[SIGCHLD] != SIG_IGN)
763 #ifdef FULL_DEBUG
764 DebPrint (("select calling SIGCHLD handler for pid %d\n",
765 cp->pid));
766 #endif
767 dead_child = cp;
768 sig_handlers[SIGCHLD](SIGCHLD);
769 dead_child = NULL;
772 /* Clean up the child process entry in the table */
773 remove_child (cp);
776 return nr;
780 Substitute for certain kill () operations
782 int
783 win32_kill_process (int pid, int sig)
785 child_process *cp;
787 /* Only handle signals that will result in the process dying */
788 if (sig != SIGINT && sig != SIGKILL && sig != SIGQUIT && sig != SIGHUP)
790 errno = EINVAL;
791 return -1;
794 cp = find_child_pid (pid);
795 if (cp == NULL)
797 DebPrint (("win32_kill_process didn't find a child with pid %lu\n", pid));
798 errno = ECHILD;
799 return -1;
802 if (sig == SIGINT)
804 /* Fake Ctrl-Break. */
805 if (!GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, pid))
807 DebPrint (("win32_kill_process.GenerateConsoleCtrlEvent return %d "
808 "for pid %lu\n", GetLastError (), pid));
809 errno = EINVAL;
810 return -1;
813 else
815 /* Kill the process. On Win32 this doesn't kill child processes
816 so it doesn't work very well for shells which is why it's
817 not used in every case. */
818 if (!TerminateProcess (cp->process, 0xff))
820 DebPrint (("win32_kill_process.TerminateProcess returned %d "
821 "for pid %lu\n", GetLastError (), pid));
822 errno = EINVAL;
823 return -1;
826 return 0;
829 /* If the channel is a pipe this read might block since we don't
830 know how many characters are available, so check and read only
831 what's there
832 We also need to wake up the reader thread once we've read our data. */
833 int
834 read_child_output (int fd, char *buf, int max)
836 HANDLE h;
837 int to_read, nchars;
838 DWORD waiting;
839 child_process *cp;
841 h = (HANDLE)_get_osfhandle (fd);
842 if (GetFileType (h) == FILE_TYPE_PIPE)
844 PeekNamedPipe (h, NULL, 0, NULL, &waiting, NULL);
845 to_read = min (waiting, (DWORD)max);
847 else
848 to_read = max;
850 /* Use read to get CRLF translation */
851 nchars = read (fd, buf, to_read);
853 if (GetFileType (h) == FILE_TYPE_PIPE)
855 /* Wake up the reader thread
856 for this process */
857 cp = find_child_fd (fd);
858 if (cp)
860 if (!SetEvent (cp->char_consumed))
861 DebPrint (("read_child_output.SetEvent failed with "
862 "%lu for fd %ld\n", GetLastError (), fd));
864 else
865 DebPrint (("read_child_output couldn't find a child with fd %d\n",
866 fd));
869 return nchars;