1c9e2be55Smrg/* $XConsortium: command.c,v 2.49 95/04/05 19:59:06 kaleb Exp $ */
2c9e2be55Smrg/* $XFree86: xc/programs/xmh/command.c,v 3.8 2001/12/09 15:48:36 herrb Exp $ */
3c9e2be55Smrg
4c9e2be55Smrg/*
5c9e2be55Smrg *			  COPYRIGHT 1987, 1989
6c9e2be55Smrg *		   DIGITAL EQUIPMENT CORPORATION
7c9e2be55Smrg *		       MAYNARD, MASSACHUSETTS
8c9e2be55Smrg *			ALL RIGHTS RESERVED.
9c9e2be55Smrg *
10c9e2be55Smrg * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND
11c9e2be55Smrg * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION.
12c9e2be55Smrg * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR
13c9e2be55Smrg * ANY PURPOSE.  IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
14c9e2be55Smrg *
15c9e2be55Smrg * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT
16c9e2be55Smrg * RIGHTS, APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN
17c9e2be55Smrg * ADDITION TO THAT SET FORTH ABOVE.
18c9e2be55Smrg *
19c9e2be55Smrg *
20c9e2be55Smrg * Permission to use, copy, modify, and distribute this software and its
21c9e2be55Smrg * documentation for any purpose and without fee is hereby granted, provided
22c9e2be55Smrg * that the above copyright notice appear in all copies and that both that
23c9e2be55Smrg * copyright notice and this permission notice appear in supporting
24c9e2be55Smrg * documentation, and that the name of Digital Equipment Corporation not be
25c9e2be55Smrg * used in advertising or publicity pertaining to distribution of the software
26c9e2be55Smrg * without specific, written prior permission.
27c9e2be55Smrg */
28c9e2be55Smrg
29c9e2be55Smrg/* command.c -- interface to exec mh commands. */
30c9e2be55Smrg
31c9e2be55Smrg#include "xmh.h"
32c9e2be55Smrg#include <X11/Xpoll.h>
33c9e2be55Smrg#include <sys/ioctl.h>
34c9e2be55Smrg#include <signal.h>
35c9e2be55Smrg#include <sys/wait.h>
36d859ff80Smrg#if defined(SVR4)
37c9e2be55Smrg#include <sys/filio.h>
38c9e2be55Smrg#endif
39c9e2be55Smrg
40c9e2be55Smrg/* number of user input events to queue before malloc */
41c9e2be55Smrg#define TYPEAHEADSIZE 20
42c9e2be55Smrg
43d859ff80Smrg#ifndef HAVE_WORKING_VFORK
44d859ff80Smrg#define vfork fork
45c9e2be55Smrg#else
46d859ff80Smrg#ifdef HAVE_VFORK_H
47c9e2be55Smrg#include <vfork.h>
48c9e2be55Smrg#endif
49c9e2be55Smrg#endif
50c9e2be55Smrg
51c9e2be55Smrgtypedef struct _CommandStatus {
52c9e2be55Smrg    Widget	popup;		 /* must be first; see PopupStatus */
53c9e2be55Smrg    struct _LastInput lastInput; /* must be second; ditto */
54c9e2be55Smrg    char*	shell_command;	 /* must be third; for XmhShellCommand */
55c9e2be55Smrg    int		child_pid;
56c9e2be55Smrg    XtInputId	output_inputId;
57c9e2be55Smrg    XtInputId	error_inputId;
58c9e2be55Smrg    int		output_pipe[2];
59c9e2be55Smrg    int		error_pipe[2];
60c9e2be55Smrg    char*	output_buffer;
61c9e2be55Smrg    int		output_buf_size;
62c9e2be55Smrg    char*	error_buffer;
63c9e2be55Smrg    int		error_buf_size;
64c9e2be55Smrg} CommandStatusRec, *CommandStatus;
65c9e2be55Smrg
66c9e2be55Smrgtypedef char* Pointer;
67c9e2be55Smrgstatic void FreeStatus(XMH_CB_ARGS);
68c9e2be55Smrgstatic void CheckReadFromPipe(int, char **, int *, Bool);
69c9e2be55Smrg
7066d665a3Smrgstatic void SystemError(const char* text)
71c9e2be55Smrg{
72c9e2be55Smrg    char msg[BUFSIZ];
7366d665a3Smrg    snprintf( msg, sizeof(msg), "%s; errno = %d %s", text, errno,
74c9e2be55Smrg	     strerror(errno));
75c9e2be55Smrg    XtWarning( msg );
76c9e2be55Smrg}
77c9e2be55Smrg
78c9e2be55Smrg
79c9e2be55Smrg/* Return the full path name of the given mh command. */
80c9e2be55Smrg
8166d665a3Smrgstatic char *FullPathOfCommand(const char *str)
82c9e2be55Smrg{
83c9e2be55Smrg    static char result[100];
8466d665a3Smrg    snprintf(result, sizeof(result), "%s/%s", app_resources.mh_path, str);
85c9e2be55Smrg    return result;
86c9e2be55Smrg}
87c9e2be55Smrg
88c9e2be55Smrg
89c9e2be55Smrg/*ARGSUSED*/
90c9e2be55Smrgstatic void ReadStdout(
91c9e2be55Smrg    XtPointer closure,
92c9e2be55Smrg    int *fd,
93c9e2be55Smrg    XtInputId *id)	/* unused */
94c9e2be55Smrg{
95c9e2be55Smrg    register CommandStatus status = (CommandStatus)closure;
96c9e2be55Smrg    CheckReadFromPipe(*fd, &status->output_buffer, &status->output_buf_size,
97c9e2be55Smrg		      False);
98c9e2be55Smrg}
99c9e2be55Smrg
100c9e2be55Smrg
101c9e2be55Smrg/*ARGSUSED*/
102c9e2be55Smrgstatic void ReadStderr(
103c9e2be55Smrg    XtPointer closure,
104c9e2be55Smrg    int *fd,
105c9e2be55Smrg    XtInputId *id)	/* unused */
106c9e2be55Smrg{
107c9e2be55Smrg    register CommandStatus status = (CommandStatus)closure;
108c9e2be55Smrg    CheckReadFromPipe(*fd, &status->error_buffer, &status->error_buf_size,
109c9e2be55Smrg		      False);
110c9e2be55Smrg}
111c9e2be55Smrg
112c9e2be55Smrg
113c9e2be55Smrgstatic volatile int childdone;		/* Gets nonzero when the child process
114c9e2be55Smrg				   finishes. */
115c9e2be55Smrg/* ARGSUSED */
116c9e2be55Smrgstatic void
117c9e2be55SmrgChildDone(int n)
118c9e2be55Smrg{
119c9e2be55Smrg    childdone++;
120c9e2be55Smrg}
121c9e2be55Smrg
122c9e2be55Smrg/* Execute the given command, and wait until it has finished.  While the
123c9e2be55Smrg   command is executing, watch the X socket and cause Xlib to read in any
124c9e2be55Smrg   incoming data.  This will prevent the socket from overflowing during
125c9e2be55Smrg   long commands.  Returns 0 if stderr empty, -1 otherwise. */
126c9e2be55Smrg
127c9e2be55Smrgstatic int _DoCommandToFileOrPipe(
12866d665a3Smrg  char * const *argv,		/* The command to execute, and its args. */
129c9e2be55Smrg  int inputfd,			/* Input stream for command. */
130c9e2be55Smrg  int outputfd,			/* Output stream; /dev/null if == -1 */
131c9e2be55Smrg  char **bufP,			/* output buffer ptr if outputfd == -2 */
132c9e2be55Smrg  int *lenP)			/* output length ptr if outputfd == -2 */
133c9e2be55Smrg{
134c9e2be55Smrg    XtAppContext appCtx = XtWidgetToApplicationContext(toplevel);
135c9e2be55Smrg    int return_status;
136c9e2be55Smrg    int old_stdin = 0, old_stdout = 0, old_stderr = 0;
137c9e2be55Smrg    int pid;
138c9e2be55Smrg    fd_set readfds, fds;
139c9e2be55Smrg    Boolean output_to_pipe = False;
140c9e2be55Smrg    CommandStatus status = XtNew(CommandStatusRec);
141c9e2be55Smrg    FD_ZERO(&fds);
142c9e2be55Smrg    FD_SET(ConnectionNumber(theDisplay), &fds);
143c9e2be55Smrg    DEBUG1("Executing %s ...", argv[0])
144c9e2be55Smrg
145c9e2be55Smrg    if (inputfd != -1) {
146c9e2be55Smrg	old_stdin = dup(fileno(stdin));
147c9e2be55Smrg	(void) dup2(inputfd, fileno(stdin));
148c9e2be55Smrg	close(inputfd);
149c9e2be55Smrg    }
150c9e2be55Smrg
151c9e2be55Smrg    if (outputfd == -1) {
152c9e2be55Smrg	if (!app_resources.debug) { /* Throw away stdout. */
153c9e2be55Smrg	    outputfd = open( "/dev/null", O_WRONLY, 0 );
154c9e2be55Smrg	}
155c9e2be55Smrg    }
156c9e2be55Smrg    else if (outputfd == -2) {	/* make pipe */
157c9e2be55Smrg	if (pipe(status->output_pipe) /*failed*/) {
158c9e2be55Smrg	    SystemError( "couldn't re-direct standard output" );
159c9e2be55Smrg	    status->output_pipe[0]=0;
160c9e2be55Smrg	}
161c9e2be55Smrg	else {
162c9e2be55Smrg	    outputfd = status->output_pipe[1];
163c9e2be55Smrg	    FD_SET(status->output_pipe[0], &fds);
164c9e2be55Smrg	    status->output_inputId =
165c9e2be55Smrg		XtAppAddInput( appCtx,
166c9e2be55Smrg			   status->output_pipe[0], (XtPointer)XtInputReadMask,
167c9e2be55Smrg			   ReadStdout, (XtPointer)status
168c9e2be55Smrg			     );
169c9e2be55Smrg	    status->output_buffer = NULL;
170c9e2be55Smrg	    status->output_buf_size = 0;
171c9e2be55Smrg	    output_to_pipe = True;
172c9e2be55Smrg	}
173c9e2be55Smrg    }
174c9e2be55Smrg
175c9e2be55Smrg    if (pipe(status->error_pipe) /*failed*/) {
176c9e2be55Smrg	SystemError( "couldn't re-direct standard error" );
177c9e2be55Smrg	status->error_pipe[0]=0;
178c9e2be55Smrg    }
179c9e2be55Smrg    else {
180c9e2be55Smrg	old_stderr = dup(fileno(stderr));
181c9e2be55Smrg	(void) dup2(status->error_pipe[1], fileno(stderr));
182c9e2be55Smrg	close(status->error_pipe[1]);
183c9e2be55Smrg	FD_SET(status->error_pipe[0], &fds);
184c9e2be55Smrg	status->error_inputId =
185c9e2be55Smrg	    XtAppAddInput( appCtx,
186c9e2be55Smrg			   status->error_pipe[0], (XtPointer)XtInputReadMask,
187c9e2be55Smrg			   ReadStderr, (XtPointer)status
188c9e2be55Smrg			  );
189c9e2be55Smrg    }
190c9e2be55Smrg    if (outputfd != -1) {
191c9e2be55Smrg	old_stdout = dup(fileno(stdout));
192c9e2be55Smrg	(void) dup2(outputfd, fileno(stdout));
193c9e2be55Smrg	close(outputfd);
194c9e2be55Smrg    }
195c9e2be55Smrg    childdone = FALSE;
196c9e2be55Smrg    status->popup = (Widget)NULL;
197c9e2be55Smrg    status->lastInput = lastInput;
198c9e2be55Smrg    status->error_buffer = NULL;
199c9e2be55Smrg    status->error_buf_size = 0;
200c9e2be55Smrg    (void) signal(SIGCHLD, ChildDone);
201c9e2be55Smrg    pid = vfork();
202c9e2be55Smrg    if (inputfd != -1) {
203c9e2be55Smrg	if (pid != 0) dup2(old_stdin,  fileno(stdin));
204c9e2be55Smrg	close(old_stdin);
205c9e2be55Smrg    }
206c9e2be55Smrg    if (outputfd != -1) {
207c9e2be55Smrg	if (pid != 0) dup2(old_stdout, fileno(stdout));
208c9e2be55Smrg	close(old_stdout);
209c9e2be55Smrg    }
210c9e2be55Smrg    if (status->error_pipe[0]) {
211c9e2be55Smrg	if (pid != 0) dup2(old_stderr, fileno(stderr));
212c9e2be55Smrg	close(old_stderr);
213c9e2be55Smrg    }
214c9e2be55Smrg
215c9e2be55Smrg    if (pid == -1) Punt("Couldn't fork!");
216c9e2be55Smrg    if (pid) {			/* We're the parent process. */
217c9e2be55Smrg	XEvent typeAheadQueue[TYPEAHEADSIZE], *eventP = typeAheadQueue;
218c9e2be55Smrg	XEvent *altQueue = NULL;
219c9e2be55Smrg	int type_ahead_count = 0, alt_queue_size = 0, alt_queue_count = 0;
220c9e2be55Smrg	XtAppContext app = XtWidgetToApplicationContext(toplevel);
221c9e2be55Smrg	int num_fds = ConnectionNumber(theDisplay)+1;
222c9e2be55Smrg	if (output_to_pipe && status->output_pipe[0] >= num_fds)
223c9e2be55Smrg	    num_fds = status->output_pipe[0]+1;
224c9e2be55Smrg	if (status->error_pipe[0] >= num_fds)
225c9e2be55Smrg	    num_fds = status->error_pipe[0]+1;
226c9e2be55Smrg	status->child_pid = pid;
227c9e2be55Smrg	DEBUG1( " pid=%d ", pid )
228c9e2be55Smrg	subProcessRunning = True;
229c9e2be55Smrg	while (!childdone) {
230c9e2be55Smrg	    while (!(XtAppPending(app) & XtIMXEvent)) {
231c9e2be55Smrg		/* this is gross, but the only other way is by
232c9e2be55Smrg		 * polling on timers or an extra pipe, since we're not
233c9e2be55Smrg		 * guaranteed to be able to malloc in a signal handler.
234c9e2be55Smrg		 */
235c9e2be55Smrg		readfds = fds;
236c9e2be55Smrg                if (childdone) break;
237c9e2be55Smrg		DEBUG("blocking.\n")
238c9e2be55Smrg		(void) Select(num_fds, &readfds, NULL, NULL, NULL);
239c9e2be55Smrg		DEBUG1("unblocked; child%s done.\n", childdone ? "" : " not")
240c9e2be55Smrg		if (childdone) break;
241c9e2be55Smrg		if (!FD_ISSET(ConnectionNumber(theDisplay), &readfds)) {
242c9e2be55Smrg		    DEBUG("reading alternate input...")
243c9e2be55Smrg		    XtAppProcessEvent(appCtx, (unsigned)XtIMAlternateInput);
244c9e2be55Smrg		    DEBUG("read.\n")
245c9e2be55Smrg		}
246c9e2be55Smrg	    }
247c9e2be55Smrg	    if (childdone) break;
248c9e2be55Smrg	    XtAppNextEvent(app, eventP);
249c9e2be55Smrg	    switch(eventP->type) {
250c9e2be55Smrg	      case LeaveNotify:
251c9e2be55Smrg		if (type_ahead_count) {
252c9e2be55Smrg		    /* do compress_enterleave here to save memory */
253c9e2be55Smrg		    XEvent *prevEvent;
254c9e2be55Smrg		    if (alt_queue_size && (alt_queue_count == 0))
255c9e2be55Smrg			prevEvent = &typeAheadQueue[type_ahead_count-1];
256c9e2be55Smrg		    else
257c9e2be55Smrg			prevEvent = eventP - 1;
258c9e2be55Smrg		    if (prevEvent->type == EnterNotify
259c9e2be55Smrg		      && prevEvent->xany.display == eventP->xany.display
260c9e2be55Smrg		      && prevEvent->xany.window == eventP->xany.window) {
261c9e2be55Smrg			eventP = prevEvent;
262c9e2be55Smrg			if (alt_queue_count > 0)
263c9e2be55Smrg			    alt_queue_count--;
264c9e2be55Smrg			else
265c9e2be55Smrg			    type_ahead_count--;
266c9e2be55Smrg			break;
267c9e2be55Smrg		    }
268c9e2be55Smrg		}
269c9e2be55Smrg		/* fall through */
270c9e2be55Smrg	      case KeyPress:
271c9e2be55Smrg	      case KeyRelease:
272c9e2be55Smrg	      case EnterNotify:
273c9e2be55Smrg	      case ButtonPress:
274c9e2be55Smrg	      case ButtonRelease:
275c9e2be55Smrg	      case MotionNotify:
276c9e2be55Smrg		if (type_ahead_count < TYPEAHEADSIZE) {
277c9e2be55Smrg		    if (++type_ahead_count == TYPEAHEADSIZE) {
27866d665a3Smrg			altQueue = XtMallocArray(
27966d665a3Smrg			    (Cardinal)TYPEAHEADSIZE, sizeof(XEvent) );
280c9e2be55Smrg			alt_queue_size = TYPEAHEADSIZE;
281c9e2be55Smrg			eventP = altQueue;
282c9e2be55Smrg		    }
283c9e2be55Smrg		    else
284c9e2be55Smrg			eventP++;
285c9e2be55Smrg		}
286c9e2be55Smrg		else {
287c9e2be55Smrg		    if (++alt_queue_count == alt_queue_size) {
288c9e2be55Smrg			alt_queue_size += TYPEAHEADSIZE;
28966d665a3Smrg			altQueue = XtReallocArray(altQueue,
29066d665a3Smrg			    (Cardinal)alt_queue_size, sizeof(XEvent) );
291c9e2be55Smrg			eventP = &altQueue[alt_queue_count];
292c9e2be55Smrg		    }
293c9e2be55Smrg		    else
294c9e2be55Smrg			eventP++;
295c9e2be55Smrg		}
296c9e2be55Smrg		break;
297c9e2be55Smrg
298c9e2be55Smrg	      default:
299c9e2be55Smrg		XtDispatchEvent(eventP);
300c9e2be55Smrg	    }
301c9e2be55Smrg	}
302c9e2be55Smrg	(void) wait(0);
303c9e2be55Smrg
304c9e2be55Smrg	DEBUG("done\n")
305c9e2be55Smrg	subProcessRunning = False;
306c9e2be55Smrg	if (output_to_pipe) {
307c9e2be55Smrg	    CheckReadFromPipe( status->output_pipe[0],
308c9e2be55Smrg			       &status->output_buffer,
309c9e2be55Smrg			       &status->output_buf_size,
310c9e2be55Smrg			       True
311c9e2be55Smrg			      );
312c9e2be55Smrg	    *bufP = status->output_buffer;
313c9e2be55Smrg	    *lenP = status->output_buf_size;
314c9e2be55Smrg	    close( status->output_pipe[0] );
315c9e2be55Smrg	    XtRemoveInput( status->output_inputId );
316c9e2be55Smrg	}
317c9e2be55Smrg	if (status->error_pipe[0]) {
318c9e2be55Smrg	    CheckReadFromPipe( status->error_pipe[0],
319c9e2be55Smrg			       &status->error_buffer,
320c9e2be55Smrg			       &status->error_buf_size,
321c9e2be55Smrg			       True
322c9e2be55Smrg			      );
323c9e2be55Smrg	    close( status->error_pipe[0] );
324c9e2be55Smrg	    XtRemoveInput( status->error_inputId );
325c9e2be55Smrg	}
326c9e2be55Smrg	if (status->error_buffer != NULL) {
327c9e2be55Smrg	    /* special case for arbitrary shell commands: capture command */
328c9e2be55Smrg	    if ((strcmp(argv[0], "/bin/sh") == 0) &&
329c9e2be55Smrg	        (strcmp(argv[1], "-c") == 0)) {
330c9e2be55Smrg	        status->shell_command = XtNewString(argv[2]);
331c9e2be55Smrg            } else status->shell_command = (char*) NULL;
332d859ff80Smrg
333c9e2be55Smrg	    while (status->error_buffer[status->error_buf_size-1]  == '\0')
334c9e2be55Smrg		status->error_buf_size--;
335c9e2be55Smrg	    while (status->error_buffer[status->error_buf_size-1]  == '\n')
336c9e2be55Smrg		status->error_buffer[--status->error_buf_size] = '\0';
337c9e2be55Smrg	    DEBUG1( "stderr = \"%s\"\n", status->error_buffer )
338c9e2be55Smrg	    PopupNotice( status->error_buffer, FreeStatus, (Pointer)status );
339c9e2be55Smrg	    return_status = -1;
340c9e2be55Smrg	}
341c9e2be55Smrg	else {
342c9e2be55Smrg	    XtFree( (Pointer)status );
343c9e2be55Smrg	    return_status = 0;
344c9e2be55Smrg	}
345c9e2be55Smrg	for (;alt_queue_count;alt_queue_count--) {
346c9e2be55Smrg	    XPutBackEvent(theDisplay, --eventP);
347c9e2be55Smrg	}
348c9e2be55Smrg	if (type_ahead_count) {
349c9e2be55Smrg	    if (alt_queue_size) eventP = &typeAheadQueue[type_ahead_count];
350c9e2be55Smrg	    for (;type_ahead_count;type_ahead_count--) {
351c9e2be55Smrg		XPutBackEvent(theDisplay, --eventP);
352c9e2be55Smrg	    }
353c9e2be55Smrg	}
354c9e2be55Smrg    } else {			/* We're the child process. */
355c9e2be55Smrg	/* take it from the user's path, else fall back to the mhPath */
356c9e2be55Smrg	(void) execvp(argv[0], argv);
357c9e2be55Smrg	(void) execv(FullPathOfCommand(argv[0]), argv);
358c9e2be55Smrg        progName = argv[0];	/* for Punt message */
359c9e2be55Smrg	Punt("(cannot execvp it)");
360c9e2be55Smrg	return_status = -1;
361c9e2be55Smrg    }
362c9e2be55Smrg    return return_status;
363c9e2be55Smrg}
364c9e2be55Smrg
365c9e2be55Smrg
366c9e2be55Smrgstatic void
367c9e2be55SmrgCheckReadFromPipe(
368c9e2be55Smrg    int fd,
369c9e2be55Smrg    char **bufP,
370c9e2be55Smrg    int *lenP,
371c9e2be55Smrg    Bool waitEOF)
372c9e2be55Smrg{
373c9e2be55Smrg    int nread;
374c9e2be55Smrg/*  DEBUG2( " CheckReadFromPipe #%d,len=%d,", fd, *lenP )  */
375c9e2be55Smrg#ifdef FIONREAD
376c9e2be55Smrg    if (!ioctl( fd, FIONREAD, &nread )) {
377c9e2be55Smrg/*      DEBUG1( "nread=%d ...", nread )			   */
378c9e2be55Smrg	if (nread) {
379c9e2be55Smrg	    int old_end = *lenP;
380c9e2be55Smrg	    *bufP = XtRealloc( *bufP, (Cardinal) ((*lenP += nread) + 1) );
381c9e2be55Smrg	    read( fd, *bufP+old_end, nread );
382c9e2be55Smrg	    (*bufP)[old_end+nread] = '\0';
383c9e2be55Smrg	}
384c9e2be55Smrg	return;
385c9e2be55Smrg    }
386c9e2be55Smrg#endif
387c9e2be55Smrg    do {
388c9e2be55Smrg	char buf[512];
389c9e2be55Smrg	int old_end = *lenP;
390c9e2be55Smrg	nread = read( fd, buf, 512 );
391c9e2be55Smrg	if (nread <= 0)
392c9e2be55Smrg	    break;
393c9e2be55Smrg	*bufP = XtRealloc( *bufP, (Cardinal) ((*lenP += nread) + 1) );
394c9e2be55Smrg	memmove( *bufP+old_end, buf, (int) nread );
395c9e2be55Smrg	(*bufP)[old_end+nread] = '\0';
396c9e2be55Smrg    } while (waitEOF);
397c9e2be55Smrg}
398c9e2be55Smrg
399c9e2be55Smrg
400c9e2be55Smrg/* ARGSUSED */
401c9e2be55Smrgstatic void FreeStatus(
402c9e2be55Smrg    Widget w,			/* unused */
403c9e2be55Smrg    XtPointer closure,
404c9e2be55Smrg    XtPointer call_data)	/* unused */
405c9e2be55Smrg{
406c9e2be55Smrg    CommandStatus status = (CommandStatus)closure;
407c9e2be55Smrg    if (status->popup != (Widget)NULL) {
408c9e2be55Smrg	XtPopdown( status->popup );
409c9e2be55Smrg	XtDestroyWidget( status->popup );
410c9e2be55Smrg    }
411c9e2be55Smrg    if (status->error_buffer != NULL) XtFree(status->error_buffer);
412c9e2be55Smrg    XtFree( closure );
413c9e2be55Smrg}
414c9e2be55Smrg
415c9e2be55Smrg/* Execute the given command, waiting until it's finished.  Put the output
416c9e2be55Smrg   in the specified file path.  Returns 0 if stderr empty, -1 otherwise */
417c9e2be55Smrg
418c9e2be55Smrgint DoCommand(
41966d665a3Smrg  char * const *argv,		/* The command to execute, and its args. */
42066d665a3Smrg  const char *inputfile,	/* Input file for command. */
42166d665a3Smrg  const char *outputfile)	/* Output file for command. */
422c9e2be55Smrg{
423c9e2be55Smrg    int fd_in, fd_out;
424c9e2be55Smrg    int status;
425c9e2be55Smrg
426c9e2be55Smrg    if (inputfile != NULL) {
427c9e2be55Smrg	FILEPTR file = FOpenAndCheck(inputfile, "r");
428c9e2be55Smrg	fd_in = dup(fileno(file));
429c9e2be55Smrg	myfclose(file);
430c9e2be55Smrg    }
431c9e2be55Smrg    else
432c9e2be55Smrg	fd_in = -1;
433c9e2be55Smrg
434c9e2be55Smrg    if (outputfile) {
435c9e2be55Smrg	FILEPTR file = FOpenAndCheck(outputfile, "w");
436c9e2be55Smrg	fd_out = dup(fileno(file));
437c9e2be55Smrg	myfclose(file);
438c9e2be55Smrg    }
439c9e2be55Smrg    else
440c9e2be55Smrg	fd_out = -1;
441c9e2be55Smrg
442c9e2be55Smrg    status = _DoCommandToFileOrPipe( argv, fd_in, fd_out, (char **) NULL,
443c9e2be55Smrg				    (int *) NULL );
444c9e2be55Smrg    return status;
445c9e2be55Smrg}
446c9e2be55Smrg
447c9e2be55Smrg/* Execute the given command, waiting until it's finished.  Put the output
448c9e2be55Smrg   in a newly mallocced string, and return a pointer to that string. */
449c9e2be55Smrg
45066d665a3Smrgchar *DoCommandToString(char * const *argv)
451c9e2be55Smrg{
452c9e2be55Smrg    char *result = NULL;
453c9e2be55Smrg    int len = 0;
454c9e2be55Smrg    _DoCommandToFileOrPipe( argv, -1, -2, &result, &len );
455c9e2be55Smrg    if (result == NULL) result = XtMalloc((Cardinal) 1);
456c9e2be55Smrg    result[len] = '\0';
457c9e2be55Smrg    DEBUG1("('%s')\n", result)
458c9e2be55Smrg    return result;
459c9e2be55Smrg}
460d859ff80Smrg
461c9e2be55Smrg
462c9e2be55Smrg/* Execute the command to a temporary file, and return the name of the file. */
463c9e2be55Smrg
46466d665a3Smrgchar *DoCommandToFile(char * const *argv)
465c9e2be55Smrg{
466c9e2be55Smrg    char *name;
467c9e2be55Smrg    FILEPTR file;
468c9e2be55Smrg    int fd;
469c9e2be55Smrg    name = MakeNewTempFileName();
470c9e2be55Smrg    file = FOpenAndCheck(name, "w");
471c9e2be55Smrg    fd = dup(fileno(file));
472c9e2be55Smrg    myfclose(file);
473c9e2be55Smrg    _DoCommandToFileOrPipe(argv, -1, fd, (char **) NULL, (int *) NULL);
474c9e2be55Smrg    return name;
475c9e2be55Smrg}
476