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