1 /*
2  * w32pipe:  win32 clone of npopen.c--utilizes native pipes for hosts
3  *           and shells that can handle same, else falls back on temp files
4  *           (but uses a much different algorithm than that of npopen.c).
5  *
6  * Background
7  * ==========
8  * The techniques used in native_npclose() and native_inout_popen() are
9  * derived from much trial and error and support "pipe" I/O in both a
10  * console and GUI environment.  You may _think_ you have a better way of
11  * effecting the functionality provided in this module and that may well be
12  * the case.  But be sure you test your new code with at least these
13  * versions of Win32:
14  *
15  *      win95, win98, OSR2, NT 4.0, win2K
16  *
17  * For each HOST, be sure to test read pipes, write pipes, and filters (and
18  * test repeatedly within the same vile session).
19  *
20  *
21  * Acknowledgments
22  * ===============
23  * Until I read Steve Kirkendall's code for the Win32 version of elvis, I
24  * did not realize that attempting to redirect stdin to a device is a
25  * _not_ a good strategy.
26  *
27  *
28  * Caveats
29  * =======
30  * -- The MSDN Knowledge Base has example code that uses anonymous pipes
31  *    to redirect a spawned process's stdin, stdout, and stderr.  Don't go
32  *    there.
33  *
34  * -- On a Win2K host, DuplicateHandle() failed.
35  *
36  * -- The original Win95 console shell (command.com) accesses the floppy
37  *    drive each and every time a process communicates with it via a pipe
38  *    and the OSR2 shell abruptly hangs under similar conditions.  By
39  *    default, then, on a WinNT host, vile's pipes are implemented using
40  *    native pipes (i.e., with the code in this module), while Win95 hosts
41  *    fall back to temp file communication.  If the user's replacement
42  *    Win95 shell does not exhibit communication problems similar to
43  *    those described above (e.g., any 32-bit shell), vile may be
44  *    forced to use native Win32 pipes by setting the global mode
45  *    "w32pipes" (e.g., "se w32pipes").  For further details, including
46  *    a description of "force-console" mode, refer to the file
47  *    doc/w32modes.doc .
48  *
49  * -- This module's native pipes implementation exhibits various problems
50  *    when a 16-bit console app is exec'd.  On a win95 host, the editor
51  *    and shell generally hang.  WinNT does better, but winvile creates
52  *    "background" shell windows that require manual closure.
53  *
54  * -- This module configures read pipes so that the exec'd app reads
55  *    it's input from an empty file.  That's a necessity, not a bug.
56  *    Consequently, if an attempt is made to read data from an app
57  *    that itself reads input (why would you do that?), the app will
58  *    appear to hang if it reopens stdin on the console (because vile's
59  *    stdin is not available to the app--another necessity).  In this
60  *    situation, kill the app by typing ^C (and then please apply for a
61  *    QA position with a certain Redmond company).
62  *
63  * $Id: w32pipe.c,v 1.41 2018/11/05 00:50:01 tom Exp $
64  */
65 
66 #define HAVE_FCNTL_H 1
67 
68 #include "estruct.h"
69 #include "edef.h"
70 
71 #include <io.h>
72 #ifdef __BORLANDC__
73 #define __MSC
74 #include <sys/stat.h>
75 #endif
76 #include <share.h>
77 #include <process.h>
78 #include <ctype.h>
79 
80 #define BAD_FD          (-1)
81 #define BAD_PROC_HANDLE (INVALID_HANDLE_VALUE)
82 #define PIPESIZ         (4096)
83 #define SHELL_ERR_MSG   \
84           "[shell process \"%s\" failed, check $shell state var]"
85 
86 static int native_inout_popen(FILE **fr, FILE **fw, char *cmd);
87 static void native_npclose(FILE *fp);
88 static int tmp_inout_popen(FILE **fr, FILE **fw, char *cmd);
89 static void tmp_npclose(FILE *fp);
90 
91 static HANDLE proc_handle;
92 static char *tmpin_name;
93 
94 /* ---------------- Cloned from npopen.c ---------------------------- */
95 
96 FILE *
npopen(char * cmd,const char * type)97 npopen(char *cmd, const char *type)
98 {
99     FILE *ff = 0;
100 
101     if (*type == 'r')
102 	(void) inout_popen(&ff, (FILE **) 0, cmd);
103     else if (*type == 'w')
104 	(void) inout_popen((FILE **) 0, &ff, cmd);
105     return ff;
106 }
107 
108 int
softfork(void)109 softfork(void)			/* dummy function to make filter-region work */
110 {
111     return 0;
112 }
113 
114 int
inout_popen(FILE ** fr,FILE ** fw,char * cmd)115 inout_popen(FILE **fr, FILE **fw, char *cmd)
116 {
117     TRACE(("inout_popen(fr=%p, fw=%p, cmd='%s')\n", fr, fw, cmd));
118 
119     ffstatus = file_is_pipe;
120     fileeof = FALSE;
121     append_libdir_to_path();
122 #ifdef GMDW32PIPES
123     if (global_g_val(GMDW32PIPES))
124 	return (native_inout_popen(fr, fw, cmd));
125 #endif
126     return (tmp_inout_popen(fr, fw, cmd));
127 }
128 
129 void
npclose(FILE * fp)130 npclose(FILE *fp)
131 {
132 #ifdef GMDW32PIPES
133     if (global_g_val(GMDW32PIPES)) {
134 	native_npclose(fp);
135 	return;
136     }
137 #endif
138     tmp_npclose(fp);
139 }
140 
141 /* ------------------- native pipe routines first ------------------- */
142 
143 /*
144  * when desperate to communicate an error, enable popup messages and
145  * use mlforce().
146  */
147 static void
lastditch_msg(char * msg)148 lastditch_msg(char *msg)
149 {
150 #if OPT_POPUP_MSGS
151     int save = global_g_val(GMDPOPUP_MSGS);
152 
153     set_global_g_val(GMDPOPUP_MSGS, TRUE);
154 #endif
155     mlforce(msg);
156 #if OPT_POPUP_MSGS
157     update(FALSE);
158     popup_msgs();
159     update(FALSE);
160     set_global_g_val(GMDPOPUP_MSGS, save);
161 #endif
162 }
163 
164 static void
common_cleanup(void)165 common_cleanup(void)
166 {
167     if (tmpin_name) {
168 	(void) remove(tmpin_name);
169 	(void) free(tmpin_name);
170 	tmpin_name = NULL;
171     }
172     restore_console_title();
173 }
174 
175 #define close_fd(fd) if (fd != BAD_FD) { close(fd); fd = BAD_FD; }
176 
177 static void
close_proc_handle(void)178 close_proc_handle(void)
179 {
180     TRACE(("close_proc_handle %p\n", proc_handle));
181     (void) w32_close_handle(proc_handle);
182     proc_handle = BAD_PROC_HANDLE;
183 }
184 
185 static HANDLE
exec_shell(char * cmd,HANDLE * handles,int hide_child GCC_UNUSED)186 exec_shell(char *cmd, HANDLE * handles, int hide_child GCC_UNUSED)
187 {
188     W32_CHAR *w32_cmdstr = 0;
189     char *cmdstr;
190     int freestr;
191     PROCESS_INFORMATION pi;
192     STARTUPINFO si;
193 
194     proc_handle = BAD_PROC_HANDLE;	/* in case of failure */
195     TRACE((T_CALLED "exec_shell %s\n", cmd));
196     if ((cmdstr = mk_shell_cmd_str(cmd, &freestr, TRUE)) == NULL) {
197 	/* heap exhausted! */
198 
199 	no_memory("exec_shell");
200 
201 	/* Give user a chance to read message--more will surely follow. */
202 	Sleep(3000);
203     } else if ((w32_cmdstr = w32_charstring(cmdstr)) == 0) {
204 	no_memory("exec_shell");
205     } else {
206 
207 	memset(&si, 0, sizeof(si));
208 	/* *INDENT-EQLS* */
209 	si.cb         = sizeof(si);
210 	si.dwFlags    = STARTF_USESTDHANDLES;
211 	si.hStdInput  = handles[0];
212 	si.hStdOutput = handles[1];
213 	si.hStdError  = handles[2];
214 #if DISP_NTWIN
215 	if (hide_child) {
216 	    si.dwFlags    |= STARTF_USESHOWWINDOW;
217 	    si.wShowWindow = SW_HIDE;
218 	}
219 #endif
220 	TRACE(("CreateProcess %s (pipe)\n", cmdstr));
221 	if (CreateProcess(NULL,
222 			  w32_cmdstr,
223 			  NULL,
224 			  NULL,
225 			  TRUE,	/* Inherit handles */
226 			  0,
227 			  NULL,
228 			  NULL,
229 			  &si,
230 			  &pi)) {
231 	    /* Success */
232 
233 	    w32_close_handle(pi.hThread);
234 	    proc_handle = pi.hProcess;
235 	    TRACE(("...created proc_handle %p\n", proc_handle));
236 	}
237     }
238     FreeIfNeeded(cmdstr);
239     FreeIfNeeded(w32_cmdstr);
240     returnPtr(proc_handle);
241 }
242 
243 static HANDLE
get_handle(int fd)244 get_handle(int fd)
245 {
246     HANDLE result;
247     result = (HANDLE) _get_osfhandle(fd);
248     return result;
249 }
250 
251 static int
native_inout_popen(FILE ** fr,FILE ** fw,char * cmd)252 native_inout_popen(FILE **fr, FILE **fw, char *cmd)
253 {
254     char buf[NFILEN + 128];
255     HANDLE handles[3];
256     int i, rc, rp[3], tmpin_fd, wp[3];
257 
258     TRACE((T_CALLED "native_inout_popen cmd=%s\n", cmd));
259 
260     /* *INDENT-EQLS* */
261     proc_handle   = BAD_PROC_HANDLE;
262     rp[0]         = rp[1] = rp[2] = wp[0] = wp[1] = wp[2] = BAD_FD;
263     handles[0]    = handles[1] = handles[2] = INVALID_HANDLE_VALUE;
264     tmpin_fd      = BAD_FD;
265     tmpin_name    = NULL;
266     set_console_title(cmd);
267 
268     if (is_win95()) {
269 	char *cmdp;
270 
271 	/*
272 	 * If w32pipes is set on a win95 host, you don't ever want slowreadf()
273 	 * to periodically update the display while an intrinisic, high
274 	 * bandwidth DOS command is in progress.  Reason:  the win95 shell,
275 	 * command.com, will simply hang in the following scenario:
276 	 *
277 	 *    ^X!<high_bandwidth_cmd>
278 	 *
279 	 *         and
280 	 *
281 	 *    PIPESIZ < output of <high_bandwidth_cmd>
282 	 *
283 	 * I'm assuming that what's going on here is that command.com is
284 	 * written in ASM and is using very low level BIOS/DOS calls to
285 	 * effect output and, furthermore, that these low level calls don't
286 	 * block when the input consumer is busy.
287 	 */
288 
289 	cmdp = skip_blanks(cmd);
290 	nowait_pipe_cmd = (strnicmp(cmdp, "dir", 3) == 0) ||
291 	    (strnicmp(cmdp, "type", 4) == 0);
292     }
293     do {
294 	if (fr) {
295 	    *fr = NULL;
296 
297 	    /*
298 	     * Open (parent's) input pipe in TEXT mode, which will force
299 	     * translation of the child's CR/LF record delimiters to NL
300 	     * and keep the dreaded ^M chars from temporarily appearing
301 	     * in a vile buffer (ugly).
302 	     */
303 	    if (_pipe(rp, PIPESIZ, O_TEXT | O_NOINHERIT) == -1)
304 		break;
305 	    if ((rp[2] = _dup(rp[1])) == -1)
306 		break;
307 	    handles[2] = handles[1] = get_handle(rp[2]);
308 	    (void) close(rp[1]);
309 	    rp[1] = BAD_FD;
310 	    if (!fw) {
311 		/*
312 		 * This is a read pipe (only).  Connect child's stdin to
313 		 * an empty file.  Under no circumstances should the
314 		 * child's stdin be connected to a device (else lots of
315 		 * screwy things will occur).  In particular, connecting
316 		 * the child's stdin to the parent's stdin will cause
317 		 * aborts and hangs on the various Win32 hosts.  You've
318 		 * been warned.
319 		 */
320 
321 		if ((tmpin_name = _tempnam(getenv("TEMP"), "vile")) == NULL)
322 		    break;
323 		if ((tmpin_fd = open(tmpin_name,
324 				     O_RDONLY | O_CREAT | O_TRUNC,
325 				     _S_IWRITE | _S_IREAD)) == BAD_FD) {
326 		    break;
327 		}
328 		handles[0] = get_handle(tmpin_fd);
329 	    }
330 	    if ((*fr = fdopen(rp[0], "r")) == 0)
331 		break;
332 	}
333 	if (fw) {
334 	    *fw = NULL;
335 
336 	    /*
337 	     * Open (child's) output pipe in binary mode, which will
338 	     * prevent translation of the parent's CR/LF record delimiters
339 	     * to NL.  Apparently, many apps want those delimiters :-) .
340 	     */
341 	    if (_pipe(wp, PIPESIZ, O_BINARY | O_NOINHERIT) == -1)
342 		break;
343 	    if ((wp[2] = _dup(wp[0])) == -1)
344 		break;
345 	    handles[0] = get_handle(wp[2]);
346 	    (void) close(wp[0]);
347 	    wp[0] = BAD_FD;
348 	    if (!fr)
349 		handles[1] = handles[2] = GetStdHandle(STD_OUTPUT_HANDLE);
350 	    if ((*fw = fdopen(wp[1], "w")) == 0)
351 		break;
352 	}
353 	rc = (exec_shell(cmd,
354 			 handles,
355 			 fr != NULL	/* Child wdw hidden unless write pipe. */
356 	      ) == BAD_PROC_HANDLE) ? FALSE : TRUE;
357 	if (fw) {
358 	    if (!rc) {
359 		/* Shell process failed, put complaint in user's face. */
360 
361 		sprintf(buf, SHELL_ERR_MSG, get_shell());
362 		lastditch_msg(buf);
363 	    }
364 	    w32_close_handle(handles[0]);
365 	}
366 	if (fr) {
367 	    if (!rc) {
368 		unsigned len;
369 
370 		/*
371 		 * Shell process failed, put complaint in user's buffer.
372 		 * Can't write to handles[1] on a win2k host because the
373 		 * previously failed CreateProcess() call damaged the
374 		 * handle.
375 		 */
376 		len = (unsigned) (lsprintf(buf,
377 					   SHELL_ERR_MSG,
378 					   get_shell()) - buf);
379 		(void) write(rp[2], buf, len);
380 		(void) close(rp[2]);	/* in weird state; why not? */
381 	    }
382 	    w32_close_handle(handles[1]);
383 	    close_fd(tmpin_fd);
384 	}
385 	returnCode(rc);
386     }
387     while (FALSE);
388 
389     /* If we get here -- some operation has failed.  Clean up. */
390 
391     close_fd(wp[0]);
392     close_fd(wp[1]);
393     close_fd(wp[2]);
394     close_fd(rp[0]);
395     close_fd(rp[1]);
396     close_fd(rp[2]);
397     close_fd(tmpin_fd);
398     for (i = 0; i < 3; i++) {
399 	if (handles[i] != INVALID_HANDLE_VALUE)
400 	    w32_close_handle(handles[i]);
401     }
402     common_cleanup();
403     returnCode(FALSE);
404 }
405 
406 static void
native_npclose(FILE * fp)407 native_npclose(FILE *fp)
408 {
409     int term_status;
410 
411     (void) fflush(fp);
412     (void) fclose(fp);
413     if (proc_handle != BAD_PROC_HANDLE) {
414 	(void) cwait(&term_status, (CWAIT_PARAM_TYPE) proc_handle, 0);
415 	TRACE(("...CreateProcess finished waiting in native_npclose\n"));
416 	close_proc_handle();
417     }
418     common_cleanup();
419 }
420 
421 /* ------------------- tmp file pipe routines last ------------------
422  * --                                                              --
423  * -- The key to making these routines work is recognizing that    --
424  * -- under windows it's necessary to wait for the writer process  --
425  * -- to finish and close tmp file A before the reader process     --
426  * -- reads from A.                                                --
427  * ------------------------------------------------------------------
428  */
429 
430 static HANDLE handles[3];
431 static int stdin_fd, stdout_fd;
432 static char *stdin_name, *stdout_name, *shcmd;
433 
434 static void
tmp_cleanup(void)435 tmp_cleanup(void)
436 {
437     close_fd(stdin_fd);
438     close_fd(stdout_fd);
439 
440     if (stdin_name) {
441 	(void) remove(stdin_name);
442 	(void) free(stdin_name);
443 	stdin_name = NULL;
444     }
445     if (stdout_name) {
446 	(void) remove(stdout_name);
447 	(void) free(stdout_name);
448 	stdout_name = NULL;
449     }
450     common_cleanup();
451 }
452 
453 static int
tmp_inout_popen(FILE ** fr,FILE ** fw,char * cmd)454 tmp_inout_popen(FILE **fr, FILE **fw, char *cmd)
455 {
456     char buf[NFILEN + 128];
457     DWORD dummy, len;
458     int rc, term_status, tmpin_fd;
459 
460     TRACE(("tmp_inout_popen cmd=%s\n", cmd));
461 
462     /* *INDENT-EQLS* */
463     proc_handle = BAD_PROC_HANDLE;
464     handles[0]  = handles[1] = handles[2] = INVALID_HANDLE_VALUE;
465     tmpin_fd    = stdin_fd = stdout_fd = BAD_FD;
466     tmpin_name  = stdin_name = stdout_name = NULL;
467     set_console_title(cmd);
468 
469     do {
470 	if (fr) {
471 	    *fr = NULL;
472 	    if ((stdin_name = _tempnam(getenv("TEMP"), "vile")) == NULL)
473 		break;
474 	    if ((stdin_fd = open(stdin_name,
475 				 O_RDWR | O_CREAT | O_TRUNC | O_TEXT,
476 				 _S_IWRITE | _S_IREAD)) == BAD_FD) {
477 		break;
478 	    }
479 	    handles[2] = handles[1] = get_handle(stdin_fd);
480 	    if (!fw) {
481 		/*
482 		 * This is a read pipe (only).  Connect child's stdin to
483 		 * an empty file.  Under no circumstances should the
484 		 * child's stdin be connected to a device (else lots of
485 		 * screwy things will occur).  In particular, connecting
486 		 * the child's stdin to the parent's stdin will cause
487 		 * aborts and hangs on the various Win32 hosts.  You've
488 		 * been warned.
489 		 */
490 
491 		if ((tmpin_name = _tempnam(getenv("TEMP"), "vile")) == NULL)
492 		    break;
493 		if ((tmpin_fd = open(tmpin_name,
494 				     O_RDONLY | O_CREAT | O_TRUNC,
495 				     _S_IWRITE | _S_IREAD)) == BAD_FD) {
496 		    break;
497 		}
498 		handles[0] = get_handle(tmpin_fd);
499 	    } else {
500 		/*
501 		 * Set up descriptor for filter operation.   Note the
502 		 * subtleties here:  exec'd shell is passed a descriptor
503 		 * to the temp file that's opened "w".  The editor
504 		 * receives a descriptor to the file that's opened "r".
505 		 */
506 
507 		if ((*fr = fopen(stdin_name, "r")) == NULL)
508 		    break;
509 	    }
510 	}
511 	if (fw) {
512 	    *fw = NULL;
513 
514 	    /* create a temp file to receive data from the editor */
515 	    if ((stdout_name = _tempnam(getenv("TEMP"), "vile")) == NULL)
516 		break;
517 	    if ((stdout_fd = open(stdout_name,
518 				  O_RDWR | O_CREAT | O_TRUNC | O_BINARY,
519 				  _S_IWRITE | _S_IREAD)) == BAD_FD) {
520 		break;
521 	    }
522 	    if ((*fw = fdopen(stdout_fd, "w")) == 0)
523 		break;
524 
525 	    /*
526 	     * we're all set up, but can't exec "cmd" until the editor
527 	     * writes data to the temp file connected to stdout.
528 	     */
529 	    shcmd = cmd;	/* remember this */
530 	    return (TRUE);
531 	}
532 
533 	/* This must be a read (only) pipe.  Appropriate to exec "cmd". */
534 	rc = (exec_shell(cmd,
535 			 handles,
536 			 TRUE	/* hide child wdw */
537 	      ) == BAD_PROC_HANDLE) ? FALSE : TRUE;
538 
539 	if (!rc) {
540 	    /*
541 	     * Shell process failed, put complaint in user's buffer, which
542 	     * is currently proxied by a temp file that the editor will
543 	     * suck in shortly.
544 	     */
545 	    len = (DWORD) (lsprintf(buf, SHELL_ERR_MSG, get_shell()) - buf);
546 	    (void) WriteFile(handles[1], buf, len, &dummy, NULL);
547 	    FlushFileBuffers(handles[1]);
548 	} else {
549 	    /* wait for exec'd process to exit */
550 
551 	    (void) cwait(&term_status, (CWAIT_PARAM_TYPE) proc_handle, 0);
552 	    TRACE(("...CreateProcess finished waiting in tmp_inout_popen\n"));
553 	    close_proc_handle();
554 	}
555 
556 	if (fr) {
557 	    /*
558 	     * When closing descriptors shared between parent and child, order
559 	     * is quite important when $shell == command.com .  In this
560 	     * situation, the descriptors can't be closed until the exec'd
561 	     * process exits (I kid you not).
562 	     */
563 	    close_fd(stdin_fd);
564 	    (void) close(tmpin_fd);
565 
566 	    /* let the editor consume the output of the read pipe */
567 	    if ((*fr = fopen(stdin_name, "r")) == NULL) {
568 		/*
569 		 * impossible to put error in user's buffer since that file
570 		 * descriptor is closed.
571 		 */
572 		sprintf(buf,
573 			"[error opening temp file \"%s\": %s]",
574 			stdin_name,
575 			strerror(errno));
576 		lastditch_msg(buf);
577 		break;
578 	    }
579 	}
580 	return (rc);
581     }
582     while (FALSE);
583 
584     /* If we get here -- some operation has failed.  Clean up. */
585     tmp_cleanup();
586     return (FALSE);
587 }
588 
589 /* npflush is called for filter ops effected with temp files */
590 void
npflush(void)591 npflush(void)
592 {
593     char buf[NFILEN + 128];
594     int rc, term_status;
595 
596     /*
597      * caller has filled and closed the write pipe data stream.  time to
598      * exec a process.
599      */
600 
601     if ((stdout_fd = open(stdout_name, O_RDONLY | O_BINARY)) == BAD_FD) {
602 	/* oh my, put complaint in user's face. */
603 
604 	sprintf(buf, "[unable to open temp file \"%s\": %s]",
605 		stdout_name,
606 		strerror(errno));
607 	lastditch_msg(buf);
608     } else {
609 	/* handles[1-2] were initialized by tmp_npopen_open() */
610 
611 	handles[0] = get_handle(stdout_fd);
612 	rc = (exec_shell(shcmd,
613 			 handles,
614 			 TRUE	/* do hide child window */
615 	      ) == BAD_PROC_HANDLE) ? FALSE : TRUE;
616 	if (!rc) {
617 	    /* Shell process failed, put complaint in user's face. */
618 
619 	    sprintf(buf, SHELL_ERR_MSG, get_shell());
620 	    lastditch_msg(buf);
621 	} else {
622 	    /* now wait for app to exit */
623 
624 	    (void) cwait(&term_status, (CWAIT_PARAM_TYPE) proc_handle, 0);
625 	    TRACE(("...CreateProcess finished waiting in npflush\n"));
626 	    close_proc_handle();
627 	}
628 
629 	/*
630 	 * When closing descriptors shared between parent and child, order
631 	 * is quite important when $shell == command.com .  In this
632 	 * situation, the descriptors can't be closed until the exec'd
633 	 * process exits.
634 	 */
635 	close_fd(stdout_fd);
636     }
637 }
638 
639 static void
tmp_npclose(FILE * fp)640 tmp_npclose(FILE *fp)
641 {
642     char buf[NFILEN + 128];
643     int rc, term_status;
644 
645     (void) fflush(fp);
646     (void) fclose(fp);
647 
648     if (stdout_fd != BAD_FD) {
649 	/*
650 	 * write pipe, but not a filter.  Editor has written data to temp
651 	 * file, time now to exec "cmd" and hook its stdin to the file.
652 	 *
653 	 * It should be noted that exec'ing a process in the npclose()
654 	 * phase of a write pipe is not exactly keeping in spirit with
655 	 * the control flow in file.c :-) .  However, the strategy used
656 	 * here ensures that the launched process reads a temp file that
657 	 * is completey flushed to disk.  The only direct drawback with
658 	 * this approach is that when the exec'd process exits, the user
659 	 * does not receive a "[press return to continue]" prompt from
660 	 * file.c .  But, cough, we can work around that problem :-) .
661 	 */
662 
663 	if ((stdout_fd = open(stdout_name, O_RDONLY | O_BINARY)) == BAD_FD) {
664 	    /* oh my, put complaint in user's face. */
665 
666 	    sprintf(buf, "[unable to open temp file \"%s\": %s]",
667 		    stdout_name,
668 		    strerror(errno));
669 	    lastditch_msg(buf);
670 	} else {
671 	    handles[0] = get_handle(stdout_fd);
672 	    handles[1] = handles[2] = GetStdHandle(STD_OUTPUT_HANDLE);
673 	    rc = (exec_shell(shcmd,
674 			     handles,
675 			     FALSE	/* don't hide child window */
676 		  ) == BAD_PROC_HANDLE) ? FALSE : TRUE;
677 	    if (!rc) {
678 		/* Shell process failed, put complaint in user's face. */
679 
680 		sprintf(buf, SHELL_ERR_MSG, get_shell());
681 		lastditch_msg(buf);
682 	    } else {
683 		/* now wait for app to exit */
684 
685 		(void) cwait(&term_status, (CWAIT_PARAM_TYPE) proc_handle, 0);
686 		TRACE(("...CreateProcess finished waiting in tmp_npclose\n"));
687 		close_proc_handle();
688 	    }
689 
690 	    /*
691 	     * When closing descriptors shared between parent and child,
692 	     * order is quite important when $shell == command.com .  In
693 	     * this situation, the descriptors can't be closed until the
694 	     * exec'd process exits.
695 	     */
696 	    close_fd(stdout_fd);
697 	}
698 	pressreturn();		/* cough */
699 	sgarbf = TRUE;
700     }
701     tmp_cleanup();
702 }
703