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