screen.c revision 65937
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 65937 2000-09-16 19:36:18Z peter $
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
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
238init_screen()
239
240{
241    /* get the old settings for safe keeping */
242#ifdef SGTTY
243    if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1)
244    {
245	/* copy the settings so we can modify them */
246	new_settings = old_settings;
247
248	/* turn on CBREAK and turn off character echo and tab expansion */
249	new_settings.sg_flags |= CBREAK;
250	new_settings.sg_flags &= ~(ECHO|XTABS);
251	(void) ioctl(STDOUT, TIOCSETP, &new_settings);
252
253	/* remember the erase and kill characters */
254	ch_erase = old_settings.sg_erase;
255	ch_kill  = old_settings.sg_kill;
256
257#ifdef TOStop
258	/* get the local mode word */
259	(void) ioctl(STDOUT, TIOCLGET, &old_lword);
260
261	/* modify it */
262	new_lword = old_lword | LTOSTOP;
263	(void) ioctl(STDOUT, TIOCLSET, &new_lword);
264#endif
265	/* remember that it really is a terminal */
266	is_a_terminal = Yes;
267
268	/* send the termcap initialization string */
269	putcap(terminal_init);
270    }
271#endif
272#ifdef TERMIO
273    if (ioctl(STDOUT, TCGETA, &old_settings) != -1)
274    {
275	/* copy the settings so we can modify them */
276	new_settings = old_settings;
277
278	/* turn off ICANON, character echo and tab expansion */
279	new_settings.c_lflag &= ~(ICANON|ECHO);
280	new_settings.c_oflag &= ~(TAB3);
281	new_settings.c_cc[VMIN] = 1;
282	new_settings.c_cc[VTIME] = 0;
283	(void) ioctl(STDOUT, TCSETA, &new_settings);
284
285	/* remember the erase and kill characters */
286	ch_erase = old_settings.c_cc[VERASE];
287	ch_kill  = old_settings.c_cc[VKILL];
288
289	/* remember that it really is a terminal */
290	is_a_terminal = Yes;
291
292	/* send the termcap initialization string */
293	putcap(terminal_init);
294    }
295#endif
296#ifdef TERMIOS
297    if (tcgetattr(STDOUT, &old_settings) != -1)
298    {
299	/* copy the settings so we can modify them */
300	new_settings = old_settings;
301
302	/* turn off ICANON, character echo and tab expansion */
303	new_settings.c_lflag &= ~(ICANON|ECHO);
304	new_settings.c_oflag &= ~(TAB3);
305	new_settings.c_cc[VMIN] = 1;
306	new_settings.c_cc[VTIME] = 0;
307	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
308
309	/* remember the erase and kill characters */
310	ch_erase = old_settings.c_cc[VERASE];
311	ch_kill  = old_settings.c_cc[VKILL];
312
313	/* remember that it really is a terminal */
314	is_a_terminal = Yes;
315
316	/* send the termcap initialization string */
317	putcap(terminal_init);
318    }
319#endif
320
321    if (!is_a_terminal)
322    {
323	/* not a terminal at all---consider it dumb */
324	smart_terminal = No;
325    }
326}
327
328end_screen()
329
330{
331    /* move to the lower left, clear the line and send "te" */
332    if (smart_terminal)
333    {
334	putcap(lower_left);
335	putcap(clear_line);
336	fflush(stdout);
337	putcap(terminal_end);
338    }
339
340    /* if we have settings to reset, then do so */
341    if (is_a_terminal)
342    {
343#ifdef SGTTY
344	(void) ioctl(STDOUT, TIOCSETP, &old_settings);
345#ifdef TOStop
346	(void) ioctl(STDOUT, TIOCLSET, &old_lword);
347#endif
348#endif
349#ifdef TERMIO
350	(void) ioctl(STDOUT, TCSETA, &old_settings);
351#endif
352#ifdef TERMIOS
353	(void) tcsetattr(STDOUT, TCSADRAIN, &old_settings);
354#endif
355    }
356}
357
358reinit_screen()
359
360{
361    /* install our settings if it is a terminal */
362    if (is_a_terminal)
363    {
364#ifdef SGTTY
365	(void) ioctl(STDOUT, TIOCSETP, &new_settings);
366#ifdef TOStop
367	(void) ioctl(STDOUT, TIOCLSET, &new_lword);
368#endif
369#endif
370#ifdef TERMIO
371	(void) ioctl(STDOUT, TCSETA, &new_settings);
372#endif
373#ifdef TERMIOS
374	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
375#endif
376    }
377
378    /* send init string */
379    if (smart_terminal)
380    {
381	putcap(terminal_init);
382    }
383}
384
385get_screensize()
386
387{
388
389#ifdef TIOCGWINSZ
390
391    struct winsize ws;
392
393    if (ioctl (1, TIOCGWINSZ, &ws) != -1)
394    {
395	if (ws.ws_row != 0)
396	{
397	    screen_length = ws.ws_row;
398	}
399	if (ws.ws_col != 0)
400	{
401	    screen_width = ws.ws_col - 1;
402	}
403    }
404
405#else
406#ifdef TIOCGSIZE
407
408    struct ttysize ts;
409
410    if (ioctl (1, TIOCGSIZE, &ts) != -1)
411    {
412	if (ts.ts_lines != 0)
413	{
414	    screen_length = ts.ts_lines;
415	}
416	if (ts.ts_cols != 0)
417	{
418	    screen_width = ts.ts_cols - 1;
419	}
420    }
421
422#endif /* TIOCGSIZE */
423#endif /* TIOCGWINSZ */
424
425    (void) strncpy(lower_left, tgoto(cursor_motion, 0, screen_length - 1),
426	sizeof(lower_left) - 1);
427    lower_left[sizeof(lower_left) - 1] = '\0';
428}
429
430standout(msg)
431
432char *msg;
433
434{
435    if (smart_terminal)
436    {
437	putcap(start_standout);
438	fputs(msg, stdout);
439	putcap(end_standout);
440    }
441    else
442    {
443	fputs(msg, stdout);
444    }
445}
446
447clear()
448
449{
450    if (smart_terminal)
451    {
452	putcap(clear_screen);
453    }
454}
455
456clear_eol(len)
457
458int len;
459
460{
461    if (smart_terminal && !overstrike && len > 0)
462    {
463	if (clear_line)
464	{
465	    putcap(clear_line);
466	    return(0);
467	}
468	else
469	{
470	    while (len-- > 0)
471	    {
472		putchar(' ');
473	    }
474	    return(1);
475	}
476    }
477    return(-1);
478}
479
480go_home()
481
482{
483    if (smart_terminal)
484    {
485	putcap(home);
486    }
487}
488
489/* This has to be defined as a subroutine for tputs (instead of a macro) */
490
491putstdout(ch)
492
493char ch;
494
495{
496    putchar(ch);
497}
498
499