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