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