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