]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/dialog/util.c
Make linux_ptrace() use linux_msg() instead of printf().
[FreeBSD/FreeBSD.git] / contrib / dialog / util.c
1 /*
2  *  $Id: util.c,v 1.272 2018/06/21 23:47:10 tom Exp $
3  *
4  *  util.c -- miscellaneous utilities for dialog
5  *
6  *  Copyright 2000-2016,2018    Thomas E. Dickey
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU Lesser General Public License, version 2.1
10  *  as published by the Free Software Foundation.
11  *
12  *  This program is distributed in the hope that it will be useful, but
13  *  WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this program; if not, write to
19  *      Free Software Foundation, Inc.
20  *      51 Franklin St., Fifth Floor
21  *      Boston, MA 02110, USA.
22  *
23  *  An earlier version of this program lists as authors
24  *      Savio Lam (lam836@cs.cuhk.hk)
25  */
26
27 #include <dialog.h>
28 #include <dlg_keys.h>
29
30 #ifdef HAVE_SETLOCALE
31 #include <locale.h>
32 #endif
33
34 #ifdef NEED_WCHAR_H
35 #include <wchar.h>
36 #endif
37
38 #ifdef NCURSES_VERSION
39 #if defined(HAVE_NCURSESW_TERM_H)
40 #include <ncursesw/term.h>
41 #elif defined(HAVE_NCURSES_TERM_H)
42 #include <ncurses/term.h>
43 #else
44 #include <term.h>
45 #endif
46 #endif
47
48 #if defined(HAVE_WCHGAT)
49 #  if defined(NCURSES_VERSION_PATCH)
50 #    if NCURSES_VERSION_PATCH >= 20060715
51 #      define USE_WCHGAT 1
52 #    else
53 #      define USE_WCHGAT 0
54 #    endif
55 #  else
56 #    define USE_WCHGAT 1
57 #  endif
58 #else
59 #  define USE_WCHGAT 0
60 #endif
61
62 /* globals */
63 DIALOG_STATE dialog_state;
64 DIALOG_VARS dialog_vars;
65
66 #if !(defined(HAVE_WGETPARENT) && defined(HAVE_WINDOW__PARENT))
67 #define NEED_WGETPARENT 1
68 #else
69 #undef NEED_WGETPARENT
70 #endif
71
72 #define concat(a,b) a##b
73
74 #ifdef HAVE_RC_FILE
75 #define RC_DATA(name,comment) , #name "_color", comment " color"
76 #else
77 #define RC_DATA(name,comment)   /*nothing */
78 #endif
79
80 #ifdef HAVE_COLOR
81 #include <dlg_colors.h>
82 #define COLOR_DATA(upr) , \
83         concat(DLGC_FG_,upr), \
84         concat(DLGC_BG_,upr), \
85         concat(DLGC_HL_,upr)
86 #else
87 #define COLOR_DATA(upr)         /*nothing */
88 #endif
89
90 #define DATA(atr,upr,lwr,cmt) { atr COLOR_DATA(upr) RC_DATA(lwr,cmt) }
91
92 #define UseShadow(dw) ((dw) != 0 && (dw)->normal != 0 && (dw)->shadow != 0)
93
94 /*
95  * Table of color and attribute values, default is for mono display.
96  * The order matches the DIALOG_ATR() values.
97  */
98 /* *INDENT-OFF* */
99 DIALOG_COLORS dlg_color_table[] =
100 {
101     DATA(A_NORMAL,      SCREEN,                 screen, "Screen"),
102     DATA(A_NORMAL,      SHADOW,                 shadow, "Shadow"),
103     DATA(A_REVERSE,     DIALOG,                 dialog, "Dialog box"),
104     DATA(A_REVERSE,     TITLE,                  title, "Dialog box title"),
105     DATA(A_REVERSE,     BORDER,                 border, "Dialog box border"),
106     DATA(A_BOLD,        BUTTON_ACTIVE,          button_active, "Active button"),
107     DATA(A_DIM,         BUTTON_INACTIVE,        button_inactive, "Inactive button"),
108     DATA(A_UNDERLINE,   BUTTON_KEY_ACTIVE,      button_key_active, "Active button key"),
109     DATA(A_UNDERLINE,   BUTTON_KEY_INACTIVE,    button_key_inactive, "Inactive button key"),
110     DATA(A_NORMAL,      BUTTON_LABEL_ACTIVE,    button_label_active, "Active button label"),
111     DATA(A_NORMAL,      BUTTON_LABEL_INACTIVE,  button_label_inactive, "Inactive button label"),
112     DATA(A_REVERSE,     INPUTBOX,               inputbox, "Input box"),
113     DATA(A_REVERSE,     INPUTBOX_BORDER,        inputbox_border, "Input box border"),
114     DATA(A_REVERSE,     SEARCHBOX,              searchbox, "Search box"),
115     DATA(A_REVERSE,     SEARCHBOX_TITLE,        searchbox_title, "Search box title"),
116     DATA(A_REVERSE,     SEARCHBOX_BORDER,       searchbox_border, "Search box border"),
117     DATA(A_REVERSE,     POSITION_INDICATOR,     position_indicator, "File position indicator"),
118     DATA(A_REVERSE,     MENUBOX,                menubox, "Menu box"),
119     DATA(A_REVERSE,     MENUBOX_BORDER,         menubox_border, "Menu box border"),
120     DATA(A_REVERSE,     ITEM,                   item, "Item"),
121     DATA(A_NORMAL,      ITEM_SELECTED,          item_selected, "Selected item"),
122     DATA(A_REVERSE,     TAG,                    tag, "Tag"),
123     DATA(A_REVERSE,     TAG_SELECTED,           tag_selected, "Selected tag"),
124     DATA(A_NORMAL,      TAG_KEY,                tag_key, "Tag key"),
125     DATA(A_BOLD,        TAG_KEY_SELECTED,       tag_key_selected, "Selected tag key"),
126     DATA(A_REVERSE,     CHECK,                  check, "Check box"),
127     DATA(A_REVERSE,     CHECK_SELECTED,         check_selected, "Selected check box"),
128     DATA(A_REVERSE,     UARROW,                 uarrow, "Up arrow"),
129     DATA(A_REVERSE,     DARROW,                 darrow, "Down arrow"),
130     DATA(A_NORMAL,      ITEMHELP,               itemhelp, "Item help-text"),
131     DATA(A_BOLD,        FORM_ACTIVE_TEXT,       form_active_text, "Active form text"),
132     DATA(A_REVERSE,     FORM_TEXT,              form_text, "Form text"),
133     DATA(A_NORMAL,      FORM_ITEM_READONLY,     form_item_readonly, "Readonly form item"),
134     DATA(A_REVERSE,     GAUGE,                  gauge, "Dialog box gauge"),
135     DATA(A_REVERSE,     BORDER2,                border2, "Dialog box border2"),
136     DATA(A_REVERSE,     INPUTBOX_BORDER2,       inputbox_border2, "Input box border2"),
137     DATA(A_REVERSE,     SEARCHBOX_BORDER2,      searchbox_border2, "Search box border2"),
138     DATA(A_REVERSE,     MENUBOX_BORDER2,        menubox_border2, "Menu box border2")
139 };
140 /* *INDENT-ON* */
141
142 /*
143  * Maintain a list of subwindows so that we can delete them to cleanup.
144  * More important, this provides a fallback when wgetparent() is not available.
145  */
146 static void
147 add_subwindow(WINDOW *parent, WINDOW *child)
148 {
149     DIALOG_WINDOWS *p = dlg_calloc(DIALOG_WINDOWS, 1);
150
151     if (p != 0) {
152         p->normal = parent;
153         p->shadow = child;
154         p->next = dialog_state.all_subwindows;
155         dialog_state.all_subwindows = p;
156     }
157 }
158
159 static void
160 del_subwindows(WINDOW *parent)
161 {
162     DIALOG_WINDOWS *p = dialog_state.all_subwindows;
163     DIALOG_WINDOWS *q = 0;
164     DIALOG_WINDOWS *r;
165
166     while (p != 0) {
167         if (p->normal == parent) {
168             delwin(p->shadow);
169             r = p->next;
170             if (q == 0) {
171                 dialog_state.all_subwindows = r;
172             } else {
173                 q->next = r;
174             }
175             free(p);
176             p = r;
177         } else {
178             q = p;
179             p = p->next;
180         }
181     }
182 }
183
184 /*
185  * Display background title if it exists ...
186  */
187 void
188 dlg_put_backtitle(void)
189 {
190     int i;
191
192     if (dialog_vars.backtitle != NULL) {
193         chtype attr = A_NORMAL;
194         int backwidth = dlg_count_columns(dialog_vars.backtitle);
195
196         dlg_attrset(stdscr, screen_attr);
197         (void) wmove(stdscr, 0, 1);
198         dlg_print_text(stdscr, dialog_vars.backtitle, COLS - 2, &attr);
199         for (i = 0; i < COLS - backwidth; i++)
200             (void) waddch(stdscr, ' ');
201         (void) wmove(stdscr, 1, 1);
202         for (i = 0; i < COLS - 2; i++)
203             (void) waddch(stdscr, dlg_boxchar(ACS_HLINE));
204     }
205
206     (void) wnoutrefresh(stdscr);
207 }
208
209 /*
210  * Set window to attribute 'attr'.  There are more efficient ways to do this,
211  * but will not work on older/buggy ncurses versions.
212  */
213 void
214 dlg_attr_clear(WINDOW *win, int height, int width, chtype attr)
215 {
216     int i, j;
217
218     dlg_attrset(win, attr);
219     for (i = 0; i < height; i++) {
220         (void) wmove(win, i, 0);
221         for (j = 0; j < width; j++)
222             (void) waddch(win, ' ');
223     }
224     (void) touchwin(win);
225 }
226
227 void
228 dlg_clear(void)
229 {
230     dlg_attr_clear(stdscr, LINES, COLS, screen_attr);
231 }
232
233 #define isprivate(s) ((s) != 0 && strstr(s, "\033[?") != 0)
234
235 #define TTY_DEVICE "/dev/tty"
236
237 /*
238  * If $DIALOG_TTY exists, allow the program to try to open the terminal
239  * directly when stdout is redirected.  By default we require the "--stdout"
240  * option to be given, but some scripts were written making use of the
241  * behavior of dialog which tried opening the terminal anyway. 
242  */
243 static char *
244 dialog_tty(void)
245 {
246     char *result = getenv("DIALOG_TTY");
247     if (result != 0 && atoi(result) == 0)
248         result = 0;
249     return result;
250 }
251
252 /*
253  * Open the terminal directly.  If one of stdin, stdout or stderr really points
254  * to a tty, use it.  Otherwise give up and open /dev/tty.
255  */
256 static int
257 open_terminal(char **result, int mode)
258 {
259     const char *device = TTY_DEVICE;
260     if (!isatty(fileno(stderr))
261         || (device = ttyname(fileno(stderr))) == 0) {
262         if (!isatty(fileno(stdout))
263             || (device = ttyname(fileno(stdout))) == 0) {
264             if (!isatty(fileno(stdin))
265                 || (device = ttyname(fileno(stdin))) == 0) {
266                 device = TTY_DEVICE;
267             }
268         }
269     }
270     *result = dlg_strclone(device);
271     return open(device, mode);
272 }
273
274 #ifdef NCURSES_VERSION
275 static int
276 my_putc(int ch)
277 {
278     char buffer[2];
279     int fd = fileno(dialog_state.screen_output);
280
281     buffer[0] = (char) ch;
282     return (int) write(fd, buffer, (size_t) 1);
283 }
284 #endif
285
286 /*
287  * Do some initialization for dialog.
288  *
289  * 'input' is the real tty input of dialog.  Usually it is stdin, but if
290  * --input-fd option is used, it may be anything.
291  *
292  * 'output' is where dialog will send its result.  Usually it is stderr, but
293  * if --stdout or --output-fd is used, it may be anything.  We are concerned
294  * mainly with the case where it happens to be the same as stdout.
295  */
296 void
297 init_dialog(FILE *input, FILE *output)
298 {
299     int fd1, fd2;
300     char *device = 0;
301
302     setlocale(LC_ALL, "");
303
304     dialog_state.output = output;
305     dialog_state.tab_len = TAB_LEN;
306     dialog_state.aspect_ratio = DEFAULT_ASPECT_RATIO;
307 #ifdef HAVE_COLOR
308     dialog_state.use_colors = USE_COLORS;       /* use colors by default? */
309     dialog_state.use_shadow = USE_SHADOW;       /* shadow dialog boxes by default? */
310 #endif
311
312 #ifdef HAVE_RC_FILE
313     if (dlg_parse_rc() == -1)   /* Read the configuration file */
314         dlg_exiterr("init_dialog: dlg_parse_rc");
315 #endif
316
317     /*
318      * Some widgets (such as gauge) may read from the standard input.  Pipes
319      * only connect stdout/stdin, so there is not much choice.  But reading a
320      * pipe would get in the way of curses' normal reading stdin for getch.
321      *
322      * As in the --stdout (see below), reopening the terminal does not always
323      * work properly.  dialog provides a --pipe-fd option for this purpose.  We
324      * test that case first (differing fileno's for input/stdin).  If the
325      * fileno's are equal, but we're not reading from a tty, see if we can open
326      * /dev/tty.
327      */
328     dialog_state.pipe_input = stdin;
329     if (fileno(input) != fileno(stdin)) {
330         if ((fd1 = dup(fileno(input))) >= 0
331             && (fd2 = dup(fileno(stdin))) >= 0) {
332             (void) dup2(fileno(input), fileno(stdin));
333             dialog_state.pipe_input = fdopen(fd2, "r");
334             if (fileno(stdin) != 0)     /* some functions may read fd #0 */
335                 (void) dup2(fileno(stdin), 0);
336         } else {
337             dlg_exiterr("cannot open tty-input");
338         }
339         close(fd1);
340     } else if (!isatty(fileno(stdin))) {
341         if ((fd1 = open_terminal(&device, O_RDONLY)) >= 0) {
342             if ((fd2 = dup(fileno(stdin))) >= 0) {
343                 dialog_state.pipe_input = fdopen(fd2, "r");
344                 if (freopen(device, "r", stdin) == 0)
345                     dlg_exiterr("cannot open tty-input");
346                 if (fileno(stdin) != 0)         /* some functions may read fd #0 */
347                     (void) dup2(fileno(stdin), 0);
348             }
349             close(fd1);
350         }
351         free(device);
352     }
353
354     /*
355      * If stdout is not a tty and dialog is called with the --stdout option, we
356      * have to provide for a way to write to the screen.
357      *
358      * The curses library normally writes its output to stdout, leaving stderr
359      * free for scripting.  Scripts are simpler when stdout is redirected.  The
360      * newterm function is useful; it allows us to specify where the output
361      * goes.  Reopening the terminal is not portable since several
362      * configurations do not allow this to work properly:
363      *
364      * a) some getty implementations (and possibly broken tty drivers, e.g., on
365      *    HPUX 10 and 11) cause stdin to act as if it is still in cooked mode
366      *    even though results from ioctl's state that it is successfully
367      *    altered to raw mode.  Broken is the proper term.
368      *
369      * b) the user may not have permissions on the device, e.g., if one su's
370      *    from the login user to another non-privileged user.
371      */
372     if (!isatty(fileno(stdout))
373         && (fileno(stdout) == fileno(output) || dialog_tty())) {
374         if ((fd1 = open_terminal(&device, O_WRONLY)) >= 0
375             && (dialog_state.screen_output = fdopen(fd1, "w")) != 0) {
376             if (newterm(NULL, dialog_state.screen_output, stdin) == 0) {
377                 dlg_exiterr("cannot initialize curses");
378             }
379             free(device);
380         } else {
381             dlg_exiterr("cannot open tty-output");
382         }
383     } else {
384         dialog_state.screen_output = stdout;
385         (void) initscr();
386     }
387 #ifdef NCURSES_VERSION
388     /*
389      * Cancel xterm's alternate-screen mode.
390      */
391     if (!dialog_vars.keep_tite
392         && (fileno(dialog_state.screen_output) != fileno(stdout)
393             || isatty(fileno(dialog_state.screen_output)))
394         && key_mouse != 0       /* xterm and kindred */
395         && isprivate(enter_ca_mode)
396         && isprivate(exit_ca_mode)) {
397         /*
398          * initscr() or newterm() already wrote enter_ca_mode as a side
399          * effect of initializing the screen.  It would be nice to not even
400          * do that, but we do not really have access to the correct copy of
401          * the terminfo description until those functions have been invoked.
402          */
403         (void) refresh();
404         (void) tputs(exit_ca_mode, 0, my_putc);
405         (void) tputs(clear_screen, 0, my_putc);
406         /*
407          * Prevent ncurses from switching "back" to the normal screen when
408          * exiting from dialog.  That would move the cursor to the original
409          * location saved in xterm.  Normally curses sets the cursor position
410          * to the first line after the display, but the alternate screen
411          * switching is done after that point.
412          *
413          * Cancelling the strings altogether also works around the buggy
414          * implementation of alternate-screen in rxvt, etc., which clear
415          * more of the display than they should.
416          */
417         enter_ca_mode = 0;
418         exit_ca_mode = 0;
419     }
420 #endif
421 #ifdef HAVE_FLUSHINP
422     (void) flushinp();
423 #endif
424     (void) keypad(stdscr, TRUE);
425     (void) cbreak();
426     (void) noecho();
427
428     if (!dialog_state.no_mouse) {
429         mouse_open();
430     }
431
432     dialog_state.screen_initialized = TRUE;
433
434 #ifdef HAVE_COLOR
435     if (dialog_state.use_colors || dialog_state.use_shadow)
436         dlg_color_setup();      /* Set up colors */
437 #endif
438
439     /* Set screen to screen attribute */
440     dlg_clear();
441 }
442
443 #ifdef HAVE_COLOR
444 static int defined_colors = 1;  /* pair-0 is reserved */
445 /*
446  * Setup for color display
447  */
448 void
449 dlg_color_setup(void)
450 {
451     unsigned i;
452
453     if (has_colors()) {         /* Terminal supports color? */
454         (void) start_color();
455
456 #if defined(HAVE_USE_DEFAULT_COLORS)
457         use_default_colors();
458 #endif
459
460 #if defined(__NetBSD__) && defined(_CURSES_)
461 #define C_ATTR(x,y) (((x) != 0 ? A_BOLD :  0) | COLOR_PAIR((y)))
462         /* work around bug in NetBSD curses */
463         for (i = 0; i < sizeof(dlg_color_table) /
464              sizeof(dlg_color_table[0]); i++) {
465
466             /* Initialize color pairs */
467             (void) init_pair(i + 1,
468                              dlg_color_table[i].fg,
469                              dlg_color_table[i].bg);
470
471             /* Setup color attributes */
472             dlg_color_table[i].atr = C_ATTR(dlg_color_table[i].hilite, i + 1);
473         }
474         defined_colors = i + 1;
475 #else
476         for (i = 0; i < sizeof(dlg_color_table) /
477              sizeof(dlg_color_table[0]); i++) {
478
479             /* Initialize color pairs */
480             chtype color = dlg_color_pair(dlg_color_table[i].fg,
481                                           dlg_color_table[i].bg);
482
483             /* Setup color attributes */
484             dlg_color_table[i].atr = ((dlg_color_table[i].hilite
485                                        ? A_BOLD
486                                        : 0)
487                                       | color);
488         }
489 #endif
490     } else {
491         dialog_state.use_colors = FALSE;
492         dialog_state.use_shadow = FALSE;
493     }
494 }
495
496 int
497 dlg_color_count(void)
498 {
499     return sizeof(dlg_color_table) / sizeof(dlg_color_table[0]);
500 }
501
502 /*
503  * Wrapper for getattrs(), or the more cumbersome X/Open wattr_get().
504  */
505 chtype
506 dlg_get_attrs(WINDOW *win)
507 {
508     chtype result;
509 #ifdef HAVE_GETATTRS
510     result = (chtype) getattrs(win);
511 #else
512     attr_t my_result;
513     short my_pair;
514     wattr_get(win, &my_result, &my_pair, NULL);
515     result = my_result;
516 #endif
517     return result;
518 }
519
520 /*
521  * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we
522  * have (or can) define a pair with the given color as foreground on the
523  * window's defined background.
524  */
525 chtype
526 dlg_color_pair(int foreground, int background)
527 {
528     chtype result = 0;
529     int pair;
530     short fg, bg;
531     bool found = FALSE;
532
533     for (pair = 1; pair < defined_colors; ++pair) {
534         if (pair_content((short) pair, &fg, &bg) != ERR
535             && fg == foreground
536             && bg == background) {
537             result = (chtype) COLOR_PAIR(pair);
538             found = TRUE;
539             break;
540         }
541     }
542     if (!found && (defined_colors + 1) < COLOR_PAIRS) {
543         pair = defined_colors++;
544         (void) init_pair((short) pair, (short) foreground, (short) background);
545         result = (chtype) COLOR_PAIR(pair);
546     }
547     return result;
548 }
549
550 /*
551  * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we
552  * have (or can) define a pair with the given color as foreground on the
553  * window's defined background.
554  */
555 static chtype
556 define_color(WINDOW *win, int foreground)
557 {
558     int pair;
559     short fg, bg, background;
560     if (dialog_state.text_only) {
561         background = COLOR_BLACK;
562     } else {
563         chtype attrs = dlg_get_attrs(win);
564
565         if ((pair = PAIR_NUMBER(attrs)) != 0
566             && pair_content((short) pair, &fg, &bg) != ERR) {
567             background = bg;
568         } else {
569             background = COLOR_BLACK;
570         }
571     }
572     return dlg_color_pair(foreground, background);
573 }
574 #endif
575
576 /*
577  * End using dialog functions.
578  */
579 void
580 end_dialog(void)
581 {
582     if (dialog_state.screen_initialized) {
583         dialog_state.screen_initialized = FALSE;
584         mouse_close();
585         (void) endwin();
586         (void) fflush(stdout);
587     }
588 }
589
590 #define ESCAPE_LEN 3
591 #define isOurEscape(p) (((p)[0] == '\\') && ((p)[1] == 'Z') && ((p)[2] != 0))
592
593 int
594 dlg_count_real_columns(const char *text)
595 {
596     int result = 0;
597     if (*text) {
598         result = dlg_count_columns(text);
599         if (result && dialog_vars.colors) {
600             int hidden = 0;
601             while (*text) {
602                 if (dialog_vars.colors && isOurEscape(text)) {
603                     hidden += ESCAPE_LEN;
604                     text += ESCAPE_LEN;
605                 } else {
606                     ++text;
607                 }
608             }
609             result -= hidden;
610         }
611     }
612     return result;
613 }
614
615 static int
616 centered(int width, const char *string)
617 {
618     int need = dlg_count_real_columns(string);
619     int left;
620
621     left = (width - need) / 2 - 1;
622     if (left < 0)
623         left = 0;
624     return left;
625 }
626
627 #ifdef USE_WIDE_CURSES
628 static bool
629 is_combining(const char *txt, int *combined)
630 {
631     bool result = FALSE;
632
633     if (*combined == 0) {
634         if (UCH(*txt) >= 128) {
635             wchar_t wch;
636             mbstate_t state;
637             size_t given = strlen(txt);
638             size_t len;
639
640             memset(&state, 0, sizeof(state));
641             len = mbrtowc(&wch, txt, given, &state);
642             if ((int) len > 0 && wcwidth(wch) == 0) {
643                 *combined = (int) len - 1;
644                 result = TRUE;
645             }
646         }
647     } else {
648         result = TRUE;
649         *combined -= 1;
650     }
651     return result;
652 }
653 #endif
654
655 /*
656  * Print the name (tag) or text from a DIALOG_LISTITEM, highlighting the
657  * first character if selected.
658  */
659 void
660 dlg_print_listitem(WINDOW *win,
661                    const char *text,
662                    int climit,
663                    bool first,
664                    int selected)
665 {
666     chtype attr = A_NORMAL;
667     int limit;
668     const int *cols;
669     chtype attrs[4];
670
671     if (text == 0)
672         text = "";
673
674     if (first) {
675         const int *indx = dlg_index_wchars(text);
676         attrs[3] = tag_key_selected_attr;
677         attrs[2] = tag_key_attr;
678         attrs[1] = tag_selected_attr;
679         attrs[0] = tag_attr;
680
681         dlg_attrset(win, selected ? attrs[3] : attrs[2]);
682         (void) waddnstr(win, text, indx[1]);
683
684         if ((int) strlen(text) > indx[1]) {
685             limit = dlg_limit_columns(text, climit, 1);
686             if (limit > 1) {
687                 dlg_attrset(win, selected ? attrs[1] : attrs[0]);
688                 (void) waddnstr(win,
689                                 text + indx[1],
690                                 indx[limit] - indx[1]);
691             }
692         }
693     } else {
694         attrs[1] = item_selected_attr;
695         attrs[0] = item_attr;
696
697         cols = dlg_index_columns(text);
698         limit = dlg_limit_columns(text, climit, 0);
699
700         if (limit > 0) {
701             dlg_attrset(win, selected ? attrs[1] : attrs[0]);
702             dlg_print_text(win, text, cols[limit], &attr);
703         }
704     }
705 }
706
707 /*
708  * Print up to 'cols' columns from 'text', optionally rendering our escape
709  * sequence for attributes and color.
710  */
711 void
712 dlg_print_text(WINDOW *win, const char *txt, int cols, chtype *attr)
713 {
714     int y_origin, x_origin;
715     int y_before, x_before = 0;
716     int y_after, x_after;
717     int tabbed = 0;
718     bool thisTab;
719     bool ended = FALSE;
720     chtype useattr;
721 #ifdef USE_WIDE_CURSES
722     int combined = 0;
723 #endif
724
725     if (dialog_state.text_only) {
726         y_origin = y_after = 0;
727         x_origin = x_after = 0;
728     } else {
729         getyx(win, y_origin, x_origin);
730     }
731     while (cols > 0 && (*txt != '\0')) {
732         if (dialog_vars.colors) {
733             while (isOurEscape(txt)) {
734                 int code;
735
736                 txt += 2;
737                 switch (code = CharOf(*txt)) {
738 #ifdef HAVE_COLOR
739                 case '0':
740                 case '1':
741                 case '2':
742                 case '3':
743                 case '4':
744                 case '5':
745                 case '6':
746                 case '7':
747                     *attr &= ~A_COLOR;
748                     *attr |= define_color(win, code - '0');
749                     break;
750 #endif
751                 case 'B':
752                     *attr &= ~A_BOLD;
753                     break;
754                 case 'b':
755                     *attr |= A_BOLD;
756                     break;
757                 case 'R':
758                     *attr &= ~A_REVERSE;
759                     break;
760                 case 'r':
761                     *attr |= A_REVERSE;
762                     break;
763                 case 'U':
764                     *attr &= ~A_UNDERLINE;
765                     break;
766                 case 'u':
767                     *attr |= A_UNDERLINE;
768                     break;
769                 case 'n':
770                     *attr = A_NORMAL;
771                     break;
772                 }
773                 ++txt;
774             }
775         }
776         if (ended || *txt == '\n' || *txt == '\0')
777             break;
778         useattr = (*attr) & A_ATTRIBUTES;
779 #ifdef HAVE_COLOR
780         /*
781          * Prevent this from making text invisible when the foreground and
782          * background colors happen to be the same, and there's no bold
783          * attribute.
784          */
785         if ((useattr & A_COLOR) != 0 && (useattr & A_BOLD) == 0) {
786             short pair = (short) PAIR_NUMBER(useattr);
787             short fg, bg;
788             if (pair_content(pair, &fg, &bg) != ERR
789                 && fg == bg) {
790                 useattr &= ~A_COLOR;
791                 useattr |= dlg_color_pair(fg, ((bg == COLOR_BLACK)
792                                                ? COLOR_WHITE
793                                                : COLOR_BLACK));
794             }
795         }
796 #endif
797         /*
798          * Write the character, using curses to tell exactly how wide it
799          * is.  If it is a tab, discount that, since the caller thinks
800          * tabs are nonprinting, and curses will expand tabs to one or
801          * more blanks.
802          */
803         thisTab = (CharOf(*txt) == TAB);
804         if (dialog_state.text_only) {
805             y_before = y_after;
806             x_before = x_after;
807         } else {
808             if (thisTab) {
809                 getyx(win, y_before, x_before);
810                 (void) y_before;
811             }
812         }
813         if (dialog_state.text_only) {
814             int ch = CharOf(*txt++);
815             if (thisTab) {
816                 while ((x_after++) % 8) {
817                     fputc(' ', dialog_state.output);
818                 }
819             } else {
820                 fputc(ch, dialog_state.output);
821                 x_after++;      /* FIXME: handle meta per locale */
822             }
823         } else {
824             (void) waddch(win, CharOf(*txt++) | useattr);
825             getyx(win, y_after, x_after);
826         }
827         if (thisTab && (y_after == y_origin))
828             tabbed += (x_after - x_before);
829         if ((y_after != y_origin) ||
830             (x_after >= (cols + tabbed + x_origin)
831 #ifdef USE_WIDE_CURSES
832              && !is_combining(txt, &combined)
833 #endif
834             )) {
835             ended = TRUE;
836         }
837     }
838     if (dialog_state.text_only) {
839         fputc('\n', dialog_state.output);
840     }
841 }
842
843 /*
844  * Print one line of the prompt in the window within the limits of the
845  * specified right margin.  The line will end on a word boundary and a pointer
846  * to the start of the next line is returned, or a NULL pointer if the end of
847  * *prompt is reached.
848  */
849 const char *
850 dlg_print_line(WINDOW *win,
851                chtype *attr,
852                const char *prompt,
853                int lm, int rm, int *x)
854 {
855     const char *wrap_ptr;
856     const char *test_ptr;
857     const char *hide_ptr = 0;
858     const int *cols = dlg_index_columns(prompt);
859     const int *indx = dlg_index_wchars(prompt);
860     int wrap_inx = 0;
861     int test_inx = 0;
862     int cur_x = lm;
863     int hidden = 0;
864     int limit = dlg_count_wchars(prompt);
865     int n;
866     int tabbed = 0;
867
868     *x = 1;
869
870     /*
871      * Set *test_ptr to the end of the line or the right margin (rm), whichever
872      * is less, and set wrap_ptr to the end of the last word in the line.
873      */
874     for (n = 0; n < limit; ++n) {
875         int ch = *(test_ptr = prompt + indx[test_inx]);
876         if (ch == '\n' || ch == '\0' || cur_x >= (rm + hidden))
877             break;
878         if (ch == TAB && n == 0) {
879             tabbed = 8;         /* workaround for leading tabs */
880         } else if (isblank(UCH(ch))
881                    && n != 0
882                    && !isblank(UCH(prompt[indx[n - 1]]))) {
883             wrap_inx = n;
884             *x = cur_x;
885         } else if (dialog_vars.colors && isOurEscape(test_ptr)) {
886             hide_ptr = test_ptr;
887             hidden += ESCAPE_LEN;
888             n += (ESCAPE_LEN - 1);
889         }
890         cur_x = lm + tabbed + cols[n + 1];
891         if (cur_x > (rm + hidden))
892             break;
893         test_inx = n + 1;
894     }
895
896     /*
897      * If the line doesn't reach the right margin in the middle of a word, then
898      * we don't have to wrap it at the end of the previous word.
899      */
900     test_ptr = prompt + indx[test_inx];
901     if (*test_ptr == '\n' || isblank(UCH(*test_ptr)) || *test_ptr == '\0') {
902         wrap_inx = test_inx;
903         while (wrap_inx > 0 && isblank(UCH(prompt[indx[wrap_inx - 1]]))) {
904             wrap_inx--;
905         }
906         *x = lm + indx[wrap_inx];
907     } else if (*x == 1 && cur_x >= rm) {
908         /*
909          * If the line has no spaces, then wrap it anyway at the right margin
910          */
911         *x = rm;
912         wrap_inx = test_inx;
913     }
914     wrap_ptr = prompt + indx[wrap_inx];
915 #ifdef USE_WIDE_CURSES
916     if (UCH(*wrap_ptr) >= 128) {
917         int combined = 0;
918         while (is_combining(wrap_ptr, &combined)) {
919             ++wrap_ptr;
920         }
921     }
922 #endif
923
924     /*
925      * If we found hidden text past the last point that we will display,
926      * discount that from the displayed length.
927      */
928     if ((hide_ptr != 0) && (hide_ptr >= wrap_ptr)) {
929         hidden -= ESCAPE_LEN;
930         test_ptr = wrap_ptr;
931         while (test_ptr < wrap_ptr) {
932             if (dialog_vars.colors && isOurEscape(test_ptr)) {
933                 hidden -= ESCAPE_LEN;
934                 test_ptr += ESCAPE_LEN;
935             } else {
936                 ++test_ptr;
937             }
938         }
939     }
940
941     /*
942      * Print the line if we have a window pointer.  Otherwise this routine
943      * is just being called for sizing the window.
944      */
945     if (dialog_state.text_only || win) {
946         dlg_print_text(win, prompt, (cols[wrap_inx] - hidden), attr);
947     }
948
949     /* *x tells the calling function how long the line was */
950     if (*x == 1) {
951         *x = rm;
952     }
953
954     *x -= hidden;
955
956     /* Find the start of the next line and return a pointer to it */
957     test_ptr = wrap_ptr;
958     while (isblank(UCH(*test_ptr)))
959         test_ptr++;
960     if (*test_ptr == '\n')
961         test_ptr++;
962     dlg_finish_string(prompt);
963     return (test_ptr);
964 }
965
966 static void
967 justify_text(WINDOW *win,
968              const char *prompt,
969              int limit_y,
970              int limit_x,
971              int *high, int *wide)
972 {
973     chtype attr = A_NORMAL;
974     int x = (2 * MARGIN);
975     int y = MARGIN;
976     int max_x = 2;
977     int lm = (2 * MARGIN);      /* left margin (box-border plus a space) */
978     int rm = limit_x;           /* right margin */
979     int bm = limit_y;           /* bottom margin */
980     int last_y = 0, last_x = 0;
981
982     dialog_state.text_height = 0;
983     dialog_state.text_width = 0;
984     if (dialog_state.text_only || win) {
985         rm -= (2 * MARGIN);
986         bm -= (2 * MARGIN);
987     }
988     if (prompt == 0)
989         prompt = "";
990
991     if (win != 0)
992         getyx(win, last_y, last_x);
993     while (y <= bm && *prompt) {
994         x = lm;
995
996         if (*prompt == '\n') {
997             while (*prompt == '\n' && y < bm) {
998                 if (*(prompt + 1) != '\0') {
999                     ++y;
1000                     if (win != 0)
1001                         (void) wmove(win, y, lm);
1002                 }
1003                 prompt++;
1004             }
1005         } else if (win != 0)
1006             (void) wmove(win, y, lm);
1007
1008         if (*prompt) {
1009             prompt = dlg_print_line(win, &attr, prompt, lm, rm, &x);
1010             if (win != 0)
1011                 getyx(win, last_y, last_x);
1012         }
1013         if (*prompt) {
1014             ++y;
1015             if (win != 0)
1016                 (void) wmove(win, y, lm);
1017         }
1018         max_x = MAX(max_x, x);
1019     }
1020     /* Move back to the last position after drawing prompt, for msgbox. */
1021     if (win != 0)
1022         (void) wmove(win, last_y, last_x);
1023
1024     /* Set the final height and width for the calling function */
1025     if (high != 0)
1026         *high = y;
1027     if (wide != 0)
1028         *wide = max_x;
1029 }
1030
1031 /*
1032  * Print a string of text in a window, automatically wrap around to the next
1033  * line if the string is too long to fit on one line.  Note that the string may
1034  * contain embedded newlines.
1035  */
1036 void
1037 dlg_print_autowrap(WINDOW *win, const char *prompt, int height, int width)
1038 {
1039     justify_text(win, prompt,
1040                  height,
1041                  width,
1042                  (int *) 0, (int *) 0);
1043 }
1044
1045 /*
1046  * Display the message in a scrollable window.  Actually the way it works is
1047  * that we create a "tall" window of the proper width, let the text wrap within
1048  * that, and copy a slice of the result to the dialog.
1049  *
1050  * It works for ncurses.  Other curses implementations show only blanks (Tru64)
1051  * or garbage (NetBSD).
1052  */
1053 int
1054 dlg_print_scrolled(WINDOW *win,
1055                    const char *prompt,
1056                    int offset,
1057                    int height,
1058                    int width,
1059                    int pauseopt)
1060 {
1061     int oldy, oldx;
1062     int last = 0;
1063
1064     (void) pauseopt;            /* used only for ncurses */
1065
1066     getyx(win, oldy, oldx);
1067 #ifdef NCURSES_VERSION
1068     if (pauseopt) {
1069         int wide = width - (2 * MARGIN);
1070         int high = LINES;
1071         int y, x;
1072         int len;
1073         int percent;
1074         WINDOW *dummy;
1075         char buffer[5];
1076
1077 #if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH >= 20040417
1078         /*
1079          * If we're not limited by the screensize, allow text to possibly be
1080          * one character per line.
1081          */
1082         if ((len = dlg_count_columns(prompt)) > high)
1083             high = len;
1084 #endif
1085         dummy = newwin(high, width, 0, 0);
1086         if (dummy == 0) {
1087             dlg_attrset(win, dialog_attr);
1088             dlg_print_autowrap(win, prompt, height + 1 + (3 * MARGIN), width);
1089             last = 0;
1090         } else {
1091             wbkgdset(dummy, dialog_attr | ' ');
1092             dlg_attrset(dummy, dialog_attr);
1093             werase(dummy);
1094             dlg_print_autowrap(dummy, prompt, high, width);
1095             getyx(dummy, y, x);
1096             (void) x;
1097
1098             copywin(dummy,      /* srcwin */
1099                     win,        /* dstwin */
1100                     offset + MARGIN,    /* sminrow */
1101                     MARGIN,     /* smincol */
1102                     MARGIN,     /* dminrow */
1103                     MARGIN,     /* dmincol */
1104                     height,     /* dmaxrow */
1105                     wide,       /* dmaxcol */
1106                     FALSE);
1107
1108             delwin(dummy);
1109
1110             /* if the text is incomplete, or we have scrolled, show the percentage */
1111             if (y > 0 && wide > 4) {
1112                 percent = (int) ((height + offset) * 100.0 / y);
1113                 if (percent < 0)
1114                     percent = 0;
1115                 if (percent > 100)
1116                     percent = 100;
1117                 if (offset != 0 || percent != 100) {
1118                     dlg_attrset(win, position_indicator_attr);
1119                     (void) wmove(win, MARGIN + height, wide - 4);
1120                     (void) sprintf(buffer, "%d%%", percent);
1121                     (void) waddstr(win, buffer);
1122                     if ((len = (int) strlen(buffer)) < 4) {
1123                         dlg_attrset(win, border_attr);
1124                         whline(win, dlg_boxchar(ACS_HLINE), 4 - len);
1125                     }
1126                 }
1127             }
1128             last = (y - height);
1129         }
1130     } else
1131 #endif
1132     {
1133         (void) offset;
1134         dlg_attrset(win, dialog_attr);
1135         dlg_print_autowrap(win, prompt, height + 1 + (3 * MARGIN), width);
1136         last = 0;
1137     }
1138     wmove(win, oldy, oldx);
1139     return last;
1140 }
1141
1142 int
1143 dlg_check_scrolled(int key, int last, int page, bool * show, int *offset)
1144 {
1145     int code = 0;
1146
1147     *show = FALSE;
1148
1149     switch (key) {
1150     case DLGK_PAGE_FIRST:
1151         if (*offset > 0) {
1152             *offset = 0;
1153             *show = TRUE;
1154         }
1155         break;
1156     case DLGK_PAGE_LAST:
1157         if (*offset < last) {
1158             *offset = last;
1159             *show = TRUE;
1160         }
1161         break;
1162     case DLGK_GRID_UP:
1163         if (*offset > 0) {
1164             --(*offset);
1165             *show = TRUE;
1166         }
1167         break;
1168     case DLGK_GRID_DOWN:
1169         if (*offset < last) {
1170             ++(*offset);
1171             *show = TRUE;
1172         }
1173         break;
1174     case DLGK_PAGE_PREV:
1175         if (*offset > 0) {
1176             *offset -= page;
1177             if (*offset < 0)
1178                 *offset = 0;
1179             *show = TRUE;
1180         }
1181         break;
1182     case DLGK_PAGE_NEXT:
1183         if (*offset < last) {
1184             *offset += page;
1185             if (*offset > last)
1186                 *offset = last;
1187             *show = TRUE;
1188         }
1189         break;
1190     default:
1191         code = -1;
1192         break;
1193     }
1194     return code;
1195 }
1196
1197 /*
1198  * Calculate the window size for preformatted text.  This will calculate box
1199  * dimensions that are at or close to the specified aspect ratio for the prompt
1200  * string with all spaces and newlines preserved and additional newlines added
1201  * as necessary.
1202  */
1203 static void
1204 auto_size_preformatted(const char *prompt, int *height, int *width)
1205 {
1206     int high = 0, wide = 0;
1207     float car;                  /* Calculated Aspect Ratio */
1208     float diff;
1209     int max_y = SLINES - 1;
1210     int max_x = SCOLS - 2;
1211     int max_width = max_x;
1212     int ar = dialog_state.aspect_ratio;
1213
1214     /* Get the initial dimensions */
1215     justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide);
1216     car = (float) (wide / high);
1217
1218     /*
1219      * If the aspect ratio is greater than it should be, then decrease the
1220      * width proportionately.
1221      */
1222     if (car > ar) {
1223         diff = car / (float) ar;
1224         max_x = (int) ((float) wide / diff + 4);
1225         justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide);
1226         car = (float) wide / (float) high;
1227     }
1228
1229     /*
1230      * If the aspect ratio is too small after decreasing the width, then
1231      * incrementally increase the width until the aspect ratio is equal to or
1232      * greater than the specified aspect ratio.
1233      */
1234     while (car < ar && max_x < max_width) {
1235         max_x += 4;
1236         justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide);
1237         car = (float) (wide / high);
1238     }
1239
1240     *height = high;
1241     *width = wide;
1242 }
1243
1244 /*
1245  * Find the length of the longest "word" in the given string.  By setting the
1246  * widget width at least this long, we can avoid splitting a word on the
1247  * margin.
1248  */
1249 static int
1250 longest_word(const char *string)
1251 {
1252     int length, result = 0;
1253
1254     while (*string != '\0') {
1255         length = 0;
1256         while (*string != '\0' && !isspace(UCH(*string))) {
1257             length++;
1258             string++;
1259         }
1260         result = MAX(result, length);
1261         if (*string != '\0')
1262             string++;
1263     }
1264     return result;
1265 }
1266
1267 /*
1268  * if (height or width == -1) Maximize()
1269  * if (height or width == 0), justify and return actual limits.
1270  */
1271 static void
1272 real_auto_size(const char *title,
1273                const char *prompt,
1274                int *height, int *width,
1275                int boxlines, int mincols)
1276 {
1277     int x = (dialog_vars.begin_set ? dialog_vars.begin_x : 2);
1278     int y = (dialog_vars.begin_set ? dialog_vars.begin_y : 1);
1279     int title_length = title ? dlg_count_columns(title) : 0;
1280     int high;
1281     int wide;
1282     int save_high = *height;
1283     int save_wide = *width;
1284     int max_high;
1285     int max_wide;
1286
1287     if (prompt == 0) {
1288         if (*height == 0)
1289             *height = -1;
1290         if (*width == 0)
1291             *width = -1;
1292     }
1293
1294     max_high = (*height < 0);
1295     max_wide = (*width < 0);
1296
1297     if (*height > 0) {
1298         high = *height;
1299     } else {
1300         high = SLINES - y;
1301     }
1302
1303     if (*width <= 0) {
1304         if (prompt != 0) {
1305             wide = MAX(title_length, mincols);
1306             if (strchr(prompt, '\n') == 0) {
1307                 double val = (dialog_state.aspect_ratio *
1308                               dlg_count_real_columns(prompt));
1309                 double xxx = sqrt(val);
1310                 int tmp = (int) xxx;
1311                 wide = MAX(wide, tmp);
1312                 wide = MAX(wide, longest_word(prompt));
1313                 justify_text((WINDOW *) 0, prompt, high, wide, height, width);
1314             } else {
1315                 auto_size_preformatted(prompt, height, width);
1316             }
1317         } else {
1318             wide = SCOLS - x;
1319             justify_text((WINDOW *) 0, prompt, high, wide, height, width);
1320         }
1321     }
1322
1323     if (*width < title_length) {
1324         justify_text((WINDOW *) 0, prompt, high, title_length, height, width);
1325         *width = title_length;
1326     }
1327
1328     dialog_state.text_height = *height;
1329     dialog_state.text_width = *width;
1330
1331     if (*width < mincols && save_wide == 0)
1332         *width = mincols;
1333     if (prompt != 0) {
1334         *width += ((2 * MARGIN) + SHADOW_COLS);
1335         *height += boxlines + (2 * MARGIN);
1336     }
1337
1338     if (save_high > 0)
1339         *height = save_high;
1340     if (save_wide > 0)
1341         *width = save_wide;
1342
1343     if (max_high)
1344         *height = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0);
1345     if (max_wide)
1346         *width = SCOLS - (dialog_vars.begin_set ? dialog_vars.begin_x : 0);
1347 }
1348
1349 /* End of real_auto_size() */
1350
1351 void
1352 dlg_auto_size(const char *title,
1353               const char *prompt,
1354               int *height,
1355               int *width,
1356               int boxlines,
1357               int mincols)
1358 {
1359     DLG_TRACE(("# dlg_auto_size(%d,%d) limits %d,%d\n",
1360                *height, *width,
1361                boxlines, mincols));
1362
1363     real_auto_size(title, prompt, height, width, boxlines, mincols);
1364
1365     if (*width > SCOLS) {
1366         (*height)++;
1367         *width = SCOLS;
1368     }
1369
1370     if (*height > SLINES) {
1371         *height = SLINES;
1372     }
1373     DLG_TRACE(("# ...dlg_auto_size(%d,%d) also %d,%d\n",
1374                *height, *width,
1375                dialog_state.text_height, dialog_state.text_width));
1376 }
1377
1378 /*
1379  * if (height or width == -1) Maximize()
1380  * if (height or width == 0)
1381  *    height=MIN(SLINES, num.lines in fd+n);
1382  *    width=MIN(SCOLS, MAX(longer line+n, mincols));
1383  */
1384 void
1385 dlg_auto_sizefile(const char *title,
1386                   const char *file,
1387                   int *height,
1388                   int *width,
1389                   int boxlines,
1390                   int mincols)
1391 {
1392     int count = 0;
1393     int len = title ? dlg_count_columns(title) : 0;
1394     int nc = 4;
1395     int numlines = 2;
1396     long offset;
1397     int ch;
1398     FILE *fd;
1399
1400     /* Open input file for reading */
1401     if ((fd = fopen(file, "rb")) == NULL)
1402         dlg_exiterr("dlg_auto_sizefile: Cannot open input file %s", file);
1403
1404     if ((*height == -1) || (*width == -1)) {
1405         *height = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0);
1406         *width = SCOLS - (dialog_vars.begin_set ? dialog_vars.begin_x : 0);
1407     }
1408     if ((*height != 0) && (*width != 0)) {
1409         (void) fclose(fd);
1410         if (*width > SCOLS)
1411             *width = SCOLS;
1412         if (*height > SLINES)
1413             *height = SLINES;
1414         return;
1415     }
1416
1417     while (!feof(fd)) {
1418         if (ferror(fd))
1419             break;
1420         offset = 0;
1421         while (((ch = getc(fd)) != '\n') && !feof(fd)) {
1422             if ((ch == TAB) && (dialog_vars.tab_correct)) {
1423                 offset += dialog_state.tab_len - (offset % dialog_state.tab_len);
1424             } else {
1425                 offset++;
1426             }
1427         }
1428
1429         if (offset > len)
1430             len = (int) offset;
1431
1432         count++;
1433     }
1434
1435     /* now 'count' has the number of lines of fd and 'len' the max length */
1436
1437     *height = MIN(SLINES, count + numlines + boxlines);
1438     *width = MIN(SCOLS, MAX((len + nc), mincols));
1439     /* here width and height can be maximized if > SCOLS|SLINES because
1440        textbox-like widgets don't put all <file> on the screen.
1441        Msgbox-like widget instead have to put all <text> correctly. */
1442
1443     (void) fclose(fd);
1444 }
1445
1446 /*
1447  * Draw a rectangular box with line drawing characters.
1448  *
1449  * borderchar is used to color the upper/left edges.
1450  *
1451  * boxchar is used to color the right/lower edges.  It also is fill-color used
1452  * for the box contents.
1453  *
1454  * Normally, if you are drawing a scrollable box, use menubox_border_attr for
1455  * boxchar, and menubox_attr for borderchar since the scroll-arrows are drawn
1456  * with menubox_attr at the top, and menubox_border_attr at the bottom.  That
1457  * also (given the default color choices) produces a recessed effect.
1458  *
1459  * If you want a raised effect (and are not going to use the scroll-arrows),
1460  * reverse this choice.
1461  */
1462 void
1463 dlg_draw_box2(WINDOW *win, int y, int x, int height, int width,
1464               chtype boxchar, chtype borderchar, chtype borderchar2)
1465 {
1466     int i, j;
1467     chtype save = dlg_get_attrs(win);
1468
1469     dlg_attrset(win, 0);
1470     for (i = 0; i < height; i++) {
1471         (void) wmove(win, y + i, x);
1472         for (j = 0; j < width; j++)
1473             if (!i && !j)
1474                 (void) waddch(win, borderchar | dlg_boxchar(ACS_ULCORNER));
1475             else if (i == height - 1 && !j)
1476                 (void) waddch(win, borderchar | dlg_boxchar(ACS_LLCORNER));
1477             else if (!i && j == width - 1)
1478                 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_URCORNER));
1479             else if (i == height - 1 && j == width - 1)
1480                 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_LRCORNER));
1481             else if (!i)
1482                 (void) waddch(win, borderchar | dlg_boxchar(ACS_HLINE));
1483             else if (i == height - 1)
1484                 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_HLINE));
1485             else if (!j)
1486                 (void) waddch(win, borderchar | dlg_boxchar(ACS_VLINE));
1487             else if (j == width - 1)
1488                 (void) waddch(win, borderchar2 | dlg_boxchar(ACS_VLINE));
1489             else
1490                 (void) waddch(win, boxchar | ' ');
1491     }
1492     dlg_attrset(win, save);
1493 }
1494
1495 void
1496 dlg_draw_box(WINDOW *win, int y, int x, int height, int width,
1497              chtype boxchar, chtype borderchar)
1498 {
1499     dlg_draw_box2(win, y, x, height, width, boxchar, borderchar, boxchar);
1500 }
1501
1502 static DIALOG_WINDOWS *
1503 find_window(WINDOW *win)
1504 {
1505     DIALOG_WINDOWS *result = 0;
1506     DIALOG_WINDOWS *p;
1507
1508     for (p = dialog_state.all_windows; p != 0; p = p->next) {
1509         if (p->normal == win) {
1510             result = p;
1511             break;
1512         }
1513     }
1514     return result;
1515 }
1516
1517 #ifdef HAVE_COLOR
1518 /*
1519  * If we have wchgat(), use that for updating shadow attributes, to work with
1520  * wide-character data.
1521  */
1522
1523 /*
1524  * Check if the given point is "in" the given window.  If so, return the window
1525  * pointer, otherwise null.
1526  */
1527 static WINDOW *
1528 in_window(WINDOW *win, int y, int x)
1529 {
1530     WINDOW *result = 0;
1531     int y_base = getbegy(win);
1532     int x_base = getbegx(win);
1533     int y_last = getmaxy(win) + y_base;
1534     int x_last = getmaxx(win) + x_base;
1535
1536     if (y >= y_base && y <= y_last && x >= x_base && x <= x_last)
1537         result = win;
1538     return result;
1539 }
1540
1541 static WINDOW *
1542 window_at_cell(DIALOG_WINDOWS * dw, int y, int x)
1543 {
1544     WINDOW *result = 0;
1545     DIALOG_WINDOWS *p;
1546     int y_want = y + getbegy(dw->shadow);
1547     int x_want = x + getbegx(dw->shadow);
1548
1549     for (p = dialog_state.all_windows; p != 0; p = p->next) {
1550         if (dw->normal != p->normal
1551             && dw->shadow != p->normal
1552             && (result = in_window(p->normal, y_want, x_want)) != 0) {
1553             break;
1554         }
1555     }
1556     if (result == 0) {
1557         result = stdscr;
1558     }
1559     return result;
1560 }
1561
1562 static bool
1563 in_shadow(WINDOW *normal, WINDOW *shadow, int y, int x)
1564 {
1565     bool result = FALSE;
1566     int ybase = getbegy(normal);
1567     int ylast = getmaxy(normal) + ybase;
1568     int xbase = getbegx(normal);
1569     int xlast = getmaxx(normal) + xbase;
1570
1571     y += getbegy(shadow);
1572     x += getbegx(shadow);
1573
1574     if (y >= ybase + SHADOW_ROWS
1575         && y < ylast + SHADOW_ROWS
1576         && x >= xlast
1577         && x < xlast + SHADOW_COLS) {
1578         /* in the right-side */
1579         result = TRUE;
1580     } else if (y >= ylast
1581                && y < ylast + SHADOW_ROWS
1582                && x >= ybase + SHADOW_COLS
1583                && x < ylast + SHADOW_COLS) {
1584         /* check the bottom */
1585         result = TRUE;
1586     }
1587
1588     return result;
1589 }
1590
1591 /*
1592  * When erasing a shadow, check each cell to make sure that it is not part of
1593  * another box's shadow.  This is a little complicated since most shadows are
1594  * merged onto stdscr.
1595  */
1596 static bool
1597 last_shadow(DIALOG_WINDOWS * dw, int y, int x)
1598 {
1599     DIALOG_WINDOWS *p;
1600     bool result = TRUE;
1601
1602     for (p = dialog_state.all_windows; p != 0; p = p->next) {
1603         if (p->normal != dw->normal
1604             && in_shadow(p->normal, dw->shadow, y, x)) {
1605             result = FALSE;
1606             break;
1607         }
1608     }
1609     return result;
1610 }
1611
1612 static void
1613 repaint_cell(DIALOG_WINDOWS * dw, bool draw, int y, int x)
1614 {
1615     WINDOW *win = dw->shadow;
1616     WINDOW *cellwin;
1617     int y2, x2;
1618
1619     if ((cellwin = window_at_cell(dw, y, x)) != 0
1620         && (draw || last_shadow(dw, y, x))
1621         && (y2 = (y + getbegy(win) - getbegy(cellwin))) >= 0
1622         && (x2 = (x + getbegx(win) - getbegx(cellwin))) >= 0
1623         && wmove(cellwin, y2, x2) != ERR) {
1624         chtype the_cell = dlg_get_attrs(cellwin);
1625         chtype the_attr = (draw ? shadow_attr : the_cell);
1626
1627         if (winch(cellwin) & A_ALTCHARSET) {
1628             the_attr |= A_ALTCHARSET;
1629         }
1630 #if USE_WCHGAT
1631         wchgat(cellwin, 1,
1632                the_attr & (chtype) (~A_COLOR),
1633                (short) PAIR_NUMBER(the_attr),
1634                NULL);
1635 #else
1636         {
1637             chtype the_char = ((winch(cellwin) & A_CHARTEXT) | the_attr);
1638             (void) waddch(cellwin, the_char);
1639         }
1640 #endif
1641         wnoutrefresh(cellwin);
1642     }
1643 }
1644
1645 #define RepaintCell(dw, draw, y, x) repaint_cell(dw, draw, y, x)
1646
1647 static void
1648 repaint_shadow(DIALOG_WINDOWS * dw, bool draw, int y, int x, int height, int width)
1649 {
1650     int i, j;
1651
1652     if (UseShadow(dw)) {
1653 #if !USE_WCHGAT
1654         chtype save = dlg_get_attrs(dw->shadow);
1655         dlg_attrset(dw->shadow, draw ? shadow_attr : screen_attr);
1656 #endif
1657         for (i = 0; i < SHADOW_ROWS; ++i) {
1658             for (j = 0; j < width; ++j) {
1659                 RepaintCell(dw, draw, i + y + height, j + x + SHADOW_COLS);
1660             }
1661         }
1662         for (i = 0; i < height; i++) {
1663             for (j = 0; j < SHADOW_COLS; ++j) {
1664                 RepaintCell(dw, draw, i + y + SHADOW_ROWS, j + x + width);
1665             }
1666         }
1667         (void) wnoutrefresh(dw->shadow);
1668 #if !USE_WCHGAT
1669         dlg_attrset(dw->shadow, save);
1670 #endif
1671     }
1672 }
1673
1674 /*
1675  * Draw a shadow on the parent window corresponding to the right- and
1676  * bottom-edge of the child window, to give a 3-dimensional look.
1677  */
1678 static void
1679 draw_childs_shadow(DIALOG_WINDOWS * dw)
1680 {
1681     if (UseShadow(dw)) {
1682         repaint_shadow(dw,
1683                        TRUE,
1684                        getbegy(dw->normal) - getbegy(dw->shadow),
1685                        getbegx(dw->normal) - getbegx(dw->shadow),
1686                        getmaxy(dw->normal),
1687                        getmaxx(dw->normal));
1688     }
1689 }
1690
1691 /*
1692  * Erase a shadow on the parent window corresponding to the right- and
1693  * bottom-edge of the child window.
1694  */
1695 static void
1696 erase_childs_shadow(DIALOG_WINDOWS * dw)
1697 {
1698     if (UseShadow(dw)) {
1699         repaint_shadow(dw,
1700                        FALSE,
1701                        getbegy(dw->normal) - getbegy(dw->shadow),
1702                        getbegx(dw->normal) - getbegx(dw->shadow),
1703                        getmaxy(dw->normal),
1704                        getmaxx(dw->normal));
1705     }
1706 }
1707
1708 /*
1709  * Draw shadows along the right and bottom edge to give a more 3D look
1710  * to the boxes.
1711  */
1712 void
1713 dlg_draw_shadow(WINDOW *win, int y, int x, int height, int width)
1714 {
1715     repaint_shadow(find_window(win), TRUE, y, x, height, width);
1716 }
1717 #endif /* HAVE_COLOR */
1718
1719 /*
1720  * Allow shell scripts to remap the exit codes so they can distinguish ESC
1721  * from ERROR.
1722  */
1723 void
1724 dlg_exit(int code)
1725 {
1726     /* *INDENT-OFF* */
1727     static const struct {
1728         int code;
1729         const char *name;
1730     } table[] = {
1731         { DLG_EXIT_CANCEL,      "DIALOG_CANCEL" },
1732         { DLG_EXIT_ERROR,       "DIALOG_ERROR" },
1733         { DLG_EXIT_ESC,         "DIALOG_ESC" },
1734         { DLG_EXIT_EXTRA,       "DIALOG_EXTRA" },
1735         { DLG_EXIT_HELP,        "DIALOG_HELP" },
1736         { DLG_EXIT_OK,          "DIALOG_OK" },
1737         { DLG_EXIT_ITEM_HELP,   "DIALOG_ITEM_HELP" },
1738     };
1739     /* *INDENT-ON* */
1740
1741     unsigned n;
1742     char *name;
1743     char *temp;
1744     long value;
1745     bool overridden = FALSE;
1746
1747   retry:
1748     for (n = 0; n < sizeof(table) / sizeof(table[0]); n++) {
1749         if (table[n].code == code) {
1750             if ((name = getenv(table[n].name)) != 0) {
1751                 value = strtol(name, &temp, 0);
1752                 if (temp != 0 && temp != name && *temp == '\0') {
1753                     code = (int) value;
1754                     overridden = TRUE;
1755                 }
1756             }
1757             break;
1758         }
1759     }
1760
1761     /*
1762      * Prior to 2004/12/19, a widget using --item-help would exit with "OK"
1763      * if the help button were selected.  Now we want to exit with "HELP",
1764      * but allow the environment variable to override.
1765      */
1766     if (code == DLG_EXIT_ITEM_HELP && !overridden) {
1767         code = DLG_EXIT_HELP;
1768         goto retry;
1769     }
1770 #ifdef HAVE_DLG_TRACE
1771     dlg_trace((const char *) 0);        /* close it */
1772 #endif
1773
1774 #ifdef NO_LEAKS
1775     _dlg_inputstr_leaks();
1776 #if defined(NCURSES_VERSION) && defined(HAVE__NC_FREE_AND_EXIT)
1777     _nc_free_and_exit(code);
1778 #endif
1779 #endif
1780
1781     if (dialog_state.input == stdin) {
1782         exit(code);
1783     } else {
1784         /*
1785          * Just in case of using --input-fd option, do not
1786          * call atexit functions of ncurses which may hang.
1787          */
1788         if (dialog_state.input) {
1789             fclose(dialog_state.input);
1790             dialog_state.input = 0;
1791         }
1792         if (dialog_state.pipe_input) {
1793             if (dialog_state.pipe_input != stdin) {
1794                 fclose(dialog_state.pipe_input);
1795                 dialog_state.pipe_input = 0;
1796             }
1797         }
1798         _exit(code);
1799     }
1800 }
1801
1802 /* quit program killing all tailbg */
1803 void
1804 dlg_exiterr(const char *fmt,...)
1805 {
1806     int retval;
1807     va_list ap;
1808
1809     end_dialog();
1810
1811     (void) fputc('\n', stderr);
1812     va_start(ap, fmt);
1813     (void) vfprintf(stderr, fmt, ap);
1814     va_end(ap);
1815     (void) fputc('\n', stderr);
1816
1817     dlg_killall_bg(&retval);
1818
1819     (void) fflush(stderr);
1820     (void) fflush(stdout);
1821     dlg_exit(DLG_EXIT_ERROR);
1822 }
1823
1824 void
1825 dlg_beeping(void)
1826 {
1827     if (dialog_vars.beep_signal) {
1828         (void) beep();
1829         dialog_vars.beep_signal = 0;
1830     }
1831 }
1832
1833 void
1834 dlg_print_size(int height, int width)
1835 {
1836     if (dialog_vars.print_siz) {
1837         fprintf(dialog_state.output, "Size: %d, %d\n", height, width);
1838         DLG_TRACE(("# print size: %dx%d\n", height, width));
1839     }
1840 }
1841
1842 void
1843 dlg_ctl_size(int height, int width)
1844 {
1845     if (dialog_vars.size_err) {
1846         if ((width > COLS) || (height > LINES)) {
1847             dlg_exiterr("Window too big. (height, width) = (%d, %d). Max allowed (%d, %d).",
1848                         height, width, LINES, COLS);
1849         }
1850 #ifdef HAVE_COLOR
1851         else if ((dialog_state.use_shadow)
1852                  && ((width > SCOLS || height > SLINES))) {
1853             if ((width <= COLS) && (height <= LINES)) {
1854                 /* try again, without shadows */
1855                 dialog_state.use_shadow = 0;
1856             } else {
1857                 dlg_exiterr("Window+Shadow too big. (height, width) = (%d, %d). Max allowed (%d, %d).",
1858                             height, width, SLINES, SCOLS);
1859             }
1860         }
1861 #endif
1862     }
1863 }
1864
1865 /*
1866  * If the --tab-correct was not selected, convert tabs to single spaces.
1867  */
1868 void
1869 dlg_tab_correct_str(char *prompt)
1870 {
1871     char *ptr;
1872
1873     if (dialog_vars.tab_correct) {
1874         while ((ptr = strchr(prompt, TAB)) != NULL) {
1875             *ptr = ' ';
1876             prompt = ptr;
1877         }
1878     }
1879 }
1880
1881 void
1882 dlg_calc_listh(int *height, int *list_height, int item_no)
1883 {
1884     /* calculate new height and list_height */
1885     int rows = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0);
1886     if (rows - (*height) > 0) {
1887         if (rows - (*height) > item_no)
1888             *list_height = item_no;
1889         else
1890             *list_height = rows - (*height);
1891     }
1892     (*height) += (*list_height);
1893 }
1894
1895 /* obsolete */
1896 int
1897 dlg_calc_listw(int item_no, char **items, int group)
1898 {
1899     int n, i, len1 = 0, len2 = 0;
1900     for (i = 0; i < (item_no * group); i += group) {
1901         if ((n = dlg_count_columns(items[i])) > len1)
1902             len1 = n;
1903         if ((n = dlg_count_columns(items[i + 1])) > len2)
1904             len2 = n;
1905     }
1906     return len1 + len2;
1907 }
1908
1909 int
1910 dlg_calc_list_width(int item_no, DIALOG_LISTITEM * items)
1911 {
1912     int n, i, len1 = 0, len2 = 0;
1913     int bits = ((dialog_vars.no_tags ? 1 : 0)
1914                 + (dialog_vars.no_items ? 2 : 0));
1915
1916     for (i = 0; i < item_no; ++i) {
1917         switch (bits) {
1918         case 0:
1919             /* FALLTHRU */
1920         case 1:
1921             if ((n = dlg_count_columns(items[i].name)) > len1)
1922                 len1 = n;
1923             if ((n = dlg_count_columns(items[i].text)) > len2)
1924                 len2 = n;
1925             break;
1926         case 2:
1927             /* FALLTHRU */
1928         case 3:
1929             if ((n = dlg_count_columns(items[i].name)) > len1)
1930                 len1 = n;
1931             break;
1932         }
1933     }
1934     return len1 + len2;
1935 }
1936
1937 char *
1938 dlg_strempty(void)
1939 {
1940     static char empty[] = "";
1941     return empty;
1942 }
1943
1944 char *
1945 dlg_strclone(const char *cprompt)
1946 {
1947     char *prompt = 0;
1948     if (cprompt != 0) {
1949         prompt = dlg_malloc(char, strlen(cprompt) + 1);
1950         assert_ptr(prompt, "dlg_strclone");
1951         strcpy(prompt, cprompt);
1952     }
1953     return prompt;
1954 }
1955
1956 chtype
1957 dlg_asciibox(chtype ch)
1958 {
1959     chtype result = 0;
1960
1961     if (ch == ACS_ULCORNER)
1962         result = '+';
1963     else if (ch == ACS_LLCORNER)
1964         result = '+';
1965     else if (ch == ACS_URCORNER)
1966         result = '+';
1967     else if (ch == ACS_LRCORNER)
1968         result = '+';
1969     else if (ch == ACS_HLINE)
1970         result = '-';
1971     else if (ch == ACS_VLINE)
1972         result = '|';
1973     else if (ch == ACS_LTEE)
1974         result = '+';
1975     else if (ch == ACS_RTEE)
1976         result = '+';
1977     else if (ch == ACS_UARROW)
1978         result = '^';
1979     else if (ch == ACS_DARROW)
1980         result = 'v';
1981
1982     return result;
1983 }
1984
1985 chtype
1986 dlg_boxchar(chtype ch)
1987 {
1988     chtype result = dlg_asciibox(ch);
1989
1990     if (result != 0) {
1991         if (dialog_vars.ascii_lines)
1992             ch = result;
1993         else if (dialog_vars.no_lines)
1994             ch = ' ';
1995     }
1996     return ch;
1997 }
1998
1999 int
2000 dlg_box_x_ordinate(int width)
2001 {
2002     int x;
2003
2004     if (dialog_vars.begin_set == 1) {
2005         x = dialog_vars.begin_x;
2006     } else {
2007         /* center dialog box on screen unless --begin-set */
2008         x = (SCOLS - width) / 2;
2009     }
2010     return x;
2011 }
2012
2013 int
2014 dlg_box_y_ordinate(int height)
2015 {
2016     int y;
2017
2018     if (dialog_vars.begin_set == 1) {
2019         y = dialog_vars.begin_y;
2020     } else {
2021         /* center dialog box on screen unless --begin-set */
2022         y = (SLINES - height) / 2;
2023     }
2024     return y;
2025 }
2026
2027 void
2028 dlg_draw_title(WINDOW *win, const char *title)
2029 {
2030     if (title != NULL) {
2031         chtype attr = A_NORMAL;
2032         chtype save = dlg_get_attrs(win);
2033         int x = centered(getmaxx(win), title);
2034
2035         dlg_attrset(win, title_attr);
2036         wmove(win, 0, x);
2037         dlg_print_text(win, title, getmaxx(win) - x, &attr);
2038         dlg_attrset(win, save);
2039         dlg_finish_string(title);
2040     }
2041 }
2042
2043 void
2044 dlg_draw_bottom_box2(WINDOW *win, chtype on_left, chtype on_right, chtype on_inside)
2045 {
2046     int width = getmaxx(win);
2047     int height = getmaxy(win);
2048     int i;
2049
2050     dlg_attrset(win, on_left);
2051     (void) wmove(win, height - 3, 0);
2052     (void) waddch(win, dlg_boxchar(ACS_LTEE));
2053     for (i = 0; i < width - 2; i++)
2054         (void) waddch(win, dlg_boxchar(ACS_HLINE));
2055     dlg_attrset(win, on_right);
2056     (void) waddch(win, dlg_boxchar(ACS_RTEE));
2057     dlg_attrset(win, on_inside);
2058     (void) wmove(win, height - 2, 1);
2059     for (i = 0; i < width - 2; i++)
2060         (void) waddch(win, ' ');
2061 }
2062
2063 void
2064 dlg_draw_bottom_box(WINDOW *win)
2065 {
2066     dlg_draw_bottom_box2(win, border_attr, dialog_attr, dialog_attr);
2067 }
2068
2069 /*
2070  * Remove a window, repainting everything else.  This would be simpler if we
2071  * used the panel library, but that is not _always_ available.
2072  */
2073 void
2074 dlg_del_window(WINDOW *win)
2075 {
2076     DIALOG_WINDOWS *p, *q, *r;
2077
2078     /*
2079      * If --keep-window was set, do not delete/repaint the windows.
2080      */
2081     if (dialog_vars.keep_window)
2082         return;
2083
2084     /* Leave the main window untouched if there are no background windows.
2085      * We do this so the current window will not be cleared on exit, allowing
2086      * things like the infobox demo to run without flicker.
2087      */
2088     if (dialog_state.getc_callbacks != 0) {
2089         touchwin(stdscr);
2090         wnoutrefresh(stdscr);
2091     }
2092
2093     for (p = dialog_state.all_windows, q = r = 0; p != 0; r = p, p = p->next) {
2094         if (p->normal == win) {
2095             q = p;              /* found a match - should be only one */
2096             if (r == 0) {
2097                 dialog_state.all_windows = p->next;
2098             } else {
2099                 r->next = p->next;
2100             }
2101         } else {
2102             if (p->shadow != 0) {
2103                 touchwin(p->shadow);
2104                 wnoutrefresh(p->shadow);
2105             }
2106             touchwin(p->normal);
2107             wnoutrefresh(p->normal);
2108         }
2109     }
2110
2111     if (q) {
2112         if (dialog_state.all_windows != 0)
2113             erase_childs_shadow(q);
2114         del_subwindows(q->normal);
2115         dlg_unregister_window(q->normal);
2116         delwin(q->normal);
2117         free(q);
2118     }
2119     doupdate();
2120 }
2121
2122 /*
2123  * Create a window, optionally with a shadow.
2124  */
2125 WINDOW *
2126 dlg_new_window(int height, int width, int y, int x)
2127 {
2128     return dlg_new_modal_window(stdscr, height, width, y, x);
2129 }
2130
2131 /*
2132  * "Modal" windows differ from normal ones by having a shadow in a window
2133  * separate from the standard screen.
2134  */
2135 WINDOW *
2136 dlg_new_modal_window(WINDOW *parent, int height, int width, int y, int x)
2137 {
2138     WINDOW *win;
2139     DIALOG_WINDOWS *p = dlg_calloc(DIALOG_WINDOWS, 1);
2140
2141     (void) parent;
2142     if (p == 0
2143         || (win = newwin(height, width, y, x)) == 0) {
2144         dlg_exiterr("Can't make new window at (%d,%d), size (%d,%d).\n",
2145                     y, x, height, width);
2146     }
2147     p->next = dialog_state.all_windows;
2148     p->normal = win;
2149     dialog_state.all_windows = p;
2150 #ifdef HAVE_COLOR
2151     if (dialog_state.use_shadow) {
2152         p->shadow = parent;
2153         draw_childs_shadow(p);
2154     }
2155 #endif
2156
2157     (void) keypad(win, TRUE);
2158     return win;
2159 }
2160
2161 /*
2162  * Move/Resize a window, optionally with a shadow.
2163  */
2164 #ifdef KEY_RESIZE
2165 void
2166 dlg_move_window(WINDOW *win, int height, int width, int y, int x)
2167 {
2168     DIALOG_WINDOWS *p;
2169
2170     if (win != 0) {
2171         dlg_ctl_size(height, width);
2172
2173         if ((p = find_window(win)) != 0) {
2174             (void) wresize(win, height, width);
2175             (void) mvwin(win, y, x);
2176 #ifdef HAVE_COLOR
2177             if (p->shadow != 0) {
2178                 if (dialog_state.use_shadow) {
2179                     (void) mvwin(p->shadow, y + SHADOW_ROWS, x + SHADOW_COLS);
2180                 } else {
2181                     p->shadow = 0;
2182                 }
2183             }
2184 #endif
2185             (void) refresh();
2186
2187 #ifdef HAVE_COLOR
2188             draw_childs_shadow(p);
2189 #endif
2190         }
2191     }
2192 }
2193
2194 /*
2195  * Having just received a KEY_RESIZE, wait a short time to ignore followup
2196  * KEY_RESIZE events.
2197  */
2198 void
2199 dlg_will_resize(WINDOW *win)
2200 {
2201     int n, ch, base;
2202     int caught = 0;
2203
2204     dlg_trace_win(win);
2205     wtimeout(win, 20);
2206     for (n = base = 0; n < base + 10; ++n) {
2207         if ((ch = wgetch(win)) != ERR) {
2208             if (ch == KEY_RESIZE) {
2209                 base = n;
2210                 ++caught;
2211             } else {
2212                 ungetch(ch);
2213                 break;
2214             }
2215         }
2216     }
2217     dlg_trace_msg("# caught %d KEY_RESIZE key%s\n",
2218                   1 + caught,
2219                   caught == 1 ? "" : "s");
2220 }
2221 #endif /* KEY_RESIZE */
2222
2223 WINDOW *
2224 dlg_sub_window(WINDOW *parent, int height, int width, int y, int x)
2225 {
2226     WINDOW *win;
2227
2228     if ((win = subwin(parent, height, width, y, x)) == 0) {
2229         dlg_exiterr("Can't make sub-window at (%d,%d), size (%d,%d).\n",
2230                     y, x, height, width);
2231     }
2232
2233     add_subwindow(parent, win);
2234     (void) keypad(win, TRUE);
2235     return win;
2236 }
2237
2238 /* obsolete */
2239 int
2240 dlg_default_item(char **items, int llen)
2241 {
2242     int result = 0;
2243
2244     if (dialog_vars.default_item != 0) {
2245         int count = 0;
2246         while (*items != 0) {
2247             if (!strcmp(dialog_vars.default_item, *items)) {
2248                 result = count;
2249                 break;
2250             }
2251             items += llen;
2252             count++;
2253         }
2254     }
2255     return result;
2256 }
2257
2258 int
2259 dlg_default_listitem(DIALOG_LISTITEM * items)
2260 {
2261     int result = 0;
2262
2263     if (dialog_vars.default_item != 0) {
2264         int count = 0;
2265         while (items->name != 0) {
2266             if (!strcmp(dialog_vars.default_item, items->name)) {
2267                 result = count;
2268                 break;
2269             }
2270             ++items;
2271             count++;
2272         }
2273     }
2274     return result;
2275 }
2276
2277 /*
2278  * Draw the string for item_help
2279  */
2280 void
2281 dlg_item_help(const char *txt)
2282 {
2283     if (USE_ITEM_HELP(txt)) {
2284         chtype attr = A_NORMAL;
2285         int y, x;
2286
2287         dlg_attrset(stdscr, itemhelp_attr);
2288         (void) wmove(stdscr, LINES - 1, 0);
2289         (void) wclrtoeol(stdscr);
2290         (void) addch(' ');
2291         dlg_print_text(stdscr, txt, COLS - 1, &attr);
2292         if (itemhelp_attr & A_COLOR) {
2293             /* fill the remainder of the line with the window's attributes */
2294             getyx(stdscr, y, x);
2295             (void) y;
2296             while (x < COLS) {
2297                 (void) addch(' ');
2298                 ++x;
2299             }
2300         }
2301         (void) wnoutrefresh(stdscr);
2302     }
2303 }
2304
2305 #ifndef HAVE_STRCASECMP
2306 int
2307 dlg_strcmp(const char *a, const char *b)
2308 {
2309     int ac, bc, cmp;
2310
2311     for (;;) {
2312         ac = UCH(*a++);
2313         bc = UCH(*b++);
2314         if (isalpha(ac) && islower(ac))
2315             ac = _toupper(ac);
2316         if (isalpha(bc) && islower(bc))
2317             bc = _toupper(bc);
2318         cmp = ac - bc;
2319         if (ac == 0 || bc == 0 || cmp != 0)
2320             break;
2321     }
2322     return cmp;
2323 }
2324 #endif
2325
2326 /*
2327  * Returns true if 'dst' points to a blank which follows another blank which
2328  * is not a leading blank on a line.
2329  */
2330 static bool
2331 trim_blank(char *base, char *dst)
2332 {
2333     int count = isblank(UCH(*dst));
2334
2335     while (dst-- != base) {
2336         if (*dst == '\n') {
2337             break;
2338         } else if (isblank(UCH(*dst))) {
2339             count++;
2340         } else {
2341             break;
2342         }
2343     }
2344     return (count > 1);
2345 }
2346
2347 /*
2348  * Change embedded "\n" substrings to '\n' characters and tabs to single
2349  * spaces.  If there are no "\n"s, it will strip all extra spaces, for
2350  * justification.  If it has "\n"'s, it will preserve extra spaces.  If cr_wrap
2351  * is set, it will preserve '\n's.
2352  */
2353 void
2354 dlg_trim_string(char *s)
2355 {
2356     char *base = s;
2357     char *p1;
2358     char *p = s;
2359     int has_newlines = !dialog_vars.no_nl_expand && (strstr(s, "\\n") != 0);
2360
2361     while (*p != '\0') {
2362         if (*p == TAB && !dialog_vars.nocollapse)
2363             *p = ' ';
2364
2365         if (has_newlines) {     /* If prompt contains "\n" strings */
2366             if (*p == '\\' && *(p + 1) == 'n') {
2367                 *s++ = '\n';
2368                 p += 2;
2369                 p1 = p;
2370                 /*
2371                  * Handle end of lines intelligently.  If '\n' follows "\n"
2372                  * then ignore the '\n'.  This eliminates the need to escape
2373                  * the '\n' character (no need to use "\n\").
2374                  */
2375                 while (isblank(UCH(*p1)))
2376                     p1++;
2377                 if (*p1 == '\n')
2378                     p = p1 + 1;
2379             } else if (*p == '\n') {
2380                 if (dialog_vars.cr_wrap)
2381                     *s++ = *p++;
2382                 else {
2383                     /* Replace the '\n' with a space if cr_wrap is not set */
2384                     if (!trim_blank(base, p))
2385                         *s++ = ' ';
2386                     p++;
2387                 }
2388             } else              /* If *p != '\n' */
2389                 *s++ = *p++;
2390         } else if (dialog_vars.trim_whitespace) {
2391             if (isblank(UCH(*p))) {
2392                 if (!isblank(UCH(*(s - 1)))) {
2393                     *s++ = ' ';
2394                     p++;
2395                 } else
2396                     p++;
2397             } else if (*p == '\n') {
2398                 if (dialog_vars.cr_wrap)
2399                     *s++ = *p++;
2400                 else if (!isblank(UCH(*(s - 1)))) {
2401                     /* Strip '\n's if cr_wrap is not set. */
2402                     *s++ = ' ';
2403                     p++;
2404                 } else
2405                     p++;
2406             } else
2407                 *s++ = *p++;
2408         } else {                /* If there are no "\n" strings */
2409             if (isblank(UCH(*p)) && !dialog_vars.nocollapse) {
2410                 if (!trim_blank(base, p))
2411                     *s++ = *p;
2412                 p++;
2413             } else
2414                 *s++ = *p++;
2415         }
2416     }
2417
2418     *s = '\0';
2419 }
2420
2421 void
2422 dlg_set_focus(WINDOW *parent, WINDOW *win)
2423 {
2424     if (win != 0) {
2425         (void) wmove(parent,
2426                      getpary(win) + getcury(win),
2427                      getparx(win) + getcurx(win));
2428         (void) wnoutrefresh(win);
2429         (void) doupdate();
2430     }
2431 }
2432
2433 /*
2434  * Returns the nominal maximum buffer size.
2435  */
2436 int
2437 dlg_max_input(int max_len)
2438 {
2439     if (dialog_vars.max_input != 0 && dialog_vars.max_input < MAX_LEN)
2440         max_len = dialog_vars.max_input;
2441
2442     return max_len;
2443 }
2444
2445 /*
2446  * Free storage used for the result buffer.
2447  */
2448 void
2449 dlg_clr_result(void)
2450 {
2451     if (dialog_vars.input_length) {
2452         dialog_vars.input_length = 0;
2453         if (dialog_vars.input_result)
2454             free(dialog_vars.input_result);
2455     }
2456     dialog_vars.input_result = 0;
2457 }
2458
2459 /*
2460  * Setup a fixed-buffer for the result.
2461  */
2462 char *
2463 dlg_set_result(const char *string)
2464 {
2465     unsigned need = string ? (unsigned) strlen(string) + 1 : 0;
2466
2467     /* inputstr.c needs a fixed buffer */
2468     if (need < MAX_LEN)
2469         need = MAX_LEN;
2470
2471     /*
2472      * If the buffer is not big enough, allocate a new one.
2473      */
2474     if (dialog_vars.input_length != 0
2475         || dialog_vars.input_result == 0
2476         || need > MAX_LEN) {
2477
2478         dlg_clr_result();
2479
2480         dialog_vars.input_length = need;
2481         dialog_vars.input_result = dlg_malloc(char, need);
2482         assert_ptr(dialog_vars.input_result, "dlg_set_result");
2483     }
2484
2485     strcpy(dialog_vars.input_result, string ? string : "");
2486
2487     return dialog_vars.input_result;
2488 }
2489
2490 /*
2491  * Accumulate results in dynamically allocated buffer.
2492  * If input_length is zero, it is a MAX_LEN buffer belonging to the caller.
2493  */
2494 void
2495 dlg_add_result(const char *string)
2496 {
2497     unsigned have = (dialog_vars.input_result
2498                      ? (unsigned) strlen(dialog_vars.input_result)
2499                      : 0);
2500     unsigned want = (unsigned) strlen(string) + 1 + have;
2501
2502     if ((want >= MAX_LEN)
2503         || (dialog_vars.input_length != 0)
2504         || (dialog_vars.input_result == 0)) {
2505
2506         if (dialog_vars.input_length == 0
2507             || dialog_vars.input_result == 0) {
2508
2509             char *save_result = dialog_vars.input_result;
2510
2511             dialog_vars.input_length = want * 2;
2512             dialog_vars.input_result = dlg_malloc(char, dialog_vars.input_length);
2513             assert_ptr(dialog_vars.input_result, "dlg_add_result malloc");
2514             dialog_vars.input_result[0] = '\0';
2515             if (save_result != 0)
2516                 strcpy(dialog_vars.input_result, save_result);
2517         } else if (want >= dialog_vars.input_length) {
2518             dialog_vars.input_length = want * 2;
2519             dialog_vars.input_result = dlg_realloc(char,
2520                                                    dialog_vars.input_length,
2521                                                    dialog_vars.input_result);
2522             assert_ptr(dialog_vars.input_result, "dlg_add_result realloc");
2523         }
2524     }
2525     strcat(dialog_vars.input_result, string);
2526 }
2527
2528 /*
2529  * These are characters that (aside from the quote-delimiter) will have to
2530  * be escaped in a single- or double-quoted string.
2531  */
2532 #define FIX_SINGLE "\n\\"
2533 #define FIX_DOUBLE FIX_SINGLE "[]{}?*;`~#$^&()|<>"
2534
2535 /*
2536  * Returns the quote-delimiter.
2537  */
2538 static const char *
2539 quote_delimiter(void)
2540 {
2541     return dialog_vars.single_quoted ? "'" : "\"";
2542 }
2543
2544 /*
2545  * Returns true if we should quote the given string.
2546  */
2547 static bool
2548 must_quote(char *string)
2549 {
2550     bool code = FALSE;
2551
2552     if (*string != '\0') {
2553         size_t len = strlen(string);
2554         if (strcspn(string, quote_delimiter()) != len)
2555             code = TRUE;
2556         else if (strcspn(string, "\n\t ") != len)
2557             code = TRUE;
2558         else
2559             code = (strcspn(string, FIX_DOUBLE) != len);
2560     } else {
2561         code = TRUE;
2562     }
2563
2564     return code;
2565 }
2566
2567 /*
2568  * Add a quoted string to the result buffer.
2569  */
2570 void
2571 dlg_add_quoted(char *string)
2572 {
2573     char temp[2];
2574     const char *my_quote = quote_delimiter();
2575     const char *must_fix = (dialog_vars.single_quoted
2576                             ? FIX_SINGLE
2577                             : FIX_DOUBLE);
2578
2579     if (must_quote(string)) {
2580         temp[1] = '\0';
2581         dlg_add_result(my_quote);
2582         while (*string != '\0') {
2583             temp[0] = *string++;
2584             if ((strchr) (my_quote, *temp) || (strchr) (must_fix, *temp))
2585                 dlg_add_result("\\");
2586             dlg_add_result(temp);
2587         }
2588         dlg_add_result(my_quote);
2589     } else {
2590         dlg_add_result(string);
2591     }
2592 }
2593
2594 /*
2595  * When adding a result, make that depend on whether "--quoted" is used.
2596  */
2597 void
2598 dlg_add_string(char *string)
2599 {
2600     if (dialog_vars.quoted) {
2601         dlg_add_quoted(string);
2602     } else {
2603         dlg_add_result(string);
2604     }
2605 }
2606
2607 bool
2608 dlg_need_separator(void)
2609 {
2610     bool result = FALSE;
2611
2612     if (dialog_vars.output_separator) {
2613         result = TRUE;
2614     } else if (dialog_vars.input_result && *(dialog_vars.input_result)) {
2615         result = TRUE;
2616     }
2617     return result;
2618 }
2619
2620 void
2621 dlg_add_separator(void)
2622 {
2623     const char *separator = (dialog_vars.separate_output) ? "\n" : " ";
2624
2625     if (dialog_vars.output_separator)
2626         separator = dialog_vars.output_separator;
2627
2628     dlg_add_result(separator);
2629 }
2630
2631 #define HELP_PREFIX             "HELP "
2632
2633 void
2634 dlg_add_help_listitem(int *result, char **tag, DIALOG_LISTITEM * item)
2635 {
2636     dlg_add_result(HELP_PREFIX);
2637     if (USE_ITEM_HELP(item->help)) {
2638         *tag = dialog_vars.help_tags ? item->name : item->help;
2639         *result = DLG_EXIT_ITEM_HELP;
2640     } else {
2641         *tag = item->name;
2642     }
2643 }
2644
2645 void
2646 dlg_add_help_formitem(int *result, char **tag, DIALOG_FORMITEM * item)
2647 {
2648     dlg_add_result(HELP_PREFIX);
2649     if (USE_ITEM_HELP(item->help)) {
2650         *tag = dialog_vars.help_tags ? item->name : item->help;
2651         *result = DLG_EXIT_ITEM_HELP;
2652     } else {
2653         *tag = item->name;
2654     }
2655 }
2656
2657 /*
2658  * Some widgets support only one value of a given variable - save/restore the
2659  * global dialog_vars so we can override it consistently.
2660  */
2661 void
2662 dlg_save_vars(DIALOG_VARS * vars)
2663 {
2664     *vars = dialog_vars;
2665 }
2666
2667 /*
2668  * Most of the data in DIALOG_VARS is normally set by command-line options.
2669  * The input_result member is an exception; it is normally set by the dialog
2670  * library to return result values.
2671  */
2672 void
2673 dlg_restore_vars(DIALOG_VARS * vars)
2674 {
2675     char *save_result = dialog_vars.input_result;
2676     unsigned save_length = dialog_vars.input_length;
2677
2678     dialog_vars = *vars;
2679     dialog_vars.input_result = save_result;
2680     dialog_vars.input_length = save_length;
2681 }
2682
2683 /*
2684  * Called each time a widget is invoked which may do output, increment a count.
2685  */
2686 void
2687 dlg_does_output(void)
2688 {
2689     dialog_state.output_count += 1;
2690 }
2691
2692 /*
2693  * Compatibility for different versions of curses.
2694  */
2695 #if !(defined(HAVE_GETBEGX) && defined(HAVE_GETBEGY))
2696 int
2697 dlg_getbegx(WINDOW *win)
2698 {
2699     int y, x;
2700     getbegyx(win, y, x);
2701     return x;
2702 }
2703 int
2704 dlg_getbegy(WINDOW *win)
2705 {
2706     int y, x;
2707     getbegyx(win, y, x);
2708     return y;
2709 }
2710 #endif
2711
2712 #if !(defined(HAVE_GETCURX) && defined(HAVE_GETCURY))
2713 int
2714 dlg_getcurx(WINDOW *win)
2715 {
2716     int y, x;
2717     getyx(win, y, x);
2718     return x;
2719 }
2720 int
2721 dlg_getcury(WINDOW *win)
2722 {
2723     int y, x;
2724     getyx(win, y, x);
2725     return y;
2726 }
2727 #endif
2728
2729 #if !(defined(HAVE_GETMAXX) && defined(HAVE_GETMAXY))
2730 int
2731 dlg_getmaxx(WINDOW *win)
2732 {
2733     int y, x;
2734     getmaxyx(win, y, x);
2735     return x;
2736 }
2737 int
2738 dlg_getmaxy(WINDOW *win)
2739 {
2740     int y, x;
2741     getmaxyx(win, y, x);
2742     return y;
2743 }
2744 #endif
2745
2746 #if !(defined(HAVE_GETPARX) && defined(HAVE_GETPARY))
2747 int
2748 dlg_getparx(WINDOW *win)
2749 {
2750     int y, x;
2751     getparyx(win, y, x);
2752     return x;
2753 }
2754 int
2755 dlg_getpary(WINDOW *win)
2756 {
2757     int y, x;
2758     getparyx(win, y, x);
2759     return y;
2760 }
2761 #endif
2762
2763 #ifdef NEED_WGETPARENT
2764 WINDOW *
2765 dlg_wgetparent(WINDOW *win)
2766 {
2767 #undef wgetparent
2768     WINDOW *result = 0;
2769     DIALOG_WINDOWS *p;
2770
2771     for (p = dialog_state.all_subwindows; p != 0; p = p->next) {
2772         if (p->shadow == win) {
2773             result = p->normal;
2774             break;
2775         }
2776     }
2777     return result;
2778 }
2779 #endif