/* BOSS interactive job controller. Copyright (c) 1987, 1988 by Charles Karney. All rights reserved. Written by Charles Karney Plasma Physics Laboratory Phone: 609/243-2607 Princeton University MFENet: Karney@PPC.MFENET PO Box 451 ARPANet: Karney%PPC.MFENET@NMFECC.ARPA Princeton, NJ 08543-0451 Bitnet: Karney%PPC.MFENET@ANLVMS.BITNET Based on the PHOTO program of Asbed Bedrossian (USC, asbed@oberon.usc.edu). It utilizes the Pseudo TTY package of Dale Moore (CMU, Dale.Moore@PS1.CS.CMU.EDU) and Kevin Carosso (Network Research Co., kvc@nrc.com, kvc@ymir.bitnet). The UW terminal emulator was written by John Bruner (LLNL, jbd@mordor.s1.gov). He also defined the UW protocol. DESCRIPTION: BOSS lets you create up to 8 processes on a VAX/VMS system. Each process is identified by a single letter (A thru Z). At most one of these processes is `current'. What you type is sent to that process, and output from that process is sent to your terminal. A process which is not current can run but cannot do output. (Output is saved until you make that process current.) You usually switch between processes by typing control-\ followed by the identifying letter. You can run any program under BOSS. For example, you might run Emacs or EVE in process E SET HOST to another machine in process H do a FORTRAN compilation in process F execute DCL commands in process D talk to your colleague using PHONE in process P and so on. As an experimental addition, BOSS can also be run with the UW multiple- window terminal emulator for the Macintosh or Meshugena-term on Amiga. In this case you interact with the various processes through their own windows. INSTALLATION: Compile and link with $ @boss_build Install with $ @boss_install ! This goes in the system startup file In order to run BOSS, you will need pseudo TTYs installed on your system. Contact me if you need a copy of this software. (The DECWindows drivers in VMS 5.3 work just fine.) BOSS can be operated without installing it with privileges (but you will still need the pseudo TTYs installed). If BOSS doesn't with PHY_IO privilege, then it won't be able to set the terminal type of the processes under BOSS (SHOW TERM will list the device type as UNKNOWN); the terminal type can be set with SET TERM. If BOSS doesn't have OPER privilege, then it won't be able to rebroadcast broadcast messages that it receives; instead these messages will be sent directly to the terminal. REVISION HISTORY: Version 1.0. August 22, 1987 Version 1.1. August 27, 1987 Add BOSS$ID, BOSS$SWITCH, BOSS$STUFF. Version 1.2. September 30, 1987 Stop PHY_IO being inherited by subprocesses. Version 1.3. March 17, 1988 C-b buffer output C-p proceed (output comes through regardless) C-o suppress output (but save the last write) C-s hang output Version 1.4. March 23, 1988 Make BOSS$SWITCH switching set output flag to s (for synchronism) Default output flag is b Version 1.5. April 5, 1988 Fix exceeded quota problem that occurs when buflen > sysgen's maxbuf. Version 1.6. April 7, 1988 Fix failure to detect failure of LIB$SPAWN (e.g., when over quota). Add C-t to make next process created as a top-level process. Version 1.7. June 5, 1988 Use separate command C-\ C-n a to create a process. C-\ C-t a does this at top-level. Cleaned up input routine. Version 1.8. June 7, 1988 Accept command line args: /COMMAND_CHARACTER=char - set control character /START_PROCESS=list - start up specified jobs /STUFF_STRING=list - and stuff their input buffers /OUTPUT_FLAGS=list - and set output flags accordingly /BEGIN_PROMPT=string - part of prompt appearing before id letter /END_PROMPT=string - part of prompt appearing after id letter /DEFAULT_OUTPUT_FLAG=char - set default output flag /SWITCH_CREATE - switching to nonexistent process creates it Version 1.9. June 8, 1988 Trap broadcasts to BOSS and send them on to the current process. Version 2.0. June 9, 1988 Add /DELETE_CHAR=char to specify a character to be interchanged with DEL. Version 2.1. June 10, 1988 Support selected terminal types via BOSS$TERM logical name. Version 2.2. June 26, 1988 Convert for new pseudo TTY drivers. Change TPA to TWA; ignore SS$_DATAOVERUN; use GETDVI to get unit number Version 2.3. June 30, 1988 C-s (to stop output) hangs process when June 1988 PTY (TWA) drivers are used with VMS 4.7. Make BOSS try both TWA and TPA, so it works with both new and old PTY drivers. Version 2.4. July 4, 1988 BOSS/FLOW_CONTROL to permit flow-control at BOSS level rather than subprocess level. To make this work, need to change C-s command to C-w. Version 2.5. July 22, 1988 Implement UW Protocol 1. BOSS/UW. Add /PROCESS_DEFAULT. Add VT52 support. C-k command added to kill current process. Version 2.6. September 26, 1988 Write out stuffed strings one character at a time to prevent part of the string being lost. (A better solution would be to handle the DATA_OVERRUN condition properly by waiting for an XON AST.) Fixed bug where the y/n response to process deletion and exiting was interpreted as a process switch command if the current process completed in the meantime. Add /AUTO_STUFF_STRING Version 2.7. November 4, 1988 Ignore error status 0. Fix C-k so it doesn't try to kill top-level process. Version 2.7b 8/22/1990 Glenn Everhart Added control-I toggle for buffering. Allows buffering of console output even from active output so one can switch to another window and get back the buffered text. Switching processes resets. Will arrange so that some "scrolled off" text can be saved, though the buffering is switched off on process switches. Another toggle will be added to allow buffer display to be a one-shot without losing old contents. Version 2.7c 8/23/90 Glenn Everhart Add C-L toggle to open or close output to BOSSx.LOG for current process. Make it apply per process. Shutdown will close the file where orderly. Version 2.7d 8/30/90 Glenn Everhart Add C-G to grab 1 to 9 lines out of one process' buffer (assuming data is being buffered) and shove it into the next process where one starts it, similar to boss_stuff operation. Kludgey but possibly useful. Still to do: Make /FLOW_CONTROL work with Emacs by checking the device characteristics of the pseudo TTY. (Not sure how best to do this: Could do this with setmode ast, or else check terminal setting, or else let user set a per-process flag.) C-l toggle output to log file sys$scratch:boss-x.log C-a append to log file? */ #define VERSION "2.7d" #include DESCRIP #include IODEF #include TTDEF #include TT2DEF #include JPIDEF #include LNMDEF #include PRVDEF #include PSLDEF #include SSDEF #include STSDEF #include DVIDEF #include stdio #include climsgdef #define ttchrlen 12 #define tpdevlen 15 #define mbsiz 40 #define ttmbsiz 256 #define maxsiz 80 #define ttmaxsiz 256 #define imagelen 80 #define linesz 512 #define bufsize 4096 /* Size of output buffers */ #define maxbuf 1200 /* Should be less than SYSGEN MAXBUF */ /* 1200 is in fact the minimum setting */ #define nprocmax 8 /* Number of process allowed */ #define nalphmax 26 /* Number of possible names */ #define bad(j) !((j) & 1) #define check(a) if (bad(st = (a))) \ {if (st != 0) LIB$SIGNAL(st);} else {} #define NORMAL 0 #define SWITCH 0 #define PENDING 1 #define CREATE 2 #define TOP 3 #define END 4 #define KILL 5 #define CUTP 6 #define BRK$C_DEVICE 1 #define BRK$C_USER16 47 /* * uw protocol * * Copyright 1985,1986 by John D. Bruner. All rights reserved. Permission to * copy this program is given provided that the copy is not sold and that * this copyright notice is included. */ #define P1_IAC 0001 /* interpret as command */ #define P1_DIR 0100 /* command direction: */ #define P1_DIR_HTOM 0000 /* from host to Mac */ #define P1_DIR_MTOH 0100 /* from Mac to host */ #define P1_FN 0070 /* function code: */ #define P1_FN_NEWW 0000 /* new window */ #define P1_FN_KILLW 0010 /* kill (delete) window */ #define P1_FN_ISELW 0020 /* select window for input */ #define P1_FN_OSELW 0030 /* select window for output */ #define P1_FN_META 0050 /* add meta to next data char */ #define P1_FN_CTLCH 0060 /* low 3 bits specify char */ #define P1_FN_MAINT 0070 /* maintenance functions */ #define P1_WINDOW 0007 /* window number mask */ #define P1_CC 0007 /* control character specifier: */ #define P1_CC_IAC 1 /* IAC */ #define P1_CC_XON 2 /* XON */ #define P1_CC_XOFF 3 /* XOFF */ #define P1_MF 0007 /* maintenance functions: */ #define P1_MF_ENTRY 0 /* beginning execution */ #define P1_MF_ASKPCL 2 /* request protocol negotiation */ #define P1_MF_CANPCL 3 /* suggest protocol */ #define P1_MF_SETPCL 4 /* set current protocol */ #define P1_MF_EXIT 7 /* execution terminating */ #define P1_NWINDOW 7 /* maximum number of windows */ #define UW_NORMAL 0 #define UW_PENDING 1 #define UW_CANPCL 2 #define UW_SETPCL 3 #define XON 021 #define XOFF 023 struct CHARBLK { unsigned char class, ttype; unsigned short pgwid; unsigned ttchr : 24; unsigned char pglen; unsigned int xchar; }; struct IOSBBLK { unsigned short stats, tmoff, tmntr, tmsiz; }; typedef struct DVIBLK { unsigned short len, code; char * buffp; long * lenp; long terminate; } DVIBLK; int py_chn[nprocmax], py_mb_chn[nprocmax], tt_chn, tt_mb_chn, pid[nprocmax], st, cur, kill_proc, count[nprocmax], buflen[nprocmax], procno[nalphmax], priv[2], privs[2], def_stuff_len, bufmod[nprocmax], bufmod2[nprocmax],logmod[nprocmax],cutpas[nprocmax], no_phy_io, no_oper, brkthru, ctlchar, init, nproc, nalph; int logfd[nprocmax]; static char *clr, *bos, *ceol, *ceoln, *retval, ctlchar_str[4], prompt_begin[30], prompt_end[30], switch_create, flow_control, delete_char, stuff_buf[256], def_stuff_buf[256], buf[256], image[imagelen], finaltp[nprocmax][tpdevlen], py_mb[nprocmax][mbsiz], tt_mb[ttmbsiz], input_char, tpline[nprocmax][linesz], buffer[nprocmax][bufsize], term_buf[maxbuf], blocked[nprocmax], mode[nprocmax], pmode[nprocmax], defmode, defproc, name[nprocmax], py_post[nprocmax], proc_type[nprocmax], enable_hangup[nprocmax], input_state = NORMAL, oboss_id = 0, super_ac_mode = PSL$C_SUPER, uw = 0, uw_state = UW_NORMAL, uw_meta = 0, first_name, last_name, mac_command = 0; extern int close(); extern int creat(); extern int write(); extern int BOSS_CLD(); struct CHARBLK tt_chr, tt_sav_chr; struct IOSBBLK tiosb, tiosbmb, piosb[nprocmax], miosb[nprocmax]; static short trnlnm_string_len; static char trnlnm_string[bufsize+151]; /* Make this string quite long so we can use it for cut/paste */ /* n.b. cut/paste a but kludgey at the moment */ struct ITEM_LST { unsigned short len, code; char *addr; short *retlen; } trnlnm_item = {80, LNM$_STRING, &trnlnm_string, &trnlnm_string_len}; quit() /* This is done upon exiting, by exit handler */ { int i,j; char id[2]; $DESCRIPTOR(d_boss_id,"BOSS$ID"); $DESCRIPTOR(d_id, id); if (uw && !mac_command) uw_fun(P1_FN_MAINT | P1_MF_EXIT, 0, 0); if (oboss_id != 0) { /* Restore BOSS$ID */ id[0] = oboss_id; j = LIB$SET_LOGICAL(&d_boss_id,&d_id,0,0,0); } for (i = 0; i < nproc; i++) { if (name[i]) { j = SYS$DELMBX(py_mb_chn[i]); if (bad(j)) printf("[SYS$DELMBX pseudo-mbx deletion failed]\n"); /* Last chance close all log files */ if (logfd[i] != 0) j=close(logfd[i]); } } j = SYS$QIOW(0,tt_chn,IO$_SETMODE,0,0,0,&tt_sav_chr,ttchrlen,0,0,0,0); if (bad(j)) printf("[SYS$QIO /setmode/ failed]\n"); printf("\nEnd BOSS\n"); } mb_srv(n) /* AST for mailbox message on top-level */ /* process completion */ int n; { if (proc_type[n] == TOP) { if (enable_hangup[n]) comp_srv(n); else { enable_hangup[n] = 1; check(SYS$QIO(0,py_mb_chn[n],IO$_READVBLK,&miosb[n],&mb_srv,n, &py_mb[n],mbsiz,0,0,0,0)); } } } comp_srv(n) /* AST for completion of processes */ int n; { int j; j = SYS$DELMBX(py_mb_chn[n]); if (name[n]) procno[name[n] - first_name] = -1; name[n] = '\0'; if (uw && !mac_command) uw_fun(P1_FN_KILLW | ((n + 1) & P1_WINDOW), 0, 0); if (kill_proc == n) kill_proc = -1; if (logfd[n] != 0) j = close(logfd[n]); if (cur == n) cur = -1; } int low_lib_spawn(n,pty_io,pid,name) /* Spawns subprocess to speaks to pseudo terminal */ char *pty_io, name; int n, *pid; { int flg = 1, len, val, i; char proc[20],prompt[50],id[2]; $DESCRIPTOR(d_pty_io, pty_io); /* PTY name + number */ $DESCRIPTOR(d_proc, proc); /* Process name */ $DESCRIPTOR(d_prompt, prompt); /* Prompt */ $DESCRIPTOR(d_boss_id,"BOSS$ID"); $DESCRIPTOR(d_id, id); /* The id */ d_pty_io.dsc$w_length = strlen(pty_io); strcpy(proc,getenv("TT")); len = strlen(proc); if (proc[len-1] == ':') proc[--len] = '\0'; strcat(proc,"_A"); len = strlen(proc); proc[len-1] = name; d_proc.dsc$w_length= len; if (proc[0] == '_') { d_proc.dsc$w_length--; d_proc.dsc$a_pointer++; } sprintf(prompt,"%s%c%s",prompt_begin,name,prompt_end); d_prompt.dsc$w_length = strlen(prompt); id[0] = name; check(LIB$SET_LOGICAL(&d_boss_id,&d_id,0,0,0)); val = LIB$SPAWN(0,&d_pty_io,&d_pty_io,&flg,&d_proc,pid,0,0, &comp_srv,n,&d_prompt,0); if (!bad(val)) for (i = 0; i < def_stuff_len; i++) { check(SYS$QIOW(0,py_chn[n],IO$_WRITEVBLK,&tiosb,0,0, def_stuff_buf+i,1,0,0,0,0)); if (tiosb.stats != SS$_DATAOVERUN) check(tiosb.stats); } check(LIB$DELETE_LOGICAL(&d_boss_id,0)); return(val); } py_srv(n) /* AST reads on pseudo terminal */ int n; { int j,kkkk; char * tpptr; py_post[n] = 0; check(piosb[n].stats); /* Check status */ count[n] = piosb[n].tmoff + piosb[n].tmsiz; /* How much was read */ if (n >= 0 && logmod[n] != 0){ if (logfd[n] != 0){ tpptr = &tpline[n]; /* write text to active logfile if one exists */ j = write(logfd[n],tpptr,count[n]); } } if (n == cur || mode[n] == 'p') { if (bufmod[n] != 0 ){ /* Switch buffering separately */ if (count[n]+buflen[n] < bufsize) { for (j = 0; j < count[n]; j++) buffer[n][buflen[n]++] = tpline[n][j]; } else { /* copy buffer down and add in new text */ /* First copy text down enough to hold the new line */ kkkk = buflen[n] - count[n]; if (kkkk <= 0 ) kkkk = 1; for (j = 0; j < kkkk; j++) buffer[n][j] = buffer[n][j+count[n]]; buflen[n] = buflen[n] - count[n]; /* Now add the new text after the line */ for (j = 0; j < count[n]; j++) buffer[n][buflen[n]++] = tpline[n][j]; } } /* bufmod */ term_out(n); /* Write the stuff to the terminal */ } else if (mode[n] == 'w') { blocked[n] = 1; } else if (mode[n] == 'o') { blocked[n] = 1; check(SYS$QIO(0,py_chn[n],IO$_READVBLK,&piosb[n],&py_srv,n, &tpline[n],linesz,0,0,0,0)); /* Queue next AST */ py_post[n] = 1; } else if (mode[n] == 'b') { if (count[n]+buflen[n] < bufsize) { for (j = 0; j < count[n]; j++) buffer[n][buflen[n]++] = tpline[n][j]; check(SYS$QIO(0,py_chn[n],IO$_READVBLK,&piosb[n],&py_srv,n, &tpline[n],linesz,0,0,0,0)); /* Queue next AST */ py_post[n] = 1; blocked[n] = 0; } else { py_post[n] = 0; blocked[n] = 1; } } } to_term(buf,len,chan) char *buf; int len,chan; { int i,j; char ochar; if (!uw) { j = 0; while (j < len) { check(SYS$QIOW(0,tt_chn,IO$_WRITEVBLK,&tiosb,0,0,&buf[j], (len - j < maxbuf) ? len - j : maxbuf, 0,0,0,0)); j += maxbuf; } } else if (chan >= 0) { j = 0; i = 0; term_buf[i++] = P1_IAC; term_buf[i++] = P1_DIR_HTOM | P1_FN_OSELW | ((chan + 1) & P1_WINDOW); while (j < len) { ochar = buf[j++]; if (ochar & 0200) { term_buf[i++] = P1_IAC; term_buf[i++] = P1_DIR_HTOM | P1_FN_META; ochar = ochar & 0177; } switch (ochar) { case P1_IAC: term_buf[i++] = P1_IAC; term_buf[i++] = P1_DIR_HTOM | P1_FN_CTLCH | P1_CC_IAC; break; case XON: term_buf[i++] = P1_IAC; term_buf[i++] = P1_DIR_HTOM | P1_FN_CTLCH | P1_CC_XON; break; case XOFF: term_buf[i++] = P1_IAC; term_buf[i++] = P1_DIR_HTOM | P1_FN_CTLCH | P1_CC_XOFF; break; default: term_buf[i++] = ochar; break; } if (i > maxbuf-4) { /* If no room for another meta-xon */ check(SYS$QIOW(0,tt_chn,IO$_WRITEVBLK,&tiosb,0,0,term_buf,i,0,0,0,0)); i = 2; /* Leave OSELW command in first 2 bytes */ } } if (i > 2) { /* Spit out rest of buffer */ check(SYS$QIOW(0,tt_chn,IO$_WRITEVBLK,&tiosb,0,0,term_buf,i,0,0,0,0)); i = 0; } } } term_out(n) int n; { int j,k,kk,kkk,kkkk; char nname; $DESCRIPTOR(d_boss_switch,"BOSS$SWITCH"); $DESCRIPTOR(d_boss_stuff,"BOSS$STUFF"); $DESCRIPTOR(d_lnm_job,"LNM$JOB"); if (buflen[n] > 0) { /* If buffering current stuff and in a print mode, print only new stuff*/ if (bufmod[n] == 0 ){ to_term(buffer[n],buflen[n],n); if (bufmod2[n] == 0) buflen[n] = 0; else bufmod[n] = 1; } else to_term(tpline[n],count[n],n); if (blocked[n]) to_term(tpline[n],count[n],n); } else to_term(tpline[n],count[n],n); /* Process boss_switch and boss_stuff logicals if present */ j = SYS$TRNLNM(0,&d_lnm_job,&d_boss_switch,&super_ac_mode,&trnlnm_item); if (!bad(j) && trnlnm_string_len == 1) { j = LIB$DELETE_LOGICAL(&d_boss_switch,&d_lnm_job); nname = toupper(trnlnm_string[0]); j = SYS$TRNLNM(0,&d_lnm_job,&d_boss_stuff,&super_ac_mode,&trnlnm_item); if (!bad(j)) { j = LIB$DELETE_LOGICAL(&d_boss_stuff,&d_lnm_job); trnlnm_string[trnlnm_string_len] = '\0'; } else { trnlnm_string[0] = '\0'; } if (nname >= first_name && nname <= last_name) { mode[n] = 'w'; mov_to(nname, 0, trnlnm_string, defproc); } } if (py_post[n] == 0) { check(SYS$QIO(0,py_chn[n],IO$_READVBLK,&piosb[n],&py_srv,n, &tpline[n],linesz,0,0,0,0)); /* Queue next AST */ py_post[n] = 1; } blocked[n] = 0; } int count_processes() { int j, i = 0; for (j = 0; j < nproc; j++) if (name[j] > 0) i++; return(i); } diag() { char bufa[8]; int j; if (count_processes()) { sprintf(buf,"%s[Processes:",bos); for (j = 0; j < nproc; j++) { if (name[j] > 0) { if (j == cur) sprintf(bufa," %c%c*",name[j],mode[j]); else if (blocked[j]) sprintf(bufa," %c%c+",name[j],mode[j]); else if (buflen[j] > 0) sprintf(bufa," %c%c-",name[j],mode[j]); else sprintf(bufa," %c%c",name[j],mode[j]); strcat(buf,bufa); } } strcat(buf,"] "); strcat(buf,ceoln); } else sprintf(buf,"%s[No processes] %s",bos,ceoln); term_msg(buf); } int next_slot() { int j = 0; while ((j < nproc) && name[j]) j++; return (j == nproc) ? -1 : j; } term_msg(msg) char *msg; { to_term(msg,strlen(msg),cur); } char *get_image(pid) /* Get the image name for a process */ int pid; { int j, item; short len; char *ptr, *ptra; $DESCRIPTOR(d_image,image); ptr = ℑ if (pid == 0) strcpy(image,""); else { item = JPI$_IMAGNAME; j = LIB$GETJPI(&item,&pid,0,0,&d_image,&len); if (bad(j)) strcpy(image,""); else { image[len]='\0'; if (len == 0) { item = JPI$_CLINAME; j = LIB$GETJPI(&item,&pid,0,0,&d_image,&len); if (bad(j)) strcpy(image,""); else image[len]='\0'; } else { if ((ptr = strrchr(image,']'))) ptr++; else ptr = ℑ if (ptra = strchr(ptr,'.')) *ptra = '\0'; } } } return(ptr); } int mov_to(nname, clear, string, proc_mode) /* Switch to process */ /* string is stuffed into input */ /* proc_mode says whether to create process */ char nname, *string; int clear,proc_mode; { int ncur,len,i,j; char *prefix; prefix = clear ? clr : bos; len = strlen(string); if ((cur >= 0) && (name[cur] == nname)) { /* Redundant move */ bufmod[cur] = 0; mode[cur] = pmode[cur]; if (len == 0 & !uw) { sprintf(buf,"%s[Already in process %c%c, %s]%s", prefix,nname,mode[cur],get_image(pid[cur]),ceoln); term_msg(buf); } j = 1; } else if ((ncur = procno[nname-first_name]) >= 0) { /* Existing proc */ cur = ncur; mode[cur] = pmode[cur]; bufmod[cur] = 0; if (!uw) { sprintf(buf,"%s[Switch to process %c%c, %s]%s", prefix,name[cur],mode[cur],get_image(pid[cur]),ceoln); term_msg(buf); } if (blocked[cur] || buflen[cur] > 0) term_out(cur); j = 1; } else if (proc_mode == SWITCH) { sprintf(buf, "%s[Process %c nonexistent\007 (type %s C-%c %c to create it)]%s", bos,nname,ctlchar_str,defproc == TOP ? 't' : 'n', clear ? nname : tolower(nname),ceoln); term_msg(buf); len = 0; j = 0; } else if ((ncur = (uw ? (nname-first_name) : next_slot())) < 0) { if (cur >= 0) sprintf(buf,"%s[No process slots left--still in %c%c]%s", bos,name[cur],mode[cur],ceoln); else sprintf(buf,"%s[No process slots left]%s",bos,ceoln); term_msg(buf); len = 0; j = 0; } else { if (!uw) { if (proc_mode == CREATE) sprintf(buf,"%s[Starting subprocess %c...%s",prefix,nname,ceol); else if (proc_mode == TOP) sprintf(buf,"%s[Starting top-level process %c...%s",prefix,nname,ceol); term_msg(buf); } j = fire_up(ncur,nname,proc_mode); if (bad(j)) { if (!uw) { if (cur >= 0) sprintf(buf,"failed!!\007--still in %c%c]%s", name[cur],mode[cur],ceoln); else sprintf(buf,"failed!!\007]%s",ceoln); } else { sprintf(buf,"%s[Couldn't start start process %c]%s", prefix,nname,ceoln); uw_fun(P1_FN_KILLW | ((ncur + 1) & P1_WINDOW), 0, 0); } term_msg(buf); len = 0; } else { if (!uw) { sprintf(buf,"done; now in process %c%c, %s]%s\r", nname,mode[ncur],get_image(pid[ncur]),ceol); term_msg(buf); } cur = ncur; if (blocked[cur]) term_out(cur); } } /*if (j && uw && !mac_command) uw_fun(P1_FN_ISELW | ((cur + 1) & P1_WINDOW), 0, 0); */ for (i = 0; i < len; i++) { check(SYS$QIOW(0,py_chn[cur],IO$_WRITEVBLK,&tiosb,0,0, string+i,1,0,0,0,0)); if (tiosb.stats != SS$_DATAOVERUN) check(tiosb.stats); } return(j); } print_help() { if (ctlchar >= 0 && ctlchar < 040) sprintf(buf,"%sBOSS commands are preceded by %s (control-%c). \ The commands are:%s",bos,ctlchar_str,tolower(ctlchar+0100),ceoln); else sprintf(buf,"%sBOSS commands are preceded by %s. \ The commands are:%s",bos,ctlchar_str,ceoln); term_msg(buf); sprintf(buf,"\r C-h This message%s",ceoln); term_msg(buf); sprintf(buf,"\r C-z Quit%s",ceoln); term_msg(buf); sprintf(buf, "\r %c Switch to process %c (similarly for %c thru %c)%s", tolower(first_name),first_name, tolower(first_name),tolower(last_name),ceoln); term_msg(buf); if (!uw) { sprintf(buf,"\r %c Clear screen and switch to process %c%s", first_name,tolower(first_name),ceoln); term_msg(buf); } sprintf(buf,"\r C-n %c Create new process %c as a subprocess%s", tolower(first_name),first_name,ceoln); term_msg(buf); sprintf(buf,"\r C-t %c Create process %c at top level%s", tolower(first_name),first_name,ceoln); term_msg(buf); sprintf(buf,"\r C-k Kill current subprocess%s",ceoln); term_msg(buf); sprintf(buf,"\r ? List processes (* means current, \ +/- means waiting for output)%s",ceoln); term_msg(buf); sprintf(buf,"\r C-b Buffer output for this process%s",ceoln); term_msg(buf); sprintf(buf,"\r C-o Discard output for this process%s",ceoln); term_msg(buf); sprintf(buf,"\r C-p Print output from this process%s",ceoln); term_msg(buf); sprintf(buf,"\r C-i Toggle buffering of active output%s",ceoln); term_msg(buf); sprintf(buf,"\r C-j Toggle multishot buffering%s",ceoln); term_msg(buf); sprintf(buf,"\r C-l Toggle logging to BOSSn.LOG%s",ceoln); term_msg(buf); sprintf(buf,"\r C-g Get n lines of cur proc -> next%s",ceoln); term_msg(buf); sprintf(buf,"\r C-w Stop output from this process%s",ceoln); term_msg(buf); sprintf(buf,"\r %-3s Send command character to current process%s", ctlchar_str,ceoln); term_msg(buf); sprintf(buf,"\rType HELP BOSS for more information.%s",ceoln); term_msg(buf); } tt_srv() /* AST: Read on real terminal */ { int i; int k, kk, kkk, kkkk; int n, kprc; char nname, nmode, *desc, dismiss, function, arg; char lognam[80]; char * lnptr; check(tiosb.stats); /* do UW decoding at first */ dismiss = 0; if (uw) { mac_command = 1; switch (uw_state) { case UW_NORMAL: if (input_char == P1_IAC) { uw_state = UW_PENDING; dismiss = 1; } break; case UW_PENDING: uw_state = UW_NORMAL; dismiss = 1; if ((input_char & P1_DIR) == P1_DIR_MTOH) { function = input_char & P1_FN; arg = input_char & P1_WINDOW; switch (function) { case P1_FN_NEWW: mov_to(first_name + (arg-1),0,"",defproc); break; case P1_FN_KILLW: if (name[arg-1] > 0 && proc_type[arg-1] != TOP) st = SYS$DELPRC(&pid[arg-1],0); break; case P1_FN_ISELW: mov_to(first_name + (arg-1),0,"",defproc); break; case P1_FN_OSELW: /* shouldn't ever occur */ break; case P1_FN_META: uw_meta = 1; break; case P1_FN_CTLCH: dismiss = 0; switch (arg) { case P1_CC_IAC: input_char = P1_IAC; break; case P1_CC_XON: input_char = XON; break; case P1_CC_XOFF: input_char = XOFF; break; default: dismiss = 1; break; } break; case P1_FN_MAINT: switch (arg) { case P1_MF_ENTRY: /* Shouldn't get this */ break; case P1_MF_ASKPCL: uw_fun(P1_FN_MAINT | P1_MF_CANPCL, 040, 1); break; case P1_MF_CANPCL: /* Shouldn't get this */ uw_state = UW_CANPCL; break; case P1_MF_SETPCL: uw_state = UW_SETPCL; break; case P1_MF_EXIT: exit(SS$_NORMAL); break; } break; case UW_CANPCL: if (input_char == 040) uw_fun(P1_MF_SETPCL, 040, 1); else uw_fun(P1_MF_CANPCL, 040, 1); uw_state = UW_NORMAL; break; case UW_SETPCL: if (input_char != 040) { sprintf(buf,"%s[Don't understand this protocol: %d]%s", bos,input_char,ceoln); } uw_state = UW_NORMAL; break; } } } if (uw_meta && !dismiss) { uw_meta = 0; input_char = 0200 | input_char; } mac_command = 0; } if (!dismiss) { if (input_char == 0177) input_char = delete_char; else if (input_char == delete_char) input_char = 0177; if (input_state == NORMAL && cur < 0) input_state = PENDING; switch (input_state) { case NORMAL: if (input_char == ctlchar) input_state = PENDING; else { check(SYS$QIOW(0,py_chn[cur],IO$_WRITEVBLK,&tiosb,0,0, &input_char,1,0,0,0,0)); if (tiosb.stats != SS$_DATAOVERUN) check(tiosb.stats); } break; case PENDING: if (input_char == ctlchar) { if (cur >=0) { check(SYS$QIOW(0,py_chn[cur],IO$_WRITEVBLK,&tiosb,0,0, &input_char,1,0,0,0,0)); if (tiosb.stats != SS$_DATAOVERUN) check(tiosb.stats); input_state = NORMAL; } break; } switch (input_char) { case '\016': input_state = CREATE; break; case '\024': input_state = TOP; break; case '\032': if (count_processes()) { diag(); sprintf(buf,"[Do you really want to quit (y or n)?]%s\007",ceol); term_msg(buf); input_state = END; } else exit(SS$_NORMAL); break; case '\007': if (cur < 0) { term_msg("\007"); input_state = NORMAL; } else { sprintf(buf, "%s[Enter # lines to move (1-9):%s",bos,ceoln); term_msg(buf); kprc = cur; input_state = CUTP; } break; case '\013': if (cur < 0) { term_msg("\007"); input_state = NORMAL; } else if (proc_type[cur] == TOP) { sprintf(buf,"%s[Can't kill top-level process %c]%s\007", bos,name[kill_proc],ceoln); term_msg(buf); input_state = NORMAL; } else { sprintf(buf, "%s[Do you really want to kill process %c (y or n)?]%s\007", bos, name[cur], ceol); term_msg(buf); kill_proc = cur; input_state = KILL; } break; case '\010': print_help(); input_state = NORMAL; break; case '?': diag(); input_state = NORMAL; break; case '\002': case '\017': case '\020': case '\027': if (input_char == '\002') desc = "Buffer"; else if (input_char == '\017') desc = "Discard"; else if (input_char == '\020') desc = "Print"; else if (input_char == '\027') desc = "Stop"; nmode = input_char + 0140; if (cur < 0) { defmode = nmode; sprintf(buf,"%s[%s output by default]%s",bos,desc,ceoln); } else { mode[cur] = nmode; pmode[cur] = nmode; sprintf(buf,"%s[%s output from process %c]%s",bos,desc, name[cur],ceoln); } term_msg(buf); input_state = NORMAL; break; case '\011': if (cur >= 0){ kkk = bufmod[cur]; if (kkk == 0){ desc = "Actv Buffered"; bufmod[cur] = 1; } if (kkk != 0){ desc = "Actv Unbuffered"; bufmod[cur] = 0; } /* Hack to keep current mode */ nmode = 'b'; } if (cur < 0) { defmode = nmode; /* sprintf(buf,"%s[%s output by default]%s",bos,desc,ceoln); */ } else { sprintf(buf,"%s[%s output from process %c]%s",bos,desc, name[cur],ceoln); } term_msg(buf); input_state = NORMAL; break; case '\012': if (cur >= 0){ kkk = bufmod2[cur]; if (kkk == 0){ desc = "Multshot Buffered"; bufmod2[cur] = 1; } if (kkk != 0){ desc = "Multshot Unbuffered"; bufmod2[cur] = 0; } /* Hack to keep current mode */ nmode = 'b'; } if (cur < 0) { defmode = nmode; /* sprintf(buf,"%s[%s output by default]%s",bos,desc,ceoln); */ } else { sprintf(buf,"%s[%s output from process %c]%s",bos,desc, name[cur],ceoln); } term_msg(buf); input_state = NORMAL; break; case '\014': if (cur >= 0){ kkk = logmod[cur]; if (kkk == 0){ desc = "Logging enabled"; logmod[cur] = 1; kkkk=cur+48; /* disambiguating letter */ strncpy(lognam,"BOSSA.LOG",9); lnptr = &lognam + 4; *lnptr = kkkk; if (logfd[cur] == 0){ logfd[cur] = creat(lognam,0,"mbf=3"); } /* creates boss0.log thru boss7.log */ } if (kkk != 0){ desc = "Logging ended"; if (logfd[cur] != 0){ kkkk = close(logfd[cur]); } logfd[cur] = 0; logmod[cur] = 0; } /* Hack to keep current mode */ nmode = 'b'; } if (cur < 0) { defmode = nmode; /* sprintf(buf,"%s[%s output by default]%s",bos,desc,ceoln); */ } else { sprintf(buf,"%s[%s output from process %c]%s",bos,desc, name[cur],ceoln); } term_msg(buf); input_state = NORMAL; break; case '\177': input_state = NORMAL; break; default: nname = toupper(input_char); if (nname >= first_name && nname <= last_name) { /* fill in any "cut/paste" text being brought in from other proc */ n = cur; /* Zero text string if not being used */ trnlnm_string[0] = '\0'; if (n >= 0 && cutpas[n] > 0) { trnlnm_string_len = 0; /* Paste last n lines (up to max in buffer) back */ /* We are assured the dest. process number is legal here */ k=0; for (kkkk = 0; kkkk < buflen[n]; kkkk++){ if (buffer[n][kkkk] == '\n'){ k++; } } /* Count CR's (newlines) in buffer, then go back by n of them */ /* and copy from there to the end of buffer into the string */ /* that would be used for boss_stuff work. This avoids anothe */ /* mechanism. */ kkk = k - cutpas[n]; if (kkk < 0) kkk = 0; /* reset save amount fornext time */ cutpas[n] = 0; kk = buflen[n] + 100; k=0; for (kkkk = 0; kkkk < buflen[n]; kkkk++){ if (buffer[n][kkkk] == '\n'){ k++; if (k == kkk) kk = kkkk; } } /* kk is now index into start of cut/paste area, buflen[n] = end */ /* guard against empty buffer */ trnlnm_string_len = 0; if (kk < buflen[n]){ for (kkkk = kk; kkkk < buflen[n]; kkkk++){ trnlnm_string[trnlnm_string_len++] = buffer[n][kkkk]; } } trnlnm_string[trnlnm_string_len] = '\0'; } /* end cut/paste */ /* mov_to(nname, 0, trnlnm_string, defproc); */ mov_to(nname,uw ? 0 : input_char < 'a', trnlnm_string, switch_create ? defproc : SWITCH); } else term_msg("\007"); input_state = NORMAL; break; } break; case CREATE: case TOP: switch (input_char) { case '\016': input_state = CREATE; break; case '\024': input_state = TOP; break; case '\010': print_help(); input_state = NORMAL; break; case '?': diag(); input_state = NORMAL; break; case '\177': input_state = NORMAL; break; default: nname = toupper(input_char); if (nname >= first_name && nname <= last_name) { /* fill in any "cut/paste" text being brought in from other proc */ n = cur; /* Zero text string if not being used */ trnlnm_string[0] = '\0'; if (n >= 0 && cutpas[n] > 0) { trnlnm_string_len = 0; /* Paste last n lines (up to max in buffer) back */ /* We are assured the dest. process number is legal here */ k=0; for (kkkk = 0; kkkk < buflen[n]; kkkk++){ if (buffer[n][kkkk] == '\n'){ k++; } } /* Count LF's (newlines) in buffer, then go back by n of them */ /* and copy from there to the end of buffer into the string */ /* that would be used for boss_stuff work. This avoids anothe */ /* mechanism. */ kkk = k - cutpas[n]; if (kkk < 0) kkk = 0; /* reset save amount fornext time */ cutpas[n] = 0; k=0; kk = buflen[n] + 100; for (kkkk = 0; kkkk < buflen[n]; kkkk++){ if (buffer[n][kkkk] == '\n'){ k++; if (k == kkk) kk = kkkk; } } /* kk is now index into start of cut/paste area, buflen[n] = end */ /* guard against empty buffer */ trnlnm_string_len = 0; if (kk < buflen[n]){ for (kkkk = kk; kkkk < buflen[n]; kkkk++){ trnlnm_string[trnlnm_string_len++] = buffer[n][kkkk]; } } trnlnm_string[trnlnm_string_len] = '\0'; } /* end cut/paste */ /* mov_to(nname, 0, trnlnm_string, defproc); */ mov_to(nname,uw ? 0 : input_char < 'a', trnlnm_string,input_state); } else term_msg("\007"); input_state = NORMAL; break; } break; case END: if (toupper(input_char) == 'Y') { term_msg(" Yes\r"); exit(SS$_NORMAL); } else { term_msg(" No\r\n"); } input_state = NORMAL; break; case CUTP: /* kprc has proc. number to cut from */ kkk = input_char; /* Make sure char is 1 to 9, else discard and ignore */ /* Add small bit to allow multiple digits of count */ if (kkk > 48 && kkk < 58){ kk=cutpas[kprc] * 10; cutpas[kprc] = kk + kkk - 48; input_state = CUTP; } else { input_state = NORMAL; } break; case KILL: if (toupper(input_char) == 'Y') { if (kill_proc >= 0 && name[kill_proc] > 0) { if (proc_type[kill_proc] == TOP) { sprintf(buf,"%s[Can't kill top-level process %c]%s\007", bos,name[kill_proc],ceoln); } else { st = SYS$DELPRC(&pid[kill_proc],0); if (bad(st)) sprintf(buf,"%s[Couldn't kill process %c]%s", bos,name[kill_proc],ceoln); else sprintf(buf,"%s[Killed process %c]%s",bos,name[kill_proc],ceoln); } } else sprintf(buf,"%s[Process already killed]%s",bos,ceoln); term_msg(buf); } else { term_msg(" No\r\n"); } input_state = NORMAL; kill_proc = -1; break; } } /* re-post read AST on real term */ check(SYS$QIO(0,tt_chn,IO$_READVBLK,&tiosb,&tt_srv,0, &input_char,1,0,0,0,0)); } uw_fun(code,arg,count) char code,arg; int count; { term_buf[0] = P1_IAC; term_buf[1] = P1_DIR_HTOM | code; if (count > 0) term_buf[2] = arg; check(SYS$QIOW(0,tt_chn,IO$_WRITEVBLK,&tiosb,0,0,term_buf,2+count,0,0,0,0)); } get_tt_info() { $DESCRIPTOR(d_tt, "SYS$COMMAND"); /* Get a channel & mailbox of terminal */ check(LIB$ASN_WTH_MBX(&d_tt,&ttmbsiz,&ttmaxsiz,&tt_chn,&tt_mb_chn)); /* Get the terminal characteristics. */ check(SYS$QIOW(0,tt_chn,IO$_SENSEMODE,0,0,0,&tt_chr,ttchrlen,0,0,0,0)); tt_sav_chr = tt_chr; tt_chr.ttchr |= TT$M_NOECHO; /* term will be Noecho */ tt_chr.ttchr &= ~TT$M_HOSTSYNC; /* no host sync */ if (flow_control) tt_chr.ttchr |= TT$M_TTSYNC; /* do sync at BOSS level */ else tt_chr.ttchr &= ~TT$M_TTSYNC; /* do sync at subprocess level */ tt_chr.xchar |= TT2$M_PASTHRU; /* it will be PASTRHU */ if (brkthru) { tt_chr.ttchr |= TT$M_MBXDSABL; /* no hangup messages */ tt_chr.ttchr |= TT$M_NOBRDCST; /* disable direct broadcast */ tt_chr.xchar |= TT2$M_BRDCSTMBX; /* send them to mailbox instead */ } check(SYS$QIOW(0,tt_chn,IO$_SETMODE,0,0,0,&tt_chr,ttchrlen,0,0,0,0)); } fix_a_tp(n) /* Set up a Pseudo term */ int n; { int dev_depend, tp_chn; struct CHARBLK tw_chr; struct IOSBBLK iosb; struct DVIBLK dvi_stuff = {4, DVI$_DEVDEPEND, &dev_depend, 0, 0}; $DESCRIPTOR(d_pynam,"PYA0:"); /* Template. */ $DESCRIPTOR(d_finaltp, &finaltp[n]); /* Assign a mailbox to PYA */ check(LIB$ASN_WTH_MBX(&d_pynam,&mbsiz,&maxsiz,&py_chn[n],&py_mb_chn[n])); /* * Use $GETDVI to get the device dependent characteristics, which * contains the associated terminal device's unit number. */ check(SYS$GETDVI(0,py_chn[n],0,&dvi_stuff,&iosb,0,0,0)); check(iosb.stats); tw_chr= tt_sav_chr; tw_chr.xchar|= TT2$M_HANGUP; sprintf(&finaltp[n],"TWA%d:",dev_depend); d_finaltp.dsc$w_length = strlen(&finaltp[n]); /* Get a channel on this TWA */ if (bad(SYS$ASSIGN(&d_finaltp,&tp_chn,0,0))) { sprintf(&finaltp[n],"TPA%d:",dev_depend); /* TWA doesn't work; try TPA */ d_finaltp.dsc$w_length = strlen(&finaltp[n]); check(SYS$ASSIGN(&d_finaltp,&tp_chn,0,0)); } if (no_phy_io) check(SYS$SETPRV(1,&priv,0,0)); /* Make it look like a terminal */ if (bad(SYS$QIOW(0,tp_chn,IO$_SETCHAR,0,0,0, /* This needs PHY_IO priv */ &tw_chr,ttchrlen,0,0,0,0))) check(SYS$QIOW(0,tp_chn,IO$_SETMODE,0,0,0, &tw_chr,ttchrlen,0,0,0,0)); if (no_phy_io) check(SYS$SETPRV(0,&priv,0,0)); check(SYS$DASSGN(tp_chn)); /* We don't need it. only the mailbox */ /* in fact keeping it kills us. */ } broadcast_handler() /* handle broadcasts to BOSS */ { int j, len; $DESCRIPTOR(d_tt_mb,tt_mb); $DESCRIPTOR(d_finaltp,finaltp[cur]); check(tiosbmb.stats); /* Check status */ len = ((0377 & tt_mb[21]) << 8) + (0377 & tt_mb[20]); /* message length */ if (cur < 0) { term_msg("\r\n"); to_term(&(tt_mb[22]),len,1); term_msg("\r"); } else { d_tt_mb.dsc$w_length = len; d_tt_mb.dsc$a_pointer = &(tt_mb[22]); if (no_oper) check(SYS$SETPRV(1,&privs,0,0)); check(SYS$BRKTHRU(0,&d_tt_mb,&d_finaltp, BRK$C_DEVICE,0,32,0,BRK$C_USER16,0,0,0)); if (no_oper) check(SYS$SETPRV(0,&privs,0,0)); } check(SYS$QIO(0,tt_mb_chn,IO$_READVBLK,&tiosbmb,&broadcast_handler,0, &tt_mb,ttmbsiz,0,0,0,0)); } post_term_reads() /* Read AST on real term */ { if (brkthru) { check(SYS$QIO(0,tt_mb_chn,IO$_READVBLK,&tiosbmb,&broadcast_handler,0, &tt_mb,ttmbsiz,0,0,0,0)); } else { check(SYS$QIO(0,tt_mb_chn,IO$_READVBLK,&tiosbmb,0,0, &tt_mb,ttmbsiz,0,0,0,0)); } check(SYS$QIO(0,tt_chn,IO$_READVBLK,&tiosb,&tt_srv,0, &input_char,1,0,0,0,0)); } post_pty_reads(n) /* Read AST on Pseudo-term */ int n; { char cr = '\r'; if (init) return(0); py_post[n] = 1; check(SYS$QIO(0,py_mb_chn[n],IO$_READVBLK,&miosb[n],&mb_srv,n, &py_mb[n],mbsiz,0,0,0,0)); check(SYS$QIO(0,py_chn[n],IO$_READVBLK,&piosb[n],&py_srv,n, &tpline[n],linesz,0,0,0,0)); if (proc_type[n] == TOP) { check(SYS$QIOW(0,py_chn[n],IO$_WRITEVBLK,&tiosb,0,0, &cr,1,0,0,0,0)); if (tiosb.stats != SS$_DATAOVERUN) check(tiosb.stats); } } int fire_up(n,nname,proc_mode) /* Fire up subprocess n */ int n,proc_mode; char nname; { int val; name[n] = nname; procno[nname - first_name] = n; count[n] = 0; /* Initialize buffer count */ blocked[n] = 0; /* It starts unblocked */ bufmod[n] = 0; /* Starts w/o buffering all */ bufmod2[n] = 0; logmod[n] = 0; /* no logging initially */ logfd[n] = 0; /* no fd either */ py_post[n] = 0; mode[n] = defmode; pmode[n] = defmode; buflen[n] = 0; proc_type[n] = proc_mode; enable_hangup[n] = 0; pid[n] = 0; fix_a_tp(n); /* Set a pseudo terminal by TT info */ check(SYS$CANCEL(py_chn[n])); /* Don't need this Half of pseudo-ter */ val = (proc_type[n] == TOP) ? 1 : low_lib_spawn(n,&finaltp[n],&pid[n],name[n]); /* Spawn a subprocess. */ if (!bad(val)) { post_pty_reads(n); /* Set up AST */ if (uw && !mac_command) uw_fun(P1_FN_NEWW | ((n + 1) & P1_WINDOW), 0, 0); } else comp_srv(n); /* Mark the process as non-existent */ return(val); } initialize() /* Initialize everything */ { int j,item; $DESCRIPTOR(d_boss_id,"BOSS$ID"); $DESCRIPTOR(d_lnm_process,"LNM$PROCESS"); nalph = last_name - first_name + 1; for (j = 0; j < nproc; j++) name[j] = '\0'; /* Initialize variables */ for (j = 0; j < nalph; j++) procno[j] = -1; /* Save old value of BOSS$ID */ j = SYS$TRNLNM(0,&d_lnm_process,&d_boss_id,&super_ac_mode,&trnlnm_item); if (!bad(j) && trnlnm_string_len == 1) { oboss_id = trnlnm_string[0]; j = LIB$DELETE_LOGICAL(&d_boss_id,0); } item = JPI$_PROCPRIV; /* Check whether have PHY_IO & OPER */ check(LIB$GETJPI(&item,0,0,&priv,0,0)); no_phy_io = !(priv[0] & PRV$M_PHY_IO); no_oper = !(priv[0] & PRV$M_OPER); item = JPI$_IMAGPRIV; /* Check whether we can do BRKTHRU */ check(LIB$GETJPI(&item,0,0,&priv,0,0)); brkthru = ((priv[0] & PRV$M_OPER) || !no_oper); priv[0] = PRV$M_PHY_IO; priv[1] = 0; privs[0] = PRV$M_OPER; privs[1] = 0; if (no_phy_io) check(SYS$SETPRV(0,&priv,0,0)); if (no_oper) check(SYS$SETPRV(0,&privs,0,0)); get_tt_info(); /* Initialize terminal */ if (uw) uw_fun(P1_FN_MAINT | P1_MF_ENTRY,0,0); start_up(); /* Start up processes */ post_term_reads(); } /* Next two routines taken from FILE program by Joe Meadows Jr. */ long int cli_present(s) char *s; { static struct dsc$descriptor s_desc = {0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; s_desc.dsc$w_length = strlen(s); s_desc.dsc$a_pointer = s; return(cli$present(&s_desc)); } long int cli_get_value(s1,s2) char *s1,**s2; { static struct dsc$descriptor s1_desc={0,DSC$K_DTYPE_T,DSC$K_CLASS_S,0}; static struct dsc$descriptor s2_desc={0,DSC$K_DTYPE_T,DSC$K_CLASS_D,0}; static char null = '\0'; static struct dsc$descriptor null_desc={1,DSC$K_DTYPE_T,DSC$K_CLASS_S,&null}; long int status; s1_desc.dsc$w_length = strlen(s1); s1_desc.dsc$a_pointer = s1; status = cli$get_value(&s1_desc,&s2_desc); if (status & 1) { str$append(&s2_desc,&null_desc); *s2 = s2_desc.dsc$a_pointer; } else *s2 = 0; return(status); } process_command_line() { long int status,boss_len; short int length; $DESCRIPTOR(d_line,buf); $DESCRIPTOR(d_cldline,buf); $DESCRIPTOR(d_boss_term,"BOSS$TERM"); $DESCRIPTOR(d_lnm_file_dev,"LNM$FILE_DEV"); strcpy(buf,"BOSS "); boss_len = strlen(buf); d_line.dsc$w_length = d_line.dsc$w_length - boss_len; d_line.dsc$a_pointer = d_line.dsc$a_pointer + boss_len; check(lib$get_foreign(&d_line,0,&length,0)); buf[length + boss_len] = '\0'; d_cldline.dsc$w_length = length + boss_len; status = cli$dcl_parse(&d_cldline,BOSS_CLD,0,0,0); if (bad(status)) exit(STS$K_ERROR+STS$M_INHIB_MSG); status = cli_present("UW"); uw = !bad(status); if (!uw) { nproc = nprocmax; defmode = 'b'; first_name = 'A'; last_name = 'Z'; } else { nproc = P1_NWINDOW; defmode = 'p'; first_name = '1'; last_name = '7'; } status = cli_get_value("COMMAND_CHARACTER",&retval); if (bad(status)) { if (!uw) ctlchar = 034; else ctlchar = -1; } else sscanf(retval,"%d",&ctlchar); if (ctlchar < (uw ? -1 : 0) || ctlchar > 0177 || ctlchar == 032) { printf("[Illegal command character (%d); using C-\\ instead]\n",ctlchar); ctlchar = 034; /* disallow C-z as a command character */ } if (ctlchar >= 0 && ctlchar < 040) sprintf(ctlchar_str,"C-%c",tolower(ctlchar+0100)); else if (ctlchar == 040) strcpy(ctlchar_str,"SPC"); else if (ctlchar == 0177) strcpy(ctlchar_str,"DEL"); else if (ctlchar == -1) strcpy(ctlchar_str,"---"); else sprintf(ctlchar_str,"%c",ctlchar); status = cli_get_value("BEGIN_PROMPT",&retval); if (bad(status)) strcpy(prompt_begin,""); else strcpy(prompt_begin,retval); status = cli_get_value("END_PROMPT",&retval); if (bad(status)) strcpy(prompt_end,""); else strcpy(prompt_end,retval); status = cli_get_value("DEFAULT_OUTPUT_FLAG",&retval); if (!bad(status)) defmode = tolower(retval[0]); status = cli_get_value("PROCESS_DEFAULT",&retval); if (bad(status)) defproc = CREATE; else defproc = ((retval[0] == 'T') ? TOP : CREATE); status = cli_present("SWITCH_CREATE"); switch_create = !bad(status); status = cli_present("FLOW_CONTROL"); flow_control = !bad(status) || uw; status = cli_get_value("DELETE_CHARACTER",&retval); if (bad(status)) delete_char = 0177; else sscanf(retval,"%d",&delete_char); if (delete_char < 0 || delete_char > 0177) { printf("[Illegal delete character (%d); using DEL instead]\n",delete_char); delete_char = 0177; } status = cli_get_value("AUTO_STUFF_STRING",&retval); if (bad(status)) def_stuff_len = 0; else { strcpy(def_stuff_buf,retval); if (strlen(retval) > 0) strcat(def_stuff_buf,"\015"); def_stuff_len = strlen(def_stuff_buf); } status = SYS$TRNLNM(0,&d_lnm_file_dev,&d_boss_term,0,&trnlnm_item); if (bad(status)) strcpy(trnlnm_string,"VT100"); else trnlnm_string[trnlnm_string_len] = '\0'; if (strcmp(trnlnm_string,"VT100") == 0) { /* VT100 */ clr = "\033[r\033[4l\033[H\033[2J"; /* Clear screen reset scroll */ bos = "\033[r\033[4l\033[99;1H\n"; /* Go to bottom of screen */ ceol = "\033[K"; /* Clear to end-of-line */ ceoln = "\033[K\r\n"; /* Clear to end-of-line and newline */ } else if (strcmp(trnlnm_string,"VT52") == 0) { /* VT52 */ clr = "\033H\033J"; bos = "\033Y7 \n"; ceol = "\033K"; ceoln = "\033K\r\n"; } else if (strcmp(trnlnm_string,"ADM3A") == 0) { /* ADM3A */ clr = "\032"; bos = "\033=7 \n"; ceol = " \010\010\010"; ceoln = " \r\n"; } else { /* UNKNOWN */ clr = "\r\n"; bos = "\r\n"; ceol = ""; ceoln = "\r\n"; } } int start_up() { long int status,j,n; char nname[30], output_flags[30], stuff_flag, odefmode; cur = -1; if (!uw) input_state = PENDING; init = 1; status = cli_present("START_PROCESS"); if (!bad(status)) { stuff_flag = 1; j = 0; while (j < 30 && !bad(cli_get_value("START_PROCESS",&retval))) { if (strlen(retval) != 1) break; nname[j] = toupper(retval[0]); if (nname[j] < first_name || nname[j] > last_name) break; j++; } n = j; j = 0; while (j < n && !bad(cli_get_value("OUTPUT_FLAGS",&retval))) { output_flags[j] = tolower(retval[0]); j++; } while (j < n) { output_flags[j] = defmode; j++; } for (j = 0; j < n; j++) { if (stuff_flag) { status = cli_get_value("STUFF_STRING",&retval); if (bad(status)) { stuff_flag = 0; strcpy(stuff_buf,""); } else { strcpy(stuff_buf,retval); if (strlen(retval) > 0) strcat(stuff_buf,"\015"); } } odefmode = defmode; defmode = output_flags[j]; status = mov_to(nname[j],0,stuff_buf,defproc); defmode = odefmode; if (bad(status)) break; input_state = NORMAL; } } init = 0; for (j = 0; j < nproc; j++) if (name[j] > 0) post_pty_reads(j); } main( ) { int exit_handler[4] = {0,quit,0,&st}; process_command_line(); check(SYS$DCLEXH(&exit_handler)); /* Define Exit handler (quit) */ if (ctlchar >= 0 && ctlchar < 040) printf("Begin BOSS %s\nType control-%c control-h for information\n", VERSION,tolower(ctlchar+0100)); else if (ctlchar < 0) printf("Begin BOSS %s\n",VERSION); else printf("Begin BOSS %s\nType %s control-h for information\n", VERSION,ctlchar_str); initialize(); sys$hiber(); }