1 /* pcterm.c -- How to handle the PC terminal for Info under MS-DOS/MS-Windows.
2 
3    Copyright 1998-2019 Free Software Foundation, Inc.
4 
5    This program is free software: you can redistribute it and/or modify
6    it under the terms of the GNU General Public License as published by
7    the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9 
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
17 
18 
19 /* WARNING WARNING WARNING!!!  This probably won't work as is with
20    anything but DJGPP and MinGW!  However, Borland should come close,
21    and other PC compilers will need minor modifications.  */
22 
23 #ifdef __MSDOS__
24 /* intl/libintl.h defines a macro `gettext' which
25    conflicts with conio.h header.  */
26 #ifdef gettext
27 # undef gettext
28 # define gettext _gettext
29 #endif
30 
31 #include <pc.h>
32 #include <keys.h>
33 #include <conio.h>
34 #endif
35 
36 #ifdef _WIN32
37 #include <io.h>
38 #include <conio.h>
39 #include <process.h>
40 #include <malloc.h>	/* for alloca */
41 #define WIN32_LEAN_AND_MEAN
42 #include <windows.h>
43 
44 #ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
45 #define ENABLE_VIRTUAL_TERMINAL_PROCESSING 4
46 #endif
47 #ifndef COMMON_LVB_UNDERSCORE
48 #define COMMON_LVB_UNDERSCORE 0x8000
49 #endif
50 
51 struct text_info {
52     WORD normattr;
53     WORD attribute;
54     SHORT winleft;
55     SHORT wintop;
56     SHORT winright;
57     SHORT winbottom;
58     SHORT screenheight;
59     SHORT screenwidth;
60     SHORT curx;
61     SHORT cury;
62     COORD bufsize;
63     unsigned char currmode;	/* unused and unsupported for Windows */
64 };
65 
66 struct termios {
67   int dummy;
68 };
69 
70 enum text_modes { LASTMODE=-1 };
71 
72 #define cprintf _cprintf
73 #define cputs _cputs
74 
75 #undef read
76 #undef _read
77 
78 #include "display.h"
79 
80 void reset_info_window_sizes (void);
81 void redisplay_after_signal (void);
82 
83 #endif
84 
85 #include "variables.h"
86 #include "session.h"
87 #include "terminal.h"
88 
89 extern int speech_friendly;	/* defined in info.c */
90 
91 /* **************************************************************** */
92 /*                                                                  */
93 /*                PC Terminal Output Functions                      */
94 /*                                                                  */
95 /* **************************************************************** */
96 
97 static struct text_info outside_info;  /* holds screen params outside Info */
98 #ifdef _WIN32
99 static WORD norm_attr, inv_attr, xref_attr;
100 static WORD current_attr;
101 static HANDLE hstdin = INVALID_HANDLE_VALUE;
102 static HANDLE hstdout = INVALID_HANDLE_VALUE;
103 static HANDLE hinfo = INVALID_HANDLE_VALUE;
104 static HANDLE hscreen = INVALID_HANDLE_VALUE;
105 static DWORD old_inpmode;
106 static DWORD old_outpmode;
107 static UINT output_cp;
108 #else
109 static unsigned char    norm_attr, inv_attr, xref_attr;
110 #endif
111 
112 static unsigned const char * find_sequence (int);
113 
114 #ifdef _WIN32
115 
116 /* Windows-specific initialization and de-initialization.  */
117 void
w32_info_prep(void)118 w32_info_prep (void)
119 {
120   if (hinfo != INVALID_HANDLE_VALUE)
121     {
122       DWORD new_mode;
123 
124       SetConsoleActiveScreenBuffer (hinfo);
125       current_attr = norm_attr;
126       hscreen = hinfo;
127       SetConsoleMode (hstdin, ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT);
128       GetConsoleMode (hscreen, &old_outpmode);
129       new_mode = old_outpmode & ~ENABLE_WRAP_AT_EOL_OUTPUT;
130       SetConsoleMode (hscreen, new_mode);
131       /* Enable underline, if available. */
132       SetConsoleMode (hscreen, new_mode | ENABLE_VIRTUAL_TERMINAL_PROCESSING);
133     }
134 }
135 
136 void
w32_info_unprep(void)137 w32_info_unprep (void)
138 {
139   if (hinfo != INVALID_HANDLE_VALUE)
140     {
141       SetConsoleActiveScreenBuffer (hstdout);
142       current_attr = outside_info.normattr;
143       hscreen = hstdout;
144       SetConsoleMode (hstdin, old_inpmode);
145     }
146 }
147 
148 void
w32_cleanup(void)149 w32_cleanup (void)
150 {
151   if (hinfo != INVALID_HANDLE_VALUE)
152     {
153       COORD cursor_pos;
154 
155       /* Restore the original position of the cursor.  */
156       cursor_pos.X = outside_info.curx;
157       cursor_pos.Y = outside_info.cury;
158       SetConsoleCursorPosition (hstdout, cursor_pos);
159 
160       /* Close the input handle we created.  */
161       CloseHandle (hinfo);
162     }
163 }
164 
165 static void w32_info_init (void) __attribute__((constructor));
166 static void pc_initialize_terminal (char *);
167 
168 static void
w32_info_init(void)169 w32_info_init (void)
170 {
171   /* We need to set this single hook here; the rest
172      will be set by pc_initialize_terminal when it is called.  */
173   terminal_initialize_terminal_hook = pc_initialize_terminal;
174 }
175 
176 /* Emulate DJGPP conio functions for Windows.  */
177 static void
gettextinfo(struct text_info * ti)178 gettextinfo (struct text_info *ti)
179 {
180   CONSOLE_SCREEN_BUFFER_INFO csbi;
181   static TCHAR errbuf[500];
182   DWORD ignored;
183 
184   hstdin = GetStdHandle (STD_INPUT_HANDLE);
185   hstdout = GetStdHandle (STD_OUTPUT_HANDLE);
186 
187   if (!GetConsoleMode (hstdin, &ignored))
188     hstdin = INVALID_HANDLE_VALUE;
189 
190   if (hstdout != INVALID_HANDLE_VALUE
191       && GetConsoleMode (hstdout, &ignored))
192     {
193       hinfo = CreateConsoleScreenBuffer (GENERIC_READ | GENERIC_WRITE,
194 					 FILE_SHARE_READ | FILE_SHARE_WRITE,
195 					 NULL, CONSOLE_TEXTMODE_BUFFER, NULL);
196       if (hinfo != INVALID_HANDLE_VALUE
197 	  && GetConsoleScreenBufferInfo (hstdout, &csbi))
198 	{
199 	  ti->normattr = csbi.wAttributes;
200 	  ti->winleft = 1;
201 	  ti->wintop = 1;
202 	  ti->winright = csbi.srWindow.Right + 1;
203 	  ti->winbottom = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
204 	  ti->attribute = csbi.wAttributes;
205 	  ti->screenheight = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
206 	  ti->screenwidth = csbi.srWindow.Right - csbi.srWindow.Left + 1;
207 	  ti->curx = csbi.dwCursorPosition.X;
208 	  ti->cury = csbi.dwCursorPosition.Y;
209 	  ti->bufsize = csbi.dwSize;
210 
211 	  atexit (w32_cleanup);
212 	}
213       else
214 	{
215 	  DWORD error_no = GetLastError ();
216 
217 	  if (!FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM, NULL,
218 			      error_no,
219 			      0, /* choose most suitable language */
220 			      errbuf, sizeof (errbuf), NULL))
221 	    sprintf (errbuf, "w32 error %u", error_no);
222 	  CloseHandle (hinfo);
223 	  info_error (_("Terminal cannot be initialized: %s\n"), errbuf);
224 	  exit (1);
225 	}
226     }
227   else
228     {
229       /* We were invoked non-interactively.  Do the minimum we must.   */
230       ti->screenheight = 24;
231       ti->screenwidth = 80;
232     }
233 }
234 
235 void
textattr(int attr)236 textattr (int attr)
237 {
238   if (hscreen != INVALID_HANDLE_VALUE)
239     SetConsoleTextAttribute (hscreen, attr);
240 }
241 
242 void
textmode(int mode)243 textmode (int mode)
244 {
245   /* Nothing.  */
246 }
247 
248 void
highvideo(void)249 highvideo (void)
250 {
251   int attr;
252   CONSOLE_SCREEN_BUFFER_INFO csbi;
253 
254   GetConsoleScreenBufferInfo (hscreen, &csbi);
255   attr = csbi.wAttributes | FOREGROUND_INTENSITY;
256   attr ^= norm_attr & FOREGROUND_INTENSITY;
257   textattr (attr);
258 }
259 
260 void
normvideo(void)261 normvideo (void)
262 {
263   int attr;
264   CONSOLE_SCREEN_BUFFER_INFO csbi;
265 
266   GetConsoleScreenBufferInfo (hscreen, &csbi);
267   attr = csbi.wAttributes & ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY
268 			      | COMMON_LVB_UNDERSCORE);
269   attr |= norm_attr & (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
270   textattr (attr);
271 }
272 
273 void
blinkvideo(void)274 blinkvideo (void)
275 {
276   int attr;
277   CONSOLE_SCREEN_BUFFER_INFO csbi;
278 
279   GetConsoleScreenBufferInfo (hscreen, &csbi);
280   attr = csbi.wAttributes | BACKGROUND_INTENSITY;
281   attr ^= norm_attr & BACKGROUND_INTENSITY;
282   textattr (attr);
283 }
284 
285 void
underline(void)286 underline (void)
287 {
288   int attr;
289   CONSOLE_SCREEN_BUFFER_INFO csbi;
290 
291   GetConsoleScreenBufferInfo (hscreen, &csbi);
292   attr = csbi.wAttributes | COMMON_LVB_UNDERSCORE;
293   textattr (attr);
294 }
295 
296 void
textcolor(int color)297 textcolor (int color)
298 {
299   int attr;
300   CONSOLE_SCREEN_BUFFER_INFO csbi;
301 
302   GetConsoleScreenBufferInfo (hscreen, &csbi);
303   attr = (csbi.wAttributes & (COMMON_LVB_UNDERSCORE | 0xf0)) | (color & 0x0f);
304   textattr (attr);
305 }
306 
307 void
textbackground(int color)308 textbackground (int color)
309 {
310   int attr;
311   CONSOLE_SCREEN_BUFFER_INFO csbi;
312 
313   GetConsoleScreenBufferInfo (hscreen, &csbi);
314   attr = (csbi.wAttributes & (COMMON_LVB_UNDERSCORE | 0x0f)) | ((color & 0x0f) << 4);
315   textattr (attr);
316 }
317 
318 void
ScreenGetCursor(int * row,int * col)319 ScreenGetCursor (int *row, int *col)
320 {
321   CONSOLE_SCREEN_BUFFER_INFO csbi;
322 
323   if (hscreen == INVALID_HANDLE_VALUE)
324     *row = *col = 0;
325   else
326     {
327       GetConsoleScreenBufferInfo (hscreen, &csbi);
328       *row = csbi.dwCursorPosition.Y;
329       *col = csbi.dwCursorPosition.X;
330     }
331 }
332 
333 void
ScreenSetCursor(int row,int col)334 ScreenSetCursor (int row, int col)
335 {
336   if (hscreen != INVALID_HANDLE_VALUE)
337     {
338       COORD cursor_pos;
339 
340       cursor_pos.X = col;
341       cursor_pos.Y = row;
342 
343       SetConsoleCursorPosition (hscreen, cursor_pos);
344     }
345 }
346 
347 void
ScreenClear(void)348 ScreenClear (void)
349 {
350   if (hscreen != INVALID_HANDLE_VALUE)
351     {
352       DWORD nchars = screenwidth * screenheight;
353       COORD start_pos;
354       DWORD written;
355 
356       start_pos.X = start_pos.Y = 0;
357       FillConsoleOutputAttribute (hscreen, norm_attr, nchars, start_pos,
358 				  &written);
359       FillConsoleOutputCharacter (hscreen, ' ', nchars, start_pos, &written);
360     }
361 }
362 
363 void
clreol(void)364 clreol (void)
365 {
366   if (hscreen != INVALID_HANDLE_VALUE)
367     {
368       DWORD nchars;
369       COORD start_pos;
370       DWORD written;
371       CONSOLE_SCREEN_BUFFER_INFO csbi;
372 
373       GetConsoleScreenBufferInfo (hscreen, &csbi);
374       start_pos = csbi.dwCursorPosition;
375       nchars = csbi.dwSize.X - start_pos.X;
376 
377       FillConsoleOutputAttribute (hscreen, current_attr, nchars, start_pos,
378 				  &written);
379       FillConsoleOutputCharacter (hscreen, ' ', nchars, start_pos, &written);
380     }
381 }
382 
383 void
ScreenVisualBell(void)384 ScreenVisualBell (void)
385 {
386   if (hscreen != INVALID_HANDLE_VALUE)
387     {
388       DWORD nchars = screenwidth * screenheight;
389       COORD start_pos;
390       DWORD written;
391       PWORD attr;
392       DWORD i;
393 
394       start_pos.X = start_pos.Y = 0;
395       attr = xmalloc (nchars * sizeof (WORD));
396       ReadConsoleOutputAttribute (hscreen, attr, nchars, start_pos, &written);
397       for (i = 0; i < nchars; ++i)
398 	attr[i] ^= norm_attr ^ inv_attr;
399       WriteConsoleOutputAttribute (hscreen, attr, nchars, start_pos, &written);
400       Sleep (50);
401       for (i = 0; i < nchars; ++i)
402 	attr[i] ^= norm_attr ^ inv_attr;
403       WriteConsoleOutputAttribute (hscreen, attr, nchars, start_pos, &written);
404       free (attr);
405     }
406   else
407     {
408       printf ("%c", '\a');
409       fflush (stdout);
410     }
411 }
412 
413 int
movetext(int left,int top,int right,int bottom,int destleft,int desttop)414 movetext(int left, int top, int right, int bottom, int destleft, int desttop)
415 {
416   if (hscreen != INVALID_HANDLE_VALUE)
417     {
418       SMALL_RECT src;
419       COORD dest;
420       CHAR_INFO fill;
421 
422       src.Left = left - 1;
423       src.Top = top - 1;
424       src.Right = right - 1;
425       src.Bottom = bottom - 1;
426 
427       dest.X = destleft - 1;
428       dest.Y = desttop - 1;
429 
430       fill.Attributes = norm_attr;
431       fill.Char.AsciiChar = (CHAR)' ';
432 
433       return ScrollConsoleScreenBuffer (hscreen, &src , NULL, dest, &fill) != 0;
434     }
435   else
436     return 0;
437 }
438 
439 int
ScreenRows(void)440 ScreenRows (void)
441 {
442   if (hscreen != INVALID_HANDLE_VALUE)
443     {
444       CONSOLE_SCREEN_BUFFER_INFO csbi;
445 
446       GetConsoleScreenBufferInfo (hscreen, &csbi);
447       return csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
448     }
449   else
450     return 24;
451 }
452 
453 int
ScreenCols(void)454 ScreenCols (void)
455 {
456   if (hscreen != INVALID_HANDLE_VALUE)
457     {
458       CONSOLE_SCREEN_BUFFER_INFO csbi;
459 
460       GetConsoleScreenBufferInfo (hscreen, &csbi);
461       return csbi.srWindow.Right - csbi.srWindow.Left + 1;
462     }
463   else
464     return 80;
465 }
466 
467 void
_set_screen_lines(int lines)468 _set_screen_lines (int lines)
469 {
470   if (hscreen != INVALID_HANDLE_VALUE)
471     {
472       SMALL_RECT window_rectangle;
473       CONSOLE_SCREEN_BUFFER_INFO csbi;
474       COORD scrbufsize;
475 
476       GetConsoleScreenBufferInfo (hscreen, &csbi);
477 
478       window_rectangle = csbi.srWindow;
479       window_rectangle.Bottom = window_rectangle.Top + lines - 1;
480       SetConsoleWindowInfo (hscreen, TRUE, &window_rectangle);
481 
482       /* Set the screen buffer size to the same dimensions as the window,
483 	 so that the dysfunctional scroll bar disappears.  */
484       scrbufsize.X = window_rectangle.Right - window_rectangle.Left + 1;
485       scrbufsize.Y = window_rectangle.Bottom - window_rectangle.Top + 1;
486       SetConsoleScreenBufferSize (hscreen, scrbufsize);
487     }
488 }
489 
490 void
w32_set_screen_dimensions(int cols,int rows)491 w32_set_screen_dimensions (int cols, int rows)
492 {
493   if (hscreen != INVALID_HANDLE_VALUE)
494     {
495       SMALL_RECT window_rectangle;
496       CONSOLE_SCREEN_BUFFER_INFO csbi;
497 
498       GetConsoleScreenBufferInfo (hscreen, &csbi);
499 
500       window_rectangle = csbi.srWindow;
501       window_rectangle.Bottom = window_rectangle.Top + rows - 1;
502       window_rectangle.Right = window_rectangle.Left + cols - 1;
503       SetConsoleWindowInfo (hscreen, TRUE, &window_rectangle);
504     }
505 }
506 
507 /* Emulate `sleep'.  */
508 unsigned
sleep(unsigned sec)509 sleep (unsigned sec)
510 {
511   Sleep (sec*1000);
512   return 0;
513 }
514 
515 /* Keyboard input support.  */
516 
517 static int
w32_our_tty(int fd)518 w32_our_tty (int fd)
519 {
520   /* Is this our tty?  */
521   return hstdin != INVALID_HANDLE_VALUE
522 	 && hstdin == (HANDLE)_get_osfhandle (fd);
523 }
524 
525 /* Translate a Windows key event into the equivalent sequence of bytes
526    to be submitted to Info dispatcher.  */
527 #define define_seq(p,s1,s2)					\
528   do {								\
529     if ((ctl & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0)	\
530       memcpy (p, s1, sizeof (s1)), p += sizeof (s1) - 1;	\
531     else							\
532       memcpy (p, s2, sizeof (s2)), p += sizeof (s2) - 1;	\
533   } while (0)
534 
535 static int
w32keyseq(unsigned char ascii_ch,WORD vkey,DWORD ctl,unsigned char * seq)536 w32keyseq (unsigned char ascii_ch, WORD vkey, DWORD ctl, unsigned char *seq)
537 {
538   unsigned char *p = seq;
539 
540   switch (ascii_ch)
541     {
542       case '\0':
543 	/* Keys with no ASCII code are extended keys, like arrows.  */
544 	switch (vkey)
545 	  {
546 	    case VK_PRIOR:
547 	      define_seq (p, "\033\061p", "\033v");
548 	      break;
549 	    case VK_NEXT:
550 	      define_seq (p, "\033\061n", "\026");
551 	      break;
552 	    case VK_END:
553 	      define_seq (p, "\033>", "\033>");
554 	      break;
555 	    case VK_HOME:
556 	      define_seq (p, "\033<", "\033<");
557 	      break;
558 	    case VK_LEFT:
559 	      define_seq (p, "\033b", "\033[D");
560 	      break;
561 	    case VK_UP:
562 	      define_seq (p, "\033\061u", "\033[A");
563 	      break;
564 	    case VK_RIGHT:
565 	      define_seq (p, "\033f", "\033[C");
566 	      break;
567 	    case VK_DOWN:
568 	      define_seq (p, "\033\061m", "\033[B");
569 	      break;
570 	    case VK_INSERT:
571 	      define_seq (p, "\033[L", "\033[L");
572 	      break;
573 	    case VK_DELETE:	/* Delete => Ctrl-d, Alt-Delete => ESC d */
574 	      if ((ctl & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0)
575 		define_seq (p, "\033d", "\033d");
576 	      else
577 		define_seq (p, "\033d", "\004");
578 	      break;
579 	    case VK_HELP:	/* F1 => Ctrl-h */
580 	    case VK_F1:
581 	      *p++ = '\010';
582 	      break;
583 	    case 50:		/* Ctrl-@ => '\0' */
584 	      if ((ctl & SHIFT_PRESSED) != 0)
585 		*p++ = '\0';
586 	      break;
587 	    default:
588 	      if (0x41 <= vkey && vkey <= 0x5a)
589 		{
590 		  /* Alt-Ctrl-a, Alt-Ctrl-b, etc.  */
591 		  *p++ = '\033';
592 		  *p++ = '\001' + vkey - 0x41;
593 		}
594 	  }
595 	break;
596       case ' ':			/* Ctrl-SPC => '\0' */
597 	if ((ctl & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)) != 0)
598 	  ascii_ch = '\0';
599 	*p++ = ascii_ch;
600 	break;
601       case '\t':		/* Shift-TAB/Alt-TAB => Esc-TAB */
602 	if ((ctl & (SHIFT_PRESSED | LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0)
603 	  {
604 	    memcpy (p, "\033\011", sizeof ("\033\011"));
605 	    p += sizeof ("\033\011") - 1;
606 	  }
607 	else
608 	  *p++ = '\t';
609 	break;
610       case '\b':
611 	/* Backspace => DEL.  */
612 	ascii_ch = '\177';
613 	/* FALLTHROUGH */
614       default:
615 	if ((ctl & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)) != 0)
616 	  *p++ = '\033';
617 	*p++ = ascii_ch;
618 	break;
619     }
620   return p - seq;
621 }
622 
623 static unsigned char buffered_chars[512];
624 static size_t buf_head;
625 static size_t buf_tail;
626 
627 static ssize_t
w32_kbd_read(unsigned char * inbuf,size_t n)628 w32_kbd_read (unsigned char *inbuf, size_t n)
629 {
630   DWORD nevents, nread;
631   INPUT_RECORD inrec;
632   ssize_t nret = 0;
633 
634   do {
635 
636     /* Stuff any unread buffered characters.  */
637     while (buf_head < buf_tail && n > 0)
638       {
639 	*inbuf++ = buffered_chars[buf_head++];
640 	nret++;
641 	n--;
642       }
643     if (n <= 0)
644       break;
645 
646     /* Wait for input.  */
647     while (GetNumberOfConsoleInputEvents (hstdin, &nevents)
648 	   && nevents < 1)
649       Sleep (20);
650 
651     while (nevents-- && n > 0)
652       {
653 	if (!ReadConsoleInput (hstdin, &inrec, 1, &nread))
654 	  return -1;
655 
656 	if (nread > 0)
657 	  {
658 	    switch (inrec.EventType)
659 	      {
660 		case KEY_EVENT:
661 		  if (inrec.Event.KeyEvent.bKeyDown == TRUE
662 		      && !(inrec.Event.KeyEvent.wVirtualScanCode == 0
663 			   || inrec.Event.KeyEvent.wVirtualKeyCode == VK_SHIFT
664 			   || inrec.Event.KeyEvent.wVirtualKeyCode == VK_CONTROL
665 			   || inrec.Event.KeyEvent.wVirtualKeyCode == VK_MENU))
666 		    {
667 		      unsigned char keyseq[10];
668 		      int count = inrec.Event.KeyEvent.wRepeatCount;
669 		      unsigned char ch = inrec.Event.KeyEvent.uChar.AsciiChar;
670 		      WORD vkey = inrec.Event.KeyEvent.wVirtualKeyCode;
671 		      DWORD ctl_state = inrec.Event.KeyEvent.dwControlKeyState;
672 		      int nbytes = w32keyseq (ch, vkey, ctl_state, keyseq);
673 
674 		      /* Supply up to N characters to the caller.  */
675 		      while (count && n >= nbytes)
676 			{
677 			  if (nbytes == 1 && keyseq[0] == '\032')
678 			    {
679 			      terminal_goto_xy (0, screenheight - 1);
680 			      terminal_clear_to_eol ();
681 			      fflush (stdout);
682 			      terminal_unprep_terminal ();
683 			      kill (getpid (), 0);
684 			      terminal_prep_terminal ();
685 			      reset_info_window_sizes ();
686 			    }
687 			  else
688 			    {
689 			      memcpy (&inbuf[nret], keyseq, nbytes);
690 			      nret += nbytes;
691 			      n -= nbytes;
692 			    }
693 			  count--;
694 			}
695 		      /* Buffer the rest.  */
696 		      if (count > 0)
697 			{
698 			  buf_head = buf_tail = 0;
699 			  while (count--
700 				 && buf_tail < sizeof(buffered_chars) - nbytes)
701 			    {
702 			      memcpy (&buffered_chars[buf_tail], keyseq, nbytes);
703 			      buf_tail += nbytes;
704 			    }
705 			}
706 		    }
707 		  break;
708 		case WINDOW_BUFFER_SIZE_EVENT:
709 		  {
710 		    int rows, cols;
711 
712 		    /* Note: this event is _supposed_ to be sent only
713 		       when the console window's _screen_buffer_ size
714 		       is changed via the Properties->Layout dialog.
715 		       However, Windows 10 seems to send it even when
716 		       the properties are not changed.  */
717 		    cols = inrec.Event.WindowBufferSizeEvent.dwSize.X;
718 		    rows = inrec.Event.WindowBufferSizeEvent.dwSize.Y;
719 		    /* Avoid needless screen redraws, they produce
720 		       annoying flickering.  */
721 		    if (cols != screenwidth || rows != screenheight)
722 		      {
723 			screenwidth = cols;
724 			screenheight = rows;
725 			w32_set_screen_dimensions (cols, rows);
726 			display_initialize_display (screenwidth, screenheight);
727 			window_new_screen_size (screenwidth, screenheight);
728 			redisplay_after_signal ();
729 		      }
730 		  }
731 		  break;
732 		case MOUSE_EVENT:
733 		  {
734 		    /* Only vertical wheel support for now.  */
735 		    int wheeled =
736 		      (inrec.Event.MouseEvent.dwEventFlags & MOUSE_WHEELED) != 0;
737 		    if (wheeled && mouse_protocol == MP_NORMAL_TRACKING)
738 		      {
739 			extern void info_up_line (WINDOW *, int count);
740 			extern void info_down_line (WINDOW *, int count);
741 			extern WINDOW *active_window;
742 
743 			int hiword =
744 			  HIWORD (inrec.Event.MouseEvent.dwButtonState);
745 
746 			if ((hiword & 0xFF00) == 0)
747 			  info_up_line (active_window, 3);
748 			else
749 			  info_down_line (active_window, 3);
750 			display_update_display ();
751 		      }
752 		  }
753 		  break;
754 		default:
755 		  break;
756 	      }
757 	  }
758       }
759   } while (n > 0);
760   return nret;
761 }
762 
763 long
w32_chars_avail(int fd)764 w32_chars_avail (int fd)
765 {
766   if (w32_our_tty (fd))
767     return buf_tail - buf_head;
768   else
769     {
770       struct stat st;
771 
772       if (fstat (fd, &st) < 0)
773 	return 1;
774       else
775 	return st.st_size;
776     }
777 }
778 
779 ssize_t
w32_read(int fd,void * buf,size_t n)780 w32_read (int fd, void *buf, size_t n)
781 {
782   if (w32_our_tty (fd))
783     return w32_kbd_read (buf, n);
784   else
785     return _read (fd, buf, n);
786 }
787 
788 /* Write to the console a string of text encoded in UTF-8 or UTF-7.  */
789 static void
write_utf(DWORD cp,const char * text,int nbytes)790 write_utf (DWORD cp, const char *text, int nbytes)
791 {
792   /* MSDN says UTF-7 requires zero in flags.  */
793   DWORD flags = (cp == CP_UTF7) ? 0 : MB_ERR_INVALID_CHARS;
794   /* How much space do we need for wide characters?  */
795   int wlen = MultiByteToWideChar (cp, flags, text, nbytes, NULL, 0);
796 
797   if (wlen)
798     {
799       WCHAR *text_w = alloca (wlen * sizeof (WCHAR));
800       DWORD written;
801 
802       if (MultiByteToWideChar (cp, flags, text, nbytes, text_w, wlen) > 0)
803 	{
804 	  WriteConsoleW (hscreen, text_w, (nbytes < 0) ? wlen - 1 : wlen,
805 			 &written, NULL);
806 	  return;
807 	}
808     }
809   /* Fall back on conio.  */
810   if (nbytes < 0)
811     cputs (text);
812   else
813     cprintf ("%.*s", nbytes, text);
814 }
815 
816 /* A replacement for nl_langinfo which does a more accurate job for
817    the console output codeset.  Windows can use 3 different encodings
818    at the same time, and the Posix-compliant nl_langinfo simply
819    doesn't know enough to decide which one is needed when CODESET is
820    requested.  */
821 #undef nl_langinfo
822 #include <langinfo.h>
823 
824 char *
rpl_nl_langinfo(nl_item item)825 rpl_nl_langinfo (nl_item item)
826 {
827   if (item == CODESET)
828     {
829       static char buf[100];
830 
831       /* We need all the help we can get from GNU libiconv, so we
832 	 request transliteration as well.  */
833       sprintf (buf, "CP%u//TRANSLIT", GetConsoleOutputCP ());
834       return buf;
835     }
836   else
837     return nl_langinfo (item);
838 }
839 
840 #ifndef HAVE_WCWIDTH
841 /* A replacement for wcwidth.  The Gnulib version calls setlocale for
842    every character Info is about to display, which makes display of
843    large nodes annoyingly slow.
844 
845    Note that the Gnulib version is still compiled and put into
846    libgnu.a, because the configure script doesn't know about this
847    replacement.  But the linker will not pull the Gnulib version into
848    the binary, because it resolves the calls to this replacement
849    function.  */
850 int
wcwidth(wchar_t wc)851 wcwidth (wchar_t wc)
852 {
853   return wc == 0 ? 0 : iswprint (wc) ? 1 : -1;
854 }
855 #endif
856 
857 #endif	/* _WIN32 */
858 
859 /* Turn on reverse video. */
860 static void
pc_begin_inverse(void)861 pc_begin_inverse (void)
862 {
863   textattr (inv_attr);
864 }
865 
866 /* Turn off reverse video. */
867 static void
pc_end_inverse(void)868 pc_end_inverse (void)
869 {
870   textattr (norm_attr);
871 }
872 
873 /* The implementation of the underlined text.  The DOS/Windows console
874    doesn't support underlined text (until Win10), so we make it blue instead
875    (blue, because this face is used for hyperlinks).  */
876 static void
pc_begin_underline(void)877 pc_begin_underline (void)
878 {
879   if (xref_attr != COMMON_LVB_UNDERSCORE)
880     textattr (xref_attr);
881   else
882     underline ();
883 }
884 
885 static void
pc_end_underline(void)886 pc_end_underline (void)
887 {
888   if (xref_attr != COMMON_LVB_UNDERSCORE)
889     textattr (norm_attr);
890   else
891     normvideo ();
892 }
893 
894 /* Standout (a.k.a. "high video") text.  */
895 static void
pc_begin_standout(void)896 pc_begin_standout (void)
897 {
898   highvideo ();
899 }
900 
901 static void
pc_end_standout(void)902 pc_end_standout (void)
903 {
904   normvideo ();
905 }
906 
907 static void
pc_begin_blink(void)908 pc_begin_blink (void)
909 {
910   blinkvideo ();
911 }
912 
913 static void
pc_default_color(void)914 pc_default_color (void)
915 {
916   textattr (norm_attr);
917 }
918 
919 /* Info definitions of 8 colors (see terminal.h) are in an order
920    that's different from Windows/DOS console colors.  This function
921    unscrambles the order, and also maps 8 standard ANSI colors to the
922    low-intensity shades of the 16 PC colors, so that "standout" works
923    by turning the intensity bit.  */
924 static int
convert_color(int terminal_color)925 convert_color (int terminal_color)
926 {
927   /* The terminal.h order is:
928      black, red, green, yellow, blue, magenta, cyan, white.  */
929   static int pc_color_map[] = {
930     0, 4, 2, 6, 1, 5, 3, 7
931   };
932   int intensity = terminal_color & (FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
933   terminal_color &= ~(FOREGROUND_INTENSITY | BACKGROUND_INTENSITY);
934 
935   if (terminal_color >= 0
936       && terminal_color < sizeof(pc_color_map) / sizeof (pc_color_map[0]))
937     return pc_color_map[terminal_color] | intensity;
938   return 7;	/* lightgray */
939 }
940 
941 static void
pc_set_fg_color(int color)942 pc_set_fg_color (int color)
943 {
944   textcolor (convert_color (color) | (norm_attr & FOREGROUND_INTENSITY));
945 }
946 
947 static void
pc_set_bg_color(int color)948 pc_set_bg_color (int color)
949 {
950   textbackground (convert_color (color) | (norm_attr & BACKGROUND_INTENSITY));
951 }
952 
953 /* Move the cursor up one line. */
954 static void
pc_up_line(void)955 pc_up_line (void)
956 {
957   int x, y;
958   ScreenGetCursor (&y, &x);
959   ScreenSetCursor (MAX (y-1, 0), x);
960 }
961 
962 /* Move the cursor down one line. */
963 static void
pc_down_line(void)964 pc_down_line (void)
965 {
966   int x, y;
967   ScreenGetCursor (&y, &x);
968   ScreenSetCursor (MIN (screenheight-1, y+1), x);
969 }
970 
971 /* Clear the entire terminal screen. */
972 static void
pc_clear_screen(void)973 pc_clear_screen (void)
974 {
975   ScreenClear ();
976 }
977 
978 /* Clear from the current position of the cursor to the end of the line. */
979 static void
pc_clear_to_eol(void)980 pc_clear_to_eol (void)
981 {
982   clreol (); /* perhaps to be replaced by a loop */
983 }
984 
985 /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
986 static void
pc_get_screen_size(void)987 pc_get_screen_size(void)
988 {
989   /* Current screen dimensions are the default.  */
990   if (!outside_info.screenheight)	/* paranoia */
991     gettextinfo (&outside_info);
992   screenwidth  = outside_info.screenwidth;
993   screenheight = outside_info.screenheight;
994 
995   /* Environment variable "LINES" overrides the default.  */
996   if (getenv ("LINES") != NULL)
997     screenheight = atoi (getenv ("LINES"));
998 
999   /* Environment variable "INFO_LINES" overrides "LINES".  */
1000   if (getenv ("INFO_LINES") != NULL)
1001     screenheight = atoi (getenv ("INFO_LINES"));
1002 }
1003 
1004 /* Move the cursor to the terminal location of X and Y. */
1005 static void
pc_goto_xy(x,y)1006 pc_goto_xy (x, y)
1007      int x, y;
1008 {
1009   ScreenSetCursor (y, x); /* yes, pc.h says ScreenSetCursor (row, column) !! */
1010 }
1011 
1012 /* Print STRING to the terminal at the current position. */
1013 static void
pc_put_text(string)1014 pc_put_text (string)
1015      char *string;
1016 {
1017   if (speech_friendly)
1018     fputs (string, stdout);
1019 #ifdef __MINGW32__
1020   else if (hscreen == INVALID_HANDLE_VALUE)
1021     fputs (string, stdout);
1022   else if (output_cp == CP_UTF8 || output_cp == CP_UTF7)
1023     write_utf (output_cp, string, -1);
1024 #endif
1025   else
1026     cputs (string);
1027 }
1028 
1029 /* Ring the terminal bell.  The bell is rung visibly if the terminal is
1030    capable of doing that, and if terminal_use_visible_bell_p is non-zero. */
1031 static void
pc_ring_bell(void)1032 pc_ring_bell(void)
1033 {
1034   if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
1035     ScreenVisualBell ();
1036   else
1037     {
1038       printf ("%c",'\a');
1039       fflush (stdout);
1040     }
1041 }
1042 
1043 /* Print NCHARS from STRING to the terminal at the current position. */
1044 static void
pc_write_chars(string,nchars)1045 pc_write_chars (string, nchars)
1046     char *string;
1047     int nchars;
1048 {
1049   if (!nchars)
1050     return;
1051 
1052   if (speech_friendly)
1053     printf ("%.*s", nchars, string);
1054 #ifdef __MINGW32__
1055   else if (hscreen == INVALID_HANDLE_VALUE)
1056     printf ("%.*s", nchars, string);
1057   else if (output_cp == CP_UTF8 || output_cp == CP_UTF7)
1058     write_utf (output_cp, string, nchars);
1059 #endif
1060   else
1061     cprintf ("%.*s", nchars, string);
1062 }
1063 
1064 /* Scroll an area of the terminal from START to (and excluding) END,
1065    AMOUNT lines.  If AMOUNT is negative, the lines are scrolled
1066    towards the top of the screen, else they are scrolled towards the
1067    bottom of the screen.  The lines of the old region which do not
1068    overlap the new region are cleared, to mimic terminal operation.  */
1069 static void
pc_scroll_terminal(start,end,amount)1070 pc_scroll_terminal (start, end, amount)
1071     int start, end, amount;
1072 {
1073   int line_to_clear = amount > 0 ? start : end + amount;
1074 
1075   /* Move the text.  Note that `movetext' expects 1-based coordinates.  */
1076   movetext (1, start + 1, ScreenCols (), end, 1, start + amount + 1);
1077 
1078   /* Now clear the lines which were left unoccupied.  */
1079   if (amount < 0)
1080     amount = -amount;
1081   while (amount--)
1082     {
1083       ScreenSetCursor (line_to_clear++, 0);
1084       clreol ();
1085     }
1086 }
1087 
1088 /* Put the screen in the video mode and colors which Info will use.
1089    Prepare to start using the terminal to read characters singly.  */
1090 static void
pc_prep_terminal(void)1091 pc_prep_terminal (void)
1092 {
1093   int tty;
1094 
1095 #ifdef _WIN32
1096   w32_info_prep ();
1097 #endif
1098 
1099   /* Do not set screen height if we already have it, because
1100      doing so erases the screen.  */
1101   if (screenheight != ScreenRows ())
1102     _set_screen_lines (screenheight);
1103 
1104   /* Don't fail if they asked for screen dimensions that their
1105      hardware cannot support.  */
1106   screenheight = ScreenRows ();
1107   screenwidth  = ScreenCols ();
1108 
1109   /* Try setting the colors user asked for.  */
1110   textattr (norm_attr);
1111   ScreenClear ();
1112 
1113   /* Switch console reads to binary mode.  */
1114   tty = fileno (stdin);
1115 #ifdef __DJGPP__
1116   setmode (tty, O_BINARY);
1117   __djgpp_set_ctrl_c (1);	/* re-enable SIGINT generation by Ctrl-C */
1118 #endif
1119 }
1120 
1121 /* Restore the tty settings back to what they were before we started using
1122    this terminal. */
1123 static void
pc_unprep_terminal(void)1124 pc_unprep_terminal (void)
1125 {
1126   int tty;
1127 
1128 #ifdef _WIN32
1129   w32_info_unprep ();
1130 #endif
1131 
1132   textattr (outside_info.normattr);
1133 
1134   /* Do not set screen height if we already have it, because
1135      doing so erases the screen.  */
1136   if (outside_info.screenheight != ScreenRows ())
1137     {
1138       _set_screen_lines (outside_info.screenheight);
1139       textmode (LASTMODE);
1140     }
1141 #ifdef __MSDOS__
1142   else
1143     pc_clear_to_eol ();	/* for text attributes to really take effect */
1144 #endif
1145 #ifdef _WIN32
1146   if (hscreen != INVALID_HANDLE_VALUE)
1147     SetConsoleScreenBufferSize (hstdout, outside_info.bufsize);
1148 #endif
1149 
1150   /* Switch back to text mode on stdin.  */
1151   tty = fileno (stdin);
1152 #ifdef __DJGPP__
1153   setmode (tty, O_TEXT);
1154 #endif
1155 }
1156 
1157 /* Initialize the terminal which is known as TERMINAL_NAME.  If this
1158    terminal doesn't have cursor addressability, `terminal_is_dumb_p'
1159    becomes nonzero.  The variables SCREENHEIGHT and SCREENWIDTH are set
1160    to the dimensions that this terminal actually has.  Finally, the
1161    terminal screen is cleared. */
1162 static void
pc_initialize_terminal(term_name)1163 pc_initialize_terminal (term_name)
1164     char *term_name;
1165 {
1166   char *info_colors;
1167 
1168   if (!term_name)
1169     {
1170       term_name = getenv ("TERM");
1171       if (!term_name)
1172 #ifdef __MSDOS__
1173 	term_name = "pc-dos";	/* ``what's in a name?'' */
1174 #endif
1175 #ifdef _WIN32
1176 	term_name = "w32console";
1177 #endif
1178     }
1179 
1180   /* Get current video information, to be restored later.  */
1181   if (outside_info.screenwidth == 0)
1182     gettextinfo (&outside_info);
1183 
1184   /* Current screen colors are the default.  */
1185   norm_attr    = outside_info.normattr;
1186   inv_attr     = (((outside_info.normattr &    7) << 4) |
1187                   ((outside_info.normattr & 0x7f) >> 4));
1188 #ifdef __MSDOS__
1189   xref_attr = CYAN;
1190 #endif
1191 #ifdef _WIN32
1192   xref_attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
1193 #endif
1194   xref_attr |= outside_info.normattr & 0xf0;
1195 
1196   /* Does the user want non-default colors?  */
1197   info_colors = getenv ("INFO_COLORS");
1198   if ((info_colors != (char *)0) && !speech_friendly)
1199     {
1200       /* Decode a color from a string descriptor.
1201 	 The descriptor string is a sequence of color specifiers separated
1202 	 by a non-numeric character.  Each color specifier should represent
1203 	 a small integer which fits into an unsigned char, and can be given
1204 	 in any base supported by strtoul.  Examples of valid descriptors:
1205 
1206 		"10 31"
1207 		"0x13/0x45"
1208 		"007.077"
1209 
1210 	The separator between two color specifiers can be any character which
1211 	cannot be used in a printed representation of an integer number.  */
1212       char *endp;
1213       unsigned long color_desc = strtoul (info_colors, &endp, 0);
1214 
1215       if (color_desc <= UCHAR_MAX)
1216 	{
1217 	  norm_attr = (unsigned char)color_desc;
1218 	  xref_attr = (xref_attr & 0x0f) | (norm_attr & 0xf0);
1219 	  color_desc = strtoul (endp + 1, &endp, 0);
1220 	  if (color_desc <= UCHAR_MAX)
1221 	    inv_attr = (unsigned char)color_desc;
1222 #ifdef _WIN32
1223 	  if (*endp == 'u')
1224 	    xref_attr = COMMON_LVB_UNDERSCORE;
1225 	  else
1226 #endif
1227 	  if (*endp != '\0')
1228 	    {
1229 	      color_desc = strtoul (endp + 1, &endp, 0);
1230 	      if (color_desc <= UCHAR_MAX)
1231 		{
1232 #ifdef _WIN32
1233 		  if (*endp == 'u')
1234 		    color_desc |= COMMON_LVB_UNDERSCORE;
1235 		  xref_attr = (WORD)color_desc;
1236 #else
1237 		  xref_attr = (unsigned char)color_desc;
1238 #endif
1239 		}
1240 	    }
1241 	}
1242     }
1243 
1244   /* We can scroll.  */
1245   terminal_can_scroll = 1;
1246 
1247   /* We know how to produce a visible bell, if somebody's looking...  */
1248   if (!speech_friendly)
1249     terminal_has_visible_bell_p = 1;
1250 
1251   /* We are *certainly* NOT dumb!  */
1252   terminal_is_dumb_p = 0;
1253 
1254   pc_get_screen_size ();
1255 
1256 #ifdef __MINGW32__
1257   /* Record the screen output codepage.  */
1258   output_cp = GetConsoleOutputCP ();
1259 #endif
1260 
1261 #ifdef __MSDOS__
1262   /* Store the arrow keys.  */
1263   term_ku = (char *)find_sequence (K_Up);
1264   term_kd = (char *)find_sequence (K_Down);
1265   term_kr = (char *)find_sequence (K_Right);
1266   term_kl = (char *)find_sequence (K_Left);
1267 
1268   term_kP = (char *)find_sequence (K_PageUp);
1269   term_kN = (char *)find_sequence (K_PageDown);
1270 
1271   term_kh = (char *)find_sequence (K_Home);
1272   term_ke = (char *)find_sequence (K_End);
1273   term_ki = (char *)find_sequence (K_Insert);
1274   term_kD = (char *)find_sequence (K_Delete);
1275 #elif defined _WIN32
1276   term_kh = "\033<";
1277   term_ke = "\033>";
1278   term_ki = "\033[L";
1279 #endif	/* __MSDOS__ */
1280 
1281   /* Set all the hooks to our PC-specific functions.  */
1282   terminal_begin_inverse_hook       = pc_begin_inverse;
1283   terminal_end_inverse_hook         = pc_end_inverse;
1284   terminal_begin_standout_hook      = pc_begin_standout;
1285   terminal_end_standout_hook        = pc_end_standout;
1286   terminal_begin_underline_hook     = pc_begin_underline;
1287   terminal_end_underline_hook       = pc_end_underline;
1288   terminal_begin_bold_hook          = pc_begin_standout;
1289   terminal_begin_blink_hook         = pc_begin_blink;
1290   terminal_end_all_modes_hook       = pc_default_color;
1291   terminal_default_colour_hook      = pc_default_color;
1292   terminal_set_colour_hook          = pc_set_fg_color;
1293   terminal_set_bgcolour_hook        = pc_set_bg_color;
1294   terminal_prep_terminal_hook       = pc_prep_terminal;
1295   terminal_unprep_terminal_hook     = pc_unprep_terminal;
1296   terminal_up_line_hook             = pc_up_line;
1297   terminal_down_line_hook           = pc_down_line;
1298   terminal_clear_screen_hook        = pc_clear_screen;
1299   terminal_clear_to_eol_hook        = pc_clear_to_eol;
1300   terminal_get_screen_size_hook     = pc_get_screen_size;
1301   terminal_goto_xy_hook             = pc_goto_xy;
1302   terminal_put_text_hook            = pc_put_text;
1303   terminal_ring_bell_hook           = pc_ring_bell;
1304   terminal_write_chars_hook         = pc_write_chars;
1305   terminal_scroll_terminal_hook     = pc_scroll_terminal;
1306 }
1307 
1308 /* **************************************************************** */
1309 /*                                                                  */
1310 /*            How to Read Characters From the PC Terminal           */
1311 /*                                                                  */
1312 /* **************************************************************** */
1313 
1314 /* This will most certainly work ONLY with DJGPP.  */
1315 #ifdef __DJGPP__
1316 
1317 #include <errno.h>
1318 #include <sys/fsext.h>
1319 #include <dpmi.h>
1320 
1321 /* Translation table for some special keys.
1322    Arrow keys which are standard on other keyboards are translated into
1323    standard ESC-sequences, in case somebody rebinds the simple keys
1324    (like C-f, C-b, C-n, etc.).
1325 
1326    The strange "\033\061" prefix in some keys is a numeric argument of
1327    one, which means ``do the next command once''.  It is here so that
1328    when the according PC key is pressed in the middle of an incremental
1329    search, Info doesn't see just an ASCII character like `n' or `B',
1330    and doesn't add it to the search string; instead, it will exit the
1331    incremental search and then perform the command.  */
1332 static struct
1333 {
1334   int inkey;
1335   unsigned char const * const sequence;
1336 } DJGPP_keytab[] = {		   /* these are for moving between nodes... */
1337   {K_Control_PageDown,  "\033\061n"},
1338   {K_Control_PageUp,    "\033\061p"},
1339   {K_Control_Up,        "\033\061u"},
1340   {K_Control_Down,      "\033\061m"},
1341   {K_Control_Center,    "\033\061l"},
1342 
1343   {K_Home,              "\033[H"}, /* ...and these are for moving IN a node */
1344   {K_End,               "\033[F"}, /* they're Numeric-Keypad-Keys, so       */
1345   {K_Left,              "\033[D"}, /* NUMLOCK should be off !!              */
1346   {K_Right,             "\033[C"},
1347   {K_Down,              "\033[B"},
1348   {K_Up,                "\033[A"},
1349   {K_PageDown,          "\033[G"},
1350   {K_PageUp,            "\033[I"},
1351   {K_Control_Left,      "\033b"},
1352   {K_Control_Right,     "\033f"},
1353   {K_Control_Home,      "\033<"},
1354   {K_Control_End,       "\033>"},
1355 
1356   {K_EHome,             "\033[H"}, /* these are also for moving IN a node */
1357   {K_EEnd,              "\033[F"}, /* they're the "extended" (Grey) keys  */
1358   {K_ELeft,             "\033[D"},
1359   {K_ERight,            "\033[C"},
1360   {K_EDown,             "\033[B"},
1361   {K_EUp,               "\033[A"},
1362   {K_EPageDown,         "\033[G"},
1363   {K_EPageUp,           "\033[I"},
1364   {K_Control_ELeft,     "\033b"},
1365   {K_Control_ERight,    "\033f"},
1366   {K_Control_EHome,     "\033<"},
1367   {K_Control_EEnd,      "\033>"},
1368 
1369   {K_BackTab,           "\033\011"},
1370   {K_F1,                "\10"},    /* YEAH, gimme that good old F-one-thing */
1371   {K_Delete,            "\177"},   /* to make Kp-Del be DEL (0x7f)          */
1372   {K_EDelete,           "\177"},   /* to make Delete be DEL (0x7f)          */
1373   {K_Insert,            "\033[L"},
1374   {K_EInsert,           "\033[L"},
1375 
1376   /* These are here to map more Alt-X keys to ESC X sequences.  */
1377   {K_Alt_Q,             "\033q"},
1378   {K_Alt_W,             "\033w"},
1379   {K_Alt_E,             "\033e"},
1380   {K_Alt_R,             "\033r"},
1381   {K_Alt_T,             "\033t"},
1382   {K_Alt_Y,             "\033y"},
1383   {K_Alt_U,             "\033u"},
1384   {K_Alt_I,             "\033i"},
1385   {K_Alt_O,             "\033o"},
1386   {K_Alt_P,             "\033p"},
1387   {K_Alt_LBracket,      "\033["},
1388   {K_Alt_RBracket,      "\033]"},
1389   {K_Alt_Return,        "\033\015"},
1390   {K_Alt_A,             "\033a"},
1391   {K_Alt_S,             "\033s"},
1392   {K_Alt_D,             "\033d"},
1393   {K_Alt_F,             "\033f"},
1394   {K_Alt_G,             "\033g"},
1395   {K_Alt_H,             "\033h"},
1396   {K_Alt_J,             "\033j"},
1397   {K_Alt_K,             "\033k"},
1398   {K_Alt_L,             "\033l"},
1399   {K_Alt_Semicolon,     "\033;"},
1400   {K_Alt_Quote,         "\033'"},
1401   {K_Alt_Backquote,     "\033`"},
1402   {K_Alt_Backslash,     "\033\\"},
1403   {K_Alt_Z,             "\033z"},
1404   {K_Alt_X,             "\033x"},
1405   {K_Alt_C,             "\033c"},
1406   {K_Alt_V,             "\033v"},
1407   {K_Alt_B,             "\033b"},
1408   {K_Alt_N,             "\033n"},
1409   {K_Alt_M,             "\033m"},
1410   {K_Alt_Comma,         "\033<"}, /* our reader cannot distinguish between */
1411   {K_Alt_Period,        "\033>"}, /* Alt-. and Alt->, so we cheat a little */
1412   {K_Alt_Slash,         "\033?"}, /* ditto, to get Alt-?                   */
1413   {K_Alt_Backspace,     "\033\177"}, /* M-DEL, to delete word backwards */
1414   {K_Alt_1,             "\033\061"},
1415   {K_Alt_2,             "\033\062"},
1416   {K_Alt_3,             "\033\063"},
1417   {K_Alt_4,             "\033\064"},
1418   {K_Alt_5,             "\033\065"},
1419   {K_Alt_6,             "\033\066"},
1420   {K_Alt_7,             "\033\067"},
1421   {K_Alt_8,             "\033\070"},
1422   {K_Alt_9,             "\033\071"},
1423   {K_Alt_0,             "\033\060"},
1424   {K_Alt_Dash,          "\033\055"},
1425   {K_Alt_EPageUp,       "\033\033[I"},
1426   {K_Alt_EPageDown,     "\033\033[G"},
1427   {K_Alt_Equals,        "\033\075"},
1428   {K_Alt_EDelete,       "\033\177"},
1429   {K_Alt_Tab,           "\033\011"},
1430   {0, 0}
1431 };
1432 
1433 /* Given a key, return the sequence of characters which
1434    our keyboard driver generates.  */
1435 static unsigned const char *
find_sequence(int key)1436 find_sequence (int key)
1437 {
1438   int i;
1439 
1440   for (i = 0; DJGPP_keytab[i].inkey; i++)
1441     if (key == DJGPP_keytab[i].inkey)
1442       return DJGPP_keytab[i].sequence;
1443 
1444   return NULL;
1445 }
1446 
1447 /* Return zero if a key is pending in the
1448    keyboard buffer, non-zero otherwise.  */
1449 static int
kbd_buffer_empty(void)1450 kbd_buffer_empty (void)
1451 {
1452   __dpmi_regs r;
1453   int retval;
1454 
1455   r.h.ah = 0x11;	/* Get enhanced keyboard status */
1456   __dpmi_int (0x16, &r);
1457 
1458   /* If the keyboard buffer is empty, the Zero Flag will be set.  */
1459   return (r.x.flags & 0x40) == 0x40;
1460 }
1461 
1462 /* The buffered characters pending to be read.
1463    Actually, Info usually reads a single character, but when we
1464    translate a key into a sequence of characters, we keep them here.  */
1465 static unsigned char buffered[512];
1466 
1467 /* Index of the next buffered character to be returned.  */
1468 static int buf_idx;
1469 
1470 /* Return the number of characters waiting to be read.  */
1471 long
pc_term_chars_avail(void)1472 pc_term_chars_avail (void)
1473 {
1474   if (buf_idx >= sizeof (buffered)) /* paranoia */
1475     {
1476       buf_idx = 0;
1477       buffered[buf_idx] = '\0';
1478       return 0;
1479     }
1480   else
1481     return strlen (buffered + buf_idx);
1482 }
1483 
1484 /* Our special terminal keyboard reader.  It will be called by
1485    low-level libc functions when the application calls `read' or
1486    the ANSI-standard stream-oriented read functions.  If the
1487    caller wants to read the terminal, we redirect the call to
1488    the BIOS keyboard functions, since that lets us recognize more
1489    keys than DOS does.  */
1490 static int
keyboard_read(__FSEXT_Fnumber func,int * retval,va_list rest_args)1491 keyboard_read (__FSEXT_Fnumber func, int *retval, va_list rest_args)
1492 {
1493   /* When we are called, REST_ARGS are: file_descriptor, buf, nbytes.  */
1494   unsigned char *buf;
1495   size_t nbytes, nread = 0;
1496   int fd = va_arg (rest_args, int);
1497 
1498   /* Is this call for us?  */
1499   if (func != __FSEXT_read || !isatty (fd))
1500     return 0;	/* and the usual DOS call will be issued */
1501 
1502   buf = va_arg (rest_args, unsigned char *);
1503   nbytes = va_arg (rest_args, size_t);
1504 
1505   if (!buf)
1506     {
1507       errno = EINVAL;
1508       *retval = -1;
1509       return 1;
1510     }
1511   if (!nbytes)
1512     {
1513       *retval = 0;
1514       return 1;
1515     }
1516 
1517   /* Loop here until enough bytes has been read.  */
1518   do
1519     {
1520       int key;
1521 
1522       /* If any ``buffered characters'' are left, return as much
1523 	 of them as the caller wanted.  */
1524       while (buffered[buf_idx] && nbytes)
1525 	{
1526 	  *buf++ = buffered[buf_idx++];
1527 	  nread++;
1528 	  nbytes--;
1529 	}
1530 
1531       if (nbytes <= 0)
1532 	break;
1533 
1534       /* Wait for another key.
1535 	 We do that in a busy-waiting loop so we don't get parked
1536 	 inside a BIOS call, which will effectively disable signals.
1537          While we wait for them to type something, we repeatedly
1538          release the rest of our time slice, so that other programs
1539          in a multitasking environment, such as Windows, get more cycles.  */
1540       while (kbd_buffer_empty ())
1541 	__dpmi_yield ();
1542 
1543       key = getxkey ();
1544 
1545       /* Translate the key if necessary.
1546 	 Untranslated non-ASCII keys are silently ignored.  */
1547       if ((key & 0x300) != 0)
1548 	{
1549 	  unsigned char const * key_sequence = find_sequence (key);
1550 
1551 	  if (key_sequence != NULL)
1552 	    {
1553 	      strcpy (buffered, key_sequence);
1554 	      buf_idx = 0;
1555 	    }
1556 	}
1557       else if (key == K_Control_Z)
1558 	raise (SIGUSR1);	/* we don't have SIGTSTP, so simulate it */
1559       else if (key <= 0xff)
1560 	{
1561 	  *buf++ = key;
1562 	  nbytes--;
1563 	  nread++;
1564 	}
1565     }
1566   while (nbytes > 0);
1567 
1568   *retval = nread;
1569   return 1;	/* meaning that we handled the call */
1570 }
1571 
1572 /* Install our keyboard handler.
1573    This is called by the startup code before `main'.  */
1574 static void __attribute__((constructor))
install_keyboard_handler(void)1575 install_keyboard_handler (void)
1576 {
1577   __FSEXT_set_function (fileno (stdin), keyboard_read);
1578 
1579   /* We need to set this single hook here; the rest
1580      will be set by pc_initialize_terminal when it is called.  */
1581   terminal_initialize_terminal_hook = pc_initialize_terminal;
1582 }
1583 
1584 #endif /* __DJGPP__ */
1585 
1586 /* **************************************************************** */
1587 /*                                                                  */
1588 /*                Emulation of SIGTSTP on Ctrl-Z                    */
1589 /*                                                                  */
1590 /* **************************************************************** */
1591 
1592 #include <limits.h>
1593 #include "signals.h"
1594 
1595 #ifndef PATH_MAX
1596 # define PATH_MAX 512
1597 #endif
1598 
1599 /* Effectively disable signals which aren't defined
1600    (assuming no signal can ever be zero).
1601    SIGINT is ANSI, so we expect it to be always defined.  */
1602 #ifndef SIGUSR1
1603 # define SIGUSR1 0
1604 #endif
1605 #ifndef SIGQUIT
1606 # define SIGQUIT 0
1607 #endif
1608 
1609 int
kill(pid_t pid,int sig)1610 kill (pid_t pid, int sig)
1611 {
1612   static char interrupted_msg[] = "Interrupted\r\n";
1613   static char stopped_msg[] = "Stopped.  Type 'exit RET' to return.\r\n";
1614   char cwd[PATH_MAX + 1];
1615 
1616   if (pid == getpid ()
1617       || pid == 0
1618       || pid == -1
1619       || pid == -getpid ())
1620     {
1621       switch (sig)
1622 	{
1623 	  void (*old_INT)(int), (*old_QUIT)(int);
1624 
1625 	case SIGINT:
1626 #ifdef __DJGPP__
1627 	  /* If SIGINT was generated by a readable key, we want to remove
1628 	     it from the PC keyboard buffer, so that DOS and other
1629 	     programs never see it.  DJGPP signal-handling mechanism
1630 	     doesn't remove the INT key from the keyboard buffer.  */
1631 	  if (!kbd_buffer_empty ())
1632 	    getxkey ();
1633 #endif
1634 	  pc_write_chars (interrupted_msg, sizeof (interrupted_msg) - 1);
1635 	  exit (EXIT_FAILURE);
1636 	case SIGUSR1:
1637 	  /* Simulate SIGTSTP by invoking a subsidiary shell.  */
1638 #ifndef _WIN32
1639 	  pc_goto_xy (0, outside_info.screenheight - 1);
1640 	  pc_clear_to_eol ();
1641 	  pc_write_chars (stopped_msg, sizeof (stopped_msg) - 1);
1642 #endif
1643 
1644 	  /* The child shell can change the working directory, so
1645 	     we need to save and restore it, since it is global.  */
1646 	  if (!getcwd (cwd, PATH_MAX)) /* should never happen */
1647 	    cwd[0] = '\0';
1648 
1649 	  /* We don't want to get fatal signals while the subshell runs.  */
1650 	  old_INT = signal (SIGINT, SIG_IGN);
1651 	  old_QUIT = signal (SIGQUIT, SIG_IGN);
1652 #ifdef _WIN32
1653 	  {
1654 	    const char *argv[2];
1655 	    const char *shell = NULL;
1656 
1657 	    argv[0] = NULL;
1658 	    shell = getenv ("SHELL");
1659 	    if (!shell)
1660 	      {
1661 		shell = getenv ("COMSPEC");
1662 		if (!shell)
1663 		  return -1;
1664 		argv[0] = " /k";
1665 	      }
1666 	    argv[1] = NULL;
1667 	    _spawnvp (_P_WAIT, shell, argv);
1668 	  }
1669 #else
1670 	  system ("");
1671 #endif
1672 	  if (*cwd)
1673 	    chdir (cwd);
1674 	  signal (SIGINT, old_INT);
1675 	  signal (SIGQUIT, old_QUIT);
1676 	  break;
1677 	default:
1678 	  if (sig)
1679 	    raise (sig);
1680 	  break;
1681 	}
1682       return 0;
1683     }
1684   else
1685     return -1;
1686 }
1687 
1688 /* These should never be called, but they make the linker happy.  */
1689 
tputs(const char * a,int b,int (* c)(int))1690 int       tputs (const char *a, int b, int (*c)(int))
1691 {
1692   perror ("tputs"); return 0; /* here and below, added dummy retvals */
1693 }
1694 
tgoto(const char * a,int b,int c)1695 char*     tgoto (const char *a, int b, int c)
1696 {
1697   perror ("tgoto"); return 0;
1698 }
1699 
tgetnum(char * a)1700 int       tgetnum (char*a)
1701 {
1702   perror ("tgetnum"); return 0;
1703 }
1704 
tgetflag(char * a)1705 int       tgetflag (char*a)
1706 {
1707   perror ("tgetflag"); return 0;
1708 }
1709 
tgetstr(char * a,char ** b)1710 char*     tgetstr (char *a, char **b)
1711 {
1712   perror ("tgetstr"); return 0;
1713 }
1714 
tgetent(char * a,const char * b)1715 int       tgetent (char *a, const char *b)
1716 {
1717   perror ("tgetent"); return 0;
1718 }
1719 
tcgetattr(int fildes,struct termios * termios_p)1720 int	tcgetattr(int fildes, struct termios *termios_p)
1721 {
1722   perror ("tcgetattr"); return 0;
1723 }
1724 
tcsetattr(int fd,int opt_actions,const struct termios * termios_p)1725 int	tcsetattr(int fd, int opt_actions, const struct termios *termios_p)
1726 {
1727   perror ("tcsetattr"); return 0;
1728 }
1729