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