/*
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 <clang.c>
#include <stdio.h>
#include <cx.h>
#include <tparbf.h>
#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 */
