KARNEY%PPC.MFENET@NMFECC.ARPA (07/19/88)
+-+-+-+ Beginning of part 3 +-+-+-+
Xinstead these messages will be sent directly to the terminal.
X
XREVISION HISTORY:
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
XVersion 1.5. April 5, 1988
X Fix exceeded quota problem that occurs when buflen > sysgen's maxbuf.
XVersion 1.6. April 7, 1988
X Fix failure to detect failure of LIB$SPAWN (e.g., when over quota).
X Add C-t to make next process created as a top-level process.
XVersion 1.7. June 5, 1988
X Use separate command C-\ C-n a to create a process.
X C-\ C-t a does this at top-level.
X Cleaned up input routine.
XVersion 1.8. June 7, 1988
X Accept command line args:
X /COMMAND_CHARACTER=char - set control character
X /START_PROCESS=list - start up specified jobs
X /STUFF_STRING=list - and stuff their input buffers
X /OUTPUT_FLAGS=list - and set output flags accordingly
X /BEGIN_PROMPT=string - part of prompt appearing before id letter
X /END_PROMPT=string - part of prompt appearing after id letter
X /DEFAULT_OUTPUT_FLAG=char - set default output flag
X /SWITCH_CREATE - switching to nonexistent process creates it
XVersion 1.9. June 8, 1988
X Trap broadcasts to BOSS and send them on to the current process.
XVersion 2.0. June 9, 1988
X Add /DELETE_CHAR=char to specify a character to be interchanged with DEL.
XVersion 2.1. June 10, 1988
X Support selected terminal types via BOSS$TERM logical name.
XVersion 2.2. June 26, 1988
X Convert for new pseudo TTY drivers.
X Change TPA to TWA; ignore SS$_DATAOVERUN; use GETDVI to get unit number
XVersion 2.3. June 30, 1988
X C-s (to stop output) hangs process when new PTY (TWA) drivers are used
X with VMS 4.7. Make BOSS try both TWA and TPA, so it works with both new
X and old PTY drivers.
XVersion 2.4. July 4, 1988
X BOSS/FLOW_CONTROL to permit flow-control at BOSS level rather than
X subprocess level. To make this work, need to change C-s command to C-w.
XStill to do:
X Make /FLOW_CONTROL work with Emacs by checking the device characteristics
X of the pseudo TTY. (Not sure how best to do this: Could do this with
X setmode ast, or else check terminal setting, or else let user set a
X per-process flag.)
X C-l toggle output to log file sys$scratch:boss-x.log
X C-a append to log file?
X
X*/
X
X#define VERSION`009`009"2.4"
X
X#include DESCRIP
X#include IODEF
X#include TTDEF
X#include TT2DEF
X#include JPIDEF
X#include LNMDEF
X#include PRVDEF
X#include PSLDEF
X#include SSDEF
X#include STSDEF
X#include DVIDEF
X
X#include stdio
X#include climsgdef
X
X#define ttchrlen 12
X#define tpdevlen 15
X#define mbsiz 40
X#define ttmbsiz 256
X#define maxsiz 80
X#define ttmaxsiz 256
X#define imagelen 80
X#define linesz 512
X#define bufsize 4096`009/* Size of output buffers */
X#define maxbuf 1200`009/* Should be less than SYSGEN MAXBUF */
X`009`009`009`009/* 1200 is in fact the minimum setting */
X#define nproc 8 /* Number of process allowed */
X#define nalph 26 /* Number of possible names */
X
X#define bad(j) !((j) & 1)
X#define check(a) if (bad(st = (a))) LIB$SIGNAL(st)
X
X#define NORMAL 0
X#define SWITCH 0
X#define PENDING 1
X#define CREATE 2
X#define TOP 3
X#define END 4
X
X#define BRK$C_DEVICE 1
X#define BRK$C_USER16 47
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
Xtypedef struct DVIBLK {
X`009unsigned short`009len, code;
X`009char`009* buffp;
X`009long`009* lenp;
X`009long`009terminate;
X`009} DVIBLK;
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], privs[2],
X no_phy_io, no_oper, brkthru, ctlchar, init;
X
Xstatic char
X *clr, *bos, *ceol, *ceoln,
X *retval, ctlchar_str[4], prompt_begin[30], prompt_end[30],
X switch_create, flow_control, delete_char,
X stuff_buf[256], buf[256],image[imagelen],
X finaltp[nproc][tpdevlen],
X py_mb[nproc][mbsiz], tt_mb[ttmbsiz],
X input_char, tpline[nproc][linesz], buffer[nproc][bufsize],
X blocked[nproc], mode[nproc], pmode[nproc], defmode,
X name[nproc], py_post[nproc], proc_type[nproc],
X enable_hangup[nproc], input_state = 0, oboss_id = 0,
X super_ac_mode = PSL$C_SUPER;
X
Xextern int BOSS_CLD();
X
Xstruct CHARBLK tt_chr, tt_sav_chr;
Xstruct IOSBBLK tiosb, tiosbmb, 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
Xquit() /* This is done upon exiting, by exit handler */
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
Xmb_srv(n)`009`009`009/* AST for mailbox message on top-level */
X`009`009`009`009/* process completion */
X int n;
X{
X if (proc_type[n] == TOP) {
X if (enable_hangup[n]) comp_srv(n);
X else {
X enable_hangup[n] = 1;
X check(SYS$QIO(0,py_mb_chn[n],IO$_READVBLK,&miosb[n],&mb_srv,n,
X`009`009 &py_mb[n],mbsiz,0,0,0,0));
X }
X }
X}
X
X
Xcomp_srv(n) /* AST for completion of processes */
X int 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 input_state = PENDING;
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, val;
X char proc[20],prompt[50],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 sprintf(prompt,"%s%c%s",prompt_begin,name,prompt_end);
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 val = 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(val);
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] == 'w') {
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 j = 0;
X while (j < buflen[n]) {
X check(SYS$QIOW(1,tt_chn,IO$_WRITEVBLK,&tiosb,0,0,&buffer[n][j],
X`009`009 (buflen[n] - j < maxbuf) ? buflen[n] - j : maxbuf,
X`009`009 0,0,0,0));
X j += maxbuf;
X }
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,&super_ac_mode,&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,&super_ac_mode,&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] = 'w';
X mov_to(nname, 0, trnlnm_string, CREATE);
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 ptr = ℑ
X if (pid == 0) strcpy(image,"<TOP>");
X else {
X item = JPI$_IMAGNAME;
X j = LIB$GETJPI(&item,&pid,0,0,&d_image,&len);
X if (bad(j)) strcpy(image,"<UNKNOWN>");
X else {
X image[len]='\0';
X if (len == 0) {
X`009item = JPI$_CLINAME;
X`009j = LIB$GETJPI(&item,&pid,0,0,&d_image,&len);
X`009if (bad(j)) strcpy(image,"<UNKNOWN>");
X`009else image[len]='\0';
X }
X else {
X`009if ((ptr = strrchr(image,']'))) ptr++;
X`009else ptr = ℑ
X`009if (ptra = strchr(ptr,'.')) *ptra = '\0';
X }
X }
X }
X return(ptr);
X}
X
Xint mov_to(nname, clear, string, proc_mode) /* Switch to process */
X /* string is stuffed into input */
X /* proc_mode says whether to create process */
Xchar nname, *string;
Xint clear,proc_mode;
X{
X int ncur,len,j;
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 j = 1;
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 j = 1;
X }
X else if (proc_mode == SWITCH) {
X sprintf(buf,
X`009 "%s[Process %c nonexisent\007 (type %s C-n %c to create it)]%s",
X`009 bos,nname,ctlchar_str,clear ? nname : tolower(nname),ceoln);
X term_msg(buf);
X len = 0;
X j = 0;
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 j = 0;
X }
X else {
X if (proc_mode == CREATE)
X sprintf(buf,"%s[Starting subprocess %c...%s",prefix,nname,ceol);
X else if (proc_mode == TOP)
X sprintf(buf,"%s[Starting top-level process %c...%s",prefix,nname,ceol);
X term_msg(buf);
X j = fire_up(ncur,nname,proc_mode);
X if (bad(j)) {
X if (cur >= 0)
X`009sprintf(buf,"failed!!\007--still in %c%c]%s",
X`009`009name[cur],mode[cur],ceoln);
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 if (tiosb.stats != SS$_DATAOVERUN) check(tiosb.stats);
X }
X return(j);
X}
X
Xprint_help()
X{
X if (ctlchar < 040)
X sprintf(buf,"%sBOSS commands are preceded by %s (control-%c). \
XThe commands are:%s",bos,ctlchar_str,tolower(ctlchar+0100),ceoln);
X else
X sprintf(buf,"%sBOSS commands are preceded by %s. \
XThe commands are:%s",bos,ctlchar_str,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 Switch to process A (similarly for a thru z)%s",
X`009 ceoln);
X term_msg(buf);
X sprintf(buf,"\r A Clear screen and switch to process A%s",ceoln);
X term_msg(buf);
X sprintf(buf,"\r C-n a Create new process A as a subprocess%s",ceoln);
X term_msg(buf);
X sprintf(buf,"\r C-t a Create process A at top level%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);
X sprintf(buf,"\r C-o Discard output for this process%s",ceoln);
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-w Stop output from this process%s",ceoln);
X term_msg(buf);
X sprintf(buf,"\r %-3s Send command character to current process%s",
X`009 ctlchar_str,ceoln);
X term_msg(buf);
X sprintf(buf,"\rType HELP BOSS for more information.%s",ceoln);
X term_msg(buf);
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 if (input_char == 0177) input_char = delete_char;
X else if (input_char == delete_char) input_char = 0177;
X if (input_state == NORMAL && cur < 0) input_state = PENDING;
X switch (input_state) {
X case NORMAL:
X if (input_char == ctlchar) input_state = PENDING;
X else {
X check(SYS$QIOW(0,py_chn[cur],IO$_WRITEVBLK,&tiosb,0,0,
X`009`009 &input_char,1,0,0,0,0));
X if (tiosb.stats != SS$_DATAOVERUN) check(tiosb.stats);
X }
X break;
X case PENDING:
X if (input_char == ctlchar) {
X if (cur >=0) {
X`009check(SYS$QIOW(0,py_chn[cur],IO$_WRITEVBLK,&tiosb,0,0,
X`009`009 &input_char,1,0,0,0,0));
-+-+-+-+-+ End of part 3 +-+-+-+-+-