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