home *** CD-ROM | disk | FTP | other *** search
/ NetNews Usenet Archive 1992 #18 / NN_1992_18.iso / spool / comp / windows / openloo / 3546 < prev    next >
Encoding:
Text File  |  1992-08-21  |  43.9 KB  |  1,788 lines

  1. Path: sparky!uunet!usc!sol.ctr.columbia.edu!ursa!buzz
  2. From: buzz@toxicavenger.bear.com (Buzz Moschetti)
  3. Newsgroups: comp.windows.open-look
  4. Subject: Re: Redirecting stdout/stderr: A SOLUTION Part01/01
  5. Message-ID: <BUZZ.92Aug21133203@toxicavenger.bear.com>
  6. Date: 21 Aug 92 17:32:03 GMT
  7. References: <1992Aug21.152603.17253@bmw.mayo.edu>
  8. Sender: news@ursa.UUCP
  9. Reply-To: buzz@fsrg.bear.com
  10. Organization: Bear, Stearns & Co. Inc.
  11. Lines: 1774
  12. In-reply-to: vdp@mayo.edu's message of 21 Aug 92 15:26:03 GMT
  13.  
  14. Anyone concerned with running child programs from within an Xview app and
  15. properly managing stdout, stderr, the death of the child, and reclaiming
  16. file descriptor and other resources should take this shar and review xrun.c.
  17. Two other C sources are supplied to provide the xrun package with two 
  18. different front-ends:  xcmd, the GUI command executor and cmd, the
  19. Notifier-based but *not* windows dependent version of the same.
  20.  
  21. All this has been tested and runs fine on SunOS 4.1.1 with OW3.0.  The xcmd
  22. UI was built with Devguide 3.0.
  23.  
  24.  
  25. #! /bin/sh
  26. # This is a shell archive.  Remove anything before this line, then unpack
  27. # it by saving it into a file and typing "sh file".  To overwrite existing
  28. # files, type "sh file -c".  You can also feed this as standard input via
  29. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  30. # will see the following message at the end:
  31. #        "End of archive 1 (of 1)."
  32. # Contents:  Makefile cmd.c xcmd.G xcmd.c xcmd.h xcmd_ui.c xcmd_ui.h
  33. #   xrun.c xrun.h
  34. # Wrapped by buzz@toxicavenger on Thu Aug 20 08:53:51 1992
  35. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  36. if test -f 'Makefile' -a "${1}" != "-c" ; then 
  37.   echo shar: Will not clobber existing file \"'Makefile'\"
  38. else
  39. echo shar: Extracting \"'Makefile'\" \(1592 characters\)
  40. sed "s/^X//" >'Makefile' <<'END_OF_FILE'
  41. X# This file was generated by `gxv' from `xcmd.G'.
  42. X
  43. X# Parameters.
  44. X
  45. XPROGRAM = xcmd
  46. XSOURCES.c = xcmd.c xrun.c
  47. XSOURCES.h = 
  48. XSOURCES.G = xcmd.G
  49. XSTUBS.G = 
  50. X
  51. X# Derived parameters.
  52. X
  53. XSOURCES = \
  54. X    $(SOURCES.G) \
  55. X    $(SOURCES.h) \
  56. X    $(SOURCES.l) \
  57. X    $(SOURCES.y) \
  58. X    $(SOURCES.cps) \
  59. X    $(SOURCES.c)
  60. X
  61. XTARGETS.c = \
  62. X    $(SOURCES.G:%.G=%_ui.c) \
  63. X    $(STUBS.G:%.G=%_stubs.c)
  64. X
  65. XTARGETS.h = \
  66. X    $(SOURCES.G:%.G=%_ui.h) \
  67. X    $(SOURCES.l:%.l=%.h) \
  68. X    $(SOURCES.y:%.y=%.h) \
  69. X    $(SOURCES.cps:%.cps=%.h)
  70. X
  71. XTARGETS.other = \
  72. X    $(SOURCES.G:%.G=%.info)
  73. X
  74. XTARGETS = \
  75. X    $(TARGETS.other) \
  76. X    $(TARGETS.h) \
  77. X    $(TARGETS.c)
  78. X
  79. XOBJECTS = \
  80. X    $(SOURCES.c:%.c=%.o) \
  81. X    $(TARGETS.c:%.c=%.o)
  82. X
  83. X# Compiler flags.
  84. X
  85. XCFLAGS += -g 
  86. XCPPFLAGS += -I$(GUIDEHOME)/include -I$(OPENWINHOME)/include -DMAIN -I.
  87. XLDFLAGS += -L$(GUIDEHOME)/lib -L$(OPENWINHOME)/lib
  88. XLDLIBS += -lguidexv -lguide -lxview -lolgx -lX11
  89. X
  90. X# Standard targets.
  91. X
  92. Xall:     $(TARGETS.other) $(PROGRAM)
  93. Xobjects: $(SOURCES.c) $(TARGETS.c) $(TARGETS.h) $(OBJECTS)
  94. Xsources: $(SOURCES)
  95. Xtargets: $(SOURCES) $(TARGETS)
  96. X
  97. X$(PROGRAM): $(SOURCES.c) $(TARGETS.c) $(TARGETS.h) $(OBJECTS)
  98. X    $(LINK.c) -o $@ $(OBJECTS) $(LDLIBS)
  99. X
  100. Xlint:
  101. X    lint $(CPPFLAGS) $(SOURCES.c)
  102. X
  103. X# Targets to be used by Saber-C.
  104. X
  105. Xsaber_src:
  106. X    #load $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) \
  107. X        $(SOURCES.c) $(TARGETS.c) $(LDLIBS)
  108. X
  109. Xsaber_obj:
  110. X    #load $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH) $(OBJECTS) $(LDLIBS)
  111. X
  112. Xclean:
  113. X    $(RM) $(SOURCES.G:%.G=%_ui.c) $(TARGETS.other) $(TARGETS.h) $(OBJECTS) *.BAK *.delta core
  114. X
  115. X%_ui.c: %.G
  116. X    $(GUIDEHOME)/bin/gxv $*
  117. X
  118. X%_ui.h: %_ui.c
  119. X    @touch $@
  120. X
  121. X%_stubs.c: %_ui.c
  122. X    @touch $@
  123. X
  124. X%.info: %_ui.c
  125. X    @touch $@
  126. END_OF_FILE
  127. if test 1592 -ne `wc -c <'Makefile'`; then
  128.     echo shar: \"'Makefile'\" unpacked with wrong size!
  129. fi
  130. # end of 'Makefile'
  131. fi
  132. if test -f 'cmd.c' -a "${1}" != "-c" ; then 
  133.   echo shar: Will not clobber existing file \"'cmd.c'\"
  134. else
  135. echo shar: Extracting \"'cmd.c'\" \(3870 characters\)
  136. sed "s/^X//" >'cmd.c' <<'END_OF_FILE'
  137. X/*
  138. X * cmd.c
  139. X * 
  140. X * The non-GUI xrun testbed.
  141. X
  142. Xcc -g -I/usr/openwin/include cmd.c xrun.o -o cmd -L/usr/openwin/lib -lxview -lX11
  143. X
  144. X */
  145. X
  146. X
  147. X
  148. X#include <stdio.h>
  149. X#include <errno.h>
  150. X
  151. X#include <sys/param.h>
  152. X#include <sys/wait.h>
  153. X#include <sys/ioctl.h>
  154. X#include <sys/types.h>
  155. X
  156. X#include <xview/notify.h>
  157. X
  158. X#include "xrun.h"
  159. X
  160. X
  161. XNotify_value data_reader(), sigchldcatcher();
  162. X
  163. X
  164. X/*  This is the transfer buffer.  The union with the double dummy forces
  165. X *  restrictive alignment of the buffer.
  166. X */
  167. X#define   INBUFSIZE    4096
  168. Xstatic union {
  169. X      double dummy;
  170. X      char buf[INBUFSIZE];
  171. X} XX;
  172. X
  173. X
  174. X/* 23 is an arbitrary value: */
  175. XNotify_client cmd_client = (Notify_client)23;
  176. XNotify_value do_cmd();
  177. X
  178. Xmain(argc, argv)
  179. Xint    argc;
  180. Xchar    **argv;
  181. X{
  182. X      int pid;
  183. X      char buf[512];
  184. X
  185. X      /* Wake up when we type at the program: */
  186. X      notify_set_input_func(cmd_client, do_cmd, 0);
  187. X
  188. X   
  189. X      /* "Boot" the screen by printing the first prompt: */
  190. X      (void) fprintf(stderr, "--> ");
  191. X
  192. X      notify_start();
  193. X}
  194. X
  195. X
  196. XNotify_value
  197. Xdo_cmd(client, fd)
  198. XNotify_client client;
  199. Xint fd;
  200. X{
  201. X      int i, bytes, pid;
  202. X
  203. X      /* Point buf at the transfer block: */
  204. X      char *buf = XX.buf;
  205. X
  206. X      if (ioctl(fd, FIONREAD, &bytes) == -1) {
  207. X        (void) sprintf(buf, "-- ioctl(%d, FIONREAD, &bytes)", fd);
  208. X        perror(buf);      
  209. X      } else if(bytes != 0) {
  210. X        i = read(fd, buf, INBUFSIZE);
  211. X
  212. X        buf[i] = '\0';
  213. X        (void) printf(stderr, "cmd: %s\n", buf);
  214. X
  215. X        pid = xrunsh(buf, data_reader, data_reader, sigchldcatcher);
  216. X
  217. X        switch(pid) {
  218. X        case XRUN_SYS_ERR:
  219. X          perror("system error spawning program");
  220. X          break;
  221. X          
  222. X        case XRUN_MAX_PROCS_ERR:
  223. X          (void) fprintf(stderr, "ERROR: max piped processes limit reached.\n");
  224. X          break;
  225. X          
  226. X        default:
  227. X          (void) fprintf(stderr, "process %d running!\n", pid);
  228. X          break;
  229. X        }
  230. X      }
  231. X
  232. X      (void) fprintf(stderr, "--> ");
  233. X
  234. X      return NOTIFY_DONE;
  235. X}
  236. X
  237. X/*
  238. X *  This callback is hit when a data client writes its stdout.
  239. X */
  240. XNotify_value
  241. Xdata_reader(client, fd)
  242. XNotify_client client;
  243. Xint fd;
  244. X{
  245. X      int bytes, i;
  246. X      char *buf, *stream;
  247. X
  248. X      /* Point buf at the transfer block: */
  249. X      buf = XX.buf;
  250. X
  251. X
  252. X      if(client == STDOUT_CLIENT) {
  253. X        stream = "stdout";
  254. X      } else if(client == STDERR_CLIENT) {
  255. X          stream = "stderr";
  256. X        }
  257. X      else {
  258. X        stream = "--unknown--";        
  259. X      }
  260. X
  261. X
  262. X
  263. X      /*
  264. X       *  The Notifier will dispatch to this function when data is ready.
  265. X       *  We must try to read ALL messages in the pipe before returning
  266. X       *  control to the Notifier:
  267. X       */
  268. X      if (ioctl(fd, FIONREAD, &bytes) == -1) {
  269. X        (void) sprintf(buf, "-- ioctl(%d, FIONREAD, &bytes)", fd);
  270. X        perror(buf);
  271. X      } else if(bytes != 0) {
  272. X        
  273. X        /*  We have bytes!  That means at least one message: */
  274. X        /*(void) printf("-- ioctl bytes: %d\n", bytes);*/
  275. X        
  276. X        /*  Get a message! */
  277. X        i = read(fd, buf, INBUFSIZE);
  278. X        
  279. X        switch(i) {
  280. X        case -1:
  281. X          perror("error reading pipe");
  282. X          exit(1);
  283. X
  284. X        case 0:
  285. X          (void) fprintf(stderr, "no bytes...?\n");
  286. X          break;
  287. X          
  288. X        default:
  289. X          buf[i] = '\0';
  290. X          (void) fprintf(stderr, "%s: [%s]", stream, buf);
  291. X          break;
  292. X        }
  293. X      }
  294. X      
  295. X      return NOTIFY_DONE;
  296. X}
  297. X
  298. X
  299. X
  300. X/*
  301. X *  Handle death of data client.
  302. X */
  303. XNotify_value
  304. Xsigchldcatcher(client, pid, status, rusage)
  305. XNotify_client client;
  306. Xint pid;
  307. Xunion wait *status;
  308. Xstruct rusage *rusage;
  309. X{
  310. X      char rcbuf[8];
  311. X
  312. X      if(WIFEXITED(*status)) {
  313. X        (void) fprintf(stderr, "child pid %d exited w/code %d\n",
  314. X               pid, WEXITSTATUS(*status));
  315. X        (void) sprintf(rcbuf, "%d", WEXITSTATUS(*status));
  316. X      } else if(WIFSIGNALED(*status)) {
  317. X        (void) fprintf(stderr, "child pid %d recv'd signal %d\n",
  318. X               pid, WTERMSIG(*status));
  319. X        (void) sprintf(rcbuf, "%d", WTERMSIG(*status));
  320. X      } else {
  321. X        fprintf(stderr, "child did something else!\n");
  322. X        (void) strcpy(rcbuf, "???");
  323. X      }
  324. X
  325. X      return NOTIFY_DONE;
  326. X}
  327. X
  328. END_OF_FILE
  329. if test 3870 -ne `wc -c <'cmd.c'`; then
  330.     echo shar: \"'cmd.c'\" unpacked with wrong size!
  331. fi
  332. # end of 'cmd.c'
  333. fi
  334. if test -f 'xcmd.G' -a "${1}" != "-c" ; then 
  335.   echo shar: Will not clobber existing file \"'xcmd.G'\"
  336. else
  337. echo shar: Extracting \"'xcmd.G'\" \(6768 characters\)
  338. sed "s/^X//" >'xcmd.G' <<'END_OF_FILE'
  339. X;GIL-3
  340. X(
  341. X(
  342. X    :type                   :base-window
  343. X    :name                   window1
  344. X    :owner                  nil
  345. X    :width                  509
  346. X    :height                 473
  347. X    :background-color       ""
  348. X    :foreground-color       ""
  349. X    :label                  "Base Window"
  350. X    :label-type             :string
  351. X    :initial-state          :open
  352. X    :show-footer            t
  353. X    :resizable              t
  354. X    :icon-file              ""
  355. X    :icon-label             ""
  356. X    :icon-mask-file         ""
  357. X    :event-handler          nil
  358. X    :user-data              ()
  359. X    :actions                ()
  360. X)
  361. X(
  362. X    :type                   :control-area
  363. X    :name                   controls1
  364. X    :owner                  window1
  365. X    :help                   ""
  366. X    :x                      0
  367. X    :y                      0
  368. X    :width                  509
  369. X    :height                 101
  370. X    :background-color       ""
  371. X    :foreground-color       ""
  372. X    :initial-state          :visible
  373. X    :show-border            nil
  374. X    :menu                   nil
  375. X    :event-handler          nil
  376. X    :user-data              ()
  377. X    :actions                ()
  378. X)
  379. X(
  380. X    :type                   :text-field
  381. X    :name                   cmd_fld
  382. X    :owner                  controls1
  383. X    :help                   ""
  384. X    :x                      24
  385. X    :y                      24
  386. X    :width                  335
  387. X    :height                 15
  388. X    :value-x                103
  389. X    :value-y                24
  390. X    :value-length           32
  391. X    :stored-length          80
  392. X    :rows                   3
  393. X    :foreground-color       ""
  394. X    :text-type              :alphanumeric
  395. X    :label                  "Command:"
  396. X    :label-type             :string
  397. X    :layout-type            :horizontal
  398. X    :value-underlined       t
  399. X    :initial-value          ""
  400. X    :initial-state          :active
  401. X    :read-only              nil
  402. X    :notify-handler         cmb_btn_cbk
  403. X    :event-handler          nil
  404. X    :user-data              ()
  405. X    :actions                (
  406. X        (
  407. X        :from                   (window1 cmd_fld)
  408. X        :when                   (Notify )
  409. X        :to                     (window1 cmd_fld)
  410. X        :function_type          CallFunction
  411. X        :arg_type               ()
  412. X        :action                 (cmb_btn_cbk)
  413. X        )
  414. X    )
  415. X)
  416. X(
  417. X    :type                   :message
  418. X    :name                   status_msg
  419. X    :owner                  controls1
  420. X    :help                   ""
  421. X    :x                      392
  422. X    :y                      24
  423. X    :width                  45
  424. X    :height                 13
  425. X    :foreground-color       ""
  426. X    :label                  "READY"
  427. X    :label-type             :string
  428. X    :label-bold             t
  429. X    :initial-state          :active
  430. X    :event-handler          nil
  431. X    :user-data              ()
  432. X    :actions                ()
  433. X)
  434. X(
  435. X    :type                   :text-field
  436. X    :name                   pid_fld
  437. X    :owner                  controls1
  438. X    :help                   ""
  439. X    :x                      48
  440. X    :y                      56
  441. X    :width                  82
  442. X    :height                 15
  443. X    :value-x                82
  444. X    :value-y                56
  445. X    :value-length           6
  446. X    :stored-length          6
  447. X    :rows                   3
  448. X    :foreground-color       ""
  449. X    :text-type              :alphanumeric
  450. X    :label                  "PID:"
  451. X    :label-type             :string
  452. X    :layout-type            :horizontal
  453. X    :value-underlined       t
  454. X    :initial-value          ""
  455. X    :initial-state          :active
  456. X    :read-only              t
  457. X    :notify-handler         nil
  458. X    :event-handler          nil
  459. X    :user-data              ()
  460. X    :actions                ()
  461. X)
  462. X(
  463. X    :type                   :text-field
  464. X    :name                   retval_fld
  465. X    :owner                  controls1
  466. X    :help                   ""
  467. X    :x                      152
  468. X    :y                      56
  469. X    :width                  90
  470. X    :height                 15
  471. X    :value-x                206
  472. X    :value-y                56
  473. X    :value-length           4
  474. X    :stored-length          4
  475. X    :rows                   3
  476. X    :foreground-color       ""
  477. X    :text-type              :alphanumeric
  478. X    :label                  "Retval:"
  479. X    :label-type             :string
  480. X    :layout-type            :horizontal
  481. X    :value-underlined       t
  482. X    :initial-value          ""
  483. X    :initial-state          :active
  484. X    :read-only              t
  485. X    :notify-handler         nil
  486. X    :event-handler          nil
  487. X    :user-data              ()
  488. X    :actions                ()
  489. X)
  490. X(
  491. X    :type                   :button
  492. X    :name                   button1
  493. X    :owner                  controls1
  494. X    :help                   ""
  495. X    :x                      328
  496. X    :y                      56
  497. X    :width                  66
  498. X    :height                 20
  499. X    :constant-width         nil
  500. X    :button-type            :normal
  501. X    :foreground-color       ""
  502. X    :label                  "Button1"
  503. X    :label-type             :string
  504. X    :initial-state          :active
  505. X    :menu                   nil
  506. X    :notify-handler         button1_cbk
  507. X    :event-handler          nil
  508. X    :user-data              ()
  509. X    :actions                (
  510. X        (
  511. X        :from                   (window1 button1)
  512. X        :when                   (Notify )
  513. X        :to                     (window1 button1)
  514. X        :function_type          CallFunction
  515. X        :arg_type               ()
  516. X        :action                 (button1_cbk)
  517. X        )
  518. X    )
  519. X)
  520. X(
  521. X    :type                   :button
  522. X    :name                   button2
  523. X    :owner                  controls1
  524. X    :help                   ""
  525. X    :x                      424
  526. X    :y                      56
  527. X    :width                  66
  528. X    :height                 20
  529. X    :constant-width         nil
  530. X    :button-type            :normal
  531. X    :foreground-color       ""
  532. X    :label                  "Button2"
  533. X    :label-type             :string
  534. X    :initial-state          :active
  535. X    :menu                   nil
  536. X    :notify-handler         button2_cbk
  537. X    :event-handler          nil
  538. X    :user-data              ()
  539. X    :actions                (
  540. X        (
  541. X        :from                   (window1 button2)
  542. X        :when                   (Notify )
  543. X        :to                     (window1 button2)
  544. X        :function_type          CallFunction
  545. X        :arg_type               ()
  546. X        :action                 (button2_cbk)
  547. X        )
  548. X    )
  549. X)
  550. X(
  551. X    :type                   :text-pane
  552. X    :name                   stdout_txt
  553. X    :owner                  window1
  554. X    :help                   ""
  555. X    :x                      0
  556. X    :y                      116
  557. X    :width                  509
  558. X    :height                 169
  559. X    :background-color       ""
  560. X    :foreground-color       ""
  561. X    :initial-state          :visible
  562. X    :show-border            t
  563. X    :read-only              t
  564. X    :event-handler          nil
  565. X    :user-data              ()
  566. X    :actions                ()
  567. X)
  568. X(
  569. X    :type                   :text-pane
  570. X    :name                   stderr_txt
  571. X    :owner                  window1
  572. X    :help                   ""
  573. X    :x                      0
  574. X    :y                      301
  575. X    :width                  509
  576. X    :height                 172
  577. X    :background-color       ""
  578. X    :foreground-color       ""
  579. X    :initial-state          :visible
  580. X    :show-border            t
  581. X    :read-only              t
  582. X    :event-handler          nil
  583. X    :user-data              ()
  584. X    :actions                ()
  585. X)
  586. X)
  587. END_OF_FILE
  588. if test 6768 -ne `wc -c <'xcmd.G'`; then
  589.     echo shar: \"'xcmd.G'\" unpacked with wrong size!
  590. fi
  591. # end of 'xcmd.G'
  592. fi
  593. if test -f 'xcmd.c' -a "${1}" != "-c" ; then 
  594.   echo shar: Will not clobber existing file \"'xcmd.c'\"
  595. else
  596. echo shar: Extracting \"'xcmd.c'\" \(5394 characters\)
  597. sed "s/^X//" >'xcmd.c' <<'END_OF_FILE'
  598. X/*
  599. X * xcmd.c
  600. X * 
  601. X *  The testbed for xrun
  602. X */
  603. X
  604. X#include <stdio.h>
  605. X#include <errno.h>
  606. X
  607. X#include <sys/param.h>
  608. X#include <sys/wait.h>
  609. X#include <sys/ioctl.h>
  610. X#include <sys/types.h>
  611. X
  612. X#include <xview/xview.h>
  613. X#include <xview/panel.h>
  614. X#include <xview/textsw.h>
  615. X#include <xview/xv_xrect.h>
  616. X
  617. X#include "xcmd_ui.h"
  618. X
  619. X#include "xrun.h"
  620. X
  621. X#include <xview/notice.h>
  622. X#include <xview/notify.h>
  623. X
  624. X
  625. X#define  INBUFSIZE   4096    /*  No msg may be bigger than this! */
  626. X
  627. X/*
  628. X * Global object definitions.
  629. X */
  630. Xxcmd_window1_objects    *Xcmd_window1;
  631. X
  632. X
  633. X
  634. XNotify_value data_reader(), sigchldcatcher();
  635. X
  636. X
  637. X/*  This is the transfer buffer.  The union with the double dummy forces
  638. X *  restrictive alignment of the buffer.
  639. X */
  640. Xstatic union {
  641. X      double dummy;
  642. X      char buf[INBUFSIZE];
  643. X} XX;
  644. X
  645. X
  646. X
  647. X
  648. X/*
  649. X * Instance XV_KEY_DATA key.  An instance is a set of related
  650. X * user interface objects.  A pointer to an object's instance
  651. X * is stored under this key in every object.  This must be a
  652. X * global variable.
  653. X */
  654. XAttr_attribute    INSTANCE;
  655. X
  656. Xmain(argc, argv)
  657. X    int    argc;
  658. X    char    **argv;
  659. X{
  660. X    /*
  661. X     * Initialize XView.
  662. X     */
  663. X    xv_init(XV_INIT_ARGC_PTR_ARGV, &argc, argv, NULL);
  664. X    INSTANCE = xv_unique_key();
  665. X    
  666. X    /*
  667. X     * Initialize user interface components.
  668. X     * Do NOT edit the object initializations by hand.
  669. X     */
  670. X    Xcmd_window1 = xcmd_window1_objects_initialize(NULL, NULL);
  671. X    
  672. X    
  673. X    /*
  674. X     * Turn control over to XView.
  675. X     */
  676. X    xv_main_loop(Xcmd_window1->window1);
  677. X    exit(0);
  678. X}
  679. X
  680. X
  681. Xvoid
  682. Xbutton1_cbk(item, event)
  683. X    Panel_item    item;
  684. X    Event        *event;
  685. X{
  686. X    xcmd_window1_objects *ip = (xcmd_window1_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
  687. X    
  688. X    fputs("xcmd: button1_cbk\n", stderr);
  689. X}
  690. X
  691. Xvoid
  692. Xbutton2_cbk(item, event)
  693. X    Panel_item    item;
  694. X    Event        *event;
  695. X{
  696. X    xcmd_window1_objects *ip = (xcmd_window1_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
  697. X    
  698. X    fputs("xcmd: button2_cbk\n", stderr);
  699. X}
  700. X
  701. X
  702. X
  703. X
  704. X/*
  705. X * Notify callback function for `cmd_fld'.
  706. X */
  707. XPanel_setting
  708. Xcmb_btn_cbk(item, event)
  709. X    Panel_item    item;
  710. X    Event        *event;
  711. X{
  712. X      char pidbuf[8];
  713. X      char *s;
  714. X      int pid;
  715. X
  716. X      xcmd_window1_objects *ip = (xcmd_window1_objects *) xv_get(item, XV_KEY_DATA, INSTANCE);
  717. X      char *    value = (char *) xv_get(item, PANEL_VALUE);
  718. X    
  719. X      fprintf(stderr, "xcmd: cmb_btn_cbk: value: %s\n", value);
  720. X      
  721. X      textsw_reset(ip->stdout_txt, 0, 0);       
  722. X      xv_set(ip->stdout_txt, TEXTSW_MEMORY_MAXIMUM, 50000, NULL);
  723. X      textsw_reset(ip->stderr_txt, 0, 0);       
  724. X      xv_set(ip->stderr_txt, TEXTSW_MEMORY_MAXIMUM, 50000, NULL);
  725. X
  726. X
  727. X/*       { */
  728. X/*         char *v[2]; */
  729. X/*         v[0] = value; */
  730. X/*         v[1] = (char *)0; */
  731. X/*         pid = xrunv(v, data_reader, data_reader, sigchldcatcher); */
  732. X/*       } */
  733. X
  734. X      pid = xrunsh(value, data_reader, data_reader, sigchldcatcher);
  735. X
  736. X      switch(pid) {
  737. X      case XRUN_SYS_ERR:
  738. X        perror("system error spawning program");
  739. X        break;
  740. X
  741. X      case XRUN_MAX_PROCS_ERR:
  742. X        (void) fprintf(stderr, "ERROR: max piped processes limit reached.\n");
  743. X        break;
  744. X
  745. X      default:
  746. X            xv_set(ip->status_msg, PANEL_LABEL_STRING, "pending...", NULL);
  747. X        (void) sprintf(pidbuf, "%d", pid);
  748. X            xv_set(ip->pid_fld, PANEL_VALUE, pidbuf, NULL);
  749. X        break;
  750. X      }
  751. X
  752. X      return panel_text_notify(item, event);
  753. X}
  754. X
  755. X/*
  756. X *  This callback is hit when a data client writes its stdout.
  757. X */
  758. XNotify_value
  759. Xdata_reader(client, fd)
  760. XNotify_client client;
  761. Xint fd;
  762. X{
  763. X      int bytes, i;
  764. X      char *buf, *stream;
  765. X      Xv_opaque    txt;
  766. X
  767. X      /* Point buf at the transfer block: */
  768. X      buf = XX.buf;
  769. X
  770. X
  771. X      if(client == STDOUT_CLIENT) {
  772. X        stream = "stdout";
  773. X        txt = Xcmd_window1->stdout_txt;
  774. X      } else if(client == STDERR_CLIENT) {
  775. X          stream = "stderr";
  776. X          txt = Xcmd_window1->stderr_txt;
  777. X        }
  778. X      else {
  779. X        stream = "--unknown--";        
  780. X      }
  781. X
  782. X
  783. X
  784. X      /*
  785. X       *  The Notifier will dispatch to this function when data is ready.
  786. X       *  We must try to read ALL messages in the pipe before returning
  787. X       *  control to the Notifier:
  788. X       */
  789. X      if (ioctl(fd, FIONREAD, &bytes) == -1) {
  790. X        (void) sprintf(buf, "-- ioctl(%d, FIONREAD, &bytes)", fd);
  791. X        perror(buf);
  792. X      } else if(bytes != 0) {
  793. X        
  794. X        /*  We have bytes!  That means at least one message: */
  795. X        /*(void) printf("-- ioctl bytes: %d\n", bytes);*/
  796. X        
  797. X        /*  Get a message! */
  798. X        i = read(fd, buf, INBUFSIZE);
  799. X        
  800. X        switch(i) {
  801. X        case -1:
  802. X          perror("error reading pipe");
  803. X          exit(1);
  804. X
  805. X        case 0:
  806. X          (void) fprintf(stderr, "no bytes...?\n");
  807. X          break;
  808. X          
  809. X        default:
  810. X          buf[i] = '\0';
  811. X          textsw_insert(txt, buf, i);
  812. X          break;
  813. X        }
  814. X      }
  815. X      
  816. X      return NOTIFY_DONE;
  817. X}
  818. X
  819. X
  820. X
  821. X/*
  822. X *  Handle death of data client.
  823. X */
  824. XNotify_value
  825. Xsigchldcatcher(client, pid, status, rusage)
  826. XNotify_client client;
  827. Xint pid;
  828. Xunion wait *status;
  829. Xstruct rusage *rusage;
  830. X{
  831. X      char rcbuf[8];
  832. X
  833. X      if(WIFEXITED(*status)) {
  834. X        (void) fprintf(stderr, "child pid %d exited w/code %d\n",
  835. X               pid, WEXITSTATUS(*status));
  836. X        (void) sprintf(rcbuf, "%d", WEXITSTATUS(*status));
  837. X      } else if(WIFSIGNALED(*status)) {
  838. X        (void) fprintf(stderr, "child pid %d recv'd signal %d\n",
  839. X               pid, WTERMSIG(*status));
  840. X        (void) sprintf(rcbuf, "%d", WTERMSIG(*status));
  841. X      } else {
  842. X        fprintf(stderr, "child did something else!\n");
  843. X        (void) strcpy(rcbuf, "???");
  844. X      }
  845. X
  846. X      xv_set(Xcmd_window1->status_msg, PANEL_LABEL_STRING, "READY", NULL);
  847. X      xv_set(Xcmd_window1->retval_fld, PANEL_VALUE, rcbuf, NULL);      
  848. X
  849. X      return NOTIFY_DONE;
  850. X}
  851. X
  852. END_OF_FILE
  853. if test 5394 -ne `wc -c <'xcmd.c'`; then
  854.     echo shar: \"'xcmd.c'\" unpacked with wrong size!
  855. fi
  856. # end of 'xcmd.c'
  857. fi
  858. if test -f 'xcmd.h' -a "${1}" != "-c" ; then 
  859.   echo shar: Will not clobber existing file \"'xcmd.h'\"
  860. else
  861. echo shar: Extracting \"'xcmd.h'\" \(107 characters\)
  862. sed "s/^X//" >'xcmd.h' <<'END_OF_FILE'
  863. X
  864. Xtypedef struct Comm {
  865. X      int pid;
  866. X      int pipe_out;
  867. X      int pipe_in;
  868. X      int pipe_errin;
  869. X} Comm;
  870. END_OF_FILE
  871. if test 107 -ne `wc -c <'xcmd.h'`; then
  872.     echo shar: \"'xcmd.h'\" unpacked with wrong size!
  873. fi
  874. # end of 'xcmd.h'
  875. fi
  876. if test -f 'xcmd_ui.c' -a "${1}" != "-c" ; then 
  877.   echo shar: Will not clobber existing file \"'xcmd_ui.c'\"
  878. else
  879. echo shar: Extracting \"'xcmd_ui.c'\" \(5788 characters\)
  880. sed "s/^X//" >'xcmd_ui.c' <<'END_OF_FILE'
  881. X/*
  882. X * xcmd_ui.c - User interface object initialization functions.
  883. X * This file was generated by `gxv' from `xcmd.G'.
  884. X * DO NOT EDIT BY HAND.
  885. X */
  886. X
  887. X#include <stdio.h>
  888. X#include <sys/param.h>
  889. X#include <sys/types.h>
  890. X#include <xview/xview.h>
  891. X#include <xview/canvas.h>
  892. X#include <xview/panel.h>
  893. X#include <xview/scrollbar.h>
  894. X#include <xview/svrimage.h>
  895. X#include <xview/termsw.h>
  896. X#include <xview/text.h>
  897. X#include <xview/tty.h>
  898. X#include <xview/xv_xrect.h>
  899. X#include "xcmd_ui.h"
  900. X
  901. X/*
  902. X * Initialize an instance of object `window1'.
  903. X */
  904. Xxcmd_window1_objects *
  905. Xxcmd_window1_objects_initialize(ip, owner)
  906. X    xcmd_window1_objects    *ip;
  907. X    Xv_opaque    owner;
  908. X{
  909. X    if (!ip && !(ip = (xcmd_window1_objects *) calloc(1, sizeof (xcmd_window1_objects))))
  910. X        return (xcmd_window1_objects *) NULL;
  911. X    if (!ip->window1)
  912. X        ip->window1 = xcmd_window1_window1_create(ip, owner);
  913. X    if (!ip->controls1)
  914. X        ip->controls1 = xcmd_window1_controls1_create(ip, ip->window1);
  915. X    if (!ip->cmd_fld)
  916. X        ip->cmd_fld = xcmd_window1_cmd_fld_create(ip, ip->controls1);
  917. X    if (!ip->status_msg)
  918. X        ip->status_msg = xcmd_window1_status_msg_create(ip, ip->controls1);
  919. X    if (!ip->pid_fld)
  920. X        ip->pid_fld = xcmd_window1_pid_fld_create(ip, ip->controls1);
  921. X    if (!ip->retval_fld)
  922. X        ip->retval_fld = xcmd_window1_retval_fld_create(ip, ip->controls1);
  923. X    if (!ip->button1)
  924. X        ip->button1 = xcmd_window1_button1_create(ip, ip->controls1);
  925. X    if (!ip->button2)
  926. X        ip->button2 = xcmd_window1_button2_create(ip, ip->controls1);
  927. X    if (!ip->stdout_txt)
  928. X        ip->stdout_txt = xcmd_window1_stdout_txt_create(ip, ip->window1);
  929. X    if (!ip->stderr_txt)
  930. X        ip->stderr_txt = xcmd_window1_stderr_txt_create(ip, ip->window1);
  931. X    return ip;
  932. X}
  933. X
  934. X/*
  935. X * Create object `window1' in the specified instance.
  936. X */
  937. XXv_opaque
  938. Xxcmd_window1_window1_create(ip, owner)
  939. X    xcmd_window1_objects    *ip;
  940. X    Xv_opaque    owner;
  941. X{
  942. X    Xv_opaque    obj;
  943. X    
  944. X    obj = xv_create(owner, FRAME,
  945. X        XV_KEY_DATA, INSTANCE, ip,
  946. X        XV_WIDTH, 509,
  947. X        XV_HEIGHT, 473,
  948. X        XV_LABEL, "Base Window",
  949. X        FRAME_SHOW_FOOTER, TRUE,
  950. X        FRAME_SHOW_RESIZE_CORNER, TRUE,
  951. X        NULL);
  952. X    return obj;
  953. X}
  954. X
  955. X/*
  956. X * Create object `controls1' in the specified instance.
  957. X */
  958. XXv_opaque
  959. Xxcmd_window1_controls1_create(ip, owner)
  960. X    xcmd_window1_objects    *ip;
  961. X    Xv_opaque    owner;
  962. X{
  963. X    Xv_opaque    obj;
  964. X    
  965. X    obj = xv_create(owner, PANEL,
  966. X        XV_KEY_DATA, INSTANCE, ip,
  967. X        XV_X, 0,
  968. X        XV_Y, 0,
  969. X        XV_WIDTH, WIN_EXTEND_TO_EDGE,
  970. X        XV_HEIGHT, 101,
  971. X        WIN_BORDER, FALSE,
  972. X        NULL);
  973. X    return obj;
  974. X}
  975. X
  976. X/*
  977. X * Create object `cmd_fld' in the specified instance.
  978. X */
  979. XXv_opaque
  980. Xxcmd_window1_cmd_fld_create(ip, owner)
  981. X    xcmd_window1_objects    *ip;
  982. X    Xv_opaque    owner;
  983. X{
  984. X    extern Panel_setting    cmb_btn_cbk();
  985. X    Xv_opaque    obj;
  986. X    
  987. X    obj = xv_create(owner, PANEL_TEXT,
  988. X        XV_KEY_DATA, INSTANCE, ip,
  989. X        XV_X, 24,
  990. X        XV_Y, 24,
  991. X        PANEL_VALUE_DISPLAY_LENGTH, 32,
  992. X        PANEL_VALUE_STORED_LENGTH, 80,
  993. X        PANEL_LABEL_STRING, "Command:",
  994. X        PANEL_LAYOUT, PANEL_HORIZONTAL,
  995. X        PANEL_READ_ONLY, FALSE,
  996. X        PANEL_NOTIFY_PROC, cmb_btn_cbk,
  997. X        NULL);
  998. X    return obj;
  999. X}
  1000. X
  1001. X/*
  1002. X * Create object `status_msg' in the specified instance.
  1003. X */
  1004. XXv_opaque
  1005. Xxcmd_window1_status_msg_create(ip, owner)
  1006. X    xcmd_window1_objects    *ip;
  1007. X    Xv_opaque    owner;
  1008. X{
  1009. X    Xv_opaque    obj;
  1010. X    
  1011. X    obj = xv_create(owner, PANEL_MESSAGE,
  1012. X        XV_KEY_DATA, INSTANCE, ip,
  1013. X        XV_X, 392,
  1014. X        XV_Y, 24,
  1015. X        PANEL_LABEL_STRING, "READY",
  1016. X        PANEL_LABEL_BOLD, TRUE,
  1017. X        NULL);
  1018. X    return obj;
  1019. X}
  1020. X
  1021. X/*
  1022. X * Create object `pid_fld' in the specified instance.
  1023. X */
  1024. XXv_opaque
  1025. Xxcmd_window1_pid_fld_create(ip, owner)
  1026. X    xcmd_window1_objects    *ip;
  1027. X    Xv_opaque    owner;
  1028. X{
  1029. X    Xv_opaque    obj;
  1030. X    
  1031. X    obj = xv_create(owner, PANEL_TEXT,
  1032. X        XV_KEY_DATA, INSTANCE, ip,
  1033. X        XV_X, 48,
  1034. X        XV_Y, 56,
  1035. X        PANEL_VALUE_DISPLAY_LENGTH, 6,
  1036. X        PANEL_VALUE_STORED_LENGTH, 6,
  1037. X        PANEL_LABEL_STRING, "PID:",
  1038. X        PANEL_LAYOUT, PANEL_HORIZONTAL,
  1039. X        PANEL_READ_ONLY, TRUE,
  1040. X        NULL);
  1041. X    return obj;
  1042. X}
  1043. X
  1044. X/*
  1045. X * Create object `retval_fld' in the specified instance.
  1046. X */
  1047. XXv_opaque
  1048. Xxcmd_window1_retval_fld_create(ip, owner)
  1049. X    xcmd_window1_objects    *ip;
  1050. X    Xv_opaque    owner;
  1051. X{
  1052. X    Xv_opaque    obj;
  1053. X    
  1054. X    obj = xv_create(owner, PANEL_TEXT,
  1055. X        XV_KEY_DATA, INSTANCE, ip,
  1056. X        XV_X, 152,
  1057. X        XV_Y, 56,
  1058. X        PANEL_VALUE_DISPLAY_LENGTH, 4,
  1059. X        PANEL_VALUE_STORED_LENGTH, 4,
  1060. X        PANEL_LABEL_STRING, "Retval:",
  1061. X        PANEL_LAYOUT, PANEL_HORIZONTAL,
  1062. X        PANEL_READ_ONLY, TRUE,
  1063. X        NULL);
  1064. X    return obj;
  1065. X}
  1066. X
  1067. X/*
  1068. X * Create object `button1' in the specified instance.
  1069. X */
  1070. XXv_opaque
  1071. Xxcmd_window1_button1_create(ip, owner)
  1072. X    xcmd_window1_objects    *ip;
  1073. X    Xv_opaque    owner;
  1074. X{
  1075. X    extern void        button1_cbk();
  1076. X    Xv_opaque    obj;
  1077. X    
  1078. X    obj = xv_create(owner, PANEL_BUTTON,
  1079. X        XV_KEY_DATA, INSTANCE, ip,
  1080. X        XV_X, 328,
  1081. X        XV_Y, 56,
  1082. X        PANEL_LABEL_STRING, "Button1",
  1083. X        PANEL_NOTIFY_PROC, button1_cbk,
  1084. X        NULL);
  1085. X    return obj;
  1086. X}
  1087. X
  1088. X/*
  1089. X * Create object `button2' in the specified instance.
  1090. X */
  1091. XXv_opaque
  1092. Xxcmd_window1_button2_create(ip, owner)
  1093. X    xcmd_window1_objects    *ip;
  1094. X    Xv_opaque    owner;
  1095. X{
  1096. X    extern void        button2_cbk();
  1097. X    Xv_opaque    obj;
  1098. X    
  1099. X    obj = xv_create(owner, PANEL_BUTTON,
  1100. X        XV_KEY_DATA, INSTANCE, ip,
  1101. X        XV_X, 424,
  1102. X        XV_Y, 56,
  1103. X        PANEL_LABEL_STRING, "Button2",
  1104. X        PANEL_NOTIFY_PROC, button2_cbk,
  1105. X        NULL);
  1106. X    return obj;
  1107. X}
  1108. X
  1109. X/*
  1110. X * Create object `stdout_txt' in the specified instance.
  1111. X */
  1112. XXv_opaque
  1113. Xxcmd_window1_stdout_txt_create(ip, owner)
  1114. X    xcmd_window1_objects    *ip;
  1115. X    Xv_opaque    owner;
  1116. X{
  1117. X    Xv_opaque    obj;
  1118. X    
  1119. X    obj = xv_create(owner, TEXTSW,
  1120. X        XV_KEY_DATA, INSTANCE, ip,
  1121. X        XV_X, 0,
  1122. X        XV_Y, 116,
  1123. X        XV_WIDTH, WIN_EXTEND_TO_EDGE,
  1124. X        XV_HEIGHT, 169,
  1125. X        OPENWIN_SHOW_BORDERS, TRUE,
  1126. X        TEXTSW_BROWSING, TRUE,
  1127. X        TEXTSW_DISABLE_LOAD, TRUE,
  1128. X        NULL);
  1129. X    return obj;
  1130. X}
  1131. X
  1132. X/*
  1133. X * Create object `stderr_txt' in the specified instance.
  1134. X */
  1135. XXv_opaque
  1136. Xxcmd_window1_stderr_txt_create(ip, owner)
  1137. X    xcmd_window1_objects    *ip;
  1138. X    Xv_opaque    owner;
  1139. X{
  1140. X    Xv_opaque    obj;
  1141. X    
  1142. X    obj = xv_create(owner, TEXTSW,
  1143. X        XV_KEY_DATA, INSTANCE, ip,
  1144. X        XV_X, 0,
  1145. X        XV_Y, 301,
  1146. X        XV_WIDTH, WIN_EXTEND_TO_EDGE,
  1147. X        XV_HEIGHT, WIN_EXTEND_TO_EDGE,
  1148. X        OPENWIN_SHOW_BORDERS, TRUE,
  1149. X        TEXTSW_BROWSING, TRUE,
  1150. X        TEXTSW_DISABLE_LOAD, TRUE,
  1151. X        NULL);
  1152. X    return obj;
  1153. X}
  1154. X
  1155. END_OF_FILE
  1156. if test 5788 -ne `wc -c <'xcmd_ui.c'`; then
  1157.     echo shar: \"'xcmd_ui.c'\" unpacked with wrong size!
  1158. fi
  1159. # end of 'xcmd_ui.c'
  1160. fi
  1161. if test -f 'xcmd_ui.h' -a "${1}" != "-c" ; then 
  1162.   echo shar: Will not clobber existing file \"'xcmd_ui.h'\"
  1163. else
  1164. echo shar: Extracting \"'xcmd_ui.h'\" \(1044 characters\)
  1165. sed "s/^X//" >'xcmd_ui.h' <<'END_OF_FILE'
  1166. X#ifndef    xcmd_HEADER
  1167. X#define    xcmd_HEADER
  1168. X
  1169. X/*
  1170. X * xcmd_ui.h - User interface object and function declarations.
  1171. X * This file was generated by `gxv' from `xcmd.G'.
  1172. X * DO NOT EDIT BY HAND.
  1173. X */
  1174. X
  1175. Xextern Attr_attribute    INSTANCE;
  1176. X
  1177. X
  1178. Xtypedef struct {
  1179. X    Xv_opaque    window1;
  1180. X    Xv_opaque    controls1;
  1181. X    Xv_opaque    cmd_fld;
  1182. X    Xv_opaque    status_msg;
  1183. X    Xv_opaque    pid_fld;
  1184. X    Xv_opaque    retval_fld;
  1185. X    Xv_opaque    button1;
  1186. X    Xv_opaque    button2;
  1187. X    Xv_opaque    stdout_txt;
  1188. X    Xv_opaque    stderr_txt;
  1189. X} xcmd_window1_objects;
  1190. X
  1191. Xextern xcmd_window1_objects    *xcmd_window1_objects_initialize();
  1192. X
  1193. Xextern Xv_opaque    xcmd_window1_window1_create();
  1194. Xextern Xv_opaque    xcmd_window1_controls1_create();
  1195. Xextern Xv_opaque    xcmd_window1_cmd_fld_create();
  1196. Xextern Xv_opaque    xcmd_window1_status_msg_create();
  1197. Xextern Xv_opaque    xcmd_window1_pid_fld_create();
  1198. Xextern Xv_opaque    xcmd_window1_retval_fld_create();
  1199. Xextern Xv_opaque    xcmd_window1_button1_create();
  1200. Xextern Xv_opaque    xcmd_window1_button2_create();
  1201. Xextern Xv_opaque    xcmd_window1_stdout_txt_create();
  1202. Xextern Xv_opaque    xcmd_window1_stderr_txt_create();
  1203. X#endif
  1204. END_OF_FILE
  1205. if test 1044 -ne `wc -c <'xcmd_ui.h'`; then
  1206.     echo shar: \"'xcmd_ui.h'\" unpacked with wrong size!
  1207. fi
  1208. # end of 'xcmd_ui.h'
  1209. fi
  1210. if test -f 'xrun.c' -a "${1}" != "-c" ; then 
  1211.   echo shar: Will not clobber existing file \"'xrun.c'\"
  1212. else
  1213. echo shar: Extracting \"'xrun.c'\" \(12963 characters\)
  1214. sed "s/^X//" >'xrun.c' <<'END_OF_FILE'
  1215. X/*
  1216. X *  DEXID
  1217. X *    xrun.3
  1218. X *  
  1219. X *  NAME
  1220. X *    xrunsh, xrunv - Notifier based program execution package
  1221. X * 
  1222. X *  SYNOPSIS
  1223. X *    #include <xrun.h>
  1224. X *
  1225. X *    int xrunsh(cmd, fd1f, fd2f, reaper)
  1226. X *    char *cmd;
  1227. X *    Notify_value (*fd1f)();
  1228. X *    Notify_value (*fd2f)();
  1229. X *    Notify_value (*reaper)();
  1230. X *
  1231. X *    int xrunv(v, fd1f, fd2f, reaper)
  1232. X *    char *v[];
  1233. X *    Notify_value (*fd1f)();
  1234. X *    Notify_value (*fd2f)();
  1235. X *    Notify_value (*reaper)();
  1236. X *
  1237. X *  DESCRIPTION
  1238. X *    xrunsh() and xrunv() are front-ends to the classic "pipe-fork-exec"
  1239. X *    model of child program execution.  xrunsh() passes cmd_ as an argument
  1240. X *    to the Bourne shell (sh -c) and therefore may be used to run commands
  1241. X *    that might contain metacharacters; xrunv() takes a vector of arguments v_
  1242. X *    and runs v[0] directly.  execvp() is used to start cmd_.
  1243. X *
  1244. X *    The application supplies callback handlers for the child process'
  1245. X *    stdout (fd1f), stderr (fd2f), and signals (reaper).  Any of these
  1246. X *    handlers may be NULL, indicating that the parent has no interest in
  1247. X *    handling these events.  Both the file descriptor handlers and the
  1248. X *    child reaper handler have declarations identical to "regular" Xview
  1249. X *    handlers:
  1250. X *    
  1251. X *            Notify_value fd_handler(client, fd)
  1252. X *            Notify_client client;
  1253. X *            int fd;
  1254. X *
  1255. X *            Notify_value reaper(client, pid, status, rusage)
  1256. X *            Notify_client client;
  1257. X *            int pid;
  1258. X *            union wait *status;
  1259. X *            struct rusage *rusage;
  1260. X *
  1261. X *    In the case of the file descriptor handlers, client will always equal
  1262. X *    either STDOUT_CLIENT or STDERR_CLIENT, depending on which stream
  1263. X *    activated the callback.  The calling program can therefore pass the
  1264. X *    same handler to xrunsh()/xrunv() for both fd1f and fd2f and
  1265. X *    distinguish between the two streams via the value of the client
  1266. X *    argument.  The child reaper is always called with client equal to
  1267. X *    REAPER_CLIENT.
  1268. X *
  1269. X *    The main features of the package are:
  1270. X *    1.  All the piping and forking is hidden away (finally...)
  1271. X *    2.  File descriptor resources are properly reclaimed.
  1272. X *
  1273. X *    Point (2) warrants a bit of explanation.  Consider the following code:
  1274. X *        
  1275. X *            main()
  1276. X *            {
  1277. X *            (void) fprintf(stdout, "I am stdout.\n")
  1278. X *            (void) fprintf(stderr, "I am stderr.\n")
  1279. X *            return 0;
  1280. X *            }
  1281. X *
  1282. X *    All the Xview manuals (including the O'Reilly series) provide examples
  1283. X *    of how to spawn this program from a X parent and get a hold of the
  1284. X *    child's stdin, out, and err, but they make an important -- and
  1285. X *    incorrect -- assumption that the streams will be drained *before* the
  1286. X *    child exits and is reaped.  The file descriptors are usually closed
  1287. X *    and the events de-registered at the time the child is reaped.  The
  1288. X *    order in which the events are received, however, *cannot* be assumed.
  1289. X *    For example, upon first inspection the program above would appear to
  1290. X *    first wake up the stdout callback, then the stderr callback, and
  1291. X *    finally the child reaper callback.  In fact, it is possible to have the
  1292. X *    events triggered in exactly the reverse order!  If the reaper is
  1293. X *    reclaiming resources, the data pending in the streams will be lost.
  1294. X *    Even worse is the permutation where the child reaper callback wakes up
  1295. X *    in the middle.  In this case, the descriptors will be closed and when
  1296. X *    the last stream callback gets hit, the descriptor will no longer be
  1297. X *    valid and an internal Xview error will be generated.  This has nothing
  1298. X *    to do with flushing streams, by the way.  It is simply a matter of
  1299. X *    properly managing a set of asynchronous resources within a synchronous
  1300. X *    context (whew!).
  1301. X *
  1302. X *    xrunsh()/xrunv() provides utility by quietly checking the status of
  1303. X *    the child and the pipes after each event callback is processed.  If
  1304. X *    the child has exited and there is no data left to be read on the
  1305. X *    pipes, then at that time and at that time only will the descriptors be
  1306. X *    closed and the callbacks de-registered.  The calling application
  1307. X *    should not perform these operations.
  1308. X *
  1309. X *  DIAGNOSTICS
  1310. X *    If the integer returned by xrunsh()/xrunv() is > 0, then the child has
  1311. X *    started successfully and the integer is the PID of the child.  A
  1312. X *    return value of XRUN_MAX_PROCS_ERR indicates that the maximum number
  1313. X *    of processes that may be managed simultaneously has been exceeded (see
  1314. X *    BUGS).  A return value of XRUN_SYS_ERR indicates that either some
  1315. X *    system call failed during the pipe-fork-exec process OR the process
  1316. X *    cannot be started; errno will be set. 
  1317. X *
  1318. X *    If the fork succeeds but the child process cannot be execed, then the
  1319. X *    fork will exit with a return value XRUN_EXEC_ERR.  The reaper process
  1320. X *    should check for this return value, although a successful exec could
  1321. X *    also return this value.
  1322. X *
  1323. X *  NOTES
  1324. X *    As always, running a command via sh -c prevents the calling app from
  1325. X *    determining if the command is in fact executable.  The shell will
  1326. X *    write 
  1327. X *           sh: program: not found
  1328. X *    to stderr and return 1 to the caller, but that's about it.  What
  1329. X *    you're really looking for is errno ENOENT.  The tightest way?  Check
  1330. X *    your program accessibility *before* calling xrunsh() or xrunv().
  1331. X *
  1332. X *  BUGS
  1333. X *    The maximum number of processes that may be managed simultaneously is
  1334. X *    dependent on a bunch of operating systems parameters known at compile
  1335. X *    time plus run-time usage of file descriptors in other parts of the
  1336. X *    calling program.  I have elected to punt on this issue and have set an
  1337. X *    arbitrary limit of 16 processes; see the explanation near the
  1338. X *    definition of the MAX_PROCS symbol.
  1339. X *
  1340. X *  AUTHOR
  1341. X *    Buzz Moschetti, BSC  08/12/92
  1342. X *
  1343. X */
  1344. X/* LINTLIBRARY */
  1345. X
  1346. X#include <stdio.h>
  1347. X#include <errno.h>
  1348. X
  1349. X#include <sys/param.h>
  1350. X#include <sys/wait.h>
  1351. X#include <sys/ioctl.h>
  1352. X#include <sys/types.h>
  1353. X
  1354. X#include <xview/notify.h>
  1355. X
  1356. X#include <xrun.h>
  1357. X
  1358. X
  1359. Xstatic Notify_value fd_reader(), child_reaper();
  1360. X
  1361. Xstatic Comm *map_fd2comm();
  1362. Xstatic Comm *map_pid2comm();
  1363. Xstatic Comm *get_comm();
  1364. X
  1365. Xstatic void free_comm();
  1366. Xstatic void clean_up();
  1367. Xstatic void drain();
  1368. X
  1369. Xstatic int xrun();
  1370. X
  1371. X
  1372. X
  1373. Xstatic Notify_client fd1_client = STDOUT_CLIENT;
  1374. Xstatic Notify_client fd2_client = STDERR_CLIENT;
  1375. Xstatic Notify_client reaper_client = REAPER_CLIENT;
  1376. X
  1377. X/*
  1378. X *  This is a hardcode, but I'm too lazy to go into operating system
  1379. X *  junk to dynamically arrive at the size.  Assume that a max of 64
  1380. X *  descriptors are available, and that stdin, stdout, stderr, X, and 
  1381. X *  other crap takes up fd 0-7.   Each proc creates (temporarily) six
  1382. X *  descriptors (3 pipes), after which three are closed, so the theoretical 
  1383. X *  max we can have is 17.  Why?  Steady state after 16 opens is 16 * 3 = 48.
  1384. X *  The 17th open temporarily adds 6, 48 + 6 = 54.  54 + 8 overhead = 62,
  1385. X *  which is within the limit.
  1386. X *
  1387. X *  NOTE:  Since 17 is *UGLY*, we will drop the max to 16.  :-)
  1388. X */
  1389. X#define   MAX_PROCS    16
  1390. X
  1391. Xstatic Comm cxx[MAX_PROCS];
  1392. X
  1393. X
  1394. X/*
  1395. X *  This buffer is used to drain pipes when the app does not provide handlers
  1396. X *  for stdout and stderr.  These streams MUST be drained or the child will
  1397. X *  hang after 4096 bytes have been written.
  1398. X */
  1399. X#define   PLAYBUFSIZE    512
  1400. Xstatic char pbuf[PLAYBUFSIZE];
  1401. X
  1402. X
  1403. X
  1404. X
  1405. X
  1406. Xint
  1407. Xxrunsh(cmd, fd1f, fd2f, reaper)
  1408. Xchar *cmd;
  1409. XNotify_value (*fd1f)();
  1410. XNotify_value (*fd2f)();
  1411. XNotify_value (*reaper)();
  1412. X{
  1413. X      int rc;
  1414. X      char *v[4];
  1415. X      
  1416. X      v[0] = "sh";
  1417. X      v[1] = "-c";
  1418. X      v[2] = cmd;
  1419. X      v[3] = (char *)0;
  1420. X
  1421. X      rc = xrunv(v, fd1f, fd2f, reaper);
  1422. X
  1423. X      return rc;
  1424. X}
  1425. X
  1426. X
  1427. Xint
  1428. Xxrunv(v, fd1f, fd2f, reaper)
  1429. Xchar *v[];
  1430. XNotify_value (*fd1f)();
  1431. XNotify_value (*fd2f)();
  1432. XNotify_value (*reaper)();
  1433. X{
  1434. X      Comm *w;
  1435. X      int pipe_io[3][2];      
  1436. X      int pid, i;
  1437. X
  1438. X
  1439. X      /*
  1440. X       *  Find an open channel:
  1441. X       */
  1442. X      w = get_comm();
  1443. X
  1444. X      /* Did we overrun the table? */
  1445. X      if(!w) {
  1446. X        return XRUN_MAX_PROCS_ERR;
  1447. X      }
  1448. X
  1449. X
  1450. X
  1451. X      if(pipe(pipe_io[0]) == -1) {
  1452. X        return XRUN_SYS_ERR;
  1453. X      }
  1454. X      if(pipe(pipe_io[1]) == -1) {
  1455. X        (void) close(pipe_io[0][0]);
  1456. X        (void) close(pipe_io[0][1]);
  1457. X        return XRUN_SYS_ERR;
  1458. X      }
  1459. X      if(pipe(pipe_io[2]) == -1) {
  1460. X        (void) close(pipe_io[0][0]);
  1461. X        (void) close(pipe_io[0][1]);
  1462. X        (void) close(pipe_io[1][0]);
  1463. X        (void) close(pipe_io[1][1]);
  1464. X        return XRUN_SYS_ERR;
  1465. X      }
  1466. X
  1467. X
  1468. X
  1469. X      switch(pid = fork()) {
  1470. X      case -1:
  1471. X        return XRUN_SYS_ERR;
  1472. X
  1473. X        
  1474. X      case 0:  /* child! */
  1475. X        /* Redirected child's std{in,out,err}: */
  1476. X        (void) dup2(pipe_io[0][0], 0);
  1477. X        (void) dup2(pipe_io[1][1], 1);
  1478. X        (void) dup2(pipe_io[2][1], 2);
  1479. X
  1480. X        /* Shut down all other open fd's in child: */
  1481. X        for(i = getdtablesize(); i > 2; i--)
  1482. X          (void) close(i);
  1483. X        
  1484. X        (void) execvp(v[0], v);
  1485. X
  1486. X        /* 
  1487. X         *  If we're here then it didn't execute.  Since we're the child
  1488. X         *  and can't "return" XRUN_SYS_ERR to the calling function, 
  1489. X         *  we will cheat by exiting with a value of XRUN_EXEC_ERR.  The 
  1490. X         *  parent can then check for this value when it reaps the child.
  1491. X         */
  1492. X        _exit(XRUN_EXEC_ERR);
  1493. X        
  1494. X
  1495. X      default:  /* parent! */
  1496. X        (void) close(pipe_io[0][0]);
  1497. X        (void) close(pipe_io[1][1]);
  1498. X        (void) close(pipe_io[2][1]);
  1499. X        break;
  1500. X      }
  1501. X
  1502. X
  1503. X      /*
  1504. X       *  Set communications resources:
  1505. X       */
  1506. X      w->pid     = pid;
  1507. X      w->pipe_out    = pipe_io[0][1];
  1508. X      w->pipe_in     = pipe_io[1][0];
  1509. X      w->pipe_errin  = pipe_io[2][0];
  1510. X
  1511. X
  1512. X      w->child_up = 1;
  1513. X
  1514. X      w->fd1f = fd1f;
  1515. X      w->fd2f = fd2f;
  1516. X      w->reaper = reaper;
  1517. X
  1518. X      notify_set_input_func(fd1_client, fd_reader, w->pipe_in);
  1519. X      notify_set_input_func(fd2_client, fd_reader, w->pipe_errin);
  1520. X
  1521. X      notify_set_wait3_func(reaper_client, child_reaper, w->pid);
  1522. X
  1523. X      return pid;
  1524. X}
  1525. X
  1526. X
  1527. X
  1528. X/*
  1529. X *  This callback is hit when a data client writes its stdout.
  1530. X */
  1531. Xstatic Notify_value
  1532. Xfd_reader(client, fd)
  1533. XNotify_client client;
  1534. Xint fd;
  1535. X{
  1536. X      Notify_value v;
  1537. X      Notify_value (*f)();
  1538. X      Comm *w;
  1539. X
  1540. X      /* Map fd to Comm: */
  1541. X      w = map_fd2comm(fd);
  1542. X
  1543. X      /*
  1544. X       *  Incoming client value can only be fd1_client or fd2_client since
  1545. X       *  this module set it up.
  1546. X       */
  1547. X      f = (client == fd1_client) ? w->fd1f : w->fd2f;
  1548. X
  1549. X      if(f) {
  1550. X        v = (*f)(client, fd);
  1551. X      } else {
  1552. X        drain(fd);
  1553. X        v = NOTIFY_DONE;
  1554. X      }
  1555. X
  1556. X      /* Perform mandatory cleanup: */
  1557. X      clean_up(w);
  1558. X
  1559. X      return v;
  1560. X}
  1561. X
  1562. X
  1563. X/*
  1564. X *  Handle death of data client.
  1565. X */
  1566. Xstatic Notify_value
  1567. Xchild_reaper(client, pid, status, rusage)
  1568. XNotify_client client;
  1569. Xint pid;
  1570. Xunion wait *status;
  1571. Xstruct rusage *rusage;
  1572. X{
  1573. X      Notify_value v;
  1574. X      Comm *w;
  1575. X
  1576. X      /* Map pid to Comm: */
  1577. X      w = map_pid2comm(pid);
  1578. X
  1579. X      /* Child is no longer active: */
  1580. X      w->child_up = 0;
  1581. X
  1582. X      /*
  1583. X       *  Unlike the fd reader, there is no special "drain" that needs to
  1584. X       *  be called.
  1585. X       */
  1586. X      v = w->reaper ? w->reaper(client, pid, status, rusage) : NOTIFY_DONE;
  1587. X
  1588. X      /* Perform mandatory cleanup: */
  1589. X      clean_up(w);
  1590. X
  1591. X      return v;
  1592. X}
  1593. X
  1594. X
  1595. X
  1596. X
  1597. Xstatic Comm *
  1598. Xget_comm()
  1599. X{
  1600. X      int w;
  1601. X
  1602. X      for(w = 0; w < MAX_PROCS; w++) {
  1603. X        if(cxx[w].pid == 0) {
  1604. X          return &cxx[w];
  1605. X        }
  1606. X      }
  1607. X
  1608. X      return (Comm *)0;
  1609. X}
  1610. X
  1611. X
  1612. Xstatic void
  1613. Xfree_comm(w)
  1614. XComm *w;
  1615. X{
  1616. X      if(w) {
  1617. X        w->pid = 0;
  1618. X      }
  1619. X}
  1620. X
  1621. Xstatic Comm *
  1622. Xmap_fd2comm(fd)
  1623. Xint fd;
  1624. X{
  1625. X      int i;
  1626. X
  1627. X      for(i = 0; i < MAX_PROCS; i++) {
  1628. X        if(cxx[i].pipe_in == fd ||
  1629. X           cxx[i].pipe_errin == fd) {
  1630. X          return &cxx[i];
  1631. X        }
  1632. X      }
  1633. X
  1634. X      /*  This should never happen! */
  1635. X      return (Comm *)0;
  1636. X}
  1637. X
  1638. X
  1639. Xstatic Comm *
  1640. Xmap_pid2comm(pid)
  1641. Xint pid;
  1642. X{
  1643. X      int i;
  1644. X
  1645. X      for(i = 0; i < MAX_PROCS; i++) {
  1646. X        if(cxx[i].pid == pid) {
  1647. X          return &cxx[i];
  1648. X        }
  1649. X      }
  1650. X
  1651. X      /*  This should never happen! */
  1652. X      return (Comm *)0;
  1653. X}
  1654. X
  1655. X
  1656. X
  1657. Xstatic void
  1658. Xclean_up(w)
  1659. XComm *w;
  1660. X{
  1661. X      int fd1b, fd2b;
  1662. X
  1663. X      /*
  1664. X       *  If there is no data left to be read on either stdout or stderr
  1665. X       *  AND the child has exited, then we are truly finished -- free up
  1666. X       *  the resources.
  1667. X       *
  1668. X       *  In order to maximize performance, we'll check child status first
  1669. X       *  followed by bytes-pending tests on the pipes:
  1670. X       */
  1671. X      if(w->child_up == 0) {
  1672. X
  1673. X        if (ioctl(w->pipe_in, FIONREAD, &fd1b) == -1) {
  1674. X          (void) sprintf(pbuf, "clean_up: ioctl error fd %d",w->pipe_in);
  1675. X          perror(pbuf);
  1676. X        }
  1677. X        if (ioctl(w->pipe_errin, FIONREAD, &fd2b) == -1) {
  1678. X          (void) sprintf(pbuf, "clean_up: ioctl error fd %d",w->pipe_errin);
  1679. X          perror(pbuf);
  1680. X        }
  1681. X        
  1682. X        if(fd1b == 0 && fd2b == 0) {
  1683. X
  1684. X          /*  Deregister and close! */
  1685. X          notify_set_input_func(fd1_client, NOTIFY_FUNC_NULL,w->pipe_in);
  1686. X          notify_set_input_func(fd2_client, NOTIFY_FUNC_NULL,w->pipe_errin);
  1687. X          notify_set_wait3_func(reaper_client, NOTIFY_FUNC_NULL, w->pid);
  1688. X          
  1689. X          (void) close(w->pipe_out);
  1690. X          (void) close(w->pipe_in);
  1691. X          (void) close(w->pipe_errin);
  1692. X          
  1693. X          /* Reset the slot: */
  1694. X          free_comm(w);
  1695. X        }
  1696. X      }
  1697. X}
  1698. X
  1699. X
  1700. Xstatic void
  1701. Xdrain(fd)
  1702. Xint fd;
  1703. X{
  1704. X      int bytes;
  1705. X
  1706. X      if (ioctl(fd, FIONREAD, &bytes) == -1) {
  1707. X        (void) sprintf(pbuf, "drain: ioctl error fd %d", fd);
  1708. X        perror(pbuf);
  1709. X      } else if(bytes != 0) {
  1710. X        /*  We have bytes!  Eat them: */
  1711. X        (void) read(fd, pbuf, PLAYBUFSIZE);
  1712. X      }
  1713. X}
  1714. X
  1715. END_OF_FILE
  1716. if test 12963 -ne `wc -c <'xrun.c'`; then
  1717.     echo shar: \"'xrun.c'\" unpacked with wrong size!
  1718. fi
  1719. # end of 'xrun.c'
  1720. fi
  1721. if test -f 'xrun.h' -a "${1}" != "-c" ; then 
  1722.   echo shar: Will not clobber existing file \"'xrun.h'\"
  1723. else
  1724. echo shar: Extracting \"'xrun.h'\" \(773 characters\)
  1725. sed "s/^X//" >'xrun.h' <<'END_OF_FILE'
  1726. X/*
  1727. X *  xrun.h
  1728. X */
  1729. X
  1730. X#if !defined(XRUN_H_INCLUDED)
  1731. X#define XRUN_H_INCLUDED
  1732. X
  1733. X#include <xview/notify.h>
  1734. X
  1735. X#define  XRUN_SYS_ERR        (-1)
  1736. X#define  XRUN_MAX_PROCS_ERR    (0)
  1737. X
  1738. X#define  XRUN_EXEC_ERR        (254)
  1739. X
  1740. X
  1741. X/*
  1742. X *   Arbitrary values 10, 11, 12, but we must put them in xrun.h
  1743. X *   so that the app level call can distinguish between the two fd
  1744. X *   streams should it elect to have a common callback.
  1745. X */
  1746. X#define  STDOUT_CLIENT    ((Notify_client)10)
  1747. X#define  STDERR_CLIENT    ((Notify_client)11)
  1748. X#define  REAPER_CLIENT  ((Notify_client)12)
  1749. X
  1750. X
  1751. Xtypedef struct Comm {
  1752. X      int pid;
  1753. X      int pipe_out;
  1754. X      int pipe_in;
  1755. X      int pipe_errin;
  1756. X
  1757. X      Notify_value (*fd1f)();
  1758. X      Notify_value (*fd2f)();
  1759. X      Notify_value (*reaper)();
  1760. X
  1761. X      char child_up;
  1762. X} Comm;
  1763. X
  1764. X#endif  /* XRUN_H_INCLUDED */
  1765. END_OF_FILE
  1766. if test 773 -ne `wc -c <'xrun.h'`; then
  1767.     echo shar: \"'xrun.h'\" unpacked with wrong size!
  1768. fi
  1769. # end of 'xrun.h'
  1770. fi
  1771. echo shar: End of archive 1 \(of 1\).
  1772. cp /dev/null ark1isdone
  1773. MISSING=""
  1774. for I in 1 ; do
  1775.     if test ! -f ark${I}isdone ; then
  1776.     MISSING="${MISSING} ${I}"
  1777.     fi
  1778. done
  1779. if test "${MISSING}" = "" ; then
  1780.     echo You have the archive.
  1781.     rm -f ark[1-9]isdone
  1782. else
  1783.     echo You still need to unpack the following archives:
  1784.     echo "        " ${MISSING}
  1785. fi
  1786. ##  End of shell archive.
  1787. exit 0
  1788.