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