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