GG.SPY@ISUMVS.BITNET ("John Hascall") (03/11/88)
> From: F026@CPC865.UEA.AC.UK > Subject: single char input in VAX C > > (as far as I know) the only way you can get single character input on a VAX > is to use the $QIO (Queued Input/Output) system service, which involves > setting what is effectively an interrupt vector which you then have to > service. To make it simpler, VMS also has the $QIOW (QIO with Wait) which > will wait for your 'interrupt'. Look it up in the System Service Manual. > > Mike Salmon To elucidate: #include <iodef.h> #include <descrip.h> typedef struct IOSB { unsigned short status; unsigned short count; unsigned long other; } IOSB; typedef unsigned short CHANNEL; $DESCRIPTOR("TT",tty_name); unsigned long status; CHANNEL tty_chan; IOSB tty_iosb; char inchar; status = SYS$ASSIGN(&tty_name,&tty_chan); /* only need to do this once */ status = SYS$QIOW(0,tty_chan,IO$_READVBLK,&tty_iosb,0,0,&inchar,1,0,0,0,0); John Hascall Iowa State University Computation Center GGUUU@ISUMVS.BITNET
hildum@iris.ucdavis.edu (Eric Hildum) (03/14/88)
You may also use SMG$ to get single characters... Eric dehildum@ucdavis.ucdavis.edu (Internet) dehildum@ucdavis.bitnet (BITNET) ucbvax!ucdavis!dehildum (uucp)
ERICMC@USU.BITNET (Eric McQueen) (04/02/88)
John Hascall (GG.SPY@ISUMVS.BITNET) in <8803130715.AA24642@ucbvax.Berkeley.EDU> gives a quick example of calling $QIOW to get a single character from the keyboard under VMS. Here's two short routines that do proper checking for errors and can be used in place of getchar(). You could even port your Un*x code by writing a ctio() front end for takecharset(): Eric Tye McQueen Mathematics Department Also at (after some ericmc@usu.bitnet Utah State University time in March[June?!]): (801) 753-4683 Logan, Utah 84322-3900 ericmc@usu.usu.edu UUCP: ...{psuvax1,uunet}!usu.bitnet!ericmc "Doodle doodle dee Arpa: ericmc%usu.bitnet@cunyvm.cuny.edu wubba wubba wubba." /* takechar.c -- by Eric McQueen Version of 1-Apr-1988 * Replacement for getchar() that allows control over the following: * Whether to wait if there are no characters in the buffer yet. * Whether to wait for a line terminator (which allows VMS line editing). * Whether to echo input. * Two routines are provided: * - takechar(): works very much like getchar(). * - takecharset(opts): modifies the behavior of takechar(): `opts' is * a string of characters from "wilces" (letter case is not important) * which stand for Wait/Immediate, Line/Character, Echo/Silent. If you * don't call takecharset(), takechar() will behave as if you made the * call takecharset("wle") (wait for input, read by line, echo input). * takechar() returns -2 if in "immediate" mode and no characters are in the * type-ahead buffer. takechar() returns EOF (-1) for CTRL-Z and '\n' for '\r' * to be compatible with getchar(). takechar() (when in "character" mode) * return '\r' for '\n' to allow programs to distinguish '\r' from '\n'. Since * '\n' is not normally typed by users, this should cause no problems. If in * both "immediate" and "line" modes, partial lines may be read. * * These capabilities could also be provided by having a routine set the * terminal /[NO]ECHO and /[NO]PASTHROUGH, but this way is much simpler * [unless you want to use scanf()] and I hate to take CTRL-C, -O, -T, and -Y * away from the user. If the user wants to give them to you via SET NOCONTROL * or SET TERM/PAST, they can. Also, CTRL-S and CTRL-Q cannot be read by this * routine. */ #include <stdio.h> /* stderr EOF */ #define odd( stat ) ( (stat) & 1 ) /* Custom data type names: */ #define bool char /* smallest addressible signed object */ #define uchar unsigned char #define ushort unsigned short #define uint unsigned int /* General VMS descriptor: */ /* I could include <DESCRIP.H>, but I prefer my short field names */ struct descr { ushort leng; /* Length of data area */ uchar type; /* Type of data in area */ uchar class; /* Class of descriptor (static/dynamic/etc.) */ char *addr; /* Address of start of data area */ }; /* String descriptors: */ globalvalue dsc$k_dtype_t; /* Text */ globalvalue dsc$k_class_s; /* Static */ /* to allocate a descriptor (dsc) for array of char (arr): */ #define desc_arr(dsc,arr) struct descr dsc = \ { (sizeof arr)-1, dsc$k_dtype_t, dsc$k_class_s, arr } /* to allocate a descriptor (dsc) for null-terminated string (str): */ #define desc_str(dsc,str) struct descr dsc = \ { strlen(str), dsc$k_dtype_t, dsc$k_class_s, str } static ushort chan = 0; /* I/O channel assigned to terminal. */ static bool wait = 1; /* Whether to wait if no input present. */ static bool echo = 1; /* Whether to echo characters read. */ static bool line = 1; /* Whether to wait for line terminator. */ static uint func = 0; /* I/O function to use in sys$qiow(). */ /* setfunc(): Sets `func' according to `wait', `echo', and `line'. */ static void setfunc() { globalvalue io$_readlblk, io$m_noecho, io$m_trmnoecho, io$m_nofiltr, io$m_timed; func = io$_readlblk; if( !echo ) func |= io$m_noecho | io$m_trmnoecho; if( !wait ) func |= io$m_timed; if( !line ) func |= io$m_nofiltr; } /* takecharset(opts): Set behavior of takechar(). `opts' is a string of zero * or more of the following characters: * I = Immediate: takechar() returns -1 if type-ahead buffer is empty, * W = Wait: takechar() waits if type-ahead buffer is empty. * * E = Echo: takechar() echoes each character/line as it is read, * S = Silent: takechar() does not echo characters (or lines). * * C = Character: takechar() doesn't wait for a line terminator, * L = Line: takechar() reads by line, allowing line editing. * Case of letters is ignored. */ void takecharset( opts ) char *opts; { while( *opts ) switch( *opts++ ) { case 'w': case 'W': /* Wait */ wait = 1; break; case 'i': case 'I': /* Immediate */ wait = 0; break; case 'e': case 'E': /* Echo */ echo = 1; break; case 's': case 'S': /* Silent */ echo = 0; break; case 'l': case 'L': /* By line */ line = 1; break; case 'c': case 'C': /* By Character */ line = 0; break; default: fprintf( stderr, "Invalid character '%c' (0x%x) in takecharset().\n", *opts, (int) *opts ); } setfunc(); } /* assign(): Assigns a channel to the terminal. The real way to do this is * to use sys$getjpi() to get the ID of the master process and then get the * name of the terminal associated with that process. The method we use here * is to translate the logical name SYS$INPUT to find the name of the terminal. * Obviosly, this won't work if the user redefines SYS$INPUT, but then, most * programs won't read from the keyboard if SYS$INPUT isn't pointing to the * terminal. Also, by not calling sys$getjpi() we shorten the code (excluding * this comment) by a factor of 6. */ static void assign() { char tt_name[] = "SYS$INPUT"; /* or "TT" or "SYS$COMMAND" */ desc_str( ttdsc, tt_name ); uint status, sys$assign(); status = sys$assign( &ttdsc, &chan, 0, 0 ); if( odd( status ) ) /* We succeeded. */ return; fprintf( stderr, "Can't assign a channel to the terminal.\n" ); sys$exit( status ); } /* takechar(): Reads a single key from the keyboard. If `wait' is 0 and * there are no characters in the type-ahead buffer, -1 is returned. Other- * wise the next character is returned. */ int takechar() { uchar c; /* So eight-bit characters can't be -1 or -2 */ ushort iosb[4]; static ushort len = 0; /* Number chars in `buf' to be sent */ static char buf[4096], *cp = buf; uint status, sys$qiow(); globalvalue ss$_timeout; if( !chan ) assign(); /* Assign a channel to the terminal. */ if( !func ) setfunc(); /* Set `func' to be appropriate. */ if( len > 0 ) { iosb[0] = status = 1; } else if( line ) { /* Read an entire line: */ status = sys$qiow( 0, chan, func, iosb, 0, 0, buf, sizeof(buf), 0, 0, 0, 0 ); len = iosb[1] + iosb[3]; /* Total bytes read. */ cp = buf; } else { status = sys$qiow( 0, chan, func, iosb, 0, 0, &c, 1, 0, 0, 0, 0 ); } if( ss$_timeout == status || ss$_timeout == iosb[0] ) return( -2 ); if( !odd(status) || !odd(iosb[0]) ) { /* Error occured: */ len = 0; fprintf( stderr, "Error reading from terminal.\n" ); sys$exit( odd(status) ? iosb[0] : status ); } if( len > 0 ) { len--; c = *cp++; } switch( c ) { case '\r': return( '\n' ); /* Act like getchar()... */ case '\n': return( '\r' ); /* ...but distinguish '\n' */ case 'Z'&31: return( EOF ); /* CTRL-Z is End Of File */ default: return( c ); } } /* End of takechar.c */