X-Git-Url: https://ncurses.scripts.mit.edu/?p=ncurses.git;a=blobdiff_plain;f=tack%2Foutput.c;fp=tack%2Foutput.c;h=3c419a8b48bc02ee7aa75c2730cb3631c9f949e5;hp=0000000000000000000000000000000000000000;hb=0eb88fc5281804773e2a0c7a488a4452463535ce;hpb=661078ddbde3ce0f3b06e95642fbb9b5fef7dca1 diff --git a/tack/output.c b/tack/output.c new file mode 100644 index 00000000..3c419a8b --- /dev/null +++ b/tack/output.c @@ -0,0 +1,818 @@ +/* +** Copyright (C) 1991, 1997 Free Software Foundation, Inc. +** +** This file is part of TACK. +** +** TACK is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2, or (at your option) +** any later version. +** +** TACK is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with TACK; see the file COPYING. If not, write to +** the Free Software Foundation, Inc., 59 Temple Place - Suite 330, +** Boston, MA 02111-1307, USA. +*/ +/* screen formatting and I/O utility functions */ + +#include +#include + +MODULE_ID("$Id: output.c,v 1.4 1999/06/16 00:46:53 tom Exp $") + +/* globals */ +long char_sent; /* number of characters sent */ +int char_count; /* counts characters */ +int line_count; /* counts line feeds */ +int expand_chars; /* length of expand() string */ +int replace_mode; /* used to output replace mode padding */ +int can_go_home; /* TRUE if we can fashion a home command */ +int can_clear_screen; /* TRUE if we can somehow clear the screen */ +int raw_characters_sent; /* Total output characters */ +int log_count; /* Number of characters on a log line */ + +/* translate mode default strings */ +#define TM_carriage_return TM_string[0].value +#define TM_cursor_down TM_string[1].value +#define TM_scroll_forward TM_string[2].value +#define TM_newline TM_string[3].value +#define TM_cursor_left TM_string[4].value +#define TM_bell TM_string[5].value +#define TM_form_feed TM_string[6].value +#define TM_tab TM_string[7].value + +struct default_string_list TM_string[TM_last] = { + {"cr", "\r", 0}, + {"cud1", "\n", 0}, + {"ind", "\n", 0}, + {"nel", "\r\n", 0}, + {"cub1", "\b", 0}, + {"bel", "\007", 0}, + {"ff", "\f", 0}, + {"ht", "\t", 0} +}; + +static const char *c0[32] = { + "NUL", "SOH", "STX", "ETX", "EOT", "ENQ", "ACK", + "BEL", "BS", "HT", "LF", "VT", "FF", "CR", "SO", "SI", + "DLE", "DC1", "DC2", "DC3", "DC4", "NAK", "SYN", "ETB", + "CAN", "EM", "SUB", "ESC", "FS", "GS", "RS", "US" +}; + +static const char *c1[32] = { + "", "", "", "", "IND", "NEL", "SSA", "ESA", + "HTS", "HTJ", "VTS", "PLD", "PLU", "RI", "SS2", "SS3", + "DCS", "PU1", "PU2", "STS", "CCH", "MW", "SPA", "EPA", + "", "", "", "CSI", "ST", "OSC", "PM", "APC" +}; + +int +getnext(int mask) +{ /* get the next character without scan mode + conversion */ + int ch; + unsigned char buf; + + tc_putp(req_for_input); + fflush(stdout); + if (nodelay_read) + while (1) { + ch = read(fileno(stdin), &buf, 1); + if (ch == -1) + return EOF; + if (ch == 1) + return buf; + } + ch = getchar(); + if (ch == EOF) + return EOF; + return ch & mask; +} + + +int +getchp(int mask) +{ /* read a character with scan mode conversion */ + if (scan_mode) { + tc_putp(req_for_input); + fflush(stdout); + return scan_key(); + } else + return getnext(mask); +} + +/* +** tc_putch(c) +** +** Output one character +*/ +int +tc_putch(int c) +{ + char_sent++; + raw_characters_sent++; + putchar(c); + if ((raw_characters_sent & 31) == 31) { + fflush(stdout); + } + if (log_fp) { + /* terminal output logging */ + c &= 0xff; + if (c < 32) { + fprintf(log_fp, "<%s>", c0[c]); + log_count += 5; + } else + if (c < 127) { + fprintf(log_fp, "%c", c); + log_count += 1; + } else { + fprintf(log_fp, "<%02x>", c); + log_count += 4; + } + if (c == '\n' || log_count >= 80) { + fprintf(log_fp, "\n"); + log_count = 0; + } + } + return (c); +} + +/* +** tt_tputs(string, reps) +** +** Output a string with tputs() translation. +** Use this function inside timing tests. +*/ +void +tt_tputs(const char *string, int reps) +{ + int i; + + if (string) { + for (i = 0; i < TT_MAX; i++) { + if (i >= ttp) { + tt_cap[i] = string; + tt_affected[i] = reps; + tt_count[i] = 1; + tt_delay[i] = msec_cost(string, reps); + ttp++; + break; + } + if (string == tt_cap[i] && reps == tt_affected[i]) { + tt_count[i]++; + tt_delay_used += tt_delay[i]; + break; + } + } + (void) tputs(string, reps, tc_putch); + } +} + +/* +** tt_putp(string) +** +** Output a string with tputs() translation. +** Use this function inside timing tests. +*/ +void +tt_putp(const char *string) +{ + tt_tputs(string, 1); +} + +/* +** tt_putparm(string, reps, arg1, arg2, ...) +** +** Send tt_tputs(tparm(string, args...), reps) +** Use this function inside timing tests. +*/ +void +tt_putparm( + NCURSES_CONST char *string, + int reps, + int arg1, + int arg2) +{ + int i; + + if (string) { + for (i = 0; i < TT_MAX; i++) { + if (i >= ttp) { + tt_cap[i] = string; + tt_affected[i] = reps; + tt_count[i] = 1; + tt_delay[i] = msec_cost(string, reps); + ttp++; + break; + } + if (string == tt_cap[i] && reps == tt_affected[i]) { + tt_count[i]++; + tt_delay_used += tt_delay[i]; + break; + } + } + (void) tputs(tparm((NCURSES_CONST char *)string, arg1, arg2), reps, tc_putch); + } +} + +/* +** tc_putp(string) +** +** Output a string with tputs() translation. +** Use this function instead of putp() so we can track +** the actual number of characters sent. +*/ +int +tc_putp(const char *string) +{ + return tputs(string, 1, tc_putch); +} + + +void +put_this(int c) +{ /* output one character (with padding) */ + tc_putch(c); + if (char_padding && replace_mode) + tt_putp(char_padding); +} + + +void +put_cr(void) +{ + if (translate_mode && carriage_return) { + tt_putp(carriage_return); + } else { + tt_putp(TM_carriage_return); + } + char_count = 0; +} + + +void +put_lf(void) +{ /* send a linefeed (only works in RAW or + CBREAK mode) */ + if (translate_mode && cursor_down) { + tt_putp(cursor_down); + } else { + tt_putp(TM_cursor_down); + } + line_count++; +} + + +void +put_ind(void) +{ /* scroll forward (only works in RAW or + CBREAK mode) */ + if (translate_mode && scroll_forward) { + tt_putp(scroll_forward); + } else { + tt_putp(TM_scroll_forward); + } + line_count++; +} + +/* +** put_crlf() +** +** Send (nel) or +*/ +void +put_crlf(void) +{ + if (translate_mode && newline) { + tt_putp(newline); + } else { + tt_putp(TM_newline); + } + char_count = 0; + line_count++; +} + +/* +** put_new_lines(count) +** +** Send a number of newlines. (nel) +*/ +void +put_newlines(int n) +{ + while (n-- > 0) { + put_crlf(); + } +} + +/* +** putchp(character) +** +** Send one character to the terminal. +** This function does translation of control characters. +*/ +void +putchp(int c) +{ + switch (c) { + case '\b': + if (translate_mode && cursor_left) { + tt_putp(cursor_left); + } else { + tt_putp(TM_cursor_left); + } + char_count--; + break; + case 7: + if (translate_mode && bell) { + tt_putp(bell); + } else { + tt_putp(TM_bell); + } + break; + case '\f': + if (translate_mode && form_feed) { + tt_putp(form_feed); + } else { + tt_putp(TM_form_feed); + } + char_count = 0; + line_count++; + break; + case '\n': + put_crlf(); + break; + case '\r': + put_cr(); + break; + case '\t': + if (translate_mode && tab) { + tt_putp(tab); + } else { + tt_putp(TM_tab); + } + char_count = ((char_count / 8) + 1) * 8; + break; + default: + put_this(c); + char_count++; + break; + } +} + + +void +put_str(const char *s) +{ /* send the string to the terminal */ + for (; *s; putchp(*s++)); +} + + +void +putln(const char *s) +{ /* output a string followed by a CR LF */ + for (; *s; putchp(*s++)); + put_crlf(); +} + + +void +put_columns(const char *s, int len, int w) +{ /* put out s in column format */ + int l; + + if (char_count + w > columns) { + put_crlf(); + } + l = char_count % w; + if (l) { + while (l < w) { + putchp(' '); + l++; + } + } + if (char_count && char_count + len >= columns) { + put_crlf(); + } + l = char_count; + put_str(s); + char_count = l + len; +} + + +/* +** ptext(string) +** +** Output a string but do not assume the terminal will wrap to a +** new line. Break the line at a word boundry then send a CR LF. +** This is more estetic on 40 column terminals. +*/ +void +ptext(const char *s) +{ + const char *t; + + while (*s) { + for (t = s + 1; *t > ' '; t++); + if ((char_count != 0) && ((t - s) + char_count >= columns)) { + put_crlf(); + while (*s == ' ') + s++; + } + while (s < t) { + putchp(*s++); + } + } +} + + +void +put_dec(char *f, int i) +{ /* print a line with a decimal number in it */ + char tm[128]; + + sprintf(tm, f, i / 10, i % 10); + ptext(tm); +} + + +void +three_digit(char *tx, int i) +{ /* convert the decimal number to a string of + at least 3 digits */ + if (i < 1000) + sprintf(tx, "%d.%d", i / 10, i % 10); + else + sprintf(tx, "%d", i / 10); +} + + +void +ptextln(const char *s) +{ /* print the text using ptext() then add a CR + LF */ + ptext(s); + put_crlf(); +} + + +static void +expand_one(int ch, char **v) +{ /* expand one character */ + char *t = *v; + + if (ch & 0x80) { /* dump it in octal (yuck) */ + *t++ = '\\'; + *t++ = '0' + ((ch >> 6) & 3); + *t++ = '0' + ((ch >> 3) & 7); + *t++ = '0' + (ch & 7); + expand_chars += 4; + } else if (ch == 127) { /* DEL */ + *t++ = '^'; + *t++ = '?'; + expand_chars += 2; + } else if (ch >= ' ') { + *t++ = ch; + expand_chars++; + } else { /* control characters */ + *t++ = '^'; + *t++ = ch + '@'; + expand_chars += 2; + } + *v = t; +} + + +char * +expand(const char *s) +{ /* convert the string to printable form */ + static char buf[4096]; + char *t, *v; + int ch; + + if (magic_cookie_glitch <= 0 && exit_attribute_mode) { + v = enter_reverse_mode; + } else { + v = NULL; + } + expand_chars = 0; + t = buf; + if (s) { + for (; (ch = *s); s++) { + if ((ch & 0x80) && v) { /* print it in reverse video + mode */ + strcpy(t, liberated(tparm(v))); + for (; *t; t++); + expand_one(ch & 0x7f, &t); + strcpy(t, liberated(tparm(exit_attribute_mode))); + for (; *t; t++); + } else { + expand_one(ch, &t); + } + } + } + *t = '\0'; + return buf; +} + + +char * +print_expand(char *s) +{ /* convert the string to 7-bit printable form */ + static char buf[4096]; + char *t; + int ch; + + expand_chars = 0; + t = buf; + if (s) { + for (; (ch = *s); s++) { + expand_one(ch, &t); + } + } + *t = '\0'; + return buf; +} + + +char * +expand_to(char *s, int l) +{ /* expand s to length l */ + char *t; + + for (s = t = expand(s); *t; t++); + for (; expand_chars < l; expand_chars++) { + *t++ = ' '; + } + *t = '\0'; + return s; +} + + +char * +hex_expand_to(char *s, int l) +{ /* expand s to length l in hex */ + static char buf[4096]; + char *t; + + for (t = buf; *s; s++) { + sprintf(t, "%02X ", *s & 0xff); + t += 3; + if (t - buf > (int) sizeof(buf) - 4) { + break; + } + } + for (; t - buf < l;) { + *t++ = ' '; + } + *t = '\0'; + expand_chars = t - buf; + return buf; +} + + +char * +expand_command(const char *c) +{ /* expand an ANSI escape sequence */ + static char buf[256]; + int i, j, ch; + char *s; + + s = buf; + for (i = FALSE; (ch = (*c & 0xff)); c++) { + if (i) { + *s++ = ' '; + } + i = TRUE; + if (ch < 32) { + j = c[1] & 0xff; + if (ch == '\033' && j >= '@' && j <= '_') { + ch = j - '@'; + c++; + for (j = 0; (*s = c1[ch][j++]); s++); + } else + for (j = 0; (*s = c0[ch][j++]); s++); + } else { + *s++ = ch; + j = c[1] & 0xff; + if (ch >= '0' && ch <= '9' && + j >= '0' && j <= '9') { + i = FALSE; + } + } + } + *s = '\0'; + return buf; +} + +/* +** go_home() +** +** Move the cursor to the home position +*/ +void +go_home(void) +{ + int i; + + if (cursor_home) + tt_putp(cursor_home); + else if (cursor_address) + tt_putparm(cursor_address, lines, 0, 0); + else if (row_address) { /* use (vpa) */ + put_cr(); + tt_putparm(row_address, 1, 0, 0); + } else if (cursor_up && cursor_to_ll) { + tt_putp(cursor_to_ll); + for (i = 1; i < lines; i++) { + tt_putp(cursor_up); + } + } else { + can_go_home = FALSE; + return; + } + char_count = line_count = 0; + can_go_home = TRUE; +} + + +void +home_down(void) +{ /* move the cursor to the lower left hand + corner */ + int i; + + if (cursor_to_ll) + tt_putp(cursor_to_ll); + else if (cursor_address) + tt_putparm(cursor_address, lines, lines - 1, 0); + else if (row_address) { /* use (vpa) */ + put_cr(); + tt_putparm(row_address, 1, lines - 1, 0); + } else if (cursor_down && cursor_home) { + tt_putp(cursor_home); + for (i = 1; i < lines; i++) + tt_putp(cursor_down); + } else + return; + char_count = 0; + line_count = lines - 1; +} + + +void +put_clear(void) +{ /* clear the screen */ + int i; + + if (clear_screen) + tt_tputs(clear_screen, lines); + else if (clr_eos && can_go_home) { + go_home(); + tt_tputs(clr_eos, lines); + } else if (scroll_forward && !over_strike && (can_go_home || cursor_up)) { + /* clear the screen by scrolling */ + put_cr(); + if (cursor_to_ll) { + tt_putp(cursor_to_ll); + } else if (cursor_address) { + tt_putparm(cursor_address, lines, lines - 1, 0); + } else if (row_address) { + tt_putparm(row_address, 1, lines - 1, 0); + } else { + for (i = 1; i < lines; i++) { + tt_putp(scroll_forward); + } + } + for (i = 1; i < lines; i++) { + tt_putp(scroll_forward); + } + if (can_go_home) { + go_home(); + } else { + for (i = 1; i < lines; i++) { + tt_putp(cursor_up); + } + } + } else { + can_clear_screen = FALSE; + return; + } + char_count = line_count = 0; + can_clear_screen = TRUE; +} + +/* +** wait_here() +** +** read one character from the input stream +** If the terminal is not in RAW mode then this function will +** wait for a or . +*/ +int +wait_here(void) +{ + char ch, cc[64]; + char message[16]; + int i, j; + + for (i = 0; i < (int) sizeof(cc); i++) { + cc[i] = ch = getchp(STRIP_PARITY); + if (ch == '\r' || ch == '\n') { + put_crlf(); + char_sent = 0; + return cc[i ? i - 1 : 0]; + } + if (ch >= ' ') { + if (stty_query(TTY_CHAR_MODE)) { + put_crlf(); + char_sent = 0; + return ch; + } + continue; + } + if (ch == 023) { /* Control S */ + /* ignore control S, but tell me about it */ + while (ch == 023 || ch == 021) { + ch = getchp(STRIP_PARITY); + if (i < (int) sizeof(cc)) + cc[++i] = ch; + } + put_str("\nThe terminal sent a ^S -"); + for (j = 0; j <= i; j++) { + sprintf(message, " %02X", cc[j] & 0xFF); + put_str(message); + } + put_crlf(); + i = -1; + } else if (ch != 021) { /* Not Control Q */ + /* could be abort character */ + spin_flush(); + if (tty_can_sync == SYNC_TESTED) { + (void) tty_sync_error(); + } else { + put_str("\n? "); + } + } + } + return '?'; +} + + +/* +** read_string(buffer, length) +** +** Read a string of characters from the input stream. +*/ +void +read_string( + char *buf, + int length) +{ + int ch, i; + + for (i = 0; i < length - 1; ) { + ch = getchp(STRIP_PARITY); + if (ch == '\r' || ch == '\n') { + break; + } + if (ch == '\b' || ch == 127) { + if (i) { + putchp('\b'); + putchp(' '); + putchp('\b'); + i--; + } + } else { + buf[i++] = ch; + putchp(ch); + } + } + buf[i] = '\0'; + put_crlf(); + char_sent = 0; +} + +/* +** maybe_wait(lines) +** +** wait if near the end of the screen, then clear screen +*/ +void +maybe_wait(int n) +{ + if (line_count + n >= lines) { + if (char_sent != 0) { + ptext("Go? "); + (void) wait_here(); + } + put_clear(); + } else { + put_crlf(); + } +}