summaryrefslogtreecommitdiff
path: root/dviware/beebe/src/keybrd.c
blob: 346515102eddf7df13e981ea089be775ad4360af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
/***********************************************************************

This package  implements a  simple keyboard  input interface  to  permit
character-at-a-time  input,  plus  optional  suppression  of   character
translation.

This is one of the areas where operating systems (even variants of Unix)
are horribly  non-standard,  and the  problem  is not  really  addressed
satisfactorily by either the emerging draft ANSI C Standard (01-Oct-86),
or the IEEE Trial Use  Standard: Portable Operating System for  Computer
Environments (POSIX), IEEE Std 1003.1, (14-Apr-86).

This package is intentionally simple--only one keyboard input device  is
supported (NOT Unix stdin!), and where possible, it is implemented  with
rather ordinary Unix library calls.  The functionality provided is open,
close, flush,  get, unget,  set-mode, and  inquire-mode.  All  functions
(except kbfile()) use the stdio.h EOF value to signal an error, and none
set the global  errno flag (that  is not the  prerogative of  non-system
software).

User programs  need  only use  a  '#include "key.h"'  statement  to  get
definitions of all visible flags, functions, and types in this  package.
Global data  which is  private to  this package  is declared  static  to
ensure just that.

The mode parameter set in calls  to kbopen() and kbmode(), and  returned
by  them,  is  simply  a  collection  of  bit  flags,  but  for   future
expandability, it has a special type  KEYMODE, instead of being an  int.
The mode flags require some explanation.  They are based on the terminal
I/O model  in  Berkeley  Unix.  Essentially  three  terminal  modes  are
supported: normal,  cbreak, and  raw.

In normal mode,  keyboard input is  handled like fopen()  / getchar()  /
fclose(), so that input editing characters are supported (and handled by
the terminal driver, not this package!).  Input may be available  before
a <CR> is typed  (e.g. 4.3BSD, TOPS-20, MS-DOS),  or may not (System  V,
VMS).

In cbreak mode, input editing characters should still work, but input is
guaranteed to be  available before a  <CR> is typed,  provided that  the
operating  system  supports  that  (CDC  and  IBM  do  not).   Interrupt
characters (CTL-C,  CTL-Z, et  al) are  not readable--they  still  cause
interrupts which are  not handled here.   Other control characters  (HT,
ESC, et al) must be readable.

In raw mode, all characters in the host character set may be input,  and
no character-initiated interrupts are possible.

These three modes  are selected  by OR'ing into  the mode  value one  of
KB_CBREAK, KB_NORMAL, or  KB_RAW; they  are mutually  exclusive, and  an
error is raised  if more  than one is  specified.

The current mode may be  saved (internally) with KB_SAVE, restored  with
KB_RESTORE, or inquired  with KB_INQUIRE;  any other  KB_xxxx flags  set
with these are ignored, and the  current mode is not changed.  Only  one
level of save/restore is  provided; successive restore requests  without
an intervening save are permissible, and simply restore the saved  mode.
The intent is that save/restore normally only be done once at job  start
and end.

Normally, input  characters  are not  echoed  to the  terminal;  if  the
side-effect of echoing is wanted, it must be requested by KB_ECHO.

The effect  of  these  modes  on  input  (and  output)  via  other  file
descriptors is operating-system dependent.  On Unix, the terminal  modes
are job-wide, and persist after a program terminates.  On MS-DOS and VAX
VMS, they affect terminal I/O, but only for the duration of the job, and
only for  that file  descriptor.   On TOPS-20,  some  of the  modes  can
persist after the job (e.g.  terminal width and pause-on-end-of-page).

All functions return the EOF error indicator if the keyboard file is not
open.  This  can  be done  with  an  initial call  to  kbopen(mode);  on
success, it returns the argument mode value.

When processing is complete, kbclose() should be called.  Further  calls
of the  kbxxxx() functions  will return  error codes  until kbopen()  is
called again.  kbclose() must restore the  terminal to the state it  was
in when  kbopen() was  called;  kbmode() does  not necessarily  do  that
(since the default mode for kbopen() is no echo).

kbget()  and  kbunget(c)   provide  for   single-character  input,   and
single-character push-back.   kbget()  returns  a  non-negative  ordinal
value on success, and EOF on error.  The pushed-back character given  to
kbunget(c) need  not be  one obtained  from the  last call  to  kbget().
Attempts to push back more than one character are ignored, and result in
an EOF  error return.	Depending  on the  mode (raw,  cbreak,  normal),
kbget() may not be able to return an end-of-file indicator; it cannot in
raw mode, but for  portability, EOF returns from  kbget() should not  be
expected.

kbflush() simply flushes typeahead.  It returns EOF only if the keyboard
file is not open; otherwise it returns 0.

In many applications, it  is often important to  have the capability  of
determining whether or nor  input is available.   For example, a  screen
display algorithm probably should suspend  display if input is  awaiting
that is  likely  to change  the  state  of the  screen.   The  kbinput()
function is therefore  an important  one; it returns  0 if  no input  is
available to be  read by kbget(),  returns EOF on  error, and  otherwise
returns a  positive number  which  in some  implementations may  be  the
number of unread input characters, and in others may be arbitrary.  This
function is  one  which is  difficult  to implement  on  some  operating
systems, with Unix System III and V being the worst.  The implementation
below for the latter  is borrowed from  Marc Rochkind's excellent  book,
"Advanced Unix Programming", Prentice-Hall (1985).

There is one  other function  provided, kbfile(); it  returns the  FILE*
pointer for the  keyboard file, or  (FILE*)NULL is none  is open, or  if
this package is not implemented with  standard C I/O routines.  Its  use
is strongly discouraged,  but the author's  long programming  experience
has shown that on rare occasions, it may be useful.  Character push-back
with direct  calls  to  ungetc(c,kbfile()) will  probably  not  work  as
expected, but other stdio functions should.  fopen() and fclose() should
not be called with the kbfile() value; that would corrupt internal  data
structures here.

Most  functions   are  necessarily   internally  system-dependent,   and
preprocessor conditionals  encapsulate the  gory details.   Preprocessor
flags are  as  follows;  at  least  one  implementation  flag,  and  one
compatible operating system flag, must be chosen:

C Implementations:
	ATT		-- AT&T Unix (System III, V)
	BSD		-- Berkeley 4.xBSD
	HPUX		-- HP 9000 series Unix (System V based)
	IBM_PC_MICROSOFT-- IBM PC Microsoft Version 3.x or later C compiler
	KCC_20		-- SRI's KCC Compiler on TOPS-20
	PCC_20		-- Portable C Compiler on TOPS-20

Operating systems:
	OS_PCDOS	-- IBM (and clones) PC DOS and MS DOS
	OS_TOPS20	-- DEC-20 TOPS-20
	OS_UNIX		-- Unix (almost any variant)
	OS_VAXVMS	-- VAX VMS

Testing of this package has been carried on TOPS-20 (KCC-20 and  PCC-20)
on DEC-20/60, , VAX VMS (CC) on VAX 8600, 4.3BSD Unix on VAX 8600,  HPUX
on HP 9000, and PC DOS (IBM  PC XT).  It appears to work  satisfactorily
on all of these, with the single exception that CTL-C handling on PC DOS
is not restored  after a switch  from raw mode;  under all modes,  CTL-C
still echoes  as  ^C<CR><LF>.	Using  DOS  interrupt  0x33  instead  of
signal() to save and  restore the ctl-break status  does not provide  an
acceptable alternative; CTL-C always terminates execution.  Further work
is necessary to resolve this.

[08-Apr-87]
***********************************************************************/

#include <stdio.h>
#include "keydef.h"

#ifndef OS_PCDOS
#define OS_PCDOS 0
#endif

#ifndef IBM_PC_MICROSOFT
#define IBM_PC_MICROSOFT 0
#endif

#ifndef OS_TOPS20
#define OS_TOPS20 0
#endif

#ifndef KCC_20
#define KCC_20 0
#endif

#ifndef PCC_20
#define PCC_20 0
#endif

#ifndef OS_UNIX
#define OS_UNIX 0
#endif

#ifndef ATT
#define ATT 0
#endif

#ifndef BSD
#define BSD 0
#endif

#ifndef HPUX
#define HPUX 0
#endif

#ifndef OS_VAXVMS
#define OS_VAXVMS 0
#endif

#if    OS_TOPS20
#if    (KCC_20 | PCC_20)
#else
#undef PCC_20
#define PCC_20 1			/* PCC-20 is default for Tops-20 */
#endif
#endif

#if    (OS_PCDOS | OS_TOPS20 | OS_UNIX | OS_VAXVMS)
#else
#undef OS_UNIX
#define OS_UNIX 1			/* Unix is default operating system */
#endif

#if    (ATT | BSD | HPUX)
#else
#undef BSD
#define BSD 1				/* BSD is default Unix variant */
#endif

#if    OS_PCDOS
#include <conio.h>
#include <signal.h>

#define TTYOPENFLAGS "rb"
#define TTYNAME "con"

#define interrupt catch_int /* MSC Version 5.0 raises syntax error otherwise */
static int (*signal_status)();
static void interrupt();
#endif /* OS_PCDOS */

#if    OS_TOPS20
#define TTYNAME "TTY:"
static int w_rfmod;			/* terminal mode word */
static int w_rpcap;			/* process capability word */
static int w_rtiw;			/* terminal interrupt word */
static int w_morxo;			/* end-of-page mode */
static int kbrestore();			/* restore terminal settings */
/*
** KCC-20 and PCC-20 have  similar enough JSYS  interfaces that we  just
** use KCC symbolic names,  and then redefine  them for PCC-20.   PCC-20
** has a working ioctl()  that could be  used for Unix-style  processing
** too, although it has a bug in that it resets the terminal width to 0,
** and disables pause at end of page.  It therefore seems easier to  use
** direct Monitor calls both both KCC-20 and PCC-20.
*/

#if    KCC_20
#include <jsys.h>
#include <monsym.h>
#define jfnof(x)	_CTTRM		/* we use direct JSYS'es for now */
#define TTYASCMOD	FLD(_TTASC,TT_DAM)
#define TTYBINMOD	FLD(_TTBIN,TT_DAM)
#define TTYDAM		FLD(-1,TT_DAM)
#define TTYECHO		FLD(-1,TT_ECO)
#define TTYOPENFLAGS	"rb8"
#define TTYPGM		FLD(-1,TT_PGM)
#endif /* KCC_20 */

#if    PCC_20
#include <file.h>
#include <monsym.h>
#undef jfnof
#define jfnof(x) _CTTRM			/* we use direct JSYS'es for now */
/* Convert names in better KCC-20 style to PCC-20 style */
#define BIN		JSbin
#define DOBE		JSdobe
#define MTOPR		JSmtopr
#define RFMOD		JSrfmod
#define RPCAP		JSrpcap
#define RTIW		JSrtiw
#define SC_CTC		makefield(SC_ctc,-1)	/* WARNING: PCC bug--see below */
#define SFMOD		JSsfmod
#define SIBE		JSsibe
#define STIW		JSstiw
#define STPAR		JSstpar
#define TTYASCMOD	makefield(TT_dam,TTasc)
#define TTYBINMOD	makefield(TT_dam,TTbin)
#define TTYDAM		makefield(TT_dam,-1)
#define TTYPGM		makefield(TT_pgm,-1)
#define TTYECHO		makefield(TT_eco,-1)
#define TTYOPENFLAGS	"rb"
#define _CTTRM		CTtrm
#define _FHJOB		0777773
#define _FHSLF		FHslf
#define _MOXOF		MOxof
#define _MOOFF		MOoff
#define _MORXO		MOrxo
#endif /* PCC_20 */

#endif /* OS_TOPS20 */

#if    OS_UNIX

#if    (ATT | HPUX)
typedef int BOOLEAN;
#define FALSE 0
#define TRUE 1

#include <fcntl.h>
#include <termio.h>

static struct termio tty;
static struct termio ttysave;
static int setblock();
#endif

#if    BSD
#include <sgtty.h>
#include <sys/ioctl.h>

static struct sgttyb tty;
static struct sgttyb ttysave;
#endif

#define TTYOPENFLAGS "r"
#define TTYNAME "/dev/tty"
#endif /* OS_UNIX */

#if    OS_VAXVMS
#include <ssdef.h>
#include <descrip.h>
#include <iodef.h>
#include <ttdef.h>
#include <tt2def.h>

#define TTYOPENFLAGS "rb"
#define TTYNAME ctermid((char*)NULL)
static int status;			/* system service status */
static int tt_channel = -1;		/* terminal channel for image QIO's */
static int iomask;			/* QIO flag mask */
static $DESCRIPTOR(sys_in,"TT:");	/* terminal descriptor */

static struct
{
    unsigned char class;
    unsigned char type;
    unsigned short buffer_size;
    unsigned long tt;
    unsigned long tt2;
} mode_buf,mode_save;

static struct
{
    unsigned short typeahead_count;
    unsigned char first_char;
    unsigned char reserved[5];
} typeahead_buf;

#define FAILED(status) (~(status) & 1)	/* failure if LSB is 0 */

#endif /* OS_VAXVMS */

/*====================================================================*/

#define BADMODE ((KEYMODE)(EOF))	/* unset mode value */
#define EMPTY 32767			/* flag for empty pushback buffer */

static KEYMODE kbcurrmode = BADMODE;	/* current mode */
static KEYMODE kbsavedmode = BADMODE;	/* saved mode */
static int kbpushback = EMPTY;		/* character pushback buffer */
static FILE* kbfp = (FILE*)NULL;	/* keyboard file pointer */
static int kbinit = 0;			/* initialization flag */

#if    OS_PCDOS
/*====================================================================*/

static void
interrupt()
{
    if (kbpushback == EMPTY)
	kbpushback = 3;			/* CTL-C */
}
#endif /* OS_PCDOS */

/*====================================================================*/

int
kbclose()				/* close the keyboard file */
{					/* return EOF on error, 0 on success */
    if (!kbinit)
	return (EOF);
    kbinit = 0;

#if    OS_PCDOS
    (void)signal(SIGINT,signal_status);
    return (0);
#endif /* OS_PCDOS */

#if    OS_TOPS20
    return (kbrestore());
#endif /* OS_TOPS20 */


#if    OS_UNIX
#if    (ATT | HPUX)
    if (ioctl(fileno(kbfp),TCSETAF,&ttysave) == -1)
	return (EOF);
#endif /* (ATT | HPUX) */
#if    BSD
    if (ioctl(fileno(kbfp),TIOCSETP,&ttysave) == -1)
	return (EOF);
#endif /* BSD */

    return (kbfp == (FILE *)NULL ? EOF : fclose(kbfp));
#endif /* OS_UNIX */

#if    OS_VAXVMS
    status = sys$qiow(0,tt_channel,IO$_SETMODE,0,0,0, &mode_save,12,0,0,0,0);
    if (FAILED(status))
	return (EOF);
    return (0);
#endif /* OS_VAXVMS */
}

/*====================================================================*/

FILE*
kbfile()				/* return keyboard file pointer */
{
    return (kbinit ? kbfp : (FILE*)NULL);
}

/*====================================================================*/

int
kbflush()				/* flush current input buffer */
{
    if (!kbinit)
	return (EOF);
    while (kbinput() > 0)
	(void)kbget();
    return (0);
}


/*====================================================================*/

int
kbget()					/* blocking read of character */
{			/* return character, or EOF on end-of-file or error */
    int c;

    if (!kbinit)
	return (EOF);
    else if (kbpushback == EMPTY)
    {

#if    OS_PCDOS
	c = (kbcurrmode & KB_ECHO) ? getche() : getch();
#endif /* OS_PCDOS */

#if    OS_TOPS20
	ac1 = _CTTRM;
	c = ((jsys(BIN,acs) == JSok) ? ac2 : EOF);
#endif /* OS_TOPS20 */


#if    OS_UNIX
#if    (ATT | HPUX)
	if (setblock(fileno(kbfp),TRUE) == EOF)
	    return (EOF);
	if (read(fileno(kbfp),&c,1) <= 0)
	    c = EOF;
#endif /* (ATT | HPUX) */
#if    BSD
	c = getc(kbfp);
#endif /* BSD */
#endif /* OS_UNIX */

#if    OS_VAXVMS
	status = sys$qiow(0,tt_channel,iomask,0,0,0,&c,1,0,0,0,0);
	if (FAILED(status))
	    c = EOF;
#endif /* OS_VAXVMS */

    }
    else
    {
	c = kbpushback;
	kbpushback = EMPTY;
    }
    return (c == EOF ? c : (int)(0377 & c));	/* make sure it is 8-bit value */
}

/*====================================================================*/

int
kbinput()				/* input available? */
{ /* return count of available input characters, 0 if none, or EOF on error */

    if (!kbinit)
	return (EOF);
    else if (kbpushback != EMPTY)
	return (1);
    else
    {
#if    OS_PCDOS
	return (kbhit() ? 1 : 0);	/* otherwise returns 255 */
#endif /* OS_PCDOS */

#if    OS_TOPS20
	ac1 = jfnof(fileno(kbfp));
	if (jsys(SIBE,acs) == JSok)
	    return (0);			/* input buffer empty */
	else
	    return (ac2);		/* input character count */
#endif /* OS_TOPS20 */

#if    OS_UNIX
#if    (ATT | HPUX)
	{
	    char c;

	    if (setblock(fileno(kbfp),FALSE) == EOF)
		return (EOF);
	    switch (read(fileno(kbfp),&c,1))
	    {
	    case -1:
		return (EOF);

	    case 0:
		return (0);

	    default:
		kbpushback = (int)c;
		return (1);
	    }
	}
#endif /* (ATT | HPUX) */
#if    BSD
	{
	    int count;

	    return ((ioctl(fileno(kbfp), FIONREAD, &count) == -1) ? EOF : count);
	}
#endif /* BSD */
#endif /* OS_UNIX */

#if    OS_VAXVMS
	status = sys$qiow(0,tt_channel,IO$_SENSEMODE | IO$M_TYPEAHDCNT,
	    0,0,0,&typeahead_buf,8,0,0,0,0);
	return (FAILED(status) ? EOF : typeahead_buf.typeahead_count);
#endif /* OS_VAXVMS */
    }
}

/*====================================================================*/

KEYMODE
kbmode(mode)				/* keyboard mode */
KEYMODE mode;			/* return new mode on success, EOF on error */
{
    if (!kbinit)
	return (EOF);

    switch (mode & (KB_INQUIRE | KB_RESTORE | KB_SAVE))
    {
    case 0:
	break;				/* must be a set-mode operation  */

    case KB_INQUIRE:
	return (kbcurrmode);

    case KB_RESTORE:
	if (kbsavedmode = BADMODE)
	    return (BADMODE);
	else
	{
	    kbcurrmode = kbsavedmode;
	    kbcurrmode &= ~(KB_INQUIRE | KB_RESTORE | KB_SAVE);
	    return (kbmode(kbcurrmode));
	}

    case KB_SAVE:
	kbsavedmode = kbcurrmode;
	return (kbcurrmode);

    default:				/* do not allow mixed mode flags */
	return (BADMODE);
    }

#if    OS_TOPS20
    if (kbrestore() == EOF)		/* restore terminal settings */
	return (BADMODE);
#endif /* OS_TOPS20 */

    switch (mode & (KB_CBREAK | KB_NORMAL | KB_RAW))	/* set-mode request */
    {
#if    OS_PCDOS
    case KB_CBREAK:
    case KB_NORMAL:
	(void)signal(SIGINT,signal_status);
	break;

    case KB_RAW:
	(void)signal(SIGINT,interrupt);
	break;
#endif /* OS_PCDOS */

#if    OS_TOPS20
    case KB_CBREAK:
    case KB_NORMAL:
	ac1 = jfnof(fileno(kbfp));
	ac2 = w_rfmod & ~TTYECHO;	/* clear TT%ECO field (want no echo) */
	if (jsys(SFMOD,acs) != JSok)
	    return (BADMODE);
	if (jsys(STPAR,acs) != JSok)
	    return (BADMODE);
	break;

    case KB_RAW:
	/* Wait for output to flush; if the screen is at end-of-page, we
	are waiting for the user to type CTL-Q, which will have no
	effect once we enter raw mode, and the terminal will be hung! */
	ac1 = _CTTRM;
	if (jsys(DOBE,acs) != JSok)	/* dismiss until output buffer empty */
	    return (BADMODE);

	ac1 = jfnof(fileno(kbfp));
	ac2 = w_rfmod;
	ac2 &= ~(TTYECHO | TTYDAM | TTYPGM);	/* clear fields */
	ac2 |= TTYBINMOD;		/* set binary mode */
	if (jsys(SFMOD,acs) != JSok)
	    return (BADMODE);
	if (jsys(STPAR,acs) != JSok)
	    return (BADMODE);

	ac1 = _FHJOB;

#if    KCC_20
	if (w_rpcap & SC_CTC)		/* then have CTL-C capability */
#endif /* KCC_20 */
#if    PCC_20
	ac3 = 0600000000000;		/* PCC BUG: 0400000000000 becomes 0! */
	ac3 &=0200000000000;		/* so make it on-the-fly */
	if (w_rpcap & ac3)
#endif /* PCC_20 */
	    ac2 &= 077;			/* read all ctl-chars */
	else
	    ac2 &= 0040000000077;	/* read all ctl-chars but CTL-C */
	if (jsys(STIW,acs) != JSok)	/* reset terminal interrupt word */
	    return (BADMODE);

	ac1 = _CTTRM;
	ac2 = _MOXOF;
	ac3 = _MOOFF;			/* disable pause at end-of-page */
	if (jsys(MTOPR,acs) != JSok)	/* otherwise we can hang waiting for */
	    return (BADMODE);		/* a CTL-Q which is eaten as input! */

	break;
#endif /* OS_TOPS20 */

#if    OS_UNIX
#if    (ATT | HPUX)
    case KB_CBREAK:
	tty = ttysave;
	tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | ISTRIP | IXON | BRKINT);
	tty.c_lflag &= ~(ECHO | ICANON);
	tty.c_cc[4] = 5;		/* MIN */
	tty.c_cc[5] = 2;		/* TIME */
	if (ioctl(fileno(kbfp),TCSETAF,&tty) == -1)
	    return (BADMODE);
	break;

    case KB_NORMAL:
	tty = ttysave;
	tty.c_lflag &= ~ECHO;
	tty.c_cc[4] = 5;		/* MIN */
	tty.c_cc[5] = 2;		/* TIME */
	if (ioctl(fileno(kbfp),TCSETAF,&tty) == -1)
	    return (BADMODE);
	break;

    case KB_RAW:
	tty = ttysave;
	tty.c_iflag &= ~(INLCR | ICRNL | IUCLC | ISTRIP | IXON | BRKINT);
	tty.c_lflag &= ~(ICANON | ISIG | ECHO);
	tty.c_cc[4] = 5;		/* MIN */
	tty.c_cc[5] = 2;		/* TIME */
	if (ioctl(fileno(kbfp),TCSETAF,&tty) == -1)
	    return (BADMODE);
	break;
#endif /* (ATT | HPUX) */

#if    BSD
    case KB_CBREAK:
	tty = ttysave;
	tty.sg_flags &= ~(CRMOD | ECHO | LCASE);
	tty.sg_flags |= CBREAK;
	if (ioctl(fileno(kbfp),TIOCSETP,&tty) == -1)
	    return (BADMODE);
	break;

    case KB_NORMAL:
	tty = ttysave;
	tty.sg_flags &= ~(CBREAK | CRMOD | ECHO | LCASE | RAW);
	if (ioctl(fileno(kbfp),TIOCSETP,&tty) == -1)
	    return (BADMODE);
	break;

    case KB_RAW:
	tty = ttysave;
	tty.sg_flags &= ~(CRMOD | ECHO | LCASE | TANDEM);
	tty.sg_flags |= (CBREAK | RAW);
	if (ioctl(fileno(kbfp),TIOCSETP,&tty) == -1)
	    return (BADMODE);

	break;
#endif /* BSD */
#endif /* OS_UNIX */

#if    OS_VAXVMS
    case KB_CBREAK:
    case KB_NORMAL:
	mode_buf = mode_save;
	mode_buf.tt &= ~TT$M_WRAP;
	status = sys$qiow(0,tt_channel,IO$_SETMODE,0,0,0,&mode_buf,12,0,0,0,0);
	if (FAILED(status))
	    return (EOF);
	iomask = IO$_TTYREADALL | IO$M_NOECHO;
	break;

    case KB_RAW:
	mode_buf = mode_save;
	mode_buf.tt &= ~(TT$M_TTSYNC | TT$M_WRAP);
	mode_buf.tt2 |= TT2$M_PASTHRU;
	mode_buf.tt2 &= ~TT2$M_XON;
	status = sys$qiow(0,tt_channel,IO$_SETMODE,0,0,0,&mode_buf,12,0,0,0,0);
	if (FAILED(status))
	    return (EOF);
	iomask = IO$_TTYREADALL | IO$M_NOECHO;
	break;
#endif /* OS_VAXVMS */

    default:				/* do not allow mixed mode flags */
	return (BADMODE);
    }
    kbcurrmode = mode;
    kbcurrmode &= ~(KB_INQUIRE | KB_RESTORE | KB_SAVE);

    if (mode & KB_ECHO)
    {
#if    OS_PCDOS
	/* NO-OP */ ;
#endif /* OS_PCDOS */

#if    OS_TOPS20
	ac1 = jfnof(fileno(kbfp));
	ac2 = w_rfmod | TTYECHO;	/* set echo mode */
	if (jsys(SFMOD,acs) != JSok)
	    return (BADMODE);
#endif /* OS_TOPS20 */


#if    OS_UNIX
#if    (ATT | HPUX)
	/* ??? do not yet know how to handle this ??? */
#endif /* (ATT | HPUX) */
#if    BSD
	if (ioctl(fileno(kbfp),TIOCGETP,&tty) == -1)
	    return (BADMODE);
	tty.sg_flags |= ECHO;
	if (ioctl(fileno(kbfp),TIOCSETP,&tty) == -1)
	    return (BADMODE);
#endif /* BSD */
#endif /* OS_UNIX */

#if    OS_VAXVMS
	iomask &= ~IO$M_NOECHO;		/* turn off no-echo flag */
#endif /* OS_VAXVMS */
    }
    return (kbcurrmode);
}

/*====================================================================*/

int
kbopen(mode)			/* open keyboard with mode (KB_xxxx flags) */
int mode;			/* return 0 on success, EOF on error */
{
    if (kbinit)				/* ignore if already open */
	return (EOF);

#if    OS_PCDOS
    kbfp = (FILE*)NULL;			/* we use direct console input */
    signal_status = signal(SIGINT,SIG_IGN);
    (void)signal(SIGINT,signal_status);
#endif /* OS_PCDOS */

#if    OS_TOPS20
    kbfp = (FILE*)NULL;			/* we use direct JSYS'es */

    /* save terminal characteristics for later modification and restoration */
    ac1 = _CTTRM;
    if (jsys(RFMOD,acs) != JSok)	/* request terminal mode word */
	return (EOF);
    w_rfmod = ac2;

    ac1 = _FHSLF;
    if (jsys(RPCAP,acs) != JSok)	/* request process capabilities */
	return (EOF);
    w_rpcap = ac2;

    ac1 = _FHJOB;
    if (jsys(RTIW,acs) != JSok)		/* request terminal interrupt word */
	return (EOF);
    w_rtiw = ac2;

    ac1 = _CTTRM;
    ac2 = _MORXO;
    if (jsys(MTOPR,acs) != JSok)	/* request end-of-page mode */
	return (EOF);
    w_morxo = ac3;
#endif /* OS_TOPS20 */

#if    OS_UNIX
    if ((kbfp = fopen(TTYNAME,TTYOPENFLAGS)) == (FILE*)NULL)
	return (EOF);

#if    (ATT | HPUX)
    if (ioctl(fileno(kbfp),TCGETA,&ttysave) == -1)
	return (EOF);
#endif

#if    BSD
    if (ioctl(fileno(kbfp),TIOCGETP,&ttysave) == -1)
	return (EOF);
#endif /* BSD */
#endif /* OS_UNIX */

#if    OS_VAXVMS
    kbfp = (FILE*)NULL;			/* we use direct system calls */

    status = sys$assign(&sys_in,&tt_channel,0,0);
    if (FAILED(status))
	return (EOF);
    status = sys$qiow(0,tt_channel,IO$_SENSEMODE,0,0,0, &mode_save,12,0,0,0,0);
    if (FAILED(status))
	return (EOF);
#endif /* OS_VAXVMS */

    kbinit = 1;
    if (kbmode(mode) == (KEYMODE)EOF)
    {
	if (kbfp != (FILE*)NULL)
	    (void)fclose(kbfp);
	kbinit = 0;
	kbfp = (FILE*)NULL;
	return (EOF);
    }

    return (0);
}

#if    OS_TOPS20
/*====================================================================*/
static int
kbrestore()				/* restore terminal settings */
{					/* return 0 on success, EOF on error */
    ac1 = jfnof(fileno(kbfp));
    ac2 = w_rfmod;
    if (jsys(SFMOD,acs) != JSok)	/* set program-related modes */
	return (EOF);
    if (jsys(STPAR,acs) != JSok)	/* set device-related modes */
	return (EOF);

    ac1 = _FHJOB;
    ac2 = w_rtiw;
    if (jsys(STIW,acs) != JSok)		/* set terminal interrupt word */
	return (EOF);

    ac1 = _CTTRM;
    ac2 = _MOXOF;
    ac3 = w_morxo;
    if (jsys(MTOPR,acs) != JSok)	/* restore end-of-page mode */
	return (EOF);

    return (0);
}

#endif /* OS_TOPS20 */

/*====================================================================*/

int
kbunget(c)	/* unget keyboard character (1-level pushback) */
char c;		/* return c, or EOF on error (already pushed back character) */
{
    if (!kbinit)
	return (EOF);
    else if (kbpushback != EMPTY)
	return (EOF);
    else
    {
	kbpushback = (int)c;
	return (kbpushback);
    }
}


#if    OS_UNIX
#if    (ATT | HPUX)
static int
setblock(fd,on)		/* turn blocking on or off */
			/* return EOF on error, 0 on success */
int fd;			/* file descriptor */
BOOLEAN on;		/* FALSE == turn off, TRUE == turn on */
{
    static int blockf;
    static int nonblockf;
    static BOOLEAN first = TRUE;
    int flags;

    if (first)
    {
	first = FALSE;
	if ((flags = fcntl(fd,F_GETFL,0)) == -1)
	    return (EOF);
	blockf = flags & ~O_NDELAY;	/* turn off O_NDELAY */
	nonblockf = flags | O_NDELAY;	/* turn on O_NDELAY */
    }
    if (fcntl(fd,F_SETFL,on ? blockf : nonblockf) == -1)
	return (EOF);
    return (0);
}
#endif /* (ATT | HPUX) */
#endif /* OS_UNIX */