/* BEGIN DOCUMENTATION Name: TCMPLI.C Created: 09/08/83 DTS Last Update: 01/13/83 Title: TCM PL/I User Interface Index: FMS, text editor, editor, TCM Abstract: Invokes separate TCM editing task to aquire textual data from a CRT that is being used concurrently by FMS. Usage: See "TCM.DOC" for PL/I usage Routine Function performed by slave task TINIT Initialize TCM, clear all text buffers TWINCR Create new window, allocate buffer space, Receive initial text data or input file name TCOLL Start edit session for named window. TRETRV Retreive text line from named window. TWINSA Save named text window to file. TWINDE Delete named window (and associated buffer). TWINRF Refresh window Screen image. TSTOP Cause TCM task to end all processing and exit. Parameters: See "TCM.DOC" Environment: RSX11M V4.0, DECUS C Compiler, AIS PL/I Compiler MMR PL/I Interface See Also: TCM.C Description: The text collection management (TCM) slave task is installed as "...TCM". This allows it to be spawned from this program and built as a multi-user task for RSX11M-PLUS. When the TCM slave is spawned it is passed a command line which contains the name of this calling task. Once this is done all data passes between the caller (this program) and the slave through a common set of task to task communication routines. These routines (vsend & vrecv) allow sending and receiving arbitarily long pieces of data. The calling task always sends a command buffer to the slave. The slave task always sends a return code back to the caller when the command is complete. Some of the commands send or receive C text strings. Example(s): Uses: Internal: See TCMNOTES.DOC Update History: 01/13/83 DTS: Added simple detach/attach around TINIT, TWINCR, TCOLL, and TWINRF END DOCUMENTATION */ #include #include #include #include #include #include #include "tcmdefs.h" #include "tcmerr.h" /* #define debug 0 */ #define plinull -1 /* Null PL/I pointer */ #define TILUN 5 /* External Routines */ extern charpointer plistr(); extern plient(); extern callpli(); extern r50toa(); extern ascr50(); extern spwn(); extern srda(); extern wtse(); /* Forward References */ extern int tsndcmd(); extern tcmabt(); extern tcm_status(); extern tcmrcv(); extern tflgwait(); /* Global Variables */ #define off 0 /* values for tcmstate */ #define idle 1 #define edit 2 #define saving 3 dsect "tcmdsw"; static int tcmdsw; /* DSW of last directive error */ dsect ""; static int tcmevf; /* Event flag used for receive, abort */ static int tcmabf; /* Event flag set if tcmaborted, not used */ static int tcflag; /* Event flag for asynchronous TCM done */ static pointer tcmxst = NULL; /* Pointer to exit_status for async calls */ static int tcmabst[8]; /* Abort status for tcm slave task */ struct cmdbuff srbuf; /* Command buffer to send to slave */ struct retbuf ret; /* Status returned from slave */ struct rad50 tcmslv; /* Task name for slave TCM task */ struct tparbf tskpar; /* Task header information */ int tcmstate = off; /* flag to mark current state */ boolean tskabo = false; /* TCM aborted flag */ char tcmotf[] = "SY:TCM.TXT"; /* Default output file for TWINSA */ /* TINIT: (INPUT: a_flag,r_flag; OUTPUT: rc); globals in: tcmstate,tcmslv,tcmevf,tskabo; globals out:tcmstate,tcmslv,tcmevf,tcmabf,tcmdsw,tskabo,tskpar; The first call to the TCM routines by a PL/I program should always be TINIT. This will spawn (SPWN$) the TCM slave task and use an event flag and an ast to indicate if the TCM task exits. A receive data ast is also specified to set an event flag on receiving data. By using an ast to set a boolean on the slave abort and receiving data at ast state the task can asynchonously receive status and set a single user event flag. After sending each command the calling task (this program) does a receive of the status (waiting for either receive data or TCM task exit). Note: This routine is very RSX11M specific! Program logic for TINIT: IF TCM not started THEN Translate running task name to ASCII tcmaborted := false Spawn "...TCM" slave task (set a_flag & tcmaborted on exit) with our name. set tcm started flag ELSE IF TCM busy THEN wait for last command to complete ENDIF Send (cmd := 'I') ENDIF Enable receive ast for r_flag Receive (from any task) rc status (or TCM exit) Set TCMSLV (slave task) to sender's name */ #define CMDLEN 10 tinit(narg,aflag,rflag,rc) int narg,*aflag,*rflag,*rc; { char cmdline[CMDLEN]; register char *cmdptr; int srsize; plient("TINIT"); iff narg != 3 then signal(NUMARGS); iff (tcmstate == off) or tskabo then { tcmevf = *rflag; tskabo = *aflag; cmdptr = cpystr(cmdline,"TCM "); /* Set up command line */ gtsk(&tskpar); r50toa(cmdptr,&tskpar.g_tstn,2); ascr50(6,"MCR...",&tcmslv); /* Send command to MCR */ tskabo = false; tcmabst[0] = 0; tcmdet(TILUN); tcmdsw = spwn(&tcmslv,NULL,tskabo,&tcmabt,tcmabst,cmdline,CMDLEN); } else { iff (*rc = tcm_status()) != TS_SUC then goto tinitxit; tcmdet(TILUN); tcmdsw = tsndcmd('I'); } iff tcmdsw != IS_SUC then { *rc = TE_SRV; goto tinitxit; }; iff (tcmdsw = srda(&tcmrcv)) != IS_SUC then /* Enable receive ast */ *rc = TE_AST; iff (*rc = tflgwait()) == TS_SUC then { srsize = sizeof(ret); iff ((tcmdsw = vrecva(&tcmslv,&ret,&srsize)) != IS_SUC) or (srsize != sizeof(ret)) then *rc = TE_SRV; else { tcmstate = idle; *rc = ret.cd; tcmatt(TILUN); } } else iff tcmabst[0] == 2 then *rc = TE_INS; tinitxit: ; } /* end tinit */ /* TWINCR: (INPUT: w_name,init_data,row,col,height,width,virt_chars, [max_chars]; OUTPUT: rc); globals in: tcmstate; globals out:tcmdsw,srbuf; Send command to create CRT window on screen Program Logic for TWINCR: IF tcm_status() != TS_SUC exit with rc = tcm_status; ELSE srbuf.name := w_name; srbuf.row := row; srbuf.height := height; srbuf.width := width; srbuf.chars := virt_chars; IF max_chars is defined THEN srbuf.max_chars := max_chars; ELSE srbuf.max_chars := 0; ENDIF IF init_data != "" THEN srbuf.opflag := 'T'; ELSE srbuf.opflag := ' '; ENDIF Send (cmd := 'C') IF srbuf.opflag = 'T' THEN Send init_data (text or file name) ENDIF Receive status (rc) ENDIF */ twincr(narg,w_name,init_data,row,col,height,width,virt_chars,max_chars,rc) int narg; plistring w_name, init_data; pointer row,col,height,width,virt_chars,max_chars,rc; { pointer retcd; plient("TWINCR"); iff narg == 8 then retcd = max_chars; else iff narg == 9 then retcd = rc; else signal(NUMARGS); iff (*retcd = tcm_status()) != TS_SUC then goto twincrxit; setname(w_name); srbuf.row = ifx row == plinull thenx 0 elsex *row; srbuf.col = ifx col == plinull thenx 0 elsex *col; srbuf.height = ifx height == plinull thenx 0 elsex *height; srbuf.width = ifx width == plinull thenx 0 elsex *width; srbuf.virt_chars = ifx virt_chars == plinull thenx 0 elsex *virt_chars; srbuf.max_chars = ifx (narg == 8) or (max_chars == plinull) thenx 0 elsex *max_chars; srbuf.opflag = ifx plilen(init_data) > 0 thenx 'T' elsex ' '; tcmdet(TILUN); /* Detach terminal */ iff (tcmdsw = tsndcmd('C')) == IS_SUC then iff srbuf.opflag == 'T' then tcmdsw = vsend(&tcmslv,plistr(init_data),plilen(init_data)); iff tcmdsw == IS_SUC then recv_status(retcd); else *retcd = TE_SRV; tcmatt(TILUN); /* Attach terminal */ twincrxit: ; } /* end twincr */ /* TCOLL: (INPUT:w_name,c_pos,err_level,max_time,[e_flag] ; OUTPUT:rc,exit_status); Program Logic: IF tcm_status() != TS_SUC exit with rc = tcm_status; ELSE srbuf.name := w_name; srbuf.opflag := c_pos; * Cursor flag * srbuf.row := err_level; srbuf.max_chars := max_time; IF terminal attached THEN srbuf.height := 1; ELSE srbuf.height := 0; ENDIF IF terminal slave THEN srbuf.width := 1; ELSE srbuf.width := 0; set terminal to slave; ENDIF srbuf.cmd := 'E'; Send (cmd := 'E'); IF srbuf.height = 1 THEN * was attached * detach terminal; ENDIF IF e_flag = 0 (or null) THEN tcflag := 0; Receive Status(exit_status); IF srbuf.height = 1 THEN * was attached * attach terminal; ENDIF IF srbuf.width = 0 THEN * wasn't slave * set terminal to noslave; ENDIF ELSE tcflag := e_flag; tcm state := edit rc := TS_SUC; ENDIF ENDIF */ tcoll(narg,w_name,c_pos,err_level,max_time,e_flag,rc,exitstat) int narg; plistring w_name,c_pos; pointer err_level,max_time,e_flag,rc,exitstat; { pointer retcd; plient("TCOLL"); iff narg == 6 then retcd = e_flag; else iff narg == 7 then retcd = rc; else signal(NUMARGS); iff (*retcd = tcm_status()) == TS_SUC then { iff narg == 6 then { tcflag = 0; tcmxst = rc; } else { tcmxst = exitstat; tcflag = ifx e_flag == plinull thenx 0 elsex *e_flag; } setname(w_name); srbuf.opflag = *plistr(c_pos); /* Cursor flag */ srbuf.row = *err_level; srbuf.max_chars = ifx max_time == plinull thenx 0 elsex *max_time; iff tcflag != 0 then tcmstate = edit; tcmdet(TILUN); /* Detach terminal */ tcmdsw = tsndcmd('E'); iff (tcflag == 0) and (tcmdsw == IS_SUC) then { recv_status(tcmxst); tcmatt(TILUN); /* Attach terminal */ } else iff tcmdsw == IS_SUC then *retcd = TS_SUC; else *retcd = TE_SRV; } } /* end tcoll */ /* TRETRV: (INPUT:w_name ; OUTPUT:stng,rc); Retrieve line from text window, wait if tcoll called with e_flag. Program Logic: IF tcm_status() != TS_SUC exit with rc = tcm_status; ELSE srbuf.name := w_name; Send (cmd := 'R') Receive stng Receive Status ENDIF */ tretrv(narg,w_name,stng,rc) int narg; plistring w_name,stng; pointer rc; { int srsize; register charpointer sp; plient("TRETRV"); iff narg != 3 then signal(NUMARGS); iff (*rc = tcm_status()) == TS_SUC then { setname(w_name); iff (tcmdsw = tsndcmd('R')) != IS_SUC then *rc = TE_SRV; else iff (*rc = tflgwait()) == TS_SUC then { srsize = stng plisize; sp = plistr(stng); iff ((tcmdsw = vrecv(&tcmslv,sp,&srsize)) != IS_SUC) then *rc = TE_SRV; else { plicpy(stng,sp,srsize); /* set PL/I string */ setf(tcmevf); /* Don't wait on receive */ recv_status(rc); } } } } /* end tretrv */ /* TWINSA: (INPUT: w_name,text_save,[e_flag]; OUTPUT: rc,exit_status); Program logic: IF tcm_status() != TS_SUC exit with rc = tcm_status; ELSE IF text_save NULL (= "") THEN text_save := TCM.TXT * set default file spec * ENDIF srbuf.name := w_name; Send (cmd := 'F'); Send text_save (output file name); RC := TS_SUC; IF e_flag null THEN tcflag := 0; ELSE tcflag := e_flag; Receive Status(exit_status); ENDIF ENDIF */ twinsa(narg,w_name,text_save,e_flag,rc,exitstat) int narg; plistring w_name,text_save; pointer e_flag,rc,exitstat; { register pointer retcd; register charpointer tsave; register int tlen; plient("TWINSA"); iff narg == 4 then retcd = e_flag; else iff narg == 5 then retcd = rc; else signal(NUMARGS); iff (*retcd = tcm_status()) == TS_SUC then { iff narg == 4 then { tcflag = 0; tcmxst = rc; } else { tcmxst = exitstat; tcflag = ifx e_flag == plinull thenx 0 elsex *e_flag; } iff (tlen = plilen(text_save)) == 0 then { tsave = tcmotf; tlen = strlen(tcmotf); } else tsave = plistr(text_save); setname(w_name); iff tcflag != 0 then tcmstate = saving; iff (tcmdsw = tsndcmd('F')) == IS_SUC then tcmdsw = vsend(&tcmslv,tsave,tlen); iff (tcflag == 0) and (tcmdsw == IS_SUC) then { recv_status(tcmxst); *retcd = TS_SUC; } else *retcd = TE_SRV; } } /* end twinsa */ /* TWINDE: (INPUT:w_name; OUTPUT:rc) Delete text window. Program Logic: IF tcm_status() != TS_SUC exit with rc = tcm_status; ELSE srbuf.name := w_name; IF w_name = " " THEN Send (cmd := 'I') ELSE Send (cmd := 'D') ENDIF Receive Status (rc) ENDIF */ twinde(narg,w_name,rc) plistring w_name; pointer rc; { plient("TWINDE"); iff narg != 2 then signal(NUMARGS); iff (*rc = tcm_status()) == TS_SUC then { setname(w_name); iff *plistr(w_name) == ' ' then tcmdsw = tsndcmd('I'); else tcmdsw = tsndcmd('D'); iff (tcmdsw == IS_SUC) then recv_status(rc); } } /* end twinde */ /* TWINRF: (INPUT:w_name; OUTPUT:rc) Refresh window on screen. Program Logic: IF tcm_status() != TS_SUC exit with rc = tcm_status; ELSE srbuf.name := w_name; Send (cmd := 'S') Receive Status (rc) ENDIF */ twinrf(narg,w_name,rc) plistring w_name; pointer rc; { plient("TWINRF"); iff narg != 2 then signal(NUMARGS); iff (*rc = tcm_status()) == TS_SUC then { setname(w_name); tcmdet(TILUN); tcmdsw = tsndcmd('S'); iff (tcmdsw == IS_SUC) then recv_status(rc); tcmatt(TILUN); } } /* end twinrf */ /* TSTOP: (INPUT: ; OUTPUT:rc) Stop text collection task. Program Logic: IF tcm_status() != TS_SUC exit with rc = tcm_status; ELSE Send (cmd := 'X') Receive Status (or exit status of TCM) ENDIF */ tstop(narg,rc) int narg; pointer rc; { plient("TSTOP"); iff narg != 1 then signal(NUMARGS); iff (*rc = tcm_status()) == TS_SUC then { tcmdsw = tsndcmd('X'); iff (tcmdsw == IS_SUC) then recv_status(rc); iff (tskabo != false) and (tcmabst[0] == IS_SUC) then *rc = TS_SUC; } tcmstate = off; } /* end tstop */ /* TCM_STATUS: Check tcm state and wait if last command busy Output: TS_SUC if tcm is running & commands are finished otherwise an error. Program Logic: IF tcm not started THEN rc := TE_STA ELSE IF tcm aborted THEN rc := TE_ACT ELSE IF tcm busy wait for last command to complete IF status > 0 then rc := TS_SUC ELSE rc := status (of last command) ENDIF */ int tcm_status() { register int rc; #ifdef debug register charpointer str; str = "\033[16;1Htcmstate = % tcmdsw = %."; msgti(str,tcmstate,tcmdsw); #endif rc = TS_SUC; /* default */ iff tcmstate == off then rc = TE_STA; else iff tskabo != false then rc = TE_ACT; else iff tcmstate != idle then { /* tcm is busy */ iff (tcmdsw = wtse(tcflag)) == IS_SUC then { /* wait on user flag */ iff (tcmxst != NULL) and (*tcmxst < 0) then rc = *tcmxst; } else rc = TE_EVF; } tcmxst = NULL; return(rc); } /* end tcm_status */ /* TSNDCMD: (INPUT:command_char) Set Command code & send to slave task globals in: tcmslv,srbuf,tskabo; globals out: srbuf.cmd; */ int tsndcmd(cmdch) char cmdch; { srbuf.cmd = cmdch; iff tskabo != false then return(IE_ACT); else return(vsend(&tcmslv,&srbuf,sizeof(srbuf))); } /* end tsndcmd */ /* STRSET: Copy PL/I string into C string */ strset(out,in) charpointer out; plistring in; { register int length; register charpointer input; length = plilen(in); for(input = plistr(in); length > 0; length--) *out++ = *input++; *out = eos; } /* end strset */ /* SETNAME: Copy PL/I name string to srbuf.name, check length globals in: globals out: srbuf.name; */ setname(wname) plistring wname; { register int len; iff (len = plilen(wname)) < 1 or (len > sizeof(srbuf.name) - 1) then signal(STRINGSIZE); strset(&srbuf.name,wname); } /* end setname */ /* Wait for logical or of receive & abort flags */ int tflgwait() { register int rc; /* unsigned int flags[4]; * RDAF 4 word buffer * iff (tcmdsw = wtlo0(tcmevm)) != IS_SUC then return(TE_SRV); else iff (tcmdsw = rdaf(&flags)) != IS_SUC then return(TE_SRV); else iff (flags[0] & (1 << (tcmevf-1))) != 0 then * TCM Aborted * return(TE_ACT); else return(TS_SUC); */ iff (tcmdsw = wtse(tcmevf)) == IS_SUC then { dsar(); clef(tcmevf); enar(); iff tskabo != false then rc = TE_ACT; else rc = TS_SUC; } else rc = TE_EVF; return(rc); } /* end tflgwait */ /* Receive status from TCM task */ recv_status(stat) pointer stat; { int srsize; srsize = sizeof(ret); iff (*stat = tflgwait()) == TS_SUC then { /* note: vrecv will hang if TCM aborted after xfer starts */ iff ((tcmdsw = vrecv(&tcmslv,&ret,&srsize)) != IS_SUC) or (srsize < sizeof(ret)) then *stat = TE_SRV; else { *stat = ret.cd; tcmdsw = ret.dsw; } iff tskabo then *stat = TE_ACT; /* Override return status if aborted */ } } /* end recv_status */ /* TCMABT: Ast for TCM abort globals in: tcmstate,tcmxst,tskpar; globals out:tcmstate,tcmevf,tcmdsw,tskabo,*tcmxst; Program Logic: unstop self (in case we were stopped) set flag(a_flag) set flag(r_flag) tcm abort := true IF tcmstate = edit (or = saving) AND tcflag > 0 THEN set flag(tcflag) *tcmexitstatus = TE_ACT ENDIF IF tcmstate != off THEN tcmstate := started ENDIF */ tcmabt() { astset(); /* save registers */ ustp(&tskpar.g_tstn); /* unstop self if stopped */ iff setf(tcmevf) < 0 then /* Note: spawn causes tcmabf to set */ tcmdsw = $dsw; tskabo = true; #ifdef debug msgti("TCM aborted status = %",tcmabst[0]); #endif iff (tcmstate == edit) or (tcmstate == saving) then { iff tcflag != 0 then iff setf(tcflag) < 0 then tcmdsw = $dsw; iff tcmxst != NULL then *tcmxst = TE_ACT; } iff (tcmstate != off) and (tcmabst[0] == IS_SUC) then tcmstate = idle; astx(1); /* remove addr of exit status block */ } /* end tcmabt */ /* TCMRCV: Ast for TCM receive Program Logic: set flag(a_flag) IF tcmstate = edit (or saving) THEN receive tcmexit status (if pointer not null) tcmexit status pointer := null IF tcflag > 0 THEN set flag(tcflag) ENDIF IF tcmstate != off THEN tcmstate := started ENDIF ENDIF */ tcmrcv() { astset(); /* save registers */ iff setf(tcmevf) < 0 then tcmdsw = $dsw; else { iff (tcmstate == edit) or (tcmstate == saving) then { iff tcmxst != NULL then recv_status(tcmxst); iff tcflag != 0 then iff setf(tcflag) < 0 then tcmdsw = $dsw; } } iff tcmstate != off then tcmstate = idle; astx(0); } /* end tcmrcv */ /*** Attach terminal (TI:) for input, set full duplex, typeahead ***/ tcmatt(lun) int lun; { int fnc; /* parm definitions for qio */ int iosb[2]; union { int i; pointer p; } devpar[6]; /* union { int dev; char name[2]; } dnam; int unit; int ttchar[5]; register int i,c; * assign TI: device to given lun * dnam.name[0] = 'T'; dnam.name[1] = 'I'; unit = 0; alun(lun,dnam.dev,unit); */ /* Attach terminal to capture all characters */ fnc = IO_ATT; iosb[0] = 0; devpar[0].i = 0; devpar[1].i = 0; devpar[2].i = 0; iff qiow(fnc,lun,tcmabf,iosb,0,devpar) < 0 then msgti("TCM Attach error dsw = %.",$dsw); /* * Set terminal characteristics for fullduplex & typeahead * fnc = SF_SMC; ttchar[0] = TC_FDX + 256; * Set full duplex * ttchar[1] = TC_ACR + 0; * Clear wrap * ttchar[2] = TC_RAT + 256; * Set typeahead * devpar[0].p = ttchar; devpar[1].i = 6; * Number of bytes in ttchar table * iff qiow(fnc,lun,eflag,iosb,0,devpar) < 0 then error("ATTACH - SMC error dsw = %d\n",$dsw); */ } /* end tcmatt */ /*** Detach terminal & kill outstanding I/O ***/ tcmdet(lun) int lun; { int iosb[2]; iff qiow(IO_KIL,lun,tcmabf,iosb,0,0) < 0 then msgti("I/O Kill error, lun %.",lun); iff qiow(IO_DET,lun,tcmabf,iosb,0,0) < 0 then msgti("Detach error, lun %.",lun); } /* end tcmdet */