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