1 /* $OpenBSD: screen.c,v 1.8 2002/07/15 17:20:36 deraadt Exp $ */ 2 3 /* 4 * Top users/processes display for Unix 5 * Version 3 6 * 7 * Copyright (c) 1984, 1989, William LeFebvre, Rice University 8 * Copyright (c) 1989, 1990, 1992, William LeFebvre, Northwestern University 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR OR HIS EMPLOYER BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 /* This file contains the routines that interface to termcap and stty/gtty. 32 * 33 * Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty. 34 * 35 * I put in code to turn on the TOSTOP bit while top was running, but I 36 * didn't really like the results. If you desire it, turn on the 37 * preprocessor variable "TOStop". --wnl 38 */ 39 40 #include <sys/types.h> 41 #include <sys/ioctl.h> 42 #include <stdio.h> 43 #include <stdlib.h> 44 #include <string.h> 45 #include <term.h> 46 #include <termios.h> 47 #include <unistd.h> 48 49 #include "top.h" 50 #include "screen.h" 51 #include "boolean.h" 52 53 extern char *myname; 54 55 int overstrike; 56 int screen_length; 57 int screen_width; 58 char ch_erase; 59 char ch_kill; 60 char smart_terminal; 61 char PC; 62 char string_buffer[1024]; 63 char home[15]; 64 char lower_left[15]; 65 char *clear_line; 66 char *clear_scr; 67 char *clear_to_end; 68 char *cursor_motion; 69 char *start_standout; 70 char *end_standout; 71 char *terminal_init; 72 char *terminal_end; 73 short ospeed; 74 75 static struct termios old_settings; 76 static struct termios new_settings; 77 78 static char is_a_terminal = No; 79 80 void init_termcap(interactive) 81 82 int interactive; 83 84 { 85 char *bufptr; 86 char *PCptr; 87 char *term_name; 88 int status; 89 90 /* set defaults in case we aren't smart */ 91 screen_width = MAX_COLS; 92 screen_length = 0; 93 94 if (!interactive) 95 { 96 /* pretend we have a dumb terminal */ 97 smart_terminal = No; 98 return; 99 } 100 101 /* assume we have a smart terminal until proven otherwise */ 102 smart_terminal = Yes; 103 104 /* get the terminal name */ 105 term_name = getenv("TERM"); 106 107 /* if there is no TERM, assume it's a dumb terminal */ 108 /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */ 109 if (term_name == NULL) 110 { 111 smart_terminal = No; 112 return; 113 } 114 115 /* now get the termcap entry */ 116 if ((status = tgetent(NULL, term_name)) != 1) 117 { 118 if (status == -1) 119 { 120 fprintf(stderr, "%s: can't open termcap file\n", myname); 121 } 122 else 123 { 124 fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n", 125 myname, term_name); 126 } 127 128 /* pretend it's dumb and proceed */ 129 smart_terminal = No; 130 return; 131 } 132 133 /* "hardcopy" immediately indicates a very stupid terminal */ 134 if (tgetflag("hc")) 135 { 136 smart_terminal = No; 137 return; 138 } 139 140 /* set up common terminal capabilities */ 141 if ((screen_length = tgetnum("li")) <= Header_lines) 142 { 143 screen_length = smart_terminal = 0; 144 return; 145 } 146 147 /* screen_width is a little different */ 148 if ((screen_width = tgetnum("co")) == -1) 149 { 150 screen_width = 79; 151 } 152 else 153 { 154 screen_width -= 1; 155 } 156 157 /* terminals that overstrike need special attention */ 158 overstrike = tgetflag("os"); 159 160 /* initialize the pointer into the termcap string buffer */ 161 bufptr = string_buffer; 162 163 /* get "ce", clear to end */ 164 if (!overstrike) 165 { 166 clear_line = tgetstr("ce", &bufptr); 167 } 168 169 /* get necessary capabilities */ 170 if ((clear_scr = tgetstr("cl", &bufptr)) == NULL || 171 (cursor_motion = tgetstr("cm", &bufptr)) == NULL) 172 { 173 smart_terminal = No; 174 return; 175 } 176 177 /* get some more sophisticated stuff -- these are optional */ 178 clear_to_end = tgetstr("cd", &bufptr); 179 terminal_init = tgetstr("ti", &bufptr); 180 terminal_end = tgetstr("te", &bufptr); 181 start_standout = tgetstr("so", &bufptr); 182 end_standout = tgetstr("se", &bufptr); 183 184 /* pad character */ 185 PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0; 186 187 /* set convenience strings */ 188 (void) strncpy(home, tgoto(cursor_motion, 0, 0), sizeof (home) -1); 189 home[sizeof (home) -1] = 0; 190 /* (lower_left is set in get_screensize) */ 191 192 /* get the actual screen size with an ioctl, if needed */ 193 /* This may change screen_width and screen_length, and it always 194 sets lower_left. */ 195 get_screensize(); 196 197 /* if stdout is not a terminal, pretend we are a dumb terminal */ 198 if (tcgetattr(STDOUT_FILENO, &old_settings) == -1) 199 { 200 smart_terminal = No; 201 } 202 } 203 204 void init_screen() 205 206 { 207 /* get the old settings for safe keeping */ 208 if (tcgetattr(STDOUT_FILENO, &old_settings) != -1) 209 { 210 /* copy the settings so we can modify them */ 211 new_settings = old_settings; 212 213 /* turn off ICANON, character echo and tab expansion */ 214 new_settings.c_lflag &= ~(ICANON|ECHO); 215 new_settings.c_oflag &= ~(OXTABS); 216 new_settings.c_cc[VMIN] = 1; 217 new_settings.c_cc[VTIME] = 0; 218 (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings); 219 220 /* remember the erase and kill characters */ 221 ch_erase = old_settings.c_cc[VERASE]; 222 ch_kill = old_settings.c_cc[VKILL]; 223 224 /* remember that it really is a terminal */ 225 is_a_terminal = Yes; 226 227 /* send the termcap initialization string */ 228 putcap(terminal_init); 229 } 230 231 if (!is_a_terminal) 232 { 233 /* not a terminal at all---consider it dumb */ 234 smart_terminal = No; 235 } 236 } 237 238 void end_screen() 239 240 { 241 /* move to the lower left, clear the line and send "te" */ 242 if (smart_terminal) 243 { 244 putcap(lower_left); 245 putcap(clear_line); 246 fflush(stdout); 247 putcap(terminal_end); 248 } 249 250 /* if we have settings to reset, then do so */ 251 if (is_a_terminal) 252 { 253 (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_settings); 254 } 255 } 256 257 void reinit_screen() 258 259 { 260 /* install our settings if it is a terminal */ 261 if (is_a_terminal) 262 { 263 (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings); 264 } 265 266 /* send init string */ 267 if (smart_terminal) 268 { 269 putcap(terminal_init); 270 } 271 } 272 273 void get_screensize() 274 275 { 276 struct winsize ws; 277 278 if (ioctl (STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) 279 { 280 if (ws.ws_row != 0) 281 { 282 screen_length = ws.ws_row; 283 } 284 if (ws.ws_col != 0) 285 { 286 screen_width = ws.ws_col - 1; 287 } 288 } 289 290 (void) strncpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1), 291 sizeof (lower_left) -1); 292 lower_left[sizeof(lower_left) -1] = 0; 293 } 294 295 void standout(msg) 296 297 char *msg; 298 299 { 300 if (smart_terminal) 301 { 302 putcap(start_standout); 303 if (fputs(msg, stdout) == EOF) 304 exit(1); 305 putcap(end_standout); 306 } 307 else 308 { 309 if (fputs(msg, stdout) == EOF) 310 exit(1); 311 } 312 } 313 314 void clear() 315 316 { 317 if (smart_terminal) 318 { 319 putcap(clear_scr); 320 } 321 } 322 323 int clear_eol(len) 324 325 int len; 326 327 { 328 if (smart_terminal && !overstrike && len > 0) 329 { 330 if (clear_line) 331 { 332 putcap(clear_line); 333 return(0); 334 } 335 else 336 { 337 while (len-- > 0) 338 { 339 if (putchar(' ') == EOF) 340 exit(1); 341 } 342 return(1); 343 } 344 } 345 return(-1); 346 } 347 348 void go_home() 349 350 { 351 if (smart_terminal) 352 { 353 putcap(home); 354 } 355 } 356 357 /* This has to be defined as a subroutine for tputs (instead of a macro) */ 358 359 int putstdout(ch) 360 361 int ch; 362 363 { 364 int ret; 365 366 ret = putchar(ch); 367 if (ret == EOF) 368 exit(1); 369 return (ret); 370 } 371 372