/* ** COPYRIGHT (c) 1993 BY ** DIGITAL EQUIPMENT CORPORATION, MAYNARD, MASSACHUSETTS. ** ALL RIGHTS RESERVED. ** ** THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY BE USED AND COPIED ** ONLY IN ACCORDANCE OF THE TERMS OF SUCH LICENSE AND WITH THE ** INCLUSION OF THE ABOVE COPYRIGHT NOTICE. THIS SOFTWARE OR ANY OTHER ** COPIES THEREOF MAY NOT BE PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY ** OTHER PERSON. NO TITLE TO AND OWNERSHIP OF THE SOFTWARE IS HEREBY ** TRANSFERRED. ** ** THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE ** AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT ** CORPORATION. ** ** DIGITAL ASSUMES NO RESPONSIBILITY FOR THE USE OR RELIABILITY OF ITS ** SOFTWARE ON EQUIPMENT WHICH IS NOT SUPPLIED BY DIGITAL. */ #ifdef VAX #module READ_WRITE_TERMINAL "V1.0-006" #else #pragma module READ_WRITE_TERMINAL "V1.0-006" #endif /* **++ ** FACILITY: SYS$EXAMPLES ** ** MODULE DESCRIPTION: ** ** READ_WRITE_TERMINAL -- Programming example to demonstrate use ** of the OpenVMS terminal driver to interact with a terminal (as ** defined by the logical name SYS$INPUT) operating in ** "full-duplex" mode (i.e. separate read and write queues). ** Every three seconds, the module puts out a randomly selected ** (from a predefined list) message to the terminal, using QIO ** services. The module also uses QIO services to declare a ** Ctrl/C Asynchronous System Trap (AST) routine and an ** out-of-band AST routine to handle Ctrl/A, and to modify the ** characteristics of the terminal (set nobroadcast). When ** either type of AST is delivered, the module uses a declared ** exit handler to flush any terminal I/O on the queue and to ** reset the modified terminal characteristics to what they were ** on entry to the program. ** ** AUTHORS: ** ** Digital Equipment Corporation ** ** CREATION DATE: ** ** 1 March 1993 (Based on FULL_DUPLEX_TERMINAL.MAR) ** ** ** MODIFICATION HISTORY: ** ** 01-Mar-1993 Conversion from FULL_DUPLEX_TERMINAL.MAR **-- */ /* ** ** INCLUDE FILES ** */ #include /* Descriptor Structure and Constant Definitions */ #include /* I/O function code Definitions */ #include /* Library (LIB$) routine definitions */ #include /* Math function definitions */ #include /* System Service Return Status Value Definitions */ #include /* System routine definitions */ #include /* Symbol definitions for the item list QIO format */ #include /* Symbol definitions for TT device characteristics */ #include /* Symbol definitions for TT characteristics */ #include /* Symbol definitions for additional TT characteristics */ /* ** ** MACRO DEFINITIONS ** */ /* ** Address and size generator for messages in message array. */ #define addr_siz(str) {str,sizeof(str)} /* ** Input buffer contents and storage reservation. */ #define ackmsgtxt "\r\nFollowing input acknowledged: " #define ack_msglen sizeof (ackmsgtxt) #define in_buflen 20 /* ** The following macro defines a status check which ** occurs numerous times throughout the program. */ #define check_s(status) \ if ( ! (status & SS$_NORMAL) ) \ {\ lib$signal (status);\ } /* ** This macro derives the number of different messages from the ** declaration of the message array itself. */ #define num_msgs (sizeof(msg_array)/sizeof(struct msg_descr)) /* ** Define and intialize constants for program */ $DESCRIPTOR (tt_desc, "SYS$INPUT"); /* Terminal Descriptor */ unsigned long int tt_chan; /* TT channel number storage */ unsigned long int exit_status = 0; /* Exit status passed to handler */ unsigned long int msgnum = 0; /* random message number (0-indexed) */ unsigned long int status = SS$_NORMAL; /* sys svc status return */ /* ** Forward routine declarations */ void exit_handler (unsigned long int * ); void ctrl_c_ast (void ); void ctrl_a_ast (char ); void read_ast (void ); /* ** Input buffer -- has the acknowledgment message immediately in front ** of it. */ struct { char ack_msg[ack_msglen]; char in_buf [in_buflen]; } ack_msg_buf = { ackmsgtxt, " " }; /* ** Define messages to be chosen at random for writes to the terminal. ** Each entry is an address and length for a short message. */ struct msg_descr { char * msg_addr; /* Address of message */ unsigned long int msg_len; /* Length of message */ }; struct msg_descr msg_array[] = { addr_siz("\r\nRED ALERT!!! RED ALERT!!"), addr_siz("\r\nALL SYSTEMS GO"), addr_siz("\r\nWARNING.....INTRUDER ALARM"), addr_siz("\r\n***** SYSTEM OVERLOAD **** ") }; /* ** Define exit handler control block. */ struct exhblkdef { void (* next_exit_hdlr) (); /*addr of next hdlr */ void (* this_exit_hdlr) (); /*addr of this hdlr */ unsigned short int num_args, n_arg_fill; /* Num of args passed */ unsigned long int * exit_status; /* Exit status from image */ } exit_handler_block = { (void(*)())0, exit_handler, 1, 0, & exit_status}; /* Define exit handler control block */ /* ** Old and new terminal characteristics buffer: */ struct termchar { unsigned char class; /* device class */ unsigned char type; /* device type */ unsigned short int buf_siz;/* buffer size ( = page width) */ union ttdef bchar; /* basic terminal characteristics */ union tt2def echar; /* extended terminal characteristics */ union tt3def e3char; /* more extended characteristics */ } oldchar_buf, /* Old terminal characteristics buffer */ newchar_buf; /* New terminal characteristics buffer */ /* ** I/O status block definition for terminal operations. Our ** definition includes a non-ANSI-standard variant union construct to ** allow reasonable length names, so we let the compiler know to ** allow the definition this way. Not all the members defined here ** are actually referenced in this example. */ #pragma nostandard struct iosb { unsigned short int status; /* Common to all forms of IOSB */ variant_union { /* Usage-dependent section of IOSB */ struct { /* Item List Read IOSB */ unsigned short int term_off; /* offset to terminator */ unsigned char term_char; /* terminating character */ unsigned char rdvf_st; /* read-verify status */ unsigned char term_lgth; /* length of terminator */ unsigned char curs_posn; } itlrd; struct { /* Terminal Read IOSB */ unsigned short int term_off; char term_esc; /* escape if present in terminator */ char term_fill; /* fill */ unsigned short int term_size; /* length of terminator */ } trmrd; unsigned short int byte_count; /* Write IOSB -- bytes written */ struct { /* Set/Sense Mode/Characteristics IOSB */ unsigned char xmit_speed; /* transmit speed */ unsigned char rcv_speed; /* receive speed if != transmit */ unsigned char cr_fill_count; /* # fill bytes for CR */ unsigned char lf_fill_count; /* # fill bytes for LF */ unsigned char par_flags; /* parity flags */ unsigned char resvd1; /* not used */ } mode_char; } io_spec; } in_iosb, /* terminal read IOSB */ sync_iosb; /* synchronous op IOSB */ #pragma standard /* ** Event flag numbers. */ unsigned long int async_efn, /* asynchronous terminal processing */ sync_efn, /* synchronous terminal processing */ timer_efn; /* timer processing */ /* ** Declare item lists which will automatically upper-case input ** and inhibit line editing (first list), and accept carriage ** return and the dollar sign as terminators (second list). As ** with the IOSB, our definition includes a non-ANSI-standard ** variant union construct, to allow reasonable length names, so ** we let the compiler know to allow the definition this way. ** There are two lists, which are initialized here except for the ** address of the buffer (in this case an out-of-band AST ** character mask) in the second item list. */ #pragma nostandard struct itmlst { unsigned short int buflen; /* Buffer Length */ unsigned short int code; /* Item code */ variant_union { unsigned long int value; /* Immediate value or */ void * bufadr; /* Buffer address */ } valadr; void * retadr; /* Return address (must be zero) */ } item_lst [2]; #pragma standard /* ** Out-of-band AST character mask to catch Ctrl/A input. */ unsigned long int cntrla_mask [2] = { 0, /* short-form terminator mask*/ 2 }; /* bit 1 on for Ctrl/A */ /* ** Time/Timer storage -- actually quadword (64-bit) signed ** integers, (units = 100-nanosecond ticks), but specified as ** longword integers sign-extended to a quadword. */ long int waitime [2] = { -10*1000*1000*3, /* 3 seconds */ -1 }, /* sign-extend negative for delta time */ currtime [2] = { 0, /* Current time to seed */ 0 }; /* pseudo-randomization */ /* ** The terminator mask has the -th bit on if ASCII character is to ** terminate a read when entered. This could also be done as a bitfield ** structure. */ struct term_mask { unsigned long int first_4; /* first 4 bytes last 2 bytes */ unsigned short int last_2; /* Terminator mask includes */ } mask = { 1 << 0x0d, /* carriage return */ 1 << 4 }; /* and "$"*/ /* **++ ** FUNCTIONAL DESCRIPTION: ** ** Main routine: Assigns terminal channel, sets up ASTs, queues reads and ** puts out a random message to the terminal every three seconds. ** ** FORMAL PARAMETERS: ** ** None. ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** No residual effects after exit. ** ** ** **-- */ main () { /* ** Initialize the two item lists. */ item_lst[0].buflen = 0; /* buf len = 0: 1st itmlst */ /* has immediate data which */ item_lst[0].code = TRM$_MODIFIERS; /* contain read modifiers */ item_lst[0].value = TRM$M_TM_CVTLOW| /* convert lower to upper */ TRM$M_TM_NOEDIT; /* case, don't do line editing */ item_lst[0].retadr = 0; /* Must be zero */ item_lst[1].buflen = 6; /* 2nd itmlst has buffer, */ /* length is 6, */ item_lst[1].code = TRM$_TERM; /* buf type is long-form */ /* terminator mask */ item_lst[1].bufadr = &mask; /* point to buffer */ item_lst[1].retadr = 0; /* Must be zero */ /* ** Begin by getting the required event flags. */ status = lib$get_ef (&async_efn); /* EFN for async terminal operations*/ check_s(status) /* ensure flag obtained ok */ status = lib$get_ef (&sync_efn); /* EFN for sync terminal operations */ check_s(status) /* ensure flag obtained ok */ status = lib$get_ef (&timer_efn); /* EFN for timer operations */ check_s(status) /* ensure flag obtained ok */ /* ** Initialize terminal characteristics. */ status = sys$assign ( /* Assign channel using logical name */ &tt_desc, /* device descriptor for SYS$INPUT */ &tt_chan, /* channel number */ 0, /* default access mode */ 0); /* No mailbox */ check_s(status) /* ensure assign went OK */ /* ** Change terminal characteristics */ /* ** Get current terminal characteristics using QIO sensemode ** and save to oldchar_buf. */ status = sys$qiow ( sync_efn, /* Use synchronous event flag */ tt_chan, /* channel for terminal */ IO$_SENSEMODE, /* Function = sense mode */ &sync_iosb, /* use sync status blk */ 0, /* No AST routine */ 0, /* or parameter for this */ &oldchar_buf, /* P1 -- characteristics go here */ sizeof(oldchar_buf), /* P2 -- Char. buffer size */ 0, 0, 0, 0); /* No P3-P6 */ check_s(status) /* Ensure get char QIO worked OK at QIO */ check_s(sync_iosb.status) /* and IOSB level */ /* ** Declare exit handler to reset terminal characteristics ** back to original. */ status = sys$dclexh (&exit_handler_block); check_s (status) /* Ensure handler was declared OK */ newchar_buf = oldchar_buf; /* Move old characteristics into */ /* new characteristics buffer */ newchar_buf.bchar.tt$v_nobrdcst = 1; /* Set no-broadcast bit */ /* ** Set new terminal characteristics using QIO setmode ** from new characteristics buffer. */ status = sys$qiow ( sync_efn, /* Sync event flag again */ tt_chan, IO$_SETMODE, /* Function = set mode */ &sync_iosb, /* Same IOSB as with sense */ 0, /* No AST routine */ 0, /* or parameter for this */ &newchar_buf, /* P1 -- characteristics from here */ sizeof(newchar_buf), /* P2 -- Char. buffer size */ 0, 0, 0, 0); /* No P3-P6 */ check_s(status) /* Ensure set char worked OK at QIO */ check_s(sync_iosb.status) /* and IOSB level */ /* ** Enable Ctrl/C AST: ** ** Use QIO setmode|ctrlcast to transfer to CTRLCAST in user ** mode for AST delivery. The program will exit when this ** happens. */ status = sys$qiow ( sync_efn, /* Sync event flag */ tt_chan, IO$_SETMODE| IO$M_CTRLCAST, /* use ^C AST modif */ &sync_iosb, /* Same IOSB as with sense/set */ 0, /* For setup itself, no AST routine */ 0, /* or parameter */ ctrl_c_ast, /* P1 -> ^C AST routine */ 0, /* No P2: ctrlcast won't get */ /* passed any params */ 3, /* P3 = 3 for user-mode AST deliv */ 0, 0, 0); /* No P4-P6 */ check_s(status) /* Ensure ^C AST setup worked OK at QIO */ check_s(sync_iosb.status) /* and IOSB level */ /* ** Enable Ctrl/A out-of-band AST: ** ** Use QIO setmode/outband to transfer to CTRLAAST in user ** mode for AST delivery. The program will exit when this ** happens. */ status = sys$qiow ( sync_efn, /* Sync event flag */ tt_chan, IO$_SETMODE| IO$M_OUTBAND, /* Use out-of-band AST modifier */ &sync_iosb, /* Same IOSB as with ^C AST */ 0, /* For setup itself, no AST routine */ 0, /* or parameter */ ctrl_a_ast, /* P1 -> ^A o-o-band AST routine */ &cntrla_mask[0], /* P2 -> short-form mask */ 3, /* P3 = 3 for user-mode AST deliv */ 0, 0, 0); /* No P4-P6 */ check_s(status) /* Ensure o-o-b AST setup worked OK at QIO */ check_s(sync_iosb.status) /* and IOSB level */ /* ** Queue a read to the terminal. Use QIO, rather than QIOW, ** so process doesn't block waiting for completion of read from ** terminal. The function is readvblk with the extend ** modifier set so that an item list read to the buffer inbuf ** will be done. Set up a one-shot AST routine (read_ast) to ** handle AST delivered when the read completes. That routine ** will re-queue the read. ** */ status = sys$qio ( async_efn, /* Async event flag */ tt_chan, IO$_READVBLK| IO$M_EXTEND, /* itemlist read virt blk */ &in_iosb, /* Use own I/O status block */ read_ast, /* AST Routine */ 0, /* has no parameter */ &ack_msg_buf.in_buf[0], /* P1 = Input buff addr */ in_buflen, /* P2 = Input buff length */ 3, /* P3 = 3 for user mode AST deliv */ 0, /* P4 not relevant for this function */ &item_lst[0], /* P5 = Itemlist read address */ sizeof(item_lst)); /* P6 = Itemlist read size */ check_s(status) /* Ensure itemlist read worked OK at QIO level */ /* ** Finally, establish random number generator seed using low word ** of system time. */ status = sys$gettim (&currtime[0]) ; /* Get current time */ check_s(status) /* ensure no error occurred */ srand (currtime[0]); /* Establish the seed /* ** Main loop -- puts out a random message every three seconds. */ while (status == SS$_NORMAL) { /* ** Use standard pseudo-random number generator as basis for ** message choice. */ msgnum = (rand() >> 16) % num_msgs; /* Put the message out to the terminal */ status = sys$qiow ( sync_efn, /* use event flag for synchronous I/O */ tt_chan, IO$_WRITEVBLK| /* Function = write virt block, modifs = */ IO$M_BREAKTHRU| /* Allow break-through read */ IO$M_REFRESH, /* Re-display partial input after write*/ &sync_iosb, /* Use the synchronous IOSB */ 0, /* No AST routine */ 0, /* or parameter */ msg_array[msgnum].msg_addr, /* P1 = addr of message */ msg_array[msgnum].msg_len, /* P2 = length of message */ 0, /* P3 ignored */ 0, /* Carriage ctl info (P4) encoded */ /* in msg itself */ 0, 0); /* No P5, P6 */ /* ** Set up timer with 3 second wait. */ status = sys$setimr ( timer_efn, /* this flag will be set */ &waitime[0], 0, 0, 0); /* in 3 seconds */ check_s(status) /* ensure timer set OK */ status = sys$waitfr (timer_efn); /* Wait for timer to expire */ check_s(status) /* ensure wait OK */ } } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** read_ast -- Executed when AST for item list read is delivered. ** The routine issues an acknowledgment message consisting of a ** standard acknowledgment header followed by the read-in message ** itself. It then requeues an item list read which may later ** cause the routine to be called again. ** ** FORMAL PARAMETERS: ** ** None. ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** A read is queued which may cause this routine to be called again. ** ** **-- */ void read_ast (void) { check_s (in_iosb.status) /* Error out if driver error */ /* ** Issue acknowledge message using asynchronous writevblk QIO. In ** this case we ignore the terminal driver write status, and so do ** not provide an IOSB or AST routine. The message length is the ** length of the fixed acknowledge message plus the offset obtained ** from the itemlist read input IOSB which triggered this AST. ** */ status = sys$qio ( async_efn, /* asynchronous event flag */ tt_chan, IO$_WRITEVBLK, 0, 0, 0, /* No IOSB or AST in this case */ &ack_msg_buf, /* P1 = Address of ack msg buffer */ ack_msglen + in_iosb.itlrd.term_off, /* and P2 = length */ 0, 0, 0, 0); /* No P3-P6 */ check_s(status) /* ensure that call went OK, even though we */ /* don't check the write itself */ /* ** Any processing to decode the input read from the terminal would be ** done at this point. */ /* ** Re-queue read itemlist AST. ** ** Since the call is exactly the same as the original read-itmlst ** set-up, both calls can be put in a single separate function if ** desired. */ status = sys$qio ( async_efn, /* Async event flag */ tt_chan, IO$_READVBLK| IO$M_EXTEND, /* itemlist read virt blk */ &in_iosb, /* Use own I/O status block */ read_ast, /* AST Routine */ 0, /* has no parameter */ &ack_msg_buf.in_buf[0], /* P1 = Input buff addr */ in_buflen, /* P2 = Input buff length */ 3, /* P3 = 3 for user mode AST deliv */ 0, /* P4 not relevant for this function */ &item_lst[0], /* P5 = Itemlist read address */ sizeof(item_lst)); /* P6 = Itemlist read size */ check_s(status) /* Ensure itemlist read worked OK at QIO level */ } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** ctrl_c_ast, ctrl_a_ast -- AST routines called when user enters ** Ctrl/A (out-of-band AST) or Ctrl/C (cancel). These routines ** call sys$exit to exit the program with normal (successful) ** status. This will in turn trigger the exit handler which ** restores the terminal's original characteristics. ** ** FORMAL PARAMETERS: ** ** None. ** ** RETURN VALUE: ** ** SS$_NORMAL: normal successful completion ** ** SIDE EFFECTS: ** ** Causes exit handler (exit_handler) to be executed. ** ** ** **-- */ void ctrl_c_ast (void) { sys$exit (SS$_NORMAL); /* Exit with normal status */ } void ctrl_a_ast (char c) { sys$exit (SS$_NORMAL); /* Exit with normal status */ } /* **++ ** FUNCTIONAL DESCRIPTION: ** ** exit_handler: Called at image exit. It cancels any ** outstanding I/O on the terminal channel, and resets terminal ** characteristics to those in force when the image was ** activated. ** ** FORMAL PARAMETERS: ** ** exit_status: ** Exit status as passed to the exit handler by reference. ** ** RETURN VALUE: ** ** None ** ** SIDE EFFECTS: ** ** Cancels outstanding terminal I/O, restores original terminal ** characteristics. ** ** ** **-- */ void exit_handler ( unsigned long int * exit_status) { unsigned long int status; /* status of setmode qio operation */ /* (different from passed-in exit status */ sys$cancel ( tt_chan) ; /* Flush any I/O on the queue */ status = sys$qiow ( sync_efn, /* use synchronous event flag */ tt_chan, IO$_SETMODE, /* Function = set mode */ &sync_iosb, /* Use synchronous IOSB */ 0, /* no AST routine */ 0, /* or parameter */ &oldchar_buf, /* P1 = addr of original characteristics */ sizeof(oldchar_buf), /* P2 = length of original characteristics buffer */ 0, 0, 0, 0); /* No P3-P6 */ check_s(status) /* Ensure that both call and */ check_s(sync_iosb.status) /* set mode actually went OK */ }