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
36int  overstrike;
37int  screen_length;
38int  screen_width;
39char ch_erase;
40char ch_kill;
41char smart_terminal;
42static char termcap_buf[1024];
43static char string_buffer[1024];
44static char home[15];
45static char lower_left[15];
46char *clear_line;
47static char *clear_screen;
48char *clear_to_end;
49char *cursor_motion;
50static char *start_standout;
51static char *end_standout;
52static char *terminal_init;
53static char *terminal_end;
54
55static struct termios old_settings;
56static struct termios new_settings;
57static char is_a_terminal = false;
58
59#define NON_INTERACTIVE_MODE_VIRTUAL_SCREEN_WIDTH 1024
60
61void
62init_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
182void
183init_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
216void
217end_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
235void
236reinit_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
251void
252get_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
274void
275top_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
289void
290top_clear(void)
291{
292    if (smart_terminal)
293    {
294	putcap(clear_screen);
295    }
296}
297
298int
299clear_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