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