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