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