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) {