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.9 2001/06/18 18:44:17 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
;
49 static short ansi_value
[256];
50 static unsigned char ansi_buf
[512], pack_buf
[512];
58 static struct ansi_reports report_list
[] = {
59 {0, 'c', "(DA) Primary device attributes", "\033[0c"},
60 {1, 0, "(DSR) Terminal status", "\033[5n"},
61 {1, 'R', "(DSR) Cursor position", "\033[6n"},
62 {62, 0, "(DA) Secondary device attributes", "\033[>0c"},
63 {62, 0, "(DSR) Printer status", "\033[?15n"},
64 {62, 0, "(DSR) Function key definition", "\033[?25n"},
65 {62, 0, "(DSR) Keyboard language", "\033[?26n"},
66 {63, 0, "(DECRQSS) Data destination", "\033P$q$}\033\\"},
67 {63, 0, "(DECRQSS) Status line type", "\033P$q$~\033\\"},
68 {63, 0, "(DECRQSS) Erase attribute", "\033P$q\"q\033\\"},
69 {63, 0, "(DECRQSS) Personality", "\033P$q\"p\033\\"},
70 {63, 0, "(DECRQSS) Top and bottom margins", "\033P$qr\033\\"},
71 {63, 0, "(DECRQSS) Character attributes", "\033P$qm\033\\"},
72 {63, 0, "(DECRQSS) Illegal request", "\033P$q@\033\\"},
73 {63, 0, "(DECRQUPSS) User pref supplemental set", "\033[&u"},
74 {63, 0, "(DECRQPSR) Cursor information", "\033[1$w"},
75 {63, 0, "(DECRQPSR) Tab stop information", "\033[2$w"},
76 {64, 0, "(DA) Tertiary device attributes", "\033[=0c"},
77 {64, 0, "(DSR) Extended cursor position", "\033[?6n"},
78 {64, 0, "(DSR) Macro space", "\033[?62n"},
79 {64, 0, "(DSR) Memory checksum", "\033[?63n"},
80 {64, 0, "(DSR) Data integrity", "\033[?75n"},
81 {64, 0, "(DSR) Multiple session status", "\033[?85n"},
82 {64, 0, "(DECRQSS) Attribute change extent", "\033P$q*x\033\\"},
83 {64, 0, "(DECRQSS) Columns per page", "\033P$q$|\033\\"},
84 {64, 0, "(DECRQSS) Lines per page", "\033P$qt\033\\"},
85 {64, 0, "(DECRQSS) Lines per screen", "\033P$q*|\033\\"},
86 {64, 0, "(DECRQSS) Left and right margins", "\033P$qs\033\\"},
87 {64, 0, "(DECRQSS) Local functions", "\033P$q+q\033\\"},
88 {64, 0, "(DECRQSS) Local function key control", "\033P$q=}\033\\"},
89 {64, 0, "(DECRQSS) Select modifier key reporting", "\033P$q+r\033\\"},
90 {64, 0, "(DECRQDE) Window report", "\033[\"v"},
94 struct request_control
{
99 const char *reset_mode
;
102 /* Request control function selection or setting */
103 static const struct request_control rqss
[] = {
104 {"Data sent to screen", "0", "$}", "\033[0$}", 0},
105 {"Data sent to disabled status line", "0", "$}", 0, 0},
106 {"\033[0$~\033[1$}", "\033[0$}", 0, 0, 0},
107 {"Data sent to enabled status line", "1", "$}", 0, 0},
108 {"\033[2$~\033[1$}", "\033[0$}", 0, 0, 0},
109 {"Disable status line", "0", "$~", "\033[0$~", 0},
110 {"Top status line", "1", "$~", "\033[1$~", 0},
111 {"Bottom status line", "2", "$~", "\033[2$~", 0},
112 {"Erasable character", "0", "\"q", "\033[0\"q", 0},
113 {"Nonerasable character", "1", "\"q", "\033[1\"q", "\033[0\"q"},
114 {"Top and bottom margins", "3;10", "r", "\0337\033[3;10r", 0},
115 {"\033[r\0338", 0, 0, 0, 0},
116 {"Top and bottom margins", "default", "r", "\0337\033[r", "\0338"},
117 {"Character attributes, dim, bold", "1", "m", "\033[2;1m", "\033[m"},
118 {"Character attributes, bold, dim", "2", "m", "\033[1;2m", "\033[m"},
119 {"Character attributes, under, rev", "4;7", "m", "\033[4;7m", "\033[m"},
120 {"Character attributes, color", "35;42", "m", "\033[35;42m", "\033[m"},
121 {"All character attributes", "", "m", "\033[1;2;3;4;5;6;7;8;9m", 0},
122 {"\033[m", 0, 0, 0, 0},
130 ** read an ANSI status report from terminal
135 int ch
, i
, j
, last_escape
;
138 read_key((char *)ansi_buf
, sizeof(ansi_buf
));
139 /* Throw away control characters inside CSI sequences.
140 Convert two character 7-bit sequences into 8-bit sequences. */
141 for (i
= j
= last_escape
= 0; (ch
= ansi_buf
[i
]) != 0; i
++) {
143 if (last_escape
== A_ESC
) {
144 pack_buf
[j
++] = A_ESC
;
148 if (last_escape
== A_ESC
&& ch
>= '@' && ch
<= '_') {
149 pack_buf
[j
++] = last_escape
= ch
+ 0x40;
151 if (last_escape
!= A_CSI
|| (ch
> 0x20 && ch
!= 0x80)) {
152 if (last_escape
== A_ESC
) {
153 pack_buf
[j
++] = A_ESC
;
155 if (ch
> 0x80 && ch
< 0xa0) {
161 if (last_escape
== A_ESC
) {
162 pack_buf
[j
++] = A_ESC
;
169 ** valid_mode(expected)
171 ** read a terminal mode status report and parse the result
172 ** Return TRUE if we got the expected terminating character.
175 valid_mode(int expected
)
183 ch
= UChar(pack_buf
[0]);
185 if (ch
!= A_CSI
&& ch
!= A_DCS
)
190 if ((*s
>= '<') & (*s
<= '?')) {
194 for (; (ch
= *s
); s
++) {
195 if (ch
>= '0' && ch
<= '9')
196 ansi_value
[ape
] = ansi_value
[ape
] * 10 + ch
- '0';
197 else if (ch
== ';' || ch
== ':')
198 ansi_value
[++ape
] = 0;
199 else if (ch
>= '<' && ch
<= '?')
202 terminator
= (terminator
<< 8) | ch
;
206 return terminator
== expected
;
212 ** read all the reports in the ANSI report structure
217 int i
, j
, k
, tc
, vcr
, lc
;
222 terminal_class
= tc
= 0;
223 for (i
= 0; report_list
[i
].text
; i
++, lc
++) {
224 if (terminal_class
< report_list
[i
].lvl
&&
225 tc
< report_list
[i
].lvl
) {
228 ptext("/status [q] > ");
230 if (j
!= 'n' && j
!= 'N')
232 tc
= report_list
[i
].lvl
;
234 } else if (lc
+ 2 >= lines
) {
236 ptext("Hit any key to continue ");
240 sprintf(temp
, "%s (%s) ", report_list
[i
].text
,
241 expand_command(report_list
[i
].request
));
243 for (j
= strlen(temp
); j
< 49; j
++)
245 tc_putp(report_list
[i
].request
);
247 if (report_list
[i
].final
== 0) {
249 } else if (valid_mode(report_list
[i
].final
))
250 switch (report_list
[i
].final
) {
252 terminal_class
= ansi_value
[0];
258 j
= UChar(pack_buf
[0]);
259 if (j
!= A_CSI
&& j
!= A_DCS
) {
261 t
= "*** The above request gives illegal response ***";
263 for (j
= strlen(t
); j
< 49; j
++)
266 s
= expand((const char *)ansi_buf
);
267 if (char_count
+ expand_chars
>= columns
) {
272 if (vcr
) { /* find out how big the screen is */
273 tc_putp(report_list
[i
].request
);
274 if (!valid_mode('R'))
278 tc_putp("\033[255B\033[255C\033[6n");
279 if (!valid_mode('R'))
281 sprintf(temp
, "\033[%d;%dH", j
, k
);
283 ptext("(DSR) Screen size (CSI 6 n)");
284 for (j
= char_count
; j
< 50; j
++)
286 sprintf(temp
, "%d x %d", ansi_value
[1], ansi_value
[0]);
292 ptext("/status r->repeat test, <return> to continue > ");
299 ** Request Control function selection or settings
308 ptextln("Request Expected Received");
310 for (i
= 0; rqss
[i
].text
; i
++) {
312 j
= strlen(rqss
[i
].text
) + strlen(rqss
[i
].expect
);
314 for (j
++; j
< 40; j
++)
316 ptext(rqss
[i
].expect
);
318 tc_putp(rqss
[i
].set_mode
);
319 sprintf(temp
, "\033P$q%s\033\\", rqss
[i
].request
);
322 tc_putp(rqss
[i
].reset_mode
);
324 for (j
= 0; ansi_buf
[j
]; j
++) {
325 if (ansi_buf
[j
] == 'r') {
326 for (k
= j
++; (ch
= UChar(ansi_buf
[k
])) != 0; k
++)
329 } else if (ch
== A_ST
) {
333 s
= expand((const char *)&ansi_buf
[j
]);
334 if (char_count
+ expand_chars
>= columns
)
341 /* calculate the valid attributes */
342 ptext("Valid attributes: 0");
344 for (i
= 1; i
< 20; i
++) {
345 sprintf(temp
, "\033[0;%dm\033P$qm\033\\", i
);
347 (void) valid_mode('m');
350 sprintf(temp
, "\033[0m; %d", i
);
355 /* calculate how many parameters can be sent */
356 ptext("Max number of parameters: ");
357 sprintf(temp
, "%dm\033P$qm\033\\", j
);
360 for (l
= 1; l
< 33; l
++) {
362 for (ch
= 1; ch
<= l
; ch
++)
365 (void) valid_mode('m');
371 sprintf(temp
, "%d", l
);
380 ** mode_display(puc, mode, initial, set, reset)
382 ** print the mode display entry
385 mode_display(const char *p
, int n
, int c
, char s
, char r
)
389 sprintf(temp
, "%s%d (%c, %c, %c)", p
, n
, c
, s
, r
);
391 if (char_count
+ k
>= columns
)
401 ** test DECRQM status reports
406 static const char *puc
[] = {"", "<", "=", ">", "?", 0};
408 int i
, j
, k
, l
, modes_found
;
410 char buf
[256], tms
[256];
411 int mode_puc
[MAX_MODES
], mode_number
[MAX_MODES
];
412 char set_value
[MAX_MODES
], reset_value
[MAX_MODES
];
413 char current_value
[MAX_MODES
];
415 ptext("Testing terminal mode status. (CSI 0 $ p)");
419 if (valid_mode(('$' << 8) | 'y')) {
420 for (i
= 0; puc
[i
]; i
++) {
423 sprintf(temp
, "Private use: %c", puc
[i
][0]);
425 strcpy(temp
, "Standard modes:");
429 for (j
= 0; j
< (int) sizeof(buf
); buf
[j
++] = ' ')
431 for (j
= l
= 0; j
< 255 && j
- l
< 50; j
++) {
432 sprintf(temp
, "\033[%s%d$p", puc
[i
], j
);
434 if (!valid_mode(('$' << 8) | 'y')) {
435 /* not valid, save terminating value */
436 s
= expand((const char *)ansi_buf
);
437 sprintf(tms
, "%s%s%d %s ", tms
,
441 if (private_use
!= puc
[i
][0])
443 if (ansi_value
[0] != j
)
451 for (k
= 0; k
< (int) sizeof(buf
);) {
456 sprintf(temp
, " %d", j
);
459 buf
[k
- 1] = ansi_value
[1] + '0';
460 if (modes_found
>= MAX_MODES
)
462 current_value
[modes_found
] =
464 /* some modes never return */
465 if ((i
== 0 && j
== 13) /* control execution */
466 || (puc
[i
][0] == '?' && j
== 2)) /* VT52 */
467 set_value
[modes_found
] =
468 reset_value
[modes_found
] = '-';
470 set_value
[modes_found
] =
471 reset_value
[modes_found
] = ' ';
472 mode_puc
[modes_found
] = i
;
473 mode_number
[modes_found
++] = j
;
477 if (buf
[k
- 1] != ' ') {
483 if ((i
= modes_found
) != 0) {
489 ptext("Hit 'Y' to test mode set/reset states: ");
492 if (i
== 'y' || i
== 'Y')
498 fp
= fopen("ted.ansi", "w");
500 fp
= fopen("/dev/console", "w");
503 for (i
= j
= 0; j
< modes_found
; j
= ++i
>> 1) {
504 if (set_value
[j
] == '-')
506 k
= (current_value
[j
] ^ i
) & 1;
507 sprintf(temp
, "\033[%s%d%c\033[%s%d$p",
508 puc
[mode_puc
[j
]], mode_number
[j
],
510 puc
[mode_puc
[j
]], mode_number
[j
]);
513 fprintf(fp
, "%s\n", expand(temp
));
518 if (!valid_mode(('$' << 8) | 'y'))
521 reset_value
[j
] = ansi_value
[1] + '0';
523 set_value
[j
] = ansi_value
[1] + '0';
526 put_str("\033[30l"); /* added for GORT bug
533 /* print the results */
535 putln("mode (initial, set, reset)");
536 for (j
= 0; j
< modes_found
; j
++) {
537 mode_display(puc
[mode_puc
[j
]], mode_number
[j
],
538 current_value
[j
], set_value
[j
], reset_value
[j
]);
540 ptext("\n\nHit 'R' to repeat test. 'S' to sort results: ");
542 if (i
== 's' || i
== 'S') { /* print the same stuff,
546 for (i
= '1'; i
<= '4'; i
++) {
547 for (j
= 0; j
< modes_found
; j
++) {
548 if (current_value
[j
] == i
)
549 mode_display(puc
[mode_puc
[j
]],
550 mode_number
[j
], current_value
[j
],
551 set_value
[j
], reset_value
[j
]);
554 ptext("\n\nHit 'R' to repeat test: ");
557 if (i
!= 'r' && i
!= 'R')
559 tty_raw(1, char_mask
);
568 ** ansi_report_help()
570 ** Display the informational data for the ANSI report test.
573 ansi_report_help(void)
575 ptext("Begin ANSI status report testing. ");
576 ptext(" Parity bit set will be displayed in reverse video. ");
577 ptext(" If the terminal hangs, hit any alphabetic key. ");
578 ptextln(" Use n to continue testing. Use q to quit.");
583 ** test_ansi_reports()
585 ** Test the ANSI status report functions
589 struct test_list
*t GCC_UNUSED
,
590 int *state GCC_UNUSED
,
597 tty_raw(1, char_mask
);
601 if (i
!= 'r' && i
!= 'R') {
607 if (terminal_class
>= 63) {
610 } while (i
== 'r' || i
== 'R');
622 ** Test a range of ANSI sgr attributes
623 ** puc -> Private Use Character
632 for (k
= 0; k
< 80; k
++) {
633 if (char_count
+ 8 > 80)
635 else if (char_count
+ 8 > columns
)
639 printf("\033[%s%dmMode %2d\033[0m", temp
, k
, k
);
654 printf("\033[%s0m", temp
);
659 ** print_sgr20(on, off)
661 ** print the sgr line for sgr20()
664 print_sgr20(int on
, int off
)
666 if (char_count
> columns
- 13) {
668 } else if (char_count
) {
672 printf("%d/%d \033[%dmon\033[%dm off\033[0m", on
, off
, on
, off
);
678 ** display the enter/exit attributes 1-9 and 20-29
686 ptextln("Test enter/exit attributes 1-9 and 21-29.");
687 for (k
= 1; k
< 10; k
++) {
688 print_sgr20(k
, k
+ 20);
690 print_sgr20(1, 22); /* bold */
691 print_sgr20(2, 22); /* dim */
692 print_sgr20(8, 22); /* blank */
698 ** tools_sgr(testlist, state, ch)
700 ** Run the ANSI graphics rendition mode tool
701 ** Return the last character typed.
705 struct test_list
*t GCC_UNUSED
,
706 int *state GCC_UNUSED
,
716 ptext("/sgr Enter =><?r [<cr>] > ");
718 if ((k
== 'r') || (k
== 'R')) {
720 } else if ((k
< '<') || (k
> '?')) {
727 *ch
= REQUEST_PROMPT
;
730 /*****************************************************************************
734 *****************************************************************************/
738 ** select a graphics character set for ANSI terminals
741 select_bank(char *bank
)
744 switch (bank
[1] & 3) {
746 putchp('O' & 0x1f); /* control O */
749 putchp('N' & 0x1f); /* control N */
753 tc_putp("\033n\033}");
756 tc_putp("\033o\033|");
762 ** show_characters(bank, bias)
764 ** print the ANSI graphics characters
767 show_characters(char *bank
, int bias
)
771 sprintf(temp
, "G%d GL ", bank
[1] & 3);
774 for (i
= ' '; i
< 0x80; i
++) {
775 if (char_count
>= columns
||
776 (i
!= ' ' && (i
& 31) == 0))
780 select_bank(default_bank
);
784 select_bank(default_bank
);
791 /* ANSI graphics test
802 Dec extended definitions
808 ** tools_charset(testlist, state, ch)
810 ** Run the ANSI alt-charset mode tool
814 struct test_list
*t GCC_UNUSED
,
815 int *state GCC_UNUSED
,
822 ptext("Enter the bank ()*+,-./ followed by the character set");
823 ptext(" 0123456789:;<=>? for private use, and");
824 ptextln(" @A...Z[\\]^_`a...z{|}~ for standard sets.");
825 strcpy(bank
, "\033)0");
828 show_characters(bank
, 0);
830 /* G0 will not print in GR */
832 show_characters(bank
, 0x80);
835 for (j
= 1; (ch
= getchp(char_mask
)); j
++) {
839 if (j
== 1 && ch
> '/')
842 if (ch
< ' ' || ch
> '/')
844 if (j
+ 1 >= (int) sizeof(bank
))
849 if (bank
[j
] < '0' || bank
[j
] > '~')