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