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