1 /* 2 * $Id: dialog.c,v 1.268 2018/06/21 09:16:05 tom Exp $ 3 * 4 * cdialog - Display simple dialog boxes from shell scripts 5 * 6 * Copyright 2000-2017,2018 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 <string.h> 29 #include <sys/types.h> 30 #include <sys/stat.h> 31 32 #ifdef HAVE_SETLOCALE 33 #include <locale.h> 34 #endif 35 36 #define PASSARGS t, av, offset_add 37 #define CALLARGS const char *t, char *av[], int *offset_add 38 typedef int (callerFn) (CALLARGS); 39 40 typedef enum { 41 o_unknown = 0 42 ,o_allow_close 43 ,o_and_widget 44 ,o_ascii_lines 45 ,o_aspect 46 ,o_auto_placement 47 ,o_backtitle 48 ,o_beep 49 ,o_beep_after 50 ,o_begin 51 ,o_cancel_label 52 ,o_checklist 53 ,o_clear 54 ,o_colors 55 ,o_column_separator 56 ,o_cr_wrap 57 ,o_create_rc 58 ,o_date_format 59 ,o_default_button 60 ,o_default_item 61 ,o_defaultno 62 ,o_exit_label 63 ,o_extra_button 64 ,o_extra_label 65 ,o_fixed_font 66 ,o_form 67 ,o_gauge 68 ,o_help 69 ,o_help_button 70 ,o_help_file 71 ,o_help_label 72 ,o_help_line 73 ,o_help_status 74 ,o_help_tags 75 ,o_icon 76 ,o_ignore 77 ,o_infobox 78 ,o_input_fd 79 ,o_inputbox 80 ,o_inputmenu 81 ,o_insecure 82 ,o_item_help 83 ,o_keep_colors 84 ,o_keep_tite 85 ,o_keep_window 86 ,o_last_key 87 ,o_max_input 88 ,o_menu 89 ,o_mixedform 90 ,o_mixedgauge 91 ,o_msgbox 92 ,o_no_close 93 ,o_no_collapse 94 ,o_no_cr_wrap 95 ,o_no_kill 96 ,o_no_label 97 ,o_no_lines 98 ,o_no_mouse 99 ,o_no_nl_expand 100 ,o_no_shadow 101 ,o_nocancel 102 ,o_nook 103 ,o_ok_label 104 ,o_output_fd 105 ,o_output_separator 106 ,o_passwordbox 107 ,o_passwordform 108 ,o_pause 109 ,o_prgbox 110 ,o_print_maxsize 111 ,o_print_size 112 ,o_print_text_only 113 ,o_print_text_size 114 ,o_print_version 115 ,o_programbox 116 ,o_progressbox 117 ,o_quoted 118 ,o_radiolist 119 ,o_screen_center 120 ,o_scrollbar 121 ,o_separate_output 122 ,o_separate_widget 123 ,o_separator 124 ,o_shadow 125 ,o_single_quoted 126 ,o_size_err 127 ,o_sleep 128 ,o_smooth 129 ,o_stderr 130 ,o_stdout 131 ,o_tab_correct 132 ,o_tab_len 133 ,o_tailbox 134 ,o_tailboxbg 135 ,o_textbox 136 ,o_time_format 137 ,o_timeout 138 ,o_title 139 ,o_trim 140 ,o_under_mouse 141 ,o_version 142 ,o_visit_items 143 ,o_wmclass 144 ,o_yes_label 145 ,o_yesno 146 #ifdef HAVE_WHIPTAIL 147 ,o_fullbutton 148 ,o_topleft 149 #endif 150 #ifdef HAVE_XDIALOG 151 ,o_calendar 152 ,o_dselect 153 ,o_editbox 154 ,o_fselect 155 ,o_timebox 156 ,o_week_start 157 #endif 158 #ifdef HAVE_XDIALOG2 159 ,o_buildlist 160 ,o_rangebox 161 ,o_reorder 162 ,o_treeview 163 #endif 164 #if defined(HAVE_XDIALOG2) || defined(HAVE_WHIPTAIL) 165 ,o_no_items 166 ,o_no_tags 167 #endif 168 #ifdef HAVE_DLG_TRACE 169 ,o_trace 170 #endif 171 ,o_iso_week 172 } eOptions; 173 174 /* 175 * The bits in 'pass' are used to decide which options are applicable at 176 * different stages in the program: 177 * 1 flags before widgets 178 * 2 widgets 179 * 4 non-widget options 180 */ 181 typedef struct { 182 const char *name; 183 eOptions code; 184 int pass; /* 1,2,4 or combination */ 185 const char *help; /* NULL to suppress, non-empty to display params */ 186 } Options; 187 188 typedef struct { 189 eOptions code; 190 int argmin, argmax; 191 callerFn *jumper; 192 } Mode; 193 194 static int known_opts = 0; 195 static const char **dialog_opts; 196 static char **dialog_argv; 197 198 static char **special_argv = 0; 199 static int special_argc = 0; 200 201 static bool ignore_unknown = FALSE; 202 203 static const char *program = "dialog"; 204 205 #ifdef NO_LEAKS 206 typedef struct _all_blobs { 207 struct _all_blobs *next; 208 void *blob; 209 } AllBlobs; 210 211 static AllBlobs *all_blobs; 212 #endif 213 214 /* 215 * The options[] table is organized this way to make it simple to maintain 216 * a sorted list of options for the help-message. 217 */ 218 /* *INDENT-OFF* */ 219 static const Options options[] = { 220 { "allow-close", o_allow_close, 1, NULL }, 221 { "and-widget", o_and_widget, 4, NULL }, 222 { "ascii-lines", o_ascii_lines, 1, "" }, 223 { "aspect", o_aspect, 1, "<ratio>" }, 224 { "auto-placement", o_auto_placement, 1, NULL }, 225 { "backtitle", o_backtitle, 1, "<backtitle>" }, 226 { "beep", o_beep, 1, "" }, 227 { "beep-after", o_beep_after, 1, "" }, 228 { "begin", o_begin, 1, "<y> <x>" }, 229 { "cancel-label", o_cancel_label, 1, "<str>" }, 230 { "checklist", o_checklist, 2, "<text> <height> <width> <list height> <tag1> <item1> <status1>..." }, 231 { "clear", o_clear, 1, "" }, 232 { "colors", o_colors, 1, "" }, 233 { "column-separator",o_column_separator, 1, "<str>" }, 234 { "cr-wrap", o_cr_wrap, 1, "" }, 235 { "create-rc", o_create_rc, 1, NULL }, 236 { "date-format", o_date_format, 1, "<str>" }, 237 { "default-button", o_default_button, 1, "<str>" }, 238 { "default-item", o_default_item, 1, "<str>" }, 239 { "defaultno", o_defaultno, 1, "" }, 240 { "exit-label", o_exit_label, 1, "<str>" }, 241 { "extra-button", o_extra_button, 1, "" }, 242 { "extra-label", o_extra_label, 1, "<str>" }, 243 { "fixed-font", o_fixed_font, 1, NULL }, 244 { "form", o_form, 2, "<text> <height> <width> <form height> <label1> <l_y1> <l_x1> <item1> <i_y1> <i_x1> <flen1> <ilen1>..." }, 245 { "gauge", o_gauge, 2, "<text> <height> <width> [<percent>]" }, 246 { "guage", o_gauge, 2, NULL }, 247 { "help", o_help, 4, "" }, 248 { "help-button", o_help_button, 1, "" }, 249 { "help-label", o_help_label, 1, "<str>" }, 250 { "help-status", o_help_status, 1, "" }, 251 { "help-tags", o_help_tags, 1, "" }, 252 { "hfile", o_help_file, 1, "<str>" }, 253 { "hline", o_help_line, 1, "<str>" }, 254 { "icon", o_icon, 1, NULL }, 255 { "ignore", o_ignore, 1, "" }, 256 { "infobox", o_infobox, 2, "<text> <height> <width>" }, 257 { "input-fd", o_input_fd, 1, "<fd>" }, 258 { "inputbox", o_inputbox, 2, "<text> <height> <width> [<init>]" }, 259 { "inputmenu", o_inputmenu, 2, "<text> <height> <width> <menu height> <tag1> <item1>..." }, 260 { "insecure", o_insecure, 1, "" }, 261 { "item-help", o_item_help, 1, "" }, 262 { "keep-colors", o_keep_colors, 1, NULL }, 263 { "keep-tite", o_keep_tite, 1, "" }, 264 { "keep-window", o_keep_window, 1, "" }, 265 { "last-key", o_last_key, 1, "" }, 266 { "max-input", o_max_input, 1, "<n>" }, 267 { "menu", o_menu, 2, "<text> <height> <width> <menu height> <tag1> <item1>..." }, 268 { "mixedform", o_mixedform, 2, "<text> <height> <width> <form height> <label1> <l_y1> <l_x1> <item1> <i_y1> <i_x1> <flen1> <ilen1> <itype>..." }, 269 { "mixedgauge", o_mixedgauge, 2, "<text> <height> <width> <percent> <tag1> <item1>..." }, 270 { "msgbox", o_msgbox, 2, "<text> <height> <width>" }, 271 { "no-cancel", o_nocancel, 1, "" }, 272 { "no-close", o_no_close, 1, NULL }, 273 { "no-collapse", o_no_collapse, 1, "" }, 274 { "no-cr-wrap", o_no_cr_wrap, 1, "" }, 275 { "no-kill", o_no_kill, 1, "" }, 276 { "no-label", o_no_label, 1, "<str>" }, 277 { "no-lines", o_no_lines, 1, "" }, 278 { "no-mouse", o_no_mouse, 1, "" }, 279 { "no-nl-expand", o_no_nl_expand, 1, "" }, 280 { "no-ok", o_nook, 1, "" }, 281 { "no-shadow", o_no_shadow, 1, "" }, 282 { "nocancel", o_nocancel, 1, NULL }, /* see --no-cancel */ 283 { "nook", o_nook, 1, "" }, /* See no-ok */ 284 { "ok-label", o_ok_label, 1, "<str>" }, 285 { "output-fd", o_output_fd, 1, "<fd>" }, 286 { "output-separator",o_output_separator, 1, "<str>" }, 287 { "passwordbox", o_passwordbox, 2, "<text> <height> <width> [<init>]" }, 288 { "passwordform", o_passwordform, 2, "<text> <height> <width> <form height> <label1> <l_y1> <l_x1> <item1> <i_y1> <i_x1> <flen1> <ilen1>..." }, 289 { "pause", o_pause, 2, "<text> <height> <width> <seconds>" }, 290 { "prgbox", o_prgbox, 2, "<text> <command> <height> <width>" }, 291 { "print-maxsize", o_print_maxsize, 1, "" }, 292 { "print-size", o_print_size, 1, "" }, 293 { "print-text-only",o_print_text_only, 5, "<text> <height> <width>" }, 294 { "print-text-size",o_print_text_size, 5, "<text> <height> <width>" }, 295 { "print-version", o_print_version, 5, "" }, 296 { "programbox", o_programbox, 2, "<text> <height> <width>" }, 297 { "progressbox", o_progressbox, 2, "<text> <height> <width>" }, 298 { "quoted", o_quoted, 1, "" }, 299 { "radiolist", o_radiolist, 2, "<text> <height> <width> <list height> <tag1> <item1> <status1>..." }, 300 { "screen-center", o_screen_center, 1, NULL }, 301 { "scrollbar", o_scrollbar, 1, "" }, 302 { "separate-output",o_separate_output, 1, "" }, 303 { "separate-widget",o_separate_widget, 1, "<str>" }, 304 { "separator", o_separator, 1, NULL }, 305 { "shadow", o_shadow, 1, "" }, 306 { "single-quoted", o_single_quoted, 1, "" }, 307 { "size-err", o_size_err, 1, "" }, 308 { "sleep", o_sleep, 1, "<secs>" }, 309 { "smooth", o_smooth, 1, NULL }, 310 { "stderr", o_stderr, 1, "" }, 311 { "stdout", o_stdout, 1, "" }, 312 { "tab-correct", o_tab_correct, 1, "" }, 313 { "tab-len", o_tab_len, 1, "<n>" }, 314 { "tailbox", o_tailbox, 2, "<file> <height> <width>" }, 315 { "tailboxbg", o_tailboxbg, 2, "<file> <height> <width>" }, 316 { "textbox", o_textbox, 2, "<file> <height> <width>" }, 317 { "time-format", o_time_format, 1, "<str>" }, 318 { "timeout", o_timeout, 1, "<secs>" }, 319 { "title", o_title, 1, "<title>" }, 320 { "trim", o_trim, 1, "" }, 321 { "under-mouse", o_under_mouse, 1, NULL }, 322 { "version", o_version, 5, "" }, 323 { "visit-items", o_visit_items, 1, "" }, 324 { "wmclass", o_wmclass, 1, NULL }, 325 { "yes-label", o_yes_label, 1, "<str>" }, 326 { "yesno", o_yesno, 2, "<text> <height> <width>" }, 327 #ifdef HAVE_WHIPTAIL 328 { "cancel-button", o_cancel_label, 1, NULL }, 329 { "fb", o_fullbutton, 1, NULL }, 330 { "fullbutton", o_fullbutton, 1, NULL }, 331 { "no-button", o_no_label, 1, NULL }, 332 { "ok-button", o_ok_label, 1, NULL }, 333 { "scrolltext", o_scrollbar, 1, NULL }, 334 { "topleft", o_topleft, 1, NULL }, 335 { "yes-button", o_yes_label, 1, NULL }, 336 #endif 337 #ifdef HAVE_XDIALOG 338 { "calendar", o_calendar, 2, "<text> <height> <width> <day> <month> <year>" }, 339 { "dselect", o_dselect, 2, "<directory> <height> <width>" }, 340 { "editbox", o_editbox, 2, "<file> <height> <width>" }, 341 { "fselect", o_fselect, 2, "<filepath> <height> <width>" }, 342 { "timebox", o_timebox, 2, "<text> <height> <width> <hour> <minute> <second>" }, 343 { "week-start", o_week_start, 1, "<str>" }, 344 { "iso-week", o_iso_week, 1, NULL }, 345 #endif 346 #ifdef HAVE_XDIALOG2 347 { "buildlist", o_buildlist, 2, "<text> <height> <width> <list-height> <tag1> <item1> <status1>..." }, 348 { "no-items", o_no_items, 1, "" }, 349 { "no-tags", o_no_tags, 1, "" }, 350 { "rangebox", o_rangebox, 2, "<text> <height> <width> <min-value> <max-value> <default-value>" }, 351 { "reorder", o_reorder, 1, "" }, 352 { "treeview", o_treeview, 2, "<text> <height> <width> <list-height> <tag1> <item1> <status1> <depth1>..." }, 353 #endif 354 #if defined(HAVE_XDIALOG2) || defined(HAVE_WHIPTAIL) 355 { "noitem", o_no_items, 1, NULL }, 356 { "notags", o_no_tags, 1, NULL }, 357 #endif 358 #ifdef HAVE_DLG_TRACE 359 { "trace", o_trace, 1, "<file>" }, 360 #endif 361 }; 362 /* *INDENT-ON* */ 363 364 #ifdef NO_LEAKS 365 static void 366 ignore_leak(void *value) 367 { 368 AllBlobs *next = dlg_calloc(AllBlobs, (size_t) 1); 369 if (next != 0) { 370 next->blob = value; 371 next->next = all_blobs; 372 all_blobs = next; 373 } 374 } 375 376 static void 377 handle_leaks(void) 378 { 379 while (all_blobs != 0) { 380 char *blob = all_blobs->blob; 381 AllBlobs *next = all_blobs->next; 382 free(blob); 383 free(all_blobs); 384 all_blobs = next; 385 } 386 free(dialog_opts); 387 if (special_argv != 0) { 388 free(special_argv[0]); 389 free(special_argv); 390 special_argv = 0; 391 special_argc = 0; 392 } 393 } 394 #else 395 #define handle_leaks() /* nothing */ 396 #define ignore_leak(n) /* nothing */ 397 #endif 398 399 #define OptionChars "\ 400 0123456789\ 401 -\ 402 abcdefghijklmnopqrstuvwxyz\ 403 " 404 405 /* 406 * Check if the given string from main's argv is an option. 407 */ 408 static bool 409 isOption(const char *arg) 410 { 411 bool result = FALSE; 412 413 if (arg != 0) { 414 if (dialog_opts != 0) { 415 int n; 416 for (n = 0; dialog_opts[n] != 0; ++n) { 417 if (dialog_opts[n] == arg) { 418 result = TRUE; 419 break; 420 } 421 } 422 } else if (!strncmp(arg, "--", (size_t) 2) && isalpha(UCH(arg[2]))) { 423 if (strlen(arg) == (strspn) (arg, OptionChars)) { 424 result = TRUE; 425 } else { 426 handle_leaks(); 427 dlg_exiterr("Invalid option \"%s\"", arg); 428 } 429 } 430 } 431 return result; 432 } 433 434 /* 435 * Make an array showing which argv[] entries are options. Use "--" as a 436 * special token to escape the next argument, allowing it to begin with "--". 437 * When we find a "--" argument, also remove it from argv[] and adjust argc. 438 * That appears to be an undocumented feature of the popt library. 439 * 440 * Also, if we see a "--file", expand it into the parameter list by reading the 441 * text from the given file and stripping quotes, treating whitespace outside 442 * quotes as a parameter delimiter. 443 * 444 * Finally, if we see a "--args", dump the current list of arguments to the 445 * standard error. This is used for debugging complex --file combinations. 446 */ 447 static void 448 unescape_argv(int *argcp, char ***argvp) 449 { 450 int j, k; 451 int limit_includes = 20 + *argcp; 452 int count_includes = 0; 453 bool doalloc = FALSE; 454 char *filename; 455 const char **my_argv = 0; 456 int my_argc; 457 458 DLG_TRACE(("# unescape_argv\n")); 459 for (k = 0; k < 2; ++k) { 460 461 my_argc = 0; 462 if (special_argv != 0) { 463 for (j = 0; special_argv[j] != 0; ++j) { 464 if (!strcmp(special_argv[j], "--")) { 465 break; 466 } else if (isOption(special_argv[j])) { 467 if (k != 0) 468 my_argv[my_argc] = special_argv[j]; 469 my_argc++; 470 } 471 } 472 } 473 474 if (k == 0) { 475 my_argc += (*argcp + 1); 476 my_argv = dlg_calloc(const char *, (size_t) my_argc); 477 assert_ptr(my_argv, "unescape_argv"); 478 } 479 } 480 481 for (j = 1; j < *argcp; j++) { 482 bool escaped = FALSE; 483 if (!strcmp((*argvp)[j], "--")) { 484 escaped = TRUE; 485 dlg_eat_argv(argcp, argvp, j, 1); 486 } else if (!strcmp((*argvp)[j], "--args")) { 487 fprintf(stderr, "Showing arguments at arg%d\n", j); 488 for (k = 0; k < *argcp; ++k) { 489 fprintf(stderr, " arg%d:%s\n", k, (*argvp)[k]); 490 } 491 dlg_eat_argv(argcp, argvp, j, 1); 492 --j; 493 } else if (!strcmp((*argvp)[j], "--file")) { 494 if (++count_includes > limit_includes) { 495 handle_leaks(); 496 dlg_exiterr("Too many --file options"); 497 } 498 499 if ((filename = (*argvp)[j + 1]) != 0) { 500 FILE *fp; 501 char **list; 502 char *blob; 503 int added; 504 size_t bytes_read; 505 size_t length; 506 int n; 507 508 if (*filename == '&') { 509 fp = fdopen(atoi(filename + sizeof(char)), "r"); 510 } else { 511 fp = fopen(filename, "r"); 512 } 513 514 if (fp) { 515 DLG_TRACE(("# opened --file %s ..\n", filename)); 516 blob = NULL; 517 length = 0; 518 do { 519 blob = dlg_realloc(char, length + BUFSIZ + 1, blob); 520 assert_ptr(blob, "unescape_argv"); 521 bytes_read = fread(blob + length, 522 sizeof(char), 523 (size_t) BUFSIZ, 524 fp); 525 length += bytes_read; 526 if (ferror(fp)) { 527 handle_leaks(); 528 dlg_exiterr("error on filehandle in unescape_argv"); 529 } 530 } while (bytes_read == BUFSIZ); 531 fclose(fp); 532 533 blob[length] = '\0'; 534 ignore_leak(blob); 535 536 list = dlg_string_to_argv(blob); 537 added = dlg_count_argv(list); 538 if (added > 2) { 539 /* *argcp arguments before the expansion of --file 540 - 2 for the removal of '--file <filepath>' 541 + added for the arguments contained in <filepath> 542 + 1 for the terminating NULL pointer */ 543 size_t need = (size_t) (*argcp + added - 1); 544 if (doalloc) { 545 *argvp = dlg_realloc(char *, need, *argvp); 546 assert_ptr(*argvp, "unescape_argv"); 547 } else { 548 char **newp = dlg_malloc(char *, need); 549 ignore_leak(newp); 550 assert_ptr(newp, "unescape_argv"); 551 for (n = 0; n < *argcp; ++n) { 552 newp[n] = (*argvp)[n]; 553 } 554 /* The new array is not NULL-terminated yet. */ 555 *argvp = newp; 556 doalloc = TRUE; 557 } 558 my_argv = dlg_realloc(const char *, need, my_argv); 559 assert_ptr(my_argv, "unescape_argv"); 560 561 /* Shift the arguments after '--file <filepath>' 562 right by (added - 2) positions */ 563 for (n = *argcp - 1; n >= j + 2; --n) { 564 (*argvp)[n + added - 2] = (*argvp)[n]; 565 } 566 } else if (added < 2) { 567 /* 0 or 1 argument read from the included file 568 -> shift the arguments after '--file <filepath>' 569 left by (2 - added) positions */ 570 for (n = j + added; n + 2 - added < *argcp; ++n) { 571 (*argvp)[n] = (*argvp)[n + 2 - added]; 572 } 573 } 574 /* Copy the inserted arguments to *argvp */ 575 for (n = 0; n < added; ++n) { 576 (*argvp)[n + j] = list[n]; 577 } 578 *argcp += added - 2; 579 (*argvp)[*argcp] = 0; /* Write the NULL terminator */ 580 free(list); /* No-op if 'list' is NULL */ 581 /* Force rescan starting from the first inserted argument */ 582 --j; 583 DLG_TRACE(("# finished --file\n")); 584 continue; 585 } else { 586 handle_leaks(); 587 dlg_exiterr("Cannot open --file %s", filename); 588 } 589 } else { 590 handle_leaks(); 591 dlg_exiterr("No value given for --file"); 592 } 593 } 594 if (!escaped 595 && (*argvp)[j] != 0 596 && !strncmp((*argvp)[j], "--", (size_t) 2) 597 && isalpha(UCH((*argvp)[j][2]))) { 598 my_argv[my_argc++] = (*argvp)[j]; 599 DLG_TRACE(("#\toption argv[%d]=%s\n", j, (*argvp)[j])); 600 } 601 } 602 603 my_argv[my_argc] = 0; 604 605 known_opts = my_argc; 606 dialog_opts = my_argv; 607 608 DLG_TRACE(("#\t%d options vs %d arguments\n", known_opts, *argcp)); 609 dialog_argv = (*argvp); 610 } 611 612 static eOptions 613 lookupOption(const char *name, int pass) 614 { 615 unsigned n; 616 eOptions result = o_unknown; 617 618 if (isOption(name)) { 619 name += 2; 620 for (n = 0; n < sizeof(options) / sizeof(options[0]); n++) { 621 if ((pass & options[n].pass) != 0 622 && !strcmp(name, options[n].name)) { 623 result = options[n].code; 624 break; 625 } 626 } 627 } 628 return result; 629 } 630 631 static void 632 Usage(const char *msg) 633 { 634 handle_leaks(); 635 dlg_exiterr("Error: %s.\nUse --help to list options.\n\n", msg); 636 } 637 638 /* 639 * Count arguments, stopping at the end of the argument list, or on any of our 640 * "--" tokens. 641 */ 642 static int 643 arg_rest(char *argv[]) 644 { 645 int i = 1; /* argv[0] points to a "--" token */ 646 647 while (argv[i] != 0 648 && (!isOption(argv[i]) || lookupOption(argv[i], 7) == o_unknown)) 649 i++; 650 return i; 651 } 652 653 /* 654 * In MultiWidget this function is needed to count how many tags 655 * a widget (menu, checklist, radiolist) has 656 */ 657 static int 658 howmany_tags(char *argv[], int group) 659 { 660 int result = 0; 661 int have; 662 char temp[80]; 663 664 while (argv[0] != 0) { 665 if (isOption(argv[0])) 666 break; 667 if ((have = arg_rest(argv)) < group) { 668 const char *format = _("Expected %d arguments, found only %d"); 669 sprintf(temp, format, group, have); 670 Usage(temp); 671 } else if ((have % group) != 0) { 672 const char *format = _("Expected %d arguments, found extra %d"); 673 sprintf(temp, format, group, (have % group)); 674 Usage(temp); 675 } 676 677 argv += have; 678 result += (have / group); 679 } 680 681 return result; 682 } 683 684 static int 685 numeric_arg(char **av, int n) 686 { 687 int result = 0; 688 689 if (n < dlg_count_argv(av)) { 690 char msg[80]; 691 char *last = 0; 692 result = (int) strtol(av[n], &last, 10); 693 694 if (last == 0 || *last != 0) { 695 sprintf(msg, "Expected a number for token %d of %.20s", n, av[0]); 696 Usage(msg); 697 } 698 } 699 return result; 700 } 701 702 static char * 703 optional_str(char **av, int n, char *dft) 704 { 705 char *ret = dft; 706 if (arg_rest(av) > n) 707 ret = av[n]; 708 return ret; 709 } 710 711 #if defined(HAVE_DLG_GAUGE) || defined(HAVE_XDIALOG) 712 static int 713 optional_num(char **av, int n, int dft) 714 { 715 int ret = dft; 716 if (arg_rest(av) > n) 717 ret = numeric_arg(av, n); 718 return ret; 719 } 720 #endif 721 722 /* 723 * On AIX 4.x, we have to flush the output right away since there is a bug in 724 * the curses package which discards stdout even when we've used newterm to 725 * redirect output to /dev/tty. 726 */ 727 static int 728 show_result(int ret) 729 { 730 bool either = FALSE; 731 732 switch (ret) { 733 case DLG_EXIT_OK: 734 case DLG_EXIT_EXTRA: 735 case DLG_EXIT_HELP: 736 case DLG_EXIT_ITEM_HELP: 737 if ((dialog_state.output_count > 1) && !dialog_vars.separate_output) { 738 fputs((dialog_state.separate_str 739 ? dialog_state.separate_str 740 : DEFAULT_SEPARATE_STR), 741 dialog_state.output); 742 either = TRUE; 743 } 744 if (dialog_vars.input_result != 0 745 && dialog_vars.input_result[0] != '\0') { 746 fputs(dialog_vars.input_result, dialog_state.output); 747 DLG_TRACE(("# input_result:\n%s\n", dialog_vars.input_result)); 748 either = TRUE; 749 } 750 if (either) { 751 fflush(dialog_state.output); 752 } 753 break; 754 } 755 return ret; 756 } 757 758 /* 759 * These are the widget callers. 760 */ 761 762 static int 763 call_yesno(CALLARGS) 764 { 765 *offset_add = 4; 766 return dialog_yesno(t, 767 av[1], 768 numeric_arg(av, 2), 769 numeric_arg(av, 3)); 770 } 771 772 static int 773 call_msgbox(CALLARGS) 774 { 775 *offset_add = 4; 776 return dialog_msgbox(t, 777 av[1], 778 numeric_arg(av, 2), 779 numeric_arg(av, 3), 1); 780 } 781 782 static int 783 call_infobox(CALLARGS) 784 { 785 *offset_add = 4; 786 return dialog_msgbox(t, 787 av[1], 788 numeric_arg(av, 2), 789 numeric_arg(av, 3), 0); 790 } 791 792 static int 793 call_textbox(CALLARGS) 794 { 795 *offset_add = 4; 796 return dialog_textbox(t, 797 av[1], 798 numeric_arg(av, 2), 799 numeric_arg(av, 3)); 800 } 801 802 static int 803 call_menu(CALLARGS) 804 { 805 int tags = howmany_tags(av + 5, MENUBOX_TAGS); 806 *offset_add = 5 + tags * MENUBOX_TAGS; 807 808 return dialog_menu(t, 809 av[1], 810 numeric_arg(av, 2), 811 numeric_arg(av, 3), 812 numeric_arg(av, 4), 813 tags, av + 5); 814 } 815 816 static int 817 call_inputmenu(CALLARGS) 818 { 819 int tags = howmany_tags(av + 5, MENUBOX_TAGS); 820 bool free_extra_label = FALSE; 821 int result; 822 823 dialog_vars.input_menu = TRUE; 824 825 if (dialog_vars.max_input == 0) 826 dialog_vars.max_input = MAX_LEN / 2; 827 828 if (dialog_vars.extra_label == 0) { 829 free_extra_label = TRUE; 830 dialog_vars.extra_label = dlg_strclone(_("Rename")); 831 } 832 833 dialog_vars.extra_button = TRUE; 834 835 *offset_add = 5 + tags * MENUBOX_TAGS; 836 result = dialog_menu(t, 837 av[1], 838 numeric_arg(av, 2), 839 numeric_arg(av, 3), 840 numeric_arg(av, 4), 841 tags, av + 5); 842 if (free_extra_label) { 843 free(dialog_vars.extra_label); 844 dialog_vars.extra_label = 0; 845 } 846 return result; 847 } 848 849 static int 850 call_checklist(CALLARGS) 851 { 852 int tags = howmany_tags(av + 5, CHECKBOX_TAGS); 853 int code; 854 855 *offset_add = 5 + tags * CHECKBOX_TAGS; 856 code = dialog_checklist(t, 857 av[1], 858 numeric_arg(av, 2), 859 numeric_arg(av, 3), 860 numeric_arg(av, 4), 861 tags, av + 5, FLAG_CHECK); 862 return code; 863 } 864 865 static int 866 call_radiolist(CALLARGS) 867 { 868 int tags = howmany_tags(av + 5, CHECKBOX_TAGS); 869 *offset_add = 5 + tags * CHECKBOX_TAGS; 870 return dialog_checklist(t, 871 av[1], 872 numeric_arg(av, 2), 873 numeric_arg(av, 3), 874 numeric_arg(av, 4), 875 tags, av + 5, FLAG_RADIO); 876 } 877 878 static int 879 call_inputbox(CALLARGS) 880 { 881 *offset_add = arg_rest(av); 882 return dialog_inputbox(t, 883 av[1], 884 numeric_arg(av, 2), 885 numeric_arg(av, 3), 886 optional_str(av, 4, 0), 0); 887 } 888 889 static int 890 call_passwordbox(CALLARGS) 891 { 892 *offset_add = arg_rest(av); 893 return dialog_inputbox(t, 894 av[1], 895 numeric_arg(av, 2), 896 numeric_arg(av, 3), 897 optional_str(av, 4, 0), 1); 898 } 899 900 #ifdef HAVE_XDIALOG 901 static int 902 call_calendar(CALLARGS) 903 { 904 *offset_add = arg_rest(av); 905 return dialog_calendar(t, 906 av[1], 907 numeric_arg(av, 2), 908 numeric_arg(av, 3), 909 optional_num(av, 4, -1), 910 optional_num(av, 5, -1), 911 optional_num(av, 6, -1)); 912 } 913 914 static int 915 call_dselect(CALLARGS) 916 { 917 *offset_add = arg_rest(av); 918 return dialog_dselect(t, 919 av[1], 920 numeric_arg(av, 2), 921 numeric_arg(av, 3)); 922 } 923 924 static int 925 call_editbox(CALLARGS) 926 { 927 *offset_add = 4; 928 return dialog_editbox(t, 929 av[1], 930 numeric_arg(av, 2), 931 numeric_arg(av, 3)); 932 } 933 934 static int 935 call_fselect(CALLARGS) 936 { 937 *offset_add = arg_rest(av); 938 return dialog_fselect(t, 939 av[1], 940 numeric_arg(av, 2), 941 numeric_arg(av, 3)); 942 } 943 944 static int 945 call_timebox(CALLARGS) 946 { 947 *offset_add = arg_rest(av); 948 return dialog_timebox(t, 949 av[1], 950 numeric_arg(av, 2), 951 numeric_arg(av, 3), 952 optional_num(av, 4, -1), 953 optional_num(av, 5, -1), 954 optional_num(av, 6, -1)); 955 } 956 #endif /* HAVE_XDIALOG */ 957 958 /* dialog 1.2 widgets */ 959 #ifdef HAVE_XDIALOG2 960 961 #define DisableNoTags() \ 962 bool save_no_tags = dialog_vars.no_tags; \ 963 bool save_no_items = dialog_vars.no_items; \ 964 dialog_vars.no_tags = TRUE; \ 965 dialog_vars.no_items = FALSE 966 967 #define RestoreNoTags() \ 968 dialog_vars.no_tags = save_no_tags; \ 969 dialog_vars.no_items = save_no_items 970 971 static int 972 call_buildlist(CALLARGS) 973 { 974 int tags = howmany_tags(av + 5, CHECKBOX_TAGS); 975 int result; 976 977 DisableNoTags(); 978 979 *offset_add = 5 + tags * CHECKBOX_TAGS; 980 result = dialog_buildlist(t, 981 av[1], 982 numeric_arg(av, 2), 983 numeric_arg(av, 3), 984 numeric_arg(av, 4), 985 tags, av + 5, 986 dialog_vars.reorder); 987 RestoreNoTags(); 988 return result; 989 } 990 991 static int 992 call_rangebox(CALLARGS) 993 { 994 int min_value; 995 996 *offset_add = arg_rest(av); 997 min_value = numeric_arg(av, 4); 998 return dialog_rangebox(t, 999 av[1], 1000 numeric_arg(av, 2), 1001 numeric_arg(av, 3), 1002 min_value, 1003 numeric_arg(av, 5), 1004 (*offset_add > 6) ? numeric_arg(av, 6) : min_value); 1005 } 1006 1007 static int 1008 call_treeview(CALLARGS) 1009 { 1010 int tags = howmany_tags(av + 5, TREEVIEW_TAGS); 1011 int result; 1012 1013 DisableNoTags(); 1014 1015 *offset_add = arg_rest(av); 1016 result = dialog_treeview(t, 1017 av[1], 1018 numeric_arg(av, 2), 1019 numeric_arg(av, 3), 1020 numeric_arg(av, 4), 1021 tags, av + 5, FLAG_RADIO); 1022 RestoreNoTags(); 1023 return result; 1024 } 1025 #endif /* HAVE_XDIALOG */ 1026 1027 #ifdef HAVE_DLG_FORMBOX 1028 static int 1029 call_form(CALLARGS) 1030 { 1031 int group = FORMBOX_TAGS; 1032 int tags = howmany_tags(av + 5, group); 1033 *offset_add = 5 + tags * group; 1034 1035 return dialog_form(t, 1036 av[1], 1037 numeric_arg(av, 2), 1038 numeric_arg(av, 3), 1039 numeric_arg(av, 4), 1040 tags, av + 5); 1041 } 1042 1043 static int 1044 call_password_form(CALLARGS) 1045 { 1046 unsigned save = dialog_vars.formitem_type; 1047 int result; 1048 1049 dialog_vars.formitem_type = 1; 1050 result = call_form(PASSARGS); 1051 dialog_vars.formitem_type = save; 1052 1053 return result; 1054 } 1055 #endif /* HAVE_DLG_FORMBOX */ 1056 1057 #ifdef HAVE_DLG_MIXEDFORM 1058 static int 1059 call_mixed_form(CALLARGS) 1060 { 1061 int group = MIXEDFORM_TAGS; 1062 int tags = howmany_tags(av + 5, group); 1063 *offset_add = 5 + tags * group; 1064 1065 return dialog_mixedform(t, 1066 av[1], 1067 numeric_arg(av, 2), 1068 numeric_arg(av, 3), 1069 numeric_arg(av, 4), 1070 tags, av + 5); 1071 } 1072 #endif /* HAVE_DLG_MIXEDFORM */ 1073 1074 #ifdef HAVE_DLG_GAUGE 1075 static int 1076 call_gauge(CALLARGS) 1077 { 1078 *offset_add = arg_rest(av); 1079 return dialog_gauge(t, 1080 av[1], 1081 numeric_arg(av, 2), 1082 numeric_arg(av, 3), 1083 optional_num(av, 4, 0)); 1084 } 1085 1086 static int 1087 call_pause(CALLARGS) 1088 { 1089 *offset_add = arg_rest(av); 1090 return dialog_pause(t, 1091 av[1], 1092 numeric_arg(av, 2), 1093 numeric_arg(av, 3), 1094 numeric_arg(av, 4)); 1095 } 1096 #endif 1097 1098 #ifdef HAVE_MIXEDGAUGE 1099 static int 1100 call_mixed_gauge(CALLARGS) 1101 { 1102 #define MIXEDGAUGE_BASE 5 1103 int tags = howmany_tags(av + MIXEDGAUGE_BASE, MIXEDGAUGE_TAGS); 1104 *offset_add = MIXEDGAUGE_BASE + tags * MIXEDGAUGE_TAGS; 1105 return dialog_mixedgauge(t, 1106 av[1], 1107 numeric_arg(av, 2), 1108 numeric_arg(av, 3), 1109 numeric_arg(av, 4), 1110 tags, av + MIXEDGAUGE_BASE); 1111 } 1112 #endif 1113 1114 #ifdef HAVE_DLG_GAUGE 1115 static int 1116 call_prgbox(CALLARGS) 1117 { 1118 *offset_add = arg_rest(av); 1119 /* the original version does not accept a prompt string, but for 1120 * consistency we allow it. 1121 */ 1122 return ((*offset_add == 5) 1123 ? dialog_prgbox(t, 1124 av[1], 1125 av[2], 1126 numeric_arg(av, 3), 1127 numeric_arg(av, 4), TRUE) 1128 : dialog_prgbox(t, 1129 "", 1130 av[1], 1131 numeric_arg(av, 2), 1132 numeric_arg(av, 3), TRUE)); 1133 } 1134 #endif 1135 1136 #ifdef HAVE_DLG_GAUGE 1137 static int 1138 call_programbox(CALLARGS) 1139 { 1140 int result; 1141 1142 *offset_add = arg_rest(av); 1143 /* this function is a compromise between --prgbox and --progressbox. 1144 */ 1145 result = ((*offset_add == 4) 1146 ? dlg_progressbox(t, 1147 av[1], 1148 numeric_arg(av, 2), 1149 numeric_arg(av, 3), 1150 TRUE, 1151 dialog_state.pipe_input) 1152 : dlg_progressbox(t, 1153 "", 1154 numeric_arg(av, 1), 1155 numeric_arg(av, 2), 1156 TRUE, 1157 dialog_state.pipe_input)); 1158 dialog_state.pipe_input = 0; 1159 return result; 1160 } 1161 #endif 1162 1163 #ifdef HAVE_DLG_GAUGE 1164 static int 1165 call_progressbox(CALLARGS) 1166 { 1167 *offset_add = arg_rest(av); 1168 /* the original version does not accept a prompt string, but for 1169 * consistency we allow it. 1170 */ 1171 return ((*offset_add == 4) 1172 ? dialog_progressbox(t, 1173 av[1], 1174 numeric_arg(av, 2), 1175 numeric_arg(av, 3)) 1176 : dialog_progressbox(t, 1177 "", 1178 numeric_arg(av, 1), 1179 numeric_arg(av, 2))); 1180 } 1181 #endif 1182 1183 #ifdef HAVE_DLG_TAILBOX 1184 static int 1185 call_tailbox(CALLARGS) 1186 { 1187 *offset_add = 4; 1188 return dialog_tailbox(t, 1189 av[1], 1190 numeric_arg(av, 2), 1191 numeric_arg(av, 3), 1192 FALSE); 1193 } 1194 1195 static int 1196 call_tailboxbg(CALLARGS) 1197 { 1198 *offset_add = 4; 1199 return dialog_tailbox(t, 1200 av[1], 1201 numeric_arg(av, 2), 1202 numeric_arg(av, 3), 1203 TRUE); 1204 } 1205 #endif 1206 /* *INDENT-OFF* */ 1207 static const Mode modes[] = 1208 { 1209 {o_yesno, 4, 4, call_yesno}, 1210 {o_msgbox, 4, 4, call_msgbox}, 1211 {o_infobox, 4, 4, call_infobox}, 1212 {o_textbox, 4, 4, call_textbox}, 1213 {o_menu, 6, 0, call_menu}, 1214 {o_inputmenu, 6, 0, call_inputmenu}, 1215 {o_checklist, 7, 0, call_checklist}, 1216 {o_radiolist, 7, 0, call_radiolist}, 1217 {o_inputbox, 4, 5, call_inputbox}, 1218 {o_passwordbox, 4, 5, call_passwordbox}, 1219 #ifdef HAVE_DLG_GAUGE 1220 {o_gauge, 4, 5, call_gauge}, 1221 {o_pause, 5, 5, call_pause}, 1222 {o_prgbox, 4, 5, call_prgbox}, 1223 {o_programbox, 3, 4, call_programbox}, 1224 {o_progressbox, 3, 4, call_progressbox}, 1225 #endif 1226 #ifdef HAVE_DLG_FORMBOX 1227 {o_passwordform, 13, 0, call_password_form}, 1228 {o_form, 13, 0, call_form}, 1229 #endif 1230 #ifdef HAVE_MIXEDGAUGE 1231 {o_mixedgauge, MIXEDGAUGE_BASE, 0, call_mixed_gauge}, 1232 #endif 1233 #ifdef HAVE_DLG_MIXEDFORM 1234 {o_mixedform, 13, 0, call_mixed_form}, 1235 #endif 1236 #ifdef HAVE_DLG_TAILBOX 1237 {o_tailbox, 4, 4, call_tailbox}, 1238 {o_tailboxbg, 4, 4, call_tailboxbg}, 1239 #endif 1240 #ifdef HAVE_XDIALOG 1241 {o_calendar, 4, 7, call_calendar}, 1242 {o_dselect, 4, 5, call_dselect}, 1243 {o_editbox, 4, 4, call_editbox}, 1244 {o_fselect, 4, 5, call_fselect}, 1245 {o_timebox, 4, 7, call_timebox}, 1246 #endif 1247 #ifdef HAVE_XDIALOG2 1248 {o_buildlist, 4, 0, call_buildlist}, 1249 {o_rangebox, 5, 7, call_rangebox}, 1250 {o_treeview, 4, 0, call_treeview}, 1251 #endif 1252 }; 1253 /* *INDENT-ON* */ 1254 1255 static char * 1256 optionString(char **argv, int *num) 1257 { 1258 int next = *num + 1; 1259 char *result = argv[next]; 1260 if (result == 0) { 1261 char temp[80]; 1262 sprintf(temp, "Expected a string-parameter for %.20s", argv[*num]); 1263 Usage(temp); 1264 } 1265 *num = next; 1266 return result; 1267 } 1268 1269 static int 1270 optionValue(char **argv, int *num) 1271 { 1272 int next = *num + 1; 1273 char *src = argv[next]; 1274 char *tmp = 0; 1275 int result = 0; 1276 1277 if (src != 0) { 1278 result = (int) strtol(src, &tmp, 0); 1279 if (tmp == 0 || *tmp != 0) 1280 src = 0; 1281 } 1282 1283 if (src == 0) { 1284 char temp[80]; 1285 sprintf(temp, "Expected a numeric-parameter for %.20s", argv[*num]); 1286 Usage(temp); 1287 } 1288 *num = next; 1289 return result; 1290 } 1291 1292 /* Return exit-code for a named button */ 1293 static int 1294 button_code(const char *name) 1295 { 1296 /* *INDENT-OFF* */ 1297 static struct { 1298 const char *name; 1299 int code; 1300 } table[] = { 1301 { "ok", DLG_EXIT_OK }, 1302 { "yes", DLG_EXIT_OK }, 1303 { "cancel", DLG_EXIT_CANCEL }, 1304 { "no", DLG_EXIT_CANCEL }, 1305 { "help", DLG_EXIT_HELP }, 1306 { "extra", DLG_EXIT_EXTRA }, 1307 }; 1308 /* *INDENT-ON* */ 1309 1310 int code = DLG_EXIT_ERROR; 1311 size_t i; 1312 1313 for (i = 0; i < (sizeof(table) / sizeof(table[0])); i++) { 1314 if (!dlg_strcmp(name, table[i].name)) { 1315 code = table[i].code; 1316 break; 1317 } 1318 } 1319 1320 if (code == DLG_EXIT_ERROR) { 1321 char temp[80]; 1322 sprintf(temp, "Button name \"%.20s\" unknown", name); 1323 Usage(temp); 1324 } 1325 1326 return code; 1327 } 1328 1329 /* 1330 * If this is the last option, we do not want any error messages - just our 1331 * output. Calling end_dialog() cancels the refresh() at the end of the 1332 * program as well. 1333 */ 1334 static void 1335 IgnoreNonScreen(char **argv, int offset) 1336 { 1337 if (argv[offset + 1] == 0) { 1338 ignore_unknown = TRUE; 1339 end_dialog(); 1340 } 1341 } 1342 1343 static void 1344 PrintTextOnly(char **argv, int *offset, eOptions code) 1345 { 1346 /* TODO - handle two optional numeric params */ 1347 char *text; 1348 int height = 0; 1349 int width = 0; 1350 int height2 = 0; 1351 int width2 = 0; 1352 int next = arg_rest(argv + *offset); 1353 1354 if (LINES <= 0 && COLS <= 0) 1355 dlg_ttysize(fileno(dialog_state.input), &LINES, &COLS); 1356 1357 text = strdup(optionString(argv, offset)); 1358 IgnoreNonScreen(argv, *offset); 1359 1360 if (next >= 1) { 1361 next = MIN(next, 3); 1362 height = numeric_arg(argv, *offset + 1); 1363 if (next >= 2) 1364 width = numeric_arg(argv, *offset + 2); 1365 *offset += next - 1; 1366 } 1367 1368 dlg_trim_string(text); 1369 dlg_auto_size(NULL, text, &height2, &width2, height, width); 1370 1371 switch (code) { 1372 case o_print_text_only: 1373 dialog_state.text_only = TRUE; 1374 dlg_print_autowrap(stdscr, text, height2, width2); 1375 dialog_state.text_only = FALSE; 1376 break; 1377 case o_print_text_size: 1378 fprintf(dialog_state.output, "%d %d\n", 1379 dialog_state.text_height, 1380 dialog_state.text_width); 1381 break; 1382 default: 1383 break; 1384 } 1385 } 1386 1387 /* 1388 * Print parts of a message 1389 */ 1390 static void 1391 PrintList(const char *const *list) 1392 { 1393 const char *leaf = strrchr(program, '/'); 1394 unsigned n = 0; 1395 1396 if (leaf != 0) 1397 leaf++; 1398 else 1399 leaf = program; 1400 1401 while (*list != 0) { 1402 fprintf(dialog_state.output, *list, n ? leaf : dialog_version()); 1403 (void) fputc('\n', dialog_state.output); 1404 n = 1; 1405 list++; 1406 } 1407 } 1408 1409 static const Mode * 1410 lookupMode(eOptions code) 1411 { 1412 const Mode *modePtr = 0; 1413 unsigned n; 1414 1415 for (n = 0; n < sizeof(modes) / sizeof(modes[0]); n++) { 1416 if (modes[n].code == code) { 1417 modePtr = &modes[n]; 1418 break; 1419 } 1420 } 1421 return modePtr; 1422 } 1423 1424 static int 1425 compare_opts(const void *a, const void *b) 1426 { 1427 Options *const *p = (Options * const *) a; 1428 Options *const *q = (Options * const *) b; 1429 return strcmp((*p)->name, (*q)->name); 1430 } 1431 1432 /* 1433 * Print program's version. 1434 */ 1435 static void 1436 PrintVersion(FILE *fp) 1437 { 1438 fprintf(fp, "Version: %s\n", dialog_version()); 1439 } 1440 1441 /* 1442 * Print program help-message 1443 */ 1444 static void 1445 Help(void) 1446 { 1447 static const char *const tbl_1[] = 1448 { 1449 "cdialog (ComeOn Dialog!) version %s", 1450 "Copyright 2000-2017,2018 Thomas E. Dickey", 1451 "This is free software; see the source for copying conditions. There is NO", 1452 "warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.", 1453 "", 1454 "* Display dialog boxes from shell scripts *", 1455 "", 1456 "Usage: %s <options> { --and-widget <options> }", 1457 "where options are \"common\" options, followed by \"box\" options", 1458 "", 1459 #ifdef HAVE_RC_FILE 1460 "Special options:", 1461 " [--create-rc \"file\"]", 1462 #endif 1463 0 1464 }, *const tbl_3[] = 1465 { 1466 "", 1467 "Auto-size with height and width = 0. Maximize with height and width = -1.", 1468 "Global-auto-size if also menu_height/list_height = 0.", 1469 0 1470 }; 1471 size_t limit = sizeof(options) / sizeof(options[0]); 1472 size_t j, k; 1473 const Options **opts; 1474 1475 end_dialog(); 1476 dialog_state.output = stdout; 1477 1478 opts = dlg_calloc(const Options *, limit); 1479 assert_ptr(opts, "Help"); 1480 for (j = 0; j < limit; ++j) { 1481 opts[j] = &(options[j]); 1482 } 1483 qsort(opts, limit, sizeof(Options *), compare_opts); 1484 1485 PrintList(tbl_1); 1486 fprintf(dialog_state.output, "Common options:\n "); 1487 for (j = k = 0; j < limit; j++) { 1488 if ((opts[j]->pass & 1) 1489 && opts[j]->help != 0) { 1490 size_t len = 6 + strlen(opts[j]->name) + strlen(opts[j]->help); 1491 k += len; 1492 if (k > 75) { 1493 fprintf(dialog_state.output, "\n "); 1494 k = len; 1495 } 1496 fprintf(dialog_state.output, " [--%s%s%s]", opts[j]->name, 1497 *(opts[j]->help) ? " " : "", opts[j]->help); 1498 } 1499 } 1500 fprintf(dialog_state.output, "\nBox options:\n"); 1501 for (j = 0; j < limit; j++) { 1502 if ((opts[j]->pass & 2) != 0 1503 && opts[j]->help != 0 1504 && lookupMode(opts[j]->code)) { 1505 fprintf(dialog_state.output, " --%-12s %s\n", opts[j]->name, 1506 opts[j]->help); 1507 } 1508 } 1509 PrintList(tbl_3); 1510 1511 free(opts); 1512 handle_leaks(); 1513 dlg_exit(DLG_EXIT_OK); 1514 } 1515 1516 #ifdef HAVE_DLG_TRACE 1517 /* 1518 * Only the first call to dlg_trace will open a trace file. But each time 1519 * --trace is parsed, we show the whole parameter list as it is at that moment, 1520 * counting discarded parameters. The only way to capture the whole parameter 1521 * list is if --trace is the first option. 1522 */ 1523 static void 1524 process_trace_option(char **argv, int *offset) 1525 { 1526 int j; 1527 1528 if (dialog_state.trace_output == 0) { 1529 dlg_trace(optionString(argv, offset)); 1530 } else { 1531 DLG_TRACE(("# ignore extra --trace option\n")); 1532 *offset += 1; 1533 } 1534 1535 DLG_TRACE(("# Parameters:\n")); 1536 for (j = 0; argv[j] != 0; ++j) { 1537 DLG_TRACE(("#\targv[%d] = %s\n", j, argv[j])); 1538 } 1539 } 1540 #endif 1541 1542 /* 1543 * "Common" options apply to all widgets more/less. Most of the common options 1544 * set values in dialog_vars, a few set dialog_state and a couple write to the 1545 * output stream. 1546 */ 1547 static int 1548 process_common_options(int argc, char **argv, int offset, bool output) 1549 { 1550 bool done = FALSE; 1551 eOptions code; 1552 1553 DLG_TRACE(("# process_common_options, offset %d\n", offset)); 1554 1555 while (offset < argc && !done) { /* Common options */ 1556 DLG_TRACE(("#\targv[%d] = %s\n", offset, argv[offset])); 1557 switch (code = lookupOption(argv[offset], 1)) { 1558 case o_title: 1559 dialog_vars.title = optionString(argv, &offset); 1560 break; 1561 case o_backtitle: 1562 dialog_vars.backtitle = optionString(argv, &offset); 1563 break; 1564 case o_separate_widget: 1565 dialog_state.separate_str = optionString(argv, &offset); 1566 break; 1567 case o_separate_output: 1568 dialog_vars.separate_output = TRUE; 1569 break; 1570 case o_colors: 1571 dialog_vars.colors = TRUE; 1572 break; 1573 case o_cr_wrap: 1574 dialog_vars.cr_wrap = TRUE; 1575 break; 1576 case o_no_nl_expand: 1577 dialog_vars.no_nl_expand = TRUE; 1578 break; 1579 case o_no_collapse: 1580 dialog_vars.nocollapse = TRUE; 1581 break; 1582 case o_no_kill: 1583 dialog_vars.cant_kill = TRUE; 1584 break; 1585 case o_nocancel: 1586 dialog_vars.nocancel = TRUE; 1587 break; 1588 case o_nook: 1589 dialog_vars.nook = TRUE; 1590 break; 1591 case o_quoted: 1592 dialog_vars.quoted = TRUE; 1593 break; 1594 case o_single_quoted: 1595 dialog_vars.single_quoted = TRUE; 1596 break; 1597 case o_size_err: 1598 dialog_vars.size_err = TRUE; 1599 break; 1600 case o_beep: 1601 dialog_vars.beep_signal = TRUE; 1602 break; 1603 case o_beep_after: 1604 dialog_vars.beep_after_signal = TRUE; 1605 break; 1606 case o_scrollbar: 1607 dialog_state.use_scrollbar = TRUE; 1608 break; 1609 case o_shadow: 1610 dialog_state.use_shadow = TRUE; 1611 break; 1612 case o_defaultno: 1613 dialog_vars.defaultno = TRUE; 1614 dialog_vars.default_button = DLG_EXIT_CANCEL; 1615 break; 1616 case o_default_button: 1617 dialog_vars.default_button = button_code(optionString(argv, &offset)); 1618 dialog_vars.defaultno = dialog_vars.default_button == DLG_EXIT_CANCEL; 1619 break; 1620 case o_default_item: 1621 dialog_vars.default_item = optionString(argv, &offset); 1622 break; 1623 case o_insecure: 1624 dialog_vars.insecure = TRUE; 1625 break; 1626 case o_item_help: 1627 dialog_vars.item_help = TRUE; 1628 break; 1629 case o_help_line: 1630 dialog_vars.help_line = optionString(argv, &offset); 1631 break; 1632 case o_help_file: 1633 dialog_vars.help_file = optionString(argv, &offset); 1634 break; 1635 case o_help_button: 1636 dialog_vars.help_button = TRUE; 1637 break; 1638 case o_help_status: 1639 dialog_vars.help_status = TRUE; 1640 break; 1641 case o_help_tags: 1642 dialog_vars.help_tags = TRUE; 1643 break; 1644 case o_extra_button: 1645 dialog_vars.extra_button = TRUE; 1646 break; 1647 case o_ignore: 1648 ignore_unknown = TRUE; 1649 break; 1650 case o_keep_window: 1651 dialog_vars.keep_window = TRUE; 1652 break; 1653 case o_last_key: 1654 dialog_vars.last_key = TRUE; 1655 break; 1656 case o_no_shadow: 1657 dialog_state.use_shadow = FALSE; 1658 break; 1659 case o_print_size: 1660 dialog_vars.print_siz = TRUE; 1661 break; 1662 case o_print_text_only: 1663 case o_print_text_size: 1664 PrintTextOnly(argv, &offset, code); 1665 break; 1666 case o_print_maxsize: 1667 if (output) { 1668 IgnoreNonScreen(argv, offset); 1669 fflush(dialog_state.output); 1670 fprintf(dialog_state.output, "MaxSize: %d, %d\n", SLINES, SCOLS); 1671 } 1672 break; 1673 case o_print_version: 1674 if (output) { 1675 PrintVersion(dialog_state.output); 1676 } 1677 break; 1678 case o_separator: 1679 case o_output_separator: 1680 dialog_vars.output_separator = optionString(argv, &offset); 1681 break; 1682 case o_column_separator: 1683 dialog_vars.column_separator = optionString(argv, &offset); 1684 break; 1685 case o_tab_correct: 1686 dialog_vars.tab_correct = TRUE; 1687 break; 1688 case o_sleep: 1689 dialog_vars.sleep_secs = optionValue(argv, &offset); 1690 break; 1691 case o_timeout: 1692 dialog_vars.timeout_secs = optionValue(argv, &offset); 1693 break; 1694 case o_max_input: 1695 dialog_vars.max_input = optionValue(argv, &offset); 1696 break; 1697 case o_tab_len: 1698 dialog_state.tab_len = optionValue(argv, &offset); 1699 break; 1700 case o_trim: 1701 dialog_vars.trim_whitespace = TRUE; 1702 break; 1703 case o_visit_items: 1704 dialog_state.visit_items = TRUE; 1705 dialog_state.visit_cols = 1; 1706 break; 1707 case o_aspect: 1708 dialog_state.aspect_ratio = optionValue(argv, &offset); 1709 break; 1710 case o_begin: 1711 dialog_vars.begin_set = TRUE; 1712 dialog_vars.begin_y = optionValue(argv, &offset); 1713 dialog_vars.begin_x = optionValue(argv, &offset); 1714 break; 1715 case o_clear: 1716 dialog_vars.dlg_clear_screen = TRUE; 1717 break; 1718 case o_yes_label: 1719 dialog_vars.yes_label = optionString(argv, &offset); 1720 break; 1721 case o_no_label: 1722 dialog_vars.no_label = optionString(argv, &offset); 1723 break; 1724 case o_ok_label: 1725 dialog_vars.ok_label = optionString(argv, &offset); 1726 break; 1727 case o_cancel_label: 1728 dialog_vars.cancel_label = optionString(argv, &offset); 1729 break; 1730 case o_extra_label: 1731 dialog_vars.extra_label = optionString(argv, &offset); 1732 break; 1733 case o_exit_label: 1734 dialog_vars.exit_label = optionString(argv, &offset); 1735 break; 1736 case o_help_label: 1737 dialog_vars.help_label = optionString(argv, &offset); 1738 break; 1739 case o_date_format: 1740 dialog_vars.date_format = optionString(argv, &offset); 1741 break; 1742 case o_time_format: 1743 dialog_vars.time_format = optionString(argv, &offset); 1744 break; 1745 case o_keep_tite: 1746 dialog_vars.keep_tite = TRUE; 1747 break; 1748 case o_ascii_lines: 1749 dialog_vars.ascii_lines = TRUE; 1750 dialog_vars.no_lines = FALSE; 1751 break; 1752 case o_no_lines: 1753 dialog_vars.no_lines = TRUE; 1754 dialog_vars.ascii_lines = FALSE; 1755 break; 1756 case o_no_mouse: 1757 dialog_state.no_mouse = TRUE; 1758 mouse_close(); 1759 break; 1760 #ifdef HAVE_WHIPTAIL 1761 case o_topleft: 1762 dialog_vars.begin_set = TRUE; 1763 dialog_vars.begin_y = 0; 1764 dialog_vars.begin_x = 0; 1765 break; 1766 case o_fullbutton: 1767 /* ignore */ 1768 break; 1769 #endif 1770 /* options of Xdialog which we ignore */ 1771 case o_icon: 1772 case o_wmclass: 1773 (void) optionString(argv, &offset); 1774 /* FALLTHRU */ 1775 case o_allow_close: 1776 case o_auto_placement: 1777 case o_fixed_font: 1778 case o_keep_colors: 1779 case o_no_close: 1780 case o_no_cr_wrap: 1781 case o_screen_center: 1782 case o_smooth: 1783 case o_under_mouse: 1784 break; 1785 case o_unknown: 1786 if (ignore_unknown) 1787 break; 1788 /* FALLTHRU */ 1789 default: /* no more common options */ 1790 done = TRUE; 1791 break; 1792 #ifdef HAVE_DLG_TRACE 1793 case o_trace: 1794 process_trace_option(argv, &offset); 1795 break; 1796 #endif 1797 #if defined(HAVE_XDIALOG2) || defined(HAVE_WHIPTAIL) 1798 case o_no_items: 1799 dialog_vars.no_items = TRUE; 1800 break; 1801 case o_no_tags: 1802 dialog_vars.no_tags = TRUE; 1803 break; 1804 #endif 1805 #ifdef HAVE_XDIALOG2 1806 case o_reorder: 1807 dialog_vars.reorder = TRUE; 1808 break; 1809 #endif 1810 #ifdef HAVE_XDIALOG 1811 case o_week_start: 1812 dialog_vars.week_start = optionString(argv, &offset); 1813 break; 1814 #endif 1815 case o_iso_week: 1816 dialog_vars.iso_week = TRUE; 1817 if (dialog_vars.week_start == 0) { /* Monday is implied */ 1818 static char default_1st[] = "1"; 1819 dialog_vars.week_start = default_1st; 1820 } 1821 break; 1822 } 1823 if (!done) 1824 offset++; 1825 } 1826 1827 if (dialog_state.aspect_ratio == 0) 1828 dialog_state.aspect_ratio = DEFAULT_ASPECT_RATIO; 1829 1830 return offset; 1831 } 1832 1833 /* 1834 * Initialize options at the start of a series of common options culminating 1835 * in a widget. 1836 */ 1837 static void 1838 init_result(char *buffer) 1839 { 1840 static bool first = TRUE; 1841 1842 DLG_TRACE(("# init_result\n")); 1843 1844 /* clear everything we do not save for the next widget */ 1845 memset(&dialog_vars, 0, sizeof(dialog_vars)); 1846 1847 dialog_vars.input_result = buffer; 1848 dialog_vars.input_result[0] = '\0'; 1849 1850 dialog_vars.default_button = -1; 1851 1852 /* 1853 * The first time this is called, check for common options given by an 1854 * environment variable. 1855 */ 1856 if (first) { 1857 char *env = getenv("DIALOGOPTS"); 1858 if (env != 0) 1859 env = dlg_strclone(env); 1860 if (env != 0) { 1861 special_argv = dlg_string_to_argv(env); 1862 special_argc = dlg_count_argv(special_argv); 1863 } 1864 first = FALSE; 1865 } 1866 1867 if (special_argv != 0) { 1868 process_common_options(special_argc, special_argv, 0, FALSE); 1869 } 1870 } 1871 1872 int 1873 main(int argc, char *argv[]) 1874 { 1875 char temp[256]; 1876 bool esc_pressed = FALSE; 1877 bool keep_tite = FALSE; 1878 bool first_time = TRUE; 1879 int offset = 1; 1880 int offset_add; 1881 int retval = DLG_EXIT_OK; 1882 int j, have; 1883 eOptions code; 1884 const Mode *modePtr; 1885 char my_buffer[MAX_LEN + 1]; 1886 1887 memset(&dialog_state, 0, sizeof(dialog_state)); 1888 memset(&dialog_vars, 0, sizeof(dialog_vars)); 1889 1890 #if defined(ENABLE_NLS) 1891 /* initialize locale support */ 1892 setlocale(LC_ALL, ""); 1893 bindtextdomain(NLS_TEXTDOMAIN, LOCALEDIR); 1894 textdomain(NLS_TEXTDOMAIN); 1895 #elif defined(HAVE_SETLOCALE) 1896 (void) setlocale(LC_ALL, ""); 1897 #endif 1898 1899 init_result(my_buffer); /* honor $DIALOGOPTS */ 1900 unescape_argv(&argc, &argv); 1901 program = argv[0]; 1902 dialog_state.output = stderr; 1903 dialog_state.input = stdin; 1904 1905 /* 1906 * Look for the last --stdout, --stderr or --output-fd option, and use 1907 * that. We can only write to one of them. If --stdout is used, that 1908 * can interfere with initializing the curses library, so we want to 1909 * know explicitly if it is used. 1910 * 1911 * Also, look for any --version or --help message, processing those 1912 * immediately. 1913 */ 1914 while (offset < argc) { 1915 int base = offset; 1916 switch (lookupOption(argv[offset], 7)) { 1917 case o_stdout: 1918 dialog_state.output = stdout; 1919 break; 1920 case o_stderr: 1921 dialog_state.output = stderr; 1922 break; 1923 case o_input_fd: 1924 if ((j = optionValue(argv, &offset)) < 0 1925 || (dialog_state.input = fdopen(j, "r")) == 0) { 1926 handle_leaks(); 1927 dlg_exiterr("Cannot open input-fd\n"); 1928 } 1929 break; 1930 case o_output_fd: 1931 if ((j = optionValue(argv, &offset)) < 0 1932 || (dialog_state.output = fdopen(j, "w")) == 0) { 1933 handle_leaks(); 1934 dlg_exiterr("Cannot open output-fd\n"); 1935 } 1936 break; 1937 case o_keep_tite: 1938 keep_tite = TRUE; 1939 break; 1940 case o_version: 1941 dialog_state.output = stdout; 1942 PrintVersion(dialog_state.output); 1943 dlg_exit(DLG_EXIT_OK); 1944 break; 1945 case o_help: 1946 Help(); 1947 break; 1948 #ifdef HAVE_DLG_TRACE 1949 case o_trace: 1950 /* 1951 * Process/remove the --trace option if it is the first option. 1952 * Otherwise, process it in more/less expected order as a 1953 * "common" option. 1954 */ 1955 if (base == 1) { 1956 process_trace_option(argv, &offset); 1957 break; 1958 } else { 1959 ++offset; 1960 continue; 1961 } 1962 #endif 1963 default: 1964 ++offset; 1965 continue; 1966 } 1967 DLG_TRACE(("# discarding %d parameters starting with argv[%d] (%s)\n", 1968 1 + offset - base, base, 1969 argv[base])); 1970 for (j = base; j < argc; ++j) { 1971 dialog_argv[j] = dialog_argv[j + 1 + (offset - base)]; 1972 } 1973 argc -= (1 + offset - base); 1974 offset = base; 1975 } 1976 offset = 1; 1977 init_result(my_buffer); 1978 dialog_vars.keep_tite = keep_tite; /* init_result() cleared global */ 1979 1980 /* 1981 * Dialog's output may be redirected (see above). Handle the special 1982 * case of options that only report information without interaction. 1983 */ 1984 if (argc == 2) { 1985 switch (code = lookupOption(argv[1], 7)) { 1986 case o_print_maxsize: 1987 (void) initscr(); 1988 endwin(); 1989 fflush(dialog_state.output); 1990 fprintf(dialog_state.output, "MaxSize: %d, %d\n", SLINES, SCOLS); 1991 break; 1992 case o_print_version: 1993 PrintVersion(dialog_state.output); 1994 break; 1995 case o_clear: 1996 initscr(); 1997 refresh(); 1998 endwin(); 1999 break; 2000 case o_ignore: 2001 break; 2002 default: 2003 Help(); 2004 break; 2005 } 2006 dlg_exit(DLG_EXIT_OK); 2007 } else if (argc < 2) { 2008 Help(); 2009 } 2010 #ifdef HAVE_RC_FILE 2011 else if (lookupOption(argv[1], 7) == o_create_rc) { 2012 if (argc != 3) { 2013 sprintf(temp, "Expected a filename for %.50s", argv[1]); 2014 Usage(temp); 2015 } 2016 if (dlg_parse_rc() == -1) { /* Read the configuration file */ 2017 handle_leaks(); 2018 dlg_exiterr("dialog: dlg_parse_rc"); 2019 } 2020 dlg_create_rc(argv[2]); 2021 dlg_exit(DLG_EXIT_OK); 2022 } 2023 #endif 2024 else { 2025 /* 2026 * Handle combinations of common options including --print-text-only 2027 * which can be done before involving curses, in case we can exit 2028 * without initializing curses (and writing to the terminal). 2029 */ 2030 offset = process_common_options(argc, argv, offset, TRUE); 2031 if (offset >= argc) 2032 dlg_exit(DLG_EXIT_OK); 2033 } 2034 2035 init_dialog(dialog_state.input, dialog_state.output); 2036 2037 while (offset < argc && !esc_pressed) { 2038 if (first_time) { 2039 first_time = FALSE; 2040 } else { 2041 init_result(my_buffer); 2042 offset = process_common_options(argc, argv, offset, TRUE); 2043 } 2044 2045 if (argv[offset] == NULL) { 2046 if (ignore_unknown) 2047 break; 2048 Usage("Expected a box option"); 2049 } 2050 2051 if (dialog_vars.separate_output) { 2052 switch (lookupOption(argv[offset], 2)) { 2053 #ifdef HAVE_XDIALOG2 2054 case o_buildlist: 2055 case o_treeview: 2056 #endif 2057 case o_checklist: 2058 break; 2059 default: 2060 sprintf(temp, 2061 "Unexpected widget with --separate-output %.20s", 2062 argv[offset]); 2063 Usage(temp); 2064 } 2065 } 2066 2067 dlg_put_backtitle(); 2068 2069 /* use a table to look for the requested mode, to avoid code duplication */ 2070 2071 modePtr = 0; 2072 if ((code = lookupOption(argv[offset], 2)) != o_unknown) 2073 modePtr = lookupMode(code); 2074 if (modePtr == 0) { 2075 sprintf(temp, "%s option %.20s", 2076 lookupOption(argv[offset], 7) != o_unknown 2077 ? "Unexpected" 2078 : "Unknown", 2079 argv[offset]); 2080 Usage(temp); 2081 } 2082 2083 have = arg_rest(&argv[offset]); 2084 if (have < modePtr->argmin) { 2085 sprintf(temp, "Expected at least %d tokens for %.20s, have %d", 2086 modePtr->argmin - 1, argv[offset], 2087 have - 1); 2088 Usage(temp); 2089 } 2090 if (modePtr->argmax && have > modePtr->argmax) { 2091 sprintf(temp, 2092 "Expected no more than %d tokens for %.20s, have %d", 2093 modePtr->argmax - 1, argv[offset], 2094 have - 1); 2095 Usage(temp); 2096 } 2097 2098 /* 2099 * Trim whitespace from non-title option values, e.g., the ones that 2100 * will be used as captions or prompts. Do that only for the widget 2101 * we are about to process, since the "--trim" option is reset before 2102 * accumulating options for each widget. 2103 */ 2104 for (j = offset + 1; j <= offset + have; j++) { 2105 switch (lookupOption(argv[j - 1], 7)) { 2106 case o_unknown: 2107 case o_title: 2108 case o_backtitle: 2109 case o_help_line: 2110 case o_help_file: 2111 break; 2112 default: 2113 if (argv[j] != 0) { 2114 char *argv_j = strdup(argv[j]); 2115 if (argv_j != 0) { 2116 dlg_trim_string(argv_j); 2117 argv[j] = argv_j; 2118 } else { 2119 argv[j] = strdup("?"); 2120 } 2121 ignore_leak(argv[j]); 2122 } 2123 break; 2124 } 2125 } 2126 2127 DLG_TRACE(("# execute %s\n", argv[offset])); 2128 retval = show_result((*(modePtr->jumper)) (dialog_vars.title, 2129 argv + offset, 2130 &offset_add)); 2131 DLG_TRACE(("# widget returns %d\n", retval)); 2132 offset += offset_add; 2133 2134 if (dialog_vars.input_result != my_buffer) { 2135 free(dialog_vars.input_result); 2136 dialog_vars.input_result = 0; 2137 } 2138 2139 if (retval == DLG_EXIT_ESC) { 2140 esc_pressed = TRUE; 2141 } else { 2142 2143 if (dialog_vars.beep_after_signal) 2144 (void) beep(); 2145 2146 if (dialog_vars.sleep_secs) 2147 (void) napms(dialog_vars.sleep_secs * 1000); 2148 2149 if (offset < argc) { 2150 switch (lookupOption(argv[offset], 7)) { 2151 case o_and_widget: 2152 offset++; 2153 break; 2154 case o_unknown: 2155 sprintf(temp, "Expected --and-widget, not %.20s", 2156 argv[offset]); 2157 Usage(temp); 2158 break; 2159 default: 2160 /* if we got a cancel, etc., stop chaining */ 2161 if (retval != DLG_EXIT_OK) 2162 esc_pressed = TRUE; 2163 else 2164 dialog_vars.dlg_clear_screen = TRUE; 2165 break; 2166 } 2167 } 2168 if (dialog_vars.dlg_clear_screen) 2169 dlg_clear(); 2170 } 2171 } 2172 2173 dlg_killall_bg(&retval); 2174 if (dialog_state.screen_initialized) { 2175 (void) refresh(); 2176 end_dialog(); 2177 } 2178 handle_leaks(); 2179 dlg_exit(retval); 2180 } 2181