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