From:	CRDGW2::CRDGW2::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX"  4-FEB-1991 23:22:40.07
To:	MRGATE::"ARISIA::EVERHART"
CC:	
Subj:	PTD -- PHOTO-like utility using the FTDRIVER (part 1 of 2)

Received:  by crdgw1.ge.com (5.57/GE 1.80)
	 id AA25120; Mon, 4 Feb 91 22:50:43 EST
Received: From UCBVAX.BERKELEY.EDU by CRVAX.SRI.COM with TCP; Mon,  4 FEB 91 12:25:50 PST
Received: by ucbvax.Berkeley.EDU (5.63/1.42)
	id AA28392; Mon, 4 Feb 91 12:06:49 -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: 4 Feb 91 19:09:29 GMT
From: peirce@gumby.wisc.edu  (Leonard Peirce)
Organization: Western Michigan University Academic Computing Services
Subject: PTD -- PHOTO-like utility using the FTDRIVER (part 1 of 2)
Message-Id: <1991Feb4.190929.13266@gumby.cc.wmich.edu>
Sender: info-vax-request@kl.sri.com
To: info-vax@kl.sri.com

PTD (the first part of which is included below) is a coded version of the
pseudo-code that can be found in section 9.6.1, Example 9-1 of the I/O User's Manual
Part I for VMS 5.4.  It is basically PHOTO using the FTDRIVER.  I didn't write it
but I've tried it and it does work well.  The author asked me to post it. (Sorry
it took so long, Forrest).

The second part will follow in another article.

$! ------------------ CUT HERE -----------------------
$ v='f$verify(f$trnlnm("SHARE_VERIFY"))'
$!
$! This archive created by VMS_SHARE Version 7.2-007  22-FEB-1990
$!   On  4-FEB-1991 10:49:22.74   By user PEIRCE 
$!
$! 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 75 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. FTDRIVER.C;1
$!
$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/*
X*
X* **************************************************************************
V**
X* ++
X* FACILITY:
X*
X*`09PTD
X*
X* ABSTRACT:`20
X*`09`09Simple program to demonstrate using a Pseudo Terminal.  This
X*`09program is based loosely on the popular PHOTO program that is available
X*`09on the ARPA net.  The program is discussed in Appendix B of the pseudo
X*`09terminal functional sepcification.
X*
X*
X* AUTHOR: Forrest A. Kenney`0901-Nov-1989
X*
X* Revision history:
X*
X*`09X-XX`09XXX000 `09`09Xxxxxxx X. Xxxxxx`09`09xx-Xxx-XXXX
X*`09`09Description of change.
X*
X* --
X*
X* Link Command File example
X*
X*`09$ !
X*`09$ ! Command file to link logger program
X*`09$ !
X*`09$ link LOGGER/MAP=LOGGER sys$input:/opt
X*`09!
X* `09sys$system:sys.stb/selective
X*`09sys$share:vaxcrtl/share
X*`09$ exit
X*
X* --
X*/
X`0C
X
X
X/* Define constants */
X
X#define`09BELL`09`09`090x7
X#define BUFFIO_OVERHEAD`09`0948`09/* Overhead of TERMINAL buffered I/O  */
X#define`09CR`09`09`090x0D
X#define CHAR_BUF_SIZE`09`09492`09/* Space in I/O buffer for data       */
X#define`09FALSE`09`09`090
X#define`09IO_BUFFERS`09`096`09/* Number of one page I/O buffers     */
X#define`09LF`09`09`090x0A
X#define`09NULL`09`09`090
X#define`09NOWAIT`09`09`091`09/* No wait flag for LIB$SPAWN `09      */
X#define`09PAGE`09`09`090X1FF`09/* Last byte in a page `09`09      */
X#define`09PAGE_SIZE`09`090X200`09/* Size of a page in bytes `09      */
X#define TRUE`09`09`091
X#define`09XOFF`09`09`090x13
X#define`09XON`09`09`090x11
X
X#define PTD$C_SEND_XON`09`090`09/* Pseudo Terminal Driver event       */
X#define PTD$C_SEND_BELL`09`091`09/* types. When these are in           */
X#define PTD$C_SEND_XOFF `092`09/* SYS$LIBRARY:VAXCDEF.TLB they       */
X#define PTD$C_STOP_OUTPUT`093`09/* should be removed from here.`09      */
X#define PTD$C_RESUME_OUTPUT `094
X#define PTD$C_CHAR_CHANGED `095
X#define PTD$C_ABORT_OUTPUT `096
X#define PTD$C_START_READ `097
X#define PTD$C_MIDDLE_READ `098
X#define PTD$C_END_READ `09`099
X#define PTD$C_ENABLE_READ `0910
X#define PTD$C_DISABLE_READ `0911
X#define PTD$C_MAX_EVENTS `0912
X`0C
X
X
X/* Include various necessary description files */
X
X#include`09descrip
X#include`09dvidef
X#include`09iodef
X#include`09libdef
X#include`09rms
X#include`09ssdef
X#include`09stdio
X#include`09ttdef
X#include`09tt2def
X`0C
X
X
X/* Define several gloabl structures */
X
Xstruct`09dev_char`09`09`09/* Device characteristics block       */
X`7B
Xunsigned char`09class;
Xunsigned char`09type;
Xshort`09int`09buffer_size;
Xunsigned int`09basic_chars;
Xunsigned int`09extended_chars;
X`7D;
X
Xstruct`09iosb`09`09`09`09/* Standard I/O status block`09      */
X`7B
Xshort`09int`09status;
Xshort`09int`09byte_cnt;
Xint`09`09unused;
X`7D;
X
Xstruct`09sense_iosb`09`09`09/* IOSB for set and sense requests    */
X`7B
Xshort`09int`09status;
Xunsigned char`09xmit_speed;
Xunsigned char`09rcv_speed;
Xunsigned char`09cr_fill;
Xunsigned char`09lf_fill;
Xunsigned char`09parity_flags;
Xunsigned char`09unused;
X`7D;
X
Xstruct`09io_buff`09`09`09`09/* I/O block used by logger code      */
X`7B
Xunsigned int`09flink; `09`09`09/* forward and backard queue links    */
Xunsigned int`09blink;
Xshort`09int`09status;`09`09`09/* IOSB used to terminal requests     */
Xshort`09int`09byte_cnt;
X`09int`09unused;
Xshort`09int`09io_status;`09`09/* Status longword used by pseudo     */
Xshort`09int`09io_byte_cnt;`09`09/* terminal control requests`09      */
Xchar`09`09data`5BCHAR_BUF_SIZE`5D;`09/* Data buffer`09`09`09      */
X`7D;
X
Xstruct`09q_head`09`09`09`09/* Queue head structure`09`09      */
X`7B
Xint`09`09`09flink;
Xint`09`09`09blink;
X`7D;
X`0C
X/* Forward routine references */
X
Xint`09`09`09initialization();
Xint`09`09`09create_log_file();
Xint`09`09`09create_pseudo_terminal();
Xint`09`09`09setup_tty();
X
Xstruct`09io_buff`09`09*allocate_io_buffer();
X
Xvoid`09`09`09bell_ast();
Xvoid`09`09`09free_io_buffer();
Xvoid`09`09`09ft_echo_ast();
Xvoid`09`09`09ft_read_ast();
Xvoid`09`09`09kbd_read_ast();
Xvoid`09`09`09set_line_ast();
Xvoid`09`09`09subprocess_exit();
Xvoid`09`09`09terminal_output_ast();
Xvoid`09`09`09xoff_ast();
Xvoid`09`09`09xon_ast();
X`0C
X/* Global Variables*/
X
Xchar`09`09`09*rec_buffer;
Xchar`09`09`09*char_pos;
X
Xshort`09int `09`09char_count;
Xshort`09int`09`09cr_seen = FALSE;`20
Xshort`09int`09`09exiting`09= FALSE;`20
Xshort`09int`09`09ft_chan;
Xshort`09int`09`09have_subprocess = TRUE;
Xshort`09int`09`09read_stopped = FALSE;`09
Xshort`09int`09`09tty_chan;
X
Xint`09`09`09exit_status;
Xint`09`09`09pid;
Xint`09`09`09term_mask`5B8`5D = `7B0, 0, 0, 0, 0, 0, 0, 0`7D;
X
Xglobalref`09short int`09IOC$GW_MAXBUF;
X
Xstruct`09FAB`09`09logger_fab;
Xstruct`09RAB`09`09logger_rab;
Xstruct`09dev_char`09starting_chars;
Xstruct`09sense_iosb`09starting_iosb;
Xstruct`09io_buff`09`09*tty_r_buff;
Xstruct`09q_head`09`09 _align(QUADWORD)`09buffer_queue = (0,0);
Xstruct`09q_head`09`09 _align(QUADWORD)`09log_queue = (0,0);
Xstruct`09term_descrip
X`7B
Xshort int`09size;
Xshort int`09unused;
Xint`09`09*ptr;
X`7D term_block = `7B32, 0, &term_mask`5B0`5D`7D;
X`0C
X/*
X**+
X** main - Main routine
X**
X** Functional Description:
X**
X**`09The program intitializes the environment and then hibernates waiting to
X** be awakened. When awakened, it checks to see if exiting or if more log da
Vta`20
X** is available.  If more data to log, the data is appended to the current l
Vog
X** record and checked to see if a log record should be written.  A log recor
Vd`20
X** will be written when either maxbuf characters are in the log buffer, or a
V`20
X** <CR><LF> character pair are seen.  The algorithm allows an unlimited
X** number of <NULL> fill characters to occur between the <CR> and the
X** <LF>. If exiting the program closes the log file, deletes the pseudo`20
X** terminal, resets terminal, and exits.
X**
X** Explicit Inputs:
X**
X**`09None
X**
X** Implicit Inputs:
X**
X**`09char_pos`09-`09Point to next available character position in
X**`09`09`09`09rec_buffer
X**`09char_count`09-`09Number of characters presently in rec_buffer
X**`09cr_seen`09`09-`09Flag indication if last significant character
X**`09`09`09`09seen was a <CR>
X**`09exiting`09`09-`09Flag indicating if program is finishing up
X**`09`09`09`09processing
X**`09exit_status`09-`09Contains the reason while logging is stopping
X**`09ft_chan`09`09-`09VMS channel pointing to control side of pseudo
X**`09`09`09`09terminal
X**`09have_subprocess`09-`09Flag indicating if subprocess is still running
X**`09IOC$GW_MAXBUF`09-`09System cell containing value of largest legal
X**`09`09`09`09buffered I/O
X**`09log_queue`09-`09Queue of I/O buffers to be written to log file`20
X**`09logger_fab`09-`09File Access Block of log file
X**`09logger_rab`09-`09Record Access Block of log file
X**`09pid`09`09-`09Process ID of subprocess
X**`09rec_buffer`09-`09Pointer to buffer holding characters to be
X**`09`09`09`09written to the log file
X**`09starting_chars`09-`09Terminal characteristics when logger started up
X**`09tty_chan`09-`09VMS channel pointing to terminal
X**
X** Local Variables:
X**
X**`09buffer_pos`09-`09Character position in current log buffer
X**`09got_buf_status`09-`09Return status from call to remove log buffer
X**`09`09`09`09from log queue
X**`09log_buff`09-`09Pointer to current log buffer being processed
X**`09set_iosb`09-`09I/O status block used when resetting terminal
X**`09`09`09`09to startup characteristics
X**`09status`09`09-`09Return status from various routine calls
X**`09
X**
X** Outputs:
X**
X**       None
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
X
Xmain()
X
X`7B
X
Xint`09`09`09buffer_pos;
Xint`09`09`09got_buf_status;
Xint`09`09`09status;
X
Xstruct`09io_buff`09`09*log_buff;
Xstruct`09sense_iosb`09set_iosb;
X
Xstatus= initialization();
Xif (status & SS$_NORMAL)
X`7B
X   do
X   `7B
X      got_buf_status = LIB$REMQHI(&log_queue, &log_buff);
X      while (got_buf_status & SS$_NORMAL)
X      `7B
X`09 for (buffer_pos = 0; buffer_pos < log_buff->io_byte_cnt; buffer_pos++)
X`09 `7B
X`09    if (cr_seen)
X`09    `7B
X`09       if (log_buff->data`5Bbuffer_pos`5D == LF)
X`09       `7B
X`09`09  logger_rab.rab$w_rsz = char_count;
X`09`09  status = SYS$PUT(&logger_rab);
X`09`09  if (!(status & SS$_NORMAL))`20
X                  `7B
X`09`09     SYS$FORCEX(&pid, 0, 0);
X`09`09     LIB$SIGNAL(status);
X                  `7D
X`09`09  cr_seen = FALSE;
X`09`09  char_count = 0;
X`09`09  char_pos = rec_buffer;
X`09       `7D
X`09       else if (log_buff->data`5Bbuffer_pos`5D != NULL)
X`09       `7B
X`09`09  *char_pos++ = CR;
X`09`09  *char_pos++ = log_buff->data`5Bbuffer_pos`5D;
X`09`09  char_count += 2;
X`09`09  cr_seen = FALSE;
X`09       `7D
X`09    `7D
X`09    else if (log_buff->data`5Bbuffer_pos`5D != CR)
X`09    `7B
X`09       *char_pos++ = log_buff->data`5Bbuffer_pos`5D;
X`09       char_count += 1;
X`09    `7D
X`09    else
X`09    `7B
X`09`09cr_seen = TRUE;
X`09    `7D
X
X`09    if (char_count >= (IOC$GW_MAXBUF-BUFFIO_OVERHEAD))`20
X`09    `7B
X
X`09       logger_rab.rab$w_rsz = char_count;
X`09       status = SYS$PUT(&logger_rab);
X`09       if (!(status & SS$_NORMAL))`20
X               `7B
X`09`09  SYS$FORCEX(&pid, 0, 0);
X`09`09  LIB$SIGNAL(status);
X               `7D
X`09       cr_seen = FALSE;
X`09       char_count = 0;
X`09       char_pos = rec_buffer;
X`09    `7D
X         `7D
X`09 free_io_buffer(log_buff);
X`09 got_buf_status = LIB$REMQHI(&log_queue, &log_buff);
X      `7D
X      if (!exiting) SYS$HIBER();
X   `7D while ((!exiting) `7C`7C (log_queue.flink != 0));
X
X   if (char_count != 0)
X   `7B
X       logger_rab.rab$w_rsz = char_count;
X       status = SYS$PUT(&logger_rab);
X      if (!(status & SS$_NORMAL) && (exit_status & SS$_NORMAL))`20
X`09 exit_status = status;
X   `7D
X   status = SYS$CLOSE(&logger_fab);
X   if (!(status & SS$_NORMAL) && (exit_status & SS$_NORMAL))`20
X      exit_status = status;
X   if (have_subprocess) SYS$FORCEX (&pid, 0, 0);
X   PTD$CANCEL(ft_chan);
X   SYS$CANCEL(tty_chan);
X   status = PTD$DELETE(ft_chan);
X   if (!(status & SS$_NORMAL) && (exit_status & SS$_NORMAL))`20
X      exit_status = status;
X   status = SYS$QIOW(0, tty_chan, IO$_SETMODE, &set_iosb, 0, 0, &starting_ch
Vars,
X`09`09     sizeof(starting_chars), 0, 0, 0, 0);
X   if (!(status & SS$_NORMAL) && (exit_status & SS$_NORMAL))`20
X      exit_status = status;
X   if (!(set_iosb.status & SS$_NORMAL) && (exit_status & SS$_NORMAL))
X      exit_status = set_iosb.status;
X`7D
Xelse
X`7B
X   exit_status = status;
X`7D
X
XLIB$SIGNAL(exit_status);
X
X`7D
X`0C
X/*
X**+
X** initialization - Set up environment
X**
X** Functional Description:
X**
X**`09This routine sets the terminal characteristics, creates the pseudo
X** terminal, starts up the subprocess, and opens the log file.  If any of th
Vese
X** steps fails it backs out all steps done up to this points and returns to
V the
X** mainline.
X**
X** Explicit Inputs:
X**
X**`09None
X**
X** Implicit Inputs:
X**
X**`09buffer_queue`09-`09Queue of free I/O buffer
X**`09char_pos`09-`09Point to next available character position in
X**`09`09`09`09rec_buffer
X**`09ft_chan`09`09-`09VMS channel pointing to control side of pseudo
X**`09`09`09`09terminal
X**`09IOC$GW_MAXBUF`09-`09System cell containing value of largest legal
X**`09`09`09`09buffered I/O
X**`09logger_fab`09-`09File Access Block of log file
X**`09rec_buffer`09-`09Pointer to buffer holding characters to be
X**`09`09`09`09written to the log file
X**`09starting_chars`09-`09Terminal characteristics when logger started up
X**`09starting_iosb`09-`09IOSB used to hold starting characteristics like
X**`09`09`09`09terminal speed and parity
X**`09tty_chan`09-`09VMS channel pointing to terminal
X**`09tty_r_buff`09-`09Pointer to I/O buffer used to read characters
X**`09`09`09`09from terminal
X**
X** Local Variables:
X**
X**`09loop`09`09-`09Temporary loop counter used will inserting I/O
X**`09`09`09`09buffers onto queue of free I/O buffers
X**`09rec_size`09-`09Local varible used to pass record buffer size
X**`09`09`09`09to LIB$GET_VM
X**`09ret_addr`09-`09Pointer to start and end of pages allocated for
X**`09`09`09`09I/O buffers
X**`09status`09`09-`09Return status from various routine calls
X**`09tty_name`09-`09String descriptor contianing terminal name`20
X**`09
X**
X** Outputs:
X**
X**       R0`09`09-`09SS$_NORMAL
X**`09`09`09`09Various error status values
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
X
Xinitialization(void)
X
X`7B
X
X$DESCRIPTOR(tty_name,"TT:");
X
Xint`09`09`09loop;
Xint`09`09`09rec_size;
Xint`09`09`09status;
X
Xstruct`09mem_region
X`7B
Xstruct`09io_buff`09`09*start;
Xstruct`09io_buff`09`09*end;
X`7D`09`09`09ret_addr;
X
Xrec_size = (int)IOC$GW_MAXBUF;
Xstatus = LIB$GET_VM (&rec_size, &rec_buffer);
Xif (status & SS$_NORMAL)
X`7B
X   char_pos = rec_buffer;
X   status = SYS$EXPREG (IO_BUFFERS, &ret_addr, 0, 0);
X   if (status & SS$_NORMAL)
X   `7B
X      tty_r_buff = (char *)ret_addr.end - PAGE;
X      for (loop = 0; loop < IO_BUFFERS-1; loop++)
X      `7B
X`09 free_io_buffer((char *)ret_addr.start + loop*PAGE_SIZE);
X      `7D
X      status = SYS$ASSIGN(&tty_name, &tty_chan, 0, 0);
X      if (status &SS$_NORMAL)
X      `7B
X`09 status = SYS$QIOW (0, tty_chan, IO$_SENSEMODE, &starting_iosb, 0, 0,
X`09`09`09    &starting_chars, sizeof(starting_chars), 0, 0, 0,`20
X`09`09`09    0);
X`09 if ((status & SS$_NORMAL) && (starting_iosb.status & SS$_NORMAL))
X`09 `7B
X`09    status = create_pseudo_terminal(&ret_addr);
X`09    if (status & SS$_NORMAL)
X`09    `7B
X`09        status = create_log_file();
X`09`09if (status & SS$_NORMAL)
X`09`09`7B
X`09`09   status = setup_tty();
X`09`09   if (!(status & SS$_NORMAL))
X`09`09   `7B
X`09`09      PTD$DELETE(ft_chan);
X`09`09      logger_fab.fab$l_fop = logger_fab.fab$l_fop `7C FAB$M_DLT;
X`09`09      SYS$CLOSE(&logger_fab);
X`09`09   `7D
X`09`09`7D
X`09        else
X`09`09`7B
X`09`09   PTD$DELETE(ft_chan);
X`09`09`7D
X`09    `7D
X`09 `7D
X`09 else if (status & SS$_NORMAL)
X`09 `7B
X`09    status = starting_iosb.status;
X`09 `7D
X      `7D
X   `7D
X`7D
X
Xreturn(status);
X
X`7D`20
X`0C
X/*
X**+
X** setup_tty - Start subprocess and setup terminal`20
X**
X** Functional Description:
X**
X**
X**
X** Explicit Inputs:
X**
X**`09None
X**
X** Implicit Inputs:
X**`09
X**`09ft_chan`09`09-`09VMS channel pointing to control side of pseudo
X**`09`09`09`09terminal
X**`09pid`09`09-`09Process ID of subprocess
X**`09starting_chars`09-`09Terminal characteristics when logger started up
X**`09term_block`09-`09Long form to terminal driver read termination
X**`09`09`09`09mask
X**`09tty_chan`09-`09VMS channel pointing to terminal
X**
X** Local Variables:
X**
X**`09devnam`09`09-`09String descriptor containing name of pseudo
X**`09`09`09`09terminal
X**`09flags`09`09-`09Flags passed to LIB$SPAWN set to NOWAIT
X**`09item_list`09-`09VMS item list used to request device name using
X**`09`09`09`09SYS$GETDVIW
X**`09modified_chars`09-`09Device characteristics used to setup terminal
X**`09`09`09`09to have attributes we want
X**`09set_iosb`09-`09IOSB used to synchronize various requests
X**`09status`09`09-`09Return status from various routine calls
X**`09tmp_status`09-`09Return status from various routine calls used
X**`09`09`09`09on error path so that original error status is
X**`09`09`09`09preserved
X**
X** Outputs:
X**
X**       R0`09`09-`09SS$_NORMAL
X**`09`09`09`09Various error status values
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
Xint setup_tty(void)
X
X`7B
X
X$DESCRIPTOR(devnam,"                ");
X
Xint`09`09`09flags`09= NOWAIT;
Xint`09`09`09status;
Xint`09`09`09tmp_status;
X
Xstruct`09item
X`7B
Xshort`09int`09`09buf_len;
Xshort`09int`09`09item;
Xint`09`09`09*buf_addr;
Xint`09`09`09ret_len;
Xint`09`09`09end;
X`7D`09`09`09item_list = `7Bdevnam.dsc$w_length, DVI$_DEVNAM,
X`09`09`09`09     devnam.dsc$a_pointer,`20
X`09`09`09`09     &devnam.dsc$w_length, 0`7D;
Xstruct`09dev_char`09modified_chars;
Xstruct`09sense_iosb`09set_iosb;
X
Xstatus = SYS$GETDVIW (0, ft_chan, 0, &item_list, &set_iosb, 0, 0, 0);
Xif (status & SS$_NORMAL)
X`7B
X   status = LIB$SPAWN(0, &devnam, &devnam, &flags, 0, &pid, 0, 0,`20
X`09`09      &subprocess_exit, 0);
X   if (status & SS$_NORMAL)
X   `7B
X      modified_chars = starting_chars;
X      modified_chars.basic_chars = modified_chars.basic_chars `7C TT$M_NOECH
VO;
X      modified_chars.extended_chars = modified_chars.extended_chars `7C`20
X`09`09`09`09      TT2$M_PASTHRU;
X      status = SYS$QIOW (0, tty_chan, IO$_SETMODE, &set_iosb, 0, 0,
X`09`09`09 &modified_chars, sizeof(modified_chars), 0, 0, 0, 0);
X      if ((status & SS$_NORMAL) && (set_iosb.status & SS$_NORMAL))
X      `7B
X`09 status = SYS$QIO (0, tty_chan, IO$_READVBLK, &tty_r_buff->status,
X`09`09`09   &kbd_read_ast, 0, &tty_r_buff->data`5B0`5D,
X`09`09`09   1, 0, &term_block, 0, 0);
X`09 if (!(status & SS$_NORMAL))
X`09 `7B
X`09    tmp_status = SYS$QIOW (0,tty_chan, IO$_SETMODE, &set_iosb, 0, 0,
X`09`09`09`09   &starting_chars, sizeof(starting_chars), 0,
X`09`09`09`09   0, 0, 0);
X`09    tmp_status = SYS$FORCEX (&pid, 0, 0);
X`09 `7D
X
X      `7D
X      else if (status & SS$_NORMAL)
X      `7B
X`09 status = set_iosb.status;
X`09 tmp_status = SYS$FORCEX (&pid, 0, 0);
X      `7D
X   `7D
X`7D
X
Xreturn(status);
X
X`7D
X`0C
X/*
X**+
X** create_pseudo_terminal - Create and setup pseudo terminal
X**
X** Functional Description:
X**
X**`09This routine creates the pseudo terminal enables the AST's and starts
X** the first read on the pseudo terminal.
X**
X** Explicit Inputs:
X**
X**`09ret_addr`09-`09Pointer to start and end of pages allocated for
X**`09`09`09`09I/O buffers
X**
X** Implicit Inputs:
X**
X**`09ft_chan`09`09-`09VMS channel pointing to control side of pseudo
X**`09`09`09`09terminal
X**`09starting_chars`09-`09Terminal characteristics when logger started up
X**
X** Local Variables:
X**
X**`09read_buffer`09-`09Pointer to I/O buffer used to hold data read`20
X**`09`09`09`09from the pseudo terminal
X**`09status`09`09-`09Return status from various routine calls
X**
X** Outputs:
X**
X**       R0`09`09-`09SS$_NORMAL
X**`09`09`09`09Various return status codes
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
X
Xcreate_pseudo_terminal (ret_addr)
X
Xstruct`09mem_region
X`7B
Xstruct`09io_buff`09`09*start;
Xstruct`09io_buff`09`09*end;
X`7D`09`09`09*ret_addr;
X
X
X`7B
X
Xint`09`09`09status;
X
Xstruct`09io_buff`09`09*read_buffer;
X
Xstatus = PTD$CREATE(&ft_chan, 0, &starting_chars, sizeof(starting_chars), 0,
V 0,
X`09`09    0, ret_addr);
Xif (status & SS$_NORMAL)
X`7B
X   status = PTD$SET_EVENT_NOTIFICATION (ft_chan, &bell_ast, 0, 0,
X`09`09`09`09`09PTD$C_SEND_BELL);
X   if (status & SS$_NORMAL)
X   `7B
X      status = PTD$SET_EVENT_NOTIFICATION (ft_chan, &xoff_ast, 0, 0,
X`09`09`09`09`09   PTD$C_SEND_XOFF);
X      if (status & SS$_NORMAL)
X      `7B
X`09 status = PTD$SET_EVENT_NOTIFICATION (ft_chan, &xon_ast, 0, 0,
X`09`09`09`09`09      PTD$C_SEND_XON);
X`09 if (status &SS$_NORMAL)
X`09 `7B
X`09    status = PTD$SET_EVENT_NOTIFICATION (ft_chan, &set_line_ast, 0, 0,
X`09`09`09`09`09`09 PTD$C_CHAR_CHANGED);
X`09    if (status & SS$_NORMAL)`20
X`09    `7B
X`09       read_buffer = allocate_io_buffer();
X`09       if ((read_buffer != LIB$_QUEWASEMP) &&`20
X`09`09   (read_buffer != SS$_FORCEDEXIT))
X`09       `7B
X`09`09  status = PTD$READ (0, ft_chan, &ft_read_ast, read_buffer,
X`09`09`09`09     &read_buffer->io_status, CHAR_BUF_SIZE);
X`09       `7D
X`09    `7D
X`09 `7D
X      `7D
X   `7D
X   if (!(status & SS$_NORMAL)) PTD$DELETE (ft_chan);
X`7D
X
Xreturn(status);
X
X`7D
X`0C
X/*
X**+
X** create_log_file - Initialize FAB, RAB and open log file
X**
X** Functional Description:
X**
X**`09This routine initialize the File Access Block (FAB) and the Record
X** Access Block (RAB).  It opens the file and connects the RAB to the FAB so
X** that the file is ready to have records written to it.
X**
X** Explicit Inputs:
X**
X**`09None
X**
X** Implicit Inputs:
X**`09
X** `09logger_fab`09-`09File Access Block for log file
X**`09logger_rab`09-`09Record Access Block for log file
X**
X** Local Variables:
X**
X**`09status`09`09-`09Return status from various routine calls
X**
X** Outputs:
X**
X**       R0`09`09-`09RMS$_NORMAL successful completion
X**`09`09`09`09Various RMS errors
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
X
Xint create_log_file (void)
X
X`7B
X
Xint`09`09`09status;
X
Xlogger_fab = cc$rms_fab;
Xlogger_fab.fab$l_fop = FAB$M_SUP `7C FAB$M_SQO;
Xlogger_fab.fab$b_fac = FAB$M_PUT;
Xlogger_fab.fab$b_shr = FAB$M_SHRGET;
Xlogger_fab.fab$b_org = FAB$C_SEQ;
Xlogger_fab.fab$b_rat = FAB$M_CR;
Xlogger_fab.fab$b_rfm = FAB$C_RFM_DFLT;
Xlogger_fab.fab$l_fna = "SESSION";`09
Xlogger_fab.fab$l_dna = ".LOG";
Xlogger_fab.fab$b_fns = 7;
Xlogger_fab.fab$b_dns = 4;
Xlogger_fab.fab$w_mrs = IOC$GW_MAXBUF;
X
Xstatus = SYS$CREATE(&logger_fab);
Xif (status & SS$_NORMAL)
X`7B
X   logger_rab = cc$rms_rab;
X   logger_rab.rab$l_rbf = rec_buffer;
X   logger_rab.rab$l_fab = &logger_fab;
X   status = SYS$CONNECT (&logger_rab);
X`7D
X
Xreturn (status);
X
X`7D
X`0C
X/*
X**+
X** kbd_read_ast - Process characters typed at keyboard
X**
X** Functional Description:
X**
X**`09This routine is called every time data is read from the users terminal.
V`20
X** If the program is exiting then the routine will exit without restarting t
Vhe
X** read.   The character read is checked to see if the terminate processing
X** character CTRL-\ was entered.  If the terminate processing character was`
V20
X** entered the exiting state will be set and a SYS$WAKE will be issued to st
Vart
X** the mainline code.  Now an attempt will be made to get an I/O buffer to`2
V0
X** store echoed output in.  If cannot get an I/O buffer a simple PTD$WRITE w
Vill
X** be used otherwise a PTD$WRITE with echo will be issued.  If the write`20
X** completed successfully another read read will be issued to the keyboard.
X**
X** Explicit Inputs:
X**
X**`09None
X**
X** Implicit Inputs:
X**
X**`09exiting`09`09-`09Flag indicating if program is finishing up
X**`09`09`09`09processing
X**`09exit_status`09-`09Contains the reason while logging is stopping
X**`09ft_chan`09`09-`09VMS channel pointing to control side of pseudo
X**`09`09`09`09terminal
X**`09term_block`09-`09Long form to terminal driver read termination
X**`09`09`09`09mask
X**`09tty_chan`09-`09VMS channel pointing to terminal
X**`09tty_r_buff`09-`09Pointer to I/O buffer used to read characters
X**`09`09`09`09from terminal
X**
X** Local Variables:
X**
X**`09echo_buff`09-`09Pointe to I/O buffer used to receive any
X**`09`09`09`09characters when data was written to the pseudo
X**`09`09`09`09terminal
X**`09status`09`09-`09Return status from various routine calls
X**
X** Outputs:
X**
X**       None
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
X
Xvoid`09kbd_read_ast(void)
X
X`7B
X
Xint`09`09`09status;
X
Xstruct`09io_buff`09`09*echo_buff;
X
Xif (!exiting)
X`7B
X   if (tty_r_buff->status & SS$_NORMAL)
X   `7B
X      if (tty_r_buff->data`5B0`5D != '\034')
X      `7B
X`09 echo_buff = allocate_io_buffer();
X`09 if ((echo_buff != LIB$_QUEWASEMP) && (echo_buff != SS$_FORCEDEXIT))
X`09 `7B
X`09    status = PTD$WRITE (ft_chan, &ft_echo_ast, echo_buff,
X`09`09`09`09&tty_r_buff->io_status, tty_r_buff->byte_cnt,`20
X`09`09`09`09&echo_buff->io_status, CHAR_BUF_SIZE);
X`09 `7D
X`09 else
X`09 `7B
X`09    status = PTD$WRITE (ft_chan, 0, echo_buff, &tty_r_buff->io_status,
X`09`09`09`09tty_r_buff->byte_cnt, 0, 0);
X`09 `7D
X`09 if (status & SS$_NORMAL)`20
X`09 `7B
X`09     if ((tty_r_buff->io_status & SS$_NORMAL) `7C`7C
X`09`09 (tty_r_buff->io_status == SS$_DATAOVERUN))
X`09     `7B
X`09`09status = SYS$QIO (0, tty_chan, IO$_READVBLK,
X`09`09`09`09  &tty_r_buff->status, &kbd_read_ast, 0,
X`09`09`09`09  &tty_r_buff->data`5B0`5D, 1, 0, &term_block,`20
X`09`09`09`09  0, 0);
X`09`09if ((status & SS$_NORMAL) != SS$_NORMAL)
X`09`09`7B
X`09`09   exiting = TRUE;
X`09`09   exit_status = status;
X`09`09`7D
X`09     `7D
X`09     else
X`09     `7B
X`09`09exiting = TRUE;
X`09`09exit_status = tty_r_buff->io_status;
X`09     `7D
X`09 `7D
X`09 else
X`09 `7B
X`09    exiting = TRUE;
X`09    exit_status = status;
X`09 `7D
X      `7D
X      else
X      `7B
X`09 exiting = TRUE;
X`09 exit_status = SS$_NORMAL;
X      `7D
X   `7D
X   else
X   `7B
X      exiting = TRUE;
X      exit_status = tty_r_buff->status;
X   `7D
X   if (exiting) SYS$WAKE(0, 0);
X`7D
X
X`7D
X`0C
X/*
X**+
X** terminal_output_ast - Queue output to logging queue`20
X**
X** Functional Description:
X**
X**`09This routine is called every time an a I/O buffer has been written to
X** the terminal.  If the terminal write request completed successfully, it w
Vill
X** queue  the I/O buffer into the queue of I/O buffers to be logged.  If the
V I/O
X** buffer is the only entry on the queue then it will issue a SYS$WAKE to st
Vart
X** the mainline.  If multiple entries are already on the queue then it will
V not
X** issue a SYS$WAKE.  This should prevent spurious wake requests from`20
X** occurring.  If a terminal write error occurred it will set the exit flag
V and
X** wake the mainline.
X**
X** Explicit Inputs:
X**
X**`09buff_addr`09-`09Pointer to I/O buffer to be placed on log queue
X**
X** Implicit Inputs:
X**
X**`09exiting`09`09-`09Flag indicating if program is finishing up
X**`09`09`09`09processing
X**`09exit_status`09-`09Contains the reason while logging is stopping
X**`09log_queue`09-`09Queue of I/O buffers to be written to log file`20
X**
X** Local Variables:
X**
X**`09status`09`09-`09Return status from LIB$INSQTI call
X**
X** Outputs:
X**
X**       None
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
X
Xvoid`09terminal_output_ast(buff_addr)
X
Xstruct`09io_buff`09`09*buff_addr;
X
X`7B
X
Xint`09`09`09status;
X
X
Xif (buff_addr->status & SS$_NORMAL)
X`7B
X   status = LIB$INSQTI(buff_addr, &log_queue);
X   if (status = LIB$_ONEENTQUE)
X   `7B
X      SYS$WAKE (0, 0);
X   `7D
X`7D
Xelse
X`7B
X   exiting = TRUE;
X   exit_status = buff_addr->status;
X   SYS$WAKE (0, 0);
X`7D
X
X`7D
X`0C
X/*
X**+
X** ft_read_ast - Output characters read from pseudo terminal
X**
X** Functional Description:
X**
X**`09This routine is called when a pseudo terminal read request completes.`2
V0
X** It will write the buffer to the terminal and attempt to start another rea
Vd`20
X** from the pseudo terminal.  If the program is not exiting it will write th
Ve`20
X** buffer to the terminal, and it will attempt to start another pseudo termi
Vnal
X** read.
X**
X** Explicit Inputs:
X**
X**`09buff_addr`09-`09Pointer to I/O buffer containing characters
X**`09`09`09`09read from pseudo terminal
X**
X** Implicit Inputs:
X**
X**`09exiting`09`09-`09Flag indicating if program is finishing up
X**`09`09`09`09processing
X**`09ft_chan`09`09-`09VMS channel pointing to control side of pseudo
X**`09`09`09`09terminal
X**`09exit_status`09-`09Contains the reason while logging is stopping
X**`09tty_chan`09-`09VMS channel pointing to terminal
X**
X** Local Variables:
X**
X**`09status`09`09-`09Return status from various routine calls
X**
X** Outputs:
X**
X**       None
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
X
Xvoid`09ft_read_ast(buff_addr)
X
Xstruct`09io_buff`09`09*buff_addr;
X
X`7B
X
Xint`09`09`09status;
X
Xif (!exiting)
X`7B
X   if (buff_addr->io_status & SS$_NORMAL)
X   `7B
X      status = SYS$QIO (0, tty_chan, IO$_WRITEVBLK, &buff_addr->status,
X`09`09        &terminal_output_ast, buff_addr, &buff_addr->data`5B0`5D,
X`09`09`09buff_addr->io_byte_cnt, 0, 0, 0, 0);
X      if (status & SS$_NORMAL)
X      `7B
X         buff_addr = allocate_io_buffer();
X         if ((buff_addr != LIB$_QUEWASEMP)  && (buff_addr != SS$_FORCEDEXIT)
V)
X         `7B
X`09    status = PTD$READ (0, ft_chan, &ft_read_ast, buff_addr,
X`09`09`09       &buff_addr->io_status, CHAR_BUF_SIZE);
X`09    if ((status & SS$_NORMAL) != SS$_NORMAL)
X`09    `7B
X`09       exiting = TRUE;
X`09       exit_status = status;
X`09       SYS$WAKE (0, 0);
X            `7D
X         `7D
X         else
X         `7B
X`09    read_stopped = TRUE;
X         `7D
X      `7D
X      else
X      `7B
X         exiting = TRUE;
X         exit_status = status;
X         SYS$WAKE (0, 0);
X      `7D
X   `7D
X   else
X   `7B
X      exiting = TRUE;
X      exit_status = buff_addr->io_status;
X      SYS$WAKE (0, 0);
X   `7D
X`7D
X
X
X`7D
X`0C
X/*
X**+
X** ft_echo_ast - Write any immediately echoed data
X**
X** Functional Description:
X**
X**`09This routine is called if a write to the pseudo terminal used an ECHO
X** buffer.  If any data was echoed it needs to write the output to the`20
X** terminal. If no data was echoed then the I/O buffer needs to be freed so
V it
X** can be used later. If the program is exiting this routine will just exit
V and
X** not worry about cleaning up.
X**
X** Explicit Inputs:
X**
X**`09buff_addr`09-`09Pointer to I/O buffer contianing any characters
X**`09`09`09`09that resulted from a write to the pseudo
X**`09`09`09`09terminal
X**
X** Implicit Inputs:
X**
X**`09exiting`09`09-`09Flag indicating if program is finishing up
X**`09`09`09`09processing
X**`09exit_status`09-`09Contains the reason while logging is stopping
X**`09tty_chan`09-`09VMS channel pointing to terminal
X**
X** Local Variables:
X**
X**`09status`09`09-`09Return status from various routine calls
X**
X** Outputs:
X**
X**       None
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
X
Xvoid`09ft_echo_ast(buff_addr)
X
Xstruct`09io_buff`09`09*buff_addr;
X
X`7B
X
Xint`09`09`09status;
X
X
Xif (!exiting)
X`7B
X   if (buff_addr->io_byte_cnt != 0)
X   `7B
X      status = SYS$QIO (0, tty_chan, IO$_WRITEVBLK, &buff_addr->status,
X`09`09`09&terminal_output_ast, buff_addr, &buff_addr->data`5B0`5D,
X`09`09`09buff_addr->io_byte_cnt, 0, 0, 0, 0);
X      if ((status & SS$_NORMAL) != SS$_NORMAL)
X      `7B
X         exiting = TRUE;
X         exit_status = status;
X         status = SYS$WAKE (0, 0);
X      `7D
X   `7D
X   else
X   `7B
X      free_io_buffer(buff_addr);
X   `7D
X`7D
X
X`7D
X`0C
X/*
X**+
X** free_io_buffer - Put I/O buffer on queue of available buffers
X**
X** Functional Description:
X**
X**`09This routine will place a free I/O buffer on the queue of available I/O
X** buffers.  It has the additional responsibility for restarting reads from
V the
X** pseudo terminal if they were stopped.  This routine disables AST delivery
V`20
X** while running to synchronize reading and resetting the read stopped flag.
V `20
X** This is a big hammer approach but is the easiest way to do it.
X**
X** Explicit Inputs:
X**
X**`09buff_addr`09-`09Pointer to I/O buffer to be placed on queue of
X**`09`09`09`09free I/O buffers
X**
X** Implicit Inputs:
X**
X**`09buffer_queue`09-`09Queue of free I/O buffer
X**`09exiting`09`09-`09Flag indicating if program is finishing up
X**`09`09`09`09processing
X**`09exit_status`09-`09Contains the reason while logging is stopping
X**
X** Local Variables:
X**
X**`09ast_stat`09-`09Return status from SYS$SETAST used to determine
X**`09`09`09`09if AST delivery should turned back on
X**`09status`09`09-`09Return status from PTD$READ`20
X**
X** Outputs:
X**
X**       None
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
X
Xvoid`09free_io_buffer (buff_addr)
X
Xstruct`09io_buff`09`09*buff_addr;
X
X`7B
X
Xint`09`09`09ast_stat;
Xint`09`09`09status;
X
X
Xif (!exiting)
X`7B
X   ast_stat = SYS$SETAST(0);
X   if (!read_stopped)
X   `7B
X      LIB$INSQHI(buff_addr, &buffer_queue);
X   `7D
X   else
X   `7B
X      status = PTD$READ (0, ft_chan, &ft_read_ast, buff_addr,
X`09`09`09 &buff_addr->io_status, CHAR_BUF_SIZE);
X      if (status & SS$_NORMAL)`20
X      `7B
X`09 read_stopped = FALSE;
X      `7D
X      else
X      `7B
X         exiting = TRUE;
X`09 exit_status = status;
X         status = SYS$WAKE (0, 0);
X      `7D
X   `7D
X   if (ast_stat == SS$_WASSET) ast_stat = SYS$SETAST(1);`09 `20
X`7D
X
X`7D
X`0C
X/*
X**+
X** allocate_io_buffer - Get a free I/O buffer from queue
X**
X** Functional Description:
X**
X**`09This routine is used to get a free I/O buffer from the queue of
X** available I/O buffers.  If the program is exiting then this routine will`
V20
X** return with an error.
X**
X** Explicit Inputs:
X**
X**`09None
X**
X** Implicit Inputs:
X**`09
X**`09buffer_queue`09-`09Queue of free I/O buffer
X**`09exiting`09`09-`09Flag indicating if program is finishing up
X**`09`09`09`09processing
X**
X** Local Variables:
X**
X**`09buff_addr`09-`09Pointer to free I/O buffer if one was available
X**`09status`09`09-`09Return status from LIB$REMQHI
X**
X** Outputs:
X**
X**`09return_status`09-`09Address of buffer or
X**`09  LIB$_QUEWASEMP `09no I/O buffer
X**        SS$_FORCEDEXIT `09program exiting
X**
X** NOTES:
X**
X**       None
X**
X**-
X*/
X
Xstruct`09io_buff`09*allocate_io_buffer(void)
X`7B
X
Xint`09`09`09status;
X
Xstruct`09io_buff`09`09*buff_addr;
X
Xif (!exiting)
X`7B
X   status = LIB$REMQHI(&buffer_queue, &buff_addr);
X   if (status & SS$_NORMAL)
X   `7B
X      return(buff_addr);
X   `7D
X   else
X   `7B
+-+-+-+-+-+-+-+-  END  OF PART 1 +-+-+-+-+-+-+-+-

