From:	CSBVAX::MRGATE!@KL.SRI.Com:asbed%acrux.usc.edu@usc-oberon.arpa@SMTP  8-JUL-1987 21:55
To:	EVERHART
Subj:	photo

Received: from oberon.USC.EDU by KL.SRI.COM with TCP; Wed 8 Jul 87 14:33:48-PDT
Received: by oberon.USC.EDU (5.51/5.5) id AA03898; 
	Wed, 8 Jul 87 14:25:06 PDT
Received: by acrux.usc.edu (3.2/SMI-3.0DEV3)
	id AA23810; Wed, 8 Jul 87 14:24:55 PDT
Date: Wed 8 Jul 87 14:24:51-PDT
From: Liquid Len <ASBED%acrux.usc.edu@oberon.USC.EDU>
Subject: photo
To: info-vax@sri-kl
Message-Id: <SUN-MM(195)+TOPSLIB(124) 8-Jul-87 14:24:51.acrux.usc.edu>

  Ok Ok! You asked for it...
  Cut where it says cut here, and put it in a COM file and execute it.
It works for me, so obviously it'll work for you too. :-)
  It yields PHOTO.C, PHOTO.CLD, PHOTO.HLP. Then it formats the HLP file
into PHOTO.HLB which you can insert into your local/system help library.
Then It compiles and links(/notrace) the source file, so you can put it
in your system directory and install it (with priv PHY_IO)
  The CLD file can go into your favorite DCLtables. 
  All instructions are at the top of the source file.
  Best-o-luck. Let me know if you have problems.

_________________________________________________________________________
Asbed Bedrossian, University Computing Services, U of Southern California
  ARPA: asbed@oberon.usc.edu              BITNET: asbed@uscvaxq
  UUCP: {sdcrdcf, cit-vax}!oberon!asbed   PHONE: (213) 743-4266


$!============================= Cut Here ===============================
$
$ Create Photo.C
$ Deck
/* PHOTO.C
 *
 * Copywrite 1986, University of Southern California
 * Author - Asbed Bedrossian
 * This program may be copied or distributed for any non-commercial
 * purpose, as long as this notice remains intact.
 *
 * This program is used to log a terminal session.
 *
 * Compile: CC PHOTO
 * Link:    LINK/NOTRACE PHOTO
 * Place:   SYS$SYSTEM:
 * Install: INSTALL> ADD SYS$SYSTEM:PHOTO/PRIV=PHY_IO
 *
 * For proper execution, You will need to install KVC's PseudoTerminal
 * driver on your system. You need PYDRIVER.EXE and PTDRIVER.EXE.
 * (Available through anonymous FTP from somewhere)
 * These drivers can be installed with the following SYSGEN commands:
 *   SYSGEN> RELOAD SYS$SYSTEM:PTDRIVER
 *   SYSGEN> CONNECT TPA0/NOADAPTER/DRIVER=PTDRIVER
 *   SYSGEN> RELOAD SYS$SYSTEM:PYDRIVER
 *   SYSGEN> CONNECT PYA0/NOADAPTER/DRIVER=PYDRIVER
 *
 * Note: that really is TPA0 and not PTA0. I didn't mistype it. The
 * change took effect ever since DEC came up with a PTA0:.
 *
 */

#include DESCRIP
#include FAB
#include IODEF
#include RAB
#include TTDEF
#include TT2DEF
#include stdio

#define  CR		13
#define  LF		10
#define  logchar        28	       /* Logging on/off flag ^\ */
#define  timchar	29	       /* Time logging flag ^] */
#define  ttchrlen       12
#define  tpdevlen       15
#define  mbsiz          40
#define  maxsiz         80
#define  linesz        512

#define  bad(j)         !(j & 1)
#define  check(a)	st=a; if (bad(st)) LIB$SIGNAL(st)
#define  garbage(a)	(a == CR) || (a == LF) || (!a)

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;
               };

long py_chn, py_mb_chn,
     tt_chn, tt_mb_chn,
     pid,
     st;

char finaltp[tpdevlen],
     line[linesz], time[24],
     py_mb[mbsiz], tt_mb[mbsiz],
     ttline[linesz],   tpline[linesz],
     tim= 0, com= 0, log= 1, enable_hangups= 0;

struct CHARBLK tt_chr, tt_sav_chr;
struct IOSBBLK tiosb, piosb, miosb;
struct FAB     zfab;
struct RAB     zrab;

quit()			       /* This is done upon exiting, by exit handler */
{int j;

j= SYS$DELMBX(py_mb_chn);
if (bad(j))
  printf("[SYS$DELMBX pseudo-mbx deletion failed]\n");
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 PHOTO session\n");
}

low_lib_spawn(pty_io,pid)  /* Spawns subprocess to speaks to pseudo terminal */
char *pty_io;
int  *pid;
{int flg= 1;

$DESCRIPTOR(d_pty_io, pty_io);  /* PTY name + number */
d_pty_io.dsc$w_length= strlen(pty_io);
st= LIB$SPAWN(0,&d_pty_io,&d_pty_io,&flg,0,pid,0,0,0,0,0,0);
return(st);
}

asctim()			       /* Returns Time-stamp info */
{char buf[24];

$DESCRIPTOR(d_asctim,&buf);
check(SYS$ASCTIM(0,&d_asctim,0,0));
buf[20]= 0;
sprintf(&time,"[%s]",&buf);
}

toggle(swtch)			       /* Set switches as desired */
char swtch;
{char buf[40];

if (swtch == logchar)
  {
  log= !log;
  if (log)
    sprintf(&buf,"Logging has been ENABLED\r\n");
  else
    sprintf(&buf,"Logging has been DISABLED\r\n");
  }
else
  {
  tim= !tim;
  if (tim)
    sprintf(&buf,"Time stamping has been ENABLED\r\n");
  else
    sprintf(&buf,"Time stamping has been DISABLED\r\n");
  }
check(SYS$QIOW(0,tt_chn,IO$_WRITEVBLK,0,0,0,&buf,strlen(&buf),0,0,0,0));
}

tp_srv()       /* AST: on pseudo terminal. Deals with spurious hangups */
{
check(miosb.stats);
if (enable_hangups) 
  exit(1);
else
  check(SYS$QIO(0,py_mb_chn,IO$_READVBLK,&miosb,&tp_srv,0,&py_mb,mbsiz,0,0,0,0));
}

tt_srv()			       /* AST: Read on real terminal */
{int j;

check(tiosb.stats);
				       /* Read everything typed right away */
check(SYS$QIOW(0,tt_chn,IO$_READVBLK|IO$M_TIMED,&tiosb,0,0,&ttline,linesz,0,0,0,0));
j= tiosb.tmoff + tiosb.tmsiz;	       /* How many chars read */
com= ttline[0] == CR;
if (com)
  {
  if (tim) asctim();		       /* Time stampi info */
  }
else
  if ((ttline[0] == logchar) || (ttline[0] == timchar))   /* flags */
    toggle(ttline[0]);
		       /* write everything we read to pseudo-terminal */
check(SYS$QIOW(0,py_chn,IO$_WRITEVBLK,&tiosb,0,0,&ttline,j,0,0,0,0));
check(tiosb.stats);
				       /* re-post read AST on real term */
check(SYS$QIO(0,tt_mb_chn,IO$_READVBLK,&tiosb,&tt_srv,0,&tt_mb,mbsiz,0,0,0,0));
}

char *clean_up(num)
int *num;
{int j= 0, k;
 char *rad;

rad= &tpline;
k= *num;
while ((garbage(tpline[j])) && (k))    /* Kill beginning of line CR&LFs */
  {
  rad++;
  k--;
  j++;
  }
j= *num - 1;
while ((garbage(tpline[j])) && (k))    /* Kill end of line CR&LFs */
  {
  tpline[j--]= 0;
  k--;
  }
*num= k;
return(rad);
}

log_line(num)			       /* This logs the input into a file */
int num;
{char *rad, eol;

eol= garbage(tpline[num-1]);
rad= clean_up(&num);		       /* strip line of extraneous CRLFs */
strncat(&line,rad,num);
if (eol)
  {
  num= strlen(&line);
  line[num]= 0;
  zrab.rab$l_rbf= &line;
  zrab.rab$w_rsz= num;
  check(SYS$PUT(&zrab,0,0));	       /* write line into file */
  line[0]= 0;
  if ((com) && (tim))		       /* are we Time-stamping? */
    {
    zrab.rab$l_rbf= &time;
    zrab.rab$w_rsz= strlen(&time);
    check(SYS$PUT(&zrab,0,0));	       /* if yes: write Time-stamp in file */
    }
  }
}

get_file(filspec)		       /* Sets up a file RAB and FAB */
char *filspec;
{
zfab= cc$rms_fab;
zrab= cc$rms_rab;
zfab.fab$b_fac= FAB$M_GET+FAB$M_PUT;
zfab.fab$l_fna= filspec;
zfab.fab$b_fns= strlen(filspec);
zfab.fab$b_org= FAB$C_SEQ;
zfab.fab$b_rat= FAB$M_CR;
zfab.fab$b_rfm= FAB$C_VAR;
zfab.fab$b_shr= FAB$M_SHRPUT | FAB$M_SHRGET | FAB$M_UPI;
check(SYS$CREATE(&zfab,0,0));
zrab.rab$l_fab= &zfab;
zrab.rab$w_isi= 0;
zrab.rab$l_rop= RAB$M_EOF;
check(SYS$CONNECT(&zrab, 0, 0));
zrab.rab$b_rac= RAB$C_SEQ;
zrab.rab$l_rop= RAB$M_WBH;
}

get_tt_info()
{
$DESCRIPTOR(d_tt, "SYS$COMMAND");
			       /* Get a channel & mailbox of terminal */
check(LIB$ASN_WTH_MBX(&d_tt,&mbsiz,&maxsiz,&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 & ~TT$M_TTSYNC;   /* it will have ^S */
tt_chr.xchar|= TT2$M_PASTHRU;	       /* it will be PASTRHU */
check(SYS$QIOW(0,tt_chn,IO$_SETMODE,0,0,0,&tt_chr,ttchrlen,0,0,0,0));
}

fix_a_tp()			       /* Set up a Pseudo term */
{int efn=1, f1[3], tp_chn;
 struct IOSBBLK iosb;

$DESCRIPTOR(d_pynam,"PYA0:");	       /* Template. */
$DESCRIPTOR(d_finaltp, &finaltp);
				       /* Assign a mailbox to PYA */
check(LIB$ASN_WTH_MBX(&d_pynam,&mbsiz,&maxsiz,&py_chn,&py_mb_chn));
		       /* this gives us a TPA number through SENSEMODE */
check(SYS$QIOW(0,py_chn,IO$_SENSEMODE,&iosb,0,0,&f1,ttchrlen,0,0,0,0));
sprintf(&finaltp,"TPA%d:",iosb.tmntr); /* see? */
d_finaltp.dsc$w_length= strlen(&finaltp);
check(SYS$ASSIGN(&d_finaltp,&tp_chn,0,0));   /* Get a channel on this TPA */
				       /* Make it look like a terminal */
check(SYS$QIOW(0,tp_chn,IO$_SETCHAR,0,0,0,&tt_sav_chr,ttchrlen,0,0,0,0));
check(SYS$DASSGN(tp_chn));	       /* We don't need it. only the mailbox */
				       /* in fact keeping it kills us. */
}

post_reads()			       /* This sets up ASTs */
{
				       /* Read AST on Pseudo-term */
check(SYS$QIO(0,py_mb_chn,IO$_READVBLK,&miosb,&tp_srv,0,&py_mb,mbsiz,0,0,0,0));
				       /* Read AST on real term */
check(SYS$QIO(0,tt_mb_chn,IO$_READVBLK,&tiosb,&tt_srv,0,&tt_mb,mbsiz,0,0,0,0));
}

begin_session()
{int j;

for(;;)				       /* Read/write forever loop */
  {
				       /* Read the pseudo-term dry */
  check(SYS$QIOW(0,py_chn,IO$_READVBLK,&piosb,0,0,&tpline,linesz,0,0,0,0));
  check(piosb.stats);		       /* everything fine? */
  j= piosb.tmoff + piosb.tmsiz;	       /* How much stuff did we read? */
				       /* Write the stuff to the terminal */
  check(SYS$QIO(1,tt_chn,IO$_WRITEVBLK,&tiosb,0,0,&tpline,j,0,0,0,0));
  check(SYS$WAITFR(1));		       /* for some reason QIOW does not work */
  if (log)
    {
    log_line(j);		       /* If we're looging, log it. */
    if ((tim) && (com)) 
      {
      sprintf(&time,"%s\r\n",&time);   /* Time stamp */
      check(SYS$QIOW(0,tt_chn,IO$_WRITEVBLK,0,0,0,&time,strlen(&time),0,0,0,0));
      com= 0;
      }
    }
  }
}

main(how_many,rscan)
int how_many;
char *rscan[];
{int  exit_handler[4]= {0,quit,0,&st};
 char filnam[40];

check(SYS$DCLEXH(&exit_handler));      /* Define Exit handler (quit) */
if (how_many > 1)
  strcpy(&filnam,rscan[1]);
else
  strcpy(&filnam,"Photo.Log");	       /* default log file if none specified */
get_file(filnam);		       /* Open the file */
get_tt_info();			       /* Get terminal channel & info */
fix_a_tp();			       /* Set a pseudo terminal by TT info */
check(SYS$CANCEL(py_chn));	       /* Don't need this Half of pseudo-ter */
check(low_lib_spawn(&finaltp,&pid));   /* Spawn a subprocess. */
post_reads();
enable_hangups= 1;
printf("Begin PHOTO session\n");
begin_session();
}
$EOD
$
$ Create Photo.Cld
define verb PHOTO
	image sys$system:photo
	parameter p1 value(type=$rest_of_line)
$
$ Create Photo.Hlp
1 Photo
!********************************************************
!*                                                      *
!*                                                      *
!*                      Photo                           *
!*                                                      *
!*                                                      *
!********************************************************

Photo copies all of the Input/Output (IO) that  appears  on  the
terminal  to  a  file.   It  is  useful  for turning in homework
assignments, which do most of their IO to the terminal.

Format:

           $ PHOTO [FILENAME.EXT]

This starts recording, and creates a file called FILENAME.EXT in
the  user's  current directory.  If a filename is not specified,
the  default  filename  is  PHOTO.LOG,  in  the  user's  current
directory.  To end PHOTO, either LOGOUT or POP.
2 Author
Photo was written by Asbed Bedrossian.   The  program  uses  the
PTDRIVER   PYDRIVER   combination  to  trap  terminal  IO  on  a
pseudoterminal.  The PT/PY package is  the  latest  update  from
Kevin Carosso (@Hughes) on the old PTYDRIVER.
2 Usage
Photo is a predefined command.  The character ^\ toggles logging
to  the  photo  file.   The  character ^] toggles Time stamping.
Time stamping will  appear  as  the  date  and  time  in  square
brackets after every command the user types on the terminal.
$
$ Library/Create/Help Photo.Hlb Photo.Hlp
$ Cc Photo
$ Link/Notrace Photo
$ Exit
-------
