| 1 | /* pcterm.c -- How to handle the PC terminal for Info under MS-DOS/MS-Windows. | 
|---|
| 2 | $Id: pcterm.c,v 1.4 2004/04/11 17:56:46 karl Exp $ | 
|---|
| 3 |  | 
|---|
| 4 | Copyright (C) 1998, 1999, 2003, 2004 Free Software Foundation, Inc. | 
|---|
| 5 |  | 
|---|
| 6 | This program is free software; you can redistribute it and/or modify | 
|---|
| 7 | it under the terms of the GNU General Public License as published by | 
|---|
| 8 | the Free Software Foundation; either version 2, or (at your option) | 
|---|
| 9 | any later version. | 
|---|
| 10 |  | 
|---|
| 11 | This program is distributed in the hope that it will be useful, | 
|---|
| 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|---|
| 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | 
|---|
| 14 | GNU General Public License for more details. | 
|---|
| 15 |  | 
|---|
| 16 | You should have received a copy of the GNU General Public License along | 
|---|
| 17 | with this program; if not, write to the Free Software Foundation, Inc., | 
|---|
| 18 | 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */ | 
|---|
| 19 |  | 
|---|
| 20 |  | 
|---|
| 21 | /* WARNING WARNING WARNING!!! | 
|---|
| 22 | This probably won't work as is with anything but DJGPP!  However, Borland | 
|---|
| 23 | should come close, and other PC compilers will need minor modifications.  */ | 
|---|
| 24 |  | 
|---|
| 25 | /* intl/libintl.h defines a macro `gettext' which | 
|---|
| 26 | conflicts with conio.h header.  */ | 
|---|
| 27 | #ifdef gettext | 
|---|
| 28 | # undef gettext | 
|---|
| 29 | # define gettext _gettext | 
|---|
| 30 | #endif | 
|---|
| 31 |  | 
|---|
| 32 | #include <pc.h> | 
|---|
| 33 | #include <keys.h> | 
|---|
| 34 | #include <conio.h> | 
|---|
| 35 |  | 
|---|
| 36 | #include "variables.h" | 
|---|
| 37 |  | 
|---|
| 38 | extern int speech_friendly;     /* defined in info.c */ | 
|---|
| 39 |  | 
|---|
| 40 | /* **************************************************************** */ | 
|---|
| 41 | /*                                                                  */ | 
|---|
| 42 | /*                PC Terminal Output Functions                      */ | 
|---|
| 43 | /*                                                                  */ | 
|---|
| 44 | /* **************************************************************** */ | 
|---|
| 45 |  | 
|---|
| 46 | static struct text_info outside_info;  /* holds screen params outside Info */ | 
|---|
| 47 | static unsigned char    norm_attr, inv_attr; | 
|---|
| 48 |  | 
|---|
| 49 | static unsigned const char * find_sequence (int); | 
|---|
| 50 |  | 
|---|
| 51 | /* Turn on reverse video. */ | 
|---|
| 52 | static void | 
|---|
| 53 | pc_begin_inverse (void) | 
|---|
| 54 | { | 
|---|
| 55 | textattr (inv_attr); | 
|---|
| 56 | } | 
|---|
| 57 |  | 
|---|
| 58 | /* Turn off reverse video. */ | 
|---|
| 59 | static void | 
|---|
| 60 | pc_end_inverse (void) | 
|---|
| 61 | { | 
|---|
| 62 | textattr (norm_attr); | 
|---|
| 63 | } | 
|---|
| 64 |  | 
|---|
| 65 | /* Move the cursor up one line. */ | 
|---|
| 66 | static void | 
|---|
| 67 | pc_up_line (void) | 
|---|
| 68 | { | 
|---|
| 69 | int x, y; | 
|---|
| 70 | ScreenGetCursor (&y, &x); | 
|---|
| 71 | ScreenSetCursor (MAX (y-1, 0), x); | 
|---|
| 72 | } | 
|---|
| 73 |  | 
|---|
| 74 | /* Move the cursor down one line. */ | 
|---|
| 75 | static void | 
|---|
| 76 | pc_down_line (void) | 
|---|
| 77 | { | 
|---|
| 78 | int x, y; | 
|---|
| 79 | ScreenGetCursor (&y, &x); | 
|---|
| 80 | ScreenSetCursor (MIN (screenheight-1, y+1), x); | 
|---|
| 81 | } | 
|---|
| 82 |  | 
|---|
| 83 | /* Clear the entire terminal screen. */ | 
|---|
| 84 | static void | 
|---|
| 85 | pc_clear_screen (void) | 
|---|
| 86 | { | 
|---|
| 87 | ScreenClear (); | 
|---|
| 88 | } | 
|---|
| 89 |  | 
|---|
| 90 | /* Clear from the current position of the cursor to the end of the line. */ | 
|---|
| 91 | static void | 
|---|
| 92 | pc_clear_to_eol (void) | 
|---|
| 93 | { | 
|---|
| 94 | clreol (); /* perhaps to be replaced by a loop */ | 
|---|
| 95 | } | 
|---|
| 96 |  | 
|---|
| 97 | /* Set the global variables SCREENWIDTH and SCREENHEIGHT. */ | 
|---|
| 98 | static void | 
|---|
| 99 | pc_get_screen_size(void) | 
|---|
| 100 | { | 
|---|
| 101 | /* Current screen dimensions are the default.  */ | 
|---|
| 102 | if (!outside_info.screenheight)       /* paranoia */ | 
|---|
| 103 | gettextinfo (&outside_info); | 
|---|
| 104 | screenwidth  = outside_info.screenwidth; | 
|---|
| 105 | screenheight = outside_info.screenheight; | 
|---|
| 106 |  | 
|---|
| 107 | /* Environment variable "LINES" overrides the default.  */ | 
|---|
| 108 | if (getenv ("LINES") != NULL) | 
|---|
| 109 | screenheight = atoi (getenv ("LINES")); | 
|---|
| 110 |  | 
|---|
| 111 | /* Environment variable "INFO_LINES" overrides "LINES".  */ | 
|---|
| 112 | if (getenv ("INFO_LINES") != NULL) | 
|---|
| 113 | screenheight = atoi (getenv ("INFO_LINES")); | 
|---|
| 114 | } | 
|---|
| 115 |  | 
|---|
| 116 | /* Move the cursor to the terminal location of X and Y. */ | 
|---|
| 117 | static void | 
|---|
| 118 | pc_goto_xy (x, y) | 
|---|
| 119 | int x, y; | 
|---|
| 120 | { | 
|---|
| 121 | ScreenSetCursor (y, x); /* yes, pc.h says ScreenSetCursor (row, column) !! */ | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | /* Print STRING to the terminal at the current position. */ | 
|---|
| 125 | static void | 
|---|
| 126 | pc_put_text (string) | 
|---|
| 127 | char *string; | 
|---|
| 128 | { | 
|---|
| 129 | if (speech_friendly) | 
|---|
| 130 | fputs (string, stdout); | 
|---|
| 131 | else | 
|---|
| 132 | cputs (string); | 
|---|
| 133 | } | 
|---|
| 134 |  | 
|---|
| 135 | /* Ring the terminal bell.  The bell is rung visibly if the terminal is | 
|---|
| 136 | capable of doing that, and if terminal_use_visible_bell_p is non-zero. */ | 
|---|
| 137 | static void | 
|---|
| 138 | pc_ring_bell(void) | 
|---|
| 139 | { | 
|---|
| 140 | if (terminal_has_visible_bell_p && terminal_use_visible_bell_p) | 
|---|
| 141 | ScreenVisualBell (); | 
|---|
| 142 | else | 
|---|
| 143 | { | 
|---|
| 144 | printf ("%c",'\a'); | 
|---|
| 145 | fflush (stdout); | 
|---|
| 146 | } | 
|---|
| 147 | } | 
|---|
| 148 |  | 
|---|
| 149 | /* Print NCHARS from STRING to the terminal at the current position. */ | 
|---|
| 150 | static void | 
|---|
| 151 | pc_write_chars (string, nchars) | 
|---|
| 152 | char *string; | 
|---|
| 153 | int nchars; | 
|---|
| 154 | { | 
|---|
| 155 | if (!nchars) | 
|---|
| 156 | return; | 
|---|
| 157 |  | 
|---|
| 158 | if (speech_friendly) | 
|---|
| 159 | printf ("%.*s",nchars, string); | 
|---|
| 160 | else | 
|---|
| 161 | cprintf ("%..*s",nchars, string); | 
|---|
| 162 | } | 
|---|
| 163 |  | 
|---|
| 164 | /* Scroll an area of the terminal from START to (and excluding) END, | 
|---|
| 165 | AMOUNT lines.  If AMOUNT is negative, the lines are scrolled | 
|---|
| 166 | towards the top of the screen, else they are scrolled towards the | 
|---|
| 167 | bottom of the screen.  The lines of the old region which do not | 
|---|
| 168 | overlap the new region are cleared, to mimic terminal operation.  */ | 
|---|
| 169 | static void | 
|---|
| 170 | pc_scroll_terminal (start, end, amount) | 
|---|
| 171 | int start, end, amount; | 
|---|
| 172 | { | 
|---|
| 173 | int line_to_clear = amount > 0 ? start : end + amount; | 
|---|
| 174 |  | 
|---|
| 175 | /* Move the text.  Note that `movetext' expects 1-based coordinates.  */ | 
|---|
| 176 | movetext (1, start + 1, ScreenCols (), end, 1, start + amount + 1); | 
|---|
| 177 |  | 
|---|
| 178 | /* Now clear the lines which were left unoccupied.  */ | 
|---|
| 179 | if (amount < 0) | 
|---|
| 180 | amount = -amount; | 
|---|
| 181 | while (amount--) | 
|---|
| 182 | { | 
|---|
| 183 | ScreenSetCursor (line_to_clear++, 0); | 
|---|
| 184 | clreol (); | 
|---|
| 185 | } | 
|---|
| 186 | } | 
|---|
| 187 |  | 
|---|
| 188 | /* Put the screen in the video mode and colors which Info will use. | 
|---|
| 189 | Prepare to start using the terminal to read characters singly.  */ | 
|---|
| 190 | static void | 
|---|
| 191 | pc_prep_terminal (void) | 
|---|
| 192 | { | 
|---|
| 193 | int tty; | 
|---|
| 194 |  | 
|---|
| 195 | /* Do not set screen height if we already have it, because | 
|---|
| 196 | doing so erases the screen.  */ | 
|---|
| 197 | if (screenheight != ScreenRows ()) | 
|---|
| 198 | _set_screen_lines (screenheight); | 
|---|
| 199 |  | 
|---|
| 200 | /* Don't fail if they asked for screen dimensions that their | 
|---|
| 201 | hardware cannot support.  */ | 
|---|
| 202 | screenheight = ScreenRows (); | 
|---|
| 203 | screenwidth  = ScreenCols (); | 
|---|
| 204 |  | 
|---|
| 205 | /* Try setting the colors user asked for.  */ | 
|---|
| 206 | textattr (norm_attr); | 
|---|
| 207 | ScreenClear (); | 
|---|
| 208 |  | 
|---|
| 209 | /* Switch console reads to binary mode.  */ | 
|---|
| 210 | tty = fileno (stdin); | 
|---|
| 211 | #ifdef __DJGPP__ | 
|---|
| 212 | setmode (tty, O_BINARY); | 
|---|
| 213 | __djgpp_set_ctrl_c (1);       /* re-enable SIGINT generation by Ctrl-C */ | 
|---|
| 214 | #endif | 
|---|
| 215 | } | 
|---|
| 216 |  | 
|---|
| 217 | /* Restore the tty settings back to what they were before we started using | 
|---|
| 218 | this terminal. */ | 
|---|
| 219 | static void | 
|---|
| 220 | pc_unprep_terminal (void) | 
|---|
| 221 | { | 
|---|
| 222 | int tty; | 
|---|
| 223 |  | 
|---|
| 224 | textattr (outside_info.normattr); | 
|---|
| 225 |  | 
|---|
| 226 | /* Do not set screen height if we already have it, because | 
|---|
| 227 | doing so erases the screen.  */ | 
|---|
| 228 | if (outside_info.screenheight != ScreenRows ()) | 
|---|
| 229 | { | 
|---|
| 230 | _set_screen_lines (outside_info.screenheight); | 
|---|
| 231 | textmode (LASTMODE); | 
|---|
| 232 | } | 
|---|
| 233 | else | 
|---|
| 234 | pc_clear_to_eol (); /* for text attributes to really take effect */ | 
|---|
| 235 |  | 
|---|
| 236 | /* Switch back to text mode on stdin.  */ | 
|---|
| 237 | tty = fileno (stdin); | 
|---|
| 238 | #ifdef __DJGPP__ | 
|---|
| 239 | setmode (tty, O_TEXT); | 
|---|
| 240 | #endif | 
|---|
| 241 | } | 
|---|
| 242 |  | 
|---|
| 243 | /* Initialize the terminal which is known as TERMINAL_NAME.  If this | 
|---|
| 244 | terminal doesn't have cursor addressability, `terminal_is_dumb_p' | 
|---|
| 245 | becomes nonzero.  The variables SCREENHEIGHT and SCREENWIDTH are set | 
|---|
| 246 | to the dimensions that this terminal actually has.  The variable | 
|---|
| 247 | TERMINAL_HAS_META_P becomes nonzero if this terminal supports a Meta | 
|---|
| 248 | key.  Finally, the terminal screen is cleared. */ | 
|---|
| 249 | static void | 
|---|
| 250 | pc_initialize_terminal (term_name) | 
|---|
| 251 | char *term_name; | 
|---|
| 252 | { | 
|---|
| 253 | char *info_colors; | 
|---|
| 254 |  | 
|---|
| 255 | if (!term_name) | 
|---|
| 256 | { | 
|---|
| 257 | term_name = getenv ("TERM"); | 
|---|
| 258 | if (!term_name) | 
|---|
| 259 | term_name = "pc-dos";   /* ``what's in a name?'' */ | 
|---|
| 260 | } | 
|---|
| 261 |  | 
|---|
| 262 | /* Get current video information, to be restored later.  */ | 
|---|
| 263 | if (outside_info.screenwidth == 0) | 
|---|
| 264 | gettextinfo (&outside_info); | 
|---|
| 265 |  | 
|---|
| 266 | /* Current screen colors are the default.  */ | 
|---|
| 267 | norm_attr    = outside_info.normattr; | 
|---|
| 268 | inv_attr     = (((outside_info.normattr &    7) << 4) | | 
|---|
| 269 | ((outside_info.normattr & 0x7f) >> 4)); | 
|---|
| 270 |  | 
|---|
| 271 | /* Does the user want non-default colors?  */ | 
|---|
| 272 | info_colors = getenv ("INFO_COLORS"); | 
|---|
| 273 | if ((info_colors != (char *)0) && !speech_friendly) | 
|---|
| 274 | { | 
|---|
| 275 | /* Decode a color from a string descriptor. | 
|---|
| 276 | The descriptor string is a sequence of color specifiers separated | 
|---|
| 277 | by a non-numeric character.  Each color specifier should represent | 
|---|
| 278 | a small integer which fits into an unsigned char, and can be given | 
|---|
| 279 | in any base supported by strtoul.  Examples of valid descriptors: | 
|---|
| 280 |  | 
|---|
| 281 | "10 31" | 
|---|
| 282 | "0x13/0x45" | 
|---|
| 283 | "007.077" | 
|---|
| 284 |  | 
|---|
| 285 | The separator between two color specifiers can be any character which | 
|---|
| 286 | cannot be used in a printed representation of an integer number.  */ | 
|---|
| 287 | char *endp; | 
|---|
| 288 | unsigned long color_desc = strtoul (info_colors, &endp, 0); | 
|---|
| 289 |  | 
|---|
| 290 | if (color_desc <= UCHAR_MAX) | 
|---|
| 291 | { | 
|---|
| 292 | norm_attr = (unsigned char)color_desc; | 
|---|
| 293 | color_desc = strtoul (endp + 1, &endp, 0); | 
|---|
| 294 | if (color_desc <= UCHAR_MAX) | 
|---|
| 295 | inv_attr = (unsigned char)color_desc; | 
|---|
| 296 | } | 
|---|
| 297 | } | 
|---|
| 298 |  | 
|---|
| 299 | /* We can scroll.  */ | 
|---|
| 300 | terminal_can_scroll = 1; | 
|---|
| 301 |  | 
|---|
| 302 | /* We know how to produce a visible bell, if somebody's looking...  */ | 
|---|
| 303 | if (!speech_friendly) | 
|---|
| 304 | terminal_has_visible_bell_p = 1; | 
|---|
| 305 |  | 
|---|
| 306 | /* We have a Meta key.  */ | 
|---|
| 307 | terminal_has_meta_p = 1; | 
|---|
| 308 |  | 
|---|
| 309 | /* We are *certainly* NOT dumb!  */ | 
|---|
| 310 | terminal_is_dumb_p = 0; | 
|---|
| 311 |  | 
|---|
| 312 | pc_get_screen_size (); | 
|---|
| 313 |  | 
|---|
| 314 | /* Store the arrow keys.  */ | 
|---|
| 315 | term_ku = (char *)find_sequence (K_Up); | 
|---|
| 316 | term_kd = (char *)find_sequence (K_Down); | 
|---|
| 317 | term_kr = (char *)find_sequence (K_Right); | 
|---|
| 318 | term_kl = (char *)find_sequence (K_Left); | 
|---|
| 319 |  | 
|---|
| 320 | term_kP = (char *)find_sequence (K_PageUp); | 
|---|
| 321 | term_kN = (char *)find_sequence (K_PageDown); | 
|---|
| 322 |  | 
|---|
| 323 | #if defined(INFOKEY) | 
|---|
| 324 | term_kh = (char *)find_sequence (K_Home); | 
|---|
| 325 | term_ke = (char *)find_sequence (K_End); | 
|---|
| 326 | term_ki = (char *)find_sequence (K_Insert); | 
|---|
| 327 | term_kx = (char *)find_sequence (K_Delete); | 
|---|
| 328 | #endif | 
|---|
| 329 |  | 
|---|
| 330 | /* Set all the hooks to our PC-specific functions.  */ | 
|---|
| 331 | terminal_begin_inverse_hook       = pc_begin_inverse; | 
|---|
| 332 | terminal_end_inverse_hook         = pc_end_inverse; | 
|---|
| 333 | terminal_prep_terminal_hook       = pc_prep_terminal; | 
|---|
| 334 | terminal_unprep_terminal_hook     = pc_unprep_terminal; | 
|---|
| 335 | terminal_up_line_hook             = pc_up_line; | 
|---|
| 336 | terminal_down_line_hook           = pc_down_line; | 
|---|
| 337 | terminal_clear_screen_hook        = pc_clear_screen; | 
|---|
| 338 | terminal_clear_to_eol_hook        = pc_clear_to_eol; | 
|---|
| 339 | terminal_get_screen_size_hook     = pc_get_screen_size; | 
|---|
| 340 | terminal_goto_xy_hook             = pc_goto_xy; | 
|---|
| 341 | terminal_put_text_hook            = pc_put_text; | 
|---|
| 342 | terminal_ring_bell_hook           = pc_ring_bell; | 
|---|
| 343 | terminal_write_chars_hook         = pc_write_chars; | 
|---|
| 344 | terminal_scroll_terminal_hook     = pc_scroll_terminal; | 
|---|
| 345 | } | 
|---|
| 346 |  | 
|---|
| 347 |  | 
|---|
| 348 | /* **************************************************************** */ | 
|---|
| 349 | /*                                                                  */ | 
|---|
| 350 | /*            How to Read Characters From the PC Terminal           */ | 
|---|
| 351 | /*                                                                  */ | 
|---|
| 352 | /* **************************************************************** */ | 
|---|
| 353 |  | 
|---|
| 354 | /* This will most certainly work ONLY with DJGPP.  */ | 
|---|
| 355 | #ifdef __DJGPP__ | 
|---|
| 356 |  | 
|---|
| 357 | #include <errno.h> | 
|---|
| 358 | #include <sys/fsext.h> | 
|---|
| 359 | #include <dpmi.h> | 
|---|
| 360 |  | 
|---|
| 361 | /* Translation table for some special keys. | 
|---|
| 362 | Arrow keys which are standard on other keyboards are translated into | 
|---|
| 363 | standard ESC-sequences, in case somebody rebinds the simple keys | 
|---|
| 364 | (like C-f, C-b, C-n, etc.). | 
|---|
| 365 |  | 
|---|
| 366 | The strange "\033\061" prefix in some keys is a numeric argument of | 
|---|
| 367 | one, which means ``do the next command once''.  It is here so that | 
|---|
| 368 | when the according PC key is pressed in the middle of an incremental | 
|---|
| 369 | search, Info doesn't see just an ASCII character like `n' or `B', | 
|---|
| 370 | and doesn't add it to the search string; instead, it will exit the | 
|---|
| 371 | incremental search and then perform the command.  */ | 
|---|
| 372 | static struct | 
|---|
| 373 | { | 
|---|
| 374 | int inkey; | 
|---|
| 375 | unsigned char const * const sequence; | 
|---|
| 376 | } DJGPP_keytab[] = {               /* these are for moving between nodes... */ | 
|---|
| 377 | {K_Control_PageDown,  "\033\061n"}, | 
|---|
| 378 | {K_Control_PageUp,    "\033\061p"}, | 
|---|
| 379 | {K_Control_Up,        "\033\061u"}, | 
|---|
| 380 | {K_Control_Down,      "\033\061m"}, | 
|---|
| 381 | {K_Control_Center,    "\033\061l"}, | 
|---|
| 382 |  | 
|---|
| 383 | #if defined(INFOKEY) | 
|---|
| 384 | {K_Home,              "\033[H"}, /* ...and these are for moving IN a node */ | 
|---|
| 385 | {K_End,               "\033[F"}, /* they're Numeric-Keypad-Keys, so       */ | 
|---|
| 386 | #else | 
|---|
| 387 | {K_Home,              "\001"}, | 
|---|
| 388 | {K_End,               "\005"}, | 
|---|
| 389 | #endif | 
|---|
| 390 | {K_Left,              "\033[D"}, /* NUMLOCK should be off !!              */ | 
|---|
| 391 | {K_Right,             "\033[C"}, | 
|---|
| 392 | {K_Down,              "\033[B"}, | 
|---|
| 393 | {K_Up,                "\033[A"}, | 
|---|
| 394 | {K_PageDown,          "\033[G"}, | 
|---|
| 395 | {K_PageUp,            "\033[I"}, | 
|---|
| 396 | {K_Control_Left,      "\033b"}, | 
|---|
| 397 | {K_Control_Right,     "\033f"}, | 
|---|
| 398 | {K_Control_Home,      "\033<"}, | 
|---|
| 399 | {K_Control_End,       "\033>"}, | 
|---|
| 400 |  | 
|---|
| 401 | #if defined(INFOKEY) | 
|---|
| 402 | {K_EHome,             "\033[H"}, /* these are also for moving IN a node */ | 
|---|
| 403 | {K_EEnd,              "\033[F"}, /* they're the "extended" (Grey) keys  */ | 
|---|
| 404 | #else | 
|---|
| 405 | {K_EHome,             "\001"}, | 
|---|
| 406 | {K_EEnd,              "\005"}, | 
|---|
| 407 | #endif | 
|---|
| 408 | {K_ELeft,             "\033[D"}, | 
|---|
| 409 | {K_ERight,            "\033[C"}, | 
|---|
| 410 | {K_EDown,             "\033[B"}, | 
|---|
| 411 | {K_EUp,               "\033[A"}, | 
|---|
| 412 | {K_EPageDown,         "\033[G"}, | 
|---|
| 413 | {K_EPageUp,           "\033[I"}, | 
|---|
| 414 | {K_Control_ELeft,     "\033b"}, | 
|---|
| 415 | {K_Control_ERight,    "\033f"}, | 
|---|
| 416 | {K_Control_EHome,     "\033<"}, | 
|---|
| 417 | {K_Control_EEnd,      "\033>"}, | 
|---|
| 418 |  | 
|---|
| 419 | {K_BackTab,           "\033\011"}, | 
|---|
| 420 | {K_F1,                "\10"},    /* YEAH, gimme that good old F-one-thing */ | 
|---|
| 421 | {K_Delete,            "\177"},   /* to make Kp-Del be DEL (0x7f)          */ | 
|---|
| 422 | {K_EDelete,           "\177"},   /* to make Delete be DEL (0x7f)          */ | 
|---|
| 423 | #if defined(INFOKEY) | 
|---|
| 424 | {K_Insert,            "\033[L"}, | 
|---|
| 425 | {K_EInsert,           "\033[L"}, | 
|---|
| 426 | #endif | 
|---|
| 427 |  | 
|---|
| 428 | /* These are here to map more Alt-X keys to ESC X sequences.  */ | 
|---|
| 429 | {K_Alt_Q,             "\033q"}, | 
|---|
| 430 | {K_Alt_W,             "\033w"}, | 
|---|
| 431 | {K_Alt_E,             "\033e"}, | 
|---|
| 432 | {K_Alt_R,             "\033r"}, | 
|---|
| 433 | {K_Alt_T,             "\033t"}, | 
|---|
| 434 | {K_Alt_Y,             "\033y"}, | 
|---|
| 435 | {K_Alt_U,             "\033u"}, | 
|---|
| 436 | {K_Alt_I,             "\033i"}, | 
|---|
| 437 | {K_Alt_O,             "\033o"}, | 
|---|
| 438 | {K_Alt_P,             "\033p"}, | 
|---|
| 439 | {K_Alt_LBracket,      "\033["}, | 
|---|
| 440 | {K_Alt_RBracket,      "\033]"}, | 
|---|
| 441 | {K_Alt_Return,        "\033\015"}, | 
|---|
| 442 | {K_Alt_A,             "\033a"}, | 
|---|
| 443 | {K_Alt_S,             "\033s"}, | 
|---|
| 444 | {K_Alt_D,             "\033d"}, | 
|---|
| 445 | {K_Alt_F,             "\033f"}, | 
|---|
| 446 | {K_Alt_G,             "\033g"}, | 
|---|
| 447 | {K_Alt_H,             "\033h"}, | 
|---|
| 448 | {K_Alt_J,             "\033j"}, | 
|---|
| 449 | {K_Alt_K,             "\033k"}, | 
|---|
| 450 | {K_Alt_L,             "\033l"}, | 
|---|
| 451 | {K_Alt_Semicolon,     "\033;"}, | 
|---|
| 452 | {K_Alt_Quote,         "\033'"}, | 
|---|
| 453 | {K_Alt_Backquote,     "\033`"}, | 
|---|
| 454 | {K_Alt_Backslash,     "\033\\"}, | 
|---|
| 455 | {K_Alt_Z,             "\033z"}, | 
|---|
| 456 | {K_Alt_X,             "\033x"}, | 
|---|
| 457 | {K_Alt_C,             "\033c"}, | 
|---|
| 458 | {K_Alt_V,             "\033v"}, | 
|---|
| 459 | {K_Alt_B,             "\033b"}, | 
|---|
| 460 | {K_Alt_N,             "\033n"}, | 
|---|
| 461 | {K_Alt_M,             "\033m"}, | 
|---|
| 462 | {K_Alt_Comma,         "\033<"}, /* our reader cannot distinguish between */ | 
|---|
| 463 | {K_Alt_Period,        "\033>"}, /* Alt-. and Alt->, so we cheat a little */ | 
|---|
| 464 | {K_Alt_Slash,         "\033?"}, /* ditto, to get Alt-?                   */ | 
|---|
| 465 | {K_Alt_Backspace,     "\033\177"}, /* M-DEL, to delete word backwards */ | 
|---|
| 466 | {K_Alt_1,             "\033\061"}, | 
|---|
| 467 | {K_Alt_2,             "\033\062"}, | 
|---|
| 468 | {K_Alt_3,             "\033\063"}, | 
|---|
| 469 | {K_Alt_4,             "\033\064"}, | 
|---|
| 470 | {K_Alt_5,             "\033\065"}, | 
|---|
| 471 | {K_Alt_6,             "\033\066"}, | 
|---|
| 472 | {K_Alt_7,             "\033\067"}, | 
|---|
| 473 | {K_Alt_8,             "\033\070"}, | 
|---|
| 474 | {K_Alt_9,             "\033\071"}, | 
|---|
| 475 | {K_Alt_0,             "\033\060"}, | 
|---|
| 476 | {K_Alt_Dash,          "\033\055"}, | 
|---|
| 477 | {K_Alt_EPageUp,       "\033\033[I"}, | 
|---|
| 478 | {K_Alt_EPageDown,     "\033\033[G"}, | 
|---|
| 479 | {K_Alt_Equals,        "\033\075"}, | 
|---|
| 480 | {K_Alt_EDelete,       "\033\177"}, | 
|---|
| 481 | {K_Alt_Tab,           "\033\011"}, | 
|---|
| 482 | {0, 0} | 
|---|
| 483 | }; | 
|---|
| 484 |  | 
|---|
| 485 | /* Given a key, return the sequence of characters which | 
|---|
| 486 | our keyboard driver generates.  */ | 
|---|
| 487 | static unsigned const char * | 
|---|
| 488 | find_sequence (int key) | 
|---|
| 489 | { | 
|---|
| 490 | int i; | 
|---|
| 491 |  | 
|---|
| 492 | for (i = 0; DJGPP_keytab[i].inkey; i++) | 
|---|
| 493 | if (key == DJGPP_keytab[i].inkey) | 
|---|
| 494 | return DJGPP_keytab[i].sequence; | 
|---|
| 495 |  | 
|---|
| 496 | return (unsigned const char *)NULL; | 
|---|
| 497 | } | 
|---|
| 498 |  | 
|---|
| 499 | /* Return zero if a key is pending in the | 
|---|
| 500 | keyboard buffer, non-zero otherwise.  */ | 
|---|
| 501 | static int | 
|---|
| 502 | kbd_buffer_empty (void) | 
|---|
| 503 | { | 
|---|
| 504 | __dpmi_regs r; | 
|---|
| 505 | int retval; | 
|---|
| 506 |  | 
|---|
| 507 | r.h.ah = 0x11;        /* Get enhanced keyboard status */ | 
|---|
| 508 | __dpmi_int (0x16, &r); | 
|---|
| 509 |  | 
|---|
| 510 | /* If the keyboard buffer is empty, the Zero Flag will be set.  */ | 
|---|
| 511 | return (r.x.flags & 0x40) == 0x40; | 
|---|
| 512 | } | 
|---|
| 513 |  | 
|---|
| 514 | /* The buffered characters pending to be read. | 
|---|
| 515 | Actually, Info usually reads a single character, but when we | 
|---|
| 516 | translate a key into a sequence of characters, we keep them here.  */ | 
|---|
| 517 | static unsigned char buffered[512]; | 
|---|
| 518 |  | 
|---|
| 519 | /* Index of the next buffered character to be returned.  */ | 
|---|
| 520 | static int buf_idx; | 
|---|
| 521 |  | 
|---|
| 522 | /* Return the number of characters waiting to be read.  */ | 
|---|
| 523 | long | 
|---|
| 524 | pc_term_chars_avail (void) | 
|---|
| 525 | { | 
|---|
| 526 | if (buf_idx >= sizeof (buffered)) /* paranoia */ | 
|---|
| 527 | { | 
|---|
| 528 | buf_idx = 0; | 
|---|
| 529 | buffered[buf_idx] = '\0'; | 
|---|
| 530 | return 0; | 
|---|
| 531 | } | 
|---|
| 532 | else | 
|---|
| 533 | return (long)strlen (buffered + buf_idx); | 
|---|
| 534 | } | 
|---|
| 535 |  | 
|---|
| 536 | /* Our special terminal keyboard reader.  It will be called by | 
|---|
| 537 | low-level libc functions when the application calls `read' or | 
|---|
| 538 | the ANSI-standard stream-oriented read functions.  If the | 
|---|
| 539 | caller wants to read the terminal, we redirect the call to | 
|---|
| 540 | the BIOS keyboard functions, since that lets us recognize more | 
|---|
| 541 | keys than DOS does.  */ | 
|---|
| 542 | static int | 
|---|
| 543 | keyboard_read (__FSEXT_Fnumber func, int *retval, va_list rest_args) | 
|---|
| 544 | { | 
|---|
| 545 | /* When we are called, REST_ARGS are: file_descriptor, buf, nbytes.  */ | 
|---|
| 546 | unsigned char *buf; | 
|---|
| 547 | size_t nbytes, nread = 0; | 
|---|
| 548 | int fd = va_arg (rest_args, int); | 
|---|
| 549 |  | 
|---|
| 550 | /* Is this call for us?  */ | 
|---|
| 551 | if (func != __FSEXT_read || !isatty (fd)) | 
|---|
| 552 | return 0;   /* and the usual DOS call will be issued */ | 
|---|
| 553 |  | 
|---|
| 554 | buf = va_arg (rest_args, unsigned char *); | 
|---|
| 555 | nbytes = va_arg (rest_args, size_t); | 
|---|
| 556 |  | 
|---|
| 557 | if (!buf) | 
|---|
| 558 | { | 
|---|
| 559 | errno = EINVAL; | 
|---|
| 560 | *retval = -1; | 
|---|
| 561 | return 1; | 
|---|
| 562 | } | 
|---|
| 563 | if (!nbytes) | 
|---|
| 564 | { | 
|---|
| 565 | *retval = 0; | 
|---|
| 566 | return 1; | 
|---|
| 567 | } | 
|---|
| 568 |  | 
|---|
| 569 | /* Loop here until enough bytes has been read.  */ | 
|---|
| 570 | do | 
|---|
| 571 | { | 
|---|
| 572 | int key; | 
|---|
| 573 |  | 
|---|
| 574 | /* If any ``buffered characters'' are left, return as much | 
|---|
| 575 | of them as the caller wanted.  */ | 
|---|
| 576 | while (buffered[buf_idx] && nbytes) | 
|---|
| 577 | { | 
|---|
| 578 | *buf++ = buffered[buf_idx++]; | 
|---|
| 579 | nread++; | 
|---|
| 580 | nbytes--; | 
|---|
| 581 | } | 
|---|
| 582 |  | 
|---|
| 583 | if (nbytes <= 0) | 
|---|
| 584 | break; | 
|---|
| 585 |  | 
|---|
| 586 | /* Wait for another key. | 
|---|
| 587 | We do that in a busy-waiting loop so we don't get parked | 
|---|
| 588 | inside a BIOS call, which will effectively disable signals. | 
|---|
| 589 | While we wait for them to type something, we repeatedly | 
|---|
| 590 | release the rest of our time slice, so that other programs | 
|---|
| 591 | in a multitasking environment, such as Windows, get more cycles.  */ | 
|---|
| 592 | while (kbd_buffer_empty ()) | 
|---|
| 593 | __dpmi_yield (); | 
|---|
| 594 |  | 
|---|
| 595 | key = getxkey (); | 
|---|
| 596 |  | 
|---|
| 597 | /* Translate the key if necessary. | 
|---|
| 598 | Untranslated non-ASCII keys are silently ignored.  */ | 
|---|
| 599 | if ((key & 0x300) != 0) | 
|---|
| 600 | { | 
|---|
| 601 | unsigned char const * key_sequence = find_sequence (key); | 
|---|
| 602 |  | 
|---|
| 603 | if (key_sequence != NULL) | 
|---|
| 604 | { | 
|---|
| 605 | strcpy (buffered, key_sequence); | 
|---|
| 606 | buf_idx = 0; | 
|---|
| 607 | } | 
|---|
| 608 | } | 
|---|
| 609 | else if (key == K_Control_Z) | 
|---|
| 610 | raise (SIGUSR1);        /* we don't have SIGTSTP, so simulate it */ | 
|---|
| 611 | else if (key <= 0xff) | 
|---|
| 612 | { | 
|---|
| 613 | *buf++ = key; | 
|---|
| 614 | nbytes--; | 
|---|
| 615 | nread++; | 
|---|
| 616 | } | 
|---|
| 617 | } | 
|---|
| 618 | while (nbytes > 0); | 
|---|
| 619 |  | 
|---|
| 620 | *retval = nread; | 
|---|
| 621 | return 1;     /* meaning that we handled the call */ | 
|---|
| 622 | } | 
|---|
| 623 |  | 
|---|
| 624 | /* Install our keyboard handler. | 
|---|
| 625 | This is called by the startup code before `main'.  */ | 
|---|
| 626 | static void __attribute__((constructor)) | 
|---|
| 627 | install_keyboard_handler (void) | 
|---|
| 628 | { | 
|---|
| 629 | __FSEXT_set_function (fileno (stdin), keyboard_read); | 
|---|
| 630 |  | 
|---|
| 631 | /* We need to set this single hook here; the rest | 
|---|
| 632 | will be set by pc_initialize_terminal when it is called.  */ | 
|---|
| 633 | terminal_initialize_terminal_hook = pc_initialize_terminal; | 
|---|
| 634 | } | 
|---|
| 635 |  | 
|---|
| 636 | #endif /* __DJGPP__ */ | 
|---|
| 637 |  | 
|---|
| 638 |  | 
|---|
| 639 | /* **************************************************************** */ | 
|---|
| 640 | /*                                                                  */ | 
|---|
| 641 | /*                Emulation of SIGTSTP on Ctrl-Z                    */ | 
|---|
| 642 | /*                                                                  */ | 
|---|
| 643 | /* **************************************************************** */ | 
|---|
| 644 |  | 
|---|
| 645 | #include <limits.h> | 
|---|
| 646 | #include "signals.h" | 
|---|
| 647 | #include "session.h" | 
|---|
| 648 |  | 
|---|
| 649 | #ifndef PATH_MAX | 
|---|
| 650 | # define PATH_MAX 512 | 
|---|
| 651 | #endif | 
|---|
| 652 |  | 
|---|
| 653 | /* Effectively disable signals which aren't defined | 
|---|
| 654 | (assuming no signal can ever be zero). | 
|---|
| 655 | SIGINT is ANSI, so we expect it to be always defined.  */ | 
|---|
| 656 | #ifndef SIGUSR1 | 
|---|
| 657 | # define SIGUSR1 0 | 
|---|
| 658 | #endif | 
|---|
| 659 | #ifndef SIGQUIT | 
|---|
| 660 | # define SIGQUIT 0 | 
|---|
| 661 | #endif | 
|---|
| 662 |  | 
|---|
| 663 | int | 
|---|
| 664 | kill (pid_t pid, int sig) | 
|---|
| 665 | { | 
|---|
| 666 | static char interrupted_msg[] = "Interrupted\r\n"; | 
|---|
| 667 | static char stopped_msg[] = "Stopped.  Type `exit RET' to return.\r\n"; | 
|---|
| 668 | char cwd[PATH_MAX + 1]; | 
|---|
| 669 |  | 
|---|
| 670 | if (pid == getpid () | 
|---|
| 671 | || pid == 0 | 
|---|
| 672 | || pid == -1 | 
|---|
| 673 | || pid == -getpid ()) | 
|---|
| 674 | { | 
|---|
| 675 | switch (sig) | 
|---|
| 676 | { | 
|---|
| 677 | RETSIGTYPE (*old_INT)(int), (*old_QUIT)(int); | 
|---|
| 678 |  | 
|---|
| 679 | case SIGINT: | 
|---|
| 680 | #ifdef __DJGPP__ | 
|---|
| 681 | /* If SIGINT was generated by a readable key, we want to remove | 
|---|
| 682 | it from the PC keyboard buffer, so that DOS and other | 
|---|
| 683 | programs never see it.  DJGPP signal-handling mechanism | 
|---|
| 684 | doesn't remove the INT key from the keyboard buffer.  */ | 
|---|
| 685 | if (!kbd_buffer_empty ()) | 
|---|
| 686 | getxkey (); | 
|---|
| 687 | #endif | 
|---|
| 688 | pc_write_chars (interrupted_msg, sizeof (interrupted_msg) - 1); | 
|---|
| 689 | xexit (1); | 
|---|
| 690 | case SIGUSR1: | 
|---|
| 691 | /* Simulate SIGTSTP by invoking a subsidiary shell.  */ | 
|---|
| 692 | pc_goto_xy (0, outside_info.screenheight - 1); | 
|---|
| 693 | pc_clear_to_eol (); | 
|---|
| 694 | pc_write_chars (stopped_msg, sizeof (stopped_msg) - 1); | 
|---|
| 695 |  | 
|---|
| 696 | /* The child shell can change the working directory, so | 
|---|
| 697 | we need to save and restore it, since it is global.  */ | 
|---|
| 698 | if (!getcwd (cwd, PATH_MAX)) /* should never happen */ | 
|---|
| 699 | cwd[0] = '\0'; | 
|---|
| 700 |  | 
|---|
| 701 | /* We don't want to get fatal signals while the subshell runs.  */ | 
|---|
| 702 | old_INT = signal (SIGINT, SIG_IGN); | 
|---|
| 703 | old_QUIT = signal (SIGQUIT, SIG_IGN); | 
|---|
| 704 | system (""); | 
|---|
| 705 | if (*cwd) | 
|---|
| 706 | chdir (cwd); | 
|---|
| 707 | signal (SIGINT, old_INT); | 
|---|
| 708 | signal (SIGQUIT, old_QUIT); | 
|---|
| 709 | break; | 
|---|
| 710 | default: | 
|---|
| 711 | if (sig) | 
|---|
| 712 | raise (sig); | 
|---|
| 713 | break; | 
|---|
| 714 | } | 
|---|
| 715 | return 0; | 
|---|
| 716 | } | 
|---|
| 717 | else | 
|---|
| 718 | return -1; | 
|---|
| 719 | } | 
|---|
| 720 |  | 
|---|
| 721 |  | 
|---|
| 722 | /* These should never be called, but they make the linker happy.  */ | 
|---|
| 723 |  | 
|---|
| 724 | void       tputs (char *a, int b, int (*c)()) | 
|---|
| 725 | { | 
|---|
| 726 | perror ("tputs"); | 
|---|
| 727 | } | 
|---|
| 728 |  | 
|---|
| 729 | char*     tgoto (char*a, int b, int c) | 
|---|
| 730 | { | 
|---|
| 731 | perror ("tgoto"); return 0; /* here and below, added dummy retvals */ | 
|---|
| 732 | } | 
|---|
| 733 |  | 
|---|
| 734 | int       tgetnum (char*a) | 
|---|
| 735 | { | 
|---|
| 736 | perror ("tgetnum"); return 0; | 
|---|
| 737 | } | 
|---|
| 738 |  | 
|---|
| 739 | int       tgetflag (char*a) | 
|---|
| 740 | { | 
|---|
| 741 | perror ("tgetflag"); return 0; | 
|---|
| 742 | } | 
|---|
| 743 |  | 
|---|
| 744 | char*     tgetstr (char *a, char **b) | 
|---|
| 745 | { | 
|---|
| 746 | perror ("tgetstr"); return 0; | 
|---|
| 747 | } | 
|---|
| 748 |  | 
|---|
| 749 | int       tgetent (char*a, char*b) | 
|---|
| 750 | { | 
|---|
| 751 | perror ("tgetent"); return 0; | 
|---|
| 752 | } | 
|---|
| 753 |  | 
|---|
| 754 | int     tcgetattr(int fildes, struct termios *termios_p) | 
|---|
| 755 | { | 
|---|
| 756 | perror ("tcgetattr"); return 0; | 
|---|
| 757 | } | 
|---|
| 758 |  | 
|---|
| 759 | int     tcsetattr(int fd, int opt_actions, const struct termios *termios_p) | 
|---|
| 760 | { | 
|---|
| 761 | perror ("tcsetattr"); return 0; | 
|---|
| 762 | } | 
|---|