MHICKEY@UDCVM.BITNET (Mike Hickey) (07/02/86)
We recently got VAX-C ver 2.2 and I've been busy porting programs from UNIX/DOS. When I was working with the CURSES package, I found that getch() wasn't breaking on a character. Instead, it would block until a RETURN was hit. I then tried the getchar() function in the standard library and, lo and behold, it reacted the same way. On every other system I've worked with, these functions "woke up" on any character input whereas these act more like incremental gets(). Have I missed something somewhere? I borrowed the ttxxxx() functions from Micro Emacs which perform character-breaking I/O but they won't work with CURSES. Thanks in advance, Mike Hickey Systems programmer University of DC "Structured programmers DO it OD"
gwyn@BRL.ARPA.UUCP (07/03/86)
Any implementation of getchar() that does not require an entire line to be typed before returning data from the terminal in the default case is broken. This behavior is permitted only in raw (non-canonicalizing) mode, which is not supposed to be enabled by default. Sounds like VAX-C ver 2.2 is doing this right.
gu044150%pitt@cisunx.UUCP (elliot m) (07/07/86)
In article <8607031156.AA07330@ucbvax.Berkeley.EDU> MHICKEY@UDCVM.BITNET writes: > > We recently got VAX-C ver 2.2 and I've been busy porting programs >from UNIX/DOS. When I was working with the CURSES package, I found that >getch() wasn't breaking on a character. Instead, it would block until >a RETURN was hit. I then tried the getchar() function in the standard >library and, lo and behold, it reacted the same way. > I haven't worked with curses that much but it seems to me that even when you do curses on UNIX you have to throw your terminal into cbreak mode to get a character without hitting a <RET>. I believe the curses routine which does this is CRMODE. Unfortunately VMS ignores the CRMODE routine because VMS doesn't have a cbreak mode. The only way I have been able to get around this is to use the SMG routines. Mike cadre!pitt!cisunx!gu044150
sam@brspyr1.UUCP.UUCP (07/16/86)
> Mike Hickey writes ... > > ... When I was working with the CURSES package, I found that > getch() wasn't breaking on a character. Instead, it would block until > a RETURN was hit... > Doug Gwyn writes ... > > ... This behavior is permitted only in raw > (non-canonicalizing) mode, which is not supposed to be enabled by > default. Sounds like VAX-C ver 2.2 is doing this right. Take a look in the VAX-11 C manual. Hmmm ... CRMODE(): provided for link compatibility only, no function performed. Hmmm ... RAW(): same story. So what is a Unix hack supposed to do? Grab those dozen orange binders and ... We discovered that the following reasonably simple routines emulate grabbing a single character off the VMS terminal, similar to UNIX raw mode. Efficiency? No promises here, but they work. For good measure, I threw in a VMS "getpass()" (now why doesn't the C RTL have one of these?) /* ** The following code segment is likely copyrighted by my employer, but ** since I originally wrote it and hacked it before posting it and you ** WILL hack it more before even THINKING about using it, well ... ** ** Okay, okay, whatever you do don't make any money on it, huh? But ** then again, what are we doing reading this net and asking these ** question if we didn't (ultimately) want to make money for someone or ** something. Hmmmmm. What a dilemma. ** ** Okay, you and me, we'll just pretend this should have been published ** in one of those twenty or so orange books DEC calls documentation, ** right? And anyway, it is all in there if you look long enough. So it ** must be _almost_ PD. ** ** No flames, dammit. I was only trying to help. */ #include <stdio.h> #include <descrip.h> #include <ssdef.h> #include <iodef.h> #define VMS$IOKFLGS (IO$_READVBLK | IO$M_NOECHO | IO$M_NOFILTR) #define VMS$IOPFLGS (IO$_READVBLK | IO$M_NOECHO ) #define VMS$INTR '\003' /* ^C */ #define VMS$TTYDEV "TT" #define MAX$PASSWD 8 static int kb_chan; /* hold keyboard channel here */ /* ** Assigns 'kb_chan' to the terminal I/O channel for later use. The ** system logical "TT" is used as the terminal device. */ void initialize() /* call me once at program start */ { static $DESCRIPTOR(name,VMS$TTYDEV); if (sys$assign(&name,&kb_chan,0,0) != SS$_NORMAL) perror("sys$assign(TT) failed"); return; } /* ** This subroutine performs the opposite of initialize(): it deassigns ** the channel. This should be done before the application is exited. */ void de_initialize() /* call me once at program end */ { if (sys$dassgn(kb_chan) != SS$_NORMAL) perror("sys$dassgn(TT) failed"); return; } /* ** This subroutine recovers a single byte from the user's keyboard. ** This subroutine may not be used unless initialize() has been called, ** but no enforcement of this is made. ** ** Returns one of: ** (char) - byte retrieved ** NULL - interrupt encountered */ char get_key_stroke() { int i; /* for catching return value */ char c; /* place for char */ /* sys$qiow() ignores EOF's and does not modify 'c' if interrupt. */ /* better handling here is probably more than possible; VMS$IOKFLGS */ /* (defined at top) has impact on special char handling. */ do { c = VMS$INTR; i = sys$qiow(0,kb_chan,VMS$IOKFLGS,0,0,0,&c,1,0,0,0,0); } while (i != SS$_NORMAL); return(c == VMS$INTR ? NULL : c); } /* ** As in getpass(3), basically. Very minimum; probably should check for ** EOF or INTR. Dependent on a prior call to initialize(). */ char *getpass(p) char *p; { static char passwd[MAX$PASSWD+1]; fputs(p,stdout); fflush(stdout); *passwd = NULL; sys$qiow(0,kb_chan,VMS$IOPFLGS,0,0,0,passwd,MAX$PASSWD,0,0,0,0); for (p = passwd; p - passwd < MAX$PASSWD; p++) if (*p == '\n' || *p == '\r' || *p == NULL) break; *p = NULL; return(passwd); } From postnews Wed Jul 16 01:02:28 1986 Subject: Re: getch for VAX C (was: VAX C problem) Newsgroups: mod.computers.vax To: info-vax@sri-kl.arpa References: <8607031156.AA07330@ucbvax.Berkeley.EDU> > Mike Hickey writes ... > > ... When I was working with the CURSES package, I found that > getch() wasn't breaking on a character. Instead, it would block until > a RETURN was hit... > Doug Gwyn writes ... > > ... This behavior is permitted only in raw > (non-canonicalizing) mode, which is not supposed to be enabled by > default. Sounds like VAX-C ver 2.2 is doing this right. Take a look in the VAX-11 C manual. Hmmm ... CRMODE(): provided for link compatibility only, no function performed. Hmmm ... RAW(): same story. So what is a Unix hack supposed to do? Grab those dozen orange binders and ... We discovered that the following reasonably simple routines emulate grabbing a single character off the VMS terminal, similar to UNIX raw mode. Efficiency? No promises here, but they work. For good measure, I threw in a VMS "getpass()" (now why doesn't the C RTL have one of these?) /* ** The following code segment is likely copyrighted by my employer, but ** since I originally wrote it and hacked it before posting it and you ** WILL hack it more before even THINKING about using it, well ... ** ** Okay, okay, whatever you do don't make any money on it, huh? But ** then again, what are we doing reading this net and asking these ** question if we didn't (ultimately) want to make money for someone or ** something. Hmmmmm. What a dilemma. ** ** Okay, you and me, we'll just pretend this should have been published ** in one of those twenty or so orange books DEC calls documentation, ** right? And anyway, it is all in there if you look long enough. So it ** must be _almost_ PD. ** ** No flames, dammit. I was only trying to help. */ #include <stdio.h> #include <descrip.h> #include <ssdef.h> #include <iodef.h> #define VMS$IOKFLGS (IO$_READVBLK | IO$M_NOECHO | IO$M_NOFILTR) #define VMS$IOPFLGS (IO$_READVBLK | IO$M_NOECHO ) #define VMS$INTR '\003' /* ^C */ #define VMS$TTYDEV "TT" #define MAX$PASSWD 8 static int kb_chan; /* hold keyboard channel here */ /* ** Assigns 'kb_chan' to the terminal I/O channel for later use. The ** system logical "TT" is used as the terminal device. */ void initialize() /* call me once at program start */ { static $DESCRIPTOR(name,VMS$TTYDEV); if (sys$assign(&name,&kb_chan,0,0) != SS$_NORMAL) perror("sys$assign(TT) failed"); return; } /* ** This subroutine performs the opposite of initialize(): it deassigns ** the channel. This should be done before the application is exited. */ void de_initialize() /* call me once at program end */ { if (sys$dassgn(kb_chan) != SS$_NORMAL) perror("sys$dassgn(TT) failed"); return; } /* ** This subroutine recovers a single byte from the user's keyboard. ** This subroutine may not be used unless initialize() has been called, ** but no enforcement of this is made. ** ** Returns one of: ** (char) - byte retrieved ** NULL - interrupt encountered */ char get_key_stroke() { int i; /* for catching return value */ char c; /* place for char */ /* sys$qiow() ignores EOF's and does not modify 'c' if interrupt. */ /* better handling here is probably more than possible; VMS$IOKFLGS */ /* (defined at top) has impact on special char handling. */ do { c = VMS$INTR; i = sys$qiow(0,kb_chan,VMS$IOKFLGS,0,0,0,&c,1,0,0,0,0); } while (i != SS$_NORMAL); return(c == VMS$INTR ? NULL : c); } /* ** As in getpass(3), basically. Very minimum; probably should check for ** EOF or INTR. Dependent on a prior call to initialize(). */ char *getpass(p) char *p; { static char passwd[MAX$PASSWD+1]; fputs(p,stdout); fflush(stdout); *passwd = NULL; sys$qiow(0,kb_chan,VMS$IOPFLGS,0,0,0,passwd,MAX$PASSWD,0,0,0,0); for (p = passwd; p - passwd < MAX$PASSWD; p++) if (*p == '\n' || *p == '\r' || *p == NULL) break; *p = NULL; return(passwd); }
LEICHTER-JERRY@YALE.ARPA.UUCP (07/20/86)
A recent message from Samuel Baxter (brspyr1!sam) provided a simple program to do single-character terminal I/O on VMS. Enclosed below is a program that does the same thing, though often much more efficiently, since it buffers up typed-ahead stuff. In addition, it can be do "non-blocking" terminal reads. The program has a long history and several people have hacked at it - I'm just the last. It's been used successfully for quite some time. It differs form Mr. Baxter's code in not attempting to trap CTRL/C. That, and related changes, can be made easily enough. Mr. Baxter also asks why VAX C provides crmode() only as a dummy function, and does not provide getpass(). A guess at the answers: crmode() has some very specific effects on Unix that would be very difficult to duplicate exactly on VMS - different purposes of crmode() would probably want to use slightly different combinations of VMS terminal settings. For example, should crmode() disable xon/xoff (CTRL/Q - CTRL/S) handling? To be exactly like Unix, probab- ly, but for most purposes, in a VMS environment, the probably not. The Unix and VMS terminal drivers are so different that any attempt to provide anything beyond the usual buffered I/O is going to run into problems. As to getpass(), it's pretty rarely used outside of the kind of system pro- grams that are unlikely to be moved to VMS - I'll bet at least 90% of Unix programmers have never heard of it - and it's pretty easy to write anyway. So, it must have come far down on the list of things to get written. -- Jerry ------------------------------Cut Here------------------------------------------ /*#define TESTING */ /* * k b i n . c * * Read tt: one byte at a time without echo, with or without waiting if there * is no typeahead. * * Synopsis * * int * _kbin(wait) * int wait; * * int * kbin() * * int * kbinr_() * * Description * * Returns the next character. Returns EOF on error, or if there is no * typeahead and "no wait" was requested. * * _kbin(wait) waits if wait is TRUE, returns EOF if there is no * typeahead and wait is FALSE. kbin() is the same as _kbin(TRUE); * kbinr() is the same as _kbin(FALSE). * * Note -- this routine does not prevent CTRL/C or CTRL/Y aborts * from occurring. * * Revision History * 0.0 19-Jun-79 SB Original version due to Stoney Ballard. * 0.1 19-Oct-79 MM "Munged". * 0.2 31-Aug-81 MM Name changed to kbin(). * 1.0 ??-???-?? MM Made part of VAXLIB. * 1.1 22-Jul-83 JSL Old kbin() ==> _kbin() and gets the "wait" argument; * kbin(), kbinr() in terms of _kbin(). */ #include <stdio.h> #include <ssdef> #include <iodef> #include <descrip> #define FALSE 0 #define TRUE 1 #define EOS 0 #define BUFFLEN 10 /* Size of typeahead buffer */ /* * Local static database: */ static $DESCRIPTOR(inpdev, "TT"); /* Terminal to use for input */ static long termset[2] = { 0, 0 }; /* No terminator */ /* * Local variables */ static long ichan; /* Gets channel number for TT: */ static char opened = FALSE; /* TRUE when opened */ static char ibuff[BUFFLEN]; /* Input buffer -- one byte */ static char *buffptr = ibuff; /* For typeahead processing */ static char *buffend = ibuff; /* For typeahead processing */ int _kbin(wait) int wait; /* * Get one byte without echoing, with or without waiting. */ { register int errorcode; struct IOSTAB { short int status; short int offset_to_terminator; short int terminator; short int terminator_size; } iostab; if (buffptr < buffend) return (*buffptr++ & 0377); /* Empty our typeahead buffer */ if (!opened) { if ((errorcode = sys$assign(&inpdev, &ichan, 0, 0)) != SS$_NORMAL) { fprintf(stderr, "KBIN assign failed. code = %X\n", errorcode); exit(errorcode); } else opened = TRUE; } /* * See if there's something in the system typeahead buffer * Read up to BUFLEN bytes with "zero" timeout. This will return * whatever's in the timeout buffer. The iostab.offset_to_terminator * and iostab.terminator_size will yield the number of bytes read. */ errorcode = sys$qiow(1, /* Event flag */ ichan, /* Input channel */ IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED, /* Timed read with zero wait */ &iostab, /* I/O status block */ NULL, /* AST block (none) */ 0, /* AST parameter */ &ibuff, /* P1 - input buffer */ BUFFLEN, /* P2 - buffer length */ 0, /* P3 - ignored (timeout) */ &termset, /* P4 - terminator set */ NULL, /* P5 - ignored (prompt buffer) */ 0); /* P6 - ignored (prompt size) */ #ifdef TESTING printf("timed read code = %X, ", errorcode); printf("status = %d, offset = %d, terminator = %d, size = %d\n", iostab.status, iostab.offset_to_terminator, iostab.terminator, iostab.terminator_size); #endif buffend = &ibuff[iostab.offset_to_terminator + iostab.terminator_size]; if (buffend > ibuff) { buffptr = &ibuff[1]; /* Setup typeahead pointer and */ return (ibuff[0] & 0377); /* Return the first character */ } /* * Nothing in typeahead buffer, nothing read. If the user doesn't * want us to wait, just return EOF; else read one character. */ if (!wait) return (EOF); errorcode = sys$qiow(1, /* Event flag */ ichan, /* Input channel */ IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR, /* Read, no echo, no translate */ &iostab, /* I/O status block */ NULL, /* AST block (none) */ 0, /* AST parameter */ &ibuff, /* P1 - input buffer */ 1, /* P2 - buffer length */ 0, /* P3 - ignored (timeout) */ &termset, /* P4 - terminator set */ NULL, /* P5 - ignored (prompt buffer) */ 0); /* P6 - ignored (prompt size) */ #ifdef TESTING printf("read one byte, code = %X, ", errorcode); printf("status = %d, offset = %d, terminator = %d, size = %d\n", iostab.status, iostab.offset_to_terminator, iostab.terminator, iostab.terminator_size); #endif if (errorcode == SS$_NORMAL) { return (ibuff[0] & 0377); } else { return (EOF); } } int kbin() /* * Get one byte without echoing, with waiting. */ { return (_kbin(TRUE)); } int kbinr() /* * Get one byte without echoing, without waiting. */ { return (_kbin(FALSE)); } #ifdef TESTING main() { register int datum; printf("kbin() testing - CTRL/Z to exit\n"); while ((datum = kbin()) != EOF) { printf("%03o '", datum); dumpc(datum); printf("'\n"); if (datum == ('Z' - 0100)) break; } printf("EOF\n"); printf("kbinr() testing - CTRL/Z to exit\n"); while (1) { datum = kbinr(); if (datum == EOF) { printf("EOF - sleeping..."); sleep(2); printf("\n"); continue; } printf("%03o '", datum); dumpc(datum); printf("'\n"); if (datum == ('Z' - 0100)) break; } printf("EOF\n"); } dumpc(datum) int datum; /* * Dump a character readably */ { datum &= 0377; if ((datum & 0200) != 0) { putchar('~'); datum &= 0177; } if (datum < ' ') { putchar('^'); putchar(datum + '@'); } else if (datum > 0176) { printf("<RUB>"); } else putchar(datum); } #endif -------
carl@CITHEX.CALTECH.EDU (Carl J Lydick) (07/21/86)
/*****************************************************************************\ * > ...a program that does the same thing, though often much more * * > efficiently, since it buffers up typed-ahead stuff.... * * * * Not quite the same thing, Jerry; your "more efficient" implementation * * has the following rather odd properties: * * 1) An image exit may flush any, all, or none of the type-ahead. * * 2) The amount of type-ahead flushed when the image exits * * depends, in principle, on every keystroke typed since the * * first call to your _kbin(). In fact, the dependence is upon * * the number of characters typed since the last time the * * terminal's type-ahead queue was empty. * * The reason for this, of course, is that you and the terminal driver are * * both buffering typeahead, but you neither return whatever you've got * * left at image exit to the terminal data stream (which is reasonable, * * since the VMS terminal drivers don't have any provision for this) and * * you don't do anything to force a flush of the terminal's type-ahead * * buffer at image exit (which is not reasonable, since you can declare * * your own exit handler to take care of this problem. What you want is * * somthing like: * \*****************************************************************************/ /* clean up after single-character I/O software */ void flush_stream(status, chan) long *status, chan; { SYS$QIOW(0, chan, IO$_READVBLK | IO$M_TIMED | IO$M_NOFILTR | IO$M_NOECHO | IO$M_PURGE, 0, 0, 0,0, 0, 0, 0, 0, 0); SYS$DASSGN(chan); } /* exit handler descriptor block */ struct EXH$DESBLK { long exh$l_flink; void (*exh$a_handler)(); long exh$b_nargs; long *exh$a_status; long exh$l_chan; long exh$l_stat; } flush = { 0, flush_stream, 2, &(flush.exh$l_stat), 0, 0 }; /*****************************************************************************\ * Then add something like: * * * * flush.exh$l_chan = chan; * * SYS$DCLEXH(&flush); * * * * to the code that opens the channel on the first call. When your image * * exits, flush will be called, and it will flush all typeahead on the * * terminal channel and deassign the channel. * \*****************************************************************************/