1 /* $OpenBSD: screen.c,v 1.14 2004/05/09 22:14:15 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 /*
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
init_termcap(int interactive)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
init_screen(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
end_screen(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
reinit_screen(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
get_screensize(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
standout(char * msg)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
clear(void)255 clear(void)
256 {
257 if (smart_terminal)
258 putcap(clear_scr);
259 }
260
261 int
clear_eol(int len)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
go_home(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
putstdout(int ch)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