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