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