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