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