From:	CRDGW2::CRDGW2::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX" 22-FEB-1991 17:53:03.26
To:	ARISIA::EVERHART
CC:	
Subj:	mirror part 1 of 2,  multiple terminals to single session

From:	RELAY-INFO-VAX@CRVAX.SRI.COM@SMTP@CRDGW2
To:	Everhart@Arisia@MRGATE

Received:  by crdgw1.ge.com (5.57/GE 1.83)
	 id AA00788; Fri, 22 Feb 91 17:36:22 EST
Received: From UCBVAX.BERKELEY.EDU by CRVAX.SRI.COM with TCP; Thu, 21 FEB 91 07:19:44 PST
Received: by ucbvax.Berkeley.EDU (5.63/1.42)
	id AA21727; Thu, 21 Feb 91 07:14:23 -0800
Received: from USENET by ucbvax.Berkeley.EDU with netnews
	for info-vax@kl.sri.com (info-vax@kl.sri.com)
	(contact usenet@ucbvax.Berkeley.EDU if you have questions)
Date: 20 Feb 91 13:46:02 GMT
From: uhccux!waikato.ac.nz!comp.vuw.ac.nz!wnv!grace!srghcxp@ames.arc.nasa.gov
Organization: DSIR, Gracefield, New Zealand
Subject: mirror part 1 of 2,  multiple terminals to single session
Message-Id: <8614@grace.dsir.govt.nz>
Sender: info-vax-request@kl.sri.com
To: info-vax@kl.sri.com

$ !	Here follows a two part share file of a program called
$ ! 	MIRROR which allows multiple terminals to connect to 
$ ! 	a single session.  They can all see the same output and 
$ ! 	can all type input.  It seems to work with most applications
$ ! 	(including EDT etc)
$ !
$ !	It is based on PHOTO and you need the TWDRIVER and PYDRIVER's
$ !
$ !	Append the two halfs, removed mail headers, and @
$ !	Then @compile.com
$ !	Then @mirror.com and answer YES on the first terminal.
$ ! 	
$ !	(only tested on VMS 5.3-1) 
$ !
$ !				Enjoy,  Chris.
$ ! 
$! ------------------ CUT HERE -----------------------
$ v='f$verify(f$trnlnm("SHARE_VERIFY"))'
$!
$! This archive created by VMS_SHARE Version 7.2-007  22-FEB-1990
$!   On 20-FEB-1991 12:58:16.43   By user SRGHCXP 
$!
$! This VMS_SHARE Written by:
$!    Andy Harper, Kings College London UK
$!
$! Acknowledgements to:
$!    James Gray       - Original VMS_SHARE
$!    Michael Bednarek - Original Concept and implementation
$!
$!+ THIS PACKAGE DISTRIBUTED IN 2 PARTS, TO KEEP EACH PART
$!  BELOW 30 BLOCKS
$!
$! TO UNPACK THIS SHARE FILE, CONCATENATE ALL PARTS IN ORDER
$! AND EXECUTE AS A COMMAND PROCEDURE  (  @name  )
$!
$! THE FOLLOWING FILE(S) WILL BE CREATED AFTER UNPACKING:
$!       1. COMPILE.COM;3
$!       2. LINK.COM;3
$!       3. MIRROR.C;84
$!       4. MIRROR.COM;7
$!       5. MIRRORSPY.C;18
$!
$set="set"
$set symbol/scope=(nolocal,noglobal)
$f=f$parse("SHARE_TEMP","SYS$SCRATCH:.TMP_"+f$getjpi("","PID"))
$e="write sys$error  ""%UNPACK"", "
$w="write sys$output ""%UNPACK"", "
$ if f$trnlnm("SHARE_LOG") then $ w = "!"
$ ve=f$getsyi("version")
$ if ve-f$extract(0,1,ve) .ges. "4.4" then $ goto START
$ e "-E-OLDVER, Must run at least VMS 4.4"
$ v=f$verify(v)
$ exit 44
$UNPACK: SUBROUTINE ! P1=filename, P2=checksum
$ if f$search(P1) .eqs. "" then $ goto file_absent
$ e "-W-EXISTS, File ''P1' exists. Skipped."
$ delete 'f'*
$ exit
$file_absent:
$ if f$parse(P1) .nes. "" then $ goto dirok
$ dn=f$parse(P1,,,"DIRECTORY")
$ w "-I-CREDIR, Creating directory ''dn'."
$ create/dir 'dn'
$ if $status then $ goto dirok
$ e "-E-CREDIRFAIL, Unable to create ''dn'. File skipped."
$ delete 'f'*
$ exit
$dirok:
$ w "-I-PROCESS, Processing file ''P1'."
$ if .not. f$verify() then $ define/user sys$output nl:
$ EDIT/TPU/NOSEC/NODIS/COM=SYS$INPUT 'f'/OUT='P1'
PROCEDURE Unpacker ON_ERROR ENDON_ERROR;SET(FACILITY_NAME,"UNPACK");SET(
SUCCESS,OFF);SET(INFORMATIONAL,OFF);f:=GET_INFO(COMMAND_LINE,"file_name");b:=
CREATE_BUFFER(f,f);p:=SPAN(" ")@r&LINE_END;POSITION(BEGINNING_OF(b));
LOOP EXITIF SEARCH(p,FORWARD)=0;POSITION(r);ERASE(r);ENDLOOP;POSITION(
BEGINNING_OF(b));g:=0;LOOP EXITIF MARK(NONE)=END_OF(b);x:=ERASE_CHARACTER(1);
IF g=0 THEN IF x="X" THEN MOVE_VERTICAL(1);ENDIF;IF x="V" THEN APPEND_LINE;
MOVE_HORIZONTAL(-CURRENT_OFFSET);MOVE_VERTICAL(1);ENDIF;IF x="+" THEN g:=1;
ERASE_LINE;ENDIF;ELSE IF x="-" THEN IF INDEX(CURRENT_LINE,"+-+-+-+-+-+-+-+")=
1 THEN g:=0;ENDIF;ENDIF;ERASE_LINE;ENDIF;ENDLOOP;t:="0123456789ABCDEF";
POSITION(BEGINNING_OF(b));LOOP r:=SEARCH("`",FORWARD);EXITIF r=0;POSITION(r);
ERASE(r);x1:=INDEX(t,ERASE_CHARACTER(1))-1;x2:=INDEX(t,ERASE_CHARACTER(1))-1;
COPY_TEXT(ASCII(16*x1+x2));ENDLOOP;WRITE_FILE(b,GET_INFO(COMMAND_LINE,
"output_file"));ENDPROCEDURE;Unpacker;QUIT;
$ delete/nolog 'f'*
$ CHECKSUM 'P1'
$ IF CHECKSUM$CHECKSUM .eqs. P2 THEN $ EXIT
$ e "-E-CHKSMFAIL, Checksum of ''P1' failed."
$ ENDSUBROUTINE
$START:
$ create 'f'
X$ set verify
X$ cc mirror
X$ cc mirrorspy
X$ define lnk$library sys$library:vaxcrtl
X$ link mirror /notrace
X$ link mirrorspy /notrace
$ CALL UNPACK COMPILE.COM;3 2084253445
$ create 'f'
X$ define lnk$library sys$library:vaxcrtl
X$ link mirror /notrace
$ CALL UNPACK LINK.COM;3 1751859181
$ create 'f'
X/* MIRROR.C
X *`20
X * `09MIRROR allows multiple terminals to connect to a single session.
X * `09Only tested on VMS5.3-1
X * `09
X * `09Chief Munger:`09Chris Pugmire, DSIR Physical Sciences
X * `09`09`09in%"srghcxp@grv.dsir.govt.nz"
X *`20
X * `09You will need the CMU-TEK terminal drivers (tw and py) and`20
X *`09GRPNAM priv so the mailboxes can talk to each other
X * `09(The tw/py drivers must match your version of vms!)
X *`20
X *`09(alternatively see the code in MIRROR_START.COM which
X *`09creates a logical name table which can be written to`20
X * `09without priv)
X *
X *`09The mirror spy program doesn't use PASTHRU mode so that`20
X * `09control y's will abort the program, this could easily be fixed.
X *
X * `09MIRROR is a munged version of photo,  the photo copyright messages
X * `09follow.
X * -----------------------------------------------------------------------
X * Copyright 1986, University of Southern California
X * Author - Asbed Bedrossian
X * This program may be copied or distributed for any non-commercial
X * purpose, as long as this notice remains intact.
X *
X * This program is used to log a terminal session.
X *
X * Compile: CC MIRROR
X * Link:    LINK/NOTRACE MIRROR,SYS$LIBRARY:CSHARE/OPT
X * Place:   SYS$SYSTEM:
X * Install: INSTALL> ADD SYS$SYSTEM:MIRROR/PRIV=(PHY_IO,GRPNAM)
X *
X * For proper execution, You will need to install CMU's PseudoTerminal (PTY)
X * driver on your system. You need PYDRIVER.EXE and TWDRIVER.EXE.
X * These drivers can be installed with the following SYSGEN commands:
X *   SYSGEN> RELOAD SYS$SYSTEM:TWDRIVER
X *   SYSGEN> CONNECT TWA0 /NOADAPTER
X *   SYSGEN> RELOAD SYS$SYSTEM:PYDRIVER
X *   SYSGEN> CONNECT PYA0 /NOADAPTER
X *
X */
X
X/*
XHistory:
X
XAsbed Bedrossian`091986`09Created File.
X
XCharles Karney`09`091987`09Spawn child process to be name MIRROR_xxxxxx
X
XBob Sloane`09`091987`09Added the IO$M_ESCAPE modifier to the QIO to
X`09`09`09`09read from the terminal and the problem went
X`09`09`09`09away.  I also fixed up my code in log_line so
X`09`09`09`09that it will handle CR/LF and LF/CR pairs
X`09`09`09`09spread across lines.
X
XChris Ho, USC`09`091988`09Fix privileges after DEC security patch v2.
X
XKevin Carosso, NRC`099-JUN-1988
X`09`09`09`09Modify for new PY/TW driver set.  Change
X`09`09`09`09the name of the pseudo-terminal from TZA
X`09`09`09`09to TWA.  Change SENSEMODE to $GETDVI to
X`09`09`09`09get unit number in DEVDEPEND.  If we
X`09`09`09`09don't have PHY_IO, issue warning but
X`09`09`09`09don't exit.  For now, ignore SS$_DATAOVERUN
X`09`09`09`09return on writes to the PY.  We really should
X`09`09`09`09watch the delivered char count in the IOSB
X`09`09`09`09and wait for an XON AST.
X*/
X
X#include DESCRIP
X#include FAB
X#include IODEF
X#include RAB
X#include TTDEF
X#include TT2DEF
X#include stdio
X#include JPIDEF
X#include SSDEF
X#include PRCDEF
X#include PRVDEF
X#include DVIDEF
X
X#define  CR             13
X#define  LF             10
X#define  logchar        28           /* Logging on/off flag `5E\ */
X#define  timchar        29           /* Time logging flag `5E`5D */
X#define  ttchrlen       12
X#define  twdevlen       15
X#define  mbsiz          40
X#define  maxsiz         80
X#define  linesz        512
X
X#define  bad(j)      !((j) & 1)
X#define  check(a)    if (bad(st = (a))) LIB$SIGNAL(st)
X#define  garbage(a)  ((a) == CR) `7C`7C ((a) == LF) `7C`7C (!(a))
Xunsigned int z_ef;
X/*---------------------------------------------------------------*/
X
X
Xstruct CHARBLK `7Bunsigned char     class,
X                                  ttype;
X                unsigned short    pgwid;
X                unsigned          ttchr    :  24;
X                unsigned char     pglen;
X                unsigned int      xchar;
X               `7D;
X
Xstruct IOSBBLK `7Bunsigned short stats,
X                               tmoff,
X                               tmntr,
X                               tmsiz;
X               `7D;
X
Xtypedef struct DVIBLK `7B
X`09unsigned short`09len, code;
X`09char`09* buffp;
X`09long`09* lenp;
X`09long`09terminate;
X`09`7D DVIBLK;
X
Xlong py_chn, py_mb_chn,
X     tt_chn, tt_mb_chn,
X     pid,st,line_index=0;
X
Xchar finaltw`5Btwdevlen`5D,
X     line`5Blinesz`5D=0, time`5B24`5D,
X     py_mb`5Bmbsiz`5D, tt_mb`5Bmbsiz`5D,
X     ttline`5Blinesz`5D,   twline`5Blinesz`5D,
X     tim= 0, com= 0, log= 1, enable_hangups= 0;
X
Xstruct CHARBLK tt_chr, tt_sav_chr;
Xstruct IOSBBLK tiosb, piosb, miosb;
Xstruct FAB     zfab;
Xstruct RAB     zrab;
X/*---------------------------------------------------------------*/
X
X
Xquit()                   /* This is done upon exiting, by exit handler */
X`7B
X    int j;
X
X    j= SYS$DELMBX(py_mb_chn);
X    if (bad(j))
X        puts ("`5BSYS$DELMBX pseudo-mbx deletion failed`5D");
X    j= SYS$QIOW(0,tt_chn,IO$_SETMODE,0,0,0,&tt_sav_chr,ttchrlen,0,0,0,0);
X    if (bad(j))
X        puts ("`5BSYS$QIO /setmode/ failed`5D");
X    puts ("\nEnd MIRROR session");
X`7D
X/*---------------------------------------------------------------*/
X
X
Xlow_lib_spawn(pty_io,pid)  /* Spawns subprocess to speaks to pseudo terminal
V */
X    char *pty_io;
X    int  *pid;
X`7B
X    long prv`5B2`5D, oprv`5B2`5D;
X    int my_pid, owner, item;
X    char procnm`5B16`5D, ownprc`5B16`5D;
X
X    $DESCRIPTOR(d_procnm, procnm);
X    $DESCRIPTOR(d_pty_io, pty_io);  /* PTY name + number */
X
X    my_pid = getpid ();
X/*
X *      check to see if we are already in MIRROR
X */
X    check (LIB$GETJPI (&JPI$_PRCNAM,0, 0, 0, &d_procnm) );
X    check (LIB$GETJPI (&JPI$_OWNER, 0, 0, &owner) );
X    sprintf (ownprc, "MIRROR_%04X", owner & 0xFFFF);
X    if (! strncmp (ownprc, procnm, 10) )
X`09`7B printf("MIRROR is in use\n"); return 2; `7D
X
X    sprintf (procnm, "MIRROR_%04X", my_pid & 0xFFFF);
X    d_procnm.dsc$w_length = 10;
X    d_pty_io.dsc$w_length = strlen (pty_io);
X
X    prv`5B0`5D = prv`5B1`5D = -1;
X    check (SYS$SETPRV (0, prv, 0, oprv) );`09/* disable image privs */
X    check (LIB$GETJPI (&JPI$_PROCPRIV, 0, 0, prv) );
X    prv`5B0`5D `7C= PRV$M_GRPNAM;`09`09`09
X    SYS$SETPRV (1, prv, 0, 0) ;`09`09/* enable normal privs + GRPNAM */
X
X    st = LIB$SPAWN (0, &d_pty_io, &d_pty_io, &1, &d_procnm, pid);
X    if (st & SS$_NORMAL)
X`09check (SYS$SETPRV (1, oprv, 0, 0) );`09/* enable image privs */
X    return st;
X`7D
X/*---------------------------------------------------------------*/
X
X
Xasctim()                   /* Returns Time-stamp info */
X`7B
X    char buf`5B24`5D;
X    $DESCRIPTOR(d_asctim,&buf);
X
X    check(SYS$ASCTIM(0,&d_asctim,0,0));
X    buf`5B20`5D= 0;
X    sprintf(&time,"`5B%s`5D",&buf);
X`7D
X/*---------------------------------------------------------------*/
X
Xtoggle(swtch)                   /* Set switches as desired */
X    char swtch;
X`7B
X    char buf`5B40`5D;
X
X    if (swtch == logchar) `7B
X`09exit(1);
X    `7D
X    else `7B
X        tim= !tim;
X        if (tim)
X            sprintf(&buf,"Time stamping has been ENABLED\r\n");
X        else
X            sprintf(&buf,"Time stamping has been DISABLED\r\n");
X    `7D
X    check(SYS$QIOW(0,tt_chn,IO$_WRITEVBLK,0,0,0,&buf,strlen(&buf),0,0,0,0));
X`7D
X/*---------------------------------------------------------------*/
X
X
Xtw_srv()       /* AST: on pseudo terminal. Deals with spurious hangups */
X`7B
X    check(miosb.stats);
X    if (enable_hangups)
X        exit(1);
X    else
X        check(SYS$QIO(0,py_mb_chn,IO$_READVBLK,&miosb,&tw_srv,0,
X                               &py_mb,mbsiz,0,0,0,0));
X`7D
X/*---------------------------------------------------------------*/
X
X
Xtt_srv()                   /* AST: Read on real terminal */
X`7B
X    int j;
X
X    check(tiosb.stats);
X                       /* Read everything typed right away */
X    check(SYS$QIO(z_ef,tt_chn,IO$_READVBLK`7CIO$M_TIMED`7CIO$M_ESCAPE,&tiosb
V,0,0,
X                               &ttline,linesz,0,0,0,0));
X    check(SYS$WAITFR(z_ef));           /* for some reason QIOW does not work
V */
Xread_again:
X    j= tiosb.tmoff + tiosb.tmsiz;           /* How many chars read */
X    if ((ttline`5B0`5D == logchar) `7C`7C (ttline`5B0`5D == timchar))   /* f
Vlags */
X            toggle(ttline`5B0`5D);
X               /* write everything we read to pseudo-terminal */
X    check(SYS$QIO(z_ef,py_chn,IO$_WRITEVBLK,&tiosb,0,0,&ttline,j,0,0,0,0));
X    check(SYS$WAITFR(z_ef));           /* for some reason QIOW does not work
V */
X    if (tiosb.stats != SS$_DATAOVERUN)
X`09check(tiosb.stats);
X
X    check(SYS$QIO(z_ef,tt_chn,IO$_READVBLK`7CIO$M_TIMED`7CIO$M_ESCAPE,&tiosb
V,0,0,
X                               &ttline,linesz,0,0,0,0));
X    check(SYS$WAITFR(z_ef));           /* for some reason QIOW does not work
V */
X    if (tiosb.stats!=SS$_TIMEOUT) goto read_again;
X
X                       /* re-post read AST on real term */
X    check(SYS$QIO(0,tt_mb_chn,IO$_READVBLK,&tiosb,&tt_srv,0,
X                               &tt_mb,mbsiz,0,0,0,0));
X`7D
X/*---------------------------------------------------------------*/
X
X
Xint lastch = -1;                /* holds last character processed for
X                                   CR/LF or LF/CR processing. <0 means
X                                   this is the first line to avoid a null
X                                   line at the start of the file */
Xlog_line(num)                   /* This logs the input into a file */
X    int num;
X`7B
X    int i;                                       /* counter var */
X    char ch;                                     /* current character */
X
X    for ( i=0; i<num; ++i ) `7B                    /* go through the whole l
Vine */
X        ch = twline`5Bi`5D;                          /* get the current char
V */
X        if ( (ch == LF && lastch >= 0 )          /* if starting a new line *
V/
X              `7C`7C line_index >= linesz-1 ) `7B      /* or the buffer is f
Vull */
X            line_index = 0;                      /* start the new line */
X            line`5Bline_index`5D = 0;                /* make sure it has an
V EOS */
X        `7D
X        if ( (lastch == LF `7C`7C lastch < 0) && ch == CR ) /* CR starting l
Vine? */
X            continue;                            /* yes - ignore it */
X        if ( lastch == CR && ch != LF ) `7B        /* check for CR/LF pair *
V/
X            line`5Bline_index++`5D = CR;             /* not CR/LF pair, file
V it */
X            line`5Bline_index`5D = 0;                /* make line a C string
V */
X        `7D
X        if ( ch != LF && ch != CR ) `7B            /* don't want these on fi
Vle */
X            line`5Bline_index++`5D = ch;             /* add the character to
V line */
X            line`5Bline_index`5D = 0;                /* and the EOS */
X        `7D
X        lastch = ch;                             /* remember the last char *
V/
X    `7D
X`7D
X/*---------------------------------------------------------------*/
X
Xget_tt_info()
X`7B
X    $DESCRIPTOR(d_tt, "SYS$COMMAND");
X                   /* Get a channel & mailbox of terminal */
X    check(LIB$ASN_WTH_MBX(&d_tt,&mbsiz,&maxsiz,&tt_chn,&tt_mb_chn));
X                   /* Get the terminal characteristics. */
X    check(SYS$QIOW(0,tt_chn,IO$_SENSEMODE,0,0,0,&tt_chr,ttchrlen,0,0,0,0));
X    tt_sav_chr= tt_chr;
+-+-+-+-+-+-+-+-  END  OF PART 1 +-+-+-+-+-+-+-+-

