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