/* BEGIN DOCUMENTATION Name: TCMC.C Created: 07/18/84 DTS Last Update: Title: TCM C User Interface Index: Text editor, editor, TCM Abstract: Invokes separate TCM editing task to aquire textual data from a CRT. Usage: See "TCM.DOC" for C 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 See Also: TCM.C, TCMPLI.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: 07/18/84 DTS: Copied TCMPLI.C to make C callable interface END DOCUMENTATION */ #include #include #include #include #include "tcmdefs.h" #include "tcmedopt.h" #include "tcmerr.h" /* #define debug 0 */ /* #define test 0 */ #define TILUN 5 /* External Routines */ 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 extern int tcmdsw; /* DSW of last directive error */ static int tcmevf; /* Event flag used for receive, abort */ static int tcflag; /* Event flag for asynchronous TCM done */ static pointer tcmxst = NULL; /* Pointer to exit_status for async calls */ static pointer tcmtrm = NULL; /* Pointer to terminator character for async calls */ static struct plioptions *tcmopt = NULL; /* Pointer to options for async edit */ 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 */ boolean tcmtat = true; /* Terminal attached flag */ boolean tcmtsl = false; /* TCM slaved flag */ char tcmotf[] = "SY:TCM.TXT"; /* Default output file for TWINSA */ /* TINIT: (INPUT: e_flag; OUTPUT: rc); globals in: tcmstate,tcmslv,tcmevf,tskabo; globals out:tcmstate,tcmslv,tcmevf,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 terminal slave THEN srbuf.opflag := 1; term_slave := true; ELSE term_slave := false; srbuf.opflag := 0; set terminal to slave; ENDIF IF terminal attached THEN term_attached := true; detach terminal; ELSE term_attached := false; ENDIF IF TCM not started THEN Translate running task name to ASCII tcmaborted := false Spawn "...TCM" slave task (set tcmaborted on exit) with our name. set tcm started flag ELSE IF TCM busy THEN wait for last command to complete ENDIF ENDIF Send (cmd := 'I') Enable receive ast for e_flag Receive (from any task) rc status (or TCM exit) Set TCMSLV (slave task) to sender's name Restore terminal state (Attached & Slave) */ #define CMDLEN 10 tinit(eflag,rc) int *eflag,*rc; { char cmdline[CMDLEN]; register char *cmdptr; int srsize; iff (tcmstate == off) or tskabo then { tcmevf = *eflag; #ifdef test cmdptr = cpystr(cmdline,"TCT "); /* Set up command line */ #else cmdptr = cpystr(cmdline,"TCM "); /* Set up command line */ #endif 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,tcmevf,&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,w_def; 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_def.w_name; srbuf.row := w_def.row; srbuf.col := w_def.col; srbuf.height := w_def.height; srbuf.width := w_def.width; srbuf.fillchr := w_def.fillchr; srbuf.lines := w_def.virt_lines; IF w_def.max_chars is defined THEN srbuf.max_chars := w_def.max_chars; ELSE srbuf.max_chars := 0; ENDIF IF init_data != "" THEN srbuf.opflag := 1; ELSE srbuf.opflag := 0; ENDIF IF w_def.borders != 0 THEN srbuf.opflag := srbuf.opflag + 2; Send (cmd := 'C') IF srbuf.opflag is odd THEN Send init_data (text or file name) ENDIF Receive status (rc) ENDIF */ twincr(w_name,init_data,w_def,rc) charpointer w_name,init_data; struct pliwdef **w_def; pointer rc; { register int optns; register struct pliwdef *win_def; iff (*rc = tcm_status()) != TS_SUC then goto twincrxit; setname(w_name); win_def = *w_def; srbuf.row = win_def->row; srbuf.col = win_def->col; srbuf.height = win_def->height; srbuf.width = win_def->width; srbuf.fillchr = win_def->fillchr; srbuf.virt_lines = win_def->virt_lines; srbuf.max_chars = win_def->max_chars; optns = ifx strlen(init_data) > 0 thenx INITEXT elsex 0; iff (int) win_def->borders != 0 then optns += BORDER; srbuf.opflag = (char) optns; #ifdef debug msgti("\033[22;1H opt = %, w_def = %, row = %, maxch = %.",optns,(int) *w_def,srbuf.row,srbuf.max_chars); #endif tcmdet(TILUN); /* Detach terminal */ iff (tcmdsw = tsndcmd('C')) == IS_SUC then iff (optns & INITEXT) != 0 then tcmdsw = vsend(&tcmslv,init_data,strlen(init_data)); iff tcmdsw == IS_SUC then recv_status(rc); else *rc = TE_SRV; tcmatt(TILUN); /* Attach terminal */ twincrxit: ; } /* end twincr */ /* TCOLL: (INPUT:w_name,options,[e_flag] ; OUTPUT:options.line,options.column, [term],rc,exit_status); Program Logic: IF tcm_status() != TS_SUC exit with rc := tcm_status; ELSE srbuf.name := w_name; srbuf.col := options.column; srbuf.row := options.line; srbuf.virt_lines := options.err_level; srbuf.max_chars := options.max_time; IF options.status_line != 0 THEN srbuf.opflag := 1; ELSE srbuf.opflag := 0; ENDIF IF options.read_only != 0 THEN srbuf.opflag := srbuf.opflag + 2; ENDIF IF options.insert != 0 THEN srbuf.opflag := srbuf.opflag + 4; ENDIF srbuf.cmd := 'E'; Send (cmd := 'E'); IF term_attached THEN detach terminal; ENDIF IF e_flag = 0 (or null) THEN tcflag := 0; Receive Status(exit_status); IF term_attached THEN * was attached * attach terminal; ENDIF IF term_slave = false THEN * wasn't slave * set terminal to noslave; ENDIF ELSE tcflag := e_flag; tcm state := edit rc := TS_SUC; ENDIF ENDIF */ tcoll(w_name,options,e_flag,term,rc,exitstat) charpointer w_name; struct plioptions **options; pointer e_flag,term,rc,exitstat; { int optns; iff (*rc = tcm_status()) == TS_SUC then { tcmxst = exitstat; tcmopt = *options; tcflag = ifx e_flag == NULL thenx 0 elsex *e_flag; tcmtrm = ifx term == NULL thenx NULL elsex term; setname(w_name); srbuf.row = (*options)->line; srbuf.col = (*options)->column; srbuf.virt_lines = (*options)->errlevel; srbuf.max_chars = (*options)->maxtime; #ifdef debug msgti("\033[22;22H opt = %, erlvl = %.",(int) options,srbuf.row); #endif iff (int) (*options)->statlin != 0 then optns = STATLIN; else optns = 0; iff (int) (*options)->readonly != 0 then optns = optns + READOLY; iff (int) (*options)->insrtflg != 0 then optns = optns + INSRT; srbuf.opflag = (char) optns; iff tcflag != 0 then tcmstate = edit; tcmdet(TILUN); /* Detach terminal */ tcmdsw = tsndcmd('E'); iff (tcflag == 0) and (tcmdsw == IS_SUC) then { recv_status(tcmxst); tcmopt->line = ret.bline; tcmopt->column = ret.bcol; tcmatt(TILUN); /* Attach terminal */ iff tcmtrm != NULL then *tcmtrm = ret.term; } else iff tcmdsw == IS_SUC then *rc = TS_SUC; else *rc = TE_SRV; } } /* end tcoll */ /* TRETRV: (INPUT:w_name,line ; 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; srbuf.row := line; Send (cmd := 'R') Receive stng Receive Status ENDIF */ tretrv(w_name,stng,stlen,line,rc) charpointer w_name,stng; pointer line,rc; int stlen; { static int srsize; iff (*rc = tcm_status()) == TS_SUC then { setname(w_name); srbuf.row = ifx line == NULL thenx NULL elsex *line; iff (tcmdsw = tsndcmd('R')) != IS_SUC then *rc = TE_SRV; else iff (*rc = tflgwait()) == TS_SUC then { srsize = stlen; iff ((tcmdsw = vrecv(&tcmslv,stng,&srsize)) != IS_SUC) then *rc = TE_SRV; else { 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(w_name,text_save,e_flag,rc,exitstat) charpointer w_name,text_save; pointer e_flag,rc,exitstat; { register charpointer tsave; register int tlen; iff (*rc = tcm_status()) == TS_SUC then { tcmxst = exitstat; tcflag = ifx e_flag == NULL thenx 0 elsex *e_flag; iff (tlen = strlen(text_save)) == 0 then { tsave = tcmotf; tlen = strlen(tcmotf); } else tsave = 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); *rc = TS_SUC; } else *rc = 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(w_name,rc) charpointer w_name; pointer rc; { iff (*rc = tcm_status()) == TS_SUC then { setname(w_name); iff *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(w_name,rc) charpointer w_name; pointer rc; { 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(rc) pointer rc; { iff (*rc = tcm_status()) == TS_SUC then { tcmdet(TILUN); tcmdsw = tsndcmd('X'); iff (tcmdsw == IS_SUC) then recv_status(rc); iff (tskabo != false) and (tcmabst[0] == IS_SUC) then *rc = TS_SUC; tcmatt(TILUN); } 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 tcmexit status pointer := null */ 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 then { iff *tcmxst < 0 then rc = *tcmxst; iff tcmstate == edit then { tcmopt->line = ret.bline; tcmopt->column = ret.bcol; iff tcmtrm != NULL then *tcmtrm = ret.term; } } } else rc = TE_EVF; } tcmxst = NULL; tcmtrm = 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 */ /* SETNAME: Copy name string to srbuf.name, check length globals in: globals out: srbuf.name; */ setname(wname) charpointer wname; { register int len; iff (len = strlen(wname)) < 1 or (len > sizeof(srbuf.name) - 1) then error("Window name too long"); cpystr(&srbuf.name,wname); } /* end setname */ /* Wait for logical or of receive & abort flags */ int tflgwait() { register int rc; 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 { 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(e_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 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: IF tcmstate = edit (or saving) THEN receive tcmexit status (if pointer not 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,tcmevf,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,tcmevf,iosb,0,0) < 0 then msgti("I/O Kill error, lun %.",lun); iff qiow(IO_DET,lun,tcmevf,iosb,0,0) < 0 then msgti("Detach error, lun %.",lun); } /* end tcmdet */