2 ** Copyright (C) 1991, 1997 Free Software Foundation, Inc.
4 ** This file is part of TACK.
6 ** TACK is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU General Public License as published by
8 ** the Free Software Foundation; either version 2, or (at your option)
11 ** TACK is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
16 ** You should have received a copy of the GNU General Public License
17 ** along with TACK; see the file COPYING. If not, write to
18 ** the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 ** Boston, MA 02111-1307, USA.
21 /* screen formatting and I/O utility functions */
26 MODULE_ID("$Id: output.c,v 1.8 2003/10/25 20:43:43 tom Exp $")
29 long char_sent
; /* number of characters sent */
30 int char_count
; /* counts characters */
31 int line_count
; /* counts line feeds */
32 int expand_chars
; /* length of expand() string */
33 int replace_mode
; /* used to output replace mode padding */
34 int can_go_home
; /* TRUE if we can fashion a home command */
35 int can_clear_screen
; /* TRUE if we can somehow clear the screen */
36 int raw_characters_sent
; /* Total output characters */
37 int log_count
; /* Number of characters on a log line */
39 /* translate mode default strings */
40 #define TM_carriage_return TM_string[0].value
41 #define TM_cursor_down TM_string[1].value
42 #define TM_scroll_forward TM_string[2].value
43 #define TM_newline TM_string[3].value
44 #define TM_cursor_left TM_string[4].value
45 #define TM_bell TM_string[5].value
46 #define TM_form_feed TM_string[6].value
47 #define TM_tab TM_string[7].value
49 struct default_string_list TM_string
[TM_last
] = {
60 static const char *c0
[32] = {
61 "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK",
62 "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI",
63 "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB",
64 "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US"
67 static const char *c1
[32] = {
68 "", "", "", "", "IND", "NEL", "SSA", "ESA",
69 "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3",
70 "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA",
71 "", "", "", "CSI", "ST", "OSC", "PM", "APC"
76 { /* get the next character without scan mode
81 tc_putp(req_for_input
);
85 ch
= read(fileno(stdin
), &buf
, 1);
100 { /* read a character with scan mode conversion */
102 tc_putp(req_for_input
);
106 return getnext(mask
);
112 ** Output one character
118 raw_characters_sent
++;
120 if ((raw_characters_sent
& 31) == 31) {
124 /* terminal output logging */
127 fprintf(log_fp
, "<%s>", c0
[c
]);
131 fprintf(log_fp
, "%c", c
);
134 fprintf(log_fp
, "<%02x>", c
);
137 if (c
== '\n' || log_count
>= 80) {
138 fprintf(log_fp
, "\n");
146 ** tt_tputs(string, reps)
148 ** Output a string with tputs() translation.
149 ** Use this function inside timing tests.
152 tt_tputs(const char *string
, int reps
)
157 for (i
= 0; i
< TT_MAX
; i
++) {
160 tt_affected
[i
] = reps
;
162 tt_delay
[i
] = msec_cost(string
, reps
);
166 if (string
== tt_cap
[i
] && reps
== tt_affected
[i
]) {
168 tt_delay_used
+= tt_delay
[i
];
172 (void) tputs(string
, reps
, tc_putch
);
179 ** Output a string with tputs() translation.
180 ** Use this function inside timing tests.
183 tt_putp(const char *string
)
189 ** tt_putparm(string, reps, arg1, arg2)
191 ** Send tt_tputs(tparm(string, args1, arg2), reps)
192 ** Use this function inside timing tests.
196 NCURSES_CONST
char *string
,
204 for (i
= 0; i
< TT_MAX
; i
++) {
207 tt_affected
[i
] = reps
;
209 tt_delay
[i
] = msec_cost(string
, reps
);
213 if (string
== tt_cap
[i
] && reps
== tt_affected
[i
]) {
215 tt_delay_used
+= tt_delay
[i
];
219 (void) tputs(tparm((NCURSES_CONST
char *)string
, arg1
, arg2
), reps
, tc_putch
);
226 ** Output a string with tputs() translation.
227 ** Use this function instead of putp() so we can track
228 ** the actual number of characters sent.
231 tc_putp(const char *string
)
233 return tputs(string
, 1, tc_putch
);
239 { /* output one character (with padding) */
241 if (char_padding
&& replace_mode
)
242 tt_putp(char_padding
);
249 if (translate_mode
&& carriage_return
) {
250 tt_putp(carriage_return
);
252 tt_putp(TM_carriage_return
);
260 { /* send a linefeed (only works in RAW or
262 if (translate_mode
&& cursor_down
) {
263 tt_putp(cursor_down
);
265 tt_putp(TM_cursor_down
);
273 { /* scroll forward (only works in RAW or
275 if (translate_mode
&& scroll_forward
) {
276 tt_putp(scroll_forward
);
278 tt_putp(TM_scroll_forward
);
286 ** Send (nel) or <cr> <lf>
291 if (translate_mode
&& newline
) {
301 ** put_new_lines(count)
303 ** Send a number of newlines. (nel)
316 ** Send one character to the terminal.
317 ** This function does translation of control characters.
324 if (translate_mode
&& cursor_left
) {
325 tt_putp(cursor_left
);
327 tt_putp(TM_cursor_left
);
332 if (translate_mode
&& bell
) {
339 if (translate_mode
&& form_feed
) {
342 tt_putp(TM_form_feed
);
354 if (translate_mode
&& tab
) {
359 char_count
= ((char_count
/ 8) + 1) * 8;
370 put_str(const char *s
)
371 { /* send the string to the terminal */
372 for (; *s
; putchp(*s
++));
378 { /* output a string followed by a CR LF */
379 for (; *s
; putchp(*s
++));
385 put_columns(const char *s
, int len
, int w
)
386 { /* put out s in column format */
389 if (char_count
+ w
> columns
) {
399 if (char_count
&& char_count
+ len
>= columns
) {
404 char_count
= l
+ len
;
411 ** Output a string but do not assume the terminal will wrap to a
412 ** new line. Break the line at a word boundary then send a CR LF.
413 ** This is more esthetic on 40 column terminals.
421 for (t
= s
+ 1; *t
> ' '; t
++);
422 if ((char_count
!= 0) && ((t
- s
) + char_count
>= columns
)) {
435 put_dec(char *f
, int i
)
436 { /* print a line with a decimal number in it */
439 sprintf(tm
, f
, i
/ 10, i
% 10);
445 three_digit(char *tx
, int i
)
446 { /* convert the decimal number to a string of
449 sprintf(tx
, "%d.%d", i
/ 10, i
% 10);
451 sprintf(tx
, "%d", i
/ 10);
456 ptextln(const char *s
)
457 { /* print the text using ptext() then add a CR
465 expand_one(int ch
, char **v
)
466 { /* expand one character */
469 if (ch
& 0x80) { /* dump it in octal (yuck) */
471 *t
++ = '0' + ((ch
>> 6) & 3);
472 *t
++ = '0' + ((ch
>> 3) & 7);
473 *t
++ = '0' + (ch
& 7);
475 } else if (ch
== 127) { /* DEL */
479 } else if (ch
>= ' ') {
482 } else { /* control characters */
492 expand(const char *s
)
493 { /* convert the string to printable form */
494 static char buf
[4096];
498 if (magic_cookie_glitch
<= 0 && exit_attribute_mode
) {
499 v
= enter_reverse_mode
;
506 for (; (ch
= *s
); s
++) {
507 if ((ch
& 0x80) && v
) { /* print it in reverse video
509 strcpy(t
, liberated(tparm(v
)));
511 expand_one(ch
& 0x7f, &t
);
512 strcpy(t
, liberated(tparm(exit_attribute_mode
)));
525 print_expand(char *s
)
526 { /* convert the string to 7-bit printable form */
527 static char buf
[4096];
534 for (; (ch
= *s
); s
++) {
544 expand_to(char *s
, int l
)
545 { /* expand s to length l */
548 for (s
= t
= expand(s
); *t
; t
++);
549 for (; expand_chars
< l
; expand_chars
++) {
558 hex_expand_to(char *s
, int l
)
559 { /* expand s to length l in hex */
560 static char buf
[4096];
563 for (t
= buf
; *s
; s
++) {
564 sprintf(t
, "%02X ", UChar(*s
));
566 if (t
- buf
> (int) sizeof(buf
) - 4) {
570 for (; t
- buf
< l
;) {
574 expand_chars
= t
- buf
;
580 expand_command(const char *c
)
581 { /* expand an ANSI escape sequence */
582 static char buf
[256];
587 for (i
= FALSE
; (ch
= UChar(*c
)) != 0; c
++) {
594 if (ch
== '\033' && j
>= '@' && j
<= '_') {
597 for (j
= 0; (*s
= c1
[ch
][j
++]); s
++);
599 for (j
= 0; (*s
= c0
[ch
][j
++]); s
++);
603 if (ch
>= '0' && ch
<= '9' &&
604 j
>= '0' && j
<= '9') {
616 ** Move the cursor to the home position
624 tt_putp(cursor_home
);
625 else if (cursor_address
)
626 tt_putparm(cursor_address
, lines
, 0, 0);
627 else if (row_address
) { /* use (vpa) */
629 tt_putparm(row_address
, 1, 0, 0);
630 } else if (cursor_up
&& cursor_to_ll
) {
631 tt_putp(cursor_to_ll
);
632 for (i
= 1; i
< lines
; i
++) {
639 char_count
= line_count
= 0;
646 { /* move the cursor to the lower left hand
651 tt_putp(cursor_to_ll
);
652 else if (cursor_address
)
653 tt_putparm(cursor_address
, lines
, lines
- 1, 0);
654 else if (row_address
) { /* use (vpa) */
656 tt_putparm(row_address
, 1, lines
- 1, 0);
657 } else if (cursor_down
&& cursor_home
) {
658 tt_putp(cursor_home
);
659 for (i
= 1; i
< lines
; i
++)
660 tt_putp(cursor_down
);
664 line_count
= lines
- 1;
670 { /* clear the screen */
674 tt_tputs(clear_screen
, lines
);
675 else if (clr_eos
&& can_go_home
) {
677 tt_tputs(clr_eos
, lines
);
678 } else if (scroll_forward
&& !over_strike
&& (can_go_home
|| cursor_up
)) {
679 /* clear the screen by scrolling */
682 tt_putp(cursor_to_ll
);
683 } else if (cursor_address
) {
684 tt_putparm(cursor_address
, lines
, lines
- 1, 0);
685 } else if (row_address
) {
686 tt_putparm(row_address
, 1, lines
- 1, 0);
688 for (i
= 1; i
< lines
; i
++) {
689 tt_putp(scroll_forward
);
692 for (i
= 1; i
< lines
; i
++) {
693 tt_putp(scroll_forward
);
698 for (i
= 1; i
< lines
; i
++) {
703 can_clear_screen
= FALSE
;
706 char_count
= line_count
= 0;
707 can_clear_screen
= TRUE
;
713 ** read one character from the input stream
714 ** If the terminal is not in RAW mode then this function will
715 ** wait for a <cr> or <lf>.
724 for (i
= 0; i
< (int) sizeof(cc
); i
++) {
725 cc
[i
] = ch
= getchp(STRIP_PARITY
);
726 if (ch
== '\r' || ch
== '\n') {
729 return cc
[i
? i
- 1 : 0];
732 if (stty_query(TTY_CHAR_MODE
)) {
739 if (ch
== 023) { /* Control S */
740 /* ignore control S, but tell me about it */
741 while (ch
== 023 || ch
== 021) {
742 ch
= getchp(STRIP_PARITY
);
743 if (i
< (int) sizeof(cc
))
746 put_str("\nThe terminal sent a ^S -");
747 for (j
= 0; j
<= i
; j
++) {
748 sprintf(message
, " %02X", cc
[j
] & 0xFF);
753 } else if (ch
!= 021) { /* Not Control Q */
754 /* could be abort character */
756 if (tty_can_sync
== SYNC_TESTED
) {
757 (void) tty_sync_error();
768 ** read_string(buffer, length)
770 ** Read a string of characters from the input stream.
779 for (i
= 0; i
< length
- 1; ) {
780 ch
= getchp(STRIP_PARITY
);
781 if (ch
== '\r' || ch
== '\n') {
784 if (ch
== '\b' || ch
== 127) {
804 ** wait if near the end of the screen, then clear screen
809 if (line_count
+ n
>= lines
) {
810 if (char_sent
!= 0) {