1 /* $Id: charsets.c,v 1.60 2018/07/26 00:20:16 tom Exp $ */
2 
3 /*
4  * Test character-sets (e.g., SCS control, DECNRCM mode)
5  */
6 #include <vttest.h>
7 #include <esc.h>
8 
9 /* the values, where specified, correspond to the keyboard-language codes */
10 typedef enum {
11   ASCII = 1,
12   British = 2,
13   Flemish = 3,
14   French_Canadian = 4,
15   Danish = 5,
16   Finnish = 6,
17   German = 7,
18   Dutch = 8,
19   Italian = 9,
20   Swiss_French = 10,
21   Swiss_German = 11,
22   Swiss,
23   Swedish = 12,
24   Norwegian_Danish = 13,
25   French = 14,
26   Spanish = 15,
27   Portugese = 16,
28   Hebrew = 17,
29   British_Latin_1,
30   Cyrillic,
31   DEC_Alt_Chars,
32   DEC_Alt_Graphics,
33   DEC_Spec_Graphic,
34   DEC_Supp,
35   DEC_Supp_Graphic,
36   DEC_Tech,
37   Greek,
38   Greek_Supp,
39   Hebrew_Supp,
40   Latin_5_Supp,
41   Latin_Cyrillic,
42   Russian,
43   Turkish,
44   SCS_NRCS,
45   Unknown
46 } National;
47 
48 typedef struct {
49   National code;                /* internal name (chosen to sort 'name' member) */
50   int allow96;                  /* flag for 96-character sets (e.g., GR mapping) */
51   int order;                    /* check-column so we can mechanically-sort this table */
52   int first;                    /* first model: 0=base, 2=vt220, 3=vt320, etc. */
53   int last;                     /* lastmodel: 0=base, 2=vt220, 3=vt320, etc. */
54   const char *final;            /* end of SCS string */
55   const char *name;             /* the string we'll show the user */
56   const char *not11;            /* cells which are not 1-1 with ISO-8859-1 */
57 } CHARSETS;
58 /* *INDENT-OFF* */
59 
60 /* compare mappings using only 7-bits */
61 #define Not11(a,b) (((a) & 0x7f) == ((b) & 0x7f))
62 
63 /*
64  * The VT220 and VT340 reference manuals show tables and details for the
65  * character sets.  The VT520 reference manual does not show these details, so
66  * mappings for the VT5xx character sets are not highlighted by this program.
67  */
68 static const char map_pound[] = "#";
69 static const char map_all94[] = "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~";
70 static const char map_DEC_Supp[] = "$&,-./48>GHIJKLMNOPW^pw}~";
71 static const char map_Spec_Graphic[] = "`abcdefghijklmnopqrstuvwxyz{|}~";
72 static const char map_Supp_Graphic[] = "$&,-./48>PW^p}~\177";
73 static const char map_Dutch[] = "#@[\\]{|}~";
74 static const char map_Finnish[] = "`[\\]^{|}~";
75 static const char map_French[] = "#@[\\]{|}~";
76 static const char map_French_Canadian[] = "@`[\\]^{|}~";
77 static const char map_German[] = "@[\\]{|}~";
78 static const char map_Italian[] = "#@`[\\]{|}~";
79 static const char map_Norwegian[] = "@`[\\]^{|}~";
80 static const char map_Portuguese[] = "[\\]{|}";
81 static const char map_Spanish[] = "@[\\]{|}";
82 static const char map_Swedish[] = "@`[\\]^{|}~";
83 static const char map_Swiss[] = "#@`[\\]^_{|}~";
84 
85 static const CHARSETS KnownCharsets[] = {
86   { ASCII,             0, 0, 0, 9, "B",    "US ASCII", 0 },
87   { British,           0, 0, 0, 9, "A",    "British", map_pound },
88   { British_Latin_1,   1, 0, 3, 9, "A",    "ISO Latin-1", 0 },
89   { Cyrillic,          0, 0, 5, 9, "&4",   "Cyrillic (DEC)", 0 },
90   { DEC_Spec_Graphic,  0, 0, 0, 9, "0",    "DEC Special graphics and line drawing", map_Spec_Graphic },
91   { DEC_Alt_Chars,     0, 0, 0, 0, "1",    "DEC Alternate character ROM standard characters", 0 },
92   { DEC_Alt_Graphics,  0, 0, 0, 0, "2",    "DEC Alternate character ROM special graphics", 0 },
93   { DEC_Supp,          0, 0, 2, 9, "<",    "DEC Supplemental", map_DEC_Supp },
94   { DEC_Supp_Graphic,  0, 0, 3, 9, "%5",   "DEC Supplemental Graphic", map_Supp_Graphic },
95   { DEC_Tech,          0, 0, 3, 9, ">",    "DEC Technical", map_all94 },
96   { Dutch,             0, 0, 2, 9, "4",    "Dutch", map_Dutch },
97   { Finnish,           0, 0, 2, 9, "5",    "Finnish", map_Finnish },
98   { Finnish,           0, 1, 2, 9, "C",    "Finnish", map_French },
99   { French,            0, 0, 2, 9, "R",    "French", map_French },
100   { French,            0, 1, 2, 9, "f",    "French", map_French }, /* Kermit (vt340 model?) */
101   { French_Canadian,   0, 0, 2, 9, "Q",    "French Canadian", map_French_Canadian },
102   { French_Canadian,   0, 1, 3, 9, "9",    "French Canadian", map_French_Canadian },
103   { German,            0, 0, 2, 9, "K",    "German", map_German },
104   { Greek,             0, 0, 5, 9, "\"?",  "Greek (DEC)", 0 },
105   { Greek_Supp,        1, 0, 5, 9, "F",    "ISO Greek Supplemental", 0 },
106   { Hebrew,            0, 0, 5, 9, "\"4",  "Hebrew (DEC)", 0 },
107   { Hebrew,            0, 1, 5, 9, "%=",   "Hebrew NRCS", 0 },
108   { Hebrew_Supp,       1, 0, 5, 9, "H",    "ISO Hebrew Supplemental", 0 },
109   { Italian,           0, 0, 2, 9, "Y",    "Italian", map_Italian },
110   { Latin_5_Supp,      1, 0, 5, 9, "M",    "ISO Latin-5 Supplemental", 0 },
111   { Latin_Cyrillic,    1, 0, 5, 9, "L",    "ISO Latin-Cyrillic", 0 },
112   { Norwegian_Danish,  0, 0, 3, 9, "`",    "Norwegian/Danish", map_Norwegian },
113   { Norwegian_Danish,  0, 1, 2, 9, "E",    "Norwegian/Danish", map_Norwegian },
114   { Norwegian_Danish,  0, 2, 2, 9, "6",    "Norwegian/Danish", map_Norwegian },
115   { Portugese,         0, 0, 3, 9, "%6",   "Portugese", map_Portuguese },
116   { Russian,           0, 0, 5, 9, "&5",   "Russian", 0 },
117   { SCS_NRCS,          0, 0, 5, 9, "%3",   "SCS NRCS", 0 },
118   { Spanish,           0, 0, 2, 9, "Z",    "Spanish", map_Spanish },
119   { Swedish,           0, 0, 2, 9, "7",    "Swedish", map_Swedish },
120   { Swedish,           0, 1, 2, 9, "H",    "Swedish", map_Swedish },
121   { Swiss,             0, 0, 2, 9, "=",    "Swiss", map_Swiss },
122   { Turkish,           0, 0, 5, 9, "%0",   "Turkish (DEC)", 0 },
123   { Turkish,           0, 1, 5, 9, "%2",   "Turkish NRCS", 0 },
124   { Unknown,           0, 0,-1,-1, "?",    "Unknown", 0 }
125 };
126 /* *INDENT-ON* */
127 
128 static int hilite_not11;
129 static int national;
130 static int cleanup;
131 
132 static char sgr_hilite[10];
133 static char sgr_reset[10];
134 
135 static int current_Gx[4];
136 
137 static int
lookupCode(National code)138 lookupCode(National code)
139 {
140   int n;
141   for (n = 0; n < TABLESIZE(KnownCharsets); n++) {
142     if (KnownCharsets[n].code == code)
143       return n;
144   }
145   return lookupCode(ASCII);
146 }
147 
148 static const CHARSETS *
lookupCharset(int g,int n)149 lookupCharset(int g, int n)
150 {
151   const CHARSETS *result = 0;
152   if (n >= 0 && n < TABLESIZE(KnownCharsets)) {
153     if (!strcmp(KnownCharsets[n].final, "A")) {
154       if (national || (g == 0)) {
155         n = lookupCode(British);
156       } else {
157         n = lookupCode(British_Latin_1);
158       }
159     }
160     result = &KnownCharsets[n];
161   }
162   return result;
163 }
164 
165 static const char *
charset_name(int g,int n)166 charset_name(int g, int n)
167 {
168   return lookupCharset(g, n)->name;
169 }
170 
171 static int
append_sgr(char * buffer,int used,const char * sgr_string)172 append_sgr(char *buffer, int used, const char *sgr_string)
173 {
174   strcpy(buffer + used, sgr_string);
175   used += (int) strlen(sgr_string);
176   return used;
177 }
178 
179 static void
send32(int row,int upper,const char * not11)180 send32(int row, int upper, const char *not11)
181 {
182   int col;
183   int used = 0;
184   int hilited = 0;
185   char buffer[33 * 8];
186 
187   if (LOG_ENABLED) {
188     fprintf(log_fp, "Note: send32 row %d, upper %d, not11:%s\n",
189             row, upper, not11 ? not11 : "");
190   }
191   for (col = 0; col <= 31; col++) {
192     char ch = (char) (row * 32 + upper + col);
193     if (not11 != 0 && hilite_not11) {
194       const char *p;
195       int found = 0;
196       for (p = not11; *p; ++p) {
197         if (Not11(*p, ch)) {
198           found = 1;
199           break;
200         }
201       }
202       if (found) {
203         if (!hilited) {
204           used = append_sgr(buffer, used, sgr_hilite);
205           hilited = 1;
206         }
207       } else {
208         if (hilited) {
209           used = append_sgr(buffer, used, sgr_reset);
210           hilited = 0;
211         }
212       }
213     }
214     buffer[used++] = ch;
215   }
216   if (hilited) {
217     used = append_sgr(buffer, used, sgr_reset);
218   }
219   buffer[used] = 0;
220   tprintf("%s", buffer);
221 }
222 
223 static char *
scs_params(char * dst,int g)224 scs_params(char *dst, int g)
225 {
226   const CHARSETS *tbl = lookupCharset(g, current_Gx[g]);
227 
228   sprintf(dst, "%c%s",
229           ((tbl->allow96 && get_level() > 2)
230            ? "?-./"[g]
231            : "()*+"[g]),
232           tbl->final);
233   return dst;
234 }
235 
236 static void
do_scs(int g)237 do_scs(int g)
238 {
239   char buffer[80];
240 
241   esc(scs_params(buffer, g));
242 }
243 
244 /* reset given Gg back to sane setting */
245 static int
sane_cs(int g)246 sane_cs(int g)
247 {
248   return lookupCode(((g == 0) || (get_level() <= 1))
249                     ? ASCII
250                     : (get_level() < 3
251                        ? British
252                        : British_Latin_1));   /* ...to get 8-bit codes 128-255 */
253 }
254 
255 /* reset given Gg back to sane setting */
256 static int
reset_scs(int g)257 reset_scs(int g)
258 {
259   int n = sane_cs(g);
260   do_scs(n);
261   return n;
262 }
263 
264 /* reset all of the Gn to sane settings */
265 static int
reset_charset(MENU_ARGS)266 reset_charset(MENU_ARGS)
267 {
268   int n;
269 
270   decnrcm(national = FALSE);
271   for (n = 0; n < 4; n++) {
272     int m = sane_cs(cleanup ? 0 : n);
273     if (m != current_Gx[n] || (m == 0)) {
274       current_Gx[n] = m;
275       do_scs(n);
276     }
277   }
278   return MENU_NOHOLD;
279 }
280 
281 static int the_code;
282 static int the_list[TABLESIZE(KnownCharsets) + 2];
283 
284 static int
lookup_Gx(MENU_ARGS)285 lookup_Gx(MENU_ARGS)
286 {
287   int n;
288   the_code = -1;
289   for (n = 0; n < TABLESIZE(KnownCharsets); n++) {
290     if (the_list[n]
291         && !strcmp(the_title, KnownCharsets[n].name)) {
292       the_code = n;
293       break;
294     }
295   }
296   return MENU_NOHOLD;
297 }
298 
299 static void
specify_any_Gx(MENU_ARGS,int g)300 specify_any_Gx(MENU_ARGS, int g)
301 {
302   MENU my_menu[TABLESIZE(KnownCharsets) + 2];
303   int n, m;
304 
305   /*
306    * Build up a menu of the character sets we will allow the user to specify.
307    * There are a couple of tentative table entries (the "?" ones), which we
308    * won't show in any event.  Beyond that, we limit some of the character sets
309    * based on the emulation level (vt220 implements national replacement
310    * character sets, for example, but not the 96-character ISO Latin-1).
311    */
312   for (n = m = 0; n < TABLESIZE(KnownCharsets); n++) {
313     the_list[n] = 0;
314     if (!strcmp(KnownCharsets[n].final, "?"))
315       continue;
316     if (get_level() < KnownCharsets[n].first)
317       continue;
318     if (get_level() > KnownCharsets[n].last)
319       continue;
320     if (((g == 0) || national) && KnownCharsets[n].allow96)
321       continue;
322     if (((g != 0) && !national) && (KnownCharsets[n].code == British))
323       continue;
324     if (m && !strcmp(my_menu[m - 1].description, KnownCharsets[n].name))
325       continue;
326     my_menu[m].description = KnownCharsets[n].name;
327     my_menu[m].dispatch = lookup_Gx;
328     the_list[n] = 1;
329     m++;
330   }
331   my_menu[m].description = "";
332   my_menu[m].dispatch = 0;
333 
334   do {
335     vt_clear(2);
336     __(title(0), println(the_title));
337     __(title(2), println("Choose character-set:"));
338   } while (menu(my_menu) && the_code < 0);
339 
340   current_Gx[g] = the_code;
341 }
342 
343 static int
toggle_hilite(MENU_ARGS)344 toggle_hilite(MENU_ARGS)
345 {
346   hilite_not11 = !hilite_not11;
347   if (hilite_not11) {
348     sprintf(sgr_hilite, "%s7m", csi_output());
349     sprintf(sgr_reset, "%sm", csi_output());
350   }
351   return MENU_NOHOLD;
352 }
353 
354 static int
toggle_nrc(MENU_ARGS)355 toggle_nrc(MENU_ARGS)
356 {
357   national = !national;
358   decnrcm(national);
359   return MENU_NOHOLD;
360 }
361 
362 static int
specify_G0(MENU_ARGS)363 specify_G0(MENU_ARGS)
364 {
365   specify_any_Gx(PASS_ARGS, 0);
366   return MENU_NOHOLD;
367 }
368 
369 static int
specify_G1(MENU_ARGS)370 specify_G1(MENU_ARGS)
371 {
372   specify_any_Gx(PASS_ARGS, 1);
373   return MENU_NOHOLD;
374 }
375 
376 static int
specify_G2(MENU_ARGS)377 specify_G2(MENU_ARGS)
378 {
379   specify_any_Gx(PASS_ARGS, 2);
380   return MENU_NOHOLD;
381 }
382 
383 static int
specify_G3(MENU_ARGS)384 specify_G3(MENU_ARGS)
385 {
386   specify_any_Gx(PASS_ARGS, 3);
387   return MENU_NOHOLD;
388 }
389 
390 static int
tst_layout(MENU_ARGS)391 tst_layout(MENU_ARGS)
392 {
393   char buffer[80];
394   return tst_keyboard_layout(scs_params(buffer, 0));
395 }
396 
397 static int
tst_vt100_charsets(MENU_ARGS)398 tst_vt100_charsets(MENU_ARGS)
399 {
400   /* Test of:
401    * SCS    (Select character Set)
402    */
403   int i, g, count, cset;
404 
405   __(cup(1, 10), printf("Selected as G0 (with SI)"));
406   __(cup(1, 48), printf("Selected as G1 (with SO)"));
407   for (count = cset = 0; count < TABLESIZE(KnownCharsets); count++) {
408     const CHARSETS *tbl = KnownCharsets + count;
409     if (tbl->first == 0) {
410       int row = 3 + (4 * cset);
411 
412       scs(1, 'B');
413       cup(row, 1);
414       sgr("1");
415       tprintf("Character set %s (%s)", tbl->final, tbl->name);
416       sgr("0");
417       for (g = 0; g <= 1; g++) {
418         int set_nrc = (get_level() >= 2 && tbl->final[0] == 'A');
419         if (set_nrc)
420           decnrcm(TRUE);
421         scs(g, (int) tbl->final[0]);
422         for (i = 1; i <= 3; i++) {
423           cup(row + i, 10 + 38 * g);
424           send32(i, 0, tbl->not11);
425         }
426         if (set_nrc != national)
427           decnrcm(national);
428       }
429       ++cset;
430     }
431   }
432   scs_normal();
433   __(cup(max_lines, 1), printf("These are the installed character sets. "));
434   return MENU_HOLD;
435 }
436 
437 static int
tst_shift_in_out(MENU_ARGS)438 tst_shift_in_out(MENU_ARGS)
439 {
440   /* Test of:
441      SCS    (Select character Set)
442    */
443   static const char *label[] =
444   {
445     "Selected as G0 (with SI)",
446     "Selected as G1 (with SO)"
447   };
448   int i, cset;
449   char buffer[80];
450 
451   __(cup(1, 10), printf("These are the G0 and G1 character sets."));
452   for (cset = 0; cset < 2; cset++) {
453     const CHARSETS *tbl = lookupCharset(cset, current_Gx[cset]);
454     int row = 3 + (4 * cset);
455 
456     scs(cset, 'B');
457     cup(row, 1);
458     sgr("1");
459     tprintf("Character set %s (%s)", tbl->final, tbl->name);
460     sgr("0");
461 
462     cup(row, 48);
463     tprintf("%s", label[cset]);
464 
465     esc(scs_params(buffer, cset));
466     for (i = 1; i <= 3; i++) {
467       cup(row + i, 10);
468       send32(i, 0, tbl->not11);
469     }
470     scs(cset, 'B');
471   }
472   cup(max_lines, 1);
473   return MENU_HOLD;
474 }
475 
476 #define map_g1_to_gr() esc("~")   /* LS1R */
477 
478 static int
tst_vt220_locking(MENU_ARGS)479 tst_vt220_locking(MENU_ARGS)
480 {
481   /* *INDENT-OFF* */
482   static const struct {
483     int upper;
484     int mapped;
485     const char *code;
486     const char *msg;
487   } table[] = {
488     { 1, 1, "~", "G1 into GR (LS1R)" },
489     { 0, 2, "n", "G2 into GL (LS2)"  }, /* "{" vi */
490     { 1, 2, "}", "G2 into GR (LS2R)" },
491     { 0, 3, "o", "G3 into GL (LS3)"  },
492     { 1, 3, "|", "G3 into GR (LS3R)" },
493   };
494   /* *INDENT-ON* */
495 
496   int i, cset;
497 
498   __(cup(1, 10), tprintf("Locking shifts, with NRC %s:",
499                          STR_ENABLED(national)));
500   for (cset = 0; cset < TABLESIZE(table); cset++) {
501     int row = 3 + (4 * cset);
502     int map = table[cset].mapped;
503     const CHARSETS *tbl = lookupCharset(map, current_Gx[map]);
504     int map_gl = (strstr(table[cset].msg, "into GL") != 0);
505 
506     scs_normal();
507     cup(row, 1);
508     sgr("1");
509     tprintf("Character set %s (%s) in G%d", tbl->final, tbl->name, map);
510     sgr("0");
511 
512     cup(row, 48);
513     tprintf("Maps %s", table[cset].msg);
514 
515     for (i = 1; i <= 3; i++) {
516       if (table[cset].upper) {
517         scs_normal();
518         map_g1_to_gr();
519       } else {
520         do_scs(map);
521         esc(table[cset].code);
522       }
523       cup(row + i, 5);
524       send32(i, 0, map_gl ? tbl->not11 : 0);
525 
526       if (table[cset].upper) {
527         do_scs(map);
528         esc(table[cset].code);
529       } else {
530         scs_normal();
531         map_g1_to_gr();
532       }
533       cup(row + i, 40);
534       send32(i, 128, map_gl ? 0 : tbl->not11);
535     }
536     reset_scs(cset);
537   }
538   scs_normal();
539   cup(max_lines, 1);
540   return MENU_HOLD;
541 }
542 
543 static int
tst_vt220_single(MENU_ARGS)544 tst_vt220_single(MENU_ARGS)
545 {
546   int pass, x, y;
547 
548   for (pass = 0; pass < 2; pass++) {
549     int g = pass + 2;
550     const CHARSETS *tbl = lookupCharset(g, current_Gx[g]);
551 
552     vt_clear(2);
553     cup(1, 1);
554     tprintf("Testing single-shift G%d into GL (SS%d) with NRC %s\n",
555             g, g, STR_ENABLED(national));
556     tprintf("G%d is %s", g, tbl->name);
557 
558     do_scs(g);
559     for (y = 0; y < 16; y++) {
560       for (x = 0; x < 6; x++) {
561         int ch = y + (x * 16) + 32;
562         int hilited = 0;
563 
564         cup(y + 5, (x * 12) + 5);
565         tprintf("%3d: (", ch);
566         esc(pass ? "O" : "N");  /* SS3 or SS2 */
567         if (tbl->not11 && hilite_not11) {
568           const char *p;
569           for (p = tbl->not11; *p; ++p) {
570             if (Not11(*p, ch)) {
571               tprintf("%s", sgr_hilite);
572               hilited = 1;
573               break;
574             }
575           }
576         }
577         tprintf("%c", ch);
578         if (ch == 127 && !tbl->allow96)
579           tprintf(" ");   /* DEL should have been eaten - skip past */
580         if (hilited) {
581           tprintf("%s", sgr_reset);
582         }
583         tprintf(")");
584       }
585     }
586 
587     cup(max_lines, 1);
588     holdit();
589   }
590 
591   return MENU_NOHOLD;
592 }
593 
594 /******************************************************************************/
595 
596 /*
597  * For parsing DECCIR response.  The end of the response consists of so-called
598  * intermediate and final bytes as used by the SCS controls.  Most of the
599  * strings fit into that description, but note that '<', '=' and '>' do not,
600  * since they are used to denote private parameters rather than final bytes.
601  * (But ECMA-48 hedges this by stating that the format in those cases is not
602  * specified).
603  */
604 const char *
parse_Sdesig(const char * source,int * offset)605 parse_Sdesig(const char *source, int *offset)
606 {
607   int j;
608   const char *first = source + (*offset);
609   const char *result = 0;
610   size_t limit = strlen(first);
611 
612   for (j = 0; j < TABLESIZE(KnownCharsets); ++j) {
613     if (KnownCharsets[j].code != Unknown) {
614       size_t check = strlen(KnownCharsets[j].final);
615       if (check <= limit
616           && !strncmp(KnownCharsets[j].final, first, check)) {
617         result = KnownCharsets[j].name;
618         *offset += (int) check;
619         break;
620       }
621     }
622   }
623   if (result == 0) {
624     static char temp[80];
625     sprintf(temp, "? %#x\n", *source);
626     *offset += 1;
627     result = temp;
628   }
629   return result;
630 }
631 
632 /*
633  * Reset G0 to ASCII
634  * Reset G1 to ASCII
635  * Shift-in.
636  */
637 void
scs_normal(void)638 scs_normal(void)
639 {
640   scs(0, 'B');
641 }
642 
643 /*
644  * Set G0 to Line Graphics
645  * Reset G1 to ASCII
646  * Shift-in.
647  */
648 void
scs_graphics(void)649 scs_graphics(void)
650 {
651   scs(0, '0');
652 }
653 
654 int
tst_characters(MENU_ARGS)655 tst_characters(MENU_ARGS)
656 {
657   static char whatis_Gx[4][80];
658   static char hilite_mesg[80];
659   static char nrc_mesg[80];
660   /* *INDENT-OFF* */
661   static MENU my_menu[] = {
662       { "Exit",                                              0 },
663       { "Reset (ASCII for G0, G1, no NRC mode)",             reset_charset },
664       { hilite_mesg,                                         toggle_hilite },
665       { nrc_mesg,                                            toggle_nrc },
666       { whatis_Gx[0],                                        specify_G0 },
667       { whatis_Gx[1],                                        specify_G1 },
668       { whatis_Gx[2],                                        specify_G2 },
669       { whatis_Gx[3],                                        specify_G3 },
670       { "Test VT100 Character Sets",                         tst_vt100_charsets },
671       { "Test Shift In/Shift Out (SI/SO)",                   tst_shift_in_out },
672       { "Test VT220 Locking Shifts",                         tst_vt220_locking },
673       { "Test VT220 Single Shifts",                          tst_vt220_single },
674       { "Test Soft Character Sets",                          not_impl },
675       { "Test Keyboard Layout with G0 Selection",            tst_layout },
676       { "",                                                  0 }
677   };
678   /* *INDENT-ON* */
679 
680   cleanup = 0;
681   hilite_not11 = 1;
682   toggle_hilite(PASS_ARGS);
683   reset_charset(PASS_ARGS);   /* make the menu consistent */
684 
685   if (get_level() > 1 || input_8bits || output_8bits) {
686     int n;
687 
688     do {
689       vt_clear(2);
690       __(title(0), printf("Character-Set Tests"));
691       __(title(2), println("Choose test type:"));
692       sprintf(hilite_mesg, "%s highlighting of non-ISO-8859-1 mapping",
693               STR_ENABLE(hilite_not11));
694       sprintf(nrc_mesg, "%s National Replacement Character (NRC) mode",
695               STR_ENABLE(national));
696       for (n = 0; n < 4; n++) {
697         sprintf(whatis_Gx[n], "Specify G%d (now %s)",
698                 n, charset_name(n, current_Gx[n]));
699       }
700     } while (menu(my_menu));
701     cleanup = 1;
702     /* tidy in case a "vt100" emulator does not ignore SCS */
703     vt_clear(1);
704     return reset_charset(PASS_ARGS);
705   } else {
706     return tst_vt100_charsets(PASS_ARGS);
707   }
708 }
709