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