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