ncurses 5.3
[ncurses.git] / tack / ansi.c
1 /*
2 ** Copyright (C) 1991, 1997 Free Software Foundation, Inc.
3 ** 
4 ** This file is part of TACK.
5 ** 
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)
9 ** any later version.
10 ** 
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.
15 ** 
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.
20 */
21
22 #include <tack.h>
23
24 MODULE_ID("$Id: ansi.c,v 1.9 2001/06/18 18:44:17 tom Exp $")
25
26 /*
27  * Standalone tests for ANSI terminals.  Three entry points:
28  * test_ansi_graphics(), test_ansi_reports() and test_ansi_sgr().
29  */
30
31 /*****************************************************************************
32  *
33  * Test ANSI status reports
34  *
35  *****************************************************************************/
36
37 /* ASCII control characters */
38 #define A_DC1 0x11              /* Control Q */
39 #define A_DC3 0x13              /* Control S */
40 #define A_ESC 0x1b
41 #define A_DCS 0x90
42 #define A_CSI 0x9b
43 #define A_ST  0x9c
44
45 #define MAX_MODES 256
46
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];
51
52 struct ansi_reports {
53         int lvl, final;
54         const char *text;
55         const char *request;
56 };
57
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"},
91         {0, 0, 0, 0}
92 };
93
94 struct request_control {
95         const char *text;
96         const char *expect;
97         const char *request;
98         const char *set_mode;
99         const char *reset_mode;
100 };
101
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},
123         {0, 0, 0, 0, 0}
124 };
125
126
127 /*
128 **      read_ansi()
129 **
130 **      read an ANSI status report from terminal
131 */
132 static void
133 read_ansi(void)
134 {
135         int ch, i, j, last_escape;
136
137         fflush(stdout);
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++) {
142                 if (ch == A_ESC) {
143                         if (last_escape == A_ESC) {
144                                 pack_buf[j++] = A_ESC;
145                         }
146                         last_escape = A_ESC;
147                 } else
148                 if (last_escape == A_ESC && ch >= '@' && ch <= '_') {
149                         pack_buf[j++] = last_escape = ch + 0x40;
150                 } else
151                 if (last_escape != A_CSI || (ch > 0x20 && ch != 0x80)) {
152                         if (last_escape == A_ESC) {
153                                 pack_buf[j++] = A_ESC;
154                         }
155                         if (ch > 0x80 && ch < 0xa0) {
156                                 last_escape = ch;
157                         }
158                         pack_buf[j++] = ch;
159                 }
160         }
161         if (last_escape == A_ESC) {
162                 pack_buf[j++] = A_ESC;
163         }
164         pack_buf[j] = '\0';
165         return;
166 }
167
168 /*
169 **      valid_mode(expected)
170 **
171 **      read a terminal mode status report and parse the result
172 **      Return TRUE if we got the expected terminating character.
173 */
174 static int
175 valid_mode(int expected)
176 {
177         unsigned char *s;
178         int ch, terminator;
179
180         read_ansi();
181
182         ape = 0;
183         ch = UChar(pack_buf[0]);
184         ansi_value[0] = 0;
185         if (ch != A_CSI && ch != A_DCS)
186                 return FALSE;
187
188         s = pack_buf + 1;
189         private_use = 0;
190         if ((*s >= '<') & (*s <= '?')) {
191                 private_use = *s++;
192         }
193         terminator = 0;
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 <= '?')
200                         private_use = ch;
201                 else if (ch >= ' ')
202                         terminator = (terminator << 8) | ch;
203                 else
204                         break;
205         }
206         return terminator == expected;
207 }
208
209 /*
210 **      read_reports()
211 **
212 **      read all the reports in the ANSI report structure
213 */
214 static int
215 read_reports(void)
216 {
217         int i, j, k, tc, vcr, lc;
218         char *s;
219         const char *t;
220
221         lc = 5;
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) {
226                         put_crlf();
227                         menu_prompt();
228                         ptext("/status [q] > ");
229                         j = wait_here();
230                         if (j != 'n' && j != 'N')
231                                 return 0;
232                         tc = report_list[i].lvl;
233                         lc = 1;
234                 } else if (lc + 2 >= lines) {
235                         put_crlf();
236                         ptext("Hit any key to continue ");
237                         (void) wait_here();
238                         lc = 1;
239                 }
240                 sprintf(temp, "%s (%s) ", report_list[i].text,
241                         expand_command(report_list[i].request));
242                 ptext(temp);
243                 for (j = strlen(temp); j < 49; j++)
244                         putchp(' ');
245                 tc_putp(report_list[i].request);
246                 vcr = 0;
247                 if (report_list[i].final == 0) {
248                         read_ansi();
249                 } else if (valid_mode(report_list[i].final))
250                         switch (report_list[i].final) {
251                         case 'c':
252                                 terminal_class = ansi_value[0];
253                                 break;
254                         case 'R':
255                                 vcr = TRUE;
256                                 break;
257                         }
258                 j = UChar(pack_buf[0]);
259                 if (j != A_CSI && j != A_DCS) {
260                         put_crlf();
261                         t = "*** The above request gives illegal response ***";
262                         ptext(t);
263                         for (j = strlen(t); j < 49; j++)
264                                 putchp(' ');
265                 }
266                 s = expand((const char *)ansi_buf);
267                 if (char_count + expand_chars >= columns) {
268                         put_str("\r\n        ");
269                         lc++;
270                 }
271                 putln(s);
272                 if (vcr) {      /* find out how big the screen is */
273                         tc_putp(report_list[i].request);
274                         if (!valid_mode('R'))
275                                 continue;
276                         j = ansi_value[0];
277                         k = ansi_value[1];
278                         tc_putp("\033[255B\033[255C\033[6n");
279                         if (!valid_mode('R'))
280                                 continue;
281                         sprintf(temp, "\033[%d;%dH", j, k);
282                         tc_putp(temp);
283                         ptext("(DSR) Screen size (CSI 6 n)");
284                         for (j = char_count; j < 50; j++)
285                                 putchp(' ');
286                         sprintf(temp, "%d x %d", ansi_value[1], ansi_value[0]);
287                         ptextln(temp);
288
289                 }
290         }
291         menu_prompt();
292         ptext("/status r->repeat test, <return> to continue > ");
293         return wait_here();
294 }
295
296 /*
297 **      request_cfss()
298 **
299 **      Request Control function selection or settings
300 */
301 static int
302 request_cfss(void)
303 {
304         int i, j, k, l, ch;
305         char *s;
306
307         put_clear();
308         ptextln("Request                         Expected  Received");
309         put_crlf();
310         for (i = 0; rqss[i].text; i++) {
311                 ptext(rqss[i].text);
312                 j = strlen(rqss[i].text) + strlen(rqss[i].expect);
313                 putchp(' ');
314                 for (j++; j < 40; j++)
315                         putchp(' ');
316                 ptext(rqss[i].expect);
317                 putchp(' ');
318                 tc_putp(rqss[i].set_mode);
319                 sprintf(temp, "\033P$q%s\033\\", rqss[i].request);
320                 tc_putp(temp);
321                 read_ansi();
322                 tc_putp(rqss[i].reset_mode);
323                 putchp(' ');
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++)
327                                         if (ch == A_ESC) {
328                                                 break;
329                                         } else if (ch == A_ST) {
330                                                 break;
331                                         }
332                                 ansi_buf[k] = '\0';
333                                 s = expand((const char *)&ansi_buf[j]);
334                                 if (char_count + expand_chars >= columns)
335                                         put_str("\r\n        ");
336                                 put_str(s);
337                         }
338                 }
339                 put_crlf();
340         }
341         /* calculate the valid attributes */
342         ptext("Valid attributes:         0");
343         j = 0;
344         for (i = 1; i < 20; i++) {
345                 sprintf(temp, "\033[0;%dm\033P$qm\033\\", i);
346                 tc_putp(temp);
347                 (void) valid_mode('m');
348                 if (ape > 0) {
349                         j = i;
350                         sprintf(temp, "\033[0m; %d", i);
351                         tc_putp(temp);
352                 }
353         }
354         put_crlf();
355         /* calculate how many parameters can be sent */
356         ptext("Max number of parameters: ");
357         sprintf(temp, "%dm\033P$qm\033\\", j);
358         l = -1;
359         if (j > 0)
360                 for (l = 1; l < 33; l++) {
361                         tc_putp("\033[0");
362                         for (ch = 1; ch <= l; ch++)
363                                 put_this(';');
364                         tc_putp(temp);
365                         (void) valid_mode('m');
366                         if (ape == 0)
367                                 break;
368                 }
369         tc_putp("\033[m");
370         if (l >= 0) {
371                 sprintf(temp, "%d", l);
372                 ptext(temp);
373         } else
374                 ptext("unknown");
375         put_crlf();
376         return wait_here();
377 }
378
379 /*
380 **      mode_display(puc, mode, initial, set, reset)
381 **
382 **      print the mode display entry
383 */
384 static void
385 mode_display(const char *p, int n, int c, char s, char r)
386 {
387         int k;
388
389         sprintf(temp, "%s%d (%c, %c, %c)", p, n, c, s, r);
390         k = strlen(temp);
391         if (char_count + k >= columns)
392                 put_crlf();
393         for (; k < 14; k++)
394                 putchp(' ');
395         put_str(temp);
396 }
397
398 /*
399 **      terminal_state()
400 **
401 **      test DECRQM status reports
402 */
403 static void
404 terminal_state(void)
405 {
406         static const char *puc[] = {"", "<", "=", ">", "?", 0};
407
408         int i, j, k, l, modes_found;
409         char *s;
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];
414
415         ptext("Testing terminal mode status. (CSI 0 $ p)");
416         tc_putp("\033[0$p");
417         modes_found = 0;
418         tms[0] = '\0';
419         if (valid_mode(('$' << 8) | 'y')) {
420                 for (i = 0; puc[i]; i++) {
421                         put_crlf();
422                         if (i) {
423                                 sprintf(temp, "Private use: %c", puc[i][0]);
424                         } else {
425                                 strcpy(temp, "Standard modes:");
426                         }
427                         k = strlen(temp);
428                         ptext(temp);
429                         for (j = 0; j < (int) sizeof(buf); buf[j++] = ' ')
430                                 ;
431                         for (j = l = 0; j < 255 && j - l < 50; j++) {
432                                 sprintf(temp, "\033[%s%d$p", puc[i], j);
433                                 tc_putp(temp);
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,
438                                                 puc[i], j, s);
439                                         break;
440                                 }
441                                 if (private_use != puc[i][0])
442                                         break;
443                                 if (ansi_value[0] != j)
444                                         break;
445                                 if (ansi_value[1]) {
446                                         l = j;
447                                         if (k > 70) {
448                                                 buf[k] = '\0';
449                                                 put_crlf();
450                                                 ptextln(buf);
451                                                 for (k = 0; k < (int) sizeof(buf);) {
452                                                         buf[k++] = ' ';
453                                                 }
454                                                 k = 0;
455                                         }
456                                         sprintf(temp, " %d", j);
457                                         ptext(temp);
458                                         k += strlen(temp);
459                                         buf[k - 1] = ansi_value[1] + '0';
460                                         if (modes_found >= MAX_MODES)
461                                                 continue;
462                                         current_value[modes_found] =
463                                                 ansi_value[1] + '0';
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] = '-';
469                                         else
470                                                 set_value[modes_found] =
471                                                         reset_value[modes_found] = ' ';
472                                         mode_puc[modes_found] = i;
473                                         mode_number[modes_found++] = j;
474                                 }
475                         }
476                         buf[k] = '\0';
477                         if (buf[k - 1] != ' ') {
478                                 put_crlf();
479                                 ptext(buf);
480                         }
481                 }
482
483         if ((i = modes_found) != 0) {
484                 put_crlf();
485                 put_crlf();
486                 if (tms[0]) {
487                         ptextln(tms);
488                 }
489                 ptext("Hit 'Y' to test mode set/reset states: ");
490                 i = wait_here();
491         }
492         if (i == 'y' || i == 'Y')
493                 while (1) {
494 #ifdef STATUSFIX
495                         FILE *fp;
496
497 #ifdef TEDANSI
498                         fp = fopen("ted.ansi", "w");
499 #else
500                         fp = fopen("/dev/console", "w");
501 #endif
502 #endif
503                         for (i = j = 0; j < modes_found; j = ++i >> 1) {
504                                 if (set_value[j] == '-')
505                                         continue;
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],
509                                         k ? 'l' : 'h',
510                                         puc[mode_puc[j]], mode_number[j]);
511 #ifdef STATUSFIX
512                                 if (fp) {
513                                         fprintf(fp, "%s\n", expand(temp));
514                                         fflush(fp);
515                                 }
516 #endif
517                                 tc_putp(temp);
518                                 if (!valid_mode(('$' << 8) | 'y'))
519                                         continue;
520                                 if (k) {
521                                         reset_value[j] = ansi_value[1] + '0';
522                                 } else {
523                                         set_value[j] = ansi_value[1] + '0';
524                                 }
525                         }
526                         put_str("\033[30l");    /* added for GORT bug
527                                                    (WY-185) */
528 #ifdef STATUSFIX
529                         if (fp)
530                                 fclose(fp);
531 #endif
532                         tty_set();
533                         /* print the results */
534                         put_clear();
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]);
539                         }
540                         ptext("\n\nHit 'R' to repeat test.  'S' to sort results: ");
541                         i = wait_here();
542                         if (i == 's' || i == 'S') {     /* print the same stuff,
543                                                            sorted by
544                                                            current_value */
545                                 put_crlf();
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]);
552                                         }
553                                 }
554                                 ptext("\n\nHit 'R' to repeat test: ");
555                                 i = wait_here();
556                         }
557                         if (i != 'r' && i != 'R')
558                                 break;
559                         tty_raw(1, char_mask);
560                 }
561         } else {
562                 tty_set();
563         }
564 }
565
566
567 /*
568 **      ansi_report_help()
569 **
570 **      Display the informational data for the ANSI report test.
571 */
572 static void
573 ansi_report_help(void)
574 {
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.");
579         put_crlf();
580 }
581
582 /*
583 **      test_ansi_reports()
584 **
585 **      Test the ANSI status report functions
586 */
587 void
588 tools_status(
589         struct test_list *t GCC_UNUSED,
590         int *state GCC_UNUSED,
591         int *ch)
592 {
593         int i;
594
595         put_clear();
596         ansi_report_help();
597         tty_raw(1, char_mask);
598
599         do {
600                 i = read_reports();
601                 if (i != 'r' && i != 'R') {
602                         *ch = i;
603                         return;
604                 }
605         } while (i);
606
607         if (terminal_class >= 63) {
608                 do {
609                         i = request_cfss();
610                 } while (i == 'r' || i == 'R');
611                 *ch = i;
612                 terminal_state();
613         } else {
614                 tty_set();
615         }
616 }
617
618
619 /*
620 **      display_sgr()
621 **
622 **      Test a range of ANSI sgr attributes
623 **      puc -> Private Use Character
624 */
625 static void 
626 display_sgr(int puc)
627 {
628         int k;
629
630         temp[0] = puc;
631         temp[1] = '\0';
632         for (k = 0; k < 80; k++) {
633                 if (char_count + 8 > 80)
634                         put_crlf();
635                 else if (char_count + 8 > columns)
636                         put_crlf();
637                 else if (k > 0)
638                         printf(" ");
639                 printf("\033[%s%dmMode %2d\033[0m", temp, k, k);
640                 char_count += 8;
641                 if (puc == '\0') {
642                         if (k == 19)
643                                 printf("\033[10m");
644                         if (k == 39)
645                                 printf("\033[37m");
646                         if (k == 49)
647                                 printf("\033[40m");
648                 }
649         }
650         put_crlf();
651         if (puc == '<')
652                 printf("\033[<1m");
653         else if (puc)
654                 printf("\033[%s0m", temp);
655         set_attr(0);
656 }
657
658 /*
659 **      print_sgr20(on, off)
660 **
661 **      print the sgr line for sgr20()
662 */
663 static void 
664 print_sgr20(int on, int off)
665 {
666         if (char_count > columns - 13) {
667                 put_crlf();
668         } else if (char_count) {
669                 put_str("  ");
670         }
671         char_count += 11;
672         printf("%d/%d \033[%dmon\033[%dm off\033[0m", on, off, on, off);
673 }
674
675 /*
676 **      sgr20(void)
677 **
678 **      display the enter/exit attributes 1-9 and 20-29
679 */
680 static void 
681 sgr20(void)
682 {
683         int k;
684
685         put_crlf();
686         ptextln("Test enter/exit attributes 1-9 and 21-29.");
687         for (k = 1; k < 10; k++) {
688                 print_sgr20(k, k + 20);
689         }
690         print_sgr20(1, 22);     /* bold */
691         print_sgr20(2, 22);     /* dim */
692         print_sgr20(8, 22);     /* blank */
693         printf("\033[0m");
694         set_attr(0);
695 }
696
697 /*
698 **      tools_sgr(testlist, state, ch)
699 **
700 **      Run the ANSI graphics rendition mode tool
701 **      Return the last character typed.
702 */
703 void
704 tools_sgr(
705         struct test_list *t GCC_UNUSED,
706         int *state GCC_UNUSED,
707         int *ch)
708 {
709         int k;
710
711         put_clear();
712         for (k = 0;;) {
713                 display_sgr(k);
714                 put_crlf();
715                 menu_prompt();
716                 ptext("/sgr Enter =><?r [<cr>] > ");
717                 k = wait_here();
718                 if ((k == 'r') || (k == 'R')) {
719                         k = 0;
720                 } else if ((k < '<') || (k > '?')) {
721                         break;
722                 }
723         }
724         sgr20();
725
726         put_newlines(2);
727         *ch = REQUEST_PROMPT;
728 }
729
730 /*****************************************************************************
731  *
732  * Test ANSI graphics
733  *
734  *****************************************************************************/
735 /*
736 **      select_bank(bank)
737 **
738 **      select a graphics character set for ANSI terminals
739 */
740 static void
741 select_bank(char *bank)
742 {
743         tc_putp(bank);
744         switch (bank[1] & 3) {
745         case 0:
746                 putchp('O' & 0x1f);     /* control O */
747                 break;
748         case 1:
749                 putchp('N' & 0x1f);     /* control N */
750                 tc_putp("\033~");
751                 break;
752         case 2:
753                 tc_putp("\033n\033}");
754                 break;
755         case 3:
756                 tc_putp("\033o\033|");
757                 break;
758         }
759 }
760
761 /*
762 **      show_characters(bank, bias)
763 **
764 **      print the ANSI graphics characters
765 */
766 static void
767 show_characters(char *bank, int bias)
768 {
769         int i;
770
771         sprintf(temp, "G%d GL   ", bank[1] & 3);
772         ptext(temp);
773         select_bank(bank);
774         for (i = ' '; i < 0x80; i++) {
775                 if (char_count >= columns ||
776                         (i != ' ' && (i & 31) == 0))
777                         put_str("\n        ");
778                 putchp(i + bias);
779         }
780         select_bank(default_bank);
781         put_str("   DEL <");
782         select_bank(bank);
783         putchp(0x7f + bias);
784         select_bank(default_bank);
785         putchp('>');
786         put_crlf();
787         put_crlf();
788 }
789
790
791 /* ANSI graphics test
792         94     96   character sets
793    G0   (      ,
794    G1   )      -
795    G2   *      .
796    G3   +      /
797
798 Standard Definitions
799    A    UK
800    B    US ASCII
801
802 Dec extended definitions
803    0    Special graphics
804
805  */
806
807 /*
808 **      tools_charset(testlist, state, ch)
809 **
810 **      Run the ANSI alt-charset mode tool
811 */
812 void
813 tools_charset(
814         struct test_list *t GCC_UNUSED,
815         int *state GCC_UNUSED,
816         int *chp GCC_UNUSED)
817 {
818         int j, ch;
819         char bank[32];
820
821         put_clear();
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");
826         for (; bank[0];) {
827                 put_crlf();
828                 show_characters(bank, 0);
829
830                 /* G0 will not print in GR */
831                 if (bank[1] & 3) {
832                         show_characters(bank, 0x80);
833                 }
834                 ptext("bank+set> ");
835                 for (j = 1; (ch = getchp(char_mask)); j++) {
836                         if (ch == EOF)
837                                 break;
838                         putchp(ch);
839                         if (j == 1 && ch > '/')
840                                 j++;
841                         bank[j] = ch;
842                         if (ch < ' ' || ch > '/')
843                                 break;
844                         if (j + 1 >= (int) sizeof(bank))
845                                 break;
846                 }
847                 if (j == 1)
848                         break;
849                 if (bank[j] < '0' || bank[j] > '~')
850                         break;
851                 bank[j + 1] = '\0';
852         }
853         put_crlf();
854 }