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
34int  overstrike;
35int  screen_length;
36int  screen_width;
37char ch_erase;
38char ch_kill;
39char smart_terminal;
40static char termcap_buf[1024];
41static char string_buffer[1024];
42static char home[15];
43static char lower_left[15];
44char *clear_line;
45static char *clear_screen;
46char *clear_to_end;
47char *cursor_motion;
48static char *start_standout;
49static char *end_standout;
50static char *terminal_init;
51static char *terminal_end;
52
53static struct termios old_settings;
54static struct termios new_settings;
55static char is_a_terminal = false;
56
57#define NON_INTERACTIVE_MODE_VIRTUAL_SCREEN_WIDTH 1024
58
59void
60init_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
180void
181init_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
214void
215end_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
233void
234reinit_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
249void
250get_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
272void
273top_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
287void
288top_clear(void)
289{
290    if (smart_terminal)
291    {
292	putcap(clear_screen);
293    }
294}
295
296int
297clear_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