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