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