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 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 (void) strcpy(lower_left, tgoto(tc_cursor_motion, 0, screen_length - 1)); 208 } 209 210 int 211 screen_readtermcap(int interactive) 212 213 { 214 char *bufptr; 215 char *PCptr; 216 char *term_name; 217 int status; 218 219 /* set defaults in case we aren't smart */ 220 screen_width = MAX_COLS; 221 screen_length = 0; 222 223 if (interactive == No) 224 { 225 /* pretend we have a dumb terminal */ 226 smart_terminal = No; 227 return No; 228 } 229 230 /* assume we have a smart terminal until proven otherwise */ 231 smart_terminal = Yes; 232 233 /* get the terminal name */ 234 term_name = getenv("TERM"); 235 236 /* if there is no TERM, assume it's a dumb terminal */ 237 /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */ 238 if (term_name == NULL) 239 { 240 smart_terminal = No; 241 return No; 242 } 243 244 /* now get the termcap entry */ 245 if ((status = tgetent(termcap_buf, term_name)) != 1) 246 { 247 if (status == -1) 248 { 249 fprintf(stderr, "%s: can't open termcap file\n", myname); 250 } 251 else 252 { 253 fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n", 254 myname, term_name); 255 } 256 257 /* pretend it's dumb and proceed */ 258 smart_terminal = No; 259 return No; 260 } 261 262 /* "hardcopy" immediately indicates a very stupid terminal */ 263 if (tgetflag("hc")) 264 { 265 smart_terminal = No; 266 return No; 267 } 268 269 /* set up common terminal capabilities */ 270 if ((screen_length = tgetnum("li")) <= 0) 271 { 272 screen_length = smart_terminal = 0; 273 return No; 274 } 275 276 /* screen_width is a little different */ 277 if ((screen_width = tgetnum("co")) == -1) 278 { 279 screen_width = 79; 280 } 281 else 282 { 283 screen_width -= 1; 284 } 285 286 /* terminals that overstrike need special attention */ 287 tc_overstrike = tgetflag("os"); 288 289 /* initialize the pointer into the termcap string buffer */ 290 bufptr = string_buffer; 291 292 /* get "ce", clear to end */ 293 if (!tc_overstrike) 294 { 295 tc_clear_line = tgetstr("ce", &bufptr); 296 } 297 298 /* get necessary capabilities */ 299 if ((tc_clear_screen = tgetstr("cl", &bufptr)) == NULL || 300 (tc_cursor_motion = tgetstr("cm", &bufptr)) == NULL) 301 { 302 smart_terminal = No; 303 return No; 304 } 305 306 /* get some more sophisticated stuff -- these are optional */ 307 tc_clear_to_end = tgetstr("cd", &bufptr); 308 terminal_init = tgetstr("ti", &bufptr); 309 terminal_end = tgetstr("te", &bufptr); 310 tc_start_standout = tgetstr("so", &bufptr); 311 tc_end_standout = tgetstr("se", &bufptr); 312 313 /* pad character */ 314 PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0; 315 316 /* set convenience strings */ 317 (void) strcpy(home, tgoto(tc_cursor_motion, 0, 0)); 318 /* (lower_left is set in screen_getsize) */ 319 320 /* get the actual screen size with an ioctl, if needed */ 321 /* This may change screen_width and screen_length, and it always 322 sets lower_left. */ 323 screen_getsize(); 324 325 /* if stdout is not a terminal, pretend we are a dumb terminal */ 326 #ifdef USE_SGTTY 327 if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1) 328 { 329 smart_terminal = No; 330 } 331 #endif 332 #ifdef USE_TERMIO 333 if (ioctl(STDOUT, TCGETA, &old_settings) == -1) 334 { 335 smart_terminal = No; 336 } 337 #endif 338 #ifdef USE_TERMIOS 339 if (tcgetattr(STDOUT, &old_settings) == -1) 340 { 341 smart_terminal = No; 342 } 343 #endif 344 345 return smart_terminal; 346 } 347 348 void 349 screen_init() 350 351 { 352 /* get the old settings for safe keeping */ 353 #ifdef USE_SGTTY 354 if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1) 355 { 356 /* copy the settings so we can modify them */ 357 new_settings = old_settings; 358 359 /* turn on CBREAK and turn off character echo and tab expansion */ 360 new_settings.sg_flags |= CBREAK; 361 new_settings.sg_flags &= ~(ECHO|XTABS); 362 (void) ioctl(STDOUT, TIOCSETP, &new_settings); 363 364 /* remember the erase and kill characters */ 365 ch_erase = old_settings.sg_erase; 366 ch_kill = old_settings.sg_kill; 367 ch_werase = old_settings.sg_werase; 368 369 #ifdef TOStop 370 /* get the local mode word */ 371 (void) ioctl(STDOUT, TIOCLGET, &old_lword); 372 373 /* modify it */ 374 new_lword = old_lword | LTOSTOP; 375 (void) ioctl(STDOUT, TIOCLSET, &new_lword); 376 #endif 377 /* remember that it really is a terminal */ 378 is_a_terminal = Yes; 379 380 /* send the termcap initialization string */ 381 putcap(terminal_init); 382 } 383 #endif 384 #ifdef USE_TERMIO 385 if (ioctl(STDOUT, TCGETA, &old_settings) != -1) 386 { 387 /* copy the settings so we can modify them */ 388 new_settings = old_settings; 389 390 /* turn off ICANON, character echo and tab expansion */ 391 new_settings.c_lflag &= ~(ICANON|ECHO); 392 new_settings.c_oflag &= ~(TAB3); 393 new_settings.c_cc[VMIN] = 1; 394 new_settings.c_cc[VTIME] = 0; 395 (void) ioctl(STDOUT, TCSETA, &new_settings); 396 397 /* remember the erase and kill characters */ 398 ch_erase = old_settings.c_cc[VERASE]; 399 ch_kill = old_settings.c_cc[VKILL]; 400 ch_werase = old_settings.c_cc[VWERASE]; 401 402 /* remember that it really is a terminal */ 403 is_a_terminal = Yes; 404 405 /* send the termcap initialization string */ 406 putcap(terminal_init); 407 } 408 #endif 409 #ifdef USE_TERMIOS 410 if (tcgetattr(STDOUT, &old_settings) != -1) 411 { 412 /* copy the settings so we can modify them */ 413 new_settings = old_settings; 414 415 /* turn off ICANON, character echo and tab expansion */ 416 new_settings.c_lflag &= ~(ICANON|ECHO); 417 new_settings.c_oflag &= ~(TAB3); 418 new_settings.c_cc[VMIN] = 1; 419 new_settings.c_cc[VTIME] = 0; 420 (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings); 421 422 /* remember the erase and kill characters */ 423 ch_erase = old_settings.c_cc[VERASE]; 424 ch_kill = old_settings.c_cc[VKILL]; 425 ch_werase = old_settings.c_cc[VWERASE]; 426 427 /* remember that it really is a terminal */ 428 is_a_terminal = Yes; 429 430 /* send the termcap initialization string */ 431 putcap(terminal_init); 432 } 433 #endif 434 435 if (!is_a_terminal) 436 { 437 /* not a terminal at all---consider it dumb */ 438 smart_terminal = No; 439 } 440 } 441 442 void 443 screen_end() 444 445 { 446 /* move to the lower left, clear the line and send "te" */ 447 if (smart_terminal) 448 { 449 putcap(lower_left); 450 putcap(tc_clear_line); 451 fflush(stdout); 452 putcap(terminal_end); 453 } 454 455 /* if we have settings to reset, then do so */ 456 if (is_a_terminal) 457 { 458 #ifdef USE_SGTTY 459 (void) ioctl(STDOUT, TIOCSETP, &old_settings); 460 #ifdef TOStop 461 (void) ioctl(STDOUT, TIOCLSET, &old_lword); 462 #endif 463 #endif 464 #ifdef USE_TERMIO 465 (void) ioctl(STDOUT, TCSETA, &old_settings); 466 #endif 467 #ifdef USE_TERMIOS 468 (void) tcsetattr(STDOUT, TCSADRAIN, &old_settings); 469 #endif 470 } 471 } 472 473 void 474 screen_reinit() 475 476 { 477 /* install our settings if it is a terminal */ 478 if (is_a_terminal) 479 { 480 #ifdef USE_SGTTY 481 (void) ioctl(STDOUT, TIOCSETP, &new_settings); 482 #ifdef TOStop 483 (void) ioctl(STDOUT, TIOCLSET, &new_lword); 484 #endif 485 #endif 486 #ifdef USE_TERMIO 487 (void) ioctl(STDOUT, TCSETA, &new_settings); 488 #endif 489 #ifdef USE_TERMIOS 490 (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings); 491 #endif 492 } 493 494 /* send init string */ 495 if (smart_terminal) 496 { 497 putcap(terminal_init); 498 } 499 } 500 501 void 502 screen_move(int x, int y) 503 504 { 505 tputs(tgoto(tc_cursor_motion, x, y), 1, putstdout); 506 } 507 508 void 509 screen_standout(const char *msg) 510 511 { 512 if (smart_terminal) 513 { 514 putcap(tc_start_standout); 515 fputs(msg, stdout); 516 putcap(tc_end_standout); 517 } 518 else 519 { 520 fputs(msg, stdout); 521 } 522 } 523 524 void 525 screen_clear(void) 526 527 { 528 if (smart_terminal) 529 { 530 putcap(tc_clear_screen); 531 } 532 } 533 534 int 535 screen_cte(void) 536 537 { 538 if (smart_terminal) 539 { 540 if (tc_clear_to_end) 541 { 542 putcap(tc_clear_to_end); 543 return(Yes); 544 } 545 } 546 return(No); 547 } 548 549 void 550 screen_cleareol(int len) 551 552 { 553 int i; 554 555 if (smart_terminal && !tc_overstrike && len > 0) 556 { 557 if (tc_clear_line) 558 { 559 putcap(tc_clear_line); 560 return; 561 } 562 else 563 { 564 i = 0; 565 while (i++ < 0) 566 { 567 putchar(' '); 568 } 569 i = 0; 570 while (i++ < 0) 571 { 572 putchar('\b'); 573 } 574 return; 575 } 576 } 577 return; 578 } 579 580 void 581 screen_home(void) 582 583 { 584 if (smart_terminal) 585 { 586 putcap(home); 587 } 588 } 589 590 591