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