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