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