1 /* $OpenBSD: screen.c,v 1.13 2003/06/16 17:24:44 millert 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 /* 32 * This file contains the routines that interface to termcap and stty/gtty. 33 * 34 * Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty. 35 * 36 * I put in code to turn on the TOSTOP bit while top was running, but I didn't 37 * really like the results. If you desire it, turn on the preprocessor 38 * variable "TOStop". --wnl 39 */ 40 41 #include <sys/types.h> 42 #include <sys/ioctl.h> 43 #include <err.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 #include <string.h> 47 #include <term.h> 48 #include <termios.h> 49 #include <unistd.h> 50 51 #include "top.h" 52 #include "screen.h" 53 #include "boolean.h" 54 55 int overstrike, screen_length, screen_width; 56 char ch_erase, ch_kill, smart_terminal, PC; 57 char string_buffer[1024], home[15], lower_left[15]; 58 char *clear_line, *clear_scr, *clear_to_end; 59 char *cursor_motion, *start_standout, *end_standout; 60 char *terminal_init, *terminal_end; 61 short ospeed; 62 63 static struct termios old_settings, new_settings; 64 65 static char is_a_terminal = No; 66 67 void 68 init_termcap(int interactive) 69 { 70 char *bufptr, *PCptr, *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 /* pretend we have a dumb terminal */ 79 smart_terminal = No; 80 return; 81 } 82 /* assume we have a smart terminal until proven otherwise */ 83 smart_terminal = Yes; 84 85 /* get the terminal name */ 86 term_name = getenv("TERM"); 87 88 /* if there is no TERM, assume it's a dumb terminal */ 89 /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */ 90 if (term_name == NULL) { 91 smart_terminal = No; 92 return; 93 } 94 95 /* now get the termcap entry */ 96 if ((status = tgetent(NULL, term_name)) != 1) { 97 if (status == -1) 98 warnx("can't open termcap file"); 99 else 100 warnx("no termcap entry for a `%s' terminal", term_name); 101 102 /* pretend it's dumb and proceed */ 103 smart_terminal = No; 104 return; 105 } 106 107 /* "hardcopy" immediately indicates a very stupid terminal */ 108 if (tgetflag("hc")) { 109 smart_terminal = No; 110 return; 111 } 112 /* set up common terminal capabilities */ 113 if ((screen_length = tgetnum("li")) <= Header_lines) { 114 screen_length = smart_terminal = 0; 115 return; 116 } 117 118 /* screen_width is a little different */ 119 if ((screen_width = tgetnum("co")) == -1) 120 screen_width = 79; 121 else 122 screen_width -= 1; 123 124 /* terminals that overstrike need special attention */ 125 overstrike = tgetflag("os"); 126 127 /* initialize the pointer into the termcap string buffer */ 128 bufptr = string_buffer; 129 130 /* get "ce", clear to end */ 131 if (!overstrike) { 132 clear_line = tgetstr("ce", &bufptr); 133 } 134 /* get necessary capabilities */ 135 if ((clear_scr = tgetstr("cl", &bufptr)) == NULL || 136 (cursor_motion = tgetstr("cm", &bufptr)) == NULL) { 137 smart_terminal = No; 138 return; 139 } 140 /* get some more sophisticated stuff -- these are optional */ 141 clear_to_end = tgetstr("cd", &bufptr); 142 terminal_init = tgetstr("ti", &bufptr); 143 terminal_end = tgetstr("te", &bufptr); 144 start_standout = tgetstr("so", &bufptr); 145 end_standout = tgetstr("se", &bufptr); 146 147 /* pad character */ 148 PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0; 149 150 /* set convenience strings */ 151 (void) strlcpy(home, tgoto(cursor_motion, 0, 0), sizeof(home)); 152 /* (lower_left is set in get_screensize) */ 153 154 /* get the actual screen size with an ioctl, if needed */ 155 /* 156 * This may change screen_width and screen_length, and it always sets 157 * lower_left. 158 */ 159 get_screensize(); 160 161 /* if stdout is not a terminal, pretend we are a dumb terminal */ 162 if (tcgetattr(STDOUT_FILENO, &old_settings) == -1) 163 smart_terminal = No; 164 } 165 166 void 167 init_screen(void) 168 { 169 /* get the old settings for safe keeping */ 170 if (tcgetattr(STDOUT_FILENO, &old_settings) != -1) { 171 /* copy the settings so we can modify them */ 172 new_settings = old_settings; 173 174 /* turn off ICANON, character echo and tab expansion */ 175 new_settings.c_lflag &= ~(ICANON | ECHO); 176 new_settings.c_oflag &= ~(OXTABS); 177 new_settings.c_cc[VMIN] = 1; 178 new_settings.c_cc[VTIME] = 0; 179 (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings); 180 181 /* remember the erase and kill characters */ 182 ch_erase = old_settings.c_cc[VERASE]; 183 ch_kill = old_settings.c_cc[VKILL]; 184 185 /* remember that it really is a terminal */ 186 is_a_terminal = Yes; 187 188 /* send the termcap initialization string */ 189 putcap(terminal_init); 190 } 191 if (!is_a_terminal) { 192 /* not a terminal at all---consider it dumb */ 193 smart_terminal = No; 194 } 195 } 196 197 void 198 end_screen(void) 199 { 200 /* move to the lower left, clear the line and send "te" */ 201 if (smart_terminal) { 202 putcap(lower_left); 203 putcap(clear_line); 204 fflush(stdout); 205 putcap(terminal_end); 206 } 207 208 /* if we have settings to reset, then do so */ 209 if (is_a_terminal) 210 (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &old_settings); 211 } 212 213 void 214 reinit_screen(void) 215 { 216 /* install our settings if it is a terminal */ 217 if (is_a_terminal) 218 (void) tcsetattr(STDOUT_FILENO, TCSADRAIN, &new_settings); 219 220 /* send init string */ 221 if (smart_terminal) 222 putcap(terminal_init); 223 } 224 225 void 226 get_screensize(void) 227 { 228 struct winsize ws; 229 230 if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) != -1) { 231 if (ws.ws_row != 0) 232 screen_length = ws.ws_row; 233 if (ws.ws_col != 0) 234 screen_width = ws.ws_col - 1; 235 } 236 (void) strlcpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1), 237 sizeof(lower_left)); 238 } 239 240 void 241 standout(char *msg) 242 { 243 if (smart_terminal) { 244 putcap(start_standout); 245 if (fputs(msg, stdout) == EOF) 246 exit(1); 247 putcap(end_standout); 248 } else { 249 if (fputs(msg, stdout) == EOF) 250 exit(1); 251 } 252 } 253 254 void 255 clear() 256 { 257 if (smart_terminal) 258 putcap(clear_scr); 259 } 260 261 int 262 clear_eol(int len) 263 { 264 if (smart_terminal && !overstrike && len > 0) { 265 if (clear_line) { 266 putcap(clear_line); 267 return (0); 268 } else { 269 while (len-- > 0) { 270 if (putchar(' ') == EOF) 271 exit(1); 272 } 273 return (1); 274 } 275 } 276 return (-1); 277 } 278 279 void 280 go_home(void) 281 { 282 if (smart_terminal) 283 putcap(home); 284 } 285 286 /* This has to be defined as a subroutine for tputs (instead of a macro) */ 287 int 288 putstdout(int ch) 289 { 290 int ret; 291 292 ret = putchar(ch); 293 if (ret == EOF) 294 exit(1); 295 return (ret); 296 } 297