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