Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Copyright (c) 1984 through 2008, William LeFebvre
      3  * All rights reserved.
      4  *
      5  * Redistribution and use in source and binary forms, with or without
      6  * modification, are permitted provided that the following conditions are met:
      7  *
      8  *     * Redistributions of source code must retain the above copyright
      9  * notice, this list of conditions and the following disclaimer.
     10  *
     11  *     * Redistributions in binary form must reproduce the above
     12  * copyright notice, this list of conditions and the following disclaimer
     13  * in the documentation and/or other materials provided with the
     14  * distribution.
     15  *
     16  *     * Neither the name of William LeFebvre nor the names of other
     17  * contributors may be used to endorse or promote products derived from
     18  * this software without specific prior written permission.
     19  *
     20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
     21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
     22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
     23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
     24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
     25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
     26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     31  */
     32 
     33 /*
     34  *  Top users/processes display for Unix
     35  *  Version 3
     36  */
     37 
     38 /*  This file contains the routines that interface to termcap and stty/gtty.
     39  *
     40  *  Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty.
     41  *
     42  *  I put in code to turn on the TOSTOP bit while top was running, but I
     43  *  didn't really like the results.  If you desire it, turn on the
     44  *  preprocessor variable "TOStop".   --wnl
     45  */
     46 
     47 #include "os.h"
     48 #include "top.h"
     49 
     50 #if HAVE_CURSES_H && HAVE_TERM_H
     51 #include <curses.h>
     52 #include <term.h>
     53 #else
     54 #if HAVE_TERMCAP_H
     55 #include <termcap.h>
     56 #else
     57 #if HAVE_CURSES_H
     58 #include <curses.h>
     59 #endif
     60 #endif
     61 #endif
     62 
     63 #if !HAVE_DECL_TPUTS
     64 int tputs(const char *, int, int (*)(int));
     65 #endif
     66 #if !HAVE_DECL_TGOTO
     67 char *tgoto(const char *, int, int);
     68 #endif
     69 #if !HAVE_DECL_TGETENT
     70 int tgetent(const char *, char *);
     71 #endif
     72 #if !HAVE_DECL_TGETFLAG
     73 int tgetflag(const char *);
     74 #endif
     75 #if !HAVE_DECL_TGETNUM
     76 int tgetnum(const char *);
     77 #endif
     78 #if !HAVE_DECL_TGETSTR
     79 char *tgetstr(const char *, char **);
     80 #endif
     81 
     82 #include <sys/ioctl.h>
     83 #ifdef CBREAK
     84 # include <sgtty.h>
     85 # define USE_SGTTY
     86 #else
     87 # ifdef TCGETA
     88 #  define USE_TERMIO
     89 #  include <termio.h>
     90 # else
     91 #  define USE_TERMIOS
     92 #  include <termios.h>
     93 # endif
     94 #endif
     95 #if defined(USE_TERMIO) || defined(USE_TERMIOS)
     96 # ifndef TAB3
     97 #  ifdef OXTABS
     98 #   define TAB3 OXTABS
     99 #  else
    100 #   define TAB3 0
    101 #  endif
    102 # endif
    103 #endif
    104 
    105 #include "screen.h"
    106 #include "boolean.h"
    107 
    108 #define putcap(str)     ((str) != NULL ? (void)tputs(str, 1, putstdout) : (void)0)
    109 
    110 extern char *myname;
    111 
    112 char ch_erase;
    113 char ch_kill;
    114 char ch_werase;
    115 char smart_terminal;
    116 int  screen_length;
    117 int  screen_width;
    118 
    119 char PC;
    120 
    121 static int  tc_overstrike;
    122 static char termcap_buf[1024];
    123 static char string_buffer[1024];
    124 static char home[15];
    125 static char lower_left[15];
    126 static char *tc_clear_line;
    127 static char *tc_clear_screen;
    128 static char *tc_clear_to_end;
    129 static char *tc_cursor_motion;
    130 static char *tc_start_standout;
    131 static char *tc_end_standout;
    132 static char *terminal_init;
    133 static char *terminal_end;
    134 
    135 #ifdef USE_SGTTY
    136 static struct sgttyb old_settings;
    137 static struct sgttyb new_settings;
    138 #endif
    139 #ifdef USE_TERMIO
    140 static struct termio old_settings;
    141 static struct termio new_settings;
    142 #endif
    143 #ifdef USE_TERMIOS
    144 static struct termios old_settings;
    145 static struct termios new_settings;
    146 #endif
    147 static char is_a_terminal = No;
    148 #ifdef TOStop
    149 static int old_lword;
    150 static int new_lword;
    151 #endif
    152 
    153 #define	STDIN	0
    154 #define	STDOUT	1
    155 #define	STDERR	2
    156 
    157 /* This has to be defined as a subroutine for tputs (instead of a macro) */
    158 
    159 static int
    160 putstdout(TPUTS_PUTC_ARGTYPE ch)
    161 
    162 {
    163     return putchar((int)ch);
    164 }
    165 
    166 void
    167 screen_getsize()
    168 
    169 {
    170     char *go;
    171 #ifdef TIOCGWINSZ
    172 
    173     struct winsize ws;
    174 
    175     if (ioctl (1, TIOCGWINSZ, &ws) != -1)
    176     {
    177 	if (ws.ws_row != 0)
    178 	{
    179 	    screen_length = ws.ws_row;
    180 	}
    181 	if (ws.ws_col != 0)
    182 	{
    183 	    screen_width = ws.ws_col - 1;
    184 	}
    185     }
    186 
    187 #else
    188 #ifdef TIOCGSIZE
    189 
    190     struct ttysize ts;
    191 
    192     if (ioctl (1, TIOCGSIZE, &ts) != -1)
    193     {
    194 	if (ts.ts_lines != 0)
    195 	{
    196 	    screen_length = ts.ts_lines;
    197 	}
    198 	if (ts.ts_cols != 0)
    199 	{
    200 	    screen_width = ts.ts_cols - 1;
    201 	}
    202     }
    203 
    204 #endif /* TIOCGSIZE */
    205 #endif /* TIOCGWINSZ */
    206 
    207     if ((go = tgoto(tc_cursor_motion, 0, screen_length - 1)) != NULL)
    208 	(void) strcpy(lower_left, go);
    209     else
    210 	lower_left[0] = '\0';
    211 }
    212 
    213 int
    214 screen_readtermcap(int interactive)
    215 
    216 {
    217     char *bufptr;
    218     char *PCptr;
    219     char *term_name;
    220     char *go;
    221     int status;
    222 
    223     /* set defaults in case we aren't smart */
    224     screen_width = MAX_COLS;
    225     screen_length = 0;
    226 
    227     if (interactive == No)
    228     {
    229 	/* pretend we have a dumb terminal */
    230 	smart_terminal = No;
    231 	return No;
    232     }
    233 
    234     /* assume we have a smart terminal until proven otherwise */
    235     smart_terminal = Yes;
    236 
    237     /* get the terminal name */
    238     term_name = getenv("TERM");
    239 
    240     /* if there is no TERM, assume it's a dumb terminal */
    241     /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
    242     if (term_name == NULL)
    243     {
    244 	smart_terminal = No;
    245 	return No;
    246     }
    247 
    248     /* now get the termcap entry */
    249     if ((status = tgetent(termcap_buf, term_name)) != 1)
    250     {
    251 	if (status == -1)
    252 	{
    253 	    fprintf(stderr, "%s: can't open termcap file\n", myname);
    254 	}
    255 	else
    256 	{
    257 	    fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
    258 		    myname, term_name);
    259 	}
    260 
    261 	/* pretend it's dumb and proceed */
    262 	smart_terminal = No;
    263 	return No;
    264     }
    265 
    266     /* "hardcopy" immediately indicates a very stupid terminal */
    267     if (tgetflag("hc"))
    268     {
    269 	smart_terminal = No;
    270 	return No;
    271     }
    272 
    273     /* set up common terminal capabilities */
    274     if ((screen_length = tgetnum("li")) <= 0)
    275     {
    276 	screen_length = 0;
    277     }
    278 
    279     /* screen_width is a little different */
    280     if ((screen_width = tgetnum("co")) == -1)
    281     {
    282 	screen_width = 79;
    283     }
    284     else
    285     {
    286 	screen_width -= 1;
    287     }
    288 
    289     /* terminals that overstrike need special attention */
    290     tc_overstrike = tgetflag("os");
    291 
    292     /* initialize the pointer into the termcap string buffer */
    293     bufptr = string_buffer;
    294 
    295     /* get "ce", clear to end */
    296     if (!tc_overstrike)
    297     {
    298 	tc_clear_line = tgetstr("ce", &bufptr);
    299     }
    300 
    301     /* get necessary capabilities */
    302     if ((tc_clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
    303 	(tc_cursor_motion = tgetstr("cm", &bufptr)) == NULL)
    304     {
    305 	smart_terminal = No;
    306 	return No;
    307     }
    308 
    309     /* get some more sophisticated stuff -- these are optional */
    310     tc_clear_to_end   = tgetstr("cd", &bufptr);
    311     terminal_init  = tgetstr("ti", &bufptr);
    312     terminal_end   = tgetstr("te", &bufptr);
    313     tc_start_standout = tgetstr("so", &bufptr);
    314     tc_end_standout   = tgetstr("se", &bufptr);
    315 
    316     /* pad character */
    317     PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
    318 
    319     /* set convenience strings */
    320     if ((go = tgoto(tc_cursor_motion, 0, 0)) != NULL)
    321 	(void) strcpy(home, go);
    322     else
    323 	home[0] = '\0';
    324     /* (lower_left is set in screen_getsize) */
    325 
    326     /* get the actual screen size with an ioctl, if needed */
    327     /* This may change screen_width and screen_length, and it always
    328        sets lower_left. */
    329     screen_getsize();
    330 
    331     /* If screen_length is 0 from both termcap and ioctl then we are dumb */
    332     if (screen_length == 0)
    333     {
    334         smart_terminal = No;
    335         return No;
    336     }
    337 
    338     /* if stdout is not a terminal, pretend we are a dumb terminal */
    339 #ifdef USE_SGTTY
    340     if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1)
    341     {
    342 	smart_terminal = No;
    343     }
    344 #endif
    345 #ifdef USE_TERMIO
    346     if (ioctl(STDOUT, TCGETA, &old_settings) == -1)
    347     {
    348 	smart_terminal = No;
    349     }
    350 #endif
    351 #ifdef USE_TERMIOS
    352     if (tcgetattr(STDOUT, &old_settings) == -1)
    353     {
    354 	smart_terminal = No;
    355     }
    356 #endif
    357 
    358     return smart_terminal;
    359 }
    360 
    361 void
    362 screen_init()
    363 
    364 {
    365     /* get the old settings for safe keeping */
    366 #ifdef USE_SGTTY
    367     if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1)
    368     {
    369 	/* copy the settings so we can modify them */
    370 	new_settings = old_settings;
    371 
    372 	/* turn on CBREAK and turn off character echo and tab expansion */
    373 	new_settings.sg_flags |= CBREAK;
    374 	new_settings.sg_flags &= ~(ECHO|XTABS);
    375 	(void) ioctl(STDOUT, TIOCSETP, &new_settings);
    376 
    377 	/* remember the erase and kill characters */
    378 	ch_erase = old_settings.sg_erase;
    379 	ch_kill  = old_settings.sg_kill;
    380 	ch_werase  = old_settings.sg_werase;
    381 
    382 #ifdef TOStop
    383 	/* get the local mode word */
    384 	(void) ioctl(STDOUT, TIOCLGET, &old_lword);
    385 
    386 	/* modify it */
    387 	new_lword = old_lword | LTOSTOP;
    388 	(void) ioctl(STDOUT, TIOCLSET, &new_lword);
    389 #endif
    390 	/* remember that it really is a terminal */
    391 	is_a_terminal = Yes;
    392 
    393 	/* send the termcap initialization string */
    394 	putcap(terminal_init);
    395     }
    396 #endif
    397 #ifdef USE_TERMIO
    398     if (ioctl(STDOUT, TCGETA, &old_settings) != -1)
    399     {
    400 	/* copy the settings so we can modify them */
    401 	new_settings = old_settings;
    402 
    403 	/* turn off ICANON, character echo and tab expansion */
    404 	new_settings.c_lflag &= ~(ICANON|ECHO);
    405 	new_settings.c_oflag &= ~(TAB3);
    406 	new_settings.c_cc[VMIN] = 1;
    407 	new_settings.c_cc[VTIME] = 0;
    408 	(void) ioctl(STDOUT, TCSETA, &new_settings);
    409 
    410 	/* remember the erase and kill characters */
    411 	ch_erase  = old_settings.c_cc[VERASE];
    412 	ch_kill   = old_settings.c_cc[VKILL];
    413 	ch_werase = old_settings.c_cc[VWERASE];
    414 
    415 	/* remember that it really is a terminal */
    416 	is_a_terminal = Yes;
    417 
    418 	/* send the termcap initialization string */
    419 	putcap(terminal_init);
    420     }
    421 #endif
    422 #ifdef USE_TERMIOS
    423     if (tcgetattr(STDOUT, &old_settings) != -1)
    424     {
    425 	/* copy the settings so we can modify them */
    426 	new_settings = old_settings;
    427 
    428 	/* turn off ICANON, character echo and tab expansion */
    429 	new_settings.c_lflag &= ~(ICANON|ECHO);
    430 	new_settings.c_oflag &= ~(TAB3);
    431 	new_settings.c_cc[VMIN] = 1;
    432 	new_settings.c_cc[VTIME] = 0;
    433 	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
    434 
    435 	/* remember the erase and kill characters */
    436 	ch_erase  = old_settings.c_cc[VERASE];
    437 	ch_kill   = old_settings.c_cc[VKILL];
    438 	ch_werase = old_settings.c_cc[VWERASE];
    439 
    440 	/* remember that it really is a terminal */
    441 	is_a_terminal = Yes;
    442 
    443 	/* send the termcap initialization string */
    444 	putcap(terminal_init);
    445     }
    446 #endif
    447 
    448     if (!is_a_terminal)
    449     {
    450 	/* not a terminal at all---consider it dumb */
    451 	smart_terminal = No;
    452     }
    453 }
    454 
    455 void
    456 screen_end()
    457 
    458 {
    459     /* move to the lower left, clear the line and send "te" */
    460     if (smart_terminal)
    461     {
    462 	putcap(lower_left);
    463 	putcap(tc_clear_line);
    464 	fflush(stdout);
    465 	putcap(terminal_end);
    466     }
    467 
    468     /* if we have settings to reset, then do so */
    469     if (is_a_terminal)
    470     {
    471 #ifdef USE_SGTTY
    472 	(void) ioctl(STDOUT, TIOCSETP, &old_settings);
    473 #ifdef TOStop
    474 	(void) ioctl(STDOUT, TIOCLSET, &old_lword);
    475 #endif
    476 #endif
    477 #ifdef USE_TERMIO
    478 	(void) ioctl(STDOUT, TCSETA, &old_settings);
    479 #endif
    480 #ifdef USE_TERMIOS
    481 	(void) tcsetattr(STDOUT, TCSADRAIN, &old_settings);
    482 #endif
    483     }
    484 }
    485 
    486 void
    487 screen_reinit()
    488 
    489 {
    490     /* install our settings if it is a terminal */
    491     if (is_a_terminal)
    492     {
    493 #ifdef USE_SGTTY
    494 	(void) ioctl(STDOUT, TIOCSETP, &new_settings);
    495 #ifdef TOStop
    496 	(void) ioctl(STDOUT, TIOCLSET, &new_lword);
    497 #endif
    498 #endif
    499 #ifdef USE_TERMIO
    500 	(void) ioctl(STDOUT, TCSETA, &new_settings);
    501 #endif
    502 #ifdef USE_TERMIOS
    503 	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
    504 #endif
    505     }
    506 
    507     /* send init string */
    508     if (smart_terminal)
    509     {
    510 	putcap(terminal_init);
    511     }
    512 }
    513 
    514 void
    515 screen_move(int x, int y)
    516 
    517 {
    518     char *go = tgoto(tc_cursor_motion, x, y);
    519     if (go)
    520 	tputs(go, 1, putstdout);
    521 }
    522 
    523 void
    524 screen_standout(const char *msg)
    525 
    526 {
    527     if (smart_terminal)
    528     {
    529 	putcap(tc_start_standout);
    530 	fputs(msg, stdout);
    531 	putcap(tc_end_standout);
    532     }
    533     else
    534     {
    535 	fputs(msg, stdout);
    536     }
    537 }
    538 
    539 void
    540 screen_clear(void)
    541 
    542 {
    543     if (smart_terminal)
    544     {
    545 	putcap(tc_clear_screen);
    546     }
    547 }
    548 
    549 int
    550 screen_cte(void)
    551 
    552 {
    553     if (smart_terminal)
    554     {
    555 	if (tc_clear_to_end)
    556 	{
    557 	    putcap(tc_clear_to_end);
    558 	    return(Yes);
    559 	}
    560     }
    561     return(No);
    562 }
    563 
    564 void
    565 screen_cleareol(int len)
    566 
    567 {
    568     int i;
    569 
    570     if (smart_terminal && !tc_overstrike && len > 0)
    571     {
    572 	if (tc_clear_line)
    573 	{
    574 	    putcap(tc_clear_line);
    575 	    return;
    576 	}
    577 	else
    578 	{
    579 	    i = 0;
    580 	    while (i++ < 0)
    581 	    {
    582 		putchar(' ');
    583 	    }
    584 	    i = 0;
    585 	    while (i++ < 0)
    586 	    {
    587 		putchar('\b');
    588 	    }
    589 	    return;
    590 	}
    591     }
    592     return;
    593 }
    594 
    595 void
    596 screen_home(void)
    597 
    598 {
    599     if (smart_terminal)
    600     {
    601 	putcap(home);
    602     }
    603 }
    604 
    605 
    606