home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1995 August / NEBULA.mdf / Apps / DevTools / Compiler2.0 / Source / Subprocess.m < prev   
Encoding:
Text File  |  1993-01-25  |  7.3 KB  |  325 lines

  1. /*
  2.     Subprocess.m    (v10)
  3.     by Charles L. Oei
  4.     pty support by Joe Freeman
  5.     Subprocess Example, Release 2.0
  6.     NeXT Computer, Inc. 
  7.  
  8.     You may freely copy, distribute and reuse the code in this example.
  9.     NeXT disclaims any warranty of any kind, expressed or implied, as to
  10.     its fitness for any particular use.
  11. */
  12. /*
  13.     This has been modified to return the status of the child at
  14.     the end of the process
  15. */
  16. #import "Subprocess.h"
  17. // #import <sgtty.h>    // needed to compile under Release 1.0
  18. #import <appkit/nextstd.h>
  19. #import <appkit/Application.h>
  20. #import <appkit/Panel.h>
  21.  
  22. #define    PTY_TEMPLATE "/dev/pty??"
  23. #define    PTY_LENGTH 11
  24.  
  25. static void showError();
  26.  
  27.  
  28. /*==========================================================
  29.  *
  30.  * Private Instance Methods
  31.  *
  32.  *==========================================================*/
  33.  
  34. @interface Subprocess(Private)
  35. - childDidExit;
  36. - fdHandler:(int)theFd;
  37. @end
  38.  
  39. @implementation Subprocess(Private)
  40.  
  41. - childDidExit
  42.     // cleanup after a child process exits
  43. {
  44.     union wait result;
  45.     
  46.     if (childPid) {
  47.         wait(&result);
  48.         DPSRemoveFD(fromChild);
  49.         close(fromChild);
  50.         fclose(fpToChild);
  51.         childPid=0;    // specify that child is dead
  52.         if (delegate && [delegate respondsTo:@selector(subprocessDone:)])
  53.             [delegate perform:@selector(subprocessDone:) 
  54.                 with:(id)result.w_status];
  55.     }
  56.     return self;
  57. }
  58.  
  59. - fdHandler:(int)theFd
  60.     // DPS handler for output from subprocess
  61. {
  62.     if (((bufferCount = read(theFd, outputBuffer, BUFFERSIZE-1)) < 0) ||
  63.       (!bufferCount))
  64.     {
  65.     [self childDidExit];
  66.     return self;
  67.     }
  68.     outputBuffer[bufferCount] = '\0';
  69.     if (delegate && [delegate respondsTo:@selector(subprocessOutput:)])
  70.     [delegate perform:@selector(subprocessOutput:)
  71.           with:(void *)&outputBuffer];
  72.     return self;
  73. }
  74.  
  75. @end
  76.  
  77.  
  78. /*==========================================================
  79.  *
  80.  * Private Utility Routines
  81.  *
  82.  *==========================================================*/
  83.  
  84. static void
  85. showError (const char *errorString, id theDelegate)
  86.     // ensure errors never get dropped on the floor
  87. {
  88.     if (theDelegate && [theDelegate respondsTo:@selector(subprocessError:)])
  89.     [theDelegate
  90.         perform:@selector(subprocessError:)
  91.         with:(void *)errorString];
  92.     else if (NXApp)    // no delegate, but we're running w/in an App
  93.     NXRunAlertPanel(0, errorString, 0, 0, 0);
  94.     else
  95.     perror(errorString);
  96. }
  97.  
  98. static void
  99. fdHandler (int theFd, id self)
  100.     // DPS handler for output from subprocess
  101. {
  102.     [self fdHandler:theFd];
  103. }
  104.  
  105. static void
  106. getptys (int *master, int *slave)
  107.     // attempt to setup the ptys
  108. {
  109.     char device[PTY_LENGTH];
  110.     char *block, *num;
  111.     char *blockLoc; // specifies the location of block for the device string
  112.     char *numLoc; // specifies the pty name with the digit ptyxD
  113.     char *msLoc; // specifies the master (ptyxx) or slave (ttyxx)
  114.     
  115.     struct sgttyb setp =
  116.     {B9600, B9600, (char)0x7f, (char)0x15, (CRMOD|ANYP)};
  117.     struct tchars setc =
  118.     {CINTR, CQUIT, CSTART, CSTOP, CEOF, CBRK};
  119.     struct ltchars sltc =
  120.     {CSUSP, CDSUSP, CRPRNT, CFLUSH, CWERASE, CLNEXT};
  121.     int    lset =
  122.     (LCRTBS|LCRTERA|LCRTKIL|LCTLECH|LPENDIN|LDECCTQ);
  123.     int    setd = NTTYDISC;
  124.     
  125.     strcpy(device, PTY_TEMPLATE); // string constants are not writable
  126.     blockLoc = &device[ strlen("/dev/pty") ];
  127.     numLoc = &device[ strlen("/dev/pty?") ];
  128.     msLoc = &device[ strlen("/dev/") ];
  129.     for (block = "pqrs"; *block; block++)
  130.     {
  131.     *blockLoc = *block;
  132.     for (num = "0123456789abcdef"; *num; num++)
  133.     {
  134.         *numLoc = *num;
  135.         *master = open(device, O_RDWR);
  136.         if (*master >= 0)
  137.         {
  138.         *msLoc = 't';
  139.         *slave = open(device, O_RDWR);
  140.         if (*slave >= 0)
  141.         {
  142.             (void) ioctl(*slave, TIOCSETP, (char *)&setp);
  143.             (void) ioctl(*slave, TIOCSETC, (char *)&setc);
  144.             (void) ioctl(*slave, TIOCSETD, (char *)&setd);
  145.             (void) ioctl(*slave, TIOCSLTC, (char *)&sltc);
  146.             (void) ioctl(*slave, TIOCLSET, (char *)&lset);
  147.             return;
  148.         }
  149.         }
  150.     } /* hunting through a bank of ptys */
  151.     } /* hunting through blocks of ptys in all the right places */
  152.     *master = -1;
  153.     *slave = -1;
  154. }
  155.  
  156.  
  157. @implementation Subprocess
  158.  
  159. /*==========================================================
  160.  *
  161.  * Public Instance Methods
  162.  *
  163.  *==========================================================*/
  164.  
  165. - init:(const char *)subprocessString
  166.     // a cover for the below withDelegate:nil, andPtySupport:NO, andStdErr:YES
  167. {
  168.     return
  169.     [self
  170.         init:subprocessString
  171.         withDelegate:nil
  172.         andPtySupport:NO
  173.         andStdErr:YES];
  174. }
  175.  
  176. - init:(const char *)subprocessString
  177.     withDelegate:theDelegate
  178.     andPtySupport:(BOOL)wantsPty
  179.     andStdErr:(BOOL)wantsStdErr
  180.     // initializes an instance of Subprocess and corresponding UNIX process
  181. {
  182.     int pipeTo[2];        // for non-Pty support
  183.     int pipeFrom[2];
  184.     int    tty, numFds, fd;    // for temporary use
  185.     int processGroup;
  186.     int pidChild;        // needed because childPid does not exist
  187.                 // until Subprocess is instantiated
  188.  
  189.     if (wantsPty)
  190.     {
  191.         tty = open("/dev/tty", O_RDWR);
  192.     getptys(&masterPty,&slavePty);
  193.     if (masterPty <= 0 || slavePty <= 0)
  194.     {
  195.         showError("Error grabbing ptys for subprocess.", theDelegate);
  196.         return self;
  197.     }
  198.     // remove the controlling tty if launched from a shell,
  199.     // but not Workspace;
  200.     // so that we have job control over the parent application in shell
  201.     // and so that subprocesses can be restarted in Workspace
  202.     if  ((tty<0) && ((tty = open("/dev/tty", 2))>=0))
  203.     {
  204.         ioctl(tty, TIOCNOTTY, 0);
  205.         close(tty);
  206.     }
  207.     }
  208.     else
  209.     {
  210.     if (pipe(pipeTo) < 0 || pipe(pipeFrom) < 0)
  211.     {
  212.         showError("Error starting UNIX pipes to subprocess.", theDelegate);
  213.         return self;
  214.     }
  215.     }
  216.     
  217.     switch (pidChild = vfork())
  218.     {
  219.     case -1:    // error
  220.     showError("Error starting UNIX vfork of subprocess.", theDelegate);
  221.     return self;
  222.  
  223.     case 0:    // child
  224.     if (wantsPty)
  225.     {
  226.         dup2(slavePty, 0);
  227.         dup2(slavePty, 1);
  228.         if (wantsStdErr)
  229.         dup2(slavePty, 2);
  230.     }
  231.     else
  232.     {
  233.         dup2(pipeTo[0], 0);
  234.         dup2(pipeFrom[1], 1);
  235.         if (wantsStdErr)
  236.         dup2(pipeFrom[1], 2);
  237.     }
  238.     
  239.     numFds = getdtablesize();
  240.     for (fd=3; fd<numFds; fd++)
  241.         close(fd);
  242.  
  243.     processGroup = getpid();
  244.     ioctl(0, TIOCSPGRP, (char *)&processGroup);
  245.     setpgrp (0, processGroup);
  246.     
  247.     // we exec a /bin/sh so that cmds are easier to specify for the user
  248.     execl("/bin/sh", "sh", "-c", subprocessString, 0);
  249.     perror("vfork (child)"); // should never gets here tho
  250.     _exit(1);
  251.  
  252.     default:    // parent
  253.     [self setDelegate:theDelegate];
  254.     childPid = pidChild;
  255.  
  256.     if (wantsPty)
  257.     {
  258.         close(slavePty);
  259.         
  260.         fpToChild = fdopen(masterPty, "w");
  261.         fromChild = masterPty;
  262.     }
  263.     else
  264.     {
  265.         close(pipeTo[0]);
  266.         close(pipeFrom[1]);
  267.     
  268.         fpToChild = fdopen(pipeTo[1], "w");
  269.         fromChild = pipeFrom[0];
  270.     }
  271.  
  272.     setbuf(fpToChild, NULL);
  273.     DPSAddFD(
  274.         fromChild,
  275.         (DPSFDProc)fdHandler,
  276.         (id)self,
  277.         NX_MODALRESPTHRESHOLD+1);
  278.     return self;
  279.     }
  280. }
  281.  
  282. - send:(const char *)string withNewline:(BOOL)wantNewline
  283. {
  284.     fputs(string, fpToChild);
  285.     if (wantNewline)
  286.         fputc('\n', fpToChild);
  287.     return self;
  288. }
  289.  
  290. - send:(const char *)string
  291. {
  292.     [self send:string withNewline:YES];
  293.     return self;
  294. }
  295.  
  296. - terminateInput
  297.     // effectively sends an EOF to the child process stdin
  298. {
  299.     fclose(fpToChild);
  300.     return self;
  301. }
  302.  
  303. - terminate:sender
  304. {
  305.     if (childPid)
  306.     {
  307.     kill(childPid+1, SIGTERM);
  308.     [self childDidExit];
  309.     }
  310.     return self;
  311. }
  312.  
  313. - setDelegate:anObject
  314. {
  315.     delegate = anObject;
  316.     return self;
  317. }
  318.  
  319. - delegate
  320. {
  321.     return delegate;
  322. }
  323.  
  324. @end
  325.