HP OpenVMS I/O User’s Reference Manual: OpenVMS Version 8.4 > Chapter 6 Pseudoterminal Driver
6.6 Pseudoterminal Driver Programming Example
Example 6-1 shows
how to use the pseudoterminal. (The example is also included in the
SYS$EXAMPLES directory.) This section begins with a brief overview
of the example. The example itself briefly discusses each module;
the pseudocode for that module follows its discussion. The scenario chosen for this example is a simple
terminal session logging utility that uses most of the pseudoterminal
capabilities. This example also shows how to use the write-with-echo
capability, which provides a significant gain in performance. 6.6.1 Design Overview | |
The design approach writes the log record in a
main loop that hibernates when it has no work to do. The loop uses
ASTs to read keystrokes from the terminal, write to the pseudoterminal,
and write data to the terminal. When a block of characters is written
to the terminal, that block is placed into a queue of blocks to be
written to the log file, and a wake request is issued. Logging is
stopped if you log out of the subprocess, if you enter the stop logging
character Ctrl\, or if a severe error occurs during data processing.
When any of these events occur, all outstanding log records are written
before the program exits. One major design consideration is how flow control
should be handled — either by attempting to enforce flow control,
or by letting the terminal and terminal driver handle it. In this
example, the terminal and terminal driver handle flow control; the
driver sends XON, XOFF, or BELL characters to the terminal as necessary. One of the six I/O buffers is permanently reserved
as the terminal read buffer. This buffer is passed directly to the
terminal read $QIO. This eliminates having to move data that is read
from the terminal into the read buffer. The other five buffers are
placed in a queue and are allocated and deallocated as needed. This
pool of buffers reserves the first two longwords to be used as queue
headers and traditional IOSBs. The third longword and the I/O status
longwords are used by the pseudoterminal driver. Example 6-1 Sample Pseudocode for Pseudoterminal Driver Program |
/*
** Main Routine
**
** Function: Intitializes the environment and then hibernates, waiting
** to be awakened. When awakened, the program checks to see whether it
** is exiting, or whether more log data is available. If more data is
** available, the data is appended to the current log record and checked
** to see whether a log record should be written. A log record is written
** either when maxbuf characters are in the log buffer,
** or when it finds a <CR>character pair. The algorithm
** allows an unlimited number of <NULL>fill characters to occur
** between the <CR>and the <LF>. If the program is
** exiting, it closes the log file, deletes the pseudoterminal, resets the
** terminal, and exits.
*/
Initialize environments (This includes creating pseudoterminal, the log file
and starting up the subprocess.)
If (Initialization OK) Then
Do
while (I/O buffer to log)
Data size = number of bytes in I/O buff
For all data in I/O buffer
If (cr_seen) Then
If (current char == <LF>) Then
write current log buffer
reset cr_seen
point to start of log buffer
Else if (current char != <NULL>) Then
insert <CR>and current char into log buffer
move log buffer ptr over 2 characters
reset cr_seen
Endif
Else if (current character != <CR>) Then
insert character into log buffer
move log buffer ptr over 1 character
Else
set cr_seen
Endif
If (log buffer ptr >= IOC$GW_MAX-48) Then
write log buffer
reset log buffer pointer
reset cr_seen
Endif
Endloop
Free I/O buffer call free_io_buffers
Endwhile
If (not exiting) Then
Wait for more to do call SYS$HIBER
Endif
Until ( (exiting) and (no I/O buffers to log) )
close log file
If ( (close failed) and (exit reason is SS$_NORMAL) ) Then
set exit to status to failure reason
Endif
If (subprocess still running) Then
call SYS$FORCEX to run down the subprocess
Endif
call PTD$CANCEL to flush all pending pseudoterminal read requests
call SYS$CANCEL to flush all terminal requests
call PTD$DELETE to delete the pseudoterminal
If ( (delete failed) and (exit reason is SS$_NORMAL) ) Then
set exit to status to failure reason
Endif
reset terminal to startup condition using SYS$QIOW
If ( (terminal reset failed) and (exit reason is SS$_NORMAL) ) Then
exit to status to failure reason
Endif
Endif
call LIB$SIGNAL and report exit reason
Exit
/*
**
** Initialization Code
**
** Function: This routine sets the terminal characteristics, creates the
** pseudoterminal, starts up the subprocess, and opens the log file. If
** any of these steps fail, the program undoes any steps already done and
** returns to the main routine.
**
*/
read the maximum buffer size from IOC$GW_MAXBUF
Assign a channel to SYS$INPUT
If (assign ok) Then
Read the terminal characteristics from the terminal
If (read of terminal characteristics ok) Then
Open log file with maximum record size of IOC$GW_MAXBUF
If (open ok) Then
Create the pseudoterminal with characteristics of terminal
If (create ok) then
Place 4 of the buffers on the queue of free I/O buffers
Copy terminal characteristics and modify them to NOECHO and PASTHRU
Set the terminal characteristics use modified value
If (set ok) Then
Get device name of pseudoterminal use SYS$GETDVI
If (get ok) Then
Create subprocess
If (create ok) Then
Enable XON, XOFF, BELL, SET_LINE event notification ASTs
If (AST setup OK) Then
Call PTD$READ to start reading from the pseudoterminal
ASTADR = ft_read_ast
ASTPRM = buffer address
READBUF = I/O buffer + 8
READBUF_LEN = 500
If (read ok) Then
Call SYS$QIO and read a single character from the
keyboard ASTADR = kbd_read_ast
If (read failed) Then
Call PTD$CANCEL to flush queued pseudoterminal read
Call PTD$DELETE to delete pseudoterminal
Reset terminal to original state
Close log file and delete it
Endif
Else
Call PTD$DELETE to delete pseudoterminal
Reset terminal to original state
Close log file and delete it
Endif
Else
Call PTD$DELETE to delete pseudoterminal
Reset terminal to original state
Close log file and delete it
Endif
Else
Call PTD$DELETE to delete pseudoterminal
Reset terminal to original state
Close log file and delete it
Endif
Else
Call PTD$DELETE to delete pseudoterminal
Reset terminal to original state
Close log file and delete it
Endif
Else
Call PTD$DELETE to delete pseudoterminal
Close log file and delete it
Endif
Else
Close log file and delete it
Endif
Endif
Endif
Endif
/*
** kbd_read_ast
**
** Function: This routine is called every time data is read from the terminal.
** If the program is exiting, then the routine exits without restarting the
** read. The character read is checked to see if the terminate processing
** character Ctrl\ was entered. If the terminate processing character was
** entered, the exiting state is set and a SYS$WAKE is issued to start the
** main routine. Now an attempt is made to obtain an I/O buffer in which
** to store echoed output. If an I/O buffer is unavailable, a simple
** PTD$WRITE is issued; a PTD$WRITE with echo is issued if a buffer is
** available. If the write completes successfully, another read is issued
** to the keyboard.
**
*/
If (not exiting) Then
If (read ok) Then
Search input data for Ctrl\
Allocate a read buffer call allocate_io_buffer
If (got a buffer) Then
Call PTD$WRITE to write characters to pseudoterminal
ASTADR = ft_echo_ast
ASTPRM = allocated I/O buffer
WRTBUF = read I/O buffer
WRTBUF_LEN = number of characters read
ECHOBUF = allocated I/O buffer
ECHOBUF_LEN = 500
Else
Call PTD$WRITE to write characters to pseudoterminal
WRTBUF = read I/O buffer
WRTBUF_LEN = number of characters read
Endif
If (write setup ok)
If ( (write status is ok) or (write status is SS$_DATALOST) )
Issue another single character read to terminal with
ASTADR = kbd_read_ast, with data going to read I/O buffer
If (read setup failed) Then
Set exit flag
Set exiting reason to SS$_NORMAL
Endif
Else
Set exit flag
Set exiting reason to SS$_NORMAL
Endif
Else
Set exit flag
Set exiting reason to SS$_NORMAL
Endif
Else
Set exit flag
Set exiting reason to read failure status
Endif
If (exiting) Then
Wake the mainline call SYS$WAKE
Endif
Endif
/*
** terminal_output_ast
**
** Function: This routine is called every time an I/O buffer is written
** to the terminal. If the terminal write request completes successfully,
** it inserts the I/O buffer into the queue of I/O buffers to be logged.
** If the I/O buffer is the only entry on the queue, it issues a SYS$WAKE
** to start the main routine. To prevent spurious wake requests,
** SYS$WAKE is not issued if multiple entries are already on
** the queue. If a terminal write error occurs, the routine sets the
** exit flag and wakes the main routine.
**
*/
If (terminal write completed ok) Then
insert I/O buffer onto logging queue
If (this is only entry on queue) Then
wake the mainline call SYS$WAKE
Endif
Else
set exit flag
set exiting reason to terminal write error reason
wake the mainline call SYS$WAKE
Endif
/*
**
** ft_read_ast
**
** Function: This routine is called when a pseudoterminal read request
** completes. It writes the buffer to the terminal and attempts to start
** another read from the pseudoterminal. If the program is not exiting,
** this routine writes the buffer to the terminal and does not start another
** pseudoterminal read.
**
*/
If (not exiting)
If (Pseudoterminal read ok) Then
write buffer to the terminal ASTADR = terminal_output_ast
If (write setup ok) Then
Allocate another read buffer call allocate_io_buffer
If (got a buffer) Then
Call PTD$READ to restart reads from the pseudoterminal.
ASTADR = ft_read_ast
ASTPRM = buffer address
READBUF = I/O buffer + 8
READBUF_LEN = 500
If (read setup failed) Then
Set exit flag
Set exiting reason to read failure reason
Wake the mainline call SYS$WAKE
Endif
Else
Set read stopped flag
Endif
Else
Set exit flag
Set exiting reason to terminal write failure reason
Wake the mainline call SYS$WAKE
Endif
Else
Set exit flag
Set exiting reason to terminal read failure reason
Wake the mainline call SYS$WAKE
Endif
Endif
/*
**
** ft_echo_ast
**
** Function: This routine is called if a write to the pseudoterminal used
** an ECHO buffer. If any data was echoed, the output is written to the
** terminal. If no data was echoed, the I/O buffer is freed so it can be
** used later. If the program is exiting, this routine exits.
**
*/
If (not exiting) Then
If (ECHO buffer has data) Then
Write the buffer to the terminal with ASTADR = terminal_output_ast
If (error setting up write) Then
Set exit flag
Set exiting reason to write failure reason
Wake mainline call SYS$WAKE
Endif
Else
Free I/O buffer call free_io_buffers
Endif
Endif
/*
** free_io_buffers
**
** Function: This routine places a free I/O buffer on the queue of available
** I/O buffers. It also restarts any stopped read operations from the
** pseudoterminals. This routine disables AST delivery while it is running
** in order to synchronize reading and resetting the read stopped flag.
**
*/
If (not exiting) Then
Disable AST deliver using SYS$SETAST
If (Pseudoterminal reads not stopped) Then
Insert I/O buffer on the interlocked queue of free I/O buffers
Else
Call PTD$READ to restart reads from the pseudoterminal.
ASTADR = ft_read_ast
ASTPRM = buffer address
READBUF = I/O buffer + 8
READBUF_LEN = 500
If (no error starting read) Then
Clear read stopped flag
Else
Set exit flag
Set exit reason to read setup reason
Endif
Endif
Enable AST delivery using SYS$SETAST
Endif
/*
**
** allocate_io_buffer
**
** Function: This routine obtains a free I/O buffer from the queue of
** available I/O buffers. If the program is exiting, this routine
** returns an SS$_FORCEDEXIT error.
**
*/
If (not exiting) Then
remove a I/O buffer from the interlocked queue of I/O buffers
If (queue empty) Then
exit with reason LIB$_QUEWASEMP
else
exit with reason SS$_FORCEDEXIT
Endif
/*
** subprocess_exit
**
** Function: This routine is called when the subprocess has completed
** and exited. This routine checks whether the program is already exiting.
** If not, then the routine indicates that the program is exiting and wakes
** up the main program.
**
*/
If (not exiting) Then
indicate subprocess no longer running
set exit status to SS$_NORMAL
indicate exiting
call SYS$WAKE to start up main loop
Endif
/*
** xon_ast
**
** Function: This routine is called for the pseudoterminal driver to signal
** that it is ready to accept keyboard input. The routine attempts to send
** an XON character to the terminal by sending XON DC1 using SYS$QIO.
** If the attempt fails, it is not retried.
**
*/
If (not exiting) Then
call SYS$QIO to send a <DC1>character to the terminal
Endif
/*
** bell_ast
**
** Function: This routine is called when the pseudoterminal driver wants
** to warn the user to stop sending keyboard data. The routine attempts
** to ring the terminal bell by sending the BELL character to the terminal
** using SYS$QIO. If the attempt fails, it is not retried.
**
*/
If (not exiting) Then
call SYS$QIO to send a <BELL>character to the terminal
Endif
/*
** xoff_ast
**
** Function: This routine is called when the pseudoterminal driver wants to
** signal that it will stop accepting keyboard input. The routine attempts
** to send an XOFF character to the terminal by sending XOFF DC3 to the
** terminal using SYS$QIO. If the attempt fails, it is not retried.
**
*/
If (not exiting) Then
call SYS$QIO to send a <DC3>character to the terminal
Endif
/*
** set_line_ast
**
** Function: This routine is called when the pseudoterminal device
** characteristics change. The routine reads the current pseudoterminal
** characteristics, changes the characteristics to set PASTHRU and NOECHO,
** and applies the characteristics to the input terminal. If the attempt
** to alter the terminal characteristics fails, it is not retried.
**
*/
If (not exiting) Then
call SYS$QIOW to read the pseudoterminals characteristics
If (not error) Then
Set the alter the just read characteristics to have PASTHRU and NOECHO
attributes
call SYS$QIO to set the terminal characteristics.
Endif
Endif
|
|
|