From:	CSBVAX::MRGATE!@KL.SRI.Com:KARNEY%PPC.MFENET@NMFECC.ARPA@SMTP 25-AUG-1987 15:32
To:	EVERHART
Subj:	BOSS interactive job controller source (part 2 of 2)

Received: from NMFECC.ARPA ([128.115.3.7].#Internet) by KL.SRI.COM with TCP; Tue 25 Aug 87 08:47:58-PDT
Received: from ppc.mfenet by ccx.mfenet with Tell via MfeNet ;
	Tue, 25 Aug 87 08:44:51 PDT
Date:	Tue, 25 Aug 87 08:44:51 PDT
From:     KARNEY%PPC.MFENET@NMFECC.ARPA
Message-Id: <870825084451.00o@NMFECC.ARPA>
Subject: BOSS interactive job controller source (part 2 of 2)
To:       info-vax@KL.SRI.COM
Comment: From KARNEY@PPC.MFENET on 25-AUG-1987 11:12:51.27 EDT

$Part2:
$ File_is="BOSS.C"
$ Check_Sum_is=1078629640
$ Copy SYS$Input VMS_SHAR_DUMMY.DUMMY
X/* BOSS interactive job controller.
X
XVersion 1.0.  August 22, 1987.
X
XWritten by
X    Charles Karney
X    Plasma Physics Laboratory   Phone:   609/683-2607
X    Princeton University        MFENet:  Karney@PPC.MFENET
X    PO Box 451                  ARPANet: Karney%PPC.MFENET@NMFECC.ARPA
X    Princeton, NJ 08544-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 Kevin Carosso
X(Hughes Aircraft Co., kvc@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
X*/
X
X#include DESCRIP
X#include IODEF
X#include TTDEF
X#include TT2DEF
X#include JPIDEF
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  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" /* 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], procno[nalph];
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],
X  blocked[nproc],
X  enable_hangups[nproc], name[nproc],
X  pending = 0, quit_pending = 0;
X
Xstruct CHARBLK tt_chr, tt_sav_chr;
Xstruct IOSBBLK tiosb, piosb[nproc], miosb[nproc];
X
Xquit()                         /* This is done upon exiting, by exit handler */
X{
X  int i,j;
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)
X                           /* Spawns subprocess to speaks to pseudo terminal */
Xchar *pty_io, name;
Xint  n, *pid;
X{
X  int flg = 1, len;
X  char proc[20],prompt[4];
X  $DESCRIPTOR(d_pty_io, pty_io); /* PTY name + number */
X  $DESCRIPTOR(d_proc, proc);   /* Process name */
X  $DESCRIPTOR(d_prompt, prompt); /* Prompt */
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  st = LIB$SPAWN(0,&d_pty_io,&d_pty_io,&flg,&d_proc,pid,0,0,
X                &comp_srv,n,&d_prompt,0);
X  return(st);
X}
X
Xpy_srv(n)                       /* AST reads on pseudo terminal */
Xint n;
X{
X  check(piosb[n].stats);        /* Check status */
X  count[n] = piosb[n].tmoff + piosb[n].tmsiz; /* How much was read */
X  if (n == cur) term_out(n);    /* Write the stuff to the terminal */
X  else blocked[n] = 1;
X}
X
Xterm_out(n)
Xint n;
X{
X  check(SYS$QIO(1,tt_chn,IO$_WRITEVBLK,&tiosb,0,0,
X                &tpline[n],count[n],0,0,0,0));
X  check(SYS$WAITFR(1));         /* for some reason QIOW does not work */
X  check(SYS$QIO(0,py_chn[n],IO$_READVBLK,&piosb[n],&py_srv,n,
X                &tpline[n],linesz,0,0,0,0)); /* Queue next AST */
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*",name[j]);
X        else if (blocked[j]) sprintf(bufa," %c+",name[j]);
X        else sprintf(bufa," %c ",name[j]);
X        strcat(buf,bufa);
X      }
X    }
X    strcat(buf,"] ");
X    strcat(buf,ceoln);
X  }
X  else sprintf(buf,"\r[No processes] %s",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, 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,"<UNAVAIL>");
X  else if (image[0] == ' ') strcpy(image,"DCL");
X  else {
X    if ((ptr = strrchr(image,']'))) ptr++;
X    else ptr = &image;
X    if (ptra = strchr(ptr,'.')) *ptra = '\0';
X  }
X  return(ptr);
X}
X
Xtt_srv()                        /* AST: Read on real terminal */
X{
X  int i, ncur;
X  char post, nname, *prefix;
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()) {
X        sprintf(buf,"%s[Do you really want to quit (y or n)?]%s\007",bos,ceol);
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-\\         Send a real C-\\ to the 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 {
X      nname = toupper(input_char);
X      if (nname >= 'A' && nname <= 'Z') {
X        if (input_char < 'a') prefix = clr; /* Clear if upper case */
X        else prefix = bos;
X        if ((cur >= 0) && (name[cur] == nname)) { /* Redundant move */
X          sprintf(buf,"%s[Already in process %c, %s]%s",
X                  prefix,nname,get_image(pid[cur]),ceoln);
X          term_msg(buf);
X        }
X        else if ((ncur = procno[nname-'A']) >= 0) { /* Existing proc */
X          cur = ncur;
X          sprintf(buf,"%s[Switch to process %c, %s]%s",
X                  prefix,name[cur],get_image(pid[cur]),ceoln);
X          term_msg(buf);
X          if (blocked[cur]) 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]%s",
X                    bos,name[cur],ceoln);
X          else sprintf(buf,"%s[No process slots left]%s",bos,ceoln);
X          term_msg(buf);
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              sprintf(buf,"failed!!\007--still in %c]%s",name[cur],ceoln);
X            else
X              sprintf(buf,"failed!!\007]%s",ceoln);
X            term_msg(buf);
X          } else {
X            sprintf(buf,"done; now in process %c, %s]%s\r",
X                    nname,get_image(pid[ncur]),ceol);
X            term_msg(buf);
X            cur = ncur;
X            if (blocked[cur]) term_out(cur);
X          }
X        }
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 (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  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}
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  enable_hangups[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;
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  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\nType control-\\ control-h for information\n\n");
X  initialize();
X  sys$hiber();
X}
$ GoSub Convert_File
$ Exit
