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