From:	CSBVAX::MRGATE!KARNEY%PPC.MFENET@NMFECC.ARPA@SMTP 30-MAR-1988 19:08
To:	ARISIA::EVERHART
Subj:	BOSS 1.4 2/2


Received: from ppc.mfenet by ccc.mfenet with Tell via MfeNet ;
	Wed, 30 Mar 88 15:31:32 PST
Date:	  Wed, 30 Mar 88 15:31:32 PST
From:     KARNEY%PPC.MFENET@NMFECC.ARPA
Message-Id: <880330153132.20c0021d@NMFECC.ARPA>
Subject:   BOSS 1.4 2/2
To:       Everhart%Arisia.decnet@ge-crd.arpa
Comment: From KARNEY@PPC.MFENET on 30-MAR-1988 18:26:55.87 EST

$Part2:
$ File_is="BOSS.C"
$ Check_Sum_is=1662134782
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X/* BOSS interactive job controller.
XCopyright (c) 1987, 1988 by Charles Karney.  All rights reserved.
X
XVersion 1.0.  August 22, 1987
XVersion 1.1.  August 27, 1987
X    Add BOSS$ID, BOSS$SWITCH, BOSS$STUFF.
XVersion 1.2.  September 30, 1987
X    Stop PHY_IO being inherited by subprocesses.
XVersion 1.3.  March 17, 1988
X  C-b buffer output
X  C-p proceed (output comes through regardless)
X  C-o suppress output (but save the last write)
X  C-s hang output
XVersion 1.4.  March 23, 1988
X  Make BOSS$SWITCH switching set output flag to s (for synchronism)
X  Default output flag is b
XStill to do:
X  C-l toggle output to log file sys$scratch:boss-x.log
X  Accept command line args:
X    /CONTROL_CHAR=char - set control character
X    /START=list - start up specified jobs
X    /STUFF=list - and stuff their input buffers
X    /OUTPUT_FLAG=list - and set output flags accordingly
X    /PROMPTBEG=string - part of prompt appearing before id letter
X    /PROMPTEND=string - part of prompt appearing after id letter
X    /DEFAULT_OUTPUT_FLAG=char - set default output flag
X
XWritten by
X    Charles Karney
X    Plasma Physics Laboratory   Phone:   609/243-2607
X    Princeton University        MFENet:  Karney@PPC.MFENET
X    PO Box 451                  ARPANet: Karney%PPC.MFENET@NMFECC.ARPA
X    Princeton, NJ 08543-0451    Bitnet:  Karney%PPC.MFENET@ANLVMS.BITNET
X
XBased on the PHOTO program of Asbed Bedrossian (USC, asbed@oberon.usc.edu).
X
XIt utilizes the Pseudo TTY package of Dale Moore (CMU,
XDale.Moore@PS1.CS.CMU.EDU) and Kevin Carosso (Hughes Aircraft Co.,
Xkvc@engvax.scg.hac.com, kvc%engvax@oberon.usc.edu).
X
XBOSS lets you create up to 8 processes each of which is identified by a
Xsingle letter (A thru Z).  This letter is used in the process name and in
Xthe DCL prompt.  At most one of these processes is ``current'.  This means
Xthat what you type is sent to that process, and output from that process is
Xsent to your terminal.  A process which is not current can run but cannot do
Xoutput.  (Output is saved until you make that process current.)
X
XCompile and link with
X    $ cc boss
X    $ link/notraceback boss,sys$library:vaxcrtl/lib
X
XInstall with
X    $ install :== $install/command_mode
X    $ if  f$file("usr:[utility]boss.exe","known") then -
X        install delete usr:[utility]boss
X    $ install create usr:[utility]boss/priv=phy_io
XThis needs to go in your system startup file.
X*/
X
X#include DESCRIP
X#include IODEF
X#include TTDEF
X#include TT2DEF
X#include JPIDEF
X#include LNMDEF
X#include PRVDEF
X#include stdio
X
X#define  ctlchar        28      /* Control character ^\ */
X#define  ttchrlen       12
X#define  tpdevlen       15
X#define  mbsiz          40
X#define  maxsiz         80
X#define  imagelen       80
X#define  linesz        512
X#define  bufsize      4096`009/* Size of output buffers */
X#define  nproc           8      /* Number of process allowed */
X#define  nalph          26      /* Number of possible names */
X#define  clr     "\033[r\033[H\033[2J" /* Clear screen reset scroll */
X#define  bos     "\033[r\033[99;1H\n" /* Go to bottom of screen */
X#define  ceol    "\033[K"       /* Clear to end-of-line */
X#define  ceoln   "\033[K\r\n"   /* Clear to end-of-line and newline */
X#define  bad(j)         !(j & 1)
X#define  check(a)       st=a; if (bad(st)) LIB$SIGNAL(st)
X
Xstruct CHARBLK {
X  unsigned char class, ttype;
X  unsigned short pgwid;
X  unsigned ttchr : 24;
X  unsigned char pglen;
X  unsigned int xchar;
X};
X
Xstruct IOSBBLK {
X  unsigned short stats, tmoff, tmntr, tmsiz;
X};
X
Xint
X  py_chn[nproc], py_mb_chn[nproc], tt_chn, tt_mb_chn,
X  pid[nproc], st, cur, count[nproc], buflen[nproc], 
X  procno[nalph], priv[2], no_phy_io;
X
Xchar
X  buf[100],image[imagelen],
X  finaltp[nproc][tpdevlen],
X  py_mb[nproc][mbsiz], tt_mb[mbsiz],
X  input_char, tpline[nproc][linesz], buffer[nproc][bufsize],
X  blocked[nproc], mode[nproc], pmode[nproc], defmode,
X  name[nproc], py_post[nproc],
X  pending = 0, quit_pending = 0, oboss_id = 0;
X
Xstruct CHARBLK tt_chr, tt_sav_chr;
Xstruct IOSBBLK tiosb, piosb[nproc], miosb[nproc];
X
Xstatic short trnlnm_string_len;
Xstatic char trnlnm_string[81];
X
Xstruct ITEM_LST {
X`009unsigned short len, code; char *addr; short *retlen;
X  } trnlnm_item = {80, LNM$_STRING, &trnlnm_string, &trnlnm_string_len};
X
Vquit()                         /* This is done upon exiting, by exit handler *
X/
X{
X  int i,j;
X  char id[2];
X
X  $DESCRIPTOR(d_boss_id,"BOSS$ID");
X  $DESCRIPTOR(d_id, id);
X  if (oboss_id != 0) {`009`009/* Restore BOSS$ID */
X    id[0] = oboss_id;
X    j = LIB$SET_LOGICAL(&d_boss_id,&d_id,0,0,0);
X  }
X
X  for (i = 0; i < nproc; i++) {
X    if (name[i]) {
X      j = SYS$DELMBX(py_mb_chn[i]);
X      if (bad(j))
X        printf("[SYS$DELMBX pseudo-mbx deletion failed]\n");
X    }
X  }
X  j = SYS$QIOW(0,tt_chn,IO$_SETMODE,0,0,0,&tt_sav_chr,ttchrlen,0,0,0,0);
X  if (bad(j)) printf("[SYS$QIO /setmode/ failed]\n");
X  printf("\nEnd BOSS\n");
X}
X
Xcomp_srv(n)                     /* AST for completion of processes */
Xint n;
X{
X  int j;
X
X  j = SYS$DELMBX(py_mb_chn[n]);
X  if (name[n]) procno[name[n] - 'A'] = -1;
X  name[n] = '\0';
X  if (cur == n) {
X    cur = -1;
X    pending = 1;
X  }
X}
X
Xint low_lib_spawn(n,pty_io,pid,name)
V                           /* Spawns subprocess to speaks to pseudo terminal *
X/
Xchar *pty_io, name;
Xint  n, *pid;
X{
X  int flg = 1, len;
X  char proc[20],prompt[4],id[2];
X  $DESCRIPTOR(d_pty_io, pty_io); /* PTY name + number */
X  $DESCRIPTOR(d_proc, proc);   /* Process name */
X  $DESCRIPTOR(d_prompt, prompt); /* Prompt */
X  $DESCRIPTOR(d_boss_id,"BOSS$ID");
X  $DESCRIPTOR(d_id, id); /* The id */
X  d_pty_io.dsc$w_length = strlen(pty_io);
X  strcpy(proc,getenv("TT"));
X  len = strlen(proc);
X  if (proc[len-1] == ':') proc[--len] = '\0';
X  strcat(proc,"-A");
X  len = strlen(proc);
X  proc[len-1] = name;
X  d_proc.dsc$w_length= len;
X  if (proc[0] == '_') {
X    d_proc.dsc$w_length--;
X    d_proc.dsc$a_pointer++;
X  }
X  strcpy(prompt,"A> ");
X  prompt[0] = name;
X  d_prompt.dsc$w_length = strlen(prompt);
X  id[0] = name;
X  check(LIB$SET_LOGICAL(&d_boss_id,&d_id,0,0,0));
X  st = LIB$SPAWN(0,&d_pty_io,&d_pty_io,&flg,&d_proc,pid,0,0,
X                &comp_srv,n,&d_prompt,0);
X  check(LIB$DELETE_LOGICAL(&d_boss_id,0));
X  return(st);
X}
X
Xpy_srv(n)                       /* AST reads on pseudo terminal */
Xint n;
X{
X  int j;
X  py_post[n] = 0;
X  check(piosb[n].stats);        /* Check status */
X  count[n] = piosb[n].tmoff + piosb[n].tmsiz; /* How much was read */
X  if (n == cur || mode[n] == 'p') term_out(n);
X`009`009`009`009/* Write the stuff to the terminal */
X  else if (mode[n] == 's') {
X    blocked[n] = 1;
X  }
X  else if (mode[n] == 'o') {
X    blocked[n] = 1;
X    check(SYS$QIO(0,py_chn[n],IO$_READVBLK,&piosb[n],&py_srv,n,
X`009`009  &tpline[n],linesz,0,0,0,0)); /* Queue next AST */
X    py_post[n] = 1;
X    }
X  else if (mode[n] == 'b') {
X    if (count[n]+buflen[n] < bufsize) {
X      for (j = 0; j < count[n]; j++) buffer[n][buflen[n]++] = tpline[n][j];
X      check(SYS$QIO(0,py_chn[n],IO$_READVBLK,&piosb[n],&py_srv,n,
X`009`009    &tpline[n],linesz,0,0,0,0)); /* Queue next AST */
X      py_post[n] = 1;
X      blocked[n] = 0;
X    }
X    else {
X      py_post[n] = 0;
X      blocked[n] = 1;
X    }
X  }
X}
X
Xterm_out(n)
Xint n;
X{
X  int j;
X  char nname;
X  $DESCRIPTOR(d_boss_switch,"BOSS$SWITCH");
X  $DESCRIPTOR(d_boss_stuff,"BOSS$STUFF");
X  $DESCRIPTOR(d_lnm_job,"LNM$JOB");
X  if (buflen[n] > 0) {
X    check(SYS$QIOW(1,tt_chn,IO$_WRITEVBLK,&tiosb,0,0,
X`009`009  &buffer[n],buflen[n],0,0,0,0));
X    buflen[n] = 0;
X    if (blocked[n]) {
X      check(SYS$QIOW(1,tt_chn,IO$_WRITEVBLK,&tiosb,0,0,
X`009`009    &tpline[n],count[n],0,0,0,0));
X    }
X  }
X  else {
X    check(SYS$QIOW(1,tt_chn,IO$_WRITEVBLK,&tiosb,0,0,
X`009`009  &tpline[n],count[n],0,0,0,0));
X  }
X  j = SYS$TRNLNM(0,&d_lnm_job,&d_boss_switch,0,&trnlnm_item);
X  if (!bad(j) && trnlnm_string_len == 1) {
X    j = LIB$DELETE_LOGICAL(&d_boss_switch,&d_lnm_job);
X    nname = toupper(trnlnm_string[0]);
X    j = SYS$TRNLNM(0,&d_lnm_job,&d_boss_stuff,0,&trnlnm_item);
X    if (!bad(j)) {
X      j = LIB$DELETE_LOGICAL(&d_boss_stuff,&d_lnm_job);
X      trnlnm_string[trnlnm_string_len] = '\0';
X    } else {
X      trnlnm_string[0] = '\0';
X    }
X    if (nname >= 'A' && nname <= 'Z') {
X      mode[n] = 's';
X      mov_to(nname, 0, trnlnm_string);
X    }
X  }
X  if (py_post[n] == 0) {
X    check(SYS$QIO(0,py_chn[n],IO$_READVBLK,&piosb[n],&py_srv,n,
X`009`009  &tpline[n],linesz,0,0,0,0)); /* Queue next AST */
X    py_post[n] = 1;
X    }
X  blocked[n] = 0;
X}
X
Xint count_processes()
X{
X  int j, i = 0;
X
X  for (j = 0; j < nproc; j++) if (name[j] > 0) i++;
X  return(i);
X}
X
Xdiag()
X{
X  char bufa[8];
X  int j;
X
X  if (count_processes()) {
X    sprintf(buf,"%s[Processes:",bos);
X    for (j = 0; j < nproc; j++) {
X      if (name[j] > 0) {
X        if (j == cur) sprintf(bufa," %c%c*",name[j],mode[j]);
X        else if (blocked[j]) sprintf(bufa," %c%c+",name[j],mode[j]);
X`009else if (buflen[j] > 0) sprintf(bufa," %c%c-",name[j],mode[j]);
X        else sprintf(bufa," %c%c",name[j],mode[j]);
X        strcat(buf,bufa);
X      }
X    }
X    strcat(buf,"] ");
X    strcat(buf,ceoln);
X  }
X  else sprintf(buf,"%s[No processes] %s",bos,ceoln);
X  term_msg(buf);
X}
X
Xint next_slot()
X{
X  int j = 0;
X  while ((j < nproc) && name[j]) j++;
X  return (j == nproc) ? -1 : j;
X}
X
Xterm_msg(msg)
Xchar *msg;
X{
X  check(SYS$QIOW(0,tt_chn,IO$_WRITEVBLK,0,0,0,msg,strlen(msg),0,0,0,0));
X}
X
Xchar *get_image(pid)           /* Get the image name for a process */
X     int pid;
X{
X  int j, item;
X  short len;
X  char *ptr, *ptra;
X  $DESCRIPTOR(d_image,image);
X  item = JPI$_IMAGNAME;
X  j = LIB$GETJPI(&item,&pid,0,0,&d_image,&len);
X  ptr = &image;
X  if (bad(j)) strcpy(image,"<UNKNOWN>");
X  else {
X    image[len]='\0';
X    if (len == 0) {
X      item = JPI$_CLINAME;
X      j = LIB$GETJPI(&item,&pid,0,0,&d_image,&len);
X      if (bad(j)) strcpy(image,"<UNKNOWN>");
X      else image[len]='\0';
X    }
X    else {
X      if ((ptr = strrchr(image,']'))) ptr++;
X      else ptr = &image;
X      if (ptra = strchr(ptr,'.')) *ptra = '\0';
X    }
X  }
X  return(ptr);
X}
X
Xmov_to(nname, clear, string)`009/* Switch to process */
Xchar nname, *string;
Xint clear;
X{
X  int ncur,len;
X  char *prefix;
X  prefix = clear ? clr : bos;
X  len = strlen(string);
X  if ((cur >= 0) && (name[cur] == nname)) { /* Redundant move */
X    mode[cur] = pmode[cur];
X    if (len == 0) {
X      sprintf(buf,"%s[Already in process %c%c, %s]%s",
X`009      prefix,nname,mode[cur],get_image(pid[cur]),ceoln);
X      term_msg(buf);
X    }
X  }
X  else if ((ncur = procno[nname-'A']) >= 0) { /* Existing proc */
X    cur = ncur;
X    mode[cur] = pmode[cur];
X    sprintf(buf,"%s[Switch to process %c%c, %s]%s",
X`009    prefix,name[cur],mode[cur],get_image(pid[cur]),ceoln);
X    term_msg(buf);
X    if (blocked[cur] || buflen[cur] > 0) term_out(cur);
X  }
X  else if ((ncur = next_slot()) < 0) {
X    if (cur >= 0)
X      sprintf(buf,"%s[No process slots left--still in %c%c]%s",
X`009      bos,name[cur],mode[cur],ceoln);
X    else sprintf(buf,"%s[No process slots left]%s",bos,ceoln);
X    term_msg(buf);
X    len = 0;
X  }
X  else {
X    sprintf(buf,"%s[Starting process %c...%s",prefix,nname,ceol);
X    term_msg(buf);
X    if (bad(fire_up(ncur,nname))) {
X      if (cur >= 0)
X`009sprintf(buf,"failed!!\007--still in %c%c]%s",name[cur],mode[cur],
X`009`009ceoln);
X      else
X`009sprintf(buf,"failed!!\007]%s",ceoln);
X      term_msg(buf);
X      len = 0;
X    } else {
X      sprintf(buf,"done; now in process %c%c, %s]%s\r",
X`009      nname,mode[ncur],get_image(pid[ncur]),ceol);
X      term_msg(buf);
X      cur = ncur;
X      if (blocked[cur]) term_out(cur);
X    }
X  }
X  if (len > 0) {
X    check(SYS$QIOW(0,py_chn[cur],IO$_WRITEVBLK,&tiosb,0,0,
X                   string,len,0,0,0,0));
X    check(tiosb.stats);
X  }
X}
X
Xtt_srv()                        /* AST: Read on real terminal */
X{
X  int i;
X  char post, nname, nmode, *desc;
X
X  check(tiosb.stats);
X                                /* Read everything typed right away */
X  post = 1;
X  if (quit_pending) {
X    if (toupper(input_char) == 'Y') {
X      term_msg(" Yes\r");
X      exit(1);
X    } else {
X      post = 0;
X      pending = cur < 0;
X      quit_pending = 0;
X      term_msg(" No\r\n");
X    }
X  }
X  else if (pending) {
X    post = 0;
X    if (input_char == '\032') { /* Ctrl-Z quits */
X      if (count_processes()) {
V        sprintf(buf,"%s[Do you really want to quit (y or n)?]%s\007",bos,ceol)
X;
X        term_msg(buf);
X        quit_pending = 1;
X      }
X      else exit(1);
X    }
X    else if (input_char == '\010') {
X      sprintf(buf,"%sBOSS commands are preceded by C-\\ (control-\\).  \
XThe commands are:%s",bos,ceoln);
X      term_msg(buf);
X      sprintf(buf,"\r    C-h         This message%s",ceoln);
X      term_msg(buf);
X      sprintf(buf,"\r    C-z         Quit%s",ceoln);
X      term_msg(buf);
X      sprintf(buf,"\r    a thru z    Switch to another process%s",ceoln);
X      term_msg(buf);
X      sprintf(buf,"\r    A thru Z     the same except clear the screen \
Xfirst%s",ceoln);
X      term_msg(buf);
X      sprintf(buf,"\r    ?           List processes (* means current, \
X+/- means waiting for output)%s",ceoln);
X      term_msg(buf);
X      sprintf(buf,"\r    C-b         Buffer output for this process%s",ceoln);
X      term_msg(buf);
V      sprintf(buf,"\r    C-o         Discard output for this process%s",ceoln)
X;
X      term_msg(buf);
X      sprintf(buf,"\r    C-p         Print output from this process%s",ceoln);
X      term_msg(buf);
X      sprintf(buf,"\r    C-s         Stop output from this process%s",ceoln);
X      term_msg(buf);
X      sprintf(buf,"\r    C-\\         Send control-\\ to current \
Xprocess%s",ceoln);
X      term_msg(buf);
X      sprintf(buf,"\rType HELP BOSS for more information.%s",ceoln);
X      term_msg(buf);
X    }
X    else if (input_char == '?') diag();
X    else if (input_char == '\002' || input_char == '\017' ||
X`009     input_char == '\020' || input_char == '\023') {
X      if (input_char == '\002') desc = "Buffer";
X      else if (input_char == '\017') desc = "Discard";
X      else if (input_char == '\020') desc = "Print";
X      else if (input_char == '\023') desc = "Stop";
X      nmode = input_char + 0140;
X      if (cur < 0) {
X`009defmode = nmode;
X`009sprintf(buf,"%s[%s output by default]%s",bos,desc,ceoln);
X      }
X      else {
X`009mode[cur] = nmode;
X`009pmode[cur] = nmode;
X`009sprintf(buf,"%s[%s output from process %c]%s",bos,desc,
X`009`009name[cur],ceoln);
X      }
X      term_msg(buf);
X    }
X    else {
X      nname = toupper(input_char);
X      if (nname >= 'A' && nname <= 'Z') {
X`009mov_to(nname,input_char < 'a',""); /* Clear if upper case */
X      } else {
X        if (input_char != ctlchar) {
X          if (input_char != '\177') term_msg("\007");
X        }
X        else post = 1;
X      }
X    }
X    pending = cur < 0;
X  }
X  else if (input_char == ctlchar) {
X    post = 0;
X    pending = 1;
X  }
X  if ( post && (cur >= 0) ) {  /* write everything we read to pseudo-term */
X    check(SYS$QIOW(0,py_chn[cur],IO$_WRITEVBLK,&tiosb,0,0,
X                   &input_char,1,0,0,0,0));
X    check(tiosb.stats);
X  }
X                               /* re-post read AST on real term */
X  check(SYS$QIO(0,tt_chn,IO$_READVBLK,&tiosb,&tt_srv,0,
X                &input_char,1,0,0,0,0));
X}
X
Xget_tt_info()
X{
X  $DESCRIPTOR(d_tt, "SYS$COMMAND");
X                                /* Get a channel & mailbox of terminal */
X  check(LIB$ASN_WTH_MBX(&d_tt,&mbsiz,&maxsiz,&tt_chn,&tt_mb_chn));
X                                /* Get the terminal characteristics. */
X  check(SYS$QIOW(0,tt_chn,IO$_SENSEMODE,0,0,0,&tt_chr,ttchrlen,0,0,0,0));
X  tt_sav_chr = tt_chr;
X  tt_chr.ttchr |= TT$M_NOECHO;  /* term will be Noecho */
X  tt_chr.ttchr &= ~TT$M_HOSTSYNC & ~TT$M_TTSYNC; /* it will have ^S */
X  tt_chr.xchar |= TT2$M_PASTHRU; /* it will be PASTRHU */
X  check(SYS$QIOW(0,tt_chn,IO$_SETMODE,0,0,0,&tt_chr,ttchrlen,0,0,0,0));
X}
X
Xfix_a_tp(n)                             /* Set up a Pseudo term */
Xint n;
X{
X  int f1[3], tp_chn;
X  struct IOSBBLK iosb;
X
X  $DESCRIPTOR(d_pynam,"PYA0:"); /* Template. */
X  $DESCRIPTOR(d_finaltp, &finaltp[n]);
X                                /* Assign a mailbox to PYA */
X  check(LIB$ASN_WTH_MBX(&d_pynam,&mbsiz,&maxsiz,&py_chn[n],&py_mb_chn[n]));
X                                /* this gives us a TPA number via SENSEMODE */
X  check(SYS$QIOW(0,py_chn[n],IO$_SENSEMODE,&iosb,0,0,&f1,ttchrlen,0,0,0,0));
X  sprintf(&finaltp[n],"TPA%d:",iosb.tmntr); /* see? */
X  d_finaltp.dsc$w_length = strlen(&finaltp[n]);
X  check(SYS$ASSIGN(&d_finaltp,&tp_chn,0,0));
X                                /* Get a channel on this TPA */
X                                /* Make it look like a terminal */
X  if (no_phy_io) check(SYS$SETPRV(1,&priv,0,0));
X  if (bad(SYS$QIOW(0,tp_chn,IO$_SETCHAR,0,0,0, /* This needs PHY_IO priv */
X                   &tt_sav_chr,ttchrlen,0,0,0,0)))
X    check(SYS$QIOW(0,tp_chn,IO$_SETMODE,0,0,0,
X                   &tt_sav_chr,ttchrlen,0,0,0,0));
X  if (no_phy_io) check(SYS$SETPRV(0,&priv,0,0));
X  check(SYS$DASSGN(tp_chn));    /* We don't need it. only the mailbox */
X                                /* in fact keeping it kills us. */
X}
X
Xpost_term_reads()                           /* Read AST on real term */
X{
X  check(SYS$QIO(0,tt_mb_chn,IO$_READVBLK,&tiosb,0,0,
X                &tt_mb,mbsiz,0,0,0,0));
X  check(SYS$QIO(0,tt_chn,IO$_READVBLK,&tiosb,&tt_srv,0,
X                &input_char,1,0,0,0,0));
X}
X
Xpost_pty_reads(n)                          /* Read AST on Pseudo-term */
Xint n;
X{
X  check(SYS$QIO(0,py_mb_chn[n],IO$_READVBLK,&miosb[n],0,0,
X                &py_mb[n],mbsiz,0,0,0,0));
X  check(SYS$QIO(0,py_chn[n],IO$_READVBLK,&piosb[n],&py_srv,n,
X                &tpline[n],linesz,0,0,0,0));
X  py_post[n] = 1;
X}
X
Xint
Xfire_up(n,nname)                /* Fire up subprocess n */
Xint n;
Xchar nname;
X{
X  int st;
X  name[n] = nname;
X  procno[nname - 'A'] = n;
X  count[n] = 0;                 /* Initialize buffer count */
X  blocked[n] = 0;               /* It starts unblocked */
X  py_post[n] = 0;
X  mode[n] = defmode;`009`009/* It starts in blocking mode */
X  pmode[n] = defmode;
X  buflen[n] = 0;
X  fix_a_tp(n);                  /* Set a pseudo terminal by TT info */
X  check(SYS$CANCEL(py_chn[n])); /* Don't need this Half of pseudo-ter */
X  st = low_lib_spawn(n,&finaltp[n],&pid[n],name[n]); /* Spawn a subprocess. */
X  if (!bad(st)) post_pty_reads(n); /* Set up AST */
X  else comp_srv(n);             /* Mark the process as non-existent */
X  return(st);
X}
X
Xinitialize()                    /* Initialize everything */
X{
X  int j,item;
X  $DESCRIPTOR(d_boss_id,"BOSS$ID");
X  $DESCRIPTOR(d_lnm_process,"LNM$PROCESS");
X
X  for (j = 0; j < nproc; j++) name[j] = '\0'; /* Initialize variables */
X  for (j = 0; j < nalph; j++) procno[j] = -1;
X  cur = -1;
X  pending = 1;
X  quit_pending = 0;
X  defmode = 'b';
X`009`009`009`009/* Save old value of BOSS$ID */
X  j = SYS$TRNLNM(0,&d_lnm_process,&d_boss_id,0,&trnlnm_item);
X  if (!bad(j) && trnlnm_string_len == 1) {
X    oboss_id = trnlnm_string[0];
X    j = LIB$DELETE_LOGICAL(&d_boss_id,0);
X  }
X
X  item = JPI$_PROCPRIV;`009`009/* Check whether have PHY_IO */
X  check(LIB$GETJPI(&item,0,0,&priv,0,0));
X  no_phy_io = !(priv[0] & PRV$M_PHY_IO);
X  priv[0] = PRV$M_PHY_IO; priv[1] = 0;
X  if (no_phy_io) check(SYS$SETPRV(0,&priv,0,0));
X
X  get_tt_info();                /* Initialize terminal */
X  post_term_reads();
X}
X
Xmain( )
X{
X  int  exit_handler[4] = {0,quit,0,&st};
X
X  check(SYS$DCLEXH(&exit_handler)); /* Define Exit handler (quit) */
X  printf("Begin BOSS 1.4\nType control-\\ control-h for information\n");
X  initialize();
X  sys$hiber();
X}
$ GoSub Convert_File
$ Exit

[Distribution to more than ten people]
