"Fossies" - the Fresh Open Source Software Archive

Member "conspy-1.16/conspy.c" (19 Aug 2020, 21365 Bytes) of package /linux/privat/conspy-1.16.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "conspy.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.14_vs_1.16.

    1 /*
    2  * conspy
    3  * ------
    4  *
    5  * A text-mode VNC like program for Linux virtual terminals.
    6  *
    7  * Author: Russell Stuart, russell-conspy@stuart.id.au
    8  *         22/05/2003
    9  *
   10  * To compile:
   11  *   gcc -Werror -Wall -Wextra -O2 --std=c99 -lncurses -o conspy conspy.c
   12  *
   13  *
   14  * License
   15  * -------
   16  *
   17  * Copyright (c) 2009-2014,2015,2016,2020 Russell Stuart.
   18  *
   19  * This program is free software: you can redistribute it and/or modify
   20  * it under the terms of the GNU Affero General Public License as published
   21  * by the Free Software Foundation, either version 3 of the License, or (at
   22  * your option) any later version.
   23  *
   24  * The copyright holders grant you an additional permission under Section 7
   25  * of the GNU Affero General Public License, version 3, exempting you from
   26  * the requirement in Section 6 of the GNU General Public License, version 3,
   27  * to accompany Corresponding Source with Installation Information for the
   28  * Program or any work based on the Program. You are still required to
   29  * comply with all other Section 6 requirements to provide Corresponding
   30  * Source.
   31  *
   32  * This program is distributed in the hope that it will be useful, but
   33  * WITHOUT ANY WARRANTY; without even the implied warranty of
   34  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   35  * Affero General Public License for more details.
   36  *
   37  * You should have received a copy of the GNU Affero General Public License
   38  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
   39  */
   40 #define _GNU_SOURCE
   41 #include <curses.h>
   42 #include <errno.h>
   43 #include <fcntl.h>
   44 #include <getopt.h>
   45 #include <signal.h>
   46 #include <stdarg.h>
   47 #include <stdint.h>
   48 #include <stdlib.h>
   49 #include <string.h>
   50 #include <sys/ioctl.h>
   51 #include <sys/kd.h>
   52 #include <sys/select.h>
   53 #include <sys/stat.h>
   54 #include <termios.h>
   55 #include <term.h>
   56 #include <sys/types.h>
   57 #include <sys/time.h>
   58 #include <unistd.h>
   59 
   60 /* GNU/kFreeBSD has different #define's */
   61 #if defined(__FreeBSD_kernel__)
   62 #define K_UNICODE K_CODE
   63 #define IUCLC 0
   64 #endif
   65 
   66 extern int errno;
   67 
   68 /*
   69  * Version info.
   70  */
   71 static char conspy_date[]   = "2020-08-19";
   72 static char conspy_version[]    = "1.16";
   73 
   74 /*
   75  * VGA colour definitions, as found in a nibble in an attribute
   76  * byte within VGA video memory.
   77  */
   78 #define VGA_BLACK   0x00
   79 #define VGA_BLUE    0x01
   80 #define VGA_GREEN   0x02
   81 #define VGA_CYAN    0x03
   82 #define VGA_RED     0x04
   83 #define VGA_MAGENTA 0x05
   84 #define VGA_YELLOW  0x06
   85 #define VGA_WHITE   0x07
   86 
   87 /*
   88  * Box characters used by the Linux console.
   89  */
   90 #define IBM_BLOCK   0x0c
   91 #define IBM_BOARD   0x09
   92 #define IBM_BTEE    0xcb
   93 #define IBM_CKBOARD 0x0a
   94 #define IBM_DARROW  0x19
   95 #define IBM_DEGREE  0xb0
   96 #define IBM_GEQUAL  0x14
   97 #define IBM_HLINE   0xca
   98 #define IBM_LANTERN 0xdf
   99 #define IBM_LARROW  0x16
  100 #define IBM_LLCORNER    0xc3
  101 #define IBM_LRCORNER    0xc9
  102 #define IBM_LTEE    0xc7
  103 #define IBM_PI      0x1f
  104 #define IBM_PLMINUS 0xb1
  105 #define IBM_RTEE    0xcd
  106 #define IBM_STERLING    0xa3
  107 #define IBM_TTEE    0xce
  108 #define IBM_UARROW  0x18
  109 #define IBM_ULCORNER    0xc6
  110 #define IBM_URCORNER    0xcc
  111 #define IBM_VLINE   0xc5
  112 
  113 /*
  114  * This is the original IBM PC charcter set.  I thought this is what the
  115  * Linux console would use, but apparently not.
  116  */
  117 #if 0
  118 #define IBM_BLOCK   0xdb
  119 #define IBM_BTEE    0xc1
  120 #define IBM_BULLET  0xf9
  121 #define IBM_DARROW  0x19
  122 #define IBM_D_BTEE  0xca
  123 #define IBM_DEGREE  0xf8
  124 #define IBM_D_HLINE 0xcd
  125 #define IBM_DIAMOND 0x04
  126 #define IBM_D_LLCORNER  0xc8
  127 #define IBM_D_LRCORNER  0xbc
  128 #define IBM_D_LTEE  0xcc
  129 #define IBM_D_RTEE  0xb9
  130 #define IBM_DS_BTEE 0xcf
  131 #define IBM_DS_LLCORNER 0xd3
  132 #define IBM_DS_LRCORNER 0xbd
  133 #define IBM_DS_LTEE 0xc7
  134 #define IBM_DS_RTEE 0xb6
  135 #define IBM_DS_TTEE 0xd1
  136 #define IBM_DS_ULCORNER 0xd6
  137 #define IBM_DS_URCORNER 0xb7
  138 #define IBM_DS_XCROSS   0xd7
  139 #define IBM_D_TTEE  0xcb
  140 #define IBM_D_ULCORNER  0xc9
  141 #define IBM_D_URCORNER  0xbb
  142 #define IBM_D_VLINE 0xba
  143 #define IBM_D_XCROSS    0xce
  144 #define IBM_GEQUAL  0xf2
  145 #define IBM_HLINE   0xc4
  146 #define IBM_LANTERN 0x0f
  147 #define IBM_LARROW  0x1b
  148 #define IBM_LLCORNER    0xc0
  149 #define IBM_LRCORNER    0xd9
  150 #define IBM_LTEE    0xc3
  151 #define IBM_PLMINUS 0xf1
  152 #define IBM_RARROW  0x1a
  153 #define IBM_RTEE    0xb4
  154 #define IBM_SD_BTEE 0xd0
  155 #define IBM_SD_LLCORNER 0xd4
  156 #define IBM_SD_LRCORNER 0xbe
  157 #define IBM_SD_LTEE 0xc6
  158 #define IBM_SD_RTEE 0xb5
  159 #define IBM_SD_TTEE 0xd2
  160 #define IBM_SD_ULCORNER 0xd5
  161 #define IBM_SD_URCORNER 0xb8
  162 #define IBM_SD_XCROSS   0xd8
  163 #define IBM_STERLING    0x9c
  164 #define IBM_TTEE    0xc2
  165 #define IBM_UARROW  0x18
  166 #define IBM_ULCORNER    0xda
  167 #define IBM_URCORNER    0xbf
  168 #define IBM_VLINE   0xb3
  169 #define IBM_XCROSS  0xc5
  170 #endif
  171 
  172 /*
  173  * This function maps a VGA colour pair to a curses COLOR_PAIR()
  174  * number.  In the curses scheme colour pair 0 must be white text
  175  * on a black background, so the origin is moved to there.
  176  */
  177 #define VGA_PAIR(foreground, background) \
  178             (((foreground) + ((background) << 3)) ^ 0x07)
  179 
  180 /*
  181  * Forward declarations.
  182  */
  183 static void cleanup();
  184 static void conspy(int use_colour);
  185 static void finish(int signal);
  186 static void init_cursesbox();
  187 static void process_command_line(int argc, char** argv);
  188 static int setup();
  189 static void syserror(char* message, ...);
  190 static void usage(char* message, ...);
  191 
  192 /*
  193  * Local variables.
  194  */
  195 static char*        me;
  196 static struct termios   old_termios;
  197 static int      opt_columns;
  198 static int      opt_lines;
  199 static int      opt_viewonly;
  200 static int          tty_handle = -1;
  201 static char         tty_name[20];
  202 static int          device_handle = -1;
  203 static char         device_name[20];
  204 
  205 /*
  206  * This array translates a VGA colour (defined above) to a
  207  * CURSES colour.
  208  */
  209 static short        colour_map[] =
  210   { COLOR_BLACK,    /* VGA_BLACK */
  211     COLOR_BLUE,     /* VGA_BLUE */
  212     COLOR_GREEN,    /* VGA_GREEN */
  213     COLOR_CYAN,     /* VGA_CYAN */
  214     COLOR_RED,      /* VGA_RED */
  215     COLOR_MAGENTA,  /* VGA_MAGENTA */
  216     COLOR_YELLOW,   /* VGA_YELLOW */
  217     COLOR_WHITE,    /* VGA_WHITE */
  218   };
  219 
  220 /*
  221  * Special IBM characters & their translations.
  222  */
  223 static unsigned short cursesbox[256];
  224 
  225 /*
  226  * A character as it appears in the VGA video buffer.
  227  */
  228 struct vidchar {
  229 #if 0
  230   unsigned char     vidchar_char;       /* The IBM-ASCII Char code */
  231   unsigned char     vidchar_attribute;  /* Colour/blink/bold spec */
  232 #define VIDCHAR_CHAR(vidchar)       ((vidchar)->vidchar_char)
  233 #define VIDCHAR_ATTRIBUTE(vidchar)  ((vidchar)->vidchar_attribute)
  234 #else
  235   unsigned short    vidchar_charattr;   /* Attr in msb, char in lsb */
  236 #define VIDCHAR_CHAR(vidchar)       ((vidchar)->vidchar_charattr & 0xFF)
  237 #define VIDCHAR_ATTRIBUTE(vidchar)  ((vidchar)->vidchar_charattr >> 8)
  238 #endif
  239 };
  240 
  241 
  242 /*
  243  * The data returned by reading a /dev/vcsa device.
  244  */
  245 struct vidbuf {
  246   unsigned char     vidbuf_lines;       /* Line on screen */
  247   unsigned char     vidbuf_columns;     /* Columns on screen */
  248   unsigned char     vidbuf_curcolumn;   /* Column cursor is in */
  249   unsigned char     vidbuf_curline;     /* Line cursor is in */
  250   struct vidchar    vidbuf_chars[0];    /* Char in VGA video buf */
  251 };
  252 
  253 #define VIDBUF_SIZE(cols, lines) (sizeof(struct vidbuf) + cols * lines * sizeof(struct vidchar))
  254 
  255 /*
  256  * Options we allow.
  257  */
  258 static struct option options[] =
  259   {
  260     {"geometry",    1,  0,  'g'},
  261     {"version",     0,  0,  'V'},
  262     {"viewonly",    0,  0,  'v'},
  263     {0,0,0,0},
  264   };
  265 
  266 /*
  267  * Entry point.
  268  */
  269 int main(int argc, char** argv)
  270 {
  271   int           use_colour;
  272 
  273   me = strrchr(argv[0], '/');
  274   me = me == 0 ? argv[0] : me + 1;
  275   process_command_line(argc, argv);
  276   use_colour = setup();
  277   init_cursesbox();
  278   conspy(use_colour);
  279   cleanup();
  280   /*
  281    * Clear screen & home cursor.  Added when _I_ became confused
  282    * as to whether this program was running or not(!)
  283    */
  284   if (tigetstr("clear") != (char*)0)
  285   {
  286     putp(tigetstr("clear"));
  287     fflush(stdout);
  288   }
  289   return 0;
  290 }
  291 
  292 /*
  293  * Print our a usage message and exit.
  294  */
  295 static void usage(char* message, ...)
  296 {
  297   va_list       list;
  298 
  299   if (message != 0)
  300   {
  301     fprintf(stderr, "%s: ", me);
  302     va_start(list, message);
  303     vfprintf(stderr, message, list);
  304     va_end(list);
  305     fprintf(stderr, ".\n");
  306   }
  307   fprintf(stderr, "usage: %s [options] [virtual_console].\n", me);
  308   fprintf(stderr, "options:\n");
  309   fprintf(stderr, "  -g G,--geometry=G Console size is G, format COLSxROWS.\n");
  310   fprintf(stderr, "  -V,--version      Print %s's version number and exit.\n", me);
  311   fprintf(stderr, "  -v,--viewonly     Don't send keystrokes to the console.\n");
  312   fprintf(stderr, "virtual_console:\n");
  313   fprintf(stderr, "  omitted  Track the current console.\n");
  314   fprintf(stderr, "  1..63    Virtual console N.\n");
  315   fprintf(stderr, "To exit, quickly press escape 3 times.\n");
  316   exit(1);
  317 }
  318 
  319 /*
  320  * Process the command line.
  321  */
  322 static void process_command_line(int argc, char** argv)
  323 {
  324   char*         end;
  325   int           opt;
  326   size_t        optindex;
  327   char          opts[(sizeof(options) / sizeof(*options))*2 + 1];
  328   char          vcc_name[sizeof(device_name)];
  329   int           vcc_errno;
  330   char          vcsa_name[sizeof(device_name)];
  331   int           vcsa_errno;
  332   char*         virtual_console;
  333 
  334   opt = 0;
  335   for (optindex = 0; optindex < sizeof(options)/sizeof(*options); optindex += 1)
  336   {
  337     opts[opt++] = options[optindex].val;
  338     if (options[optindex].has_arg)
  339       opts[opt++] = ':';
  340   }
  341   opts[opt] = '\0';
  342   while ((opt = getopt_long(argc, argv, opts, options, 0)) != -1)
  343   {
  344     switch (opt)
  345     {
  346       case 'g':
  347     opt_lines = 0;
  348     opt_columns = strtol(optarg, &end, 10);
  349     if (*end == 'x' || *end == 'X') {
  350       opt_lines = strtol(end + 1, &end, 10);
  351     }
  352     if (*end != '\0' || opt_lines <= 0 || opt_columns <= 0)
  353     {
  354       usage("geometry must be COLSxROWS");
  355     }
  356     printf("%s: version %s %s\n", me, conspy_version, conspy_date);
  357     exit(0);
  358       case 'V':
  359     printf("%s: version %s %s\n", me, conspy_version, conspy_date);
  360     exit(0);
  361       case 'v':
  362     opt_viewonly = 1;
  363     break;
  364       default:
  365     usage(0);
  366     }
  367   }
  368   virtual_console = argv[optind];
  369   if (virtual_console != 0)
  370   {
  371     strtol(virtual_console, &end, 10);
  372     if (*end != '\0' || end - virtual_console > 2)
  373       usage("invalid virtual console \"%s\"", virtual_console);
  374   }
  375   /*
  376    *  Verify we can open the devices.
  377    */
  378   strcpy(vcsa_name, "/dev/vcsa");
  379   vcc_errno = 0;
  380   if (virtual_console != 0)
  381     strcat(vcsa_name, virtual_console);
  382   strcpy(vcc_name, "/dev/vcc/a");
  383   if (virtual_console != 0 && *virtual_console != '\0')
  384     strcat(vcc_name, virtual_console);
  385   else
  386     strcat(vcc_name, "0");
  387   strcpy(device_name, vcsa_name);
  388   device_handle = open(vcsa_name, O_RDONLY);
  389   vcsa_errno = errno;
  390   if (device_handle == -1)
  391   {
  392     strcpy(device_name, vcc_name);
  393     device_handle = open(vcc_name, O_RDONLY);
  394     vcc_errno = errno;
  395   }
  396   if (device_handle == -1)
  397   {
  398     fprintf(stderr, "%s: could not open either the alternate device files.\n", me);
  399     errno = vcsa_errno;
  400     perror(vcsa_name);
  401     errno = vcc_errno;
  402     perror(vcc_name);
  403     exit(1);
  404   }
  405   if (!opt_viewonly)
  406   {
  407     strcpy(tty_name, "/dev/tty");
  408     if (virtual_console != 0 && *virtual_console != '\0')
  409       strcat(tty_name, virtual_console);
  410     else
  411       strcat(tty_name, "0");
  412     tty_handle = open(tty_name, O_WRONLY);
  413     if (tty_handle == -1 && errno == ENOENT)
  414     {
  415       strcpy(tty_name, "/dev/vc/");
  416       if (virtual_console != 0 && *virtual_console != '\0')
  417     strcat(tty_name, virtual_console);
  418       else
  419     strcat(tty_name, "0");
  420       tty_handle = open(tty_name, O_WRONLY);
  421     }
  422     if (tty_handle == -1)
  423     {
  424       perror(tty_name);
  425       exit(1);
  426     }
  427   }
  428 }
  429 
  430 /*
  431  * Initialise the Curses box char set.  This must follow
  432  */
  433 static void init_cursesbox()
  434 {
  435   cursesbox[IBM_BLOCK]      = ACS_BLOCK;
  436   cursesbox[IBM_BOARD]      = ACS_BOARD;
  437   cursesbox[IBM_BTEE]       = ACS_BTEE;
  438   cursesbox[IBM_CKBOARD]    = ACS_CKBOARD;
  439   cursesbox[IBM_DARROW]     = ACS_DARROW;
  440   cursesbox[IBM_DEGREE]     = ACS_DEGREE;
  441   cursesbox[IBM_GEQUAL]     = ACS_GEQUAL;
  442   cursesbox[IBM_HLINE]      = ACS_HLINE;
  443   cursesbox[IBM_LANTERN]    = ACS_LANTERN;
  444   cursesbox[IBM_LARROW]     = ACS_LARROW;
  445   cursesbox[IBM_LLCORNER]   = ACS_LLCORNER;
  446   cursesbox[IBM_LRCORNER]   = ACS_LRCORNER;
  447   cursesbox[IBM_LTEE]       = ACS_LTEE;
  448   cursesbox[IBM_PI]     = ACS_PI;
  449   cursesbox[IBM_PLMINUS]    = ACS_PLMINUS;
  450   cursesbox[IBM_RTEE]       = ACS_RTEE;
  451   cursesbox[IBM_STERLING]   = ACS_STERLING;
  452   cursesbox[IBM_TTEE]       = ACS_TTEE;
  453   cursesbox[IBM_UARROW]     = ACS_UARROW;
  454   cursesbox[IBM_ULCORNER]   = ACS_ULCORNER;
  455   cursesbox[IBM_URCORNER]   = ACS_URCORNER;
  456   cursesbox[IBM_VLINE]      = ACS_VLINE;
  457 }
  458 
  459 /*
  460  * Print an OS error and die.
  461  */
  462 static void syserror(char* message, ...)
  463 {
  464   int           errnr = errno;
  465   va_list       list;
  466 
  467   cleanup();
  468   va_start(list, message);
  469   vfprintf(stderr, message, list);
  470   va_end(list);
  471   fprintf(stderr, ": %s.\n", strerror(errnr));
  472   exit(1);
  473 }
  474 
  475 /*
  476  * Allocate some memory.
  477  */
  478 static void* checked_malloc(size_t bytes)
  479 {
  480   void* result = malloc(bytes);
  481   if (result == 0)
  482     syserror("memory allocation failed");
  483   return result;
  484 }
  485 
  486 /*
  487  * Die, possibly from a signal.
  488  */
  489 static void finish(int sig)
  490 {
  491   sigset_t              sigset;
  492 
  493   cleanup();
  494   if (sig <= 0)
  495     exit(-sig);
  496   sigemptyset(&sigset);
  497   sigaddset(&sigset, sig);
  498   sigprocmask(SIG_UNBLOCK, &sigset, 0);
  499   signal(sig, SIG_DFL);
  500   kill(getpid(), sig);
  501   pause();
  502 }
  503 
  504 /*
  505  * Set up curses and the TTY.
  506  */
  507 static int setup()
  508 {
  509   int           colour;
  510   int           background;
  511   int           foreground;
  512   struct termios    termios;
  513   int           use_colour;
  514 
  515   /*
  516    * Get a copy of the current TTY settings.
  517    */
  518   if (tcgetattr(0, &old_termios) == -1)
  519   {
  520     perror("tcgetattr(0)");
  521     exit(1);
  522   }
  523   /*
  524    * Start curses.
  525    */
  526   (void)signal(SIGHUP, finish);
  527   (void)signal(SIGINT, finish);
  528   (void)signal(SIGTERM, finish);
  529   (void)initscr();
  530   (void)nonl();
  531   /*
  532    * Set up the tty.  All characters must be passed through to
  533    * us unaltered.
  534    */
  535   termios = old_termios;
  536   termios.c_iflag &= ~(BRKINT|INLCR|ICRNL|IXON|IXOFF|IUCLC|IXANY|IMAXBEL);
  537   termios.c_oflag &= ~(OPOST);
  538   termios.c_lflag &= ~(ISIG|ICANON|ECHO);
  539   if (tcsetattr(0, TCSANOW, &termios) == -1)
  540     syserror("tcsetattr(0)");
  541   /*
  542    * Set up the colour map, if we can.
  543    */
  544   use_colour = 0;
  545   if (has_colors())
  546   {
  547     start_color();
  548     if (COLOR_PAIRS >= 64)
  549       use_colour = 1;
  550   }
  551   if (use_colour)
  552   {
  553     for (foreground = 0; foreground < 8; foreground += 1)
  554     {
  555       for (background = 0; background < 8; background += 1)
  556       {
  557     colour = VGA_PAIR(foreground, background);
  558     if (colour != 0)
  559       init_pair(colour, colour_map[foreground], colour_map[background]);
  560       }
  561     }
  562   }
  563   return use_colour;
  564 }
  565 
  566 /*
  567  * Shut down curses, and restore everything.
  568  */
  569 static void cleanup()
  570 {
  571   tcsetattr(0, TCSANOW, &old_termios);
  572   endwin();
  573   if (device_handle != -1)
  574     close(device_handle);
  575   if (tty_handle != -1)
  576     close(tty_handle);
  577 }
  578 
  579 /*
  580  * This is where the actual work is done.
  581  */
  582 static void conspy(int use_colour)
  583 {
  584   unsigned short    box;
  585   size_t        bytes_read;
  586   unsigned int      column;
  587   attr_t        curses_attribute;
  588   short         curses_colour;
  589   int           escape_pressed;
  590   int           escape_notpressed;
  591   int           ioerror_count;
  592   unsigned int      key_count;
  593   unsigned int      key_index;
  594   uint32_t      keyboard_mode;
  595   char          keys_pressed[256];
  596   unsigned int      last_attribute;
  597   unsigned int      last_columns;
  598   unsigned int      last_lines;
  599   unsigned int      line;
  600   chtype        line_buf[256];
  601   int           line_chars;
  602   fd_set        readset;
  603   int           result;
  604   struct timeval    timeval;
  605   unsigned int      curr_columns;
  606   unsigned int      curr_lines;
  607   int           tty_result;
  608   unsigned int      video_attribute;
  609   int           video_char;
  610   struct vidbuf*    vidbuf;
  611   size_t        vidbuf_size;
  612   struct vidchar*   vidchar;
  613 
  614   curses_colour = 0;
  615   curses_attribute = 0;
  616   escape_notpressed = 0;
  617   escape_pressed = 0;
  618   ioerror_count = 0;
  619   key_count = 0;
  620   last_attribute = ~0U;
  621   last_columns = 0;
  622   last_lines = 0;
  623   curr_columns = opt_columns ? opt_columns : 80;
  624   curr_lines = opt_lines ? opt_lines : 25;
  625   vidbuf_size = VIDBUF_SIZE(curr_columns, curr_lines) + sizeof(vidchar);
  626   vidbuf = checked_malloc(vidbuf_size);
  627   for (;;)
  628   {
  629     /*
  630      * Read the video buffer.
  631      */
  632     for (;;)
  633     {
  634     if (lseek(device_handle, 0L, SEEK_SET) != 0L)
  635       syserror(device_name);
  636     bytes_read = read(device_handle, vidbuf, vidbuf_size);
  637     if (bytes_read < sizeof(*vidbuf) || bytes_read > vidbuf_size)
  638       syserror(device_name);
  639     if (bytes_read < vidbuf_size)
  640       break;
  641     vidbuf_size *= 2;
  642     free(vidbuf);
  643     vidbuf = checked_malloc(vidbuf_size);
  644     }
  645     if (bytes_read == VIDBUF_SIZE(opt_columns, opt_lines))
  646     {
  647       curr_columns = opt_columns;
  648       curr_lines = opt_lines;
  649     }
  650     else
  651     {
  652       int i, j = -1, k = -1;
  653       for (i = 0; i <= 7; i += 1)
  654       {
  655     curr_columns = vidbuf->vidbuf_columns + (i / 2 * 256);
  656     curr_lines = vidbuf->vidbuf_lines + (i % 2 * 256);
  657     if (bytes_read == VIDBUF_SIZE(curr_columns, curr_lines))
  658     {
  659       k = j;
  660       j = i;
  661     }
  662       }
  663       if (j == -1 || k != -1)
  664       {
  665     fprintf(stderr, "\nCan not guess the geometry of the console.\n");
  666     exit(1);
  667       }
  668       curr_columns = vidbuf->vidbuf_columns + (j / 2 * 256);
  669       curr_lines = vidbuf->vidbuf_lines + (j % 2 * 256);
  670     }
  671     /*
  672      * If the screen size has changed blank out the unused portions.
  673      */
  674     if (curr_lines < last_lines && last_lines < (unsigned)LINES)
  675     {
  676       move(curr_lines, 0);
  677       clrtobot();
  678     }
  679     if (curr_columns < last_columns && last_columns < (unsigned)COLS)
  680     {
  681       for (line = 0; line < last_lines && line < (unsigned)LINES; line += 1)
  682       {
  683     move(line, last_columns);
  684     clrtoeol();
  685       }
  686     }
  687     last_lines = curr_lines;
  688     last_columns = curr_columns;
  689     /*
  690      * Write the data to the screen.
  691      */
  692     vidchar = vidbuf->vidbuf_chars;
  693     for (line = 0; line < curr_lines && line < (unsigned)LINES; line += 1)
  694     {
  695       line_chars = 0;
  696       for (column = 0; column < curr_columns; column += 1)
  697       {
  698     if (column >= (unsigned)COLS)
  699     {
  700       vidchar += curr_columns - column;
  701       break;
  702     }
  703     video_attribute = VIDCHAR_ATTRIBUTE(vidchar);
  704     video_char = VIDCHAR_CHAR(vidchar);
  705     box = cursesbox[video_char];
  706     if (box != 0)
  707     {
  708       video_attribute |= 0x100;
  709       video_char = box;
  710     }
  711     if (video_char < ' ')
  712       video_char = ' ';
  713     if (video_attribute != last_attribute)
  714     {
  715       if (line_chars > 0)
  716       {
  717         move(line, column - line_chars);
  718         addchnstr(line_buf, line_chars);
  719         wchgat(stdscr, line_chars, curses_attribute, curses_colour, 0);
  720         line_chars = 0;
  721       }
  722       curses_attribute = A_NORMAL;
  723       if (video_attribute & 0x100)
  724         curses_attribute |= A_ALTCHARSET;
  725       if (video_attribute & 0x80)
  726         curses_attribute |= A_BLINK;
  727       if (video_attribute & 0x08)
  728         curses_attribute |= A_BOLD;
  729       if (use_colour)
  730       {
  731         curses_colour =
  732         VGA_PAIR(video_attribute & 0x7, video_attribute>>4 & 0x7);
  733       }
  734       last_attribute = video_attribute;
  735     }
  736     line_buf[line_chars++] = video_char;
  737     vidchar += 1;
  738       }
  739       move(line, column - line_chars);
  740       addchnstr(line_buf, line_chars);
  741       wchgat(stdscr, line_chars, curses_attribute, curses_colour, 0);
  742     }
  743     if (vidbuf->vidbuf_curline < LINES && vidbuf->vidbuf_curcolumn < COLS)
  744       move(vidbuf->vidbuf_curline, vidbuf->vidbuf_curcolumn);
  745     refresh();
  746     /*
  747      * Wait for 1/4 or a second, or for a character to be pressed.
  748      */
  749     FD_ZERO(&readset);
  750     FD_SET(0, &readset);
  751     timeval.tv_sec = 0;
  752     timeval.tv_usec = 250 * 1000L;
  753     result = select(0 + 1, &readset, 0, 0, &timeval);
  754     if (result == -1)
  755     {
  756       if (errno != EINTR)
  757     syserror("select([tty_handle],0,0,timeval)");
  758       endwin();
  759       refresh();
  760       continue;
  761     }
  762     /*
  763      * Read the keys pressed.
  764      */
  765     bytes_read = 0;
  766     if (result == 1)
  767     {
  768       bytes_read =
  769     read(0, keys_pressed + key_count, sizeof(keys_pressed) - key_count);
  770       if (bytes_read == ~0U)
  771     syserror(tty_name);
  772     }
  773     /*
  774      * Do exit processing.
  775      */
  776     if (result == 0 && ++escape_notpressed == 4)
  777     {                   /* >1sec since last key press */
  778       escape_pressed = 0;       /* That ends any exit sequence */
  779       escape_notpressed = 0;
  780     }
  781     for (key_index = key_count; key_index < key_count+bytes_read; key_index += 1)
  782     {                   /* See if escape pressed 3 times */
  783       if (keys_pressed[key_index] != '\033')
  784     escape_pressed = 0;
  785       else if (++escape_pressed == 3)
  786     return;
  787       if (keys_pressed[key_index] == ('L' & 0x1F))
  788     wrefresh(curscr);
  789     }
  790     /*
  791      * Insert all keys pressed into the virtual console's input
  792      * buffer.  Don't do this if the virtual console is in scan
  793      * code mode - giving ASCII characters to a program expecting
  794      * scan codes will confuse it.
  795      */
  796     if (!opt_viewonly)
  797     {
  798       /*
  799        * Close & re-open tty in case they have swapped virtual consoles.
  800        */
  801       close(tty_handle);
  802       tty_handle = open(tty_name, O_WRONLY);
  803       if (tty_handle == -1)
  804     syserror(tty_name);
  805       key_count += bytes_read;
  806       tty_result = ioctl(tty_handle, KDGKBMODE, &keyboard_mode);
  807       if (tty_result == -1)
  808     ;
  809       else if (keyboard_mode != K_XLATE && keyboard_mode != K_UNICODE)
  810     key_count = 0;          /* Keyboard is in scan code mode */
  811       else
  812       {
  813     for (key_index = 0; key_index < key_count; key_index += 1)
  814     {
  815       tty_result = ioctl(tty_handle, TIOCSTI, keys_pressed + key_index);
  816       if (tty_result == -1)
  817         break;
  818     }
  819     if (key_index == key_count) /* All keys sent? */
  820       key_count = 0;        /* Yes - clear the buffer */
  821     else
  822     {
  823       memmove(keys_pressed, keys_pressed+key_index, key_count-key_index);
  824       key_count -= key_index;
  825     }
  826       }
  827       /*
  828        * We sometimes get spurious IO errors on the TTY as programs
  829        * close and re-open it.  Usually they will just go away, if
  830        * we are patient.
  831        */
  832       if (tty_result != -1)
  833     ioerror_count = 0;
  834       else if (errno != EIO || ++ioerror_count > 4)
  835     syserror(tty_name);
  836     }
  837   }
  838 }