screen.c revision 210386
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: head/contrib/top/screen.c 210386 2010-07-22 18:52:29Z rpaulo $
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
53int putstdout();
54
55int  overstrike;
56int  screen_length;
57int  screen_width;
58char ch_erase;
59char ch_kill;
60char smart_terminal;
61char PC;
62char *tgetstr();
63char *tgoto();
64char termcap_buf[1024];
65char string_buffer[1024];
66char home[15];
67char lower_left[15];
68char *clear_line;
69char *clear_screen;
70char *clear_to_end;
71char *cursor_motion;
72char *start_standout;
73char *end_standout;
74char *terminal_init;
75char *terminal_end;
76
77#ifdef SGTTY
78static struct sgttyb old_settings;
79static struct sgttyb new_settings;
80#endif
81#ifdef TERMIO
82static struct termio old_settings;
83static struct termio new_settings;
84#endif
85#ifdef TERMIOS
86static struct termios old_settings;
87static struct termios new_settings;
88#endif
89static char is_a_terminal = No;
90#ifdef TOStop
91static int old_lword;
92static int new_lword;
93#endif
94
95#define	STDIN	0
96#define	STDOUT	1
97#define	STDERR	2
98
99void
100init_termcap(interactive)
101
102int interactive;
103
104{
105    char *bufptr;
106    char *PCptr;
107    char *term_name;
108    char *getenv();
109    int status;
110
111    /* set defaults in case we aren't smart */
112    screen_width = MAX_COLS;
113    screen_length = 0;
114
115    if (!interactive)
116    {
117	/* pretend we have a dumb terminal */
118	smart_terminal = No;
119	return;
120    }
121
122    /* assume we have a smart terminal until proven otherwise */
123    smart_terminal = Yes;
124
125    /* get the terminal name */
126    term_name = getenv("TERM");
127
128    /* if there is no TERM, assume it's a dumb terminal */
129    /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
130    if (term_name == NULL)
131    {
132	smart_terminal = No;
133	return;
134    }
135
136    /* now get the termcap entry */
137    if ((status = tgetent(termcap_buf, term_name)) != 1)
138    {
139	if (status == -1)
140	{
141	    fprintf(stderr, "%s: can't open termcap file\n", myname);
142	}
143	else
144	{
145	    fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
146		    myname, term_name);
147	}
148
149	/* pretend it's dumb and proceed */
150	smart_terminal = No;
151	return;
152    }
153
154    /* "hardcopy" immediately indicates a very stupid terminal */
155    if (tgetflag("hc"))
156    {
157	smart_terminal = No;
158	return;
159    }
160
161    /* set up common terminal capabilities */
162    if ((screen_length = tgetnum("li")) <= 0)
163    {
164	screen_length = smart_terminal = 0;
165	return;
166    }
167
168    /* screen_width is a little different */
169    if ((screen_width = tgetnum("co")) == -1)
170    {
171	screen_width = 79;
172    }
173    else
174    {
175	screen_width -= 1;
176    }
177
178    /* terminals that overstrike need special attention */
179    overstrike = tgetflag("os");
180
181    /* initialize the pointer into the termcap string buffer */
182    bufptr = string_buffer;
183
184    /* get "ce", clear to end */
185    if (!overstrike)
186    {
187	clear_line = tgetstr("ce", &bufptr);
188    }
189
190    /* get necessary capabilities */
191    if ((clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
192	(cursor_motion = tgetstr("cm", &bufptr)) == NULL)
193    {
194	smart_terminal = No;
195	return;
196    }
197
198    /* get some more sophisticated stuff -- these are optional */
199    clear_to_end   = tgetstr("cd", &bufptr);
200    terminal_init  = tgetstr("ti", &bufptr);
201    terminal_end   = tgetstr("te", &bufptr);
202    start_standout = tgetstr("so", &bufptr);
203    end_standout   = tgetstr("se", &bufptr);
204
205    /* pad character */
206    PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
207
208    /* set convenience strings */
209    (void) strncpy(home, tgoto(cursor_motion, 0, 0), sizeof(home) - 1);
210    home[sizeof(home) - 1] = '\0';
211    /* (lower_left is set in get_screensize) */
212
213    /* get the actual screen size with an ioctl, if needed */
214    /* This may change screen_width and screen_length, and it always
215       sets lower_left. */
216    get_screensize();
217
218    /* if stdout is not a terminal, pretend we are a dumb terminal */
219#ifdef SGTTY
220    if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1)
221    {
222	smart_terminal = No;
223    }
224#endif
225#ifdef TERMIO
226    if (ioctl(STDOUT, TCGETA, &old_settings) == -1)
227    {
228	smart_terminal = No;
229    }
230#endif
231#ifdef TERMIOS
232    if (tcgetattr(STDOUT, &old_settings) == -1)
233    {
234	smart_terminal = No;
235    }
236#endif
237}
238
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
329end_screen()
330
331{
332    /* move to the lower left, clear the line and send "te" */
333    if (smart_terminal)
334    {
335	putcap(lower_left);
336	putcap(clear_line);
337	fflush(stdout);
338	putcap(terminal_end);
339    }
340
341    /* if we have settings to reset, then do so */
342    if (is_a_terminal)
343    {
344#ifdef SGTTY
345	(void) ioctl(STDOUT, TIOCSETP, &old_settings);
346#ifdef TOStop
347	(void) ioctl(STDOUT, TIOCLSET, &old_lword);
348#endif
349#endif
350#ifdef TERMIO
351	(void) ioctl(STDOUT, TCSETA, &old_settings);
352#endif
353#ifdef TERMIOS
354	(void) tcsetattr(STDOUT, TCSADRAIN, &old_settings);
355#endif
356    }
357}
358
359reinit_screen()
360
361{
362    /* install our settings if it is a terminal */
363    if (is_a_terminal)
364    {
365#ifdef SGTTY
366	(void) ioctl(STDOUT, TIOCSETP, &new_settings);
367#ifdef TOStop
368	(void) ioctl(STDOUT, TIOCLSET, &new_lword);
369#endif
370#endif
371#ifdef TERMIO
372	(void) ioctl(STDOUT, TCSETA, &new_settings);
373#endif
374#ifdef TERMIOS
375	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
376#endif
377    }
378
379    /* send init string */
380    if (smart_terminal)
381    {
382	putcap(terminal_init);
383    }
384}
385
386get_screensize()
387
388{
389
390#ifdef TIOCGWINSZ
391
392    struct winsize ws;
393
394    if (ioctl (1, TIOCGWINSZ, &ws) != -1)
395    {
396	if (ws.ws_row != 0)
397	{
398	    screen_length = ws.ws_row;
399	}
400	if (ws.ws_col != 0)
401	{
402	    screen_width = ws.ws_col - 1;
403	}
404    }
405
406#else
407#ifdef TIOCGSIZE
408
409    struct ttysize ts;
410
411    if (ioctl (1, TIOCGSIZE, &ts) != -1)
412    {
413	if (ts.ts_lines != 0)
414	{
415	    screen_length = ts.ts_lines;
416	}
417	if (ts.ts_cols != 0)
418	{
419	    screen_width = ts.ts_cols - 1;
420	}
421    }
422
423#endif /* TIOCGSIZE */
424#endif /* TIOCGWINSZ */
425
426    (void) strncpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1),
427	sizeof(lower_left) - 1);
428    lower_left[sizeof(lower_left) - 1] = '\0';
429}
430
431standout(msg)
432
433char *msg;
434
435{
436    if (smart_terminal)
437    {
438	putcap(start_standout);
439	fputs(msg, stdout);
440	putcap(end_standout);
441    }
442    else
443    {
444	fputs(msg, stdout);
445    }
446}
447
448clear()
449
450{
451    if (smart_terminal)
452    {
453	putcap(clear_screen);
454    }
455}
456
457clear_eol(len)
458
459int len;
460
461{
462    if (smart_terminal && !overstrike && len > 0)
463    {
464	if (clear_line)
465	{
466	    putcap(clear_line);
467	    return(0);
468	}
469	else
470	{
471	    while (len-- > 0)
472	    {
473		putchar(' ');
474	    }
475	    return(1);
476	}
477    }
478    return(-1);
479}
480
481go_home()
482
483{
484    if (smart_terminal)
485    {
486	putcap(home);
487    }
488}
489
490/* This has to be defined as a subroutine for tputs (instead of a macro) */
491
492putstdout(ch)
493
494char ch;
495
496{
497    putchar(ch);
498}
499
500