Merge branch 'vendor/OPENSSL'
[dragonfly.git] / contrib / top / screen.c
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)     (void)((str) != NULL ? tputs(str, 1, putstdout) : 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
160 putstdout(TPUTS_PUTC_ARGTYPE ch)
161
162 {
163     return putchar((int)ch);
164 }
165
166 void
167 screen_getsize()
168
169 {
170
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     (void) strcpy(lower_left, tgoto(tc_cursor_motion, 0, screen_length - 1));
208 }
209
210 int
211 screen_readtermcap(int interactive)
212
213 {
214     char *bufptr;
215     char *PCptr;
216     char *term_name;
217     char *getenv();
218     int status;
219
220     /* set defaults in case we aren't smart */
221     screen_width = MAX_COLS;
222     screen_length = 0;
223
224     if (interactive == No)
225     {
226         /* pretend we have a dumb terminal */
227         smart_terminal = No;
228         return No;
229     }
230
231     /* assume we have a smart terminal until proven otherwise */
232     smart_terminal = Yes;
233
234     /* get the terminal name */
235     term_name = getenv("TERM");
236
237     /* if there is no TERM, assume it's a dumb terminal */
238     /* patch courtesy of Sam Horrocks at telegraph.ics.uci.edu */
239     if (term_name == NULL)
240     {
241         smart_terminal = No;
242         return No;
243     }
244
245     /* now get the termcap entry */
246     if ((status = tgetent(termcap_buf, term_name)) != 1)
247     {
248         if (status == -1)
249         {
250             fprintf(stderr, "%s: can't open termcap file\n", myname);
251         }
252         else
253         {
254             fprintf(stderr, "%s: no termcap entry for a `%s' terminal\n",
255                     myname, term_name);
256         }
257
258         /* pretend it's dumb and proceed */
259         smart_terminal = No;
260         return No;
261     }
262
263     /* "hardcopy" immediately indicates a very stupid terminal */
264     if (tgetflag("hc"))
265     {
266         smart_terminal = No;
267         return No;
268     }
269
270     /* set up common terminal capabilities */
271     if ((screen_length = tgetnum("li")) <= 0)
272     {
273         screen_length = smart_terminal = 0;
274         return No;
275     }
276
277     /* screen_width is a little different */
278     if ((screen_width = tgetnum("co")) == -1)
279     {
280         screen_width = 79;
281     }
282     else
283     {
284         screen_width -= 1;
285     }
286
287     /* terminals that overstrike need special attention */
288     tc_overstrike = tgetflag("os");
289
290     /* initialize the pointer into the termcap string buffer */
291     bufptr = string_buffer;
292
293     /* get "ce", clear to end */
294     if (!tc_overstrike)
295     {
296         tc_clear_line = tgetstr("ce", &bufptr);
297     }
298
299     /* get necessary capabilities */
300     if ((tc_clear_screen  = tgetstr("cl", &bufptr)) == NULL ||
301         (tc_cursor_motion = tgetstr("cm", &bufptr)) == NULL)
302     {
303         smart_terminal = No;
304         return No;
305     }
306
307     /* get some more sophisticated stuff -- these are optional */
308     tc_clear_to_end   = tgetstr("cd", &bufptr);
309     terminal_init  = tgetstr("ti", &bufptr);
310     terminal_end   = tgetstr("te", &bufptr);
311     tc_start_standout = tgetstr("so", &bufptr);
312     tc_end_standout   = tgetstr("se", &bufptr);
313
314     /* pad character */
315     PC = (PCptr = tgetstr("pc", &bufptr)) ? *PCptr : 0;
316
317     /* set convenience strings */
318     (void) strcpy(home, tgoto(tc_cursor_motion, 0, 0));
319     /* (lower_left is set in screen_getsize) */
320
321     /* get the actual screen size with an ioctl, if needed */
322     /* This may change screen_width and screen_length, and it always
323        sets lower_left. */
324     screen_getsize();
325
326     /* if stdout is not a terminal, pretend we are a dumb terminal */
327 #ifdef USE_SGTTY
328     if (ioctl(STDOUT, TIOCGETP, &old_settings) == -1)
329     {
330         smart_terminal = No;
331     }
332 #endif
333 #ifdef USE_TERMIO
334     if (ioctl(STDOUT, TCGETA, &old_settings) == -1)
335     {
336         smart_terminal = No;
337     }
338 #endif
339 #ifdef USE_TERMIOS
340     if (tcgetattr(STDOUT, &old_settings) == -1)
341     {
342         smart_terminal = No;
343     }
344 #endif
345
346     return smart_terminal;
347 }
348
349 void
350 screen_init()
351
352 {
353     /* get the old settings for safe keeping */
354 #ifdef USE_SGTTY
355     if (ioctl(STDOUT, TIOCGETP, &old_settings) != -1)
356     {
357         /* copy the settings so we can modify them */
358         new_settings = old_settings;
359
360         /* turn on CBREAK and turn off character echo and tab expansion */
361         new_settings.sg_flags |= CBREAK;
362         new_settings.sg_flags &= ~(ECHO|XTABS);
363         (void) ioctl(STDOUT, TIOCSETP, &new_settings);
364
365         /* remember the erase and kill characters */
366         ch_erase = old_settings.sg_erase;
367         ch_kill  = old_settings.sg_kill;
368         ch_werase  = old_settings.sg_werase;
369
370 #ifdef TOStop
371         /* get the local mode word */
372         (void) ioctl(STDOUT, TIOCLGET, &old_lword);
373
374         /* modify it */
375         new_lword = old_lword | LTOSTOP;
376         (void) ioctl(STDOUT, TIOCLSET, &new_lword);
377 #endif
378         /* remember that it really is a terminal */
379         is_a_terminal = Yes;
380
381         /* send the termcap initialization string */
382         putcap(terminal_init);
383     }
384 #endif
385 #ifdef USE_TERMIO
386     if (ioctl(STDOUT, TCGETA, &old_settings) != -1)
387     {
388         /* copy the settings so we can modify them */
389         new_settings = old_settings;
390
391         /* turn off ICANON, character echo and tab expansion */
392         new_settings.c_lflag &= ~(ICANON|ECHO);
393         new_settings.c_oflag &= ~(TAB3);
394         new_settings.c_cc[VMIN] = 1;
395         new_settings.c_cc[VTIME] = 0;
396         (void) ioctl(STDOUT, TCSETA, &new_settings);
397
398         /* remember the erase and kill characters */
399         ch_erase  = old_settings.c_cc[VERASE];
400         ch_kill   = old_settings.c_cc[VKILL];
401         ch_werase = old_settings.c_cc[VWERASE];
402
403         /* remember that it really is a terminal */
404         is_a_terminal = Yes;
405
406         /* send the termcap initialization string */
407         putcap(terminal_init);
408     }
409 #endif
410 #ifdef USE_TERMIOS
411     if (tcgetattr(STDOUT, &old_settings) != -1)
412     {
413         /* copy the settings so we can modify them */
414         new_settings = old_settings;
415
416         /* turn off ICANON, character echo and tab expansion */
417         new_settings.c_lflag &= ~(ICANON|ECHO);
418         new_settings.c_oflag &= ~(TAB3);
419         new_settings.c_cc[VMIN] = 1;
420         new_settings.c_cc[VTIME] = 0;
421         (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
422
423         /* remember the erase and kill characters */
424         ch_erase  = old_settings.c_cc[VERASE];
425         ch_kill   = old_settings.c_cc[VKILL];
426         ch_werase = old_settings.c_cc[VWERASE];
427
428         /* remember that it really is a terminal */
429         is_a_terminal = Yes;
430
431         /* send the termcap initialization string */
432         putcap(terminal_init);
433     }
434 #endif
435
436     if (!is_a_terminal)
437     {
438         /* not a terminal at all---consider it dumb */
439         smart_terminal = No;
440     }
441 }
442
443 void
444 screen_end()
445
446 {
447     /* move to the lower left, clear the line and send "te" */
448     if (smart_terminal)
449     {
450         putcap(lower_left);
451         putcap(tc_clear_line);
452         fflush(stdout);
453         putcap(terminal_end);
454     }
455
456     /* if we have settings to reset, then do so */
457     if (is_a_terminal)
458     {
459 #ifdef USE_SGTTY
460         (void) ioctl(STDOUT, TIOCSETP, &old_settings);
461 #ifdef TOStop
462         (void) ioctl(STDOUT, TIOCLSET, &old_lword);
463 #endif
464 #endif
465 #ifdef USE_TERMIO
466         (void) ioctl(STDOUT, TCSETA, &old_settings);
467 #endif
468 #ifdef USE_TERMIOS
469         (void) tcsetattr(STDOUT, TCSADRAIN, &old_settings);
470 #endif
471     }
472 }
473
474 void
475 screen_reinit()
476
477 {
478     /* install our settings if it is a terminal */
479     if (is_a_terminal)
480     {
481 #ifdef USE_SGTTY
482         (void) ioctl(STDOUT, TIOCSETP, &new_settings);
483 #ifdef TOStop
484         (void) ioctl(STDOUT, TIOCLSET, &new_lword);
485 #endif
486 #endif
487 #ifdef USE_TERMIO
488         (void) ioctl(STDOUT, TCSETA, &new_settings);
489 #endif
490 #ifdef USE_TERMIOS
491         (void) tcsetattr(STDOUT, TCSADRAIN, &new_settings);
492 #endif
493     }
494
495     /* send init string */
496     if (smart_terminal)
497     {
498         putcap(terminal_init);
499     }
500 }
501
502 void
503 screen_move(int x, int y)
504
505 {
506     tputs(tgoto(tc_cursor_motion, x, y), 1, putstdout);
507 }
508
509 void
510 screen_standout(char *msg)
511
512 {
513     if (smart_terminal)
514     {
515         putcap(tc_start_standout);
516         fputs(msg, stdout);
517         putcap(tc_end_standout);
518     }
519     else
520     {
521         fputs(msg, stdout);
522     }
523 }
524
525 void
526 screen_clear()
527
528 {
529     if (smart_terminal)
530     {
531         putcap(tc_clear_screen);
532     }
533 }
534
535 int
536 screen_cte()
537
538 {
539     if (smart_terminal)
540     {
541         if (tc_clear_to_end)
542         {
543             putcap(tc_clear_to_end);
544             return(Yes);
545         }
546     }
547     return(No);
548 }
549
550 void
551 screen_cleareol(int len)
552
553 {
554     int i;
555
556     if (smart_terminal && !tc_overstrike && len > 0)
557     {
558         if (tc_clear_line)
559         {
560             putcap(tc_clear_line);
561             return;
562         }
563         else
564         {
565             i = 0;
566             while (i++ < 0)
567             {
568                 putchar(' ');
569             }
570             i = 0;
571             while (i++ < 0)
572             {
573                 putchar('\b');
574             }
575             return;
576         }
577     }
578     return;
579 }
580
581 void
582 screen_home()
583
584 {
585     if (smart_terminal)
586     {
587         putcap(home);
588     }
589 }
590
591