"Fossies" - the Fresh Open Source Software Archive

Member "xterm-379/vms.c" (6 Oct 2022, 18035 Bytes) of package /linux/misc/xterm-379.tgz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "vms.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 373_vs_374.

    1 /* $XTermId: vms.c,v 1.15 2022/10/06 19:35:35 tom Exp $ */
    2 
    3 /*  vms.c
    4  *
    5  * This module contains the VMS version of the routine SPAWN (from the module
    6  * MAIN.C) and the routines that do IO to the pseudo terminal.
    7  *
    8  * Modification History:
    9  * Stephan Jansen 1-Mar-1990  Original version
   10  * Hal R. Brand   5-Sep-1990  Added code to propagate DECW$DISPLAY
   11  * Aaron Leonard 11-Sep-1990  Fix string descriptor lengths
   12  * Stephan Jansen 2-Dec-1991  Modify to use new Pseudo terminal drivers
   13  *                            (patterned after photo.c by Forrest A. Kenney)
   14  * Patrick Mahan  7-Jan-1991  Removed reference to <dvidef.h> from VMS.C
   15  *                Forced device type to be VT102 since that is
   16  *                what we are emulating.
   17  */
   18 
   19 #include <libdef.h>
   20 #include <lnmdef.h>
   21 
   22 #include <stdio.h>
   23 #include <string.h>
   24 
   25 #include "xterm.h"
   26 #include "data.h"
   27 #include "vms.h"
   28 
   29 #define PTD$C_SEND_XON      0   /* Pseudo Terminal Driver event      */
   30 #define PTD$C_SEND_BELL     1
   31 #define PTD$C_SEND_XOFF     2
   32 #define PTD$C_STOP_OUTPUT   3
   33 #define PTD$C_RESUME_OUTPUT     4
   34 #define PTD$C_CHAR_CHANGED  5
   35 #define PTD$C_ABORT_OUTPUT  6
   36 #define PTD$C_START_READ    7
   37 #define PTD$C_MIDDLE_READ   8
   38 #define PTD$C_END_READ      9
   39 #define PTD$C_ENABLE_READ   10
   40 #define PTD$C_DISABLE_READ  11
   41 #define PTD$C_MAX_EVENTS    12
   42 
   43 #define BUFFERS             6
   44 #define PAGE            512
   45 
   46 typedef struct  tt_buffer
   47 {
   48 unsigned int    flink;
   49 unsigned int    blink;
   50 short   int status;
   51 short   int length;
   52 char        data[VMS_TERM_BUFFER_SIZE];
   53 } TT_BUF_STRUCT;
   54 
   55 TT_BUF_STRUCT       *tt_w_buff;
   56 struct  q_head       _align(QUADWORD)   buffer_queue = (0,0);
   57 struct  q_head       _align(QUADWORD)   read_queue = (0,0);
   58 
   59 static char          tt_name[64];
   60 static $DESCRIPTOR   (tt_name_desc, &tt_name);
   61 
   62 static char          ws_name[64];
   63 static $DESCRIPTOR   (ws_name_desc, &ws_name);
   64 
   65 static struct        tt_char {
   66    char        class;
   67    char        type;
   68    short int   page_width;
   69    char        characteristics[3];
   70    char        length;
   71    int         extended;
   72  } tt_mode, tt_chars, orig_tt_chars;
   73 
   74 struct mem_region
   75 {
   76   TT_BUF_STRUCT *start;
   77   TT_BUF_STRUCT *end;
   78 } ret_addr;
   79 
   80 int read_stopped = False;
   81 int write_stopped = False;
   82 
   83 int tt_width;
   84 int tt_length;
   85 int tt_changed;
   86 int tt_pasting=False;         /* drm */
   87 int tt_new_output=False;      /* Cleared by flushlog(), set whenever something new
   88    goes to the screen through tt_write */
   89 
   90 int trnlnm(char *in,int id,char *out);
   91 void spawn (void);
   92 
   93 static void tt_echo_ast(TT_BUF_STRUCT *buff_addr);
   94 static void tt_read_ast(TT_BUF_STRUCT *buff_addr);
   95 
   96 /*
   97 static void tt_start_read(void);
   98 */
   99 void tt_start_read(void);
  100 int tt_read(char *buffer);
  101 static void send_xon(void);
  102 static void send_xoff(void);
  103 static void send_bell(void);
  104 static void char_change(void);
  105 static void freeBuff (TT_BUF_STRUCT *buff_addr);
  106 TT_BUF_STRUCT *getBuff(void);
  107 static void CloseDown(int exit_status);
  108 static void mbx_read_ast(void);
  109 static void mbx_read(void);
  110 
  111 
  112 
  113 #define DESCRIPTOR(name,string) struct dsc$descriptor_s name = \
  114 { strlen(string), DSC$K_DTYPE_T, DSC$K_CLASS_S, string }
  115 
  116 int trnlnm(char *in, int id, char *out)
  117 {
  118   int status, num, len, attr = LNM$M_CASE_BLIND, foo = id;
  119   short outlen;
  120   struct itemlist
  121     {
  122       short buffer_length;
  123       short item_code;
  124       char  *buffer_addr;
  125       int   *return_length;
  126     } itmlst[] =
  127       {
  128     4  , LNM$_INDEX    , &foo, 0,
  129     255, LNM$_STRING   , out , &outlen,
  130     4  , LNM$_MAX_INDEX, &num, &len,
  131     0  , 0
  132     };
  133   DESCRIPTOR(lognam,in);
  134   DESCRIPTOR(tabnam,"LNM$DCL_LOGICAL");
  135 
  136   status = sys$trnlnm(&attr,&tabnam,&lognam,0,itmlst);
  137   if(status != SS$_NORMAL) return(-1);   /* error status */
  138   out[outlen] = 0;         /* terminate the output string */
  139   return(++num);         /* return number of translations */
  140 }
  141 
  142 static int           pty;
  143 static int           Xsocket;
  144 
  145 void spawn (void)
  146 {
  147   int                  status;
  148   static $DESCRIPTOR   (dtime, "0 00:00:00.01");
  149   static int           delta[2];
  150   register TScreen     *screen = TScreenOf(term);
  151   static struct IOSB   iosb;
  152   static unsigned int  flags;
  153   static unsigned int  uic;
  154   static char          imagename[64];
  155   static int           privs;
  156   static $DESCRIPTOR(device, "FTA0:");
  157   static int           type;
  158   static int           class;
  159   static int           devdepend;
  160   static int           mem_size;
  161   int                  i;
  162 
  163   /* if pid and mbx_chan are nonzero then close them in CloseDown() */
  164   pid = 0;
  165   mbx_chan = 0;
  166 
  167   status = SYS$EXPREG (BUFFERS, &ret_addr, 0, 0);
  168   if(!(status & SS$_NORMAL)) lib$signal(status);
  169 
  170   tt_w_buff = (char *)ret_addr.end - PAGE + 1;
  171 
  172   /* use one buffer for writing, the reset go in the free buffer queue */
  173   for(i=0; i < BUFFERS-1; i++)
  174     {
  175       freeBuff((char *)ret_addr.start +i*PAGE);
  176     }
  177 
  178   /* avoid double MapWindow requests, for wm's that care... */
  179   XtSetMappedWhenManaged( screen->TekEmu ? XtParent(tekWidget) :
  180              XtParent(term), False );
  181   /* Realize the Tek or VT widget, depending on which mode we're in.
  182      If VT mode, this calls VTRealize (the widget's Realize proc) */
  183   XtRealizeWidget (screen->TekEmu ? XtParent(tekWidget) :
  184            XtParent(term));
  185 
  186   /* get the default device characteristics of the pseudo terminal */
  187 
  188   itemlist[0].buflen      = 4;
  189   itemlist[0].code        = DVI$_DEVTYPE;
  190   itemlist[0].buffer      = &type;
  191   itemlist[0].return_addr = &tt_name_desc.dsc$w_length;
  192 
  193   itemlist[1].buflen      = 4;
  194   itemlist[1].code        = DVI$_DEVCLASS;
  195   itemlist[1].buffer      = &class;
  196   itemlist[1].return_addr = &tt_name_desc.dsc$w_length;
  197 
  198   itemlist[2].buflen      = 4;
  199   itemlist[2].code        = DVI$_DEVDEPEND;
  200   itemlist[2].buffer      = &devdepend;
  201   itemlist[2].return_addr = &tt_name_desc.dsc$w_length;
  202 
  203   itemlist[3].buflen      = 4;
  204   itemlist[3].code        = DVI$_DEVDEPEND2;
  205   itemlist[3].buffer      = &tt_chars.extended;
  206   itemlist[3].return_addr = &tt_name_desc.dsc$w_length;
  207 
  208   itemlist[4].buflen      = 0;
  209   itemlist[4].code        = 0;
  210 
  211 
  212   status = sys$getdviw(0,0,&device,&itemlist,&iosb,0,0,0);
  213   if(!(status & SS$_NORMAL)) lib$signal(status);
  214   if(!(iosb.status & SS$_NORMAL)) lib$signal(iosb.status);
  215 
  216   tt_chars.type        = DT$_VT102; /* XTerm supports VT102 mode */
  217   tt_chars.class       = class;
  218   tt_chars.page_width  = screen->max_col+1;
  219   tt_chars.length      = screen->max_row+1;
  220 
  221   /* copy the default char's along with the created window size */
  222 
  223   bcopy(&devdepend, &tt_chars.characteristics, 3);
  224 
  225   tt_chars.extended |= TT2$M_ANSICRT | TT2$M_AVO | TT2$M_DECCRT;
  226 
  227 
  228   /* create the pseudo terminal with the proper char's */
  229   status = ptd$create(&tt_chan,0,&tt_chars,12,0,0,0,&ret_addr);
  230   if(!(status & SS$_NORMAL)) lib$signal(status);
  231 
  232 
  233   /* get the device name of the Pseudo Terminal */
  234 
  235   itemlist[0].buflen      = 64;
  236   itemlist[0].code        = DVI$_DEVNAM;
  237   itemlist[0].buffer      = &tt_name;
  238   itemlist[0].return_addr = &tt_name_desc.dsc$w_length;
  239 
  240   /* terminate the list */
  241   itemlist[1].buflen      = 0;
  242   itemlist[1].code        = 0;
  243 
  244   status = sys$getdviw(0,tt_chan,0,&itemlist,&iosb,0,0,0);
  245   if(!(status & SS$_NORMAL)) CloseDown(status);
  246   if(!(iosb.status & SS$_NORMAL)) CloseDown(iosb.status);
  247 
  248   /*
  249    * set up AST's for XON, XOFF, BELL and characteristics change.
  250    */
  251 
  252   status = ptd$set_event_notification(tt_chan,&send_xon,0,0,PTD$C_SEND_XON);
  253   if(!(status & SS$_NORMAL)) CloseDown(status);
  254 
  255   status = ptd$set_event_notification(tt_chan,&send_xoff,0,0,PTD$C_SEND_XOFF);
  256   if(!(status & SS$_NORMAL)) CloseDown(status);
  257 
  258   status = ptd$set_event_notification(tt_chan,&send_bell,0,0,PTD$C_SEND_BELL);
  259   if(!(status & SS$_NORMAL)) CloseDown(status);
  260 
  261   status = ptd$set_event_notification(tt_chan,&char_change,0,0,PTD$C_CHAR_CHANGED);
  262   if(!(status & SS$_NORMAL)) CloseDown(status);
  263 
  264   /* create a mailbox for the detached process to detect hangup */
  265 
  266   status = sys$crembx(0,&mbx_chan,ACC$K_TERMLEN,0,255,0,0);
  267   if(!(status & SS$_NORMAL)) CloseDown(status);
  268 
  269 
  270   /*
  271    * get the device unit number for created process completion
  272    * status to be sent to.
  273    */
  274 
  275   itemlist[0].buflen      = 4;
  276   itemlist[0].code        = DVI$_UNIT;
  277   itemlist[0].buffer      = &mbxunit;
  278   itemlist[0].return_addr = 0;
  279 
  280   /* terminate the list */
  281   itemlist[1].buflen      = 0;
  282   itemlist[1].code        = 0;
  283 
  284   status = sys$getdviw(0,mbx_chan,0,&itemlist,&iosb,0,0,0);
  285   if(!(status & SS$_NORMAL)) CloseDown(status);
  286   if(!(iosb.status & SS$_NORMAL)) CloseDown(iosb.status);
  287 
  288 
  289   tt_start_read();
  290 
  291   /*
  292    * find the current process's UIC so that it can be used in the
  293    * call to sys$creprc
  294    */
  295   itemlist[0].buflen = 4;
  296   itemlist[0].code = JPI$_UIC;
  297   itemlist[0].buffer = &uic;
  298   itemlist[0].return_addr = 0;
  299 
  300   /* terminate the list */
  301   itemlist[1].buflen      = 0;
  302   itemlist[1].code        = 0;
  303 
  304   status = sys$getjpiw(0,0,0,&itemlist,0,0,0);
  305   if(!(status & SS$_NORMAL)) CloseDown(status);
  306 
  307   /* Complete a descriptor for the WS (DECW$DISPLAY) device */
  308 
  309   trnlnm("DECW$DISPLAY",0,ws_name);
  310   ws_name_desc.dsc$w_length = strlen(ws_name);
  311 
  312   /* create the process */
  313   /*  Set sys$error to be the WS (DECW$DISPLAY) device. LOGINOUT  */
  314   /*  has special code for DECWINDOWS that will:                  */
  315   /*    1) do a DEFINE/JOB DECW$DISPLAY 'f$trnlnm(sys$error)'     */
  316   /*    2) then redefine SYS$ERROR to match SYS$OUTPUT!           */
  317   /*  This will propagate DECW$DISPLAY to the XTERM process!!!    */
  318   /*  Thanks go to Joel M Snyder who posted this info to INFO-VAX */
  319 
  320   flags = PRC$M_INTER | PRC$M_NOPASSWORD | PRC$M_DETACH;
  321   status = sys$creprc(&pid,&image,&tt_name_desc,&tt_name_desc,
  322               &ws_name_desc,0,0,0,4,uic,mbxunit,flags);
  323   if(!(status & SS$_NORMAL)) CloseDown(status);
  324 
  325 
  326   /* hang a read on the mailbox waiting for completion */
  327   mbx_read();
  328 
  329 
  330 /* set time value and schedule a periodic wakeup (every 1/100 of a second)
  331  * this is used to prevent the controlling process from using up all the
  332  * CPU.  The controlling process will hibernate at strategic points in
  333  * the program when it is just waiting for input.
  334  */
  335 
  336   status = sys$bintim(&dtime,&delta);
  337   if (!(status & SS$_NORMAL)) CloseDown(status);
  338 
  339   status = sys$schdwk(0,0,&delta,&delta);
  340   if (!(status & SS$_NORMAL)) CloseDown(status);
  341 
  342 
  343   /*
  344    * This is rather funky, but it saves me from having to totally
  345    * rewrite some parts of the code (namely in_put in module CHARPROC.C)
  346    */
  347   pty = 1;
  348   screen->respond = pty;
  349   pty_mask = 1 << pty;
  350   Select_mask = pty_mask;
  351   X_mask = 1 << Xsocket;
  352 
  353 }
  354 
  355 
  356 /*
  357  * This routine handles completion of write with echo.  It takes the
  358  * echo buffer and puts it on the read queue.  It will then be processed
  359  * by the routine tt_read.  If the echo buffer is empty, it is put back
  360  * on the free buffer queue.
  361  */
  362 
  363 static void tt_echo_ast(TT_BUF_STRUCT *buff_addr)
  364 {
  365   int status;
  366 
  367   if (buff_addr->length != 0)
  368     {
  369       status = LIB$INSQTI(buff_addr, &read_queue);
  370       if((status != SS$_NORMAL) && (status != LIB$_ONEENTQUE))
  371     {
  372       CloseDown(status);
  373     }
  374     }
  375   else
  376     {
  377       freeBuff(buff_addr);
  378     }
  379 }
  380 
  381 
  382 /*
  383  * This routine writes to the pseudo terminal.  If there is a free
  384  * buffer then write with an echo buffer completing asynchronously, else
  385  * write synchronously using the buffer reserved for writing.  All errors
  386  *  are fatal, except DATAOVERUN and DATALOST,these errors can be ignored.
  387 
  388  CAREFUL! Whatever calls this must NOT pass more than VMS_TERM_BUFFER_SIZE
  389  bytes at a time.  This definition has been moved to VMS.H
  390 
  391  */
  392 
  393 int tt_write(const char *tt_write_buf, size_t size)
  394 {
  395   int status;
  396   TT_BUF_STRUCT *echoBuff;
  397 
  398   /* if writing stopped, return 0 until Xon */
  399   if(write_stopped) return (0);
  400 
  401   memmove(&tt_w_buff->data,tt_write_buf,size);
  402 
  403   echoBuff = getBuff();
  404   if (echoBuff != LIB$_QUEWASEMP)
  405     {
  406       status = PTD$WRITE (tt_chan, &tt_echo_ast, echoBuff,
  407               &tt_w_buff->status, size,
  408               &echoBuff->status, VMS_TERM_BUFFER_SIZE);
  409     }
  410   else
  411     {
  412       status = PTD$WRITE (tt_chan, 0, 0, &tt_w_buff->status, size, 0, 0);
  413     }
  414   if (status & SS$_NORMAL)
  415     {
  416       if ((tt_w_buff->status != SS$_NORMAL) &&
  417       (tt_w_buff->status != SS$_DATAOVERUN) &&
  418       (tt_w_buff->status != SS$_DATALOST))
  419     {
  420       CloseDown(tt_w_buff->status);
  421     }
  422     }
  423   else
  424     {
  425       CloseDown(status);
  426     }
  427 
  428   return(size);
  429 }
  430 
  431 
  432 /*
  433  * This routine is called when a read to the pseudo terminal completes.
  434  * Put the newly read buffer onto the read queue.  It will be processed
  435  * and freed in the routine tt_read.
  436  */
  437 
  438 static void tt_read_ast(TT_BUF_STRUCT *buff_addr)
  439 {
  440   int status;
  441 
  442   if (buff_addr->status & SS$_NORMAL)
  443     {
  444       status = LIB$INSQTI(buff_addr, &read_queue);
  445       if ((status != SS$_NORMAL) && (status != LIB$_ONEENTQUE))
  446     {
  447       CloseDown(status);
  448     }
  449     }
  450   else
  451     CloseDown(buff_addr->status);
  452 
  453   tt_start_read();
  454   sys$wake(0,0);
  455   return;
  456 }
  457 
  458 
  459 /*
  460  * If there is a free buffer on the buffer queue then Start a read from
  461  * the pseudo terminal, otherwise set a flag, the reading will be restarted
  462  * in the routine freeBuff when a buffer is freed.
  463  */
  464 
  465 void tt_start_read(void)
  466 {
  467   int status;
  468   static int size;
  469   TT_BUF_STRUCT *buff_addr;
  470 
  471   buff_addr = getBuff();
  472   if (buff_addr != LIB$_QUEWASEMP)
  473     {
  474       if(!tt_pasting){
  475       status = PTD$READ (0, tt_chan, &tt_read_ast, buff_addr,
  476              &buff_addr->status, VMS_TERM_BUFFER_SIZE);
  477       if ((status & SS$_NORMAL) != SS$_NORMAL)
  478     {
  479       CloseDown(status);
  480     }
  481       }
  482       }
  483   else
  484     {
  485       read_stopped = True;
  486     }
  487   return;
  488 }
  489 
  490 
  491 /*
  492  * Get data from the pseudo terminal.  Return the data from the first item
  493  * on the read queue, and put that buffer back onto the free buffer queue.
  494  * Return the length or zero if the read queue is empty.
  495  *
  496  */
  497 
  498 int tt_read(char *buffer)
  499 {
  500   TT_BUF_STRUCT *read_buff;
  501   int status;
  502   int len;
  503 
  504    status = LIB$REMQHI(&read_queue, &read_buff);
  505    if(status == LIB$_QUEWASEMP){
  506      return(0);
  507    }
  508    else if (status & SS$_NORMAL)
  509      {
  510        len = read_buff->length;
  511        memmove(buffer,&read_buff->data,len);
  512        freeBuff(read_buff);
  513        tt_new_output=True; /* DRM something will be written */
  514      }
  515    else
  516      CloseDown(status);
  517 
  518    return(len);
  519 }
  520 
  521 
  522 /*
  523  * if xon then it is safe to start writing again.
  524  */
  525 
  526 static void send_xon(void)
  527 {
  528   write_stopped = False;
  529 }
  530 
  531 
  532 /*
  533  * If Xoff then stop writing to the pseudo terminal until you get Xon.
  534  */
  535 static void send_xoff(void)
  536 {
  537   write_stopped = True;
  538 }
  539 
  540 
  541 
  542 /*
  543  * Beep the terminal to let the user know data will be lost because
  544  * of too much data.
  545  */
  546 
  547 static void send_bell(void)
  548 {
  549    Bell(term);
  550 }
  551 
  552 /*
  553  * if the pseudo terminal's characteristics change, check to see if the
  554  * page size changed.  If it did, resize the widget, otherwise, ignore
  555  * it!  This routine just gets the new term dimensions and sets a flag
  556  * to indicate the term chars have changed.  The widget gets resized in
  557  * the routine in_put in the module CHARPROC.C.  You can't resize the
  558  * widget in this routine because this is an AST and X is not reenterent.
  559  */
  560 
  561 static void char_change(void)
  562 {
  563   int status;
  564 
  565   /*
  566    * Don't do anything if in Tek mode
  567    */
  568 
  569   if(!(TScreenOf(term)->TekEmu))
  570     {
  571       status = sys$qiow(0,tt_chan,IO$_SENSEMODE,0,0,0,&tt_mode,8,0,0,0,0);
  572       if(!(status & SS$_NORMAL)) CloseDown(status);
  573 
  574       if((TScreenOf(term)->max_row != tt_mode.length) ||
  575      (TScreenOf(term)->max_col != tt_mode.page_width))
  576     {
  577       tt_length = tt_mode.length;
  578       tt_width =  tt_mode.page_width;
  579 
  580       tt_changed = True;
  581 
  582     }
  583     }
  584 }
  585 
  586 
  587 /*
  588  * Put a free buffer back onto the buffer queue.  If reading was
  589  * stopped for lack of free buffers, start reading again.
  590  */
  591 
  592 static void freeBuff (TT_BUF_STRUCT *buff_addr)
  593 {
  594   int ast_stat;
  595   int status;
  596 
  597   ast_stat = SYS$SETAST(0);
  598   if (!read_stopped)
  599     {
  600       LIB$INSQHI(buff_addr, &buffer_queue);
  601     }
  602   else
  603     {
  604       status = PTD$READ (0, tt_chan, &tt_read_ast, buff_addr,
  605              &buff_addr->status, VMS_TERM_BUFFER_SIZE);
  606       if (status & SS$_NORMAL)
  607     {
  608       read_stopped = False;
  609     }
  610       else
  611     {
  612       CloseDown(status);
  613     }
  614     }
  615   if (ast_stat == SS$_WASSET) ast_stat = SYS$SETAST(1);
  616 }
  617 
  618 
  619 /*
  620  * return a free buffer from the buffer queue.
  621  */
  622 
  623 TT_BUF_STRUCT *getBuff(void)
  624 {
  625   int status;
  626   TT_BUF_STRUCT *buff_addr;
  627 
  628   status = LIB$REMQHI(&buffer_queue, &buff_addr);
  629   if (status & SS$_NORMAL)
  630     {
  631       return(buff_addr);
  632     }
  633   else
  634     {
  635       return(status);
  636     }
  637 }
  638 
  639 
  640 /*
  641  * Close down and exit.  Kill the detached process (if it still
  642  * exists), deassign mailbox channell (if assigned), cancel any
  643  * waiting IO to the pseudo terminal and delete it, exit with any
  644  * status information.
  645  */
  646 
  647 static void CloseDown(int exit_status)
  648 {
  649   int status;
  650 
  651   /* if process has not terminated, do so now! */
  652   if(pid != 0)
  653     {
  654       status = sys$forcex(&pid,0,0);
  655       if(!(status & SS$_NORMAL)) lib$signal(status);
  656     }
  657 
  658   /* if mbx_chan is assigned, deassign it */
  659   if(mbx_chan != 0)
  660     {
  661       sys$dassgn(mbx_chan);
  662     }
  663 
  664   /* cancel pseudo terminal IO requests */
  665   status = ptd$cancel(tt_chan);
  666   if(!(status & SS$_NORMAL)) lib$signal(status);
  667 
  668   /* delete pseudo terminal */
  669   status = ptd$delete(tt_chan);
  670   if(!(status & SS$_NORMAL)) lib$signal(status);
  671 
  672   if(!(exit_status & SS$_NORMAL)) lib$signal(exit_status);
  673 
  674   exit(1);
  675 
  676 }
  677 
  678 
  679 /*
  680  * This routine gets called when the detached process terminates (for
  681  * whatever reason).  The mailbox buffer has final exit status.  Close
  682  * down and exit.
  683  */
  684 
  685 static void mbx_read_ast(void)
  686 {
  687   int status;
  688 
  689   pid = 0;
  690 
  691   status = mbx_read_iosb.status;
  692   if (!(status & SS$_NORMAL)) CloseDown(status);
  693 
  694   status = (unsigned long int) mbx_buf.acc$l_finalsts;
  695   if (!(status & SS$_NORMAL)) CloseDown(status);
  696 
  697   CloseDown(1);
  698 
  699 }
  700 
  701 
  702 /*
  703  * This routine starts a read on the mailbox associated with the detached
  704  * process.  The AST routine gets called when the detached process terminates.
  705  */
  706 
  707 static void mbx_read(void)
  708 {
  709 int status;
  710 static int size;
  711 
  712    size = ACC$K_TERMLEN;
  713    status = sys$qio(0,mbx_chan,
  714           IO$_READVBLK,
  715           &mbx_read_iosb,
  716           &mbx_read_ast,
  717           0,
  718           &mbx_buf,
  719           size,0,0,0,0);
  720 
  721    if (!(status & SS$_NORMAL)) CloseDown(status);
  722 
  723    return;
  724 }