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