[an error occurred while processing this directive]

HP OpenVMS Systems Documentation

Content starts here 5.5 Terminal Driver Programming Examples
HP OpenVMS I/O User’s Reference Manual: OpenVMS Version 8.4 > Chapter 5 Terminal Driver

5.5 Terminal Driver Programming Examples

The C program LAT.C shown in Example 5-1 initiates and maintains an outbound LAT session from the local node. It demonstrates the following LAT $QIO functions:

  • Cloning the LAT template device (LTA0:)

  • IO$M_LT_SETMODE

  • IO$M_LT_CONNECT (on forward port)

  • IO$M_LT_SENSEMODE

Example 5-1 LAT.C Terminal Driver Programming Example

#module LAT_FORWARD_CONNECT "X1.0-001"/*
**++
**
**  MODULE DESCRIPTION:
**
**      In initiating and maintaining an outbound LAT session from the local
**      node, this program demonstrates the following LAT $QIO functions:
**
**          o Cloning the LAT template device (LTA0:)
**          o IO$M_LT_SETMODE
**          o IO$M_LT_CONNECT   (on forward port)
**          o IO$M_LT_SENSEMODE
**
**--
*/


/*
**
**  INCLUDE FILES
**
*/
#include /* VMS Descriptor Definitions      */
#include /* I/O Function Codes Definitions  */
#include /* LAT Definitions                 */
#include /* System Service Return Status    */ 
                                        /* Code Definitions                */
#include /* Terminal Characteristics        */
#include /* Terminal Extended               */
                                        /* Characteristics                 */
/*
**
**  MACRO DEFINITIONS
**
*/

/*
** Service name which the session will be to.
*/

#define SERVICE_NAME        "LAT_SERVICE"
#define SERVICE_NAME_LENGTH 11

/*
** For the sake of clarity, the sizes of the buffers used for reading from
** and writing to the LTA and TT devices are set to the values below.  In
** order to gain maximum throughput from this program, the following system
** parameters can be set:
**
**      o TTY_ALTYPAHD - 1500
**      o TTY_TYPAHDSZ - 80
**
** To get the best performance from this program without touching these
** system parameters on your system, modify the program to set the size of
** the buffers to the following:
**
**      o LTA_BUFFER_SIZE = MIN(TTY_ALTYPAHD, 1500)
**      o TT_BUFFER_SIZE  = MIN(TTY_TYPAHDSZ, 132)
*/


#define LTA_MAXBUF              1500
#define TT_MAXBUF               80

/*
** Size of the LAT SENSEmode itemlist.
*/

#define MAX_SENSE_ITEMLIST_SIZE 1500

/*
** Character user can press to terminate the LAT connection (CTRL+\).
*/

#define CONNECTION_TERMINATOR   0x1C

/*
**
**  FUNCTION PROTOTYPES
**
*/

unsigned long   SetDeviceChars(void);
void            ConnectAST(void);
void            LTAreadChannelAST(void);
void            TTreadChannelAST(void);
void            LTAhangupHandler(void);
void            EndSession(void);
void            ExitHandler(void);

/*
**
**  GLOBAL DATA
**
*/

char            *LTAbuffer, /* LTA device I/O buffer                */
                *TTbuffer,  /* TT device  I/O buffer                */

                /*
                ** Text for LAT reject codes.  Note that some LAT
                ** implementations will return a 0 reject code to
                ** indicate a normal disconnect.
                */


                *LATrejectTable[] = {
                        "Unknown",
                        "User requested disconnect",
                        "System shutdown in progress",
                        "Invalid slot received",
                        "Invalid service class received",
                        "Insufficient resources at server",
                        "Port or service in use",
                        "No such service",
                        "Service is disabled",
                        "Service is not offeredon the requested port",
                        "Port name is unknown",
                        "Invalid service password",
                        "Remote entry is not in queue",
                        "Immediate access rejected",
                        "Access denied",
                        "Corrupted request",
                        "Requested function is not supported",
                        "Session cannot be started",
                        "Queue entry deleted by server",
                        "Illegal request parameters"                };

unsigned short  LTAchannel,         /* LTA device I/O channel               */
                TTchannel,          /* TT device I/O channel                */
                LTA_QIOiosb[4],     /* IOSB for LTA device functions        */
                TT_QIOiosb[4];      /* IOSB for TT device functions         */


unsigned long   ReadTerminatorMask[2] = { 0, 0 },
                                    /* $QIO read terminator mask            */
                SavedTTdeviceChar[3],
                                    /* Saved TT device characteristics      */
                DeviceCharBuffSize = sizeof(SavedTTdeviceChar);
                                    /* Size of device characteristics buffer*/
                ExitConditionValue, /* Exit condition value of program      */
                LATrejectTableSize =/* Number of elements in LAT reject tbl */
                    sizeof(LATrejectTable) / sizeof(LATrejectTable[0]);

/*
** Itemlist for setting LAT port with the target service name.
*/

struct {
        unsigned short  item_code;
        char            item_byte_count;
        char            item_value[ SERVICE_NAME_LENGTH ];
} PortSetmodeItemlist = {
        LAT$_ITM_TARGET_SERVICE_NAME, SERVICE_NAME_LENGTH, SERVICE_NAME
};

/*
** Exit handler block.
*/

struct {
        unsigned long   flink;
        void            (*exit_handler)();
        unsigned long   arg_count;
        unsigned long   *exit_status;
} ExitHandlerBlock = { 0, ExitHandler, 1,  };

/*
** Devices which channels are assigned to.
*/

$DESCRIPTOR(LTAtemplateDSC, "LTA0:");
$DESCRIPTOR(TTchannelDSC, "SYS$COMMAND");



main()
{
        /*
        ** Local Variables:
        */

        unsigned long        status,
                        portSetmodeItemlistSize = sizeof(PortSetmodeItemlist);

        /*
        ** BEGIN:
        **
        ** Declare an exit handler.
        */

        if (!( (status = sys$dclexh() ) & 1) )
                lib$signal(status);

        /*
        ** Assign a channel to LTA0: to get a forward LAT port and assign a
        ** channel to the terminal.
        */

        if (!( (status = sys$assign(, , 0, 0) ) & 1) )
                lib$signal(status);
        if (!( (status = sys$assign(, , 0, 0) ) & 1) )
                lib$signal(status);

        /*
        ** Allocate memory for the channel data buffers.
        */

        LTAbuffer = malloc(LTA_MAXBUF);
        TTbuffer = malloc(TT_MAXBUF);

        /*
        ** Set device characteristics for the two channels.
        */

        if (!( (status = SetDeviceChars() ) & 1) )
                lib$signal(status);

        /*
        ** Do SETmode $QIO to set the port entity with the target service name
        ** specified in the item list.
        */


        if (!( (status = sys$qiow(
                            0,
                            LTAchannel,
                            IO$_TTY_PORT|IO$M_LT_SETMODE,
                            _QIOiosb, 0, 0,
                            ,
                            portSetmodeItemlistSize,
                            LAT$C_ENT_PORT|(LAT$C_ENTS_OLD << 0x10),
                            0, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(LTA_QIOiosb[0] & 1) )
                lib$signal(LTA_QIOiosb[0]);

        /*
        ** Enable a CTRL+Y AST on the LAT channel.
        */

        if (!( (status = sys$qiow(
                            0,
                            LTAchannel,
                            IO$_SETMODE|IO$M_CTRLYAST,
                            _QIOiosb, 0, 0,
                            LTAhangupHandler,
                            0, 0, 0, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(LTA_QIOiosb[0] & 1) )
                lib$signal(LTA_QIOiosb[0]);

        /*
        ** Post the first read (with AST) on the LTA device to ensure that the
        ** first burst of data from the target service is not lost.  It is very
        ** important that the first read is queued before doing the connect
        ** $QIO to ensure no data lossage.
        */

        if (!( (status = sys$qio(
                            0,
                            LTAchannel,
                            IO$_READVBLK|IO$M_NOECHO,
                            _QIOiosb,
                            LTAreadChannelAST, 0,
                            LTAbuffer,
                            1, 0, , 0, 0) ) & 1) )
                lib$signal(status);

        /*
        ** Do the LAT connect $QIO and hibernate until program exit.  The
        ** ConnectAST will execute when the connection completes and post the
        ** initial read on the TT channel.
        */

        if (!( (status = sys$qio(
                            0,
                            LTAchannel,
                            IO$_TTY_PORT|IO$M_LT_CONNECT,
                            _QIOiosb,
                            ConnectAST, 0, 0, 0, 0, 0, 0, 0) ) & 1) )
                lib$signal(status);
        sys$hiber();

}       /* END - main() */



/*
**++
**
**  FUNCTIONAL DESCRIPTION:
**
**      This routine sets device characteristics of the LTA and TT devices.
**      The HOSTSYNC, NOBRDCST, EIGHTBIT and PASTHRU characteristics are set
**      	on the LTA device.  The ESCAPE and TTSYNC characteristics are cleared.
**
**      The TTSYNC, HOSTSYNC, EIGHTBIT, and PASTHRU characteristics are set
**      on the TT device.  The ESCAPE characteristic is cleared.  The TT
**      characterisitcs are also saved for restoration at program exit.
**
**--
*/

unsigned long   SetDeviceChars(void)
{
        /*
        ** Local Variables:
        */

        unsigned long   status,
                        deviceChar[3];

        /*
        ** BEGIN:
        **
        ** Mask and set the characteristics of the LTA device.  Sense the
        ** current characteristics, and mask in and set the new ones.
        */

        if (!( (status = sys$qiow(
                            0,
                            LTAchannel,
                            IO$_SENSEMODE,
                            _QIOiosb, 0, 0,
                            ,
                            DeviceCharBuffSize, 0, 0, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(LTA_QIOiosb[0] & 1) )
                lib$signal(LTA_QIOiosb[0]);

        deviceChar[1] = 
            (deviceChar[1] | (TT$M_HOSTSYNC | TT$M_NOBRDCST | TT$M_EIGHTBIT) )
	    & ~TT$M_ESCAPE & ~TT$M_TTSYNC;
        deviceChar[2] |= TT2$M_PASTHRU;


        if (!( (status = sys$qiow(
                            0,
                            LTAchannel,
                            IO$_SETMODE,
                            &TT_QIOiosb, 0, 0,
                            &deviceChar
                            DeviceCharBuffSize, 0, 0, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(LTA_QIOiosb[0] & 1) )
                lib$signal(LTA_QIOiosb[0]);

        /*
        ** Repeat the procedure for TT device characteristics.  However, save
        ** the current characteristics for restoration at program exit.
        */

        if (!( (status = sys$qiow(
                            0,
                            TTchannel,
                            IO$_SENSEMODE,
                            $TT_QIOiosb, 0, 0,
                            &SavedTTdeviceChar
                            DeviceCharBuffSize, 0, 0, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(TT_QIOiosb[0] & 1) )
                lib$signal(TT_QIOiosb[0]);

        deviceChar[0] = SavedTTdeviceChar[0];
        deviceChar[1] = (SavedTTdeviceChar[1] |
            (TT$M_TTSYNC | TT$M_HOSTSYNC | TT$M_EIGHTBIT) ) & ~TT$M_ESCAPE;
        deviceChar[2] = SavedTTdeviceChar[2] | TT2$M_PASTHRU;

        if (!( (status = sys$qiow(
                            0,
                            TTchannel,
                            IO$_SETMODE,
                            &TT_QIOiosb, 0, 0,
                            &deviceChar
                            DeviceCharBuffSize, 0, 0, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(TT_QIOiosb[0] & 1) )
                lib$signal(TT_QIOiosb[0]);

        return(status);

}       /* END - SetDeviceChars */



/*
**++
**
**  FUNCTIONAL DESCRIPTION:
**
**      This routine is an AST which executes when the connect $QIO completes.
**      First the IOSB is checked.  If the connection timed out or was aborted,
**      simply end the session.  Any other abnormal status causes the program
**      to exit.
**
**      Otherwise the connection completed successfully and a read on the TT
**      channel is posted.
**
**--
*/

void    ConnectAST()
{
        /*
        ** Local Variables:
        */

        unsigned long   status;


        /*
        ** BEGIN:
        **
        ** If the status in the IOSB indicates that the connection timed out
        ** or aborted, call the session end routine.  Any other abnormal
        ** status causes program exit.
        */

        if ( (LTA_QIOiosb[0] == SS$_TIMEOUT) || (LTA_QIOiosb[0] == SS$_ABORT) )
                EndSession();

        if (!(LTA_QIOiosb[0] & 1) )
                sys$exit(LTA_QIOiosb[0]);

        /*
        ** The connection completed successfully!  Post a read (with AST) on
        ** the TT device and return.
        */

        if (!( (status = sys$qio(
                            0,
                            TTchannel,
                            IO$_READVBLK|IO$M_NOECHO,
                            &TT_QIOiosb,
                            TTreadChannelAST, 0,
                            TTbuffer,
                            1, 0, &ReadTerminatorMask 0, 0) ) & 1) )
                lib$signal(status);

        return;

}       /* END - ConnectAST */



/*
**++
**
**  FUNCTIONAL DESCRIPTION:
**
**      This routine is an AST which executes when the first character read on
**      the LTA channel completes.  It does a "flush" read of the channel to
**      drain any data out of the ALTYPAHD buffer and writes the data to the
**      TT channel.  It then posts another read on the channel.
**
**--
*/

void    LTAreadChannelAST(void)
{
        /*
        ** Local Variables:
        */

        unsigned long   status;

        /*
        ** BEGIN:
        **
        ** If the status in the IOSB indicates channel hangup, simply end the
        ** session.  Signal any other abnormal status.
        */

        if (LTA_QIOiosb[0] == SS$_HANGUP)
                EndSession();
        if (!(LTA_QIOiosb[0] & 1) )
                lib$signal(LTA_QIOiosb[0]);

        /*
        ** Do a "flush" read of the LTA device.  This is done by doing a timed
        ** read with a 0 timeout.  There may or may not be any data to drain.
        ** This method is more efficient than using single character reads.
        */

        if (!( (status = sys$qiow(
                            0,
                            LTAchannel,
                            IO$_READVBLK|IO$M_TIMED|IO$M_NOECHO,
                            _QIOiosb, 0, 0,
                            LTAbuffer+1,
                            LTA_MAXBUF-1, 0,
                            &ReadTerminatorMask, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(LTA_QIOiosb[0] & 1) && (LTA_QIOiosb[0] != SS$_TIMEOUT) )
                lib$signal(LTA_QIOiosb[0]);

        /*
        ** The second word of the IOSB contains the number of characters
        ** read.  Write the characters plus 1 for the initial read to the
        ** TT device.
        */


        if (!( (status = sys$qiow(
                            0,
                            TTchannel,
                            IO$_WRITEVBLK,
                            _QIOiosb, 0, 0,
                            LTAbuffer,
                            LTA_QIOiosb[1]+1, 0, 0, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(TT_QIOiosb[0] & 1) )
                lib$signal(TT_QIOiosb[0]);

        /*
        ** Post another read on the LTA device.
        */

        if (!( (status = sys$qio(
                            0,
                            LTAchannel,
                            IO$_READVBLK|IO$M_NOECHO,
                            &LTA_QIOiosb,
                            LTAreadChannelAST, 0,
                            LTAbuffer,
                            1, 0, &ReadTerminatorMask, 0, 0) ) & 1) )
                lib$signal(status);

        return;

}       /* END - LTAreadChannelAST */


/*
**++
**
**  FUNCTIONAL DESCRIPTION:
**
**      This routine is an AST which executes when the first character read on
**      the TT channel completes.  It does a "flush" read of the channel to
**      drain any data out of the TYPAHD buffer and writes the data to the
**      LTA channel.  It then posts another read on the channel.
**
**--
*/


void    TTreadChannelAST(void)
{
        /*
        ** Local Variables:
        */

        unsigned long   status;

        /*
        ** BEGIN:
        **
        ** If the user pressed the connection terminator character, do a LAT
        ** disconnect $QIO and exit.
        */

        if (*TTbuffer == CONNECTION_TERMINATOR)
        {
                if (!( (status = sys$qiow(
                                    0,
                                    LTAchannel,
                                    IO$_TTY_PORT|IO$M_LT_DISCON,
                                    _QIOiosb, 0, 0, 0, 0, 0, 0, 0, 0) ) & 1) )
                        lib$signal(status);
                if (!(LTA_QIOiosb[0] & 1) )
                        lib$signal(LTA_QIOiosb[0]);
                return;
        }

        /*
        ** Do a "flush" read of the TT device.  This is done by doing a timed
        ** read with a 0 timeout.  There may or may not be any data to drain.
        */

        if (!( (status = sys$qiow(
                            0,
                            TTchannel,
                            IO$_READVBLK|IO$M_TIMED|IO$M_NOECHO,
                            _QIOiosb, 0, 0,
                            TTbuffer+1,
                            TT_MAXBUF-1, 0,
                            &ReadTerminatorMask, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(TT_QIOiosb[0] & 1) && (TT_QIOiosb[0] != SS$_TIMEOUT) )
                lib$signal(TT_QIOiosb[0]);


        /*
        ** The second word of the IOSB contains the number of characters
        ** read.  Write the characters plus 1 for the initial read to the
        ** TT device.
        */

        if (!( (status = sys$qiow(
                            0,
                            LTAchannel,
                            IO$_WRITEVBLK,
                            _QIOiosb, 0, 0,
                            TTbuffer,
                            TT_QIOiosb[1]+1, 0, 0, 0, 0) ) & 1) )
                lib$signal(status);

        /*
        ** If the status in the IOSB indicates channel hangup, simply end
        ** the session.  Signal any other abnormal status.
        */

        if (LTA_QIOiosb[0] == SS$_HANGUP)
                EndSession();
        if (!(LTA_QIOiosb[0] & 1) )
                lib$signal(LTA_QIOiosb[0]);

        /*
        ** Post another read on the LTA device.
        */

        if (!( (status = sys$qio(
                            0,
                            TTchannel,
                            IO$_READVBLK|IO$M_NOECHO,
                            _QIOiosb,
                            TTreadChannelAST, 0,
                            TTbuffer,
                            1, 0, &ReadTerminatorMask, 0, 0) ) & 1) )
                lib$signal(status);

        return;

}       /* END - TTreadChannelAST */



/*
**++
**
**  FUNCTIONAL DESCRIPTION:
**
**      This routine is the CTRL+Y AST for the LTA channel.  It executes when
**      a hangup on the LTA channel is recognized (connection timed out or
**      aborted).  It will call the session end routine if it hasn't already
**      been called by ConnectAST.
**
**      NOTE:   CTRL+Y ASTs for application ports will NOT execute when the
**              connection is disconnected.
**
**--
*/

void    LTAhangupHandler(void)
{
        /*
        ** BEGIN:
        **
        ** Call the session end routine and return.
        */

        EndSession();
        return;

}       /* END - LTAhanghupHandler */


/*
**++
**
**  FUNCTIONAL DESCRIPTION:
**
**      This routine is executed at session end.  It will do a $QIO SENSEmode
**      and search the resulting itemlist to find the reason for the LAT
**      disconnect.  The reason for the disconnect is displayed on the
**      terminal and the image exits.
**
**--
*/

void    EndSession(void)
{

        /*
        ** Local Variables:
        */

        struct ITEM_ENTRY   *itemlistEntry;
        unsigned long       status;
        char                *senseItemlist = malloc(MAX_SENSE_ITEMLIST_SIZE),
                            *itemlistEntryPointer;

        /*
        ** BEGIN:
        **
        ** Do the SENSEmode on the port.
        */


        if (!( (status = sys$qiow(
                            0,
                            LTAchannel,
                            IO$_TTY_PORT|IO$M_LT_SENSEMODE,
                            &LTA_QIOiosb, 0, 0,
                            senseItemlist,
                            MAX_SENSE_ITEMLIST_SIZE,
                            LAT$C_ENT_PORT|(LAT$M_SENSE_FULL << 0x10),
                            0, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(LTA_QIOiosb[0] & 1) )
                lib$signal(LTA_QIOiosb[0]);

        /*
        ** Set up two pointers used to traverse the itemlist.
        */

        itemlistEntry = (struct ITEM_ENTRY *) senseItemlist;
        itemlistEntryPointer = senseItemlist;

        /*
        ** Search the itemlist for the LAT$_ITM_DISCONNECT_REASON code to find
        ** out why the connection terminated.
        */

        while (itemlistEntry->LAT$R_ITM_CODE.LAT$W_ITEMCODE !=
                LAT$_ITM_DISCONNECT_REASON)
        {
                /*
                ** If the current itemcode being checked has a string value,
                ** advance the pointer to the next itemcode by skipping
                ** BCNT bytes plus 3 bytes for the BCNT byte itself and the
                ** 2 byte itemcode.
                */

                if (itemlistEntry->
                    LAT$R_ITM_CODE.LAT$R_ITM_BITS.LAT$V_STRING)
                        itemlistEntryPointer +=
                            itemlistEntry->LAT$R_ITEM_VALUE.
                                LAT$R_ITEM_COUNTED_STRING.LAT$B_ITEM_BCNT + 3;

                /*
                ** If the current itemcode being checked has a scalar value,
                ** advance the pointer to the next itemcode by skipping 6
                ** bytes for the itemcode and the 4 byte scalar.
                */

                else
                        itemlistEntryPointer += 6;
                itemlistEntry = (struct ITEM_ENTRY *) itemlistEntryPointer;
        }


        /*
        ** If the disconnect reason is a LAT reject code, print out the
        ** text corresponding to the code and set the exit condition value
        ** to SS$_NORMAL.
        */

        if (itemlistEntry->LAT$R_ITEM_VALUE.LAT$L_ITEM_SCALAR_VALUE <=
            LATrejectTableSize)
        {
                printf("\nSession disconnected.  Reason: %s\n\n\n",
                    LATrejectTable[ itemlistEntry->LAT$R_ITEM_VALUE.
                                    LAT$L_ITEM_SCALAR_VALUE ]);
                ExitConditionValue = SS$_NORMAL;
        }

        /*
        ** The scalar value is a LAT facility message code.  Set the exit
        ** condition value to be the scalar.  Upon image exit, the
        ** corresponding LAT facility message will be displayed.
        */

        else
                ExitConditionValue =
                    itemlistEntry->LAT$R_ITEM_VALUE.LAT$L_ITEM_SCALAR_VALUE;

        sys$exit(ExitConditionValue);

}        /* END - EndSession */


/*
**++
**
**  FUNCTIONAL DESCRIPTION:
**
**      This is the program exit handler which is executed upon image exit.
**      It will cancel all pending I/O on the two channels and restore the
**      TT channel characteristics.
**
**--
*/


void    ExitHandler(void)
{
        /*
        ** Local Variables:
        */

        unsigned long   status;

        /*
        ** BEGIN:
        **
        ** Cancel I/O on the channels, reset terminal characteristics and
        ** return.
        */

        if (!( (status = sys$cancel(LTAchannel) ) & 1) )
                lib$signal(status);
        if (!( (status = sys$cancel(TTchannel) ) & 1) )
                lib$signal(status);

        if (!( (status = sys$qiow(
                            0,
                            TTchannel,
                            IO$_SETMODE,
                            &TT_QIOiosb, 0, 0,
                            &SavedTTDeviceChar,
                            DeviceCharBuffSize, 0, 0, 0, 0) ) & 1) )
                lib$signal(status);
        if (!(TT_QIOiosb[0] & 1) )
                lib$signal(TT_QIOiosb[0]);

        return;

}       /* END - ExitHandler */

The MACRO 32 program FULL_DUPLEX_TERMINAL.MAR (Example 5-2) shows several I/O operations using the full-duplex capabilities of the terminal. This program shows some important concepts about terminal driver programming: assigning an I/O channel, performing full-duplex I/O operations, enabling Ctrl/C AST requests, and itemlist read operations. The program is designed to run with a terminal set to full-duplex mode.

The initialization code queues a read request to the terminal and enables Ctrl/C AST requests. The main loop then prints out a random message every three seconds. When you enter a message on the terminal, the read AST routine prints an acknowledgment message and queues another read request. If you press Ctrl/C, the associated AST routine cancels the I/O operation on the assigned channel and exits to the command interpreter.

Example 5-2 FULL_DUPLEX_TERMINAL.MAR Terminal Driver Programming Example

        .TITLE  FULL_DUPLEX TERMINAL PROGRAMMING EXAMPLE
        .IDENT  /05/

; ********************************************************************
;
;                         TERMINAL PROGRAM
;
; ********************************************************************

        .SBTTL  DECLARATIONS
        .DISABLE        GLOBAL

;
; Declare the external symbols and MACRO libraries.
;

        .EXTERNAL       LIB$GET_EF
        .LIBRARY        'SYS$LIBRARY:LIB.MLB'
        .LIBRARY        'SYS$LIBRARY:STARLET.MLB'
;
; Define symbols
;

        $IODEF                  ; Define I/O function codes
        $QIODEF                 ; Define QIO definition codes
        $SSDEF                  ; Define the system service status codes
        $TRMDEF                 ; Define itemlist read codes
        $TTDEF                  ; Terminal characteristic definitions


;
; Define macros
;
        .SHOW
        .MACRO  ITEM    LEN=0,CODE,VALUE
        .WORD   LEN
        .WORD   TRM$_'CODE'
        .LONG   VALUE
        .LONG   0
        .ENDM   ITEM
        .NOSHOW

;
; Declare exit handler control block
;
EXIT_HANDLER_BLOCK:
        .LONG   0               ; System uses this for pointer
        .LONG   EXIT_HANDLER    ; Address of exit handler
        .LONG   1               ; Argument count for handler
        .LONG   STATUS          ; Destination of status code
STATUS: .BLKL   1               ; Status code from $EXIT

;
; Allocate terminal descriptor and channel number storage
;

TT_DESC:
        .ASCID  /SYS$INPUT/     ; Logical name of terminal
TT_CHAN:
        .BLKW   1               ; TT channel number storage

;
; Define acknowledgment message.  This is done right above input buffer
; so that we can concatenate the two together when the acknowledgment
; message is issued.
;

ACK_MSG:
        .ASCII  <CR> /Following input acknowledged: /
ACK_MSGLEN=.-ACK_MSG            ; Calculate length of message

;
; Allocate input buffer
;

IN_BUFLEN = 20                  ; Set length of buffer
IN_BUF:
        .BLKB   IN_BUFLEN       ; Allocate character buffer
IN_IOSB:
        .BLKQ   1               ; Input I/O status block
;
; Define out-of-band ast character mask
;
CNTRLA_MASK:
        .LONG   0
        .LONG   ^B0010          ; Control A mask



;
; Define old terminal characteristics buffer
;
OLDCHAR_BUF_LEN = 12
OLDCHAR_BUF:
        .BLKB   OLDCHAR_BUF_LEN

;
; Define new terminal characteristics buffer
;
NEWCHAR_BUF_LEN = 12
NEWCHAR_BUF:
        .BLKB   NEWCHAR_BUF_LEN

;
; Define carriage control symbols
;

        CR=^X0D                 ; Carriage return
        LF=^X0A                 ; Line feed

;
; Define output messages
;
; Output messages are accessed by indexing into a table of
; longwords with each message described by a message address and
; message length
;

ARRAY:                          ; Table of message addresses and
                                ; lengths
        .LONG   10$             ; First message address
        .LONG   15$             ; First message length
        .LONG   20$
        .LONG   25$
        .LONG   30$
        .LONG   35$
        .LONG   40$
        .LONG   45$
;
; Define messages
;

10$:    .ASCII            <CR>/RED ALERT! RED ALERT!/
15$=.-10$
;
20$:    .ASCII            <CR>/ALL SYSTEMS GO/
25$=.-20$
;
30$:    .ASCII            <CR>/WARNING..INTRUDER ALARM/
35$=.-30$
;
40$:    .ASCII            <CR>/** SYSTEM OVERLOAD **/
45$=.-40$
;
; Static QIO packet for message output using QIO$_G form
;


WRITE_QIO:
        $QIO    EFN=SYNC_EFN, - ; QIO packet
                FUNC=IO$_WRITEVBLK!IO$M_BREAKTHRU!IO$M_REFRESH, -
                IOSB=SYNC_IOSB

;
; Declare the required I/O status blocks.
;
SYNC_IOSB::     .BLKQ   1       ; I/O status block for synchronous terminal processing.

;
; Declare the required event flags.
;
ASYNC_EFN::     .BLKL   1       ; Event flag for asynchronous terminal processing.
SYNC_EFN        ==      WRITE_QIO + 4   ; Event flag for sync terminal processing.
TIMER_EFN::     .BLKL   1       ; Event flag for timer processing.

;
; Timer storage
;

WAITIME:
        .LONG   -10*1000*1000*3,-1     ; 3 second delta time
TIME:
        .BLKQ   1               ; Current storage time used for
                                ; random number

        .PAGE
        .SBTTL  START - MAIN ROUTINE
        .ENABLE LOCAL_BLOCK
;++
;
; Functional description:
;
; ********************************************************************
;
;                      Start program
;
; ********************************************************************
;
;       The following code performs initialization functions.
;       It is assumed that the terminal is already in
;       FULL-DUPLEX mode.
;
;       NOTE: When doing QIO_S calls, parameters P1 and P3-P6 should be
;             passed by value, while P2 should be passed by reference.
;
; Input parameters:
;       None
;
; Output parameters:
;       None
;
;--
        .ENTRY  START   ^M < >


;               Get the required event flags.

        PUSHAL  ASYNC_EFN
        CALLS   # 1, G^ LIB$GET_EF      ; Get EFN for async terminal operations.
        BLBC    R0, 10$                 ; Error - branch.
        PUSHAL  SYNC_EFN
        CALLS   # 1, G^ LIB$GET_EF      ; Get EFN for sync terminal operations.
        BLBC    R0, 10$                 ; Error - branch.
        PUSHAL  TIMER_EFN
        CALLS   # 1, G^ LIB$GET_EF      ; Get EFN for timer operations.
        BLBC    R0, 10$                 ; Error - branch.


;               Initialize the terminal characteristics.

        $ASSIGN_S       DEVNAM=TT_DESC,-; Assign terminal channel using
                        CHAN=TT_CHAN    ; logical name and channel number
        BLBC    R0, 10$                 ; Error - branch.
        BSBW    CHANGE_CHARACTERISTICS  ; Change the characteristics of
                                        ; terminal
        BSBW    ENABLE_CTRLCAST         ; Allow Ctrl/C traps
        BSBW    ENABLE_OUTBANDAST       ; Enable Ctrl/A out-of-band AST
        BSBW    ENABLE_READ             ; Queue read
        MOVZWL  TT_CHAN, WRITE_QIO+8    ; Insert channel into
        BRB     LOOP                    ; static QIO packet

10$:
        BRW     ERROR

;
; This loop outputs a message based on a random number and then
; delays for 3 seconds
;

LOOP:
        $GETTIM_S      TIMADR=TIME      ; Get random time
        BLBC    R0, 10$                 ; Error - branch.
        EXTZV   #6, #2, TIME, R0        ; Load random bits into switch
        MOVQ    ARRAY[R0], -            ; Load message address
                WRITE_QIO+QIO$_P1       ; and size into QIO
                                        ; packet

;
; Issue QIO write using packet defined in data area
;

        $QIOW_G WRITE_QIO
        BLBC    R0, 10$                 ; QIO error - branch.
        MOVZWL  SYNC_IOSB, R0           ; Get the terminal driver status.
        BLBC    R0, 10$                 ; Terminal driver error - branch.



;
; Delay for 3 seconds before issuing next message
;

        $SETIMR_S       EFN=TIMER_EFN,- ; Timer service
                        DAYTIM=WAITIME  ; will set event flag
                                        ; in 3 seconds
        BLBC    R0, 10$                 ; Error - branch.
        $WAITFR_S       EFN=TIMER_EFN   ; Wait for event flag
        BLBS    R0, LOOP                ; No error if set
        BRB     10$                     ; Error - branch.

        .DISABLE        LOCAL_BLOCK

        .PAGE
        .SBTTL  CHANGE_CHARACTERISTICS - CHANGE CHARACTERISTICS OF TERMINAL
;++
;
; Functional description:
;
;       Routine to change the characteristics of the terminal.
;
; Input parameters:
;       None
;
; Output parameters:
;       R0 - status from $QIO call.
;       R1 - R5 destroyed
;
;--
;



CHANGE_CHARACTERISTICS:
        $QIOW_S EFN=SYNC_EFN, -         ; Get current terminal characteristics
                CHAN=TT_CHAN, -
                FUNC=#IO$_SENSEMODE, -
                IOSB=SYNC_IOSB, -
                P1=OLDCHAR_BUF, -
                P2=#OLDCHAR_BUF_LEN
        BLBC    R0, 10$                 ; Error if clear
        MOVZWL  SYNC_IOSB, R0           ; Get the terminal driver status.
        BLBC    R0, 10$                 ; Error - branch

        $DCLEXH_S EXIT_HANDLER_BLOCK    ; Declare exit handler to reset
                                        ; characteristics
        BLBC    R0, 10$                 ; Error - branch.
        MOVC3   #OLDCHAR_BUF_LEN, -     ; Move old characteristics into
                OLDCHAR_BUF, -          ; new characteristics buffer
                NEWCHAR_BUF
        BISL2   #TT$M_NOBRDCST, -       ; Set nobroadcast bit
                NEWCHAR_BUF+4           ; ...
        $QIOW_S EFN=SYNC_EFN, -         ; Set current terminal characteristics
                CHAN=TT_CHAN, -
                FUNC=#IO$_SETMODE, -
                IOSB=SYNC_IOSB, -
                P1=NEWCHAR_BUF, -
                P2=#NEWCHAR_BUF_LEN
        BLBC    R0, 10$                 ; QIO error - branch.
        MOVZWL  SYNC_IOSB, R0           ; Get the terminal driver status.
        BLBC    R0, 10$                 ; Terminal driver error - branch.
        RSB
10$:
        BRW     ERROR

        .PAGE
        .SBTTL  ENABLE_CTRLCAST - ENABLE Ctrl/C AST
;++
;
; Functional description:
;
;       Routine to allow Ctrl/C recognition.
;
; Input parameters:
;       None
;
; Output parameters:
;       None
;
;--
;

ENABLE_CTRLCAST:
        $QIOW_S EFN=SYNC_EFN, -               
                CHAN=TT_CHAN, -
                FUNC=#IO$_SETMODE!IO$M_CTRLCAST, -
                IOSB=SYNC_IOSB, -
                P1=CTRLCAST, -          ; AST routine address
                P3=#3                   ; User mode
        BLBC    R0, 10$                 ; Error - branch.
        MOVZWL  SYNC_IOSB, R0           ; Get the terminal driver status.
        BLBC    R0, 10$                 ; Terminal driver error - branch.
        RSB



10$:
        BRW     ERROR

        .PAGE
        .SBTTL  ENABLE_OUTBANDAST - ENABLE Ctrl/A AST
;++
;
; Functional description:
;
;       Routine to allow CNTRL/A recognition.
;
; Input parameters:
;       None
;
; Output parameters:
;       None
;

ENABLE_OUTBANDAST:
        $QIOW_S EFN=SYNC_EFN, -
                CHAN=TT_CHAN, -
                FUNC=#IO$_SETMODE!IO$M_OUTBAND, -
                IOSB=SYNC_IOSB, -
                P1=CTRLAAST, -          ; AST routine address
                P2=#CNTRLA_MASK, -      ; Character mask
                P3=#3                   ; User mode
        BLBC    R0, 10$                 ; QIO error - branch.
        MOVZWL  SYNC_IOSB, R0           ; Get the terminal driver status.
        BLBC    R0, 10$                 ; Terminal driver error - branch.
        RSB

10$:
        BRW     ERROR

        .PAGE
        .SBTTL  ENABLE_READ - QUEUE A READ TO THE TERMINAL.
;++
;
; Functional description:
;
;       Routine to queue a read operation to the terminal.
;
; Input parameters:
;       None
;
; Output parameters:
;       None
;
; Define item list for itemlist read
;
ITEM_LST:
        ITEM    0, MODIFIERS, -                 ; Convert lowercase to
                TRM$M_TM_CVTLOW!TRM$M_TM_NOEDIT ; upper and inhibit line
        ITEM    6, TERM,MASK_ADDR               ; editing
                                                ; Set up terminator mask


ITEM_LEN        =       . - ITEM_LST
MASK_ADDR:
        .LONG   1@^XD                   ; Terminator mask is 
                                         ; <CR>
.WORD   1@4                     ; and "$"ENABLE_READ:
        $QIO_S  EFN=ASYNC_EFN, -        ; Must not be QIOW form or read will block
                CHAN=TT_CHAN, -         ; process
                FUNC=#IO$_READVBLK!IO$M_EXTEND, -
                IOSB=IN_IOSB, -
                ASTADR=READAST, -       ; AST routine to execute
                P1=IN_BUF, -            ; on
                P2=#IN_BUFLEN, -
                P5=#ITEM_LST, -         ; Itemlist read address
                P6=#ITEM_LEN            ; Itemlist read size
        BLBC    R0, 10$                 ; QIO error - branch.

; The queued read operation will not affect write operations due
; to the fact that breakthru has been set for the write operations.

        RSB

10$:
        BRW     ERROR

        .PAGE
        .SBTTL  READAST - AST ROUTINE FOR READ COMPLETION
        .ENABLE LOCAL_BLOCK
;++
;
; Functional description:
;
;       AST routine to execute on read completion.
;
; Input parameters:
;       None
;
; Output parameters:
;       None
;
;--
;

10$:
        MOVZWL  IN_IOSB, R0             ; Get the terminal driver status
20$:
        BRW     ERROR                   ; Exit with error status.


        .ENTRY  READAST         ^M < R2, R3, R4, R5 >   ; Procedure entry mask

        BLBC    IN_IOSB, 10$            ; Terminal driver error - branch
        MOVZWL  IN_IOSB+2, R0           ; Get number of characters read into R0
        ADDL2   #ACK_MSGLEN, R0         ; Add size of fixed acknowledge message
        $QIO_S  EFN=ASYNC_EFN, -        ; Issue acknowledge message
                CHAN=TT_CHAN, -         ; Note, ACK must be asynchronous (QIO) 
                FUNC=#IO$_WRITEVBLK, -  ; and the terminal driver write status 
                P1=ACK_MSG, -           ; is ignored (no IOSB and AST routine).
                P2=R0                   ; Specify IOSB and AST routine if output
                                        ; must be displayed on the terminal.
        BLBC    R0, 20$                 ; QIO error - branch


;
; Process read message
;
;        .
;        .
;        .
;(user-provided code to decode command inserted here)
;        .
;        .
;        .

        BSBW     ENABLE_READ            ; Queue next read
        RET                             ; Return to mainline loop

        .DISABLE        LOCAL_BLOCK

        .PAGE
        .SBTTL  CTRLAAST - AST ROUTINE FOR Ctrl/A
        .SBTTL  CTRLCAST - AST ROUTINE FOR Ctrl/C
        .SBTTL  ERROR - EXIT ROUTINE
;++
;
; Functional description:
;
;       AST routine to execute when Ctrl/C or Ctrl/A is entered.
;
; Input parameters:
;       None
;
; Output parameters:
;       None
;

CTRLCAST::
CTRLAAST::
        .WORD   ^M < >                  ; Procedure entry mask
        MOVL    #SS$_NORMAL, R0         ; Put success in R0

ERROR::
        $EXIT_S R0                      ; Exit
        RSB

        .PAGE
        .SBTTL  EXIT_HANDLER - EXIT HANDLER ROUTINE
;++
;
; Functional description:
;
;       Exit handler routine to execute when image exits.  It cancels
;       any outstanding I/O on this channel and resets the terminal
;       characteristics to their original state.
;
; Input parameters:
;       None
;
; Output parameters:
;       None
;
;--
;


        .ENTRY  EXIT_HANDLER    ^M< >
        $CANCEL_S       CHAN=TT_CHAN    ; Flush any I/O on queue
        $QIOW_S EFN=SYNC_EFN, -         ; Reset terminal characteristics
                CHAN=TT_CHAN, -
                FUNC=#IO$_SETMODE, -
                IOSB=SYNC_IOSB, -
                P1=OLDCHAR_BUF, -
                P2=#OLDCHAR_BUF_LEN
        BLBC    R0, 10$                 ; QIO error - branch.
        MOVZWL  SYNC_IOSB, R0           ; Get the terminal driver status.

10$:
        RET

        .END     START

The MACRO 32 program READ_VERIFY.MAR (Example 5-3) shows the read verify function. The program shows a typical build of itemlists (both the right and left fields), channel assignment, a right- and left-justified read verify operation, and then the read QIO operation.

Example 5-3 READ_VERIFY.MAR Terminal Driver Programming Example

        .TITLE READ_VERIFY - Read Verify Coding Example
        .IDENT  'V05-000'

        .SBTTL  DECLARATIONS
        .DISABLE        GLOBAL

;
; Declare the external system routines and MACRO libraries.
;
        .EXTERNAL       LIB$GET_EF
        .EXTERNAL       SCR$ERASE_PAGE

        .LIBRARY        'SYS$LIBRARY:LIB.MLB'
        .LIBRARY        'SYS$LIBRARY:STARLET.MLB'
;
; Include files:
;
        $IODEF
        $TRMDEF
;
; Macros:
;
.MACRO ITEM LEN=0,CODE,VALUE
        .WORD   LEN
        .WORD   TRM$_'CODE'
        .LONG   VALUE
        .LONG   0
.ENDM ITEM

;
; Equated symbols:
;
INBUF_LEN = 20
ESC = ^X1B


;
; Own storage:
;
; Build item lists for the read verify QIO
;

;
; Right-justified field
;
R_ITEM_LIST:
        ITEM    CODE    = MODIFIERS, -
                VALUE   = TRM$M_TM_R_JUST       ; Right justify

        ITEM    CODE    = EDITMODE, -
                VALUE   = TRM$K_EM_RDVERIFY     ; Enable read verify

        ITEM    CODE    = PROMPT, -
                VALUE   = R_PROMPT_ADDR, -
                LEN     = R_PROMPT_LEN          ; Set up prompt

        ITEM    CODE    = INISTRNG, -
                VALUE   = R_INISTR_ADDR, -
                LEN     = R_INISTR_LEN          ; Set up initial string

        ITEM    CODE    = INIOFFSET, -
                VALUE   = R_INISTR_LEN

        ITEM    CODE    = PICSTRNG, -
                VALUE   = R_PICSTR_ADDR, -
                LEN     = R_PICSTR_LEN          ; Set up picture string

        ITEM    CODE    = FILLCHR, -
                VALUE   = <^A/* />            ; clear = *, fill = space

R_ITEM_LIST_LEN = .-R_ITEM_LIST

R_PROMPT_ADDR:
        .ASCII  /[12;12H$/
R_PROMPT_LEN = .-R_PROMPT_ADDR

R_INISTR_ADDR:
        .ASCII  /   ,   /
R_INISTR_LEN = .-R_INISTR_ADDR

MASK = TRM$M_CV_NUMERIC!TRM$M_CV_NUMPUNC

R_PICSTR_ADDR:
        .BYTE   MASK
        .BYTE   MASK
        .BYTE   MASK
        .BYTE   0               ; Marker character
        .BYTE   MASK
        .BYTE   MASK
        .BYTE   MASK
R_PICSTR_LEN = .-R_PICSTR_ADDR
;
; Left-justified field
;
L_ITEM_LIST:
        ITEM    CODE    = MODIFIERS, -
                VALUE   = TRM$M_TM_CVTLOW!TRM$M_TM_AUTO_TAB
                                                ; Upcase input and
                                                ; complete on field full

        ITEM    CODE    = EDITMODE, -
                VALUE   = TRM$K_EM_RDVERIFY     ; Enable read verify

        ITEM    CODE    = PROMPT, -
                VALUE   = L_PROMPT_ADDR, -
                LEN     = L_PROMPT_LEN          ; Set up prompt

        ITEM    CODE    = INISTRNG, -
                VALUE   = L_INISTR_ADDR, -
                LEN     = L_INISTR_LEN          ; Set up initial string

        ITEM    CODE    = INIOFFSET, -
                VALUE   = 0

        ITEM    CODE    = PICSTRNG, -
                VALUE   = L_PICSTR_ADDR, -
                LEN     = L_PICSTR_LEN          ; Set up picture string

        ITEM    CODE    = FILLCHR, -
                VALUE   = <^A/* />              ; clear = *, fill = space

L_ITEM_LIST_LEN = .-L_ITEM_LIST

L_PROMPT_ADDR:
        .ASCII  /[13;12H Enter Date: /
L_PROMPT_LEN = .-L_PROMPT_ADDR

L_INISTR_ADDR:
        .ASCII  /  -   -  /
L_INISTR_LEN = .-L_INISTR_ADDR

MASK1 = TRM$M_CV_NUMERIC
MASK2 = TRM$M_CV_UPPER!TRM$M_CV_LOWER


L_PICSTR_ADDR:
        .BYTE   MASK1
        .BYTE   MASK1
        .BYTE   0               ; Marker character
        .BYTE   MASK2
        .BYTE   MASK2
        .BYTE   MASK2
        .BYTE   0               ; marker character
        .BYTE   MASK1
        .BYTE   MASK1
L_PICSTR_LEN = .-L_PICSTR_ADDR

IN_IOSB:        .BLKL   2
TT_CHAN:        .BLKW   1
INBUF:          .BLKB   INBUF_LEN
SYSINPUT:       .ASCID  /SYS$INPUT/
SYNC_EFN:       .BLKL   1

        .PAGE

        .ENTRY  READ_VERIFY     ^M < >

;
; Get the required event flags.
;

        PUSHAL  SYNC_EFN
        CALLS   # 1, G^ LIB$GET_EF
        BLBC    R0, ERROR                       ; Error - branch
;
; Assign the channel to SYS$INPUT
;

        $ASSIGN_S -
                CHAN = TT_CHAN -
                DEVNAM = SYSINPUT               ; SYS$INPUT
        BLBC    R0, ERROR                       ; Branch on error

;
; Clear the screen
;

        CLRQ    -(SP)
        CALLS   #2, G^ SCR$ERASE_PAGE
        BLBC    R0, ERROR

;
; Do the right-justified read operation
;

        PUSHL   #R_ITEM_LIST_LEN
        PUSHAB  R_ITEM_LIST
        CALLS   #2, DO_READ
        BLBC    R0, ERROR


;
; Do the left-justified read operation
;

        PUSHL   #L_ITEM_LIST_LEN
        PUSHAB  L_ITEM_LIST
        CALLS   #2, DO_READ
        BLBC    R0, ERROR

ERROR:
        RET

        .PAGE
;++
;
; DO_READ - do the actual QIO
;
; Inputs:
;
;       4(AP)   the address of the itemlist
;       8(AP)   the length of the itemlist
;
;--

        .ENTRY  DO_READ, ^M
        $QIOW_S EFN=SYNC_EFN, -
                CHAN = TT_CHAN, -
                FUNC = #$_READVBLK!IO$M_EXTEND>, -
                IOSB = IN_IOSB, -
                p1 = inbuf, -
                p2 = #inbuf_len, -
                p5 = 4(AP), -
                P6 = 8(AP)
        BLBC    R0, 10$                 ; QIO error - branch
        MOVZWL  IN_IOSB, R0             ; Get the terminal driver status.
        BLBC    R0, 10$                 ; Terminal driver error - branch

;  Handle the input...

10$:
        RET
        .END READ_VERIFY

Example 5-4 LIB$XXABLE_CTRL.C Terminal Driver Programming Example

//Demonstrates CTRL-Y and CTRL-C handling under OpenVMS,

//as well as
//some basic dynamic string descriptor operations and a few other
//string-related operations.
////To build and use:
//$ CC/DECC LIB$XXABLE_CTRL
//$ LINK LIB$XXABLE_CTRL
//$ RUN LIB$XXABLE_CTRL
/*#include <descrip.h>>#include <iodef.h>>
#include <libclidef.h>>#include <lib$routines.h>>
#include <ssdef.h>>#include <starlet.h>>
#include <stdio.h>>#include <stsdef.h
>>void CtrlYHandler()*/
  {
  int RetStat;
  $DESCRIPTOR( Y, "<CTRL/Y>>was detected" );
  RetStat = lib$put_output(  );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return;
  RetStat = lib$enable_ctrl( $M_CLI_CTRLY );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return;
  return;
  }

void CtrlCHandler()
  {
  int RetStat;
  $DESCRIPTOR( Y, "<CTRL/C>>was detected" );
  RetStat = lib$put_output(  );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return;
  RetStat = lib$enable_ctrl( $M_CLI_CTRLY );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return;
  return;
  }

main()
  {
  int RetStat;
  unsigned short int IOChan;
  unsigned short int GotLen;
  struct dsc$descriptor GotDsc = { 0, DSC$K_DTYPE_T, DSC$K_CLASS_D, NULL };
  $DESCRIPTOR( Prompt, "Enter CTRL/Y, CTRL/C, or any characters and RETURN:" );
  $DESCRIPTOR( Exiting, "Exiting" );
  $DESCRIPTOR( TTDsc, "TT:");

  RetStat = lib$disable_ctrl( $M_CLI_CTRLY );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return RetStat;
  RetStat = sys$assign( , , 0, 0 );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return RetStat;
  RetStat = sys$qiow( 0, IOChan, IO$_SETMODE|IO$M_CTRLYAST, 0, 0, 0,
    CtrlYHandler, 0, 0, 0, 0, 0 );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return RetStat;
  RetStat = sys$qiow( 0, IOChan, IO$_SETMODE|IO$M_CTRLCAST, 0, 0, 0,
    CtrlCHandler, 0, 0, 0, 0, 0 );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return RetStat;
  RetStat = lib$get_input( , ,  );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return RetStat;
  RetStat = sys$dassgn( IOChan );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return RetStat;
  RetStat = lib$enable_ctrl( $M_CLI_CTRLY );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return RetStat;
  RetStat = lib$put_output(  );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return RetStat;
  RetStat = lib$sfree1_dd(  );
  if (!$VMS_STATUS_SUCCESS( RetStat ) )
    return RetStat;
  return SS$_NORMAL;
  }