1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2022 Alfonso Sabato Siciliano
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/ioctl.h>
29 
30 #include <getopt.h>
31 #include <locale.h>
32 #include <signal.h>
33 #include <stdlib.h>
34 #include <stdio.h>
35 #include <string.h>
36 #include <term.h>
37 #include <time.h>
38 #include <unistd.h>
39 
40 #include <bsddialog.h>
41 #include <bsddialog_theme.h>
42 
43 #include "util_theme.h"
44 
45 enum OPTS {
46 	/* Options */
47 	ALTERNATE_SCREEN = '?' + 1,
48 	AND_DIALOG,
49 	ASCII_LINES,
50 	BACKTITLE,
51 	BEGIN_X,
52 	BEGIN_Y,
53 	BIKESHED,
54 	CANCEL_LABEL,
55 	CLEAR_DIALOG,
56 	CLEAR_SCREEN,
57 	COLORS,
58 	COLUMNS_PER_ROW,
59 	CR_WRAP,
60 	DATE_FORMAT,
61 	DEFAULT_BUTTON,
62 	DEFAULT_ITEM,
63 	DEFAULT_NO,
64 	DISABLE_ESC,
65 	ESC_RETURNCANCEL,
66 	EXIT_LABEL,
67 	EXTRA_BUTTON,
68 	EXTRA_LABEL,
69 	GENERIC_BUTTON1,
70 	GENERIC_BUTTON2,
71 	HELP_BUTTON,
72 	HELP_LABEL,
73 	HELP_PRINT_NAME,
74 	HELP_STATUS,
75 	HFILE,
76 	HLINE,
77 	HMSG,
78 	IGNORE,
79 	INSECURE,
80 	ITEM_BOTTOM_DESC,
81 	ITEM_DEPTH,
82 	ITEM_PREFIX,
83 	LOAD_THEME,
84 	MAX_INPUT,
85 	NO_CANCEL,
86 	NO_DESCRIPTIONS,
87 	NO_LINES,
88 	NO_NAMES,
89 	NO_OK,
90 	NO_SHADOW,
91 	NORMAL_SCREEN,
92 	OK_LABEL,
93 	OUTPUT_FD,
94 	OUTPUT_SEPARATOR,
95 	PRINT_MAXSIZE,
96 	PRINT_SIZE,
97 	PRINT_VERSION,
98 	QUOTED,
99 	SAVE_THEME,
100 	SEPARATE_OUTPUT,
101 	SHADOW,
102 	SINGLE_QUOTED,
103 	SLEEP,
104 	STDERR,
105 	STDOUT,
106 	SWITCH_BUTTONS,
107 	TAB_ESCAPE,
108 	TAB_LEN,
109 	TEXT_UNCHANGED,
110 	THEME,
111 	TIME_FORMAT,
112 	TITLE,
113 	/* Dialogs */
114 	CALENDAR,
115 	CHECKLIST,
116 	DATEBOX,
117 	FORM,
118 	GAUGE,
119 	INFOBOX,
120 	INPUTBOX,
121 	MENU,
122 	MIXEDFORM,
123 	MIXEDGAUGE,
124 	MSGBOX,
125 	PASSWORDBOX,
126 	PASSWORDFORM,
127 	PAUSE,
128 	RADIOLIST,
129 	RANGEBOX,
130 	TEXTBOX,
131 	TIMEBOX,
132 	TREEVIEW,
133 	YESNO
134 };
135 
136 /* options descriptor */
137 static struct option longopts[] = {
138 	/* Options */
139 	{"alternate-screen",  no_argument,       NULL, ALTERNATE_SCREEN},
140 	{"and-dialog",        no_argument,       NULL, AND_DIALOG},
141 	{"and-widget",        no_argument,       NULL, AND_DIALOG},
142 	{"ascii-lines",       no_argument,       NULL, ASCII_LINES},
143 	{"backtitle",         required_argument, NULL, BACKTITLE},
144 	{"begin-x",           required_argument, NULL, BEGIN_X},
145 	{"begin-y",           required_argument, NULL, BEGIN_Y},
146 	{"bikeshed",          no_argument,       NULL, BIKESHED},
147 	{"cancel-label",      required_argument, NULL, CANCEL_LABEL},
148 	{"clear",             no_argument,       NULL, CLEAR_SCREEN},
149 	{"clear-dialog",      no_argument,       NULL, CLEAR_DIALOG},
150 	{"clear-screen",      no_argument,       NULL, CLEAR_SCREEN},
151 	{"colors",            no_argument,       NULL, COLORS},
152 	{"columns-per-row",   required_argument, NULL, COLUMNS_PER_ROW},
153 	{"cr-wrap",           no_argument,       NULL, CR_WRAP},
154 	{"date-format",       required_argument, NULL, DATE_FORMAT},
155 	{"defaultno",         no_argument,       NULL, DEFAULT_NO},
156 	{"default-button",    required_argument, NULL, DEFAULT_BUTTON},
157 	{"default-item",      required_argument, NULL, DEFAULT_ITEM},
158 	{"default-no",        no_argument,       NULL, DEFAULT_NO},
159 	{"disable-esc",       no_argument,       NULL, DISABLE_ESC},
160 	{"esc-return-cancel", no_argument,       NULL, ESC_RETURNCANCEL},
161 	{"exit-label",        required_argument, NULL, EXIT_LABEL},
162 	{"extra-button",      no_argument,       NULL, EXTRA_BUTTON},
163 	{"extra-label",       required_argument, NULL, EXTRA_LABEL},
164 	{"generic-button1",   required_argument, NULL, GENERIC_BUTTON1},
165 	{"generic-button2",   required_argument, NULL, GENERIC_BUTTON2},
166 	{"help-button",       no_argument,       NULL, HELP_BUTTON},
167 	{"help-label",        required_argument, NULL, HELP_LABEL},
168 	{"help-print-name",   no_argument,       NULL, HELP_PRINT_NAME},
169 	{"help-status",       no_argument,       NULL, HELP_STATUS},
170 	{"help-tags",         no_argument,       NULL, HELP_PRINT_NAME},
171 	{"hfile",             required_argument, NULL, HFILE},
172 	{"hline",             required_argument, NULL, HLINE},
173 	{"hmsg",              required_argument, NULL, HMSG},
174 	{"ignore",            no_argument,       NULL, IGNORE},
175 	{"insecure",          no_argument,       NULL, INSECURE},
176 	{"item-bottom-desc",  no_argument,       NULL, ITEM_BOTTOM_DESC},
177 	{"item-depth",        no_argument,       NULL, ITEM_DEPTH},
178 	{"item-help",         no_argument,       NULL, ITEM_BOTTOM_DESC},
179 	{"item-prefix",       no_argument,       NULL, ITEM_PREFIX},
180 	{"keep-tite",         no_argument,       NULL, ALTERNATE_SCREEN},
181 	{"load-theme",        required_argument, NULL, LOAD_THEME},
182 	{"max-input",         required_argument, NULL, MAX_INPUT},
183 	{"no-cancel",         no_argument,       NULL, NO_CANCEL},
184 	{"nocancel",          no_argument,       NULL, NO_CANCEL},
185 	{"no-descriptions",   no_argument,       NULL, NO_DESCRIPTIONS},
186 	{"no-items",          no_argument,       NULL, NO_DESCRIPTIONS},
187 	{"no-label",          required_argument, NULL, CANCEL_LABEL},
188 	{"no-lines",          no_argument,       NULL, NO_LINES},
189 	{"no-names",          no_argument,       NULL, NO_NAMES},
190 	{"no-ok",             no_argument,       NULL, NO_OK},
191 	{"nook ",             no_argument,       NULL, NO_OK},
192 	{"no-shadow",         no_argument,       NULL, NO_SHADOW},
193 	{"no-tags",           no_argument,       NULL, NO_NAMES},
194 	{"normal-screen",     no_argument,       NULL, NORMAL_SCREEN},
195 	{"ok-label",          required_argument, NULL, OK_LABEL},
196 	{"output-fd",         required_argument, NULL, OUTPUT_FD},
197 	{"output-separator",  required_argument, NULL, OUTPUT_SEPARATOR},
198 	{"print-maxsize",     no_argument,       NULL, PRINT_MAXSIZE},
199 	{"print-size",        no_argument,       NULL, PRINT_SIZE},
200 	{"print-version",     no_argument,       NULL, PRINT_VERSION},
201 	{"quoted",            no_argument,       NULL, QUOTED},
202 	{"save-theme",        required_argument, NULL, SAVE_THEME},
203 	{"separate-output",   no_argument,       NULL, SEPARATE_OUTPUT},
204 	{"separator",         required_argument, NULL, OUTPUT_SEPARATOR},
205 	{"shadow",            no_argument,       NULL, SHADOW},
206 	{"single-quoted",     no_argument,       NULL, SINGLE_QUOTED},
207 	{"sleep",             required_argument, NULL, SLEEP},
208 	{"stderr",            no_argument,       NULL, STDERR},
209 	{"stdout",            no_argument,       NULL, STDOUT},
210 	{"switch-buttons",    no_argument,       NULL, SWITCH_BUTTONS},
211 	{"tab-escape",        no_argument,       NULL, TAB_ESCAPE},
212 	{"tab-len",           required_argument, NULL, TAB_LEN},
213 	{"text-unchanged",    no_argument,       NULL, TEXT_UNCHANGED},
214 	{"theme",             required_argument, NULL, THEME},
215 	{"time-format",       required_argument, NULL, TIME_FORMAT},
216 	{"title",             required_argument, NULL, TITLE},
217 	{"yes-label",         required_argument, NULL, OK_LABEL},
218 	/* Dialogs */
219 	{"calendar",     no_argument, NULL, CALENDAR},
220 	{"checklist",    no_argument, NULL, CHECKLIST},
221 	{"datebox",      no_argument, NULL, DATEBOX},
222 	{"form",         no_argument, NULL, FORM},
223 	{"gauge",        no_argument, NULL, GAUGE},
224 	{"infobox",      no_argument, NULL, INFOBOX},
225 	{"inputbox",     no_argument, NULL, INPUTBOX},
226 	{"menu",         no_argument, NULL, MENU},
227 	{"mixedform",    no_argument, NULL, MIXEDFORM},
228 	{"mixedgauge",   no_argument, NULL, MIXEDGAUGE},
229 	{"msgbox",       no_argument, NULL, MSGBOX},
230 	{"passwordbox",  no_argument, NULL, PASSWORDBOX},
231 	{"passwordform", no_argument, NULL, PASSWORDFORM},
232 	{"pause",        no_argument, NULL, PAUSE},
233 	{"radiolist",    no_argument, NULL, RADIOLIST},
234 	{"rangebox",     no_argument, NULL, RANGEBOX},
235 	{"textbox",      no_argument, NULL, TEXTBOX},
236 	{"timebox",      no_argument, NULL, TIMEBOX},
237 	{"treeview",     no_argument, NULL, TREEVIEW},
238 	{"yesno",        no_argument, NULL, YESNO},
239 	/* END */
240 	{ NULL, 0, NULL, 0}
241 };
242 
243 /* Menus options */
244 static bool item_prefix_opt;
245 static bool item_bottomdesc_opt;
246 static bool item_output_sepnl_opt;
247 static bool item_singlequote_opt;
248 static bool list_items_on_opt;
249 static bool item_help_print_name_opt;
250 static bool item_always_quote_opt;
251 static bool item_depth_opt;
252 static char *item_output_sep_opt;
253 static char *item_default_opt;
254 /* Date and Time options */
255 static char *date_fmt_opt;
256 static char *time_fmt_opt;
257 /* Forms options */
258 static int unsigned max_input_form_opt;
259 /* General options */
260 static bool esc_return_cancel_opt;
261 static bool ignore_opt;
262 static int output_fd_opt;
263 static int getH_opt;
264 static int getW_opt;
265 /* Text option */
266 static bool cr_wrap_opt;
267 static bool tab_escape_opt;
268 static bool text_unchanged_opt;
269 /* Theme and Screen options*/
270 static bool bikeshed_opt;
271 static enum bsddialog_default_theme theme_opt;
272 static char *backtitle_opt;
273 static bool clear_screen_opt;
274 static char *loadthemefile;
275 static char *savethemefile;
276 static const char *screen_mode_opt;
277 
278 /* Functions */
279 #define UNUSED_PAR(x) UNUSED_ ## x __attribute__((__unused__))
280 static void custom_text(char *text, char *buf);
281 static void usage(void);
282 /* Dialogs */
283 #define BUILDER_ARGS struct bsddialog_conf *conf, char* text, int rows,        \
284 	int cols, int argc, char **argv
285 static int calendar_builder(BUILDER_ARGS);
286 static int checklist_builder(BUILDER_ARGS);
287 static int datebox_builder(BUILDER_ARGS);
288 static int form_builder(BUILDER_ARGS);
289 static int gauge_builder(BUILDER_ARGS);
290 static int infobox_builder(BUILDER_ARGS);
291 static int inputbox_builder(BUILDER_ARGS);
292 static int menu_builder(BUILDER_ARGS);
293 static int mixedform_builder(BUILDER_ARGS);
294 static int mixedgauge_builder(BUILDER_ARGS);
295 static int msgbox_builder(BUILDER_ARGS);
296 static int passwordbox_builder(BUILDER_ARGS);
297 static int passwordform_builder(BUILDER_ARGS);
298 static int pause_builder(BUILDER_ARGS);
299 static int radiolist_builder(BUILDER_ARGS);
300 static int rangebox_builder(BUILDER_ARGS);
301 static int textbox_builder(BUILDER_ARGS);
302 static int timebox_builder(BUILDER_ARGS);
303 static int treeview_builder(BUILDER_ARGS);
304 static int yesno_builder(BUILDER_ARGS);
305 
306 /* init, exit and internals */
307 static bool in_bsddialog_mode;
308 static bool mandatory_dialog;
309 static int (*dialogbuilder)(BUILDER_ARGS);
310 
exit_error(const char * errstr,bool with_usage)311 static void exit_error(const char *errstr, bool with_usage)
312 {
313 	if (in_bsddialog_mode)
314 		bsddialog_end();
315 
316 	printf("Error: %s.\n\n", errstr);
317 	if (with_usage) {
318 		printf("See \'bsddialog --help\' or \'man 1 bsddialog\' ");
319 		printf("for more information.\n");
320 	}
321 
322 	exit (255);
323 }
324 
sigint_handler(int UNUSED_PAR (sig))325 static void sigint_handler(int UNUSED_PAR(sig))
326 {
327 	bsddialog_end();
328 
329 	exit(255);
330 }
331 
start_bsddialog_mode(void)332 static void start_bsddialog_mode(void)
333 {
334 	if (in_bsddialog_mode)
335 		return;
336 
337 	if (bsddialog_init() != BSDDIALOG_OK)
338 		exit_error(bsddialog_geterror(), false);
339 
340 	in_bsddialog_mode = true;
341 	signal(SIGINT, sigint_handler);
342 }
343 
error_args(const char * dialog,int argc,char ** argv)344 static void error_args(const char *dialog, int argc, char **argv)
345 {
346 	int i;
347 
348 	if (in_bsddialog_mode)
349 		bsddialog_end();
350 
351 	printf("Error: %s unexpected argument%s:", dialog,
352 	    argc > 1 ? "s" : "");
353 	for (i = 0; i < argc; i++)
354 		printf(" \"%s\"", argv[i]);
355 	printf(".\n\n");
356 	printf("See \'bsddialog --help\' or \'man 1 bsddialog\' ");
357 	printf("for more information.\n");
358 
359 	exit (255);
360 }
361 
usage(void)362 static void usage(void)
363 {
364 	printf("usage: bsddialog --help\n");
365 	printf("       bsddialog --version\n");
366 	printf("       bsddialog [--<opt>] --<dialog> <text> <rows> <cols> "
367 	    "[<arg>]\n");
368 	printf("       bsddialog --<dialog1> ... [--and-dialog --<dialog2> "
369 	    "...] ...\n");
370 	printf("\n");
371 
372 	printf("Options:\n");
373 	printf(" --alternate-screen, --ascii-lines, --backtitle <backtitle>,"
374 	    " --begin-x <x>,\n --begin-y <y>, --bikeshed, --calendar,"
375 	    " --cancel-label <label>, --clear-dialog,\n --clear-screen,"
376 	    " --colors, --columns-per-row <columns>, --cr-wrap,\n"
377 	    " --date-format <format>, --default-button <label>,"
378 	    " --default-item <name>,\n --default-no, --disable-esc,"
379 	    " --esc-return-cancel, --exit-label <label>,\n --extra-button,"
380 	    " --extra-label <label>, --generic-button1 <label>,\n"
381 	    " --generic-button2 <label>, --help-button, --help-label <label>,\n"
382 	    " --help-print-name, --help-status, --hfile <file>,"
383 	    " --hline <string>,\n --hmsg <string>, --ignore, --insecure,"
384 	    " --item-bottom-desc, --item-depth,\n --item-prefix,"
385 	    " --load-theme <file>, --max-input <size>, --no-cancel,\n"
386 	    " --no-descriptions, --no-label <label>, --no-lines, --no-names,"
387 	    " --no-ok,\n --no-shadow, --normal-screen, --ok-label <label>,"
388 	    " --output-fd <fd>,\n --output-separator <sep>, --print-maxsize,"
389 	    " --print-size, --print-version,\n --quoted, --save-theme <file>,"
390 	    " --separate-output, --separator <sep>, --shadow,\n"
391 	    " --single-quoted, --sleep <secs>, --stderr, --stdout,"
392 	    " --tab-escape,\n --tab-len <spaces>, --text-unchanged,"
393 	    " --switch-buttons,\n --theme <blackwhite|bsddialog|flat|dialog>,"
394 	    " --time-format <format>,\n --title <title>,"
395 	    " --yes-label <label>.\n");
396 	printf("\n");
397 
398 	printf("Dialogs:\n");
399 	printf(" --calendar <text> <rows> <cols> [<dd> <mm> <yy>]\n");
400 	printf(" --checklist <text> <rows> <cols> <menurows> [<name> <desc> "
401 	    "<on|off>] ...\n");
402 	printf(" --datebox <text> <rows> <cols> [<dd> <mm> <yy>]\n");
403 	printf(" --form <text> <rows> <cols> <formrows> [<label> <ylabel> "
404 	    "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxletters>] "
405 	    "...\n");
406 	printf(" --gauge <text> <rows> <cols> [<perc>]\n");
407 	printf(" --infobox <text> <rows> <cols>\n");
408 	printf(" --inputbox <text> <rows> <cols> [init]\n");
409 	printf(" --menu <text> <rows> <cols> <menurows> [<name> <desc>] ...\n");
410 	printf(" --mixedform <text> <rows> <cols> <formrows> [<label> <ylabel> "
411 	    "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxletters> "
412 	    "<0|1|2>] ...\n");
413 	printf(" --mixedgauge <text> <rows> <cols> <mainperc> [<minilabel> "
414 	    "<miniperc>] ...\n");
415 	printf(" --msgbox <text> <rows> <cols>\n");
416 	printf(" --passwordbox <text> <rows> <cols> [init]\n");
417 	printf(" --passwordform <text> <rows> <cols> <formrows> [<label> "
418 	    "<ylabel> <xlabel> <init> <yfield> <xfield> <fieldlen> "
419 	    "<maxletters>] ...\n");
420 	printf(" --pause <text> <rows> <cols> <secs>\n");
421 	printf(" --radiolist <text> <rows> <cols> <menurows> [<name> <desc> "
422 	    "<on|off>] ...\n");
423 	printf(" --rangebox <text> <rows> <cols> <min> <max> [<init>]\n");
424 	printf(" --textbox <file> <rows> <cols>\n");
425 	printf(" --timebox <text> <rows> <cols> [<hh> <mm> <ss>]\n");
426 	printf(" --treeview <text> <rows> <cols> <menurows> [<depth> <name> "
427 	    "<desc> <on|off>] ...\n");
428 	printf(" --yesno <text> <rows> <cols>\n");
429 	printf("\n");
430 
431 	printf("See 'man 1 bsddialog' for more information.\n");
432 }
433 
parseargs(int argc,char ** argv,struct bsddialog_conf * conf)434 static int parseargs(int argc, char **argv, struct bsddialog_conf *conf)
435 {
436 	int arg, parsed, i;
437 	struct winsize ws;
438 
439 	bsddialog_initconf(conf);
440 	conf->key.enable_esc = true;
441 	conf->menu.on_without_ok = true;
442 	conf->form.value_without_ok = true;
443 	conf->button.always_active = true;
444 
445 	dialogbuilder = NULL;
446 
447 	backtitle_opt = NULL;
448 	theme_opt = -1;
449 	output_fd_opt = STDERR_FILENO;
450 	ignore_opt = false;
451 	cr_wrap_opt = false;
452 	tab_escape_opt = false;
453 	text_unchanged_opt = false;
454 	esc_return_cancel_opt = false;
455 	bikeshed_opt = false;
456 	savethemefile = NULL;
457 	loadthemefile = NULL;
458 	clear_screen_opt = false;
459 	screen_mode_opt = NULL;
460 
461 	item_output_sepnl_opt = false;
462 	item_singlequote_opt = false;
463 	item_prefix_opt = false;
464 	item_bottomdesc_opt = false;
465 	item_depth_opt = false;
466 	list_items_on_opt = false;
467 	item_help_print_name_opt = false;
468 	item_always_quote_opt = false;
469 	item_output_sep_opt = NULL;
470 	item_default_opt = NULL;
471 
472 	date_fmt_opt = NULL;
473 	time_fmt_opt = NULL;
474 
475 	max_input_form_opt = 2048;
476 
477 	for (i = 0; i < argc; i++) {
478 		if (strcmp(argv[i], "--and-dialog") == 0 ||
479 		    strcmp(argv[i], "--and-widget") == 0) {
480 			argc = i + 1;
481 			break;
482 		}
483 	}
484 	parsed = argc;
485 	while ((arg = getopt_long(argc, argv, "", longopts, NULL)) != -1) {
486 		switch (arg) {
487 		/* Options */
488 		case ALTERNATE_SCREEN:
489 			screen_mode_opt = "smcup";
490 			break;
491 		case AND_DIALOG:
492 			if (dialogbuilder == NULL)
493 				exit_error("--and-dialog without previous "
494 				    "--<dialog>", true);
495 			break;
496 		case ASCII_LINES:
497 			conf->ascii_lines = true;
498 			break;
499 		case BACKTITLE:
500 			backtitle_opt = optarg;
501 			if (conf->y == BSDDIALOG_CENTER)
502 				conf->auto_topmargin = 2;
503 			break;
504 		case BEGIN_X:
505 			conf->x = (int)strtol(optarg, NULL, 10);
506 			if (conf->x < BSDDIALOG_CENTER)
507 				exit_error("--begin-x < -1", false);
508 			break;
509 		case BEGIN_Y:
510 			conf->y = (int)strtol(optarg, NULL, 10);
511 			if (conf->y < BSDDIALOG_CENTER)
512 				exit_error("--begin-y < -1", false);
513 			conf->auto_topmargin = 0;
514 			break;
515 		case BIKESHED:
516 			bikeshed_opt = true;
517 			break;
518 		case CANCEL_LABEL:
519 			conf->button.cancel_label = optarg;
520 			break;
521 		case CLEAR_DIALOG:
522 			conf->clear = true;
523 			break;
524 		case CLEAR_SCREEN:
525 			mandatory_dialog = false;
526 			clear_screen_opt = true;
527 			break;
528 		case COLORS:
529 			conf->text.highlight = true;
530 			break;
531 		case COLUMNS_PER_ROW:
532 			conf->text.cols_per_row =
533 			    (u_int)strtoul(optarg, NULL, 10);
534 			break;
535 		case CR_WRAP:
536 			cr_wrap_opt = true;
537 			break;
538 		case DATE_FORMAT:
539 			date_fmt_opt = optarg;
540 			break;
541 		case DEFAULT_BUTTON:
542 			conf->button.default_label = optarg;
543 			break;
544 		case DEFAULT_ITEM:
545 			item_default_opt = optarg;
546 			break;
547 		case DEFAULT_NO:
548 			conf->button.default_cancel = true;
549 			break;
550 		case DISABLE_ESC:
551 			conf->key.enable_esc = false;
552 			break;
553 		case ESC_RETURNCANCEL:
554 			esc_return_cancel_opt = true;
555 			break;
556 		case EXIT_LABEL:
557 			conf->button.ok_label = optarg;
558 			break;
559 		case EXTRA_BUTTON:
560 			conf->button.with_extra = true;
561 			break;
562 		case EXTRA_LABEL:
563 			conf->button.extra_label = optarg;
564 			break;
565 		case GENERIC_BUTTON1:
566 			conf->button.generic1_label = optarg;
567 			break;
568 		case GENERIC_BUTTON2:
569 			conf->button.generic2_label = optarg;
570 			break;
571 		case HELP_BUTTON:
572 			conf->button.with_help = true;
573 			break;
574 		case HELP_LABEL:
575 			conf->button.help_label = optarg;
576 			break;
577 		case HELP_PRINT_NAME:
578 			item_help_print_name_opt = true;
579 			break;
580 		case HELP_STATUS:
581 			list_items_on_opt = true;
582 			break;
583 		case HFILE:
584 			conf->key.f1_file = optarg;
585 			break;
586 		case HLINE:
587 			if (optarg[0] != '\0')
588 				conf->bottomtitle = optarg;
589 			break;
590 		case HMSG:
591 			conf->key.f1_message = optarg;
592 			break;
593 		case IGNORE:
594 			ignore_opt = true;
595 			break;
596 		case INSECURE:
597 			conf->form.securech = '*';
598 			break;
599 		case ITEM_BOTTOM_DESC:
600 			item_bottomdesc_opt = true;
601 			break;
602 		case ITEM_DEPTH:
603 			item_depth_opt = true;
604 			break;
605 		case ITEM_PREFIX:
606 			item_prefix_opt = true;
607 			break;
608 		case LOAD_THEME:
609 			loadthemefile = optarg;
610 			break;
611 		case MAX_INPUT:
612 			max_input_form_opt = (u_int)strtoul(optarg, NULL, 10);
613 			break;
614 		case NO_CANCEL:
615 			conf->button.without_cancel = true;
616 			break;
617 		case NO_DESCRIPTIONS:
618 			conf->menu.no_desc = true;
619 			break;
620 		case NO_LINES:
621 			conf->no_lines = true;
622 			break;
623 		case NO_NAMES:
624 			conf->menu.no_name = true;
625 			break;
626 		case NO_OK:
627 			conf->button.without_ok = true;
628 			break;
629 		case NO_SHADOW:
630 			conf->shadow = false;
631 			break;
632 		case NORMAL_SCREEN:
633 			screen_mode_opt = "rmcup";
634 			break;
635 		case OK_LABEL:
636 			conf->button.ok_label = optarg;
637 			break;
638 		case OUTPUT_FD:
639 			output_fd_opt = (int)strtol(optarg, NULL, 10);
640 			break;
641 		case OUTPUT_SEPARATOR:
642 			item_output_sep_opt = optarg;
643 			break;
644 		case QUOTED:
645 			item_always_quote_opt = true;
646 			break;
647 		case PRINT_MAXSIZE:
648 			mandatory_dialog = false;
649 			ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws);
650 			dprintf(output_fd_opt, "MaxSize: %d, %d\n",
651 			    ws.ws_row, ws.ws_col);
652 			break;
653 		case PRINT_SIZE:
654 			conf->get_height = &getH_opt;
655 			conf->get_width = &getW_opt;
656 			break;
657 		case PRINT_VERSION:
658 			mandatory_dialog = false;
659 			dprintf(output_fd_opt, "Version: %s\n",
660 			    LIBBSDDIALOG_VERSION);
661 			break;
662 		case SAVE_THEME:
663 			mandatory_dialog = false;
664 			savethemefile = optarg;
665 			break;
666 		case SEPARATE_OUTPUT:
667 			item_output_sepnl_opt = true;
668 			break;
669 		case SHADOW:
670 			conf->shadow = true;
671 			break;
672 		case SINGLE_QUOTED:
673 			item_singlequote_opt = true;
674 			break;
675 		case SLEEP:
676 			conf->sleep = (u_int)strtoul(optarg, NULL, 10);
677 			break;
678 		case STDERR:
679 			output_fd_opt = STDERR_FILENO;
680 			break;
681 		case STDOUT:
682 			output_fd_opt = STDOUT_FILENO;
683 			break;
684 		case SWITCH_BUTTONS:
685 			conf->button.always_active = false;
686 			break;
687 		case TAB_ESCAPE:
688 			tab_escape_opt = true;
689 			break;
690 		case TAB_LEN:
691 			conf->text.tablen = (u_int)strtoul(optarg, NULL, 10);
692 			break;
693 		case TEXT_UNCHANGED:
694 			text_unchanged_opt = true;
695 			break;
696 		case THEME:
697 			if (strcasecmp(optarg, "bsddialog") == 0)
698 				theme_opt = BSDDIALOG_THEME_BSDDIALOG;
699 			else if (strcasecmp(optarg, "blackwhite") == 0)
700 				theme_opt = BSDDIALOG_THEME_BLACKWHITE;
701 			else if (strcasecmp(optarg, "flat") == 0)
702 				theme_opt = BSDDIALOG_THEME_FLAT;
703 			else if (strcasecmp(optarg, "dialog") == 0)
704 				theme_opt = BSDDIALOG_THEME_DIALOG;
705 			else
706 				exit_error("--theme: <unknown> theme", false);
707 			break;
708 		case TIME_FORMAT:
709 			time_fmt_opt = optarg;
710 			break;
711 		case TITLE:
712 			conf->title = optarg;
713 			break;
714 		/* Dialogs */
715 		case CALENDAR:
716 			if (dialogbuilder != NULL)
717 				exit_error("unexpected --calendar", true);
718 			dialogbuilder = calendar_builder;
719 			break;
720 		case CHECKLIST:
721 			if (dialogbuilder != NULL)
722 				exit_error("unexpected --checklist", true);
723 			dialogbuilder = checklist_builder;
724 			conf->auto_downmargin = 1;
725 			break;
726 		case DATEBOX:
727 			if (dialogbuilder != NULL)
728 				exit_error("unexpected --datebox", true);
729 			dialogbuilder = datebox_builder;
730 			break;
731 		case FORM:
732 			if (dialogbuilder != NULL)
733 				exit_error("unexpected --form", true);
734 			dialogbuilder = form_builder;
735 			conf->auto_downmargin = 1;
736 			break;
737 		case GAUGE:
738 			if (dialogbuilder != NULL)
739 				exit_error("unexpected --gauge", true);
740 			dialogbuilder = gauge_builder;
741 			break;
742 		case INFOBOX:
743 			if (dialogbuilder != NULL)
744 				exit_error("unexpected --infobox", true);
745 			dialogbuilder = infobox_builder;
746 			break;
747 		case INPUTBOX:
748 			if (dialogbuilder != NULL)
749 				exit_error("unexpected --inputbox", true);
750 			dialogbuilder = inputbox_builder;
751 			conf->auto_downmargin = 1;
752 			break;
753 		case MENU:
754 			if (dialogbuilder != NULL)
755 				exit_error("unexpected --menu", true);
756 			dialogbuilder = menu_builder;
757 			conf->auto_downmargin = 1;
758 			break;
759 		case MIXEDFORM:
760 			if (dialogbuilder != NULL)
761 				exit_error("unexpected --mixedform", true);
762 			dialogbuilder = mixedform_builder;
763 			conf->auto_downmargin = 1;
764 			break;
765 		case MIXEDGAUGE:
766 			if (dialogbuilder != NULL)
767 				exit_error("unexpected --mixedgauge", true);
768 			dialogbuilder = mixedgauge_builder;
769 			break;
770 		case MSGBOX:
771 			if (dialogbuilder != NULL)
772 				exit_error("unexpected --msgbox", true);
773 			dialogbuilder = msgbox_builder;
774 			break;
775 		case PAUSE:
776 			if (dialogbuilder != NULL)
777 				exit_error("unexpected --pause", true);
778 			dialogbuilder = pause_builder;
779 			break;
780 		case PASSWORDBOX:
781 			if (dialogbuilder != NULL)
782 				exit_error("unexpected --passwordbox", true);
783 			dialogbuilder = passwordbox_builder;
784 			conf->auto_downmargin = 1;
785 			break;
786 		case PASSWORDFORM:
787 			if (dialogbuilder != NULL)
788 				exit_error("unexpected --passwordform", true);
789 			dialogbuilder = passwordform_builder;
790 			conf->auto_downmargin = 1;
791 			break;
792 		case RADIOLIST:
793 			if (dialogbuilder != NULL)
794 				exit_error("unexpected --radiolist", true);
795 			dialogbuilder = radiolist_builder;
796 			conf->auto_downmargin = 1;
797 			break;
798 		case RANGEBOX:
799 			if (dialogbuilder != NULL)
800 				exit_error("unexpected --rangebox", true);
801 			dialogbuilder = rangebox_builder;
802 			break;
803 		case TEXTBOX:
804 			if (dialogbuilder != NULL)
805 				exit_error("unexpected --textbox", true);
806 			dialogbuilder = textbox_builder;
807 			break;
808 		case TIMEBOX:
809 			if (dialogbuilder != NULL)
810 				exit_error("unexpected --timebox", true);
811 			dialogbuilder = timebox_builder;
812 			break;
813 		case TREEVIEW:
814 			if (dialogbuilder != NULL)
815 				exit_error("unexpected --treeview", true);
816 			dialogbuilder = treeview_builder;
817 			conf->auto_downmargin = 1;
818 			break;
819 		case YESNO:
820 			if (dialogbuilder != NULL)
821 				exit_error("unexpected --yesno", true);
822 			dialogbuilder = yesno_builder;
823 			break;
824 		default: /* Error */
825 			if (ignore_opt == true)
826 				break;
827 			exit_error("--ignore to continue", true);
828 		}
829 	}
830 
831 	return (parsed);
832 }
833 
main(int argc,char * argv[argc])834 int main(int argc, char *argv[argc])
835 {
836 	int i, rows, cols, retval, parsed, nargc, firstoptind;
837 	char *text, **nargv, *pn;
838 	struct bsddialog_conf conf;
839 
840 	setlocale(LC_ALL, "");
841 
842 	in_bsddialog_mode = false;
843 	mandatory_dialog = true;
844 	firstoptind = optind;
845 	pn = argv[0];
846 	retval = BSDDIALOG_OK;
847 
848 	for (i = 0; i < argc; i++) {
849 		if (strcmp(argv[i], "--version") == 0) {
850 			printf("Version: %s\n", LIBBSDDIALOG_VERSION);
851 			return (BSDDIALOG_OK);
852 		}
853 		if (strcmp(argv[i], "--help") == 0) {
854 			usage();
855 			return (BSDDIALOG_OK);
856 		}
857 	}
858 
859 	while (true) {
860 		parsed = parseargs(argc, argv, &conf);
861 		nargc = argc - parsed;
862 		nargv = argv + parsed;
863 		argc = parsed - optind;
864 		argv += optind;
865 
866 		if (mandatory_dialog && dialogbuilder == NULL)
867 			exit_error("expected a --<dialog>", true);
868 
869 		if (dialogbuilder == NULL && argc > 0)
870 			error_args("(no --<dialog>)", argc, argv);
871 
872 		/* --print-maxsize or --print-version */
873 		if (mandatory_dialog == false && savethemefile == NULL &&
874 		    clear_screen_opt == false)
875 			return (BSDDIALOG_OK);
876 
877 		/* --<dialog>, --save-theme or clear-screen */
878 		if (dialogbuilder != NULL) {
879 			if (argc < 3)
880 				exit_error("expected <text> <rows> <cols>",
881 				    true);
882 			if ((text = strdup(argv[0])) == NULL)
883 				exit_error("cannot allocate text", false);
884 			if (dialogbuilder != textbox_builder)
885 				custom_text(argv[0], text);
886 			rows = (int)strtol(argv[1], NULL, 10);
887 			cols = (int)strtol(argv[2], NULL, 10);
888 			argc -= 3;
889 			argv += 3;
890 		}
891 
892 		/* bsddialog terminal mode (first iteration) */
893 		start_bsddialog_mode();
894 
895 		if (screen_mode_opt != NULL) {
896 			screen_mode_opt = tigetstr(screen_mode_opt);
897 			if (screen_mode_opt != NULL &&
898 			    screen_mode_opt != (char*)-1) {
899 				tputs(screen_mode_opt, 1, putchar);
900 				fflush(stdout);
901 				 /* only to refresh, useless in the library */
902 				bsddialog_clearterminal();
903 			}
904 		}
905 
906 		/* theme */
907 		if (theme_opt >= 0)
908 			bsddialog_set_default_theme(theme_opt);
909 		if (loadthemefile != NULL)
910 			loadtheme(loadthemefile);
911 		if (bikeshed_opt)
912 			bikeshed(&conf);
913 		if (savethemefile != NULL)
914 			savetheme(savethemefile, LIBBSDDIALOG_VERSION);
915 
916 		/* backtitle and dialog */
917 		if (dialogbuilder == NULL)
918 			break;
919 		if (backtitle_opt != NULL)
920 			if(bsddialog_backtitle(&conf, backtitle_opt))
921 				exit_error(bsddialog_geterror(), false);
922 		retval = dialogbuilder(&conf, text, rows, cols, argc, argv);
923 		free(text);
924 		if (retval == BSDDIALOG_ERROR)
925 			exit_error(bsddialog_geterror(), false);
926 		if (retval == BSDDIALOG_ESC && esc_return_cancel_opt)
927 			retval = BSDDIALOG_CANCEL;
928 		if (conf.get_height != NULL && conf.get_width != NULL)
929 			dprintf(output_fd_opt, "DialogSize: %d, %d\n",
930 			    *conf.get_height, *conf.get_width);
931 		if (clear_screen_opt)
932 			bsddialog_clearterminal();
933 		clear_screen_opt = false;
934 		/* --and-dialog ends loop with Cancel or ESC */
935 		if (retval == BSDDIALOG_CANCEL || retval == BSDDIALOG_ESC)
936 			break;
937 		argc = nargc;
938 		argv = nargv;
939 		if (argc <= 0)
940 			break;
941 		/* prepare next parseargs() call */
942 		argc++;
943 		argv--;
944 		argv[0] = pn;
945 		optind = firstoptind;
946 	}
947 
948 	if (in_bsddialog_mode) {
949 		/* --clear-screen can be a single option */
950 		if (clear_screen_opt)
951 			bsddialog_clearterminal();
952 		bsddialog_end();
953 	}
954 	/* end bsddialog terminal mode */
955 
956 	return (retval);
957 }
958 
custom_text(char * text,char * buf)959 void custom_text(char *text, char *buf)
960 {
961 	bool trim, crwrap;
962 	int i, j;
963 
964 	if (strstr(text, "\\n") == NULL) {
965 		/* "hasnl" mode */
966 		trim = true;
967 		crwrap = true;
968 	} else {
969 		trim = false;
970 		crwrap = cr_wrap_opt;
971 	}
972 	if (text_unchanged_opt) {
973 		trim = false;
974 		crwrap = true;
975 	}
976 
977 	i = j = 0;
978 	while (text[i] != '\0') {
979 		switch (text[i]) {
980 		case '\\':
981 			buf[j] = '\\';
982 			switch (text[i+1]) {
983 			case 'n': /* implicitly in "hasnl" mode */
984 				buf[j] = '\n';
985 				i++;
986 				if (text[i+1] == '\n')
987 					i++;
988 				break;
989 			case 't':
990 				if (tab_escape_opt) {
991 					buf[j] = '\t';
992 				} else {
993 					j++;
994 					buf[j] = 't';
995 				}
996 				i++;
997 				break;
998 			}
999 			break;
1000 		case '\n':
1001 			buf[j] = crwrap ? '\n' : ' ';
1002 			break;
1003 		case '\t':
1004 			buf[j] = text_unchanged_opt ? '\t' : ' ';
1005 			break;
1006 		default:
1007 			buf[j] = text[i];
1008 		}
1009 		i++;
1010 		if (!trim || buf[j] != ' ' || j == 0 || buf[j-1] != ' ')
1011 			j++;
1012 	}
1013 	buf[j] = '\0';
1014 }
1015 
1016 /* Dialogs */
gauge_builder(BUILDER_ARGS)1017 int gauge_builder(BUILDER_ARGS)
1018 {
1019 	int output;
1020 	unsigned int perc;
1021 
1022 	perc = 0;
1023 	if (argc == 1) {
1024 		perc = (u_int)strtoul(argv[0], NULL, 10);
1025 		perc = perc > 100 ? 100 : perc;
1026 	} else if (argc > 1) {
1027 		error_args("--gauge", argc - 1, argv + 1);
1028 	}
1029 
1030 	output = bsddialog_gauge(conf, text, rows, cols, perc, STDIN_FILENO,
1031 	    "XXX");
1032 
1033 	return (output);
1034 }
1035 
infobox_builder(BUILDER_ARGS)1036 int infobox_builder(BUILDER_ARGS)
1037 {
1038 	if (argc > 0)
1039 		error_args("--infobox", argc, argv);
1040 
1041 	return (bsddialog_infobox(conf, text, rows, cols));
1042 }
1043 
mixedgauge_builder(BUILDER_ARGS)1044 int mixedgauge_builder(BUILDER_ARGS)
1045 {
1046 	int output, *minipercs;
1047 	unsigned int i, mainperc, nminibars;
1048 	const char **minilabels;
1049 
1050 	if (argc < 1 || (((argc-1) % 2) != 0) )
1051 		exit_error("bad --mixedgauge arguments", true);
1052 
1053 	mainperc = (u_int)strtoul(argv[0], NULL, 10);
1054 	mainperc = mainperc > 100 ? 100 : mainperc;
1055 	argc--;
1056 	argv++;
1057 
1058 	nminibars  = argc / 2;
1059 	if ((minilabels = calloc(nminibars, sizeof(char*))) == NULL)
1060 		exit_error("Cannot allocate memory for minilabels", false);
1061 	if ((minipercs = calloc(nminibars, sizeof(int))) == NULL)
1062 		exit_error("Cannot allocate memory for minipercs", false);
1063 
1064 	for (i = 0; i < nminibars; i++) {
1065 		minilabels[i] = argv[i * 2];
1066 		minipercs[i] = (int)strtol(argv[i * 2 + 1], NULL, 10);
1067 	}
1068 
1069 	output = bsddialog_mixedgauge(conf, text, rows, cols, mainperc,
1070 	    nminibars, minilabels, minipercs);
1071 
1072 	return (output);
1073 }
1074 
msgbox_builder(BUILDER_ARGS)1075 int msgbox_builder(BUILDER_ARGS)
1076 {
1077 	if (argc > 0)
1078 		error_args("--msgbox", argc, argv);
1079 
1080 	return (bsddialog_msgbox(conf, text, rows, cols));
1081 }
1082 
pause_builder(BUILDER_ARGS)1083 int pause_builder(BUILDER_ARGS)
1084 {
1085 	int output;
1086 	unsigned int secs;
1087 
1088 	if (argc == 0)
1089 		exit_error("--pause missing <seconds>", true);
1090 	if (argc > 1)
1091 		error_args("--pause", argc - 1, argv + 1);
1092 
1093 	secs = (u_int)strtoul(argv[0], NULL, 10);
1094 	output = bsddialog_pause(conf, text, rows, cols, secs);
1095 
1096 	return (output);
1097 }
1098 
rangebox_builder(BUILDER_ARGS)1099 int rangebox_builder(BUILDER_ARGS)
1100 {
1101 	int output, min, max, value;
1102 
1103 	if (argc < 2)
1104 		exit_error("--rangebox missing <min> <max> [<init>]", true);
1105 	if (argc > 3)
1106 		error_args("--rangebox", argc - 3, argv + 3);
1107 
1108 	min = (int)strtol(argv[0], NULL, 10);
1109 	max = (int)strtol(argv[1], NULL, 10);
1110 
1111 	if (argc == 3) {
1112 		value = (int)strtol(argv[2], NULL, 10);
1113 		value = value < min ? min : value;
1114 		value = value > max ? max : value;
1115 	} else
1116 		value = min;
1117 
1118 	output = bsddialog_rangebox(conf, text, rows, cols, min, max, &value);
1119 	dprintf(output_fd_opt, "%d", value);
1120 
1121 	return (output);
1122 }
1123 
textbox_builder(BUILDER_ARGS)1124 int textbox_builder(BUILDER_ARGS)
1125 {
1126 	if (argc > 0)
1127 		error_args("--textbox", argc, argv);
1128 
1129 	return (bsddialog_textbox(conf, text, rows, cols));
1130 }
1131 
yesno_builder(BUILDER_ARGS)1132 int yesno_builder(BUILDER_ARGS)
1133 {
1134 	if (argc > 0)
1135 		error_args("--yesno", argc, argv);
1136 
1137 	return (bsddialog_yesno(conf, text, rows, cols));
1138 }
1139 
1140 /* CALENDAR, DATE and TIME */
date(BUILDER_ARGS,bool is_datebox)1141 static int date(BUILDER_ARGS, bool is_datebox)
1142 {
1143 	int ret;
1144 	unsigned int yy, mm, dd;
1145 	time_t cal;
1146 	struct tm *localtm;
1147 	char stringdate[1024];
1148 	const char *name;
1149 
1150 	name = is_datebox ? "--datebox" : "--calendar";
1151 	time(&cal);
1152 	localtm = localtime(&cal);
1153 	yy = localtm->tm_year + 1900;
1154 	mm = localtm->tm_mon + 1;
1155 	dd = localtm->tm_mday;
1156 
1157 	if (argc > 3) {
1158 		error_args(name, argc - 3, argv + 3);
1159 	} else if (argc == 3) {
1160 		dd = (u_int)strtoul(argv[0], NULL, 10);
1161 		mm = (u_int)strtoul(argv[1], NULL, 10);
1162 		yy = (u_int)strtoul(argv[2], NULL, 10);
1163 		if (yy < 1900)
1164 			yy = 1900;
1165 		/* max yy check is in lib */
1166 	}
1167 
1168 	if (is_datebox)
1169 		ret = bsddialog_datebox(conf, text, rows, cols, &yy, &mm, &dd);
1170 	else
1171 		ret = bsddialog_calendar(conf, text, rows, cols, &yy, &mm, &dd);
1172 	if (ret != BSDDIALOG_OK)
1173 		return (ret);
1174 
1175 	if (date_fmt_opt != NULL) {
1176 		time(&cal);
1177 		localtm = localtime(&cal);
1178 		localtm->tm_year = yy - 1900;
1179 		localtm->tm_mon = mm - 1;
1180 		localtm->tm_mday = dd;
1181 		strftime(stringdate, 1024, date_fmt_opt, localtm);
1182 		dprintf(output_fd_opt, "%s", stringdate);
1183 	} else if (bikeshed_opt && (dd % 2 == 0)) {
1184 		dprintf(output_fd_opt, "%u/%u/%u", dd, mm, yy);
1185 	} else {
1186 		dprintf(output_fd_opt, "%02u/%02u/%u", dd, mm, yy);
1187 	}
1188 
1189 	return (ret);
1190 }
1191 
calendar_builder(BUILDER_ARGS)1192 int calendar_builder(BUILDER_ARGS)
1193 {
1194 	if (rows == 2) {
1195 		/*
1196 		 * (bsdconfig/share/dialog.subr:1352) f_dialog_calendar_size()
1197 		 * computes height 2 for `dialog --calendar' in
1198 		 * (bsdconfig/usermgmt/share/user_input.subr:517)
1199 		 * f_dialog_input_expire_password() and
1200 		 * (bsdconfig/usermgmt/share/user_input.subr:660)
1201 		 * f_dialog_input_expire_account().
1202 		 * Use height auto-sizing that is min height like dialog,
1203 		 * documented in bsddialog(1).
1204 		 */
1205 		rows = 0;
1206 	}
1207 
1208 	return (date(conf, text, rows, cols, argc, argv, false));
1209 }
1210 
datebox_builder(BUILDER_ARGS)1211 int datebox_builder(BUILDER_ARGS)
1212 {
1213 	return (date(conf, text, rows, cols, argc, argv, true));
1214 }
1215 
timebox_builder(BUILDER_ARGS)1216 int timebox_builder(BUILDER_ARGS)
1217 {
1218 	int output;
1219 	unsigned int hh, mm, ss;
1220 	time_t clock;
1221 	struct tm *localtm;
1222 	char stringtime[1024];
1223 
1224 	time(&clock);
1225 	localtm = localtime(&clock);
1226 	hh = localtm->tm_hour;
1227 	mm = localtm->tm_min;
1228 	ss = localtm->tm_sec;
1229 
1230 	if (argc > 3) {
1231 		error_args("--timebox", argc - 3, argv + 3);
1232 	} else if (argc == 3) {
1233 		hh = (u_int)strtoul(argv[0], NULL, 10);
1234 		mm = (u_int)strtoul(argv[1], NULL, 10);
1235 		ss = (u_int)strtoul(argv[2], NULL, 10);
1236 	}
1237 
1238 	output = bsddialog_timebox(conf, text, rows, cols, &hh, &mm, &ss);
1239 	if (output != BSDDIALOG_OK)
1240 		return (output);
1241 
1242 	if (time_fmt_opt != NULL) {
1243 		time(&clock);
1244 		localtm = localtime(&clock);
1245 		localtm->tm_hour = hh;
1246 		localtm->tm_min = mm;
1247 		localtm->tm_sec = ss;
1248 		strftime(stringtime, 1024, time_fmt_opt, localtm);
1249 		dprintf(output_fd_opt, "%s", stringtime);
1250 	} else if (bikeshed_opt && (ss % 2 == 0)) {
1251 		dprintf(output_fd_opt, "%u:%u:%u", hh, mm, ss);
1252 	} else {
1253 		dprintf(output_fd_opt, "%02u:%02u:%02u", hh, mm, ss);
1254 	}
1255 
1256 	return (output);
1257 }
1258 
1259 /* MENU */
1260 static void
get_menu_items(int argc,char ** argv,bool setprefix,bool setdepth,bool setname,bool setdesc,bool setstatus,bool sethelp,unsigned int * nitems,struct bsddialog_menuitem ** items,int * focusitem)1261 get_menu_items(int argc, char **argv, bool setprefix, bool setdepth,
1262     bool setname, bool setdesc, bool setstatus, bool sethelp,
1263     unsigned int *nitems, struct bsddialog_menuitem **items, int *focusitem)
1264 {
1265 	unsigned int i, j, sizeitem;
1266 
1267 	*focusitem = -1;
1268 
1269 	sizeitem = 0;
1270 	sizeitem += setprefix ? 1 : 0;
1271 	sizeitem += setdepth  ? 1 : 0;
1272 	sizeitem += setname   ? 1 : 0;
1273 	sizeitem += setdesc   ? 1 : 0;
1274 	sizeitem += setstatus ? 1 : 0;
1275 	sizeitem += sethelp   ? 1 : 0;
1276 	if ((argc % sizeitem) != 0)
1277 		exit_error("\"menu\" bad arguments items number", true);
1278 
1279 	*nitems = argc / sizeitem;
1280 
1281 	*items = calloc(*nitems, sizeof(struct bsddialog_menuitem));
1282 	if (items == NULL)
1283 		exit_error("cannot allocate memory \"menu\" items", false);
1284 
1285 	j = 0;
1286 	for (i = 0; i < *nitems; i++) {
1287 		(*items)[i].prefix = setprefix ? argv[j++] : "";
1288 		(*items)[i].depth = setdepth ?
1289 		    (u_int)strtoul(argv[j++], NULL, 0) : 0;
1290 		(*items)[i].name = setname ? argv[j++] : "";
1291 		(*items)[i].desc = setdesc ? argv[j++] : "";
1292 		if (setstatus)
1293 			(*items)[i].on = strcmp(argv[j++], "on") == 0 ?
1294 			    true : false;
1295 		else
1296 			(*items)[i].on = false;
1297 		(*items)[i].bottomdesc = sethelp ? argv[j++] : "";
1298 
1299 		if (item_default_opt != NULL && *focusitem == -1)
1300 			if (strcmp((*items)[i].name, item_default_opt) == 0)
1301 				*focusitem = i;
1302 	}
1303 }
1304 
1305 static void
print_menu_items(int output,int nitems,struct bsddialog_menuitem * items,int focusitem,bool ismenu)1306 print_menu_items(int output, int nitems, struct bsddialog_menuitem *items,
1307     int focusitem, bool ismenu)
1308 {
1309 	bool sep, sepfirst, seplast, toquote;
1310 	int i;
1311 	char quotech;
1312 	const char *focusname, *sepstr;
1313 
1314 	sep = false;
1315 	quotech = item_singlequote_opt ? '\'' : '"';
1316 
1317 	if (output == BSDDIALOG_ERROR || output == BSDDIALOG_CANCEL ||
1318 	    output == BSDDIALOG_ESC)
1319 		return;
1320 
1321 	if (output == BSDDIALOG_HELP) {
1322 		dprintf(output_fd_opt, "HELP ");
1323 
1324 		if (focusitem >= 0) {
1325 			focusname = items[focusitem].name;
1326 			if (item_bottomdesc_opt &&
1327 			    item_help_print_name_opt == false)
1328 				focusname = items[focusitem].bottomdesc;
1329 
1330 			toquote = false;
1331 			if (strchr(focusname, ' ') != NULL) {
1332 				toquote = item_always_quote_opt;
1333 				if (ismenu == false &&
1334 				    item_output_sepnl_opt == false)
1335 					toquote = true;
1336 			}
1337 			if (toquote) {
1338 				dprintf(output_fd_opt, "%c%s%c",
1339 				    quotech, focusname, quotech);
1340 			} else
1341 				dprintf(output_fd_opt, "%s", focusname);
1342 		}
1343 
1344 		if (ismenu || list_items_on_opt == false)
1345 			return;
1346 		sep = true;
1347 	}
1348 
1349 	sepfirst = false;
1350 	if ((sepstr = item_output_sep_opt) == NULL)
1351 		sepstr = item_output_sepnl_opt ? "\n" : " ";
1352 	else
1353 		sepfirst = true;
1354 
1355 	seplast = false;
1356 	if (item_output_sepnl_opt) {
1357 		sepfirst = false;
1358 		seplast = true;
1359 	}
1360 
1361 	for (i = 0; i < nitems; i++) {
1362 		if (items[i].on == false)
1363 			continue;
1364 
1365 		if (sep || sepfirst)
1366 			dprintf(output_fd_opt, "%s", sepstr);
1367 		sep = false;
1368 
1369 		toquote = false;
1370 		if (strchr(items[i].name, ' ') != NULL) {
1371 			toquote = item_always_quote_opt;
1372 			if (ismenu == false && item_output_sepnl_opt == false)
1373 				toquote = true;
1374 		}
1375 		if (toquote)
1376 			dprintf(output_fd_opt, "%c%s%c",
1377 			    quotech, items[i].name, quotech);
1378 		else
1379 			dprintf(output_fd_opt, "%s", items[i].name);
1380 
1381 		if (seplast)
1382 			dprintf(output_fd_opt, "%s", sepstr);
1383 	}
1384 }
1385 
checklist_builder(BUILDER_ARGS)1386 int checklist_builder(BUILDER_ARGS)
1387 {
1388 	int output, focusitem;
1389 	unsigned int menurows, nitems;
1390 	struct bsddialog_menuitem *items;
1391 
1392 	if (argc < 1)
1393 		exit_error("--checklist missing <menurows>", true);
1394 	menurows = (u_int)strtoul(argv[0], NULL, 10);
1395 
1396 	get_menu_items(argc-1, argv+1, item_prefix_opt, item_depth_opt, true,
1397 	    true, true, item_bottomdesc_opt, &nitems, &items, &focusitem);
1398 
1399 	output = bsddialog_checklist(conf, text, rows, cols, menurows, nitems,
1400 	    items, &focusitem);
1401 
1402 	print_menu_items(output, nitems, items, focusitem, false);
1403 
1404 	free(items);
1405 
1406 	return (output);
1407 }
1408 
menu_builder(BUILDER_ARGS)1409 int menu_builder(BUILDER_ARGS)
1410 {
1411 	int output, focusitem;
1412 	unsigned int menurows, nitems;
1413 	struct bsddialog_menuitem *items;
1414 
1415 	if (argc < 1)
1416 		exit_error("--menu missing <menurows>", true);
1417 	menurows = (u_int)strtoul(argv[0], NULL, 10);
1418 
1419 	get_menu_items(argc-1, argv+1, item_prefix_opt, item_depth_opt, true,
1420 	    true, false, item_bottomdesc_opt, &nitems, &items, &focusitem);
1421 
1422 	output = bsddialog_menu(conf, text, rows, cols, menurows, nitems,
1423 	    items, &focusitem);
1424 
1425 	print_menu_items(output, nitems, items, focusitem, true);
1426 
1427 	free(items);
1428 
1429 	return (output);
1430 }
1431 
radiolist_builder(BUILDER_ARGS)1432 int radiolist_builder(BUILDER_ARGS)
1433 {
1434 	int output, focusitem;
1435 	unsigned int menurows, nitems;
1436 	struct bsddialog_menuitem *items;
1437 
1438 	if (argc < 1)
1439 		exit_error("--radiolist missing <menurows>", true);
1440 	menurows = (u_int)strtoul(argv[0], NULL, 10);
1441 
1442 	get_menu_items(argc-1, argv+1, item_prefix_opt, item_depth_opt, true,
1443 	    true, true, item_bottomdesc_opt, &nitems, &items, &focusitem);
1444 
1445 	output = bsddialog_radiolist(conf, text, rows, cols, menurows, nitems,
1446 	    items, &focusitem);
1447 
1448 	print_menu_items(output, nitems, items, focusitem, false);
1449 
1450 	free(items);
1451 
1452 	return (output);
1453 }
1454 
treeview_builder(BUILDER_ARGS)1455 int treeview_builder(BUILDER_ARGS)
1456 {
1457 	int output, focusitem;
1458 	unsigned int menurows, nitems;
1459 	struct bsddialog_menuitem *items;
1460 
1461 	if (argc < 1)
1462 		exit_error("--treeview missing <menurows>", true);
1463 	menurows = (u_int)strtoul(argv[0], NULL, 10);
1464 
1465 	get_menu_items(argc-1, argv+1, item_prefix_opt, true, true, true, true,
1466 	    item_bottomdesc_opt, &nitems, &items, &focusitem);
1467 
1468 	conf->menu.no_name = true;
1469 	conf->menu.align_left = true;
1470 
1471 	output = bsddialog_radiolist(conf, text, rows, cols, menurows, nitems,
1472 	    items, &focusitem);
1473 
1474 	print_menu_items(output, nitems, items, focusitem, false);
1475 
1476 	free(items);
1477 
1478 	return (output);
1479 }
1480 
1481 /* FORM */
1482 static void
print_form_items(int output,int nitems,struct bsddialog_formitem * items)1483 print_form_items(int output, int nitems, struct bsddialog_formitem *items)
1484 {
1485 	int i;
1486 
1487 	if (output == BSDDIALOG_ERROR)
1488 		return;
1489 
1490 	for (i = 0; i < nitems; i++) {
1491 		dprintf(output_fd_opt, "%s\n", items[i].value);
1492 		free(items[i].value);
1493 	}
1494 }
1495 
form_builder(BUILDER_ARGS)1496 int form_builder(BUILDER_ARGS)
1497 {
1498 	int output, fieldlen, valuelen;
1499 	unsigned int i, j, flags, formheight, nitems, sizeitem;
1500 	struct bsddialog_formitem *items;
1501 
1502 	if (argc < 1)
1503 		exit_error("--form missing <formheight>", true);
1504 	formheight = (u_int)strtoul(argv[0], NULL, 10);
1505 
1506 	argc--;
1507 	argv++;
1508 	sizeitem = item_bottomdesc_opt ? 9 : 8;
1509 	if (argc % sizeitem != 0)
1510 		exit_error("--form bad number of arguments items", true);
1511 
1512 	nitems = argc / sizeitem;
1513 	if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL)
1514 		exit_error("cannot allocate memory for form items", false);
1515 	j = 0;
1516 	for (i = 0; i < nitems; i++) {
1517 		items[i].label	= argv[j++];
1518 		items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10);
1519 		items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10);
1520 		items[i].init	= argv[j++];
1521 		items[i].yfield	= (u_int)strtoul(argv[j++], NULL, 10);
1522 		items[i].xfield	= (u_int)strtoul(argv[j++], NULL, 10);
1523 
1524 		fieldlen = (int)strtol(argv[j++], NULL, 10);
1525 		items[i].fieldlen = abs(fieldlen);
1526 
1527 		valuelen = (int)strtol(argv[j++], NULL, 10);
1528 		items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen;
1529 
1530 		flags = (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0);
1531 		items[i].flags = flags;
1532 
1533 		items[i].bottomdesc = item_bottomdesc_opt ? argv[j++] : "";
1534 	}
1535 
1536 	output = bsddialog_form(conf, text, rows, cols, formheight, nitems,
1537 	    items);
1538 	print_form_items(output, nitems, items);
1539 	free(items);
1540 
1541 	return (output);
1542 }
1543 
inputbox_builder(BUILDER_ARGS)1544 int inputbox_builder(BUILDER_ARGS)
1545 {
1546 	int output;
1547 	struct bsddialog_formitem item;
1548 
1549 	if (argc > 1)
1550 		error_args("--inputbox", argc - 1, argv + 1);
1551 
1552 	item.label	 = "";
1553 	item.ylabel	 = 0;
1554 	item.xlabel	 = 0;
1555 	item.init	 = argc > 0 ? argv[0] : "";
1556 	item.yfield	 = 0;
1557 	item.xfield	 = 0;
1558 	item.fieldlen    = 1;
1559 	item.maxvaluelen = max_input_form_opt;
1560 	item.flags	 = BSDDIALOG_FIELDNOCOLOR;
1561 	item.flags      |= BSDDIALOG_FIELDCURSOREND;
1562 	item.flags      |= BSDDIALOG_FIELDEXTEND;
1563 	item.bottomdesc  = "";
1564 
1565 	output = bsddialog_form(conf, text, rows, cols, 1, 1, &item);
1566 	print_form_items(output, 1, &item);
1567 
1568 	return (output);
1569 }
1570 
mixedform_builder(BUILDER_ARGS)1571 int mixedform_builder(BUILDER_ARGS)
1572 {
1573 	int output;
1574 	unsigned int i, j, formheight, nitems, sizeitem;
1575 	struct bsddialog_formitem *items;
1576 
1577 	if (argc < 1)
1578 		exit_error("--mixedform missing <formheight>", true);
1579 	formheight = (u_int)strtoul(argv[0], NULL, 10);
1580 
1581 	argc--;
1582 	argv++;
1583 	sizeitem = item_bottomdesc_opt ? 10 : 9;
1584 	if (argc % sizeitem != 0)
1585 		exit_error("--mixedform bad number of arguments items", true);
1586 
1587 	nitems = argc / sizeitem;
1588 	if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL)
1589 		exit_error("cannot allocate memory for form items", false);
1590 	j = 0;
1591 	for (i = 0; i < nitems; i++) {
1592 		items[i].label	     = argv[j++];
1593 		items[i].ylabel      = (u_int)strtoul(argv[j++], NULL, 10);
1594 		items[i].xlabel      = (u_int)strtoul(argv[j++], NULL, 10);
1595 		items[i].init	     = argv[j++];
1596 		items[i].yfield	     = (u_int)strtoul(argv[j++], NULL, 10);
1597 		items[i].xfield	     = (u_int)strtoul(argv[j++], NULL, 10);
1598 		items[i].fieldlen    = (u_int)strtoul(argv[j++], NULL, 10);
1599 		items[i].maxvaluelen = (u_int)strtoul(argv[j++], NULL, 10);
1600 		items[i].flags       = (u_int)strtoul(argv[j++], NULL, 10);
1601 		items[i].bottomdesc  = item_bottomdesc_opt ? argv[j++] : "";
1602 	}
1603 
1604 	output = bsddialog_form(conf, text, rows, cols, formheight, nitems,
1605 	    items);
1606 	print_form_items(output, nitems, items);
1607 	free(items);
1608 
1609 	return (output);
1610 }
1611 
passwordbox_builder(BUILDER_ARGS)1612 int passwordbox_builder(BUILDER_ARGS)
1613 {
1614 	int output;
1615 	struct bsddialog_formitem item;
1616 
1617 	if (argc > 1)
1618 		error_args("--passwordbox", argc - 1, argv + 1);
1619 
1620 	item.label	 = "";
1621 	item.ylabel	 = 0;
1622 	item.xlabel	 = 0;
1623 	item.init	 = argc > 0 ? argv[0] : "";
1624 	item.yfield	 = 0;
1625 	item.xfield	 = 0;
1626 	item.fieldlen	 = 1;
1627 	item.maxvaluelen = max_input_form_opt;
1628 	item.flags       = BSDDIALOG_FIELDHIDDEN;
1629 	item.flags      |= BSDDIALOG_FIELDNOCOLOR;
1630 	item.flags      |= BSDDIALOG_FIELDCURSOREND;
1631 	item.flags      |= BSDDIALOG_FIELDEXTEND;
1632 	item.bottomdesc  = "";
1633 
1634 	output = bsddialog_form(conf, text, rows, cols, 1, 1, &item);
1635 	print_form_items(output, 1, &item);
1636 
1637 	return (output);
1638 }
1639 
passwordform_builder(BUILDER_ARGS)1640 int passwordform_builder(BUILDER_ARGS)
1641 {
1642 	int output, fieldlen, valuelen;
1643 	unsigned int i, j, flags, formheight, nitems, sizeitem;
1644 	struct bsddialog_formitem *items;
1645 
1646 	if (argc < 1)
1647 		exit_error("--passwordform missing <formheight>", true);
1648 	formheight = (u_int)strtoul(argv[0], NULL, 10);
1649 
1650 	argc--;
1651 	argv++;
1652 	sizeitem = item_bottomdesc_opt ? 9 : 8;
1653 	if (argc % sizeitem != 0)
1654 		exit_error("--passwordform bad arguments items number", true);
1655 
1656 	flags = BSDDIALOG_FIELDHIDDEN;
1657 	nitems = argc / sizeitem;
1658 	if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL)
1659 		exit_error("cannot allocate memory for form items", false);
1660 	j = 0;
1661 	for (i = 0; i < nitems; i++) {
1662 		items[i].label	= argv[j++];
1663 		items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10);
1664 		items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10);
1665 		items[i].init	= argv[j++];
1666 		items[i].yfield	= (u_int)strtoul(argv[j++], NULL, 10);
1667 		items[i].xfield	= (u_int)strtoul(argv[j++], NULL, 10);
1668 
1669 		fieldlen = (int)strtol(argv[j++], NULL, 10);
1670 		items[i].fieldlen = abs(fieldlen);
1671 
1672 		valuelen = (int)strtol(argv[j++], NULL, 10);
1673 		items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen;
1674 
1675 		flags |= (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0);
1676 		items[i].flags = flags;
1677 
1678 		items[i].bottomdesc  = item_bottomdesc_opt ? argv[j++] : "";
1679 	}
1680 
1681 	output = bsddialog_form(conf, text, rows, cols, formheight, nitems,
1682 	    items);
1683 	print_form_items(output, nitems, items);
1684 	free(items);
1685 
1686 	return (output);
1687 }
1688