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