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