(root)/
texinfo-7.1/
info/
pcterm.c
       1  /* pcterm.c -- How to handle the PC terminal for Info under MS-DOS/MS-Windows.
       2  
       3     Copyright 1998-2023 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
     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
     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
     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
     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
     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
     236  textattr (int attr)
     237  {
     238    if (hscreen != INVALID_HANDLE_VALUE)
     239      SetConsoleTextAttribute (hscreen, attr);
     240  }
     241  
     242  void
     243  textmode (int mode)
     244  {
     245    /* Nothing.  */
     246  }
     247  
     248  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
     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
     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
     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
     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
     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
     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
     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
     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
     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
     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
     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
     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
     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
     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
     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
     509  sleep (unsigned sec)
     510  {
     511    Sleep (sec*1000);
     512    return 0;
     513  }
     514  
     515  /* Keyboard input support.  */
     516  
     517  static int
     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
     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
     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
     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
     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
     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 *
     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
     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
     861  pc_begin_inverse (void)
     862  {
     863    textattr (inv_attr);
     864  }
     865  
     866  /* Turn off reverse video. */
     867  static 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
     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
     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
     896  pc_begin_standout (void)
     897  {
     898    highvideo ();
     899  }
     900  
     901  static void
     902  pc_end_standout (void)
     903  {
     904    normvideo ();
     905  }
     906  
     907  static void
     908  pc_begin_blink (void)
     909  {
     910    blinkvideo ();
     911  }
     912  
     913  static 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
     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
     942  pc_set_fg_color (int color)
     943  {
     944    textcolor (convert_color (color) | (norm_attr & FOREGROUND_INTENSITY));
     945  }
     946  
     947  static void
     948  pc_set_bg_color (int color)
     949  {
     950    textbackground (convert_color (color) | (norm_attr & BACKGROUND_INTENSITY));
     951  }
     952  
     953  #ifdef MAX
     954  #undef MAX
     955  #endif
     956  #define MAX(a,b) ((a) > (b) ? (a) : (b))
     957  #ifdef MIN
     958  #undef MIN
     959  #endif
     960  #define MIN(a,b) ((a) < (b) ? (a) : (b))
     961  
     962  /* Move the cursor up one line. */
     963  static void
     964  pc_up_line (void)
     965  {
     966    int x, y;
     967    ScreenGetCursor (&y, &x);
     968    ScreenSetCursor (MAX (y-1, 0), x);
     969  }
     970  
     971  /* Move the cursor down one line. */
     972  static void
     973  pc_down_line (void)
     974  {
     975    int x, y;
     976    ScreenGetCursor (&y, &x);
     977    ScreenSetCursor (MIN (screenheight-1, y+1), x);
     978  }
     979  
     980  /* Clear the entire terminal screen. */
     981  static void
     982  pc_clear_screen (void)
     983  {
     984    ScreenClear ();
     985  }
     986  
     987  /* Clear from the current position of the cursor to the end of the line. */
     988  static void
     989  pc_clear_to_eol (void)
     990  {
     991    clreol (); /* perhaps to be replaced by a loop */
     992  }
     993  
     994  /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */
     995  static void
     996  pc_get_screen_size(void)
     997  {
     998    /* Current screen dimensions are the default.  */
     999    if (!outside_info.screenheight)	/* paranoia */
    1000      gettextinfo (&outside_info);
    1001    screenwidth  = outside_info.screenwidth;
    1002    screenheight = outside_info.screenheight;
    1003  
    1004    /* Environment variable "LINES" overrides the default.  */
    1005    if (getenv ("LINES") != NULL)
    1006      screenheight = atoi (getenv ("LINES"));
    1007  
    1008    /* Environment variable "INFO_LINES" overrides "LINES".  */
    1009    if (getenv ("INFO_LINES") != NULL)
    1010      screenheight = atoi (getenv ("INFO_LINES"));
    1011  }
    1012  
    1013  /* Move the cursor to the terminal location of X and Y. */
    1014  static void
    1015  pc_goto_xy (x, y)
    1016       int x, y;
    1017  {
    1018    ScreenSetCursor (y, x); /* yes, pc.h says ScreenSetCursor (row, column) !! */
    1019  }
    1020  
    1021  /* Print STRING to the terminal at the current position. */
    1022  static void
    1023  pc_put_text (string)
    1024       char *string;
    1025  {
    1026    if (speech_friendly)
    1027      fputs (string, stdout);
    1028  #ifdef __MINGW32__
    1029    else if (hscreen == INVALID_HANDLE_VALUE)
    1030      fputs (string, stdout);
    1031    else if (output_cp == CP_UTF8 || output_cp == CP_UTF7)
    1032      write_utf (output_cp, string, -1);
    1033  #endif
    1034    else
    1035      cputs (string);
    1036  }
    1037  
    1038  /* Ring the terminal bell.  The bell is rung visibly if the terminal is
    1039     capable of doing that, and if terminal_use_visible_bell_p is non-zero. */
    1040  static void
    1041  pc_ring_bell(void)
    1042  {
    1043    if (terminal_has_visible_bell_p && terminal_use_visible_bell_p)
    1044      ScreenVisualBell ();
    1045    else
    1046      {
    1047        printf ("%c",'\a');
    1048        fflush (stdout);
    1049      }
    1050  }
    1051  
    1052  /* Print NCHARS from STRING to the terminal at the current position. */
    1053  static void
    1054  pc_write_chars (string, nchars)
    1055      char *string;
    1056      int nchars;
    1057  {
    1058    if (!nchars)
    1059      return;
    1060  
    1061    if (speech_friendly)
    1062      printf ("%.*s", nchars, string);
    1063  #ifdef __MINGW32__
    1064    else if (hscreen == INVALID_HANDLE_VALUE)
    1065      printf ("%.*s", nchars, string);
    1066    else if (output_cp == CP_UTF8 || output_cp == CP_UTF7)
    1067      write_utf (output_cp, string, nchars);
    1068  #endif
    1069    else
    1070      cprintf ("%.*s", nchars, string);
    1071  }
    1072  
    1073  /* Scroll an area of the terminal from START to (and excluding) END,
    1074     AMOUNT lines.  If AMOUNT is negative, the lines are scrolled
    1075     towards the top of the screen, else they are scrolled towards the
    1076     bottom of the screen.  The lines of the old region which do not
    1077     overlap the new region are cleared, to mimic terminal operation.  */
    1078  static void
    1079  pc_scroll_terminal (start, end, amount)
    1080      int start, end, amount;
    1081  {
    1082    int line_to_clear = amount > 0 ? start : end + amount;
    1083  
    1084    /* Move the text.  Note that `movetext' expects 1-based coordinates.  */
    1085    movetext (1, start + 1, ScreenCols (), end, 1, start + amount + 1);
    1086  
    1087    /* Now clear the lines which were left unoccupied.  */
    1088    if (amount < 0)
    1089      amount = -amount;
    1090    while (amount--)
    1091      {
    1092        ScreenSetCursor (line_to_clear++, 0);
    1093        clreol ();
    1094      }
    1095  }
    1096  
    1097  /* Put the screen in the video mode and colors which Info will use.
    1098     Prepare to start using the terminal to read characters singly.  */
    1099  static void
    1100  pc_prep_terminal (void)
    1101  {
    1102    int tty;
    1103  
    1104  #ifdef _WIN32
    1105    w32_info_prep ();
    1106  #endif
    1107  
    1108    /* Do not set screen height if we already have it, because
    1109       doing so erases the screen.  */
    1110    if (screenheight != ScreenRows ())
    1111      _set_screen_lines (screenheight);
    1112  
    1113    /* Don't fail if they asked for screen dimensions that their
    1114       hardware cannot support.  */
    1115    screenheight = ScreenRows ();
    1116    screenwidth  = ScreenCols ();
    1117  
    1118    /* Try setting the colors user asked for.  */
    1119    textattr (norm_attr);
    1120    ScreenClear ();
    1121  
    1122    /* Switch console reads to binary mode.  */
    1123    tty = fileno (stdin);
    1124  #ifdef __DJGPP__
    1125    setmode (tty, O_BINARY);
    1126    __djgpp_set_ctrl_c (1);	/* re-enable SIGINT generation by Ctrl-C */
    1127  #endif
    1128  }
    1129  
    1130  /* Restore the tty settings back to what they were before we started using
    1131     this terminal. */
    1132  static void
    1133  pc_unprep_terminal (void)
    1134  {
    1135    int tty;
    1136  
    1137  #ifdef _WIN32
    1138    w32_info_unprep ();
    1139  #endif
    1140  
    1141    textattr (outside_info.normattr);
    1142  
    1143    /* Do not set screen height if we already have it, because
    1144       doing so erases the screen.  */
    1145    if (outside_info.screenheight != ScreenRows ())
    1146      {
    1147        _set_screen_lines (outside_info.screenheight);
    1148        textmode (LASTMODE);
    1149      }
    1150  #ifdef __MSDOS__
    1151    else
    1152      pc_clear_to_eol ();	/* for text attributes to really take effect */
    1153  #endif
    1154  #ifdef _WIN32
    1155    if (hscreen != INVALID_HANDLE_VALUE)
    1156      SetConsoleScreenBufferSize (hstdout, outside_info.bufsize);
    1157  #endif
    1158  
    1159    /* Switch back to text mode on stdin.  */
    1160    tty = fileno (stdin);
    1161  #ifdef __DJGPP__
    1162    setmode (tty, O_TEXT);
    1163  #endif
    1164  }
    1165  
    1166  /* Initialize the terminal which is known as TERMINAL_NAME.  If this
    1167     terminal doesn't have cursor addressability, `terminal_is_dumb_p'
    1168     becomes nonzero.  The variables SCREENHEIGHT and SCREENWIDTH are set
    1169     to the dimensions that this terminal actually has.  Finally, the
    1170     terminal screen is cleared. */
    1171  static void
    1172  pc_initialize_terminal (term_name)
    1173      char *term_name;
    1174  {
    1175    char *info_colors;
    1176  
    1177    if (!term_name)
    1178      {
    1179        term_name = getenv ("TERM");
    1180        if (!term_name)
    1181  #ifdef __MSDOS__
    1182  	term_name = "pc-dos";	/* ``what's in a name?'' */
    1183  #endif
    1184  #ifdef _WIN32
    1185  	term_name = "w32console";
    1186  #endif
    1187      }
    1188  
    1189    /* Get current video information, to be restored later.  */
    1190    if (outside_info.screenwidth == 0)
    1191      gettextinfo (&outside_info);
    1192  
    1193    /* Current screen colors are the default.  */
    1194    norm_attr    = outside_info.normattr;
    1195    inv_attr     = (((outside_info.normattr &    7) << 4) |
    1196                    ((outside_info.normattr & 0x7f) >> 4));
    1197  #ifdef __MSDOS__
    1198    xref_attr = CYAN;
    1199  #endif
    1200  #ifdef _WIN32
    1201    xref_attr = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY;
    1202  #endif
    1203    xref_attr |= outside_info.normattr & 0xf0;
    1204  
    1205    /* Does the user want non-default colors?  */
    1206    info_colors = getenv ("INFO_COLORS");
    1207    if ((info_colors != (char *)0) && !speech_friendly)
    1208      {
    1209        /* Decode a color from a string descriptor.
    1210  	 The descriptor string is a sequence of color specifiers separated
    1211  	 by a non-numeric character.  Each color specifier should represent
    1212  	 a small integer which fits into an unsigned char, and can be given
    1213  	 in any base supported by strtoul.  Examples of valid descriptors:
    1214  
    1215  		"10 31"
    1216  		"0x13/0x45"
    1217  		"007.077"
    1218  
    1219  	The separator between two color specifiers can be any character which
    1220  	cannot be used in a printed representation of an integer number.  */
    1221        char *endp;
    1222        unsigned long color_desc = strtoul (info_colors, &endp, 0);
    1223  
    1224        if (color_desc <= UCHAR_MAX)
    1225  	{
    1226  	  norm_attr = (unsigned char)color_desc;
    1227  	  xref_attr = (xref_attr & 0x0f) | (norm_attr & 0xf0);
    1228  	  color_desc = strtoul (endp + 1, &endp, 0);
    1229  	  if (color_desc <= UCHAR_MAX)
    1230  	    inv_attr = (unsigned char)color_desc;
    1231  #ifdef _WIN32
    1232  	  if (*endp == 'u')
    1233  	    xref_attr = COMMON_LVB_UNDERSCORE;
    1234  	  else
    1235  #endif
    1236  	  if (*endp != '\0')
    1237  	    {
    1238  	      color_desc = strtoul (endp + 1, &endp, 0);
    1239  	      if (color_desc <= UCHAR_MAX)
    1240  		{
    1241  #ifdef _WIN32
    1242  		  if (*endp == 'u')
    1243  		    color_desc |= COMMON_LVB_UNDERSCORE;
    1244  		  xref_attr = (WORD)color_desc;
    1245  #else
    1246  		  xref_attr = (unsigned char)color_desc;
    1247  #endif
    1248  		}
    1249  	    }
    1250  	}
    1251      }
    1252  
    1253    /* We can scroll.  */
    1254    terminal_can_scroll = 1;
    1255  
    1256    /* We know how to produce a visible bell, if somebody's looking...  */
    1257    if (!speech_friendly)
    1258      terminal_has_visible_bell_p = 1;
    1259  
    1260    /* We are *certainly* NOT dumb!  */
    1261    terminal_is_dumb_p = 0;
    1262  
    1263    pc_get_screen_size ();
    1264  
    1265  #ifdef __MINGW32__
    1266    /* Record the screen output codepage.  */
    1267    output_cp = GetConsoleOutputCP ();
    1268  #endif
    1269  
    1270  #ifdef __MSDOS__
    1271    /* Store the arrow keys.  */
    1272    term_ku = (char *)find_sequence (K_Up);
    1273    term_kd = (char *)find_sequence (K_Down);
    1274    term_kr = (char *)find_sequence (K_Right);
    1275    term_kl = (char *)find_sequence (K_Left);
    1276  
    1277    term_kP = (char *)find_sequence (K_PageUp);
    1278    term_kN = (char *)find_sequence (K_PageDown);
    1279  
    1280    term_kh = (char *)find_sequence (K_Home);
    1281    term_ke = (char *)find_sequence (K_End);
    1282    term_ki = (char *)find_sequence (K_Insert);
    1283    term_kD = (char *)find_sequence (K_Delete);
    1284  #elif defined _WIN32
    1285    term_kh = "\033<";
    1286    term_ke = "\033>";
    1287    term_ki = "\033[L";
    1288  #endif	/* __MSDOS__ */
    1289  
    1290    /* Set all the hooks to our PC-specific functions.  */
    1291    terminal_begin_inverse_hook       = pc_begin_inverse;
    1292    terminal_end_inverse_hook         = pc_end_inverse;
    1293    terminal_begin_standout_hook      = pc_begin_standout;
    1294    terminal_end_standout_hook        = pc_end_standout;
    1295    terminal_begin_underline_hook     = pc_begin_underline;
    1296    terminal_end_underline_hook       = pc_end_underline;
    1297    terminal_begin_bold_hook          = pc_begin_standout;
    1298    terminal_begin_blink_hook         = pc_begin_blink;
    1299    terminal_end_all_modes_hook       = pc_default_color;
    1300    terminal_default_colour_hook      = pc_default_color;
    1301    terminal_set_colour_hook          = pc_set_fg_color;
    1302    terminal_set_bgcolour_hook        = pc_set_bg_color;
    1303    terminal_prep_terminal_hook       = pc_prep_terminal;
    1304    terminal_unprep_terminal_hook     = pc_unprep_terminal;
    1305    terminal_up_line_hook             = pc_up_line;
    1306    terminal_down_line_hook           = pc_down_line;
    1307    terminal_clear_screen_hook        = pc_clear_screen;
    1308    terminal_clear_to_eol_hook        = pc_clear_to_eol;
    1309    terminal_get_screen_size_hook     = pc_get_screen_size;
    1310    terminal_goto_xy_hook             = pc_goto_xy;
    1311    terminal_put_text_hook            = pc_put_text;
    1312    terminal_ring_bell_hook           = pc_ring_bell;
    1313    terminal_write_chars_hook         = pc_write_chars;
    1314    terminal_scroll_terminal_hook     = pc_scroll_terminal;
    1315  }
    1316  
    1317  /* **************************************************************** */
    1318  /*                                                                  */
    1319  /*            How to Read Characters From the PC Terminal           */
    1320  /*                                                                  */
    1321  /* **************************************************************** */
    1322  
    1323  /* This will most certainly work ONLY with DJGPP.  */
    1324  #ifdef __DJGPP__
    1325  
    1326  #include <errno.h>
    1327  #include <sys/fsext.h>
    1328  #include <dpmi.h>
    1329  
    1330  /* Translation table for some special keys.
    1331     Arrow keys which are standard on other keyboards are translated into
    1332     standard ESC-sequences, in case somebody rebinds the simple keys
    1333     (like C-f, C-b, C-n, etc.).
    1334  
    1335     The strange "\033\061" prefix in some keys is a numeric argument of
    1336     one, which means ``do the next command once''.  It is here so that
    1337     when the according PC key is pressed in the middle of an incremental
    1338     search, Info doesn't see just an ASCII character like `n' or `B',
    1339     and doesn't add it to the search string; instead, it will exit the
    1340     incremental search and then perform the command.  */
    1341  static struct
    1342  {
    1343    int inkey;
    1344    unsigned char const * const sequence;
    1345  } DJGPP_keytab[] = {		   /* these are for moving between nodes... */
    1346    {K_Control_PageDown,  "\033\061n"},
    1347    {K_Control_PageUp,    "\033\061p"},
    1348    {K_Control_Up,        "\033\061u"},
    1349    {K_Control_Down,      "\033\061m"},
    1350    {K_Control_Center,    "\033\061l"},
    1351  
    1352    {K_Home,              "\033[H"}, /* ...and these are for moving IN a node */
    1353    {K_End,               "\033[F"}, /* they're Numeric-Keypad-Keys, so       */
    1354    {K_Left,              "\033[D"}, /* NUMLOCK should be off !!              */
    1355    {K_Right,             "\033[C"},
    1356    {K_Down,              "\033[B"},
    1357    {K_Up,                "\033[A"},
    1358    {K_PageDown,          "\033[G"},
    1359    {K_PageUp,            "\033[I"},
    1360    {K_Control_Left,      "\033b"},
    1361    {K_Control_Right,     "\033f"},
    1362    {K_Control_Home,      "\033<"},
    1363    {K_Control_End,       "\033>"},
    1364  
    1365    {K_EHome,             "\033[H"}, /* these are also for moving IN a node */
    1366    {K_EEnd,              "\033[F"}, /* they're the "extended" (Grey) keys  */
    1367    {K_ELeft,             "\033[D"},
    1368    {K_ERight,            "\033[C"},
    1369    {K_EDown,             "\033[B"},
    1370    {K_EUp,               "\033[A"},
    1371    {K_EPageDown,         "\033[G"},
    1372    {K_EPageUp,           "\033[I"},
    1373    {K_Control_ELeft,     "\033b"},
    1374    {K_Control_ERight,    "\033f"},
    1375    {K_Control_EHome,     "\033<"},
    1376    {K_Control_EEnd,      "\033>"},
    1377  
    1378    {K_BackTab,           "\033\011"},
    1379    {K_F1,                "\10"},    /* YEAH, gimme that good old F-one-thing */
    1380    {K_Delete,            "\177"},   /* to make Kp-Del be DEL (0x7f)          */
    1381    {K_EDelete,           "\177"},   /* to make Delete be DEL (0x7f)          */
    1382    {K_Insert,            "\033[L"},
    1383    {K_EInsert,           "\033[L"},
    1384  
    1385    /* These are here to map more Alt-X keys to ESC X sequences.  */
    1386    {K_Alt_Q,             "\033q"},
    1387    {K_Alt_W,             "\033w"},
    1388    {K_Alt_E,             "\033e"},
    1389    {K_Alt_R,             "\033r"},
    1390    {K_Alt_T,             "\033t"},
    1391    {K_Alt_Y,             "\033y"},
    1392    {K_Alt_U,             "\033u"},
    1393    {K_Alt_I,             "\033i"},
    1394    {K_Alt_O,             "\033o"},
    1395    {K_Alt_P,             "\033p"},
    1396    {K_Alt_LBracket,      "\033["},
    1397    {K_Alt_RBracket,      "\033]"},
    1398    {K_Alt_Return,        "\033\015"},
    1399    {K_Alt_A,             "\033a"},
    1400    {K_Alt_S,             "\033s"},
    1401    {K_Alt_D,             "\033d"},
    1402    {K_Alt_F,             "\033f"},
    1403    {K_Alt_G,             "\033g"},
    1404    {K_Alt_H,             "\033h"},
    1405    {K_Alt_J,             "\033j"},
    1406    {K_Alt_K,             "\033k"},
    1407    {K_Alt_L,             "\033l"},
    1408    {K_Alt_Semicolon,     "\033;"},
    1409    {K_Alt_Quote,         "\033'"},
    1410    {K_Alt_Backquote,     "\033`"},
    1411    {K_Alt_Backslash,     "\033\\"},
    1412    {K_Alt_Z,             "\033z"},
    1413    {K_Alt_X,             "\033x"},
    1414    {K_Alt_C,             "\033c"},
    1415    {K_Alt_V,             "\033v"},
    1416    {K_Alt_B,             "\033b"},
    1417    {K_Alt_N,             "\033n"},
    1418    {K_Alt_M,             "\033m"},
    1419    {K_Alt_Comma,         "\033<"}, /* our reader cannot distinguish between */
    1420    {K_Alt_Period,        "\033>"}, /* Alt-. and Alt->, so we cheat a little */
    1421    {K_Alt_Slash,         "\033?"}, /* ditto, to get Alt-?                   */
    1422    {K_Alt_Backspace,     "\033\177"}, /* M-DEL, to delete word backwards */
    1423    {K_Alt_1,             "\033\061"},
    1424    {K_Alt_2,             "\033\062"},
    1425    {K_Alt_3,             "\033\063"},
    1426    {K_Alt_4,             "\033\064"},
    1427    {K_Alt_5,             "\033\065"},
    1428    {K_Alt_6,             "\033\066"},
    1429    {K_Alt_7,             "\033\067"},
    1430    {K_Alt_8,             "\033\070"},
    1431    {K_Alt_9,             "\033\071"},
    1432    {K_Alt_0,             "\033\060"},
    1433    {K_Alt_Dash,          "\033\055"},
    1434    {K_Alt_EPageUp,       "\033\033[I"},
    1435    {K_Alt_EPageDown,     "\033\033[G"},
    1436    {K_Alt_Equals,        "\033\075"},
    1437    {K_Alt_EDelete,       "\033\177"},
    1438    {K_Alt_Tab,           "\033\011"},
    1439    {0, 0}
    1440  };
    1441  
    1442  /* Given a key, return the sequence of characters which
    1443     our keyboard driver generates.  */
    1444  static unsigned const char *
    1445  find_sequence (int key)
    1446  {
    1447    int i;
    1448  
    1449    for (i = 0; DJGPP_keytab[i].inkey; i++)
    1450      if (key == DJGPP_keytab[i].inkey)
    1451        return DJGPP_keytab[i].sequence;
    1452  
    1453    return NULL;
    1454  }
    1455  
    1456  /* Return zero if a key is pending in the
    1457     keyboard buffer, non-zero otherwise.  */
    1458  static int
    1459  kbd_buffer_empty (void)
    1460  {
    1461    __dpmi_regs r;
    1462    int retval;
    1463  
    1464    r.h.ah = 0x11;	/* Get enhanced keyboard status */
    1465    __dpmi_int (0x16, &r);
    1466  
    1467    /* If the keyboard buffer is empty, the Zero Flag will be set.  */
    1468    return (r.x.flags & 0x40) == 0x40;
    1469  }
    1470  
    1471  /* The buffered characters pending to be read.
    1472     Actually, Info usually reads a single character, but when we
    1473     translate a key into a sequence of characters, we keep them here.  */
    1474  static unsigned char buffered[512];
    1475  
    1476  /* Index of the next buffered character to be returned.  */
    1477  static int buf_idx;
    1478  
    1479  /* Return the number of characters waiting to be read.  */
    1480  long
    1481  pc_term_chars_avail (void)
    1482  {
    1483    if (buf_idx >= sizeof (buffered)) /* paranoia */
    1484      {
    1485        buf_idx = 0;
    1486        buffered[buf_idx] = '\0';
    1487        return 0;
    1488      }
    1489    else
    1490      return strlen (buffered + buf_idx);
    1491  }
    1492  
    1493  /* Our special terminal keyboard reader.  It will be called by
    1494     low-level libc functions when the application calls `read' or
    1495     the ANSI-standard stream-oriented read functions.  If the
    1496     caller wants to read the terminal, we redirect the call to
    1497     the BIOS keyboard functions, since that lets us recognize more
    1498     keys than DOS does.  */
    1499  static int
    1500  keyboard_read (__FSEXT_Fnumber func, int *retval, va_list rest_args)
    1501  {
    1502    /* When we are called, REST_ARGS are: file_descriptor, buf, nbytes.  */
    1503    unsigned char *buf;
    1504    size_t nbytes, nread = 0;
    1505    int fd = va_arg (rest_args, int);
    1506  
    1507    /* Is this call for us?  */
    1508    if (func != __FSEXT_read || !isatty (fd))
    1509      return 0;	/* and the usual DOS call will be issued */
    1510  
    1511    buf = va_arg (rest_args, unsigned char *);
    1512    nbytes = va_arg (rest_args, size_t);
    1513  
    1514    if (!buf)
    1515      {
    1516        errno = EINVAL;
    1517        *retval = -1;
    1518        return 1;
    1519      }
    1520    if (!nbytes)
    1521      {
    1522        *retval = 0;
    1523        return 1;
    1524      }
    1525  
    1526    /* Loop here until enough bytes has been read.  */
    1527    do
    1528      {
    1529        int key;
    1530  
    1531        /* If any ``buffered characters'' are left, return as much
    1532  	 of them as the caller wanted.  */
    1533        while (buffered[buf_idx] && nbytes)
    1534  	{
    1535  	  *buf++ = buffered[buf_idx++];
    1536  	  nread++;
    1537  	  nbytes--;
    1538  	}
    1539  
    1540        if (nbytes <= 0)
    1541  	break;
    1542  
    1543        /* Wait for another key.
    1544  	 We do that in a busy-waiting loop so we don't get parked
    1545  	 inside a BIOS call, which will effectively disable signals.
    1546           While we wait for them to type something, we repeatedly
    1547           release the rest of our time slice, so that other programs
    1548           in a multitasking environment, such as Windows, get more cycles.  */
    1549        while (kbd_buffer_empty ())
    1550  	__dpmi_yield ();
    1551  
    1552        key = getxkey ();
    1553  
    1554        /* Translate the key if necessary.
    1555  	 Untranslated non-ASCII keys are silently ignored.  */
    1556        if ((key & 0x300) != 0)
    1557  	{
    1558  	  unsigned char const * key_sequence = find_sequence (key);
    1559  
    1560  	  if (key_sequence != NULL)
    1561  	    {
    1562  	      strcpy (buffered, key_sequence);
    1563  	      buf_idx = 0;
    1564  	    }
    1565  	}
    1566        else if (key == K_Control_Z)
    1567  	raise (SIGUSR1);	/* we don't have SIGTSTP, so simulate it */
    1568        else if (key <= 0xff)
    1569  	{
    1570  	  *buf++ = key;
    1571  	  nbytes--;
    1572  	  nread++;
    1573  	}
    1574      }
    1575    while (nbytes > 0);
    1576  
    1577    *retval = nread;
    1578    return 1;	/* meaning that we handled the call */
    1579  }
    1580  
    1581  /* Install our keyboard handler.
    1582     This is called by the startup code before `main'.  */
    1583  static void __attribute__((constructor))
    1584  install_keyboard_handler (void)
    1585  {
    1586    __FSEXT_set_function (fileno (stdin), keyboard_read);
    1587  
    1588    /* We need to set this single hook here; the rest
    1589       will be set by pc_initialize_terminal when it is called.  */
    1590    terminal_initialize_terminal_hook = pc_initialize_terminal;
    1591  }
    1592  
    1593  #endif /* __DJGPP__ */
    1594  
    1595  /* **************************************************************** */
    1596  /*                                                                  */
    1597  /*                Emulation of SIGTSTP on Ctrl-Z                    */
    1598  /*                                                                  */
    1599  /* **************************************************************** */
    1600  
    1601  #include <limits.h>
    1602  #include "signals.h"
    1603  
    1604  #ifndef PATH_MAX
    1605  # define PATH_MAX 512
    1606  #endif
    1607  
    1608  /* Effectively disable signals which aren't defined
    1609     (assuming no signal can ever be zero).
    1610     SIGINT is ANSI, so we expect it to be always defined.  */
    1611  #ifndef SIGUSR1
    1612  # define SIGUSR1 0
    1613  #endif
    1614  #ifndef SIGQUIT
    1615  # define SIGQUIT 0
    1616  #endif
    1617  
    1618  int
    1619  kill (pid_t pid, int sig)
    1620  {
    1621    static char interrupted_msg[] = "Interrupted\r\n";
    1622    static char stopped_msg[] = "Stopped.  Type 'exit RET' to return.\r\n";
    1623    char cwd[PATH_MAX + 1];
    1624  
    1625    if (pid == getpid ()
    1626        || pid == 0
    1627        || pid == -1
    1628        || pid == -getpid ())
    1629      {
    1630        switch (sig)
    1631  	{
    1632  	  void (*old_INT)(int), (*old_QUIT)(int);
    1633  
    1634  	case SIGINT:
    1635  #ifdef __DJGPP__
    1636  	  /* If SIGINT was generated by a readable key, we want to remove
    1637  	     it from the PC keyboard buffer, so that DOS and other
    1638  	     programs never see it.  DJGPP signal-handling mechanism
    1639  	     doesn't remove the INT key from the keyboard buffer.  */
    1640  	  if (!kbd_buffer_empty ())
    1641  	    getxkey ();
    1642  #endif
    1643  	  pc_write_chars (interrupted_msg, sizeof (interrupted_msg) - 1);
    1644  	  exit (EXIT_FAILURE);
    1645  	case SIGUSR1:
    1646  	  /* Simulate SIGTSTP by invoking a subsidiary shell.  */
    1647  #ifndef _WIN32
    1648  	  pc_goto_xy (0, outside_info.screenheight - 1);
    1649  	  pc_clear_to_eol ();
    1650  	  pc_write_chars (stopped_msg, sizeof (stopped_msg) - 1);
    1651  #endif
    1652  
    1653  	  /* The child shell can change the working directory, so
    1654  	     we need to save and restore it, since it is global.  */
    1655  	  if (!getcwd (cwd, PATH_MAX)) /* should never happen */
    1656  	    cwd[0] = '\0';
    1657  
    1658  	  /* We don't want to get fatal signals while the subshell runs.  */
    1659  	  old_INT = signal (SIGINT, SIG_IGN);
    1660  	  old_QUIT = signal (SIGQUIT, SIG_IGN);
    1661  #ifdef _WIN32
    1662  	  {
    1663  	    const char *argv[2];
    1664  	    const char *shell = NULL;
    1665  
    1666  	    argv[0] = NULL;
    1667  	    shell = getenv ("SHELL");
    1668  	    if (!shell)
    1669  	      {
    1670  		shell = getenv ("COMSPEC");
    1671  		if (!shell)
    1672  		  return -1;
    1673  		argv[0] = " /k";
    1674  	      }
    1675  	    argv[1] = NULL;
    1676  	    _spawnvp (_P_WAIT, shell, argv);
    1677  	  }
    1678  #else
    1679  	  system ("");
    1680  #endif
    1681  	  if (*cwd)
    1682  	    chdir (cwd);
    1683  	  signal (SIGINT, old_INT);
    1684  	  signal (SIGQUIT, old_QUIT);
    1685  	  break;
    1686  	default:
    1687  	  if (sig)
    1688  	    raise (sig);
    1689  	  break;
    1690  	}
    1691        return 0;
    1692      }
    1693    else
    1694      return -1;
    1695  }
    1696  
    1697  /* These should never be called, but they make the linker happy.  */
    1698  
    1699  int       tputs (const char *a, int b, int (*c)(int))
    1700  {
    1701    perror ("tputs"); return 0; /* here and below, added dummy retvals */
    1702  }
    1703  
    1704  char*     tgoto (const char *a, int b, int c)
    1705  {
    1706    perror ("tgoto"); return 0;
    1707  }
    1708  
    1709  int       tgetnum (char*a)
    1710  {
    1711    perror ("tgetnum"); return 0;
    1712  }
    1713  
    1714  int       tgetflag (char*a)
    1715  {
    1716    perror ("tgetflag"); return 0;
    1717  }
    1718  
    1719  char*     tgetstr (char *a, char **b)
    1720  {
    1721    perror ("tgetstr"); return 0;
    1722  }
    1723  
    1724  int       tgetent (char *a, const char *b)
    1725  {
    1726    perror ("tgetent"); return 0;
    1727  }
    1728  
    1729  int	tcgetattr(int fildes, struct termios *termios_p)
    1730  {
    1731    perror ("tcgetattr"); return 0;
    1732  }
    1733  
    1734  int	tcsetattr(int fd, int opt_actions, const struct termios *termios_p)
    1735  {
    1736    perror ("tcsetattr"); return 0;
    1737  }