ncurses 6.0 - patch 20171028
[ncurses.git] / test / ncurses.c
1 /****************************************************************************
2  * Copyright (c) 1998-2016,2017 Free Software Foundation, Inc.              *
3  *                                                                          *
4  * Permission is hereby granted, free of charge, to any person obtaining a  *
5  * copy of this software and associated documentation files (the            *
6  * "Software"), to deal in the Software without restriction, including      *
7  * without limitation the rights to use, copy, modify, merge, publish,      *
8  * distribute, distribute with modifications, sublicense, and/or sell       *
9  * copies of the Software, and to permit persons to whom the Software is    *
10  * furnished to do so, subject to the following conditions:                 *
11  *                                                                          *
12  * The above copyright notice and this permission notice shall be included  *
13  * in all copies or substantial portions of the Software.                   *
14  *                                                                          *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
16  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
18  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
19  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
21  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
22  *                                                                          *
23  * Except as contained in this notice, the name(s) of the above copyright   *
24  * holders shall not be used in advertising or otherwise to promote the     *
25  * sale, use or other dealings in this Software without prior written       *
26  * authorization.                                                           *
27  ****************************************************************************/
28 /****************************************************************************
29
30 NAME
31    ncurses.c --- ncurses library exerciser
32
33 SYNOPSIS
34    ncurses
35
36 DESCRIPTION
37    An interactive test module for the ncurses library.
38
39 AUTHOR
40    Author: Eric S. Raymond <esr@snark.thyrsus.com> 1993
41            Thomas E. Dickey (beginning revision 1.27 in 1996).
42
43 $Id: ncurses.c,v 1.487 2017/10/28 00:54:41 tom Exp $
44
45 ***************************************************************************/
46
47 #include <test.priv.h>
48
49 #ifdef __hpux
50 #undef mvwdelch                 /* HPUX 11.23 macro will not compile */
51 #endif
52
53 #if HAVE_GETTIMEOFDAY
54 #if HAVE_SYS_TIME_H && HAVE_SYS_TIME_SELECT
55 #include <sys/time.h>
56 #endif
57 #if HAVE_SYS_SELECT_H
58 #include <sys/select.h>
59 #endif
60 #endif
61
62 #if USE_LIBPANEL
63 #include <panel.h>
64 #endif
65
66 #if USE_LIBMENU
67 #include <menu.h>
68 #endif
69
70 #if USE_LIBFORM
71 #include <form.h>
72 #endif
73
74 #ifdef NCURSES_VERSION
75
76 #define NCURSES_CONST_PARAM const void
77
78 #ifdef TRACE
79 static unsigned save_trace = TRACE_ORDINARY | TRACE_ICALLS | TRACE_CALLS;
80 #endif
81
82 #else
83
84 #define NCURSES_CONST_PARAM char
85
86 #define mmask_t chtype          /* not specified in XSI */
87
88 #ifndef ACS_S3
89 #ifdef CURSES_ACS_ARRAY
90 #define ACS_S3          (CURSES_ACS_ARRAY['p'])         /* scan line 3 */
91 #define ACS_S7          (CURSES_ACS_ARRAY['r'])         /* scan line 7 */
92 #define ACS_LEQUAL      (CURSES_ACS_ARRAY['y'])         /* less/equal */
93 #define ACS_GEQUAL      (CURSES_ACS_ARRAY['z'])         /* greater/equal */
94 #define ACS_PI          (CURSES_ACS_ARRAY['{'])         /* Pi */
95 #define ACS_NEQUAL      (CURSES_ACS_ARRAY['|'])         /* not equal */
96 #define ACS_STERLING    (CURSES_ACS_ARRAY['}'])         /* UK pound sign */
97 #else
98 #define ACS_S3          (A_ALTCHARSET + 'p')    /* scan line 3 */
99 #define ACS_S7          (A_ALTCHARSET + 'r')    /* scan line 7 */
100 #define ACS_LEQUAL      (A_ALTCHARSET + 'y')    /* less/equal */
101 #define ACS_GEQUAL      (A_ALTCHARSET + 'z')    /* greater/equal */
102 #define ACS_PI          (A_ALTCHARSET + '{')    /* Pi */
103 #define ACS_NEQUAL      (A_ALTCHARSET + '|')    /* not equal */
104 #define ACS_STERLING    (A_ALTCHARSET + '}')    /* UK pound sign */
105 #endif
106 #endif /* ACS_S3 */
107
108 #ifndef WACS_S3
109 #ifdef CURSES_WACS_ARRAY
110 #define WACS_S3         (&(CURSES_WACS_ARRAY['p']))     /* scan line 3 */
111 #define WACS_S7         (&(CURSES_WACS_ARRAY['r']))     /* scan line 7 */
112 #define WACS_LEQUAL     (&(CURSES_WACS_ARRAY['y']))     /* less/equal */
113 #define WACS_GEQUAL     (&(CURSES_WACS_ARRAY['z']))     /* greater/equal */
114 #define WACS_PI         (&(CURSES_WACS_ARRAY['{']))     /* Pi */
115 #define WACS_NEQUAL     (&(CURSES_WACS_ARRAY['|']))     /* not equal */
116 #define WACS_STERLING   (&(CURSES_WACS_ARRAY['}']))     /* UK pound sign */
117 #endif
118 #endif
119
120 #endif
121
122 #if HAVE_WCSRTOMBS
123 #define count_wchars(src, len, state)      wcsrtombs(0,   &src, len, state)
124 #define trans_wchars(dst, src, len, state) wcsrtombs(dst, &src, len, state)
125 #define reset_wchars(state) init_mb(state)
126 #elif HAVE_WCSTOMBS && HAVE_MBTOWC && HAVE_MBLEN
127 #define count_wchars(src, len, state)      wcstombs(0,   src, len)
128 #define trans_wchars(dst, src, len, state) wcstombs(dst, src, len)
129 #define reset_wchars(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
130 #define state_unused
131 #endif
132
133 #if HAVE_MBSRTOWCS
134 #define count_mbytes(src, len, state)      mbsrtowcs(0,   &src, len, state)
135 #define trans_mbytes(dst, src, len, state) mbsrtowcs(dst, &src, len, state)
136 #define reset_mbytes(state) init_mb(state)
137 #elif HAVE_MBSTOWCS && HAVE_MBTOWC && HAVE_MBLEN
138 #define count_mbytes(src, len, state)      mbstowcs(0,   src, len)
139 #define trans_mbytes(dst, src, len, state) mbstowcs(dst, src, len)
140 #define reset_mbytes(state) IGNORE_RC(mblen(NULL, 0)), IGNORE_RC(mbtowc(NULL, NULL, 0))
141 #define state_unused
142 #endif
143
144 #define ToggleAcs(temp,real) temp = ((temp == real) ? NULL : real)
145
146 #define P(string)       printw("%s\n", string)
147
148 #define BLANK           ' '     /* this is the background character */
149
150 static int MaxColors;           /* the actual number of colors we'll use */
151 static int MinColors;           /* the minimum color code */
152 static bool UseColors;          /* true if we use colors */
153
154 #undef max_pairs
155 static int max_pairs;           /* ...and the number of color pairs */
156
157 #if HAVE_COLOR_CONTENT
158 typedef struct {
159     NCURSES_COLOR_T red;
160     NCURSES_COLOR_T green;
161     NCURSES_COLOR_T blue;
162 } RGB_DATA;
163
164 static RGB_DATA *all_colors;
165 #endif
166
167 static void main_menu(bool);
168 static void failed(const char *s) GCC_NORETURN;
169
170 static void
171 failed(const char *s)
172 {
173     perror(s);
174     endwin();
175     ExitProgram(EXIT_FAILURE);
176 }
177
178 static void
179 Repaint(void)
180 {
181     touchwin(stdscr);
182 #if HAVE_CURSCR
183     touchwin(curscr);
184     wrefresh(curscr);
185 #else
186     wrefresh(stdscr);
187 #endif
188 }
189
190 static bool
191 isQuit(int c, bool escape)
192 {
193     return ((c) == QUIT || (escape && ((c) == ESCAPE)));
194 }
195 #define case_QUIT       QUIT: case ESCAPE
196
197 /* Common function to allow ^T to toggle trace-mode in the middle of a test
198  * so that trace-files can be made smaller.
199  */
200 static int
201 wGetchar(WINDOW *win)
202 {
203     int c;
204 #ifdef TRACE
205     while ((c = wgetch(win)) == CTRL('T')) {
206         if (_nc_tracing) {
207             save_trace = _nc_tracing;
208             Trace(("TOGGLE-TRACING OFF"));
209             _nc_tracing = 0;
210         } else {
211             _nc_tracing = save_trace;
212         }
213         trace(_nc_tracing);
214         if (_nc_tracing)
215             Trace(("TOGGLE-TRACING ON"));
216     }
217 #else
218     c = wgetch(win);
219 #endif
220     return c;
221 }
222 #define Getchar() wGetchar(stdscr)
223
224 #if USE_SOFTKEYS
225 /* replaces wgetnstr(), since we want to be able to edit values */
226 static void
227 wGetstring(WINDOW *win, char *buffer, int limit)
228 {
229     int y0, x0, x, ch;
230     bool done = FALSE;
231
232     echo();
233     getyx(win, y0, x0);
234     (void) wattrset(win, A_REVERSE);
235
236     x = (int) strlen(buffer);
237     while (!done) {
238         if (x > (int) strlen(buffer))
239             x = (int) strlen(buffer);
240         wmove(win, y0, x0);
241         wprintw(win, "%-*s", limit, buffer);
242         wmove(win, y0, x0 + x);
243         switch (ch = wGetchar(win)) {
244         case '\n':
245         case KEY_ENTER:
246             done = TRUE;
247             break;
248         case CTRL('U'):
249             *buffer = '\0';
250             break;
251         case '\b':
252         case KEY_BACKSPACE:
253         case KEY_DC:
254             if (x > 0) {
255                 int j;
256                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
257                     ;
258                 }
259             } else {
260                 beep();
261             }
262             break;
263         case KEY_LEFT:
264             if (x > 0) {
265                 --x;
266             } else {
267                 flash();
268             }
269             break;
270         case KEY_RIGHT:
271             ++x;
272             break;
273         default:
274             if (!isprint(ch) || ch >= KEY_MIN) {
275                 beep();
276             } else if ((int) strlen(buffer) < limit) {
277                 int j;
278                 for (j = (int) strlen(buffer) + 1; j > x; --j) {
279                     buffer[j] = buffer[j - 1];
280                 }
281                 buffer[x++] = (char) ch;
282             } else {
283                 flash();
284             }
285         }
286     }
287
288     wattroff(win, A_REVERSE);
289     wmove(win, y0, x0);
290     noecho();
291 }
292 #endif
293
294 #if USE_WIDEC_SUPPORT
295 static wchar_t
296 fullwidth_digit(int ch)
297 {
298     return (wchar_t) (ch + 0xff10 - '0');
299 }
300
301 static void
302 make_fullwidth_text(wchar_t *target, const char *source)
303 {
304     int ch;
305     while ((ch = *source++) != 0) {
306         *target++ = fullwidth_digit(ch);
307     }
308     *target = 0;
309 }
310
311 static void
312 make_narrow_text(wchar_t *target, const char *source)
313 {
314     int ch;
315     while ((ch = *source++) != 0) {
316         *target++ = (wchar_t) ch;
317     }
318     *target = 0;
319 }
320
321 #if USE_LIBPANEL
322 static void
323 make_fullwidth_digit(cchar_t *target, int digit)
324 {
325     wchar_t source[2];
326
327     source[0] = fullwidth_digit(digit + '0');
328     source[1] = 0;
329     setcchar(target, source, A_NORMAL, 0, 0);
330 }
331 #endif
332
333 static int
334 wGet_wchar(WINDOW *win, wint_t *result)
335 {
336     int c;
337 #ifdef TRACE
338     while ((c = wget_wch(win, result)) == CTRL('T')) {
339         if (_nc_tracing) {
340             save_trace = _nc_tracing;
341             Trace(("TOGGLE-TRACING OFF"));
342             _nc_tracing = 0;
343         } else {
344             _nc_tracing = save_trace;
345         }
346         trace(_nc_tracing);
347         if (_nc_tracing)
348             Trace(("TOGGLE-TRACING ON"));
349     }
350 #else
351     c = wget_wch(win, result);
352 #endif
353     return c;
354 }
355 #define Get_wchar(result) wGet_wchar(stdscr, result)
356
357 /* replaces wgetn_wstr(), since we want to be able to edit values */
358 static void
359 wGet_wstring(WINDOW *win, wchar_t *buffer, int limit)
360 {
361     int y0, x0, x;
362     wint_t ch;
363     bool done = FALSE;
364     bool fkey = FALSE;
365
366     echo();
367     getyx(win, y0, x0);
368     (void) wattrset(win, A_REVERSE);
369
370     x = (int) wcslen(buffer);
371     while (!done) {
372         if (x > (int) wcslen(buffer))
373             x = (int) wcslen(buffer);
374
375         /* clear the "window' */
376         wmove(win, y0, x0);
377         wprintw(win, "%*s", limit, " ");
378
379         /* write the existing buffer contents */
380         wmove(win, y0, x0);
381         waddnwstr(win, buffer, limit);
382
383         /* positions the cursor past character 'x' */
384         wmove(win, y0, x0);
385         waddnwstr(win, buffer, x);
386
387         switch (wGet_wchar(win, &ch)) {
388         case KEY_CODE_YES:
389             fkey = TRUE;
390             switch (ch) {
391             case KEY_ENTER:
392                 ch = '\n';
393                 fkey = FALSE;
394                 break;
395             case KEY_BACKSPACE:
396             case KEY_DC:
397                 ch = '\b';
398                 fkey = FALSE;
399                 break;
400             case KEY_LEFT:
401             case KEY_RIGHT:
402                 break;
403             default:
404                 ch = (wint_t) -1;
405                 break;
406             }
407             break;
408         case OK:
409             fkey = FALSE;
410             break;
411         default:
412             ch = (wint_t) -1;
413             fkey = TRUE;
414             break;
415         }
416
417         switch (ch) {
418         case '\n':
419             done = TRUE;
420             break;
421         case CTRL('U'):
422             *buffer = '\0';
423             break;
424         case '\b':
425             if (x > 0) {
426                 int j;
427                 for (j = --x; (buffer[j] = buffer[j + 1]) != '\0'; ++j) {
428                     ;
429                 }
430             } else {
431                 beep();
432             }
433             break;
434         case KEY_LEFT:
435             if (x > 0) {
436                 --x;
437             } else {
438                 beep();
439             }
440             break;
441         case KEY_RIGHT:
442             ++x;
443             break;
444         default:
445             if (fkey) {
446                 beep();
447             } else if ((int) wcslen(buffer) < limit) {
448                 int j;
449                 for (j = (int) wcslen(buffer) + 1; j > x; --j) {
450                     buffer[j] = buffer[j - 1];
451                 }
452                 buffer[x++] = (wchar_t) ch;
453             } else {
454                 beep();
455             }
456         }
457     }
458
459     wattroff(win, A_REVERSE);
460     wmove(win, y0, x0);
461     noecho();
462 }
463
464 #endif
465
466 static void
467 Pause(void)
468 {
469     move(LINES - 1, 0);
470     addstr("Press any key to continue... ");
471     (void) Getchar();
472 }
473
474 static void
475 Cannot(const char *what)
476 {
477     printw("\nThis %s terminal %s\n\n", getenv("TERM"), what);
478     Pause();
479     endwin();
480 }
481
482 static void
483 ShellOut(bool message)
484 {
485     if (message)
486         addstr("Shelling out...");
487     def_prog_mode();
488     endwin();
489 #ifdef __MINGW32__
490     system("cmd.exe");
491 #else
492     IGNORE_RC(system("sh"));
493 #endif
494     if (message)
495         addstr("returned from shellout.\n");
496     refresh();
497 }
498
499 #ifdef NCURSES_MOUSE_VERSION
500 /*
501  * This function is the same as _tracemouse(), but we cannot count on that
502  * being available in the non-debug library.
503  */
504 static const char *
505 mouse_decode(MEVENT const *ep)
506 {
507     static char buf[80 + (5 * 10) + (32 * 15)];
508
509     (void) _nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf))
510                        "id %2d at (%2d, %2d, %d) state %4lx = {",
511                        ep->id, ep->x, ep->y, ep->z, (unsigned long) ep->bstate);
512
513 #define SHOW(m, s) \
514         if ((ep->bstate & m)==m) { \
515                 _nc_STRCAT(buf, s, sizeof(buf)); \
516                 _nc_STRCAT(buf, ", ", sizeof(buf)); \
517         }
518
519     SHOW(BUTTON1_RELEASED, "release-1");
520     SHOW(BUTTON1_PRESSED, "press-1");
521     SHOW(BUTTON1_CLICKED, "click-1");
522     SHOW(BUTTON1_DOUBLE_CLICKED, "doubleclick-1");
523     SHOW(BUTTON1_TRIPLE_CLICKED, "tripleclick-1");
524 #if NCURSES_MOUSE_VERSION == 1
525     SHOW(BUTTON1_RESERVED_EVENT, "reserved-1");
526 #endif
527
528     SHOW(BUTTON2_RELEASED, "release-2");
529     SHOW(BUTTON2_PRESSED, "press-2");
530     SHOW(BUTTON2_CLICKED, "click-2");
531     SHOW(BUTTON2_DOUBLE_CLICKED, "doubleclick-2");
532     SHOW(BUTTON2_TRIPLE_CLICKED, "tripleclick-2");
533 #if NCURSES_MOUSE_VERSION == 1
534     SHOW(BUTTON2_RESERVED_EVENT, "reserved-2");
535 #endif
536
537     SHOW(BUTTON3_RELEASED, "release-3");
538     SHOW(BUTTON3_PRESSED, "press-3");
539     SHOW(BUTTON3_CLICKED, "click-3");
540     SHOW(BUTTON3_DOUBLE_CLICKED, "doubleclick-3");
541     SHOW(BUTTON3_TRIPLE_CLICKED, "tripleclick-3");
542 #if NCURSES_MOUSE_VERSION == 1
543     SHOW(BUTTON3_RESERVED_EVENT, "reserved-3");
544 #endif
545
546     SHOW(BUTTON4_RELEASED, "release-4");
547     SHOW(BUTTON4_PRESSED, "press-4");
548     SHOW(BUTTON4_CLICKED, "click-4");
549     SHOW(BUTTON4_DOUBLE_CLICKED, "doubleclick-4");
550     SHOW(BUTTON4_TRIPLE_CLICKED, "tripleclick-4");
551 #if NCURSES_MOUSE_VERSION == 1
552     SHOW(BUTTON4_RESERVED_EVENT, "reserved-4");
553 #endif
554
555 #if NCURSES_MOUSE_VERSION == 2
556     SHOW(BUTTON5_RELEASED, "release-5");
557     SHOW(BUTTON5_PRESSED, "press-5");
558     SHOW(BUTTON5_CLICKED, "click-5");
559     SHOW(BUTTON5_DOUBLE_CLICKED, "doubleclick-5");
560     SHOW(BUTTON5_TRIPLE_CLICKED, "tripleclick-5");
561 #endif
562
563     SHOW(BUTTON_CTRL, "ctrl");
564     SHOW(BUTTON_SHIFT, "shift");
565     SHOW(BUTTON_ALT, "alt");
566     SHOW(ALL_MOUSE_EVENTS, "all-events");
567     SHOW(REPORT_MOUSE_POSITION, "position");
568
569 #undef SHOW
570
571     if (buf[strlen(buf) - 1] == ' ')
572         buf[strlen(buf) - 2] = '\0';
573     _nc_STRCAT(buf, "}", sizeof(buf));
574     return (buf);
575 }
576
577 static void
578 show_mouse(WINDOW *win)
579 {
580     int y, x;
581     MEVENT event;
582     bool outside;
583     bool show_loc;
584
585     getmouse(&event);
586     outside = !wenclose(win, event.y, event.x);
587
588     if (outside) {
589         (void) wstandout(win);
590         waddstr(win, "KEY_MOUSE");
591         (void) wstandend(win);
592     } else {
593         waddstr(win, "KEY_MOUSE");
594     }
595     wprintw(win, ", %s", mouse_decode(&event));
596
597     if (outside)
598         win = stdscr;
599
600     show_loc = wmouse_trafo(win, &event.y, &event.x, FALSE);
601
602     if (show_loc) {
603         getyx(win, y, x);
604         wmove(win, event.y, event.x);
605         waddch(win, '*');
606         wmove(win, y, x);
607     }
608
609     if (outside)
610         wnoutrefresh(win);
611 }
612 #endif /* NCURSES_MOUSE_VERSION */
613
614 /****************************************************************************
615  *
616  * Character input test
617  *
618  ****************************************************************************/
619
620 #define NUM_GETCH_FLAGS 256
621 typedef bool GetchFlags[NUM_GETCH_FLAGS];
622
623 static void
624 setup_getch(WINDOW *win, GetchFlags flags)
625 {
626     keypad(win, flags['k']);    /* should be redundant, but for testing */
627     meta(win, flags['m']);      /* force this to a known state */
628     if (flags['e'])
629         echo();
630     else
631         noecho();
632 }
633
634 static void
635 init_getch(WINDOW *win, GetchFlags flags, int delay)
636 {
637     memset(flags, FALSE, NUM_GETCH_FLAGS);
638     flags[UChar('k')] = (win == stdscr);
639     flags[UChar('m')] = TRUE;
640     flags[UChar('t')] = (delay != 0);
641
642     setup_getch(win, flags);
643 }
644
645 static bool
646 blocking_getch(GetchFlags flags, int delay)
647 {
648     return ((delay < 0) && flags['t']);
649 }
650
651 #define ExitOnEscape() (flags[UChar('k')] && flags[UChar('t')])
652
653 static void
654 wgetch_help(WINDOW *win, GetchFlags flags)
655 {
656     static const char *help[] =
657     {
658         "e  -- toggle echo mode"
659         ,"g  -- triggers a getstr test"
660         ,"k  -- toggle keypad/literal mode"
661         ,"m  -- toggle meta (7-bit/8-bit) mode"
662         ,"^q -- quit"
663         ,"s  -- shell out"
664         ,"t  -- toggle timeout"
665         ,"w  -- create a new window"
666 #ifdef SIGTSTP
667         ,"z  -- suspend this process"
668 #endif
669     };
670     int y, x;
671     unsigned chk = ((SIZEOF(help) + 1) / 2);
672     unsigned n;
673
674     getyx(win, y, x);
675     move(0, 0);
676     printw("Type any key to see its %s value.  Also:\n",
677            flags['k'] ? "keypad" : "literal");
678     for (n = 0; n < SIZEOF(help); ++n) {
679         const char *msg = help[n];
680         int row = 1 + (int) (n % chk);
681         int col = (n >= chk) ? COLS / 2 : 0;
682         int flg = ((strstr(msg, "toggle") != 0)
683                    && (flags[UChar(*msg)] != FALSE));
684         if (*msg == '^' && ExitOnEscape())
685             msg = "^[,^q -- quit";
686         if (flg)
687             (void) standout();
688         MvPrintw(row, col, "%s", msg);
689         if (col == 0)
690             clrtoeol();
691         if (flg)
692             (void) standend();
693     }
694     wrefresh(stdscr);
695     wmove(win, y, x);
696 }
697
698 static void
699 wgetch_wrap(WINDOW *win, int first_y)
700 {
701     int last_y = getmaxy(win) - 1;
702     int y = getcury(win) + 1;
703
704     if (y >= last_y)
705         y = first_y;
706     wmove(win, y, 0);
707     wclrtoeol(win);
708 }
709
710 #if defined(KEY_RESIZE) && HAVE_WRESIZE
711 typedef struct {
712     WINDOW *text;
713     WINDOW *frame;
714 } WINSTACK;
715
716 static WINSTACK *winstack = 0;
717 static unsigned len_winstack = 0;
718
719 static void
720 forget_boxes(void)
721 {
722     if (winstack != 0) {
723         free(winstack);
724     }
725     winstack = 0;
726     len_winstack = 0;
727 }
728
729 static void
730 remember_boxes(unsigned level, WINDOW *txt_win, WINDOW *box_win)
731 {
732     unsigned need = (level + 1) * 2;
733
734     assert(level < (unsigned) COLS);
735
736     if (winstack == 0) {
737         len_winstack = 20;
738         winstack = typeMalloc(WINSTACK, len_winstack);
739     } else if (need >= len_winstack) {
740         len_winstack = need;
741         winstack = typeRealloc(WINSTACK, len_winstack, winstack);
742     }
743     if (!winstack)
744         failed("remember_boxes");
745     winstack[level].text = txt_win;
746     winstack[level].frame = box_win;
747 }
748
749 #if USE_SOFTKEYS && (defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH < 20071229) && NCURSES_EXT_FUNCS
750 static void
751 slk_repaint(void)
752 {
753     /* this chunk is now done in resize_term() */
754     slk_touch();
755     slk_clear();
756     slk_noutrefresh();
757 }
758
759 #else
760 #define slk_repaint()           /* nothing */
761 #endif
762
763 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
764 /*
765  * For wgetch_test(), we create pairs of windows - one for a box, one for text.
766  * Resize both and paint the box in the parent.
767  */
768 static void
769 resize_boxes(unsigned level, WINDOW *win)
770 {
771     unsigned n;
772     int base = 5;
773     int high = LINES - base;
774     int wide = COLS;
775
776     touchwin(stdscr);
777     wnoutrefresh(stdscr);
778
779     slk_repaint();
780
781     for (n = 0; n < level; ++n) {
782         wresize(winstack[n].frame, high, wide);
783         wresize(winstack[n].text, high - 2, wide - 2);
784         high -= 2;
785         wide -= 2;
786         werase(winstack[n].text);
787         box(winstack[n].frame, 0, 0);
788         wnoutrefresh(winstack[n].frame);
789         wprintw(winstack[n].text,
790                 "size %dx%d\n",
791                 getmaxy(winstack[n].text),
792                 getmaxx(winstack[n].text));
793         wnoutrefresh(winstack[n].text);
794         if (winstack[n].text == win)
795             break;
796     }
797     doupdate();
798 }
799 #endif /* resize_boxes */
800 #else
801 #define forget_boxes()          /* nothing */
802 #define remember_boxes(level,text,frame)        /* nothing */
803 #endif
804
805 /*
806  * Return-code is OK/ERR or a keyname.
807  */
808 static const char *
809 ok_keyname(int code)
810 {
811     return ((code == OK) ? "OK" : ((code == ERR) ? "ERR" : keyname(code)));
812 }
813
814 static void
815 wgetch_test(unsigned level, WINDOW *win, int delay)
816 {
817     char buf[BUFSIZ];
818     int first_y, first_x;
819     int c;
820     int incount = 0;
821     GetchFlags flags;
822
823     init_getch(win, flags, delay);
824     notimeout(win, FALSE);
825     wtimeout(win, delay);
826     getyx(win, first_y, first_x);
827
828     wgetch_help(win, flags);
829     wsetscrreg(win, first_y, getmaxy(win) - 1);
830     scrollok(win, TRUE);
831
832     for (;;) {
833         while ((c = wGetchar(win)) == ERR) {
834             incount++;
835             if (blocking_getch(flags, delay)) {
836                 (void) wprintw(win, "%05d: input error", incount);
837                 break;
838             } else {
839                 (void) wprintw(win, "%05d: input timed out", incount);
840             }
841             wgetch_wrap(win, first_y);
842         }
843         if (c == ERR && blocking_getch(flags, delay)) {
844             wprintw(win, "ERR");
845             wgetch_wrap(win, first_y);
846         } else if (isQuit(c, ExitOnEscape())) {
847             break;
848         } else if (c == 'e') {
849             flags[UChar('e')] = !flags[UChar('e')];
850             setup_getch(win, flags);
851             wgetch_help(win, flags);
852         } else if (c == 'g') {
853             waddstr(win, "getstr test: ");
854             echo();
855             c = wgetnstr(win, buf, sizeof(buf) - 1);
856             noecho();
857             wprintw(win, "I saw %d characters:\n\t`%s' (%s).",
858                     (int) strlen(buf), buf,
859                     ok_keyname(c));
860             wclrtoeol(win);
861             wgetch_wrap(win, first_y);
862         } else if (c == 'k') {
863             flags[UChar('k')] = !flags[UChar('k')];
864             setup_getch(win, flags);
865             wgetch_help(win, flags);
866         } else if (c == 'm') {
867             flags[UChar('m')] = !flags[UChar('m')];
868             setup_getch(win, flags);
869             wgetch_help(win, flags);
870         } else if (c == 's') {
871             ShellOut(TRUE);
872         } else if (c == 't') {
873             notimeout(win, flags[UChar('t')]);
874             flags[UChar('t')] = !flags[UChar('t')];
875             wgetch_help(win, flags);
876         } else if (c == 'w') {
877             int high = getmaxy(win) - 1 - first_y + 1;
878             int wide = getmaxx(win) - first_x;
879             int old_y, old_x;
880             int new_y = first_y + getbegy(win);
881             int new_x = first_x + getbegx(win);
882
883             getyx(win, old_y, old_x);
884             if (high > 2 && wide > 2) {
885                 WINDOW *wb = newwin(high, wide, new_y, new_x);
886                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
887
888                 box(wb, 0, 0);
889                 wrefresh(wb);
890                 wmove(wi, 0, 0);
891                 remember_boxes(level, wi, wb);
892                 wgetch_test(level + 1, wi, delay);
893                 delwin(wi);
894                 delwin(wb);
895
896                 wgetch_help(win, flags);
897                 wmove(win, old_y, old_x);
898                 touchwin(win);
899                 wrefresh(win);
900                 doupdate();
901             }
902 #ifdef SIGTSTP
903         } else if (c == 'z') {
904             kill(getpid(), SIGTSTP);
905 #endif
906         } else {
907             wprintw(win, "Key pressed: %04o ", c);
908 #ifdef NCURSES_MOUSE_VERSION
909             if (c == KEY_MOUSE) {
910                 show_mouse(win);
911             } else
912 #endif /* NCURSES_MOUSE_VERSION */
913             if (c >= KEY_MIN) {
914 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
915                 if (c == KEY_RESIZE) {
916                     resize_boxes(level, win);
917                 }
918 #endif
919                 (void) waddstr(win, keyname(c));
920             } else if (c >= 0x80) {
921                 unsigned c2 = (unsigned) c;
922 #if !(defined(NCURSES_VERSION) || defined(_XOPEN_CURSES))
923                 /* at least Solaris SVR4 curses breaks unctrl(128), etc. */
924                 c2 &= 0x7f;
925 #endif
926                 if (isprint(c))
927                     (void) wprintw(win, "%c", UChar(c));
928                 else if (c2 != UChar(c))
929                     (void) wprintw(win, "M-%s", unctrl(c2));
930                 else
931                     (void) wprintw(win, "%s", unctrl(c2));
932                 waddstr(win, " (high-half character)");
933             } else {
934                 if (isprint(c))
935                     (void) wprintw(win, "%c (ASCII printable character)", c);
936                 else
937                     (void) wprintw(win, "%s (ASCII control character)",
938                                    unctrl(UChar(c)));
939             }
940             wgetch_wrap(win, first_y);
941         }
942     }
943
944     wtimeout(win, -1);
945
946     if (!level)
947         init_getch(win, flags, delay);
948 }
949
950 static int
951 begin_getch_test(void)
952 {
953     char buf[BUFSIZ];
954     int delay;
955
956     refresh();
957
958 #ifdef NCURSES_MOUSE_VERSION
959     mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, (mmask_t *) 0);
960 #endif
961
962     (void) printw("Delay in 10ths of a second (<CR> for blocking input)? ");
963     echo();
964     getnstr(buf, sizeof(buf) - 1);
965     noecho();
966     nonl();
967
968     if (isdigit(UChar(buf[0]))) {
969         delay = atoi(buf) * 100;
970     } else {
971         delay = -1;
972     }
973     raw();
974     move(6, 0);
975     return delay;
976 }
977
978 static void
979 finish_getch_test(void)
980 {
981 #ifdef NCURSES_MOUSE_VERSION
982     mousemask(0, (mmask_t *) 0);
983 #endif
984     erase();
985     noraw();
986     nl();
987     endwin();
988 }
989
990 static int
991 getch_test(bool recur GCC_UNUSED)
992 {
993     int delay = begin_getch_test();
994
995     slk_restore();
996     wgetch_test(0, stdscr, delay);
997     forget_boxes();
998     finish_getch_test();
999     slk_clear();
1000     return OK;
1001 }
1002
1003 #if USE_WIDEC_SUPPORT
1004 /*
1005  * For wget_wch_test(), we create pairs of windows - one for a box, one for text.
1006  * Resize both and paint the box in the parent.
1007  */
1008 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1009 static void
1010 resize_wide_boxes(unsigned level, WINDOW *win)
1011 {
1012     unsigned n;
1013     int base = 5;
1014     int high = LINES - base;
1015     int wide = COLS;
1016
1017     touchwin(stdscr);
1018     wnoutrefresh(stdscr);
1019
1020     slk_repaint();
1021
1022     for (n = 0; n < level; ++n) {
1023         wresize(winstack[n].frame, high, wide);
1024         wresize(winstack[n].text, high - 2, wide - 2);
1025         high -= 2;
1026         wide -= 2;
1027         werase(winstack[n].text);
1028         box_set(winstack[n].frame, 0, 0);
1029         wnoutrefresh(winstack[n].frame);
1030         wprintw(winstack[n].text,
1031                 "size %dx%d\n",
1032                 getmaxy(winstack[n].text),
1033                 getmaxx(winstack[n].text));
1034         wnoutrefresh(winstack[n].text);
1035         if (winstack[n].text == win)
1036             break;
1037     }
1038     doupdate();
1039 }
1040 #endif /* KEY_RESIZE */
1041
1042 static char *
1043 wcstos(const wchar_t *src)
1044 {
1045     int need;
1046     char *result = 0;
1047     const wchar_t *tmp = src;
1048 #ifndef state_unused
1049     mbstate_t state;
1050 #endif
1051
1052     reset_wchars(state);
1053     if ((need = (int) count_wchars(tmp, 0, &state)) > 0) {
1054         unsigned have = (unsigned) need;
1055         if ((result = typeCalloc(char, have + 1)) != 0) {
1056             tmp = src;
1057             if (trans_wchars(result, tmp, have, &state) != have) {
1058                 free(result);
1059                 result = 0;
1060             }
1061         } else {
1062             failed("wcstos");
1063         }
1064     }
1065     return result;
1066 }
1067
1068 static void
1069 wget_wch_test(unsigned level, WINDOW *win, int delay)
1070 {
1071     wchar_t wchar_buf[BUFSIZ];
1072     wint_t wint_buf[BUFSIZ];
1073     int first_y, first_x;
1074     wint_t c;
1075     int incount = 0;
1076     GetchFlags flags;
1077     int code;
1078     char *temp;
1079
1080     init_getch(win, flags, delay);
1081     notimeout(win, FALSE);
1082     wtimeout(win, delay);
1083     getyx(win, first_y, first_x);
1084
1085     wgetch_help(win, flags);
1086     wsetscrreg(win, first_y, getmaxy(win) - 1);
1087     scrollok(win, TRUE);
1088
1089     for (;;) {
1090         while ((code = wGet_wchar(win, &c)) == ERR) {
1091             incount++;
1092             if (blocking_getch(flags, delay)) {
1093                 (void) wprintw(win, "%05d: input error", incount);
1094                 break;
1095             } else {
1096                 (void) wprintw(win, "%05d: input timed out", incount);
1097             }
1098             wgetch_wrap(win, first_y);
1099         }
1100         if (code == ERR && blocking_getch(flags, delay)) {
1101             wprintw(win, "ERR");
1102             wgetch_wrap(win, first_y);
1103         } else if (isQuit((int) c, ExitOnEscape())) {
1104             break;
1105         } else if (c == 'e') {
1106             flags[UChar('e')] = !flags[UChar('e')];
1107             setup_getch(win, flags);
1108             wgetch_help(win, flags);
1109         } else if (c == 'g') {
1110             waddstr(win, "getstr test: ");
1111             echo();
1112             code = wgetn_wstr(win, wint_buf, BUFSIZ - 1);
1113             noecho();
1114             if (code == ERR) {
1115                 wprintw(win, "wgetn_wstr returns an error.");
1116             } else {
1117                 int n;
1118                 for (n = 0; (wchar_buf[n] = (wchar_t) wint_buf[n]) != 0; ++n) {
1119                     ;
1120                 }
1121                 if ((temp = wcstos(wchar_buf)) != 0) {
1122                     wprintw(win, "I saw %d characters:\n\t`%s'.",
1123                             (int) wcslen(wchar_buf), temp);
1124                     free(temp);
1125                 } else {
1126                     wprintw(win, "I saw %d characters (cannot convert).",
1127                             (int) wcslen(wchar_buf));
1128                 }
1129             }
1130             wclrtoeol(win);
1131             wgetch_wrap(win, first_y);
1132         } else if (c == 'k') {
1133             flags[UChar('k')] = !flags[UChar('k')];
1134             setup_getch(win, flags);
1135             wgetch_help(win, flags);
1136         } else if (c == 'm') {
1137             flags[UChar('m')] = !flags[UChar('m')];
1138             setup_getch(win, flags);
1139             wgetch_help(win, flags);
1140         } else if (c == 's') {
1141             ShellOut(TRUE);
1142         } else if (c == 't') {
1143             notimeout(win, flags[UChar('t')]);
1144             flags[UChar('t')] = !flags[UChar('t')];
1145             wgetch_help(win, flags);
1146         } else if (c == 'w') {
1147             int high = getmaxy(win) - 1 - first_y + 1;
1148             int wide = getmaxx(win) - first_x;
1149             int old_y, old_x;
1150             int new_y = first_y + getbegy(win);
1151             int new_x = first_x + getbegx(win);
1152
1153             getyx(win, old_y, old_x);
1154             if (high > 2 && wide > 2) {
1155                 WINDOW *wb = newwin(high, wide, new_y, new_x);
1156                 WINDOW *wi = newwin(high - 2, wide - 2, new_y + 1, new_x + 1);
1157
1158                 box_set(wb, 0, 0);
1159                 wrefresh(wb);
1160                 wmove(wi, 0, 0);
1161                 remember_boxes(level, wi, wb);
1162                 wget_wch_test(level + 1, wi, delay);
1163                 delwin(wi);
1164                 delwin(wb);
1165
1166                 wgetch_help(win, flags);
1167                 wmove(win, old_y, old_x);
1168                 touchwin(win);
1169                 wrefresh(win);
1170             }
1171 #ifdef SIGTSTP
1172         } else if (c == 'z') {
1173             kill(getpid(), SIGTSTP);
1174 #endif
1175         } else {
1176             wprintw(win, "Key pressed: %04o ", (int) c);
1177 #ifdef NCURSES_MOUSE_VERSION
1178             if (c == KEY_MOUSE) {
1179                 show_mouse(win);
1180             } else
1181 #endif /* NCURSES_MOUSE_VERSION */
1182             if (code == KEY_CODE_YES) {
1183 #if defined(KEY_RESIZE) && HAVE_WRESIZE
1184                 if (c == KEY_RESIZE) {
1185                     resize_wide_boxes(level, win);
1186                 }
1187 #endif
1188                 (void) waddstr(win, keyname((wchar_t) c));
1189             } else {
1190                 (void) waddstr(win, key_name((wchar_t) c));
1191                 if (c < 256 && iscntrl(c)) {
1192                     (void) wprintw(win, " (control character)");
1193                 } else {
1194                     (void) wprintw(win, " = %#x (printable character)",
1195                                    (unsigned) c);
1196                 }
1197             }
1198             wgetch_wrap(win, first_y);
1199         }
1200     }
1201
1202     wtimeout(win, -1);
1203
1204     if (!level)
1205         init_getch(win, flags, delay);
1206 }
1207
1208 static int
1209 x_getch_test(bool recur GCC_UNUSED)
1210 {
1211     int delay = begin_getch_test();
1212
1213     slk_restore();
1214     wget_wch_test(0, stdscr, delay);
1215     forget_boxes();
1216     finish_getch_test();
1217     slk_clear();
1218     return OK;
1219 }
1220 #endif
1221
1222 /****************************************************************************
1223  *
1224  * Character attributes test
1225  *
1226  ****************************************************************************/
1227
1228 #if HAVE_SETUPTERM || HAVE_TGETENT
1229 #define get_ncv() TIGETNUM("ncv","NC")
1230 #define get_xmc() TIGETNUM("xmc","sg")
1231 #else
1232 #define get_ncv() -1
1233 #define get_xmc() -1
1234 #endif
1235
1236 #if !HAVE_TERMATTRS
1237 static chtype
1238 my_termattrs(void)
1239 {
1240     static int first = TRUE;
1241     static chtype result = 0;
1242
1243     if (first) {
1244 #if !HAVE_TIGETSTR
1245         char buffer[4096];
1246         char parsed[4096];
1247         char *area_pointer = parsed;
1248
1249         tgetent(buffer, getenv("TERM"));
1250 #endif
1251
1252         if (TIGETSTR("smso", "so"))
1253             result |= A_STANDOUT;
1254         if (TIGETSTR("smul", "us"))
1255             result |= A_UNDERLINE;
1256         if (TIGETSTR("rev", "mr"))
1257             result |= A_REVERSE;
1258         if (TIGETSTR("blink", "mb"))
1259             result |= A_BLINK;
1260         if (TIGETSTR("dim", "mh"))
1261             result |= A_DIM;
1262         if (TIGETSTR("bold", "md"))
1263             result |= A_BOLD;
1264         if (TIGETSTR("smacs", "ac"))
1265             result |= A_ALTCHARSET;
1266
1267         first = FALSE;
1268     }
1269     return result;
1270 }
1271 #define termattrs() my_termattrs()
1272 #endif
1273
1274 #define ATTRSTRING_1ST 32       /* ' ' */
1275 #define ATTRSTRING_END 126      /* '~' */
1276
1277 #define COLS_PRE_ATTRS 5
1278 #define COLS_AFT_ATTRS 15
1279 #define COL_ATTRSTRING (COLS_PRE_ATTRS + 17)
1280 #define LEN_ATTRSTRING (COLS - (COL_ATTRSTRING + COLS_AFT_ATTRS))
1281 #define MAX_ATTRSTRING (ATTRSTRING_END + 1 - ATTRSTRING_1ST)
1282
1283 static char attr_test_string[MAX_ATTRSTRING + 1];
1284
1285 static void
1286 attr_legend(WINDOW *helpwin)
1287 {
1288     int row = 1;
1289     int col = 1;
1290
1291     MvWPrintw(helpwin, row++, col,
1292               "ESC to exit.");
1293     MvWPrintw(helpwin, row++, col,
1294               "^L repaints.");
1295     ++row;
1296     MvWPrintw(helpwin, row++, col,
1297               "Modify the test strings:");
1298     MvWPrintw(helpwin, row++, col,
1299               "  A digit sets gaps on each side of displayed attributes");
1300     MvWPrintw(helpwin, row++, col,
1301               "  </> shifts the text left/right. ");
1302     ++row;
1303     MvWPrintw(helpwin, row++, col,
1304               "Toggles:");
1305     if (UseColors) {
1306         MvWPrintw(helpwin, row++, col,
1307                   "  f/F/b/B toggle foreground/background background color");
1308         MvWPrintw(helpwin, row++, col,
1309                   "  t/T     toggle text/background color attribute");
1310     }
1311     MvWPrintw(helpwin, row++, col,
1312               "  a/A     toggle ACS (alternate character set) mapping");
1313     MvWPrintw(helpwin, row, col,
1314               "  v/V     toggle video attribute to combine with each line");
1315 #if USE_WIDEC_SUPPORT
1316     MvWPrintw(helpwin, row, col,
1317               "  w/W     toggle normal/wide (double-width) test-characters");
1318 #endif
1319 }
1320
1321 static void
1322 show_color_attr(int fg, int bg, int tx)
1323 {
1324     if (UseColors) {
1325         printw("  Colors (fg %d, bg %d", fg, bg);
1326         if (tx >= 0)
1327             printw(", text %d", tx);
1328         printw("),");
1329     }
1330 }
1331
1332 static bool
1333 cycle_color_attr(int ch, NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg, NCURSES_COLOR_T *tx)
1334 {
1335     bool error = FALSE;
1336
1337     if (UseColors) {
1338         switch (ch) {
1339         case 'f':
1340             *fg = (NCURSES_COLOR_T) (*fg + 1);
1341             break;
1342         case 'F':
1343             *fg = (NCURSES_COLOR_T) (*fg - 1);
1344             break;
1345         case 'b':
1346             *bg = (NCURSES_COLOR_T) (*bg + 1);
1347             break;
1348         case 'B':
1349             *bg = (NCURSES_COLOR_T) (*bg - 1);
1350             break;
1351         case 't':
1352             *tx = (NCURSES_COLOR_T) (*tx + 1);
1353             break;
1354         case 'T':
1355             *tx = (NCURSES_COLOR_T) (*tx - 1);
1356             break;
1357         default:
1358             beep();
1359             error = TRUE;
1360             break;
1361         }
1362         if (*fg >= COLORS)
1363             *fg = (NCURSES_COLOR_T) MinColors;
1364         if (*fg < MinColors)
1365             *fg = (NCURSES_COLOR_T) (COLORS - 1);
1366         if (*bg >= COLORS)
1367             *bg = (NCURSES_COLOR_T) MinColors;
1368         if (*bg < MinColors)
1369             *bg = (NCURSES_COLOR_T) (COLORS - 1);
1370         if (*tx >= COLORS)
1371             *tx = -1;
1372         if (*tx < -1)
1373             *tx = (NCURSES_COLOR_T) (COLORS - 1);
1374     } else {
1375         beep();
1376         error = TRUE;
1377     }
1378     return error;
1379 }
1380
1381 static void
1382 adjust_attr_string(int adjust)
1383 {
1384     char save = attr_test_string[0];
1385     int first = ((int) UChar(save)) + adjust;
1386     int j, k;
1387
1388     if (first >= ATTRSTRING_1ST) {
1389         for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1390             if (k > ATTRSTRING_END)
1391                 break;
1392             attr_test_string[j] = (char) k;
1393             if (((k + 1 - first) % 5) == 0) {
1394                 if (++j >= MAX_ATTRSTRING)
1395                     break;
1396                 attr_test_string[j] = ' ';
1397             }
1398         }
1399         if ((LEN_ATTRSTRING - j) > 5) {
1400             attr_test_string[0] = save;
1401             adjust_attr_string(adjust - 1);
1402         } else {
1403             while (j < MAX_ATTRSTRING)
1404                 attr_test_string[j++] = ' ';
1405             attr_test_string[j] = '\0';
1406         }
1407     }
1408 }
1409
1410 /*
1411  * Prefer the right-end of the string for starting, since that maps to the
1412  * VT100 line-drawing.
1413  */
1414 static int
1415 default_attr_string(void)
1416 {
1417     int result = (ATTRSTRING_END - LEN_ATTRSTRING);
1418     result += (LEN_ATTRSTRING / 5);
1419     if (result < ATTRSTRING_1ST)
1420         result = ATTRSTRING_1ST;
1421     return result;
1422 }
1423
1424 static void
1425 init_attr_string(void)
1426 {
1427     attr_test_string[0] = (char) default_attr_string();
1428     adjust_attr_string(0);
1429 }
1430
1431 static int
1432 show_attr(WINDOW *win, int row, int skip, bool arrow, chtype attr, const char *name)
1433 {
1434     int ncv = get_ncv();
1435     chtype test = attr & (chtype) (~(A_ALTCHARSET | A_CHARTEXT));
1436
1437     if (arrow)
1438         MvPrintw(row, COLS_PRE_ATTRS - 3, "-->");
1439     MvPrintw(row, COLS_PRE_ATTRS, "%s mode:", name);
1440     MvPrintw(row, COL_ATTRSTRING - 1, "|");
1441     if (skip)
1442         printw("%*s", skip, " ");
1443     /*
1444      * Just for testing, write text using the alternate character set one
1445      * character at a time (to pass its rendition directly), and use the
1446      * string operation for the other attributes.
1447      */
1448     wmove(win, 0, 0);
1449     werase(win);
1450     if (attr & A_ALTCHARSET) {
1451         const char *s;
1452         chtype ch;
1453
1454         for (s = attr_test_string; *s != '\0'; ++s) {
1455             ch = UChar(*s);
1456             (void) waddch(win, ch | attr);
1457         }
1458     } else {
1459         (void) wattrset(win, AttrArg(attr, 0));
1460         (void) waddstr(win, attr_test_string);
1461         (void) wattroff(win, (int) attr);
1462     }
1463     if (skip)
1464         printw("%*s", skip, " ");
1465     MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1466     if (test != A_NORMAL) {
1467         if (!(termattrs() & test)) {
1468             printw(" (N/A)");
1469         } else {
1470             if (ncv > 0 && stdscr && (getbkgd(stdscr) & A_COLOR)) {
1471                 static const chtype table[] =
1472                 {
1473                     A_STANDOUT,
1474                     A_UNDERLINE,
1475                     A_REVERSE,
1476                     A_BLINK,
1477                     A_DIM,
1478                     A_BOLD,
1479 #ifdef A_INVIS
1480                     A_INVIS,
1481 #endif
1482 #ifdef A_ITALIC
1483                     A_ITALIC,
1484 #endif
1485                     A_PROTECT,
1486                     A_ALTCHARSET
1487                 };
1488                 unsigned n;
1489                 bool found = FALSE;
1490                 for (n = 0; n < SIZEOF(table); n++) {
1491                     if ((table[n] & attr) != 0
1492                         && ((1 << n) & ncv) != 0) {
1493                         found = TRUE;
1494                         break;
1495                     }
1496                 }
1497                 if (found)
1498                     printw(" (NCV)");
1499             }
1500             if ((termattrs() & test) != test) {
1501                 printw(" (Part)");
1502             }
1503         }
1504     }
1505     return row + 2;
1506 }
1507
1508 typedef struct {
1509     chtype attr;
1510     NCURSES_CONST char *name;
1511 } ATTR_TBL;
1512 /* *INDENT-OFF* */
1513 static const ATTR_TBL attrs_to_test[] = {
1514     { A_STANDOUT,       "STANDOUT" },
1515     { A_REVERSE,        "REVERSE" },
1516     { A_BOLD,           "BOLD" },
1517     { A_UNDERLINE,      "UNDERLINE" },
1518     { A_DIM,            "DIM" },
1519     { A_BLINK,          "BLINK" },
1520     { A_PROTECT,        "PROTECT" },
1521 #ifdef A_INVIS
1522     { A_INVIS,          "INVISIBLE" },
1523 #endif
1524 #ifdef A_ITALIC
1525     { A_ITALIC,         "ITALIC" },
1526 #endif
1527     { A_NORMAL,         "NORMAL" },
1528 };
1529 /* *INDENT-ON* */
1530
1531 static unsigned
1532 init_attr_list(ATTR_TBL * target, attr_t attrs)
1533 {
1534     unsigned result = 0;
1535     size_t n;
1536
1537     for (n = 0; n < SIZEOF(attrs_to_test); ++n) {
1538         attr_t test = attrs_to_test[n].attr;
1539         if (test == A_NORMAL || (test & attrs) != 0) {
1540             target[result++] = attrs_to_test[n];
1541         }
1542     }
1543     return result;
1544 }
1545
1546 #if USE_WIDEC_SUPPORT
1547 typedef struct {
1548     attr_t attr;
1549     NCURSES_CONST char *name;
1550 } W_ATTR_TBL;
1551 /* *INDENT-OFF* */
1552 static const W_ATTR_TBL w_attrs_to_test[] = {
1553     { WA_STANDOUT,      "STANDOUT" },
1554     { WA_REVERSE,       "REVERSE" },
1555     { WA_BOLD,          "BOLD" },
1556     { WA_UNDERLINE,     "UNDERLINE" },
1557     { WA_DIM,           "DIM" },
1558     { WA_BLINK,         "BLINK" },
1559     { WA_PROTECT,       "PROTECT" },
1560 #ifdef WA_INVIS
1561     { WA_INVIS,         "INVISIBLE" },
1562 #endif
1563 #ifdef WA_ITALIC
1564     { WA_ITALIC,        "ITALIC" },
1565 #endif
1566     { WA_NORMAL,        "NORMAL" },
1567 };
1568 /* *INDENT-ON* */
1569
1570 static unsigned
1571 init_w_attr_list(W_ATTR_TBL * target, attr_t attrs)
1572 {
1573     unsigned result = 0;
1574     size_t n;
1575
1576     for (n = 0; n < SIZEOF(w_attrs_to_test); ++n) {
1577         attr_t test = w_attrs_to_test[n].attr;
1578         if (test == WA_NORMAL || (test & attrs) != 0) {
1579             target[result++] = w_attrs_to_test[n];
1580         }
1581     }
1582     return result;
1583 }
1584 #endif
1585
1586 static bool
1587 attr_getc(int *skip,
1588           NCURSES_COLOR_T *fg,
1589           NCURSES_COLOR_T *bg,
1590           NCURSES_COLOR_T *tx,
1591           int *ac,
1592           unsigned *kc,
1593           unsigned limit)
1594 {
1595     bool result = TRUE;
1596     bool error = FALSE;
1597     WINDOW *helpwin;
1598
1599     do {
1600         int ch = Getchar();
1601
1602         error = FALSE;
1603         if (ch < 256 && isdigit(ch)) {
1604             *skip = (ch - '0');
1605         } else {
1606             switch (ch) {
1607             case CTRL('L'):
1608                 Repaint();
1609                 break;
1610             case HELP_KEY_1:
1611                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1612                     box(helpwin, 0, 0);
1613                     attr_legend(helpwin);
1614                     wGetchar(helpwin);
1615                     delwin(helpwin);
1616                 }
1617                 break;
1618             case 'a':
1619                 *ac = 0;
1620                 break;
1621             case 'A':
1622                 *ac = A_ALTCHARSET;
1623                 break;
1624             case 'v':
1625                 if (*kc == 0)
1626                     *kc = limit - 1;
1627                 else
1628                     *kc -= 1;
1629                 break;
1630             case 'V':
1631                 *kc += 1;
1632                 if (*kc >= limit)
1633                     *kc = 0;
1634                 break;
1635             case '<':
1636                 adjust_attr_string(-1);
1637                 break;
1638             case '>':
1639                 adjust_attr_string(1);
1640                 break;
1641             case case_QUIT:
1642                 result = FALSE;
1643                 break;
1644             default:
1645                 error = cycle_color_attr(ch, fg, bg, tx);
1646                 break;
1647             }
1648         }
1649     } while (error);
1650     return result;
1651 }
1652
1653 static int
1654 attr_test(bool recur GCC_UNUSED)
1655 /* test text attributes */
1656 {
1657     int n;
1658     int skip = get_xmc();
1659     NCURSES_COLOR_T fg = COLOR_BLACK;   /* color pair 0 is special */
1660     NCURSES_COLOR_T bg = COLOR_BLACK;
1661     NCURSES_COLOR_T tx = -1;
1662     int ac = 0;
1663     unsigned j, k;
1664     WINDOW *my_wins[SIZEOF(attrs_to_test)];
1665     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
1666     unsigned my_size = init_attr_list(my_list, termattrs());
1667
1668     if (my_size > 1) {
1669         for (j = 0; j < my_size; ++j) {
1670             my_wins[j] = subwin(stdscr,
1671                                 1, LEN_ATTRSTRING,
1672                                 2 + (int) (2 * j), COL_ATTRSTRING);
1673             scrollok(my_wins[j], FALSE);
1674         }
1675
1676         if (skip < 0)
1677             skip = 0;
1678
1679         n = skip;               /* make it easy */
1680         k = my_size - 1;
1681         init_attr_string();
1682
1683         do {
1684             int row = 2;
1685             chtype normal = A_NORMAL | BLANK;
1686             chtype extras = (chtype) ac;
1687
1688             if (UseColors) {
1689                 NCURSES_PAIRS_T pair = 0;
1690                 if ((fg != COLOR_BLACK) || (bg != COLOR_BLACK)) {
1691                     pair = 1;
1692                     if (init_pair(pair, fg, bg) == ERR) {
1693                         beep();
1694                     } else {
1695                         normal |= (chtype) COLOR_PAIR(pair);
1696                     }
1697                 }
1698                 if (tx >= 0) {
1699                     pair = 2;
1700                     if (init_pair(pair, tx, bg) == ERR) {
1701                         beep();
1702                     } else {
1703                         extras |= (chtype) COLOR_PAIR(pair);
1704                         normal &= ~A_COLOR;
1705                     }
1706                 }
1707             }
1708             bkgd(normal);
1709             bkgdset(normal);
1710             erase();
1711
1712             box(stdscr, 0, 0);
1713             MvAddStr(0, 20, "Character attribute test display");
1714
1715             for (j = 0; j < my_size; ++j) {
1716                 bool arrow = (j == k);
1717                 row = show_attr(my_wins[j], row, n, arrow,
1718                                 normal |
1719                                 extras |
1720                                 my_list[j].attr |
1721                                 my_list[k].attr,
1722                                 my_list[j].name);
1723             }
1724
1725             MvPrintw(row, COLS_PRE_ATTRS,
1726                      "This terminal does %shave the magic-cookie glitch",
1727                      get_xmc() > -1 ? "" : "not ");
1728             MvPrintw(row + 1, COLS_PRE_ATTRS, "Enter '?' for help.");
1729             show_color_attr(fg, bg, tx);
1730             printw("  ACS (%d)", ac != 0);
1731
1732             refresh();
1733         } while (attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
1734
1735         bkgdset(A_NORMAL | BLANK);
1736         erase();
1737         endwin();
1738         return OK;
1739     } else {
1740         Cannot("does not support video attributes.");
1741         return ERR;
1742     }
1743 }
1744
1745 #if USE_WIDEC_SUPPORT
1746 static bool use_fullwidth;
1747 static wchar_t wide_attr_test_string[MAX_ATTRSTRING + 1];
1748
1749 #define FULL_LO 0xff00
1750 #define FULL_HI 0xff5e
1751 #define HALF_LO 0x20
1752
1753 #define isFullWidth(ch)   ((int)(ch) >= FULL_LO && (int)(ch) <= FULL_HI)
1754 #define ToNormalWidth(ch) (wchar_t) (((int)(ch) - FULL_LO) + HALF_LO)
1755 #define ToFullWidth(ch)   (wchar_t) (((int)(ch) - HALF_LO) + FULL_LO)
1756
1757 /*
1758  * Returns an ASCII code in [32..126]
1759  */
1760 static wchar_t
1761 normal_wchar(int ch)
1762 {
1763     wchar_t result = (wchar_t) ch;
1764     if (isFullWidth(ch))
1765         result = ToNormalWidth(ch);
1766     return result;
1767 }
1768
1769 /*
1770  * Returns either an ASCII code in in [32..126] or full-width in
1771  * [0xff00..0xff5e], according to use_fullwidth setting.
1772  */
1773 static wchar_t
1774 target_wchar(int ch)
1775 {
1776     wchar_t result = (wchar_t) ch;
1777     if (use_fullwidth) {
1778         if (!isFullWidth(ch))
1779             result = ToFullWidth(ch);
1780     } else {
1781         if (isFullWidth(ch))
1782             result = ToNormalWidth(ch);
1783     }
1784     return result;
1785 }
1786
1787 static void
1788 wide_adjust_attr_string(int adjust)
1789 {
1790     wchar_t save = wide_attr_test_string[0];
1791     int first = ((int) normal_wchar(save)) + adjust;
1792     int j, k;
1793
1794     if (first >= ATTRSTRING_1ST) {
1795         for (j = 0, k = first; j < MAX_ATTRSTRING; ++j, ++k) {
1796             if (k > ATTRSTRING_END)
1797                 break;
1798             wide_attr_test_string[j] = target_wchar(k);
1799             if (((k + 1 - first) % 5) == 0) {
1800                 if (++j >= MAX_ATTRSTRING)
1801                     break;
1802                 wide_attr_test_string[j] = ' ';
1803             }
1804         }
1805         if ((LEN_ATTRSTRING - j) > 5) {
1806             wide_attr_test_string[0] = save;
1807             wide_adjust_attr_string(adjust - 1);
1808         } else {
1809             while (j < MAX_ATTRSTRING)
1810                 wide_attr_test_string[j++] = ' ';
1811             wide_attr_test_string[j] = '\0';
1812         }
1813     }
1814 }
1815
1816 static void
1817 wide_init_attr_string(void)
1818 {
1819     use_fullwidth = FALSE;
1820     wide_attr_test_string[0] = (wchar_t) default_attr_string();
1821     wide_adjust_attr_string(0);
1822 }
1823
1824 static void
1825 set_wide_background(NCURSES_PAIRS_T pair)
1826 {
1827     cchar_t normal;
1828     wchar_t blank[2];
1829
1830     blank[0] = ' ';
1831     blank[1] = 0;
1832     setcchar(&normal, blank, A_NORMAL, pair, 0);
1833     bkgrnd(&normal);
1834     bkgrndset(&normal);
1835 }
1836
1837 static attr_t
1838 get_wide_background(void)
1839 {
1840     attr_t result = WA_NORMAL;
1841     attr_t attr;
1842     cchar_t ch;
1843     NCURSES_PAIRS_T pair;
1844     wchar_t wch[CCHARW_MAX];
1845
1846     memset(&ch, 0, sizeof(ch));
1847     if (getbkgrnd(&ch) != ERR) {
1848         if (getcchar(&ch, wch, &attr, &pair, 0) != ERR) {
1849             result = attr;
1850         }
1851     }
1852     return result;
1853 }
1854
1855 static int
1856 wide_show_attr(WINDOW *win,
1857                int row,
1858                int skip,
1859                bool arrow,
1860                attr_t attr,
1861                NCURSES_PAIRS_T pair,
1862                const char *name)
1863 {
1864     int ncv = get_ncv();
1865     attr_t test = attr & ~WA_ALTCHARSET;
1866
1867     if (arrow)
1868         MvPrintw(row, COLS_PRE_ATTRS - 3, "-->");
1869     MvPrintw(row, COLS_PRE_ATTRS, "%s mode:", name);
1870     MvPrintw(row, COL_ATTRSTRING - 1, "|");
1871     if (skip)
1872         printw("%*s", skip, " ");
1873
1874     /*
1875      * Just for testing, write text using the alternate character set one
1876      * character at a time (to pass its rendition directly), and use the
1877      * string operation for the other attributes.
1878      */
1879     wmove(win, 0, 0);
1880     werase(win);
1881     if (attr & WA_ALTCHARSET) {
1882         const wchar_t *s;
1883         cchar_t ch;
1884
1885         for (s = wide_attr_test_string; *s != L'\0'; ++s) {
1886             wchar_t fill[2];
1887             fill[0] = *s;
1888             fill[1] = L'\0';
1889             setcchar(&ch, fill, attr, pair, 0);
1890             (void) wadd_wch(win, &ch);
1891         }
1892     } else {
1893         attr_t old_attr = 0;
1894         NCURSES_PAIRS_T old_pair = 0;
1895
1896         (void) (wattr_get) (win, &old_attr, &old_pair, 0);
1897         (void) wattr_set(win, attr, pair, 0);
1898         (void) waddwstr(win, wide_attr_test_string);
1899         (void) wattr_set(win, old_attr, old_pair, 0);
1900     }
1901     if (skip)
1902         printw("%*s", skip, " ");
1903     MvPrintw(row, COL_ATTRSTRING + LEN_ATTRSTRING, "|");
1904     if (test != A_NORMAL) {
1905         if (!(term_attrs() & test)) {
1906             printw(" (N/A)");
1907         } else {
1908             if (ncv > 0 && (get_wide_background() & A_COLOR)) {
1909                 static const attr_t table[] =
1910                 {
1911                     WA_STANDOUT,
1912                     WA_UNDERLINE,
1913                     WA_REVERSE,
1914                     WA_BLINK,
1915                     WA_DIM,
1916                     WA_BOLD,
1917                     WA_INVIS,
1918                     WA_PROTECT,
1919                     WA_ALTCHARSET
1920                 };
1921                 unsigned n;
1922                 bool found = FALSE;
1923                 for (n = 0; n < SIZEOF(table); n++) {
1924                     if ((table[n] & attr) != 0
1925                         && ((1 << n) & ncv) != 0) {
1926                         found = TRUE;
1927                         break;
1928                     }
1929                 }
1930                 if (found)
1931                     printw(" (NCV)");
1932             }
1933             if ((term_attrs() & test) != test) {
1934                 printw(" (Part)");
1935             }
1936         }
1937     }
1938     return row + 2;
1939 }
1940
1941 static bool
1942 wide_attr_getc(int *skip,
1943                NCURSES_COLOR_T *fg, NCURSES_COLOR_T *bg,
1944                NCURSES_COLOR_T *tx, int *ac,
1945                unsigned *kc, unsigned limit)
1946 {
1947     bool result = TRUE;
1948     bool error = FALSE;
1949     WINDOW *helpwin;
1950
1951     do {
1952         int ch = Getchar();
1953
1954         error = FALSE;
1955         if (ch < 256 && isdigit(ch)) {
1956             *skip = (ch - '0');
1957         } else {
1958             switch (ch) {
1959             case CTRL('L'):
1960                 Repaint();
1961                 break;
1962             case HELP_KEY_1:
1963                 if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
1964                     box_set(helpwin, 0, 0);
1965                     attr_legend(helpwin);
1966                     wGetchar(helpwin);
1967                     delwin(helpwin);
1968                 }
1969                 break;
1970             case 'a':
1971                 *ac = 0;
1972                 break;
1973             case 'A':
1974                 *ac = A_ALTCHARSET;
1975                 break;
1976             case 'v':
1977                 if (*kc == 0)
1978                     *kc = limit - 1;
1979                 else
1980                     *kc -= 1;
1981                 break;
1982             case 'V':
1983                 *kc += 1;
1984                 if (*kc >= limit)
1985                     *kc = 0;
1986                 break;
1987             case 'w':
1988                 use_fullwidth = FALSE;
1989                 wide_adjust_attr_string(0);
1990                 break;
1991             case 'W':
1992                 use_fullwidth = TRUE;
1993                 wide_adjust_attr_string(0);
1994                 break;
1995             case '<':
1996                 wide_adjust_attr_string(-1);
1997                 break;
1998             case '>':
1999                 wide_adjust_attr_string(1);
2000                 break;
2001             case case_QUIT:
2002                 result = FALSE;
2003                 break;
2004             default:
2005                 error = cycle_color_attr(ch, fg, bg, tx);
2006                 break;
2007             }
2008         }
2009     } while (error);
2010     return result;
2011 }
2012
2013 static int
2014 x_attr_test(bool recur GCC_UNUSED)
2015 /* test text attributes using wide-character calls */
2016 {
2017     int n;
2018     int skip = get_xmc();
2019     NCURSES_COLOR_T fg = COLOR_BLACK;   /* color pair 0 is special */
2020     NCURSES_COLOR_T bg = COLOR_BLACK;
2021     NCURSES_COLOR_T tx = -1;
2022     int ac = 0;
2023     unsigned j, k;
2024     W_ATTR_TBL my_list[SIZEOF(w_attrs_to_test)];
2025     WINDOW *my_wins[SIZEOF(w_attrs_to_test)];
2026     unsigned my_size = init_w_attr_list(my_list, term_attrs());
2027
2028     if (my_size > 1) {
2029         for (j = 0; j < my_size; ++j) {
2030             my_wins[j] = subwin(stdscr,
2031                                 1, LEN_ATTRSTRING,
2032                                 2 + (int) (2 * j), COL_ATTRSTRING);
2033             scrollok(my_wins[j], FALSE);
2034         }
2035
2036         if (skip < 0)
2037             skip = 0;
2038
2039         n = skip;               /* make it easy */
2040         k = my_size - 1;
2041         wide_init_attr_string();
2042
2043         do {
2044             int row = 2;
2045             NCURSES_PAIRS_T pair = 0;
2046             NCURSES_PAIRS_T extras = 0;
2047
2048             if (UseColors) {
2049                 pair = (NCURSES_PAIRS_T) (fg != COLOR_BLACK || bg != COLOR_BLACK);
2050                 if (pair != 0) {
2051                     pair = 1;
2052                     if (init_pair(pair, fg, bg) == ERR) {
2053                         beep();
2054                     }
2055                 }
2056                 extras = pair;
2057                 if (tx >= 0) {
2058                     extras = 2;
2059                     if (init_pair(extras, tx, bg) == ERR) {
2060                         beep();
2061                     }
2062                 }
2063             }
2064             set_wide_background(pair);
2065             erase();
2066
2067             box_set(stdscr, 0, 0);
2068             MvAddStr(0, 20, "Character attribute test display");
2069
2070             for (j = 0; j < my_size; ++j) {
2071                 row = wide_show_attr(my_wins[j], row, n, (j == k),
2072                                      ((attr_t) ac |
2073                                       my_list[j].attr |
2074                                       my_list[k].attr),
2075                                      extras,
2076                                      my_list[j].name);
2077             }
2078
2079             MvPrintw(row, COLS_PRE_ATTRS,
2080                      "This terminal does %shave the magic-cookie glitch",
2081                      get_xmc() > -1 ? "" : "not ");
2082             MvPrintw(row + 1, COLS_PRE_ATTRS, "Enter '?' for help.");
2083             show_color_attr(fg, bg, tx);
2084             printw("  ACS (%d)", ac != 0);
2085
2086             refresh();
2087         } while (wide_attr_getc(&n, &fg, &bg, &tx, &ac, &k, my_size));
2088
2089         set_wide_background(0);
2090         erase();
2091         endwin();
2092         return OK;
2093     } else {
2094         Cannot("does not support extended video attributes.");
2095         return ERR;
2096     }
2097 }
2098 #endif
2099
2100 /****************************************************************************
2101  *
2102  * Color support tests
2103  *
2104  ****************************************************************************/
2105
2106 static NCURSES_CONST char *the_color_names[] =
2107 {
2108     "black",
2109     "red",
2110     "green",
2111     "yellow",
2112     "blue",
2113     "magenta",
2114     "cyan",
2115     "white",
2116     "BLACK",
2117     "RED",
2118     "GREEN",
2119     "YELLOW",
2120     "BLUE",
2121     "MAGENTA",
2122     "CYAN",
2123     "WHITE"
2124 };
2125
2126 static void
2127 show_color_name(int y, int x, int color, bool wide)
2128 {
2129     if (move(y, x) != ERR) {
2130         char temp[80];
2131         int width = 8;
2132
2133         if (wide) {
2134             _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2135                         "%02d", color);
2136             width = 4;
2137         } else if (color >= 8) {
2138             _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2139                         "[%02d]", color);
2140         } else if (color < 0) {
2141             _nc_STRCPY(temp, "default", sizeof(temp));
2142         } else {
2143             _nc_SPRINTF(temp, _nc_SLIMIT(sizeof(temp))
2144                         "%.*s", 16, the_color_names[color]);
2145         }
2146         printw("%-*.*s", width, width, temp);
2147     }
2148 }
2149
2150 static void
2151 color_legend(WINDOW *helpwin, bool wide)
2152 {
2153     int row = 1;
2154     int col = 1;
2155
2156     MvWPrintw(helpwin, row++, col,
2157               "ESC to exit.");
2158     ++row;
2159     MvWPrintw(helpwin, row++, col,
2160               "Use up/down arrow to scroll through the display if it is");
2161     MvWPrintw(helpwin, row++, col,
2162               "longer than one screen. Control/N and Control/P can be used");
2163     MvWPrintw(helpwin, row++, col,
2164               "in place of up/down arrow.  Use pageup/pagedown to scroll a");
2165     MvWPrintw(helpwin, row++, col,
2166               "full screen; control/B and control/F can be used here.");
2167     ++row;
2168     MvWPrintw(helpwin, row++, col,
2169               "Toggles:");
2170     MvWPrintw(helpwin, row++, col,
2171               "  a/A     toggle altcharset off/on");
2172     MvWPrintw(helpwin, row++, col,
2173               "  b/B     toggle bold off/on");
2174     if (has_colors()) {
2175         MvWPrintw(helpwin, row++, col,
2176                   "  c/C     cycle used-colors through 8,16,...,COLORS");
2177     }
2178     MvWPrintw(helpwin, row++, col,
2179               "  n/N     toggle text/number on/off");
2180     MvWPrintw(helpwin, row++, col,
2181               "  r/R     toggle reverse on/off");
2182     MvWPrintw(helpwin, row++, col,
2183               "  w/W     toggle width between 8/16 colors");
2184 #if USE_WIDEC_SUPPORT
2185     if (wide) {
2186         MvWPrintw(helpwin, row++, col,
2187                   "Wide characters:");
2188         MvWPrintw(helpwin, row, col,
2189                   "  x/X     toggle text between ASCII and wide-character");
2190     }
2191 #else
2192     (void) wide;
2193 #endif
2194 }
2195
2196 #define set_color_test(name, value) if (name != value) { name = value; base_row = 0; }
2197
2198 static int
2199 color_cycle(int current, int step)
2200 {
2201     int result = current;
2202     if (step < 0) {
2203         if (current <= 8) {
2204             result = COLORS;
2205         } else {
2206             result = 8;
2207             if ((result * 2) > COLORS) {
2208                 result = COLORS;
2209             } else {
2210                 while ((result * 2) < current) {
2211                     result *= 2;
2212                 }
2213             }
2214         }
2215     } else {
2216         if (current >= COLORS) {
2217             result = 8;
2218         } else {
2219             result *= 2;
2220         }
2221         if (result > COLORS)
2222             result = COLORS;
2223     }
2224     return result;
2225 }
2226
2227 /* generate a color test pattern */
2228 static int
2229 color_test(bool recur GCC_UNUSED)
2230 {
2231     NCURSES_PAIRS_T i;
2232     int top = 0, width;
2233     int base_row = 0;
2234     int grid_top = top + 3;
2235     int page_size = (LINES - grid_top);
2236     int pairs_max;
2237     int colors_max = COLORS;
2238     int col_limit;
2239     int row_limit;
2240     int per_row;
2241     char *numbered = 0;
2242     const char *hello;
2243     bool done = FALSE;
2244     bool opt_acsc = FALSE;
2245     bool opt_bold = FALSE;
2246     bool opt_revs = FALSE;
2247     bool opt_nums = FALSE;
2248     bool opt_wide = FALSE;
2249     WINDOW *helpwin;
2250
2251     if (!UseColors) {
2252         Cannot("does not support color.");
2253         return ERR;
2254     }
2255
2256     numbered = (char *) calloc((size_t) (COLS + 1), sizeof(char));
2257     done = ((COLS < 16) || (numbered == 0));
2258
2259     /*
2260      * Because the number of colors is usually a power of two, we also use
2261      * a power of two for the number of colors shown per line (to be tidy).
2262      */
2263     for (col_limit = 1; col_limit * 2 < COLS; col_limit *= 2) ;
2264
2265     while (!done) {
2266         int shown = 0;
2267         double colors_max2 = (double) colors_max * (double) colors_max;
2268
2269         pairs_max = PAIR_NUMBER(A_COLOR) + 1;
2270         if (colors_max2 <= COLOR_PAIRS) {
2271             int limit = (colors_max - MinColors) * (colors_max - MinColors);
2272             if (pairs_max > limit)
2273                 pairs_max = limit;
2274         }
2275         if (pairs_max < COLOR_PAIRS)
2276             pairs_max = COLOR_PAIRS;
2277
2278         /* this assumes an 80-column line */
2279         if (opt_wide) {
2280             width = 4;
2281             hello = "Test";
2282             per_row = (col_limit / ((colors_max > 8) ? 4 : 8));
2283         } else {
2284             width = 8;
2285             hello = "Hello";
2286             per_row = (col_limit / 8);
2287         }
2288         per_row -= MinColors;
2289
2290         row_limit = (pairs_max + per_row - 1) / per_row;
2291
2292         move(0, 0);
2293         (void) printw("There are %d color pairs and %d colors",
2294                       pairs_max, COLORS);
2295         if (colors_max != COLORS)
2296             (void) printw(" (using %d colors)", colors_max);
2297         if (MinColors)
2298             (void) addstr(" besides 'default'");
2299
2300         clrtobot();
2301         MvPrintw(top + 1, 0,
2302                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2303                  row_limit,
2304                  per_row,
2305                  opt_bold ? "on" : "off");
2306
2307         /* show color names/numbers across the top */
2308         for (i = 0; i < per_row; i++)
2309             show_color_name(top + 2, (i + 1) * width, i + MinColors, opt_wide);
2310
2311         /* show a grid of colors, with color names/ numbers on the left */
2312         for (i = (NCURSES_PAIRS_T) (base_row * per_row); i < pairs_max; i++) {
2313             int row = grid_top + (i / per_row) - base_row;
2314             int col = (i % per_row + 1) * width;
2315             NCURSES_PAIRS_T pair = i;
2316
2317             if ((i / per_row) > row_limit)
2318                 break;
2319
2320 #define InxToFG(i) (NCURSES_COLOR_T) ((i % (colors_max - MinColors)) + MinColors)
2321 #define InxToBG(i) (NCURSES_COLOR_T) ((i / (colors_max - MinColors)) + MinColors)
2322             if (row >= 0 && move(row, col) != ERR) {
2323                 NCURSES_COLOR_T fg = InxToFG(i);
2324                 NCURSES_COLOR_T bg = InxToBG(i);
2325
2326                 init_pair(pair, fg, bg);
2327                 attron(COLOR_PAIR(pair));
2328                 if (opt_acsc)
2329                     attron(A_ALTCHARSET);
2330                 if (opt_bold)
2331                     attron(A_BOLD);
2332                 if (opt_revs)
2333                     attron(A_REVERSE);
2334
2335                 if (opt_nums) {
2336                     _nc_SPRINTF(numbered, _nc_SLIMIT((size_t) (COLS + 1))
2337                                 "{%02X}", (int) i);
2338                     hello = numbered;
2339                 }
2340                 printw("%-*.*s", width, width, hello);
2341                 (void) attrset(A_NORMAL);
2342
2343                 if ((i % per_row) == 0 && InxToFG(i) == MinColors) {
2344                     show_color_name(row, 0, InxToBG(i), opt_wide);
2345                 }
2346                 ++shown;
2347             } else if (shown) {
2348                 break;
2349             }
2350         }
2351
2352         switch (wGetchar(stdscr)) {
2353         case 'a':
2354             opt_acsc = FALSE;
2355             break;
2356         case 'A':
2357             opt_acsc = TRUE;
2358             break;
2359         case 'b':
2360             opt_bold = FALSE;
2361             break;
2362         case 'B':
2363             opt_bold = TRUE;
2364             break;
2365         case 'c':
2366             colors_max = color_cycle(colors_max, -1);
2367             break;
2368         case 'C':
2369             colors_max = color_cycle(colors_max, 1);
2370             break;
2371         case 'n':
2372             opt_nums = FALSE;
2373             break;
2374         case 'N':
2375             opt_nums = TRUE;
2376             break;
2377         case 'r':
2378             opt_revs = FALSE;
2379             break;
2380         case 'R':
2381             opt_revs = TRUE;
2382             break;
2383         case case_QUIT:
2384             done = TRUE;
2385             continue;
2386         case 'w':
2387             set_color_test(opt_wide, FALSE);
2388             break;
2389         case 'W':
2390             set_color_test(opt_wide, TRUE);
2391             break;
2392         case CTRL('p'):
2393         case KEY_UP:
2394             if (base_row <= 0) {
2395                 beep();
2396             } else {
2397                 base_row -= 1;
2398             }
2399             break;
2400         case CTRL('n'):
2401         case KEY_DOWN:
2402             if (base_row + page_size >= row_limit) {
2403                 beep();
2404             } else {
2405                 base_row += 1;
2406             }
2407             break;
2408         case CTRL('b'):
2409         case KEY_PREVIOUS:
2410         case KEY_PPAGE:
2411             if (base_row <= 0) {
2412                 beep();
2413             } else {
2414                 base_row -= (page_size - 1);
2415                 if (base_row < 0)
2416                     base_row = 0;
2417             }
2418             break;
2419         case CTRL('f'):
2420         case KEY_NEXT:
2421         case KEY_NPAGE:
2422             if (base_row + page_size >= row_limit) {
2423                 beep();
2424             } else {
2425                 base_row += page_size - 1;
2426                 if (base_row + page_size >= row_limit) {
2427                     base_row = row_limit - page_size - 1;
2428                 }
2429             }
2430             break;
2431         case HELP_KEY_1:
2432             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2433                 box(helpwin, 0, 0);
2434                 color_legend(helpwin, FALSE);
2435                 wGetchar(helpwin);
2436                 delwin(helpwin);
2437             }
2438             break;
2439         default:
2440             beep();
2441             continue;
2442         }
2443     }
2444
2445     erase();
2446     endwin();
2447
2448     free(numbered);
2449     return OK;
2450 }
2451
2452 #if USE_WIDEC_SUPPORT
2453 /* generate a color test pattern */
2454 static int
2455 x_color_test(bool recur GCC_UNUSED)
2456 {
2457     int i;
2458     int top = 0, width;
2459     int base_row = 0;
2460     int grid_top = top + 3;
2461     int page_size = (LINES - grid_top);
2462     int pairs_max = (unsigned short) (-1);
2463     int colors_max = COLORS;
2464     int col_limit;
2465     int row_limit;
2466     int per_row;
2467     char *numbered = 0;
2468     const char *hello;
2469     bool done = FALSE;
2470     bool opt_acsc = FALSE;
2471     bool opt_bold = FALSE;
2472     bool opt_revs = FALSE;
2473     bool opt_wide = FALSE;
2474     bool opt_nums = FALSE;
2475     bool opt_xchr = FALSE;
2476     wchar_t *buffer = 0;
2477     WINDOW *helpwin;
2478
2479     if (!UseColors) {
2480         Cannot("does not support color.");
2481         return ERR;
2482     }
2483     numbered = (char *) calloc((size_t) (COLS + 1), sizeof(char));
2484     buffer = (wchar_t *) calloc((size_t) (COLS + 1), sizeof(wchar_t));
2485     done = ((COLS < 16) || (numbered == 0) || (buffer == 0));
2486
2487     /*
2488      * Because the number of colors is usually a power of two, we also use
2489      * a power of two for the number of colors shown per line (to be tidy).
2490      */
2491     for (col_limit = 1; col_limit * 2 < COLS; col_limit *= 2) ;
2492
2493     while (!done) {
2494         int shown = 0;
2495         double colors_max2 = (double) colors_max * (double) colors_max;
2496
2497         pairs_max = ((unsigned) (-1)) / 2;
2498         if (colors_max2 <= COLOR_PAIRS) {
2499             int limit = (colors_max - MinColors) * (colors_max - MinColors);
2500             if (pairs_max > limit)
2501                 pairs_max = limit;
2502         }
2503         if (pairs_max > COLOR_PAIRS)
2504             pairs_max = COLOR_PAIRS;
2505
2506         if (opt_wide) {
2507             width = 4;
2508             hello = "Test";
2509             per_row = (col_limit / ((colors_max > 8) ? 4 : 8));
2510         } else {
2511             width = 8;
2512             hello = "Hello";
2513             per_row = (col_limit / 8);
2514         }
2515         per_row -= MinColors;
2516
2517         if (opt_xchr) {
2518             make_fullwidth_text(buffer, hello);
2519             width *= 2;
2520             per_row /= 2;
2521         } else {
2522             make_narrow_text(buffer, hello);
2523         }
2524
2525         row_limit = (pairs_max + per_row - 1) / per_row;
2526
2527         move(0, 0);
2528         (void) printw("There are %d color pairs and %d colors",
2529                       pairs_max, COLORS);
2530         if (colors_max != COLORS)
2531             (void) printw(" (using %d colors)", colors_max);
2532         if (MinColors)
2533             (void) addstr(" besides 'default'");
2534
2535         clrtobot();
2536         MvPrintw(top + 1, 0,
2537                  "%dx%d matrix of foreground/background colors, bold *%s*\n",
2538                  row_limit,
2539                  per_row,
2540                  opt_bold ? "on" : "off");
2541
2542         /* show color names/numbers across the top */
2543         for (i = 0; i < per_row; i++)
2544             show_color_name(top + 2, (i + 1) * width, i + MinColors, opt_wide);
2545
2546         /* show a grid of colors, with color names/ numbers on the left */
2547         for (i = (base_row * per_row); i < pairs_max; i++) {
2548             int row = grid_top + (i / per_row) - base_row;
2549             int col = (i % per_row + 1) * width;
2550             NCURSES_PAIRS_T pair = (NCURSES_PAIRS_T) i;
2551
2552             if ((i / per_row) > row_limit)
2553                 break;
2554
2555             if (row >= 0 && move(row, col) != ERR) {
2556                 init_pair(pair, InxToFG(i), InxToBG(i));
2557                 (void) color_set(pair, NULL);
2558                 if (opt_acsc)
2559                     attr_on(WA_ALTCHARSET, NULL);
2560                 if (opt_bold)
2561                     attr_on(WA_BOLD, NULL);
2562                 if (opt_revs)
2563                     attr_on(WA_REVERSE, NULL);
2564
2565                 if (opt_nums) {
2566                     _nc_SPRINTF(numbered,
2567                                 _nc_SLIMIT((size_t) (COLS + 1) * sizeof(wchar_t))
2568                                 "{%02X}", i);
2569                     if (opt_xchr) {
2570                         make_fullwidth_text(buffer, numbered);
2571                     } else {
2572                         make_narrow_text(buffer, numbered);
2573                     }
2574                 }
2575                 addnwstr(buffer, width);
2576                 (void) attr_set(A_NORMAL, 0, NULL);
2577
2578                 if ((i % per_row) == 0 && InxToFG(i) == MinColors) {
2579                     show_color_name(row, 0, InxToBG(i), opt_wide);
2580                 }
2581                 ++shown;
2582             } else if (shown) {
2583                 break;
2584             }
2585         }
2586
2587         switch (wGetchar(stdscr)) {
2588         case 'a':
2589             opt_acsc = FALSE;
2590             break;
2591         case 'A':
2592             opt_acsc = TRUE;
2593             break;
2594         case 'b':
2595             opt_bold = FALSE;
2596             break;
2597         case 'B':
2598             opt_bold = TRUE;
2599             break;
2600         case 'c':
2601             colors_max = color_cycle(colors_max, -1);
2602             break;
2603         case 'C':
2604             colors_max = color_cycle(colors_max, 1);
2605             break;
2606         case 'n':
2607             opt_nums = FALSE;
2608             break;
2609         case 'N':
2610             opt_nums = TRUE;
2611             break;
2612         case 'r':
2613             opt_revs = FALSE;
2614             break;
2615         case 'R':
2616             opt_revs = TRUE;
2617             break;
2618         case case_QUIT:
2619             done = TRUE;
2620             continue;
2621         case 'w':
2622             set_color_test(opt_wide, FALSE);
2623             break;
2624         case 'W':
2625             set_color_test(opt_wide, TRUE);
2626             break;
2627         case 'x':
2628             opt_xchr = FALSE;
2629             break;
2630         case 'X':
2631             opt_xchr = TRUE;
2632             break;
2633         case CTRL('p'):
2634         case KEY_UP:
2635             if (base_row <= 0) {
2636                 beep();
2637             } else {
2638                 base_row -= 1;
2639             }
2640             break;
2641         case CTRL('n'):
2642         case KEY_DOWN:
2643             if (base_row + page_size >= row_limit) {
2644                 beep();
2645             } else {
2646                 base_row += 1;
2647             }
2648             break;
2649         case CTRL('b'):
2650         case KEY_PREVIOUS:
2651         case KEY_PPAGE:
2652             if (base_row <= 0) {
2653                 beep();
2654             } else {
2655                 base_row -= (page_size - 1);
2656                 if (base_row < 0)
2657                     base_row = 0;
2658             }
2659             break;
2660         case CTRL('f'):
2661         case KEY_NEXT:
2662         case KEY_NPAGE:
2663             if (base_row + page_size >= row_limit) {
2664                 beep();
2665             } else {
2666                 base_row += page_size - 1;
2667                 if (base_row + page_size >= row_limit) {
2668                     base_row = row_limit - page_size - 1;
2669                 }
2670             }
2671             break;
2672         case HELP_KEY_1:
2673             if ((helpwin = newwin(LINES - 1, COLS - 2, 0, 0)) != 0) {
2674                 box(helpwin, 0, 0);
2675                 color_legend(helpwin, TRUE);
2676                 wGetchar(helpwin);
2677                 delwin(helpwin);
2678             }
2679             break;
2680         default:
2681             beep();
2682             continue;
2683         }
2684     }
2685
2686     erase();
2687     endwin();
2688
2689     free(numbered);
2690     free(buffer);
2691     return OK;
2692 }
2693 #endif /* USE_WIDEC_SUPPORT */
2694
2695 #if HAVE_COLOR_CONTENT
2696 static void
2697 change_color(NCURSES_PAIRS_T current, int field, int value, int usebase)
2698 {
2699     NCURSES_COLOR_T red, green, blue;
2700
2701     color_content(current, &red, &green, &blue);
2702
2703     switch (field) {
2704     case 0:
2705         red = (NCURSES_COLOR_T) (usebase ? (red + value) : value);
2706         break;
2707     case 1:
2708         green = (NCURSES_COLOR_T) (usebase ? (green + value) : value);
2709         break;
2710     case 2:
2711         blue = (NCURSES_COLOR_T) (usebase ? (blue + value) : value);
2712         break;
2713     }
2714
2715     if (init_color(current, red, green, blue) == ERR)
2716         beep();
2717 }
2718
2719 static void
2720 reset_all_colors(void)
2721 {
2722     NCURSES_PAIRS_T c;
2723
2724     for (c = 0; c < COLORS; ++c)
2725         init_color(c,
2726                    all_colors[c].red,
2727                    all_colors[c].green,
2728                    all_colors[c].blue);
2729 }
2730
2731 #define okCOLOR(n) ((n) >= 0 && (n) < MaxColors)
2732 #define okRGB(n)   ((n) >= 0 && (n) <= 1000)
2733 #define DecodeRGB(n) (NCURSES_COLOR_T) ((n * 1000) / 0xffff)
2734
2735 static void
2736 init_all_colors(bool xterm_colors, char *palette_file)
2737 {
2738     NCURSES_PAIRS_T cp;
2739     all_colors = typeMalloc(RGB_DATA, (unsigned) MaxColors);
2740     if (!all_colors)
2741         failed("all_colors");
2742     for (cp = 0; cp < MaxColors; ++cp) {
2743         color_content(cp,
2744                       &all_colors[cp].red,
2745                       &all_colors[cp].green,
2746                       &all_colors[cp].blue);
2747     }
2748     /* xterm and compatible terminals can read results of an OSC string
2749      * asking for the current color palette.
2750      */
2751     if (xterm_colors) {
2752         int n;
2753         int got;
2754         char result[BUFSIZ];
2755         int check_n;
2756         unsigned check_r, check_g, check_b;
2757
2758         raw();
2759         noecho();
2760         for (n = 0; n < MaxColors; ++n) {
2761             fprintf(stderr, "\033]4;%d;?\007", n);
2762             got = (int) read(0, result, sizeof(result) - 1);
2763             if (got < 0)
2764                 break;
2765             result[got] = '\0';
2766             if (sscanf(result, "\033]4;%d;rgb:%x/%x/%x\007",
2767                        &check_n,
2768                        &check_r,
2769                        &check_g,
2770                        &check_b) == 4 &&
2771                 check_n == n) {
2772                 all_colors[n].red = DecodeRGB(check_r);
2773                 all_colors[n].green = DecodeRGB(check_g);
2774                 all_colors[n].blue = DecodeRGB(check_b);
2775             } else {
2776                 break;
2777             }
2778         }
2779         reset_prog_mode();
2780     }
2781     if (palette_file != 0) {
2782         FILE *fp = fopen(palette_file, "r");
2783         if (fp != 0) {
2784             char buffer[BUFSIZ];
2785             int red, green, blue;
2786             int scale = 1000;
2787             int c;
2788             while (fgets(buffer, sizeof(buffer), fp) != 0) {
2789                 if (sscanf(buffer, "scale:%d", &c) == 1) {
2790                     scale = c;
2791                 } else if (sscanf(buffer, "%d:%d %d %d",
2792                                   &c,
2793                                   &red,
2794                                   &green,
2795                                   &blue) == 4
2796                            && okCOLOR(c)
2797                            && okRGB(red)
2798                            && okRGB(green)
2799                            && okRGB(blue)) {
2800 #define Scaled(n) (NCURSES_COLOR_T) (((n) * 1000) / scale)
2801                     all_colors[c].red = Scaled(red);
2802                     all_colors[c].green = Scaled(green);
2803                     all_colors[c].blue = Scaled(blue);
2804                 }
2805             }
2806             fclose(fp);
2807         }
2808     }
2809 }
2810
2811 #define scaled_rgb(n) ((255 * (n)) / 1000)
2812
2813 static int
2814 color_edit(bool recur GCC_UNUSED)
2815 /* display the color test pattern, without trying to edit colors */
2816 {
2817     int i;
2818     int current;
2819     int this_c, value, field;
2820     int last_c;
2821     int top_color;
2822     int page_size;
2823
2824     if (!UseColors) {
2825         Cannot("does not support color.");
2826         return ERR;
2827     } else if (!can_change_color()) {
2828         Cannot("has hardwired color values.");
2829         return ERR;
2830     }
2831
2832     reset_all_colors();
2833 #ifdef KEY_RESIZE
2834   retry:
2835 #endif
2836     current = 0;
2837     this_c = 0;
2838     value = 0;
2839     field = 0;
2840     top_color = 0;
2841     page_size = (LINES - 6);
2842     erase();
2843
2844     for (i = 0; i < MaxColors; i++)
2845         init_pair((NCURSES_PAIRS_T) i,
2846                   (NCURSES_COLOR_T) COLOR_WHITE,
2847                   (NCURSES_COLOR_T) i);
2848
2849     MvPrintw(LINES - 2, 0, "Number: %d", value);
2850
2851     do {
2852         NCURSES_COLOR_T red, green, blue;
2853
2854         attron(A_BOLD);
2855         MvAddStr(0, 20, "Color RGB Value Editing");
2856         attroff(A_BOLD);
2857
2858         for (i = (NCURSES_COLOR_T) top_color;
2859              (i - top_color < page_size)
2860              && (i < MaxColors); i++) {
2861             char numeric[80];
2862
2863             _nc_SPRINTF(numeric, _nc_SLIMIT(sizeof(numeric)) "[%d]", i);
2864             MvPrintw(2 + i - top_color, 0, "%c %-8s:",
2865                      (i == current ? '>' : ' '),
2866                      (i < (int) SIZEOF(the_color_names)
2867                       ? the_color_names[i] : numeric));
2868             (void) attrset(AttrArg(COLOR_PAIR(i), 0));
2869             addstr("        ");
2870             (void) attrset(A_NORMAL);
2871
2872             color_content((NCURSES_PAIRS_T) i, &red, &green, &blue);
2873             addstr("   R = ");
2874             if (current == i && field == 0)
2875                 attron(A_STANDOUT);
2876             printw("%04d", (int) red);
2877             if (current == i && field == 0)
2878                 (void) attrset(A_NORMAL);
2879             addstr(", G = ");
2880             if (current == i && field == 1)
2881                 attron(A_STANDOUT);
2882             printw("%04d", (int) green);
2883             if (current == i && field == 1)
2884                 (void) attrset(A_NORMAL);
2885             addstr(", B = ");
2886             if (current == i && field == 2)
2887                 attron(A_STANDOUT);
2888             printw("%04d", (int) blue);
2889             if (current == i && field == 2)
2890                 (void) attrset(A_NORMAL);
2891             (void) attrset(A_NORMAL);
2892             printw(" ( %3d %3d %3d )",
2893                    (int) scaled_rgb(red),
2894                    (int) scaled_rgb(green),
2895                    (int) scaled_rgb(blue));
2896         }
2897
2898         MvAddStr(LINES - 3, 0,
2899                  "Use up/down to select a color, left/right to change fields.");
2900         MvAddStr(LINES - 2, 0,
2901                  "Modify field by typing nnn=, nnn-, or nnn+.  ? for help.");
2902
2903         move(2 + current - top_color, 0);
2904
2905         last_c = this_c;
2906         this_c = Getchar();
2907         if (this_c < 256 && isdigit(this_c) && !isdigit(last_c))
2908             value = 0;
2909
2910         switch (this_c) {
2911 #ifdef KEY_RESIZE
2912         case KEY_RESIZE:
2913             move(0, 0);
2914             goto retry;
2915 #endif
2916         case '!':
2917             ShellOut(FALSE);
2918             /* FALLTHRU */
2919         case CTRL('r'):
2920             endwin();
2921             refresh();
2922             break;
2923         case CTRL('l'):
2924             refresh();
2925             break;
2926         case CTRL('b'):
2927         case KEY_PPAGE:
2928             if (current > 0)
2929                 current -= (page_size - 1);
2930             else
2931                 beep();
2932             break;
2933
2934         case CTRL('f'):
2935         case KEY_NPAGE:
2936             if (current < (MaxColors - 1))
2937                 current += (page_size - 1);
2938             else
2939                 beep();
2940             break;
2941
2942         case CTRL('p'):
2943         case KEY_UP:
2944             current = (current == 0 ? (MaxColors - 1) : current - 1);
2945             break;
2946
2947         case CTRL('n'):
2948         case KEY_DOWN:
2949             current = (current == (MaxColors - 1) ? 0 : current + 1);
2950             break;
2951
2952         case '\t':
2953         case KEY_RIGHT:
2954             field = (field == 2 ? 0 : field + 1);
2955             break;
2956
2957         case KEY_BTAB:
2958         case KEY_LEFT:
2959             field = (field == 0 ? 2 : field - 1);
2960             break;
2961
2962         case '0':
2963         case '1':
2964         case '2':
2965         case '3':
2966         case '4':
2967         case '5':
2968         case '6':
2969         case '7':
2970         case '8':
2971         case '9':
2972             value = value * 10 + (this_c - '0');
2973             break;
2974
2975         case '+':
2976             change_color((NCURSES_PAIRS_T) current, field, value, 1);
2977             break;
2978
2979         case '-':
2980             change_color((NCURSES_PAIRS_T) current, field, -value, 1);
2981             break;
2982
2983         case '=':
2984             change_color((NCURSES_PAIRS_T) current, field, value, 0);
2985             break;
2986
2987         case HELP_KEY_1:
2988             erase();
2989             P("                      RGB Value Editing Help");
2990             P("");
2991             P("You are in the RGB value editor.  Use the arrow keys to select one of");
2992             P("the fields in one of the RGB triples of the current colors; the one");
2993             P("currently selected will be reverse-video highlighted.");
2994             P("");
2995             P("To change a field, enter the digits of the new value; they are echoed");
2996             P("as entered.  Finish by typing `='.  The change will take effect instantly.");
2997             P("To increment or decrement a value, use the same procedure, but finish");
2998             P("with a `+' or `-'.");
2999             P("");
3000             P("Use `!' to shell-out, ^R or ^L to repaint the screen.");
3001             P("");
3002             P("Press 'm' to invoke the top-level menu with the current color settings.");
3003             P("To quit, do ESC");
3004
3005             Pause();
3006             erase();
3007             break;
3008
3009         case 'm':
3010             endwin();
3011             main_menu(FALSE);
3012             for (i = 0; i < MaxColors; i++)
3013                 init_pair((NCURSES_PAIRS_T) i,
3014                           (NCURSES_COLOR_T) COLOR_WHITE,
3015                           (NCURSES_COLOR_T) i);
3016             refresh();
3017             break;
3018
3019         case case_QUIT:
3020             break;
3021
3022         default:
3023             beep();
3024             break;
3025         }
3026
3027         if (current < 0)
3028             current = 0;
3029         if (current >= MaxColors)
3030             current = MaxColors - 1;
3031         if (current < top_color)
3032             top_color = current;
3033         if (current - top_color >= page_size)
3034             top_color = current - (page_size - 1);
3035
3036         MvPrintw(LINES - 1, 0, "Number: %d", value);
3037         clrtoeol();
3038     } while
3039         (!isQuit(this_c, TRUE));
3040
3041     erase();
3042
3043     /*
3044      * ncurses does not reset each color individually when calling endwin().
3045      */
3046     reset_all_colors();
3047
3048     endwin();
3049     return OK;
3050 }
3051 #endif /* HAVE_COLOR_CONTENT */
3052
3053 /****************************************************************************
3054  *
3055  * Alternate character-set stuff
3056  *
3057  ****************************************************************************/
3058 static bool
3059 cycle_attr(int ch, unsigned *at_code, chtype *attr, ATTR_TBL * list, unsigned limit)
3060 {
3061     bool result = TRUE;
3062
3063     switch (ch) {
3064     case 'v':
3065         if ((*at_code += 1) >= limit)
3066             *at_code = 0;
3067         break;
3068     case 'V':
3069         if (*at_code == 0)
3070             *at_code = limit - 1;
3071         else
3072             *at_code -= 1;
3073         break;
3074     default:
3075         result = FALSE;
3076         break;
3077     }
3078     if (result)
3079         *attr = list[*at_code].attr;
3080     return result;
3081 }
3082
3083 #if USE_WIDEC_SUPPORT
3084 static bool
3085 cycle_w_attr(int ch, unsigned *at_code, attr_t *attr, W_ATTR_TBL * list, unsigned limit)
3086 {
3087     bool result = TRUE;
3088
3089     switch (ch) {
3090     case 'v':
3091         if ((*at_code += 1) >= limit)
3092             *at_code = 0;
3093         break;
3094     case 'V':
3095         if (*at_code == 0)
3096             *at_code = limit - 1;
3097         else
3098             *at_code -= 1;
3099         break;
3100     default:
3101         result = FALSE;
3102         break;
3103     }
3104     if (result)
3105         *attr = list[*at_code].attr;
3106     return result;
3107 }
3108 #endif
3109
3110 static bool
3111 cycle_colors(int ch, int *fg, int *bg, NCURSES_PAIRS_T *pair)
3112 {
3113     bool result = FALSE;
3114
3115     if (UseColors) {
3116         result = TRUE;
3117         switch (ch) {
3118         case 'F':
3119             if ((*fg -= 1) < 0)
3120                 *fg = COLORS - 1;
3121             break;
3122         case 'f':
3123             if ((*fg += 1) >= COLORS)
3124                 *fg = 0;
3125             break;
3126         case 'B':
3127             if ((*bg -= 1) < 0)
3128                 *bg = COLORS - 1;
3129             break;
3130         case 'b':
3131             if ((*bg += 1) >= COLORS)
3132                 *bg = 0;
3133             break;
3134         default:
3135             result = FALSE;
3136             break;
3137         }
3138         if (result) {
3139             *pair = (NCURSES_PAIRS_T) (*fg != COLOR_BLACK || *bg != COLOR_BLACK);
3140             if (*pair != 0) {
3141                 *pair = 1;
3142                 if (init_pair(*pair,
3143                               (NCURSES_COLOR_T) *fg,
3144                               (NCURSES_COLOR_T) *bg) == ERR) {
3145                     result = FALSE;
3146                 }
3147             }
3148         }
3149     }
3150     return result;
3151 }
3152
3153 /****************************************************************************
3154  *
3155  * Soft-key label test
3156  *
3157  ****************************************************************************/
3158
3159 #if USE_SOFTKEYS
3160
3161 #define SLK_HELP 17
3162 #define SLK_WORK (SLK_HELP + 3)
3163
3164 static void
3165 slk_help(void)
3166 {
3167     static const char *table[] =
3168     {
3169         "Available commands are:"
3170         ,""
3171         ,"^L         -- repaint this message and activate soft keys"
3172         ,"a/d        -- activate/disable soft keys"
3173         ,"c          -- set centered format for labels"
3174         ,"l          -- set left-justified format for labels"
3175         ,"r          -- set right-justified format for labels"
3176         ,"[12345678] -- set label; labels are numbered 1 through 8"
3177         ,"e          -- erase stdscr (should not erase labels)"
3178         ,"s          -- test scrolling of shortened screen"
3179         ,"v/V        -- cycle through video attributes"
3180 #if HAVE_SLK_COLOR
3181         ,"F/f/B/b    -- cycle through foreground/background colors"
3182 #endif
3183         ,"ESC        -- return to main menu"
3184         ,""
3185         ,"Note: if activating the soft keys causes your terminal to scroll up"
3186         ,"one line, your terminal auto-scrolls when anything is written to the"
3187         ,"last screen position.  The ncurses code does not yet handle this"
3188         ,"gracefully."
3189     };
3190     unsigned j;
3191
3192     move(2, 0);
3193     for (j = 0; j < SIZEOF(table); ++j) {
3194         P(table[j]);
3195     }
3196     refresh();
3197 }
3198
3199 #if HAVE_SLK_COLOR
3200 static void
3201 call_slk_color(int fg, int bg)
3202 {
3203     init_pair(1, (NCURSES_COLOR_T) bg, (NCURSES_COLOR_T) fg);
3204     slk_color(1);
3205     MvPrintw(SLK_WORK, 0, "Colors %d/%d\n", fg, bg);
3206     clrtoeol();
3207     slk_touch();
3208     slk_noutrefresh();
3209     refresh();
3210 }
3211 #endif
3212
3213 static int
3214 slk_test(bool recur GCC_UNUSED)
3215 /* exercise the soft keys */
3216 {
3217     int c, fmt = 1;
3218     char buf[9];
3219     char *s;
3220     chtype attr = A_NORMAL;
3221     unsigned at_code = 0;
3222 #if HAVE_SLK_COLOR
3223     int fg = COLOR_BLACK;
3224     int bg = COLOR_WHITE;
3225     NCURSES_PAIRS_T pair = 0;
3226 #endif
3227     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3228     unsigned my_size = init_attr_list(my_list, termattrs());
3229
3230     c = CTRL('l');
3231 #if HAVE_SLK_COLOR
3232     if (UseColors) {
3233         call_slk_color(fg, bg);
3234     }
3235 #endif
3236
3237     do {
3238         move(0, 0);
3239         switch (c) {
3240         case CTRL('l'):
3241             erase();
3242             attron(A_BOLD);
3243             MvAddStr(0, 20, "Soft Key Exerciser");
3244             attroff(A_BOLD);
3245
3246             slk_help();
3247             /* fall through */
3248
3249         case 'a':
3250             slk_restore();
3251             break;
3252
3253         case 'e':
3254             wclear(stdscr);
3255             break;
3256
3257         case 's':
3258             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3259             while ((c = Getchar()) != 'Q' && (c != ERR))
3260                 AddCh(c);
3261             break;
3262
3263         case 'd':
3264             slk_clear();
3265             break;
3266
3267         case 'l':
3268             fmt = 0;
3269             break;
3270
3271         case 'c':
3272             fmt = 1;
3273             break;
3274
3275         case 'r':
3276             fmt = 2;
3277             break;
3278
3279         case '1':
3280         case '2':
3281         case '3':
3282         case '4':
3283         case '5':
3284         case '6':
3285         case '7':
3286         case '8':
3287             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3288             _nc_STRCPY(buf, "", sizeof(buf));
3289             if ((s = slk_label(c - '0')) != 0) {
3290                 _nc_STRNCPY(buf, s, (size_t) 8);
3291             }
3292             wGetstring(stdscr, buf, 8);
3293             slk_set((c - '0'), buf, fmt);
3294             slk_refresh();
3295             move(SLK_WORK, 0);
3296             clrtobot();
3297             break;
3298
3299         case case_QUIT:
3300             goto done;
3301
3302 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3303         case KEY_RESIZE:
3304             wnoutrefresh(stdscr);
3305             break;
3306 #endif
3307
3308         default:
3309             if (cycle_attr(c, &at_code, &attr, my_list, my_size)) {
3310                 slk_attrset(attr);
3311                 slk_touch();
3312                 slk_noutrefresh();
3313                 break;
3314             }
3315 #if HAVE_SLK_COLOR
3316             if (cycle_colors(c, &fg, &bg, &pair)) {
3317                 if (UseColors) {
3318                     call_slk_color(fg, bg);
3319                 } else {
3320                     beep();
3321                 }
3322                 break;
3323             }
3324 #endif
3325             beep();
3326             break;
3327         }
3328     } while (!isQuit(c = Getchar(), TRUE));
3329
3330   done:
3331     slk_clear();
3332     erase();
3333     endwin();
3334     return OK;
3335 }
3336
3337 #if USE_WIDEC_SUPPORT
3338 #define SLKLEN 8
3339 static int
3340 x_slk_test(bool recur GCC_UNUSED)
3341 /* exercise the soft keys */
3342 {
3343     int c, fmt = 1;
3344     wchar_t buf[SLKLEN + 1];
3345     char *s;
3346     attr_t attr = WA_NORMAL;
3347     unsigned at_code = 0;
3348     int fg = COLOR_BLACK;
3349     int bg = COLOR_WHITE;
3350     NCURSES_PAIRS_T pair = 0;
3351     W_ATTR_TBL my_list[SIZEOF(w_attrs_to_test)];
3352     unsigned my_size = init_w_attr_list(my_list, term_attrs());
3353
3354     c = CTRL('l');
3355     if (UseColors) {
3356         call_slk_color(fg, bg);
3357     }
3358     do {
3359         move(0, 0);
3360         switch (c) {
3361         case CTRL('l'):
3362             erase();
3363             attr_on(WA_BOLD, NULL);
3364             MvAddStr(0, 20, "Soft Key Exerciser");
3365             attr_off(WA_BOLD, NULL);
3366
3367             slk_help();
3368             /* fall through */
3369
3370         case 'a':
3371             slk_restore();
3372             break;
3373
3374         case 'e':
3375             wclear(stdscr);
3376             break;
3377
3378         case 's':
3379             MvPrintw(SLK_WORK, 0, "Press Q to stop the scrolling-test: ");
3380             while ((c = Getchar()) != 'Q' && (c != ERR))
3381                 AddCh(c);
3382             break;
3383
3384         case 'd':
3385             slk_clear();
3386             break;
3387
3388         case 'l':
3389             fmt = 0;
3390             break;
3391
3392         case 'c':
3393             fmt = 1;
3394             break;
3395
3396         case 'r':
3397             fmt = 2;
3398             break;
3399
3400         case '1':
3401         case '2':
3402         case '3':
3403         case '4':
3404         case '5':
3405         case '6':
3406         case '7':
3407         case '8':
3408             MvAddStr(SLK_WORK, 0, "Please enter the label value: ");
3409             *buf = 0;
3410             if ((s = slk_label(c - '0')) != 0) {
3411                 char *temp = strdup(s);
3412                 size_t used = strlen(temp);
3413                 size_t want = SLKLEN;
3414                 size_t test;
3415 #ifndef state_unused
3416                 mbstate_t state;
3417 #endif
3418
3419                 buf[0] = L'\0';
3420                 while (want > 0 && used != 0) {
3421                     const char *base = s;
3422                     reset_mbytes(state);
3423                     test = count_mbytes(base, 0, &state);
3424                     if (test == (size_t) -1) {
3425                         temp[--used] = 0;
3426                     } else if (test > want) {
3427                         temp[--used] = 0;
3428                     } else {
3429                         reset_mbytes(state);
3430                         trans_mbytes(buf, base, want, &state);
3431                         break;
3432                     }
3433                 }
3434                 free(temp);
3435             }
3436             wGet_wstring(stdscr, buf, SLKLEN);
3437             slk_wset((c - '0'), buf, fmt);
3438             slk_refresh();
3439             move(SLK_WORK, 0);
3440             clrtobot();
3441             break;
3442
3443         case case_QUIT:
3444             goto done;
3445
3446         case 'F':
3447             if (UseColors) {
3448                 fg = (NCURSES_COLOR_T) ((fg + 1) % COLORS);
3449                 call_slk_color(fg, bg);
3450             }
3451             break;
3452         case 'B':
3453             if (UseColors) {
3454                 bg = (NCURSES_COLOR_T) ((bg + 1) % COLORS);
3455                 call_slk_color(fg, bg);
3456             }
3457             break;
3458 #if defined(NCURSES_VERSION) && defined(KEY_RESIZE) && HAVE_WRESIZE
3459         case KEY_RESIZE:
3460             wnoutrefresh(stdscr);
3461             break;
3462 #endif
3463         default:
3464             if (cycle_w_attr(c, &at_code, &attr, my_list, my_size)) {
3465                 slk_attr_set(attr, (NCURSES_COLOR_T) (fg || bg), NULL);
3466                 slk_touch();
3467                 slk_noutrefresh();
3468                 break;
3469             }
3470 #if HAVE_SLK_COLOR
3471             if (cycle_colors(c, &fg, &bg, &pair)) {
3472                 if (UseColors) {
3473                     call_slk_color(fg, bg);
3474                 } else {
3475                     beep();
3476                 }
3477                 break;
3478             }
3479 #endif
3480             beep();
3481             break;
3482         }
3483     } while (!isQuit(c = Getchar(), TRUE));
3484
3485   done:
3486     slk_clear();
3487     erase();
3488     endwin();
3489     return OK;
3490 }
3491 #endif
3492 #endif /* SLK_INIT */
3493
3494 static void
3495 show_256_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3496 {
3497     unsigned first = 0;
3498     unsigned last = 255;
3499     unsigned code;
3500     int count;
3501
3502     erase();
3503     attron(A_BOLD);
3504     MvPrintw(0, 20, "Display of Character Codes %#0x to %#0x",
3505              first, last);
3506     attroff(A_BOLD);
3507     refresh();
3508
3509     for (code = first; code <= last; ++code) {
3510         int row = (int) (2 + (code / 16));
3511         int col = (int) (5 * (code % 16));
3512         IGNORE_RC(mvaddch(row, col, colored_chtype(code, attr, pair)));
3513         for (count = 1; count < repeat; ++count) {
3514             AddCh(colored_chtype(code, attr, pair));
3515         }
3516     }
3517
3518 }
3519
3520 /*
3521  * Show a slice of 32 characters, allowing those to be repeated up to the
3522  * screen's width.
3523  *
3524  * ISO 6429:  codes 0x80 to 0x9f may be control characters that cause the
3525  * terminal to perform functions.  The remaining codes can be graphic.
3526  */
3527 static void
3528 show_upper_chars(int base, int pagesize, int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3529 {
3530     unsigned code;
3531     unsigned first = (unsigned) base;
3532     unsigned last = first + (unsigned) pagesize - 2;
3533     bool C1 = (first == 128);
3534     int reply;
3535
3536     erase();
3537     attron(A_BOLD);
3538     MvPrintw(0, 20, "Display of %s Character Codes %d to %d",
3539              C1 ? "C1" : "GR", first, last);
3540     attroff(A_BOLD);
3541     refresh();
3542
3543     for (code = first; code <= last; code++) {
3544         int count = repeat;
3545         int row = 2 + ((int) (code - first) % (pagesize / 2));
3546         int col = ((int) (code - first) / (pagesize / 2)) * COLS / 2;
3547         char tmp[80];
3548         _nc_SPRINTF(tmp, _nc_SLIMIT(sizeof(tmp)) "%3u (0x%x)", code, code);
3549         MvPrintw(row, col, "%*s: ", COLS / 4, tmp);
3550
3551         do {
3552             if (C1)
3553                 nodelay(stdscr, TRUE);
3554             echochar(colored_chtype(code, attr, pair));
3555             if (C1) {
3556                 /* (yes, this _is_ crude) */
3557                 while ((reply = Getchar()) != ERR) {
3558                     AddCh(UChar(reply));
3559                     napms(10);
3560                 }
3561                 nodelay(stdscr, FALSE);
3562             }
3563         } while (--count > 0);
3564     }
3565 }
3566
3567 #define PC_COLS 4
3568
3569 static void
3570 show_pc_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3571 {
3572     unsigned code;
3573
3574     erase();
3575     attron(A_BOLD);
3576     MvPrintw(0, 20, "Display of PC Character Codes");
3577     attroff(A_BOLD);
3578     refresh();
3579
3580     for (code = 0; code < 16; ++code) {
3581         MvPrintw(2, (int) code * PC_COLS + 8, "%X", code);
3582     }
3583     for (code = 0; code < 256; code++) {
3584         int count = repeat;
3585         int row = 3 + (int) (code / 16) + (code >= 128);
3586         int col = 8 + (int) (code % 16) * PC_COLS;
3587         if ((code % 16) == 0)
3588             MvPrintw(row, 0, "0x%02x:", code);
3589         move(row, col);
3590         do {
3591             switch (code) {
3592             case '\n':
3593             case '\r':
3594             case '\b':
3595             case '\f':
3596             case '\033':
3597             case 0x9b:
3598                 /*
3599                  * Skip the ones that do not work.
3600                  */
3601                 break;
3602             default:
3603                 AddCh(colored_chtype(code, A_ALTCHARSET | attr, pair));
3604                 break;
3605             }
3606         } while (--count > 0);
3607     }
3608 }
3609
3610 static void
3611 show_box_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3612 {
3613     (void) repeat;
3614
3615     attr |= (attr_t) COLOR_PAIR(pair);
3616
3617     erase();
3618     attron(A_BOLD);
3619     MvAddStr(0, 20, "Display of the ACS Line-Drawing Set");
3620     attroff(A_BOLD);
3621     refresh();
3622     /* *INDENT-OFF* */
3623     wborder(stdscr,
3624             colored_chtype(ACS_VLINE,    attr, pair),
3625             colored_chtype(ACS_VLINE,    attr, pair),
3626             colored_chtype(ACS_HLINE,    attr, pair),
3627             colored_chtype(ACS_HLINE,    attr, pair),
3628             colored_chtype(ACS_ULCORNER, attr, pair),
3629             colored_chtype(ACS_URCORNER, attr, pair),
3630             colored_chtype(ACS_LLCORNER, attr, pair),
3631             colored_chtype(ACS_LRCORNER, attr, pair));
3632     MvHLine(LINES / 2, 0,        colored_chtype(ACS_HLINE, attr, pair), COLS);
3633     MvVLine(0,         COLS / 2, colored_chtype(ACS_VLINE, attr, pair), LINES);
3634     MvAddCh(0,         COLS / 2, colored_chtype(ACS_TTEE,  attr, pair));
3635     MvAddCh(LINES / 2, COLS / 2, colored_chtype(ACS_PLUS,  attr, pair));
3636     MvAddCh(LINES - 1, COLS / 2, colored_chtype(ACS_BTEE,  attr, pair));
3637     MvAddCh(LINES / 2, 0,        colored_chtype(ACS_LTEE,  attr, pair));
3638     MvAddCh(LINES / 2, COLS - 1, colored_chtype(ACS_RTEE,  attr, pair));
3639     /* *INDENT-ON* */
3640
3641 }
3642
3643 static int
3644 show_1_acs(int n, int repeat, const char *name, chtype code)
3645 {
3646     const int height = 16;
3647     int row = 2 + (n % height);
3648     int col = (n / height) * COLS / 2;
3649
3650     MvPrintw(row, col, "%*s : ", COLS / 4, name);
3651     do {
3652         AddCh(code);
3653     } while (--repeat > 0);
3654     return n + 1;
3655 }
3656
3657 static void
3658 show_acs_chars(int repeat, attr_t attr, NCURSES_PAIRS_T pair)
3659 /* display the ACS character set */
3660 {
3661     int n;
3662
3663 #define BOTH(name) #name, colored_chtype(name, attr, (chtype) pair)
3664
3665     erase();
3666     attron(A_BOLD);
3667     MvAddStr(0, 20, "Display of the ACS Character Set");
3668     attroff(A_BOLD);
3669     refresh();
3670
3671     n = show_1_acs(0, repeat, BOTH(ACS_ULCORNER));
3672     n = show_1_acs(n, repeat, BOTH(ACS_URCORNER));
3673     n = show_1_acs(n, repeat, BOTH(ACS_LLCORNER));
3674     n = show_1_acs(n, repeat, BOTH(ACS_LRCORNER));
3675
3676     n = show_1_acs(n, repeat, BOTH(ACS_LTEE));
3677     n = show_1_acs(n, repeat, BOTH(ACS_RTEE));
3678     n = show_1_acs(n, repeat, BOTH(ACS_TTEE));
3679     n = show_1_acs(n, repeat, BOTH(ACS_BTEE));
3680
3681     n = show_1_acs(n, repeat, BOTH(ACS_HLINE));
3682     n = show_1_acs(n, repeat, BOTH(ACS_VLINE));
3683
3684     /*
3685      * HPUX's ACS definitions are broken here.  Just give up.
3686      */
3687 #if !(defined(__hpux) && !defined(NCURSES_VERSION))
3688     n = show_1_acs(n, repeat, BOTH(ACS_LARROW));
3689     n = show_1_acs(n, repeat, BOTH(ACS_RARROW));
3690     n = show_1_acs(n, repeat, BOTH(ACS_UARROW));
3691     n = show_1_acs(n, repeat, BOTH(ACS_DARROW));
3692
3693     n = show_1_acs(n, repeat, BOTH(ACS_BLOCK));
3694     n = show_1_acs(n, repeat, BOTH(ACS_BOARD));
3695     n = show_1_acs(n, repeat, BOTH(ACS_LANTERN));
3696     n = show_1_acs(n, repeat, BOTH(ACS_BULLET));
3697     n = show_1_acs(n, repeat, BOTH(ACS_CKBOARD));
3698     n = show_1_acs(n, repeat, BOTH(ACS_DEGREE));
3699     n = show_1_acs(n, repeat, BOTH(ACS_DIAMOND));
3700     n = show_1_acs(n, repeat, BOTH(ACS_PLMINUS));
3701     n = show_1_acs(n, repeat, BOTH(ACS_PLUS));
3702
3703     n = show_1_acs(n, repeat, BOTH(ACS_GEQUAL));
3704     n = show_1_acs(n, repeat, BOTH(ACS_NEQUAL));
3705     n = show_1_acs(n, repeat, BOTH(ACS_LEQUAL));
3706
3707     n = show_1_acs(n, repeat, BOTH(ACS_STERLING));
3708     n = show_1_acs(n, repeat, BOTH(ACS_PI));
3709     n = show_1_acs(n, repeat, BOTH(ACS_S1));
3710     n = show_1_acs(n, repeat, BOTH(ACS_S3));
3711     n = show_1_acs(n, repeat, BOTH(ACS_S7));
3712     (void) show_1_acs(n, repeat, BOTH(ACS_S9));
3713 #endif
3714 #undef BOTH
3715 }
3716
3717 static int
3718 acs_test(bool recur GCC_UNUSED)
3719 {
3720     int c = 'a';
3721     int pagesize = 32;
3722     char *term = getenv("TERM");
3723     const char *pch_kludge = ((term != 0 && strstr(term, "linux"))
3724                               ? "p=PC, "
3725                               : "");
3726     chtype attr = A_NORMAL;
3727     int digit = 0;
3728     int repeat = 1;
3729     int fg = COLOR_BLACK;
3730     int bg = COLOR_BLACK;
3731     unsigned at_code = 0;
3732     NCURSES_PAIRS_T pair = 0;
3733     void (*last_show_acs) (int, attr_t, NCURSES_PAIRS_T) = 0;
3734     ATTR_TBL my_list[SIZEOF(attrs_to_test)];
3735     unsigned my_size = init_attr_list(my_list, termattrs());
3736
3737     do {
3738         switch (c) {
3739         case CTRL('L'):
3740             Repaint();
3741             break;
3742         case 'a':
3743             ToggleAcs(last_show_acs, show_acs_chars);
3744             break;
3745         case 'p':
3746             if (*pch_kludge)
3747                 ToggleAcs(last_show_acs, show_pc_chars);
3748             else
3749                 beep();
3750             break;
3751         case 'w':
3752             if (pagesize == 32) {
3753                 pagesize = 256;
3754             } else {
3755                 pagesize = 32;
3756             }
3757             break;
3758         case 'x':
3759             ToggleAcs(last_show_acs, show_box_chars);
3760             break;
3761         case '0':
3762         case '1':
3763         case '2':
3764         case '3':
3765             digit = (c - '0');
3766             last_show_acs = 0;
3767             break;
3768         case '-':
3769             if (digit > 0) {
3770                 --digit;