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: stable/11/contrib/top/screen.c 332948 2018-04-24 17:37:29Z lidl $
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 "os.h"
24#include "top.h"
25
26#include <sys/ioctl.h>
27#ifdef CBREAK
28# include <sgtty.h>
29# define SGTTY
30#else
31# ifdef TCGETA
32#  define TERMIO
33#  include <termio.h>
34# else
35#  define TERMIOS
36#  include <termios.h>
37# endif
38#endif
39#if defined(TERMIO) || defined(TERMIOS)
40# ifndef TAB3
41#  ifdef OXTABS
42#   define TAB3 OXTABS
43#  else
44#   define TAB3 0
45#  endif
46# endif
47#endif
48#include <curses.h>
49#include <termcap.h>
50#include "screen.h"
51#include "boolean.h"
52
53extern char *myname;
54
55
56int  overstrike;
57int  screen_length;
58int  screen_width;
59char ch_erase;
60char ch_kill;
61char smart_terminal;
62char PC;
63char *tgetstr();
64char *tgoto();
65char termcap_buf[1024];
66char string_buffer[1024];
67char home[15];
68char lower_left[15];
69char *clear_line;
70char *clear_screen;
71char *clear_to_end;
72char *cursor_motion;
73char *start_standout;
74char *end_standout;
75char *terminal_init;
76char *terminal_end;
77
78#ifdef SGTTY
79static struct sgttyb old_settings;
80static struct sgttyb new_settings;
81#endif
82#ifdef TERMIO
83static struct termio old_settings;
84static struct termio new_settings;
85#endif
86#ifdef TERMIOS
87static struct termios old_settings;
88static struct termios new_settings;
89#endif
90static char is_a_terminal = No;
91#ifdef TOStop
92static int old_lword;
93static int new_lword;
94#endif
95
96#define	STDIN	0
97#define	STDOUT	1
98#define	STDERR	2
99
100void
101init_termcap(interactive)
102
103int interactive;
104
105{
106    char *bufptr;
107    char *PCptr;
108    char *term_name;
109    char *getenv();
110    int status;
111
112    /* set defaults in case we aren't smart */
113    screen_width = MAX_COLS;
114    screen_length = 0;
115
116    if (!interactive)
117    {
118	/* pretend we have a dumb terminal */
119	smart_terminal = No;
120	return;
121    }
122
123    /* assume we have a smart terminal until proven otherwise */
124    smart_terminal = Yes;
125
126    /* get the terminal name */
127    term_name = getenv("TERM");
128
129    /* if there is no TERM, assume it's a dumb terminal */
130    /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
131    if (term_name == NULL)
132    {
133	smart_terminal = No;
134	return;
135    }
136
137    /* now get the termcap entry */
138    if ((status = tgetent(termcap_buf, term_name)) != 1)
139    {
140	if (status == -1)
141	{
142	    fprintf(stderr, "%s: can't open termcap file\n", myname);
143	}
144	else
145	{
146	    fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
147		    myname, term_name);
148	}
149
150	/* pretend it's dumb and proceed */
151	smart_terminal = No;
152	return;
153    }
154
155    /* "hardcopy" immediately indicates a very stupid terminal */
156    if (tgetflag("hc"))
157    {
158	smart_terminal = No;
159	return;
160    }
161
162    /* set up common terminal capabilities */
163    if ((screen_length = tgetnum("li")) <= 0)
164    {
165	screen_length = smart_terminal = 0;
166	return;
167    }
168
169    /* screen_width is a little different */
170    if ((screen_width = tgetnum("co")) == -1)
171    {
172	screen_width = 79;
173    }
174    else
175    {
176	screen_width -= 1;
177    }
178
179    /* terminals that overstrike need special attention */
180    overstrike = tgetflag("os");
181
182    /* initialize the pointer into the termcap string buffer */
183    bufptr = string_buffer;
184
185    /* get "ce", clear to end */
186    if (!overstrike)
187    {
188	clear_line = tgetstr("ce", &bufptr);
189    }
190
191    /* get necessary capabilities */
192    if ((clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
193	(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
194    {
195	smart_terminal = No;
196	return;
197    }
198
199    /* get some more sophisticated stuff -- these are optional */
200    clear_to_end   = tgetstr("cd", &bufptr);
201    terminal_init  = tgetstr("ti", &bufptr);
202    terminal_end   = tgetstr("te", &bufptr);
203    start_standout = tgetstr("so", &bufptr);
204    end_standout   = tgetstr("se", &bufptr);
205
206    /* pad character */
207    PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
208
209    /* set convenience strings */
210    (void) strncpy(home, tgoto(cursor_motion, 0, 0), sizeof(home) - 1);
211    home[sizeof(home) - 1] = '\0';
212    /* (lower_left is set in get_screensize) */
213
214    /* get the actual screen size with an ioctl, if needed */
215    /* This may change screen_width and screen_length, and it always
216       sets lower_left. */
217    get_screensize();
218
219    /* if stdout is not a terminal, pretend we are a dumb terminal */
220#ifdef SGTTY
221    if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1)
222    {
223	smart_terminal = No;
224    }
225#endif
226#ifdef TERMIO
227    if (ioctl(STDOUT, TCGETA, &old_settings) == -1)
228    {
229	smart_terminal = No;
230    }
231#endif
232#ifdef TERMIOS
233    if (tcgetattr(STDOUT, &old_settings) == -1)
234    {
235	smart_terminal = No;
236    }
237#endif
238}
239
240void
241init_screen()
242
243{
244    /* get the old settings for safe keeping */
245#ifdef SGTTY
246    if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1)
247    {
248	/* copy the settings so we can modify them */
249	new_settings = old_settings;
250
251	/* turn on CBREAK and turn off character echo and tab expansion */
252	new_settings.sg_flags |= CBREAK;
253	new_settings.sg_flags &= ~(ECHO|XTABS);
254	(void) ioctl(STDOUT, TIOCSETP, &new_settings);
255
256	/* remember the erase and kill characters */
257	ch_erase = old_settings.sg_erase;
258	ch_kill  = old_settings.sg_kill;
259
260#ifdef TOStop
261	/* get the local mode word */
262	(void) ioctl(STDOUT, TIOCLGET, &old_lword);
263
264	/* modify it */
265	new_lword = old_lword | LTOSTOP;
266	(void) ioctl(STDOUT, TIOCLSET, &new_lword);
267#endif
268	/* remember that it really is a terminal */
269	is_a_terminal = Yes;
270
271	/* send the termcap initialization string */
272	putcap(terminal_init);
273    }
274#endif
275#ifdef TERMIO
276    if (ioctl(STDOUT, TCGETA, &old_settings) != -1)
277    {
278	/* copy the settings so we can modify them */
279	new_settings = old_settings;
280
281	/* turn off ICANON, character echo and tab expansion */
282	new_settings.c_lflag &= ~(ICANON|ECHO);
283	new_settings.c_oflag &= ~(TAB3);
284	new_settings.c_cc[VMIN] = 1;
285	new_settings.c_cc[VTIME] = 0;
286	(void) ioctl(STDOUT, TCSETA, &new_settings);
287
288	/* remember the erase and kill characters */
289	ch_erase = old_settings.c_cc[VERASE];
290	ch_kill  = old_settings.c_cc[VKILL];
291
292	/* remember that it really is a terminal */
293	is_a_terminal = Yes;
294
295	/* send the termcap initialization string */
296	putcap(terminal_init);
297    }
298#endif
299#ifdef TERMIOS
300    if (tcgetattr(STDOUT, &old_settings) != -1)
301    {
302	/* copy the settings so we can modify them */
303	new_settings = old_settings;
304
305	/* turn off ICANON, character echo and tab expansion */
306	new_settings.c_lflag &= ~(ICANON|ECHO);
307	new_settings.c_oflag &= ~(TAB3);
308	new_settings.c_cc[VMIN] = 1;
309	new_settings.c_cc[VTIME] = 0;
310	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
311
312	/* remember the erase and kill characters */
313	ch_erase = old_settings.c_cc[VERASE];
314	ch_kill  = old_settings.c_cc[VKILL];
315
316	/* remember that it really is a terminal */
317	is_a_terminal = Yes;
318
319	/* send the termcap initialization string */
320	putcap(terminal_init);
321    }
322#endif
323
324    if (!is_a_terminal)
325    {
326	/* not a terminal at all---consider it dumb */
327	smart_terminal = No;
328    }
329}
330
331void
332end_screen()
333
334{
335    /* move to the lower left, clear the line and send "te" */
336    if (smart_terminal)
337    {
338	putcap(lower_left);
339	putcap(clear_line);
340	fflush(stdout);
341	putcap(terminal_end);
342    }
343
344    /* if we have settings to reset, then do so */
345    if (is_a_terminal)
346    {
347#ifdef SGTTY
348	(void) ioctl(STDOUT, TIOCSETP, &old_settings);
349#ifdef TOStop
350	(void) ioctl(STDOUT, TIOCLSET, &old_lword);
351#endif
352#endif
353#ifdef TERMIO
354	(void) ioctl(STDOUT, TCSETA, &old_settings);
355#endif
356#ifdef TERMIOS
357	(void) tcsetattr(STDOUT, TCSADRAIN, &old_settings);
358#endif
359    }
360}
361
362void
363reinit_screen()
364
365{
366    /* install our settings if it is a terminal */
367    if (is_a_terminal)
368    {
369#ifdef SGTTY
370	(void) ioctl(STDOUT, TIOCSETP, &new_settings);
371#ifdef TOStop
372	(void) ioctl(STDOUT, TIOCLSET, &new_lword);
373#endif
374#endif
375#ifdef TERMIO
376	(void) ioctl(STDOUT, TCSETA, &new_settings);
377#endif
378#ifdef TERMIOS
379	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
380#endif
381    }
382
383    /* send init string */
384    if (smart_terminal)
385    {
386	putcap(terminal_init);
387    }
388}
389
390void
391get_screensize()
392
393{
394
395#ifdef TIOCGWINSZ
396
397    struct winsize ws;
398
399    if (ioctl (1, TIOCGWINSZ, &ws) != -1)
400    {
401	if (ws.ws_row != 0)
402	{
403	    screen_length = ws.ws_row;
404	}
405	if (ws.ws_col != 0)
406	{
407	    screen_width = ws.ws_col - 1;
408	}
409    }
410
411#else
412#ifdef TIOCGSIZE
413
414    struct ttysize ts;
415
416    if (ioctl (1, TIOCGSIZE, &ts) != -1)
417    {
418	if (ts.ts_lines != 0)
419	{
420	    screen_length = ts.ts_lines;
421	}
422	if (ts.ts_cols != 0)
423	{
424	    screen_width = ts.ts_cols - 1;
425	}
426    }
427
428#endif /* TIOCGSIZE */
429#endif /* TIOCGWINSZ */
430
431    (void) strncpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1),
432	sizeof(lower_left) - 1);
433    lower_left[sizeof(lower_left) - 1] = '\0';
434}
435
436void
437top_standout(char *msg)
438{
439    if (smart_terminal)
440    {
441	putcap(start_standout);
442	fputs(msg, stdout);
443	putcap(end_standout);
444    }
445    else
446    {
447	fputs(msg, stdout);
448    }
449}
450
451void
452top_clear()
453{
454    if (smart_terminal)
455    {
456	putcap(clear_screen);
457    }
458}
459
460int
461clear_eol(int len)
462{
463    if (smart_terminal && !overstrike && len > 0)
464    {
465	if (clear_line)
466	{
467	    putcap(clear_line);
468	    return(0);
469	}
470	else
471	{
472	    while (len-- > 0)
473	    {
474		putchar(' ');
475	    }
476	    return(1);
477	}
478    }
479    return(-1);
480}
481
482void
483go_home()
484
485{
486    if (smart_terminal)
487    {
488	putcap(home);
489    }
490}
491
492/* This has to be defined as a subroutine for tputs (instead of a macro) */
493
494int
495putstdout(int ch)
496{
497    return putchar(ch);
498}
499