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., 51 Franklin Street, Fifth Floor,
19** Boston, MA 02110-1301, USA
20*/
21
22#include <tack.h>
23
24MODULE_ID("$Id: ansi.c,v 1.10 2005/09/17 19:49:16 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
47static char default_bank[] = "\033(B\017";
48static int private_use, ape, terminal_class;
49static short ansi_value[256];
50static unsigned char ansi_buf[512], pack_buf[512];
51
52struct ansi_reports {
53	int lvl, final;
54	const char *text;
55	const char *request;
56};
57
58static 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
94struct 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 */
103static 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*/
132static void
133read_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*/
174static int
175valid_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*/
214static int
215read_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*/
301static int
302request_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*/
384static void
385mode_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*/
403static void
404terminal_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*/
572static void
573ansi_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*/
587void
588tools_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*/
625static void
626display_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*/
663static void
664print_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*/
680static void
681sgr20(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*/
703void
704tools_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*/
740static void
741select_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*/
766static void
767show_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
798Standard Definitions
799   A    UK
800   B    US ASCII
801
802Dec 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*/
812void
813tools_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}
855