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