5 kx /*
5 kx * $Id: util.c,v 1.303 2021/06/15 00:10:11 tom Exp $
5 kx *
5 kx * util.c -- miscellaneous utilities for dialog
5 kx *
5 kx * Copyright 2000-2020,2021 Thomas E. Dickey
5 kx *
5 kx * This program is free software; you can redistribute it and/or modify
5 kx * it under the terms of the GNU Lesser General Public License, version 2.1
5 kx * as published by the Free Software Foundation.
5 kx *
5 kx * This program is distributed in the hope that it will be useful, but
5 kx * WITHOUT ANY WARRANTY; without even the implied warranty of
5 kx * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
5 kx * Lesser General Public License for more details.
5 kx *
5 kx * You should have received a copy of the GNU Lesser General Public
5 kx * License along with this program; if not, write to
5 kx * Free Software Foundation, Inc.
5 kx * 51 Franklin St., Fifth Floor
5 kx * Boston, MA 02110, USA.
5 kx *
5 kx * An earlier version of this program lists as authors
5 kx * Savio Lam (lam836@cs.cuhk.hk)
5 kx */
5 kx
5 kx #include <dialog.h>
5 kx #include <dlg_keys.h>
5 kx #include <dlg_internals.h>
5 kx
5 kx #include <sys/time.h>
5 kx
5 kx #ifdef HAVE_SETLOCALE
5 kx #include <locale.h>
5 kx #endif
5 kx
5 kx #ifdef NEED_WCHAR_H
5 kx #include <wchar.h>
5 kx #endif
5 kx
5 kx #ifdef HAVE_SYS_PARAM_H
5 kx #include <sys/param.h>
5 kx #endif
5 kx
5 kx #if defined(NCURSES_VERSION)
5 kx #define CAN_KEEP_TITE 1
5 kx #elif defined(__NetBSD_Version__) && (__NetBSD_Version__ >= 800000000)
5 kx #define CAN_KEEP_TITE 1
5 kx #else
5 kx #define CAN_KEEP_TITE 0
5 kx #endif
5 kx
5 kx #if CAN_KEEP_TITE
5 kx #if defined(NCURSES_VERSION) && defined(HAVE_NCURSESW_TERM_H)
5 kx #include <ncursesw/term.h>
5 kx #elif defined(NCURSES_VERSION) && defined(HAVE_NCURSES_TERM_H)
5 kx #include <ncurses/term.h>
5 kx #else
5 kx #include <term.h>
5 kx #endif
5 kx #endif
5 kx
5 kx #if defined(HAVE_WCHGAT)
5 kx # if defined(NCURSES_VERSION_PATCH)
5 kx # if NCURSES_VERSION_PATCH >= 20060715
5 kx # define USE_WCHGAT 1
5 kx # else
5 kx # define USE_WCHGAT 0
5 kx # endif
5 kx # else
5 kx # define USE_WCHGAT 1
5 kx # endif
5 kx #else
5 kx # define USE_WCHGAT 0
5 kx #endif
5 kx
5 kx /* globals */
5 kx DIALOG_STATE dialog_state;
5 kx DIALOG_VARS dialog_vars;
5 kx
5 kx #if !(defined(HAVE_WGETPARENT) && defined(HAVE_WINDOW__PARENT))
5 kx #define NEED_WGETPARENT 1
5 kx #else
5 kx #undef NEED_WGETPARENT
5 kx #endif
5 kx
5 kx #define concat(a,b) a##b
5 kx
5 kx #ifdef HAVE_RC_FILE
5 kx #define RC_DATA(name,comment) , #name "_color", comment " color"
5 kx #else
5 kx #define RC_DATA(name,comment) /*nothing */
5 kx #endif
5 kx
5 kx #ifdef HAVE_COLOR
5 kx #include <dlg_colors.h>
5 kx #ifdef HAVE_RC_FILE2
5 kx #define COLOR_DATA(upr) , \
5 kx concat(DLGC_FG_,upr), \
5 kx concat(DLGC_BG_,upr), \
5 kx concat(DLGC_HL_,upr), \
5 kx concat(DLGC_UL_,upr), \
5 kx concat(DLGC_RV_,upr)
5 kx #else /* HAVE_RC_FILE2 */
5 kx #define COLOR_DATA(upr) , \
5 kx concat(DLGC_FG_,upr), \
5 kx concat(DLGC_BG_,upr), \
5 kx concat(DLGC_HL_,upr)
5 kx #endif /* HAVE_RC_FILE2 */
5 kx #else /* HAVE_COLOR */
5 kx #define COLOR_DATA(upr) /*nothing */
5 kx #endif /* HAVE_COLOR */
5 kx
5 kx #define UseShadow(dw) ((dw) != 0 && (dw)->normal != 0 && (dw)->shadow != 0)
5 kx
5 kx /*
5 kx * Table of color and attribute values, default is for mono display.
5 kx * The order matches the DIALOG_ATR() values.
5 kx */
5 kx #define DATA(atr,upr,lwr,cmt) { atr COLOR_DATA(upr) RC_DATA(lwr,cmt) }
5 kx /* *INDENT-OFF* */
5 kx DIALOG_COLORS dlg_color_table[] =
5 kx {
5 kx DATA(A_NORMAL, SCREEN, screen, "Screen"),
5 kx DATA(A_NORMAL, SHADOW, shadow, "Shadow"),
5 kx DATA(A_REVERSE, DIALOG, dialog, "Dialog box"),
5 kx DATA(A_REVERSE, TITLE, title, "Dialog box title"),
5 kx DATA(A_REVERSE, BORDER, border, "Dialog box border"),
5 kx DATA(A_BOLD, BUTTON_ACTIVE, button_active, "Active button"),
5 kx DATA(A_DIM, BUTTON_INACTIVE, button_inactive, "Inactive button"),
5 kx DATA(A_UNDERLINE, BUTTON_KEY_ACTIVE, button_key_active, "Active button key"),
5 kx DATA(A_UNDERLINE, BUTTON_KEY_INACTIVE, button_key_inactive, "Inactive button key"),
5 kx DATA(A_NORMAL, BUTTON_LABEL_ACTIVE, button_label_active, "Active button label"),
5 kx DATA(A_NORMAL, BUTTON_LABEL_INACTIVE, button_label_inactive, "Inactive button label"),
5 kx DATA(A_REVERSE, INPUTBOX, inputbox, "Input box"),
5 kx DATA(A_REVERSE, INPUTBOX_BORDER, inputbox_border, "Input box border"),
5 kx DATA(A_REVERSE, SEARCHBOX, searchbox, "Search box"),
5 kx DATA(A_REVERSE, SEARCHBOX_TITLE, searchbox_title, "Search box title"),
5 kx DATA(A_REVERSE, SEARCHBOX_BORDER, searchbox_border, "Search box border"),
5 kx DATA(A_REVERSE, POSITION_INDICATOR, position_indicator, "File position indicator"),
5 kx DATA(A_REVERSE, MENUBOX, menubox, "Menu box"),
5 kx DATA(A_REVERSE, MENUBOX_BORDER, menubox_border, "Menu box border"),
5 kx DATA(A_REVERSE, ITEM, item, "Item"),
5 kx DATA(A_NORMAL, ITEM_SELECTED, item_selected, "Selected item"),
5 kx DATA(A_REVERSE, TAG, tag, "Tag"),
5 kx DATA(A_REVERSE, TAG_SELECTED, tag_selected, "Selected tag"),
5 kx DATA(A_NORMAL, TAG_KEY, tag_key, "Tag key"),
5 kx DATA(A_BOLD, TAG_KEY_SELECTED, tag_key_selected, "Selected tag key"),
5 kx DATA(A_REVERSE, CHECK, check, "Check box"),
5 kx DATA(A_REVERSE, CHECK_SELECTED, check_selected, "Selected check box"),
5 kx DATA(A_REVERSE, UARROW, uarrow, "Up arrow"),
5 kx DATA(A_REVERSE, DARROW, darrow, "Down arrow"),
5 kx DATA(A_NORMAL, ITEMHELP, itemhelp, "Item help-text"),
5 kx DATA(A_BOLD, FORM_ACTIVE_TEXT, form_active_text, "Active form text"),
5 kx DATA(A_REVERSE, FORM_TEXT, form_text, "Form text"),
5 kx DATA(A_NORMAL, FORM_ITEM_READONLY, form_item_readonly, "Readonly form item"),
5 kx DATA(A_REVERSE, GAUGE, gauge, "Dialog box gauge"),
5 kx DATA(A_REVERSE, BORDER2, border2, "Dialog box border2"),
5 kx DATA(A_REVERSE, INPUTBOX_BORDER2, inputbox_border2, "Input box border2"),
5 kx DATA(A_REVERSE, SEARCHBOX_BORDER2, searchbox_border2, "Search box border2"),
5 kx DATA(A_REVERSE, MENUBOX_BORDER2, menubox_border2, "Menu box border2")
5 kx };
5 kx #undef DATA
5 kx /* *INDENT-ON* */
5 kx
5 kx /*
5 kx * Maintain a list of subwindows so that we can delete them to cleanup.
5 kx * More important, this provides a fallback when wgetparent() is not available.
5 kx */
5 kx static void
5 kx add_subwindow(WINDOW *parent, WINDOW *child)
5 kx {
5 kx DIALOG_WINDOWS *p = dlg_calloc(DIALOG_WINDOWS, 1);
5 kx
5 kx if (p != 0) {
5 kx p->normal = parent;
5 kx p->shadow = child;
5 kx p->getc_timeout = WTIMEOUT_OFF;
5 kx p->next = dialog_state.all_subwindows;
5 kx dialog_state.all_subwindows = p;
5 kx }
5 kx }
5 kx
5 kx static void
5 kx del_subwindows(WINDOW *parent)
5 kx {
5 kx DIALOG_WINDOWS *p = dialog_state.all_subwindows;
5 kx DIALOG_WINDOWS *q = 0;
5 kx DIALOG_WINDOWS *r;
5 kx
5 kx while (p != 0) {
5 kx if (p->normal == parent) {
5 kx delwin(p->shadow);
5 kx r = p->next;
5 kx if (q == 0) {
5 kx dialog_state.all_subwindows = r;
5 kx } else {
5 kx q->next = r;
5 kx }
5 kx free(p);
5 kx p = r;
5 kx } else {
5 kx q = p;
5 kx p = p->next;
5 kx }
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Display background title if it exists ...
5 kx */
5 kx void
5 kx dlg_put_backtitle(void)
5 kx {
5 kx
5 kx if (dialog_vars.backtitle != NULL) {
5 kx chtype attr = A_NORMAL;
5 kx int backwidth = dlg_count_columns(dialog_vars.backtitle);
5 kx int i;
5 kx
5 kx dlg_attrset(stdscr, screen_attr);
5 kx (void) wmove(stdscr, 0, 1);
5 kx dlg_print_text(stdscr, dialog_vars.backtitle, COLS - 2, &attr);
5 kx for (i = 0; i < COLS - backwidth; i++)
5 kx (void) waddch(stdscr, ' ');
5 kx (void) wmove(stdscr, 1, 1);
5 kx for (i = 0; i < COLS - 2; i++)
5 kx (void) waddch(stdscr, dlg_boxchar(ACS_HLINE));
5 kx }
5 kx
5 kx (void) wnoutrefresh(stdscr);
5 kx }
5 kx
5 kx /*
5 kx * Set window to attribute 'attr'. There are more efficient ways to do this,
5 kx * but will not work on older/buggy ncurses versions.
5 kx */
5 kx void
5 kx dlg_attr_clear(WINDOW *win, int height, int width, chtype attr)
5 kx {
5 kx int i, j;
5 kx
5 kx dlg_attrset(win, attr);
5 kx for (i = 0; i < height; i++) {
5 kx (void) wmove(win, i, 0);
5 kx for (j = 0; j < width; j++)
5 kx (void) waddch(win, ' ');
5 kx }
5 kx (void) touchwin(win);
5 kx }
5 kx
5 kx void
5 kx dlg_clear(void)
5 kx {
5 kx dlg_attr_clear(stdscr, LINES, COLS, screen_attr);
5 kx }
5 kx
5 kx #ifdef KEY_RESIZE
5 kx void
5 kx _dlg_resize_cleanup(WINDOW *w)
5 kx {
5 kx dlg_clear();
5 kx dlg_put_backtitle();
5 kx dlg_del_window(w);
5 kx dlg_mouse_free_regions();
5 kx }
5 kx #endif /* KEY_RESIZE */
5 kx
5 kx #define isprivate(s) ((s) != 0 && strstr(s, "\033[?") != 0)
5 kx
5 kx #define TTY_DEVICE "/dev/tty"
5 kx
5 kx /*
5 kx * If $DIALOG_TTY exists, allow the program to try to open the terminal
5 kx * directly when stdout is redirected. By default we require the "--stdout"
5 kx * option to be given, but some scripts were written making use of the
5 kx * behavior of dialog which tried opening the terminal anyway.
5 kx */
5 kx #define dialog_tty() (dlg_getenv_num("DIALOG_TTY", (int *)0) > 0)
5 kx
5 kx /*
5 kx * Open the terminal directly. If one of stdin, stdout or stderr really points
5 kx * to a tty, use it. Otherwise give up and open /dev/tty.
5 kx */
5 kx static int
5 kx open_terminal(char **result, int mode)
5 kx {
5 kx const char *device = TTY_DEVICE;
5 kx if (!isatty(fileno(stderr))
5 kx || (device = ttyname(fileno(stderr))) == 0) {
5 kx if (!isatty(fileno(stdout))
5 kx || (device = ttyname(fileno(stdout))) == 0) {
5 kx if (!isatty(fileno(stdin))
5 kx || (device = ttyname(fileno(stdin))) == 0) {
5 kx device = TTY_DEVICE;
5 kx }
5 kx }
5 kx }
5 kx *result = dlg_strclone(device);
5 kx return open(device, mode);
5 kx }
5 kx
5 kx #if CAN_KEEP_TITE
5 kx static int
5 kx my_putc(int ch)
5 kx {
5 kx char buffer[2];
5 kx int fd = fileno(dialog_state.screen_output);
5 kx
5 kx buffer[0] = (char) ch;
5 kx return (int) write(fd, buffer, (size_t) 1);
5 kx }
5 kx #endif
5 kx
5 kx /*
5 kx * Do some initialization for dialog.
5 kx *
5 kx * 'input' is the real tty input of dialog. Usually it is stdin, but if
5 kx * --input-fd option is used, it may be anything.
5 kx *
5 kx * 'output' is where dialog will send its result. Usually it is stderr, but
5 kx * if --stdout or --output-fd is used, it may be anything. We are concerned
5 kx * mainly with the case where it happens to be the same as stdout.
5 kx */
5 kx void
5 kx init_dialog(FILE *input, FILE *output)
5 kx {
5 kx int fd1, fd2;
5 kx char *device = 0;
5 kx
5 kx setlocale(LC_ALL, "");
5 kx
5 kx dialog_state.output = output;
5 kx if (dialog_state.tab_len == 0)
5 kx dialog_state.tab_len = TAB_LEN;
5 kx if (dialog_state.aspect_ratio == 0)
5 kx dialog_state.aspect_ratio = DEFAULT_ASPECT_RATIO;
5 kx #ifdef HAVE_COLOR
5 kx dialog_state.use_colors = USE_COLORS; /* use colors by default? */
5 kx dialog_state.use_shadow = USE_SHADOW; /* shadow dialog boxes by default? */
5 kx #endif
5 kx
5 kx #ifdef HAVE_RC_FILE
5 kx if (dlg_parse_rc() == -1) /* Read the configuration file */
5 kx dlg_exiterr("init_dialog: dlg_parse_rc");
5 kx #endif
5 kx
5 kx /*
5 kx * Some widgets (such as gauge) may read from the standard input. Pipes
5 kx * only connect stdout/stdin, so there is not much choice. But reading a
5 kx * pipe would get in the way of curses' normal reading stdin for getch.
5 kx *
5 kx * As in the --stdout (see below), reopening the terminal does not always
5 kx * work properly. dialog provides a --pipe-fd option for this purpose. We
5 kx * test that case first (differing fileno's for input/stdin). If the
5 kx * fileno's are equal, but we're not reading from a tty, see if we can open
5 kx * /dev/tty.
5 kx */
5 kx dialog_state.pipe_input = stdin;
5 kx if (fileno(input) != fileno(stdin)) {
5 kx if ((fd1 = dup(fileno(input))) >= 0
5 kx && (fd2 = dup(fileno(stdin))) >= 0) {
5 kx (void) dup2(fileno(input), fileno(stdin));
5 kx dialog_state.pipe_input = fdopen(fd2, "r");
5 kx if (fileno(stdin) != 0) /* some functions may read fd #0 */
5 kx (void) dup2(fileno(stdin), 0);
5 kx } else {
5 kx dlg_exiterr("cannot open tty-input");
5 kx }
5 kx close(fd1);
5 kx } else if (!isatty(fileno(stdin))) {
5 kx if ((fd1 = open_terminal(&device, O_RDONLY)) >= 0) {
5 kx if ((fd2 = dup(fileno(stdin))) >= 0) {
5 kx dialog_state.pipe_input = fdopen(fd2, "r");
5 kx if (freopen(device, "r", stdin) == 0)
5 kx dlg_exiterr("cannot open tty-input");
5 kx if (fileno(stdin) != 0) /* some functions may read fd #0 */
5 kx (void) dup2(fileno(stdin), 0);
5 kx }
5 kx close(fd1);
5 kx }
5 kx free(device);
5 kx }
5 kx
5 kx /*
5 kx * If stdout is not a tty and dialog is called with the --stdout option, we
5 kx * have to provide for a way to write to the screen.
5 kx *
5 kx * The curses library normally writes its output to stdout, leaving stderr
5 kx * free for scripting. Scripts are simpler when stdout is redirected. The
5 kx * newterm function is useful; it allows us to specify where the output
5 kx * goes. Reopening the terminal is not portable since several
5 kx * configurations do not allow this to work properly:
5 kx *
5 kx * a) some getty implementations (and possibly broken tty drivers, e.g., on
5 kx * HPUX 10 and 11) cause stdin to act as if it is still in cooked mode
5 kx * even though results from ioctl's state that it is successfully
5 kx * altered to raw mode. Broken is the proper term.
5 kx *
5 kx * b) the user may not have permissions on the device, e.g., if one su's
5 kx * from the login user to another non-privileged user.
5 kx */
5 kx if (!isatty(fileno(stdout))
5 kx && (fileno(stdout) == fileno(output) || dialog_tty())) {
5 kx if ((fd1 = open_terminal(&device, O_WRONLY)) >= 0
5 kx && (dialog_state.screen_output = fdopen(fd1, "w")) != 0) {
5 kx if (newterm(NULL, dialog_state.screen_output, stdin) == 0) {
5 kx dlg_exiterr("cannot initialize curses");
5 kx }
5 kx free(device);
5 kx } else {
5 kx dlg_exiterr("cannot open tty-output");
5 kx }
5 kx } else {
5 kx dialog_state.screen_output = stdout;
5 kx (void) initscr();
5 kx }
5 kx dlg_keep_tite(dialog_state.screen_output);
5 kx #ifdef HAVE_FLUSHINP
5 kx (void) flushinp();
5 kx #endif
5 kx (void) keypad(stdscr, TRUE);
5 kx (void) cbreak();
5 kx (void) noecho();
5 kx
5 kx if (!dialog_state.no_mouse) {
5 kx mouse_open();
5 kx }
5 kx
5 kx dialog_state.screen_initialized = TRUE;
5 kx
5 kx #ifdef HAVE_COLOR
5 kx if (dialog_state.use_colors || dialog_state.use_shadow)
5 kx dlg_color_setup(); /* Set up colors */
5 kx #endif
5 kx
5 kx /* Set screen to screen attribute */
5 kx dlg_clear();
5 kx }
5 kx
5 kx void
5 kx dlg_keep_tite(FILE *output)
5 kx {
5 kx if (!dialog_vars.keep_tite) {
5 kx #if CAN_KEEP_TITE
5 kx /*
5 kx * Cancel xterm's alternate-screen mode.
5 kx */
5 kx if ((fileno(output) != fileno(stdout)
5 kx || isatty(fileno(output)))
5 kx && key_mouse != 0 /* xterm and kindred */
5 kx && isprivate(enter_ca_mode)
5 kx && isprivate(exit_ca_mode)) {
5 kx FILE *save = dialog_state.screen_output;
5 kx
5 kx /*
5 kx * initscr() or newterm() already wrote enter_ca_mode as a side
5 kx * effect of initializing the screen. It would be nice to not even
5 kx * do that, but we do not really have access to the correct copy of
5 kx * the terminfo description until those functions have been
5 kx * invoked.
5 kx */
5 kx (void) refresh();
5 kx dialog_state.screen_output = output;
5 kx (void) tputs(exit_ca_mode, 0, my_putc);
5 kx (void) tputs(clear_screen, 0, my_putc);
5 kx dialog_state.screen_output = save;
5 kx
5 kx /*
5 kx * Prevent ncurses from switching "back" to the normal screen when
5 kx * exiting from dialog. That would move the cursor to the original
5 kx * location saved in xterm. Normally curses sets the cursor
5 kx * position to the first line after the display, but the alternate
5 kx * screen switching is done after that point.
5 kx *
5 kx * Cancelling the strings altogether also works around the buggy
5 kx * implementation of alternate-screen in rxvt, etc., which clear
5 kx * more of the display than they should.
5 kx */
5 kx enter_ca_mode = 0;
5 kx exit_ca_mode = 0;
5 kx }
5 kx #else
5 kx /*
5 kx * For other implementations, there are no useful answers:
5 kx * + SVr4 curses "could" support a similar approach, but the clue about
5 kx * xterm is absent from its terminal database.
5 kx * + PDCurses does not provide terminfo.
5 kx */
5 kx (void) output;
5 kx #endif
5 kx }
5 kx }
5 kx
5 kx #ifdef HAVE_COLOR
5 kx static int defined_colors = 1; /* pair-0 is reserved */
5 kx /*
5 kx * Setup for color display
5 kx */
5 kx void
5 kx dlg_color_setup(void)
5 kx {
5 kx if (has_colors()) { /* Terminal supports color? */
5 kx unsigned i;
5 kx
5 kx (void) start_color();
5 kx
5 kx #if defined(HAVE_USE_DEFAULT_COLORS)
5 kx use_default_colors();
5 kx #endif
5 kx
5 kx #if defined(__NetBSD__) && defined(_CURSES_)
5 kx #define C_ATTR(x,y) (((x) != 0 ? A_BOLD : 0) | COLOR_PAIR((y)))
5 kx /* work around bug in NetBSD curses */
5 kx for (i = 0; i < sizeof(dlg_color_table) /
5 kx sizeof(dlg_color_table[0]); i++) {
5 kx
5 kx /* Initialize color pairs */
5 kx (void) init_pair(i + 1,
5 kx dlg_color_table[i].fg,
5 kx dlg_color_table[i].bg);
5 kx
5 kx /* Setup color attributes */
5 kx dlg_color_table[i].atr = C_ATTR(dlg_color_table[i].hilite, i + 1);
5 kx }
5 kx defined_colors = i + 1;
5 kx #else
5 kx for (i = 0; i < sizeof(dlg_color_table) /
5 kx sizeof(dlg_color_table[0]); i++) {
5 kx
5 kx /* Initialize color pairs */
5 kx chtype atr = dlg_color_pair(dlg_color_table[i].fg,
5 kx dlg_color_table[i].bg);
5 kx
5 kx atr |= (dlg_color_table[i].hilite ? A_BOLD : 0);
5 kx #ifdef HAVE_RC_FILE2
5 kx atr |= (dlg_color_table[i].ul ? A_UNDERLINE : 0);
5 kx atr |= (dlg_color_table[i].rv ? A_REVERSE : 0);
5 kx #endif /* HAVE_RC_FILE2 */
5 kx
5 kx dlg_color_table[i].atr = atr;
5 kx }
5 kx #endif
5 kx } else {
5 kx dialog_state.use_colors = FALSE;
5 kx dialog_state.use_shadow = FALSE;
5 kx }
5 kx }
5 kx
5 kx int
5 kx dlg_color_count(void)
5 kx {
5 kx return TableSize(dlg_color_table);
5 kx }
5 kx
5 kx /*
5 kx * Wrapper for getattrs(), or the more cumbersome X/Open wattr_get().
5 kx */
5 kx chtype
5 kx dlg_get_attrs(WINDOW *win)
5 kx {
5 kx chtype result;
5 kx #ifdef HAVE_GETATTRS
5 kx result = (chtype) getattrs(win);
5 kx #else
5 kx attr_t my_result;
5 kx short my_pair;
5 kx wattr_get(win, &my_result, &my_pair, NULL);
5 kx result = my_result;
5 kx #endif
5 kx return result;
5 kx }
5 kx
5 kx /*
5 kx * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we
5 kx * have (or can) define a pair with the given color as foreground on the
5 kx * window's defined background.
5 kx */
5 kx chtype
5 kx dlg_color_pair(int foreground, int background)
5 kx {
5 kx chtype result = 0;
5 kx int pair;
5 kx short fg, bg;
5 kx bool found = FALSE;
5 kx
5 kx for (pair = 1; pair < defined_colors; ++pair) {
5 kx if (pair_content((short) pair, &fg, &bg) != ERR
5 kx && fg == foreground
5 kx && bg == background) {
5 kx result = (chtype) COLOR_PAIR(pair);
5 kx found = TRUE;
5 kx break;
5 kx }
5 kx }
5 kx if (!found && (defined_colors + 1) < COLOR_PAIRS) {
5 kx pair = defined_colors++;
5 kx (void) init_pair((short) pair, (short) foreground, (short) background);
5 kx result = (chtype) COLOR_PAIR(pair);
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx /*
5 kx * Reuse color pairs (they are limited), returning a COLOR_PAIR() value if we
5 kx * have (or can) define a pair with the given color as foreground on the
5 kx * window's defined background.
5 kx */
5 kx static chtype
5 kx define_color(WINDOW *win, int foreground)
5 kx {
5 kx short fg, bg, background;
5 kx if (dialog_state.text_only) {
5 kx background = COLOR_BLACK;
5 kx } else {
5 kx chtype attrs = dlg_get_attrs(win);
5 kx int pair;
5 kx
5 kx if ((pair = PAIR_NUMBER(attrs)) != 0
5 kx && pair_content((short) pair, &fg, &bg) != ERR) {
5 kx background = bg;
5 kx } else {
5 kx background = COLOR_BLACK;
5 kx }
5 kx }
5 kx return dlg_color_pair(foreground, background);
5 kx }
5 kx #endif
5 kx
5 kx /*
5 kx * End using dialog functions.
5 kx */
5 kx void
5 kx end_dialog(void)
5 kx {
5 kx if (dialog_state.screen_initialized) {
5 kx dialog_state.screen_initialized = FALSE;
5 kx if (dialog_vars.erase_on_exit) {
5 kx /*
5 kx * Clear the screen to the native background color, and leave the
5 kx * terminal cursor at the lower-left corner of the screen.
5 kx */
5 kx werase(stdscr);
5 kx wrefresh(stdscr);
5 kx }
5 kx mouse_close();
5 kx (void) endwin();
5 kx (void) fflush(stdout);
5 kx }
5 kx }
5 kx
5 kx #define ESCAPE_LEN 3
5 kx #define isOurEscape(p) (((p)[0] == '\\') && ((p)[1] == 'Z') && ((p)[2] != 0))
5 kx
5 kx int
5 kx dlg_count_real_columns(const char *text)
5 kx {
5 kx int result = 0;
5 kx if (*text) {
5 kx result = dlg_count_columns(text);
5 kx if (result && dialog_vars.colors) {
5 kx int hidden = 0;
5 kx while (*text) {
5 kx if (isOurEscape(text)) {
5 kx hidden += ESCAPE_LEN;
5 kx text += ESCAPE_LEN;
5 kx } else {
5 kx ++text;
5 kx }
5 kx }
5 kx result -= hidden;
5 kx }
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx static int
5 kx centered(int width, const char *string)
5 kx {
5 kx int need = dlg_count_real_columns(string);
5 kx int left;
5 kx
5 kx left = (width - need) / 2 - 1;
5 kx if (left < 0)
5 kx left = 0;
5 kx return left;
5 kx }
5 kx
5 kx #ifdef USE_WIDE_CURSES
5 kx static bool
5 kx is_combining(const char *txt, int *combined)
5 kx {
5 kx bool result = FALSE;
5 kx
5 kx if (*combined == 0) {
5 kx if (UCH(*txt) >= 128) {
5 kx wchar_t wch;
5 kx mbstate_t state;
5 kx size_t given = strlen(txt);
5 kx size_t len;
5 kx
5 kx memset(&state, 0, sizeof(state));
5 kx len = mbrtowc(&wch, txt, given, &state);
5 kx if ((int) len > 0 && wcwidth(wch) == 0) {
5 kx *combined = (int) len - 1;
5 kx result = TRUE;
5 kx }
5 kx }
5 kx } else {
5 kx result = TRUE;
5 kx *combined -= 1;
5 kx }
5 kx return result;
5 kx }
5 kx #endif
5 kx
5 kx /*
5 kx * Print the name (tag) or text from a DIALOG_LISTITEM, highlighting the
5 kx * first character if selected.
5 kx */
5 kx void
5 kx dlg_print_listitem(WINDOW *win,
5 kx const char *text,
5 kx int climit,
5 kx bool first,
5 kx int selected)
5 kx {
5 kx chtype attr = A_NORMAL;
5 kx int limit;
5 kx chtype attrs[4];
5 kx
5 kx if (text == 0)
5 kx text = "";
5 kx
5 kx if (first && !dialog_vars.no_hot_list) {
5 kx const int *indx = dlg_index_wchars(text);
5 kx attrs[3] = tag_key_selected_attr;
5 kx attrs[2] = tag_key_attr;
5 kx attrs[1] = tag_selected_attr;
5 kx attrs[0] = tag_attr;
5 kx
5 kx dlg_attrset(win, selected ? attrs[3] : attrs[2]);
5 kx if (*text != '\0') {
5 kx (void) waddnstr(win, text, indx[1]);
5 kx
5 kx if ((int) strlen(text) > indx[1]) {
5 kx limit = dlg_limit_columns(text, climit, 1);
5 kx if (limit > 1) {
5 kx dlg_attrset(win, selected ? attrs[1] : attrs[0]);
5 kx (void) waddnstr(win,
5 kx text + indx[1],
5 kx indx[limit] - indx[1]);
5 kx }
5 kx }
5 kx }
5 kx } else {
5 kx const int *cols;
5 kx
5 kx attrs[1] = item_selected_attr;
5 kx attrs[0] = item_attr;
5 kx
5 kx cols = dlg_index_columns(text);
5 kx limit = dlg_limit_columns(text, climit, 0);
5 kx
5 kx if (limit > 0) {
5 kx dlg_attrset(win, selected ? attrs[1] : attrs[0]);
5 kx dlg_print_text(win, text, cols[limit], &attr);
5 kx }
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Print up to 'cols' columns from 'text', optionally rendering our escape
5 kx * sequence for attributes and color.
5 kx */
5 kx void
5 kx dlg_print_text(WINDOW *win, const char *txt, int cols, chtype *attr)
5 kx {
5 kx int y_origin, x_origin;
5 kx int y_before, x_before = 0;
5 kx int y_after, x_after;
5 kx int tabbed = 0;
5 kx bool ended = FALSE;
5 kx #ifdef USE_WIDE_CURSES
5 kx int combined = 0;
5 kx #endif
5 kx
5 kx if (dialog_state.text_only) {
5 kx y_origin = y_after = 0;
5 kx x_origin = x_after = 0;
5 kx } else {
5 kx y_after = 0;
5 kx x_after = 0;
5 kx getyx(win, y_origin, x_origin);
5 kx }
5 kx while (cols > 0 && (*txt != '\0')) {
5 kx bool thisTab;
5 kx chtype useattr;
5 kx
5 kx if (dialog_vars.colors) {
5 kx while (isOurEscape(txt)) {
5 kx int code;
5 kx
5 kx txt += 2;
5 kx switch (code = CharOf(*txt)) {
5 kx #ifdef HAVE_COLOR
5 kx case '0':
5 kx case '1':
5 kx case '2':
5 kx case '3':
5 kx case '4':
5 kx case '5':
5 kx case '6':
5 kx case '7':
5 kx *attr &= ~A_COLOR;
5 kx *attr |= define_color(win, code - '0');
5 kx break;
5 kx #endif
5 kx case 'B':
5 kx *attr &= ~A_BOLD;
5 kx break;
5 kx case 'b':
5 kx *attr |= A_BOLD;
5 kx break;
5 kx case 'R':
5 kx *attr &= ~A_REVERSE;
5 kx break;
5 kx case 'r':
5 kx *attr |= A_REVERSE;
5 kx break;
5 kx case 'U':
5 kx *attr &= ~A_UNDERLINE;
5 kx break;
5 kx case 'u':
5 kx *attr |= A_UNDERLINE;
5 kx break;
5 kx case 'n':
5 kx *attr = A_NORMAL;
5 kx break;
5 kx default:
5 kx break;
5 kx }
5 kx ++txt;
5 kx }
5 kx }
5 kx if (ended || *txt == '\n' || *txt == '\0')
5 kx break;
5 kx useattr = (*attr) & A_ATTRIBUTES;
5 kx #ifdef HAVE_COLOR
5 kx /*
5 kx * Prevent this from making text invisible when the foreground and
5 kx * background colors happen to be the same, and there's no bold
5 kx * attribute.
5 kx */
5 kx if ((useattr & A_COLOR) != 0 && (useattr & A_BOLD) == 0) {
5 kx short pair = (short) PAIR_NUMBER(useattr);
5 kx short fg, bg;
5 kx if (pair_content(pair, &fg, &bg) != ERR
5 kx && fg == bg) {
5 kx useattr &= ~A_COLOR;
5 kx useattr |= dlg_color_pair(fg, ((bg == COLOR_BLACK)
5 kx ? COLOR_WHITE
5 kx : COLOR_BLACK));
5 kx }
5 kx }
5 kx #endif
5 kx /*
5 kx * Write the character, using curses to tell exactly how wide it
5 kx * is. If it is a tab, discount that, since the caller thinks
5 kx * tabs are nonprinting, and curses will expand tabs to one or
5 kx * more blanks.
5 kx */
5 kx thisTab = (CharOf(*txt) == TAB);
5 kx if (dialog_state.text_only) {
5 kx x_before = x_after;
5 kx } else {
5 kx if (thisTab) {
5 kx getyx(win, y_before, x_before);
5 kx (void) y_before;
5 kx }
5 kx }
5 kx if (dialog_state.text_only) {
5 kx int ch = CharOf(*txt++);
5 kx if (thisTab) {
5 kx while ((x_after++) % 8) {
5 kx fputc(' ', dialog_state.output);
5 kx }
5 kx } else {
5 kx fputc(ch, dialog_state.output);
5 kx x_after++; /* FIXME: handle meta per locale */
5 kx }
5 kx } else {
5 kx (void) waddch(win, CharOf(*txt++) | useattr);
5 kx getyx(win, y_after, x_after);
5 kx }
5 kx if (thisTab && (y_after == y_origin))
5 kx tabbed += (x_after - x_before);
5 kx if ((y_after != y_origin) ||
5 kx (x_after >= (cols + tabbed + x_origin)
5 kx #ifdef USE_WIDE_CURSES
5 kx && !is_combining(txt, &combined)
5 kx #endif
5 kx )) {
5 kx ended = TRUE;
5 kx }
5 kx }
5 kx if (dialog_state.text_only) {
5 kx fputc('\n', dialog_state.output);
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Print one line of the prompt in the window within the limits of the
5 kx * specified right margin. The line will end on a word boundary and a pointer
5 kx * to the start of the next line is returned, or a NULL pointer if the end of
5 kx * *prompt is reached.
5 kx */
5 kx const char *
5 kx dlg_print_line(WINDOW *win,
5 kx chtype *attr,
5 kx const char *prompt,
5 kx int lm, int rm, int *x)
5 kx {
5 kx const char *wrap_ptr;
5 kx const char *test_ptr;
5 kx const char *hide_ptr = 0;
5 kx const int *cols = dlg_index_columns(prompt);
5 kx const int *indx = dlg_index_wchars(prompt);
5 kx int wrap_inx = 0;
5 kx int test_inx = 0;
5 kx int cur_x = lm;
5 kx int hidden = 0;
5 kx int limit = dlg_count_wchars(prompt);
5 kx int n;
5 kx int tabbed = 0;
5 kx
5 kx *x = 1;
5 kx
5 kx /*
5 kx * Set *test_ptr to the end of the line or the right margin (rm), whichever
5 kx * is less, and set wrap_ptr to the end of the last word in the line.
5 kx */
5 kx for (n = 0; n < limit; ++n) {
5 kx int ch = *(test_ptr = prompt + indx[test_inx]);
5 kx if (ch == '\n' || ch == '\0' || cur_x >= (rm + hidden))
5 kx break;
5 kx if (ch == TAB && n == 0) {
5 kx tabbed = 8; /* workaround for leading tabs */
5 kx } else if (isblank(UCH(ch))
5 kx && n != 0
5 kx && !isblank(UCH(prompt[indx[n - 1]]))) {
5 kx wrap_inx = n;
5 kx *x = cur_x;
5 kx } else if (dialog_vars.colors && isOurEscape(test_ptr)) {
5 kx hide_ptr = test_ptr;
5 kx hidden += ESCAPE_LEN;
5 kx n += (ESCAPE_LEN - 1);
5 kx }
5 kx cur_x = lm + tabbed + cols[n + 1];
5 kx if (cur_x > (rm + hidden))
5 kx break;
5 kx test_inx = n + 1;
5 kx }
5 kx
5 kx /*
5 kx * If the line doesn't reach the right margin in the middle of a word, then
5 kx * we don't have to wrap it at the end of the previous word.
5 kx */
5 kx test_ptr = prompt + indx[test_inx];
5 kx if (*test_ptr == '\n' || isblank(UCH(*test_ptr)) || *test_ptr == '\0') {
5 kx wrap_inx = test_inx;
5 kx while (wrap_inx > 0 && isblank(UCH(prompt[indx[wrap_inx - 1]]))) {
5 kx wrap_inx--;
5 kx }
5 kx *x = lm + indx[wrap_inx];
5 kx } else if (*x == 1 && cur_x >= rm) {
5 kx /*
5 kx * If the line has no spaces, then wrap it anyway at the right margin
5 kx */
5 kx *x = rm;
5 kx wrap_inx = test_inx;
5 kx }
5 kx wrap_ptr = prompt + indx[wrap_inx];
5 kx #ifdef USE_WIDE_CURSES
5 kx if (UCH(*wrap_ptr) >= 128) {
5 kx int combined = 0;
5 kx while (is_combining(wrap_ptr, &combined)) {
5 kx ++wrap_ptr;
5 kx }
5 kx }
5 kx #endif
5 kx
5 kx /*
5 kx * If we found hidden text past the last point that we will display,
5 kx * discount that from the displayed length.
5 kx */
5 kx if ((hide_ptr != 0) && (hide_ptr >= wrap_ptr)) {
5 kx hidden -= ESCAPE_LEN;
5 kx test_ptr = wrap_ptr;
5 kx while (test_ptr < wrap_ptr) {
5 kx if (dialog_vars.colors && isOurEscape(test_ptr)) {
5 kx hidden -= ESCAPE_LEN;
5 kx test_ptr += ESCAPE_LEN;
5 kx } else {
5 kx ++test_ptr;
5 kx }
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Print the line if we have a window pointer. Otherwise this routine
5 kx * is just being called for sizing the window.
5 kx */
5 kx if (dialog_state.text_only || win) {
5 kx dlg_print_text(win, prompt, (cols[wrap_inx] - hidden), attr);
5 kx }
5 kx
5 kx /* *x tells the calling function how long the line was */
5 kx if (*x == 1) {
5 kx *x = rm;
5 kx }
5 kx
5 kx *x -= hidden;
5 kx
5 kx /* Find the start of the next line and return a pointer to it */
5 kx test_ptr = wrap_ptr;
5 kx while (isblank(UCH(*test_ptr)))
5 kx test_ptr++;
5 kx if (*test_ptr == '\n')
5 kx test_ptr++;
5 kx dlg_finish_string(prompt);
5 kx return (test_ptr);
5 kx }
5 kx
5 kx static void
5 kx justify_text(WINDOW *win,
5 kx const char *prompt,
5 kx int limit_y,
5 kx int limit_x,
5 kx int *high, int *wide)
5 kx {
5 kx chtype attr = A_NORMAL;
5 kx int x;
5 kx int y = MARGIN;
5 kx int max_x = 2;
5 kx int lm = (2 * MARGIN); /* left margin (box-border plus a space) */
5 kx int rm = limit_x; /* right margin */
5 kx int bm = limit_y; /* bottom margin */
5 kx int last_y = 0, last_x = 0;
5 kx
5 kx dialog_state.text_height = 0;
5 kx dialog_state.text_width = 0;
5 kx if (dialog_state.text_only || win) {
5 kx rm -= (2 * MARGIN);
5 kx bm -= (2 * MARGIN);
5 kx }
5 kx if (prompt == 0)
5 kx prompt = "";
5 kx
5 kx if (win != 0)
5 kx getyx(win, last_y, last_x);
5 kx while (y <= bm && *prompt) {
5 kx x = lm;
5 kx
5 kx if (*prompt == '\n') {
5 kx while (*prompt == '\n' && y < bm) {
5 kx if (*(prompt + 1) != '\0') {
5 kx ++y;
5 kx if (win != 0)
5 kx (void) wmove(win, y, lm);
5 kx }
5 kx prompt++;
5 kx }
5 kx } else if (win != 0)
5 kx (void) wmove(win, y, lm);
5 kx
5 kx if (*prompt) {
5 kx prompt = dlg_print_line(win, &attr, prompt, lm, rm, &x);
5 kx if (win != 0)
5 kx getyx(win, last_y, last_x);
5 kx }
5 kx if (*prompt) {
5 kx ++y;
5 kx if (win != 0)
5 kx (void) wmove(win, y, lm);
5 kx }
5 kx max_x = MAX(max_x, x);
5 kx }
5 kx /* Move back to the last position after drawing prompt, for msgbox. */
5 kx if (win != 0)
5 kx (void) wmove(win, last_y, last_x);
5 kx
5 kx /* Set the final height and width for the calling function */
5 kx if (high != 0)
5 kx *high = y;
5 kx if (wide != 0)
5 kx *wide = max_x;
5 kx }
5 kx
5 kx /*
5 kx * Print a string of text in a window, automatically wrap around to the next
5 kx * line if the string is too long to fit on one line. Note that the string may
5 kx * contain embedded newlines.
5 kx */
5 kx void
5 kx dlg_print_autowrap(WINDOW *win, const char *prompt, int height, int width)
5 kx {
5 kx justify_text(win, prompt,
5 kx height,
5 kx width,
5 kx (int *) 0, (int *) 0);
5 kx }
5 kx
5 kx /*
5 kx * Display the message in a scrollable window. Actually the way it works is
5 kx * that we create a "tall" window of the proper width, let the text wrap within
5 kx * that, and copy a slice of the result to the dialog.
5 kx *
5 kx * It works for ncurses. Other curses implementations show only blanks (Tru64)
5 kx * or garbage (NetBSD).
5 kx */
5 kx int
5 kx dlg_print_scrolled(WINDOW *win,
5 kx const char *prompt,
5 kx int offset,
5 kx int height,
5 kx int width,
5 kx int pauseopt)
5 kx {
5 kx int oldy, oldx;
5 kx int last = 0;
5 kx
5 kx (void) pauseopt; /* used only for ncurses */
5 kx
5 kx getyx(win, oldy, oldx);
5 kx #ifdef NCURSES_VERSION
5 kx if (pauseopt) {
5 kx int wide = width - (2 * MARGIN);
5 kx int high = LINES;
5 kx int len;
5 kx WINDOW *dummy;
5 kx
5 kx #if defined(NCURSES_VERSION_PATCH) && NCURSES_VERSION_PATCH >= 20040417
5 kx /*
5 kx * If we're not limited by the screensize, allow text to possibly be
5 kx * one character per line.
5 kx */
5 kx if ((len = dlg_count_columns(prompt)) > high)
5 kx high = len;
5 kx #endif
5 kx dummy = newwin(high, width, 0, 0);
5 kx if (dummy == 0) {
5 kx dlg_attrset(win, dialog_attr);
5 kx dlg_print_autowrap(win, prompt, height + 1 + (3 * MARGIN), width);
5 kx last = 0;
5 kx } else {
5 kx int y, x;
5 kx
5 kx wbkgdset(dummy, dialog_attr | ' ');
5 kx dlg_attrset(dummy, dialog_attr);
5 kx werase(dummy);
5 kx dlg_print_autowrap(dummy, prompt, high, width);
5 kx getyx(dummy, y, x);
5 kx (void) x;
5 kx
5 kx copywin(dummy, /* srcwin */
5 kx win, /* dstwin */
5 kx offset + MARGIN, /* sminrow */
5 kx MARGIN, /* smincol */
5 kx MARGIN, /* dminrow */
5 kx MARGIN, /* dmincol */
5 kx height, /* dmaxrow */
5 kx wide, /* dmaxcol */
5 kx FALSE);
5 kx
5 kx delwin(dummy);
5 kx
5 kx /* if the text is incomplete, or we have scrolled, show the percentage */
5 kx if (y > 0 && wide > 4) {
5 kx int percent = (int) ((height + offset) * 100.0 / y);
5 kx
5 kx if (percent < 0)
5 kx percent = 0;
5 kx if (percent > 100)
5 kx percent = 100;
5 kx
5 kx if (offset != 0 || percent != 100) {
5 kx char buffer[5];
5 kx
5 kx dlg_attrset(win, position_indicator_attr);
5 kx (void) wmove(win, MARGIN + height, wide - 4);
5 kx (void) sprintf(buffer, "%d%%", percent);
5 kx (void) waddstr(win, buffer);
5 kx if ((len = (int) strlen(buffer)) < 4) {
5 kx dlg_attrset(win, border_attr);
5 kx whline(win, dlg_boxchar(ACS_HLINE), 4 - len);
5 kx }
5 kx }
5 kx }
5 kx last = (y - height);
5 kx }
5 kx } else
5 kx #endif
5 kx {
5 kx (void) offset;
5 kx dlg_attrset(win, dialog_attr);
5 kx dlg_print_autowrap(win, prompt, height + 1 + (3 * MARGIN), width);
5 kx last = 0;
5 kx }
5 kx wmove(win, oldy, oldx);
5 kx return last;
5 kx }
5 kx
5 kx int
5 kx dlg_check_scrolled(int key, int last, int page, bool * show, int *offset)
5 kx {
5 kx int code = 0;
5 kx
5 kx *show = FALSE;
5 kx
5 kx switch (key) {
5 kx case DLGK_PAGE_FIRST:
5 kx if (*offset > 0) {
5 kx *offset = 0;
5 kx *show = TRUE;
5 kx }
5 kx break;
5 kx case DLGK_PAGE_LAST:
5 kx if (*offset < last) {
5 kx *offset = last;
5 kx *show = TRUE;
5 kx }
5 kx break;
5 kx case DLGK_GRID_UP:
5 kx if (*offset > 0) {
5 kx --(*offset);
5 kx *show = TRUE;
5 kx }
5 kx break;
5 kx case DLGK_GRID_DOWN:
5 kx if (*offset < last) {
5 kx ++(*offset);
5 kx *show = TRUE;
5 kx }
5 kx break;
5 kx case DLGK_PAGE_PREV:
5 kx if (*offset > 0) {
5 kx *offset -= page;
5 kx if (*offset < 0)
5 kx *offset = 0;
5 kx *show = TRUE;
5 kx }
5 kx break;
5 kx case DLGK_PAGE_NEXT:
5 kx if (*offset < last) {
5 kx *offset += page;
5 kx if (*offset > last)
5 kx *offset = last;
5 kx *show = TRUE;
5 kx }
5 kx break;
5 kx default:
5 kx code = -1;
5 kx break;
5 kx }
5 kx return code;
5 kx }
5 kx
5 kx /*
5 kx * Calculate the window size for preformatted text. This will calculate box
5 kx * dimensions that are at or close to the specified aspect ratio for the prompt
5 kx * string with all spaces and newlines preserved and additional newlines added
5 kx * as necessary.
5 kx */
5 kx static void
5 kx auto_size_preformatted(const char *prompt, int *height, int *width)
5 kx {
5 kx int high = 0, wide = 0;
5 kx float car; /* Calculated Aspect Ratio */
5 kx int max_y = SLINES - 1;
5 kx int max_x = SCOLS - 2;
5 kx int max_width = max_x;
5 kx int ar = dialog_state.aspect_ratio;
5 kx
5 kx /* Get the initial dimensions */
5 kx justify_text((WINDOW *) 0, prompt, max_y, max_x, &high, &wide);
5 kx car = (float) (wide / high);
5 kx
5 kx *height = high;
5 kx *width = wide;
5 kx }
5 kx
5 kx /*
5 kx * Find the length of the longest "word" in the given string. By setting the
5 kx * widget width at least this long, we can avoid splitting a word on the
5 kx * margin.
5 kx */
5 kx static int
5 kx longest_word(const char *string)
5 kx {
5 kx int result = 0;
5 kx
5 kx while (*string != '\0') {
5 kx int length = 0;
5 kx while (*string != '\0' && !isspace(UCH(*string))) {
5 kx length++;
5 kx string++;
5 kx }
5 kx result = MAX(result, length);
5 kx if (*string != '\0')
5 kx string++;
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx /*
5 kx * if (height or width == -1) Maximize()
5 kx * if (height or width == 0), justify and return actual limits.
5 kx */
5 kx static void
5 kx real_auto_size(const char *title,
5 kx const char *prompt,
5 kx int *height, int *width,
5 kx int boxlines, int mincols)
5 kx {
5 kx int x = (dialog_vars.begin_set ? dialog_vars.begin_x : 2);
5 kx int y = (dialog_vars.begin_set ? dialog_vars.begin_y : 1);
5 kx int title_length = title ? dlg_count_columns(title) : 0;
5 kx int high;
5 kx int save_high = *height;
5 kx int save_wide = *width;
5 kx int max_high;
5 kx int max_wide;
5 kx
5 kx if (prompt == 0) {
5 kx if (*height == 0)
5 kx *height = -1;
5 kx if (*width == 0)
5 kx *width = -1;
5 kx }
5 kx
5 kx max_high = (*height < 0);
5 kx max_wide = (*width < 0);
5 kx
5 kx if (*height > 0) {
5 kx high = *height;
5 kx } else {
5 kx high = SLINES - y;
5 kx }
5 kx
5 kx if (*width <= 0) {
5 kx int wide;
5 kx
5 kx if (prompt != 0) {
5 kx wide = MAX(title_length, mincols);
5 kx if (strchr(prompt, '\n') == 0) {
5 kx double val = (dialog_state.aspect_ratio *
5 kx dlg_count_real_columns(prompt));
5 kx double xxx = sqrt(val);
5 kx int tmp = (int) xxx;
5 kx wide = MAX(wide, tmp);
5 kx wide = MAX(wide, longest_word(prompt));
5 kx justify_text((WINDOW *) 0, prompt, high, wide, height, width);
5 kx } else {
5 kx auto_size_preformatted(prompt, height, width);
5 kx }
5 kx } else {
5 kx wide = SCOLS - x;
5 kx justify_text((WINDOW *) 0, prompt, high, wide, height, width);
5 kx }
5 kx }
5 kx
5 kx if (*width < title_length) {
5 kx justify_text((WINDOW *) 0, prompt, high, title_length, height, width);
5 kx *width = title_length;
5 kx }
5 kx
5 kx dialog_state.text_height = *height;
5 kx dialog_state.text_width = *width;
5 kx
5 kx if (*width < mincols && save_wide == 0)
5 kx *width = mincols;
5 kx if (prompt != 0) {
5 kx *width += ((2 * MARGIN) + SHADOW_COLS);
5 kx *height += boxlines + (2 * MARGIN);
5 kx }
5 kx
5 kx if (save_high > 0)
5 kx *height = save_high;
5 kx if (save_wide > 0)
5 kx *width = save_wide;
5 kx
5 kx if (max_high)
5 kx *height = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0);
5 kx if (max_wide)
5 kx *width = SCOLS - (dialog_vars.begin_set ? dialog_vars.begin_x : 0);
5 kx }
5 kx
5 kx /* End of real_auto_size() */
5 kx
5 kx void
5 kx dlg_auto_size(const char *title,
5 kx const char *prompt,
5 kx int *height,
5 kx int *width,
5 kx int boxlines,
5 kx int mincols)
5 kx {
5 kx DLG_TRACE(("# dlg_auto_size(%d,%d) limits %d,%d\n",
5 kx *height, *width,
5 kx boxlines, mincols));
5 kx
5 kx real_auto_size(title, prompt, height, width, boxlines, mincols);
5 kx
5 kx if (*width > SCOLS) {
5 kx (*height)++;
5 kx *width = SCOLS;
5 kx }
5 kx
5 kx if (*height > SLINES) {
5 kx *height = SLINES;
5 kx }
5 kx DLG_TRACE(("# ...dlg_auto_size(%d,%d) also %d,%d\n",
5 kx *height, *width,
5 kx dialog_state.text_height, dialog_state.text_width));
5 kx }
5 kx
5 kx /*
5 kx * if (height or width == -1) Maximize()
5 kx * if (height or width == 0)
5 kx * height=MIN(SLINES, num.lines in fd+n);
5 kx * width=MIN(SCOLS, MAX(longer line+n, mincols));
5 kx */
5 kx void
5 kx dlg_auto_sizefile(const char *title,
5 kx const char *file,
5 kx int *height,
5 kx int *width,
5 kx int boxlines,
5 kx int mincols)
5 kx {
5 kx int count = 0;
5 kx int len = title ? dlg_count_columns(title) : 0;
5 kx int nc = 4;
5 kx int numlines = 2;
5 kx FILE *fd;
5 kx
5 kx /* Open input file for reading */
5 kx if ((fd = fopen(file, "rb")) == NULL)
5 kx dlg_exiterr("dlg_auto_sizefile: Cannot open input file %s", file);
5 kx
5 kx if ((*height == -1) || (*width == -1)) {
5 kx *height = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0);
5 kx *width = SCOLS - (dialog_vars.begin_set ? dialog_vars.begin_x : 0);
5 kx }
5 kx if ((*height != 0) && (*width != 0)) {
5 kx (void) fclose(fd);
5 kx if (*width > SCOLS)
5 kx *width = SCOLS;
5 kx if (*height > SLINES)
5 kx *height = SLINES;
5 kx return;
5 kx }
5 kx
5 kx while (!feof(fd)) {
5 kx int ch;
5 kx long offset;
5 kx
5 kx if (ferror(fd))
5 kx break;
5 kx
5 kx offset = 0;
5 kx while (((ch = getc(fd)) != '\n') && !feof(fd)) {
5 kx if ((ch == TAB) && (dialog_vars.tab_correct)) {
5 kx offset += dialog_state.tab_len - (offset % dialog_state.tab_len);
5 kx } else {
5 kx offset++;
5 kx }
5 kx }
5 kx
5 kx if (offset > len)
5 kx len = (int) offset;
5 kx
5 kx count++;
5 kx }
5 kx
5 kx /* now 'count' has the number of lines of fd and 'len' the max length */
5 kx
5 kx *height = MIN(SLINES, count + numlines + boxlines);
5 kx *width = MIN(SCOLS, MAX((len + nc), mincols));
5 kx /* here width and height can be maximized if > SCOLS|SLINES because
5 kx textbox-like widgets don't put all <file> on the screen.
5 kx Msgbox-like widget instead have to put all <text> correctly. */
5 kx
5 kx (void) fclose(fd);
5 kx }
5 kx
5 kx /*
5 kx * Draw a rectangular box with line drawing characters.
5 kx *
5 kx * borderchar is used to color the upper/left edges.
5 kx *
5 kx * boxchar is used to color the right/lower edges. It also is fill-color used
5 kx * for the box contents.
5 kx *
5 kx * Normally, if you are drawing a scrollable box, use menubox_border_attr for
5 kx * boxchar, and menubox_attr for borderchar since the scroll-arrows are drawn
5 kx * with menubox_attr at the top, and menubox_border_attr at the bottom. That
5 kx * also (given the default color choices) produces a recessed effect.
5 kx *
5 kx * If you want a raised effect (and are not going to use the scroll-arrows),
5 kx * reverse this choice.
5 kx */
5 kx void
5 kx dlg_draw_box2(WINDOW *win, int y, int x, int height, int width,
5 kx chtype boxchar, chtype borderchar, chtype borderchar2)
5 kx {
5 kx int i, j;
5 kx chtype save = dlg_get_attrs(win);
5 kx
5 kx dlg_attrset(win, 0);
5 kx for (i = 0; i < height; i++) {
5 kx (void) wmove(win, y + i, x);
5 kx for (j = 0; j < width; j++)
5 kx if (!i && !j)
5 kx (void) waddch(win, borderchar | dlg_boxchar(ACS_ULCORNER));
5 kx else if (i == height - 1 && !j)
5 kx (void) waddch(win, borderchar | dlg_boxchar(ACS_LLCORNER));
5 kx else if (!i && j == width - 1)
5 kx (void) waddch(win, borderchar2 | dlg_boxchar(ACS_URCORNER));
5 kx else if (i == height - 1 && j == width - 1)
5 kx (void) waddch(win, borderchar2 | dlg_boxchar(ACS_LRCORNER));
5 kx else if (!i)
5 kx (void) waddch(win, borderchar | dlg_boxchar(ACS_HLINE));
5 kx else if (i == height - 1)
5 kx (void) waddch(win, borderchar2 | dlg_boxchar(ACS_HLINE));
5 kx else if (!j)
5 kx (void) waddch(win, borderchar | dlg_boxchar(ACS_VLINE));
5 kx else if (j == width - 1)
5 kx (void) waddch(win, borderchar2 | dlg_boxchar(ACS_VLINE));
5 kx else
5 kx (void) waddch(win, boxchar | ' ');
5 kx }
5 kx dlg_attrset(win, save);
5 kx }
5 kx
5 kx void
5 kx dlg_draw_box(WINDOW *win, int y, int x, int height, int width,
5 kx chtype boxchar, chtype borderchar)
5 kx {
5 kx dlg_draw_box2(win, y, x, height, width, boxchar, borderchar, boxchar);
5 kx }
5 kx
5 kx /*
5 kx * Search the given 'list' for the given window 'win'. Typically 'win' is an
5 kx * input-window, i.e., a window where we might use wgetch.
5 kx *
5 kx * The all-windows list has normal- and shadow-windows. Since we never use the
5 kx * shadow as an input window, normally we just look for the normal-window.
5 kx *
5 kx * However, the all-subwindows list stores parent/child windows rather than
5 kx * normal/shadow windows. When searching that list, we look for the child
5 kx * window (in the .shadow field).
5 kx */
5 kx static DIALOG_WINDOWS *
5 kx find_window(DIALOG_WINDOWS * list, WINDOW *win, bool normal)
5 kx {
5 kx DIALOG_WINDOWS *result = 0;
5 kx DIALOG_WINDOWS *p;
5 kx
5 kx for (p = list; p != 0; p = p->next) {
5 kx WINDOW *check = normal ? p->normal : p->shadow;
5 kx if (check == win) {
5 kx result = p;
5 kx break;
5 kx }
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx #define SearchTopWindows(win) find_window(dialog_state.all_windows, win, TRUE)
5 kx #define SearchSubWindows(win) find_window(dialog_state.all_subwindows, win, FALSE)
5 kx
5 kx /*
5 kx * Check for the existence of a window, e.g., when used for input or updating
5 kx * the display. This is used in dlg_getc() and related functions, to guard
5 kx * against an asynchronous window-deletion that might invalidate the input
5 kx * window used in dlg_getc().
5 kx */
5 kx DIALOG_WINDOWS *
5 kx _dlg_find_window(WINDOW *win)
5 kx {
5 kx DIALOG_WINDOWS *result = 0;
5 kx
5 kx if ((result = SearchTopWindows(win)) == NULL)
5 kx result = SearchSubWindows(win);
5 kx return result;
5 kx }
5 kx
5 kx #ifdef HAVE_COLOR
5 kx /*
5 kx * If we have wchgat(), use that for updating shadow attributes, to work with
5 kx * wide-character data.
5 kx */
5 kx
5 kx /*
5 kx * Check if the given point is "in" the given window. If so, return the window
5 kx * pointer, otherwise null.
5 kx */
5 kx static WINDOW *
5 kx in_window(WINDOW *win, int y, int x)
5 kx {
5 kx WINDOW *result = 0;
5 kx int y_base = getbegy(win);
5 kx int x_base = getbegx(win);
5 kx int y_last = getmaxy(win) + y_base;
5 kx int x_last = getmaxx(win) + x_base;
5 kx
5 kx if (y >= y_base && y <= y_last && x >= x_base && x <= x_last)
5 kx result = win;
5 kx return result;
5 kx }
5 kx
5 kx static WINDOW *
5 kx window_at_cell(DIALOG_WINDOWS * dw, int y, int x)
5 kx {
5 kx WINDOW *result = 0;
5 kx DIALOG_WINDOWS *p;
5 kx int y_want = y + getbegy(dw->shadow);
5 kx int x_want = x + getbegx(dw->shadow);
5 kx
5 kx for (p = dialog_state.all_windows; p != 0; p = p->next) {
5 kx if (dw->normal != p->normal
5 kx && dw->shadow != p->normal
5 kx && (result = in_window(p->normal, y_want, x_want)) != 0) {
5 kx break;
5 kx }
5 kx }
5 kx if (result == 0) {
5 kx result = stdscr;
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx static bool
5 kx in_shadow(WINDOW *normal, WINDOW *shadow, int y, int x)
5 kx {
5 kx bool result = FALSE;
5 kx int ybase = getbegy(normal);
5 kx int ylast = getmaxy(normal) + ybase;
5 kx int xbase = getbegx(normal);
5 kx int xlast = getmaxx(normal) + xbase;
5 kx
5 kx y += getbegy(shadow);
5 kx x += getbegx(shadow);
5 kx
5 kx if (y >= ybase + SHADOW_ROWS
5 kx && y < ylast + SHADOW_ROWS
5 kx && x >= xlast
5 kx && x < xlast + SHADOW_COLS) {
5 kx /* in the right-side */
5 kx result = TRUE;
5 kx } else if (y >= ylast
5 kx && y < ylast + SHADOW_ROWS
5 kx && x >= ybase + SHADOW_COLS
5 kx && x < ylast + SHADOW_COLS) {
5 kx /* check the bottom */
5 kx result = TRUE;
5 kx }
5 kx
5 kx return result;
5 kx }
5 kx
5 kx /*
5 kx * When erasing a shadow, check each cell to make sure that it is not part of
5 kx * another box's shadow. This is a little complicated since most shadows are
5 kx * merged onto stdscr.
5 kx */
5 kx static bool
5 kx last_shadow(DIALOG_WINDOWS * dw, int y, int x)
5 kx {
5 kx DIALOG_WINDOWS *p;
5 kx bool result = TRUE;
5 kx
5 kx for (p = dialog_state.all_windows; p != 0; p = p->next) {
5 kx if (p->normal != dw->normal
5 kx && in_shadow(p->normal, dw->shadow, y, x)) {
5 kx result = FALSE;
5 kx break;
5 kx }
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx static void
5 kx repaint_cell(DIALOG_WINDOWS * dw, bool draw, int y, int x)
5 kx {
5 kx WINDOW *win = dw->shadow;
5 kx WINDOW *cellwin;
5 kx int y2, x2;
5 kx
5 kx if ((cellwin = window_at_cell(dw, y, x)) != 0
5 kx && (draw || last_shadow(dw, y, x))
5 kx && (y2 = (y + getbegy(win) - getbegy(cellwin))) >= 0
5 kx && (x2 = (x + getbegx(win) - getbegx(cellwin))) >= 0
5 kx && wmove(cellwin, y2, x2) != ERR) {
5 kx chtype the_cell = dlg_get_attrs(cellwin);
5 kx chtype the_attr = (draw ? shadow_attr : the_cell);
5 kx
5 kx if (winch(cellwin) & A_ALTCHARSET) {
5 kx the_attr |= A_ALTCHARSET;
5 kx }
5 kx #if USE_WCHGAT
5 kx wchgat(cellwin, 1,
5 kx the_attr & (chtype) (~A_COLOR),
5 kx (short) PAIR_NUMBER(the_attr),
5 kx NULL);
5 kx #else
5 kx {
5 kx chtype the_char = ((winch(cellwin) & A_CHARTEXT) | the_attr);
5 kx (void) waddch(cellwin, the_char);
5 kx }
5 kx #endif
5 kx wnoutrefresh(cellwin);
5 kx }
5 kx }
5 kx
5 kx #define RepaintCell(dw, draw, y, x) repaint_cell(dw, draw, y, x)
5 kx
5 kx static void
5 kx repaint_shadow(DIALOG_WINDOWS * dw, bool draw, int y, int x, int height, int width)
5 kx {
5 kx if (UseShadow(dw)) {
5 kx int i, j;
5 kx
5 kx #if !USE_WCHGAT
5 kx chtype save = dlg_get_attrs(dw->shadow);
5 kx dlg_attrset(dw->shadow, draw ? shadow_attr : screen_attr);
5 kx #endif
5 kx for (i = 0; i < SHADOW_ROWS; ++i) {
5 kx for (j = 0; j < width; ++j) {
5 kx RepaintCell(dw, draw, i + y + height, j + x + SHADOW_COLS);
5 kx }
5 kx }
5 kx for (i = 0; i < height; i++) {
5 kx for (j = 0; j < SHADOW_COLS; ++j) {
5 kx RepaintCell(dw, draw, i + y + SHADOW_ROWS, j + x + width);
5 kx }
5 kx }
5 kx (void) wnoutrefresh(dw->shadow);
5 kx #if !USE_WCHGAT
5 kx dlg_attrset(dw->shadow, save);
5 kx #endif
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Draw a shadow on the parent window corresponding to the right- and
5 kx * bottom-edge of the child window, to give a 3-dimensional look.
5 kx */
5 kx static void
5 kx draw_childs_shadow(DIALOG_WINDOWS * dw)
5 kx {
5 kx if (UseShadow(dw)) {
5 kx repaint_shadow(dw,
5 kx TRUE,
5 kx getbegy(dw->normal) - getbegy(dw->shadow),
5 kx getbegx(dw->normal) - getbegx(dw->shadow),
5 kx getmaxy(dw->normal),
5 kx getmaxx(dw->normal));
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Erase a shadow on the parent window corresponding to the right- and
5 kx * bottom-edge of the child window.
5 kx */
5 kx static void
5 kx erase_childs_shadow(DIALOG_WINDOWS * dw)
5 kx {
5 kx if (UseShadow(dw)) {
5 kx repaint_shadow(dw,
5 kx FALSE,
5 kx getbegy(dw->normal) - getbegy(dw->shadow),
5 kx getbegx(dw->normal) - getbegx(dw->shadow),
5 kx getmaxy(dw->normal),
5 kx getmaxx(dw->normal));
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Draw shadows along the right and bottom edge to give a more 3D look
5 kx * to the boxes.
5 kx */
5 kx void
5 kx dlg_draw_shadow(WINDOW *win, int y, int x, int height, int width)
5 kx {
5 kx repaint_shadow(SearchTopWindows(win), TRUE, y, x, height, width);
5 kx }
5 kx #endif /* HAVE_COLOR */
5 kx
5 kx /*
5 kx * Allow shell scripts to remap the exit codes so they can distinguish ESC
5 kx * from ERROR.
5 kx */
5 kx void
5 kx dlg_exit(int code)
5 kx {
5 kx /* *INDENT-OFF* */
5 kx static const struct {
5 kx int code;
5 kx const char *name;
5 kx } table[] = {
5 kx { DLG_EXIT_CANCEL, "DIALOG_CANCEL" },
5 kx { DLG_EXIT_ERROR, "DIALOG_ERROR" },
5 kx { DLG_EXIT_ESC, "DIALOG_ESC" },
5 kx { DLG_EXIT_EXTRA, "DIALOG_EXTRA" },
5 kx { DLG_EXIT_HELP, "DIALOG_HELP" },
5 kx { DLG_EXIT_OK, "DIALOG_OK" },
5 kx { DLG_EXIT_ITEM_HELP, "DIALOG_ITEM_HELP" },
5 kx { DLG_EXIT_TIMEOUT, "DIALOG_TIMEOUT" },
5 kx };
5 kx /* *INDENT-ON* */
5 kx
5 kx unsigned n;
5 kx bool overridden = FALSE;
5 kx
5 kx retry:
5 kx for (n = 0; n < TableSize(table); n++) {
5 kx if (table[n].code == code) {
5 kx if (dlg_getenv_num(table[n].name, &code)) {
5 kx overridden = TRUE;
5 kx }
5 kx break;
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Prior to 2004/12/19, a widget using --item-help would exit with "OK"
5 kx * if the help button were selected. Now we want to exit with "HELP",
5 kx * but allow the environment variable to override.
5 kx */
5 kx if (code == DLG_EXIT_ITEM_HELP && !overridden) {
5 kx code = DLG_EXIT_HELP;
5 kx goto retry;
5 kx }
5 kx #ifdef HAVE_DLG_TRACE
5 kx dlg_trace((const char *) 0); /* close it */
5 kx #endif
5 kx
5 kx #ifdef NO_LEAKS
5 kx _dlg_inputstr_leaks();
5 kx #if defined(NCURSES_VERSION) && (defined(HAVE_EXIT_CURSES) || defined(HAVE__NC_FREE_AND_EXIT))
5 kx exit_curses(code);
5 kx #endif
5 kx #endif
5 kx
5 kx if (dialog_state.input == stdin) {
5 kx exit(code);
5 kx } else {
5 kx /*
5 kx * Just in case of using --input-fd option, do not
5 kx * call atexit functions of ncurses which may hang.
5 kx */
5 kx if (dialog_state.input) {
5 kx fclose(dialog_state.input);
5 kx dialog_state.input = 0;
5 kx }
5 kx if (dialog_state.pipe_input) {
5 kx if (dialog_state.pipe_input != stdin) {
5 kx fclose(dialog_state.pipe_input);
5 kx dialog_state.pipe_input = 0;
5 kx }
5 kx }
5 kx _exit(code);
5 kx }
5 kx }
5 kx
5 kx #define DATA(name) { DLG_EXIT_ ## name, #name }
5 kx /* *INDENT-OFF* */
5 kx static struct {
5 kx int code;
5 kx const char *name;
5 kx } exit_codenames[] = {
5 kx DATA(ESC),
5 kx DATA(UNKNOWN),
5 kx DATA(ERROR),
5 kx DATA(OK),
5 kx DATA(CANCEL),
5 kx DATA(HELP),
5 kx DATA(EXTRA),
5 kx DATA(ITEM_HELP),
5 kx };
5 kx #undef DATA
5 kx /* *INDENT-ON* */
5 kx
5 kx const char *
5 kx dlg_exitcode2s(int code)
5 kx {
5 kx const char *result = "?";
5 kx size_t n;
5 kx
5 kx for (n = 0; n < TableSize(exit_codenames); ++n) {
5 kx if (exit_codenames[n].code == code) {
5 kx result = exit_codenames[n].name;
5 kx break;
5 kx }
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx int
5 kx dlg_exitname2n(const char *name)
5 kx {
5 kx int result = DLG_EXIT_UNKNOWN;
5 kx size_t n;
5 kx
5 kx for (n = 0; n < TableSize(exit_codenames); ++n) {
5 kx if (!dlg_strcmp(exit_codenames[n].name, name)) {
5 kx result = exit_codenames[n].code;
5 kx break;
5 kx }
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx /* quit program killing all tailbg */
5 kx void
5 kx dlg_exiterr(const char *fmt, ...)
5 kx {
5 kx int retval;
5 kx va_list ap;
5 kx
5 kx end_dialog();
5 kx
5 kx (void) fputc('\n', stderr);
5 kx va_start(ap, fmt);
5 kx (void) vfprintf(stderr, fmt, ap);
5 kx va_end(ap);
5 kx (void) fputc('\n', stderr);
5 kx
5 kx #ifdef HAVE_DLG_TRACE
5 kx va_start(ap, fmt);
5 kx dlg_trace_msg("## Error: ");
5 kx dlg_trace_va_msg(fmt, ap);
5 kx va_end(ap);
5 kx #endif
5 kx
5 kx dlg_killall_bg(&retval);
5 kx
5 kx (void) fflush(stderr);
5 kx (void) fflush(stdout);
5 kx dlg_exit(strcmp(fmt, "timeout") == 0 ? DLG_EXIT_TIMEOUT : DLG_EXIT_ERROR);
5 kx }
5 kx
5 kx /*
5 kx * Get a string from the environment, rejecting those which are entirely blank.
5 kx */
5 kx char *
5 kx dlg_getenv_str(const char *name)
5 kx {
5 kx char *result = getenv(name);
5 kx if (result != NULL) {
5 kx while (*result != '\0' && isspace(UCH(*result)))
5 kx ++result;
5 kx if (*result == '\0')
5 kx result = NULL;
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx /*
5 kx * Get a number from the environment:
5 kx * + If the caller provides a pointer in the second parameter, return
5 kx * success/failure for the function return, and the actual value via the
5 kx * pointer. Use this for decoding arbitrary numbers, e.g., negative or zero.
5 kx * + If the caller does not provide a pointer, return the decoded value for
5 kx * the function-return. Use this when only values greater than zero are
5 kx * useful.
5 kx */
5 kx int
5 kx dlg_getenv_num(const char *name, int *value)
5 kx {
5 kx int result = 0;
5 kx char *data = getenv(name);
5 kx if (data != NULL) {
5 kx char *temp = NULL;
5 kx long check = strtol(data, &temp, 0);
5 kx if (temp != 0 && temp != data && *temp == '\0') {
5 kx result = (int) check;
5 kx if (value != NULL) {
5 kx *value = result;
5 kx result = 1;
5 kx }
5 kx }
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx void
5 kx dlg_beeping(void)
5 kx {
5 kx if (dialog_vars.beep_signal) {
5 kx (void) beep();
5 kx dialog_vars.beep_signal = 0;
5 kx }
5 kx }
5 kx
5 kx void
5 kx dlg_print_size(int height, int width)
5 kx {
5 kx if (dialog_vars.print_siz) {
5 kx fprintf(dialog_state.output, "Size: %d, %d\n", height, width);
5 kx DLG_TRACE(("# print size: %dx%d\n", height, width));
5 kx }
5 kx }
5 kx
5 kx void
5 kx dlg_ctl_size(int height, int width)
5 kx {
5 kx if (dialog_vars.size_err) {
5 kx if ((width > COLS) || (height > LINES)) {
5 kx dlg_exiterr("Window too big. (height, width) = (%d, %d). Max allowed (%d, %d).",
5 kx height, width, LINES, COLS);
5 kx }
5 kx #ifdef HAVE_COLOR
5 kx else if ((dialog_state.use_shadow)
5 kx && ((width > SCOLS || height > SLINES))) {
5 kx if ((width <= COLS) && (height <= LINES)) {
5 kx /* try again, without shadows */
5 kx dialog_state.use_shadow = 0;
5 kx } else {
5 kx dlg_exiterr("Window+Shadow too big. (height, width) = (%d, %d). Max allowed (%d, %d).",
5 kx height, width, SLINES, SCOLS);
5 kx }
5 kx }
5 kx #endif
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * If the --tab-correct was not selected, convert tabs to single spaces.
5 kx */
5 kx void
5 kx dlg_tab_correct_str(char *prompt)
5 kx {
5 kx char *ptr;
5 kx
5 kx if (dialog_vars.tab_correct) {
5 kx while ((ptr = strchr(prompt, TAB)) != NULL) {
5 kx *ptr = ' ';
5 kx prompt = ptr;
5 kx }
5 kx }
5 kx }
5 kx
5 kx void
5 kx dlg_calc_listh(int *height, int *list_height, int item_no)
5 kx {
5 kx /* calculate new height and list_height */
5 kx int rows = SLINES - (dialog_vars.begin_set ? dialog_vars.begin_y : 0);
5 kx if (rows - (*height) > 0) {
5 kx if (rows - (*height) > item_no)
5 kx *list_height = item_no;
5 kx else
5 kx *list_height = rows - (*height);
5 kx }
5 kx (*height) += (*list_height);
5 kx }
5 kx
5 kx /* obsolete */
5 kx int
5 kx dlg_calc_listw(int item_no, char **items, int group)
5 kx {
5 kx int i, len1 = 0, len2 = 0;
5 kx
5 kx for (i = 0; i < (item_no * group); i += group) {
5 kx int n;
5 kx
5 kx if ((n = dlg_count_columns(items[i])) > len1)
5 kx len1 = n;
5 kx if ((n = dlg_count_columns(items[i + 1])) > len2)
5 kx len2 = n;
5 kx }
5 kx return len1 + len2;
5 kx }
5 kx
5 kx int
5 kx dlg_calc_list_width(int item_no, DIALOG_LISTITEM * items)
5 kx {
5 kx int n, i, len1 = 0, len2 = 0;
5 kx int bits = ((dialog_vars.no_tags ? 1 : 0)
5 kx + (dialog_vars.no_items ? 2 : 0));
5 kx
5 kx for (i = 0; i < item_no; ++i) {
5 kx switch (bits) {
5 kx case 0:
5 kx /* FALLTHRU */
5 kx case 1:
5 kx if ((n = dlg_count_columns(items[i].name)) > len1)
5 kx len1 = n;
5 kx if ((n = dlg_count_columns(items[i].text)) > len2)
5 kx len2 = n;
5 kx break;
5 kx case 2:
5 kx /* FALLTHRU */
5 kx case 3:
5 kx if ((n = dlg_count_columns(items[i].name)) > len1)
5 kx len1 = n;
5 kx break;
5 kx }
5 kx }
5 kx return len1 + len2;
5 kx }
5 kx
5 kx char *
5 kx dlg_strempty(void)
5 kx {
5 kx static char empty[] = "";
5 kx return empty;
5 kx }
5 kx
5 kx char *
5 kx dlg_strclone(const char *cprompt)
5 kx {
5 kx char *prompt = 0;
5 kx if (cprompt != 0) {
5 kx prompt = dlg_malloc(char, strlen(cprompt) + 1);
5 kx assert_ptr(prompt, "dlg_strclone");
5 kx strcpy(prompt, cprompt);
5 kx }
5 kx return prompt;
5 kx }
5 kx
5 kx chtype
5 kx dlg_asciibox(chtype ch)
5 kx {
5 kx chtype result = 0;
5 kx
5 kx if (ch == ACS_ULCORNER)
5 kx result = '+';
5 kx else if (ch == ACS_LLCORNER)
5 kx result = '+';
5 kx else if (ch == ACS_URCORNER)
5 kx result = '+';
5 kx else if (ch == ACS_LRCORNER)
5 kx result = '+';
5 kx else if (ch == ACS_HLINE)
5 kx result = '-';
5 kx else if (ch == ACS_VLINE)
5 kx result = '|';
5 kx else if (ch == ACS_LTEE)
5 kx result = '+';
5 kx else if (ch == ACS_RTEE)
5 kx result = '+';
5 kx else if (ch == ACS_UARROW)
5 kx result = '^';
5 kx else if (ch == ACS_DARROW)
5 kx result = 'v';
5 kx
5 kx return result;
5 kx }
5 kx
5 kx chtype
5 kx dlg_boxchar(chtype ch)
5 kx {
5 kx chtype result = dlg_asciibox(ch);
5 kx
5 kx if (result != 0) {
5 kx if (dialog_vars.ascii_lines)
5 kx ch = result;
5 kx else if (dialog_vars.no_lines)
5 kx ch = ' ';
5 kx }
5 kx return ch;
5 kx }
5 kx
5 kx int
5 kx dlg_box_x_ordinate(int width)
5 kx {
5 kx int x;
5 kx
5 kx if (dialog_vars.begin_set == 1) {
5 kx x = dialog_vars.begin_x;
5 kx } else {
5 kx /* center dialog box on screen unless --begin-set */
5 kx x = (SCOLS - width) / 2;
5 kx }
5 kx return x;
5 kx }
5 kx
5 kx int
5 kx dlg_box_y_ordinate(int height)
5 kx {
5 kx int y;
5 kx
5 kx if (dialog_vars.begin_set == 1) {
5 kx y = dialog_vars.begin_y;
5 kx } else {
5 kx /* center dialog box on screen unless --begin-set */
5 kx y = (SLINES - height) / 2;
5 kx }
5 kx return y;
5 kx }
5 kx
5 kx void
5 kx dlg_draw_title(WINDOW *win, const char *title)
5 kx {
5 kx if (title != NULL) {
5 kx chtype attr = A_NORMAL;
5 kx chtype save = dlg_get_attrs(win);
5 kx int x = centered(getmaxx(win), title);
5 kx
5 kx dlg_attrset(win, title_attr);
5 kx wmove(win, 0, x);
5 kx dlg_print_text(win, title, getmaxx(win) - x, &attr);
5 kx dlg_attrset(win, save);
5 kx dlg_finish_string(title);
5 kx }
5 kx }
5 kx
5 kx void
5 kx dlg_draw_bottom_box2(WINDOW *win, chtype on_left, chtype on_right, chtype on_inside)
5 kx {
5 kx int width = getmaxx(win);
5 kx int height = getmaxy(win);
5 kx int i;
5 kx
5 kx dlg_attrset(win, on_left);
5 kx (void) wmove(win, height - 3, 0);
5 kx (void) waddch(win, dlg_boxchar(ACS_LTEE));
5 kx for (i = 0; i < width - 2; i++)
5 kx (void) waddch(win, dlg_boxchar(ACS_HLINE));
5 kx dlg_attrset(win, on_right);
5 kx (void) waddch(win, dlg_boxchar(ACS_RTEE));
5 kx dlg_attrset(win, on_inside);
5 kx (void) wmove(win, height - 2, 1);
5 kx for (i = 0; i < width - 2; i++)
5 kx (void) waddch(win, ' ');
5 kx }
5 kx
5 kx void
5 kx dlg_draw_bottom_box(WINDOW *win)
5 kx {
5 kx dlg_draw_bottom_box2(win, border_attr, dialog_attr, dialog_attr);
5 kx }
5 kx
5 kx /*
5 kx * Remove a window, repainting everything else. This would be simpler if we
5 kx * used the panel library, but that is not _always_ available.
5 kx */
5 kx void
5 kx dlg_del_window(WINDOW *win)
5 kx {
5 kx DIALOG_WINDOWS *p, *q, *r;
5 kx
5 kx /*
5 kx * If --keep-window was set, do not delete/repaint the windows.
5 kx */
5 kx if (dialog_vars.keep_window)
5 kx return;
5 kx
5 kx /* Leave the main window untouched if there are no background windows.
5 kx * We do this so the current window will not be cleared on exit, allowing
5 kx * things like the infobox demo to run without flicker.
5 kx */
5 kx if (dialog_state.getc_callbacks != 0) {
5 kx touchwin(stdscr);
5 kx wnoutrefresh(stdscr);
5 kx }
5 kx
5 kx for (p = dialog_state.all_windows, q = r = 0; p != 0; r = p, p = p->next) {
5 kx if (p->normal == win) {
5 kx q = p; /* found a match - should be only one */
5 kx if (r == 0) {
5 kx dialog_state.all_windows = p->next;
5 kx } else {
5 kx r->next = p->next;
5 kx }
5 kx } else {
5 kx if (p->shadow != 0) {
5 kx touchwin(p->shadow);
5 kx wnoutrefresh(p->shadow);
5 kx }
5 kx touchwin(p->normal);
5 kx wnoutrefresh(p->normal);
5 kx }
5 kx }
5 kx
5 kx if (q) {
5 kx if (dialog_state.all_windows != 0)
5 kx erase_childs_shadow(q);
5 kx del_subwindows(q->normal);
5 kx dlg_unregister_window(q->normal);
5 kx delwin(q->normal);
5 kx free(q);
5 kx }
5 kx doupdate();
5 kx }
5 kx
5 kx /*
5 kx * Create a window, optionally with a shadow.
5 kx */
5 kx WINDOW *
5 kx dlg_new_window(int height, int width, int y, int x)
5 kx {
5 kx return dlg_new_modal_window(stdscr, height, width, y, x);
5 kx }
5 kx
5 kx /*
5 kx * "Modal" windows differ from normal ones by having a shadow in a window
5 kx * separate from the standard screen.
5 kx */
5 kx WINDOW *
5 kx dlg_new_modal_window(WINDOW *parent, int height, int width, int y, int x)
5 kx {
5 kx WINDOW *win;
5 kx DIALOG_WINDOWS *p = dlg_calloc(DIALOG_WINDOWS, 1);
5 kx
5 kx (void) parent;
5 kx if (p == 0
5 kx || (win = newwin(height, width, y, x)) == 0) {
5 kx dlg_exiterr("Can't make new window at (%d,%d), size (%d,%d).\n",
5 kx y, x, height, width);
5 kx }
5 kx p->next = dialog_state.all_windows;
5 kx p->normal = win;
5 kx p->getc_timeout = WTIMEOUT_OFF;
5 kx dialog_state.all_windows = p;
5 kx #ifdef HAVE_COLOR
5 kx if (dialog_state.use_shadow) {
5 kx p->shadow = parent;
5 kx draw_childs_shadow(p);
5 kx }
5 kx #endif
5 kx
5 kx (void) keypad(win, TRUE);
5 kx return win;
5 kx }
5 kx
5 kx /*
5 kx * dlg_getc() uses the return-value to determine how to handle an ERR return
5 kx * from a non-blocking read:
5 kx * a) if greater than zero, there was an expired timeout (blocking for a short
5 kx * time), or
5 kx * b) if zero, it was a non-blocking read, or
5 kx * c) if negative, an error occurred on a blocking read.
5 kx */
5 kx int
5 kx dlg_set_timeout(WINDOW *win, bool will_getc)
5 kx {
5 kx DIALOG_WINDOWS *p;
5 kx int result = 0;
5 kx
5 kx if ((p = SearchTopWindows(win)) != NULL) {
5 kx int interval = (dialog_vars.timeout_secs * 1000);
5 kx
5 kx if (will_getc || dialog_vars.pause_secs) {
5 kx interval = WTIMEOUT_VAL;
5 kx } else {
5 kx result = interval;
5 kx if (interval <= 0) {
5 kx interval = WTIMEOUT_OFF;
5 kx }
5 kx }
5 kx wtimeout(win, interval);
5 kx p->getc_timeout = interval;
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx void
5 kx dlg_reset_timeout(WINDOW *win)
5 kx {
5 kx DIALOG_WINDOWS *p;
5 kx
5 kx if ((p = SearchTopWindows(win)) != NULL) {
5 kx wtimeout(win, p->getc_timeout);
5 kx } else {
5 kx wtimeout(win, WTIMEOUT_OFF);
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Move/Resize a window, optionally with a shadow.
5 kx */
5 kx #ifdef KEY_RESIZE
5 kx void
5 kx dlg_move_window(WINDOW *win, int height, int width, int y, int x)
5 kx {
5 kx if (win != 0) {
5 kx DIALOG_WINDOWS *p;
5 kx
5 kx dlg_ctl_size(height, width);
5 kx
5 kx if ((p = SearchTopWindows(win)) != 0) {
5 kx (void) wresize(win, height, width);
5 kx (void) mvwin(win, y, x);
5 kx #ifdef HAVE_COLOR
5 kx if (p->shadow != 0) {
5 kx if (dialog_state.use_shadow) {
5 kx (void) mvwin(p->shadow, y + SHADOW_ROWS, x + SHADOW_COLS);
5 kx } else {
5 kx p->shadow = 0;
5 kx }
5 kx }
5 kx #endif
5 kx (void) refresh();
5 kx
5 kx #ifdef HAVE_COLOR
5 kx draw_childs_shadow(p);
5 kx #endif
5 kx }
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Having just received a KEY_RESIZE, wait a short time to ignore followup
5 kx * KEY_RESIZE events.
5 kx */
5 kx void
5 kx dlg_will_resize(WINDOW *win)
5 kx {
5 kx int n, base;
5 kx int caught = 0;
5 kx
5 kx dialog_state.had_resize = TRUE;
5 kx dlg_trace_win(win);
5 kx wtimeout(win, WTIMEOUT_VAL * 5);
5 kx
5 kx for (n = base = 0; n < base + 10; ++n) {
5 kx int ch;
5 kx
5 kx if ((ch = wgetch(win)) != ERR) {
5 kx if (ch == KEY_RESIZE) {
5 kx base = n;
5 kx ++caught;
5 kx } else if (ch != ERR) {
5 kx ungetch(ch);
5 kx break;
5 kx }
5 kx }
5 kx }
5 kx dlg_reset_timeout(win);
5 kx DLG_TRACE(("# caught %d KEY_RESIZE key%s\n",
5 kx 1 + caught,
5 kx caught == 1 ? "" : "s"));
5 kx }
5 kx #endif /* KEY_RESIZE */
5 kx
5 kx WINDOW *
5 kx dlg_der_window(WINDOW *parent, int height, int width, int y, int x)
5 kx {
5 kx WINDOW *win;
5 kx
5 kx /* existing uses of derwin are (almost) guaranteed to succeed, and the
5 kx * caller has to allow for failure.
5 kx */
5 kx if ((win = derwin(parent, height, width, y, x)) != 0) {
5 kx add_subwindow(parent, win);
5 kx (void) keypad(win, TRUE);
5 kx }
5 kx return win;
5 kx }
5 kx
5 kx WINDOW *
5 kx dlg_sub_window(WINDOW *parent, int height, int width, int y, int x)
5 kx {
5 kx WINDOW *win;
5 kx
5 kx if ((win = subwin(parent, height, width, y, x)) == 0) {
5 kx dlg_exiterr("Can't make sub-window at (%d,%d), size (%d,%d).\n",
5 kx y, x, height, width);
5 kx }
5 kx
5 kx add_subwindow(parent, win);
5 kx (void) keypad(win, TRUE);
5 kx return win;
5 kx }
5 kx
5 kx /* obsolete */
5 kx int
5 kx dlg_default_item(char **items, int llen)
5 kx {
5 kx int result = 0;
5 kx
5 kx if (dialog_vars.default_item != 0) {
5 kx int count = 0;
5 kx while (*items != 0) {
5 kx if (!strcmp(dialog_vars.default_item, *items)) {
5 kx result = count;
5 kx break;
5 kx }
5 kx items += llen;
5 kx count++;
5 kx }
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx int
5 kx dlg_default_listitem(DIALOG_LISTITEM * items)
5 kx {
5 kx int result = 0;
5 kx
5 kx if (dialog_vars.default_item != 0) {
5 kx int count = 0;
5 kx while (items->name != 0) {
5 kx if (!strcmp(dialog_vars.default_item, items->name)) {
5 kx result = count;
5 kx break;
5 kx }
5 kx ++items;
5 kx count++;
5 kx }
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx /*
5 kx * Draw the string for item_help
5 kx */
5 kx void
5 kx dlg_item_help(const char *txt)
5 kx {
5 kx if (USE_ITEM_HELP(txt)) {
5 kx chtype attr = A_NORMAL;
5 kx
5 kx dlg_attrset(stdscr, itemhelp_attr);
5 kx (void) wmove(stdscr, LINES - 1, 0);
5 kx (void) wclrtoeol(stdscr);
5 kx (void) addch(' ');
5 kx dlg_print_text(stdscr, txt, COLS - 1, &attr);
5 kx
5 kx if (itemhelp_attr & A_COLOR) {
5 kx int y, x;
5 kx /* fill the remainder of the line with the window's attributes */
5 kx getyx(stdscr, y, x);
5 kx (void) y;
5 kx while (x < COLS) {
5 kx (void) addch(' ');
5 kx ++x;
5 kx }
5 kx }
5 kx (void) wnoutrefresh(stdscr);
5 kx }
5 kx }
5 kx
5 kx #ifndef HAVE_STRCASECMP
5 kx int
5 kx dlg_strcmp(const char *a, const char *b)
5 kx {
5 kx int ac, bc, cmp;
5 kx
5 kx for (;;) {
5 kx ac = UCH(*a++);
5 kx bc = UCH(*b++);
5 kx if (isalpha(ac) && islower(ac))
5 kx ac = _toupper(ac);
5 kx if (isalpha(bc) && islower(bc))
5 kx bc = _toupper(bc);
5 kx cmp = ac - bc;
5 kx if (ac == 0 || bc == 0 || cmp != 0)
5 kx break;
5 kx }
5 kx return cmp;
5 kx }
5 kx #endif
5 kx
5 kx /*
5 kx * Returns true if 'dst' points to a blank which follows another blank which
5 kx * is not a leading blank on a line.
5 kx */
5 kx static bool
5 kx trim_blank(char *base, char *dst)
5 kx {
5 kx int count = !!isblank(UCH(*dst));
5 kx
5 kx while (dst-- != base) {
5 kx if (*dst == '\n') {
5 kx break;
5 kx } else if (isblank(UCH(*dst))) {
5 kx count++;
5 kx } else {
5 kx break;
5 kx }
5 kx }
5 kx return (count > 1);
5 kx }
5 kx
5 kx /*
5 kx * Change embedded "\n" substrings to '\n' characters and tabs to single
5 kx * spaces. If there are no "\n"s, it will strip all extra spaces, for
5 kx * justification. If it has "\n"'s, it will preserve extra spaces. If cr_wrap
5 kx * is set, it will preserve '\n's.
5 kx */
5 kx void
5 kx dlg_trim_string(char *s)
5 kx {
5 kx char *base = s;
5 kx char *p1;
5 kx char *p = s;
5 kx int has_newlines = !dialog_vars.no_nl_expand && (strstr(s, "\\n") != 0);
5 kx
5 kx while (*p != '\0') {
5 kx if (*p == TAB && !dialog_vars.nocollapse)
5 kx *p = ' ';
5 kx
5 kx if (has_newlines) { /* If prompt contains "\n" strings */
5 kx if (*p == '\\' && *(p + 1) == 'n') {
5 kx *s++ = '\n';
5 kx p += 2;
5 kx p1 = p;
5 kx /*
5 kx * Handle end of lines intelligently. If '\n' follows "\n"
5 kx * then ignore the '\n'. This eliminates the need to escape
5 kx * the '\n' character (no need to use "\n\").
5 kx */
5 kx while (isblank(UCH(*p1)))
5 kx p1++;
5 kx if (*p1 == '\n')
5 kx p = p1 + 1;
5 kx } else if (*p == '\n') {
5 kx if (dialog_vars.cr_wrap)
5 kx *s++ = *p++;
5 kx else {
5 kx /* Replace the '\n' with a space if cr_wrap is not set */
5 kx if (!trim_blank(base, p))
5 kx *s++ = ' ';
5 kx p++;
5 kx }
5 kx } else /* If *p != '\n' */
5 kx *s++ = *p++;
5 kx } else if (dialog_vars.trim_whitespace) {
5 kx if (isblank(UCH(*p))) {
5 kx if (!isblank(UCH(*(s - 1)))) {
5 kx *s++ = ' ';
5 kx p++;
5 kx } else
5 kx p++;
5 kx } else if (*p == '\n') {
5 kx if (dialog_vars.cr_wrap)
5 kx *s++ = *p++;
5 kx else if (!isblank(UCH(*(s - 1)))) {
5 kx /* Strip '\n's if cr_wrap is not set. */
5 kx *s++ = ' ';
5 kx p++;
5 kx } else
5 kx p++;
5 kx } else
5 kx *s++ = *p++;
5 kx } else { /* If there are no "\n" strings */
5 kx if (isblank(UCH(*p)) && !dialog_vars.nocollapse) {
5 kx if (!trim_blank(base, p))
5 kx *s++ = *p;
5 kx p++;
5 kx } else
5 kx *s++ = *p++;
5 kx }
5 kx }
5 kx
5 kx *s = '\0';
5 kx }
5 kx
5 kx void
5 kx dlg_set_focus(WINDOW *parent, WINDOW *win)
5 kx {
5 kx if (win != 0) {
5 kx (void) wmove(parent,
5 kx getpary(win) + getcury(win),
5 kx getparx(win) + getcurx(win));
5 kx (void) wnoutrefresh(win);
5 kx (void) doupdate();
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Returns the nominal maximum buffer size.
5 kx */
5 kx int
5 kx dlg_max_input(int max_len)
5 kx {
5 kx if (dialog_vars.max_input != 0 && dialog_vars.max_input < MAX_LEN)
5 kx max_len = dialog_vars.max_input;
5 kx
5 kx return max_len;
5 kx }
5 kx
5 kx /*
5 kx * Free storage used for the result buffer.
5 kx */
5 kx void
5 kx dlg_clr_result(void)
5 kx {
5 kx if (dialog_vars.input_length) {
5 kx dialog_vars.input_length = 0;
5 kx if (dialog_vars.input_result)
5 kx free(dialog_vars.input_result);
5 kx }
5 kx dialog_vars.input_result = 0;
5 kx }
5 kx
5 kx /*
5 kx * Setup a fixed-buffer for the result.
5 kx */
5 kx char *
5 kx dlg_set_result(const char *string)
5 kx {
5 kx unsigned need = string ? (unsigned) strlen(string) + 1 : 0;
5 kx
5 kx /* inputstr.c needs a fixed buffer */
5 kx if (need < MAX_LEN)
5 kx need = MAX_LEN;
5 kx
5 kx /*
5 kx * If the buffer is not big enough, allocate a new one.
5 kx */
5 kx if (dialog_vars.input_length != 0
5 kx || dialog_vars.input_result == 0
5 kx || need > MAX_LEN) {
5 kx
5 kx dlg_clr_result();
5 kx
5 kx dialog_vars.input_length = need;
5 kx dialog_vars.input_result = dlg_malloc(char, need);
5 kx assert_ptr(dialog_vars.input_result, "dlg_set_result");
5 kx }
5 kx
5 kx strcpy(dialog_vars.input_result, string ? string : "");
5 kx
5 kx return dialog_vars.input_result;
5 kx }
5 kx
5 kx /*
5 kx * Accumulate results in dynamically allocated buffer.
5 kx * If input_length is zero, it is a MAX_LEN buffer belonging to the caller.
5 kx */
5 kx void
5 kx dlg_add_result(const char *string)
5 kx {
5 kx unsigned have = (dialog_vars.input_result
5 kx ? (unsigned) strlen(dialog_vars.input_result)
5 kx : 0);
5 kx unsigned want = (unsigned) strlen(string) + 1 + have;
5 kx
5 kx if ((want >= MAX_LEN)
5 kx || (dialog_vars.input_length != 0)
5 kx || (dialog_vars.input_result == 0)) {
5 kx
5 kx if (dialog_vars.input_length == 0
5 kx || dialog_vars.input_result == 0) {
5 kx
5 kx char *save_result = dialog_vars.input_result;
5 kx
5 kx dialog_vars.input_length = want * 2;
5 kx dialog_vars.input_result = dlg_malloc(char, dialog_vars.input_length);
5 kx assert_ptr(dialog_vars.input_result, "dlg_add_result malloc");
5 kx dialog_vars.input_result[0] = '\0';
5 kx if (save_result != 0)
5 kx strcpy(dialog_vars.input_result, save_result);
5 kx } else if (want >= dialog_vars.input_length) {
5 kx dialog_vars.input_length = want * 2;
5 kx dialog_vars.input_result = dlg_realloc(char,
5 kx dialog_vars.input_length,
5 kx dialog_vars.input_result);
5 kx assert_ptr(dialog_vars.input_result, "dlg_add_result realloc");
5 kx }
5 kx }
5 kx strcat(dialog_vars.input_result, string);
5 kx }
5 kx
5 kx /*
5 kx * These are characters that (aside from the quote-delimiter) will have to
5 kx * be escaped in a single- or double-quoted string.
5 kx */
5 kx #define FIX_SINGLE "\n\\"
5 kx #define FIX_DOUBLE FIX_SINGLE "[]{}?*;`~#$^&()|<>"
5 kx
5 kx /*
5 kx * Returns the quote-delimiter.
5 kx */
5 kx static const char *
5 kx quote_delimiter(void)
5 kx {
5 kx return dialog_vars.single_quoted ? "'" : "\"";
5 kx }
5 kx
5 kx /*
5 kx * Returns true if we should quote the given string.
5 kx */
5 kx static bool
5 kx must_quote(char *string)
5 kx {
5 kx bool code = FALSE;
5 kx
5 kx if (*string != '\0') {
5 kx size_t len = strlen(string);
5 kx if (strcspn(string, quote_delimiter()) != len)
5 kx code = TRUE;
5 kx else if (strcspn(string, "\n\t ") != len)
5 kx code = TRUE;
5 kx else
5 kx code = (strcspn(string, FIX_DOUBLE) != len);
5 kx } else {
5 kx code = TRUE;
5 kx }
5 kx
5 kx return code;
5 kx }
5 kx
5 kx /*
5 kx * Add a quoted string to the result buffer.
5 kx */
5 kx void
5 kx dlg_add_quoted(char *string)
5 kx {
5 kx char temp[2];
5 kx const char *my_quote = quote_delimiter();
5 kx const char *must_fix = (dialog_vars.single_quoted
5 kx ? FIX_SINGLE
5 kx : FIX_DOUBLE);
5 kx
5 kx if (must_quote(string)) {
5 kx temp[1] = '\0';
5 kx dlg_add_result(my_quote);
5 kx while (*string != '\0') {
5 kx temp[0] = *string++;
5 kx if ((strchr) (my_quote, *temp) || (strchr) (must_fix, *temp))
5 kx dlg_add_result("\\");
5 kx dlg_add_result(temp);
5 kx }
5 kx dlg_add_result(my_quote);
5 kx } else {
5 kx dlg_add_result(string);
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * When adding a result, make that depend on whether "--quoted" is used.
5 kx */
5 kx void
5 kx dlg_add_string(char *string)
5 kx {
5 kx if (dialog_vars.quoted) {
5 kx dlg_add_quoted(string);
5 kx } else {
5 kx dlg_add_result(string);
5 kx }
5 kx }
5 kx
5 kx bool
5 kx dlg_need_separator(void)
5 kx {
5 kx bool result = FALSE;
5 kx
5 kx if (dialog_vars.output_separator) {
5 kx result = TRUE;
5 kx } else if (dialog_vars.input_result && *(dialog_vars.input_result)) {
5 kx result = TRUE;
5 kx }
5 kx return result;
5 kx }
5 kx
5 kx void
5 kx dlg_add_separator(void)
5 kx {
5 kx const char *separator = (dialog_vars.separate_output) ? "\n" : " ";
5 kx
5 kx if (dialog_vars.output_separator)
5 kx separator = dialog_vars.output_separator;
5 kx
5 kx dlg_add_result(separator);
5 kx }
5 kx
5 kx #define HELP_PREFIX "HELP "
5 kx
5 kx void
5 kx dlg_add_help_listitem(int *result, char **tag, DIALOG_LISTITEM * item)
5 kx {
5 kx dlg_add_result(HELP_PREFIX);
5 kx if (USE_ITEM_HELP(item->help)) {
5 kx *tag = dialog_vars.help_tags ? item->name : item->help;
5 kx *result = DLG_EXIT_ITEM_HELP;
5 kx } else {
5 kx *tag = item->name;
5 kx }
5 kx }
5 kx
5 kx void
5 kx dlg_add_help_formitem(int *result, char **tag, DIALOG_FORMITEM * item)
5 kx {
5 kx dlg_add_result(HELP_PREFIX);
5 kx if (USE_ITEM_HELP(item->help)) {
5 kx *tag = dialog_vars.help_tags ? item->name : item->help;
5 kx *result = DLG_EXIT_ITEM_HELP;
5 kx } else {
5 kx *tag = item->name;
5 kx }
5 kx }
5 kx
5 kx /*
5 kx * Some widgets support only one value of a given variable - save/restore the
5 kx * global dialog_vars so we can override it consistently.
5 kx */
5 kx void
5 kx dlg_save_vars(DIALOG_VARS * vars)
5 kx {
5 kx *vars = dialog_vars;
5 kx }
5 kx
5 kx /*
5 kx * Most of the data in DIALOG_VARS is normally set by command-line options.
5 kx * The input_result member is an exception; it is normally set by the dialog
5 kx * library to return result values.
5 kx */
5 kx void
5 kx dlg_restore_vars(DIALOG_VARS * vars)
5 kx {
5 kx char *save_result = dialog_vars.input_result;
5 kx unsigned save_length = dialog_vars.input_length;
5 kx
5 kx dialog_vars = *vars;
5 kx dialog_vars.input_result = save_result;
5 kx dialog_vars.input_length = save_length;
5 kx }
5 kx
5 kx /*
5 kx * Called each time a widget is invoked which may do output, increment a count.
5 kx */
5 kx void
5 kx dlg_does_output(void)
5 kx {
5 kx dialog_state.output_count += 1;
5 kx }
5 kx
5 kx /*
5 kx * Compatibility for different versions of curses.
5 kx */
5 kx #if !(defined(HAVE_GETBEGX) && defined(HAVE_GETBEGY))
5 kx int
5 kx dlg_getbegx(WINDOW *win)
5 kx {
5 kx int y, x;
5 kx getbegyx(win, y, x);
5 kx (void) y;
5 kx return x;
5 kx }
5 kx int
5 kx dlg_getbegy(WINDOW *win)
5 kx {
5 kx int y, x;
5 kx getbegyx(win, y, x);
5 kx (void) x;
5 kx return y;
5 kx }
5 kx #endif
5 kx
5 kx #if !(defined(HAVE_GETCURX) && defined(HAVE_GETCURY))
5 kx int
5 kx dlg_getcurx(WINDOW *win)
5 kx {
5 kx int y, x;
5 kx getyx(win, y, x);
5 kx (void) y;
5 kx return x;
5 kx }
5 kx int
5 kx dlg_getcury(WINDOW *win)
5 kx {
5 kx int y, x;
5 kx getyx(win, y, x);
5 kx (void) x;
5 kx return y;
5 kx }
5 kx #endif
5 kx
5 kx #if !(defined(HAVE_GETMAXX) && defined(HAVE_GETMAXY))
5 kx int
5 kx dlg_getmaxx(WINDOW *win)
5 kx {
5 kx int y, x;
5 kx getmaxyx(win, y, x);
5 kx (void) y;
5 kx return x;
5 kx }
5 kx int
5 kx dlg_getmaxy(WINDOW *win)
5 kx {
5 kx int y, x;
5 kx getmaxyx(win, y, x);
5 kx (void) x;
5 kx return y;
5 kx }
5 kx #endif
5 kx
5 kx #if !(defined(HAVE_GETPARX) && defined(HAVE_GETPARY))
5 kx int
5 kx dlg_getparx(WINDOW *win)
5 kx {
5 kx int y, x;
5 kx getparyx(win, y, x);
5 kx (void) y;
5 kx return x;
5 kx }
5 kx int
5 kx dlg_getpary(WINDOW *win)
5 kx {
5 kx int y, x;
5 kx getparyx(win, y, x);
5 kx (void) x;
5 kx return y;
5 kx }
5 kx #endif
5 kx
5 kx #ifdef NEED_WGETPARENT
5 kx WINDOW *
5 kx dlg_wgetparent(WINDOW *win)
5 kx {
5 kx #undef wgetparent
5 kx WINDOW *result = 0;
5 kx DIALOG_WINDOWS *p;
5 kx
5 kx for (p = dialog_state.all_subwindows; p != 0; p = p->next) {
5 kx if (p->shadow == win) {
5 kx result = p->normal;
5 kx break;
5 kx }
5 kx }
5 kx return result;
5 kx }
5 kx #endif