xref: /minix/external/bsd/top/dist/screen.c (revision b89261ba)
1 /*
2  * Copyright (c) 1984 through 2008, William LeFebvre
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *
8  *     * Redistributions of source code must retain the above copyright
9  * notice, this list of conditions and the following disclaimer.
10  *
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *
16  *     * Neither the name of William LeFebvre nor the names of other
17  * contributors may be used to endorse or promote products derived from
18  * this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 /*
34  *  Top users/processes display for Unix
35  *  Version 3
36  */
37 
38 /*  This file contains the routines that interface to termcap and stty/gtty.
39  *
40  *  Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty.
41  *
42  *  I put in code to turn on the TOSTOP bit while top was running, but I
43  *  didn't really like the results.  If you desire it, turn on the
44  *  preprocessor variable "TOStop".   --wnl
45  */
46 
47 #include "os.h"
48 #include "top.h"
49 
50 #if HAVE_CURSES_H && HAVE_TERM_H
51 #include <curses.h>
52 #include <term.h>
53 #else
54 #if HAVE_TERMCAP_H
55 #include <termcap.h>
56 #else
57 #if HAVE_CURSES_H
58 #include <curses.h>
59 #endif
60 #endif
61 #endif
62 
63 #if !HAVE_DECL_TPUTS
64 int tputs(const char *, int, int (*)(int));
65 #endif
66 #if !HAVE_DECL_TGOTO
67 char *tgoto(const char *, int, int);
68 #endif
69 #if !HAVE_DECL_TGETENT
70 int tgetent(const char *, char *);
71 #endif
72 #if !HAVE_DECL_TGETFLAG
73 int tgetflag(const char *);
74 #endif
75 #if !HAVE_DECL_TGETNUM
76 int tgetnum(const char *);
77 #endif
78 #if !HAVE_DECL_TGETSTR
79 char *tgetstr(const char *, char **);
80 #endif
81 
82 #include <sys/ioctl.h>
83 #ifdef CBREAK
84 # include <sgtty.h>
85 # define USE_SGTTY
86 #else
87 # ifdef TCGETA
88 #  define USE_TERMIO
89 #  include <termio.h>
90 # else
91 #  define USE_TERMIOS
92 #  include <termios.h>
93 # endif
94 #endif
95 #if defined(USE_TERMIO) || defined(USE_TERMIOS)
96 # ifndef TAB3
97 #  ifdef OXTABS
98 #   define TAB3 OXTABS
99 #  else
100 #   define TAB3 0
101 #  endif
102 # endif
103 #endif
104 
105 #include "screen.h"
106 #include "boolean.h"
107 
108 #define putcap(str)     ((str) != NULL ? (void)tputs(str, 1, putstdout) : (void)0)
109 
110 extern char *myname;
111 
112 char ch_erase;
113 char ch_kill;
114 char ch_werase;
115 char smart_terminal;
116 int  screen_length;
117 int  screen_width;
118 
119 char PC;
120 
121 static int  tc_overstrike;
122 static char termcap_buf[1024];
123 static char string_buffer[1024];
124 static char home[15];
125 static char lower_left[15];
126 static char *tc_clear_line;
127 static char *tc_clear_screen;
128 static char *tc_clear_to_end;
129 static char *tc_cursor_motion;
130 static char *tc_start_standout;
131 static char *tc_end_standout;
132 static char *terminal_init;
133 static char *terminal_end;
134 
135 #ifdef USE_SGTTY
136 static struct sgttyb old_settings;
137 static struct sgttyb new_settings;
138 #endif
139 #ifdef USE_TERMIO
140 static struct termio old_settings;
141 static struct termio new_settings;
142 #endif
143 #ifdef USE_TERMIOS
144 static struct termios old_settings;
145 static struct termios new_settings;
146 #endif
147 static char is_a_terminal = No;
148 #ifdef TOStop
149 static int old_lword;
150 static int new_lword;
151 #endif
152 
153 #define	STDIN	0
154 #define	STDOUT	1
155 #define	STDERR	2
156 
157 /* This has to be defined as a subroutine for tputs (instead of a macro) */
158 
159 static int
putstdout(TPUTS_PUTC_ARGTYPE ch)160 putstdout(TPUTS_PUTC_ARGTYPE ch)
161 
162 {
163     return putchar((int)ch);
164 }
165 
166 void
screen_getsize()167 screen_getsize()
168 
169 {
170     char *go;
171 #ifdef TIOCGWINSZ
172 
173     struct winsize ws;
174 
175     if (ioctl (1, TIOCGWINSZ, &ws) != -1)
176     {
177 	if (ws.ws_row != 0)
178 	{
179 	    screen_length = ws.ws_row;
180 	}
181 	if (ws.ws_col != 0)
182 	{
183 	    screen_width = ws.ws_col - 1;
184 	}
185     }
186 
187 #else
188 #ifdef TIOCGSIZE
189 
190     struct ttysize ts;
191 
192     if (ioctl (1, TIOCGSIZE, &ts) != -1)
193     {
194 	if (ts.ts_lines != 0)
195 	{
196 	    screen_length = ts.ts_lines;
197 	}
198 	if (ts.ts_cols != 0)
199 	{
200 	    screen_width = ts.ts_cols - 1;
201 	}
202     }
203 
204 #endif /* TIOCGSIZE */
205 #endif /* TIOCGWINSZ */
206 
207     if ((go = tgoto(tc_cursor_motion, 0, screen_length - 1)) != NULL)
208 	(void) strcpy(lower_left, go);
209     else
210 	lower_left[0] = '\0';
211 }
212 
213 int
screen_readtermcap(int interactive)214 screen_readtermcap(int interactive)
215 
216 {
217     char *bufptr;
218     char *PCptr;
219     char *term_name;
220     char *go;
221     int status;
222 
223     /* set defaults in case we aren't smart */
224     screen_width = MAX_COLS;
225     screen_length = 0;
226 
227     if (interactive == No)
228     {
229 	/* pretend we have a dumb terminal */
230 	smart_terminal = No;
231 	return No;
232     }
233 
234     /* assume we have a smart terminal until proven otherwise */
235     smart_terminal = Yes;
236 
237     /* get the terminal name */
238     term_name = getenv("TERM");
239 
240     /* if there is no TERM, assume it's a dumb terminal */
241     /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
242     if (term_name == NULL)
243     {
244 	smart_terminal = No;
245 	return No;
246     }
247 
248     /* now get the termcap entry */
249     if ((status = tgetent(termcap_buf, term_name)) != 1)
250     {
251 	if (status == -1)
252 	{
253 	    fprintf(stderr, "%s: can't open termcap file\n", myname);
254 	}
255 	else
256 	{
257 	    fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
258 		    myname, term_name);
259 	}
260 
261 	/* pretend it's dumb and proceed */
262 	smart_terminal = No;
263 	return No;
264     }
265 
266     /* "hardcopy" immediately indicates a very stupid terminal */
267     if (tgetflag("hc"))
268     {
269 	smart_terminal = No;
270 	return No;
271     }
272 
273     /* set up common terminal capabilities */
274     if ((screen_length = tgetnum("li")) <= 0)
275     {
276 	screen_length = 0;
277     }
278 
279     /* screen_width is a little different */
280     if ((screen_width = tgetnum("co")) == -1)
281     {
282 	screen_width = 79;
283     }
284     else
285     {
286 	screen_width -= 1;
287     }
288 
289     /* terminals that overstrike need special attention */
290     tc_overstrike = tgetflag("os");
291 
292     /* initialize the pointer into the termcap string buffer */
293     bufptr = string_buffer;
294 
295     /* get "ce", clear to end */
296     if (!tc_overstrike)
297     {
298 	tc_clear_line = tgetstr("ce", &bufptr);
299     }
300 
301     /* get necessary capabilities */
302     if ((tc_clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
303 	(tc_cursor_motion = tgetstr("cm", &bufptr)) == NULL)
304     {
305 	smart_terminal = No;
306 	return No;
307     }
308 
309     /* get some more sophisticated stuff -- these are optional */
310     tc_clear_to_end   = tgetstr("cd", &bufptr);
311     terminal_init  = tgetstr("ti", &bufptr);
312     terminal_end   = tgetstr("te", &bufptr);
313     tc_start_standout = tgetstr("so", &bufptr);
314     tc_end_standout   = tgetstr("se", &bufptr);
315 
316     /* pad character */
317     PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
318 
319     /* set convenience strings */
320     if ((go = tgoto(tc_cursor_motion, 0, 0)) != NULL)
321 	(void) strcpy(home, go);
322     else
323 	home[0] = '\0';
324     /* (lower_left is set in screen_getsize) */
325 
326     /* get the actual screen size with an ioctl, if needed */
327     /* This may change screen_width and screen_length, and it always
328        sets lower_left. */
329     screen_getsize();
330 
331     /* If screen_length is 0 from both termcap and ioctl then we are dumb */
332     if (screen_length == 0)
333     {
334         smart_terminal = No;
335         return No;
336     }
337 
338     /* if stdout is not a terminal, pretend we are a dumb terminal */
339 #ifdef USE_SGTTY
340     if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1)
341     {
342 	smart_terminal = No;
343     }
344 #endif
345 #ifdef USE_TERMIO
346     if (ioctl(STDOUT, TCGETA, &old_settings) == -1)
347     {
348 	smart_terminal = No;
349     }
350 #endif
351 #ifdef USE_TERMIOS
352     if (tcgetattr(STDOUT, &old_settings) == -1)
353     {
354 	smart_terminal = No;
355     }
356 #endif
357 
358     return smart_terminal;
359 }
360 
361 void
screen_init()362 screen_init()
363 
364 {
365     /* get the old settings for safe keeping */
366 #ifdef USE_SGTTY
367     if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1)
368     {
369 	/* copy the settings so we can modify them */
370 	new_settings = old_settings;
371 
372 	/* turn on CBREAK and turn off character echo and tab expansion */
373 	new_settings.sg_flags |= CBREAK;
374 	new_settings.sg_flags &= ~(ECHO|XTABS);
375 	(void) ioctl(STDOUT, TIOCSETP, &new_settings);
376 
377 	/* remember the erase and kill characters */
378 	ch_erase = old_settings.sg_erase;
379 	ch_kill  = old_settings.sg_kill;
380 	ch_werase  = old_settings.sg_werase;
381 
382 #ifdef TOStop
383 	/* get the local mode word */
384 	(void) ioctl(STDOUT, TIOCLGET, &old_lword);
385 
386 	/* modify it */
387 	new_lword = old_lword | LTOSTOP;
388 	(void) ioctl(STDOUT, TIOCLSET, &new_lword);
389 #endif
390 	/* remember that it really is a terminal */
391 	is_a_terminal = Yes;
392 
393 	/* send the termcap initialization string */
394 	putcap(terminal_init);
395     }
396 #endif
397 #ifdef USE_TERMIO
398     if (ioctl(STDOUT, TCGETA, &old_settings) != -1)
399     {
400 	/* copy the settings so we can modify them */
401 	new_settings = old_settings;
402 
403 	/* turn off ICANON, character echo and tab expansion */
404 	new_settings.c_lflag &= ~(ICANON|ECHO);
405 	new_settings.c_oflag &= ~(TAB3);
406 	new_settings.c_cc[VMIN] = 1;
407 	new_settings.c_cc[VTIME] = 0;
408 	(void) ioctl(STDOUT, TCSETA, &new_settings);
409 
410 	/* remember the erase and kill characters */
411 	ch_erase  = old_settings.c_cc[VERASE];
412 	ch_kill   = old_settings.c_cc[VKILL];
413 	ch_werase = old_settings.c_cc[VWERASE];
414 
415 	/* remember that it really is a terminal */
416 	is_a_terminal = Yes;
417 
418 	/* send the termcap initialization string */
419 	putcap(terminal_init);
420     }
421 #endif
422 #ifdef USE_TERMIOS
423     if (tcgetattr(STDOUT, &old_settings) != -1)
424     {
425 	/* copy the settings so we can modify them */
426 	new_settings = old_settings;
427 
428 	/* turn off ICANON, character echo and tab expansion */
429 	new_settings.c_lflag &= ~(ICANON|ECHO);
430 	new_settings.c_oflag &= ~(TAB3);
431 	new_settings.c_cc[VMIN] = 1;
432 	new_settings.c_cc[VTIME] = 0;
433 	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
434 
435 	/* remember the erase and kill characters */
436 	ch_erase  = old_settings.c_cc[VERASE];
437 	ch_kill   = old_settings.c_cc[VKILL];
438 	ch_werase = old_settings.c_cc[VWERASE];
439 
440 	/* remember that it really is a terminal */
441 	is_a_terminal = Yes;
442 
443 	/* send the termcap initialization string */
444 	putcap(terminal_init);
445     }
446 #endif
447 
448     if (!is_a_terminal)
449     {
450 	/* not a terminal at all---consider it dumb */
451 	smart_terminal = No;
452     }
453 }
454 
455 void
screen_end()456 screen_end()
457 
458 {
459     /* move to the lower left, clear the line and send "te" */
460     if (smart_terminal)
461     {
462 	putcap(lower_left);
463 	putcap(tc_clear_line);
464 	fflush(stdout);
465 	putcap(terminal_end);
466     }
467 
468     /* if we have settings to reset, then do so */
469     if (is_a_terminal)
470     {
471 #ifdef USE_SGTTY
472 	(void) ioctl(STDOUT, TIOCSETP, &old_settings);
473 #ifdef TOStop
474 	(void) ioctl(STDOUT, TIOCLSET, &old_lword);
475 #endif
476 #endif
477 #ifdef USE_TERMIO
478 	(void) ioctl(STDOUT, TCSETA, &old_settings);
479 #endif
480 #ifdef USE_TERMIOS
481 	(void) tcsetattr(STDOUT, TCSADRAIN, &old_settings);
482 #endif
483     }
484 }
485 
486 void
screen_reinit()487 screen_reinit()
488 
489 {
490     /* install our settings if it is a terminal */
491     if (is_a_terminal)
492     {
493 #ifdef USE_SGTTY
494 	(void) ioctl(STDOUT, TIOCSETP, &new_settings);
495 #ifdef TOStop
496 	(void) ioctl(STDOUT, TIOCLSET, &new_lword);
497 #endif
498 #endif
499 #ifdef USE_TERMIO
500 	(void) ioctl(STDOUT, TCSETA, &new_settings);
501 #endif
502 #ifdef USE_TERMIOS
503 	(void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
504 #endif
505     }
506 
507     /* send init string */
508     if (smart_terminal)
509     {
510 	putcap(terminal_init);
511     }
512 }
513 
514 void
screen_move(int x,int y)515 screen_move(int x, int y)
516 
517 {
518     char *go = tgoto(tc_cursor_motion, x, y);
519     if (go)
520 	tputs(go, 1, putstdout);
521 }
522 
523 void
screen_standout(const char * msg)524 screen_standout(const char *msg)
525 
526 {
527     if (smart_terminal)
528     {
529 	putcap(tc_start_standout);
530 	fputs(msg, stdout);
531 	putcap(tc_end_standout);
532     }
533     else
534     {
535 	fputs(msg, stdout);
536     }
537 }
538 
539 void
screen_clear(void)540 screen_clear(void)
541 
542 {
543     if (smart_terminal)
544     {
545 	putcap(tc_clear_screen);
546     }
547 }
548 
549 int
screen_cte(void)550 screen_cte(void)
551 
552 {
553     if (smart_terminal)
554     {
555 	if (tc_clear_to_end)
556 	{
557 	    putcap(tc_clear_to_end);
558 	    return(Yes);
559 	}
560     }
561     return(No);
562 }
563 
564 void
screen_cleareol(int len)565 screen_cleareol(int len)
566 
567 {
568     int i;
569 
570     if (smart_terminal && !tc_overstrike && len > 0)
571     {
572 	if (tc_clear_line)
573 	{
574 	    putcap(tc_clear_line);
575 	    return;
576 	}
577 	else
578 	{
579 	    i = 0;
580 	    while (i++ < 0)
581 	    {
582 		putchar(' ');
583 	    }
584 	    i = 0;
585 	    while (i++ < 0)
586 	    {
587 		putchar('\b');
588 	    }
589 	    return;
590 	}
591     }
592     return;
593 }
594 
595 void
screen_home(void)596 screen_home(void)
597 
598 {
599     if (smart_terminal)
600     {
601 	putcap(home);
602     }
603 }
604 
605 
606