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