Merge from vendor branch LESS:
[dragonfly.git] / contrib / top / screen.c
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: src/contrib/top/screen.c,v 1.2.6.1 2000/09/20 02:27:57 jkh Exp $
12  * $DragonFly: src/contrib/top/screen.c,v 1.2 2003/06/17 04:24:07 dillon Exp $
13  */
14
15 /*  This file contains the routines that interface to termcap and stty/gtty.
16  *
17  *  Paul Vixie, February 1987: converted to use ioctl() instead of stty/gtty.
18  *
19  *  I put in code to turn on the TOSTOP bit while top was running, but I
20  *  didn't really like the results.  If you desire it, turn on the
21  *  preprocessor variable "TOStop".   --wnl
22  */
23
24 #include "os.h"
25 #include "top.h"
26
27 #include <sys/ioctl.h>
28 #ifdef CBREAK
29 # include <sgtty.h>
30 # define SGTTY
31 #else
32 # ifdef TCGETA
33 #  define TERMIO
34 #  include <termio.h>
35 # else
36 #  define TERMIOS
37 #  include <termios.h>
38 # endif
39 #endif
40 #if defined(TERMIO) || defined(TERMIOS)
41 # ifndef TAB3
42 #  ifdef OXTABS
43 #   define TAB3 OXTABS
44 #  else
45 #   define TAB3 0
46 #  endif
47 # endif
48 #endif
49 #include "screen.h"
50 #include "boolean.h"
51
52 extern char *myname;
53
54 int putstdout();
55
56 int  overstrike;
57 int  screen_length;
58 int  screen_width;
59 char ch_erase;
60 char ch_kill;
61 char smart_terminal;
62 char PC;
63 char *tgetstr();
64 char *tgoto();
65 char termcap_buf[1024];
66 char string_buffer[1024];
67 char home[15];
68 char lower_left[15];
69 char *clear_line;
70 char *clear_screen;
71 char *clear_to_end;
72 char *cursor_motion;
73 char *start_standout;
74 char *end_standout;
75 char *terminal_init;
76 char *terminal_end;
77
78 #ifdef SGTTY
79 static struct sgttyb old_settings;
80 static struct sgttyb new_settings;
81 #endif
82 #ifdef TERMIO
83 static struct termio old_settings;
84 static struct termio new_settings;
85 #endif
86 #ifdef TERMIOS
87 static struct termios old_settings;
88 static struct termios new_settings;
89 #endif
90 static char is_a_terminal = No;
91 #ifdef TOStop
92 static int old_lword;
93 static int new_lword;
94 #endif
95
96 #define STDIN   0
97 #define STDOUT  1
98 #define STDERR  2
99
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
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
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
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
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
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
448 clear()
449
450 {
451     if (smart_terminal)
452     {
453         putcap(clear_screen);
454     }
455 }
456
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
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
492 putstdout(ch)
493
494 char ch;
495
496 {
497     putchar(ch);
498 }
499