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.
24 MODULE_ID("$Id: ansi.c,v 1.2 1999/08/21 23:11:57 tom Exp $")
27 * Standalone tests for ANSI terminals. Three entry points:
28 * test_ansi_graphics(), test_ansi_reports() and test_ansi_sgr().
31 /*****************************************************************************
33 * Test ANSI status reports
35 *****************************************************************************/
37 /* ASCII control characters */
38 #define A_DC1 0x11 /* Control Q */
39 #define A_DC3 0x13 /* Control S */
47 static char default_bank[] = "\033(B\017";
48 static int private_use, ape, terminal_class, got_escape;
49 static short ansi_value[256];
50 static char ansi_buf[512], pack_buf[512];
51 static char *ach, *pch;
59 static struct ansi_reports report_list[] = {
60 {0, 'c', "(DA) Primary device attributes", "\033[0c"},
61 {1, 0, "(DSR) Terminal status", "\033[5n"},
62 {1, 'R', "(DSR) Cursor position", "\033[6n"},
63 {62, 0, "(DA) Secondary device attributes", "\033[>0c"},
64 {62, 0, "(DSR) Printer status", "\033[?15n"},
65 {62, 0, "(DSR) Function key definition", "\033[?25n"},
66 {62, 0, "(DSR) Keyboard language", "\033[?26n"},
67 {63, 0, "(DECRQSS) Data destination", "\033P$q$}\033\\"},
68 {63, 0, "(DECRQSS) Status line type", "\033P$q$~\033\\"},
69 {63, 0, "(DECRQSS) Erase attribute", "\033P$q\"q\033\\"},
70 {63, 0, "(DECRQSS) Personality", "\033P$q\"p\033\\"},
71 {63, 0, "(DECRQSS) Top and bottom margins", "\033P$qr\033\\"},
72 {63, 0, "(DECRQSS) Character attributes", "\033P$qm\033\\"},
73 {63, 0, "(DECRQSS) Illegal request", "\033P$q@\033\\"},
74 {63, 0, "(DECRQUPSS) User pref suplemental set", "\033[&u"},
75 {63, 0, "(DECRQPSR) Cursor information", "\033[1$w"},
76 {63, 0, "(DECRQPSR) Tab stop information", "\033[2$w"},
77 {64, 0, "(DA) Tertiary device attributes", "\033[=0c"},
78 {64, 0, "(DSR) Extended cursor position", "\033[?6n"},
79 {64, 0, "(DSR) Macro space", "\033[?62n"},
80 {64, 0, "(DSR) Memory checksum", "\033[?63n"},
81 {64, 0, "(DSR) Data integrity", "\033[?75n"},
82 {64, 0, "(DSR) Multiple session status", "\033[?85n"},
83 {64, 0, "(DECRQSS) Attribute change extent", "\033P$q*x\033\\"},
84 {64, 0, "(DECRQSS) Columns per page", "\033P$q$|\033\\"},
85 {64, 0, "(DECRQSS) Lines per page", "\033P$qt\033\\"},
86 {64, 0, "(DECRQSS) Lines per screen", "\033P$q*|\033\\"},
87 {64, 0, "(DECRQSS) Left and right margins", "\033P$qs\033\\"},
88 {64, 0, "(DECRQSS) Local functions", "\033P$q+q\033\\"},
89 {64, 0, "(DECRQSS) Local function key control", "\033P$q=}\033\\"},
90 {64, 0, "(DECRQSS) Select modifier key reporting", "\033P$q+r\033\\"},
91 {64, 0, "(DECRQDE) Window report", "\033[\"v"},
95 struct request_control {
100 const char *reset_mode;
103 /* Request control function selection or setting */
104 static const struct request_control rqss[] = {
105 {"Data sent to screen", "0", "$}", "\033[0$}", 0},
106 {"Data sent to disabled status line", "0", "$}", 0, 0},
107 {"\033[0$~\033[1$}", "\033[0$}", 0, 0, 0},
108 {"Data sent to enabled status line", "1", "$}", 0, 0},
109 {"\033[2$~\033[1$}", "\033[0$}", 0, 0, 0},
110 {"Disbale status line", "0", "$~", "\033[0$~", 0},
111 {"Top status line", "1", "$~", "\033[1$~", 0},
112 {"Bottom status line", "2", "$~", "\033[2$~", 0},
113 {"Eraseable character", "0", "\"q", "\033[0\"q", 0},
114 {"Noneraseable character", "1", "\"q", "\033[1\"q", "\033[0\"q"},
115 {"Top and bottom margins", "3;10", "r", "\0337\033[3;10r", 0},
116 {"\033[r\0338", 0, 0, 0, 0},
117 {"Top and bottom margins", "default", "r", "\0337\033[r", "\0338"},
118 {"Character attributes, dim, bold", "1", "m", "\033[2;1m", "\033[m"},
119 {"Character attributes, bold, dim", "2", "m", "\033[1;2m", "\033[m"},
120 {"Character attributes, under, rev", "4;7", "m", "\033[4;7m", "\033[m"},
121 {"Character attributes, color", "35;42", "m", "\033[35;42m", "\033[m"},
122 {"All character attributes", "", "m", "\033[1;2;3;4;5;6;7;8;9m", 0},
123 {"\033[m", 0, 0, 0, 0},
130 ** read and pack an ANSI character
141 ch = getchp(char_mask);
144 if (ch == A_DC1 || ch == A_DC3)
148 if (got_escape && ch >= ' ') {
150 if (ch < '@' || ch > '_') {
158 } else if (ch == A_ESC) {
173 ** read an ANSI status report from terminal
183 ansi_buf[0] = pack_buf[0] = '\0';
191 } while (ch < '0' || ch > '~');
198 } while (ch < '@' || ch > '~');
205 } while (ch != A_ST);
210 ** valid_mode(expected)
212 ** read a terminal mode status report and parse the result
213 ** Return TRUE if we got the expected terminating character.
216 valid_mode(int expected)
224 ch = pack_buf[0] & 0xff;
226 if (ch != A_CSI && ch != A_DCS)
231 if ((*s >= '<') & (*s <= '?')) {
235 for (; (ch = *s); s++) {
236 if (ch >= '0' && ch <= '9')
237 ansi_value[ape] = ansi_value[ape] * 10 + ch - '0';
238 else if (ch == ';' || ch == ':')
239 ansi_value[++ape] = 0;
240 else if (ch >= '<' && ch <= '?')
243 terminator = (terminator << 8) | ch;
247 return terminator == expected;
253 ** read all the reports in the ANSI report structure
258 int i, j, k, tc, vcr, lc;
262 terminal_class = tc = 0;
263 for (i = 0; report_list[i].text; i++, lc++) {
264 if (terminal_class < report_list[i].lvl &&
265 tc < report_list[i].lvl) {
268 ptext(" <return> to continue > ");
270 if (j != 'c' && j != 'C')
272 tc = report_list[i].lvl;
273 } else if (lc + 2 >= lines) {
275 ptext("Hit any key to continue ");
279 sprintf(temp, "%s (%s) ", report_list[i].text,
280 expand_command(report_list[i].request));
282 for (j = strlen(temp); j < 49; j++)
284 tc_putp(report_list[i].request);
286 if (report_list[i].final == 0) {
288 } else if (valid_mode(report_list[i].final))
289 switch (report_list[i].final) {
291 terminal_class = ansi_value[0];
297 j = pack_buf[0] & 0xff;
298 if (j == A_CSI || j == A_DCS) {
299 s = expand(ansi_buf);
300 if (char_count + expand_chars >= columns) {
307 if (vcr) { /* find out how big the screen is */
308 tc_putp(report_list[i].request);
309 if (!valid_mode('R'))
313 tc_putp("\033[255B\033[255C\033[6n");
314 if (!valid_mode('R'))
316 sprintf(temp, "\033[%d;%dH", j, k);
318 ptext("(DSR) Screen size (CSI 6 n)");
319 for (j = char_count; j < 50; j++)
321 sprintf(temp, "%d x %d", ansi_value[1], ansi_value[0]);
327 ptext(" r->repeat test, <return> to continue > ");
334 ** Request Control function selection or settings
343 ptextln("Request Expected Received");
345 for (i = 0; rqss[i].text; i++) {
347 j = strlen(rqss[i].text) + strlen(rqss[i].expect);
349 for (j++; j < 40; j++)
351 ptext(rqss[i].expect);
353 tc_putp(rqss[i].set_mode);
354 sprintf(temp, "\033P$q%s\033\\", rqss[i].request);
357 tc_putp(rqss[i].reset_mode);
359 for (j = 0; ansi_buf[j]; j++) {
360 if (ansi_buf[j] == 'r') {
361 for (k = j++; (ch = (ansi_buf[k] & 0xff)); k++)
364 } else if (ch == A_ST) {
368 s = expand(&ansi_buf[j]);
369 if (char_count + expand_chars >= columns)
376 /* calculate the valid attributes */
377 ptext("Valid attributes: 0");
379 for (i = 1; i < 20; i++) {
380 sprintf(temp, "\033[0;%dm\033P$qm\033\\", i);
382 (void) valid_mode('m');
385 sprintf(temp, "\033[0m; %d", i);
390 /* calculate how many parameters can be sent */
391 ptext("Max number of parameters: ");
392 sprintf(temp, "%dm\033P$qm\033\\", j);
395 for (l = 1; l < 33; l++) {
397 for (ch = 1; ch <= l; ch++)
400 (void) valid_mode('m');
406 sprintf(temp, "%d", l);
415 ** mode_display(puc, mode, initial, set, reset)
417 ** print the mode display entry
420 mode_display(const char *p, int n, int c, char s, char r)
424 sprintf(temp, "%s%d (%c, %c, %c)", p, n, c, s, r);
426 if (char_count + k >= columns)
436 ** test DECRQM status reports
441 static const char *puc[] = {"", "<", "=", ">", "?", 0};
443 int i, j, k, l, modes_found;
445 char buf[256], tms[256];
446 int mode_puc[MAX_MODES], mode_number[MAX_MODES];
447 char set_value[MAX_MODES], reset_value[MAX_MODES];
448 char current_value[MAX_MODES];
450 ptext("Testing terminal mode status. (CSI 0 $ p)");
454 if (valid_mode(('$' << 8) | 'y')) {
455 for (i = 0; puc[i]; i++) {
458 sprintf(temp, "Private use: %c", puc[i][0]);
460 strcpy(temp, "Standard modes:");
464 for (j = 0; j < (int) sizeof(buf); buf[j++] = ' ')
466 for (j = l = 0; j < 255 && j - l < 50; j++) {
467 sprintf(temp, "\033[%s%d$p", puc[i], j);
469 if (!valid_mode(('$' << 8) | 'y')) {
470 /* not valid, save terminating value */
471 s = expand(ansi_buf);
472 sprintf(tms, "%s%s%d %s ", tms,
476 if (private_use != puc[i][0])
478 if (ansi_value[0] != j)
486 for (k = 0; k < (int) sizeof(buf);) {
491 sprintf(temp, " %d", j);
494 buf[k - 1] = ansi_value[1] + '0';
495 if (modes_found >= MAX_MODES)
497 current_value[modes_found] =
499 /* some modes never return */
500 if ((i == 0 && j == 13) /* control execution */
501 || (puc[i][0] == '?' && j == 2)) /* VT52 */
502 set_value[modes_found] =
503 reset_value[modes_found] = '-';
505 set_value[modes_found] =
506 reset_value[modes_found] = ' ';
507 mode_puc[modes_found] = i;
508 mode_number[modes_found++] = j;
512 if (buf[k - 1] != ' ') {
518 if ((i = modes_found) != 0) {
524 ptext("Hit 'Y' to test mode set/reset states: ");
527 if (i == 'y' || i == 'Y')
533 fp = fopen("ted.ansi", "w");
535 fp = fopen("/dev/console", "w");
538 for (i = j = 0; j < modes_found; j = ++i >> 1) {
539 if (set_value[j] == '-')
541 k = (current_value[j] ^ i) & 1;
542 sprintf(temp, "\033[%s%d%c\033[%s%d$p",
543 puc[mode_puc[j]], mode_number[j],
545 puc[mode_puc[j]], mode_number[j]);
548 fprintf(fp, "%s\n", expand(temp));
553 if (!valid_mode(('$' << 8) | 'y'))
556 reset_value[j] = ansi_value[1] + '0';
558 set_value[j] = ansi_value[1] + '0';
561 put_str("\033[30l"); /* added for GORT bug
568 /* print the results */
570 putln("mode (initial, set, reset)");
571 for (j = 0; j < modes_found; j++) {
572 mode_display(puc[mode_puc[j]], mode_number[j],
573 current_value[j], set_value[j], reset_value[j]);
575 ptext("\n\nHit 'R' to repeat test. 'S' to sort results: ");
577 if (i == 's' || i == 'S') { /* print the same stuff,
581 for (i = '1'; i <= '4'; i++) {
582 for (j = 0; j < modes_found; j++) {
583 if (current_value[j] == i)
584 mode_display(puc[mode_puc[j]],
585 mode_number[j], current_value[j],
586 set_value[j], reset_value[j]);
589 ptext("\n\nHit 'R' to repeat test: ");
592 if (i != 'r' && i != 'R')
594 tty_raw(1, char_mask);
603 ** ansi_report_help()
605 ** Display the informational data for the ANSI report test.
608 ansi_report_help(void)
610 ptext("Begin ANSI status report testing. ");
611 ptext(" Parity bit set will be displayed in reverse video. ");
612 ptext(" If the terminal hangs, hit any alphabetic key. ");
613 ptextln(" Use c to continue testing. Use any other letter to quit.");
618 ** test_ansi_reports()
620 ** Test the ANSI status report functions
624 struct test_list *t GCC_UNUSED,
625 int *state GCC_UNUSED,
632 tty_raw(1, char_mask);
636 if (i != 'r' && i != 'R') {
642 if (terminal_class >= 63) {
645 } while (i == 'r' || i == 'R');
657 ** Test a range of ANSI sgr attributes
658 ** puc -> Private Use Character
667 for (k = 0; k < 80; k++) {
668 if (char_count + 8 > 80)
670 else if (char_count + 8 > columns)
674 printf("\033[%s%dmMode %2d\033[0m", temp, k, k);
689 printf("\033[%s0m", temp);
694 ** print_sgr20(on, off)
696 ** print the sgr line for sgr20()
699 print_sgr20(int on, int off)
701 if (char_count > columns - 13) {
703 } else if (char_count) {
707 printf("%d/%d \033[%dmon\033[%dm off\033[0m", on, off, on, off);
713 ** display the enter/exit attributes 1-9 and 20-29
721 ptextln("Test enter/exit attributes 1-9 and 21-29.");
722 for (k = 1; k < 10; k++) {
723 print_sgr20(k, k + 20);
725 print_sgr20(1, 22); /* bold */
726 print_sgr20(2, 22); /* dim */
727 print_sgr20(8, 22); /* blank */
733 ** tools_sgr(testlist, state, ch)
735 ** Run the ANSI graphics rendition mode tool
736 ** Return the last character typed.
740 struct test_list *t GCC_UNUSED,
741 int *state GCC_UNUSED,
751 ptext("/sgr Enter =><?r [<cr>] > ");
753 if ((k == 'r') || (k == 'R')) {
755 } else if ((k < '<') || (k > '?')) {
762 *ch = REQUEST_PROMPT;
765 /*****************************************************************************
769 *****************************************************************************/
773 ** select a graphics character set for ANSI terminals
776 select_bank(char *bank)
779 switch (bank[1] & 3) {
781 putchp('O' & 0x1f); /* control O */
784 putchp('N' & 0x1f); /* control N */
788 tc_putp("\033n\033}");
791 tc_putp("\033o\033|");
797 ** show_characters(bank, bias)
799 ** print the ANSI graphics characters
802 show_characters(char *bank, int bias)
806 sprintf(temp, "G%d GL ", bank[1] & 3);
809 for (i = ' '; i < 0x80; i++) {
810 if (char_count >= columns ||
811 (i != ' ' && (i & 31) == 0))
815 select_bank(default_bank);
819 select_bank(default_bank);
826 /* ANSI graphics test
837 Dec extended definitions
843 ** tools_charset(testlist, state, ch)
845 ** Run the ANSI alt-charset mode tool
849 struct test_list *t GCC_UNUSED,
850 int *state GCC_UNUSED,
857 ptext("Enter the bank ()*+,-./ followed by the character set");
858 ptext(" 0123456789:;<=>? for private use, and");
859 ptextln(" @A...Z[\\]^_`a...z{|}~ for standard sets.");
860 strcpy(bank, "\033)0");
863 show_characters(bank, 0);
865 /* G0 will not print in GR */
867 show_characters(bank, 0x80);
870 for (j = 1; (ch = getchp(char_mask)); j++) {
874 if (j == 1 && ch > '/')
877 if (ch < ' ' || ch > '/')
879 if (j + 1 >= (int) sizeof(bank))
884 if (bank[j] < '0' || bank[j] > '~')