screen.c revision 1.3 1 /* $NetBSD: screen.c,v 1.3 1998/01/09 08:03:36 perry Exp $ */
2
3 /*
4 * Copyright (c) 1988 Mark Nudleman
5 * Copyright (c) 1988, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 3. All advertising materials mentioning features or use of this software
17 * must display the following acknowledgement:
18 * This product includes software developed by the University of
19 * California, Berkeley and its contributors.
20 * 4. Neither the name of the University nor the names of its contributors
21 * may be used to endorse or promote products derived from this software
22 * without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 */
36
37 #ifndef lint
38 static char sccsid[] = "@(#)screen.c 8.2 (Berkeley) 4/20/94";
39 #endif /* not lint */
40
41 /*
42 * Routines which deal with the characteristics of the terminal.
43 * Uses termcap to be as terminal-independent as possible.
44 *
45 * {{ Someday this should be rewritten to use curses. }}
46 */
47
48 #include <stdio.h>
49 #include <string.h>
50 #include <less.h>
51
52 #define TERMIOS 1
53
54 #if TERMIO
55 #include <termio.h>
56 #else
57 #if TERMIOS
58 #include <termios.h>
59 #define TAB3 0
60 #include <sys/ioctl.h>
61 #else
62 #include <sgtty.h>
63 #endif
64 #endif
65
66 #ifdef TIOCGWINSZ
67 #include <sys/ioctl.h>
68 #else
69 /*
70 * For the Unix PC (ATT 7300 & 3B1):
71 * Since WIOCGETD is defined in sys/window.h, we can't use that to decide
72 * whether to include sys/window.h. Use SIGPHONE from sys/signal.h instead.
73 */
74 #include <sys/signal.h>
75 #ifdef SIGPHONE
76 #include <sys/window.h>
77 #endif
78 #endif
79
80 /*
81 * Strings passed to tputs() to do various terminal functions.
82 */
83 static char
84 *sc_pad, /* Pad string */
85 *sc_home, /* Cursor home */
86 *sc_addline, /* Add line, scroll down following lines */
87 *sc_lower_left, /* Cursor to last line, first column */
88 *sc_move, /* General cursor positioning */
89 *sc_clear, /* Clear screen */
90 *sc_eol_clear, /* Clear to end of line */
91 *sc_s_in, /* Enter standout (highlighted) mode */
92 *sc_s_out, /* Exit standout mode */
93 *sc_u_in, /* Enter underline mode */
94 *sc_u_out, /* Exit underline mode */
95 *sc_b_in, /* Enter bold mode */
96 *sc_b_out, /* Exit bold mode */
97 *sc_backspace, /* Backspace cursor */
98 *sc_init, /* Startup terminal initialization */
99 *sc_deinit; /* Exit terminal de-intialization */
100
101 int auto_wrap; /* Terminal does \r\n when write past margin */
102 int ignaw; /* Terminal ignores \n immediately after wrap */
103 /* The user's erase and line-kill chars */
104 int retain_below; /* Terminal retains text below the screen */
105 int erase_char, kill_char, werase_char;
106 int sc_width, sc_height = -1; /* Height & width of screen */
107 int sc_window = -1; /* window size for forward and backward */
108 int bo_width, be_width; /* Printing width of boldface sequences */
109 int ul_width, ue_width; /* Printing width of underline sequences */
110 int so_width, se_width; /* Printing width of standout sequences */
111
112 /*
113 * These two variables are sometimes defined in,
114 * and needed by, the termcap library.
115 * It may be necessary on some systems to declare them extern here.
116 */
117 /*extern*/ short ospeed; /* Terminal output baud rate */
118 /*extern*/ char PC; /* Pad character */
119
120 extern int back_scroll;
121 char *tgetstr();
122 char *tgoto();
123
124 /*
125 * Change terminal to "raw mode", or restore to "normal" mode.
126 * "Raw mode" means
127 * 1. An outstanding read will complete on receipt of a single keystroke.
128 * 2. Input is not echoed.
129 * 3. On output, \n is mapped to \r\n.
130 * 4. \t is NOT expanded into spaces.
131 * 5. Signal-causing characters such as ctrl-C (interrupt),
132 * etc. are NOT disabled.
133 * It doesn't matter whether an input \n is mapped to \r, or vice versa.
134 */
135 raw_mode(on)
136 int on;
137 {
138 #if TERMIO || TERMIOS
139
140 #if TERMIO
141 struct termio s;
142 static struct termio save_term;
143 #else
144 struct termios s;
145 static struct termios save_term;
146 #endif
147
148 if (on)
149 {
150 /*
151 * Get terminal modes.
152 */
153 #if TERMIO
154 (void)ioctl(2, TCGETA, &s);
155 #else
156 tcgetattr(2, &s);
157 #endif
158
159 /*
160 * Save modes and set certain variables dependent on modes.
161 */
162 save_term = s;
163 #if TERMIO
164 ospeed = s.c_cflag & CBAUD;
165 #else
166 ospeed = cfgetospeed(&s);
167 #endif
168 erase_char = s.c_cc[VERASE];
169 kill_char = s.c_cc[VKILL];
170 werase_char = s.c_cc[VWERASE];
171
172 /*
173 * Set the modes to the way we want them.
174 */
175 s.c_lflag &= ~(ICANON|ECHO|ECHOE|ECHOK|ECHONL);
176 s.c_oflag |= (OPOST|ONLCR|TAB3);
177 #if TERMIO
178 s.c_oflag &= ~(OCRNL|ONOCR|ONLRET);
179 #endif
180 s.c_cc[VMIN] = 1;
181 s.c_cc[VTIME] = 0;
182 } else
183 {
184 /*
185 * Restore saved modes.
186 */
187 s = save_term;
188 }
189 #if TERMIO
190 (void)ioctl(2, TCSETAW, &s);
191 #else
192 tcsetattr(2, TCSADRAIN, &s);
193 #endif
194 #else
195 struct sgttyb s;
196 struct ltchars l;
197 static struct sgttyb save_term;
198
199 if (on)
200 {
201 /*
202 * Get terminal modes.
203 */
204 (void)ioctl(2, TIOCGETP, &s);
205 (void)ioctl(2, TIOCGLTC, &l);
206
207 /*
208 * Save modes and set certain variables dependent on modes.
209 */
210 save_term = s;
211 ospeed = s.sg_ospeed;
212 erase_char = s.sg_erase;
213 kill_char = s.sg_kill;
214 werase_char = l.t_werasc;
215
216 /*
217 * Set the modes to the way we want them.
218 */
219 s.sg_flags |= CBREAK;
220 s.sg_flags &= ~(ECHO|XTABS);
221 } else
222 {
223 /*
224 * Restore saved modes.
225 */
226 s = save_term;
227 }
228 (void)ioctl(2, TIOCSETN, &s);
229 #endif
230 }
231
232 /*
233 * Get terminal capabilities via termcap.
234 */
235 get_term()
236 {
237 char termbuf[2048];
238 char *sp;
239 char *term;
240 int hard;
241 #ifdef TIOCGWINSZ
242 struct winsize w;
243 #else
244 #ifdef WIOCGETD
245 struct uwdata w;
246 #endif
247 #endif
248 static char sbuf[1024];
249
250 char *getenv(), *strcpy();
251
252 /*
253 * Find out what kind of terminal this is.
254 */
255 if ((term = getenv("TERM")) == NULL)
256 term = "unknown";
257 if (tgetent(termbuf, term) <= 0)
258 (void)strcpy(termbuf, "dumb:co#80:hc:");
259
260 /*
261 * Get size of the screen.
262 */
263 #ifdef TIOCGWINSZ
264 if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_row > 0)
265 sc_height = w.ws_row;
266 #else
267 #ifdef WIOCGETD
268 if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_height > 0)
269 sc_height = w.uw_height/w.uw_vs;
270 #endif
271 #endif
272 else
273 sc_height = tgetnum("li");
274 hard = (sc_height < 0 || tgetflag("hc"));
275 if (hard) {
276 /* Oh no, this is a hardcopy terminal. */
277 sc_height = 24;
278 }
279
280 #ifdef TIOCGWINSZ
281 if (ioctl(2, TIOCGWINSZ, &w) == 0 && w.ws_col > 0)
282 sc_width = w.ws_col;
283 else
284 #ifdef WIOCGETD
285 if (ioctl(2, WIOCGETD, &w) == 0 && w.uw_width > 0)
286 sc_width = w.uw_width/w.uw_hs;
287 else
288 #endif
289 #endif
290 sc_width = tgetnum("co");
291 if (sc_width < 0)
292 sc_width = 80;
293
294 auto_wrap = tgetflag("am");
295 ignaw = tgetflag("xn");
296 retain_below = tgetflag("db");
297
298 /*
299 * Assumes termcap variable "sg" is the printing width of
300 * the standout sequence, the end standout sequence,
301 * the underline sequence, the end underline sequence,
302 * the boldface sequence, and the end boldface sequence.
303 */
304 if ((so_width = tgetnum("sg")) < 0)
305 so_width = 0;
306 be_width = bo_width = ue_width = ul_width = se_width = so_width;
307
308 /*
309 * Get various string-valued capabilities.
310 */
311 sp = sbuf;
312
313 sc_pad = tgetstr("pc", &sp);
314 if (sc_pad != NULL)
315 PC = *sc_pad;
316
317 sc_init = tgetstr("ti", &sp);
318 if (sc_init == NULL)
319 sc_init = "";
320
321 sc_deinit= tgetstr("te", &sp);
322 if (sc_deinit == NULL)
323 sc_deinit = "";
324
325 sc_eol_clear = tgetstr("ce", &sp);
326 if (hard || sc_eol_clear == NULL || *sc_eol_clear == '\0')
327 {
328 sc_eol_clear = "";
329 }
330
331 sc_clear = tgetstr("cl", &sp);
332 if (hard || sc_clear == NULL || *sc_clear == '\0')
333 {
334 sc_clear = "\n\n";
335 }
336
337 sc_move = tgetstr("cm", &sp);
338 if (hard || sc_move == NULL || *sc_move == '\0')
339 {
340 /*
341 * This is not an error here, because we don't
342 * always need sc_move.
343 * We need it only if we don't have home or lower-left.
344 */
345 sc_move = "";
346 }
347
348 sc_s_in = tgetstr("so", &sp);
349 if (hard || sc_s_in == NULL)
350 sc_s_in = "";
351
352 sc_s_out = tgetstr("se", &sp);
353 if (hard || sc_s_out == NULL)
354 sc_s_out = "";
355
356 sc_u_in = tgetstr("us", &sp);
357 if (hard || sc_u_in == NULL)
358 sc_u_in = sc_s_in;
359
360 sc_u_out = tgetstr("ue", &sp);
361 if (hard || sc_u_out == NULL)
362 sc_u_out = sc_s_out;
363
364 sc_b_in = tgetstr("md", &sp);
365 if (hard || sc_b_in == NULL)
366 {
367 sc_b_in = sc_s_in;
368 sc_b_out = sc_s_out;
369 } else
370 {
371 sc_b_out = tgetstr("me", &sp);
372 if (hard || sc_b_out == NULL)
373 sc_b_out = "";
374 }
375
376 sc_home = tgetstr("ho", &sp);
377 if (hard || sc_home == NULL || *sc_home == '\0')
378 {
379 if (*sc_move == '\0')
380 {
381 /*
382 * This last resort for sc_home is supposed to
383 * be an up-arrow suggesting moving to the
384 * top of the "virtual screen". (The one in
385 * your imagination as you try to use this on
386 * a hard copy terminal.)
387 */
388 sc_home = "|\b^";
389 } else
390 {
391 /*
392 * No "home" string,
393 * but we can use "move(0,0)".
394 */
395 (void)strcpy(sp, tgoto(sc_move, 0, 0));
396 sc_home = sp;
397 sp += strlen(sp) + 1;
398 }
399 }
400
401 sc_lower_left = tgetstr("ll", &sp);
402 if (hard || sc_lower_left == NULL || *sc_lower_left == '\0')
403 {
404 if (*sc_move == '\0')
405 {
406 sc_lower_left = "\r";
407 } else
408 {
409 /*
410 * No "lower-left" string,
411 * but we can use "move(0,last-line)".
412 */
413 (void)strcpy(sp, tgoto(sc_move, 0, sc_height-1));
414 sc_lower_left = sp;
415 sp += strlen(sp) + 1;
416 }
417 }
418
419 /*
420 * To add a line at top of screen and scroll the display down,
421 * we use "al" (add line) or "sr" (scroll reverse).
422 */
423 if ((sc_addline = tgetstr("al", &sp)) == NULL ||
424 *sc_addline == '\0')
425 sc_addline = tgetstr("sr", &sp);
426
427 if (hard || sc_addline == NULL || *sc_addline == '\0')
428 {
429 sc_addline = "";
430 /* Force repaint on any backward movement */
431 back_scroll = 0;
432 }
433
434 if (tgetflag("bs"))
435 sc_backspace = "\b";
436 else
437 {
438 sc_backspace = tgetstr("bc", &sp);
439 if (sc_backspace == NULL || *sc_backspace == '\0')
440 sc_backspace = "\b";
441 }
442 }
443
444
445 /*
446 * Below are the functions which perform all the
447 * terminal-specific screen manipulation.
448 */
449
450 int putchr();
451
452 /*
453 * Initialize terminal
454 */
455 init()
456 {
457 tputs(sc_init, sc_height, putchr);
458 }
459
460 /*
461 * Deinitialize terminal
462 */
463 deinit()
464 {
465 tputs(sc_deinit, sc_height, putchr);
466 }
467
468 /*
469 * Home cursor (move to upper left corner of screen).
470 */
471 home()
472 {
473 tputs(sc_home, 1, putchr);
474 }
475
476 /*
477 * Add a blank line (called with cursor at home).
478 * Should scroll the display down.
479 */
480 add_line()
481 {
482 tputs(sc_addline, sc_height, putchr);
483 }
484
485 int short_file; /* if file less than a screen */
486 lower_left()
487 {
488 if (short_file) {
489 putchr('\r');
490 flush();
491 }
492 else
493 tputs(sc_lower_left, 1, putchr);
494 }
495
496 /*
497 * Ring the terminal bell.
498 */
499 bell()
500 {
501 putchr('\7');
502 }
503
504 /*
505 * Clear the screen.
506 */
507 clear()
508 {
509 tputs(sc_clear, sc_height, putchr);
510 }
511
512 /*
513 * Clear from the cursor to the end of the cursor's line.
514 * {{ This must not move the cursor. }}
515 */
516 clear_eol()
517 {
518 tputs(sc_eol_clear, 1, putchr);
519 }
520
521 /*
522 * Begin "standout" (bold, underline, or whatever).
523 */
524 so_enter()
525 {
526 tputs(sc_s_in, 1, putchr);
527 }
528
529 /*
530 * End "standout".
531 */
532 so_exit()
533 {
534 tputs(sc_s_out, 1, putchr);
535 }
536
537 /*
538 * Begin "underline" (hopefully real underlining,
539 * otherwise whatever the terminal provides).
540 */
541 ul_enter()
542 {
543 tputs(sc_u_in, 1, putchr);
544 }
545
546 /*
547 * End "underline".
548 */
549 ul_exit()
550 {
551 tputs(sc_u_out, 1, putchr);
552 }
553
554 /*
555 * Begin "bold"
556 */
557 bo_enter()
558 {
559 tputs(sc_b_in, 1, putchr);
560 }
561
562 /*
563 * End "bold".
564 */
565 bo_exit()
566 {
567 tputs(sc_b_out, 1, putchr);
568 }
569
570 /*
571 * Erase the character to the left of the cursor
572 * and move the cursor left.
573 */
574 backspace()
575 {
576 /*
577 * Try to erase the previous character by overstriking with a space.
578 */
579 tputs(sc_backspace, 1, putchr);
580 putchr(' ');
581 tputs(sc_backspace, 1, putchr);
582 }
583
584 /*
585 * Output a plain backspace, without erasing the previous char.
586 */
587 putbs()
588 {
589 tputs(sc_backspace, 1, putchr);
590 }
591