PageRenderTime 52ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/command.c

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