|
OpenVMS Programming Concepts Manual
22.5 Performing Special Input/Output Actions
Screen management input routines and the SYS$QIO and SYS$QIOW system
services allow you to perform I/O operations otherwise unavailable to
high-level languages. For example, you can allow a user to interrupt
normal program execution by typing a character and by providing a
mechanism for reading that character. You can also control such things
as echoing, time allowed for input, and whether data is read from the
type-ahead buffer.
Some of the operations described in the following sections require the
use of the SYS$QIO or SYS$QIOW system services. For more information
about the QIO system services, see the OpenVMS System Services Reference Manual and Chapter 23.
Other operations, described in the following sections, can be performed
by calling the SMG$ input routines. The SMG$ input routines can be used
alone or with the SMG$ output routines. Section 22.4 describes how to
use the input routines with the output routines. This section assumes
that you are using the input routines alone. To use the SMG$ input
routines, do the following:
- Call SMG$CREATE_VIRTUAL_KEYBOARD to associate a logical keyboard
with a device or file specification (SYS$INPUT by default).
SMG$CREATE_VIRTUAL_KEYBOARD returns a keyboard identification number;
use that number to identify the device or file to the SMG$ input
routines.
- Call an SMG$ input routine (SMG$READ_STRING or
SMG$READ_COMPOSED_LINE) to read data typed at the device associated
with the virtual keyboard.
When using the SMG$ input routines without the SMG$ output routines, do
not specify the optional VDID argument of the input routine.
22.5.1 Using Ctrl/C and Ctrl/Y Interrupts
The QIO system services enable you to detect a Ctrl/C or Ctrl/Y
interrupt at a user terminal, even if you have not issued a read to the
terminal. To do so, you must take the following steps:
- Queue an asynchronous system trap (AST)---Issue the SYS$QIO or
SYS$QIOW system service with a function code of IO$_SETMODE modified by
either IO$M_CTRLCAST (for Ctrl/C interrupts) or IO$M_CTRLYAST (for
Ctrl/Y interrupts). For the P1 argument, provide the
name of a subroutine to be executed when the interrupt occurs. For the
P2 argument, you can optionally identify one longword
argument to pass to the AST subroutine.
- Write an AST subroutine---Write the subroutine identified in the
P1 argument of the QIO system service and link the
subroutine into your program. Your subroutine can take one longword
dummy argument to be associated with the P2 argument
in the QIO system service. You must define common areas to access any
other data in your program from the AST routine.
If you press Ctrl/C or Ctrl/Y after your program queues the appropriate
AST, the system interrupts your program and transfers control to your
AST subroutine (this action is called delivering the AST). After your
AST subroutine executes, the system returns control to your program at
the point of interruption (unless your AST subroutine causes the
program to exit, or unless another AST has been queued). Note the
following guidelines for using Ctrl/C and Ctrl/Y ASTs:
- ASTs are asynchronous---Since your AST subroutine does not know
exactly where you are in your program when the interrupt occurs, you
should avoid manipulating data or performing other mainline activities.
In general, the AST subroutine should either notify the mainline code
(for example, by setting a flag) that the interrupt occurred, or clean
up and exit from the program (if that is what you want to do).
- ASTs need new channels to the terminal---If you try to access the
terminal with language I/O statements using SYS$INPUT or SYS$OUTPUT,
you may receive a redundant I/O error. You must establish another
channel to the terminal by explicitly opening the terminal.
- Ctrl/C and Ctrl/Y ASTs are one-time ASTs---After a Ctrl/C or Ctrl/Y
AST is delivered, it is dequeued. You must reissue the QIO system
service if you wish to trap another interrupt.
- Many ASTs can be queued---You can queue multiple ASTs (for the same
or different AST subroutines, on the same or different channels) by
issuing the appropriate number of QIO system services. The system
delivers the ASTs on a last-in, first-out (LIFO) basis.
- Unhandled Ctrl/Cs turn into Ctrl/Ys---If the user enters Ctrl/C and
you do not have an AST queued to handle the interrupt, the system turns
the Ctrl/C interrupt into a Ctrl/Y interrupt.
- DCL handles Ctrl/Y interrupts---DCL handles Ctrl/Y interrupts by
returning the user to DCL command level, where the user has the option
of continuing or exiting from your program. DCL takes precedence over
your AST subroutine for Ctrl/Y interrupts. Your Ctrl/Y AST subroutine
is executed only under the following circumstances:
- If Ctrl/Y interrupts are disabled at DCL level (SET NOCONTROL_Y)
before your program is executed
- If your program disables DCL Ctrl/Y interrupts with LIB$DISABLE_CTRL
- If the user elects to continue your program after DCL interrupts it
- You can dequeue Ctrl/C and Ctrl/Y ASTs---You can dequeue all Ctrl/C
or Ctrl/Y ASTs on a channel by issuing the appropriate QIO system
service with a value of 0 for the P1 argument (passed
by immediate value). You can dequeue all Ctrl/C ASTs on a channel by
issuing the SYS$CANCEL system service for the appropriate channel. You
can dequeue all Ctrl/Y ASTs on a channel by issuing the SYS$DASSGN
system service for the appropriate channel.
- You can use SMG$ routines---You can connect to the terminal using
the SMG$ routines from either AST level or mainline code. Do not
attempt to connect to the terminal from AST level if you do so in your
mainline code.
Example 22-13 permits the terminal user to interrupt a display to see
how many lines have been typed up to that point.
Example 22-13 Using Interrupts to Perform
I/O |
!Main Program
.
.
.
INTEGER STATUS
! Accumulated data records
CHARACTER*132 STORAGE (255)
INTEGER*4 STORAGE_SIZE (255),
2 STORAGE_COUNT
! QIOW and QIO structures
INTEGER*2 INPUT_CHAN
INTEGER*4 CODE
STRUCTURE /IOSTAT_BLOCK/
INTEGER*2 IOSTAT
BYTE TRANSMIT,
2 RECEIVE,
2 CRFILL,
2 LFFILL,
2 PARITY,
2 ZERO
END STRUCTURE
RECORD /IOSTAT_BLOCK/ IOSB
! Flag to notify program of CTRL/C interrupt
LOGICAL*4 CTRLC_CALLED
! AST subroutine to handle CTRL/C interrupt
EXTERNAL CTRLC_AST
! Subroutines
INTEGER SYS$ASSIGN,
2 SYS$QIOW
! Symbols used for I/O operations
INCLUDE '($IODEF)'
! Put values into array
CALL LOAD_STORAGE (STORAGE,
2 STORAGE_SIZE,
2 STORAGE_COUNT)
! Assign channel and set up QIOW structures
STATUS = SYS$ASSIGN ('SYS$INPUT',
2 INPUT_CHAN,,)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
CODE = IO$_SETMODE .OR. IO$M_CTRLCAST
! Queue an AST to handle CTRL/C interrupt
STATUS = SYS$QIOW (,
2 %VAL (INPUT_CHAN),
2 %VAL (CODE),
2 IOSB,
2 ,,
2 CTRLC_AST, ! Name of AST routine
2 CTRLC_CALLED, ! Argument for AST routine
2 ,,,)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
IF (.NOT. IOSB.IOSTAT)
2 CALL LIB$SIGNAL (%VAL (IOSB.IOSTAT))
! Display STORAGE array, one element per line
DO I = 1, STORAGE_COUNT
TYPE *, STORAGE (I) (1:STORAGE_SIZE (I))
! Additional actions if user types CTRL/C
IF (CTRLC_CALLED) THEN
CTRLC_CALLED = .FALSE.
! Show user number of lines displayed so far
TYPE *, 'Number of lines: ', I
! Requeue AST
STATUS = SYS$QIOW (,
2 %VAL (INPUT_CHAN),
2 %VAL (CODE),
2 IOSB,
2 ,,
2 CTRLC_AST,
2 CTRLC_CALLED,
2 ,,,)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
IF (.NOT. IOSB.IOSTAT)
2 CALL LIB$SIGNAL (%VAL (IOSB.IOSTAT))
END IF
END DO
END
|
AST Routine
! AST routine
! Notifies program that user typed CTRL/C
SUBROUTINE CTRLC_AST (CTRLC_CALLED)
LOGICAL*4 CTRLC_CALLED
CTRLC_CALLED = .TRUE.
END
|
22.5.2 Detecting Unsolicited Input
You can detect input from the terminal even if you have not called
SMG$READ_COMPOSED_LINE or SMG$READ_STRING by using
SMG$ENABLE_UNSOLICITED_INPUT. This routine uses the AST mechanism to
transfer control to a subprogram of your choice each time the user
types at the terminal; the AST subprogram is responsible for reading
any input. When the subprogram completes, control returns to the point
in your mainline code where it was interrupted.
The SMG$ENABLE_UNSOLICITED_INPUT routine is not an SMG$ input routine.
Before invoking SMG$ENABLE_UNSOLICITED_INPUT, you must invoke
SMG$CREATE_PASTEBOARD to associate a pasteboard with the terminal and
SMG$CREATE_VIRTUAL_KEYBOARD to associate a virtual keyboard with the
same terminal.
SMG$ENABLE_UNSOLICITED_INPUT accepts the following arguments:
- The pasteboard identification number (use the value returned by
SMG$CREATE_PASTEBOARD)
- The name of an AST subprogram
- An argument to be passed to the AST subprogram
When SMG$ENABLE_UNSOLICITED_INPUT invokes the AST subprogram, it passes
two arguments to the subprogram: the pasteboard identification number
and the argument that you specified. Typically, you write the AST
subprogram to read the unsolicited input with SMG$READ_STRING. Since
SMG$READ_STRING requires that you specify the virtual keyboard at which
the input was typed, specify the virtual keyboard identification number
as the second argument to pass to the AST subprogram.
Example 22-14 permits the terminal user to interrupt the display of a
series of arrays, and either to go on to the next array (by typing
input beginning with an uppercase N) or to exit from the program (by
typing input beginning with anything else).
Example 22-14 Receiving Unsolicited Input
from a Virtual Keyboard |
! Main Program
! The main program calls DISPLAY_ARRAY once for each array.
! DISPLAY_ARRAY displays the array in a DO loop.
! If the user enters input from the terminal, the loop is
! interrupted and the AST routine takes over.
! If the user types anything beginning with an N, the AST
! sets DO_NEXT and resumes execution -- DISPLAY_ARRAY drops
! out of the loop processing the array (because DO_NEXT is
! set -- and the main program calls DISPLAY_ARRAY for the
! next array.
! If the user types anything not beginning with an N,
! the program exits.
.
.
.
INTEGER*4 STATUS,
2 VKID, ! Virtual keyboard ID
2 PBID ! Pasteboard ID
! Storage arrays
INTEGER*4 ARRAY1 (256),
2 ARRAY2 (256),
2 ARRAY3 (256)
! System routines
INTEGER*4 SMG$CREATE_PASTEBOARD,
2 SMG$CREATE_VIRTUAL_KEYBOARD,
2 SMG$ENABLE_UNSOLICITED_INPUT
! AST routine
EXTERNAL AST_ROUTINE
! Create a pasteboard
STATUS = SMG$CREATE_PASTEBOARD (PBID, ! Pasteboard ID
2 'SYS$INPUT')
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
! Create a keyboard for the same device
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, ! Keyboard ID
2 'SYS$INPUT')
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
! Enable unsolicited input
STATUS = SMG$ENABLE_UNSOLICITED_INPUT (PBID, ! Pasteboard ID
2 AST_ROUTINE,
2 VKID) ! Pass keyboard
! ID to AST
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
.
.
.
! Call display subroutine once for each array
CALL DISPLAY_ARRAY (ARRAY1)
CALL DISPLAY_ARRAY (ARRAY2)
CALL DISPLAY_ARRAY (ARRAY3)
END
|
Array Display Routine
! Subroutine to display one array
SUBROUTINE DISPLAY_ARRAY (ARRAY)
! Dummy argument
INTEGER*4 ARRAY (256)
! Status
INTEGER*4 STATUS
! Flag for doing next array
LOGICAL*4 DO_NEXT
COMMON /DO_NEXT/ DO_NEXT
! If AST has been delivered, reset
IF (DO_NEXT) DO_NEXT = .FALSE.
! Initialize control variable
I = 1
! Display entire array unless interrupted by user
! If interrupted by user (DO_NEXT is set), drop out of loop
DO WHILE ((I .LE. 256) .AND. (.NOT. DO_NEXT))
TYPE *, ARRAY (I)
I = I + 1
END DO
END
|
AST Routine
! Subroutine to read unsolicited input
SUBROUTINE AST_ROUTINE (PBID,
2 VKID)
! dummy arguments
INTEGER*4 PBID, ! Pasteboard ID
2 VKID ! Keyboard ID
! Status
INTEGER*4 STATUS
! Flag for doing next array
LOGICAL*4 DO_NEXT
COMMON /DO_NEXT/ DO_NEXT
! Input string
CHARACTER*4 INPUT
! Routines
INTEGER*4 SMG$READ_STRING
! Read input
STATUS = SMG$READ_STRING (VKID, ! Keyboard ID
2 INPUT)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
! If user types anything beginning with N, set DO_NEXT
! otherwise, exit from program
IF (INPUT (1:1) .EQ. 'N') THEN
DO_NEXT = .TRUE.
ELSE
CALL EXIT
END IF
END
|
22.5.3 Using the Type-Ahead Buffer
Normally, if the user types at the terminal before your application is
able to read from that device, the input is saved in a special data
structure maintained by the system called the type-ahead buffer. When
your application is ready to read from the terminal, the input is
transferred from the type-ahead buffer to your input buffer. The
type-ahead buffer is preset at a size of 78 bytes. If the HOSTSYNC
characteristic is on (the usual condition), input to the type-ahead
buffer is stopped (the keyboard locks) when the buffer is within 8
bytes of being full. If the HOSTSYNC characteristic is off, the bell
rings when the type-ahead buffer is within 8 bytes of being full; if
you overflow the buffer, the excess data is lost. The TTY_ALTALARM
system parameter determines the point at which either input is stopped
or the bell rings.
You can clear the type-ahead buffer by reading from the terminal with
SMG$READ_STRING and by specifying TRM$M_TM_PURGE in the
modifiers argument. Clearing the type-ahead buffer has
the effect of reading only what the user types on the terminal after
the read operation is invoked. Any characters in the type-ahead buffer
are lost. The following example illustrates how to purge the type-ahead
buffer:
INTEGER*4 SMG$CREATE_VIRTUAL_KEYBOARD,
2 SMG$READ_STRING,
2 STATUS,
2 VKID, ! Virtual keyboard ID
2 INPUT_SIZE
CHARACTER*512 INPUT
INCLUDE '($TRMDEF)'
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID,
2 'SYS$INPUT') ! I/O device
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
STATUS = SMG$READ_STRING (VKID, ! Keyboard ID
2 INPUT, ! Data read
2 'Prompt> ',
2 512,
2 TRM$M_TM_PURGE,
2 ,,
2 INPUT_SIZE)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
|
You can also clear the type-ahead buffer with a QIO read operation
modified by IO$M_PURGE (defined in $IODEF). You can turn off the
type-ahead buffer for further read operations with a QIO set mode
operation that specifies TT$M_NOTYPEAHD as a basic terminal
characteristic.
You can examine the type-ahead buffer by issuing a QIO sense mode
operation modified by IO$M_TYPEAHDCNT. The number of characters in the
type-ahead buffer and the value of the first character are returned to
the P1 argument.
The size of the type-ahead buffer is determined by the TTY_TYPAHDSZ
system parameter. You can specify an alternative type-ahead buffer by
turning on the ALTYPEAHD terminal characteristic; the size of the
alternative type-ahead buffer is determined by the TTY_ALTYPAHD system
parameter.
22.5.4 Using Echo
Normally, the system writes back to the terminal any printable
characters that the user types at that terminal. The system also writes
highlighted words in response to certain control characters; for
example, the system writes EXIT if the user enters Ctrl/Z. If the user
types ahead of your read, the characters are not echoed until you read
them from the type-ahead buffer.
You can turn off echoing when you invoke a read operation by reading
from the terminal with SMG$READ_STRING and by specifying
TRM$M_TM_NOECHO in the modifiers argument. You can
turn off echoing for control characters only by modifying the read
operation with TRM$M_TM_TRMNOECHO. The following example turns off all
echoing for the read operation:
INTEGER*4 SMG$CREATE_VIRTUAL_KEYBOARD,
2 SMG$READ_STRING,
2 STATUS,
2 VKID, ! Virtual keyboard ID
2 INPUT_SIZE
CHARACTER*512 INPUT
INCLUDE '($TRMDEF)'
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, ! Keyboard ID
2 'SYS$INPUT') ! I/O device
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
STATUS = SMG$READ_STRING (VKID, ! Keyboard ID
2 INPUT, ! Data read
2 'Prompt> ',
2 512,
2 TRM$M_TM_NOECHO,
2 ,,
2 INPUT_SIZE)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
|
You can also turn off echoing with a QIO read operation modified by
IO$M_NOECHO (defined in $IODEF). You can turn off echoing for further
read operations with a QIO set mode operation that specifies
TT$M_NOECHO as a basic terminal characteristic.
22.5.5 Using Timeout
Using SMG$READ_STRING, you can restrict the user to a certain amount of
time in which to respond to a read command. If your application reads
data from the terminal using SMG$READ_STRING, you can modify the
timeout characteristic by specifying, in the timeout
argument, the number of seconds the user has to respond. If the user
fails to type a character in the allotted time, the error condition
SS$_TIMEOUT (defined in $SSDEF) is returned. The following example
restricts the user to 8 seconds in which to respond to a read command:
INTEGER*4 SMG$CREATE_VIRTUAL_KEYBOARD,
2 SMG$READ_STRING,
2 STATUS,
2 VKID, ! Virtual keyboard ID
2 INPUT_SIZE
CHARACTER*512 INPUT
INCLUDE '($SSDEF)'
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID,
2 'SYS$INPUT')
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
STATUS = SMG$READ_STRING (VKID, ! Keyboard ID
2 INPUT, ! Data read
2 'Prompt> ',
2 512,
2 ,
2 8,
2 ,
2 INPUT_SIZE)
IF (.NOT. STATUS) THEN
IF (STATUS .EQ. SS$_TIMEOUT) CALL NO_RESPONSE ()
ELSE
CALL LIB$SIGNAL (%VAL (STATUS))
END IF
|
You can cause a QIO read operation to time out after a certain number
of seconds by modifying the operation with IO$M_TIMED and by specifying
the number of seconds in the P3 argument. A message
broadcast to a terminal resets a timer that is set for a timed read
operation (regardless of whether the operation was initiated with QIO
or SMG).
Note that the timed read operations work on a character-by-character
basis. To set a time limit on an input record rather than an input
character, you must use the SYS$SETIMR system service. The SYS$SETIMR
executes an AST routine at a specified time. The specified time is the
input time limit. When the specified time is reached, the AST routine
cancels any outstanding I/O on the channel that is assigned to the
user's terminal.
22.5.6 Converting Lowercase to Uppercase
You can automatically convert lowercase user input to uppercase by
reading from the terminal with the SMG$READ_STRING routine and by
specifying TRM$M_TM_CVTLOW in the modifiers argument,
as shown in the following example:
INTEGER*4 SMG$CREATE_VIRTUAL_KEYBOARD,
2 SMG$READ_STRING,
2 STATUS,
2 VKID, ! Virtual keyboard ID
2 INPUT_SIZE
CHARACTER*512 INPUT
INCLUDE '($TRMDEF)'
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, ! Keyboard ID
2 'SYS$INPUT')
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
STATUS = SMG$READ_STRING (VKID, ! Keyboard ID
2 INPUT, ! Data read
2 'Prompt> ',
2 512,
2 TRM$M_TM_CVTLOW,
2 ,,
2 INPUT_SIZE)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
|
You can also convert lowercase characters to uppercase with a QIO read
operation modified by IO$M_CVTLOW (defined in $IODEF).
22.5.7 Performing Line Editing and Control Actions
Normally, the user can edit input as explained in the OpenVMS I/O User's Reference Manual.
You can inhibit line editing on the read operation by reading from the
terminal with SMG$READ_STRING and by specifying TRM$M_TM_NOFILTR in the
modifiers argument. The following example shows how
you can inhibit line editing:
INTEGER*4 SMG$CREATE_VIRTUAL_KEYBOARD,
2 SMG$READ_STRING,
2 STATUS,
2 VKID, ! Virtual keyboard ID
2 INPUT_SIZE
CHARACTER*512 INPUT
INCLUDE '($TRMDEF)'
STATUS = SMG$CREATE_VIRTUAL_KEYBOARD (VKID, ! Keyboard ID
2 'SYS$INPUT')
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
STATUS = SMG$READ_STRING (VKID, ! Keyboard ID
2 INPUT, ! Data read
2 'Prompt> ',
2 512,
2 TRM$M_TM_NOFILTR,
2 ,,
2 INPUT_SIZE)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
|
You can also inhibit line editing with a QIO read operation modified by
IO$M_NOFILTR (defined in $IODEF).
22.5.8 Using Broadcasts
You can write, or broadcast, to any interactive terminal by using the
SYS$BRKTHRU system service. The following example broadcasts a message
to all terminals at which users are currently logged in. Use of
SYS$BRKTHRU to write to a terminal allocated to a process other than
your own requires the OPER privilege.
INTEGER*4 STATUS,
2 SYS$BRKTHRUW
INTEGER*2 B_STATUS (4)
INCLUDE '($BRKDEF)'
STATUS = SYS$BRKTHRUW (,
2 'Accounting system started',,
2 %VAL (BRK$C_ALLUSERS),
2 B_STATUS,,,,,,)
IF (.NOT. STATUS) CALL LIB$SIGNAL (%VAL (STATUS))
|
|