1 #include <config.h>
2 
3 #if (SCREEN_LINES > 0)
4 
5 #if defined(HAVE_SYS_IOCTL_H)
6 #include <sys/ioctl.h>
7 #endif
8 
9 #if defined(HAVE_STDLIB_H)
10 #include <stdlib.h>
11 #endif
12 
13 #include "xctype.h"
14 #include "xstring.h"
15 #include "xunistd.h"
16 
17 RCSID("$Id: keybrd.c,v 1.11 2017/06/09 14:46:20 beebe Exp beebe $")
18 
19 #include "ch.h"
20 #include "keybrd.h"
21 #include "yesorno.h"
22 
23 #define LAST_SCREEN_LINE (-2)	/* used in opt_help() and do_more() */
24 
25 #if defined(MAX)
26 #undef MAX
27 #endif
28 
29 #define MAX(a,b)	(((a) > (b)) ? (a) : (b))
30 
31 #ifdef MIN
32 #undef MIN
33 #endif /* MIN */
34 
35 #define	MIN(a,b)	((a) < (b) ? (a) : (b))
36 
37 #undef MAX_CHAR
38 #define MAX_CHAR		256
39 
40 static keyboard_code_t	keymap[MAX_CHAR];
41 
42 extern int	screen_lines;		/* kbopen() and out_lines() reset */
43 
44 extern FILE	*tfopen ARGS((const char *filename_, const char *mode_));
45 
46 static void	beep ARGS((FILE *fpout));
47 
48 int		do_more ARGS((FILE *fpout_, int line_, int pause_after_,
49 		    const char *lines[]));
50 
51 static int	do_search ARGS((FILE *fpout_, int code_, int line_number_,
52 				int pause_after_, const char *lines_[]));
53 
54 static void	erase_characters ARGS((FILE *fpout_, int how_many_));
55 
56 static keyboard_code_t kbcode ARGS((void));
57 
58 static int	kbget ARGS((void));
59 
60 static void	kbinitmap ARGS((void));
61 
62 
63 static void
64 #if defined (HAVE_STDC)
beep(FILE * fpout)65 beep(FILE *fpout)
66 #else
67 beep(fpout)
68 FILE *fpout;
69 #endif
70 {
71     (void)fputc(CH_BELL,fpout);
72     (void)fflush(fpout);
73 }
74 
75 
76 #if defined(HAVE_STDC)
77 int
do_more(FILE * fpout,int line_number,int pause_after,const char * lines[])78 do_more(FILE *fpout,int line_number, int pause_after, const char *lines[])
79 #else /* K&R style */
80 int
81 do_more(fpout, line_number, pause_after, lines)
82 FILE *fpout;
83 int line_number;
84 int pause_after;
85 const char *lines[];
86 #endif
87 {
88     int code;
89 
90 #if OS_PCDOS
91 #define MORE_HELP \
92 "More? f)orwd b)ackwd e)nd q)uit r)efresh t)op \\/)search \030 \031 PgUp \
93 PgDn Home End\n\r"
94 #else /* NOT OS_PCDOS */
95 #define MORE_HELP \
96 "More? f)orward b)ackward d)own e)nd q)uit r)efresh t)op u)p \\/)search\n\r"
97 #endif /* OS_PCDOS */
98 
99     (void)fputs(MORE_HELP,fpout);
100     (void)fflush(fpout);		/* make screen up-to-date */
101     for (;;)			/* loop until a valid input code is received */
102     {
103 	switch (code = kbcode())
104 	{
105 	case KEYBOARD_PGUP:		/* backward screen */
106 	    return (MAX(0,line_number + 1 - 2*pause_after));
107 
108 	case KEYBOARD_DOWN:		/* go down 1 line (scroll up 1 line) */
109 	    return (line_number + 2 - pause_after);
110 
111 	case KEYBOARD_END:		/* end */
112 	    return (LAST_SCREEN_LINE);
113 
114 	case KEYBOARD_PGDN:		/* forward screen */
115 	    return (line_number + 1);
116 
117 	case KEYBOARD_HELP:
118 	    (void)fputs(MORE_HELP,fpout);
119 	    break;
120 
121 	case KEYBOARD_EOF:
122 	case KEYBOARD_QUIT:
123 	    return (EOF);
124 
125 	case KEYBOARD_AGAIN:		/* refresh */
126 	    return (MAX(0,line_number + 1 - pause_after));
127 
128 	case KEYBOARD_HOME:		/* top */
129 	    return (0);
130 
131 	case KEYBOARD_UP:		/* go up 1 line (scroll down 1 line) */
132 	    return (line_number + 0 - pause_after);
133 
134 	case KEYBOARD_DOWN_PARAGRAPH:
135 	    /* Move down so that paragraph following the paragraph of
136 	       the current top-of-screen line is at top-of-screen */
137 	    line_number++;
138 	    if (line_number >= (screen_lines - 2))
139 		line_number -= (screen_lines - 2);
140 	    while ((lines[line_number] != (char*)NULL) &&
141 		   (lines[line_number][0] != '\n'))
142 		line_number++;
143 	    return ((lines[line_number] == (char*)NULL) ? LAST_SCREEN_LINE :
144 		    line_number + 1);
145 
146 	case KEYBOARD_UP_PARAGRAPH:
147 	    /* Move up so that paragraph of current top-of-screen line
148 	       has its first line at top of screen. */
149 	    line_number--;
150 	    if (line_number >= (screen_lines - 2))
151 		line_number -= (screen_lines - 2);
152 	    while ((line_number >= 0) && (lines[line_number] != (char*)NULL) &&
153 		   (lines[line_number][0] != '\n'))
154 		line_number--;
155 	    return (line_number + 1);
156 
157 	case KEYBOARD_SEARCH_BACKWARD:
158 	case KEYBOARD_SEARCH_FORWARD:
159 	    return (do_search(fpout,code,line_number,pause_after,lines));
160 
161 	case KEYBOARD_UNKNOWN:
162 	default:			/* anything else produces */
163 	    beep(fpout);		/* an error beep */
164 	    break;
165 	}				/* end switch (c...) */
166     }					/* end for (;;) */
167 }
168 
169 
170 static int
171 #if defined(HAVE_STDC)
do_search(FILE * fpout,int code,int line_number,int pause_after,const char * lines[])172 do_search(FILE *fpout, int code, int line_number, int pause_after, const char *lines[])
173 #else
174 do_search(fpout, code, line_number, pause_after, lines)
175 FILE *fpout;
176 int code;
177 int line_number;
178 int pause_after;
179 const char *lines[];
180 #endif
181 {
182     int c;
183     int k;
184     int last_line_number = line_number;
185 
186     /*@-modobserver@*/
187     static char search_string[80] = ""; /* preserved across calls */
188 
189     (void)fputs((code == KEYBOARD_SEARCH_BACKWARD) ?
190 		"Search backward: " :
191 		"Search forward: ",fpout);
192     (void)fflush(fpout);
193     for (k = 0; ;)
194     {
195 	c = kbget();
196 	if ((c == CH_NUL) || (c == CH_ESCAPE) ||
197 	    (c == (int)'\r') || (c == (int)'\n'))
198 	    break;		/* here's the loop exit */
199 	else if ((c == CH_BACKSPACE) || (c == CH_DELETE))
200 	{
201 	    erase_characters(fpout,1);
202 	    if (k > 0)
203 		k--;
204 	    search_string[k] = '\0';
205 	}
206 	else if (c == CH_LINE_KILL)	/* erase entire search string */
207 	{
208 	    erase_characters(fpout,k);
209 	    k = 0;
210 	    search_string[k] = '\0';
211 	}
212 	else if (c == CH_REPRINT)	/* reprint entire search string */
213 	{
214 	    int m;
215 
216 	    erase_characters(fpout,k);
217 	    for (m = 0; m < k; ++m)
218 		(void)fputc((int)search_string[m],fpout);
219 	    (void)fflush(fpout);
220 	}
221 	else if (c == CH_WORD_ERASE)	/* erase last non-blank word */
222 	{
223 	    YESorNO saw_word;
224 
225 	    saw_word = NO;
226 	    for (--k ; k >= 0; --k)
227 	    {
228 		if (Isspace(search_string[k]) && (saw_word == YES))
229 		{
230 		    k++;	/* so that we keep this space */
231 		    break;
232 		}
233 		if (!Isspace(search_string[k]))
234 		    saw_word = YES;
235 		erase_characters(fpout,1);
236 	    }
237 	    if (k < 0)
238 		k = 0;
239 	    search_string[k] = '\0';
240 	    (void)fflush(fpout);
241 	}
242 	else if (k < ((int)sizeof(search_string) - 1))
243 	{
244 	    search_string[k++] = (char)c;
245 	    (void)fputc(c,fpout);	/* echo input character */
246 	    (void)fflush(fpout);
247 	}
248 	else			/* search string too long: beep at user */
249 	    beep(fpout);
250     }
251     if (k > 0)			/* got new search string */
252 	search_string[k] = '\0';
253     else			/* re-use last search string */
254     {
255 	(void)fputs(search_string,fpout);
256 	(void)fputc('\r',fpout);
257 	(void)fputc('\n',fpout);
258 	(void)fflush(fpout);
259     }
260     (void)fputc('\r',fpout);
261 
262     for (k = (int)(strlen("Search backward: ") + strlen(search_string));
263 	 (k > 0); --k)
264 	(void)fputc(' ',fpout);	/* erase the search line */
265     (void)fputc('\r',fpout);
266 
267     (void)fflush(fpout);
268 
269 #if defined(DEBUG)
270     (void)sleep(1);		/* DEBUG delay */
271 #endif
272 
273     if (code == KEYBOARD_SEARCH_BACKWARD)
274 	line_number = MAX(0,line_number - pause_after);
275     else
276 	line_number++;
277     while ((line_number >= 0) &&
278 	   (lines[line_number] != (const char*)NULL))
279     {
280 	if (stristr(lines[line_number],search_string) != (char*)NULL)
281 	    break;
282 	else if (code == KEYBOARD_SEARCH_BACKWARD)
283 	    line_number--;
284 	else
285 	    line_number++;
286     }
287     if ((line_number < 0) || (lines[line_number] == (const char*)NULL))
288     {				/* then search failed */
289 	beep(fpout);
290 	line_number = MAX(0,last_line_number + 1 - pause_after);
291     }
292     return (line_number);
293 }
294 
295 
296 static void
297 #if defined(HAVE_STDC)
erase_characters(FILE * fpout,int how_many)298 erase_characters(FILE *fpout, int how_many)
299 #else
300 erase_characters(fpout, how_many)
301 FILE *fpout;
302 int how_many;
303 #endif
304 {
305     for ( ; how_many > 0; --how_many)
306     {
307 	(void)fputc(CH_BACKSPACE,fpout);
308 	(void)fputc(' ',fpout);
309 	(void)fputc(CH_BACKSPACE,fpout);
310     }
311     (void)fflush(fpout);
312 }
313 
314 
315 #if OS_PCDOS
316 
317 #if defined(HAVE_CONIO_H)
318 #include <conio.h>		/* needed for getch() declaration */
319 #endif
320 
321 int
get_screen_lines(VOID)322 get_screen_lines(VOID)
323 {
324     return (SCREEN_LINES);
325 }
326 
327 
328 void
kbclose(VOID)329 kbclose(VOID)
330 {
331 }
332 
333 
334 static keyboard_code_t
kbcode(VOID)335 kbcode(VOID)
336 {
337     int c;
338 
339     c = kbget();			/* get from keyboard without echo */
340     if ((c == 0) || (c == 0xe0))	/* then have IBM PC function key */
341     {
342 	c = kbget();			/* function key code */
343 	switch (c)			/* convert key code to character */
344 	{
345 	case 71:			/* HOME */
346 	    return (KEYBOARD_HOME);
347 
348 	case 72:			/* UP arrow */
349 	    return (KEYBOARD_UP);
350 
351 	case 73:			/* PGUP */
352 	    return (KEYBOARD_PGUP);
353 
354 	case 79:			/* END */
355 	    return (KEYBOARD_END);
356 
357 	case 80:			/* DOWN arrow */
358 	    return (KEYBOARD_DOWN);
359 
360 	case 81:			/* PGDN */
361 	    return (KEYBOARD_PGDN);
362 
363 	default:
364 	    return (KEYBOARD_UNKNOWN);
365 	}
366     }
367     else if (c == EOF)
368 	return (KEYBOARD_EOF);
369     else
370 	return (keymap[(unsigned)c]);
371 }
372 
373 
374 static int
kbget(VOID)375 kbget(VOID)
376 {
377     return (getch());
378 }
379 
380 
381 void
kbopen(VOID)382 kbopen(VOID)
383 {
384     kbinitmap();
385 }
386 #endif /* OS_PCDOS */
387 
388 
389 #if OS_UNIX
390 
391 static void	reset_terminal ARGS((void));
392 static void	set_terminal ARGS((void));
393 
394 static FILE	*fptty = (FILE*)NULL;	/* for kbxxx() functions */
395 static YESorNO	tty_init = NO;		/* set to YES if tty_save set */
396 
397 
398 void
kbclose(VOID)399 kbclose(VOID)
400 {
401     reset_terminal();
402     if (fptty != (FILE*)NULL)
403 	(void)fclose(fptty);
404 }
405 
406 
407 keyboard_code_t
kbcode(VOID)408 kbcode(VOID)
409 {
410     int c = kbget();
411 
412     if (c == EOF)
413 	return (KEYBOARD_EOF);
414     else if (c == CH_ESCAPE)
415     {					/* handle some X Window System keys */
416 	c = kbget();
417 	if (c == (int)'[')		/* ']' for balance */
418 	{
419 	    c = kbget();
420 	    if (c == (int)'A')		/* "\e[A" ("]" for balance) */
421 		return (KEYBOARD_UP);
422 	    else if (c == (int)'B')	/* "\e[B" ("]" for balance) */
423 		return (KEYBOARD_DOWN);
424 	    else if (c == (int)'C')	/* "\e[C" ("]" for balance) */
425 		return (KEYBOARD_END);
426 	    else if (c == (int)'D')	/* "\e[D" ("]" for balance) */
427 		return (KEYBOARD_HOME);
428 	    else if ((c == (int)'5') && (kbget() == (int)'~')) /* "\e[5~" ("]" for balance) */
429 	        return (KEYBOARD_PGUP);
430 	    else if ((c == (int)'6') && (kbget() == (int)'~')) /* "\e[6~" ("]" for balance) */
431 	        return (KEYBOARD_PGDN);
432 	}
433 	return (KEYBOARD_UNKNOWN);
434     }
435     else
436 	return (keymap[(unsigned)c]);
437 }
438 
439 
440 int
kbget(VOID)441 kbget(VOID)
442 {
443     if (fptty != (FILE*)NULL)
444     {
445 #if 0	/* fflush() discards typeahead -- no good for search string input */
446 	(void)fflush(fptty);
447 #endif
448 	return (getc(fptty));
449     }
450     else
451 	return (EOF);
452 }
453 
454 
455 void
kbopen(VOID)456 kbopen(VOID)
457 {
458     kbinitmap();
459     if ((fptty = tfopen("/dev/tty","r")) != (FILE*)NULL)
460     {
461 	set_terminal();
462 	screen_lines = get_screen_lines();
463     }
464 }
465 
466 
467 #if defined(HAVE_TERMIO_H)
468 #include <termio.h>
469 static struct termio tty_save;			/* SVID2 and XPG2 interface */
470 
471 static void
reset_terminal(VOID)472 reset_terminal(VOID)				/* restore saved modes */
473 {
474     if (tty_init == YES)
475 	(void)ioctl((int)(fileno(fptty)),(int)TCSETAF,(char*)&tty_save);
476 }
477 
478 
479 static void
set_terminal(VOID)480 set_terminal(VOID)				/* set to cbreak input mode */
481 {
482     struct termio tty;			/* SVID2, XPG2 interface */
483 
484     if (ioctl((int)(fileno(fptty)),(int)TCGETA,(char*)&tty) != -1)
485     {
486 	tty_save = tty;
487 	tty_init = YES;
488 	tty.c_iflag &= ~(INLCR | ICRNL | ISTRIP | IXON | BRKINT);
489 
490 #if defined(IUCLC)
491 	tty.c_iflag &= ~IUCLC;		/* absent from POSIX */
492 #endif /* defined(IUCLC) */
493 
494 	tty.c_lflag &= ~(ECHO | ICANON);
495 	tty.c_cc[4] = 5;		/* MIN */
496 	tty.c_cc[5] = 2;		/* TIME */
497 	(void)ioctl((int)(fileno(fptty)),(int)TCSETAF,(char*)&tty);
498     }
499 }
500 #endif /* HAVE_TERMIO_H */
501 
502 
503 #if defined(HAVE_TERMIOS_H)
504 #include <termios.h>
505 static struct termios tty_save;	/* XPG3, POSIX.1, FIPS 151-1 interface */
506 
507 static void
reset_terminal(VOID)508 reset_terminal(VOID)				/* restore saved modes */
509 {
510     if (tty_init == YES)
511 	(void)tcsetattr((int)(fileno(fptty)),TCSANOW,&tty_save);
512 }
513 
514 
515 static void
set_terminal(VOID)516 set_terminal(VOID)				/* set to cbreak input mode */
517 {
518     struct termios tty;		/* XPG3, POSIX.1, FIPS 151-1 interface */
519 
520     if (tcgetattr((int)(fileno(fptty)),&tty) != -1)
521     {
522 	tty_save = tty;
523 	tty_init = YES;
524 	tty.c_iflag &= ~(INLCR | ICRNL | ISTRIP | IXON | BRKINT);
525 
526 #if defined(IUCLC)
527 	tty.c_iflag &= ~IUCLC;		/* absent from POSIX */
528 #endif /* defined(IUCLC) */
529 
530 	tty.c_lflag &= ~(ECHO | ICANON);
531 	tty.c_cc[VMIN] = 5;		/* MIN */
532 	tty.c_cc[VTIME] = 2;		/* TIME */
533 	(void)tcsetattr((int)(fileno(fptty)),TCSANOW,&tty);
534     }
535 }
536 #endif /* defined(HAVE_TERMIOS_H) */
537 
538 #if defined(HAVE_SGTTY_H)
539 #include <sgtty.h>
540 
541 static struct sgttyb tty_save;			/* Berkeley style interface */
542 
543 
544 static void
reset_terminal(VOID)545 reset_terminal(VOID)			/* restored saved terminal modes */
546 {
547     if (tty_init == YES)
548 	(void)ioctl((int)(fileno(fptty)),(int)TIOCSETP,(char*)&tty_save);
549 }
550 
551 
552 static void
set_terminal(VOID)553 set_terminal(VOID)		/* set terminal for cbreak input mode */
554 {
555     struct sgttyb tty;
556 
557     /* Try to put file into cbreak mode for character-at-a-time input */
558     if (ioctl((int)(fileno(fptty)),(int)TIOCGETP,(char*)&tty) != -1)
559     {
560 	tty_save = tty;
561 	tty_init = YES;
562 	tty.sg_flags &= ~(ECHO | LCASE);
563 	tty.sg_flags |= CBREAK;
564 	(void)ioctl((int)(fileno(fptty)),(int)TIOCSETP,(char*)&tty);
565     }
566 }
567 #endif /* defined(HAVE_SGTTY_H) */
568 
569 int
get_screen_lines(VOID)570 get_screen_lines(VOID)	/* this must come after terminal header includes! */
571 {
572 
573 #if defined(HAVE_ISATTY)
574     if ((isatty(fileno(stdin)) == 0) || (isatty(fileno(stdout)) == 0))
575 	return (0);
576 #endif
577 
578 #if defined(TIOCGWINSZ)
579     if (fptty != (FILE*)NULL)
580     {
581 	struct winsize window_size;
582 
583 	(void)ioctl((int)(fileno(fptty)),(int)TIOCGWINSZ,&window_size);
584 
585 	if (window_size.ws_row > 0)
586 	    return ((int)window_size.ws_row);
587     }
588 #else /* defined(TIOCGWINSZ) */
589     /* some systems store screen lines in environment variables  */
590     {
591 	char *p;
592 	if (((p = getenv("ROWS")) != (char*)NULL) ||
593 	    ((p = getenv("LINES")) != (char*)NULL))
594 	{
595 	    int n;
596 
597 	    n = (int)atoi(p);
598 
599 	    if (n > 0)
600 		return (n);
601 	}
602     }
603 #endif /* defined(TIOCGWINSZ) */
604 
605     return (SCREEN_LINES);
606 }
607 #endif /* OS_UNIX */
608 
609 
610 #if OS_VAXVMS
611 
612 #if defined(HAVE_SSDEF_H)
613 #include <ssdef.h>
614 #endif
615 
616 #if defined(HAVE_DESCRIP_H)
617 #include <descrip.h>
618 #endif
619 
620 #if defined(HAVE_IODEF_H)
621 #include <iodef.h>
622 #endif
623 
624 #if defined(HAVE_TTDEF_H)
625 #include <ttdef.h>
626 #endif
627 
628 #if defined(HAVE_TT2DEF_H)
629 #include <tt2def.h>
630 #endif
631 
632 #if defined(__ALPHA)
633 int	sys$assign(void *desc_,int *channel_,int n1_,int n2_);
634 int	sys$qiow(int, int channel_, int flags_, int n1_, int n2_, int n3_,
635 		int *ret_char_, int n4_,  int n5_, int n6_, int n7_, int n8_);
636 int	system(const char *s_);
637 #else
638 /* VAX VMS 6.1 has these header files */
639 #include <starlet.h>			/* for sys$assign(), sys$qiow() */
640 #endif
641 
642 extern int lib$screen_info ARGS((short *,short *,short *,short *,));
643 					/* not defined in any system header */
644 					/* file in VMS 6.x */
645 
646 static int status;			/* system service status */
647 static int tt_channel = -1;		/* terminal channel for image QIO's */
648 static int iomask;			/* QIO flag mask */
649 static $DESCRIPTOR(sys_in,"TT:");	/* terminal descriptor */
650 
651 static struct
652 {
653     unsigned char class;
654     unsigned char type;
655     unsigned short buffer_size;
656     unsigned long tt;
657     unsigned long tt2;
658 } mode_buf,mode_save;
659 
660 #define FAILED(status) (~(status) & 1)	/* failure if LSB is 0 */
661 
662 int
get_screen_lines(VOID)663 get_screen_lines(VOID)
664 {
665     short flags;
666     short dvtype;
667     short ncols;
668     short nrows = 0;
669 
670 #if defined(__ALPHA) || defined(__VMS_VERSION)
671     /* I don't know what the OpenVMS replacement for lib$screen_info() is yet. */
672     /* It may be that scr$w_pagesize and scr$w_width in the struct scrdef1 */
673     /* in <scrdef.h> are the values we need for nrows and ncols. */
674     ncols = 80;
675     nrows = 24;
676 #else
677     (void)lib$screen_info(&flags,&dvtype,&ncols,&nrows);
678 #endif
679 
680     return ((int)((nrows > 0) ? nrows : SCREEN_LINES));
681 }
682 
683 
684 void
kbclose(VOID)685 kbclose(VOID)
686 {
687 #if !defined(__ALPHA)
688     (void)sys$qiow(0,tt_channel,IO$_SETMODE,0,0,0, &mode_save,12,0,0,0,0);
689 #endif
690 }
691 
692 
693 keyboard_code_t
kbcode(VOID)694 kbcode(VOID)
695 {
696     int c = kbget();
697 
698     return ((c == EOF) ? KEYBOARD_EOF : keymap[(unsigned)c]);
699 }
700 
701 
702 int
kbget(VOID)703 kbget(VOID)
704 {
705 #if defined(__ALPHA)
706     return (getchar());
707 #else
708     int c;
709 
710     status = sys$qiow(0,tt_channel,iomask,0,0,0,&c,1,0,0,0,0);
711 
712     return ((int)(FAILED(status) ? EOF : BYTE_VAL(c)));
713 #endif
714 }
715 
716 
717 void
kbopen(VOID)718 kbopen(VOID)
719 {
720     kbinitmap();
721 #if defined(__ALPHA)
722     /* assume stdin is open for now */
723 #else
724     status = sys$assign(&sys_in,&tt_channel,0,0);
725     if (!FAILED(status))
726     {
727 	(void)sys$qiow(0,tt_channel,IO$_SENSEMODE,0,0,0,&mode_save,12,0,0,0,0);
728 	mode_buf = mode_save;
729 	mode_buf.tt &= ~TT$M_WRAP;
730 	(void)sys$qiow(0,tt_channel,IO$_SETMODE,0,0,0,&mode_buf,12,0,0,0,0);
731 	iomask = IO$_TTYREADALL | IO$M_NOECHO;
732     }
733 #endif
734 }
735 #endif /* OS_VAXVMS */
736 
737 
738 static void
kbinitmap(VOID)739 kbinitmap(VOID)
740 {
741     (void)Memset((void*)&keymap[0],(int)KEYBOARD_UNKNOWN,sizeof(keymap));
742 
743     keymap[(unsigned)'b']	= KEYBOARD_PGUP;
744     keymap[(unsigned)'B']	= KEYBOARD_PGUP;
745     keymap[(unsigned)META('V')]	= KEYBOARD_PGUP; /* Emacs scroll-down */
746 
747     keymap[(unsigned)'\r']	= KEYBOARD_DOWN; /* the less and more pagers bind */
748     keymap[(unsigned)'\n']	= KEYBOARD_DOWN; /* these keys to KEYBOARD_DOWN */
749     keymap[(unsigned)'d']	= KEYBOARD_DOWN;
750     keymap[(unsigned)'D']	= KEYBOARD_DOWN;
751     keymap[(unsigned)CTL('N')]	= KEYBOARD_DOWN; /* Emacs next-line*/
752 
753     keymap[(unsigned)'e']	= KEYBOARD_END;
754     keymap[(unsigned)'E']	= KEYBOARD_END;
755     keymap[(unsigned)META('>')]	= KEYBOARD_END; /* Emacs end-of-buffer */
756     keymap[(unsigned)'>']	= KEYBOARD_END;
757 
758     keymap[(unsigned)'f']	= KEYBOARD_PGDN;
759     keymap[(unsigned)'F']	= KEYBOARD_PGDN;
760     keymap[(unsigned)' ']	= KEYBOARD_PGDN;
761     keymap[(unsigned)CTL('V')]	= KEYBOARD_PGDN; /* Emacs scroll-up */
762 
763     keymap[(unsigned)'h']	= KEYBOARD_HELP;
764     keymap[(unsigned)'H']	= KEYBOARD_HELP;
765     keymap[(unsigned)'?']	= KEYBOARD_HELP;
766     keymap[(unsigned)CH_BACKSPACE] = KEYBOARD_HELP; /* Emacs help */
767 
768     keymap[(unsigned)CH_ESCAPE]	= KEYBOARD_QUIT; /* ESCape gets out */
769     keymap[(unsigned)'q']	= KEYBOARD_QUIT;
770     keymap[(unsigned)'Q']	= KEYBOARD_QUIT;
771 
772     keymap[(unsigned)'.']	= KEYBOARD_AGAIN;
773     keymap[(unsigned)'r']	= KEYBOARD_AGAIN;
774     keymap[(unsigned)'R']	= KEYBOARD_AGAIN;
775     keymap[(unsigned)CTL('L')]	= KEYBOARD_AGAIN; /* Emacs recenter */
776 
777     keymap[(unsigned)'t']	= KEYBOARD_HOME;
778     keymap[(unsigned)'T']	= KEYBOARD_HOME;
779     keymap[(unsigned)META('<')]	= KEYBOARD_HOME; /* Emacs beginning-of-buffer */
780     keymap[(unsigned)'<']	= KEYBOARD_HOME;
781 
782     keymap[(unsigned)CTL('R')]	= KEYBOARD_SEARCH_BACKWARD; /* Emacs */
783     keymap[(unsigned)'\\']	= KEYBOARD_SEARCH_BACKWARD;
784 
785     keymap[(unsigned)CTL('S')]	= KEYBOARD_SEARCH_FORWARD; /* Emacs */
786     keymap[(unsigned)'/']	= KEYBOARD_SEARCH_FORWARD;
787 
788     keymap[(unsigned)'u']	= KEYBOARD_UP;
789     keymap[(unsigned)'U']	= KEYBOARD_UP;
790     keymap[(unsigned)CTL('P')]	= KEYBOARD_UP; 	/* Emacs previous-line */
791 
792     keymap[(unsigned)'[']	= KEYBOARD_UP_PARAGRAPH;
793     keymap[(unsigned)']']	= KEYBOARD_DOWN_PARAGRAPH;
794 
795     keymap[(unsigned)'{']	= KEYBOARD_UP_PARAGRAPH;
796     keymap[(unsigned)'}']	= KEYBOARD_DOWN_PARAGRAPH;
797 }
798 
799 #endif /* (SCREEN_LINES > 0) */
800