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