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