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