/*#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
