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