xref: /NextBSD/contrib/top/screen.c (revision eb1a5f8de9f7ea602c373a710f531abbf81141c4)
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$
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 
51 extern char *myname;
52 
53 int putstdout();
54 
55 int  overstrike;
56 int  screen_length;
57 int  screen_width;
58 char ch_erase;
59 char ch_kill;
60 char smart_terminal;
61 char PC;
62 char *tgetstr();
63 char *tgoto();
64 char termcap_buf[1024];
65 char string_buffer[1024];
66 char home[15];
67 char lower_left[15];
68 char *clear_line;
69 char *clear_screen;
70 char *clear_to_end;
71 char *cursor_motion;
72 char *start_standout;
73 char *end_standout;
74 char *terminal_init;
75 char *terminal_end;
76 
77 #ifdef SGTTY
78 static struct sgttyb old_settings;
79 static struct sgttyb new_settings;
80 #endif
81 #ifdef TERMIO
82 static struct termio old_settings;
83 static struct termio new_settings;
84 #endif
85 #ifdef TERMIOS
86 static struct termios old_settings;
87 static struct termios new_settings;
88 #endif
89 static char is_a_terminal = No;
90 #ifdef TOStop
91 static int old_lword;
92 static int new_lword;
93 #endif
94 
95 #define	STDIN	0
96 #define	STDOUT	1
97 #define	STDERR	2
98 
99 void
init_termcap(interactive)100 init_termcap(interactive)
101 
102 int 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 
init_screen()239 init_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 
end_screen()329 end_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 
reinit_screen()359 reinit_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 
get_screensize()386 get_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 
standout(msg)431 standout(msg)
432 
433 char *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 
clear()448 clear()
449 
450 {
451     if (smart_terminal)
452     {
453 	putcap(clear_screen);
454     }
455 }
456 
clear_eol(len)457 clear_eol(len)
458 
459 int 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 
go_home()481 go_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 
putstdout(ch)492 putstdout(ch)
493 
494 char ch;
495 
496 {
497     putchar(ch);
498 }
499 
500