diff options
Diffstat (limited to 'contrib/bsddialog')
68 files changed, 9247 insertions, 5676 deletions
diff --git a/contrib/bsddialog/.gitignore b/contrib/bsddialog/.gitignore index 62f7b594a708..c9613d477f7f 100644 --- a/contrib/bsddialog/.gitignore +++ b/contrib/bsddialog/.gitignore @@ -1,27 +1,27 @@ bsddialog +.depend* *.o -*~ +*.so* *.a -examples_library/buildlist +*.gz +*.core +*~ +BSDDIALOG.geany +BSDDIALOG.tags +examples_library/calendar examples_library/checklist examples_library/datebox examples_library/form -examples_library/formw +examples_library/gauge +examples_library/infobox examples_library/menu +examples_library/mixedgauge examples_library/mixedlist -examples_library/radiolist -examples_library/theme -examples_library/treeview -examples_library/infobox examples_library/msgbox examples_library/pause +examples_library/radiolist examples_library/rangebox -examples_library/sade +examples_library/textbox +examples_library/theme examples_library/timebox examples_library/yesno -*.gz -lib/libbsddialog.so* -BSDDIALOG.geany -BSDDIALOG.tags -*.core -freebsd-lab/ diff --git a/contrib/bsddialog/CHANGELOG b/contrib/bsddialog/CHANGELOG index 22eb3342cfe2..7800098644d7 100644 --- a/contrib/bsddialog/CHANGELOG +++ b/contrib/bsddialog/CHANGELOG @@ -1,4 +1,392 @@ -2022-03-02 version 0.2 +2025-06-22 Version 1.0.5 + + Manual: + * fix: "User-friendly documentation for alternate screen" + https://bugs.freebsd.org/285459. + Improve bsddialog.1: --alternate-screen and --normal-screen. + + NetBSD (tested on amd64) refactoring, no function changes: + * https://gitlab.com/alfix/bsddialog/-/merge_requests/4 + lib: include <stdarg.h> in lib_util.c. + * https://gitlab.com/alfix/bsddialog/-/merge_requests/5 + a call to curses' refresh() is performed, while a local + variable is also called refresh. + * Makefiles: add install and uninstall targets (both GND and BSD) + https://gitlab.com/alfix/bsddialog/-/merge_requests/3 + + MacOS (tested on amd64) refactoring, no function changes: + * https://gitlab.com/alfix/bsddialog/-/merge_requests/6 + utility: replace u_int with unsigned int. + + Library: + * fix: useless refreshes, https://gitlab.com/alfix/bsddialog/-/issues/8: + "It takes lot of time when running over a 115200 UART". + Not fixed for bsddialog_gauge() because it has to be rewritten. + * change: bsddialog_backtitle() does not update the screen so the + backtitle is not printed. To use if a dialog is built later. + Rationale: see "115200 UART" problem above. + * add: bsddialog_backtitle_rf() to print a top title refreshing the + screen like bsddialog_backtitle() was previously. + * change: forms, ENTER is also a navigation keys in forms fields. + Request: https://bugs.freebsd.org/287592 + If conf.button.always_active is true the form is closes immediatly. + + Library and implicitly utility: + * fix: textbox buttons returned values (was always OK). + Thanks to https://reviews.freebsd.org/D48668. + * change: TAB is a navigation keys in forms. Previously it directly + switched form-fields <-> buttons. + Request: https://bugs.freebsd.org/287592 + + Utility: + * change: forms, ENTER is a also navigation keys in forms fields. + Previously it directly closed the form except with --switch-buttons + Request: https://bugs.freebsd.org/287592 + + +2024-07-01 Version 1.0.4 + + Utility internal refactoring (no functional change): + * change: rename an internal constant to avoid a future conflict + because FreeBSD is changing headers files for _FORTIFY_SOURCE. + Reported and fixed by Kyle Evans. + +2024-05-27 Version 1.0.3 + + Utility: + change: --form and --mixedform do not print field value to output fd if + <fieldlen> is <= 0 (readonly). To note --mixedgauge continues + to print field value if <flag> = 2 (readonly) unless <fieldlen> + is <= 0 (as described previously). + + +2024-04-11 Version 1.0.2 + + Utility: + improvements and changes for --form and --mixedform: + * add: <maxletters> 0 sets <maxletters> like <fieldlen>. + * add: <fieldlen> 0 sets <fieldlen> like <init> width and readonly. + * change: <maxletters> 0 was an error (remains error in lib). + * change: <fieldlen> 0 was an error (remains error in lib) + (<fieldlen> 0 and <init> "" is still an error.). + + Library and implicitly utility: + * add: Ctrl-l to redraw dialog. + Request stable@freebsd.org January 2024. + * add: -, +, Ctrl-p, Ctrl-n for several dialogs. + +, - request for menus, private feature request. + Ctrl-p, Ctrl-n for menu, request hackers@freebsd.org February 2024. + * fix: escaped text ending with an escape symbol. + * change: truncate mixedgauge long (over the screen/minibars) + minilabels adding "...". As a result, avoid check-size error. + https://gitlab.com/alfix/bsddialog/-/issues/6. + * change: invert UP/DOWN keys to set a rangebox value. + + +2023-11-16 Version 1.0.1 + + Library Internal Refactoring: + * add: arrow macro handlers. + * change: Box-drawing characters, from utf8 to wide chars to avoid to + handle "env NCURSES_NO_UTF8_ACS=1". + Request https://bugs.freebsd.org/274472, + Rationale https://reviews.freebsd.org/D42380. + +2023-08-01 Version 1.0 + + Utility: + * add: comments to --save-theme output file. + * add: blink, halfbright, highlight to --save-theme and --load-theme. + * add: theme.menu.[f_]prefixcolor to --save-theme and --load-theme. + * add: --datebox-format <d/m/y|m/d/y|y/m/d> to set --datebox UI. + * add: --help-print-items (--help-status becomes alias). + * add: --text-escape (--colors becomes alias). + * add: new escapes \Zd, \Zk, \Zs, \ZD, \ZK, \ZS, for --text-escape. + * add: env NO_COLOR, to set blackwhite theme. + * add: $HOME/.bsddialog.conf startup theme file. + * add: env BSDDIALOG_THEMEFILE startup theme file. + * add: --left1-button <label>. + * add: --left2-button <label>. + * add: --left3-button <label>. + * add: --right1-button <label>. + * add: --right2-button <label>. + * add: --right3-button <label>. + * add: dynamic exit codes. + - add: --error-exit-code. + - add: --ok-exit-code. + - add: --cancel-exit-code. + - add: --help-exit-code. + - add: --extra-exit-code. + - add: --timeout-exit-code. + - add: --esc-exit-code. + - add: --left1-exit-code. + - add: --left2-exit-code. + - add: --left3-exit-code. + - add: --right1-exit-code. + - add: --right2-exit-code. + - add: --right3-exit-code. + - add: env BSDDIALOG_ERROR. + - add: env BSDDIALOG_OK. + - add: env BSDDIALOG_CANCEL. + - add: env BSDDIALOG_HELP. + - add: env BSDDIALOG_EXTRA. + - add: env BSDDIALOG_TIMEOUT. + - add: env BSDDIALOG_ESC. + - add: env BSDDIALOG_LEFT1. + - add: env BSDDIALOG_LEFT2. + - add: env BSDDIALOG_LEFT3. + - add: env BSDDIALOG_RIGHT1. + - add: env BSDDIALOG_RIGHT2. + - add: env BSDDIALOG_RIGHT3. + * add: undocumented envs for bsdconfig(8) compatibility. + - env BSDDIALOG_COMPATRC for use_shadow setting. + - env BSDDIALOG_ITEM_HELP to add/set exit code. + * change: rename themes --theme <3d|blackwhite|flat>. + * change: --no-names and --no-descriptions mutually exclusive (via lia). + * change: quote only checklist output items if necessary. Previously + also radiolist item. + * change: dialogs with user input print always values except with ERROR, + ESC, Cancel. Previously the situation was quite heterogeneous. + * improve: DIAGNOSTIC messages adding fmt string errors. + * improve: disable theme setting (opt and env) with no-color terminals. + * improve: menus on|off status (strcasecmp, diagnostic, real off check). + * improve: --bikeshed with button delimiter and --date-format. + * improve: --textbox accepts button options. + * improve: Forms with Help button. + - print "HELP" (like menus). + - accept --help-list-items. + - accept --help-print-name. + * fix: --load-theme attributes. + * fix: --clear-screen with --and-dialog. + * delete: --theme <bsddialog> (partially implemented). + * delete: --esc-return-cancel (replaced by new env and option). + * delete: --generic-button1 (replaced by --right1-button). + * delete: --generic-button2 (replaced by --right2-button). + * refactor: modularize in more files (main, cli, builders, theme). + + Library: + * add: bsddialog_inmode(). + * add: bsddialog_clear(y) for utility --clear-screen. + * add: bsddialog_refresh() for utility terminal mode options. + * add: conf.date.format="d/m/y"|"m/d/y"|"y/m/d" to customize + bsddialog_datebox() UI (boxes) with a date format. + * add: 'const char *end' to bsddialog_gauge(). + * add: draw focus on the shortcut-key-selected button at exit. + * add: escapes for conf.text.highlight \Zd, \Zk, \Zs, \ZD, \ZK, \ZS. + * add: other theme flags. + - BSDDIALOG_BLINK. + - BSDDIALOG_HALFBRIGHT. + - BSDDIALOG_HIGHLIGHT. + * add: generic buttons. + - conf.button.left1_label, BSDDIALOG_LEFT1 return value. + - conf.button.left2_label, BSDDIALOG_LEFT2 return value. + - conf.button.left3_label, BSDDIALOG_LEFT3 return value. + - conf.button.right1.label, BSDDIALOG_RIGHT1 return value. + - conf.button.right2.label, BSDDIALOG_RIGHT2 return value. + - conf.button.right3.label, BSDDIALOG_RIGHT3 return value. + * add: unused bsddialog_menugroup.min_on for future features. + * add: theme.menu.f_prefixcolor and theme.menu.prefixcolor. + * improve: check (when possible) API pointers. + * improve: circolar buttons with left and right keys for msgbox, yesno, + menus, rangebox and pause. + * improve: bsddialog_textbox() handles conf.buttons. + * improve: bsddialog_datebox() a box change affects the others as well. + * improve: bsddialog_geterror() with fmt strings. + * change: API NULL strings handled like "", except gauge *sep and *end. + * change: menus and form less restrictive with text, hide text with + little screens (same behavior as other dialogs). + * change: mixedgauge BSDDIALOG_MG_BLANK does not draw minibar but prints + minilabel. The change allows mixedgauge to add sections. To restore + the previous behavior setting minilabel to "". + * change: check/set bsddialog_gauge() perc max 100. + * change: check/set bsddialog_mixedgauge() mainperc max 100. + * change: conf.menu.no_name and conf.menu.no_desc mutually exclusive. + * change: bsddialog_pause() sec -> *sec to know remaining time at exit. + * change: add *focusitem to bsddialog_form() like menus. + * change: "pointer" values are always set except when BSDDIALOG_ERROR + occurs. Examples *yy/*mm/*ss, rangebox *value. + - delete conf.menu.on_without_ok. + - delete conf.form.value_without_ok. + * rename: conf.text.highlight -> conf.text.escape. + * rename: theme.menu.namesepcolor -> theme.menu.sepnamecolor. + * rename: theme.menu.descsepcolor -> theme.menu.sepdesccolor. + * fix: bsddialog_pause() elevation bar after resize. + * fix: bsddialog_textbox() key '0'. + * fix: timebox.c checksize (boxes width). + * fix: extend menurows after shrink and enlarge. + * fix: menu pad and form pad "re-expansion" after shrink and enlarge. + * fix: shadow top-left corner (trick wresize() before wmove()). + * fix: increment bsddialog_total_progview size for more general use. + * delete: BSDDIALOG_THEME_BSDDIALOG (partially implemented). + * delete: conf.button.generic1_label (for new conf.button.right1_label). + * delete: BSDDIALOG_GENERIC1 return value (new BSDDIALOG_RIGHT1). + * delete: conf.button.generic2_label (for new conf.button.right2_label). + * delete: BSDDIALOG_GENERIC2 return value (new BSDDIALOG_RIGHT2). + * delete: bsddialog_clearterminal(), replaced by bsddialog_clear(y). + * refactor: internal implementation. + - add: internal structures to represent components. + - merge: (when possible) dialogs autosize. + - merge: (when possible) dialogs checksize. + - merge: for each dialog "build" with "update" -> <dialog>_redraw(). + - merge: new_dialog() with update_dialog() -> draw_dialog(). + - merge: infobox.c with messagebox.c (delete infobox.c) + - merge: bsddialog_datebox() + bsddialog_calendar() -> datebox.c + - change: flat and blackwhite real themes, 3d adapted from flat. + - improve: replace wrefresh() -> wnoutrefresh()/doupdate(). + - improve: replace prefresh() -> pnoutrefresh()/doupdate(). + - improve: menu split code to build private items. + - improve: form split code to build private items. + - delete: -Wno-implicit-fallthrough. + + +2023-06-12 Version 0.4.2 + + Library: + * fix: compile error with aarch64-gcc12 for "\Z[0-7]" check; + https://gitlab.com/alfix/bsddialog/-/issues/5. + * fix: BSDDIALOG_FIELDCURSOREND with multiple items + (warning aarch64-gcc12). + + +2023-01-02 Version 0.4.1 + + Utility: + * fix: default space separator menus output, except if --separator " ". + * rename: GNUMakefile to GNUmakefile to simplify linux build. + Thanks to https://gitlab.com/alfix/bsddialog/-/merge_requests/2. + + Library: + * fix: t.dialog.linelowercolor no bold-black, some terminal draws grey. + * fix: text wrapping (actual string length) with --colors. + * rename: GNUMakefile to GNUmakefile to simplify linux build. + Thanks to https://gitlab.com/alfix/bsddialog/-/merge_requests/2. + + +2022-09-24 Version 0.4 + + Utility: + * add: --normal-screen to set normal mode. + * add: --alternate-screen to set alternate mode. + * add: --keep-tite as --alternate-screen alias. + * add: --and-dialog to build other dialogs. + * add: --and-widget as --and-dialog alias. + * add: --no-names (--no-tags becomes alias). + * add: --no-descriptions (--no-items becomes alias). + * add: --help-print-name (--help-tags becomes alias). + * add: --item-bottom-desc (--item-help becomes alias). + * add: --cr-wrap (was partially implemented) to keep '\n' with "\n". + * add: --text-unchanged to avoid default modification. + * add: --tab-escape to enable "\t" in text. + * add: --clear-screen to clear the screen. + * add: --clear-dialog to clear the dialog (was --clear). + * add: --calendar dialog to select a date. + * add: DIAGNOSTICS messages for bad arguments number. + * add: DIAGNOSTICS messages for missing and unexpected options. + * change: --clear becomes alias for --clear-screen. + * change: --print-maxsize format output. + * change: --menu, --radiolist, --checklist and --treeview output. + - no printed items with Cancel or ESC. + - --separator prints <sepstr> before each item except HELP. + - --separator and --separate-output print <sepstr> after each item. + - quoted item name/desc only when needed. + - --menu avoids to print selected item after focused HELP item. + * change: text default modification. + - without a "\n": '\t' -> space, '\n' -> '\n', trim spaces. + - with a "\n": '\t' -> space, '\n' -> space, "\n" -> '\n', no trim. + - delete '\n' after "\n" (also with --cr-wrap). + * change: --datebox input and output format yy/mm/dd -> dd/mm/yy. + * delete: --no-collapse (partially implemented). + * delete: --no-nl-expand (partially implemented). + * delete: --trim (partially implemented). + + Library: + * add: bsddialog_msgbox() HOME, END, PPAGE and NPAGE keys. + * add: bsddialog_yesno() HOME, END, PPAGE and NPAGE keys. + * add: bsddialog_menu() SPACE key (equivalent to ENTER). + * add: bsddialog_calendar() to select a date. + * change: rename enum bsddialog_grouptype -> enum bsddialog_menutype. + * change: fixed-menurows becomes at most menurows (depending on text). + * change: fixed-rows becomes at most rows, min(rows, screenH - shadow). + * change: fixed-cols becomes at most cols, min(cols, screenW - shadow). + * delete: undocumented internal bsddialog_menuitem.depth factor (was 2). + + +2022-08-29 Version 0.3 + + Utility: + * add: --textbox accepts options for the first button. + * add: --columns-per-row for text autosizing. + * add: --load-theme to read and set a custom theme at runtime. + * add: --save-theme to save current theme. + * add: --bikeshed for random settings. + * add: --switch-buttons to enable buttons/input widgets focus switching. + Available for: --form, --inputbox, --mixedform, --passwordform, + --passwordbox, --timebox and --datebox. + * change: rename --esc-cancelvalue to --esc-return-cancel. + * change: form field value is printed like multibyte charachter string, + previously widechar string. + * change: --timebox output with zero padding. + * change: --datebox output mm and dd with zero padding. + * fix: --hline with empty string. + * fix: avoid to overlay the backtitle by setting a top margin. + * fix: avoid to overlay down shadow with menus and forms bottomdesc + by setting a down margin. + * fix: --form read-only flag with multiple fields. + + Library: + * add: conf.auto_topmargin and conf.auto_downmargin. + * add: bsddialog_textbox() accepts conf.button.* for the first button. + * add: bsddialog_textbox() arrows and percentage. + * add: conf.text.cols_per_row to set a ratio for text autosizing. + * add: timebox and datebox arrows and focus background for boxes. + * add: timebox and datebox UP key to switch focus. + * add: bsddialog_init_notheme() in bsddialog.h. + * add: bsddialog_hascolors() in bsddialog_theme.h. + * add: theme.form.bottomdesccolor and theme.menu.bottomdesccolor. + * add: conf.button.always_active to disable buttons/input-boxes switch. + * add: dynamic buttons margin. + - add: theme.button.minmargin and theme.button.maxmargin. + - delete: theme.button.hmargin. + * add: Unicode. + - UI handles multicolumn charachters: backtitle, title, + text (word wrapping, autosizing), menus (shortcuts, name, desc), + forms (label, field), textbox, mixedgauge (minilabel), + buttons (label, shortcuts), bottomtitle. + - API handles char* arguments like multibyte charachter string, + depending on the current locale. + - Internally wide charachters are used to get input from keyboard + and to adapt word wrapping and dynamic text autosizing to + muticolumn charachters. + * refactoring: (rewrite) form.c. + - delete: libformw dep implementing its features from scratch. + - delete: maxvaluelen >= valuelen constraint. + - delete: conf.form.enable_wchar, get always unicode (wchar) input. + - add: KEY_HOME, KEY_END, KEY_PPAGE, KEY_NPAGE keys in field. + - add: KEY_UP can move focus from buttons to fields. + - add: KEY_DOWN can move focus from item to buttons, if nitem is 1. + - add: conf.form.securembch secure multibyte charachter. + - add: BSDDIALOG_FIELDNOCOLOR for formitem.flags. + - add: BSDDIALOG_FIELDCURSOREND for formitem.flags. + - add: BSDDIALOG_FIELDEXTEND for formitem.flags. + - add: BSDDIALOG_FIELDSINGLEBYTE for formitem.flags. + - add: resizing and refresh after KEY_RESIZE (SIGWINCH). + - add: items scrolling. + - add: conf.form.value_wchar, value is wchar_t* instead of MB-char*. + - add: formheight autosizing. + - add: dynamic item position. + * fix: bsddialog_gauge() with fd < 0. + * fix: bsddialog_gauge() refresh new text. + * fix: internal segmentation fault with disabled shadow. + * fix: center position without shadow. + * fix: bsddialog_infobox() with zero text length. + * fix: text wrapping with more than 1024 words. + * fix: rename theme.shadow.h to theme.shadow.y. + * fix: rename theme.shadow.w to theme.shadow.x. + * fix: menurows autosize with fixed rows improving text_size(). + * fix: messagebox.c scrolling and checksize without text. + + +2022-03-02 Version 0.2 Utility: * add: (this) CHANGELOG. @@ -19,7 +407,7 @@ * change: theme.button.[left|right]ch -> theme.button.[left|right]delim. * change: theme.button.space -> theme.button.hmargin. * change: theme.menu.arrowcolor -> theme.dialog.arrowcolor. - * change: default menu item depth 4 -> 2. + * change: internal bsddialog_menuitem.depth factor 4 -> 2. * fix: disable HOME, PPAGE, END and NPAGE keys in bsddialog_form(). * fix: visible cursor for timebox.c and form.c in VM VirtualBox. * fix: mixedlist, center position of separator with big pad. @@ -31,9 +419,9 @@ * improve: "menus" colors for accessibility. -2022-01-27 version 0.1 +2022-01-27 Version 0.1 - * Common-Options: --ascii-lines, --backtitle <backtitle>, --begin-x <x>, + * Options: --ascii-lines, --backtitle <backtitle>, --begin-x <x>, --begin-y <y>, --cancel-label <label>, --clear, --colors, --cr-wrap, --date-format <format>, --defaultno, --default-button <label>, --default-no, --default-item <name>, --disable-esc, @@ -58,4 +446,3 @@ --passwordbox, --passwordform, --pause, --radiolist, --rangebox, --textbox, --timebox, --treeview, --yesno. * Manuals: bsddialog.1, bsddialog.3. - diff --git a/contrib/bsddialog/GNUMakefile b/contrib/bsddialog/GNUMakefile deleted file mode 100644 index ad3d4f55f4a7..000000000000 --- a/contrib/bsddialog/GNUMakefile +++ /dev/null @@ -1,28 +0,0 @@ -# PUBLIC DOMAIN - NO WARRANTY, see: -# <http://creativecommons.org/publicdomain/zero/1.0/> -# -# Written in 2021 by Alfonso Sabato Siciliano - -OUTPUT= bsddialog -SOURCES= bsddialog.c -OBJECTS= $(SOURCES:.c=.o) -LIBPATH= ./lib -LIBBSDDIALOG= $(LIBPATH)/libbsddialog.so -CFLAGS= -Wall -Wextra -Wno-unused-parameter -I$(LIBPATH) -LDFLAGS= -Wl,-rpath=$(LIBPATH) -L$(LIBPATH) -lbsddialog -RM = rm -f - -all : $(OUTPUT) - -$(OUTPUT): $(LIBBSDDIALOG) $(OBJECTS) - $(CC) $^ -o $@ $(LDFLAGS) - -${LIBBSDDIALOG}: - make -C ${LIBPATH} -f GNUMakefile - -%.o: %.c $(LIBBSDDIALOG) - $(CC) $(CFLAGS) -c $< - -clean: - make -C ${LIBPATH} -f GNUMakefile clean - $(RM) $(OUTPUT) *.o *~ diff --git a/contrib/bsddialog/LICENSE b/contrib/bsddialog/LICENSE index f24be045375b..9ea4a4a62f4b 100644 --- a/contrib/bsddialog/LICENSE +++ b/contrib/bsddialog/LICENSE @@ -1,6 +1,6 @@ BSD 2-Clause License -Copyright (c) 2021-2022, Alfonso Sabato Siciliano +Copyright (c) 2021-2025, Alfonso Sabato Siciliano Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/contrib/bsddialog/Makefile b/contrib/bsddialog/Makefile index 5d5d0cc923a5..335b693470e6 100644 --- a/contrib/bsddialog/Makefile +++ b/contrib/bsddialog/Makefile @@ -1,50 +1,47 @@ # PUBLIC DOMAIN - NO WARRANTY, see: # <http://creativecommons.org/publicdomain/zero/1.0/> # -# Written in 2021 by Alfonso Sabato Siciliano - -OUTPUT= bsddialog -SOURCES= bsddialog.c -OBJECTS= ${SOURCES:.c=.o} -LIBPATH= ${.CURDIR}/lib -LIBBSDDIALOG= ${LIBPATH}/libbsddialog.so - -CFLAGS+= -I${LIBPATH} -std=gnu99 -Wall -Wextra -Werror -Wno-unused-parameter -# `make -DDEBUG` -.if defined(DEBUG) -CFLAGS= -g -Wall -I${LIBPATH} -LIBDEBUG= -DDEBUG -.endif -LDFLAGS+= -Wl,-rpath=${LIBPATH} -L${LIBPATH} -lbsddialog - -BINDIR= /usr/local/bin -MAN= ${OUTPUT}.1 -GZIP= gzip -cn -MANDIR= /usr/local/share/man/man1 - -INSTALL= install +# Written in 2023 by Alfonso Sabato Siciliano + +OUTPUT = bsddialog +export VERSION=1.0.5 +.CURDIR ?= ${CURDIR} +LIBPATH = ${.CURDIR}/lib +LIBBSDDIALOG = ${LIBPATH}/libbsddialog.so +UTILITYPATH = ${.CURDIR}/utility + RM= rm -f +LN = ln -s -f -all : ${OUTPUT} +### command-line options ### +# FreeBSD port Makefile: 'MAKE_ARGS = -DNORPATH' +NORPATH ?= +export DISABLERPATH=${NORPATH} +# Debug: `make -DDEBUG` or `gmake DEBUG=1` +DEBUG ?= +export ENABLEDEBUG=${DEBUG} +################### -${OUTPUT}: ${LIBBSDDIALOG} ${OBJECTS} - ${CC} ${LDFLAGS} ${OBJECTS} -o ${.PREFIX} +all: ${OUTPUT} -${LIBBSDDIALOG}: - make -C ${LIBPATH} ${LIBDEBUG} +install: all + ${MAKE} -C ${LIBPATH} install + ${MAKE} -C ${UTILITYPATH} install -.c.o: - ${CC} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} +uninstall: + ${MAKE} -C ${UTILITYPATH} uninstall + ${MAKE} -C ${LIBPATH} uninstall -install: - ${INSTALL} -s -m 555 ${OUTPUT} ${BINDIR} - ${GZIP} ${MAN} > ${MAN}.gz - ${INSTALL} -m 444 ${MAN}.gz ${MANDIR} +${OUTPUT}: ${LIBBSDDIALOG} + ${MAKE} -C ${UTILITYPATH} LIBPATH=${LIBPATH} + ${LN} ${UTILITYPATH}/${OUTPUT} ${.CURDIR}/${OUTPUT} -unistall: - ${RM} ${BINDIR}/${OUTPUT} - ${RM} ${MANDIR}/${MAN}.gz +${LIBBSDDIALOG}: + ${MAKE} -C ${LIBPATH} clean: - make -C ${LIBPATH} clean - ${RM} ${OUTPUT} *.o *~ *.core ${MAN}.gz + ${MAKE} -C ${LIBPATH} clean + ${MAKE} -C ${UTILITYPATH} clean + ${RM} ${OUTPUT} *.core + +.PHONY: all install uninstall clean diff --git a/contrib/bsddialog/README.md b/contrib/bsddialog/README.md index 3a814b38ddb4..5a25109775fe 100644 --- a/contrib/bsddialog/README.md +++ b/contrib/bsddialog/README.md @@ -1,28 +1,17 @@ -# BSDDialog 0.2 +# BSDDialog 1.0.5 +This project provides **bsddialog** and **libbsddialog**, an utility +and a library to build scripts and tools with TUI dialogs and widgets. -This project provides **bsddialog** and **libbsddialog**, an utility and a -library to build scripts and tools with TUI dialogs and widgets. +## Demo -## Intro - -Briefly: -<https://www.freebsd.org/status/report-2021-04-2021-06/#_bsddialog_tui_widgets> - -Utility: -<https://alfonsosiciliano.gitlab.io/posts/2021-12-07-bsddialog.html> - -Library: -<https://alfonsosiciliano.gitlab.io/posts/2022-01-16-libbsddialog.html> - -Screenshots: -<https://www.flickr.com/photos/alfonsosiciliano/albums/72157720215006074> +[Screenshots](https://www.flickr.com/photos/alfonsosiciliano/albums/72157720215006074). ## Getting Started -FreeBSD: +FreeBSD and Linux: ``` % git clone https://gitlab.com/alfix/bsddialog.git @@ -40,9 +29,9 @@ Output: **Dialogs:** ---checklist, --datebox, --form, --gauge, --inputbox, --menu, --mixedform, ---mixedgauge, --msgbox, --passwordbox, --passwordform, --pause, --radiolist, ---rangebox, --textbox, --timebox, --treeview, --yesno. +--calendar, --checklist, --datebox, --form, --gauge, --infobox, --inputbox, +--menu, --mixedform, --mixedgauge, --msgbox, --passwordbox, --passwordform, +--pause, --radiolist, --rangebox, --textbox, --timebox, --treeview, --yesno. **Manual** @@ -64,7 +53,9 @@ Output: and [Examples](https://gitlab.com/alfix/bsddialog/-/tree/main/examples_utility) in the _Public Domain_ to build new projects: ``` +% sh ./examples_utility/calendar.sh % sh ./examples_utility/checklist.sh +% sh ./examples_utility/datebox.sh % sh ./examples_utility/form.sh % sh ./examples_utility/gauge.sh % sh ./examples_utility/infobox.sh @@ -77,6 +68,7 @@ in the _Public Domain_ to build new projects: % sh ./examples_utility/passwordform.sh % sh ./examples_utility/pause.sh % sh ./examples_utility/radiolist.sh +% sh ./examples_utility/rangebox.sh % sh ./examples_utility/timebox.sh % sh ./examples_utility/yesno.sh ``` @@ -101,10 +93,14 @@ in the _Public Domain_ to build new projects: ``` % cd examples_library % sh compile +% ./calendar +% ./checklist % ./datebox % ./form +% ./gauge % ./infobox % ./menu +% ./mixedgauge % ./mixedlist % ./msgbox % ./pause @@ -114,4 +110,32 @@ in the _Public Domain_ to build new projects: % ./timebox % ./yesno ``` - + + +## TODO and Ideas + + - menubar feature. + - key callback. + - Right-To-Left text. + - some terminal does not hide the cursor, move it bottom-right before to getch. + - refactor backtitle: add WINDOW \*dialog.backtitle for multiline and fix expanding screen. + - refactor bottomdesc: add WINDOW \*dialog.bottomdesc to fix expandig screen. + - accessibility https://wiki.freebsd.org/Accessibility/Wishlist/Base. + - add bool conf.menu.depthlines. + - implement custom getopt\_long(). + - refactor/redesign gauge(). + - improve grey lines expanding terminal (maybe redrawwin() in hide\_dialog()). + - more restrictive strtol() and strtoul(). + - implement global buttons handler. + - doc: external tutorial, theming guide. + - implement menutype.min\_on. + - add debug API: bsddialog\_debug(y,x,refresh,"fmt",...). + - add mouse support. + - use alarm(2) for bsddialog\_pause. + - delete form fieldlen constraint, hide or truncate long field in little screens. + - improve --inputbox autosizing, consider also input length. + - fix --form "" 0 0 0 Label 1 0 Init 1 12 0 0 (with 0 editable field). + - fix --mixedform "" 0 0 0 Label 1 0 Init 1 12 0 0 2 (with 0 editable field). + - add *text* customization to --hmsg *help-message* + - check --passwordform *fieldlen* like --form and --mixedform. + - add manuals to Makefiles installe and uninstall targets. diff --git a/contrib/bsddialog/bsddialog.1 b/contrib/bsddialog/bsddialog.1 deleted file mode 100644 index c87b760d2ea3..000000000000 --- a/contrib/bsddialog/bsddialog.1 +++ /dev/null @@ -1,590 +0,0 @@ -.\" -.\" Copyright (c) 2021-2022 Alfonso Sabato Siciliano -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.Dd February 3, 2022 -.Dt BSDDIALOG 1 -.Os -.Sh NAME -.Nm bsddialog -.Nd TUI dialogs -.Sh SYNOPSIS -.Nm -.Fl Fl help -.Nm -.Fl Fl version -.Nm -.Op Fl Fl common-option -.Fl Fl dialog -.Ar text -.Ar rows -.Ar cols -.Op Ar dialog-parameter -.Sh DESCRIPTION -The -.Nm bsddialog -utility builds Text User Interface dialogs and widgets: to display messages, -to get input and to inform about a computation status. -.Pp -The options -.Fl Fl help -and -.Fl Fl version -print the list of options and the version, respectively, then exit. -.Pp -The following options are available for each dialog. -.Ar text -is a message printed inside the dialog, except for -.Fl Fl textbox -described later. -.Ar rows -and -.Ar cols -are the height and width, 0 for autosize and -1 for fullscreen. -The possible input got from the user interface is printed to standard error. -.Ss Common options -The following options can change the default behavior of the utility and are -common to some dialog. -.Bl -tag -width Ds -.It Fl Fl ascii-lines -Ascii characters to draw lines. -.It Fl Fl backtitle Ar backtitle -Title on the top side of the screen. -.It Fl Fl begin-x Ar x -Dialog horizontal position, 0 is the left screen side, -1 center. -.It Fl Fl begin-y Ar y -Dialog vertical position, 0 is the top screen side, -1 center. -.It Fl Fl cancel-label Ar label -Label for the -.Dq Cancel -button. -.It Fl Fl clear -Hide the dialog at exit. -.It Fl Fl colors -Enable highlights for text, the following sequences are considered escapes: -.Bl -column -compact -.It Dq \eZ0 -black. -.It Dq \eZ1 -red. -.It Dq \eZ2 -green. -.It Dq \eZ3 -yellow. -.It Dq \eZ4 -blue. -.It Dq \eZ5 -magenta. -.It Dq \eZ6 -cyan. -.It Dq \eZ7 -white. -.It Dq \eZr -reverse foreground and background colors. -.It Dq \eZR -disable reverse. -.It Dq \eZb -bold. -.It Dq \eZB -disable bold. -.It Dq \eZu -underline. -.It Dq \eZU -disable underline. -.It Dq \eZn -restore to normal text. -.El -.It Fl Fl cr-wrap -Replace new line with a space in -.Ar text . -.It Fl Fl date-format Ar format -String accepted by -.Xr strftime 3 -to customize the output of -.Fl Fl datebox . -.It Fl Fl default-button Ar label -Focus on the button with -.Ar label -on startup. -.It Fl Fl default-item Ar name -Focus on the item with -.Ar name , -for Checklist, Menu, Radiolist and Treeview. -.It Fl Fl default-no -Focus on -.Dq Cancel -or -.Dq \&No -button on startup. -.It Fl Fl defaultno -Equivalent to -.Fl Fl default-no . -.It Fl Fl disable-esc -Disable ESC key to quit. -.It Fl Fl esc-cancelvalue -Exits with the -.Dq Cancel -button value if the ESC key is pressed. -.It Fl Fl exit-label Ar label -Equivalent to -.Fl Fl ok-label . -.It Fl Fl extra-button -Add a button with -.Dq Extra -label. -.It Fl Fl extra-label Ar label -Set -.Ar label -for the -.Dq Extra -button. -.It Fl Fl generic-button1 Ar label -Add a button with -.Ar label . -.It Fl Fl generic-button2 Ar label -Add a button with -.Ar label . -.It Fl Fl help-button -Add a button with -.Dq Help -label. -.It Fl Fl help-label Ar label -Set -.Ar label -for -.Dq Help -button. -.It Fl Fl help-status -Print also the selected items if the -.Dq Help -button is pressed. -.It Fl Fl help-tags -Print the name of the focused item if the Help button is pressed also -with -.Fl Fl item-help . -.It Fl Fl hfile Ar filename -Open -.Ar filename -in a Textbox if F1 key is pressed, -.It Fl Fl hline Ar string -Dialog subtitle. -.It Fl Fl hmsg Ar string -Open a Msgbox with -.Ar string -if the F1 key is pressed. -.It Fl Fl ignore -Do not exit with unknown options. -.It Fl Fl insecure -Print -.Sq * -to hide passwords while typing, default space -.Sq " " . -.It Fl Fl item-depth -Specify a margin for items, available for Checklist, Menu and Radiolist. -.It Fl Fl item-help -Set a help string for each element of a Checklist, Form, Menu, Mixedform, -Passwordform, Radiolist and Treeview to display at the bottom screen side. -.It Fl Fl item-prefix -Set a string to prefix each item of a Checklist, Menu, Radiolist or Treeview. -.It Fl Fl max-input Ar size -Maximum length of the input for -.Fl Fl input-box -ans -.Fl Fl passwordbox , -default 2048. -.It Fl Fl no-cancel -Do not show -.Dq Cancel -button. -.It Fl Fl no-collapse -Do not replace a TAB character with a space in -.Ar text . -.It Fl Fl no-items -Do not display items desciption, for Checklist, Menu, Radiolist or Treeview. -.It Fl Fl no-label Ar label -Equivalent to -.Fl Fl cancel-label . -.It Fl Fl no-lines -Do not draw borders and lines. -.It Fl Fl no-nl-expand -do not consider the sequence -.Dq \en -like new line. -.It Fl Fl no-ok -Do not draw -.Dq OK -button. -.It Fl Fl no-shadow -No not draw the shadow of the dialog. -.It Fl Fl no-tags -Do not display items name, for Checklist, Menu and Radiolist. -.It Fl Fl nocancel -Equivalent to -.Fl Fl no-cancel . -.It Fl Fl nook -Equivalent to -.Fl Fl no-ok . -.It Fl Fl ok-label Ar label -Set -.Ar label -for -.Dq OK -button. -.It Fl Fl output-fd Ar fd -Print input from user interface to the specified file descriptor. -.It Fl Fl output-separator Ar sep -Set a sepator for the items in output, default whitespace. -.It Fl Fl print-maxsize -Screen size. -.It Fl Fl print-size -Print Dialog height and widget at exit. -.It Fl Fl print-version -Print version. -.It Fl Fl quoted -Quote items in output, default only when necessary. -.It Fl Fl separate-output -Separate selected items with a new line and avoid to quote. -.It Fl Fl separator Ar sep -Equivalent to -.Fl Fl output-separator . -.It Fl Fl shadow -Show a pseudo shadow for the dialog, enabled by default. -.It Fl Fl single-quoted -Use single quote for items in output. -.It Fl Fl sleep Ar secs -Wait -.Ar secs -seconds to close the dialog. -.It Fl Fl stderr -Print input from user interface to standand error, default. -.It Fl Fl stdout -Print input from user interface to standard output. -.It Fl Fl tab-len Ar spaces -Number of spaces to print a TAB in -.Ar text . -.It Fl Fl theme Ar theme -Set a graphical style: blackwhite, bsddialog, flat or dialog. -.It Fl Fl time-format Ar format -String accepted by -.Xr strftime 3 -to customize the output of -.Fl Fl timebox . -.It Fl Fl title Ar title -Dialog title. -.It Fl Fl trim -remove consecutive spaces in -.Ar text . -.It Fl Fl yes-label Ar label -Equivalent to -.Fl Fl ok-label . -.El -.Ss Dialogs -The following dialogs are available: -.Bl -tag -width Ds -.It Fl Fl checklist Ar text Ar rows Ar cols Ar menurows Oo Ar name Ar desc \ -Ar status Oc ... -Checklist to select some item from a list via the SPACE key. -An item has a -.Ar name , -.Ar desc -and a default -.Ar status -specified by -.Dq on -or -.Dq off . -The names of the selected items are printed to standard error. -.Ar menurows -is the graphical height of the list, 0 for autosize. -.It Fl Fl datebox Ar text Ar rows Ar cols Op Ar year Ar month Ar day -Dialog to select a date. -.It Fl Fl form Ar text Ar rows Ar cols Ar formrows Oo Ar label Ar ylabel \ -Ar xlabel Ar init Ar yfield Ar xfield Ar fieldlen Ar maxvaluelen Oc ... -Dialog to get a list of strings via forms. -A form has a -.Ar label -at the position -.Ar ylabel -and -.Ar xlabel , -a field to get the input at the position -.Ar yfield -and -.Ar xfield -with graphical length -.Ar fieldlen , -.Ar maxvaluelen -is the maximum input length. -The field can be customized, if -.Ar fieldlen -is 0 its length is the absolute value of -.Ar maxvaluelen , -if -.Ar maxvaluelen -is negative the field is read only, -.Ar init -is a default value. -.Ar formrows -is the graphical height of the list, has to be at least the number of forms. -.It Fl Fl gauge Ar text Ar rows Ar cols Op Ar percentage -Dialog with a bar to shows -.Ar percentage , -then it waits to read -.Dq XXX -from the standard input, then the first string replaces percentage and the -following strings replace -.Ar text -until the next -.Dq XXX , -the loop ends reading -.Dv EOF . -.It Fl Fl infobox Ar text Ar rows Ar cols -Dialog without buttons to display a message and to exit immediately. -.It Fl Fl inputbox Ar text Ar rows Ar cols Op Ar init -Dialog to get a string in input, -.Ar init -is the default value. -.It Fl Fl menu Ar text Ar rows Ar cols Ar menurows Oo Ar name desc Oc ... -Builds a menu to select an item from a list. -An item has a -.Ar name -and a -.Ar desc . -The name of the selected item is printed to standard error. -.Ar menurows -is the graphical height of the list, 0 for autosize. -.It Fl Fl mixedform Ar text Ar rows Ar cols Ar formrows Oo Ar label Ar ylabel \ -Ar xlabel Ar init Ar yfield Ar xfield Ar fieldlen Ar maxvaluelen Ar flag Oc ... -Dialog to get a list of strings via forms. -A form has a -.Ar label -at the position -.Ar ylabel -and -.Ar xlabel , -a field to get the input with graphical length -.Ar fieldlen -at the position -.Ar yfield -and -.Ar xfield , -.Ar maxvaluelen -is the maximum input length, -.Ar init -is a default value, -.Ar flag -can be 0 for normal field, 1 to hide the typed characters and 2 to set the -field read only. -.Ar formrows -is the graphical height of the list, has to be at least the number of forms. -.It Fl Fl mixedgauge Ar text Ar rows Ar cols Ar mainperc Oo Ar minilabel \ -Ar miniperc Oc ... -Dialog to show a main bar to represent -.Ar mainperc -from 0 to 100 and some mini bar with a -.Ar minilabel -string and a -.Ar miniperc -with value from 0 and 100 or negative to print a descriptive string: -1 -.Dq Succeeded , --2 -.Dq Failed , --3 -.Dq Passed , --4 -.Dq Completed , --5 -.Dq Checked , --6 -.Dq Done , --7 -.Dq Skipped , --8 -.Dq \&In Progress , --9 -a blank line, --10 -.Dq N/A , --11 -.Dq Pending . -.It Fl Fl msgbox Ar text Ar rows Ar cols -Dialog to diplay a message without the -.Dq Cancel -button. -.It Fl Fl passwordbox Ar text Ar rows Ar cols Op Ar init -Dialog to get a password, -.Ar init -is the default value. -.It Fl Fl passwordform Ar text Ar rows Ar cols Ar formrows Oo Ar label \ -Ar ylabel Ar xlabel Ar init Ar yfield Ar xfield Ar fieldlen Ar valuelen Oc ... -Dialog to get a list of passwords, equivalent to -.Fl Fl form -except typed characters are hidden. -.It Fl Fl pause Ar text Ar rows Ar cols Ar seconds -Dialog runs until the timeout in -.Ar seconds -expires or a button is pressed. -.It Fl Fl radiolist Ar text Ar rows Ar cols Ar menurows Oo Ar name Ar desc \ -Ar status Oc ... -Radiolist to select at most an item from a list via the SPACE key. -An item has a -.Ar name , -.Ar desc -and a default -.Ar status -specified by -.Dq on -or -.Dq off . -The name of the selected item is printed to standard error. -.Ar menurows -is the graphical height of the list, 0 for autosize. -.It Fl Fl rangebox Ar text Ar rows Ar cols Ar min Ar max Op Ar init -Dialog to select a value between -.Ar min -and -.Ar max , -.Ar init -is the default value, the keys UP, DOWN, HOME, END, PAGEUP and PAGEDOWN can -change it. -.It Fl Fl textbox Ar file Ar rows Ar cols -Opens and prints -.Ar file -the UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to navigate; -.Dq OK -button is renamed -.Dq EXIT . -.It Fl Fl timebox Ar text Ar rows Ar cols Op Ar hour Ar min Ar sec -Dialog to select a time. -.It Fl Fl treeview Ar text Ar rows Ar cols Ar menurows Oo Ar depth Ar name \ -Ar desc Ar status Oc ... -Equivalent to Radiolist with -.Fl Fl item-depth -and -.Fl Fl no-name . -.It Fl Fl yesno Ar text Ar rows Ar cols -.Dq Yes-No Question , -.Dq OK -and -.Dq Cancel -buttons are renamed -.Dq Yes -and -.Dq \&No . -.El -.Sh EXIT STATUS -The -.Nm -utility exits 255 on unsuccessful, otherwise depending on the button or key -pressed the following values can be returned: -.Bl -column -compact -.It 0 -.Dq OK , -.Dq Yes -or -.Dq Exit -button. -.It 1 -.Dq Cancel -or -.Dq \&No -button. -.It 2 -.Dq Help -button. -.It 3 -.Dq Extra -button. -.It 4 -Timeout. -.It 5 -ESC key. -.It 6 -Generic 1 button. -.It 7 -Generic 2 button. -.El -.Sh EXAMPLES -Backtitle, title and message: -.Dl bsddialog --backtitle MESSAGE --title Msgbox --msgbox Message 0 0 -.Pp -Yes-No Question and theme: -.Dl bsddialog --theme blackwhite --yesno Question 10 30 -.Pp -Checklist: -.Dl bsddialog --checklist Checklist 0 0 3 N1 \&D1 off N2 D2 on N3 D3 off -.Pp -Mixedgauge: -.Dl bsddialog --sleep 3 --mixedgauge Example 10 30 60 L1 "\(dq" -1" L2 30 -.Pp -Form: -.Dl bsddialog --form Form 0 0 2 L1: 1 1 X 1 5 20 25 L2: 2 1 X 2 5 20 25 -.Pp -Gauge script: -.Bd -literal -offset indent -compact -i=1 -for c in A B C D E F G H -do - sleep 1 - echo XXX - echo "$(expr $(expr $i "*" 100) "/" 8)" - echo "[$i/8] Char: $c" - echo XXX - if [ $i -eq 8 ] - then - sleep 1 - echo EOF - fi - i=`expr $i + 1` -done | bsddialog --title Gauge --gauge "Starting..." 10 70 -.Ed -.Pp -Mixedgauge script: -.Bd -literal -offset indent -compact -perc=0 -while [ $perc -le 100 ] -do - bsddialog --sleep 1 --title Mixedgauge \e - --mixedgauge "\enExample...\en" 0 0 $perc \e - "Hidden" " -9" \e - "Label 1" " -4" \e - "Label 2" " -4" \e - "Label 3" $perc - - perc=`expr $perc + 20` -done -.Ed -.Sh SEE ALSO -.Xr bsddialog 3 -.Sh HISTORY -The -.Nm bsddialog -utility first appeared in -.Fx 14.0 . -.Sh AUTHORS -.Nm bsddialog -was written by -.An Alfonso Sabato Siciliano Aq Mt alf.siciliano@gmail.com . -.Sh BUGS -Forms do not resize the dialog after a terminal change and do not provide -scrolling for items.
\ No newline at end of file diff --git a/contrib/bsddialog/bsddialog.c b/contrib/bsddialog/bsddialog.c deleted file mode 100644 index d84df4b494d9..000000000000 --- a/contrib/bsddialog/bsddialog.c +++ /dev/null @@ -1,1431 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/ioctl.h> - -#include <getopt.h> -#include <locale.h> -#include <signal.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <time.h> -#include <unistd.h> - -#include <bsddialog.h> -#include <bsddialog_theme.h> - -#define BSDDIALOG_VERSION "0.2" - -enum OPTS { - /* Common options */ - ASCII_LINES = '?' + 1, - BACKTITLE, - BEGIN_X, - BEGIN_Y, - CANCEL_LABEL, - CLEAR, - COLORS, - CR_WRAP, - DATE_FORMAT, - DEFAULT_BUTTON, - DEFAULT_ITEM, - DEFAULT_NO, - DISABLE_ESC, - ESC_CANCELVALUE, - EXIT_LABEL, - EXTRA_BUTTON, - EXTRA_LABEL, - GENERIC_BUTTON1, - GENERIC_BUTTON2, - HELP, - HELP_BUTTON, - HELP_LABEL, - HELP_STATUS, - HELP_TAGS, - HFILE, - HLINE, - HMSG, - IGNORE, - INSECURE, - ITEM_DEPTH, - ITEM_HELP, - ITEM_PREFIX, - MAX_INPUT, - NO_CANCEL, - NO_COLLAPSE, - NO_ITEMS, - NO_LINES, - NO_NL_EXPAND, - NO_OK, - NO_SHADOW, - NO_TAGS, - OK_LABEL, - OUTPUT_FD, - OUTPUT_SEPARATOR, - PRINT_MAXSIZE, - PRINT_SIZE, - PRINT_VERSION, - QUOTED, - SEPARATE_OUTPUT, - SHADOW, - SINGLE_QUOTED, - SLEEP, - STDERR, - STDOUT, - TAB_LEN, - THEME, - TIME_FORMAT, - TITLE, - TRIM, - VERSION, - /* Dialogs */ - CHECKLIST, - DATEBOX, - FORM, - GAUGE, - INFOBOX, - INPUTBOX, - MENU, - MIXEDFORM, - MIXEDGAUGE, - MSGBOX, - PASSWORDBOX, - PASSWORDFORM, - PAUSE, - RADIOLIST, - RANGEBOX, - TEXTBOX, - TIMEBOX, - TREEVIEW, - YESNO -}; - -/* Menus options */ -static bool item_prefix_opt, item_bottomdesc_opt, item_output_sepnl_opt; -static bool item_singlequote_opt, list_items_on_opt, item_tag_help_opt; -static bool item_always_quote_opt, item_depth_opt; -static char *item_output_sep_opt, *item_default_opt; -/* Date and Time options */ -static char *date_fmt_opt, *time_fmt_opt; -/* Forms options */ -static int unsigned max_input_form_opt; -/* General options */ -static int output_fd_opt; - -static void -custom_text(bool cr_wrap, bool no_collapse, bool no_nl_expand, bool trim, - char *text, char *buf); - -static void sigint_handler(int sig); - -/* Dialogs */ -#define BUILDER_ARGS struct bsddialog_conf conf, char* text, int rows, \ - int cols, int argc, char **argv, char *errbuf -static int checklist_builder(BUILDER_ARGS); -static int datebox_builder(BUILDER_ARGS); -static int form_builder(BUILDER_ARGS); -static int gauge_builder(BUILDER_ARGS); -static int infobox_builder(BUILDER_ARGS); -static int inputbox_builder(BUILDER_ARGS); -static int menu_builder(BUILDER_ARGS); -static int mixedform_builder(BUILDER_ARGS); -static int mixedgauge_builder(BUILDER_ARGS); -static int msgbox_builder(BUILDER_ARGS); -static int passwordbox_builder(BUILDER_ARGS); -static int passwordform_builder(BUILDER_ARGS); -static int pause_builder(BUILDER_ARGS); -static int radiolist_builder(BUILDER_ARGS); -static int rangebox_builder(BUILDER_ARGS); -static int textbox_builder(BUILDER_ARGS); -static int timebox_builder(BUILDER_ARGS); -static int treeview_builder(BUILDER_ARGS); -static int yesno_builder(BUILDER_ARGS); - -static void usage(void) -{ - printf("usage: bsddialog --help\n"); - printf(" bsddialog --version\n"); - printf(" bsddialog [--<common-opts>] --<dialog> <text> <rows> " - "<cols> [--<dialog-opts>]\n"); - printf("\n"); - - printf("Common Options:\n"); - printf("--ascii-lines, --backtitle <backtitle>, --begin-x <x>, " - "--begin-y <y>, --cancel-label <label>, --clear, --colors, " - "--cr-wrap, --date-format <format>, --defaultno, " - "--default-button <label>, --default-no, --default-item <name>, " - "--disable-esc, --esc-cancelvalue, --exit-label <label>, " - "--extra-button, --extra-label <label>, " - "--generic-button1 <label>, --generic-button2 <label>, --help, " - "--help-button, --help-label <label>, --help-status, --help-tags, " - "--hfile <filename>, --hline <string>, --hmsg <string>, --ignore, " - "--insecure, --item-depth, --item-help, --items-prefix, " - "--max-input <size>, --no-cancel, --nocancel, --no-collapse, " - "--no-items, --no-label <label>, --no-lines, --no-nl-expand, " - "--no-ok, --nook, --no-shadow, --no-tags, --ok-label <label>, " - "--output-fd <fd>, --output-separator <sep>, --print-maxsize, " - "--print-size, --print-version, --quoted, --separate-output, " - "--separator <sep>, --shadow, --single-quoted, --sleep <secs>, " - "--stderr, --stdout, --tab-len <spaces>, " - "--theme <blackwhite|bsddialog|flat|dialog>, " - "--time-format <format>, --title <title>, --trim, --version, " - "--yes-label <label>.\n"); - printf("\n"); - - printf("Dialogs:\n"); - printf("--checklist <text> <rows> <cols> <menurows> [<name> <desc> " - "<on|off>] ...\n"); - printf("--datebox <text> <rows> <cols> [<yy> <mm> <dd>]\n"); - printf("--form <text> <rows> <cols> <formrows> [<label> <ylabel> " - "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxvaluelen>] " - "...\n"); - printf("--gauge <text> <rows> <cols> [<perc>]\n"); - printf("--infobox <text> <rows> <cols>\n"); - printf("--inputbox <text> <rows> <cols> [init]\n"); - printf("--menu <text> <rows> <cols> <menurows> [<name> <desc>] ...\n"); - printf("--mixedform <text> <rows> <cols> <formrows> [<label> <ylabel> " - "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxvaluelen> " - "<0|1|2>] ...\n"); - printf("--mixedgauge <text> <rows> <cols> <mainperc> [<minilabel> " - "<miniperc>] ...\n"); - printf("--msgbox <text> <rows> <cols>\n"); - printf("--passwordbox <text> <rows> <cols> [init]\n"); - printf("--passwordform <text> <rows> <cols> <formrows> [<label> " - "<ylabel> <xlabel> <init> <yfield> <xfield> <fieldlen> " - "<maxvaluelen>] ...\n"); - printf("--pause <text> <rows> <cols> <secs>\n"); - printf("--radiolist <text> <rows> <cols> <menurows> [<name> <desc> " - "<on|off>] ...\n"); - printf("--rangebox <text> <rows> <cols> <min> <max> [<init>]\n"); - printf("--textbox <file> <rows> <cols>\n"); - printf("--timebox <text> <rows> <cols> [<hh> <mm> <ss>]\n"); - printf("--treeview <text> <rows> <cols> <menurows> [<depth> <name> " - "<desc> <on|off>] ...\n"); - printf("--yesno <text> <rows> <cols>\n"); - printf("\n"); - - printf("See 'man 1 bsddialog' for more information.\n"); -} - -int main(int argc, char *argv[argc]) -{ - bool cr_wrap_opt, no_collapse_opt, no_nl_expand_opt, trim_opt; - bool esc_cancelvalue_opt, ignore_opt, print_maxsize_opt;; - int input, rows, cols, output, getH, getW; - int (*dialogbuilder)(BUILDER_ARGS) = NULL; - enum bsddialog_default_theme theme_opt; - char *text, *backtitle_opt; - char errorbuilder[1024]; - struct winsize ws; - struct bsddialog_conf conf; - - setlocale(LC_ALL, ""); - - bsddialog_initconf(&conf); - conf.key.enable_esc = true; - conf.menu.on_without_ok = true; - conf.form.value_without_ok = true; - conf.form.enable_wchar = true; - - backtitle_opt = NULL; - theme_opt = BSDDIALOG_THEME_FLAT; - output_fd_opt = STDERR_FILENO; - print_maxsize_opt = false; - ignore_opt = false; - cr_wrap_opt = no_collapse_opt = no_nl_expand_opt = trim_opt = false; - esc_cancelvalue_opt = false; - errorbuilder[0] = '\0'; - - item_output_sepnl_opt = item_singlequote_opt = false; - item_prefix_opt = item_bottomdesc_opt = item_depth_opt = false; - list_items_on_opt = item_tag_help_opt = false; - item_always_quote_opt = false; - item_output_sep_opt = NULL; - item_default_opt = NULL; - - date_fmt_opt = time_fmt_opt = NULL; - - max_input_form_opt = 0; - - /* options descriptor */ - struct option longopts[] = { - /* common options */ - {"ascii-lines", no_argument, NULL, ASCII_LINES}, - {"backtitle", required_argument, NULL, BACKTITLE}, - {"begin-x", required_argument, NULL, BEGIN_X}, - {"begin-y", required_argument, NULL, BEGIN_Y}, - {"cancel-label", required_argument, NULL, CANCEL_LABEL}, - {"clear", no_argument, NULL, CLEAR}, - {"colors", no_argument, NULL, COLORS}, - {"cr-wrap", no_argument, NULL, CR_WRAP}, - {"date-format", required_argument, NULL, DATE_FORMAT}, - {"defaultno", no_argument, NULL, DEFAULT_NO}, - {"default-button", required_argument, NULL, DEFAULT_BUTTON}, - {"default-item", required_argument, NULL, DEFAULT_ITEM}, - {"default-no", no_argument, NULL, DEFAULT_NO}, - {"disable-esc", no_argument, NULL, DISABLE_ESC}, - {"esc-cancelvalue", no_argument, NULL, ESC_CANCELVALUE}, - {"exit-label", required_argument, NULL, EXIT_LABEL}, - {"extra-button", no_argument, NULL, EXTRA_BUTTON}, - {"extra-label", required_argument, NULL, EXTRA_LABEL}, - {"generic-button1", required_argument, NULL, GENERIC_BUTTON1}, - {"generic-button2", required_argument, NULL, GENERIC_BUTTON2}, - {"help", no_argument, NULL, HELP}, - {"help-button", no_argument, NULL, HELP_BUTTON}, - {"help-label", required_argument, NULL, HELP_LABEL}, - {"help-status", no_argument, NULL, HELP_STATUS}, - {"help-tags", no_argument, NULL, HELP_TAGS}, - {"hfile", required_argument, NULL, HFILE}, - {"hline", required_argument, NULL, HLINE}, - {"hmsg", required_argument, NULL, HMSG}, - {"ignore", no_argument, NULL, IGNORE}, - {"insecure", no_argument, NULL, INSECURE}, - {"item-depth", no_argument, NULL, ITEM_DEPTH}, - {"item-help", no_argument, NULL, ITEM_HELP}, - {"item-prefix", no_argument, NULL, ITEM_PREFIX}, - {"max-input", required_argument, NULL, MAX_INPUT}, - {"no-cancel", no_argument, NULL, NO_CANCEL}, - {"nocancel", no_argument, NULL, NO_CANCEL}, - {"no-collapse", no_argument, NULL, NO_COLLAPSE}, - {"no-items", no_argument, NULL, NO_ITEMS}, - {"no-label", required_argument, NULL, CANCEL_LABEL}, - {"no-lines", no_argument, NULL, NO_LINES}, - {"no-nl-expand", no_argument, NULL, NO_NL_EXPAND}, - {"no-ok", no_argument, NULL, NO_OK}, - {"nook ", no_argument, NULL, NO_OK}, - {"no-shadow", no_argument, NULL, NO_SHADOW}, - {"no-tags", no_argument, NULL, NO_TAGS}, - {"ok-label", required_argument, NULL, OK_LABEL}, - {"output-fd", required_argument, NULL, OUTPUT_FD}, - {"output-separator", required_argument, NULL, OUTPUT_SEPARATOR}, - {"print-maxsize", no_argument, NULL, PRINT_MAXSIZE}, - {"print-size", no_argument, NULL, PRINT_SIZE}, - {"print-version", no_argument, NULL, PRINT_VERSION}, - {"quoted", no_argument, NULL, QUOTED}, - {"separate-output", no_argument, NULL, SEPARATE_OUTPUT}, - {"separator", required_argument, NULL, OUTPUT_SEPARATOR}, - {"shadow", no_argument, NULL, SHADOW}, - {"single-quoted", no_argument, NULL, SINGLE_QUOTED}, - {"sleep", required_argument, NULL, SLEEP}, - {"stderr", no_argument, NULL, STDERR}, - {"stdout", no_argument, NULL, STDOUT}, - {"tab-len", required_argument, NULL, TAB_LEN}, - {"theme", required_argument, NULL, THEME}, - {"time-format", required_argument, NULL, TIME_FORMAT}, - {"title", required_argument, NULL, TITLE}, - {"trim", no_argument, NULL, TRIM}, - {"version", no_argument, NULL, VERSION}, - {"yes-label", required_argument, NULL, OK_LABEL}, - /* Dialogs */ - {"checklist", no_argument, NULL, CHECKLIST}, - {"datebox", no_argument, NULL, DATEBOX}, - {"form", no_argument, NULL, FORM}, - {"gauge", no_argument, NULL, GAUGE}, - {"infobox", no_argument, NULL, INFOBOX}, - {"inputbox", no_argument, NULL, INPUTBOX}, - {"menu", no_argument, NULL, MENU}, - {"mixedform", no_argument, NULL, MIXEDFORM}, - {"mixedgauge", no_argument, NULL, MIXEDGAUGE}, - {"msgbox", no_argument, NULL, MSGBOX}, - {"passwordbox", no_argument, NULL, PASSWORDBOX}, - {"passwordform", no_argument, NULL, PASSWORDFORM}, - {"pause", no_argument, NULL, PAUSE}, - {"radiolist", no_argument, NULL, RADIOLIST}, - {"rangebox", no_argument, NULL, RANGEBOX}, - {"textbox", no_argument, NULL, TEXTBOX}, - {"timebox", no_argument, NULL, TIMEBOX}, - {"treeview", no_argument, NULL, TREEVIEW}, - {"yesno", no_argument, NULL, YESNO}, - /* END */ - { NULL, 0, NULL, 0} - }; - - while ((input = getopt_long(argc, argv, "", longopts, NULL)) != -1) { - switch (input) { - /* Common options */ - case ASCII_LINES: - conf.ascii_lines = true; - break; - case BACKTITLE: - backtitle_opt = optarg; - break; - case BEGIN_X: - conf.x = (int)strtol(optarg, NULL, 10); - if (conf.x < BSDDIALOG_CENTER) { - printf("Error: --begin-x %d < %d", - conf.x, BSDDIALOG_CENTER); - return (255); - } - break; - case BEGIN_Y: - conf.y = (int)strtol(optarg, NULL, 10); - if (conf.y < BSDDIALOG_CENTER) { - printf("Error: --begin-y %d < %d", - conf.y, BSDDIALOG_CENTER); - return (255); - } - break; - case CANCEL_LABEL: - conf.button.cancel_label = optarg; - break; - case CLEAR: - conf.clear = true; - break; - case COLORS: - conf.text.highlight = true; - break; - case CR_WRAP: - cr_wrap_opt = true; - break; - case DATE_FORMAT: - date_fmt_opt = optarg; - break; - case DEFAULT_BUTTON: - conf.button.default_label = optarg; - break; - case DEFAULT_ITEM: - item_default_opt = optarg; - break; - case DEFAULT_NO: - conf.button.default_cancel = true; - break; - case DISABLE_ESC: - conf.key.enable_esc = false; - break; - case ESC_CANCELVALUE: - esc_cancelvalue_opt = true; - break; - case EXIT_LABEL: - conf.button.ok_label = optarg; - break; - case EXTRA_BUTTON: - conf.button.with_extra = true; - break; - case EXTRA_LABEL: - conf.button.extra_label = optarg; - break; - case GENERIC_BUTTON1: - conf.button.generic1_label = optarg; - break; - case GENERIC_BUTTON2: - conf.button.generic2_label = optarg; - break; - case HELP: - usage(); - return (BSDDIALOG_OK); - case HELP_BUTTON: - conf.button.with_help = true; - break; - case HELP_LABEL: - conf.button.help_label = optarg; - break; - case HELP_STATUS: - list_items_on_opt = true; - break; - case HELP_TAGS: - item_tag_help_opt = true; - break; - case HFILE: - conf.key.f1_file = optarg; - break; - case HLINE: - if (strlen(optarg) > 0) - conf.bottomtitle = optarg; - break; - case HMSG: - conf.key.f1_message = optarg; - break; - case IGNORE: - ignore_opt = true; - break; - case INSECURE: - conf.form.securech = '*'; - break; - case ITEM_DEPTH: - item_depth_opt = true; - break; - case ITEM_HELP: - item_bottomdesc_opt = true; - break; - case ITEM_PREFIX: - item_prefix_opt = true; - break; - case MAX_INPUT: - max_input_form_opt = (u_int)strtoul(optarg, NULL, 10); - break; - case NO_ITEMS: - conf.menu.no_desc = true; - break; - case NO_CANCEL: - conf.button.without_cancel = true; - break; - case NO_COLLAPSE: - no_collapse_opt = true; - break; - case NO_LINES: - conf.no_lines = true; - break; - case NO_NL_EXPAND: - no_nl_expand_opt = true; - break; - case NO_OK: - conf.button.without_ok = true; - break; - case NO_TAGS: - conf.menu.no_name = true; - break; - case NO_SHADOW: - conf.shadow = false; - break; - case OK_LABEL: - conf.button.ok_label = optarg; - break; - case OUTPUT_FD: - output_fd_opt = (int)strtol(optarg, NULL, 10); - break; - case OUTPUT_SEPARATOR: - item_output_sep_opt = optarg; - break; - case QUOTED: - item_always_quote_opt = true; - break; - case PRINT_MAXSIZE: - print_maxsize_opt = true; - break; - case PRINT_SIZE: - conf.get_height = &getH; - conf.get_width = &getW; - break; - case PRINT_VERSION: - printf("bsddialog version %s\n", BSDDIALOG_VERSION); - break; - case SEPARATE_OUTPUT: - item_output_sepnl_opt = true; - break; - case SHADOW: - conf.shadow = true; - break; - case SINGLE_QUOTED: - item_singlequote_opt = true; - break; - case SLEEP: - conf.sleep = (u_int)strtoul(optarg, NULL, 10); - break; - case STDERR: - output_fd_opt = STDERR_FILENO; - break; - case STDOUT: - output_fd_opt = STDOUT_FILENO; - break; - case TAB_LEN: - conf.text.tablen = (u_int)strtoul(optarg, NULL, 10); - break; - case THEME: - if (strcasecmp(optarg, "bsddialog") == 0) - theme_opt = BSDDIALOG_THEME_BSDDIALOG; - else if (strcasecmp(optarg, "blackwhite") == 0) - theme_opt = BSDDIALOG_THEME_BLACKWHITE; - else if (strcasecmp(optarg, "flat") == 0) - theme_opt = BSDDIALOG_THEME_FLAT; - else if (strcasecmp(optarg, "dialog") == 0) - theme_opt = BSDDIALOG_THEME_DIALOG; - else { - printf("Error: unknown theme\n"); - return (255); - } - break; - case TIME_FORMAT: - time_fmt_opt = optarg; - break; - case TITLE: - conf.title = optarg; - break; - case TRIM: - trim_opt = true; - break; - case VERSION: - printf("bsddialog %s (libbsddialog %s)\n", - BSDDIALOG_VERSION, LIBBSDDIALOG_VERSION); - return (BSDDIALOG_OK); - /* Dialogs */ - case CHECKLIST: - dialogbuilder = checklist_builder; - break; - case DATEBOX: - dialogbuilder = datebox_builder; - break; - case FORM: - dialogbuilder = form_builder; - break; - case GAUGE: - dialogbuilder = gauge_builder; - break; - case INFOBOX: - dialogbuilder = infobox_builder; - break; - case INPUTBOX: - dialogbuilder = inputbox_builder; - break; - case MENU: - dialogbuilder = menu_builder; - break; - case MIXEDFORM: - dialogbuilder = mixedform_builder; - break; - case MIXEDGAUGE: - dialogbuilder = mixedgauge_builder; - break; - case MSGBOX: - dialogbuilder = msgbox_builder; - break; - case PAUSE: - dialogbuilder = pause_builder; - break; - case PASSWORDBOX: - dialogbuilder = passwordbox_builder; - break; - case PASSWORDFORM: - dialogbuilder = passwordform_builder; - break; - case RADIOLIST: - dialogbuilder = radiolist_builder; - break; - case RANGEBOX: - dialogbuilder = rangebox_builder; - break; - case TEXTBOX: - dialogbuilder = textbox_builder; - break; - case TIMEBOX: - dialogbuilder = timebox_builder; - break; - case TREEVIEW: - dialogbuilder = treeview_builder; - break; - case YESNO: - dialogbuilder = yesno_builder; - break; - /* Error */ - default: - if (ignore_opt == true) - break; - usage(); - return (255); - } - } - argc -= optind; - argv += optind; - - if (print_maxsize_opt) { - ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); - dprintf(output_fd_opt, "Screen size: (%d - %d)\n", - ws.ws_row, ws.ws_col); - if (argc == 0) - return (BSDDIALOG_OK); - } - - if (argc < 3) { - usage(); - return (255); - } - if (dialogbuilder == textbox_builder) - text = argv[0]; - else { - if ((text = malloc(strlen(argv[0]) + 1)) == NULL) { - printf("Error: cannot allocate memory for text\n"); - return (255); - } - custom_text(cr_wrap_opt, no_collapse_opt, no_nl_expand_opt, - trim_opt, argv[0], text); - } - rows = (int)strtol(argv[1], NULL, 10); - cols = (int)strtol(argv[2], NULL, 10); - argc -= 3; - argv += 3; - - /* bsddialog terminal mode */ - if (bsddialog_init() != 0) { - printf("Error: %s\n", bsddialog_geterror()); - return (BSDDIALOG_ERROR); - } - - signal(SIGINT, sigint_handler); - - if (theme_opt != BSDDIALOG_THEME_FLAT) - bsddialog_set_default_theme(theme_opt); - - if (backtitle_opt != NULL) - bsddialog_backtitle(&conf, backtitle_opt); - - errorbuilder[0] = '\0'; - output = BSDDIALOG_OK; - if (dialogbuilder != NULL) - output = dialogbuilder(conf, text, rows, cols, argc, argv, - errorbuilder); - - if (dialogbuilder != textbox_builder) - free(text); - - bsddialog_end(); - /* end bsddialog terminal mode */ - - if (output == BSDDIALOG_ERROR) { - if (errorbuilder[0] != '\0') - printf("Error: %s\n", errorbuilder); - else - printf("Error: %s\n", bsddialog_geterror()); - return (255); - } - - if (conf.get_height != NULL && conf.get_width != NULL) - dprintf(output_fd_opt, "Dialog size: (%d - %d)\n", - *conf.get_height, *conf.get_width); - - if (output == BSDDIALOG_ESC && esc_cancelvalue_opt) - output = BSDDIALOG_CANCEL; - - return (output); -} - -void sigint_handler(int sig) -{ - bsddialog_end(); - - exit(255); -} - -void -custom_text(bool cr_wrap, bool no_collapse, bool no_nl_expand, bool trim, - char *text, char *buf) -{ - int i, j; - - i = j = 0; - while (text[i] != '\0') { - switch (text[i]) { - case '\\': - buf[j] = '\\'; - switch (text[i+1]) { - case '\\': - i++; - break; - case 'n': - if (no_nl_expand) { - j++; - buf[j] = 'n'; - } else - buf[j] = '\n'; - i++; - break; - case 't': - if (no_collapse) { - j++; - buf[j] = 't'; - } else - buf[j] = '\t'; - i++; - break; - } - break; - case '\n': - buf[j] = cr_wrap ? ' ' : '\n'; - break; - case '\t': - buf[j] = no_collapse ? '\t' : ' '; - break; - default: - buf[j] = text[i]; - } - i++; - j += (buf[j] == ' ' && trim && j > 0 && buf[j-1] == ' ') ? - 0 : 1; - } - buf[j] = '\0'; -} - -/* Dialogs */ -int gauge_builder(BUILDER_ARGS) -{ - int output; - unsigned int perc; - - if (argc > 0) { - perc = argc > 0 ? (u_int)strtoul(argv[0], NULL, 10) : 0; - perc = perc > 100 ? 100 : perc; - } - else - perc = 0; - - output = bsddialog_gauge(&conf, text, rows, cols, perc, STDIN_FILENO, - "XXX"); - - return (output); -} - -int infobox_builder(BUILDER_ARGS) -{ - int output; - - output = bsddialog_infobox(&conf, text, rows, cols); - - return (output); -} - -int mixedgauge_builder(BUILDER_ARGS) -{ - int output, *minipercs; - unsigned int i, mainperc, nminibars; - const char **minilabels; - - if (argc < 1 || (((argc-1) % 2) != 0) ) { - strcpy(errbuf, "bad --mixedgauge arguments\n"); - return (BSDDIALOG_ERROR); - } - - mainperc = (u_int)strtoul(argv[0], NULL, 10); - mainperc = mainperc > 100 ? 100 : mainperc; - argc--; - argv++; - - nminibars = argc / 2; - if ((minilabels = calloc(nminibars, sizeof(char*))) == NULL) { - strcpy(errbuf, "Cannot allocate memory for minilabels\n"); - return BSDDIALOG_ERROR; - } - if ((minipercs = calloc(nminibars, sizeof(int))) == NULL) { - strcpy(errbuf, "Cannot allocate memory for minipercs\n"); - return BSDDIALOG_ERROR; - } - - for (i = 0; i < nminibars; i++) { - minilabels[i] = argv[i * 2]; - minipercs[i] = (int)strtol(argv[i * 2 + 1], NULL, 10); - } - - output = bsddialog_mixedgauge(&conf, text, rows, cols, mainperc, - nminibars, minilabels, minipercs); - - return (output); -} - -int msgbox_builder(BUILDER_ARGS) -{ - int output; - - output = bsddialog_msgbox(&conf, text, rows, cols); - - return (output); -} - -int pause_builder(BUILDER_ARGS) -{ - int output; - unsigned int secs; - - if (argc < 1) { - strcpy(errbuf, "missing <seconds> for --pause\n"); - return (BSDDIALOG_ERROR); - } - - secs = (u_int)strtoul(argv[0], NULL, 10); - output = bsddialog_pause(&conf, text, rows, cols, secs); - - return (output); -} - -int rangebox_builder(BUILDER_ARGS) -{ - int output, min, max, value; - - if (argc < 2) { - strcpy(errbuf, "usage --rangebox <text> <rows> <cols> " - "<min> <max> [<init>]\n"); - return (BSDDIALOG_ERROR); - } - - min = (int)strtol(argv[0], NULL, 10); - max = (int)strtol(argv[1], NULL, 10); - - if (argc > 2) { - value = (int)strtol(argv[2], NULL, 10); - value = value < min ? min : value; - value = value > max ? max : value; - } - else - value = min; - - output = bsddialog_rangebox(&conf, text, rows, cols, min, max, &value); - - dprintf(output_fd_opt, "%d", value); - - return (output); -} - -int textbox_builder(BUILDER_ARGS) -{ - int output; - - output = bsddialog_textbox(&conf, text, rows, cols); - - return (output); -} - -int yesno_builder(BUILDER_ARGS) -{ - int output; - - output = bsddialog_yesno(&conf, text, rows, cols); - - return (output); -} - -/* DATE and TIME */ -int datebox_builder(BUILDER_ARGS) -{ - int output; - unsigned int yy, mm, dd; - time_t cal; - struct tm *localtm; - char stringdate[1024]; - - time(&cal); - localtm = localtime(&cal); - yy = localtm->tm_year + 1900; - mm = localtm->tm_mon + 1; - dd = localtm->tm_mday; - - if (argc == 3) { - yy = (u_int)strtoul(argv[0], NULL, 10); - mm = (u_int)strtoul(argv[1], NULL, 10); - dd = (u_int)strtoul(argv[2], NULL, 10); - } - - output = bsddialog_datebox(&conf, text, rows, cols, &yy, &mm, &dd); - if (output != BSDDIALOG_OK) - return (output); - - if (date_fmt_opt == NULL) { - dprintf(output_fd_opt, "%u/%u/%u", yy, mm, dd); - } else { - time(&cal); - localtm = localtime(&cal); - localtm->tm_year = yy - 1900; - localtm->tm_mon = mm - 1; - localtm->tm_mday = dd; - strftime(stringdate, 1024, date_fmt_opt, localtm); - dprintf(output_fd_opt, "%s", stringdate); - } - - return (output); -} - -int timebox_builder(BUILDER_ARGS) -{ - int output; - unsigned int hh, mm, ss; - time_t clock; - struct tm *localtm; - char stringtime[1024]; - - time(&clock); - localtm = localtime(&clock); - hh = localtm->tm_hour; - mm = localtm->tm_min; - ss = localtm->tm_sec; - - if (argc == 3) { - hh = (u_int)strtoul(argv[0], NULL, 10); - mm = (u_int)strtoul(argv[1], NULL, 10); - ss = (u_int)strtoul(argv[2], NULL, 10); - } - - output = bsddialog_timebox(&conf, text, rows, cols, &hh, &mm, &ss); - if (output != BSDDIALOG_OK) - return (output); - - if (time_fmt_opt == NULL) { - dprintf(output_fd_opt, "%u:%u:%u", hh, mm, ss); - } else { - time(&clock); - localtm = localtime(&clock); - localtm->tm_hour = hh; - localtm->tm_min = mm; - localtm->tm_sec = ss; - strftime(stringtime, 1024, time_fmt_opt, localtm); - dprintf(output_fd_opt, "%s", stringtime); - } - - return (output); -} - -/* MENU */ -static int -get_menu_items(char *errbuf, 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) -{ - unsigned int i, j, sizeitem; - - *focusitem = -1; - - sizeitem = 0; - sizeitem += setprefix ? 1 : 0; - sizeitem += setdepth ? 1 : 0; - sizeitem += setname ? 1 : 0; - sizeitem += setdesc ? 1 : 0; - sizeitem += setstatus ? 1 : 0; - sizeitem += sethelp ? 1 : 0; - if ((argc % sizeitem) != 0) { - strcpy(errbuf, "bad number of arguments for this menu\n"); - return (BSDDIALOG_ERROR); - } - *nitems = argc / sizeitem; - - *items = calloc(*nitems, sizeof(struct bsddialog_menuitem)); - if (items == NULL) { - strcpy(errbuf, "cannot allocate memory menu items\n"); - return (BSDDIALOG_ERROR); - } - - j = 0; - for (i = 0; i < *nitems; i++) { - (*items)[i].prefix = setprefix ? argv[j++] : ""; - (*items)[i].depth = setdepth ? - (u_int)strtoul(argv[j++], NULL, 0) : 0; - (*items)[i].name = setname ? argv[j++] : ""; - (*items)[i].desc = setdesc ? argv[j++] : ""; - if (setstatus) - (*items)[i].on = strcmp(argv[j++], "on") == 0 ? - true : false; - else - (*items)[i].on = false; - (*items)[i].bottomdesc = sethelp ? argv[j++] : ""; - - if (item_default_opt != NULL && *focusitem == -1) - if (strcmp((*items)[i].name, item_default_opt) == 0) - *focusitem = i; - } - - return (BSDDIALOG_OK); -} - -static void -print_menu_items(int output, int nitems, struct bsddialog_menuitem *items, - int focusitem) -{ - bool sep, toquote; - int i; - char *sepstr, quotech; - const char *focusname; - - sep = false; - quotech = item_singlequote_opt ? '\'' : '"'; - sepstr = item_output_sep_opt != NULL ? item_output_sep_opt : " "; - - if (output != BSDDIALOG_OK && output != BSDDIALOG_ERROR && - focusitem >= 0) { - focusname = items[focusitem].name; - - if (output == BSDDIALOG_HELP) { - dprintf(output_fd_opt, "HELP "); - - if (item_bottomdesc_opt && item_tag_help_opt == false) - focusname = items[focusitem].bottomdesc; - } - - toquote = item_always_quote_opt || - (item_output_sepnl_opt == false && - strchr(focusname, ' ') != NULL); - - if (toquote) - dprintf(output_fd_opt, "%c", quotech); - dprintf(output_fd_opt, "%s", focusname); - if (toquote) - dprintf(output_fd_opt, "%c", quotech); - - sep = true; - } - - if (output != BSDDIALOG_OK && - !(output == BSDDIALOG_HELP && list_items_on_opt)) - return; - - for (i = 0; i < nitems; i++) { - if (items[i].on == false) - continue; - - if (sep == true) { - dprintf(output_fd_opt, "%s", sepstr); - if (item_output_sepnl_opt) - dprintf(output_fd_opt, "\n"); - } - sep = true; - - toquote = item_always_quote_opt || - (item_output_sepnl_opt == false && - strchr(items[i].name, ' ') != NULL); - - if (toquote) - dprintf(output_fd_opt, "%c", quotech); - dprintf(output_fd_opt, "%s", items[i].name); - if (toquote) - dprintf(output_fd_opt, "%c", quotech); - } -} - -int checklist_builder(BUILDER_ARGS) -{ - int output, focusitem; - unsigned int menurows, nitems; - struct bsddialog_menuitem *items; - - if (argc < 1) { - strcpy(errbuf, "<menurows> not provided"); - return (BSDDIALOG_ERROR); - } - - menurows = (u_int)strtoul(argv[0], NULL, 10); - - output = get_menu_items(errbuf, argc-1, argv+1, item_prefix_opt, - item_depth_opt, true, true, true, item_bottomdesc_opt, &nitems, - &items, &focusitem); - if (output != 0) - return (output); - - output = bsddialog_checklist(&conf, text, rows, cols, menurows, nitems, - items, &focusitem); - - print_menu_items(output, nitems, items, focusitem); - - free(items); - - return (output); -} - -int menu_builder(BUILDER_ARGS) -{ - int output, focusitem; - unsigned int menurows, nitems; - struct bsddialog_menuitem *items; - - if (argc < 1) { - strcpy(errbuf, "<menurows> not provided"); - return (BSDDIALOG_ERROR); - } - - menurows = (u_int)strtoul(argv[0], NULL, 10); - - output = get_menu_items(errbuf, argc-1, argv+1, item_prefix_opt, - item_depth_opt, true, true, false, item_bottomdesc_opt, &nitems, - &items, &focusitem); - if (output != 0) - return (output); - - output = bsddialog_menu(&conf, text, rows, cols, menurows, nitems, - items, &focusitem); - - print_menu_items(output, nitems, items, focusitem); - - free(items); - - return (output); -} - -int radiolist_builder(BUILDER_ARGS) -{ - int output, focusitem; - unsigned int menurows, nitems; - struct bsddialog_menuitem *items; - - if (argc < 1) { - strcpy(errbuf, "<menurows> not provided"); - return (BSDDIALOG_ERROR); - } - - menurows = (u_int)strtoul(argv[0], NULL, 10); - - output = get_menu_items(errbuf, argc-1, argv+1, item_prefix_opt, - item_depth_opt, true, true, true, item_bottomdesc_opt, &nitems, - &items, &focusitem); - if (output != 0) - return (output); - - output = bsddialog_radiolist(&conf, text, rows, cols, menurows, nitems, - items, &focusitem); - - print_menu_items(output, nitems, items, focusitem); - - free(items); - - return (output); -} - -int treeview_builder(BUILDER_ARGS) -{ - int output, focusitem; - unsigned int menurows, nitems; - struct bsddialog_menuitem *items; - - if (argc < 1) { - strcpy(errbuf, "<menurows> not provided"); - return (BSDDIALOG_ERROR); - } - - menurows = (u_int)strtoul(argv[0], NULL, 10); - - output = get_menu_items(errbuf, argc-1, argv+1, item_prefix_opt, - true, true, true, true, item_bottomdesc_opt, &nitems, &items, - &focusitem); - if (output != 0) - return (output); - - conf.menu.no_name = true; - conf.menu.align_left = true; - - output = bsddialog_radiolist(&conf, text, rows, cols, menurows, nitems, - items, &focusitem); - - print_menu_items(output, nitems, items, focusitem); - - free(items); - - return (output); -} - -/* FORM */ -static int -alloc_formitems(int nitems, struct bsddialog_formitem **items, char *errbuf) -{ - *items = calloc(nitems, sizeof(struct bsddialog_formitem)); - if (items == NULL) { - strcpy(errbuf, "cannot allocate memory for form items\n"); - return (BSDDIALOG_ERROR); - } - - return (BSDDIALOG_OK); -} - -static void -print_form_items(int output, int nitems, struct bsddialog_formitem *items) -{ - int i; - - if (output == BSDDIALOG_ERROR) - return; - - for (i = 0; i < nitems; i++) { - dprintf(output_fd_opt, "%ls\n", (wchar_t*)items[i].value); - free(items[i].value); - } -} - -int form_builder(BUILDER_ARGS) -{ - int output, fieldlen, valuelen; - unsigned int i, j, flags, formheight, nitems, sizeitem; - struct bsddialog_formitem *items; - - sizeitem = item_bottomdesc_opt ? 9 : 8; - if (argc < 1 || (argc - 1) % sizeitem != 0) { - strcpy(errbuf, "bad number of arguments for this form\n"); - return (BSDDIALOG_ERROR); - } - - formheight = (u_int)strtoul(argv[0], NULL, 10); - flags = 0; - - argc--; - argv++; - - nitems = argc / sizeitem; - if (alloc_formitems(nitems, &items, errbuf) != BSDDIALOG_OK) - return (BSDDIALOG_ERROR); - j = 0; - for (i = 0; i < nitems; i++) { - items[i].label = argv[j++]; - items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10); - items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10); - items[i].init = argv[j++]; - items[i].yfield = (u_int)strtoul(argv[j++], NULL, 10); - items[i].xfield = (u_int)strtoul(argv[j++], NULL, 10); - - fieldlen = (int)strtol(argv[j++], NULL, 10); - items[i].fieldlen = abs(fieldlen); - - valuelen = (int)strtol(argv[j++], NULL, 10); - items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen; - - flags |= (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0); - items[i].flags = flags; - - items[i].bottomdesc = item_bottomdesc_opt ? argv[j++] : ""; - } - - output = bsddialog_form(&conf, text, rows, cols, formheight, nitems, - items); - print_form_items(output, nitems, items); - free(items); - - return (output); -} - -int inputbox_builder(BUILDER_ARGS) -{ - int output; - struct bsddialog_formitem item; - - item.label = ""; - item.ylabel = 0; - item.xlabel = 0; - item.init = argc > 0 ? argv[0] : ""; - item.yfield = 1; - item.xfield = 1; - item.fieldlen = cols > 4 ? cols-4 : 25; - item.maxvaluelen = max_input_form_opt > 0 ? max_input_form_opt : 2048; - item.flags = 0; - item.bottomdesc = ""; - - output = bsddialog_form(&conf, text, rows, cols, 1, 1, &item); - print_form_items(output, 1, &item); - - return (output); -} - -int mixedform_builder(BUILDER_ARGS) -{ - int output; - unsigned int i, j, formheight, nitems, sizeitem; - struct bsddialog_formitem *items; - - sizeitem = item_bottomdesc_opt ? 10 : 9; - if (argc < 1 || (argc-1) % sizeitem != 0) { - strcpy(errbuf, "bad number of arguments for this form\n"); - return (BSDDIALOG_ERROR); - } - - formheight = (u_int)strtoul(argv[0], NULL, 10); - - argc--; - argv++; - - nitems = argc / sizeitem; - if (alloc_formitems(nitems, &items, errbuf) != BSDDIALOG_OK) - return (BSDDIALOG_ERROR); - j = 0; - for (i = 0; i < nitems; i++) { - items[i].label = argv[j++]; - items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10); - items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10); - items[i].init = argv[j++]; - items[i].yfield = (u_int)strtoul(argv[j++], NULL, 10); - items[i].xfield = (u_int)strtoul(argv[j++], NULL, 10); - items[i].fieldlen = (u_int)strtoul(argv[j++], NULL, 10); - items[i].maxvaluelen = (u_int)strtoul(argv[j++], NULL, 10); - items[i].flags = (u_int)strtoul(argv[j++], NULL, 10); - items[i].bottomdesc = item_bottomdesc_opt ? argv[j++] : ""; - } - - output = bsddialog_form(&conf, text, rows, cols, formheight, nitems, - items); - print_form_items(output, nitems, items); - free(items); - - return (output); -} - -int passwordbox_builder(BUILDER_ARGS) -{ - int output; - struct bsddialog_formitem item; - - item.label = ""; - item.ylabel = 0; - item.xlabel = 0; - item.init = argc > 0 ? argv[0] : ""; - item.yfield = 1; - item.xfield = 1; - item.fieldlen = cols > 4 ? cols-4 : 25; - item.maxvaluelen = max_input_form_opt > 0 ? max_input_form_opt : 2048; - item.flags = BSDDIALOG_FIELDHIDDEN; - item.bottomdesc = ""; - - output = bsddialog_form(&conf, text, rows, cols, 1, 1, &item); - print_form_items(output, 1, &item); - - return (output); -} - -int passwordform_builder(BUILDER_ARGS) -{ - int output, fieldlen, valuelen; - unsigned int i, j, flags, formheight, nitems, sizeitem; - struct bsddialog_formitem *items; - - sizeitem = item_bottomdesc_opt ? 9 : 8; - if (argc < 1 || (argc - 1) % sizeitem != 0) { - strcpy(errbuf, "bad number of arguments for this form\n"); - return (BSDDIALOG_ERROR); - } - - formheight = (u_int)strtoul(argv[0], NULL, 10); - flags = BSDDIALOG_FIELDHIDDEN; - - argc--; - argv++; - - nitems = argc / sizeitem; - if (alloc_formitems(nitems, &items, errbuf) != BSDDIALOG_OK) - return (BSDDIALOG_ERROR); - j = 0; - for (i = 0; i < nitems; i++) { - items[i].label = argv[j++]; - items[i].ylabel = (u_int)strtoul(argv[j++], NULL, 10); - items[i].xlabel = (u_int)strtoul(argv[j++], NULL, 10); - items[i].init = argv[j++]; - items[i].yfield = (u_int)strtoul(argv[j++], NULL, 10); - items[i].xfield = (u_int)strtoul(argv[j++], NULL, 10); - - fieldlen = (int)strtol(argv[j++], NULL, 10); - items[i].fieldlen = abs(fieldlen); - - valuelen = (int)strtol(argv[j++], NULL, 10); - items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen; - - flags |= (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0); - items[i].flags = flags; - - items[i].bottomdesc = item_bottomdesc_opt ? argv[j++] : ""; - } - - output = bsddialog_form(&conf, text, rows, cols, formheight, nitems, - items); - print_form_items(output, nitems, items); - free(items); - - return (output); -} diff --git a/contrib/bsddialog/examples_library/calendar.c b/contrib/bsddialog/examples_library/calendar.c new file mode 100644 index 000000000000..5899fc4986fd --- /dev/null +++ b/contrib/bsddialog/examples_library/calendar.c @@ -0,0 +1,45 @@ +/*- + * SPDX-License-Identifier: CC0-1.0 + * + * Written in 2022 by Alfonso Sabato Siciliano. + * To the extent possible under law, the author has dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty, see: + * <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +#include <bsddialog.h> +#include <stdio.h> +#include <time.h> + +int main() +{ + int output; + unsigned int yy, mm, dd; + struct bsddialog_conf conf; + time_t cal; + struct tm *localtm; + + time(&cal); + localtm = localtime(&cal); + yy = localtm->tm_year + 1900; + mm = localtm->tm_mon + 1; + dd = localtm->tm_mday; + + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } + bsddialog_initconf(&conf); + conf.title = "calendar"; + output = bsddialog_calendar(&conf, "Example", 18, 40, &yy, &mm, &dd); + bsddialog_end(); + if (output == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } + + printf("Date: %u/%u/%u\n", yy, mm, dd); + + return (0); +}
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/checklist.c b/contrib/bsddialog/examples_library/checklist.c index 6d860b91a076..a2b178d14270 100644 --- a/contrib/bsddialog/examples_library/checklist.c +++ b/contrib/bsddialog/examples_library/checklist.c @@ -10,7 +10,6 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> int main() { @@ -28,27 +27,19 @@ int main() printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "checklist"; output = bsddialog_checklist(&conf, "Example", 15, 30, 5, 5, items, NULL); - bsddialog_end(); - if (output == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); return (1); } - if (output == BSDDIALOG_CANCEL) { - printf("Cancel\n"); - return (0); - } - printf("Checklist:\n"); for (i = 0; i < 5; i++) printf(" [%c] %s\n", items[i].on ? 'X' : ' ', items[i].name); - return (output); + return (0); }
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/compile b/contrib/bsddialog/examples_library/compile index 3c3961d06baf..1a68313090f6 100755 --- a/contrib/bsddialog/examples_library/compile +++ b/contrib/bsddialog/examples_library/compile @@ -8,12 +8,16 @@ # worldwide. This software is distributed without any warranty, see: # <http://creativecommons.org/publicdomain/zero/1.0/>. +set -x + libpath=../lib examples="menu checklist radiolist mixedlist theme infobox yesno msgbox \ - datebox form formw timebox rangebox pause" +datebox form timebox rangebox pause calendar gauge mixedgauge textbox" + +rm -f $examples for e in $examples do - cc -g -Wall -Wextra -I$libpath ${e}.c -o $e -L$libpath -lbsddialog \ - -Wl,-rpath=$libpath + cc -g -Wall -Wextra -I$libpath ${e}.c -o $e \ + -Wl,-rpath=$libpath -L$libpath -lbsddialog done diff --git a/contrib/bsddialog/examples_library/datebox.c b/contrib/bsddialog/examples_library/datebox.c index a3c8946b7f79..e0741319d388 100644 --- a/contrib/bsddialog/examples_library/datebox.c +++ b/contrib/bsddialog/examples_library/datebox.c @@ -10,7 +10,6 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> #include <time.h> int main() @@ -31,27 +30,15 @@ int main() printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "datebox"; - output = bsddialog_datebox(&conf, - "TAB / RIGHT / LEFT to move,\nUP / DOWN to select time", 10, 35, - &yy, &mm, &dd); - + output = bsddialog_datebox(&conf, "Example", 9, 35, &yy, &mm, &dd); bsddialog_end(); - - switch (output) { - case BSDDIALOG_OK: - printf("Date: %u/%u/%u", yy, mm, dd); - break; - case BSDDIALOG_CANCEL: - printf("Cancel"); - break; - case BSDDIALOG_ERROR: - printf("Error: %s", bsddialog_geterror()); - break; + if (output == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); } - printf("\n"); + printf("Date: %u/%u/%u\n", yy, mm, dd); - return (output); + return (0); }
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/form.c b/contrib/bsddialog/examples_library/form.c index 0256975f2023..4c6336610852 100644 --- a/contrib/bsddialog/examples_library/form.c +++ b/contrib/bsddialog/examples_library/form.c @@ -9,9 +9,9 @@ */ #include <bsddialog.h> +#include <locale.h> #include <stdio.h> #include <stdlib.h> -#include <string.h> #define H BSDDIALOG_FIELDHIDDEN #define RO BSDDIALOG_FIELDREADONLY @@ -21,37 +21,32 @@ int main() int i, output; struct bsddialog_conf conf; struct bsddialog_formitem items[3] = { - {"Input:", 1, 1, "value", 1, 11, 30, 50, NULL, 0, "desc 1"}, - {"Input:", 2, 1, "read only", 2, 11, 30, 50, NULL, RO, "desc 2"}, - {"Password:", 3, 1, "", 3, 11, 30, 50, NULL, H, "desc 3"} + {"Input:", 0, 0, "value", 0, 10, 30, 50, NULL, 0, "desc 1"}, + {"Input:", 1, 0, "read only", 1, 10, 30, 50, NULL, RO, "desc 2"}, + {"Password:", 2, 0, "", 2, 10, 30, 50, NULL, H, "desc 3"} }; + /* Optional, unless for unicode/multi-column characters */ + setlocale(LC_ALL, ""); + if (bsddialog_init() == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "form"; conf.form.securech = '*'; - output = bsddialog_form(&conf, "Example", 10, 50, 3, 3, items); - + output = bsddialog_form(&conf, "Example", 10, 50, 3, 3, items, NULL); bsddialog_end(); - if (output == BSDDIALOG_ERROR) { printf("Error: %s", bsddialog_geterror()); return (1); } - if (output == BSDDIALOG_CANCEL) { - printf("Cancel\n"); - return (0); - } - for (i = 0; i < 3; i++) { printf("%s \"%s\"\n", items[i].label, items[i].value); free(items[i].value); } - return (output); -}
\ No newline at end of file + return (0); +} diff --git a/contrib/bsddialog/examples_library/formw.c b/contrib/bsddialog/examples_library/formw.c deleted file mode 100644 index edbeec98f2a3..000000000000 --- a/contrib/bsddialog/examples_library/formw.c +++ /dev/null @@ -1,61 +0,0 @@ -/*- - * SPDX-License-Identifier: CC0-1.0 - * - * Written in 2022 by Alfonso Sabato Siciliano. - * To the extent possible under law, the author has dedicated all copyright - * and related and neighboring rights to this software to the public domain - * worldwide. This software is distributed without any warranty, see: - * <http://creativecommons.org/publicdomain/zero/1.0/>. - */ - -#include <bsddialog.h> -#include <locale.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#define H BSDDIALOG_FIELDHIDDEN -#define RO BSDDIALOG_FIELDREADONLY - -int main() -{ - int i, output; - struct bsddialog_conf conf; - struct bsddialog_formitem items[3] = { - {"Input:", 1, 1, "value", 1, 11, 30, 50, NULL, 0, "desc 1"}, - {"Input:", 2, 1, "read only", 2, 11, 30, 50, NULL, RO, "desc 2"}, - {"Password:", 3, 1, "", 3, 11, 30, 50, NULL, H, "desc 3"} - }; - - setlocale(LC_ALL, ""); - - if (bsddialog_init() == BSDDIALOG_ERROR) { - printf("Error: %s\n", bsddialog_geterror()); - return (1); - } - - bsddialog_initconf(&conf); - conf.title = "form"; - conf.form.securech = '*'; - conf.form.enable_wchar = true; - output = bsddialog_form(&conf, "Example", 10, 50, 3, 3, items); - - bsddialog_end(); - - if (output == BSDDIALOG_ERROR) { - printf("Error: %s", bsddialog_geterror()); - return (1); - } - - if (output == BSDDIALOG_CANCEL) { - printf("Cancel\n"); - return (0); - } - - for (i = 0; i < 3; i++) { - printf("%s \"%ls\"\n", items[i].label, (wchar_t*)items[i].value); - free(items[i].value); - } - - return (output); -}
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/gauge.c b/contrib/bsddialog/examples_library/gauge.c new file mode 100644 index 000000000000..8ed1a8f57664 --- /dev/null +++ b/contrib/bsddialog/examples_library/gauge.c @@ -0,0 +1,57 @@ +/*- + * SPDX-License-Identifier: CC0-1.0 + * + * Written in 2023 by Alfonso Sabato Siciliano. + * To the extent possible under law, the author has dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty, see: + * <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +#include <bsddialog.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static void sender(int fd) +{ + int i; + + for (i = 1; i <= 10; i++) { + sleep(1); + dprintf(fd, "SEP\n"); + dprintf(fd, "%d\n", i * 10); + dprintf(fd, "In Progress... [%d / 10]\n", i); + dprintf(fd, "SEP\n"); + } + sleep(1); + dprintf(fd, "EOF\n"); +} + +int main() +{ + int rv, fd[2]; + struct bsddialog_conf conf; + + /* add checks and sync */ + pipe(fd); + if (fork() == 0) { + close(fd[0]); + sender(fd[1]); + exit (0); + } + close(fd[1]); + + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } + bsddialog_initconf(&conf); + conf.title = "gauge"; + rv = bsddialog_gauge(&conf, "Example", 7, 30, 0, fd[0], "SEP", "EOF"); + bsddialog_end(); + if (rv == BSDDIALOG_ERROR) + printf("Error: %s\n", bsddialog_geterror()); + + return (0); +}
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/infobox.c b/contrib/bsddialog/examples_library/infobox.c index 7334cd7536bf..bbd7f665d5a6 100644 --- a/contrib/bsddialog/examples_library/infobox.c +++ b/contrib/bsddialog/examples_library/infobox.c @@ -10,7 +10,6 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> int main() { @@ -21,18 +20,15 @@ int main() printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "infobox"; conf.sleep = 3; output = bsddialog_infobox(&conf, "Example\n(3 seconds)", 7, 20); - bsddialog_end(); - if (output == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); return (1); } - return (output); + return (0); }
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/menu.c b/contrib/bsddialog/examples_library/menu.c index 044e0030a010..5c4941f6d5de 100644 --- a/contrib/bsddialog/examples_library/menu.c +++ b/contrib/bsddialog/examples_library/menu.c @@ -10,7 +10,6 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> int main() { @@ -28,26 +27,18 @@ int main() printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "menu"; output = bsddialog_menu(&conf, "Example", 15, 30, 5, 5, items, NULL); - bsddialog_end(); - if (output == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); return (1); } - if (output == BSDDIALOG_CANCEL) { - printf("Cancel\n"); - return (0); - } - printf("Menu:\n"); for (i = 0; i < 5; i++) printf(" [%c] %s\n", items[i].on ? 'X' : ' ', items[i].name); - return (output); + return (0); }
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/mixedgauge.c b/contrib/bsddialog/examples_library/mixedgauge.c new file mode 100644 index 000000000000..e74bb3512a9a --- /dev/null +++ b/contrib/bsddialog/examples_library/mixedgauge.c @@ -0,0 +1,79 @@ +/*- + * SPDX-License-Identifier: CC0-1.0 + * + * Written in 2023 by Alfonso Sabato Siciliano. + * To the extent possible under law, the author has dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty, see: + * <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +#include <bsddialog.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +#define NMINIBAR 13 + +static const char *minilabels[NMINIBAR] = { + "Label 1", + "Label 2", + "Label 3", + "Label 4", + "Label 5", + "Label 6", + "Label 7", + "Label 8", + "Label 9", + "Label 10", + "Label 11", + "Label X", + "Label Y", +}; + +static int minipercs[NMINIBAR] = { + BSDDIALOG_MG_SUCCEEDED, + BSDDIALOG_MG_FAILED, + BSDDIALOG_MG_PASSED, + BSDDIALOG_MG_COMPLETED, + BSDDIALOG_MG_CHECKED, + BSDDIALOG_MG_DONE, + BSDDIALOG_MG_SKIPPED, + BSDDIALOG_MG_INPROGRESS, + BSDDIALOG_MG_BLANK, + BSDDIALOG_MG_NA, + BSDDIALOG_MG_PENDING, + 67, + 0, +}; + +static void exit_error() +{ + if (bsddialog_inmode()) + bsddialog_end(); + printf("Error: %s\n", bsddialog_geterror()); + exit (1); +} + +int main() +{ + int retval, i; + struct bsddialog_conf conf; + + if (bsddialog_init() == BSDDIALOG_ERROR) + exit_error(); + bsddialog_initconf(&conf); + conf.title = "mixedgauge"; + for (i = 0; i <= 10; i++) { + minipercs[11] += 3; + minipercs[12] = i * 10; + retval= bsddialog_mixedgauge(&conf, "Example", 20, 40, + 50 + i * 5, NMINIBAR, minilabels, minipercs); + if (retval == BSDDIALOG_ERROR) + exit_error(); + sleep(1); + } + bsddialog_end(); + + return (0); +}
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/mixedlist.c b/contrib/bsddialog/examples_library/mixedlist.c index 30198d20a155..6d286996931b 100644 --- a/contrib/bsddialog/examples_library/mixedlist.c +++ b/contrib/bsddialog/examples_library/mixedlist.c @@ -10,7 +10,6 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> int main() { @@ -40,34 +39,26 @@ int main() { "", false, 0, "Name 5", "Desc 5", "Bottom Desc 5" } }; struct bsddialog_menugroup group[4] = { - { BSDDIALOG_SEPARATOR, 1, sep1 }, - { BSDDIALOG_CHECKLIST, 5, check }, - { BSDDIALOG_SEPARATOR, 2, sep2 }, - { BSDDIALOG_RADIOLIST, 5, radio } + { BSDDIALOG_SEPARATOR, 1, sep1, 0 }, + { BSDDIALOG_CHECKLIST, 5, check, 0 }, + { BSDDIALOG_SEPARATOR, 2, sep2, 0 }, + { BSDDIALOG_RADIOLIST, 5, radio, 0 } }; if (bsddialog_init() == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "mixedlist"; output = bsddialog_mixedlist(&conf, "Example", 20, 0, 13, 4, group, NULL, NULL); - bsddialog_end(); - if (output == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); return (1); } - if (output == BSDDIALOG_CANCEL) { - printf("Cancel\n"); - return (0); - } - printf("Mixedlist:\n"); for (i = 0; i < 4; i++) { for (j = 0; j < group[i].nitems; j++) { @@ -83,5 +74,5 @@ int main() } } - return (output); -}
\ No newline at end of file + return (0); +} diff --git a/contrib/bsddialog/examples_library/msgbox.c b/contrib/bsddialog/examples_library/msgbox.c index 7d14073143ca..988e2976ee8e 100644 --- a/contrib/bsddialog/examples_library/msgbox.c +++ b/contrib/bsddialog/examples_library/msgbox.c @@ -10,7 +10,6 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> int main() { @@ -21,21 +20,19 @@ int main() printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "msgbox"; output = bsddialog_msgbox(&conf, "Example", 7, 20); - bsddialog_end(); switch (output) { case BSDDIALOG_ERROR: printf("Error %s\n", bsddialog_geterror()); - break; + return (1); case BSDDIALOG_OK: - printf("OK\n"); + printf("[OK]\n"); break; } - return (output); + return (0); }
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/pause.c b/contrib/bsddialog/examples_library/pause.c index 1cd64f24a0cd..fd79d33aa4c0 100644 --- a/contrib/bsddialog/examples_library/pause.c +++ b/contrib/bsddialog/examples_library/pause.c @@ -10,38 +10,37 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> int main() { int output; + unsigned int sec; struct bsddialog_conf conf; if (bsddialog_init() == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "pause"; - output = bsddialog_pause(&conf, "Example", 8, 50, 10); - + sec = 10; + output = bsddialog_pause(&conf, "Example", 8, 50, &sec); bsddialog_end(); switch (output) { + case BSDDIALOG_ERROR: + printf("Error: %s\n", bsddialog_geterror()); + return (1); case BSDDIALOG_OK: - printf("OK\n"); + printf("[OK] remaining time: %u\n", sec); break; case BSDDIALOG_CANCEL: - printf("Cancel\n"); - break; - case BSDDIALOG_ERROR: - printf("Error: %s\n", bsddialog_geterror()); + printf("[Cancel] remaining time: %u\n", sec); break; case BSDDIALOG_TIMEOUT: printf("Timeout\n"); break; } - return (output); -}
\ No newline at end of file + return (0); +} diff --git a/contrib/bsddialog/examples_library/radiolist.c b/contrib/bsddialog/examples_library/radiolist.c index ac8ed5bb4064..09df32ac2e74 100644 --- a/contrib/bsddialog/examples_library/radiolist.c +++ b/contrib/bsddialog/examples_library/radiolist.c @@ -10,7 +10,6 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> int main() { @@ -28,26 +27,19 @@ int main() printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "radiolist"; - output = bsddialog_radiolist(&conf, "Example", 15, 30, 5, 5, items, NULL); - + output = bsddialog_radiolist(&conf, "Example", 15, 30, 5, 5, items, + NULL); bsddialog_end(); - if (output == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); return (1); } - if (output == BSDDIALOG_CANCEL) { - printf("Cancel\n"); - return (0); - } - printf("Radiolist:\n"); for (i = 0; i < 5; i++) printf(" (%c) %s\n", items[i].on ? '*' : ' ', items[i].name); - return (output); + return (0); }
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/rangebox.c b/contrib/bsddialog/examples_library/rangebox.c index 8e6bf91c35a6..50498c459fc6 100644 --- a/contrib/bsddialog/examples_library/rangebox.c +++ b/contrib/bsddialog/examples_library/rangebox.c @@ -10,7 +10,6 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> int main() { @@ -21,20 +20,16 @@ int main() printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "rangebox"; value = 5; output = bsddialog_rangebox(&conf, "Example", 8, 50, 0, 10, &value); - bsddialog_end(); - if (output == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); return (1); } - printf("Value: %d\n", value); - return (output); + return (0); }
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/textbox.c b/contrib/bsddialog/examples_library/textbox.c new file mode 100644 index 000000000000..2e76cbb97891 --- /dev/null +++ b/contrib/bsddialog/examples_library/textbox.c @@ -0,0 +1,38 @@ +/*- + * SPDX-License-Identifier: CC0-1.0 + * + * Written in 2025 by Alfonso Sabato Siciliano. + * To the extent possible under law, the author has dedicated all copyright + * and related and neighboring rights to this software to the public domain + * worldwide. This software is distributed without any warranty, see: + * <http://creativecommons.org/publicdomain/zero/1.0/>. + */ + +#include <bsddialog.h> +#include <stdio.h> + +int main() +{ + int output; + struct bsddialog_conf conf; + + if (bsddialog_init() == BSDDIALOG_ERROR) { + printf("Error: %s\n", bsddialog_geterror()); + return (1); + } + bsddialog_initconf(&conf); + conf.title = "textbox"; + output = bsddialog_textbox(&conf, "./textbox.c", 20, 80); + bsddialog_end(); + + switch (output) { + case BSDDIALOG_ERROR: + printf("Error %s\n", bsddialog_geterror()); + return (1); + case BSDDIALOG_OK: + printf("[Exit]\n"); + break; + } + + return (0); +} diff --git a/contrib/bsddialog/examples_library/theme.c b/contrib/bsddialog/examples_library/theme.c index fcc5fd5cc31a..689633e624ed 100644 --- a/contrib/bsddialog/examples_library/theme.c +++ b/contrib/bsddialog/examples_library/theme.c @@ -11,64 +11,54 @@ #include <bsddialog.h> #include <bsddialog_theme.h> #include <stdio.h> -#include <string.h> int main() { int output, focusitem; struct bsddialog_conf conf; enum bsddialog_default_theme theme; - struct bsddialog_menuitem items[5] = { - {"", false, 0, "Flat", "dialog-like", - "BSDDIALOG_THEME_FLAT" }, - {"", false, 0, "Dialog", "dialog clone", - "BSDDIALOG_THEME_DIALOG" }, - {"", false, 0, "BSDDialog", "new theme", - "BSDDIALOG_THEME_BSDDIALOG" }, - {"", false, 0, "BlackWhite","black and white", - "BSDDIALOG_THEME_BLACKWHITE" }, - {"", false, 0, "Quit", "Exit", "Quit or Cancel to exit" } + struct bsddialog_menuitem items[4] = { + {"", false, 0, "Flat", "default flat theme", + "enum bsddialog_default_theme BSDDIALOG_THEME_FLAT" }, + {"", false, 0, "3D", "pseudo 3D theme", + "enum bsddialog_default_theme BSDDIALOG_THEME_3D" }, + {"", false, 0, "BlackWhite","black and white theme", + "enum bsddialog_default_theme BSDDIALOG_THEME_BLACKWHITE" }, + {"", false, 0, "Quit", "Exit", "Quit, Cancel or ESC to exit" } }; if (bsddialog_init() == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); - + conf.ascii_lines = true; bsddialog_backtitle(&conf, "Theme Example"); - + bsddialog_initconf(&conf); + conf.key.enable_esc = true; conf.title = " Theme "; focusitem = -1; while (true) { - output = bsddialog_menu(&conf, "Choose theme", 15, 45, 5, 5, + output = bsddialog_menu(&conf, "Choose theme", 15, 45, 4, 4, items, &focusitem); - if (output != BSDDIALOG_OK || items[4].on) + if (output != BSDDIALOG_OK || items[3].on) break; if (items[0].on) { theme = BSDDIALOG_THEME_FLAT; focusitem = 0; - } - else if (items[1].on) { - theme = BSDDIALOG_THEME_DIALOG; + } else if (items[1].on) { + theme = BSDDIALOG_THEME_3D; focusitem = 1; - } - else if (items[2].on) { - theme = BSDDIALOG_THEME_BSDDIALOG; - focusitem = 2; - } - else if (items[3].on) { + } else if (items[2].on) { theme = BSDDIALOG_THEME_BLACKWHITE; - focusitem = 3; + focusitem = 2; } - bsddialog_set_default_theme(theme); } bsddialog_end(); - return (output); + return (0); }
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/timebox.c b/contrib/bsddialog/examples_library/timebox.c index 0e27f824af11..a9354c99c9be 100644 --- a/contrib/bsddialog/examples_library/timebox.c +++ b/contrib/bsddialog/examples_library/timebox.c @@ -10,7 +10,6 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> #include <time.h> int main() @@ -31,26 +30,15 @@ int main() printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "timebox"; - output = bsddialog_timebox(&conf, - "TAB / RIGHT / LEFT to move,\nUP / DOWN to select time", 10, 35, - &hh, &mm, &ss); - + output = bsddialog_timebox(&conf, "Example", 9, 35, &hh, &mm, &ss); bsddialog_end(); - - switch (output) { - case BSDDIALOG_OK: - printf("Time: [%u:%u:%u]\n", hh, mm, ss); - break; - case BSDDIALOG_CANCEL: - printf("Cancel\n"); - break; - case BSDDIALOG_ERROR: + if (output == BSDDIALOG_ERROR) { printf("Error: %s\n", bsddialog_geterror()); - break; + return (1); } + printf("Time: %u:%u:%u\n", hh, mm, ss); - return (output); + return (0); }
\ No newline at end of file diff --git a/contrib/bsddialog/examples_library/yesno.c b/contrib/bsddialog/examples_library/yesno.c index 364a6c77fc26..36cb59fa013b 100644 --- a/contrib/bsddialog/examples_library/yesno.c +++ b/contrib/bsddialog/examples_library/yesno.c @@ -10,7 +10,6 @@ #include <bsddialog.h> #include <stdio.h> -#include <string.h> int main() { @@ -21,24 +20,22 @@ int main() printf("Error: %s\n", bsddialog_geterror()); return (1); } - bsddialog_initconf(&conf); conf.title = "yesno"; output = bsddialog_yesno(&conf, "Example", 7, 25); - bsddialog_end(); switch (output) { case BSDDIALOG_ERROR: printf("Error %s\n", bsddialog_geterror()); - break; + return (1); case BSDDIALOG_YES: - printf("YES\n"); + printf("[YES]\n"); break; case BSDDIALOG_NO: - printf("NO\n"); + printf("[NO]\n"); break; } - return (output); + return (0); }
\ No newline at end of file diff --git a/contrib/bsddialog/examples_utility/calendar.sh b/contrib/bsddialog/examples_utility/calendar.sh new file mode 100644 index 000000000000..a7ce4f1bb1d5 --- /dev/null +++ b/contrib/bsddialog/examples_utility/calendar.sh @@ -0,0 +1,34 @@ +#!/bin/sh +#- +# SPDX-License-Identifier: CC0-1.0 +# +# Written in 2022 by Alfonso Sabato Siciliano. +# +# To the extent possible under law, the author has dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty, see: +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +DATE=$(./bsddialog --title " calendar " --date-format "%x" \ + --calendar "Hello World!" 20 40 \ +3>&1 1>&2 2>&3 3>&-) + +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $DATE" + ;; +esac diff --git a/contrib/bsddialog/examples_utility/checklist.sh b/contrib/bsddialog/examples_utility/checklist.sh index fefabc282c80..7ff525cf765d 100755 --- a/contrib/bsddialog/examples_utility/checklist.sh +++ b/contrib/bsddialog/examples_utility/checklist.sh @@ -15,11 +15,11 @@ : ${BSDDIALOG_ESC=5} ITEMS=$(./bsddialog --title " checklist " --checklist "Hello World!" 15 30 5 \ - "Tag 1" "DESC 1 xyz" on \ - "Tag 2" "DESC 2 xyz" off \ - "Tag 3" "DESC 3 xyz" on \ - "Tag 4" "DESC 4 xyz" off \ - "Tag 5" "DESC 5 xyz" on \ + "1 Name" "DESC 1 xyz" on \ + "2 Name" "DESC 2 xyz" off \ + "3 Name" "DESC 3 xyz" on \ + "4 Name" "DESC 4 xyz" off \ + "5 Name" "DESC 5 xyz" on \ 3>&1 1>&2 2>&3 3>&-) case $? in @@ -27,12 +27,12 @@ case $? in exit 1 ;; $BSDDIALOG_ESC ) - echo "[ESC] focus: $ITEMS" + echo "[ESC]" ;; $BSDDIALOG_CANCEL ) - echo "[Cancel] focus: $ITEMS" + echo "[Cancel]" ;; $BSDDIALOG_OK ) - echo "[OK] Selected: $ITEMS" + echo "[OK] $ITEMS" ;; esac diff --git a/contrib/bsddialog/examples_utility/datebox.sh b/contrib/bsddialog/examples_utility/datebox.sh new file mode 100755 index 000000000000..bea4559dfbec --- /dev/null +++ b/contrib/bsddialog/examples_utility/datebox.sh @@ -0,0 +1,34 @@ +#!/bin/sh +#- +# SPDX-License-Identifier: CC0-1.0 +# +# Written in 2023 by Alfonso Sabato Siciliano. +# +# To the extent possible under law, the author has dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty, see: +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +DATE=$(./bsddialog --title " datebox " --date-format "%x" \ + --datebox "Hello World!" 9 30 \ +3>&1 1>&2 2>&3 3>&-) + +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] $DATE" + ;; +esac diff --git a/contrib/bsddialog/examples_utility/form.sh b/contrib/bsddialog/examples_utility/form.sh index bd1bac8a3939..ee25fa9cf352 100755 --- a/contrib/bsddialog/examples_utility/form.sh +++ b/contrib/bsddialog/examples_utility/form.sh @@ -15,11 +15,11 @@ : ${BSDDIALOG_ESC=5} FORMS=$(./bsddialog --title " form " --form "Hello World!" 12 40 5 \ - Label1: 1 1 Value1 1 9 18 25 \ - Label2: 2 1 Value2 2 9 18 25 \ - Label3: 3 1 Value3 3 9 18 25 \ - Label4: 4 1 Value4 4 9 18 25 \ - Label5: 5 1 Value5 5 9 18 25 \ + Label1: 0 0 Value1 0 8 18 25 \ + Label2: 1 0 Value2 1 8 18 25 \ + Label3: 2 0 Value3 2 8 18 25 \ + Label4: 3 0 Value4 3 8 18 25 \ + Label5: 4 0 Value5 4 8 18 25 \ 3>&1 1>&2 2>&3 3>&-) case $? in @@ -33,8 +33,6 @@ case $? in echo "[Cancel]" ;; $BSDDIALOG_OK ) - echo "[OK]" + echo "[OK] $FORMS" ;; esac - -echo "$FORMS" diff --git a/contrib/bsddialog/examples_utility/infobox.sh b/contrib/bsddialog/examples_utility/infobox.sh index ff39ad4d5f0e..b782e8cbf69e 100755 --- a/contrib/bsddialog/examples_utility/infobox.sh +++ b/contrib/bsddialog/examples_utility/infobox.sh @@ -9,4 +9,4 @@ # worldwide. This software is distributed without any warranty, see: # <http://creativecommons.org/publicdomain/zero/1.0/>. -./bsddialog --sleep 3 --title " infobox " --infobox "Hello World!\n3 secs" 6 20 +./bsddialog --normal-screen --title " infobox " --infobox "Hello World!" 6 20 diff --git a/contrib/bsddialog/examples_utility/inputbox.sh b/contrib/bsddialog/examples_utility/inputbox.sh index a359a3b8e833..756aec3c0241 100755 --- a/contrib/bsddialog/examples_utility/inputbox.sh +++ b/contrib/bsddialog/examples_utility/inputbox.sh @@ -28,9 +28,6 @@ case $? in echo "[Cancel]" ;; $BSDDIALOG_OK ) - echo "[OK]" + echo "[OK] $FORM" ;; esac - -echo "$FORM" - diff --git a/contrib/bsddialog/examples_utility/menu.sh b/contrib/bsddialog/examples_utility/menu.sh index 002a82f6a870..5b2f090037d8 100755 --- a/contrib/bsddialog/examples_utility/menu.sh +++ b/contrib/bsddialog/examples_utility/menu.sh @@ -15,10 +15,11 @@ : ${BSDDIALOG_ESC=5} ITEM=$(./bsddialog --title " menu " --menu "Hello World!" 15 30 5 \ - "Tag 1" "DESC 1 xyz" \ - "Tag 2" "DESC 2 xyz" \ - "Tag 3" "DESC 3 xyz" \ - "Tag 4" "DESC 4 xyz" \ + "1 Name" "DESC 1 xyz" \ + "2 Name" "DESC 2 xyz" \ + "3 Name" "DESC 3 xyz" \ + "4 Name" "DESC 4 xyz" \ + "5 Name" "DESC 5 xyz" \ 3>&1 1>&2 2>&3 3>&-) case $? in @@ -26,10 +27,10 @@ case $? in exit 1 ;; $BSDDIALOG_ESC ) - echo "[ESC] $ITEM" + echo "[ESC]" ;; $BSDDIALOG_CANCEL ) - echo "[Cancel] $ITEM" + echo "[Cancel]" ;; $BSDDIALOG_OK ) echo "[OK] $ITEM" diff --git a/contrib/bsddialog/examples_utility/mixedform.sh b/contrib/bsddialog/examples_utility/mixedform.sh index 80b7abf93745..6b690e7e5b8c 100755 --- a/contrib/bsddialog/examples_utility/mixedform.sh +++ b/contrib/bsddialog/examples_utility/mixedform.sh @@ -16,9 +16,9 @@ FORMS=$(./bsddialog --insecure --title " mixedform " \ --mixedform "Hello World!" 12 40 3 \ - Label: 1 1 Entry 1 11 18 25 0 \ - Label: 2 1 Read-Only 2 11 18 25 2 \ - Password: 3 1 "" 3 11 18 25 1 \ + Label: 0 0 Entry 0 10 18 25 0 \ + Label: 1 0 Read-Only 1 10 18 25 2 \ + Password: 2 0 "" 2 10 18 25 1 \ 3>&1 1>&2 2>&3 3>&-) case $? in @@ -32,8 +32,6 @@ case $? in echo "[Cancel]" ;; $BSDDIALOG_OK ) - echo "[OK]" + echo "[OK] $FORMS" ;; esac - -echo "$FORMS" diff --git a/contrib/bsddialog/examples_utility/mixedgauge.sh b/contrib/bsddialog/examples_utility/mixedgauge.sh index e98ff70614db..aa4ff2910cd9 100755 --- a/contrib/bsddialog/examples_utility/mixedgauge.sh +++ b/contrib/bsddialog/examples_utility/mixedgauge.sh @@ -10,11 +10,11 @@ # <http://creativecommons.org/publicdomain/zero/1.0/>. perc=0 +mainperc=50 while [ $perc -le 100 ] do ./bsddialog --sleep 1 --title " mixedgauge " \ - --mixedgauge "Example..." 20 45 $perc \ - "(Hidden)" " -9" \ + --mixedgauge "Example..." 20 45 $mainperc \ "Label 1" " -1" \ "Label 2" " -2" \ "Label 3" " -3" \ @@ -23,9 +23,11 @@ do "Label 6" " -6" \ "Label 7" " -7" \ "Label 8" " -8" \ - "Label 9" " -10" \ - "Label 10" " -11" \ + "Label 9" " -9" \ + "Label 10" " -10" \ + "Label 11" " -11" \ "Label X" $perc perc=`expr $perc + 20` + mainperc=`expr $mainperc + 10` done diff --git a/contrib/bsddialog/examples_utility/passwordbox.sh b/contrib/bsddialog/examples_utility/passwordbox.sh index 0f93a13c877c..b43ef6f6b994 100755 --- a/contrib/bsddialog/examples_utility/passwordbox.sh +++ b/contrib/bsddialog/examples_utility/passwordbox.sh @@ -29,8 +29,6 @@ case $? in echo "[Cancel]" ;; $BSDDIALOG_OK ) - echo "[OK]" + echo "[OK] $FORM" ;; esac - -echo "$FORM" diff --git a/contrib/bsddialog/examples_utility/passwordform.sh b/contrib/bsddialog/examples_utility/passwordform.sh index 19b3a355b6eb..874a185ee66f 100755 --- a/contrib/bsddialog/examples_utility/passwordform.sh +++ b/contrib/bsddialog/examples_utility/passwordform.sh @@ -16,11 +16,11 @@ FORMS=$(./bsddialog --insecure --title " passwordform " \ --passwordform "Example" 12 40 5 \ - Password1: 1 1 "" 1 12 18 25 \ - Password2: 2 1 "" 2 12 18 25 \ - Password3: 3 1 "" 3 12 18 25 \ - Password4: 4 1 "" 4 12 18 25 \ - Password5: 5 1 "" 5 12 18 25 \ + Password1: 0 0 "" 0 11 18 25 \ + Password2: 1 0 "" 1 11 18 25 \ + Password3: 2 0 "" 2 11 18 25 \ + Password4: 3 0 "" 3 11 18 25 \ + Password5: 4 0 "" 4 11 18 25 \ 3>&1 1>&2 2>&3 3>&-) case $? in @@ -34,8 +34,6 @@ case $? in echo "[Cancel]" ;; $BSDDIALOG_OK ) - echo "[OK]" + echo "[OK] $FORMS" ;; esac - -echo "$FORMS" diff --git a/contrib/bsddialog/examples_utility/radiolist.sh b/contrib/bsddialog/examples_utility/radiolist.sh index 9de483369c75..359473abf35e 100755 --- a/contrib/bsddialog/examples_utility/radiolist.sh +++ b/contrib/bsddialog/examples_utility/radiolist.sh @@ -15,11 +15,11 @@ : ${BSDDIALOG_ESC=5} ITEM=$(./bsddialog --title " radiolist " --radiolist "Hello World!" 15 30 5 \ - "Tag 1" "DESC 1 xyz" off \ - "Tag 2" "DESC 2 xyz" off \ - "Tag 3" "DESC 3 xyz" on \ - "Tag 4" "DESC 4 xyz" off \ - "Tag 5" "DESC 5 xyz" off \ + "1 Name" "DESC 1 xyz" off \ + "2 Name" "DESC 2 xyz" off \ + "3 Name" "DESC 3 xyz" on \ + "4 Name" "DESC 4 xyz" off \ + "5 Name" "DESC 5 xyz" off \ 3>&1 1>&2 2>&3 3>&-) case $? in @@ -27,14 +27,12 @@ case $? in exit 1 ;; $BSDDIALOG_ESC ) - echo "[ESC] focus " + echo "[ESC]" ;; $BSDDIALOG_CANCEL ) - echo "[Cancel] focus " + echo "[Cancel]" ;; $BSDDIALOG_OK ) - echo "[OK]" + echo "[OK] $ITEM" ;; esac - -echo "$ITEM" diff --git a/contrib/bsddialog/examples_utility/rangebox.sh b/contrib/bsddialog/examples_utility/rangebox.sh new file mode 100644 index 000000000000..9b3213d8acad --- /dev/null +++ b/contrib/bsddialog/examples_utility/rangebox.sh @@ -0,0 +1,33 @@ +#!/bin/sh +#- +# SPDX-License-Identifier: CC0-1.0 +# +# Written in 2023 by Alfonso Sabato Siciliano. +# +# To the extent possible under law, the author has dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty, see: +# <http://creativecommons.org/publicdomain/zero/1.0/>. + +: ${BSDDIALOG_ERROR=255} +: ${BSDDIALOG_OK=0} +: ${BSDDIALOG_CANCEL=1} +: ${BSDDIALOG_ESC=5} + +VALUE=$(./bsddialog --title " rangebox " --rangebox "Hello World!" 7 35 0 10 5 \ +3>&1 1>&2 2>&3 3>&-) + +case $? in + $BSDDIALOG_ERROR ) + exit 1 + ;; + $BSDDIALOG_ESC ) + echo "[ESC]" + ;; + $BSDDIALOG_CANCEL ) + echo "[Cancel]" + ;; + $BSDDIALOG_OK ) + echo "[OK] Value: $VALUE" + ;; +esac diff --git a/contrib/bsddialog/examples_utility/timebox.sh b/contrib/bsddialog/examples_utility/timebox.sh index 81d24d558dc9..233434b29195 100755 --- a/contrib/bsddialog/examples_utility/timebox.sh +++ b/contrib/bsddialog/examples_utility/timebox.sh @@ -14,8 +14,7 @@ : ${BSDDIALOG_CANCEL=1} : ${BSDDIALOG_ESC=5} -TIME=$(./bsddialog --title " timebox " \ - --timebox "Tab / Left / Right to move\nUp / Down to select" 10 40 \ +TIME=$(./bsddialog --title " timebox " --timebox "Hello World!" 8 25 \ 3>&1 1>&2 2>&3 3>&-) case $? in diff --git a/contrib/bsddialog/lib/GNUMakefile b/contrib/bsddialog/lib/GNUMakefile deleted file mode 100644 index 0d724b803be3..000000000000 --- a/contrib/bsddialog/lib/GNUMakefile +++ /dev/null @@ -1,31 +0,0 @@ -# PUBLIC DOMAIN - NO WARRANTY, see: -# <http://creativecommons.org/publicdomain/zero/1.0/> -# -# Written in 2021 by Alfonso Sabato Siciliano - -VERSION = 0.2 -LIBRARY = bsddialog -LIBRARY_SO = lib${LIBRARY:=.so} -HEADERS = bsddialog.h bsddialog_theme.h bsddialog_progressview.h -SOURCES = barbox.c formbox.c infobox.c libbsddialog.c lib_util.c menubox.c \ - messagebox.c textbox.c theme.c timebox.c -OBJECTS = $(SOURCES:.c=.o) -CFLAGS = -D_XOPEN_SOURCE_EXTENDED -Wall -Wextra -Wno-implicit-fallthrough \ - -Werror -fpic -LDFLAGS = -lformw -lncursesw -ltinfo -LIBFLAG = -shared - -RM = rm -f -LN = ln -s -f - -all : $(LIBRARY) - -$(LIBRARY): $(OBJECTS) - $(CC) $(LIBFLAG) $^ -o $(LIBRARY_SO).$(VERSION) $(LDFLAGS) - ${LN} ${LIBRARY_SO}.${VERSION} ${LIBRARY_SO} - -%.o: %.c $(HEADERS) - $(CC) $(CFLAGS) -c $< - -clean: - $(RM) $(LIBRARY_SO)* *.o *~ diff --git a/contrib/bsddialog/lib/GNUmakefile b/contrib/bsddialog/lib/GNUmakefile new file mode 100644 index 000000000000..2cb060381a46 --- /dev/null +++ b/contrib/bsddialog/lib/GNUmakefile @@ -0,0 +1,51 @@ +# PUBLIC DOMAIN - NO WARRANTY, see: +# <http://creativecommons.org/publicdomain/zero/1.0/> +# +# Written in 2021 by Alfonso Sabato Siciliano + +LIBRARY = bsddialog +LIBRARY_SO = lib${LIBRARY:=.so} +HEADERS = bsddialog.h bsddialog_theme.h bsddialog_progressview.h +SOURCES = barbox.c datebox.c formbox.c libbsddialog.c lib_util.c \ + menubox.c messagebox.c textbox.c theme.c timebox.c +OBJECTS = $(SOURCES:.c=.o) +PREFIX = /usr/local + +ifneq ($(ENABLEDEBUG),) +CFLAGS += -g +endif +CFLAGS += -D_XOPEN_SOURCE_EXTENDED -D_XOPEN_SOURCE -D_GNU_SOURCE \ + -Wall -Wextra -Werror -fpic +LDFLAGS += -lncursesw -ltinfo +LIBFLAG = -shared + +RM = rm -f +LN = ln -s -f + +all: $(LIBRARY) + +install: all + ${INSTALL} -m 0644 bsddialog.h ${DESTDIR}${PREFIX}/include/bsddialog.h + ${INSTALL} -m 0644 bsddialog_progressview.h ${DESTDIR}${PREFIX}/include/bsddialog_progressview.h + ${INSTALL} -m 0644 bsddialog_theme.h ${DESTDIR}${PREFIX}/include/bsddialog_theme.h + ${INSTALL} -m 0755 ${LIBRARY_SO}.${VERSION} ${DESTDIR}${PREFIX}/lib/${LIBRARY_SO}.${VERSION} + ${LN} ${LIBRARY_SO}.${VERSION} ${DESTDIR}${PREFIX}/lib/${LIBRARY_SO} + +uninstall: + ${RM} ${DESTDIR}${PREFIX}/include/bsddialog.h + ${RM} ${DESTDIR}${PREFIX}/include/bsddialog_progressview.h + ${RM} ${DESTDIR}${PREFIX}/include/bsddialog_theme.h + ${RM} ${DESTDIR}${PREFIX}/lib/${LIBRARY_SO}.${VERSION} + ${RM} ${DESTDIR}${PREFIX}/lib/${LIBRARY_SO} + +$(LIBRARY): $(OBJECTS) + $(CC) $(LIBFLAG) $^ -o $(LIBRARY_SO).$(VERSION) $(LDFLAGS) + ${LN} ${LIBRARY_SO}.${VERSION} ${LIBRARY_SO} + +%.o: %.c $(HEADERS) + $(CC) $(CFLAGS) -c $< + +clean: + $(RM) $(LIBRARY_SO)* *.o *~ + +.PHONY: all install uninstall ${LIBRARY} clean diff --git a/contrib/bsddialog/lib/Makefile b/contrib/bsddialog/lib/Makefile index 962b059b3e03..c728541a9f7a 100644 --- a/contrib/bsddialog/lib/Makefile +++ b/contrib/bsddialog/lib/Makefile @@ -3,39 +3,44 @@ # # Written in 2021 by Alfonso Sabato Siciliano -VERSION = 0.2 LIBRARY = bsddialog LIBRARY_SO = lib${LIBRARY:=.so} LIBRARY_A = lib${LIBRARY:=.a} HEADERS = bsddialog.h bsddialog_theme.h bsddialog_progressview.h -SOURCES = barbox.c formbox.c infobox.c libbsddialog.c lib_util.c menubox.c \ - messagebox.c textbox.c theme.c timebox.c +SOURCES = barbox.c datebox.c formbox.c libbsddialog.c lib_util.c \ + menubox.c messagebox.c textbox.c theme.c timebox.c OBJECTS = ${SOURCES:.c=.o} -CFLAGS += -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra -LDFLAGS += -fstack-protector-strong -shared -Wl,-x -Wl,--fatal-warnings \ - -Wl,--warn-shared-textrel -Wl,-soname,${LIBRARY_SO}.${VERSION} \ - -L/usr/lib -lformw -lncursesw -ltinfow +PREFIX = /usr/local .if defined(DEBUG) -# `make -DDEBUG` -CFLAGS = -g -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra -.else -CFLAGS += -std=gnu99 -fstack-protector-strong +CFLAGS += -g .endif +CFLAGS += -D_XOPEN_SOURCE_EXTENDED -fPIC -Wall -Wextra -std=gnu99 \ + -fstack-protector-strong +LDFLAGS += -fstack-protector-strong -shared -Wl,-x -Wl,--fatal-warnings \ + -Wl,--warn-shared-textrel -Wl,-soname,${LIBRARY_SO}.${VERSION} \ + -L/usr/lib -lncursesw -ltinfow -LOCALBASE = /usr/local LN = ln -s -f RM = rm -f -CP = cp -GZIP = gzip -cn -LDCONFIG = /sbin/ldconfig -m -MAN = ${OUTPUT}.3 -GZIP = gzip -cn -MANDIR = ${LOCALBASE}/share/man/man3 -INSTALL = install -RM = rm -f -all : man ${LIBRARY} +all: ${LIBRARY} + +install: all + ${INSTALL} -m 0644 bsddialog.h ${DESTDIR}${PREFIX}/include/bsddialog.h + ${INSTALL} -m 0644 bsddialog_progressview.h ${DESTDIR}${PREFIX}/include/bsddialog_progressview.h + ${INSTALL} -m 0644 bsddialog_theme.h ${DESTDIR}${PREFIX}/include/bsddialog_theme.h + ${INSTALL} -m 0644 ${LIBRARY_A} ${DESTDIR}${PREFIX}/lib/${LIBRARY_A} + ${INSTALL} -m 0755 ${LIBRARY_SO}.${VERSION} ${DESTDIR}${PREFIX}/lib/${LIBRARY_SO}.${VERSION} + ${LN} ${LIBRARY_SO}.${VERSION} ${DESTDIR}${PREFIX}/lib/${LIBRARY_SO} + +uninstall: + ${RM} ${DESTDIR}${PREFIX}/include/bsddialog.h + ${RM} ${DESTDIR}${PREFIX}/include/bsddialog_progressview.h + ${RM} ${DESTDIR}${PREFIX}/include/bsddialog_theme.h + ${RM} ${DESTDIR}${PREFIX}/lib/${LIBRARY_A} + ${RM} ${DESTDIR}${PREFIX}/lib/${LIBRARY_SO}.${VERSION} + ${RM} ${DESTDIR}${PREFIX}/lib/${LIBRARY_SO} ${LIBRARY}: ${LIBRARY_SO} ${LIBRARY_A} @@ -52,24 +57,7 @@ ${LIBRARY_A}: ${OBJECTS} .c.o: ${CC} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} -man: - ${GZIP} ${LIBRARY}.3 > ${LIBRARY}.3.gz - clean: ${RM} ${LIBRARY_SO}* *.o *~ *.gz ${LIBRARY_A} - -install: - ${INSTALL} -m 644 ${HEADERS} ${LOCALBASE}/include - ${INSTALL} -m 644 -s ${LIBRARY_SO}.${VERSION} ${LOCALBASE}/lib/ - ${INSTALL} -l rs ${LOCALBASE}/lib/${LIBRARY_SO}.${VERSION} ${LOCALBASE}/lib/${LIBRARY_SO} - ${INSTALL} -m 644 ${LIBRARY_A} ${LOCALBASE}/lib - ${LDCONFIG} ${LOCALBASE}/lib - ${INSTALL} -m 644 ${LIBRARY}.3.gz ${MANDIR} - -unistall: - ${RM} ${LOCALBASE}/include/${LIBRARY}*.h - ${RM} ${LOCALBASE}/lib/${LIBRARY_SO} - ${RM} ${LOCALBASE}/lib/${LIBRARY_SO}.${VERSION} - ${LDCONFIG} ${LOCALBASE}/lib - ${RM} ${MANDIR}/${LIBRARY}.3.gz +.PHONY: all install uninstall ${LIBRARY} clean diff --git a/contrib/bsddialog/lib/barbox.c b/contrib/bsddialog/lib/barbox.c index 49aa105c1de3..51f81ecbca68 100644 --- a/contrib/bsddialog/lib/barbox.c +++ b/contrib/bsddialog/lib/barbox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,9 +25,6 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> #include <curses.h> #include <stdlib.h> #include <string.h> @@ -39,139 +36,138 @@ #include "bsddialog_theme.h" #include "lib_util.h" -#define BARPADDING 2 -#define MINBARLEN 15 -#define MINBARWIDTH (2 + 2 * BARPADDING + MINBARLEN) -#define MINMGBARLEN 18 -#define MINMGBARWIDTH (2 + 2 * BARPADDING + MINMGBARLEN) +#define BARPADDING 2 /* Dialog border | BARPADDING | box bar */ +#define BOXBORDERS 2 +#define MIN_WBAR 15 +#define MIN_WBOX (BARPADDING + BOXBORDERS + MIN_WBAR + BARPADDING) +#define MIN_WMGBAR 18 /* Mixedgauge main bar */ +#define MIN_WMGBOX (BARPADDING + BOXBORDERS + MIN_WMGBAR + BARPADDING) +#define HBOX 3 +#define WBOX(d) ((d)->w - BORDERS - BARPADDING - BARPADDING) +#define WBAR(d) (WBOX(d) - BOXBORDERS) bool bsddialog_interruptprogview; bool bsddialog_abortprogview; -int bsddialog_total_progview; - -static void -draw_bar(WINDOW *win, int y, int x, int barlen, int perc, bool withlabel, - int label) +long long int bsddialog_total_progview; + +static const char states[12][14] = { + " Succeeded ", /* -1 */ + " Failed ", /* -2 */ + " Passed ", /* -3 */ + " Completed ", /* -4 */ + " Checked ", /* -5 */ + " Done ", /* -6 */ + " Skipped ", /* -7 */ + " In Progress ", /* -8 */ + "(blank) ", /* -9 */ + " N/A ", /* -10 */ + " Pending ", /* -11 */ + " UNKNOWN ", /* < -11, no API */ +}; + +struct bar { + bool toupdate; + WINDOW *win; + int y; /* bar y in win */ + int x; /* bar x in win */ + int w; /* width in win */ + int perc; /* barlen = (w * perc) / 100 */ + const char* fmt; /* format for label */ + int label; /* rangebox and pause perc!=label */ +}; + +static void draw_bar(struct bar *b) { - int i, blue_x, color, stringlen; - char labelstr[128]; - - blue_x = perc > 0 ? (perc * barlen) / 100 : -1; - - wmove(win, y, x); - for (i = 0; i < barlen; i++) { - color = (i <= blue_x) ? t.bar.f_color : t.bar.color; - wattron(win, color); - waddch(win, ' '); - wattroff(win, color); - } - - if (withlabel) - sprintf(labelstr, "%d", label); - else - sprintf(labelstr, "%3d%%", perc); - stringlen = (int)strlen(labelstr); - wmove(win, y, x + barlen/2 - stringlen/2); - for (i = 0; i < stringlen; i++) { - color = (blue_x + 1 <= barlen/2 - stringlen/2 + i ) ? - t.bar.color : t.bar.f_color; - wattron(win, color); - waddch(win, labelstr[i]); - wattroff(win, color); - } + int barlen, xlabel; + chtype ch; + char label[128]; + + barlen = b->perc > 0 ? (b->perc * b->w) / 100 : 0; + + ch = ' ' | t.bar.f_color; + mvwhline(b->win, b->y, b->x, ch, barlen); + ch = ' ' | t.bar.color; + mvwhline(b->win, b->y, b->x + barlen, ch, b->w - barlen); + + sprintf(label, b->fmt, b->label); + xlabel = b->x + b->w/2 - (int)strlen(label)/2; /* 1-byte-char string */ + wattron(b->win, t.bar.color); /* x+barlen < xlabel */ + mvwaddstr(b->win, b->y, xlabel, label); + wattroff(b->win, t.bar.color); + wattron(b->win, t.bar.f_color); /* x+barlen >= xlabel */ + mvwaddnstr(b->win, b->y, xlabel, label, MAX((b->x+barlen) - xlabel, 0)); + wattroff(b->win, t.bar.f_color); + + if (b->toupdate) + wnoutrefresh(b->win); + b->toupdate = false; } -static int -bar_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - const char *text, struct buttons *bs) +static void update_barbox(struct dialog *d, struct bar *b, bool buttons) { - int htext, wtext; - - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, bs, 3, MINBARWIDTH, - &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); - } - - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, MINBARWIDTH, bs); - - if (rows == BSDDIALOG_AUTOSIZE) - *h = widget_min_height(conf, htext, 3 /* bar */, bs != NULL); - - return (0); -} - -static int -bar_checksize(int rows, int cols, struct buttons *bs) -{ - int minheight, minwidth; - - minwidth = 0; - if (bs != NULL) /* gauge has not buttons */ - minwidth = buttons_width(*bs); + int y; - minwidth = MAX(minwidth, MINBARWIDTH); - minwidth += VBORDERS; - - if (cols < minwidth) - RETURN_ERROR("Few cols to draw bar and/or buttons"); - - minheight = HBORDERS + 3; - if (bs != NULL) - minheight += 2; - if (rows < minheight) - RETURN_ERROR("Few rows to draw bar"); - - return (0); + y = d->y + d->h - BORDER - HBOX; + if (buttons) + y -= HBUTTONS; + update_box(d->conf, b->win, y, d->x + BORDER + BARPADDING, HBOX, + WBOX(d), RAISED); } int bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int perc, int fd, const char *sep) + int cols, unsigned int perc, int fd, const char *sep, const char *end) { bool mainloop; - int y, x, h, w, fd2; + int fd2; FILE *input; - WINDOW *widget, *textpad, *bar, *shadow; char inputbuf[2048], ntext[2048], *pntext; + struct bar b; + struct dialog d; - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (bar_autosize(conf, rows, cols, &h, &w, text, NULL) != 0) - return (BSDDIALOG_ERROR); - if (bar_checksize(h, w, NULL) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, NULL, - false) != 0) + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + b.y = b.x = 1; + b.fmt = "%3d%%"; - bar = new_boxed_window(conf, y+h-4, x+3, 3, w-6, RAISED); + input = NULL; + if (fd >= 0) { + CHECK_PTR(sep); + CHECK_PTR(end); - mainloop = (fd < 0) ? false : true; - - if (mainloop) { fd2 = dup(fd); - input = fdopen(fd2, "r"); - if (input == NULL) - RETURN_ERROR("Cannot build FILE* from fd"); - } else - input = NULL; + if ((input = fdopen(fd2, "r")) == NULL) + RETURN_FMTERROR("Cannot build FILE* from fd %d", fd); + } + perc = MIN(perc, 100); + mainloop = true; while (mainloop) { - wrefresh(widget); - prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-4, - x+w-1-TEXTHMARGIN); - draw_borders(conf, bar, 3, w-6, RAISED); - draw_bar(bar, 1, 1, w-8, perc, false, -1 /*unused*/); - wrefresh(bar); + if (d.built) { + hide_dialog(&d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(&d, HBOX, MIN_WBOX, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(&d)) + return (BSDDIALOG_ERROR); + if (d.built) + refresh(); /* fix grey lines expanding screen */ + TEXTPAD(&d, HBOX); + update_barbox(&d, &b, false); + b.w = WBAR(&d); + b.perc = b.label = perc; + b.toupdate = true; + draw_bar(&b); + doupdate(); + if (input == NULL) /* that is fd < 0 */ + break; while (true) { fscanf(input, "%s", inputbuf); - if (strcmp(inputbuf,"EOF") == 0) { + if (strcmp(inputbuf, end) == 0) { mainloop = false; break; } @@ -181,154 +177,173 @@ bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, if (mainloop == false) break; fscanf(input, "%d", &perc); - perc = perc > 100 ? 100 : perc; + perc = MIN(perc, 100); pntext = &ntext[0]; ntext[0] = '\0'; while (true) { fscanf(input, "%s", inputbuf); - if (strcmp(inputbuf,"EOF") == 0) { + if (strcmp(inputbuf, end) == 0) { mainloop = false; break; } if (strcmp(inputbuf, sep) == 0) break; strcpy(pntext, inputbuf); - pntext += strlen(inputbuf); + pntext += strlen(inputbuf); /* end string, no strlen */ pntext[0] = ' '; pntext++; } - if (update_dialog(conf, shadow, widget, y, x, h, w, textpad, - ntext, NULL, false) != 0) - return (BSDDIALOG_ERROR); + pntext[0] = '\0'; + d.text = ntext; } if (input != NULL) fclose(input); - delwin(bar); - end_dialog(conf, shadow, widget, textpad); + delwin(b.win); + end_dialog(&d); return (BSDDIALOG_OK); } /* Mixedgauge */ +static void +mvwaddcstr(WINDOW *win, int y, int x, const char *mbstring, unsigned int cols) +{ + size_t charlen, n, w; + mbstate_t mbs; + const char *pmbstring; + wchar_t wch; + + w = n = 0; + pmbstring = mbstring; + memset(&mbs, 0, sizeof(mbs)); + while ((charlen = mbrlen(pmbstring, MB_CUR_MAX, &mbs)) != 0 && + charlen != (size_t)-1 && charlen != (size_t)-2) { + mbtowc(&wch, pmbstring, charlen); + w += (wch == L'\t') ? TABSIZE : wcwidth(wch); + if (w > cols) + break; + pmbstring += charlen; + n += charlen; + } + mvwaddnstr(win, y, x, mbstring, n); + if(w > cols) + mvwaddstr(win, y, (x + cols) - 3, "..."); +} + +static int +mixedgauge_size_position(struct dialog *d, int nminibars, + const char **minilabels, int *htext) +{ + int i, max_minibarlen; + + max_minibarlen = 0; + for (i = 0; i < (int)nminibars; i++) + max_minibarlen = MAX(max_minibarlen, + (int)strcols(CHECK_STR(minilabels[i]))); + max_minibarlen += 18; /* ' '<max_minibarlen>' ['13'] ' */ + max_minibarlen = MAX(max_minibarlen, MIN_WMGBOX); /* mainbar */ + + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, htext, &d->bs, nminibars + HBOX, max_minibarlen) != 0) + return (BSDDIALOG_ERROR); + if (widget_checksize(d->h, d->w, &d->bs, nminibars + HBOX, + MIN_WMGBOX) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); +} + static int do_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int mainperc, unsigned int nminibars, const char **minilabels, int *minipercs, bool color) { - int i, output, miniperc, y, x, h, w, ypad, max_minbarlen; - int htextpad, htext, wtext; - int colorperc, red, green; - WINDOW *widget, *textpad, *bar, *shadow; - char states[12][14] = { - " Succeeded ", /* -1 */ - " Failed ", /* -2 */ - " Passed ", /* -3 */ - " Completed ", /* -4 */ - " Checked ", /* -5 */ - " Done ", /* -6 */ - " Skipped ", /* -7 */ - " In Progress ", /* -8 */ - "(blank) ", /* -9 */ - " N/A ", /* -10 */ - " Pending ", /* -11 */ - " UNKNOWN ", /* < -11, no API */ - }; + int i, miniperc; + int ystext, htext; + int minicolor, red, green; + struct bar b; + struct dialog d; + + CHECK_ARRAY(nminibars, minilabels); + CHECK_ARRAY(nminibars, minipercs); red = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_RED, BSDDIALOG_BOLD); green = bsddialog_color(BSDDIALOG_WHITE,BSDDIALOG_GREEN,BSDDIALOG_BOLD); - max_minbarlen = 0; - for (i = 0; i < (int)nminibars; i++) - max_minbarlen = MAX(max_minbarlen, (int)strlen(minilabels[i])); - max_minbarlen += 3 + 16; /* seps + [...] */ - max_minbarlen = MAX(max_minbarlen, MINMGBARWIDTH); /* mainbar */ - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); - - /* mixedgauge autosize */ - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, NULL, nminibars + 3, - max_minbarlen, &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); - } - if (cols == BSDDIALOG_AUTOSIZE) - w = widget_min_width(conf, wtext, max_minbarlen, NULL); - if (rows == BSDDIALOG_AUTOSIZE) - h = widget_min_height(conf, htext, nminibars + 3, false); - - /* mixedgauge checksize */ - if (w < max_minbarlen + 2) - RETURN_ERROR("Few cols for this mixedgauge"); - if (h < 5 + (int)nminibars) - RETURN_ERROR("Few rows for this mixedgauge"); - - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (mixedgauge_size_position(&d, nminibars, minilabels, &htext) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(&d) != 0) return (BSDDIALOG_ERROR); - - output = new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, - NULL, false); - if (output == BSDDIALOG_ERROR) - return (output); /* mini bars */ + b.win = d.widget; + b.x = 1 + d.w - 2 - 15; + b.w = 13; + b.fmt = "%3d%%"; + b.toupdate = false; for (i = 0; i < (int)nminibars; i++) { miniperc = minipercs[i]; - if (miniperc == BSDDIALOG_MG_BLANK) - continue; /* label */ - if (color && (miniperc >= 0)) - wattron(widget, A_BOLD); - mvwaddstr(widget, i+1, 2, minilabels[i]); - wattroff(widget, A_BOLD); + if (color && miniperc >= 0) + wattron(d.widget, A_BOLD); + mvwaddcstr(d.widget, i+1, 2, CHECK_STR(minilabels[i]), d.w-20); + if (color && miniperc >= 0) + wattroff(d.widget, A_BOLD); /* perc */ - if (miniperc < -11) - mvwaddstr(widget, i+1, w-2-15, states[11]); - else if (miniperc < 0) { - mvwaddstr(widget, i+1, w-2-15, "[ ]"); - colorperc = -1; + if (miniperc == BSDDIALOG_MG_BLANK) + continue; + mvwaddstr(d.widget, i+1, d.w-2-15, "[ ]"); + if (miniperc >= 0) { + b.y = i + 1; + b.perc = b.label = MIN(miniperc, 100); + draw_bar(&b); + } else { /* miniperc < 0 */ + if (miniperc < BSDDIALOG_MG_PENDING) + miniperc = -12; /* UNKNOWN */ + minicolor = t.dialog.color; if (color && miniperc == BSDDIALOG_MG_FAILED) - colorperc = red; - if (color && miniperc == BSDDIALOG_MG_DONE) - colorperc = green; - if (colorperc != -1) - wattron(widget, colorperc); + minicolor = red; + else if (color && miniperc == BSDDIALOG_MG_DONE) + minicolor = green; + wattron(d.widget, minicolor); miniperc = abs(miniperc + 1); - mvwaddstr(widget, i+1, 1+w-2-15, states[miniperc]); - if (colorperc != -1) - wattroff(widget, colorperc); - } - else { /* miniperc >= 0 */ - if (miniperc > 100) - miniperc = 100; - mvwaddstr(widget, i+1, w-2-15, "[ ]"); - draw_bar(widget, i+1, 1+w-2-15, 13, miniperc, false, - -1 /*unused*/); + mvwaddstr(d.widget, i+1, 1+d.w-2-15, states[miniperc]); + wattroff(d.widget, minicolor); } } + wnoutrefresh(d.widget); - wrefresh(widget); - getmaxyx(textpad, htextpad, i /* unused */); - ypad = y + h - 4 - htextpad; - ypad = ypad < y+(int)nminibars ? y+(int)nminibars : ypad; - prefresh(textpad, 0, 0, ypad, x+2, y+h-4, x+w-2); + /* text */ + ystext = MAX(d.h - BORDERS - htext - HBOX, (int)nminibars); + rtextpad(&d, 0, 0, ystext, HBOX); /* main bar */ - bar = new_boxed_window(conf, y+h -4, x+3, 3, w-6, RAISED); - - draw_bar(bar, 1, 1, w-8, mainperc, false, -1 /*unused*/); - - wattron(bar, t.bar.color); - mvwaddstr(bar, 0, 2, "Overall Progress"); - wattroff(bar, t.bar.color); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + update_barbox(&d, &b, false); + wattron(b.win, t.bar.color); + mvwaddstr(b.win, 0, 2, "Overall Progress"); + wattroff(b.win, t.bar.color); + + b.y = b.x = 1; + b.w = WBAR(&d); + b.fmt = "%3d%%"; + b.perc = b.label = MIN(mainperc, 100); + b.toupdate = true; + draw_bar(&b); - wrefresh(bar); - - /* getch(); port ncurses shows nothing */ + doupdate(); + /* getch(); to test with "alternate mode" */ - delwin(bar); - end_dialog(conf, shadow, widget, textpad); + delwin(b.win); + end_dialog(&d); return (BSDDIALOG_OK); } @@ -338,12 +353,12 @@ bsddialog_mixedgauge(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int mainperc, unsigned int nminibars, const char **minilabels, int *minipercs) { - int output; + int retval; - output = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars, + retval = do_mixedgauge(conf, text, rows, cols, mainperc, nminibars, minilabels, minipercs, false); - return (output); + return (retval); } int @@ -352,11 +367,11 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, struct bsddialog_fileminibar *minibar) { bool update; - int perc, output, *minipercs; + int perc, retval, *minipercs; unsigned int i, mainperc, totaltodo; float readforsec; const char **minilabels; - time_t tstart, told, tnew, refresh; + time_t tstart, told, tnew, trefresh; if ((minilabels = calloc(nminibar, sizeof(char*))) == NULL) RETURN_ERROR("Cannot allocate memory for minilabels"); @@ -370,8 +385,8 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, minipercs[i] = minibar[i].status; } - refresh = pvconf->refresh == 0 ? 0 : pvconf->refresh - 1; - output = BSDDIALOG_OK; + trefresh = pvconf->refresh == 0 ? 0 : pvconf->refresh - 1; + retval = BSDDIALOG_OK; i = 0; update = true; time(&told); @@ -383,10 +398,10 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, mainperc = (bsddialog_total_progview * 100) / totaltodo; time(&tnew); - if (update || tnew > told + refresh) { - output = do_mixedgauge(conf, text, rows, cols, mainperc, + if (update || tnew > told + trefresh) { + retval = do_mixedgauge(conf, text, rows, cols, mainperc, nminibar, minilabels, minipercs, true); - if (output == BSDDIALOG_ERROR) + if (retval == BSDDIALOG_ERROR) return (BSDDIALOG_ERROR); move(SCREENLINES - 1, 2); @@ -412,7 +427,8 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, minipercs[i] = BSDDIALOG_MG_DONE; update = true; i++; - } else if (minibar[i].status == BSDDIALOG_MG_FAILED || perc < 0) { + } else if (minibar[i].status == BSDDIALOG_MG_FAILED || + perc < 0) { minipercs[i] = BSDDIALOG_MG_FAILED; update = true; } else /* perc >= 0 */ @@ -421,326 +437,280 @@ bsddialog_progressview (struct bsddialog_conf *conf, const char *text, int rows, free(minilabels); free(minipercs); - return (output); + return (retval); +} + +static int +rangebox_redraw(struct dialog *d, bool redraw, struct bar *b, int *bigchange) +{ + if (redraw) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) /* doupdate() in main loop */ + return (BSDDIALOG_ERROR); + if (redraw) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, HBOX + HBUTTONS); + + b->w = WBAR(d); + *bigchange = MAX(1, b->w / 10); + update_barbox(d, b, true); + b->toupdate = true; + + return (0); } int bsddialog_rangebox(struct bsddialog_conf *conf, const char *text, int rows, int cols, int min, int max, int *value) { - bool loop, buttupdate, barupdate; - int y, x, h, w; - int input, currvalue, output, sizebar, bigchange, positions; - float perc; - WINDOW *widget, *textpad, *bar, *shadow; - struct buttons bs; - - if (value == NULL) - RETURN_ERROR("*value cannot be NULL"); + bool loop; + int currvalue, retval, bigchange, positions; + wint_t input; + struct bar b; + struct dialog d; + CHECK_PTR(value); if (min >= max) - RETURN_ERROR("min >= max"); + RETURN_FMTERROR("min (%d) >= max (%d)", min, max); + if (*value < min) + RETURN_FMTERROR("value (%d) < min (%d)", *value, min); + if (*value > max) + RETURN_FMTERROR("value (%d) > max (%d)", *value, max); currvalue = *value; positions = max - min + 1; - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); - if (bar_checksize(h, w, &bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + b.y = b.x = 1; + b.fmt = "%d"; + if (rangebox_redraw(&d, false, &b, &bigchange) != 0) return (BSDDIALOG_ERROR); - doupdate(); - - prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, x+w-1-TEXTHMARGIN); - - sizebar = w - HBORDERS - (2 * BARPADDING) - 2; - bigchange = MAX(1, sizebar/10); - - bar = new_boxed_window(conf, y + h - 6, x + 1 + BARPADDING, 3, - sizebar + 2, RAISED); - - loop = buttupdate = barupdate = true; + loop = true; while (loop) { - if (buttupdate) { - draw_buttons(widget, bs, true); - wrefresh(widget); - buttupdate = false; + if (b.toupdate) { + b.perc = ((float)(currvalue - min)*100) / (positions-1); + b.label = currvalue; + draw_bar(&b); } - if (barupdate) { - perc = ((float)(currvalue - min)*100) / (positions-1); - draw_bar(bar, 1, 1, sizebar, perc, true, currvalue); - barupdate = false; - wrefresh(bar); - } - - input = getch(); + doupdate(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; - *value = currvalue; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - buttupdate = true; + case KEY_CTRL('n'): + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; + case KEY_CTRL('p'): case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - buttupdate = true; - } - break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttupdate = true; - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; case KEY_HOME: currvalue = max; - barupdate = true; + b.toupdate = true; break; case KEY_END: currvalue = min; - barupdate = true; + b.toupdate = true; break; case KEY_NPAGE: currvalue -= bigchange; if (currvalue < min) currvalue = min; - barupdate = true; + b.toupdate = true; break; case KEY_PPAGE: currvalue += bigchange; if (currvalue > max) currvalue = max; - barupdate = true; + b.toupdate = true; break; + case '-': case KEY_UP: - if (currvalue < max) { - currvalue++; - barupdate = true; + if (currvalue > min) { + currvalue--; + b.toupdate = true; } break; + case '+': case KEY_DOWN: - if (currvalue > min) { - currvalue--; - barupdate = true; + if (currvalue < max) { + currvalue++; + b.toupdate = true; } break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) + if (f1help_dialog(conf) != 0) return (BSDDIALOG_ERROR); - if (bar_autosize(conf, rows, cols, &h, &w, text, - &bs) != 0) + if (rangebox_redraw(&d, true, &b, &bigchange) != 0) return (BSDDIALOG_ERROR); - if (bar_checksize(h, w, &bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget,y, x, h, w, - textpad, text, &bs, true) != 0) + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (rangebox_redraw(&d, true, &b, &bigchange) != 0) return (BSDDIALOG_ERROR); - - doupdate(); - - sizebar = w - HBORDERS - (2 * BARPADDING) - 2; - bigchange = MAX(1, sizebar/10); - wclear(bar); - mvwin(bar, y + h - 6, x + 1 + BARPADDING); - wresize(bar, 3, sizebar + 2); - draw_borders(conf, bar, 3, sizebar+2, RAISED); - - prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, - x+w-1-TEXTHMARGIN); - - barupdate = true; break; default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); loop = false; } } } - delwin(bar); - end_dialog(conf, shadow, widget, textpad); + *value = currvalue; + + delwin(b.win); + end_dialog(&d); - return (output); + return (retval); } -int -bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int sec) +static int pause_redraw(struct dialog *d, bool redraw, struct bar *b) { - bool loop, buttupdate, barupdate; - int output, y, x, h, w, input, tout, sizebar; - float perc; - WINDOW *widget, *textpad, *bar, *shadow; - struct buttons bs; - - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (bar_autosize(conf, rows, cols, &h, &w, text, &bs) != 0) - return (BSDDIALOG_ERROR); - if (bar_checksize(h, w, &bs) != 0) + if (redraw) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, HBOX, MIN_WBOX, NULL) != 0) return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (draw_dialog(d) != 0) /* doupdate() in main loop */ return (BSDDIALOG_ERROR); + if (redraw) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, HBOX + HBUTTONS); - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) - return (BSDDIALOG_ERROR); + b->w = WBAR(d); + update_barbox(d, b, true); + b->toupdate = true; - doupdate(); - - prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, x+w-1-TEXTHMARGIN); + return (0); +} - sizebar = w - HBORDERS - (2 * BARPADDING) - 2; - bar = new_boxed_window(conf, y + h - 6, x + 1 + BARPADDING, 3, - sizebar + 2, RAISED); +int +bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *seconds) +{ + bool loop; + int retval, tout; + wint_t input; + struct bar b; + struct dialog d; + + CHECK_PTR(seconds); + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if ((b.win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW bar"); + b.y = b.x = 1; + b.fmt = "%d"; + if (pause_redraw(&d, false, &b) != 0) + return (BSDDIALOG_ERROR); - tout = sec; + tout = *seconds; nodelay(stdscr, TRUE); timeout(1000); - loop = buttupdate = barupdate = true; + loop = true; while (loop) { - if (barupdate) { - perc = (float)tout * 100 / sec; - draw_bar(bar, 1, 1, sizebar, perc, true, tout); - barupdate = false; - wrefresh(bar); - } - - if (buttupdate) { - draw_buttons(widget, bs, true); - wrefresh(widget); - buttupdate = false; + if (b.toupdate) { + b.perc = (float)tout * 100 / *seconds; + b.label = tout; + draw_bar(&b); } - - input = getch(); - if (input < 0) { /* timeout */ + doupdate(); + if (get_wch(&input) == ERR) { /* timeout */ tout--; if (tout < 0) { - output = BSDDIALOG_TIMEOUT; + retval = BSDDIALOG_TIMEOUT; break; } else { - barupdate = true; + b.toupdate = true; continue; } } switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - buttupdate = true; + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - buttupdate = true; - } - break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttupdate = true; - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (bar_autosize(conf, rows, cols, &h, &w, text, - &bs) != 0) - return (BSDDIALOG_ERROR); - if (bar_checksize(h, w, &bs) != 0) + if (f1help_dialog(conf) != 0) return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (pause_redraw(&d, true, &b) != 0) return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget,y, x, h, w, - textpad, text, &bs, true) != 0) + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (pause_redraw(&d, true, &b) != 0) return (BSDDIALOG_ERROR); - - doupdate(); - - sizebar = w - HBORDERS - (2 * BARPADDING) - 2; - wclear(bar); - mvwin(bar, y + h - 6, x + 1 + BARPADDING); - wresize(bar, 3, sizebar + 2); - draw_borders(conf, bar, 3, sizebar+2, LOWERED); - - prefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-7, - x+w-1-TEXTHMARGIN); - - barupdate = true; break; default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); loop = false; } } } - nodelay(stdscr, FALSE); - delwin(bar); - end_dialog(conf, shadow, widget, textpad); + *seconds = MAX(tout, 0); - return (output); -}
\ No newline at end of file + delwin(b.win); + end_dialog(&d); + + return (retval); +} diff --git a/contrib/bsddialog/lib/bsddialog.3 b/contrib/bsddialog/lib/bsddialog.3 index 38500b4da6ca..bbd756661a78 100644 --- a/contrib/bsddialog/lib/bsddialog.3 +++ b/contrib/bsddialog/lib/bsddialog.3 @@ -1,5 +1,5 @@ .\" -.\" Copyright (c) 2021-2022 Alfonso Sabato Siciliano +.\" Copyright (c) 2021-2025 Alfonso Sabato Siciliano .\" .\" Redistribution and use in source and binary forms, with or without .\" modification, are permitted provided that the following conditions @@ -22,13 +22,16 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd February 9, 2022 +.Dd June 22, 2025 .Dt BSDDIALOG 3 .Os .Sh NAME .Nm bsddialog_backtitle , -.Nm bsddialog_clearterminal , +.Nm bsddialog_backtitle_rf , +.Nm bsddialog_calendar , +.Nm bsddialog_clear , .Nm bsddialog_color , +.Nm bsddialog_color_attrs , .Nm bsddialog_checklist , .Nm bsddialog_datebox , .Nm bsddialog_end , @@ -36,9 +39,12 @@ .Nm bsddialog_gauge , .Nm bsddialog_geterror , .Nm bsddialog_get_theme , +.Nm bsddialog_hascolors , .Nm bsddialog_infobox , .Nm bsddialog_init , +.Nm bsddialog_init_notheme , .Nm bsddialog_initconf , +.Nm bsddialog_inmode , .Nm bsddialog_menu , .Nm bsddialog_mixedgauge , .Nm bsddialog_mixedlist , @@ -46,6 +52,7 @@ .Nm bsddialog_pause , .Nm bsddialog_radiolist , .Nm bsddialog_rangebox , +.Nm bsddialog_refresh , .Nm bsddialog_set_theme , .Nm bsddialog_set_default_theme , .Nm bsddialog_textbox , @@ -59,6 +66,18 @@ .Ft int .Fn bsddialog_backtitle "struct bsddialog_conf *conf" "const char *backtitle" .Ft int +.Fn bsddialog_backtitle_rf "struct bsddialog_conf *conf" "const char *backtitle" +.Ft int +.Fo bsddialog_calendar +.Fa "struct bsddialog_conf *conf" +.Fa "const char *text" +.Fa "int rows" +.Fa "int cols" +.Fa "unsigned int *year" +.Fa "unsigned int *month" +.Fa "unsigned int *day" +.Fc +.Ft int .Fo bsddialog_checklist .Fa "struct bsddialog_conf *conf" .Fa "const char *text" @@ -69,17 +88,17 @@ .Fa "struct bsddialog_menuitem *items" .Fa "int *focusitem" .Fc +.Ft void +.Fn bsddialog_clear "unsigned int y" .Ft int -.Fn bsddialog_clearterminal "void" -.Ft int -.Fo bsddialog_datebox" +.Fo bsddialog_datebox .Fa "struct bsddialog_conf *conf" .Fa "const char *text" .Fa "int rows" .Fa "int cols" -.Fa "unsigned int *yy" -.Fa "unsigned int *mm" -.Fa "unsigned int *dd" +.Fa "unsigned int *year" +.Fa "unsigned int *month" +.Fa "unsigned int *day" .Fc .Ft int .Fn bsddialog_end "void" @@ -92,6 +111,7 @@ .Fa "unsigned int formrows" .Fa "unsigned int nitems" .Fa "struct bsddialog_formitem *items" +.Fa "int *focusitem" .Fc .Ft int .Fo bsddialog_gauge @@ -102,6 +122,7 @@ .Fa "unsigned int perc" .Fa "int fd" .Fa "const char *sep" +.Fa "const char *end" .Fc .Ft const char * .Fn bsddialog_geterror "void" @@ -115,6 +136,10 @@ .Ft int .Fn bsddialog_init "void" .Ft int +.Fn bsddialog_init_notheme "void" +.Ft bool +.Fn bsddialog_inmode "void" +.Ft int .Fn bsddialog_initconf "struct bsddialog_conf *conf" .Ft int .Fo bsddialog_menu @@ -163,7 +188,7 @@ .Fa "const char *text" .Fa "int rows" .Fa "int cols" -.Fa "unsigned int seconds" +.Fa "unsigned int *seconds" .Fc .Ft int .Fo bsddialog_radiolist @@ -186,6 +211,8 @@ .Fa "int max" .Fa "int *value" .Fc +.Ft void +.Fn bsddialog_refresh "void" .Ft int .Fo bsddialog_textbox .Fa "struct bsddialog_conf *conf" @@ -218,7 +245,16 @@ .Fa "unsigned int flags" .Fc .Ft int +.Fo bsddialog_color_attrs +.Fa "int color" +.Fa "enum bsddialog_color *foreground" +.Fa "enum bsddialog_color *background" +.Fa "unsigned int *flags" +.Fc +.Ft int .Fn bsddialog_get_theme "struct bsddialog_theme *theme" +.Ft bool +.Fn bsddialog_hascolors "void" .Ft int .Fn bsddialog_set_default_theme "enum bsddialog_default_theme theme" .Ft int @@ -226,8 +262,7 @@ .Sh DESCRIPTION The .Nm bsddialog -library provides an API to build Text User Interface dialogs and widgets: to -display messages, to get input and to inform about a computation status. +library provides an API to build Text User Interface dialogs and widgets. .Pp .Fn bsddialog_init initializes the library, the only functions that can be called before is @@ -235,53 +270,85 @@ initializes the library, the only functions that can be called before is described later. After the initialization the input and output should be handled via the library API. +.Pp +.Fn bsddialog_init_notheme +is equivalent to +.Fn bsddialog_init +except it does not set the default graphical theme; see +.Sx Theme +subsection to set a theme explicitly. +.Pp .Fn bsddialog_end restores the screen like before -.Fn bsddialog_init , -then it is not possible to use the library functions. +.Fn bsddialog_init . +After the call is not possible to use the library functions. .Pp -.Fn bsddialog_error -returns a string to describe the last error, it should be called after a -.Dv BSDDIALOG_ERROR -returned value. -.Fn bsddialog_clearterminal -clears the screen. -.Fn bsddialog_backtitle +.Fn bsddialog_inmode +returns +.Dv true +after +.Fn bsddialog_init +or +.Fn bsddialog_init_notheme +and before +.Fn bsddialog_end , +.Dv false +otherwise. +.Pp +.Fn bsddialog_backtitle_rf prints .Fa backtitle -on the top of the screen, it is possible to set +on the top of the screen. +The function handles .Fa conf.ascii_lines and -.Fa conf.no_lines ; -.Fa conf -is described later. +.Fa conf.no_lines +described later. .Pp -Each -.Fa char* -argument has to be a well terminated string, can be empty -.Pq Dq -but not -.Dv NULL . +.Fn bsddialog_backtitle +is like +.Fn bsddialog_backtitle_rf +but it does not update the screen, using if a dialog is built later. +.Pp +.Fn bsddialog_error +returns a string to describe the last error. +The function should be called after a +.Dv BSDDIALOG_ERROR +returned value. +.Pp +.Fn bsddialog_clear +clears the screen from +.Fa y . +.Pp +.Fn bsddialog_refresh +useful to refresh the screen after a terminal mode change, see +.Xr terminfo 5 . .Ss Dialogs The dialogs have common arguments. .Fa text is a string printed inside the dialog. +Each +.Fa char* +parameter can be a multibyte character string depending on current locale, see +.Xr setlocale 3 . .Fa rows and .Fa cols -are height and width, their value can be between 2 and the screen size, +are height and width, their value can be a fixed size, .Dv BSDDIALOG_AUTOSIZE or .Dv BSDDIALOG_FULLSCREEN . .Fa conf -is a struct to customize the dialog, it does not set global properties to the -library. +is a struct to customize the current dialog, it does not set global properties +to the library. .Pp .Bd -literal -offset indent -compact struct bsddialog_conf { bool ascii_lines; unsigned int auto_minheight; unsigned int auto_minwidth; + unsigned int auto_topmargin; + unsigned int auto_downmargin; const char *bottomtitle; bool clear; int *get_height; @@ -298,22 +365,29 @@ struct bsddialog_conf { const char *f1_message; } key; struct { - bool highlight; + unsigned int cols_per_row; + bool escape; unsigned int tablen; } text; struct { bool align_left; bool no_desc; bool no_name; - bool on_without_ok; bool shortcut_buttons; } menu; struct { - bool enable_wchar; - int securech; - bool value_without_ok; + char securech; + char *securembch; + bool value_wchar; } form; struct { + const char *format; + } date; + struct { + bool always_active; + const char *left1_label; + const char *left2_label; + const char *left3_label; bool without_ok; const char *ok_label; bool with_extra; @@ -323,8 +397,9 @@ struct bsddialog_conf { bool default_cancel; bool with_help; const char *help_label; - const char *generic1_label; - const char *generic2_label; + const char *right1_label; + const char *right2_label; + const char *right3_label; const char *default_label; } button; }; @@ -343,8 +418,25 @@ minimum width if .Fa cols is .Dv BSDDIALOG_AUTOSIZE . +.It Fa conf.auto_topmargin +top margin if +.Fa rows +is +.Dv BSDDIALOG_AUTOSIZE +or +.Dv BSDDIALOG_FULLSCREEN , +.Fa conf.y +has to be +.Dv BSDDIALOG_CENTER . +.It Fa conf.auto_downmargin +down margin if +.Fa rows +is +.Dv BSDDIALOG_AUTOSIZE +or +.Dv BSDDIALOG_FULLSCREEN . .It Fa conf.bottomtitle -subtitle at the dialog bottom side. +dialog subtitle. .It Fa conf.clear hide the dialog at exit. .It Fa conf.get_height @@ -362,31 +454,35 @@ draw shadow. .It Fa conf.sleep wait before to return, the value is in seconds. .It Fa conf.title -title at the top dialog side. +dialog title. .It Fa conf.y -vertical position, 0 is top screen size, can be +dialog vertical position, 0 is top screen, can be .Dv BSDDIALOG_CENTER . .It Fa conf.x -horizontal position, 0 is left screen side, can be +dialog horizontal position, 0 is left screen, can be .Dv BSDDIALOG_CENTER . .El .Pp .Bl -column -compact .It Fa conf.key.enable_esc -enables +enable .Dv ESC key to close the dialog. .It Fa conf.key.f1_file -file to open if F1 is pressed. +open a file in a textbox if F1 is pressed. .It Fa conf.key.f1_message -message to display if F1 is pressed. +build a msgbox with message if F1 is pressed. .El .Pp -.Fa conf.text.highlight -enables highlights for -.Fa text , -properly the following sequences are considered escapes: .Bl -column -compact +.It Fa conf.text.cols_per_row +Try to set the number of columns for a row of +.Fa text +with autosizing, default +.Dv 10 . +.It Fa conf.text.escape +enable escapes in +.Fa text : .It Dq \eZ0 black. .It Dq \eZ1 @@ -403,25 +499,55 @@ magenta. cyan. .It Dq \eZ7 white. -.It Dq \eZr -reverse colors between foreground and background. -.It Dq \eZR -disable reverse. .It Dq \eZb bold. .It Dq \eZB disable bold. +.It Dq \eZd +Half bright. +.It Dq \eZD +disable half bright. +.It Dq \eZk +Blink. +.It Dq \eZK +disable blinking. +.It Dq \eZr +reverse foreground and background. +.It Dq \eZR +disable reverse. +.It Dq \eZs +Highlight. +.It Dq \eZS +disable highlighting. .It Dq \eZu underline. .It Dq \eZU disable underline. .It Dq \eZn disable each customization. +.It Fa conf.text.tablen +tab length for +.Fa text +argument and +.Fn bsddialog_textbox +function. .El -.Fa conf.text.tablen -tab length. .Pp .Bl -column -compact +.It Fa conf.button.always_active +buttons always active, avoiding focus switch between buttons and input fields or +input boxes in +.Fn bsddialog_form , +.Fn bsddialog_datebox , +.Fn bsddialog_calendar +and +.Fn bsddialog_timebox . +.It Fa conf.button.left1_label +add a button with the specified label. +.It Fa conf.button.left2_label +add a button with the specified label. +.It Fa conf.button.left3_label +add a button with the specified label. .It Fa conf.button.without_ok disable OK button. .It Fa conf.button.ok_label @@ -440,9 +566,11 @@ on startup focus on the Cancel button. add Help button. .It Fa conf.button.help_label set a label for Help button. -.It Fa conf.button.generic1_label +.It Fa conf.button.right1_label +add a button with the specified label. +.It Fa conf.button.right2_label add a button with the specified label. -.It Fa conf.button.generic2_label +.It Fa conf.button.right3_label add a button with the specified label. .It Fa conf.button.default_label focus on the button with the specified label. @@ -458,148 +586,58 @@ to true, and .Fa conf.x to -.Dv BSDDIALOG_CENTER . -.Pp -.Fn bsddialog_infobox -builds a dialog without buttons and returns instantly. -.Fn bsddialog_msgbox -builds a dialog with OK button. -.Fn bsddialog_yesno -provides a dialog for a -.Dq Yes-No Question , -the labels on buttons are Yes and No. -.Pp -.Fn bsddialog_pause -builds a dialog waiting until the timeout in -.Fa seconds -expires or a button is pressed. +.Dv BSDDIALOG_CENTER , +.Fa conf.text.cols_per_row +to +.Dv 10 . .Pp -.Fn bsddialog_datebox -builds a dialog to select a date, -.Fa yy , -.Fa mm , +.Fn bsddialog_calendar +builds a dialog to select a date. +.Fa year , +.Fa month , and -.Fa dd +.Fa day are default values on startup, selected date at exit. -.Fn bsddialog_timebox -builds a dialog to choose a time, -.Fa hh , -.Fa mm , -and -.Fa ss -are default values on startup, selected time at exit. -.Pp -.Fn bsddialog_checklist , -.Fn bsddialog_menu -and -.Fn bsddialog_radiolist -build dialogs to select some item from a list via the SPACE key, an item is -defined like: .Pp -.Bd -literal -offset indent -compact -struct bsddialog_menuitem { - const char *prefix; - bool on; - unsigned int depth; - const char *name; - const char *desc; - const char *bottomdesc; -}; -.Ed +.Fn bsddialog_checklist +builds dialogs to select some item from a list via the SPACE key, can be +customized by +.Fa conf.menu.* . +See +.Fn bsddialog_menu . .Pp -.Fa prefix , -.Fa name -and -.Fa desc -are strings to describe the item and are printed on its row, -.Fa bottomdesc -is printed on the bottom side of the screen, -.Fa depth -is a margin between the -.Fa prefix -and -.Fa name -useful to implement a -.Dq treeview, -.Fa on -is set to -.Dv true -if the item is selected, -.Dv false -otherwise. -.Fa items -is an array of items of -.Fa nitem -elements, -.Fa menurows -specifies the graphical fixed height of the list, if -.Fa cols -is set to -.Dv BSDDIALOG_AUTOSIZE -.Fa menurows -specifies a maximum value. -Finally, if not -.Dv NULL , -.Fa focusitem -specifies the default item on startup and the last focused item at exit, could -be a negative value if no item is focused. -.Pp -.Fn bsddialog_mixedlist -builds a dialog with collections of checklists, radiolists and separators. -A collection is a set defined like: -.Pp -.Bd -literal -offset indent -compact -enum bsddialog_grouptype { - BSDDIALOG_CHECKLIST, - BSDDIALOG_RADIOLIST, - BSDDIALOG_SEPARATOR, -}; - -struct bsddialog_menugroup { - enum bsddialog_grouptype type; - unsigned int nitems; - struct bsddialog_menuitem *items; -}; -.Ed -.Pp -.Fa groups -is an array of sets of -.Fa ngroups -elements. -.Fa menurows -is the graphical height size for the list. -If not -.Dv NULL , -.Fa focuslist -and -.Fa focusitem -specify the default item on startup and the last focused item at exit, could be -a negative value if no item is focused. -.Pp -.Fn bsddialog_checklist , -.Fn bsddialog_menu , -.Fn bsddialog_mixedlist +.Fn bsddialog_datebox +builds a dialog to select a date. +.Fa year , +.Fa month , and -.Fn bsddialog_radiolist -can be costomizated by: +.Fa day +are default values on startup, selected date at exit. +The function can be customized by: .Bl -column -compact -.It Fa conf.menu.align_left -aligns items to left, default center. -.It Fa conf.menu.no_desc -hide description. -.It Fa conf.menu.no_name -hide names. -.It Fa conf.menu.on_without_ok -set items -.Fa on -also if the OK button is not pressed. -.It Fa conf.menu.shortcut_buttons -enable shortcut keys on buttons, default on items. +.It Fa conf.date.format +date format user interface, possible values: +.Dq d/m/y , +.Dq m/d/y , +.Dq y/m/d . .El .Pp .Fn bsddialog_form -builds a dialog to display a list of items to get strings in input, an item is -defined like: +builds a dialog to display an array of +.Fa items +of +.Fa nitems +elements to get input strings. +.Fa formrows +is the graphical height for the items inside the dialog, +.Dv 0 +for autosizing. +If not +.Dv NULL +.Fa focusitem +is the default item index on startup and the last focused item at exit, a +negative value if no item is focused. +An item is defined like: .Pp .Bd -literal -offset indent -compact struct bsddialog_formitem { @@ -621,7 +659,7 @@ struct bsddialog_formitem { .Ed .Pp .Fa label -describes the request, it is printed at the position +is a string to describe the request at the position .Fa ylabel and .Fa xlabel . @@ -632,77 +670,142 @@ and .Fa fieldlen is its graphical width, while .Fa maxvalelen -is the maximum length of the input string, +is the maximum number of characters of the input string. .Fa init -is the default value. -If the OK button is pressed +is the default field value. +If no error occurs .Fa value -is the allocated memory with the current field string. +is the allocated memory with the current field string at exit, its size depends +on the current locale. .Fa flags -is an OR value to set the -.Dv BSDDIALOG_FIELDHIDDEN -and -.Dv BSDDIALOG_FIELDREADONLY -flags for the field. +is an OR value to set the field: +.Dv BSDDIALOG_FIELDHIDDEN , +.Dv BSDDIALOG_FIELDREADONLY , +.Dv BSDDIALOG_FIELDNOCOLOR , +.Dv BSDDIALOG_FIELDCURSOREND , +.Dv BSDDIALOG_FIELDEXTEND , +.Dv BSDDIALOG_FIELDSINGLEBYTE . .Fa bottomdesc -is printed on the bottom side of the screen if the item is focused. -.Fa items -is an array of items of -.Fa nitems -elements, -.Fa formrows -specifies the graphical fixed height for the items list; -.Fa ylabel -and -.Fa yfield -have to be between 1 and -.Fa formrows . +is printed at bottom screen if the item is focused. .Pp .Fn bsddialog_form can be customized by: .Bl -column -compact -.It Fa conf.form.enable_wchar -enables characters greater than 127 in the field, -.Fa value -is a pointer to allocated memory for a -.Em wchar_t -string. .It Fa conf.form.securech -charachter to hide the input -with +charachter to hide the input with .Dv BSDDIALOG_FIELDHIDDEN . -.It Fa conf.form.value_without_ok -allocate memory and set +.It Fa conf.form.securembch +multibyte charachter to hide the input with +.Dv BSDDIALOG_FIELDHIDDEN , +.Fa conf.form.securech +is ignored. +.It Fa conf.form.value_wchar +the allocated .Fa value -also if the OK button is not pressed. +is a +.Em wchar_t* +string. .El .Pp .Fn bsddialog_gauge -builds a dialog with a bar to shows -.Fa perc , -if the file descriptor +builds a dialog with a bar to show +.Fa perc . +If the file descriptor .Fa fd is greater or equal to 0 the dialog waits to read -.Fa separator +.Fa sep from it, then the first string replaces .Fa perc and the following strings replace .Fa text until the next -.Fa separator , +.Fa sep , the loop ends reading -.Dv EOF . +.Fa end . +.Pp +.Fn bsddialog_infobox +builds a dialog without buttons and returns instantly. +.Pp +.Fn bsddialog_menu +builds a dialog to select an item from a list via SPACE and ENTER. +An item is defined like: +.Pp +.Bd -literal -offset indent -compact +struct bsddialog_menuitem { + const char *prefix; + bool on; + unsigned int depth; + const char *name; + const char *desc; + const char *bottomdesc; +}; +.Ed +.Pp +.Fa prefix , +.Fa name +and +.Fa desc +are printed at the item row. +.Fa bottomdesc +is printed at bottom screen if the item is focused. +.Fa depth +is a margin between +.Fa prefix +and +.Fa name . +At exit +.Fa on +is set to +.Dv true +if the item is selected, +.Dv false +otherwise. +.Fa items +is an array of items of +.Fa nitem +elements. +.Fa menurows +is the graphical height of the list inside the dialog, if +.Fa cols +is +.Dv BSDDIALOG_AUTOSIZE +.Fa menurows +specifies a maximum value. +if not +.Dv NULL +.Fa focusitem +is the default item index on startup and the last focused item at exit, a +negative value if no item is focused. +.Pp +.Fn bsddialog_checklist , +.Fn bsddialog_menu , +.Fn bsddialog_mixedlist +and +.Fn bsddialog_radiolist +can be customized by: +.Bl -column -compact +.It Fa conf.menu.align_left +align items to left, default center. +.It Fa conf.menu.no_desc +hide items description. +.It Fa conf.menu.no_name +hide items name, mutually exclusive with +.Fa conf.menu.no_desc . +.It Fa conf.menu.shortcut_buttons +enable shortcut keys on buttons, default on items. +.El .Pp .Fn bsddialog_mixedgauge -draws a main bar with the +builds a dialog with a main bar with the .Fa mainperc percentage and .Fa nminibars each one with a .Fa minilabel and a +.Fa miniperc . .Fa miniperc -with a value between 0 and 100 or +can be: a positive value to print a bar with a percentace, a negative constant .Dv BSDDIALOG_MG_SUCCEEDED , .Dv BSDDIALOG_MG_FAILED , .Dv BSDDIALOG_MG_PASSED , @@ -711,11 +814,69 @@ with a value between 0 and 100 or .Dv BSDDIALOG_MG_DONE , .Dv BSDDIALOG_MG_SKIPPED , .Dv BSDDIALOG_MG_INPROGRESS , -.Dv BSDDIALOG_MG_BLANK , -.Dv BSDDIALOG_MG_NA -or +.Dv BSDDIALOG_MG_BLANK +to hide +.Fa miniperc , +.Dv BSDDIALOG_MG_NA , .Dv BSDDIALOG_MG_PENDING -to print a descriptive string. +to print a descriptive string, otherwise +.Dq "UNKNOWN" +is printed. +.Pp +.Fn bsddialog_mixedlist +builds a dialog with collections of checklists, radiolists and separators. +A collection is a set defined like: +.Pp +.Bd -literal -offset indent -compact +enum bsddialog_menutype { + BSDDIALOG_CHECKLIST, + BSDDIALOG_RADIOLIST, + BSDDIALOG_SEPARATOR, +}; + +struct bsddialog_menugroup { + enum bsddialog_menutype type; + unsigned int nitems; + struct bsddialog_menuitem *items; + unsigned int min_on; /* unused for now */ +}; +.Ed +.Pp +.Fa groups +is an array of sets of +.Fa ngroups +elements. +.Fa menurows +is the graphical height size for the list. +If not +.Dv NULL , +.Fa focuslist +and +.Fa focusitem +specify the default item on startup and the last focused item at exit, could be +a negative value if no item is focused. +The dialog can be customized by +.Fa conf.menu.* , +see +.Fn bsddialog_menu . +.Pp +.Fn bsddialog_msgbox +builds a dialog with OK button. +.Pp +.Fn bsddialog_pause +builds a dialog waiting until the timeout in +.Fa seconds +expires or a button is pressed. +At exit +.Fa seconds +is set like remaining time. +.Pp +.Fn bsddialog_radiolist +builds dialogs to select at most an item from a list via the SPACE key, can be +customized by +.Fa conf.menu.* . +See +.Fn bsddialog_menu . .Pp .Fn bsddialog_rangebox to select a value between @@ -724,17 +885,49 @@ and .Fa max . .Fa value is the default value on startup and the selected value at exit. -The current value is printed inside a bar, the keys UP, DOWN, HOME, END, PAGEUP -and PAGEDOWN can change it. +The current value is printed inside a bar. .Pp .Fn bsddialog_textbox opens and prints -.Fa file -in a dialog, the UP, DOWN, HOME, END, PAGEUP and PAGEDOWN keys are availble to -navigate the file. -OK button is renamed EXIT. +.Fa file . +TAB key changes button. +Extra keys 0, h, l, k, j are available to navigate the text. +.Dq OK +button is renamed +.Dq EXIT . +.Pp +.Fn bsddialog_timebox +builds a dialog to choose a time. +.Fa hh , +.Fa mm , +and +.Fa ss +are default values on startup, selected time at exit. +.Pp +.Fn bsddialog_yesno +provides a dialog for a +.Dq Yes-No Question , +the labels on buttons are +.Dq Yes +and +.Dq &No . +.Ss Keys +.Bl -tag -width Ds +.It Ctrl-l +Redraw the dialog. +.It F1 +Refer to +.Fa conf.key.f1_file +and +.Fa conf.key.f1_message . +.It SPACE +Select menu item. +.It UP DOWN LEFT RIGHT - + HOME END PAGEUP PAGEDOWN Ctrl-p Ctrl-n TAB +Navigate elements and set value, depending on the dialog. +.El .Ss Theme -The graphical properties are global to the library, they are represented by +The graphical properties are global to the library. +They are represented by .Fa struct bsddialog_theme and can be customized at runtime via the .In bsddialog_theme.h @@ -747,8 +940,8 @@ struct bsddialog_theme { } screen; struct { int color; - unsigned int h; - unsigned int w; + unsigned int y; + unsigned int x; } shadow; struct { int color; @@ -760,56 +953,56 @@ struct bsddialog_theme { int arrowcolor; } dialog; struct { + int f_prefixcolor; + int prefixcolor; int f_selectorcolor; int selectorcolor; int f_namecolor; int namecolor; int f_desccolor; int desccolor; - int namesepcolor; - int descsepcolor; int f_shortcutcolor; int shortcutcolor; + int bottomdesccolor; + int sepnamecolor; + int sepdesccolor; } menu; struct { int f_fieldcolor; int fieldcolor; int readonlycolor; + int bottomdesccolor; } form; struct { int f_color; int color; } bar; struct { - unsigned int hmargin; - int leftdelim; - int rightdelim; - int delimcolor; + unsigned int minmargin; + unsigned int maxmargin; + char leftdelim; + char rightdelim; int f_delimcolor; - int color; + int delimcolor; int f_color; - int shortcutcolor; + int color; int f_shortcutcolor; + int shortcutcolor; } button; }; .Ed .Pp A member with the .Dq f_ -prefix refers to an element with focus. +refers to focus when an element can be in selected or not selected state. .Pp -.Fn bsddialog_get_theme -sets -.Fa theme -like the current theme. -.Pp -A color can be set by the value returned by -.Fn bsddialog_color , -Possible values for -.Fa background -and -.Fa foreground -are: +.Fn bsddialog_color +generates and returns a color to set a +.Fa struct bsddialog_theme +color member. +An +.Fa enum bsddialog_color +can be: .Dv BSDDIALOG_BLACK , .Dv BSDDIALOG_RED , .Dv BSDDIALOG_GREEN , @@ -817,29 +1010,51 @@ are: .Dv BSDDIALOG_BLUE , .Dv BSDDIALOG_MAGENTA , .Dv BSDDIALOG_CYAN , -and -.Dv BSDDIALOG_WHITE , +.Dv BSDDIALOG_WHITE . .Fa flags -specifies OR-flags, possible values: +is an OR value: +.Dv BSDDIALOG_BLINK , .Dv BSDDIALOG_BOLD , -.Dv BSDDIALOG_REVERSE -and +.Dv BSDDIALOG_HALFBRIGHT , +.Dv BSDDIALOG_HIGHLIGHT , +.Dv BSDDIALOG_REVERSE , .Dv BSDDIALOG_UNDERLINE . .Pp +.Fn bsddialog_color_attrs +sets, if not NULL, +.Fa foreground , +.Fa background , +.Fa flags , +like the properties of +.Fa color , +see +.Fn bsddialog_color . +.Pp +.Fn bsddialog_get_theme +sets +.Fa theme +like the current runtime theme. +.Pp +.Fn bsddialog_hascolors +returns +.Dv true +if the terminal provides colors, +.Dv false +otherwise. +.Pp .Fn bsddialog_set_theme sets .Fa theme -like current theme, the changes takes effect only for dialogs built after the -call. +like current runtime theme. +Changes take effect only for dialogs built after +the call. .Pp -The library provides predefined themes: +.Fn bsddialog_set_default_theme +sets a library default theme like current theme, possible values: .Dv BSDDIALOG_THEME_BLACKWHITE , -.Dv BSDDIALOG_THEME_BSDDIALOG , -.Dv BSDDIALOG_THEME_FLAT -and -.Dv BSDDIALOG_THEME_DIALOG , -they can be set via -.Fn bsddialog_set_default_theme . +.Dv BSDDIALOG_THEME_FLAT , +.Dv BSDDIALOG_THEME_3D . +Changes take effect only for dialogs built after the call. .Sh RETURN VALUES The functions return the value .Dv BSDDIALOG_ERROR @@ -850,9 +1065,12 @@ returned: .Dv BSDDIALOG_CANCEL , .Dv BSDDIALOG_HELP , .Dv BSDDIALOG_EXTRA , -.Dv BSDDIALOG_GENERIC1 -or -.Dv BSDDIALOG_GENERIC2 . +.Dv BSDDIALOG_LEFT1 , +.Dv BSDDIALOG_LEFT2 , +.Dv BSDDIALOG_LEFT3 , +.Dv BSDDIALOG_RIGHT1 , +.Dv BSDDIALOG_RIGHT2 , +.Dv BSDDIALOG_RIGHT3 . .Dv BSDDIALOG_YES and .Dv BSDDIALOG_NO @@ -894,10 +1112,11 @@ case BSDDIALOG_YES: printf("Yes\\n"); break; case BSDDIALOG_NO - printf("NO\\n"); + printf("No\\n"); break; case BSDDIALOG_ERROR: printf("Error: %s\\n", bsddialog_geterror()); + break; } .Ed .Pp @@ -938,7 +1157,7 @@ struct bsddialog_menuitem check[2] = { struct bsddialog_menuitem sep[1] = { { "3", true, 0, "Radiolist", "(desc)", "" } }; -struct bsddialog_menuitem radio[5] = { +struct bsddialog_menuitem radio[2] = { { "4", true, 0, "Name 1", "Desc 1", "Radio Bottom Desc 1" }, { "5", false, 0, "Name 2", "Desc 2", "Radio Bottom Desc 2" } }; @@ -975,8 +1194,7 @@ for (i = 0; i < 3; i++) { .Ed .Sh SEE ALSO .Xr bsddialog 1 , -.Xr curses 3 , -.Xr ncurses 3 +.Xr curses 3 .Sh HISTORY The .Nm bsddialog @@ -985,8 +1203,4 @@ library first appeared in .Sh AUTHORS .Nm bsddialog was written by -.An Alfonso Sabato Siciliano Aq Mt alf.siciliano@gmail.com . -.Sh BUGS -.Fn bsddialog_form -does not resize the dialog after a terminal resize and does not provide -scrolling for items.
\ No newline at end of file +.An Alfonso Sabato Siciliano Aq Mt asiciliano@FreeBSD.org . diff --git a/contrib/bsddialog/lib/bsddialog.h b/contrib/bsddialog/lib/bsddialog.h index 37f9899141c0..fc59071c6fa0 100644 --- a/contrib/bsddialog/lib/bsddialog.h +++ b/contrib/bsddialog/lib/bsddialog.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -30,9 +30,9 @@ #include <stdbool.h> -#define LIBBSDDIALOG_VERSION "0.2" +#define LIBBSDDIALOG_VERSION "1.0.5" -/* Exit status */ +/* Return values */ #define BSDDIALOG_ERROR -1 #define BSDDIALOG_OK 0 #define BSDDIALOG_YES BSDDIALOG_OK @@ -42,8 +42,12 @@ #define BSDDIALOG_EXTRA 3 #define BSDDIALOG_TIMEOUT 4 #define BSDDIALOG_ESC 5 -#define BSDDIALOG_GENERIC1 6 -#define BSDDIALOG_GENERIC2 7 +#define BSDDIALOG_LEFT1 6 +#define BSDDIALOG_LEFT2 7 +#define BSDDIALOG_LEFT3 8 +#define BSDDIALOG_RIGHT1 9 +#define BSDDIALOG_RIGHT2 10 +#define BSDDIALOG_RIGHT3 11 /* Size and position */ #define BSDDIALOG_FULLSCREEN -1 @@ -64,13 +68,19 @@ #define BSDDIALOG_MG_PENDING -11 /* Form */ -#define BSDDIALOG_FIELDHIDDEN 1U -#define BSDDIALOG_FIELDREADONLY 2U +#define BSDDIALOG_FIELDHIDDEN 1U +#define BSDDIALOG_FIELDREADONLY 2U +#define BSDDIALOG_FIELDNOCOLOR 4U +#define BSDDIALOG_FIELDCURSOREND 8U +#define BSDDIALOG_FIELDEXTEND 16U +#define BSDDIALOG_FIELDSINGLEBYTE 32U struct bsddialog_conf { bool ascii_lines; unsigned int auto_minheight; unsigned int auto_minwidth; + unsigned int auto_topmargin; + unsigned int auto_downmargin; const char *bottomtitle; bool clear; int *get_height; @@ -87,22 +97,29 @@ struct bsddialog_conf { const char *f1_message; } key; struct { - bool highlight; + unsigned int cols_per_row; + bool escape; unsigned int tablen; } text; struct { bool align_left; bool no_desc; bool no_name; - bool on_without_ok; bool shortcut_buttons; } menu; struct { - bool enable_wchar; - int securech; - bool value_without_ok; + char securech; + char *securembch; + bool value_wchar; } form; struct { + const char *format; + } date; + struct { + bool always_active; + const char *left1_label; + const char *left2_label; + const char *left3_label; bool without_ok; const char *ok_label; bool with_extra; @@ -112,8 +129,9 @@ struct bsddialog_conf { bool default_cancel; bool with_help; const char *help_label; - const char *generic1_label; - const char *generic2_label; + const char *right1_label; + const char *right2_label; + const char *right3_label; const char *default_label; } button; }; @@ -127,16 +145,17 @@ struct bsddialog_menuitem { const char *bottomdesc; }; -enum bsddialog_grouptype { +enum bsddialog_menutype { BSDDIALOG_CHECKLIST, BSDDIALOG_RADIOLIST, BSDDIALOG_SEPARATOR, }; struct bsddialog_menugroup { - enum bsddialog_grouptype type; + enum bsddialog_menutype type; unsigned int nitems; struct bsddialog_menuitem *items; + unsigned int min_on; /* unused for now */ }; struct bsddialog_formitem { @@ -156,30 +175,38 @@ struct bsddialog_formitem { }; int bsddialog_init(void); +int bsddialog_init_notheme(void); +bool bsddialog_inmode(void); int bsddialog_end(void); int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle); +int bsddialog_backtitle_rf(struct bsddialog_conf *conf, const char *backtitle); int bsddialog_initconf(struct bsddialog_conf *conf); -int bsddialog_clearterminal(void); +void bsddialog_clear(unsigned int y); +void bsddialog_refresh(void); const char *bsddialog_geterror(void); /* Dialogs */ int +bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *year, unsigned int *month, unsigned int *day); + +int bsddialog_checklist(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, unsigned int nitems, struct bsddialog_menuitem *items, int *focusitem); int bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int *yy, unsigned int *mm, unsigned int *dd); + int cols, unsigned int *year, unsigned int *month, unsigned int *day); int bsddialog_form(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int formheight, unsigned int nitems, - struct bsddialog_formitem *items); + struct bsddialog_formitem *items, int *focusitem); int bsddialog_gauge(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int perc, int fd, const char *sep); + int cols, unsigned int perc, int fd, const char *sep, const char *end); int bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows, @@ -206,7 +233,7 @@ bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows, int bsddialog_pause(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int seconds); + int cols, unsigned int *seconds); int bsddialog_radiolist(struct bsddialog_conf *conf, const char *text, int rows, diff --git a/contrib/bsddialog/lib/bsddialog_progressview.h b/contrib/bsddialog/lib/bsddialog_progressview.h index 0cd9368a1040..5203b798bb07 100644 --- a/contrib/bsddialog/lib/bsddialog_progressview.h +++ b/contrib/bsddialog/lib/bsddialog_progressview.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2023 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -36,14 +36,14 @@ extern bool bsddialog_interruptprogview; extern bool bsddialog_abortprogview; -extern int bsddialog_total_progview; +extern long long int bsddialog_total_progview; struct bsddialog_fileminibar { const char *path; const char *label; int status; /* next if BSDDIALOG_MG_DONE or BSDDIALOG_MG_FAILED */ - long long size; - long long read; + long long int size; + long long int read; }; struct bsddialog_progviewconf { diff --git a/contrib/bsddialog/lib/bsddialog_theme.h b/contrib/bsddialog/lib/bsddialog_theme.h index 89381cfe28d5..77938c65b6ce 100644 --- a/contrib/bsddialog/lib/bsddialog_theme.h +++ b/contrib/bsddialog/lib/bsddialog_theme.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -29,9 +29,12 @@ #define _LIBBSDDIALOG_THEME_H_ /* color flags */ -#define BSDDIALOG_BOLD 1U -#define BSDDIALOG_REVERSE 2U -#define BSDDIALOG_UNDERLINE 4U +#define BSDDIALOG_BLINK 1U +#define BSDDIALOG_BOLD 2U +#define BSDDIALOG_HALFBRIGHT 4U +#define BSDDIALOG_HIGHLIGHT 8U +#define BSDDIALOG_REVERSE 16U +#define BSDDIALOG_UNDERLINE 32U struct bsddialog_theme { struct { @@ -39,8 +42,8 @@ struct bsddialog_theme { } screen; struct { int color; - unsigned int h; - unsigned int w; + unsigned int y; + unsigned int x; } shadow; struct { int color; @@ -52,44 +55,48 @@ struct bsddialog_theme { int arrowcolor; } dialog; struct { + int f_prefixcolor; + int prefixcolor; int f_selectorcolor; int selectorcolor; int f_namecolor; int namecolor; int f_desccolor; int desccolor; - int namesepcolor; - int descsepcolor; int f_shortcutcolor; int shortcutcolor; + int bottomdesccolor; + int sepnamecolor; + int sepdesccolor; } menu; struct { int f_fieldcolor; int fieldcolor; int readonlycolor; + int bottomdesccolor; } form; struct { int f_color; int color; } bar; struct { - unsigned int hmargin; - int leftdelim; - int rightdelim; - int delimcolor; + unsigned int minmargin; + unsigned int maxmargin; + char leftdelim; + char rightdelim; int f_delimcolor; - int color; + int delimcolor; int f_color; - int shortcutcolor; + int color; int f_shortcutcolor; + int shortcutcolor; } button; }; enum bsddialog_default_theme { + BSDDIALOG_THEME_3D, BSDDIALOG_THEME_BLACKWHITE, - BSDDIALOG_THEME_BSDDIALOG, - BSDDIALOG_THEME_FLAT, - BSDDIALOG_THEME_DIALOG + BSDDIALOG_THEME_FLAT }; enum bsddialog_color { @@ -106,7 +113,11 @@ enum bsddialog_color { int bsddialog_color(enum bsddialog_color foreground, enum bsddialog_color background, unsigned int flags); +int +bsddialog_color_attrs(int color, enum bsddialog_color *foreground, + enum bsddialog_color *background, unsigned int *flags); int bsddialog_get_theme(struct bsddialog_theme *theme); +bool bsddialog_hascolors(void); int bsddialog_set_default_theme(enum bsddialog_default_theme theme); int bsddialog_set_theme(struct bsddialog_theme *theme); diff --git a/contrib/bsddialog/lib/datebox.c b/contrib/bsddialog/lib/datebox.c new file mode 100644 index 000000000000..66f36f5f4a99 --- /dev/null +++ b/contrib/bsddialog/lib/datebox.c @@ -0,0 +1,746 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022-2025 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <curses.h> +#include <stdlib.h> +#include <string.h> + +#include "bsddialog.h" +#include "bsddialog_theme.h" +#include "lib_util.h" + +/* Calendar */ +#define MIN_YEAR_CAL 0 +#define MAX_YEAR_CAL 999999999 +#define MINHCAL 13 +#define MINWCAL 36 /* 34 calendar, 1 + 1 margins */ +/* Datebox */ +#define MIN_YEAR_DATE 0 +#define MAX_YEAR_DATE 9999 +#define MINWDATE 23 /* 3 windows and their borders */ + +#define ISLEAP(year) ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) + +static int minyear; +static int maxyear; + +static const char *m[12] = { + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" +}; + +enum operation { + UP_DAY, + DOWN_DAY, + LEFT_DAY, + RIGHT_DAY, + UP_MONTH, + DOWN_MONTH, + UP_YEAR, + DOWN_YEAR +}; + +/* private datebox item */ +struct dateitem { + enum operation up; + enum operation down; + WINDOW *win; + int width; + const char *fmt; + int *value; +}; + +static int month_days(int yy, int mm) +{ + int days; + + if (mm == 2) + days = ISLEAP(yy) ? 29 : 28; + else if (mm == 4 || mm == 6 || mm == 9 || mm == 11) + days = 30; + else + days = 31; + + return (days); +} + +static int week_day(int yy, int mm, int dd) +{ + int wd; + + dd += mm < 3 ? yy-- : yy - 2; + wd = 23*mm/9 + dd + 4 + yy/4 - yy/100 + yy/400; + wd %= 7; + + return (wd); +} + +static void +init_date(unsigned int *year, unsigned int *month, unsigned int *day, int *yy, + int *mm, int *dd) +{ + *yy = MIN(*year, (unsigned int)maxyear); + if (*yy < minyear) + *yy = minyear; + *mm = MIN(*month, 12); + if (*mm == 0) + *mm = 1; + *dd = (*day == 0) ? 1 : *day; + if (*dd > month_days(*yy, *mm)) + *dd = month_days(*yy, *mm); +} + +static void datectl(enum operation op, int *yy, int *mm, int *dd) +{ + int ndays; + + ndays = month_days(*yy, *mm); + + switch (op) { + case UP_DAY: + if (*dd > 7) + *dd -= 7; + else { + if (*mm == 1) { + *yy -= 1; + *mm = 12; + } else + *mm -= 1; + ndays = month_days(*yy, *mm); + *dd = ndays - abs(7 - *dd); + } + break; + case DOWN_DAY: + if (*dd + 7 < ndays) + *dd += 7; + else { + if (*mm == 12) { + *yy += 1; + *mm = 1; + } else + *mm += 1; + *dd = *dd + 7 - ndays; + } + break; + case LEFT_DAY: + if (*dd > 1) + *dd -= 1; + else { + if (*mm == 1) { + *yy -= 1; + *mm = 12; + } else + *mm -= 1; + *dd = month_days(*yy, *mm); + } + break; + case RIGHT_DAY: + if (*dd < ndays) + *dd += 1; + else { + if (*mm == 12) { + *yy += 1; + *mm = 1; + } else + *mm += 1; + *dd = 1; + } + break; + case UP_MONTH: + if (*mm == 1) { + *mm = 12; + *yy -= 1; + } else + *mm -= 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + case DOWN_MONTH: + if (*mm == 12) { + *mm = 1; + *yy += 1; + } else + *mm += 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + case UP_YEAR: + *yy -= 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + case DOWN_YEAR: + *yy += 1; + ndays = month_days(*yy, *mm); + if (*dd > ndays) + *dd = ndays; + break; + } + + if (*yy < minyear) { + *yy = minyear; + *mm = 1; + *dd = 1; + } + if (*yy > maxyear) { + *yy = maxyear; + *mm = 12; + *dd = 31; + } +} + +static void +drawsquare(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev, + const char *fmt, int value, bool focus) +{ + int h, l, w; + + getmaxyx(win, h, w); + draw_borders(conf, win, elev); + if (focus) { + l = 2 + w%2; + wattron(win, t.dialog.arrowcolor); + mvwhline(win, 0, w/2 - l/2, UARROW(conf), l); + mvwhline(win, h-1, w/2 - l/2, DARROW(conf), l); + wattroff(win, t.dialog.arrowcolor); + } + + if (focus) + wattron(win, t.menu.f_namecolor); + if (strchr(fmt, 's') != NULL) + mvwprintw(win, 1, 1, fmt, m[value - 1]); + else + mvwprintw(win, 1, 1, fmt, value); + if (focus) + wattroff(win, t.menu.f_namecolor); + + wnoutrefresh(win); +} + +static void +print_calendar(struct bsddialog_conf *conf, WINDOW *win, int yy, int mm, int dd, + bool active) +{ + int ndays, i, y, x, wd, h, w; + + getmaxyx(win, h, w); + wclear(win); + draw_borders(conf, win, RAISED); + if (active) { + wattron(win, t.dialog.arrowcolor); + mvwhline(win, 0, 15, UARROW(conf), 4); + mvwhline(win, h-1, 15, DARROW(conf), 4); + mvwvline(win, 3, 0, LARROW(conf), 3); + mvwvline(win, 3, w-1, RARROW(conf), 3); + wattroff(win, t.dialog.arrowcolor); + } + + mvwaddstr(win, 1, 5, "Sun Mon Tue Wed Thu Fri Sat"); + ndays = month_days(yy, mm); + y = 2; + wd = week_day(yy, mm, 1); + for (i = 1; i <= ndays; i++) { + x = 5 + (4 * wd); /* x has to be 6 with week number */ + wmove(win, y, x); + mvwprintw(win, y, x, "%2d", i); + if (i == dd) { + wattron(win, t.menu.f_namecolor); + mvwprintw(win, y, x, "%2d", i); + wattroff(win, t.menu.f_namecolor); + } + wd++; + if (wd > 6) { + wd = 0; + y++; + } + } + + wnoutrefresh(win); +} + +static int +calendar_draw(struct dialog *d, bool redraw, WINDOW *yy_win, WINDOW *mm_win, + WINDOW *dd_win) +{ + int ycal, xcal; + + if (redraw) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, MINHCAL, MINWCAL, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) /* doupdate in main loop */ + return (BSDDIALOG_ERROR); + if (redraw) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, MINHCAL + HBUTTONS); + + ycal = d->y + d->h - 15; + xcal = d->x + d->w/2 - 17; + mvwaddstr(d->widget, d->h - 16, d->w/2 - 17, "Month"); + update_box(d->conf, mm_win, ycal, xcal, 3, 17, RAISED); + mvwaddstr(d->widget, d->h - 16, d->w/2, "Year"); + update_box(d->conf, yy_win, ycal, xcal + 17, 3, 17, RAISED); + update_box(d->conf, dd_win, ycal + 3, xcal, 9, 34, RAISED); + wnoutrefresh(d->widget); + + return (0); +} + +int +bsddialog_calendar(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *year, unsigned int *month, unsigned int *day) +{ + bool loop, focusbuttons; + int retval, sel, yy, mm, dd; + wint_t input; + WINDOW *yy_win, *mm_win, *dd_win; + struct dialog d; + + CHECK_PTR(year); + CHECK_PTR(month); + CHECK_PTR(day); + minyear = MIN_YEAR_CAL; + maxyear = MAX_YEAR_CAL; + init_date(year, month, day, &yy, &mm, &dd); + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if ((yy_win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW for yy"); + wbkgd(yy_win, t.dialog.color); + if ((mm_win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW for mm"); + wbkgd(mm_win, t.dialog.color); + if ((dd_win = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW for dd"); + wbkgd(dd_win, t.dialog.color); + if (calendar_draw(&d, false, yy_win, mm_win, dd_win) != 0) + return (BSDDIALOG_ERROR); + + sel = -1; + loop = focusbuttons = true; + while (loop) { + drawsquare(conf, mm_win, RAISED, "%15s", mm, sel == 0); + drawsquare(conf, yy_win, RAISED, "%15d", yy, sel == 1); + print_calendar(conf, dd_win, yy, mm, dd, sel == 2); + doupdate(); + + if (get_wch(&input) == ERR) + continue; + switch(input) { + case KEY_ENTER: + case 10: /* Enter */ + if (focusbuttons || conf->button.always_active) { + retval = BUTTONVALUE(d.bs); + loop = false; + } + break; + case 27: /* Esc */ + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } + break; + case '\t': /* TAB */ + if (focusbuttons) { + d.bs.curr++; + if (d.bs.curr >= (int)d.bs.nbuttons) { + focusbuttons = false; + sel = 0; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else { + sel++; + if (sel > 2) { + focusbuttons = true; + sel = -1; + d.bs.curr = 0; + } + } + DRAW_BUTTONS(d); + break; + case KEY_CTRL('n'): + case KEY_RIGHT: + if (focusbuttons) { + d.bs.curr++; + if (d.bs.curr >= (int)d.bs.nbuttons) { + focusbuttons = false; + sel = 0; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else if (sel == 2) { + datectl(RIGHT_DAY, &yy, &mm, &dd); + } else { /* Month or Year*/ + sel++; + } + DRAW_BUTTONS(d); + break; + case KEY_CTRL('p'): + case KEY_LEFT: + if (focusbuttons) { + d.bs.curr--; + if (d.bs.curr < 0) { + focusbuttons = false; + sel = 2; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else if (sel == 2) { + datectl(LEFT_DAY, &yy, &mm, &dd); + } else if (sel == 1) { + sel = 0; + } else { /* sel = 0, Month */ + focusbuttons = true; + sel = -1; + d.bs.curr = 0; + } + DRAW_BUTTONS(d); + break; + case KEY_UP: + if (focusbuttons) { + sel = 2; + focusbuttons = false; + d.bs.curr = conf->button.always_active ? 0 : -1; + DRAW_BUTTONS(d); + } else if (sel == 0) { + datectl(UP_MONTH, &yy, &mm, &dd); + } else if (sel == 1) { + datectl(UP_YEAR, &yy, &mm, &dd); + } else { /* sel = 2 */ + datectl(UP_DAY, &yy, &mm, &dd); + } + break; + case KEY_DOWN: + if (focusbuttons) { + break; + } else if (sel == 0) { + datectl(DOWN_MONTH, &yy, &mm, &dd); + } else if (sel == 1) { + datectl(DOWN_YEAR, &yy, &mm, &dd); + } else { /* sel = 2 */ + datectl(DOWN_DAY, &yy, &mm, &dd); + } + break; + case '-': + if (focusbuttons) { + break; + } else if (sel == 0) { + datectl(UP_MONTH, &yy, &mm, &dd); + } else if (sel == 1) { + datectl(UP_YEAR, &yy, &mm, &dd); + } else { /* sel = 2 */ + datectl(LEFT_DAY, &yy, &mm, &dd); + } + break; + case '+': + if (focusbuttons) { + break; + } else if (sel == 0) { + datectl(DOWN_MONTH, &yy, &mm, &dd); + } else if (sel == 1) { + datectl(DOWN_YEAR, &yy, &mm, &dd); + } else { /* sel = 2 */ + datectl(RIGHT_DAY, &yy, &mm, &dd); + } + break; + case KEY_HOME: + datectl(UP_MONTH, &yy, &mm, &dd); + break; + case KEY_END: + datectl(DOWN_MONTH, &yy, &mm, &dd); + break; + case KEY_PPAGE: + datectl(UP_YEAR, &yy, &mm, &dd); + break; + case KEY_NPAGE: + datectl(DOWN_YEAR, &yy, &mm, &dd); + break; + case KEY_F(1): + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) + break; + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (calendar_draw(&d, true, yy_win, mm_win, dd_win) != 0) + return (BSDDIALOG_ERROR); + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (calendar_draw(&d, true, yy_win, mm_win, dd_win) != 0) + return (BSDDIALOG_ERROR); + break; + default: + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; + } + } + } + + *year = yy; + *month = mm; + *day = dd; + + delwin(yy_win); + delwin(mm_win); + delwin(dd_win); + end_dialog(&d); + + return (retval); +} + +static int datebox_draw(struct dialog *d, bool redraw, struct dateitem *di) +{ + int y, x; + + if (redraw) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, 3 /*windows*/, MINWDATE, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) + return (BSDDIALOG_ERROR); + if (redraw) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, 3 /*windows*/ + HBUTTONS); + + y = d->y + d->h - 6; + x = (d->x + d->w / 2) - 11; + update_box(d->conf, di[0].win, y, x, 3, di[0].width, LOWERED); + mvwaddch(d->widget, d->h - 5, x - d->x + di[0].width, '/'); + x += di[0].width + 1; + update_box(d->conf, di[1].win, y, x , 3, di[1].width, LOWERED); + mvwaddch(d->widget, d->h - 5, x - d->x + di[1].width, '/'); + x += di[1].width + 1; + update_box(d->conf, di[2].win, y, x, 3, di[2].width, LOWERED); + wnoutrefresh(d->widget); + + return (0); +} + +static int +build_dateitem(const char *format, int *yy, int *mm, int *dd, + struct dateitem *dt) +{ + int i; + wchar_t *wformat; + struct dateitem init[3] = { + {UP_YEAR, DOWN_YEAR, NULL, 6, "%4d", yy}, + {UP_MONTH, DOWN_MONTH, NULL, 11, "%9s", mm}, + {LEFT_DAY, RIGHT_DAY, NULL, 4, "%02d", dd}, + }; + + for (i = 0; i < 3; i++) { + if ((init[i].win = newwin(1, 1, 1, 1)) == NULL) + RETURN_FMTERROR("Cannot build WINDOW dateitem[%d]", i); + wbkgd(init[i].win, t.dialog.color); + } + + if ((wformat = alloc_mbstows(CHECK_STR(format))) == NULL) + RETURN_ERROR("Cannot allocate conf.date.format in wchar_t*"); + if (format == NULL || wcscmp(wformat, L"d/m/y") == 0) { + dt[0] = init[2]; + dt[1] = init[1]; + dt[2] = init[0]; + } else if (wcscmp(wformat, L"m/d/y") == 0) { + dt[0] = init[1]; + dt[1] = init[2]; + dt[2] = init[0]; + } else if (wcscmp(wformat, L"y/m/d") == 0) { + dt[0] = init[0]; + dt[1] = init[1]; + dt[2] = init[2]; + } else + RETURN_FMTERROR("Invalid conf.date.format=\"%s\"", format); + free(wformat); + + return (0); +} + +int +bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int *year, unsigned int *month, unsigned int *day) +{ + bool loop, focusbuttons; + int retval, i, sel, yy, mm, dd; + wint_t input; + struct dateitem di[3]; + struct dialog d; + + CHECK_PTR(year); + CHECK_PTR(month); + CHECK_PTR(day); + minyear = MIN_YEAR_DATE; + maxyear = MAX_YEAR_DATE; + init_date(year, month, day, &yy, &mm, &dd); + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + if (build_dateitem(conf->date.format, &yy, &mm, &dd, di) != 0) + return (BSDDIALOG_ERROR); + if (datebox_draw(&d, false, di) != 0) + return (BSDDIALOG_ERROR); + + sel = -1; + loop = focusbuttons = true; + while (loop) { + for (i = 0; i < 3; i++) + drawsquare(conf, di[i].win, LOWERED, di[i].fmt, + *di[i].value, sel == i); + doupdate(); + + if (get_wch(&input) == ERR) + continue; + switch(input) { + case KEY_ENTER: + case 10: /* Enter */ + if (focusbuttons || conf->button.always_active) { + retval = BUTTONVALUE(d.bs); + loop = false; + } + break; + case 27: /* Esc */ + if (conf->key.enable_esc) { + retval = BSDDIALOG_ESC; + loop = false; + } + break; + case '\t': /* TAB */ + case KEY_CTRL('n'): + case KEY_RIGHT: + if (focusbuttons) { + d.bs.curr++; + focusbuttons = d.bs.curr < (int)d.bs.nbuttons ? + true : false; + if (focusbuttons == false) { + sel = 0; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else { + sel++; + focusbuttons = sel > 2 ? true : false; + if (focusbuttons) { + d.bs.curr = 0; + } + } + DRAW_BUTTONS(d); + break; + case KEY_CTRL('p'): + case KEY_LEFT: + if (focusbuttons) { + d.bs.curr--; + focusbuttons = d.bs.curr < 0 ? false : true; + if (focusbuttons == false) { + sel = 2; + d.bs.curr = conf->button.always_active ? + 0 : -1; + } + } else { + sel--; + focusbuttons = sel < 0 ? true : false; + if (focusbuttons) + d.bs.curr = (int)d.bs.nbuttons - 1; + } + DRAW_BUTTONS(d); + break; + case '-': + if (focusbuttons == false) + datectl(di[sel].up, &yy, &mm, &dd); + break; + case KEY_UP: + if (focusbuttons) { + sel = 0; + focusbuttons = false; + d.bs.curr = conf->button.always_active ? 0 : -1; + DRAW_BUTTONS(d); + } else { + datectl(di[sel].up, &yy, &mm, &dd); + } + break; + case '+': + case KEY_DOWN: + if (focusbuttons) + break; + datectl(di[sel].down, &yy, &mm, &dd); + break; + case KEY_F(1): + if (conf->key.f1_file == NULL && + conf->key.f1_message == NULL) + break; + if (f1help_dialog(conf) != 0) + return (BSDDIALOG_ERROR); + if (datebox_draw(&d, true, di) != 0) + return (BSDDIALOG_ERROR); + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (datebox_draw(&d, true, di) != 0) + return (BSDDIALOG_ERROR); + break; + default: + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + loop = false; + } + } + } + + *year = yy; + *month = mm; + *day = dd; + + for (i = 0; i < 3 ; i++) + delwin(di[i].win); + end_dialog(&d); + + return (retval); +}
\ No newline at end of file diff --git a/contrib/bsddialog/lib/formbox.c b/contrib/bsddialog/lib/formbox.c index 564fa99d69a8..a072461c43e1 100644 --- a/contrib/bsddialog/lib/formbox.c +++ b/contrib/bsddialog/lib/formbox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,10 +25,8 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> -#include <form.h> +#include <curses.h> +#include <limits.h> #include <stdlib.h> #include <string.h> @@ -36,502 +34,911 @@ #include "bsddialog_theme.h" #include "lib_util.h" -#define ISFIELDHIDDEN(item) (item.flags & BSDDIALOG_FIELDHIDDEN) -#define ISFIELDREADONLY(item) (item.flags & BSDDIALOG_FIELDREADONLY) -#define REDRAWFORM 19860214 /* magic number */ - -/* field_userptr for private buffer and view options */ -struct myfield { - int buflen; - wchar_t *buf; - int pos; - int maxpos; - bool secure; - int securech; - const char *bottomdesc; +enum field_action { + MOVE_CURSOR_BEGIN, + MOVE_CURSOR_END, + MOVE_CURSOR_RIGHT, + MOVE_CURSOR_LEFT, + DEL_LETTER +}; + +struct privateitem { + const char *label; /* formitem.label */ + unsigned int ylabel; /* formitem.ylabel */ + unsigned int xlabel; /* formitem.xlabel */ + unsigned int yfield; /* formitem.yfield */ + unsigned int xfield; /* formitem.xfield */ + bool secure; /* formitem.flags & BSDDIALOG_FIELDHIDDEN */ + bool readonly; /* formitem.flags & BSDDIALOG_FIELDREADONLY */ + bool fieldnocolor; /* formitem.flags & BSDDIALOG_FIELDNOCOLOR */ + bool extendfield; /* formitem.flags & BSDDIALOG_FIELDEXTEND */ + bool fieldonebyte; /* formitem.flags & BSDDIALOG_FIELDSINGLEBYTE */ + bool cursorend; /* formitem.flags & BSDDIALOG_FIELDCURSOREND */ + bool cursor; /* field cursor visibility */ + const char *bottomdesc; /* formitem.bottomdesc */ + + wchar_t *privwbuf; /* formitem.value */ + wchar_t *pubwbuf; /* string for drawitem() */ + unsigned int maxletters; /* formitem.maxvaluelen, [priv|pub]wbuf size */ + unsigned int nletters; /* letters in privwbuf and pubwbuf */ + unsigned int pos; /* pos in privwbuf and pubwbuf */ + unsigned int fieldcols; /* formitem.fieldlen */ + unsigned int xcursor; /* position in fieldcols [0 - fieldcols-1] */ + unsigned int xposdraw; /* first pubwbuf index to draw */ +}; + +struct privateform { + WINDOW *box; /* window to draw borders */ + WINDOW *pad; + unsigned int h; /* only to create pad */ + unsigned int w; /* only to create pad */ + unsigned int wmin; /* to refresh, w can change for FIELDEXTEND */ + unsigned int ys; /* to refresh */ + unsigned int ye; /* to refresh */ + unsigned int xs; /* to refresh */ + unsigned int xe; /* to refresh */ + unsigned int y; /* changes moving focus around items */ + unsigned int formheight; /* API formheight */ + unsigned int viewrows; /* visible rows, real formheight */ + unsigned int minviewrows; /* min viewrows, ylabel != yfield */ + wchar_t securewch; /* wide char of conf.form.secure[mb]ch */ + unsigned int nitems; /* like API nitems */ + struct privateitem *pritems; + int sel; /* selected item in pritem, can be -1 */ + bool hasbottomdesc; /* some item has bottomdesc */ }; -#define GETMYFIELD(field) ((struct myfield*)field_userptr(field)) -#define GETMYFIELD2(form) ((struct myfield*)field_userptr(current_field(form))) -static void insertch(struct myfield *mf, int ch) +static int +build_privateform(struct bsddialog_conf*conf, unsigned int nitems, + struct bsddialog_formitem *items, struct privateform *f) { - int i; + bool insecurecursor; + int mbchsize; + unsigned int i, j, itemybeg, itemxbeg, tmp; + wchar_t *winit; + struct privateitem *item; + + /* checks */ + CHECK_ARRAY(nitems, items); + for (i = 0; i < nitems; i++) { + if (items[i].fieldlen == 0) + RETURN_FMTERROR("item %u [0-%u] fieldlen = 0", + i, nitems); + if (items[i].maxvaluelen == 0) + RETURN_FMTERROR("item %u [0-%u] maxvaluelen = 0", + i, nitems); + } + f->nitems = nitems; + + /* insecure ch */ + insecurecursor = false; + if (conf->form.securembch != NULL) { + mbchsize = mblen(conf->form.securembch, MB_LEN_MAX); + if (mbtowc(&f->securewch, conf->form.securembch, mbchsize) < 0) + RETURN_ERROR("Cannot convert securembch to wchar_t"); + insecurecursor = true; + } else if (conf->form.securech != '\0') { + f->securewch = btowc(conf->form.securech); + insecurecursor = true; + } else { + f->securewch = L' '; + } - if (mf->buflen > mf->maxpos) - return; + /* alloc and set private items */ + f->pritems = malloc(f->nitems * sizeof(struct privateitem)); + if (f->pritems == NULL) + RETURN_ERROR("Cannot allocate internal form.pritems"); + f->hasbottomdesc = false; + f->h = f->w = f->minviewrows = 0; + for (i = 0; i < f->nitems; i++) { + item = &f->pritems[i]; + item->label = CHECK_STR(items[i].label); + item->ylabel = items[i].ylabel; + item->xlabel = items[i].xlabel; + item->yfield = items[i].yfield; + item->xfield = items[i].xfield; + item->secure = items[i].flags & BSDDIALOG_FIELDHIDDEN; + item->readonly = items[i].flags & BSDDIALOG_FIELDREADONLY; + item->fieldnocolor = items[i].flags & BSDDIALOG_FIELDNOCOLOR; + item->extendfield = items[i].flags & BSDDIALOG_FIELDEXTEND; + item->fieldonebyte = items[i].flags & + BSDDIALOG_FIELDSINGLEBYTE; + item->cursorend = items[i].flags & BSDDIALOG_FIELDCURSOREND; + item->bottomdesc = CHECK_STR(items[i].bottomdesc); + if (items[i].bottomdesc != NULL) + f->hasbottomdesc = true; + if (item->readonly || (item->secure && !insecurecursor)) + item->cursor = false; + else + item->cursor = true; + + item->maxletters = items[i].maxvaluelen; + item->privwbuf = calloc(item->maxletters + 1, sizeof(wchar_t)); + if (item->privwbuf == NULL) + RETURN_ERROR("Cannot allocate item private buffer"); + memset(item->privwbuf, 0, item->maxletters + 1); + item->pubwbuf = calloc(item->maxletters + 1, sizeof(wchar_t)); + if (item->pubwbuf == NULL) + RETURN_ERROR("Cannot allocate item private buffer"); + memset(item->pubwbuf, 0, item->maxletters + 1); + + if ((winit = alloc_mbstows(CHECK_STR(items[i].init))) == NULL) + RETURN_ERROR("Cannot allocate item.init in wchar_t*"); + wcsncpy(item->privwbuf, winit, item->maxletters); + wcsncpy(item->pubwbuf, winit, item->maxletters); + free(winit); + item->nletters = wcslen(item->pubwbuf); + if (item->secure) { + for (j = 0; j < item->nletters; j++) + item->pubwbuf[j] = f->securewch; + } - for (i = mf->buflen; i >= mf->pos; i--) { - mf->buf[i+1] = mf->buf[i]; + item->fieldcols = items[i].fieldlen; + item->xposdraw = 0; + item->xcursor = 0; + item->pos = 0; + + /* size and position */ + f->h = MAX(f->h, item->ylabel); + f->h = MAX(f->h, item->yfield); + f->w = MAX(f->w, item->xlabel + strcols(item->label)); + f->w = MAX(f->w, item->xfield + item->fieldcols); + if (i == 0) { + itemybeg = MIN(item->ylabel, item->yfield); + itemxbeg = MIN(item->xlabel, item->xfield); + } else { + tmp = MIN(item->ylabel, item->yfield); + itemybeg = MIN(itemybeg, tmp); + tmp = MIN(item->xlabel, item->xfield); + itemxbeg = MIN(itemxbeg, tmp); + } + tmp = abs((int)item->ylabel - (int)item->yfield); + f->minviewrows = MAX(f->minviewrows, tmp); + } + if (f->nitems > 0) { + f->h = f->h + 1 - itemybeg; + f->w -= itemxbeg; + f->minviewrows += 1; + } + f->wmin = f->w; + for (i = 0; i < f->nitems; i++) { + f->pritems[i].ylabel -= itemybeg; + f->pritems[i].yfield -= itemybeg; + f->pritems[i].xlabel -= itemxbeg; + f->pritems[i].xfield -= itemxbeg; } - mf->buf[mf->pos] = ch; - mf->pos += 1; - if (mf->pos > mf->maxpos) - mf->pos = mf->maxpos; - mf->buflen += 1; - mf->buf[mf->buflen] = '\0'; + return (0); } -static void shiftleft(struct myfield *mf) +static bool fieldctl(struct privateitem *item, enum field_action act) { - int i, last; + bool change; + int width, oldwidth, nextwidth, cols; + unsigned int i; + + change = false; + switch (act){ + case MOVE_CURSOR_BEGIN: + if (item->pos == 0 && item->xcursor == 0) + break; + /* here the cursor is changed */ + change = true; + item->pos = 0; + item->xcursor = 0; + item->xposdraw = 0; + break; + case MOVE_CURSOR_END: + while (fieldctl(item, MOVE_CURSOR_RIGHT)) + change = true; + break; + case MOVE_CURSOR_LEFT: + if (item->pos == 0) + break; + /* check redundant by item->pos == 0 because of 'while' below */ + if (item->xcursor == 0 && item->xposdraw == 0) + break; + /* here some letter to left */ + change = true; + item->pos -= 1; + width = wcwidth(item->pubwbuf[item->pos]); + if (((int)item->xcursor) - width < 0) { + item->xcursor = 0; + item->xposdraw -= 1; + } else + item->xcursor -= width; + + while (true) { + if (item->xposdraw == 0) + break; + if (item->xcursor >= item->fieldcols / 2) + break; + if (wcwidth(item->pubwbuf[item->xposdraw - 1]) + + item->xcursor + width > item->fieldcols) + break; - for (i = mf->pos; i < mf->buflen -1; i++) { - mf->buf[i] = mf->buf[i+1]; + item->xposdraw -= 1; + item->xcursor += + wcwidth(item->pubwbuf[item->xposdraw]); + } + break; + case DEL_LETTER: + if (item->nletters == 0) + break; + if (item->pos == item->nletters) + break; + /* here a letter under the cursor */ + change = true; + for (i = item->pos; i < item->nletters; i++) { + item->privwbuf[i] = item->privwbuf[i+1]; + item->pubwbuf[i] = item->pubwbuf[i+1]; + } + item->nletters -= 1; + item->privwbuf[i] = L'\0'; + item->pubwbuf[i] = L'\0'; + break; + case MOVE_CURSOR_RIGHT: /* used also by "insert", see handler loop */ + if (item->pos + 1 == item->maxletters) + break; + if (item->pos == item->nletters) + break; + /* here a change to right */ + change = true; + oldwidth = wcwidth(item->pubwbuf[item->pos]); + item->pos += 1; + if (item->pos == item->nletters) { /* empty column */ + nextwidth = 1; + } else { /* a letter to right */ + nextwidth = wcwidth(item->pubwbuf[item->pos]); + } + if (item->xcursor + oldwidth + nextwidth - 1 >= item->fieldcols) { + cols = nextwidth; + item->xposdraw = item->pos; + while (item->xposdraw != 0) { + cols += wcwidth(item->pubwbuf[item->xposdraw - 1]); + if (cols > (int)item->fieldcols) + break; + item->xposdraw -= 1; + } + item->xcursor = 0; + for (i = item->xposdraw; i < item->pos ; i++) + item->xcursor += wcwidth(item->pubwbuf[i]); + } + else { + item->xcursor += oldwidth; + } + + break; } - last = mf->buflen > 0 ? mf->buflen -1 : 0; - mf->buf[last] = '\0'; - mf->buflen = last; + return (change); } -static void print_bottomdesc(struct myfield *mf) +static bool insertch(struct privateitem *item, wchar_t wch, wchar_t securewch) { - move(SCREENLINES - 1, 2); - clrtoeol(); - if (mf->bottomdesc != NULL) { - addstr(mf->bottomdesc); - refresh(); + int i; + + if (item->nletters >= item->maxletters) + return (false); + + for (i = (int)item->nletters - 1; i >= (int)item->pos; i--) { + item->privwbuf[i+1] = item->privwbuf[i]; + item->pubwbuf[i+1] = item->pubwbuf[i]; } + + item->privwbuf[item->pos] = wch; + item->pubwbuf[item->pos] = item->secure ? securewch : wch; + item->nletters += 1; + item->privwbuf[item->nletters] = L'\0'; + item->pubwbuf[item->nletters] = L'\0'; + + return (true); } -static char *w2c(wchar_t *string) +static char* alloc_wstomb(wchar_t *wstr) { - int i, len; - char *value; - - len = wcslen(string); - if ((value = calloc(len + 1, sizeof(char))) == NULL) - return NULL; + int len, nbytes, i; + char mbch[MB_LEN_MAX], *mbstr; + + nbytes = MB_LEN_MAX; /* to ensure a null terminated string */ + len = wcslen(wstr); + for (i = 0; i < len; i++) { + wctomb(mbch, wstr[i]); + nbytes += mblen(mbch, MB_LEN_MAX); + } + if ((mbstr = malloc(nbytes)) == NULL) + return (NULL); - for (i = 0; i < len; i++) - value[i] = string[i]; - value[i] = '\0'; + wcstombs(mbstr, wstr, nbytes); - return value; + return (mbstr); } static int -return_values(struct bsddialog_conf *conf, int output, int nitems, - struct bsddialog_formitem *items, FORM *form, FIELD **cfield) +return_values(struct bsddialog_conf *conf, struct privateform *f, + struct bsddialog_formitem *items) +{ + unsigned int i; + + for (i = 0; i < f->nitems; i++) { + if (conf->form.value_wchar) + items[i].value = (char*)wcsdup(f->pritems[i].privwbuf); + else + items[i].value = alloc_wstomb(f->pritems[i].privwbuf); + + if (items[i].value == NULL) + RETURN_FMTERROR( + "Cannot allocate memory for item[%d].value", i); + } + + return (0); +} + +static void set_first_with_default(struct privateform *f, int *focusitem) +{ + unsigned int i; + + f->sel = -1; + if (focusitem != NULL && *focusitem >=0 && *focusitem < (int)f->nitems) + if (f->pritems[*focusitem].readonly == false) { + f->sel = *focusitem; + return; + } + for (i = 0 ; i < f->nitems; i++) + if (f->pritems[i].readonly == false) { + f->sel = i; + break; + } +} + +static unsigned int firstitem(unsigned int nitems, struct privateitem *items) { int i; - struct myfield *mf; - if (output != BSDDIALOG_OK && conf->form.value_without_ok == false) - return (output); + for (i = 0; i < (int)nitems; i++) + if (items[i].readonly == false) + break; - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_FIELD); - form_driver_w(form, KEY_CODE_YES, REQ_PREV_FIELD); - for (i = 0; i < nitems; i++) { - mf = GETMYFIELD(cfield[i]); - if (conf->form.enable_wchar) { - items[i].value = (char*)wcsdup(mf->buf); - } else { - items[i].value = w2c(mf->buf); + return (i); +} + +static unsigned int lastitem(unsigned int nitems, struct privateitem *items) +{ + int i; + + for (i = nitems - 1; i >= 0 ; i--) + if (items[i].readonly == false) + break; + + return (i); +} + +static unsigned int +previtem(unsigned int nitems, struct privateitem *items, int curritem) +{ + int i; + + for (i = curritem - 1; i >= 0; i--) + if (items[i].readonly == false) + return(i); + + for (i = nitems - 1; i > curritem - 1; i--) + if (items[i].readonly == false) + return(i); + + return (curritem); +} + +static unsigned int +nextitem(unsigned int nitems, struct privateitem *items, int curritem) +{ + int i; + + for (i = curritem + 1; i < (int)nitems; i++) + if (items[i].readonly == false) + return(i); + + for (i = 0; i < curritem; i++) + if (items[i].readonly == false) + return(i); + + return (curritem); +} + +static void redrawbuttons(struct dialog *d, bool focus, bool shortcut) +{ + int selected; + + selected = d->bs.curr; + if (focus == false) + d->bs.curr = -1; + d->bs.shortcut = shortcut; + draw_buttons(d); + d->bs.curr = selected; +} + +static void +drawitem(struct privateform *f, int idx, bool focus) +{ + int color; + unsigned int n, cols; + struct privateitem *item; + + item = &f->pritems[idx]; + + /* Label */ + wattron(f->pad, t.dialog.color); + mvwaddstr(f->pad, item->ylabel, item->xlabel, item->label); + wattroff(f->pad, t.dialog.color); + + /* Field */ + if (item->readonly) + color = t.form.readonlycolor; + else if (item->fieldnocolor) + color = t.dialog.color; + else + color = focus ? t.form.f_fieldcolor : t.form.fieldcolor; + wattron(f->pad, color); + mvwhline(f->pad, item->yfield, item->xfield, ' ', item->fieldcols); + n = 0; + cols = wcwidth(item->pubwbuf[item->xposdraw]); + while (cols <= item->fieldcols && + item->xposdraw + n < wcslen(item->pubwbuf)) { + n++; + cols += wcwidth(item->pubwbuf[item->xposdraw + n]); + + } + mvwaddnwstr(f->pad, item->yfield, item->xfield, + &item->pubwbuf[item->xposdraw], n); + wattroff(f->pad, color); + + /* Bottom Desc */ + if (f->hasbottomdesc) { + move(SCREENLINES - 1, 2); + clrtoeol(); + if (item->bottomdesc != NULL && focus) { + attron(t.form.bottomdesccolor); + addstr(item->bottomdesc); + attroff(t.form.bottomdesccolor); + refresh(); } - if (items[i].value == NULL) - RETURN_ERROR("Cannot allocate memory for form value"); } - return (output); + /* Cursor */ + curs_set((focus && item->cursor) ? 1 : 0); + wmove(f->pad, item->yfield, item->xfield + item->xcursor); +} + +/* + * Trick: draw 2 times an item switching focus. + * Problem: curses tries to optimize the rendering but sometimes it misses some + * updates or draws old stuff. libformw has a similar problem fixed by the + * same trick. + * Case 1: KEY_DC and KEY_BACKSPACE, deleted multicolumn letters are drawn + * again. It seems fixed by new items pad and prefresh(), previously WINDOW. + * Case2: some terminal, tmux and ssh does not show the cursor. + */ +#define DRAWITEM_TRICK(f, idx, focus) do { \ + drawitem(f, idx, !focus); \ + prefresh((f)->pad, (f)->y, 0, (f)->ys, (f)->xs, (f)->ye, (f)->xe); \ + drawitem(f, idx, focus); \ + prefresh((f)->pad, (f)->y, 0, (f)->ys, (f)->xs, (f)->ye, (f)->xe); \ +} while (0) + +static void update_formbox(struct bsddialog_conf *conf, struct privateform *f) +{ + int h, w; + + getmaxyx(f->box, h, w); + draw_borders(conf, f->box, LOWERED); + + if (f->viewrows < f->h) { + wattron(f->box, t.dialog.arrowcolor); + if (f->y > 0) + mvwhline(f->box, 0, (w / 2) - 2, UARROW(conf), 5); + + if (f->y + f->viewrows < f->h) + mvwhline(f->box, h-1, (w / 2) - 2, DARROW(conf), 5); + wattroff(f->box, t.dialog.arrowcolor); + } +} + +static void curriteminview(struct privateform *f, struct privateitem *item) +{ + unsigned int yup, ydown; + + yup = MIN(item->ylabel, item->yfield); + ydown = MAX(item->ylabel, item->yfield); + + /* selected item in view */ + if (f->y > yup && f->y > 0) + f->y = yup; + if ((int)(f->y + f->viewrows) - 1 < (int)ydown) + f->y = ydown - f->viewrows + 1; + /* lower pad after a terminal expansion */ + if (f->y > 0 && (f->h - f->y) < f->viewrows) + f->y = f->h - f->viewrows; +} + +static int form_size_position(struct dialog *d, struct privateform *f) +{ + int htext, hform; + + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + + /* autosize */ + hform = (int) f->viewrows; + if (f->viewrows == BSDDIALOG_AUTOSIZE) + hform = MAX(f->h, f->minviewrows); + hform += 2; /* formborders */ + + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, &htext, &d->bs, hform, f->w + 4) != 0) + return (BSDDIALOG_ERROR); + /* formheight: avoid overflow, "at most" and at least minviewrows */ + if (d->h - BORDERS - htext - HBUTTONS < 2 + (int)f->minviewrows) { + f->viewrows = f->minviewrows; /* for widget_checksize() */ + } else if (f->viewrows == BSDDIALOG_AUTOSIZE) { + f->viewrows = MIN(d->h - BORDERS - htext - HBUTTONS, hform) - 2; + f->viewrows = MAX(f->viewrows, f->minviewrows); + } else { + f->viewrows = MIN(d->h - BORDERS - htext - HBUTTONS, hform) - 2; + } + + /* checksize */ + if (f->viewrows < f->minviewrows) + RETURN_FMTERROR("formheight, current: %u needed at least %u", + f->viewrows, f->minviewrows); + if (widget_checksize(d->h, d->w, &d->bs, + 2 /* borders */ + f->minviewrows, f->w + 4) != 0) + return (BSDDIALOG_ERROR); + + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); } static int -form_handler(struct bsddialog_conf *conf, WINDOW *widget, struct buttons bs, - WINDOW *formwin, FORM *form, FIELD **cfield, int nitems, - struct bsddialog_formitem *items) +form_draw(struct dialog *d, bool redraw, struct privateform *f, bool focusinform) { - bool loop, buttupdate, informwin; - int i, chtype, output; + unsigned int i; + + if (redraw) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + f->viewrows = f->formheight; + f->w = f->wmin; + if (form_size_position(d, f) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) /* doupdate() in main loop */ + return (BSDDIALOG_ERROR); + if (redraw) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, 2 /* box borders */ + f->viewrows + HBUTTONS); + + update_box(d->conf, f->box, d->y + d->h - 5 - f->viewrows, d->x + 2, + f->viewrows + 2, d->w - 4, LOWERED); + + for (i = 0; i < f->nitems; i++) { + fieldctl(&f->pritems[i], MOVE_CURSOR_BEGIN); + if (f->pritems[i].extendfield) { + f->w = d->w - 6; + f->pritems[i].fieldcols = f->w - f->pritems[i].xfield; + } + if (f->pritems[i].cursorend) + fieldctl(&f->pritems[i], MOVE_CURSOR_END); + } + + wresize(f->pad, f->h, f->w); + for (i = 0; i < f->nitems; i++) + drawitem(f, i, false); + + f->ys = d->y + d->h - 5 - f->viewrows + 1; + f->ye = d->y + d->h - 5 ; + if ((int)f->w >= d->w - 6) { /* left */ + f->xs = d->x + 3; + f->xe = f->xs + d->w - 7; + } else { /* center */ + f->xs = d->x + 3 + (d->w - 6)/2 - f->w/2; + f->xe = f->xs + d->w - 5; + } + + if (f->sel != -1) { /* at least 1 writable item */ + redrawbuttons(d, + d->conf->button.always_active || !focusinform, + !focusinform); + wnoutrefresh(d->widget); + curriteminview(f, &f->pritems[f->sel]); + update_formbox(d->conf, f); + wnoutrefresh(f->box); + DRAWITEM_TRICK(f, f->sel, focusinform); + } else if (f->sel == -1 && f->nitems > 0) { /* all read only */ + redrawbuttons(d, true, true); + wnoutrefresh(d->widget); + update_formbox(d->conf, f); + wnoutrefresh(f->box); + DRAWITEM_TRICK(f, 0, false); /* to refresh pad*/ + } else { /* no item */ + wnoutrefresh(f->box); + } + + return (0); +} + +/* API */ +int +bsddialog_form(struct bsddialog_conf *conf, const char *text, int rows, + int cols, unsigned int formheight, unsigned int nitems, + struct bsddialog_formitem *items, int *focusitem) +{ + bool switchfocus, changeitem, focusinform, loop; + int next, retval, wchtype; + unsigned int i; wint_t input; - struct myfield *mf; + struct privateitem *item; + struct privateform form; + struct dialog d; + + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + + if (build_privateform(conf, nitems, items, &form) != 0) + return (BSDDIALOG_ERROR); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - pos_form_cursor(form); - form_driver_w(form, KEY_CODE_YES, REQ_END_LINE); - mf->pos = MIN(mf->buflen, mf->maxpos); - curs_set(1); - informwin = true; + if ((form.box = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW form box"); + wbkgd(form.box, t.dialog.color); + if ((form.pad = newpad(1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW form pad"); + wbkgd(form.pad, t.dialog.color); + + set_first_with_default(&form, focusitem); + if (form.sel != -1) { + focusinform = true; + form.y = 0; + item = &form.pritems[form.sel]; + } else { + item = NULL; + focusinform = false; + } - bs.curr = -1; - buttupdate = true; + form.formheight = formheight; + if (form_draw(&d, false, &form, focusinform) != 0) + return (BSDDIALOG_ERROR); + changeitem = switchfocus = false; loop = true; while (loop) { - if (buttupdate) { - draw_buttons(widget, bs, !informwin); - wrefresh(widget); - buttupdate = false; - } - wrefresh(formwin); - chtype = get_wch(&input); - if (chtype != KEY_CODE_YES && input > 127 && - conf->form.enable_wchar == false) + doupdate(); + if ((wchtype = get_wch(&input)) == ERR) continue; switch(input) { - case KEY_HOME: - case KEY_PPAGE: - case KEY_END: - case KEY_NPAGE: - /* disabled keys */ - break; case KEY_ENTER: case 10: /* Enter */ - if (informwin) - break; - output = return_values(conf, bs.value[bs.curr], nitems, - items, form, cfield); - loop = false; + if (focusinform && conf->button.always_active == false) { + next = nextitem(form.nitems, form.pritems, form.sel); + if (next > form.sel) + changeitem = true; /* needs next */ + else + switchfocus = true; + } else { + retval = BUTTONVALUE(d.bs); + loop = false; + } break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = return_values(conf, BSDDIALOG_ESC, - nitems, items, form, cfield); + retval = BSDDIALOG_ESC; loop = false; } break; case '\t': /* TAB */ - if (informwin) { - bs.curr = 0; - informwin = false; - curs_set(0); + if (focusinform) { + next = nextitem(form.nitems, form.pritems, + form.sel); + if (next > form.sel) + changeitem = true; /* needs next */ + else + switchfocus = true; } else { - bs.curr++; - informwin = bs.curr >= (int)bs.nbuttons ? - true : false; - if (informwin) { - curs_set(1); - pos_form_cursor(form); + if (d.bs.curr + 1 < (int)d.bs.nbuttons) { + d.bs.curr++; + } else { + d.bs.curr = 0; + if (form.sel != -1) { + switchfocus = true; + } } + redrawbuttons(&d, true, true); + wnoutrefresh(d.widget); } - buttupdate = true; break; case KEY_LEFT: - if (informwin) { - form_driver_w(form, KEY_CODE_YES, REQ_PREV_CHAR); - mf = GETMYFIELD2(form); - if (mf->pos > 0) - mf->pos -= 1; - } else { - if (bs.curr > 0) { - bs.curr--; - buttupdate = true; - } + if (focusinform) { + if (fieldctl(item, MOVE_CURSOR_LEFT)) + DRAWITEM_TRICK(&form, form.sel, true); + } else if (d.bs.curr > 0) { + d.bs.curr--; + redrawbuttons(&d, true, true); + wnoutrefresh(d.widget); + } else if (form.sel != -1) { + switchfocus = true; } break; case KEY_RIGHT: - if (informwin) { - mf = GETMYFIELD2(form); - if (mf->pos >= mf->buflen) - break; - mf->pos += 1; - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_CHAR); - } else { - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - buttupdate = true; - } + if (focusinform) { + if (fieldctl(item, MOVE_CURSOR_RIGHT)) + DRAWITEM_TRICK(&form, form.sel, true); + } else if (d.bs.curr < (int) d.bs.nbuttons - 1) { + d.bs.curr++; + redrawbuttons(&d, true, true); + wnoutrefresh(d.widget); + } else if (form.sel != -1) { + switchfocus = true; } break; + case KEY_CTRL('p'): case KEY_UP: - if (nitems < 2) - break; - set_field_fore(current_field(form), t.form.fieldcolor); - set_field_back(current_field(form), t.form.fieldcolor); - form_driver_w(form, KEY_CODE_YES, REQ_PREV_FIELD); - form_driver_w(form, KEY_CODE_YES, REQ_END_LINE); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - mf->pos = MIN(mf->buflen, mf->maxpos); - set_field_fore(current_field(form), t.form.f_fieldcolor); - set_field_back(current_field(form), t.form.f_fieldcolor); + if (focusinform) { + next = previtem(form.nitems, form.pritems, + form.sel); + changeitem = form.sel != next; + } else if (form.sel != -1) { + switchfocus = true; + } break; + case KEY_CTRL('n'): case KEY_DOWN: - if (nitems < 2) + if (focusinform == false) break; - set_field_fore(current_field(form), t.form.fieldcolor); - set_field_back(current_field(form), t.form.fieldcolor); - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_FIELD); - form_driver_w(form, KEY_CODE_YES, REQ_END_LINE); - mf = GETMYFIELD2(form); - print_bottomdesc(mf); - mf->pos = MIN(mf->buflen, mf->maxpos); - set_field_fore(current_field(form), t.form.f_fieldcolor); - set_field_back(current_field(form), t.form.f_fieldcolor); + if (form.nitems == 1) { + switchfocus = true; + } else { + next = nextitem(form.nitems, form.pritems, + form.sel); + changeitem = form.sel != next; + } + break; + case KEY_PPAGE: + if (focusinform) { + next = firstitem(form.nitems, form.pritems); + changeitem = form.sel != next; + } + break; + case KEY_NPAGE: + if (focusinform) { + next = lastitem(form.nitems, form.pritems); + changeitem = form.sel != next; + } break; case KEY_BACKSPACE: case 127: /* Backspace */ - mf = GETMYFIELD2(form); - if (mf->pos <= 0) + if (focusinform == false) break; - form_driver_w(form, KEY_CODE_YES, REQ_DEL_PREV); - form_driver_w(form, KEY_CODE_YES, REQ_BEG_LINE); - mf->pos = mf->pos - 1; - for (i = 0; i < mf->pos; i++) - form_driver_w(form, KEY_CODE_YES, REQ_NEXT_CHAR); - shiftleft(mf); + if (fieldctl(item, MOVE_CURSOR_LEFT)) + if (fieldctl(item, DEL_LETTER)) + DRAWITEM_TRICK(&form, form.sel, true); break; case KEY_DC: - form_driver_w(form, KEY_CODE_YES, REQ_DEL_CHAR); - mf = GETMYFIELD2(form); - if (mf->pos < mf->buflen) - shiftleft(mf); + if (focusinform == false) + break; + if (fieldctl(item, DEL_LETTER)) + DRAWITEM_TRICK(&form, form.sel, true); + break; + case KEY_HOME: + if (focusinform == false) + break; + if (fieldctl(item, MOVE_CURSOR_BEGIN)) + DRAWITEM_TRICK(&form, form.sel, true); + break; + case KEY_END: + if (focusinform == false) + break; + if (fieldctl(item, MOVE_CURSOR_END)) + DRAWITEM_TRICK(&form, form.sel, true); break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) + curs_set(0); + if (f1help_dialog(conf) != 0) { + retval = BSDDIALOG_ERROR; + loop = false; + } + if (form_draw(&d, true, &form, focusinform) != 0) return (BSDDIALOG_ERROR); - /* No Break */ + break; + case KEY_CTRL('l'): case KEY_RESIZE: - output = REDRAWFORM; - loop = false; + if (form_draw(&d, true, &form, focusinform) != 0) + return (BSDDIALOG_ERROR); break; default: - if (informwin) { - if (chtype == KEY_CODE_YES) + if (wchtype == KEY_CODE_YES) + break; + if (focusinform) { + if (item->fieldonebyte && wctob(input) == EOF) break; - mf = GETMYFIELD2(form); - if (mf->secure) - form_driver_w(form, chtype, mf->securech); - else - form_driver_w(form, chtype, input); - insertch(mf, input); - } - else { - if (shortcut_buttons(input, &bs)) { - output = return_values(conf, - bs.value[bs.curr], nitems, items, - form, cfield); + /* + * MOVE_CURSOR_RIGHT manages new positions + * because the cursor remains on the new letter, + * "if" and "while" update the positions. + */ + if (insertch(item, input, form.securewch)) { + fieldctl(item, MOVE_CURSOR_RIGHT); + /* + * no if (fieldctl), update always + * because it fails with maxletters. + */ + DRAWITEM_TRICK(&form, form.sel, true); + } + } else { + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); loop = false; } } break; + } /* end switch get_wch() */ + + if (switchfocus) { + focusinform = !focusinform; + d.bs.curr = 0; + redrawbuttons(&d, + conf->button.always_active || !focusinform, + !focusinform); + wnoutrefresh(d.widget); + if (focusinform == false) + DRAWITEM_TRICK(&form, form.sel, false); + else { + next = firstitem(form.nitems, form.pritems); + if (next == form.sel) + DRAWITEM_TRICK(&form, form.sel, true); + else + changeitem = true; + } + switchfocus = false; } - } - - curs_set(0); - - return (output); -} - -static int -form_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - const char *text, int linelen, unsigned int *formheight, int nitems, - struct buttons bs) -{ - int htext, wtext, menusize; - - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, &bs, *formheight + 2, - linelen + 2, &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); - } - - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, linelen + 2, &bs); - - if (rows == BSDDIALOG_AUTOSIZE) { - if (*formheight == 0) { - menusize = widget_max_height(conf) - HBORDERS - - 2 /*buttons*/ - htext; - menusize = MIN(menusize, nitems + 2); - *formheight = menusize - 2 < 0 ? 0 : menusize - 2; - } - else /* h autosize with fixed formheight */ - menusize = *formheight + 2; - - *h = widget_min_height(conf, htext, menusize, true); - } else { - if (*formheight == 0) - *formheight = MIN(rows-6-htext, nitems); - } - - return (0); -} - -static int -form_checksize(int rows, int cols, const char *text, int formheight, int nitems, - unsigned int linelen, struct buttons bs) -{ - int mincols, textrow, formrows; - - mincols = VBORDERS; - /* buttons */ - mincols += buttons_width(bs); - mincols = MAX(mincols, (int)linelen + 4); - - if (cols < mincols) - RETURN_ERROR("Few cols, width < size buttons or " - "forms (label + field)"); - - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; - - if (nitems > 0 && formheight == 0) - RETURN_ERROR("fields > 0 but formheight == 0, probably " - "terminal too small"); - - formrows = nitems > 0 ? 3 : 0; - if (rows < 2 + 2 + formrows + textrow) - RETURN_ERROR("Few lines for this menus"); - - return (0); -} - -int -bsddialog_form(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int formheight, unsigned int nitems, - struct bsddialog_formitem *items) -{ - int i, output, color, y, x, h, w; - unsigned long j, maxline, mybufsize; - struct buttons bs; - struct myfield *myfields; - FIELD **cfield; - FORM *form; - WINDOW *widget, *formwin, *textpad, *shadow; - - /* disable form scrolling */ - if (formheight < nitems) - formheight = nitems; - - for (i = 0; i < (int)nitems; i++) { - if (items[i].maxvaluelen == 0) - RETURN_ERROR("maxvaluelen cannot be zero"); - if (items[i].fieldlen == 0) - RETURN_ERROR("fieldlen cannot be zero"); - if (items[i].fieldlen > items[i].maxvaluelen) - RETURN_ERROR("fieldlen cannot be > maxvaluelen"); - } - - maxline = 0; - myfields = malloc(nitems * sizeof(struct myfield)); - cfield = calloc(nitems + 1, sizeof(FIELD*)); - for (i = 0; i < (int)nitems; i++) { - cfield[i] = new_field(1, items[i].fieldlen, items[i].yfield-1, - items[i].xfield-1, 0, 0); - field_opts_off(cfield[i], O_STATIC); - set_max_field(cfield[i], items[i].maxvaluelen); - /* setlocale() should handle set_field_buffer() */ - set_field_buffer(cfield[i], 0, items[i].init); - - mybufsize = (items[i].maxvaluelen + 1) * sizeof(wchar_t); - myfields[i].buf = malloc(mybufsize); - memset(myfields[i].buf, 0, mybufsize); - for (j = 0; j < items[i].maxvaluelen && j < strlen(items[i].init); - j++) - myfields[i].buf[j] = items[i].init[j]; - - myfields[i].buflen = wcslen(myfields[i].buf); - - myfields[i].maxpos = items[i].maxvaluelen -1; - myfields[i].pos = MIN(myfields[i].buflen, myfields[i].maxpos); - - myfields[i].bottomdesc = items[i].bottomdesc; - set_field_userptr(cfield[i], &myfields[i]); - - field_opts_off(cfield[i], O_AUTOSKIP); - field_opts_off(cfield[i], O_BLANK); - - if (ISFIELDHIDDEN(items[i])) { - myfields[i].secure = true; - myfields[i].securech = ' '; - if (conf->form.securech != '\0') - myfields[i].securech = conf->form.securech; - } - else - myfields[i].secure = false; - if (ISFIELDREADONLY(items[i])) { - field_opts_off(cfield[i], O_EDIT); - field_opts_off(cfield[i], O_ACTIVE); - color = t.form.readonlycolor; - } else { - color = i == 0 ? t.form.f_fieldcolor : t.form.fieldcolor; + if (changeitem) { + /* useless after if(switchfocus) */ + DRAWITEM_TRICK(&form, form.sel, false); + form.sel = next; + item = &form.pritems[form.sel]; + curriteminview(&form, item); + update_formbox(conf, &form); + wnoutrefresh(form.box); + DRAWITEM_TRICK(&form, form.sel, true); + changeitem = false; } - set_field_fore(cfield[i], color); - set_field_back(cfield[i], color); + } /* end while (loop) */ - maxline = MAX(maxline, items[i].xlabel + strlen(items[i].label)); - maxline = MAX(maxline, items[i].xfield + items[i].fieldlen - 1); - } - cfield[i] = NULL; - - /* disable focus with 1 item (inputbox or passwordbox) */ - if (formheight == 1 && nitems == 1 && strlen(items[0].label) == 0 && - items[0].xfield == 1 ) { - set_field_fore(cfield[0], t.dialog.color); - set_field_back(cfield[0], t.dialog.color); - } - - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (form_autosize(conf, rows, cols, &h, &w, text, maxline, &formheight, - nitems, bs) != 0) - return (BSDDIALOG_ERROR); - if (form_checksize(h, w, text, formheight, nitems, maxline, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); + curs_set(0); - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) + if (return_values(conf, &form, items) == BSDDIALOG_ERROR) return (BSDDIALOG_ERROR); - prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - formheight, x + 1 + w - TEXTHMARGIN); - - formwin = new_boxed_window(conf, y + h - 3 - formheight -2, x +1, - formheight+2, w-2, LOWERED); - - form = new_form(cfield); - set_form_win(form, formwin); - /* should be formheight */ - set_form_sub(form, derwin(formwin, nitems, w-4, 1, 1)); - post_form(form); - - for (i = 0; i < (int)nitems; i++) - mvwaddstr(formwin, items[i].ylabel, items[i].xlabel, - items[i].label); - - wrefresh(formwin); - - do { - output = form_handler(conf, widget, bs, formwin, form, cfield, - nitems, items); + if (focusitem != NULL) + *focusitem = form.sel; - if (update_dialog(conf, shadow, widget, y, x, h, w, textpad, - text, &bs, true) != 0) - return (BSDDIALOG_ERROR); - - doupdate(); - wrefresh(widget); - - prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - formheight, x + 1 + w - TEXTHMARGIN); - - draw_borders(conf, formwin, formheight+2, w-2, LOWERED); - wrefresh(formwin); - - refresh(); - } while (output == REDRAWFORM); - - unpost_form(form); - free_form(form); - for (i = 0; i < (int)nitems; i++) { - free_field(cfield[i]); - free(myfields[i].buf); + if (form.hasbottomdesc && conf->clear) { + move(SCREENLINES - 1, 2); + clrtoeol(); } - free(cfield); - free(myfields); - - delwin(formwin); - end_dialog(conf, shadow, widget, textpad); + for (i = 0; i < form.nitems; i++) { + free(form.pritems[i].privwbuf); + free(form.pritems[i].pubwbuf); + } + delwin(form.pad); + delwin(form.box); + end_dialog(&d); - return (output); + return (retval); } diff --git a/contrib/bsddialog/lib/infobox.c b/contrib/bsddialog/lib/infobox.c deleted file mode 100644 index 5a6b7c2fd692..000000000000 --- a/contrib/bsddialog/lib/infobox.c +++ /dev/null @@ -1,96 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include <sys/param.h> - -#include <curses.h> - -#include "bsddialog.h" -#include "lib_util.h" - -static int -infobox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, const char *text) -{ - int htext, wtext; - - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, NULL, 0, SCREENCOLS/2, - &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); - } - - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, 0, NULL); - - if (rows == BSDDIALOG_AUTOSIZE) - *h = widget_min_height(conf, htext, 0, false); - - return (0); -} - -static int infobox_checksize(int rows, int cols) -{ - if (cols < HBORDERS) - RETURN_ERROR("Few cols, infobox needs at least width 2"); - - if (rows < VBORDERS) - RETURN_ERROR("Infobox needs at least height 2"); - - return (0); -} - -/* API */ -int -bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows, - int cols) -{ - int y, x, h, w; - WINDOW *shadow, *widget, *textpad; - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (infobox_autosize(conf, rows, cols, &h, &w, text) != 0) - return (BSDDIALOG_ERROR); - if (infobox_checksize(h, w) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, - NULL, false) != 0) - return (BSDDIALOG_ERROR); - - pnoutrefresh(textpad, 0, 0, y+1, x+1+TEXTHMARGIN, y+h-2, - x+w-TEXTHMARGIN); - - doupdate(); - - end_dialog(conf, shadow, widget, textpad); - - return (BSDDIALOG_OK); -}
\ No newline at end of file diff --git a/contrib/bsddialog/lib/lib_util.c b/contrib/bsddialog/lib/lib_util.c index 506003b52c8d..f042a2832eb9 100644 --- a/contrib/bsddialog/lib/lib_util.c +++ b/contrib/bsddialog/lib/lib_util.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,22 +25,83 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> +#include <stdarg.h> #include <curses.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <wctype.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" -#define TABLEN 4 /* Default tab len */ -#define ERRBUFLEN 1024 /* Error buffer */ +/* + * -1- Error and diagnostic + * + * get_error_string(); + * set_error_string(); + * set_fmt_error_string(); + * + * ---------------------------------------------------- + * -2- (Unicode) Multicolumn character strings + * + * alloc_mbstows(); + * mvwaddwch(); + * str_props(); + * strcols(); + * + * ---------------------------------------------------- + * -3- Buttons + * + * [static] buttons_min_width(); + * [static] draw_button(); + * draw_buttons(); + * set_buttons(); (to call 1 time after prepare_dialog()). + * shortcut_buttons(); + * + * ---------------------------------------------------- + * -4- (Auto) Sizing and (Auto) Position + * + * [static] widget_max_height(conf); + * [static] widget_max_width(struct bsddialog_conf *conf) + * [static] is_wtext_attr(); + * [static] text_properties(); + * [static] text_autosize(); + * [static] text_size(); + * [static] widget_min_height(conf, htext, hnotext, bool buttons); + * [static] widget_min_width(conf, wtext, minw, buttons); + * set_widget_size(); + * set_widget_autosize(); (not for all dialogs). + * widget_checksize(); (not for all dialogs). + * set_widget_position(); + * dialog_size_position(struct dialog); (not for all dialogs). + * + * ---------------------------------------------------- + * -5- (Dialog) Widget components and utils + * + * hide_dialog(struct dialog); + * f1help_dialog(conf); + * draw_borders(conf, win, elev); + * update_box(conf, win, y, x, h, w, elev); + * rtextpad(); (helper for pnoutrefresh(textpad)). + * + * ---------------------------------------------------- + * -6- Dialog init/build, update/draw, destroy + * + * end_dialog(struct dialog); + * [static] check_set_wtext_attr(); + * [static] print_string(); (word wrapping). + * [static] print_textpad(); + * draw_dialog(struct dialog); + * prepare_dialog(struct dialog); + */ + +/* + * -1- Error and diagnostic + */ +#define ERRBUFLEN 1024 -/* Error */ static char errorbuffer[ERRBUFLEN]; const char *get_error_string(void) @@ -53,55 +114,122 @@ void set_error_string(const char *str) strncpy(errorbuffer, str, ERRBUFLEN-1); } -/* Clear */ -int hide_widget(int y, int x, int h, int w, bool withshadow) +void set_fmt_error_string(const char *fmt, ...) { - WINDOW *clear; + va_list arg_ptr; - if ((clear = newwin(h, w, y + t.shadow.h, x + t.shadow.w)) == NULL) - RETURN_ERROR("Cannot hide the widget"); - wbkgd(clear, t.screen.color); + va_start(arg_ptr, fmt); + vsnprintf(errorbuffer, ERRBUFLEN-1, fmt, arg_ptr); + va_end(arg_ptr); +} - if (withshadow) - wrefresh(clear); +/* + * -2- (Unicode) Multicolumn character strings + */ +wchar_t* alloc_mbstows(const char *mbstring) +{ + size_t charlen, nchar; + mbstate_t mbs; + const char *pmbstring; + wchar_t *wstring; + + nchar = 1; + pmbstring = mbstring; + memset(&mbs, 0, sizeof(mbs)); + while ((charlen = mbrlen(pmbstring, MB_CUR_MAX, &mbs)) != 0 && + charlen != (size_t)-1 && charlen != (size_t)-2) { + pmbstring += charlen; + nchar++; + } - mvwin(clear, y, x); - wrefresh(clear); + if ((wstring = calloc(nchar, sizeof(wchar_t))) == NULL) + return (NULL); + mbstowcs(wstring, mbstring, nchar); - delwin(clear); + return (wstring); +} + +void mvwaddwch(WINDOW *w, int y, int x, wchar_t wch) +{ + wchar_t ws[2]; + + ws[0] = wch; + ws[1] = L'\0'; + mvwaddwstr(w, y, x, ws); +} + +int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col) +{ + bool multicol; + int w; + unsigned int ncol; + size_t charlen, mb_cur_max; + wchar_t wch; + mbstate_t mbs; + + multicol = false; + mb_cur_max = MB_CUR_MAX; + ncol = 0; + memset(&mbs, 0, sizeof(mbs)); + while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 && + charlen != (size_t)-1 && charlen != (size_t)-2) { + if (mbtowc(&wch, mbstring, mb_cur_max) < 0) + return (-1); + w = (wch == L'\t') ? TABSIZE : wcwidth(wch); + ncol += (w < 0) ? 0 : w; + if (w > 1 && wch != L'\t') + multicol = true; + mbstring += charlen; + } + + if (cols != NULL) + *cols = ncol; + if (has_multi_col != NULL) + *has_multi_col = multicol; return (0); } -/* F1 help */ -int f1help(struct bsddialog_conf *conf) +unsigned int strcols(const char *mbstring) { - int output; - struct bsddialog_conf hconf; + int w; + unsigned int ncol; + size_t charlen, mb_cur_max; + wchar_t wch; + mbstate_t mbs; + + mb_cur_max = MB_CUR_MAX; + ncol = 0; + memset(&mbs, 0, sizeof(mbs)); + while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 && + charlen != (size_t)-1 && charlen != (size_t)-2) { + if (mbtowc(&wch, mbstring, mb_cur_max) < 0) + return (0); + w = (wch == L'\t') ? TABSIZE : wcwidth(wch); + ncol += (w < 0) ? 0 : w; + mbstring += charlen; + } - bsddialog_initconf(&hconf); - hconf.title = "HELP"; - hconf.button.ok_label = "EXIT"; - hconf.clear = true; - hconf.ascii_lines = conf->ascii_lines; - hconf.no_lines = conf->no_lines; - hconf.shadow = conf->shadow; - hconf.text.highlight = conf->text.highlight; + return (ncol); +} - output = BSDDIALOG_OK; - if (conf->key.f1_message != NULL) - output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0); +/* + * -3- Buttons + */ +static int buttons_min_width(struct buttons *bs) +{ + unsigned int width; - if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL) - output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0); + width = bs->nbuttons * bs->sizebutton; + if (bs->nbuttons > 0) + width += (bs->nbuttons - 1) * t.button.minmargin; - return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0); + return (width); } -/* Buttons */ static void draw_button(WINDOW *window, int y, int x, int size, const char *text, - bool selected, bool shortcut) + wchar_t first, bool selected, bool shortcut) { int i, color_arrows, color_shortkey, color_button; @@ -126,129 +254,160 @@ draw_button(WINDOW *window, int y, int x, int size, const char *text, mvwaddch(window, y, x + i, t.button.rightdelim); wattroff(window, color_arrows); - x = x + 1 + ((size - 2 - strlen(text))/2); + x = x + 1 + ((size - 2 - strcols(text))/2); wattron(window, color_button); mvwaddstr(window, y, x, text); wattroff(window, color_button); if (shortcut) { wattron(window, color_shortkey); - mvwaddch(window, y, x, text[0]); + mvwaddwch(window, y, x, first); wattroff(window, color_shortkey); } } -void -draw_buttons(WINDOW *window, struct buttons bs, bool shortcut) +void draw_buttons(struct dialog *d) { - int i, x, startx, y, rows, cols; + int i, x, startx, y; + unsigned int newmargin, margin, wbuttons; - getmaxyx(window, rows, cols); - y = rows - 2; + y = d->h - 2; - startx = cols/2 - buttons_width(bs)/2; + newmargin = d->w - BORDERS - (d->bs.nbuttons * d->bs.sizebutton); + newmargin /= (d->bs.nbuttons + 1); + newmargin = MIN(newmargin, t.button.maxmargin); + if (newmargin == 0) { + margin = t.button.minmargin; + wbuttons = buttons_min_width(&d->bs); + } else { + margin = newmargin; + wbuttons = d->bs.nbuttons * d->bs.sizebutton; + wbuttons += (d->bs.nbuttons + 1) * margin; + } - for (i = 0; i < (int)bs.nbuttons; i++) { - x = i * (bs.sizebutton + t.button.hmargin); - draw_button(window, y, startx + x, bs.sizebutton, bs.label[i], - i == bs.curr, shortcut); + startx = d->w/2 - wbuttons/2 + newmargin; + for (i = 0; i < (int)d->bs.nbuttons; i++) { + x = i * (d->bs.sizebutton + margin); + draw_button(d->widget, y, startx + x, d->bs.sizebutton, + d->bs.label[i], d->bs.first[i], i == d->bs.curr, + d->bs.shortcut); } } void -get_buttons(struct bsddialog_conf *conf, struct buttons *bs, - const char *yesoklabel, const char *nocancellabel) +set_buttons(struct dialog *d, bool shortcut, const char *oklabel, + const char *cancellabel) { int i; #define SIZEBUTTON 8 -#define DEFAULT_BUTTON_LABEL BUTTON_OK_LABEL +#define DEFAULT_BUTTON_LABEL OK_LABEL #define DEFAULT_BUTTON_VALUE BSDDIALOG_OK + wchar_t first; - bs->nbuttons = 0; - bs->curr = 0; - bs->sizebutton = 0; + d->bs.nbuttons = 0; + d->bs.curr = 0; + d->bs.sizebutton = 0; + d->bs.shortcut = shortcut; - if (yesoklabel != NULL && conf->button.without_ok == false) { - bs->label[0] = conf->button.ok_label != NULL ? - conf->button.ok_label : yesoklabel; - bs->value[0] = BSDDIALOG_OK; - bs->nbuttons += 1; + if (d->conf->button.left1_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.left1_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT1; + d->bs.nbuttons += 1; } - if (conf->button.with_extra) { - bs->label[bs->nbuttons] = conf->button.extra_label != NULL ? - conf->button.extra_label : "Extra"; - bs->value[bs->nbuttons] = BSDDIALOG_EXTRA; - bs->nbuttons += 1; + if (d->conf->button.left2_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.left2_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT2; + d->bs.nbuttons += 1; } - if (nocancellabel != NULL && conf->button.without_cancel == false) { - bs->label[bs->nbuttons] = conf->button.cancel_label ? - conf->button.cancel_label : nocancellabel; - bs->value[bs->nbuttons] = BSDDIALOG_CANCEL; - if (conf->button.default_cancel) - bs->curr = bs->nbuttons; - bs->nbuttons += 1; + if (d->conf->button.left3_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.left3_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT3; + d->bs.nbuttons += 1; } - if (conf->button.with_help) { - bs->label[bs->nbuttons] = conf->button.help_label != NULL ? - conf->button.help_label : "Help"; - bs->value[bs->nbuttons] = BSDDIALOG_HELP; - bs->nbuttons += 1; + if (oklabel != NULL && d->conf->button.without_ok == false) { + d->bs.label[d->bs.nbuttons] = d->conf->button.ok_label != NULL ? + d->conf->button.ok_label : oklabel; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_OK; + d->bs.nbuttons += 1; } - if (conf->button.generic1_label != NULL) { - bs->label[bs->nbuttons] = conf->button.generic1_label; - bs->value[bs->nbuttons] = BSDDIALOG_GENERIC1; - bs->nbuttons += 1; + if (d->conf->button.with_extra) { + d->bs.label[d->bs.nbuttons] = d->conf->button.extra_label != NULL ? + d->conf->button.extra_label : "Extra"; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_EXTRA; + d->bs.nbuttons += 1; } - if (conf->button.generic2_label != NULL) { - bs->label[bs->nbuttons] = conf->button.generic2_label; - bs->value[bs->nbuttons] = BSDDIALOG_GENERIC2; - bs->nbuttons += 1; + if (cancellabel != NULL && d->conf->button.without_cancel == false) { + d->bs.label[d->bs.nbuttons] = d->conf->button.cancel_label ? + d->conf->button.cancel_label : cancellabel; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_CANCEL; + if (d->conf->button.default_cancel) + d->bs.curr = d->bs.nbuttons; + d->bs.nbuttons += 1; } - if (bs->nbuttons == 0) { - bs->label[0] = DEFAULT_BUTTON_LABEL; - bs->value[0] = DEFAULT_BUTTON_VALUE; - bs->nbuttons = 1; + if (d->conf->button.with_help) { + d->bs.label[d->bs.nbuttons] = d->conf->button.help_label != NULL ? + d->conf->button.help_label : "Help"; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_HELP; + d->bs.nbuttons += 1; } - if (conf->button.default_label != NULL) { - for (i = 0; i < (int)bs->nbuttons; i++) { - if (strcmp(conf->button.default_label, - bs->label[i]) == 0) - bs->curr = i; - } + if (d->conf->button.right1_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.right1_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT1; + d->bs.nbuttons += 1; } - bs->sizebutton = MAX(SIZEBUTTON - 2, strlen(bs->label[0])); - for (i = 1; i < (int)bs->nbuttons; i++) - bs->sizebutton = MAX(bs->sizebutton, strlen(bs->label[i])); - bs->sizebutton += 2; -} + if (d->conf->button.right2_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.right2_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT2; + d->bs.nbuttons += 1; + } -int buttons_width(struct buttons bs) -{ - unsigned int width; + if (d->conf->button.right3_label != NULL) { + d->bs.label[d->bs.nbuttons] = d->conf->button.right3_label; + d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT3; + d->bs.nbuttons += 1; + } + + if (d->bs.nbuttons == 0) { + d->bs.label[0] = DEFAULT_BUTTON_LABEL; + d->bs.value[0] = DEFAULT_BUTTON_VALUE; + d->bs.nbuttons = 1; + } - width = bs.nbuttons * bs.sizebutton; - if (bs.nbuttons > 0) - width += (bs.nbuttons - 1) * t.button.hmargin; + for (i = 0; i < (int)d->bs.nbuttons; i++) { + mbtowc(&first, d->bs.label[i], MB_CUR_MAX); + d->bs.first[i] = first; + } - return (width); + if (d->conf->button.default_label != NULL) { + for (i = 0; i < (int)d->bs.nbuttons; i++) { + if (strcmp(d->conf->button.default_label, + d->bs.label[i]) == 0) + d->bs.curr = i; + } + } + + d->bs.sizebutton = MAX(SIZEBUTTON - 2, strcols(d->bs.label[0])); + for (i = 1; i < (int)d->bs.nbuttons; i++) + d->bs.sizebutton = MAX(d->bs.sizebutton, strcols(d->bs.label[i])); + d->bs.sizebutton += 2; } -bool shortcut_buttons(int key, struct buttons *bs) +bool shortcut_buttons(wint_t key, struct buttons *bs) { bool match; unsigned int i; match = false; for (i = 0; i < bs->nbuttons; i++) { - if (tolower(key) == tolower(bs->label[i][0])) { + if (towlower(key) == towlower(bs->first[i])) { bs->curr = i; match = true; break; @@ -258,233 +417,192 @@ bool shortcut_buttons(int key, struct buttons *bs) return (match); } -/* Text */ -static bool is_text_attr(const char *text) +/* + * -4- (Auto) Sizing and (Auto) Position + */ +static int widget_max_height(struct bsddialog_conf *conf) { - if (strnlen(text, 3) < 3) - return (false); - - if (text[0] != '\\' || text[1] != 'Z') - return (false); - - return (strchr("nbBrRuU01234567", text[2]) == NULL ? false : true); -} + int maxheight; -static bool check_set_text_attr(WINDOW *win, char *text) -{ - if (is_text_attr(text) == false) - return (false); + maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.y : SCREENLINES; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - shadow <= 0"); - if ((text[2] - '0') >= 0 && (text[2] - '0') < 8) { - wattron(win, bsddialog_color(text[2] - '0', COLOR_WHITE, 0)); - return (true); + if (conf->y != BSDDIALOG_CENTER && conf->auto_topmargin > 0) + RETURN_ERROR("conf.y > 0 and conf->auto_topmargin > 0"); + else if (conf->y == BSDDIALOG_CENTER) { + maxheight -= conf->auto_topmargin; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - top " + "margins <= 0"); + } else if (conf->y > 0) { + maxheight -= conf->y; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - " + "shadow - y <= 0"); } - switch (text[2]) { - case 'n': - wattron(win, t.dialog.color); - wattrset(win, A_NORMAL); - break; - case 'b': - wattron(win, A_BOLD); - break; - case 'B': - wattroff(win, A_BOLD); - break; - case 'r': - wattron(win, A_REVERSE); - break; - case 'R': - wattroff(win, A_REVERSE); - break; - case 'u': - wattron(win, A_UNDERLINE); - break; - case 'U': - wattroff(win, A_UNDERLINE); - break; - } + maxheight -= conf->auto_downmargin; + if (maxheight <= 0) + RETURN_ERROR("Terminal too small, screen lines - Down margins " + "<= 0"); - return (true); + return (maxheight); } -static void -print_string(WINDOW *win, int *rows, int cols, int *y, int *x, char *str, - bool color) +static int widget_max_width(struct bsddialog_conf *conf) { - int i, j, len, reallen; + int maxwidth; - len = reallen = strlen(str); - if (color) { - i=0; - while (i < len) { - if (is_text_attr(str+i)) - reallen -= 3; - i++; - } - } + maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.x : SCREENCOLS; + if (maxwidth <= 0) + RETURN_ERROR("Terminal too small, screen cols - shadow <= 0"); - i = 0; - while (i < len) { - if (*x + reallen > cols) { - *y = (*x != 0 ? *y+1 : *y); - if (*y >= *rows) { - *rows = *y + 1; - wresize(win, *rows, cols); - } - *x = 0; - } - j = *x; - while (j < cols && i < len) { - if (color && check_set_text_attr(win, str+i)) { - i += 3; - } else { - mvwaddch(win, *y, j, str[i]); - i++; - reallen--; - j++; - *x = j; - } - } + if (conf->x > 0) { + maxwidth -= conf->x; + if (maxwidth <= 0) + RETURN_ERROR("Terminal too small, screen cols - shadow " + "- x <= 0"); } + + return (maxwidth); } -static int -print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text) +static bool is_wtext_attr(const wchar_t *wtext) { - bool loop; - int i, j, z, rows, cols, x, y, tablen; - char *string; - - if ((string = malloc(strlen(text) + 1)) == NULL) - RETURN_ERROR("Cannot build (analyze) text"); - - getmaxyx(pad, rows, cols); - tablen = (conf->text.tablen == 0) ? TABLEN : (int)conf->text.tablen; + bool att; - i = j = x = y = 0; - loop = true; - while (loop) { - string[j] = text[i]; - - if (strchr("\n\t ", string[j]) != NULL || string[j] == '\0') { - string[j] = '\0'; - print_string(pad, &rows, cols, &y, &x, string, - conf->text.highlight); - } - - switch (text[i]) { - case '\0': - loop = false; - break; - case '\n': - x = 0; - y++; - j = -1; - break; - case '\t': - for (z = 0; z < tablen; z++) { - if (x >= cols) { - x = 0; - y++; - } - x++; - } - j = -1; - break; - case ' ': - x++; - if (x >= cols) { - x = 0; - y++; - } - j = -1; - } + if (wcsnlen(wtext, 3) < 3) + return (false); + if (wtext[0] != L'\\' || wtext[1] != L'Z') + return (false); - if (y >= rows) { - rows = y + 1; - wresize(pad, rows, cols); - } + att = wcschr(L"nbBdDkKrRsSuU01234567", wtext[2]) == NULL ? false : true; - j++; - i++; - } + return (att); +} - free(string); +#define NL -1 +#define WS -2 +#define TB -3 - return (0); -} +struct textproperties { + int nword; + int *words; + uint8_t *wletters; + int maxwordcols; + int maxline; + bool hasnewline; +}; -/* Autosize */ static int -text_autosize(struct bsddialog_conf *conf, const char *text, int maxrows, - int mincols, bool increasecols, int *h, int *w) +text_properties(struct bsddialog_conf *conf, const char *text, + struct textproperties *tp) { - int i, j, z, x, y; - int tablen, wordlen, maxwordlen, nword, maxwords, line, maxwidth; - int *words; -#define NL -1 -#define WS -2 + int i, l, currlinecols, maxwords, wtextlen, tablen, wordcols; + wchar_t *wtext; + + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; maxwords = 1024; - if ((words = calloc(maxwords, sizeof(int))) == NULL) + if ((tp->words = calloc(maxwords, sizeof(int))) == NULL) RETURN_ERROR("Cannot alloc memory for text autosize"); - tablen = (conf->text.tablen == 0) ? TABLEN : (int)conf->text.tablen; - maxwidth = widget_max_width(conf) - HBORDERS - TEXTHMARGINS; - - nword = 0; - wordlen = 0; - maxwordlen = 0; - i=0; - while (true) { - if (conf->text.highlight && is_text_attr(text + i)) { - i += 3; + if ((wtext = alloc_mbstows(text)) == NULL) + RETURN_ERROR("Cannot allocate/autosize text in wchar_t*"); + wtextlen = wcslen(wtext); + if ((tp->wletters = calloc(wtextlen, sizeof(uint8_t))) == NULL) + RETURN_ERROR("Cannot allocate wletters for text autosizing"); + + tp->nword = 0; + tp->maxline = 0; + tp->maxwordcols = 0; + tp->hasnewline = false; + currlinecols = 0; + wordcols = 0; + l = 0; + for (i = 0; i < wtextlen; i++) { + if (conf->text.escape && is_wtext_attr(wtext + i)) { + i += 2; /* +1 for update statement */ continue; } - if (nword + tablen + 1 >= maxwords) { + if (tp->nword + 1 >= maxwords) { maxwords += 1024; - words = realloc(words, maxwords * sizeof(int)); - if (words == NULL) + tp->words = realloc(tp->words, maxwords * sizeof(int)); + if (tp->words == NULL) RETURN_ERROR("Cannot realloc memory for text " "autosize"); } - if (text[i] == '\0') { - words[nword] = wordlen; - maxwordlen = MAX(wordlen, maxwordlen); - break; - } - - if (strchr("\t\n ", text[i]) != NULL) { - maxwordlen = MAX(wordlen, maxwordlen); + if (wcschr(L"\t\n ", wtext[i]) != NULL) { + tp->maxwordcols = MAX(wordcols, tp->maxwordcols); - if (wordlen != 0) { - words[nword] = wordlen; - nword++; - wordlen = 0; + if (wordcols != 0) { + /* line */ + currlinecols += wordcols; + /* word */ + tp->words[tp->nword] = wordcols; + tp->nword += 1; + wordcols = 0; } - if (text[i] == '\t') { - for (j = 0; j < tablen; j++) - words[nword + j] = 1; - nword += tablen; - } else { - words[nword] = text[i] == '\n' ? NL : WS; - nword++; + switch (wtext[i]) { + case L'\t': + /* line */ + currlinecols += tablen; + /* word */ + tp->words[tp->nword] = TB; + break; + case L'\n': + /* line */ + tp->hasnewline = true; + tp->maxline = MAX(tp->maxline, currlinecols); + currlinecols = 0; + /* word */ + tp->words[tp->nword] = NL; + break; + case L' ': + /* line */ + currlinecols += 1; + /* word */ + tp->words[tp->nword] = WS; + break; } + tp->nword += 1; + } else { + tp->wletters[l] = wcwidth(wtext[i]); + wordcols += tp->wletters[l]; + l++; } - else - wordlen++; - - i++; } + /* word */ + if (wordcols != 0) { + tp->words[tp->nword] = wordcols; + tp->nword += 1; + tp->maxwordcols = MAX(wordcols, tp->maxwordcols); + } + /* line */ + tp->maxline = MAX(tp->maxline, currlinecols); + + free(wtext); + + return (0); +} + +static int +text_autosize(struct bsddialog_conf *conf, struct textproperties *tp, + int maxrows, int mincols, bool increasecols, int *h, int *w) +{ + int i, j, x, y, z, l, line, maxwidth, tablen; + + maxwidth = widget_max_width(conf) - BORDERS - TEXTHMARGINS; + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; if (increasecols) { - mincols = MAX(mincols, maxwordlen); + mincols = MAX(mincols, tp->maxwordcols); mincols = MAX(mincols, - (int)conf->auto_minwidth - HBORDERS - TEXTHMARGINS); + (int)conf->auto_minwidth - BORDERS - TEXTHMARGINS); mincols = MIN(mincols, maxwidth); } @@ -492,26 +610,50 @@ text_autosize(struct bsddialog_conf *conf, const char *text, int maxrows, x = 0; y = 1; line=0; - for (i = 0; i <= nword; i++) { - if (words[i] == NL) { + l = 0; + for (i = 0; i < tp->nword; i++) { + switch (tp->words[i]) { + case TB: + for (j = 0; j < tablen; j++) { + if (x >= mincols) { + x = 0; + y++; + } + x++; + } + break; + case NL: y++; x = 0; - } - else if (words[i] == WS) { + break; + case WS: x++; if (x >= mincols) { x = 0; y++; } - } - else { - if (words[i] + x <= mincols) - x += words[i]; - else { - for (z = words[i]; z > 0; ) { - y++; - x = MIN(mincols, z); - z -= x; + break; + default: + if (tp->words[i] + x <= mincols) { + x += tp->words[i]; + for (z = 0 ; z != tp->words[i]; l++ ) + z += tp->wletters[l]; + } else if (tp->words[i] <= mincols) { + y++; + x = tp->words[i]; + for (z = 0 ; z != tp->words[i]; l++ ) + z += tp->wletters[l]; + } else { + for (j = tp->words[i]; j > 0; ) { + y = (x == 0) ? y : y + 1; + z = 0; + while (z != j && z < mincols) { + z += tp->wletters[l]; + l++; + } + x = z; + line = MAX(line, x); + j -= z; } } } @@ -520,141 +662,111 @@ text_autosize(struct bsddialog_conf *conf, const char *text, int maxrows, if (increasecols == false) break; - if (y <= maxrows || mincols >= maxwidth) + if (mincols >= maxwidth) + break; + if (line >= y * (int)conf->text.cols_per_row && y <= maxrows) break; mincols++; } - *h = (nword == 0 && words[0] == 0) ? 0 : y; + *h = (tp->nword == 0) ? 0 : y; *w = MIN(mincols, line); /* wtext can be less than mincols */ - free(words); - return (0); } -int +static int text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text, struct buttons *bs, int rowsnotext, int startwtext, int *htext, int *wtext) { - int wbuttons, maxhtext; bool changewtext; + int wbuttons, maxhtext; + struct textproperties tp; wbuttons = 0; - if (bs != NULL) - wbuttons = buttons_width(*bs); + if (bs->nbuttons > 0) + wbuttons = buttons_min_width(bs); + /* Rows */ + if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) { + maxhtext = widget_max_height(conf) - BORDERS - rowsnotext; + } else { /* fixed */ + maxhtext = rows - BORDERS - rowsnotext; + } + if (bs->nbuttons > 0) + maxhtext -= 2; + if (maxhtext <= 0) + maxhtext = 1; /* text_autosize() computes always htext */ + + /* Cols */ if (cols == BSDDIALOG_AUTOSIZE) { startwtext = MAX(startwtext, wbuttons - TEXTHMARGINS); changewtext = true; } else if (cols == BSDDIALOG_FULLSCREEN) { - startwtext = widget_max_width(conf) - VBORDERS - TEXTHMARGINS; + startwtext = widget_max_width(conf) - BORDERS - TEXTHMARGINS; changewtext = false; } else { /* fixed */ - startwtext = cols - VBORDERS - TEXTHMARGINS; + startwtext = cols - BORDERS - TEXTHMARGINS; changewtext = false; } - if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) { - maxhtext = widget_max_height(conf) - VBORDERS - rowsnotext; - if (bs != NULL) - maxhtext -= 2; - } else { /* fixed */ - maxhtext = rows - VBORDERS - rowsnotext; - if (bs != NULL) - maxhtext -= 2; - } - if (startwtext <= 0 && changewtext) startwtext = 1; - if (maxhtext <= 0 || startwtext <= 0) { - *htext = *wtext = 0; - return (0); - } - if (text_autosize(conf, text, maxhtext, startwtext, changewtext, - htext, wtext) != 0) + /* Sizing calculation */ + if (text_properties(conf, text, &tp) != 0) + return (BSDDIALOG_ERROR); + if (tp.nword > 0 && startwtext <= 0) + RETURN_FMTERROR("(fixed cols or fullscreen) " + "needed at least %d cols to draw text", + BORDERS + TEXTHMARGINS + 1); + if (text_autosize(conf, &tp, maxhtext, startwtext, changewtext, htext, + wtext) != 0) return (BSDDIALOG_ERROR); - return (0); -} - -int widget_max_height(struct bsddialog_conf *conf) -{ - int maxheight; - - maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.h : SCREENLINES; - if (maxheight <= 0) - RETURN_ERROR("Terminal too small, screen lines - shadow <= 0"); - - if (conf->y > 0) { - maxheight -= conf->y; - if (maxheight <= 0) - RETURN_ERROR("Terminal too small, screen lines - " - "shadow - y <= 0"); - } - - return (maxheight); -} - -int widget_max_width(struct bsddialog_conf *conf) -{ - int maxwidth; - - maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.w : SCREENCOLS; - if (maxwidth <= 0) - RETURN_ERROR("Terminal too small, screen cols - shadow <= 0"); + free(tp.words); + free(tp.wletters); - if (conf->x > 0) { - maxwidth -= conf->x; - if (maxwidth <= 0) - RETURN_ERROR("Terminal too small, screen cols - shadow " - "- x <= 0"); - } - - return (maxwidth); + return (0); } -int -widget_min_height(struct bsddialog_conf *conf, int htext, int minwidget, +static int +widget_min_height(struct bsddialog_conf *conf, int htext, int hnotext, bool withbuttons) { int min; - min = 0; - - /* buttons */ - if (withbuttons) - min += 2; /* buttons and border */ + /* dialog borders */ + min = BORDERS; /* text */ min += htext; - /* specific widget min height */ - min += minwidget; + /* specific widget lines without text */ + min += hnotext; + + /* buttons */ + if (withbuttons) + min += HBUTTONS; /* buttons and their up-border */ - /* dialog borders */ - min += HBORDERS; /* conf.auto_minheight */ min = MAX(min, (int)conf->auto_minheight); - /* avoid terminal overflow */ - min = MIN(min, widget_max_height(conf)); return (min); } -int +static int widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, struct buttons *bs) { - int min, delimtitle; + int min, delimtitle, wbottomtitle, wtitle; min = 0; /* buttons */ - if (bs != NULL) - min += buttons_width(*bs); + if (bs->nbuttons > 0) + min += buttons_min_width(bs); /* text */ if (wtext > 0) @@ -666,19 +778,20 @@ widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, /* title */ if (conf->title != NULL) { delimtitle = t.dialog.delimtitle ? 2 : 0; - min = MAX(min, (int)strlen(conf->title) + 2 + delimtitle); + wtitle = strcols(conf->title); + min = MAX(min, wtitle + 2 + delimtitle); } /* bottom title */ - if (conf->bottomtitle != NULL) - min = MAX(min, (int)strlen(conf->bottomtitle) + 4); + if (conf->bottomtitle != NULL) { + wbottomtitle = strcols(conf->bottomtitle); + min = MAX(min, wbottomtitle + 4); + } /* dialog borders */ - min += VBORDERS; + min += BORDERS; /* conf.auto_minwidth */ min = MAX(min, (int)conf->auto_minwidth); - /* avoid terminal overflow */ - min = MIN(min, widget_max_width(conf)); return (min); } @@ -695,11 +808,8 @@ set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w) *h = maxheight; else if (rows < BSDDIALOG_FULLSCREEN) RETURN_ERROR("Negative (less than -1) height"); - else if (rows > BSDDIALOG_AUTOSIZE) { - if ((*h = rows) > maxheight) - RETURN_ERROR("Height too big (> terminal height - " - "shadow)"); - } + else if (rows > BSDDIALOG_AUTOSIZE) /* fixed rows */ + *h = MIN(rows, maxheight); /* rows is at most maxheight */ /* rows == AUTOSIZE: each widget has to set its size */ if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR) @@ -709,21 +819,78 @@ set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w) *w = maxwidth; else if (cols < BSDDIALOG_FULLSCREEN) RETURN_ERROR("Negative (less than -1) width"); - else if (cols > BSDDIALOG_AUTOSIZE) { - if ((*w = cols) > maxwidth) - RETURN_ERROR("Width too big (> terminal width - " - "shadow)"); - } + else if (cols > BSDDIALOG_AUTOSIZE) /* fixed cols */ + *w = MIN(cols, maxwidth); /* cols is at most maxwidth */ /* cols == AUTOSIZE: each widget has to set its size */ return (0); } int +set_widget_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, + int *w, const char *text, int *rowstext, struct buttons *bs, int hnotext, + int minw) +{ + int htext, wtext; + + if (rows == BSDDIALOG_AUTOSIZE || cols == BSDDIALOG_AUTOSIZE || + rowstext != NULL) { + if (text_size(conf, rows, cols, text, bs, hnotext, minw, + &htext, &wtext) != 0) + return (BSDDIALOG_ERROR); + if (rowstext != NULL) + *rowstext = htext; + } + + if (rows == BSDDIALOG_AUTOSIZE) { + *h = widget_min_height(conf, htext, hnotext, bs->nbuttons > 0); + *h = MIN(*h, widget_max_height(conf)); + } + + if (cols == BSDDIALOG_AUTOSIZE) { + *w = widget_min_width(conf, wtext, minw, bs); + *w = MIN(*w, widget_max_width(conf)); + } + + return (0); +} + +int widget_checksize(int h, int w, struct buttons *bs, int hnotext, int minw) +{ + int minheight, minwidth; + + minheight = BORDERS + hnotext; + if (bs->nbuttons > 0) + minheight += HBUTTONS; + if (h < minheight) + RETURN_FMTERROR("Current rows: %d, needed at least: %d", + h, minheight); + + minwidth = 0; + if (bs->nbuttons > 0) + minwidth = buttons_min_width(bs); + minwidth = MAX(minwidth, minw); + minwidth += BORDERS; + if (w < minwidth) + RETURN_FMTERROR("Current cols: %d, nedeed at least %d", + w, minwidth); + + return (0); +} + +int set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) { - if (conf->y == BSDDIALOG_CENTER) - *y = SCREENLINES/2 - (h + t.shadow.h)/2; + int hshadow = conf->shadow ? (int)t.shadow.y : 0; + int wshadow = conf->shadow ? (int)t.shadow.x : 0; + + if (conf->y == BSDDIALOG_CENTER) { + *y = SCREENLINES/2 - (h + hshadow)/2; + if (*y < (int)conf->auto_topmargin) + *y = conf->auto_topmargin; + if (*y + h + hshadow > SCREENLINES - (int)conf->auto_downmargin) + *y = SCREENLINES - h - hshadow - conf->auto_downmargin; + } else if (conf->y < BSDDIALOG_CENTER) RETURN_ERROR("Negative begin y (less than -1)"); else if (conf->y >= SCREENLINES) @@ -731,13 +898,13 @@ set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) else *y = conf->y; - if ((*y + h + (conf->shadow ? (int) t.shadow.h : 0)) > SCREENLINES) + if (*y + h + hshadow > SCREENLINES) RETURN_ERROR("The lower of the box under the terminal " "(begin Y + height (+ shadow) > terminal lines)"); if (conf->x == BSDDIALOG_CENTER) - *x = SCREENCOLS/2 - (w + t.shadow.w)/2; + *x = SCREENCOLS/2 - (w + wshadow)/2; else if (conf->x < BSDDIALOG_CENTER) RETURN_ERROR("Negative begin x (less than -1)"); else if (conf->x >= SCREENCOLS) @@ -745,228 +912,456 @@ set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w) else *x = conf->x; - if ((*x + w + (conf->shadow ? (int) t.shadow.w : 0)) > SCREENCOLS) + if ((*x + w + wshadow) > SCREENCOLS) RETURN_ERROR("The right of the box over the terminal " "(begin X + width (+ shadow) > terminal cols)"); return (0); } -/* Widgets builders */ -void -draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols, - enum elevation elev) +int dialog_size_position(struct dialog *d, int hnotext, int minw, int *htext) +{ + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, htext, &d->bs, hnotext, minw) != 0) + return (BSDDIALOG_ERROR); + if (widget_checksize(d->h, d->w, &d->bs, hnotext, minw) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); + + return (0); +} + +/* + * -5- Widget components and utilities + */ +int hide_dialog(struct dialog *d) { + WINDOW *clear; + + if ((clear = newwin(d->h, d->w, d->y, d->x)) == NULL) + RETURN_ERROR("Cannot hide the widget"); + wbkgd(clear, t.screen.color); + wrefresh(clear); + + if (d->conf->shadow) { + mvwin(clear, d->y + t.shadow.y, d->x + t.shadow.x); + wrefresh(clear); + } + + delwin(clear); + + return (0); +} + +int f1help_dialog(struct bsddialog_conf *conf) +{ + int output; + struct bsddialog_conf hconf; + + bsddialog_initconf(&hconf); + hconf.title = "HELP"; + hconf.button.ok_label = "EXIT"; + hconf.clear = true; + hconf.ascii_lines = conf->ascii_lines; + hconf.no_lines = conf->no_lines; + hconf.shadow = conf->shadow; + hconf.text.escape = conf->text.escape; + + output = BSDDIALOG_OK; + if (conf->key.f1_message != NULL) + output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0); + + if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL) + output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0); + + return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0); +} + +void draw_borders(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev) +{ + int h, w; int leftcolor, rightcolor; - int ls, rs, ts, bs, tl, tr, bl, br, ltee, rtee; + cchar_t *ls, *rs, *ts, *bs, *tl, *tr, *bl, *br; + cchar_t hline, vline, corner; if (conf->no_lines) return; if (conf->ascii_lines) { - ls = rs = '|'; - ts = bs = '-'; - tl = tr = bl = br = ltee = rtee = '+'; + setcchar(&hline, L"|", 0, 0, NULL); + ls = rs = &hline; + setcchar(&vline, L"-", 0, 0, NULL); + ts = bs = &vline; + setcchar(&corner, L"+", 0, 0, NULL); + tl = tr = bl = br = &corner; } else { - ls = rs = ACS_VLINE; - ts = bs = ACS_HLINE; - tl = ACS_ULCORNER; - tr = ACS_URCORNER; - bl = ACS_LLCORNER; - br = ACS_LRCORNER; - ltee = ACS_LTEE; - rtee = ACS_RTEE; + ls = rs = WACS_VLINE; + ts = bs = WACS_HLINE; + tl = WACS_ULCORNER; + tr = WACS_URCORNER; + bl = WACS_LLCORNER; + br = WACS_LRCORNER; } - leftcolor = elev == RAISED ? + getmaxyx(win, h, w); + leftcolor = (elev == RAISED) ? t.dialog.lineraisecolor : t.dialog.linelowercolor; - rightcolor = elev == RAISED ? + rightcolor = (elev == RAISED) ? t.dialog.linelowercolor : t.dialog.lineraisecolor; + wattron(win, leftcolor); - wborder(win, ls, rs, ts, bs, tl, tr, bl, br); + wborder_set(win, ls, rs, ts, bs, tl, tr, bl, br); wattroff(win, leftcolor); wattron(win, rightcolor); - mvwaddch(win, 0, cols-1, tr); - mvwvline(win, 1, cols-1, rs, rows-2); - mvwaddch(win, rows-1, cols-1, br); - mvwhline(win, rows-1, 1, bs, cols-2); + mvwadd_wch(win, 0, w-1, tr); + mvwvline_set(win, 1, w-1, rs, h-2); + mvwadd_wch(win, h-1, w-1, br); + mvwhline_set(win, h-1, 1, bs, w-2); wattroff(win, rightcolor); } -WINDOW * -new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols, +void +update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w, enum elevation elev) { - WINDOW *win; + wclear(win); + wresize(win, h, w); + mvwin(win, y, x); + draw_borders(conf, win, elev); +} - if ((win = newwin(rows, cols, y, x)) == NULL) { - set_error_string("Cannot build boxed window"); - return (NULL); - } +void +rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext) +{ + pnoutrefresh(d->textpad, ytext, xtext, + d->y + BORDER + upnotext, + d->x + BORDER + TEXTHMARGIN, + d->y + d->h - 1 - downnotext - BORDER, + d->x + d->w - TEXTHMARGIN - BORDER); +} - wbkgd(win, t.dialog.color); +/* + * -6- Dialog init/build, update/draw, destroy + */ +void end_dialog(struct dialog *d) +{ + if (d->conf->sleep > 0) + sleep(d->conf->sleep); - draw_borders(conf, win, rows, cols, elev); + delwin(d->textpad); + delwin(d->widget); + if (d->conf->shadow) + delwin(d->shadow); - return (win); + if (d->conf->clear) + hide_dialog(d); + + if (d->conf->get_height != NULL) + *d->conf->get_height = d->h; + if (d->conf->get_width != NULL) + *d->conf->get_width = d->w; } -static int -draw_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, - WINDOW *textpad, const char *text, struct buttons *bs, bool shortcutbuttons) +static bool check_set_wtext_attr(WINDOW *win, wchar_t *wtext) { - int h, w, ts, ltee, rtee; + enum bsddialog_color bg; + + if (is_wtext_attr(wtext) == false) + return (false); + + if ((wtext[2] >= L'0') && (wtext[2] <= L'7')) { + bsddialog_color_attrs(t.dialog.color, NULL, &bg, NULL); + wattron(win, bsddialog_color(wtext[2] - L'0', bg, 0)); + return (true); + } - ts = conf->ascii_lines ? '-' : ACS_HLINE; - ltee = conf->ascii_lines ? '+' : ACS_LTEE; - rtee = conf->ascii_lines ? '+' : ACS_RTEE; + switch (wtext[2]) { + case L'n': + wattron(win, t.dialog.color); + wattrset(win, A_NORMAL); + break; + case L'b': + wattron(win, A_BOLD); + break; + case L'B': + wattroff(win, A_BOLD); + break; + case L'd': + wattron(win, A_DIM); + break; + case L'D': + wattroff(win, A_DIM); + break; + case L'k': + wattron(win, A_BLINK); + break; + case L'K': + wattroff(win, A_BLINK); + break; + case L'r': + wattron(win, A_REVERSE); + break; + case L'R': + wattroff(win, A_REVERSE); + break; + case L's': + wattron(win, A_STANDOUT); + break; + case L'S': + wattroff(win, A_STANDOUT); + break; + case L'u': + wattron(win, A_UNDERLINE); + break; + case L'U': + wattroff(win, A_UNDERLINE); + break; + } - getmaxyx(widget, h, w); + return (true); +} - if (conf->shadow) - wnoutrefresh(shadow); +static void +print_string(WINDOW *win, int *rows, int cols, int *y, int *x, wchar_t *str, + bool color) +{ + int charwidth, i, j, strlen, strwidth; + wchar_t ws[2]; - draw_borders(conf, widget, h, w, RAISED); + ws[1] = L'\0'; - if (conf->title != NULL) { - if (t.dialog.delimtitle && conf->no_lines == false) { - wattron(widget, t.dialog.lineraisecolor); - mvwaddch(widget, 0, w/2-strlen(conf->title)/2-1, rtee); - wattroff(widget, t.dialog.lineraisecolor); - } - wattron(widget, t.dialog.titlecolor); - mvwaddstr(widget, 0, w/2 - strlen(conf->title)/2, conf->title); - wattroff(widget, t.dialog.titlecolor); - if (t.dialog.delimtitle && conf->no_lines == false) { - wattron(widget, t.dialog.lineraisecolor); - waddch(widget, ltee); - wattroff(widget, t.dialog.lineraisecolor); + strlen = wcslen(str); + if (color) { + strwidth = 0; + i=0; + while (i < strlen) { + if (is_wtext_attr(str+i) == false) { + strwidth += wcwidth(str[i]); + i++; + } else { + i += 3; + } } - } + } else + strwidth = wcswidth(str, strlen); - if (bs != NULL) { - if (conf->no_lines == false) { - wattron(widget, t.dialog.lineraisecolor); - mvwaddch(widget, h-3, 0, ltee); - mvwhline(widget, h-3, 1, ts, w-2); - wattroff(widget, t.dialog.lineraisecolor); + i = 0; + while (i < strlen) { + if (*x + strwidth > cols) { + if (*x != 0) + *y = *y + 1; + if (*y >= *rows) { + *rows = *y + 1; + wresize(win, *rows, cols); + } + *x = 0; + } + j = *x; + while (i < strlen) { + if (color && check_set_wtext_attr(win, str+i)) { + i += 3; + continue; + } - wattron(widget, t.dialog.linelowercolor); - mvwaddch(widget, h-3, w-1, rtee); - wattroff(widget, t.dialog.linelowercolor); + charwidth = wcwidth(str[i]); + if (j + wcwidth(str[i]) > cols) + break; + /* inline mvwaddwch() for efficiency */ + ws[0] = str[i]; + mvwaddwstr(win, *y, j, ws); + strwidth -= charwidth; + j += charwidth; + *x = j; + i++; } - draw_buttons(widget, *bs, shortcutbuttons); } +} - if (conf->bottomtitle != NULL) { - wattron(widget, t.dialog.bottomtitlecolor); - wmove(widget, h - 1, w/2 - strlen(conf->bottomtitle)/2 - 1); - waddch(widget, ' '); - waddstr(widget, conf->bottomtitle); - waddch(widget, ' '); - wattroff(widget, t.dialog.bottomtitlecolor); - } +static int +print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text) +{ + bool loop; + int i, j, z, rows, cols, x, y, tablen; + wchar_t *wtext, *string; - wnoutrefresh(widget); + if ((wtext = alloc_mbstows(text)) == NULL) + RETURN_ERROR("Cannot allocate/print text in wchar_t*"); - if (textpad != NULL && text != NULL) /* textbox */ - if (print_textpad(conf, textpad, text) !=0) - return (BSDDIALOG_ERROR); + if ((string = calloc(wcslen(wtext) + 1, sizeof(wchar_t))) == NULL) + RETURN_ERROR("Cannot build (analyze) text"); - return (0); -} + getmaxyx(pad, rows, cols); + tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen; -int -update_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, - int y, int x, int h, int w, WINDOW *textpad, const char *text, - struct buttons *bs, bool shortcutbuttons) -{ - int error; + i = j = x = y = 0; + loop = true; + while (loop) { + string[j] = wtext[i]; - if (conf->shadow) { - wclear(shadow); - mvwin(shadow, y + t.shadow.h, x + t.shadow.w); - wresize(shadow, h, w); - } + if (wcschr(L"\n\t ", string[j]) != NULL || string[j] == L'\0') { + string[j] = L'\0'; + print_string(pad, &rows, cols, &y, &x, string, + conf->text.escape); + } - wclear(widget); - mvwin(widget, y, x); - wresize(widget, h, w); + switch (wtext[i]) { + case L'\0': + loop = false; + break; + case L'\n': + x = 0; + y++; + j = -1; + break; + case L'\t': + for (z = 0; z < tablen; z++) { + if (x >= cols) { + x = 0; + y++; + } + x++; + } + j = -1; + break; + case L' ': + x++; + if (x >= cols) { + x = 0; + y++; + } + j = -1; + } - if (textpad != NULL) { - wclear(textpad); - wresize(textpad, 1, w - HBORDERS - TEXTHMARGINS); + if (y >= rows) { + rows = y + 1; + wresize(pad, rows, cols); + } + + j++; + i++; } - error = draw_dialog(conf, shadow, widget, textpad, text, bs, - shortcutbuttons); + free(wtext); + free(string); - return (error); + return (0); } -int -new_dialog(struct bsddialog_conf *conf, WINDOW **shadow, WINDOW **widget, int y, - int x, int h, int w, WINDOW **textpad, const char *text, struct buttons *bs, - bool shortcutbuttons) +int draw_dialog(struct dialog *d) { - int error; + int wtitle, wbottomtitle; + cchar_t ts, ltee, rtee; - if (conf->shadow) { - *shadow = newwin(h, w, y + t.shadow.h, x + t.shadow.w); - if (*shadow == NULL) - RETURN_ERROR("Cannot build shadow"); - wbkgd(*shadow, t.shadow.color); + if (d->conf->ascii_lines) { + setcchar(&ts, L"-", 0, 0, NULL); + setcchar(<ee, L"+", 0, 0,NULL); + setcchar(&rtee, L"+", 0, 0, NULL); + } else { + ts = *WACS_HLINE; + ltee = *WACS_LTEE; + rtee = *WACS_RTEE; } - if ((*widget = new_boxed_window(conf, y, x, h, w, RAISED)) == NULL) { - if (conf->shadow) - delwin(*shadow); - return (BSDDIALOG_ERROR); + if (d->conf->shadow) { + wclear(d->shadow); + wresize(d->shadow, d->h, d->w); + mvwin(d->shadow, d->y + t.shadow.y, d->x + t.shadow.x); + wnoutrefresh(d->shadow); } - if (textpad != NULL && text != NULL) { /* textbox */ - *textpad = newpad(1, w - HBORDERS - TEXTHMARGINS); - if (*textpad == NULL) { - delwin(*widget); - if (conf->shadow) - delwin(*shadow); - RETURN_ERROR("Cannot build the pad window for text"); + wclear(d->widget); + wresize(d->widget, d->h, d->w); + mvwin(d->widget, d->y, d->x); + draw_borders(d->conf, d->widget, RAISED); + + if (d->conf->title != NULL) { + if ((wtitle = strcols(d->conf->title)) < 0) + return (BSDDIALOG_ERROR); + if (t.dialog.delimtitle && d->conf->no_lines == false) { + wattron(d->widget, t.dialog.lineraisecolor); + mvwadd_wch(d->widget, 0, d->w/2 - wtitle/2 -1, &rtee); + wattroff(d->widget, t.dialog.lineraisecolor); + } + wattron(d->widget, t.dialog.titlecolor); + mvwaddstr(d->widget, 0, d->w/2 - wtitle/2, d->conf->title); + wattroff(d->widget, t.dialog.titlecolor); + if (t.dialog.delimtitle && d->conf->no_lines == false) { + wattron(d->widget, t.dialog.lineraisecolor); + wadd_wch(d->widget, <ee); + wattroff(d->widget, t.dialog.lineraisecolor); } - wbkgd(*textpad, t.dialog.color); } - error = draw_dialog(conf, *shadow, *widget, - textpad == NULL ? NULL : *textpad, text, bs, shortcutbuttons); + if (d->bs.nbuttons > 0) { + if (d->conf->no_lines == false) { + wattron(d->widget, t.dialog.lineraisecolor); + mvwadd_wch(d->widget, d->h-3, 0, <ee); + mvwhline_set(d->widget, d->h-3, 1, &ts, d->w-2); + wattroff(d->widget, t.dialog.lineraisecolor); - return (error); -} + wattron(d->widget, t.dialog.linelowercolor); + mvwadd_wch(d->widget, d->h-3, d->w-1, &rtee); + wattroff(d->widget, t.dialog.linelowercolor); + } + draw_buttons(d); + } -void -end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, - WINDOW *textpad) -{ - int y, x, h, w; + if (d->conf->bottomtitle != NULL) { + if ((wbottomtitle = strcols(d->conf->bottomtitle)) < 0) + return (BSDDIALOG_ERROR); + wattron(d->widget, t.dialog.bottomtitlecolor); + wmove(d->widget, d->h - 1, d->w/2 - wbottomtitle/2 - 1); + waddch(d->widget, ' '); + waddstr(d->widget, d->conf->bottomtitle); + waddch(d->widget, ' '); + wattroff(d->widget, t.dialog.bottomtitlecolor); + } - getbegyx(widget, y, x); - getmaxyx(widget, h, w); + wnoutrefresh(d->widget); - if (conf->sleep > 0) - sleep(conf->sleep); + wclear(d->textpad); + /* `infobox "" 0 2` fails but text is empty and textpad remains 1 1 */ + wresize(d->textpad, 1, d->w - BORDERS - TEXTHMARGINS); - if (textpad != NULL) - delwin(textpad); + if (print_textpad(d->conf, d->textpad, d->text) != 0) + return (BSDDIALOG_ERROR); - delwin(widget); + d->built = true; - if (conf->shadow) - delwin(shadow); + return (0); +} - if (conf->clear) - hide_widget(y, x, h, w, conf->shadow); +int +prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows, + int cols, struct dialog *d) +{ + CHECK_PTR(conf); + + d->built = false; + d->conf = conf; + d->rows = rows; + d->cols = cols; + d->text = CHECK_STR(text); + d->bs.nbuttons = 0; + + if (d->conf->shadow) { + if ((d->shadow = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW shadow"); + wbkgd(d->shadow, t.shadow.color); + } - if (conf->get_height != NULL) - *conf->get_height = h; - if (conf->get_width != NULL) - *conf->get_width = w; -} + if ((d->widget = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW widget"); + wbkgd(d->widget, t.dialog.color); + + /* fake for textpad */ + if ((d->textpad = newpad(1, 1)) == NULL) + RETURN_ERROR("Cannot build the pad WINDOW for text"); + wbkgd(d->textpad, t.dialog.color); + + return (0); +}
\ No newline at end of file diff --git a/contrib/bsddialog/lib/lib_util.h b/contrib/bsddialog/lib/lib_util.h index 6ebc73cf1055..1adc34f3b80a 100644 --- a/contrib/bsddialog/lib/lib_util.h +++ b/contrib/bsddialog/lib/lib_util.h @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -28,106 +28,141 @@ #ifndef _LIBBSDDIALOG_UTIL_H_ #define _LIBBSDDIALOG_UTIL_H_ -#define HBORDERS 2 -#define VBORDERS 2 +#define BORDER 1 +#define BORDERS (BORDER + BORDER) #define TEXTHMARGIN 1 #define TEXTHMARGINS (TEXTHMARGIN + TEXTHMARGIN) +#define HBUTTONS 2 +#define OK_LABEL "OK" +#define CANCEL_LABEL "Cancel" -/* current theme */ +/* theme util */ extern struct bsddialog_theme t; +extern bool hastermcolors; +#define MIN(a,b) (((a)<(b))?(a):(b)) +#define MAX(a,b) (((a)>(b))?(a):(b)) /* debug */ -#define BSDDIALOG_DEBUG(y,x,fmt, ...) do { \ - mvprintw(y, x, fmt, __VA_ARGS__); \ - refresh(); \ +#define BSDDIALOG_DEBUG(y,x,fmt, ...) do { \ + mvprintw(y, x, fmt, __VA_ARGS__); \ + refresh(); \ } while (0) - -/* error buffer */ -const char *get_error_string(void); -void set_error_string(const char *string); - -#define RETURN_ERROR(str) do { \ - set_error_string(str); \ - return (BSDDIALOG_ERROR); \ +/* error and diagnostic */ +#define RETURN_ERROR(str) do { \ + set_error_string(str); \ + return (BSDDIALOG_ERROR); \ +} while (0) +#define RETURN_FMTERROR(fmt, ...) do { \ + set_fmt_error_string(fmt, __VA_ARGS__); \ + return (BSDDIALOG_ERROR); \ +} while (0) +/* check ptr */ +#define CHECK_PTR(p) do { \ + if (p == NULL) \ + RETURN_ERROR("*" #p " is NULL"); \ +} while (0) +#define CHECK_ARRAY(nitem, a) do { \ + if (nitem > 0 && a == NULL) \ + RETURN_FMTERROR(#nitem " is %d but *" #a " is NULL", nitem); \ +} while (0) +/* widget utils */ +#define KEY_CTRL(c) (c & 037) +#define TEXTPAD(d, downnotext) rtextpad(d, 0, 0, 0, downnotext) +#define SCREENLINES (getmaxy(stdscr)) +#define SCREENCOLS (getmaxx(stdscr)) +#define CHECK_STR(s) (s == NULL ? "" : s) +#define UARROW(c) (c->ascii_lines ? '^' : ACS_UARROW) +#define DARROW(c) (c->ascii_lines ? 'v' : ACS_DARROW) +#define LARROW(c) (c->ascii_lines ? '<' : ACS_LARROW) +#define RARROW(c) (c->ascii_lines ? '>' : ACS_RARROW) +#define DRAW_BUTTONS(d) do { \ + draw_buttons(&d); \ + wnoutrefresh(d.widget); \ } while (0) -/* buttons */ +/* internal types */ +enum elevation { RAISED, LOWERED }; + struct buttons { unsigned int nbuttons; -#define MAXBUTTONS 6 /* ok + extra + cancel + help + 2 generics */ +#define MAXBUTTONS 10 /* 3left + ok + extra + cancel + help + 3 right */ const char *label[MAXBUTTONS]; + bool shortcut; + wchar_t first[MAXBUTTONS]; int value[MAXBUTTONS]; int curr; +#define BUTTONVALUE(bs) bs.value[bs.curr] unsigned int sizebutton; /* including left and right delimiters */ }; -#define BUTTON_OK_LABEL "OK" -#define BUTTON_CANCEL_LABEL "Cancel" -void -get_buttons(struct bsddialog_conf *conf, struct buttons *bs, - const char *yesoklabel, const char *nocancellabel); - -void -draw_buttons(WINDOW *window, struct buttons bs, bool shortcut); - -int buttons_width(struct buttons bs); -bool shortcut_buttons(int key, struct buttons *bs); +struct dialog { + bool built; /* true after the first draw_dialog() */ + struct bsddialog_conf *conf; /* Checked API conf */ + WINDOW *widget; /* Size and position refer to widget */ + int y, x; /* Current position, API conf.[y|x]: -1, >=0 */ + int rows, cols; /* API rows and cols: -1, 0, >0 */ + int h, w; /* Current height and width */ + const char *text; /* Checked API text, at least "" */ + WINDOW *textpad; /* Fake for textbox */ + struct buttons bs; /* bs.nbuttons = 0 for no buttons */ + WINDOW *shadow; +}; -/* help window with F1 key */ -int f1help(struct bsddialog_conf *conf); +/* error and diagnostic */ +const char *get_error_string(void); +void set_error_string(const char *string); +void set_fmt_error_string(const char *fmt, ...); -/* cleaner */ -int hide_widget(int y, int x, int h, int w, bool withshadow); +/* multicolumn character string */ +unsigned int strcols(const char *mbstring); +int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col); +void mvwaddwch(WINDOW *w, int y, int x, wchar_t wch); +wchar_t* alloc_mbstows(const char *mbstring); -/* (auto) size and (auto) position */ -#define SCREENLINES (getmaxy(stdscr)) -#define SCREENCOLS (getmaxx(stdscr)) +/* buttons */ +void +set_buttons(struct dialog *d, bool shortcut, const char *oklabel, + const char *canclabel); +void draw_buttons(struct dialog *d); +bool shortcut_buttons(wint_t key, struct buttons *bs); -int -text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text, - struct buttons *bs, int rowsnotext, int startwtext, int *htext, int *wtext); +/* widget utils */ +int hide_dialog(struct dialog *d); +int f1help_dialog(struct bsddialog_conf *conf); -int widget_max_height(struct bsddialog_conf *conf); -int widget_max_width(struct bsddialog_conf *conf); +void +draw_borders(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev); -int -widget_min_height(struct bsddialog_conf *conf, int htext, int minwidget, - bool withbuttons); +void +update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w, + enum elevation elev); -int -widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget, - struct buttons *bs); +void +rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext); +/* (auto) sizing and (auto) position */ int set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w); int -set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w); +set_widget_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, + int *w, const char *text, int *rowstext, struct buttons *bs, int hnotext, + int minw); -/* widget builders */ -enum elevation { RAISED, LOWERED }; +int widget_checksize(int h, int w, struct buttons *bs, int hnotext, int minw); -void -draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols, - enum elevation elev); +int +set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w); -WINDOW * -new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols, - enum elevation elev); +int dialog_size_position(struct dialog *d, int hnotext, int minw, int *htext); -int -new_dialog(struct bsddialog_conf *conf, WINDOW **shadow, WINDOW **widget, int y, - int x, int h, int w, WINDOW **textpad, const char *text, struct buttons *bs, - bool shortcutbuttons); +/* dialog */ +void end_dialog(struct dialog *d); +int draw_dialog(struct dialog *d); int -update_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, - int y, int x, int h, int w, WINDOW *textpad, const char *text, - struct buttons *bs, bool shortcutbuttons); - -void -end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget, - WINDOW *textpad); +prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows, + int cols, struct dialog *d); -#endif
\ No newline at end of file +#endif diff --git a/contrib/bsddialog/lib/libbsddialog.c b/contrib/bsddialog/lib/libbsddialog.c index 761bdc3efa77..cdb5e1e251dc 100644 --- a/contrib/bsddialog/lib/libbsddialog.c +++ b/contrib/bsddialog/lib/libbsddialog.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -26,18 +26,19 @@ */ #include <curses.h> -#include <stdlib.h> #include <string.h> -#include <unistd.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" -int bsddialog_init(void) +#define DEFAULT_COLS_PER_ROW 10 /* Default conf.text.columns_per_row */ + +static bool in_bsddialog_mode = false; + +int bsddialog_init_notheme(void) { int i, j, c, error; - enum bsddialog_default_theme theme; set_error_string(""); @@ -54,6 +55,7 @@ int bsddialog_init(void) bsddialog_end(); RETURN_ERROR("Cannot init curses (keypad and cursor)"); } + in_bsddialog_mode = true; c = 1; error += start_color(); @@ -64,13 +66,25 @@ int bsddialog_init(void) } } - if (error == OK && has_colors()) + hastermcolors = (error == OK && has_colors()) ? true : false; + + return (BSDDIALOG_OK); +} + +int bsddialog_init(void) +{ + enum bsddialog_default_theme theme; + + bsddialog_init_notheme(); + + if (bsddialog_hascolors()) theme = BSDDIALOG_THEME_FLAT; else theme = BSDDIALOG_THEME_BLACKWHITE; if (bsddialog_set_default_theme(theme) != 0) { bsddialog_end(); + in_bsddialog_mode = false; return (BSDDIALOG_ERROR); } @@ -81,22 +95,45 @@ int bsddialog_end(void) { if (endwin() != OK) RETURN_ERROR("Cannot end curses (endwin)"); + in_bsddialog_mode = false; return (BSDDIALOG_OK); } int bsddialog_backtitle(struct bsddialog_conf *conf, const char *backtitle) { - mvaddstr(0, 1, backtitle); - if (conf->no_lines != true) - mvhline(1, 1, conf->ascii_lines ? '-' : ACS_HLINE, - SCREENCOLS - 2); + CHECK_PTR(conf); + + move(0, 1); + clrtoeol(); + addstr(CHECK_STR(backtitle)); + if (conf->no_lines != true) { + if (conf->ascii_lines) + mvhline(1, 1, '-', SCREENCOLS - 2); + else + mvhline_set(1, 1, WACS_HLINE, SCREENCOLS - 2); + } - refresh(); + wnoutrefresh(stdscr); return (BSDDIALOG_OK); } +int bsddialog_backtitle_rf(struct bsddialog_conf *conf, const char *backtitle) +{ + int rv; + + rv = bsddialog_backtitle(conf, backtitle); + doupdate(); + + return (rv); +} + +bool bsddialog_inmode(void) +{ + return (in_bsddialog_mode); +} + const char *bsddialog_geterror(void) { return (get_error_string()); @@ -104,24 +141,25 @@ const char *bsddialog_geterror(void) int bsddialog_initconf(struct bsddialog_conf *conf) { - if (conf == NULL) - RETURN_ERROR("conf is NULL"); - if (sizeof(*conf) != sizeof(struct bsddialog_conf)) - RETURN_ERROR("Bad conf size"); + CHECK_PTR(conf); memset(conf, 0, sizeof(struct bsddialog_conf)); conf->y = BSDDIALOG_CENTER; conf->x = BSDDIALOG_CENTER; conf->shadow = true; + conf->text.cols_per_row = DEFAULT_COLS_PER_ROW; return (BSDDIALOG_OK); } -int bsddialog_clearterminal(void) +void bsddialog_refresh(void) { - if (clear() != OK) - RETURN_ERROR("Cannot clear the terminal"); refresh(); +} - return (BSDDIALOG_OK); +void bsddialog_clear(unsigned int y) +{ + move(y, 0); + clrtobot(); + refresh(); }
\ No newline at end of file diff --git a/contrib/bsddialog/lib/menubox.c b/contrib/bsddialog/lib/menubox.c index 22ed15e6e7a0..e6e2e7e3e63e 100644 --- a/contrib/bsddialog/lib/menubox.c +++ b/contrib/bsddialog/lib/menubox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,20 +25,13 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> #include <curses.h> #include <stdlib.h> -#include <string.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" -#define DEPTH 2 -#define MIN_HEIGHT VBORDERS + 6 /* 2 buttons 1 text 3 menu */ - enum menumode { CHECKLISTMODE, MENUMODE, @@ -47,47 +40,155 @@ enum menumode { SEPARATORMODE }; -struct lineposition { - unsigned int maxsepstr; - unsigned int maxprefix; - unsigned int xselector; - unsigned int selectorlen; - unsigned int maxdepth; - unsigned int xname; - unsigned int maxname; - unsigned int xdesc; - unsigned int maxdesc; - unsigned int line; -}; - struct privateitem { - bool on; - int group; - int index; + const char *prefix; + bool on; /* menu changes, not API on */ + unsigned int depth; + const char *name; + const char *desc; + const char *bottomdesc; + int group; /* index menu in menugroup */ + int index; /* real item index inside its menu */ enum menumode type; - struct bsddialog_menuitem *item; + wchar_t shortcut; }; -static void -set_on_output(struct bsddialog_conf *conf, int output, int ngroups, - struct bsddialog_menugroup *groups, struct privateitem *pritems) +struct privatemenu { + WINDOW *box; /* only for borders */ + WINDOW *pad; /* pad for the private items */ + int ypad; /* start pad line */ + int ys, ye, xs, xe; /* pad pos */ + unsigned int xselector; /* [] */ + unsigned int xname; /* real x: xname + item.depth */ + unsigned int xdesc; /* real x: xdesc + item.depth */ + unsigned int line; /* wpad: prefix [] depth name desc */ + unsigned int apimenurows; + unsigned int menurows; /* real menurows after menu_size_position() */ + int nitems; /* total nitems (all groups * all items) */ + struct privateitem *pritems; + int sel; /* current focus item, can be -1 */ + bool hasbottomdesc; +}; + +static enum menumode +getmode(enum menumode mode, struct bsddialog_menugroup group) +{ + if (mode == MIXEDLISTMODE) { + if (group.type == BSDDIALOG_SEPARATOR) + mode = SEPARATORMODE; + else if (group.type == BSDDIALOG_RADIOLIST) + mode = RADIOLISTMODE; + else if (group.type == BSDDIALOG_CHECKLIST) + mode = CHECKLISTMODE; + } + + return (mode); +} + +static int +build_privatemenu(struct bsddialog_conf *conf, struct privatemenu *m, + enum menumode mode, unsigned int ngroups, + struct bsddialog_menugroup *groups) { + bool onetrue; int i, j, abs; + unsigned int maxsepstr, maxprefix, selectorlen, maxdepth; + unsigned int maxname, maxdesc; + struct bsddialog_menuitem *item; + struct privateitem *pritem; - if (output != BSDDIALOG_OK && !conf->menu.on_without_ok) - return; + /* nitems and fault checks */ + CHECK_ARRAY(ngroups, groups); + m->nitems = 0; + for (i = 0; i < (int)ngroups; i++) { + CHECK_ARRAY(groups[i].nitems, groups[i].items); + m->nitems += (int)groups[i].nitems; + } - for(i = abs = 0; i < ngroups; i++) { - if (groups[i].type == BSDDIALOG_SEPARATOR) { - abs += groups[i].nitems; - continue; - } + /* alloc and set private items */ + m->pritems = calloc(m->nitems, sizeof (struct privateitem)); + if (m->pritems == NULL) + RETURN_ERROR("Cannot allocate memory for internal menu items"); + m->hasbottomdesc = false; + abs = 0; + for (i = 0; i < (int)ngroups; i++) { + onetrue = false; + for (j = 0; j < (int)groups[i].nitems; j++) { + item = &groups[i].items[j]; + pritem = &m->pritems[abs]; + + if (getmode(mode, groups[i]) == MENUMODE) { + m->pritems[abs].on = false; + } else if (getmode(mode, groups[i]) == RADIOLISTMODE) { + m->pritems[abs].on = onetrue ? false : item->on; + if (m->pritems[abs].on) + onetrue = true; + } else { /* CHECKLISTMODE */ + m->pritems[abs].on = item->on; + } + pritem->group = i; + pritem->index = j; + pritem->type = getmode(mode, groups[i]); + + pritem->prefix = CHECK_STR(item->prefix); + pritem->depth = item->depth; + pritem->name = CHECK_STR(item->name); + pritem->desc = CHECK_STR(item->desc); + pritem->bottomdesc = CHECK_STR(item->bottomdesc); + if (item->bottomdesc != NULL) + m->hasbottomdesc = true; + + mbtowc(&pritem->shortcut, conf->menu.no_name ? + pritem->desc : pritem->name, MB_CUR_MAX); - for(j = 0; j < (int)groups[i].nitems; j++) { - groups[i].items[j].on = pritems[abs].on; abs++; } } + + /* positions */ + m->xselector = m->xname = m->xdesc = m->line = 0; + maxsepstr = maxprefix = selectorlen = maxdepth = maxname = maxdesc = 0; + for (i = 0; i < m->nitems; i++) { + if (m->pritems[i].type == RADIOLISTMODE || + m->pritems[i].type == CHECKLISTMODE) + selectorlen = 4; + + if (m->pritems[i].type == SEPARATORMODE) { + maxsepstr = MAX(maxsepstr, + strcols(m->pritems[i].name) + + strcols(m->pritems[i].desc)); + continue; + } + + maxprefix = MAX(maxprefix, strcols(m->pritems[i].prefix)); + maxdepth = MAX(maxdepth, m->pritems[i].depth); + maxname = MAX(maxname, strcols(m->pritems[i].name)); + maxdesc = MAX(maxdesc, strcols(m->pritems[i].desc)); + } + maxname = conf->menu.no_name ? 0 : maxname; + maxdesc = conf->menu.no_desc ? 0 : maxdesc; + + m->xselector = maxprefix + (maxprefix != 0 ? 1 : 0); + m->xname = m->xselector + selectorlen; + m->xdesc = maxdepth + m->xname + maxname; + m->xdesc += (maxname != 0 ? 1 : 0); + m->line = MAX(maxsepstr + 3, m->xdesc + maxdesc); + + return (0); +} + +static void +set_return_on(struct privatemenu *m, struct bsddialog_menugroup *groups) +{ + int i; + struct privateitem *pritem; + + for (i = 0; i < m->nitems; i++) { + if (m->pritems[i].type == SEPARATORMODE) + continue; + pritem = &m->pritems[i]; + groups[pritem->group].items[pritem->index].on = pritem->on; + } } static int getprev(struct privateitem *pritems, int abs) @@ -176,25 +277,17 @@ getfastprev(int menurows, struct privateitem *pritems, int abs) } static int -getnextshortcut(struct bsddialog_conf *conf, int npritems, - struct privateitem *pritems, int abs, int key) +getnextshortcut(int npritems, struct privateitem *pritems, int abs, wint_t key) { - int i, ch, next; + int i, next; next = -1; for (i = 0; i < npritems; i++) { if (pritems[i].type == SEPARATORMODE) continue; - - if (conf->menu.no_name) - ch = pritems[i].item->desc[0]; - else - ch = pritems[i].item->name[0]; - - if (ch == key) { + if (pritems[i].shortcut == (wchar_t)key) { if (i > abs) return (i); - if (i < abs && next == -1) next = i; } @@ -203,81 +296,66 @@ getnextshortcut(struct bsddialog_conf *conf, int npritems, return (next != -1 ? next : abs); } -static enum menumode -getmode(enum menumode mode, struct bsddialog_menugroup group) +static void drawseparators(struct bsddialog_conf *conf, struct privatemenu *m) { - if (mode == MIXEDLISTMODE) { - if (group.type == BSDDIALOG_SEPARATOR) - mode = SEPARATORMODE; - else if (group.type == BSDDIALOG_RADIOLIST) - mode = RADIOLISTMODE; - else if (group.type == BSDDIALOG_CHECKLIST) - mode = CHECKLISTMODE; - } - - return (mode); -} - -static void -drawseparators(struct bsddialog_conf *conf, WINDOW *pad, int linelen, - int nitems, struct privateitem *pritems) -{ - int i, linech, labellen; + int i, realw, labellen; const char *desc, *name; - for (i = 0; i < nitems; i++) { - if (pritems[i].type != SEPARATORMODE) + for (i = 0; i < m->nitems; i++) { + if (m->pritems[i].type != SEPARATORMODE) continue; if (conf->no_lines == false) { - wattron(pad, t.menu.desccolor); - linech = conf->ascii_lines ? '-' : ACS_HLINE; - mvwhline(pad, i, 0, linech, linelen); - wattroff(pad, t.menu.desccolor); + wattron(m->pad, t.menu.desccolor); + if (conf->ascii_lines) + mvwhline(m->pad, i, 0, '-', m->line); + else + mvwhline_set(m->pad, i, 0, WACS_HLINE, m->line); + wattroff(m->pad, t.menu.desccolor); } - name = pritems[i].item->name; - desc = pritems[i].item->desc; - labellen = strlen(name) + strlen(desc) + 1; - wmove(pad, i, labellen < linelen ? linelen/2 - labellen/2 : 0); - wattron(pad, t.menu.namesepcolor); - waddstr(pad, name); - wattroff(pad, t.menu.namesepcolor); - if (strlen(name) > 0 && strlen(desc) > 0) - waddch(pad, ' '); - wattron(pad, t.menu.descsepcolor); - waddstr(pad, desc); - wattroff(pad, t.menu.descsepcolor); + name = m->pritems[i].name; + desc = m->pritems[i].desc; + realw = m->xe - m->xs; + labellen = strcols(name) + strcols(desc) + 1; + wmove(m->pad, i, (labellen < realw) ? realw/2 - labellen/2 : 0); + wattron(m->pad, t.menu.sepnamecolor); + waddstr(m->pad, name); + wattroff(m->pad, t.menu.sepnamecolor); + if (strcols(name) > 0 && strcols(desc) > 0) + waddch(m->pad, ' '); + wattron(m->pad, t.menu.sepdesccolor); + waddstr(m->pad, desc); + wattroff(m->pad, t.menu.sepdesccolor); } } static void -drawitem(struct bsddialog_conf *conf, WINDOW *pad, int y, - struct lineposition pos, struct privateitem *pritem, bool focus) +drawitem(struct bsddialog_conf *conf, struct privatemenu *m, int y, bool focus) { int colordesc, colorname, colorshortcut; - const char *shortcut; - struct bsddialog_menuitem *item; + struct privateitem *pritem; - item = pritem->item; + pritem = &m->pritems[y]; /* prefix */ - if (item->prefix != NULL && item->prefix[0] != '\0') - mvwaddstr(pad, y, 0, item->prefix); + wattron(m->pad, focus ? t.menu.f_prefixcolor : t.menu.prefixcolor); + mvwaddstr(m->pad, y, 0, pritem->prefix); + wattroff(m->pad, focus ? t.menu.f_prefixcolor : t.menu.prefixcolor); /* selector */ - wmove(pad, y, pos.xselector); - wattron(pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); + wmove(m->pad, y, m->xselector); + wattron(m->pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); if (pritem->type == CHECKLISTMODE) - wprintw(pad, "[%c]", pritem->on ? 'X' : ' '); + wprintw(m->pad, "[%c]", pritem->on ? 'X' : ' '); if (pritem->type == RADIOLISTMODE) - wprintw(pad, "(%c)", pritem->on ? '*' : ' '); - wattroff(pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); + wprintw(m->pad, "(%c)", pritem->on ? '*' : ' '); + wattroff(m->pad, focus ? t.menu.f_selectorcolor : t.menu.selectorcolor); /* name */ colorname = focus ? t.menu.f_namecolor : t.menu.namecolor; if (conf->menu.no_name == false) { - wattron(pad, colorname); - mvwaddstr(pad, y, pos.xname + item->depth * DEPTH, item->name); - wattroff(pad, colorname); + wattron(m->pad, colorname); + mvwaddstr(m->pad, y, m->xname + pritem->depth, pritem->name); + wattroff(m->pad, colorname); } /* description */ @@ -287,145 +365,137 @@ drawitem(struct bsddialog_conf *conf, WINDOW *pad, int y, colordesc = focus ? t.menu.f_desccolor : t.menu.desccolor; if (conf->menu.no_desc == false) { - wattron(pad, colordesc); + wattron(m->pad, colordesc); if (conf->menu.no_name) - mvwaddstr(pad, y, pos.xname + item->depth * DEPTH, - item->desc); + mvwaddstr(m->pad, y, m->xname + pritem->depth, + pritem->desc); else - mvwaddstr(pad, y, pos.xdesc, item->desc); - wattroff(pad, colordesc); + mvwaddstr(m->pad, y, m->xdesc, pritem->desc); + wattroff(m->pad, colordesc); } /* shortcut */ if (conf->menu.shortcut_buttons == false) { colorshortcut = focus ? t.menu.f_shortcutcolor : t.menu.shortcutcolor; - wattron(pad, colorshortcut); - - if (conf->menu.no_name) - shortcut = item->desc; - else - shortcut = item->name; - wmove(pad, y, pos.xname + item->depth * DEPTH); - if (shortcut != NULL && shortcut[0] != '\0') - waddch(pad, shortcut[0]); - wattroff(pad, colorshortcut); + wattron(m->pad, colorshortcut); + mvwaddwch(m->pad, y, m->xname + pritem->depth, pritem->shortcut); + wattroff(m->pad, colorshortcut); } /* bottom description */ - move(SCREENLINES - 1, 2); - clrtoeol(); - if (item->bottomdesc != NULL && focus) { - addstr(item->bottomdesc); - refresh(); + if (m->hasbottomdesc) { + move(SCREENLINES - 1, 2); + clrtoeol(); + if (focus) { + attron(t.menu.bottomdesccolor); + addstr(pritem->bottomdesc); + attroff(t.menu.bottomdesccolor); + wnoutrefresh(stdscr); + } } } -static int -menu_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w, - const char *text, int linelen, unsigned int *menurows, int nitems, - struct buttons bs) +static void update_menubox(struct bsddialog_conf *conf, struct privatemenu *m) { - int htext, wtext, menusize, notext; - - notext = 2; - if (*menurows == BSDDIALOG_AUTOSIZE) { - /* algo 1): grows vertically */ - /* notext = 1; */ - /* algo 2): grows horizontally, better with little screens */ - notext += nitems; - notext = MIN(notext, widget_max_height(conf) - HBORDERS - 3); - } else - notext += *menurows; - - /* cols autosize, rows autosize, rows fullscreen, menu particularity */ - if (cols == BSDDIALOG_AUTOSIZE || rows <= BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, &bs, notext, linelen + 4, - &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); - } + int h, w; - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, linelen + 4, &bs); + draw_borders(conf, m->box, LOWERED); + getmaxyx(m->box, h, w); - if (rows == BSDDIALOG_AUTOSIZE) { - if (*menurows == 0) { - menusize = widget_max_height(conf) - HBORDERS - - 2 /*buttons*/ - htext; - menusize = MIN(menusize, nitems + 2); - *menurows = menusize - 2 < 0 ? 0 : menusize - 2; - } - else /* h autosize with fixed menurows */ - menusize = *menurows + 2; - - *h = widget_min_height(conf, htext, menusize, true); - /* - * avoid menurows overflow and - * with rows=AUTOSIZE menurows!=0 becomes max-menurows - */ - *menurows = MIN(*h - 6 - htext, (int)*menurows); - } else { - if (*menurows == 0) - *menurows = MIN(*h-6-htext, nitems); - } + if (m->nitems > (int)m->menurows) { + wattron(m->box, t.dialog.arrowcolor); + if (m->ypad > 0) + mvwhline(m->box, 0, 2, UARROW(conf), 3); - return (0); + if ((m->ypad + (int)m->menurows) < m->nitems) + mvwhline(m->box, h-1, 2, DARROW(conf), 3); + + mvwprintw(m->box, h-1, w-6, "%3d%%", + 100 * (m->ypad + m->menurows) / m->nitems); + wattroff(m->box, t.dialog.arrowcolor); + } } -static int -menu_checksize(int rows, int cols, const char *text, int menurows, int nitems, - struct buttons bs) +static int menu_size_position(struct dialog *d, struct privatemenu *m) { - int mincols, textrow, menusize; + int htext, hmenu; - mincols = VBORDERS; - /* buttons */ - mincols += buttons_width(bs); + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + hmenu = (int)(m->menurows == BSDDIALOG_AUTOSIZE) ? + (int)m->nitems : (int)m->menurows; + hmenu += 2; /* menu borders */ /* - * linelen check, comment to allow some hidden col otherwise portconfig - * could not show big menus like www/apache24 + * algo 1: notext = 1 (grows vertically). + * algo 2: notext = hmenu (grows horizontally, better for little term). */ - /* mincols = MAX(mincols, linelen); */ - - if (cols < mincols) - RETURN_ERROR("Few cols, width < size buttons or " - "name + descripion of the items"); - - textrow = text != NULL && strlen(text) > 0 ? 1 : 0; + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, &htext, &d->bs, hmenu, m->line + 4) != 0) + return (BSDDIALOG_ERROR); + /* avoid menurows overflow and menurows becomes "at most menurows" */ + if (d->h - BORDERS - htext - HBUTTONS <= 2 /* menuborders */) + m->menurows = (m->nitems > 0) ? 1 : 0; /* widget_checksize() */ + else + m->menurows = MIN(d->h - BORDERS - htext - HBUTTONS, hmenu) - 2; - if (nitems > 0 && menurows == 0) - RETURN_ERROR("items > 0 but menurows == 0, probably terminal " - "too small"); + /* + * no minw=linelen to avoid big menu fault, then some col can be + * hidden (example portconfig www/apache24). + */ + if (widget_checksize(d->h, d->w, &d->bs, + 2 /* border box */ + MIN(m->menurows, 1), 0) != 0) + return (BSDDIALOG_ERROR); - menusize = nitems > 0 ? 3 : 0; - if (rows < 2 + 2 + menusize + textrow) - RETURN_ERROR("Few lines for this menus"); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); return (0); } -/* the caller has to call prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); */ -static void -update_menuwin(struct bsddialog_conf *conf, WINDOW *menuwin, int h, int w, - int totnitems, unsigned int menurows, int ymenupad) +static int mixedlist_draw(struct dialog *d, bool redraw, struct privatemenu *m) { - draw_borders(conf, menuwin, h, w, LOWERED); - - if (totnitems > (int)menurows) { - wattron(menuwin, t.dialog.arrowcolor); - - if (ymenupad > 0) - mvwprintw(menuwin, 0, 2, "^^^"); - - if ((ymenupad + (int)menurows) < totnitems) - mvwprintw(menuwin, h-1, 2, "vvv"); - - wattroff(menuwin, t.dialog.arrowcolor); - - mvwprintw(menuwin, h-1, w-10, "%3d%%", - 100 * (ymenupad + menurows) / totnitems); + if (redraw) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ } + m->menurows = m->apimenurows; + if (menu_size_position(d, m) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) /* doupdate() in main loop */ + return (BSDDIALOG_ERROR); + if (redraw) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, 2/*bmenu*/ + m->menurows + HBUTTONS); + + /* selected item in view*/ + if (m->ypad > m->sel && m->ypad > 0) + m->ypad = m->sel; + if ((int)(m->ypad + m->menurows) <= m->sel) + m->ypad = m->sel - m->menurows + 1; + /* lower pad after a terminal expansion */ + if (m->ypad > 0 && (m->nitems - m->ypad) < (int)m->menurows) + m->ypad = m->nitems - m->menurows; + + update_box(d->conf, m->box, d->y + d->h - 5 - m->menurows, d->x + 2, + m->menurows+2, d->w-4, LOWERED); + update_menubox(d->conf, m); + wnoutrefresh(m->box); + + m->ys = d->y + d->h - 5 - m->menurows + 1; + m->ye = d->y + d->h - 5 ; + if (d->conf->menu.align_left || (int)m->line > d->w - 6) { + m->xs = d->x + 3; + m->xe = m->xs + d->w - 7; + } else { /* center */ + m->xs = d->x + 3 + (d->w-6)/2 - m->line/2; + m->xe = m->xs + d->w - 5; + } + drawseparators(d->conf, m); /* uses xe - xs */ + pnoutrefresh(m->pad, m->ypad, 0, m->ys, m->xs, m->ye, m->xe); + + return (0); } static int @@ -433,330 +503,192 @@ do_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, enum menumode mode, unsigned int ngroups, struct bsddialog_menugroup *groups, int *focuslist, int *focusitem) { - bool loop, onetrue, movefocus, automenurows, shortcut_butts; - int i, j, y, x, h, w, output, input; - int ymenupad, ys, ye, xs, xe, abs, next, totnitems; - WINDOW *shadow, *widget, *textpad, *menuwin, *menupad; - struct buttons bs; - struct lineposition pos = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - struct bsddialog_menuitem *item; - struct privateitem *pritems; - - shortcut_butts = conf->menu.shortcut_buttons; - - automenurows = (menurows == BSDDIALOG_AUTOSIZE) ? true : false; - - totnitems = 0; - for (i = 0; i < (int)ngroups; i++) { - if (getmode(mode, groups[i]) == RADIOLISTMODE || - getmode(mode, groups[i]) == CHECKLISTMODE) - pos.selectorlen = 3; + bool loop, changeitem; + int i, next, retval; + wint_t input; + struct privatemenu m; + struct dialog d; - for (j = 0; j < (int)groups[i].nitems; j++) { - totnitems++; - item = &groups[i].items[j]; - - if (groups[i].type == BSDDIALOG_SEPARATOR) { - pos.maxsepstr = MAX(pos.maxsepstr, - strlen(item->name) + strlen(item->desc)); - continue; - } - - pos.maxprefix = MAX(pos.maxprefix,strlen(item->prefix)); - pos.maxdepth = MAX(pos.maxdepth, item->depth); - pos.maxname = MAX(pos.maxname, strlen(item->name)); - pos.maxdesc = MAX(pos.maxdesc, strlen(item->desc)); - } - } - pos.maxname = conf->menu.no_name ? 0 : pos.maxname; - pos.maxdesc = conf->menu.no_desc ? 0 : pos.maxdesc; - pos.maxdepth = DEPTH * pos.maxdepth; - - pos.xselector = pos.maxprefix + (pos.maxprefix != 0 ? 1 : 0); - pos.xname = pos.xselector + pos.selectorlen + - (pos.selectorlen > 0 ? 1 : 0); - pos.xdesc = pos.maxdepth + pos.xname + pos.maxname; - pos.xdesc += (pos.maxname != 0 ? 1 : 0); - pos.line = MAX(pos.maxsepstr + 3, pos.xdesc + pos.maxdesc); - - - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (menu_autosize(conf, rows, cols, &h, &w, text, pos.line, &menurows, - totnitems, bs) != 0) - return (BSDDIALOG_ERROR); - if (menu_checksize(h, w, text, menurows, totnitems, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); + set_buttons(&d, conf->menu.shortcut_buttons, OK_LABEL, CANCEL_LABEL); + if (d.conf->menu.no_name && d.conf->menu.no_desc) + RETURN_ERROR("Both conf.menu.no_name and conf.menu.no_desc"); - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - shortcut_butts) != 0) + if (build_privatemenu(conf, &m, mode, ngroups, groups) != 0) return (BSDDIALOG_ERROR); - doupdate(); - - prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - menurows, x + 1 + w - TEXTHMARGIN); - - menuwin = new_boxed_window(conf, y + h - 5 - menurows, x + 2, - menurows+2, w-4, LOWERED); - - menupad = newpad(totnitems, pos.line); - wbkgd(menupad, t.dialog.color); + if ((m.box = newwin(1, 1, 1, 1)) == NULL) + RETURN_ERROR("Cannot build WINDOW box menu"); + wbkgd(m.box, t.dialog.color); + m.pad = newpad(m.nitems, m.line); + wbkgd(m.pad, t.dialog.color); - if ((pritems = calloc(totnitems, sizeof (struct privateitem))) == NULL) - RETURN_ERROR("Cannot allocate memory for internal menu items"); - - abs = 0; - for (i = 0; i < (int)ngroups; i++) { - onetrue = false; - for (j = 0; j < (int)groups[i].nitems; j++) { - item = &groups[i].items[j]; - - if (getmode(mode, groups[i]) == MENUMODE) { - pritems[abs].on = false; - } else if (getmode(mode, groups[i]) == RADIOLISTMODE) { - pritems[abs].on = onetrue ? false : item->on; - if (pritems[abs].on) - onetrue = true; - } else { - pritems[abs].on = item->on; - } - pritems[abs].group = i; - pritems[abs].index = j; - pritems[abs].type = getmode(mode, groups[i]); - pritems[abs].item = item; - - drawitem(conf, menupad, abs, pos, &pritems[abs], false); - abs++; - } - } - drawseparators(conf, menupad, MIN((int)pos.line, w-6), totnitems, - pritems); - abs = getfirst_with_default(totnitems, pritems, ngroups, groups, + for (i = 0; i < m.nitems; i++) + drawitem(conf, &m, i, false); + m.sel = getfirst_with_default(m.nitems, m.pritems, ngroups, groups, focuslist, focusitem); - if (abs >= 0) - drawitem(conf, menupad, abs, pos, &pritems[abs], true); - - ys = y + h - 5 - menurows + 1; - ye = y + h - 5 ; - if (conf->menu.align_left || (int)pos.line > w - 6) { - xs = x + 3; - xe = xs + w - 7; - } else { /* center */ - xs = x + 3 + (w-6)/2 - pos.line/2; - xe = xs + w - 5; - } - - ymenupad = 0; - if ((int)(ymenupad + menurows) - 1 < abs) - ymenupad = abs - menurows + 1; - update_menuwin(conf, menuwin, menurows+2, w-4, totnitems, menurows, - ymenupad); - wrefresh(menuwin); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); + if (m.sel >= 0) + drawitem(d.conf, &m, m.sel, true); + m.ypad = 0; + m.apimenurows = menurows; + if (mixedlist_draw(&d, false, &m) != 0) + return (BSDDIALOG_ERROR); - movefocus = false; + changeitem = false; loop = true; while (loop) { - input = getch(); + doupdate(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; - if (abs >= 0 && pritems[abs].type == MENUMODE) - pritems[abs].on = true; - set_on_output(conf, output, ngroups, groups, pritems); + retval = BUTTONVALUE(d.bs); + if (m.sel >= 0 && m.pritems[m.sel].type == MENUMODE) + m.pritems[m.sel].on = true; loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; - if (abs >= 0 && pritems[abs].type == MENUMODE) - pritems[abs].on = true; - set_on_output(conf, output, ngroups, groups, - pritems); + retval = BSDDIALOG_ESC; + if (m.sel >= 0 && + m.pritems[m.sel].type == MENUMODE) + m.pritems[m.sel].on = true; loop = false; } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - draw_buttons(widget, bs, shortcut_butts); - wrefresh(widget); + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - draw_buttons(widget, bs, shortcut_butts); - wrefresh(widget); - } - break; - case KEY_RIGHT: - if (bs.curr < (int) bs.nbuttons - 1) { - bs.curr++; - draw_buttons(widget, bs, shortcut_butts); - wrefresh(widget); - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) + if (f1help_dialog(conf) != 0) return (BSDDIALOG_ERROR); - menurows = automenurows ? 0 : menurows; - if (menu_autosize(conf, rows, cols, &h, &w, text, - pos.line, &menurows, totnitems, bs) != 0) + if (mixedlist_draw(&d, true, &m) != 0) return (BSDDIALOG_ERROR); - if (menu_checksize(h, w, text, menurows, totnitems, - bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget, y, x, h, w, - textpad, text, &bs, shortcut_butts) != 0) + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (mixedlist_draw(&d, true, &m) != 0) return (BSDDIALOG_ERROR); - - doupdate(); - - prefresh(textpad, 0, 0, y + 1, x + 1 + TEXTHMARGIN, - y + h - menurows, x + 1 + w - TEXTHMARGIN); - - wclear(menuwin); - mvwin(menuwin, y + h - 5 - menurows, x + 2); - wresize(menuwin,menurows+2, w-4); - update_menuwin(conf, menuwin, menurows+2, w-4, - totnitems, menurows, ymenupad); - wrefresh(menuwin); - - ys = y + h - 5 - menurows + 1; - ye = y + h - 5 ; - if (conf->menu.align_left || (int)pos.line > w - 6) { - xs = x + 3; - xe = xs + w - 7; - } else { /* center */ - xs = x + 3 + (w-6)/2 - pos.line/2; - xe = xs + w - 5; - } - - drawseparators(conf, menupad, MIN((int)pos.line, w-6), - totnitems, pritems); - - if ((int)(ymenupad + menurows) - 1 < abs) - ymenupad = abs - menurows + 1; - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); - - refresh(); - break; } - if (abs < 0) + if (m.sel < 0) continue; switch(input) { case KEY_HOME: - next = getnext(totnitems, pritems, -1); - movefocus = next != abs; + next = getnext(m.nitems, m.pritems, -1); + changeitem = next != m.sel; break; + case '-': + case KEY_CTRL('p'): case KEY_UP: - next = getprev(pritems, abs); - movefocus = next != abs; + next = getprev(m.pritems, m.sel); + changeitem = next != m.sel; break; case KEY_PPAGE: - next = getfastprev(menurows, pritems, abs); - movefocus = next != abs; + next = getfastprev(m.menurows, m.pritems, m.sel); + changeitem = next != m.sel; break; case KEY_END: - next = getprev(pritems, totnitems); - movefocus = next != abs; + next = getprev(m.pritems, m.nitems); + changeitem = next != m.sel; break; + case '+': + case KEY_CTRL('n'): case KEY_DOWN: - next = getnext(totnitems, pritems, abs); - movefocus = next != abs; + next = getnext(m.nitems, m.pritems, m.sel); + changeitem = next != m.sel; break; case KEY_NPAGE: - next = getfastnext(menurows, totnitems, pritems, abs); - movefocus = next != abs; + next = getfastnext(m.menurows, m.nitems, m.pritems, m.sel); + changeitem = next != m.sel; break; case ' ': /* Space */ - if (pritems[abs].type == MENUMODE) - break; - else if (pritems[abs].type == CHECKLISTMODE) - pritems[abs].on = !pritems[abs].on; - else { /* RADIOLISTMODE */ - for (i = abs - pritems[abs].index; - i < totnitems && - pritems[i].group == pritems[abs].group; + if (m.pritems[m.sel].type == MENUMODE) { + retval = BUTTONVALUE(d.bs); + m.pritems[m.sel].on = true; + loop = false; + } else if (m.pritems[m.sel].type == CHECKLISTMODE) { + m.pritems[m.sel].on = !m.pritems[m.sel].on; + } else { /* RADIOLISTMODE */ + for (i = m.sel - m.pritems[m.sel].index; + i < m.nitems && + m.pritems[i].group == m.pritems[m.sel].group; i++) { - if (i != abs && pritems[i].on) { - pritems[i].on = false; - drawitem(conf, menupad, i, pos, - &pritems[i], false); + if (i != m.sel && m.pritems[i].on) { + m.pritems[i].on = false; + drawitem(conf, &m, i, false); } } - pritems[abs].on = !pritems[abs].on; + m.pritems[m.sel].on = !m.pritems[m.sel].on; } - drawitem(conf, menupad, abs, pos, &pritems[abs], true); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); + drawitem(conf, &m, m.sel, true); + pnoutrefresh(m.pad, m.ypad, 0, m.ys, m.xs, m.ye, m.xe); break; default: - if (shortcut_butts) { - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; - if (pritems[abs].type == MENUMODE) - pritems[abs].on = true; - set_on_output(conf, output, ngroups, - groups, pritems); + if (conf->menu.shortcut_buttons) { + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + if (m.pritems[m.sel].type == MENUMODE) + m.pritems[m.sel].on = true; loop = false; } break; } /* shourtcut items */ - next = getnextshortcut(conf, totnitems, pritems, abs, + next = getnextshortcut(m.nitems, m.pritems, m.sel, input); - movefocus = next != abs; + changeitem = next != m.sel; + } /* end switch get_wch() */ + + if (changeitem) { + drawitem(conf, &m, m.sel, false); + m.sel = next; + drawitem(conf, &m, m.sel, true); + if (m.ypad > m.sel && m.ypad > 0) + m.ypad = m.sel; + if ((int)(m.ypad + m.menurows) <= m.sel) + m.ypad = m.sel - m.menurows + 1; + update_menubox(conf, &m); + wnoutrefresh(m.box); + pnoutrefresh(m.pad, m.ypad, 0, m.ys, m.xs, m.ye, m.xe); + changeitem = false; } + } /* end while (loop) */ - if (movefocus) { - drawitem(conf, menupad, abs, pos, &pritems[abs], false); - abs = next; - drawitem(conf, menupad, abs, pos, &pritems[abs], true); - if (ymenupad > abs && ymenupad > 0) - ymenupad = abs; - if ((int)(ymenupad + menurows) <= abs) - ymenupad = abs - menurows + 1; - update_menuwin(conf, menuwin, menurows+2, w-4, - totnitems, menurows, ymenupad); - wrefresh(menuwin); - prefresh(menupad, ymenupad, 0, ys, xs, ye, xe); - movefocus = false; - } - } + set_return_on(&m, groups); if (focuslist != NULL) - *focuslist = abs < 0 ? -1 : pritems[abs].group; + *focuslist = m.sel < 0 ? -1 : m.pritems[m.sel].group; if (focusitem !=NULL) - *focusitem = abs < 0 ? -1 : pritems[abs].index; + *focusitem = m.sel < 0 ? -1 : m.pritems[m.sel].index; - delwin(menupad); - delwin(menuwin); - end_dialog(conf, shadow, widget, textpad); - free(pritems); + if (m.hasbottomdesc && conf->clear) { + move(SCREENLINES - 1, 2); + clrtoeol(); + } + delwin(m.pad); + delwin(m.box); + end_dialog(&d); + free(m.pritems); - return (output); + return (retval); } /* API */ @@ -765,12 +697,12 @@ bsddialog_mixedlist(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, unsigned int ngroups, struct bsddialog_menugroup *groups, int *focuslist, int *focusitem) { - int output; + int retval; - output = do_mixedlist(conf, text, rows, cols, menurows, MIXEDLISTMODE, + retval = do_mixedlist(conf, text, rows, cols, menurows, MIXEDLISTMODE, ngroups, groups, focuslist, focusitem); - return (output); + return (retval); } int @@ -778,14 +710,15 @@ bsddialog_checklist(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, unsigned int nitems, struct bsddialog_menuitem *items, int *focusitem) { - int output, focuslist = 0; + int retval, focuslist = 0; struct bsddialog_menugroup group = { - BSDDIALOG_CHECKLIST /* unused */, nitems, items}; + BSDDIALOG_CHECKLIST /* unused */, nitems, items, 0}; - output = do_mixedlist(conf, text, rows, cols, menurows, CHECKLISTMODE, + CHECK_ARRAY(nitems, items); /* efficiency, avoid do_mixedlist() */ + retval = do_mixedlist(conf, text, rows, cols, menurows, CHECKLISTMODE, 1, &group, &focuslist, focusitem); - return (output); + return (retval); } int @@ -793,14 +726,15 @@ bsddialog_menu(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, unsigned int nitems, struct bsddialog_menuitem *items, int *focusitem) { - int output, focuslist = 0; + int retval, focuslist = 0; struct bsddialog_menugroup group = { - BSDDIALOG_CHECKLIST /* unused */, nitems, items}; + BSDDIALOG_CHECKLIST /* unused */, nitems, items, 0}; - output = do_mixedlist(conf, text, rows, cols, menurows, MENUMODE, 1, + CHECK_ARRAY(nitems, items); /* efficiency, avoid do_mixedlist() */ + retval = do_mixedlist(conf, text, rows, cols, menurows, MENUMODE, 1, &group, &focuslist, focusitem); - return (output); + return (retval); } int @@ -808,12 +742,13 @@ bsddialog_radiolist(struct bsddialog_conf *conf, const char *text, int rows, int cols, unsigned int menurows, unsigned int nitems, struct bsddialog_menuitem *items, int *focusitem) { - int output, focuslist = 0; + int retval, focuslist = 0; struct bsddialog_menugroup group = { - BSDDIALOG_RADIOLIST /* unused */, nitems, items}; + BSDDIALOG_RADIOLIST /* unused */, nitems, items, 0}; - output = do_mixedlist(conf, text, rows, cols, menurows, RADIOLISTMODE, + CHECK_ARRAY(nitems, items); /* efficiency, avoid do_mixedlist() */ + retval = do_mixedlist(conf, text, rows, cols, menurows, RADIOLISTMODE, 1, &group, &focuslist, focusitem); - return (output); + return (retval); } diff --git a/contrib/bsddialog/lib/messagebox.c b/contrib/bsddialog/lib/messagebox.c index 24b34ccbce97..c3d4a20f5404 100644 --- a/contrib/bsddialog/lib/messagebox.c +++ b/contrib/bsddialog/lib/messagebox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,184 +25,172 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - #include <curses.h> -#include <string.h> #include "bsddialog.h" +#include "bsddialog_theme.h" #include "lib_util.h" -static int -message_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, const char *text, struct buttons bs) -{ - int htext, wtext; +struct scroll { + int ypad; /* y scrollable pad */ + int htext; /* real h text to draw, to use with htextpad */ + int htextpad; /* h textpad, draw_dialog() set at least 1 */ + int printrows; /* h - BORDER - HBUTTONS - BORDER */ +}; - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, &bs, 0, SCREENCOLS/2, - &htext, &wtext) != 0) - return (BSDDIALOG_ERROR); +static void textupdate(struct dialog *d, struct scroll *s) +{ + if (s->htext > 0 && s->htextpad > s->printrows) { + wattron(d->widget, t.dialog.arrowcolor); + mvwprintw(d->widget, d->h - HBUTTONS - BORDER, + d->w - 4 - TEXTHMARGIN - BORDER, + "%3d%%", 100 * (s->ypad + s->printrows) / s->htextpad); + wattroff(d->widget, t.dialog.arrowcolor); + wnoutrefresh(d->widget); } - - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, wtext, 0, &bs); - - if (rows == BSDDIALOG_AUTOSIZE) - *h = widget_min_height(conf, htext, 0, true); - - return (0); + rtextpad(d, s->ypad, 0, 0, HBUTTONS); } -static int message_checksize(int rows, int cols, struct buttons bs) +static int message_size_position(struct dialog *d, int *htext) { - int mincols; + int minw; - mincols = VBORDERS; - mincols += buttons_width(bs); - - if (cols < mincols) - RETURN_ERROR("Few cols, Msgbox and Yesno need at least width " - "for borders, buttons and spaces between buttons"); - - if (rows < HBORDERS + 2 /*buttons*/) - RETURN_ERROR("Msgbox and Yesno need at least height 4"); + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, (*htext < 0) ? htext : NULL, &d->bs, 0, 0) != 0) + return (BSDDIALOG_ERROR); + minw = (*htext > 0) ? 1 + TEXTHMARGINS : 0 ; + if (widget_checksize(d->h, d->w, &d->bs, MIN(*htext, 1), minw) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); return (0); } -static void -textupdate(WINDOW *widget, WINDOW *textpad, int htextpad, int ytextpad) +static int message_draw(struct dialog *d, bool redraw, struct scroll *s) { - int y, x, h, w; + int unused; - getbegyx(widget, y, x); - getmaxyx(widget, h, w); - - if (htextpad > h - 4) { - mvwprintw(widget, h-3, w-6, "%3d%%", - 100 * (ytextpad+h-4)/ htextpad); - wnoutrefresh(widget); + if (redraw) { /* redraw: RESIZE or F1 */ + hide_dialog(d); + refresh(); /* Important for decreasing screen */ } + if (message_size_position(d, &s->htext) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) /* doupdate() in main loop */ + return (BSDDIALOG_ERROR); + if (redraw) + refresh(); /* Important to fix grey lines expanding screen */ + + s->printrows = d->h - BORDER - HBUTTONS - BORDER; + s->ypad = 0; + getmaxyx(d->textpad, s->htextpad, unused); + (void)unused; /* fix unused error */ - pnoutrefresh(textpad, ytextpad, 0, y+1, x+2, y+h-4, x+w-2); + return (0); } static int do_message(struct bsddialog_conf *conf, const char *text, int rows, int cols, - struct buttons bs) + const char *oklabel, const char *cancellabel) { bool loop; - int y, x, h, w, input, output, ytextpad, htextpad, unused; - WINDOW *widget, *textpad, *shadow; + int retval; + wint_t input; + struct scroll s; + struct dialog d; - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (message_autosize(conf, rows, cols, &h, &w, text, bs) != 0) + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); - if (message_checksize(h, w, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) + set_buttons(&d, true, oklabel, cancellabel); + s.htext = -1; + if (message_draw(&d, false, &s) != 0) return (BSDDIALOG_ERROR); - ytextpad = 0; - getmaxyx(textpad, htextpad, unused); - unused++; /* fix unused error */ - textupdate(widget, textpad, htextpad, ytextpad); loop = true; while (loop) { + textupdate(&d, &s); doupdate(); - input = getch(); + if (get_wch(&input) == ERR) + continue; switch (input) { case KEY_ENTER: case 10: /* Enter */ - output = bs.value[bs.curr]; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ - if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + if (d.conf->key.enable_esc) { + retval = BSDDIALOG_ESC; loop = false; } break; case '\t': /* TAB */ - bs.curr = (bs.curr + 1) % bs.nbuttons; - draw_buttons(widget, bs, true); - wnoutrefresh(widget); + case KEY_RIGHT: + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); break; case KEY_LEFT: - if (bs.curr > 0) { - bs.curr--; - draw_buttons(widget, bs, true); - wnoutrefresh(widget); - } + d.bs.curr--; + if (d.bs.curr < 0) + d.bs.curr = d.bs.nbuttons - 1; + DRAW_BUTTONS(d); break; - case KEY_RIGHT: - if (bs.curr < (int)bs.nbuttons - 1) { - bs.curr++; - draw_buttons(widget, bs, true); - wnoutrefresh(widget); - } + case '-': + case KEY_CTRL('p'): + case KEY_UP: + if (s.ypad > 0) + s.ypad--; + break; + case '+': + case KEY_CTRL('n'): + case KEY_DOWN: + if (s.ypad + s.printrows < s.htextpad) + s.ypad++; + break; + case KEY_HOME: + s.ypad = 0; + break; + case KEY_END: + s.ypad = MAX(s.htextpad - s.printrows, 0); + break; + case KEY_PPAGE: + s.ypad = MAX(s.ypad - s.printrows, 0); + break; + case KEY_NPAGE: + s.ypad += s.printrows; + if (s.ypad + s.printrows > s.htextpad) + s.ypad = s.htextpad - s.printrows; break; case KEY_F(1): - if (conf->key.f1_file == NULL && - conf->key.f1_message == NULL) + if (d.conf->key.f1_file == NULL && + d.conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (message_autosize(conf, rows, cols, &h, &w, text, - bs) != 0) - return (BSDDIALOG_ERROR); - if (message_checksize(h, w, bs) != 0) + if (f1help_dialog(d.conf) != 0) return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (message_draw(&d, true, &s) != 0) return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget, y, x, h, w, - textpad, text, &bs, true) != 0) - return (BSDDIALOG_ERROR); - - getmaxyx(textpad, htextpad, unused); - textupdate(widget, textpad, htextpad, ytextpad); - - /* Important to fix grey lines expanding screen */ - refresh(); break; - case KEY_UP: - if (ytextpad == 0) - break; - ytextpad--; - textupdate(widget, textpad, htextpad, ytextpad); - break; - case KEY_DOWN: - if (ytextpad + h - 4 >= htextpad) - break; - ytextpad++; - textupdate(widget, textpad, htextpad, ytextpad); + case KEY_CTRL('l'): + case KEY_RESIZE: + if (message_draw(&d, true, &s) != 0) + return (BSDDIALOG_ERROR); break; default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); loop = false; } } } - end_dialog(conf, shadow, widget, textpad); + end_dialog(&d); - return (output); + return (retval); } /* API */ @@ -210,20 +198,34 @@ int bsddialog_msgbox(struct bsddialog_conf *conf, const char *text, int rows, int cols) { - struct buttons bs; - - get_buttons(conf, &bs, BUTTON_OK_LABEL, NULL); - - return (do_message(conf, text, rows, cols, bs)); + return (do_message(conf, text, rows, cols, OK_LABEL, NULL)); } int bsddialog_yesno(struct bsddialog_conf *conf, const char *text, int rows, int cols) { - struct buttons bs; + return (do_message(conf, text, rows, cols, "Yes", "No")); +} - get_buttons(conf, &bs, "Yes", "No"); +int +bsddialog_infobox(struct bsddialog_conf *conf, const char *text, int rows, + int cols) +{ + int htext; + struct dialog d; - return (do_message(conf, text, rows, cols, bs)); -}
\ No newline at end of file + if (prepare_dialog(conf, text, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + htext = -1; + if (message_size_position(&d, &htext) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(&d) != 0) + return (BSDDIALOG_ERROR); + TEXTPAD(&d, 0); + doupdate(); + + end_dialog(&d); + + return (BSDDIALOG_OK); +} diff --git a/contrib/bsddialog/lib/textbox.c b/contrib/bsddialog/lib/textbox.c index 69eff7c0bc2e..1f730e0d925b 100644 --- a/contrib/bsddialog/lib/textbox.c +++ b/contrib/bsddialog/lib/textbox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,199 +25,248 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - #include <curses.h> -#include <string.h> #include "bsddialog.h" #include "bsddialog_theme.h" #include "lib_util.h" -static void -textbox_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, int hpad, int wpad, struct buttons bs) +struct scrolltext { + WINDOW *pad; + int ypad; + int xpad; + int ys; + int ye; + int xs; + int xe; + int hpad; + int wpad; + int margin; /* 2 with multicolumn char, 0 otherwise */ + int printrows; /* d.h - BORDERS - HBUTTONS */ +}; + +static void updateborders(struct dialog *d, struct scrolltext *st) { - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, 0, wpad, &bs); + chtype arrowch; + cchar_t borderch; + + if (d->conf->no_lines) + setcchar(&borderch, L" ", 0, 0, NULL); + else if (d->conf->ascii_lines) + setcchar(&borderch, L"|", 0, 0, NULL); + else + borderch = *WACS_VLINE; - if (rows == BSDDIALOG_AUTOSIZE) - *h = widget_min_height(conf, 0, hpad, true); + if (st->xpad > 0) { + arrowch = LARROW(d->conf) | t.dialog.arrowcolor; + mvwvline(d->widget, (d->h / 2) - 2, 0, arrowch, 4); + } else { + wattron(d->widget, t.dialog.lineraisecolor); + mvwvline_set(d->widget, (d->h / 2) - 2, 0, &borderch, 4); + wattroff(d->widget, t.dialog.lineraisecolor); + } + + if (st->xpad + d->w - 2 - st->margin < st->wpad) { + arrowch = RARROW(d->conf) | t.dialog.arrowcolor; + mvwvline(d->widget, (d->h / 2) - 2, d->w - 1, arrowch, 4); + } else { + wattron(d->widget, t.dialog.linelowercolor); + mvwvline_set(d->widget, (d->h / 2) - 2, d->w - 1, &borderch, 4); + wattroff(d->widget, t.dialog.linelowercolor); + } + + if (st->hpad > d->h - 4) { + wattron(d->widget, t.dialog.arrowcolor); + mvwprintw(d->widget, d->h - 3, d->w - 6, + "%3d%%", 100 * (st->ypad + d->h - 4) / st->hpad); + wattroff(d->widget, t.dialog.arrowcolor); + } } -static int -textbox_checksize(int rows, int cols, int hpad, struct buttons bs) +static int textbox_size_position(struct dialog *d, struct scrolltext *st) { - int mincols; + int minw; - mincols = VBORDERS + bs.sizebutton; + if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w, + d->text, NULL, &d->bs, st->hpad, st->wpad + st->margin) != 0) + return (BSDDIALOG_ERROR); + minw = (st->wpad > 0) ? 2 /*multicolumn char*/ + st->margin : 0 ; + if (widget_checksize(d->h, d->w, &d->bs, MIN(st->hpad, 1), minw) != 0) + return (BSDDIALOG_ERROR); + if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0) + return (BSDDIALOG_ERROR); - if (cols < mincols) - RETURN_ERROR("Few cols for the textbox"); + return (0); +} + +static int textbox_draw(struct dialog *d, bool redraw, struct scrolltext *st) +{ + if (redraw) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (textbox_size_position(d, st) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) /* wrefresh() and prefresh() in main loop */ + return (BSDDIALOG_ERROR); + if (redraw) + refresh(); /* Important to fix grey lines expanding screen */ - if (rows < 4 /* HBORDERS + button*/ + (hpad > 0 ? 1 : 0)) - RETURN_ERROR("Few rows for the textbox"); + st->ys = d->y + 1; + st->xs = (st->margin == 0) ? d->x + 1 : d->x + 2; + st->ye = st->ys + d->h - 5; + st->xe = st->xs + d->w - 3 - st->margin; + st->ypad = st->xpad = 0; + st->printrows = d->h-4; return (0); } /* API */ int -bsddialog_textbox(struct bsddialog_conf *conf, const char* file, int rows, +bsddialog_textbox(struct bsddialog_conf *conf, const char *file, int rows, int cols) { - bool loop; - int i, output, input; - int y, x, h, w, hpad, wpad, ypad, xpad, ys, ye, xs, xe, printrows; + bool loop, has_multicol_ch; + int i, retval; + unsigned int defaulttablen, linecols; + wint_t input; char buf[BUFSIZ]; FILE *fp; - struct buttons bs; - WINDOW *shadow, *widget, *pad; + struct scrolltext st; + struct dialog d; + if (file == NULL) + RETURN_ERROR("*file is NULL"); if ((fp = fopen(file, "r")) == NULL) - RETURN_ERROR("Cannot open file"); + RETURN_FMTERROR("Cannot open file \"%s\"", file); + + if (prepare_dialog(conf, "" /* fake */, rows, cols, &d) != 0) + return (BSDDIALOG_ERROR); + set_buttons(&d, true, "EXIT", NULL); - hpad = 1; - wpad = 1; - pad = newpad(hpad, wpad); - wbkgd(pad, t.dialog.color); + defaulttablen = TABSIZE; + if (conf->text.tablen > 0) + set_tabsize(conf->text.tablen); + st.hpad = 1; + st.wpad = 1; + st.pad = newpad(st.hpad, st.wpad); + wbkgd(st.pad, t.dialog.color); + st.margin = 0; i = 0; while (fgets(buf, BUFSIZ, fp) != NULL) { - if ((int) strlen(buf) > wpad) { - wpad = strlen(buf); - wresize(pad, hpad, wpad); + if (str_props(buf, &linecols, &has_multicol_ch) != 0) + continue; + if ((int)linecols > st.wpad) { + st.wpad = linecols; + wresize(st.pad, st.hpad, st.wpad); } - if (i > hpad-1) { - hpad++; - wresize(pad, hpad, wpad); + if (i > st.hpad-1) { + st.hpad++; + wresize(st.pad, st.hpad, st.wpad); } - mvwaddstr(pad, i, 0, buf); + mvwaddstr(st.pad, i, 0, buf); i++; + if (has_multicol_ch) + st.margin = 2; } fclose(fp); + set_tabsize(defaulttablen); /* reset because it is curses global */ - bs.nbuttons = 1; - bs.label[0] = "EXIT"; - if (conf->button.ok_label != NULL) - bs.label[0] = conf->button.ok_label; - bs.value[0] = BSDDIALOG_OK; - bs.curr = 0; - bs.sizebutton = strlen(bs.label[0]) + 2; - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad, bs); - if (textbox_checksize(h, w, hpad, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, NULL, NULL, &bs, - true) != 0) + if (textbox_draw(&d, false, &st) != 0) return (BSDDIALOG_ERROR); - ys = y + 1; - xs = x + 1; - ye = ys + h - 5; - xe = xs + w - 3; - ypad = xpad = 0; - printrows = h-4; loop = true; while (loop) { - wnoutrefresh(widget); - pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe); - doupdate(); - input = getch(); + updateborders(&d, &st); + /* + * Trick, overflow multicolumn charchter right border: + * wnoutrefresh(widget); + * pnoutrefresh(pad, ypad, xpad, ys, xs, ye, xe); + * doupdate(); + */ + wrefresh(d.widget); + prefresh(st.pad, st.ypad, st.xpad, st.ys, st.xs, st.ye, st.xe); + if (get_wch(&input) == ERR) + continue; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); + break; /* loop */ + } switch(input) { case KEY_ENTER: case 10: /* Enter */ - output = BSDDIALOG_OK; + retval = BUTTONVALUE(d.bs); loop = false; break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; + case '\t': /* TAB */ + d.bs.curr = (d.bs.curr + 1) % d.bs.nbuttons; + DRAW_BUTTONS(d); + break; case KEY_HOME: - ypad = 0; + st.ypad = 0; break; case KEY_END: - ypad = hpad - printrows; - ypad = ypad < 0 ? 0 : ypad; + st.ypad = MAX(st.hpad - st.printrows, 0); break; case KEY_PPAGE: - ypad -= printrows; - ypad = ypad < 0 ? 0 : ypad; + st.ypad = MAX(st.ypad - st.printrows, 0); break; case KEY_NPAGE: - ypad += printrows; - if (ypad + printrows > hpad) - ypad = hpad - printrows; + st.ypad += st.printrows; + if (st.ypad + st.printrows > st.hpad) + st.ypad = st.hpad - st.printrows; break; case '0': - xpad = 0; + st.xpad = 0; + break; case KEY_LEFT: case 'h': - xpad = xpad > 0 ? xpad - 1 : 0; + st.xpad = MAX(st.xpad - 1, 0); break; case KEY_RIGHT: case 'l': - xpad = (xpad + w-2) < wpad-1 ? xpad + 1 : xpad; + if (st.xpad + d.w - 2 - st.margin < st.wpad) + st.xpad++; break; case KEY_UP: case 'k': - ypad = ypad > 0 ? ypad - 1 : 0; + st.ypad = MAX(st.ypad - 1, 0); break; case KEY_DOWN: case'j': - ypad = ypad + printrows <= hpad -1 ? ypad + 1 : ypad; + if (st.ypad + st.printrows <= st.hpad -1) + st.ypad++; break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - textbox_autosize(conf, rows, cols, &h, &w, hpad, wpad, - bs); - if (textbox_checksize(h, w, hpad, bs) != 0) + if (f1help_dialog(conf) != 0) return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) + if (textbox_draw(&d, true, &st) != 0) return (BSDDIALOG_ERROR); - - ys = y + 1; - xs = x + 1; - ye = ys + h - 5; - xe = xs + w - 3; - ypad = xpad = 0; - printrows = h - 4; - - if (update_dialog(conf, shadow, widget, y, x, h, w, - NULL, NULL, &bs, true) != 0) + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (textbox_draw(&d, true, &st) != 0) return (BSDDIALOG_ERROR); - - /* Important to fix grey lines expanding screen */ - refresh(); break; - default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; - loop = false; - } } } - end_dialog(conf, shadow, widget, pad); + delwin(st.pad); + end_dialog(&d); - return (output); -}
\ No newline at end of file + return (retval); +} diff --git a/contrib/bsddialog/lib/theme.c b/contrib/bsddialog/lib/theme.c index 20b1e35428dd..6c17d908324b 100644 --- a/contrib/bsddialog/lib/theme.c +++ b/contrib/bsddialog/lib/theme.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -32,137 +32,117 @@ #include "lib_util.h" #define GET_COLOR(bg, fg) (COLOR_PAIR(bg * 8 + fg +1)) +#define WHITE GET_COLOR(COLOR_WHITE, COLOR_BLACK) +#define BLACK GET_COLOR(COLOR_WHITE, COLOR_BLACK) | A_REVERSE +#define NFLAGS 6 struct bsddialog_theme t; +bool hastermcolors; -static struct bsddialog_theme bsddialogtheme = { -#define bgwidget COLOR_WHITE -#define bgcurr COLOR_YELLOW - .screen.color = GET_COLOR(COLOR_BLACK, COLOR_CYAN), - - .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, +struct flag_converter { + unsigned int public; + unsigned int private; +}; - .dialog.delimtitle = true, - .dialog.titlecolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .dialog.lineraisecolor = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.linelowercolor = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.color = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, bgwidget), - .dialog.arrowcolor = GET_COLOR(COLOR_YELLOW, bgwidget), - - .menu.f_selectorcolor = GET_COLOR(COLOR_BLACK, bgcurr), - .menu.selectorcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .menu.f_desccolor = GET_COLOR(COLOR_WHITE, bgcurr), - .menu.desccolor = GET_COLOR(COLOR_BLACK, bgwidget), - .menu.f_namecolor = GET_COLOR(COLOR_BLACK, bgcurr), - .menu.namecolor = GET_COLOR(COLOR_BLACK, bgwidget), - .menu.namesepcolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .menu.descsepcolor = GET_COLOR(COLOR_YELLOW, bgwidget), - .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, bgcurr), - .menu.shortcutcolor = GET_COLOR(COLOR_RED, bgwidget), - - .form.f_fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .form.fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN), - .form.readonlycolor = GET_COLOR(COLOR_CYAN,COLOR_WHITE), - - .bar.f_color = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .bar.color = GET_COLOR(COLOR_BLUE, COLOR_WHITE), - - .button.hmargin = 3, - .button.leftdelim = '[', - .button.rightdelim = ']', - .button.f_delimcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .button.delimcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .button.f_color = GET_COLOR(COLOR_BLACK, bgcurr) | A_UNDERLINE, - .button.color = GET_COLOR(COLOR_BLACK, bgwidget) | A_UNDERLINE, - .button.f_shortcutcolor = GET_COLOR(COLOR_RED, bgcurr) | A_UNDERLINE, - .button.shortcutcolor = GET_COLOR(COLOR_RED, bgwidget) | A_UNDERLINE +static struct flag_converter flagconv[NFLAGS] = { + {BSDDIALOG_BLINK, A_BLINK}, + {BSDDIALOG_BOLD, A_BOLD}, + {BSDDIALOG_HALFBRIGHT, A_DIM}, + {BSDDIALOG_HIGHLIGHT, A_STANDOUT}, + {BSDDIALOG_REVERSE, A_REVERSE}, + {BSDDIALOG_UNDERLINE, A_UNDERLINE} }; static struct bsddialog_theme blackwhite = { -#define fg COLOR_WHITE -#define bk COLOR_BLACK - .screen.color = GET_COLOR(fg, bk), + .screen.color = WHITE, - .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, + .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), + .shadow.y = 1, + .shadow.x = 2, .dialog.delimtitle = true, - .dialog.titlecolor = GET_COLOR(fg, bk), - .dialog.lineraisecolor = GET_COLOR(fg, bk), - .dialog.linelowercolor = GET_COLOR(fg, bk), - .dialog.color = GET_COLOR(fg, bk), - .dialog.bottomtitlecolor = GET_COLOR(fg, bk), - .dialog.arrowcolor = GET_COLOR(fg, bk), - - .menu.f_selectorcolor = GET_COLOR(fg, bk) | A_REVERSE, - .menu.selectorcolor = GET_COLOR(fg, bk), - .menu.f_desccolor = GET_COLOR(fg, bk) | A_REVERSE, - .menu.desccolor = GET_COLOR(fg, bk), - .menu.f_namecolor = GET_COLOR(fg, bk) | A_REVERSE, - .menu.namecolor = GET_COLOR(fg, bk), - .menu.namesepcolor = GET_COLOR(fg, bk), - .menu.descsepcolor = GET_COLOR(fg, bk), - .menu.f_shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE, - .menu.shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE, - - .form.f_fieldcolor = GET_COLOR(fg, bk) | A_REVERSE, - .form.fieldcolor = GET_COLOR(fg, bk), - .form.readonlycolor = GET_COLOR(fg, bk), - - .bar.f_color = GET_COLOR(fg, bk) | A_REVERSE, - .bar.color = GET_COLOR(fg, bk), - - .button.hmargin = 3, + .dialog.titlecolor = WHITE, + .dialog.lineraisecolor = WHITE, + .dialog.linelowercolor = WHITE, + .dialog.color = WHITE, + .dialog.bottomtitlecolor = WHITE, + .dialog.arrowcolor = WHITE, + + .menu.f_prefixcolor = WHITE, + .menu.prefixcolor = WHITE, + .menu.f_selectorcolor = BLACK, + .menu.selectorcolor = WHITE, + .menu.f_desccolor = BLACK, + .menu.desccolor = WHITE, + .menu.f_namecolor = BLACK, + .menu.namecolor = WHITE, + .menu.f_shortcutcolor = BLACK | A_UNDERLINE, + .menu.shortcutcolor = WHITE | A_UNDERLINE, + .menu.bottomdesccolor = WHITE, + .menu.sepnamecolor = WHITE, + .menu.sepdesccolor = WHITE, + + .form.f_fieldcolor = BLACK, + .form.fieldcolor = WHITE, + .form.readonlycolor = WHITE, + .form.bottomdesccolor = WHITE, + + .bar.f_color = BLACK, + .bar.color = WHITE, + + .button.minmargin = 1, + .button.maxmargin = 5, .button.leftdelim = '[', .button.rightdelim = ']', - .button.f_delimcolor = GET_COLOR(fg, bk), - .button.delimcolor = GET_COLOR(fg, bk), - .button.f_color = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE, - .button.color = GET_COLOR(fg, bk) | A_UNDERLINE, - .button.f_shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE | A_REVERSE, - .button.shortcutcolor = GET_COLOR(fg, bk) | A_UNDERLINE + .button.f_delimcolor = WHITE, + .button.delimcolor = WHITE, + .button.f_color = BLACK, + .button.color = WHITE, + .button.f_shortcutcolor = BLACK | A_UNDERLINE | A_BOLD, + .button.shortcutcolor = WHITE | A_UNDERLINE | A_BOLD }; -static struct bsddialog_theme dialogtheme = { - .screen.color = GET_COLOR(COLOR_CYAN, COLOR_BLUE) | A_BOLD, +static struct bsddialog_theme flat = { + .screen.color = GET_COLOR(COLOR_CYAN, COLOR_BLUE) | A_BOLD, - .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), - .shadow.h = 1, - .shadow.w = 2, + .shadow.color = GET_COLOR(COLOR_BLACK, COLOR_BLACK), + .shadow.y = 1, + .shadow.x = 2, - .dialog.delimtitle = false, + .dialog.delimtitle = true, .dialog.titlecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE) | A_BOLD, - .dialog.lineraisecolor = GET_COLOR(COLOR_WHITE, COLOR_WHITE) | A_BOLD, - .dialog.linelowercolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD, + .dialog.lineraisecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .dialog.linelowercolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), .dialog.color = GET_COLOR(COLOR_BLACK, COLOR_WHITE), .dialog.bottomtitlecolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE) | A_BOLD, - .dialog.arrowcolor = GET_COLOR(COLOR_GREEN, COLOR_WHITE), - - .menu.f_selectorcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .menu.selectorcolor = GET_COLOR(COLOR_BLACK, bgwidget), - .menu.f_desccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .menu.desccolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), - .menu.f_namecolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), - .menu.namecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE), - .menu.namesepcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), - .menu.descsepcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), - .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, COLOR_BLUE), - .menu.shortcutcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), - - .form.f_fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, - .form.fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN) | A_BOLD, - .form.readonlycolor = GET_COLOR(COLOR_CYAN, COLOR_WHITE)| A_BOLD, + .dialog.arrowcolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE), + + .menu.f_prefixcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.prefixcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_selectorcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.selectorcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_desccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.desccolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), + .menu.f_namecolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.namecolor = GET_COLOR(COLOR_BLUE, COLOR_WHITE), + .menu.f_shortcutcolor = GET_COLOR(COLOR_RED, COLOR_BLUE), + .menu.shortcutcolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + .menu.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), + .menu.sepnamecolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + .menu.sepdesccolor = GET_COLOR(COLOR_RED, COLOR_WHITE), + + .form.f_fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, + .form.fieldcolor = GET_COLOR(COLOR_WHITE, COLOR_CYAN) | A_BOLD, + .form.readonlycolor = GET_COLOR(COLOR_CYAN, COLOR_WHITE)| A_BOLD, + .form.bottomdesccolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE), .bar.f_color = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, .bar.color = GET_COLOR(COLOR_BLUE, COLOR_WHITE) | A_BOLD, - .button.hmargin = 3, - .button.leftdelim = '<', - .button.rightdelim = '>', + .button.minmargin = 1, + .button.maxmargin = 5, + .button.leftdelim = '[', + .button.rightdelim = ']', .button.f_delimcolor = GET_COLOR(COLOR_WHITE, COLOR_BLUE) | A_BOLD, .button.delimcolor = GET_COLOR(COLOR_BLACK, COLOR_WHITE), .button.f_color = GET_COLOR(COLOR_YELLOW, COLOR_BLUE) | A_BOLD, @@ -177,8 +157,8 @@ set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src) dst->screen.color = src->screen.color; dst->shadow.color = src->shadow.color; - dst->shadow.h = src->shadow.h; - dst->shadow.w = src->shadow.w; + dst->shadow.y = src->shadow.y; + dst->shadow.x = src->shadow.x; dst->dialog.delimtitle = src->dialog.delimtitle; dst->dialog.titlecolor = src->dialog.titlecolor; @@ -188,25 +168,30 @@ set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src) dst->dialog.bottomtitlecolor = src->dialog.bottomtitlecolor; dst->dialog.arrowcolor = src->dialog.arrowcolor; + dst->menu.f_prefixcolor = src->menu.f_prefixcolor; + dst->menu.prefixcolor = src->menu.prefixcolor; dst->menu.f_selectorcolor = src->menu.f_selectorcolor; dst->menu.selectorcolor = src->menu.selectorcolor; dst->menu.f_desccolor = src->menu.f_desccolor; dst->menu.desccolor = src->menu.desccolor; dst->menu.f_namecolor = src->menu.f_namecolor; dst->menu.namecolor = src->menu.namecolor; - dst->menu.namesepcolor = src->menu.namesepcolor; - dst->menu.descsepcolor = src->menu.descsepcolor; dst->menu.f_shortcutcolor = src->menu.f_shortcutcolor; dst->menu.shortcutcolor = src->menu.shortcutcolor; + dst->menu.bottomdesccolor = src->menu.bottomdesccolor; + dst->menu.sepnamecolor = src->menu.sepnamecolor; + dst->menu.sepdesccolor = src->menu.sepdesccolor; - dst->form.f_fieldcolor = src->form.f_fieldcolor; - dst->form.fieldcolor = src->form.fieldcolor; - dst->form.readonlycolor = src->form.readonlycolor; + dst->form.f_fieldcolor = src->form.f_fieldcolor; + dst->form.fieldcolor = src->form.fieldcolor; + dst->form.readonlycolor = src->form.readonlycolor; + dst->form.bottomdesccolor = src->form.bottomdesccolor; dst->bar.f_color = src->bar.f_color; dst->bar.color = src->bar.color; - dst->button.hmargin = src->button.hmargin; + dst->button.minmargin = src->button.minmargin; + dst->button.maxmargin = src->button.maxmargin; dst->button.leftdelim = src->button.leftdelim; dst->button.rightdelim = src->button.rightdelim; dst->button.f_delimcolor = src->button.f_delimcolor; @@ -217,68 +202,87 @@ set_theme(struct bsddialog_theme *dst, struct bsddialog_theme *src) dst->button.shortcutcolor = src->button.shortcutcolor; bkgd(dst->screen.color); - refresh(); } /* API */ int bsddialog_get_theme(struct bsddialog_theme *theme) { - if (theme == NULL) - RETURN_ERROR("theme is NULL"); - if (sizeof(*theme) != sizeof(struct bsddialog_theme)) - RETURN_ERROR("Bad suze struct bsddialog_theme"); - + CHECK_PTR(theme); set_theme(theme, &t); - return (0); + return (BSDDIALOG_OK); } int bsddialog_set_theme(struct bsddialog_theme *theme) { - if (theme == NULL) - RETURN_ERROR("theme is NULL"); - if (sizeof(*theme) != sizeof(struct bsddialog_theme)) - RETURN_ERROR("Bad size struct bsddialog_theme"); - + CHECK_PTR(theme); set_theme(&t, theme); + wnoutrefresh(stdscr); - return (0); + return (BSDDIALOG_OK); } int bsddialog_set_default_theme(enum bsddialog_default_theme newtheme) { - - if (newtheme == BSDDIALOG_THEME_FLAT) { - bsddialog_set_theme(&dialogtheme); - t.dialog.lineraisecolor = t.dialog.linelowercolor; - t.dialog.delimtitle = true; - t.button.leftdelim = '['; - t.button.rightdelim = ']'; + if (newtheme == BSDDIALOG_THEME_3D) { + set_theme(&t, &flat); + t.dialog.lineraisecolor = + GET_COLOR(COLOR_WHITE, COLOR_WHITE) | A_BOLD; + t.dialog.delimtitle = false; + t.dialog.bottomtitlecolor = t.dialog.bottomtitlecolor | A_BOLD; + } else if (newtheme == BSDDIALOG_THEME_BLACKWHITE) { + set_theme(&t, &blackwhite); + } else if (newtheme == BSDDIALOG_THEME_FLAT) { + set_theme(&t, &flat); + } else { + RETURN_FMTERROR("Unknown default theme (%d), " + "to use enum bsddialog_default_theme", + newtheme); } - else if (newtheme == BSDDIALOG_THEME_BSDDIALOG) - bsddialog_set_theme(&bsddialogtheme); - else if (newtheme == BSDDIALOG_THEME_BLACKWHITE) - bsddialog_set_theme(&blackwhite); - else if (newtheme == BSDDIALOG_THEME_DIALOG) - bsddialog_set_theme(&dialogtheme); - else - RETURN_ERROR("Unknow default theme"); - - return (0); + wnoutrefresh(stdscr); + + return (BSDDIALOG_OK); } int bsddialog_color(enum bsddialog_color foreground, enum bsddialog_color background, unsigned int flags) { - unsigned int cursesflags = 0; + unsigned int i, f; + + f = 0; + for (i=0; i < NFLAGS; i++) + if (flags & flagconv[i].public) + f |= flagconv[i].private; - if (flags & BSDDIALOG_BOLD) - cursesflags |= A_BOLD; - if (flags & BSDDIALOG_REVERSE) - cursesflags |= A_REVERSE; - if (flags & BSDDIALOG_UNDERLINE) - cursesflags |= A_UNDERLINE; + return (GET_COLOR(foreground, background) | f); +} - return (GET_COLOR(foreground, background) | cursesflags); +int +bsddialog_color_attrs(int color, enum bsddialog_color *foreground, + enum bsddialog_color *background, unsigned int *flags) +{ + short fg, bg; + unsigned int i, f; + + if (flags != NULL) { + f = 0; + for (i=0; i < NFLAGS; i++) + if (color & flagconv[i].private) + f |= flagconv[i].public; + *flags = f; + } + if (pair_content(PAIR_NUMBER(color), &fg, &bg) != OK) + RETURN_ERROR("Cannot get color attributes"); + if (foreground != NULL) + *foreground = fg; + if (background != NULL) + *background = bg; + + return (BSDDIALOG_OK); +} + +bool bsddialog_hascolors(void) +{ + return (hastermcolors); } diff --git a/contrib/bsddialog/lib/timebox.c b/contrib/bsddialog/lib/timebox.c index 7d69666c32b8..603d5fa5d7a3 100644 --- a/contrib/bsddialog/lib/timebox.c +++ b/contrib/bsddialog/lib/timebox.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2021-2022 Alfonso Sabato Siciliano + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -25,512 +25,216 @@ * SUCH DAMAGE. */ -#include <sys/param.h> - -#include <ctype.h> #include <curses.h> -#include <string.h> #include "bsddialog.h" +#include "bsddialog_theme.h" #include "lib_util.h" -#define MINWDATE 23 /* 3 windows and their borders */ #define MINWTIME 14 /* 3 windows and their borders */ - -static int -datetime_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h, - int *w, int minw, const char *text, struct buttons bs) +#define HBOX 3 +#define WBOX 4 + +struct clock { + unsigned int max; + unsigned int value; + WINDOW *win; +}; + +static void +drawsquare(struct bsddialog_conf *conf, WINDOW *win, unsigned int value, + bool focus) { - int htext, wtext; - - if (cols == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_AUTOSIZE) { - if (text_size(conf, rows, cols, text, &bs, 3, minw, &htext, - &wtext) != 0) - return (BSDDIALOG_ERROR); + draw_borders(conf, win, LOWERED); + if (focus) { + wattron(win, t.dialog.arrowcolor); + mvwhline(win, 0, 1, UARROW(conf), 2); + mvwhline(win, 2, 1, DARROW(conf), 2); + wattroff(win, t.dialog.arrowcolor); } - if (cols == BSDDIALOG_AUTOSIZE) - *w = widget_min_width(conf, htext,minw, &bs); + if (focus) + wattron(win, t.menu.f_namecolor); + mvwprintw(win, 1, 1, "%02u", value); + if (focus) + wattroff(win, t.menu.f_namecolor); - if (rows == BSDDIALOG_AUTOSIZE) - *h = widget_min_height(conf, htext, 3 /* windows */, true); - - return (0); + wnoutrefresh(win); } -static int -datetime_checksize(int rows, int cols, int minw, struct buttons bs) +static int timebox_draw(struct dialog *d, bool redraw, struct clock *c) { - int mincols; - - mincols = VBORDERS; - mincols += buttons_width(bs); - mincols = MAX(minw, mincols); + int y, x; - if (cols < mincols) - RETURN_ERROR("Few cols for this timebox/datebox"); - - if (rows < 7) /* 2 button + 2 borders + 3 windows */ - RETURN_ERROR("Few rows for this timebox/datebox, at least 7"); + if (redraw) { + hide_dialog(d); + refresh(); /* Important for decreasing screen */ + } + if (dialog_size_position(d, HBOX, MINWTIME, NULL) != 0) + return (BSDDIALOG_ERROR); + if (draw_dialog(d) != 0) /* doupdate() in mail loop */ + return (BSDDIALOG_ERROR); + if (redraw) + refresh(); /* Important to fix grey lines expanding screen */ + TEXTPAD(d, HBOX + HBUTTONS); + + y = d->y + d->h - BORDER - HBUTTONS - HBOX; + x = d->x + d->w/2 - 7; + update_box(d->conf, c[0].win, y, x, HBOX, WBOX, LOWERED); + mvwaddch(d->widget, d->h - 5, d->w/2 - 3, ':'); + update_box(d->conf, c[1].win, y, x += 5, HBOX, WBOX, LOWERED); + mvwaddch(d->widget, d->h - 5, d->w/2 + 2, ':'); + update_box(d->conf, c[2].win, y, x + 5, HBOX, WBOX, LOWERED); + wnoutrefresh(d->widget); /* for mvwaddch(':') */ return (0); } +/* API */ int bsddialog_timebox(struct bsddialog_conf *conf, const char* text, int rows, int cols, unsigned int *hh, unsigned int *mm, unsigned int *ss) { bool loop, focusbuttons; - int i, input, output, y, x, h, w, sel; - WINDOW *widget, *textpad, *shadow; - struct buttons bs; - struct myclockstruct { - unsigned int max; - unsigned int value; - WINDOW *win; - }; - - if (hh == NULL || mm == NULL || ss == NULL) - RETURN_ERROR("hh / mm / ss cannot be NULL"); - - struct myclockstruct c[3] = { + int i, retval, sel; + wint_t input; + struct dialog d; + struct clock c[3] = { {23, *hh, NULL}, {59, *mm, NULL}, {59, *ss, NULL} }; - for (i = 0 ; i < 3; i++) { - if (c[i].value > c[i].max) - c[i].value = c[i].max; - } - - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) + CHECK_PTR(hh); + CHECK_PTR(mm); + CHECK_PTR(ss); + if (prepare_dialog(conf, text, rows, cols, &d) != 0) return (BSDDIALOG_ERROR); - if (datetime_autosize(conf, rows, cols, &h, &w, MINWTIME, text, - bs) != 0) - return (BSDDIALOG_ERROR); - if (datetime_checksize(h, w, MINWTIME, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) + set_buttons(&d, true, OK_LABEL, CANCEL_LABEL); + for (i=0; i<3; i++) { + if ((c[i].win = newwin(1, 1, 1, 1)) == NULL) + RETURN_FMTERROR("Cannot build WINDOW for time[%d]", i); + wbkgd(c[i].win, t.dialog.color); + c[i].value = MIN(c[i].value, c[i].max); + } + if (timebox_draw(&d, false, c) != 0) return (BSDDIALOG_ERROR); - pnoutrefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - doupdate(); - - c[0].win = new_boxed_window(conf, y+h-6, x + w/2 - 7, 3, 4, LOWERED); - mvwaddch(widget, h - 5, w/2 - 3, ':'); - c[1].win = new_boxed_window(conf, y+h-6, x + w/2 - 2, 3, 4, LOWERED); - mvwaddch(widget, h - 5, w/2 + 2, ':'); - c[2].win = new_boxed_window(conf, y+h-6, x + w/2 + 3, 3, 4, LOWERED); - - wrefresh(widget); - + sel = -1; loop = focusbuttons = true; while (loop) { - for (i = 0; i < 3; i++) { - mvwprintw(c[i].win, 1, 1, "%2d", c[i].value); - wrefresh(c[i].win); - } - - if (focusbuttons == false) { - wmove(c[sel].win, 1, 2); - wrefresh(c[sel].win); - } - - input = getch(); + for (i = 0; i < 3; i++) + drawsquare(conf, c[i].win, c[i].value, sel == i); + doupdate(); + if (get_wch(&input) == ERR) + continue; switch(input) { case KEY_ENTER: case 10: /* Enter */ - if (focusbuttons == false) - break; - output = bs.value[bs.curr]; - loop = false; + if (focusbuttons || conf->button.always_active) { + retval = BUTTONVALUE(d.bs); + loop = false; + } break; case 27: /* Esc */ if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; + retval = BSDDIALOG_ESC; loop = false; } break; - case KEY_RIGHT: case '\t': /* TAB */ + case KEY_CTRL('n'): + case KEY_RIGHT: if (focusbuttons) { - bs.curr++; - focusbuttons = bs.curr < (int)bs.nbuttons ? + d.bs.curr++; + focusbuttons = d.bs.curr < (int)d.bs.nbuttons ? true : false; if (focusbuttons == false) { - curs_set(1); sel = 0; + d.bs.curr = + conf->button.always_active ? 0 : -1; } } else { sel++; focusbuttons = sel > 2 ? true : false; if (focusbuttons) { - curs_set(0); - bs.curr = 0; + d.bs.curr = 0; } } - draw_buttons(widget, bs, true); - wrefresh(widget); + DRAW_BUTTONS(d); break; + case KEY_CTRL('p'): case KEY_LEFT: if (focusbuttons) { - bs.curr--; - focusbuttons = bs.curr < 0 ? false : true; + d.bs.curr--; + focusbuttons = d.bs.curr < 0 ? false : true; if (focusbuttons == false) { - curs_set(1); sel = 2; + d.bs.curr = + conf->button.always_active ? 0 : -1; } } else { sel--; focusbuttons = sel < 0 ? true : false; - if (focusbuttons) { - curs_set(0); - bs.curr = (int)bs.nbuttons - 1; - } + if (focusbuttons) + d.bs.curr = (int)d.bs.nbuttons - 1; } - draw_buttons(widget, bs, true); - wrefresh(widget); - break; - case KEY_UP: - if (focusbuttons) - break; - c[sel].value = c[sel].value > 0 ? - c[sel].value - 1 : c[sel].max; - break; - case KEY_DOWN: - if (focusbuttons) - break; - c[sel].value = c[sel].value < c[sel].max ? - c[sel].value + 1 : 0; + DRAW_BUTTONS(d); break; - case KEY_F(1): - if (conf->key.f1_file == NULL && - conf->key.f1_message == NULL) - break; - curs_set(0); - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - curs_set(1); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (datetime_autosize(conf, rows, cols, &h, &w, - MINWTIME, text, bs) != 0) - return (BSDDIALOG_ERROR); - if (datetime_checksize(h, w, MINWTIME, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget, y, x, h, w, - textpad, text, &bs, true) != 0) - return (BSDDIALOG_ERROR); - - doupdate(); - - mvwaddch(widget, h - 5, w/2 - 3, ':'); - mvwaddch(widget, h - 5, w/2 + 2, ':'); - wrefresh(widget); - - prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - - wclear(c[0].win); - mvwin(c[0].win, y + h - 6, x + w/2 - 7); - draw_borders(conf, c[0].win, 3, 4, LOWERED); - wrefresh(c[0].win); - - wclear(c[1].win); - mvwin(c[1].win, y + h - 6, x + w/2 - 2); - draw_borders(conf, c[1].win, 3, 4, LOWERED); - wrefresh(c[1].win); - - wclear(c[2].win); - mvwin(c[2].win, y + h - 6, x + w/2 + 3); - draw_borders(conf, c[2].win, 3, 4, LOWERED); - wrefresh(c[2].win); - - /* Important to avoid grey lines expanding screen */ - refresh(); - break; - default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; - loop = false; - } - } - } - - if (output == BSDDIALOG_OK) { - *hh = c[0].value; - *mm = c[1].value; - *ss = c[2].value; - } - - curs_set(0); - - for (i = 0; i < 3; i++) - delwin(c[i].win); - end_dialog(conf, shadow, widget, textpad); - - return (output); -} - -int -bsddialog_datebox(struct bsddialog_conf *conf, const char *text, int rows, - int cols, unsigned int *yy, unsigned int *mm, unsigned int *dd) -{ - bool loop, focusbuttons; - int i, input, output, y, x, h, w, sel; - WINDOW *widget, *textpad, *shadow; - struct buttons bs; - struct calendar { - int max; - int value; - WINDOW *win; - unsigned int x; - }; - struct month { - const char *name; - unsigned int days; - }; - - if (yy == NULL || mm == NULL || dd == NULL) - RETURN_ERROR("yy / mm / dd cannot be NULL"); - - struct calendar c[3] = { - {9999, *yy, NULL, 4 }, - {12, *mm, NULL, 9 }, - {31, *dd, NULL, 2 } - }; - - struct month m[12] = { - { "January", 31 }, { "February", 28 }, { "March", 31 }, - { "April", 30 }, { "May", 31 }, { "June", 30 }, - { "July", 31 }, { "August", 31 }, { "September", 30 }, - { "October", 31 }, { "November", 30 }, { "December", 31 } - }; - -#define ISLEAF(year) ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) - - for (i = 0 ; i < 3; i++) { - if (c[i].value > c[i].max) - c[i].value = c[i].max; - if (c[i].value < 1) - c[i].value = 1; - } - c[2].max = m[c[1].value -1].days; - if (c[1].value == 2 && ISLEAF(c[0].value)) - c[2].max = 29; - if (c[2].value > c[2].max) - c[2].value = c[2].max; - - get_buttons(conf, &bs, BUTTON_OK_LABEL, BUTTON_CANCEL_LABEL); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (datetime_autosize(conf, rows, cols, &h, &w, MINWDATE, text, - bs) != 0) - return (BSDDIALOG_ERROR); - if (datetime_checksize(h, w, MINWDATE, bs) != 0) - return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (new_dialog(conf, &shadow, &widget, y, x, h, w, &textpad, text, &bs, - true) != 0) - return (BSDDIALOG_ERROR); - - pnoutrefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - doupdate(); - - c[0].win = new_boxed_window(conf, y+h-6, x + w/2 - 11, 3, 6, LOWERED); - mvwaddch(widget, h - 5, w/2 - 5, '/'); - c[1].win = new_boxed_window(conf, y+h-6, x + w/2 - 4, 3, 11, LOWERED); - mvwaddch(widget, h - 5, w/2 + 7, '/'); - c[2].win = new_boxed_window(conf, y+h-6, x + w/2 + 8, 3, 4, LOWERED); - - wrefresh(widget); - - loop = focusbuttons = true; - while (loop) { - mvwprintw(c[0].win, 1, 1, "%4d", c[0].value); - mvwprintw(c[1].win, 1, 1, "%9s", m[c[1].value-1].name); - mvwprintw(c[2].win, 1, 1, "%2d", c[2].value); - for (i = 0; i < 3; i++) { - wrefresh(c[i].win); - } - if (focusbuttons == false) { - wmove(c[sel].win, 1, c[sel].x); - wrefresh(c[sel].win); - } - - input = getch(); - switch(input) { - case KEY_ENTER: - case 10: /* Enter */ + case '-': if (focusbuttons == false) - break; - output = bs.value[bs.curr]; - loop = false; - break; - case 27: /* Esc */ - if (conf->key.enable_esc) { - output = BSDDIALOG_ESC; - loop = false; - } - break; - case KEY_RIGHT: - case '\t': /* TAB */ - if (focusbuttons) { - bs.curr++; - focusbuttons = bs.curr < (int)bs.nbuttons ? - true : false; - if (focusbuttons == false) { - curs_set(1); - sel = 0; - } - } else { - sel++; - focusbuttons = sel > 2 ? true : false; - if (focusbuttons) { - curs_set(0); - bs.curr = 0; - } - } - draw_buttons(widget, bs, true); - wrefresh(widget); + c[sel].value = c[sel].value > 0 ? + c[sel].value - 1 : c[sel].max; break; - case KEY_LEFT: + case KEY_UP: if (focusbuttons) { - bs.curr--; - focusbuttons = bs.curr < 0 ? false : true; - if (focusbuttons == false) { - curs_set(1); - sel = 2; - } + sel = 0; + focusbuttons = false; + d.bs.curr = conf->button.always_active ? 0 : -1; + DRAW_BUTTONS(d); } else { - sel--; - focusbuttons = sel < 0 ? true : false; - if (focusbuttons) { - curs_set(0); - bs.curr = (int)bs.nbuttons - 1; - } + c[sel].value = c[sel].value > 0 ? + c[sel].value - 1 : c[sel].max; } - draw_buttons(widget, bs, true); - wrefresh(widget); - break; - case KEY_UP: - if (focusbuttons) - break; - c[sel].value = c[sel].value > 1 ? - c[sel].value - 1 : c[sel].max ; - /* if mount change */ - c[2].max = m[c[1].value -1].days; - /* if year change */ - if (c[1].value == 2 && ISLEAF(c[0].value)) - c[2].max = 29; - /* set new day */ - if (c[2].value > c[2].max) - c[2].value = c[2].max; break; + case '+': case KEY_DOWN: if (focusbuttons) break; c[sel].value = c[sel].value < c[sel].max ? - c[sel].value + 1 : 1; - /* if mount change */ - c[2].max = m[c[1].value -1].days; - /* if year change */ - if (c[1].value == 2 && ISLEAF(c[0].value)) - c[2].max = 29; - /* set new day */ - if (c[2].value > c[2].max) - c[2].value = c[2].max; + c[sel].value + 1 : 0; break; case KEY_F(1): if (conf->key.f1_file == NULL && conf->key.f1_message == NULL) break; - curs_set(0); - if (f1help(conf) != 0) - return (BSDDIALOG_ERROR); - curs_set(1); - /* No break, screen size can change */ - case KEY_RESIZE: - /* Important for decreasing screen */ - hide_widget(y, x, h, w, conf->shadow); - refresh(); - - if (set_widget_size(conf, rows, cols, &h, &w) != 0) - return (BSDDIALOG_ERROR); - if (datetime_autosize(conf, rows, cols, &h, &w, - MINWDATE, text, bs) != 0) + if (f1help_dialog(conf) != 0) return (BSDDIALOG_ERROR); - if (datetime_checksize(h, w, MINWDATE, bs) != 0) + if (timebox_draw(&d, true, c) != 0) return (BSDDIALOG_ERROR); - if (set_widget_position(conf, &y, &x, h, w) != 0) - return (BSDDIALOG_ERROR); - - if (update_dialog(conf, shadow, widget, y, x, h, w, - textpad, text, &bs, true) != 0) + break; + case KEY_CTRL('l'): + case KEY_RESIZE: + if (timebox_draw(&d, true, c) != 0) return (BSDDIALOG_ERROR); - doupdate(); - - mvwaddch(widget, h - 5, w/2 - 5, '/'); - mvwaddch(widget, h - 5, w/2 + 7, '/'); - wrefresh(widget); - - prefresh(textpad, 0, 0, y+1, x+2, y+h-7, x+w-2); - - wclear(c[0].win); - mvwin(c[0].win, y + h - 6, x + w/2 - 11); - draw_borders(conf, c[0].win, 3, 6, LOWERED); - wrefresh(c[0].win); - - wclear(c[1].win); - mvwin(c[1].win, y + h - 6, x + w/2 - 4); - draw_borders(conf, c[1].win, 3, 11, LOWERED); - wrefresh(c[1].win); - - wclear(c[2].win); - mvwin(c[2].win, y + h - 6, x + w/2 + 8); - draw_borders(conf, c[2].win, 3, 4, LOWERED); - wrefresh(c[2].win); - - /* Important to avoid grey lines expanding screen */ - refresh(); break; default: - if (shortcut_buttons(input, &bs)) { - output = bs.value[bs.curr]; + if (shortcut_buttons(input, &d.bs)) { + DRAW_BUTTONS(d); + doupdate(); + retval = BUTTONVALUE(d.bs); loop = false; } } } - if (output == BSDDIALOG_OK) { - *yy = c[0].value; - *mm = c[1].value; - *dd = c[2].value; - } - - curs_set(0); + *hh = c[0].value; + *mm = c[1].value; + *ss = c[2].value; for (i = 0; i < 3; i++) delwin(c[i].win); - end_dialog(conf, shadow, widget, textpad); + end_dialog(&d); - return (output); + return (retval); } diff --git a/contrib/bsddialog/utility/GNUmakefile b/contrib/bsddialog/utility/GNUmakefile new file mode 100644 index 000000000000..600efc7aacf6 --- /dev/null +++ b/contrib/bsddialog/utility/GNUmakefile @@ -0,0 +1,42 @@ +# PUBLIC DOMAIN - NO WARRANTY, see: +# <http://creativecommons.org/publicdomain/zero/1.0/> +# +# Written in 2021 by Alfonso Sabato Siciliano + +OUTPUT = bsddialog +SOURCES = bsddialog.c util_builders.c util_cli.c util_theme.c +OBJECTS = $(SOURCES:.c=.o) +PREFIX = /usr/local + +ifneq ($(ENABLEDEBUG),) +CFLAGS += -g +endif +CFLAGS += -D_GNU_SOURCE -Wall -Wextra -I$(LIBPATH) + +ifneq ($(DISABLERPATH),) +LDFLAGS += -ltinfo -L$(LIBPATH) -lbsddialog +else +LDFLAGS += -ltinfo -Wl,-rpath=$(LIBPATH) -L$(LIBPATH) -lbsddialog +endif + +RM = rm -f + +all: $(OUTPUT) + +install: all + ${INSTALL} -m 0755 ${OUTPUT} ${DESTDIR}${PREFIX}/bin/${OUTPUT} + +uninstall: + ${RM} ${DESTDIR}${PREFIX}/bin/${OUTPUT} + +$(OUTPUT): $(OBJECTS) + $(CC) $^ -o $@ $(LDFLAGS) + + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +clean: + $(RM) $(OUTPUT) *.o *~ + +.PHONY: all install uninstall clean diff --git a/contrib/bsddialog/utility/Makefile b/contrib/bsddialog/utility/Makefile new file mode 100644 index 000000000000..e6cd541fded4 --- /dev/null +++ b/contrib/bsddialog/utility/Makefile @@ -0,0 +1,42 @@ +# PUBLIC DOMAIN - NO WARRANTY, see: +# <http://creativecommons.org/publicdomain/zero/1.0/> +# +# Written in 2021 by Alfonso Sabato Siciliano + +OUTPUT = bsddialog +SOURCES = bsddialog.c util_builders.c util_cli.c util_theme.c +OBJECTS = ${SOURCES:.c=.o} +PREFIX = /usr/local + +.if defined(DEBUG) +CFLAGS += -g +.endif +CFLAGS += -I${LIBPATH} -std=gnu99 -Wall -Wextra -Werror + +.if defined(NORPATH) +LDFLAGS += -ltinfow -L${LIBPATH} -lbsddialog +.else +LDFLAGS += -ltinfow -Wl,-rpath=${LIBPATH} -L${LIBPATH} -lbsddialog +.endif + +INSTALL = install +RM = rm -f + +all: ${OUTPUT} + +install: all + ${INSTALL} -m 0755 ${OUTPUT} ${DESTDIR}${PREFIX}/bin/${OUTPUT} + +uninstall: + ${RM} ${DESTDIR}${PREFIX}/bin/${OUTPUT} + +${OUTPUT}: ${OBJECTS} + ${CC} ${LDFLAGS} ${OBJECTS} -o ${.PREFIX} + +.c.o: + ${CC} ${CFLAGS} -c ${.IMPSRC} -o ${.TARGET} + +clean: + ${RM} ${OUTPUT} *.o *~ *.core *.gz + +.PHONY: all install uninstall clean diff --git a/contrib/bsddialog/utility/bsddialog.1 b/contrib/bsddialog/utility/bsddialog.1 new file mode 100644 index 000000000000..0ec2a96952bd --- /dev/null +++ b/contrib/bsddialog/utility/bsddialog.1 @@ -0,0 +1,952 @@ +.\" +.\" Copyright (c) 2021-2025 Alfonso Sabato Siciliano +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.Dd June 22, 2025 +.Dt BSDDIALOG 1 +.Os +.Sh NAME +.Nm bsddialog +.Nd TUI dialogs +.Sh SYNOPSIS +.Nm +.Fl Fl help | Fl Fl version +.Nm +.Op Fl Fl option +.Fl Fl dialog +.Ar text +.Ar rows +.Ar cols +.Op Ar dialog-argument +.Op Fl Fl option +.Nm +\&... +.Fl Fl dialog1 +.Ar ... +.Oo Fl Fl and-dialog +.Fl Fl dialog2 +.Ar ... +.Oc ... +.Sh DESCRIPTION +The +.Nm bsddialog +utility builds Text User Interface dialogs and widgets. +.Pp +The option +.Fl Fl help +prints a brief list of features and exits. +The option +.Fl Fl version +prints the version and exits. +The option +.Fl Fl and-dialog +builds another dialog unless the previous one returns Error, ESC or Cancel. +.Pp +Each dialog accepts +.Ar text +to print a message inside, +.Ar rows +and +.Ar cols +to set height and width, +.Dv 0 +for autosize and +.Dv -1 +for fullscreen. +.Pp +The possible input from the user interface is printed to standard error at exit. +.Ss Options +The following options can change the default behavior of the utility and are +common to some dialog. +.Bl -tag -width Ds +.It Fl Fl alternate-screen +Set alternate screen mode if the terminal and +.Xr curses 3 +provide it. +If enabled bsddialog draws to the alternate screen and restores the main screen +after exit. +See +.Dq smcup +in +.Xr terminfo 5 . +.It Fl Fl ascii-lines +Ascii characters to draw lines. +.It Fl Fl backtitle Ar backtitle +Screen title. +.It Fl Fl begin-x Ar x +Set dialog horizontal position, +.Dv -1 +center, +.Dv 0 +left screen. +.It Fl Fl begin-y Ar y +Set dialog vertical position, +.Dv -1 +center, +.Dv 0 +top screen. +.It Fl Fl bikeshed +Random settings. +Colors, title delimiter, button delimiter; see theme features. +Title margin. +Buttons always active or TAB to switch focus with other components, see +.Fl Fl switch-buttons . +Zero padding with time output; see +.Fl Fl time-format . +Zero padding with date output; see +.Fl Fl date-format . +User Interface date format for +.Fl Fl datebox ; +see +.Fl Fl datebox-format . +.It Fl Fl cancel-exit-code Ar retval +Set an exit code value for the +.Dq Cancel +button. +.It Fl Fl cancel-label Ar label +Label for the +.Dq Cancel +button. +.It Fl Fl clear-dialog +Hide the dialog at exit. +.It Fl Fl clear-screen +Clear the screen, wait a dialog if built. +.It Fl Fl columns-per-row Ar columns +Try to set the number of columns for a row of text with autosizing; default +.Dv 10 . +.It Fl Fl cr-wrap +Keep new line in +.Ar text +also if it constains a +.Dq \en , +see +.Fl Fl text-unchanged . +.It Fl Fl datebox-format Ar format +String to customize +.Fl Fl datebox +interface, possible values: +.Dq d/m/y , +.Dq m/y/d , +.Dq y/m/d . +.It Fl Fl date-format Ar format +String accepted by +.Xr strftime 3 +to customize the output of +.Fl Fl datebox +and +.Fl Fl calendar . +.It Fl Fl default-button Ar label +Focus on the button with +.Ar label +on startup. +.It Fl Fl default-item Ar name +Focus on the item with +.Ar name , +for Checklist, Menu, Radiolist and Treeview. +.It Fl Fl default-no +Focus on +.Dq Cancel +or +.Dq \&No +button on startup. +.It Fl Fl disable-esc +Disable ESC key to quit. +.It Fl Fl error-exit-code Ar retval +Set an exit code value for the +.Nm +errors. +.It Fl Fl esc-exit-code Ar retval +Set an exit code value for the +.Dv ESC +key. +.It Fl Fl extra-button +Add a button with +.Dq Extra +label. +.It Fl Fl extra-exit-code Ar retval +Set an exit code value for the +.Dq Extra +button. +.It Fl Fl extra-label Ar label +Set +.Ar label +for the +.Dq Extra +button. +.It Fl Fl left1-button Ar label +Add a button with +.Ar label . +.It Fl Fl left1-exit-code Ar retval +Set an exit code for +.Fl Fl left1-button . +.It Fl Fl left2-button Ar label +Add a button with +.Ar label . +.It Fl Fl left2-exit-code Ar retval +Set an exit code for +.Fl Fl left2-button . +.It Fl Fl left3-button Ar label +Add a button with +.Ar label . +.It Fl Fl left3-exit-code Ar retval +Set an exit code for +.Fl Fl left3-button . +.It Fl Fl help-button +Add a button with +.Dq Help +label. +.It Fl Fl help-exit-code Ar retval +Set an exit code value for the +.Dq Help +button. +.It Fl Fl help-label Ar label +Set +.Ar label +for +.Dq Help +button. +.It Fl Fl help-print-items +Print also the selected items or form values if the +.Dq Help +button is pressed. +.It Fl Fl help-print-name +Print the name of the focused item if the +.Dq Help +button is pressed also +with +.Fl Fl item-bottom-desc . +.It Fl Fl hfile Ar filename +Open +.Ar filename +in a Textbox if F1 key is pressed. +.It Fl Fl hline Ar string +Dialog subtitle. +.It Fl Fl hmsg Ar string +Open a Msgbox with +.Ar string +if the F1 key is pressed. +.It Fl Fl ignore +Do not exit with unknown options. +.It Fl Fl insecure +Print +.Sq * +to hide passwords while typing, white space otherwise. +.It Fl Fl item-bottom-desc +Set a help string for each item of a Checklist, Form, Menu, Mixedform, +Passwordform, Radiolist and Treeview to display at the bottom screen side. +.It Fl Fl item-depth +Specify a margin for items, available for Checklist, Menu and Radiolist. +.It Fl Fl item-prefix +Set a string to prefix each item of a Checklist, Menu, Radiolist or Treeview. +.It Fl Fl load-theme Ar file +Load theme from +.Ar file . +.It Fl Fl max-input Ar size +Maximum length of the input for +.Fl Fl inputbox +and +.Fl Fl passwordbox , +default 2048. +.It Fl Fl no-cancel +Do not show +.Dq Cancel +button. +.It Fl Fl no-descriptions +Do not display items desciption, for Checklist, Menu, Radiolist or Treeview; +mutually exclusive with +.Fl Fl no-names . +.It Fl Fl no-lines +Do not draw borders and lines. +.It Fl Fl no-names +Do not display items name, for Checklist, Menu and Radiolist; mutually exclusive +with +.Fl Fl no-descriptions . +.It Fl Fl no-ok +Do not draw +.Dq OK +button. +.It Fl Fl no-shadow +No not draw the shadow of the dialog. +.It Fl Fl ok-label Ar label +Set +.Ar label +for +.Dq OK +button. +.It Fl Fl ok-exit-code Ar retval +Set an exit code value for the +.Dq Ok +button. +.It Fl Fl normal-screen +Set normal screen mode. +bsddialog does not restore the previous screen after exit. +See +.Dq rmcup +in +.Xr terminfo 5 . +.It Fl Fl output-fd Ar fd +Print input from user interface to the specified file descriptor. +.It Fl Fl output-separator Ar sep +Set a sepator for the items in output, default white space. +.It Fl Fl print-maxsize +Screen size. +This option can be used without a dialog. +.It Fl Fl print-size +Print dialog height and width at exit. +.It Fl Fl print-version +Print version. +This option can be used without a dialog. +.It Fl Fl quoted +Quote items in output, default only when necessary. +.It Fl Fl right1-button Ar label +Add a button with +.Ar label . +.It Fl Fl right1-exit-code Ar retval +Set an exit code for +.Fl Fl right1-button . +.It Fl Fl right2-button Ar label +Add a button with +.Ar label . +.It Fl Fl right2-exit-code Ar retval +Set an exit code for +.Fl Fl right2-button . +.It Fl Fl right3-button Ar label +Add a button with +.Ar label . +.It Fl Fl right3-exit-code Ar retval +Set an exit code for +.Fl Fl right3-button . +.It Fl Fl save-theme Ar file +Save the current theme. +This option can be used without a dialog. +.It Fl Fl separate-output +Print selected items separated by a new line and avoid to quote. +.It Fl Fl shadow +Show a shadow for the dialog, enabled by default. +.It Fl Fl single-quoted +Use single quote for items in output. +.It Fl Fl sleep Ar secs +Wait +.Ar secs +seconds to close the dialog. +.It Fl Fl stderr +Print input from user interface to standand error, default. +.It Fl Fl stdout +Print input from user interface to standard output. +.It Fl Fl switch-buttons +Enable focus switching between buttons and input components pressing TAB, +otherwise buttons are always active and ENTER key closes the dialog. +Suitable for: +.Fl Fl form , +.Fl Fl inputbox , +.Fl Fl mixedform , +.Fl Fl passwordbox , +.Fl Fl passwordform , +.Fl Fl timebox , +.Fl Fl calendar +and +.Fl Fl datebox . +.It Fl Fl tab-escape +Replace +.Dq \et +with a tab in +.Ar text . +.It Fl Fl tab-len Ar spaces +Number of spaces to print a TAB in +.Ar text . +.It Fl Fl text-escape +Enable escapes in +.Ar text : +.Bl -column -compact +.It Dq \eZ0 +black. +.It Dq \eZ1 +red. +.It Dq \eZ2 +green. +.It Dq \eZ3 +yellow. +.It Dq \eZ4 +blue. +.It Dq \eZ5 +magenta. +.It Dq \eZ6 +cyan. +.It Dq \eZ7 +white. +.It Dq \eZb +bold. +.It Dq \eZB +disable bold. +.It Dq \eZd +Half bright. +.It Dq \eZD +disable half bright. +.It Dq \eZk +Blink. +.It Dq \eZK +disable blinking. +.It Dq \eZr +reverse foreground and background. +.It Dq \eZR +disable reverse. +.It Dq \eZs +Highlight. +.It Dq \eZS +disable highlighting. +.It Dq \eZu +underline. +.It Dq \eZU +disable underline. +.It Dq \eZn +disable each customization. +.El +.It Fl Fl text-unchanged +Disable +.Ar text +modification. +By default +.Ar text +is changed before to be printed in the dialog. +If it contains at least a +.Dq \en +each new line and TAB is converted to a space, subsequent spaces are merged. +Otherwise new line characters are preserved and a TAB becomes a space. +.It Fl Fl theme Ar theme +Set theme, possible values: +.Dq 3d , +.Dq blackwhite , +.Dq flat . +.It Fl Fl time-format Ar format +String accepted by +.Xr strftime 3 +to customize the output of +.Fl Fl timebox . +.It Fl Fl timeout-exit-code Ar retval +Set an exit code value for the +.Fl Fl pause +timeout. +.It Fl Fl title Ar title +Dialog title. +.El +.Ss Keys +The following keys are available at runtime: +.Bl -tag -width Ds +.It Ctrl-l +Redraw the dialog. +.It F1 +See +.Fl Fl hfile +and +.Fl Fl hmsg . +.It SPACE +Select menu item. +.It UP DOWN LEFT RIGHT - + HOME END PAGEUP PAGEDOWN Ctrl-p Ctrl-n TAB +Navigate elements and set value, depending on the dialog. +.El +.Ss Dialogs +The following dialogs are available: +.Bl -tag -width Ds +.It Fl Fl calendar Ar text Ar rows Ar cols Op Ar day Ar month Ar year +Dialog to select a date. +.It Fl Fl checklist Ar text Ar rows Ar cols Ar menurows Oo Ar name Ar desc \ +Ar status Oc ... +Checklist to select some item from a list via the SPACE key. +An item has a +.Ar name , +.Ar desc +and a default +.Ar status +specified by +.Dq on +or +.Dq off . +The names of the selected items are printed to standard error. +.Ar menurows +is the graphical height of the list, 0 for autosize. +.It Fl Fl datebox Ar text Ar rows Ar cols Op Ar day Ar month Ar year +Dialog to select a date. +.It Fl Fl form Ar text Ar rows Ar cols Ar formrows Oo Ar label Ar ylabel \ +Ar xlabel Ar init Ar yfield Ar xfield Ar fieldlen Ar maxletters Oc ... +Dialog to get a list of strings via forms. +A form has a +.Ar label +at the position +.Ar ylabel +and +.Ar xlabel , +a field to get the input at the position +.Ar yfield +and +.Ar xfield . +.Ar fieldlen +is the field width, if negative is readonly and the width is the absolute value, +if +.Dv 0 +the field becomes readonly and its value is the +.Ar init +width. +The field input is not printed to output if it is readonly. +.Ar maxletters +is the maximum input length, if is +.Dv 0 +its value is +.Ar fieldlen . +.Ar init +is the default value in the field. +.Ar formrows +is the graphical height of the list, +.Dv 0 +for autosize. +.It Fl Fl gauge Ar text Ar rows Ar cols Op Ar percentage +Dialog with a bar to shows +.Ar percentage , +then it waits to read +.Dq XXX +from the standard input, then the first string replaces percentage and the +following strings replace +.Ar text +until the next +.Dq XXX , +the loop ends reading +.Dq EOF . +.It Fl Fl infobox Ar text Ar rows Ar cols +Dialog without buttons to display a message and to exit immediately. +.It Fl Fl inputbox Ar text Ar rows Ar cols Op Ar init +Dialog to get a string in input, +.Ar init +is the default value. +.It Fl Fl menu Ar text Ar rows Ar cols Ar menurows Oo Ar name desc Oc ... +Builds a menu to select an item from a list, SPACE key is equivalent to ENTER. +An item has a +.Ar name +and a +.Ar desc . +The name of the selected item is printed to standard error. +.Ar menurows +is the graphical height of the list, 0 for autosize. +.It Fl Fl mixedform Ar text Ar rows Ar cols Ar formrows Oo Ar label Ar ylabel \ +Ar xlabel Ar init Ar yfield Ar xfield Ar fieldlen Ar maxletters Ar flag Oc ... +Dialog to get a list of strings via forms. +A form has a +.Ar label +at the position +.Ar ylabel +and +.Ar xlabel , +a field to get the input at the position +.Ar yfield +and +.Ar xfield . +.Ar fieldlen +is the field width, if negative is readonly and the width is the absolute value, +if +.Dv 0 +the field becomes readonly and its value is the +.Ar init +width. +The field input is not printed to output if +.Ar fieldlen +is less than or equal to +.Dv 0 . +.Ar maxletters +is the maximum input length, if is +.Dv 0 +its value is +.Ar fieldlen . +.Ar init +is the default value in the field. +.Ar flag +can customize +.Ar field : +.Dv 0 +normal, +.Dv 1 +hide typed characters, +.Dv 2 +readonly. +.Ar formrows +is the graphical height of the list, +.Dv 0 +for autosize. +.It Fl Fl mixedgauge Ar text Ar rows Ar cols Ar mainperc Oo Ar minilabel \ +Ar miniperc Oc ... +Dialog to show a main bar to represent +.Ar mainperc +from 0 to 100. +Some mini bar with a +.Ar minilabel +string and a +.Ar miniperc +with a value from 0 and 100 or negative to print a descriptive string: -1 +.Dq Succeeded , +-2 +.Dq Failed , +-3 +.Dq Passed , +-4 +.Dq Completed , +-5 +.Dq Checked , +-6 +.Dq Done , +-7 +.Dq Skipped , +-8 +.Dq \&In Progress , +-9 +to hide +.Fa miniperc , +-10 +.Dq N/A , +-11 +.Dq Pending , +otherwise +.Dq UNKNOWN . +.It Fl Fl msgbox Ar text Ar rows Ar cols +Dialog to diplay a message without the +.Dq Cancel +button. +.It Fl Fl passwordbox Ar text Ar rows Ar cols Op Ar init +Dialog to get a password, +.Ar init +is the default value. +.It Fl Fl passwordform Ar text Ar rows Ar cols Ar formrows Oo Ar label \ +Ar ylabel Ar xlabel Ar init Ar yfield Ar xfield Ar fieldlen Ar maxletters \ +Oc ... +Dialog to get a list of passwords, equivalent to +.Fl Fl form +except typed characters are hidden. +.It Fl Fl pause Ar text Ar rows Ar cols Ar seconds +Dialog runs until +.Ar seconds +timeout expires or a button is pressed. +.It Fl Fl radiolist Ar text Ar rows Ar cols Ar menurows Oo Ar name Ar desc \ +Ar status Oc ... +Radiolist to select at most an item from a list via the SPACE key. +An item has a +.Ar name , +.Ar desc +and a default +.Ar status +specified by +.Dq on +or +.Dq off . +The name of the selected item is printed to standard error. +.Ar menurows +is the graphical height of the list, 0 for autosize. +.It Fl Fl rangebox Ar text Ar rows Ar cols Ar min Ar max Op Ar init +Dialog to select a value between +.Ar min +and +.Ar max , +.Ar init +is the default value. +.It Fl Fl textbox Ar file Ar rows Ar cols +Opens and prints +.Ar file . +TAB changes button. +Extra keys 0, h, l, k, j are available to navigate the text. +.Dq OK +button is renamed +.Dq EXIT . +.It Fl Fl timebox Ar text Ar rows Ar cols Op Ar hour Ar min Ar sec +Dialog to select a time. +.It Fl Fl treeview Ar text Ar rows Ar cols Ar menurows Oo Ar depth Ar name \ +Ar desc Ar status Oc ... +Equivalent to Radiolist with +.Fl Fl item-depth +and +.Fl Fl no-name . +.It Fl Fl yesno Ar text Ar rows Ar cols +.Dq Yes-No Question , +.Dq OK +and +.Dq Cancel +buttons are renamed +.Dq Yes +and +.Dq \&No . +.El +.Sh ENVIRONMENT +The following environment variables take effect only on startup, other options +can override their setting. +.Bl -tag -width indent +.It Ev NO_COLOR +If present and not an empty string (regardless of its value) equivalent to +.Fl Fl theme Ar blackwhite . +.It Ev BSDDIALOG_ERROR , Ev BSDDIALOG_OK , Ev BSDDIALOG_CANCEL , \ +Ev BSDDIALOG_HELP , Ev BSDDIALOG_EXTRA , Ev BSDDIALOG_TIMEOUT , \ +Ev BSDDIALOG_ESC , Ev BSDDIALOG_LEFT1 , Ev BSDDIALOG_LEFT2 , \ +Ev BSDDIALOG_LEFT3 , Ev BSDDIALOG_RIGHT1 , Ev BSDDIALOG_RIGHT2 , \ +Ev BSDDIALOG_RIGHT3 +Set exit codes. +.It Ev BSDDIALOG_THEMEFILE Ar file +Equivalent to +.Fl Fl load-theme Ar file . +.El +.Sh FILES +The theme file +.Pa $HOME/.bsddialog.conf +is read on startup if exists. +.Sh EXIT STATUS +The +.Nm +utility exits 255 on unsuccessful, otherwise depending on the button or key +pressed the following values can be returned: +.Bl -column -compact +.It 0 +.Dq OK , +.Dq Yes +or +.Dq Exit +button. +.It 1 +.Dq Cancel +or +.Dq \&No +button. +.It 2 +.Dq Help +button. +.It 3 +.Dq Extra +button. +.It 4 +Timeout. +.It 5 +ESC key. +.It 6 +Left1 generic button. +.It 7 +Left2 generic button. +.It 8 +Left3 generic button. +.It 9 +Right1 generic button. +.It 10 +Right2 generic button. +.It 11 +Right3 generic button. +.El +.Sh EXAMPLES +Backtitle, title and message: +.Dl bsddialog --backtitle MESSAGE --title Msgbox --msgbox Message 0 0 +.Pp +Yes-No Question and theme: +.Dl bsddialog --theme blackwhite --yesno Question 10 30 +.Pp +Save a custom theme: +.Dl bsddialog --save-theme mytheme.txt --infobox \*qSaving theme...\*q 0 0 +.Pp +Load a custom theme: +.Dl bsddialog --load-theme mytheme.txt --infobox \*qCustom theme\*q 0 0 +.Pp +Checklist: +.Dl bsddialog --checklist Checklist 0 0 3 N1 \&D1 off N2 D2 on N3 D3 off +.Pp +Form: +.Dl bsddialog --form Form 0 0 0 L1: 0 0 X 0 4 20 25 L2: 1 0 Y 1 4 20 25 +.Pp +Multi-dialog: +.Dl bsddialog --normal-screen --begin-y 1 --yesno Continue? 0 0 \e \ +--and-dialog --begin-y 10 --infobox Yes 0 0 +.Pp +Bikeshed: +.Dl bsddialog --bikeshed --inputbox Example 0 0 +.Dl bsddialog --bikeshed --datebox Example 0 0 +.Pp +Mixedgauge: +.Dl bsddialog --sleep 3 --mixedgauge Example 10 30 60 L1 \*q -1\*q L2 30 +.Pp +Mixedgauge script: +.Bd -literal -offset indent -compact +perc=0 +while [ $perc -le 100 ] +do + bsddialog --sleep 1 --title Mixedgauge \e + --mixedgauge "\enExample...\en" 0 0 $perc \e + "Hidden" " -9" \e + "Label 1" " -4" \e + "Label 2" " -4" \e + "Label 3" $perc + + perc=`expr $perc + 20` +done +.Ed +.Pp +Gauge script: +.Bd -literal -offset indent -compact +i=1 +for c in A B C D E F G H +do + sleep 1 + echo XXX + echo "$(expr $(expr $i "*" 100) "/" 8)" + echo "[$i/8] Char: $c" + echo XXX + if [ $i -eq 8 ] + then + sleep 1 + echo EOF + fi + i=`expr $i + 1` +done | bsddialog --title Gauge --gauge "Starting..." 10 70 +.Ed +.Sh COMPATIBILITY +Outdated options are retained for compatibility, properly equivalent options are +used: +.Bd -literal -offset indent -compact +Obsolete Equivalent +--and-widget --and-dialog +--calendar <text> 2 <cols> --calendar <text> 0 <cols> +--clear --clear-screen +--colors --text-escape +--defaultno --default-no +--exit-label --ok-label +--help-status --help-print-items +--help-tags --help-print-name +--item-help --item-bottom-desc +--keep-tite --alternate-screen +--no-items --no-descriptions +--no-label --cancel-label +--no-tags --no-names +--nocancel --no-cancel +--nook --no-ok +--separator --output-separator +--yes-label --ok-label +.Ed +.Sh SEE ALSO +.Xr bsddialog 3 , +.Xr strftime 3 , +.Xr terminfo 5 +.Sh HISTORY +The +.Nm bsddialog +utility first appeared in +.Fx 14.0 . +.Sh AUTHORS +.Nm bsddialog +was written by +.An Alfonso Sabato Siciliano +.Aq Mt asiciliano@FreeBSD.org . +.Pp +.Nm bsddialog +provides also a subset of the functionality described in the +.Nm dialog +manual. +The following features were reimplemented: +.Pp +Options: +.Fl Fl and-widget , +.Fl Fl ascii-lines , +.Fl Fl backtitle , +.Fl Fl cancel-label , +.Fl Fl clear , +.Fl Fl colors , +.Fl Fl cr-wrap , +.Fl Fl date-format , +.Fl Fl defaultno , +.Fl Fl default-button , +.Fl Fl default-no , +.Fl Fl default-item , +.Fl Fl exit-label , +.Fl Fl extra-button , +.Fl Fl extra-label , +.Fl Fl help , +.Fl Fl help-button , +.Fl Fl help-label , +.Fl Fl help-status , +.Fl Fl help-tags , +.Fl Fl hfile , +.Fl Fl hline , +.Fl Fl ignore , +.Fl Fl insecure , +.Fl Fl item-help , +.Fl Fl keep-tite , +.Fl Fl max-input , +.Fl Fl no-cancel , +.Fl Fl nocancel , +.Fl Fl no-items , +.Fl Fl no-label , +.Fl Fl no-lines , +.Fl Fl no-ok , +.Fl Fl nook , +.Fl Fl no-shadow , +.Fl Fl no-tags , +.Fl Fl ok-label , +.Fl Fl output-fd , +.Fl Fl output-separator , +.Fl Fl print-maxsize , +.Fl Fl print-size , +.Fl Fl print-version , +.Fl Fl quoted , +.Fl Fl separate-output , +.Fl Fl separator , +.Fl Fl shadow , +.Fl Fl single-quoted , +.Fl Fl sleep , +.Fl Fl stderr , +.Fl Fl stdout , +.Fl Fl tab-len , +.Fl Fl time-format , +.Fl Fl title , +.Fl Fl version , +.Fl Fl yes-label . +.Pp +Keys: Ctrl-l, F1. +.Pp +Dialogs: +.Fl Fl calendar , +.Fl Fl checklist , +.Fl Fl form , +.Fl Fl gauge , +.Fl Fl infobox , +.Fl Fl inputbox , +.Fl Fl menu , +.Fl Fl mixedform , +.Fl Fl mixedgauge , +.Fl Fl msgbox , +.Fl Fl passwordbox , +.Fl Fl passwordform , +.Fl Fl pause , +.Fl Fl radiolist , +.Fl Fl rangebox , +.Fl Fl textbox , +.Fl Fl timebox , +.Fl Fl treeview , +.Fl Fl yesno . +.Pp +Some feature differs in input, output, or behavior. +Compatibility is not a priority for future development. +.Sh THANKS TO +.An Baptiste Daroussin +.Aq Mt bapt@FreeBSD.org , +.An \&Ed Maste +.Aq Mt emaste@FreeBSD.org , +.An Juraj Lutter +.Aq Mt otis@FreeBSD.org +and +.An Trenton Schulz +for suggestions, help, and testing. diff --git a/contrib/bsddialog/utility/bsddialog.c b/contrib/bsddialog/utility/bsddialog.c new file mode 100644 index 000000000000..bce1d0ab8452 --- /dev/null +++ b/contrib/bsddialog/utility/bsddialog.c @@ -0,0 +1,338 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <getopt.h> +#include <limits.h> +#include <locale.h> +#include <signal.h> +#include <stdarg.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <term.h> + +#include <bsddialog.h> +#include <bsddialog_theme.h> + +#include "util.h" + +#define EXITCODE(retval) (exitcodes[retval + 1].value) +#define UNUSED_PAR(x) UNUSED_ ## x __attribute__((__unused__)) + +static void custom_text(struct options *opt, char *text, char *buf); + +/* Exit codes */ +struct exitcode { + const char *name; + int value; +}; + +static struct exitcode exitcodes[14] = { + { "BSDDIALOG_ERROR", 255 }, + { "BSDDIALOG_OK", 0 }, + { "BSDDIALOG_CANCEL", 1 }, + { "BSDDIALOG_HELP", 2 }, + { "BSDDIALOG_EXTRA", 3 }, + { "BSDDIALOG_TIMEOUT", 4 }, + { "BSDDIALOG_ESC", 5 }, + { "BSDDIALOG_LEFT1", 6 }, + { "BSDDIALOG_LEFT2", 7 }, + { "BSDDIALOG_LEFT3", 8 }, + { "BSDDIALOG_RIGHT1", 9 }, + { "BSDDIALOG_RIGHT2", 10 }, + { "BSDDIALOG_RIGHT3", 11 }, + { "BSDDIALOG_ITEM_HELP", 2 } /* like HELP by default */ +}; + +void set_exit_code(int lib_retval, int exitcode) +{ + exitcodes[lib_retval + 1].value = exitcode; +} + +/* Error */ +void exit_error(bool usage, const char *fmt, ...) +{ + va_list arg_ptr; + + if (bsddialog_inmode()) + bsddialog_end(); + printf("Error: "); + va_start(arg_ptr, fmt); + vprintf(fmt, arg_ptr); + va_end(arg_ptr); + printf(".\n\n"); + if (usage) { + printf("See \'bsddialog --help\' or \'man 1 bsddialog\' "); + printf("for more information.\n"); + } + + exit (EXITCODE(BSDDIALOG_ERROR)); +} + +void error_args(const char *dialog, int argc, char **argv) +{ + int i; + + if (bsddialog_inmode()) + bsddialog_end(); + printf("Error: %s unexpected argument%s:", dialog, argc > 1 ? "s" : ""); + for (i = 0; i < argc; i++) + printf(" \"%s\"", argv[i]); + printf(".\n\n"); + printf("See \'bsddialog --help\' or \'man 1 bsddialog\' "); + printf("for more information.\n"); + + exit (EXITCODE(BSDDIALOG_ERROR)); +} + +/* init */ +static void sigint_handler(int UNUSED_PAR(sig)) +{ + bsddialog_end(); + + exit(EXITCODE(BSDDIALOG_ERROR)); +} + +static void start_bsddialog_mode(void) +{ + if (bsddialog_inmode()) + return; + if (bsddialog_init() != BSDDIALOG_OK) + exit_error(false, bsddialog_geterror()); + + signal(SIGINT, sigint_handler); +} + +static void getenv_exitcodes(void) +{ + int i; + int value; + char *envvalue; + + for (i = 0; i < 10; i++) { + envvalue = getenv(exitcodes[i].name); + if (envvalue == NULL || envvalue[0] == '\0') + continue; + value = (int)strtol(envvalue, NULL, 10); + exitcodes[i].value = value; + /* ITEM_HELP follows HELP without explicit setting */ + if (i == BSDDIALOG_HELP + 1) + exitcodes[BSDDIALOG_ITEM_HELP + 1].value = value; + } +} + +/* + * bsddialog utility: TUI widgets and dialogs. + */ +int main(int argc, char *argv[argc]) +{ + bool startup; + int i, rows, cols, retval, parsed, nargc, firstoptind; + char *text, **nargv, *pn; + struct bsddialog_conf conf; + struct options opt; + + setlocale(LC_ALL, ""); + getenv_exitcodes(); + firstoptind = optind; + pn = argv[0]; + retval = BSDDIALOG_OK; + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "--version") == 0) { + printf("Version: %s\n", LIBBSDDIALOG_VERSION); + return (BSDDIALOG_OK); + } + if (strcmp(argv[i], "--help") == 0) { + usage(); + return (BSDDIALOG_OK); + } + } + + startup = true; + while (true) { + parsed = parseargs(argc, argv, &conf, &opt); + nargc = argc - parsed; + nargv = argv + parsed; + argc = parsed - optind; + argv += optind; + + if (opt.mandatory_dialog && opt.dialogbuilder == NULL) + exit_error(true, "expected a --<dialog>"); + + if (opt.dialogbuilder == NULL && argc > 0) + error_args("(no --<dialog>)", argc, argv); + + /* --print-maxsize or --print-version */ + if (opt.mandatory_dialog == false && opt.clearscreen == false && + opt.savethemefile == NULL && opt.dialogbuilder == NULL) { + retval = BSDDIALOG_OK; + break; + } + + /* --<dialog>, --save-theme or clear-screen */ + text = NULL; /* useless inits, fix compiler warnings */ + rows = BSDDIALOG_AUTOSIZE; + cols = BSDDIALOG_AUTOSIZE; + if (opt.dialogbuilder != NULL) { + if (argc < 3) + exit_error(true, + "expected <text> <rows> <cols>"); + if ((text = strdup(argv[0])) == NULL) + exit_error(false, "cannot allocate <text>"); + if (opt.dialogbuilder != textbox_builder) + custom_text(&opt, argv[0], text); + rows = (int)strtol(argv[1], NULL, 10); + cols = (int)strtol(argv[2], NULL, 10); + argc -= 3; + argv += 3; + } + + /* bsddialog terminal mode (first iteration) */ + start_bsddialog_mode(); + + if (opt.screen_mode != NULL) { + opt.screen_mode = tigetstr(opt.screen_mode); + if (opt.screen_mode != NULL && + opt.screen_mode != (char*)-1) { + tputs(opt.screen_mode, 1, putchar); + fflush(stdout); + bsddialog_refresh(); + } + } + + /* theme */ + if (startup) + startuptheme(); + startup = false; + if ((int)opt.theme >= 0) + setdeftheme(opt.theme); + if (opt.loadthemefile != NULL) + loadtheme(opt.loadthemefile, false); + if (opt.bikeshed) + bikeshed(&conf); + if (opt.savethemefile != NULL) + savetheme(opt.savethemefile); + + /* backtitle and dialog */ + if (opt.dialogbuilder == NULL) + break; + if (opt.backtitle != NULL) + if (bsddialog_backtitle(&conf, opt.backtitle)) + exit_error(false, bsddialog_geterror()); + retval = opt.dialogbuilder(&conf, text, rows, cols, argc, argv, + &opt); + free(text); + if (retval == BSDDIALOG_ERROR) + exit_error(false, bsddialog_geterror()); + if (conf.get_height != NULL && conf.get_width != NULL) + dprintf(opt.output_fd, "DialogSize: %d, %d\n", + *conf.get_height, *conf.get_width); + if (opt.clearscreen) + bsddialog_clear(0); + opt.clearscreen = false; + /* --and-dialog ends loop with Cancel or ESC */ + if (retval == BSDDIALOG_CANCEL || retval == BSDDIALOG_ESC) + break; + argc = nargc; + argv = nargv; + if (argc <= 0) + break; + /* prepare next parseargs() call */ + argc++; + argv--; + argv[0] = pn; + optind = firstoptind; + } + + if (bsddialog_inmode()) { + /* --clear-screen can be a single option */ + if (opt.clearscreen) + bsddialog_clear(0); + bsddialog_end(); + } + /* end bsddialog terminal mode */ + + return (EXITCODE(retval)); +} + +void custom_text(struct options *opt, char *text, char *buf) +{ + bool trim, crwrap; + int i, j; + + if (strstr(text, "\\n") == NULL) { + /* "hasnl" mode */ + trim = true; + crwrap = true; + } else { + trim = false; + crwrap = opt->cr_wrap; + } + if (opt->text_unchanged) { + trim = false; + crwrap = true; + } + + i = j = 0; + while (text[i] != '\0') { + switch (text[i]) { + case '\\': + buf[j] = '\\'; + switch (text[i+1]) { + case 'n': /* implicitly in "hasnl" mode */ + buf[j] = '\n'; + i++; + if (text[i+1] == '\n') + i++; + break; + case 't': + if (opt->tab_escape) { + buf[j] = '\t'; + } else { + j++; + buf[j] = 't'; + } + i++; + break; + } + break; + case '\n': + buf[j] = crwrap ? '\n' : ' '; + break; + case '\t': + buf[j] = opt->text_unchanged ? '\t' : ' '; + break; + default: + buf[j] = text[i]; + } + i++; + if (!trim || buf[j] != ' ' || j == 0 || buf[j-1] != ' ') + j++; + } + buf[j] = '\0'; +} diff --git a/contrib/bsddialog/utility/util.h b/contrib/bsddialog/utility/util.h new file mode 100644 index 000000000000..d1f7793c9755 --- /dev/null +++ b/contrib/bsddialog/utility/util.h @@ -0,0 +1,125 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef _BSDDIALOG_UTIL_H_ +#define _BSDDIALOG_UTIL_H_ + +/* + * Exit codes and errors, bsddialog.c + */ +#define BSDDIALOG_ITEM_HELP 12 + +void set_exit_code(int lib_retval, int exitcode); +void exit_error(bool usage, const char *fmt, ...); +void error_args(const char *dialog, int argc, char **argv); + +/* + * Command Line, util_cli.c + */ +struct options { + /* Menus options */ + bool item_always_quote; + char *item_default; + bool item_depth; + char *item_output_sep; + bool item_output_sepnl; + bool item_prefix; + bool item_singlequote; + /* Menus and Forms options */ + bool help_print_item_name; + bool help_print_items; + bool item_bottomdesc; + /* Forms options */ + int unsigned max_input_form; + /* Date and Time options */ + char *date_fmt; + char *time_fmt; + /* General options */ + int getH; + int getW; + bool ignore; + int output_fd; + /* Text option */ + bool cr_wrap; + bool tab_escape; + bool text_unchanged; + /* Theme and Screen options*/ + char *backtitle; + bool bikeshed; + enum bsddialog_default_theme theme; + bool clearscreen; + char *loadthemefile; + char *savethemefile; + const char *screen_mode; + /* Dialog */ + bool mandatory_dialog; + const char *name; + int (*dialogbuilder)(struct bsddialog_conf *conf, char* text, int rows, + int cols, int argc, char **argv, struct options *opt); +}; + +void usage(void); +int +parseargs(int argc, char **argv, struct bsddialog_conf *conf, + struct options *opt); + +/* + * Dialogs builders, util_builders.c + */ +#define BUILDER_ARGS struct bsddialog_conf *conf, char* text, int rows, \ + int cols, int argc, char **argv, struct options *opt +int calendar_builder(BUILDER_ARGS); +int checklist_builder(BUILDER_ARGS); +int datebox_builder(BUILDER_ARGS); +int form_builder(BUILDER_ARGS); +int gauge_builder(BUILDER_ARGS); +int infobox_builder(BUILDER_ARGS); +int inputbox_builder(BUILDER_ARGS); +int menu_builder(BUILDER_ARGS); +int mixedform_builder(BUILDER_ARGS); +int mixedgauge_builder(BUILDER_ARGS); +int msgbox_builder(BUILDER_ARGS); +int passwordbox_builder(BUILDER_ARGS); +int passwordform_builder(BUILDER_ARGS); +int pause_builder(BUILDER_ARGS); +int radiolist_builder(BUILDER_ARGS); +int rangebox_builder(BUILDER_ARGS); +int textbox_builder(BUILDER_ARGS); +int timebox_builder(BUILDER_ARGS); +int treeview_builder(BUILDER_ARGS); +int yesno_builder(BUILDER_ARGS); + +/* + * Theme, util_theme.c + */ +void savetheme(const char *file); +void loadtheme(const char *file, bool compatibility); +void setdeftheme(enum bsddialog_default_theme theme); +void bikeshed(struct bsddialog_conf *conf); +void startuptheme(void); + +#endif diff --git a/contrib/bsddialog/utility/util_builders.c b/contrib/bsddialog/utility/util_builders.c new file mode 100644 index 000000000000..0a968d4319f9 --- /dev/null +++ b/contrib/bsddialog/utility/util_builders.c @@ -0,0 +1,811 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <wchar.h> + +#include <bsddialog.h> +#include <bsddialog_theme.h> + +#include "util.h" + +#define NO_PRINT_VALUES(rv) \ +(rv == BSDDIALOG_ERROR || rv == BSDDIALOG_CANCEL || rv == BSDDIALOG_ESC) + +/* message */ +int infobox_builder(BUILDER_ARGS) +{ + if (argc > 0) + error_args(opt->name, argc, argv); + + return (bsddialog_infobox(conf, text, rows, cols)); +} + +int msgbox_builder(BUILDER_ARGS) +{ + if (argc > 0) + error_args(opt->name, argc, argv); + + return (bsddialog_msgbox(conf, text, rows, cols)); +} + +int yesno_builder(BUILDER_ARGS) +{ + if (argc > 0) + error_args(opt->name, argc, argv); + + return (bsddialog_yesno(conf, text, rows, cols)); +} + +/* textbox */ +int textbox_builder(BUILDER_ARGS) +{ + if (argc > 0) + error_args(opt->name, argc, argv); + + return (bsddialog_textbox(conf, text, rows, cols)); +} + +/* bar */ +int gauge_builder(BUILDER_ARGS) +{ + int output; + unsigned int perc; + + perc = 0; + if (argc == 1) { + perc = (unsigned int)strtoul(argv[0], NULL, 10); + perc = perc > 100 ? 100 : perc; + } else if (argc > 1) { + error_args(opt->name, argc - 1, argv + 1); + } + + output = bsddialog_gauge(conf, text, rows, cols, perc, STDIN_FILENO, + "XXX", "EOF"); + + return (output); +} + +int mixedgauge_builder(BUILDER_ARGS) +{ + int output, *minipercs; + unsigned int i, mainperc, nminibars; + const char **minilabels; + + if (argc < 1) + exit_error(true, "%s missing <mainperc>", opt->name); + if (((argc-1) % 2) != 0) + exit_error(true, + "bad %s pair number [<minilabel> <miniperc>]", opt->name); + + mainperc = (unsigned int)strtoul(argv[0], NULL, 10); + mainperc = mainperc > 100 ? 100 : mainperc; + argc--; + argv++; + + nminibars = argc / 2; + if ((minilabels = calloc(nminibars, sizeof(char*))) == NULL) + exit_error(false, "Cannot allocate memory for minilabels"); + if ((minipercs = calloc(nminibars, sizeof(int))) == NULL) + exit_error(false, "Cannot allocate memory for minipercs"); + + for (i = 0; i < nminibars; i++) { + minilabels[i] = argv[i * 2]; + minipercs[i] = (int)strtol(argv[i * 2 + 1], NULL, 10); + } + + output = bsddialog_mixedgauge(conf, text, rows, cols, mainperc, + nminibars, minilabels, minipercs); + + return (output); +} + +int pause_builder(BUILDER_ARGS) +{ + int output; + unsigned int secs; + + if (argc == 0) + exit_error(true, "--pause missing <seconds>"); + if (argc > 1) + error_args(opt->name, argc - 1, argv + 1); + + secs = (unsigned int)strtoul(argv[0], NULL, 10); + output = bsddialog_pause(conf, text, rows, cols, &secs); + + return (output); +} + +int rangebox_builder(BUILDER_ARGS) +{ + int output, min, max, value; + + if (argc < 2) + exit_error(true, "--rangebox missing <min> <max> [<init>]"); + if (argc > 3) + error_args("--rangebox", argc - 3, argv + 3); + + min = (int)strtol(argv[0], NULL, 10); + max = (int)strtol(argv[1], NULL, 10); + + if (argc == 3) { + value = (int)strtol(argv[2], NULL, 10); + value = value < min ? min : value; + value = value > max ? max : value; + } else + value = min; + + output = bsddialog_rangebox(conf, text, rows, cols, min, max, &value); + if (NO_PRINT_VALUES(output) == false) + dprintf(opt->output_fd, "%d", value); + + return (output); +} + +/* date and time */ +static int date(BUILDER_ARGS) +{ + int rv; + unsigned int yy, mm, dd; + time_t cal; + struct tm *localtm; + char stringdate[1024]; + + time(&cal); + localtm = localtime(&cal); + yy = localtm->tm_year + 1900; + mm = localtm->tm_mon + 1; + dd = localtm->tm_mday; + + if (argc > 3) { + error_args(opt->name, argc - 3, argv + 3); + } else if (argc == 3) { + /* lib checks/sets max and min */ + dd = (unsigned int)strtoul(argv[0], NULL, 10); + mm = (unsigned int)strtoul(argv[1], NULL, 10); + yy = (unsigned int)strtoul(argv[2], NULL, 10); + } + + if (strcmp(opt->name, "--datebox") == 0) + rv = bsddialog_datebox(conf, text, rows, cols, &yy, &mm, &dd); + else + rv = bsddialog_calendar(conf, text, rows, cols, &yy, &mm, &dd); + if (NO_PRINT_VALUES(rv)) + return (rv); + + if (opt->date_fmt != NULL) { + time(&cal); + localtm = localtime(&cal); + localtm->tm_year = yy - 1900; + localtm->tm_mon = mm - 1; + localtm->tm_mday = dd; + strftime(stringdate, 1024, opt->date_fmt, localtm); + dprintf(opt->output_fd, "%s", stringdate); + } else if (opt->bikeshed && ~dd & 1) { + dprintf(opt->output_fd, "%u/%u/%u", dd, mm, yy); + } else { + dprintf(opt->output_fd, "%02u/%02u/%u", dd, mm, yy); + } + + return (rv); +} + +int calendar_builder(BUILDER_ARGS) +{ + /* Use height autosizing with rows = 2. Documented in bsddialog(1). + * + * f_dialog_calendar_size() in bsdconfig/share/dialog.subr:1352 + * computes height 2 for `dialog --calendar', called by: + * 1) f_dialog_input_expire_password() in + * bsdconfig/usermgmt/share/user_input.subr:517 and + * 2) f_dialog_input_expire_account() in + * bsdconfig/usermgmt/share/user_input.subr:660. + * + * Then use height autosizing with 2 that is min height like dialog. + */ + if (rows == 2) + rows = 0; + + return (date(conf, text, rows, cols, argc, argv, opt)); +} + +int datebox_builder(BUILDER_ARGS) +{ + return (date(conf, text, rows, cols, argc, argv, opt)); +} + +int timebox_builder(BUILDER_ARGS) +{ + int output; + unsigned int hh, mm, ss; + time_t clock; + struct tm *localtm; + char stringtime[1024]; + + time(&clock); + localtm = localtime(&clock); + hh = localtm->tm_hour; + mm = localtm->tm_min; + ss = localtm->tm_sec; + + if (argc > 3) { + error_args("--timebox", argc - 3, argv + 3); + } else if (argc == 3) { + hh = (unsigned int)strtoul(argv[0], NULL, 10); + mm = (unsigned int)strtoul(argv[1], NULL, 10); + ss = (unsigned int)strtoul(argv[2], NULL, 10); + } + + output = bsddialog_timebox(conf, text, rows, cols, &hh, &mm, &ss); + if (NO_PRINT_VALUES(output)) + return (output); + + if (opt->time_fmt != NULL) { + time(&clock); + localtm = localtime(&clock); + localtm->tm_hour = hh; + localtm->tm_min = mm; + localtm->tm_sec = ss; + strftime(stringtime, 1024, opt->time_fmt, localtm); + dprintf(opt->output_fd, "%s", stringtime); + } else if (opt->bikeshed && ~ss & 1) { + dprintf(opt->output_fd, "%u:%u:%u", hh, mm, ss); + } else { + dprintf(opt->output_fd, "%02u:%02u:%02u", hh, mm, ss); + } + + return (output); +} + +/* menu */ +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, + struct options *opt) +{ + unsigned int i, j, sizeitem; + + *focusitem = -1; + + sizeitem = 0; + sizeitem += setprefix ? 1 : 0; + sizeitem += setdepth ? 1 : 0; + sizeitem += setname ? 1 : 0; + sizeitem += setdesc ? 1 : 0; + sizeitem += setstatus ? 1 : 0; + sizeitem += sethelp ? 1 : 0; + if ((argc % sizeitem) != 0) + exit_error(true, "%s bad arguments items number", opt->name); + + *nitems = argc / sizeitem; + *items = calloc(*nitems, sizeof(struct bsddialog_menuitem)); + if (items == NULL) + exit_error(false, "%s cannot allocate items", opt->name); + + j = 0; + for (i = 0; i < *nitems; i++) { + (*items)[i].prefix = setprefix ? argv[j++] : ""; + (*items)[i].depth = setdepth ? + (unsigned int)strtoul(argv[j++], NULL, 0) : 0; + (*items)[i].name = setname ? argv[j++] : ""; + (*items)[i].desc = setdesc ? argv[j++] : ""; + if (setstatus) { + if (strcasecmp(argv[j], "on") == 0) + (*items)[i].on = true; + else if (strcasecmp(argv[j], "off") == 0) + (*items)[i].on = false; + else + exit_error(true, + "\"%s\" (item %i) invalid status \"%s\"", + (*items)[i].name, i+1, argv[j]); + j++; + } else + (*items)[i].on = false; + (*items)[i].bottomdesc = sethelp ? argv[j++] : ""; + + if (opt->item_default != NULL && *focusitem == -1) + if (strcmp((*items)[i].name, opt->item_default) == 0) + *focusitem = i; + } +} + +static void +print_menu_items(int output, int nitems, struct bsddialog_menuitem *items, + int focusitem, struct options *opt) +{ + bool sep, sepbefore, sepafter, sepsecond, toquote, ismenu, ischecklist; + int i; + char quotech; + const char *focusname, *sepstr; + + ismenu = (strcmp(opt->name, "--menu") == 0) ? true : false; + ischecklist = (strcmp(opt->name, "--checklist") == 0) ? true : false; + sep = false; + quotech = opt->item_singlequote ? '\'' : '"'; + + if (NO_PRINT_VALUES(output)) + return; + + if (output == BSDDIALOG_HELP) { + dprintf(opt->output_fd, "HELP "); + + if (focusitem >= 0) { + focusname = items[focusitem].name; + if (opt->item_bottomdesc && + opt->help_print_item_name == false) + focusname = items[focusitem].bottomdesc; + + toquote = false; + if (strchr(focusname, ' ') != NULL) { + toquote = opt->item_always_quote; + if (ismenu == false && + opt->item_output_sepnl == false) + toquote = true; + } + if (toquote) { + dprintf(opt->output_fd, "%c%s%c", + quotech, focusname, quotech); + } else + dprintf(opt->output_fd, "%s", focusname); + } + + if (ismenu || opt->help_print_items == false) + return; + sep = true; + } + + sepbefore = false; + sepsecond = false; + if ((sepstr = opt->item_output_sep) == NULL) { + if (opt->item_output_sepnl) + sepstr = "\n"; + else { + sepstr = " "; + sepsecond = true; + } + } else + sepbefore = true; + + sepafter = false; + if (opt->item_output_sepnl) { + sepbefore = false; + sepafter = true; + } + + for (i = 0; i < nitems; i++) { + if (items[i].on == false) + continue; + + if (sep || sepbefore) + dprintf(opt->output_fd, "%s", sepstr); + sep = false; + if (sepsecond) + sep = true; + + toquote = false; + if (strchr(items[i].name, ' ') != NULL) { + toquote = opt->item_always_quote; + if (ischecklist && opt->item_output_sepnl == false) + toquote = true; + } + if (toquote) + dprintf(opt->output_fd, "%c%s%c", + quotech, items[i].name, quotech); + else + dprintf(opt->output_fd, "%s", items[i].name); + + if (sepafter) + dprintf(opt->output_fd, "%s", sepstr); + } +} + +int checklist_builder(BUILDER_ARGS) +{ + int output, focusitem; + unsigned int menurows, nitems; + struct bsddialog_menuitem *items; + + if (argc < 1) + exit_error(true, "--checklist missing <menurows>"); + menurows = (unsigned int)strtoul(argv[0], NULL, 10); + + get_menu_items(argc-1, argv+1, opt->item_prefix, opt->item_depth, true, + true, true, opt->item_bottomdesc, &nitems, &items, &focusitem, opt); + + output = bsddialog_checklist(conf, text, rows, cols, menurows, nitems, + items, &focusitem); + + print_menu_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +int menu_builder(BUILDER_ARGS) +{ + int output, focusitem; + unsigned int menurows, nitems; + struct bsddialog_menuitem *items; + + if (argc < 1) + exit_error(true, "--menu missing <menurows>"); + menurows = (unsigned int)strtoul(argv[0], NULL, 10); + + get_menu_items(argc-1, argv+1, opt->item_prefix, opt->item_depth, true, + true, false, opt->item_bottomdesc, &nitems, &items, &focusitem, + opt); + + output = bsddialog_menu(conf, text, rows, cols, menurows, nitems, + items, &focusitem); + + print_menu_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +int radiolist_builder(BUILDER_ARGS) +{ + int output, focusitem; + unsigned int menurows, nitems; + struct bsddialog_menuitem *items; + + if (argc < 1) + exit_error(true, "--radiolist missing <menurows>"); + menurows = (unsigned int)strtoul(argv[0], NULL, 10); + + get_menu_items(argc-1, argv+1, opt->item_prefix, opt->item_depth, true, + true, true, opt->item_bottomdesc, &nitems, &items, &focusitem, opt); + + output = bsddialog_radiolist(conf, text, rows, cols, menurows, nitems, + items, &focusitem); + + print_menu_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +int treeview_builder(BUILDER_ARGS) +{ + int output, focusitem; + unsigned int menurows, nitems; + struct bsddialog_menuitem *items; + + if (argc < 1) + exit_error(true, "--treeview missing <menurows>"); + menurows = (unsigned int)strtoul(argv[0], NULL, 10); + + get_menu_items(argc-1, argv+1, opt->item_prefix, true, true, true, true, + opt->item_bottomdesc, &nitems, &items, &focusitem, opt); + + conf->menu.no_name = true; + conf->menu.align_left = true; + + output = bsddialog_radiolist(conf, text, rows, cols, menurows, nitems, + items, &focusitem); + + print_menu_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +/* form */ +static unsigned int strcols(const char *string) +{ + int w; + unsigned int ncol; + size_t charlen, mb_cur_max; + wchar_t wch; + mbstate_t mbs; + + mb_cur_max = MB_CUR_MAX; + ncol = 0; + memset(&mbs, 0, sizeof(mbs)); + while ((charlen = mbrlen(string, mb_cur_max, &mbs)) != 0 && + charlen != (size_t)-1 && charlen != (size_t)-2) { + if (mbtowc(&wch, string, mb_cur_max) < 0) + return (0); + if ((w = wcwidth(wch)) > 0) + ncol += w; + string += charlen; + } + + return (ncol); +} + +static void +print_form_items(int output, int nitems, struct bsddialog_formitem *items, + int focusitem, struct options *opt) +{ + int i; + const char *helpname; + + if (NO_PRINT_VALUES(output)) + return; + + if (output == BSDDIALOG_HELP) { + dprintf(opt->output_fd, "HELP"); + if (focusitem >= 0) { + helpname = items[focusitem].label; + if (opt->item_bottomdesc && + opt->help_print_item_name == false) + helpname = items[focusitem].bottomdesc; + dprintf(opt->output_fd, " %s", helpname); + } + if (opt->help_print_items == false) + return; + dprintf(opt->output_fd, "\n"); + } + + for (i = 0; i < nitems; i++) { + if (!(items[i].flags & BSDDIALOG_FIELDREADONLY)) + dprintf(opt->output_fd, "%s\n", items[i].value); + free(items[i].value); + } +} + +int form_builder(BUILDER_ARGS) +{ + int output, fieldlen, focusitem; + unsigned int i, j, flags, formheight, nitems, sizeitem; + struct bsddialog_formitem *items; + + if (argc < 1) + exit_error(true, "--form missing <formheight>"); + formheight = (unsigned int)strtoul(argv[0], NULL, 10); + + argc--; + argv++; + sizeitem = opt->item_bottomdesc ? 9 : 8; + if (argc % sizeitem != 0) + exit_error(true, "--form bad number of arguments items"); + + nitems = argc / sizeitem; + if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL) + exit_error(false, "cannot allocate memory for form items"); + j = 0; + for (i = 0; i < nitems; i++) { + items[i].label = argv[j++]; + items[i].ylabel = (unsigned int)strtoul(argv[j++], NULL, 10); + items[i].xlabel = (unsigned int)strtoul(argv[j++], NULL, 10); + items[i].init = argv[j++]; + items[i].yfield = (unsigned int)strtoul(argv[j++], NULL, 10); + items[i].xfield = (unsigned int)strtoul(argv[j++], NULL, 10); + + fieldlen = (int)strtol(argv[j++], NULL, 10); + if (fieldlen == 0) + items[i].fieldlen = strcols(items[i].init); + else + items[i].fieldlen = abs(fieldlen); + + items[i].maxvaluelen = (unsigned int)strtoul(argv[j++], NULL, 10); + if (items[i].maxvaluelen == 0) + items[i].maxvaluelen = items[i].fieldlen; + + flags = (fieldlen <= 0) ? BSDDIALOG_FIELDREADONLY : 0; + items[i].flags = flags; + + items[i].bottomdesc = opt->item_bottomdesc ? argv[j++] : ""; + } + + focusitem = -1; + output = bsddialog_form(conf, text, rows, cols, formheight, nitems, + items, &focusitem); + print_form_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +int inputbox_builder(BUILDER_ARGS) +{ + int output; + struct bsddialog_formitem item; + + if (argc > 1) + error_args("--inputbox", argc - 1, argv + 1); + + item.label = ""; + item.ylabel = 0; + item.xlabel = 0; + item.init = argc > 0 ? argv[0] : ""; + item.yfield = 0; + item.xfield = 0; + item.fieldlen = 1; + item.maxvaluelen = opt->max_input_form; + item.flags = BSDDIALOG_FIELDNOCOLOR; + item.flags |= BSDDIALOG_FIELDCURSOREND; + item.flags |= BSDDIALOG_FIELDEXTEND; + item.bottomdesc = ""; + + output = bsddialog_form(conf, text, rows, cols, 1, 1, &item, NULL); + print_form_items(output, 1, &item, -1, opt); + + return (output); +} + +int mixedform_builder(BUILDER_ARGS) +{ + int output, fieldlen, focusitem; + unsigned int i, j, formheight, nitems, sizeitem; + struct bsddialog_formitem *items; + + if (argc < 1) + exit_error(true, "--mixedform missing <formheight>"); + formheight = (unsigned int)strtoul(argv[0], NULL, 10); + + argc--; + argv++; + sizeitem = opt->item_bottomdesc ? 10 : 9; + if (argc % sizeitem != 0) + exit_error(true, "--mixedform bad number of arguments items"); + + nitems = argc / sizeitem; + if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL) + exit_error(false, "cannot allocate memory for form items"); + j = 0; + for (i = 0; i < nitems; i++) { + items[i].label = argv[j++]; + items[i].ylabel = (unsigned int)strtoul(argv[j++], NULL, 10); + items[i].xlabel = (unsigned int)strtoul(argv[j++], NULL, 10); + items[i].init = argv[j++]; + items[i].yfield = (unsigned int)strtoul(argv[j++], NULL, 10); + items[i].xfield = (unsigned int)strtoul(argv[j++], NULL, 10); + fieldlen = (int)strtol(argv[j++], NULL, 10); + if (fieldlen == 0) + items[i].fieldlen = strcols(items[i].init); + else + items[i].fieldlen = abs(fieldlen); + items[i].maxvaluelen = (unsigned int)strtoul(argv[j++], NULL, 10); + if (items[i].maxvaluelen == 0) + items[i].maxvaluelen = items[i].fieldlen; + + items[i].flags = (unsigned int)strtoul(argv[j++], NULL, 10); + if (fieldlen <= 0) + items[i].flags |= BSDDIALOG_FIELDREADONLY; + + items[i].bottomdesc = opt->item_bottomdesc ? argv[j++] : ""; + } + + focusitem = -1; + output = bsddialog_form(conf, text, rows, cols, formheight, nitems, + items, &focusitem); + for (i = 0; i < nitems; i++) { + if ((int)strtol(argv[i * sizeitem + 6], NULL, 10) > 0) + items[i].flags &= ~ BSDDIALOG_FIELDREADONLY; + } + print_form_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} + +int passwordbox_builder(BUILDER_ARGS) +{ + int output; + struct bsddialog_formitem item; + + if (argc > 1) + error_args("--passwordbox", argc - 1, argv + 1); + + item.label = ""; + item.ylabel = 0; + item.xlabel = 0; + item.init = argc > 0 ? argv[0] : ""; + item.yfield = 0; + item.xfield = 0; + item.fieldlen = 1; + item.maxvaluelen = opt->max_input_form; + item.flags = BSDDIALOG_FIELDHIDDEN; + item.flags |= BSDDIALOG_FIELDNOCOLOR; + item.flags |= BSDDIALOG_FIELDCURSOREND; + item.flags |= BSDDIALOG_FIELDEXTEND; + item.bottomdesc = ""; + + output = bsddialog_form(conf, text, rows, cols, 1, 1, &item, NULL); + print_form_items(output, 1, &item, -1, opt); + + return (output); +} + +int passwordform_builder(BUILDER_ARGS) +{ + int output, fieldlen, valuelen, focusitem; + unsigned int i, j, flags, formheight, nitems, sizeitem; + struct bsddialog_formitem *items; + + if (argc < 1) + exit_error(true, "--passwordform missing <formheight>"); + formheight = (unsigned int)strtoul(argv[0], NULL, 10); + + argc--; + argv++; + sizeitem = opt->item_bottomdesc ? 9 : 8; + if (argc % sizeitem != 0) + exit_error(true, "--passwordform bad arguments items number"); + + flags = BSDDIALOG_FIELDHIDDEN; + nitems = argc / sizeitem; + if ((items = calloc(nitems, sizeof(struct bsddialog_formitem))) == NULL) + exit_error(false, "cannot allocate memory for form items"); + j = 0; + for (i = 0; i < nitems; i++) { + items[i].label = argv[j++]; + items[i].ylabel = (unsigned int)strtoul(argv[j++], NULL, 10); + items[i].xlabel = (unsigned int)strtoul(argv[j++], NULL, 10); + items[i].init = argv[j++]; + items[i].yfield = (unsigned int)strtoul(argv[j++], NULL, 10); + items[i].xfield = (unsigned int)strtoul(argv[j++], NULL, 10); + + fieldlen = (int)strtol(argv[j++], NULL, 10); + items[i].fieldlen = abs(fieldlen); + + valuelen = (int)strtol(argv[j++], NULL, 10); + items[i].maxvaluelen = valuelen == 0 ? abs(fieldlen) : valuelen; + + flags |= (fieldlen < 0 ? BSDDIALOG_FIELDREADONLY : 0); + items[i].flags = flags; + + items[i].bottomdesc = opt->item_bottomdesc ? argv[j++] : ""; + } + + focusitem = -1; + output = bsddialog_form(conf, text, rows, cols, formheight, nitems, + items, &focusitem); + print_form_items(output, nitems, items, focusitem, opt); + free(items); + + if (output == BSDDIALOG_HELP && opt->item_bottomdesc) + output = BSDDIALOG_ITEM_HELP; + + return (output); +} diff --git a/contrib/bsddialog/utility/util_cli.c b/contrib/bsddialog/utility/util_cli.c new file mode 100644 index 000000000000..01b6fc31f065 --- /dev/null +++ b/contrib/bsddialog/utility/util_cli.c @@ -0,0 +1,841 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2021-2025 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/ioctl.h> + +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <bsddialog.h> +#include <bsddialog_theme.h> + +#include "util.h" + +enum OPTS { + /* Options */ + ALTERNATE_SCREEN = '?' + 1, + AND_DIALOG, + ASCII_LINES, + BACKTITLE, + BEGIN_X, + BEGIN_Y, + BIKESHED, + CANCEL_EXIT_CODE, + CANCEL_LABEL, + CLEAR_DIALOG, + CLEAR_SCREEN, + COLUMNS_PER_ROW, + CR_WRAP, + DATEBOX_FORMAT, + DATE_FORMAT, + DEFAULT_BUTTON, + DEFAULT_ITEM, + DEFAULT_NO, + DISABLE_ESC, + ERROR_EXIT_CODE, + ESC_EXIT_CODE, + EXIT_LABEL, + EXTRA_BUTTON, + EXTRA_EXIT_CODE, + EXTRA_LABEL, + HELP_BUTTON, + HELP_EXIT_CODE, + HELP_LABEL, + HELP_PRINT_ITEMS, + HELP_PRINT_NAME, + HFILE, + HLINE, + HMSG, + IGNORE, + INSECURE, + ITEM_BOTTOM_DESC, + ITEM_DEPTH, + ITEM_PREFIX, + LEFT1_BUTTON, + LEFT1_EXIT_CODE, + LEFT2_BUTTON, + LEFT2_EXIT_CODE, + LEFT3_BUTTON, + LEFT3_EXIT_CODE, + LOAD_THEME, + MAX_INPUT_FORM, + NO_CANCEL, + NO_DESCRIPTIONS, + NO_LINES, + NO_NAMES, + NO_OK, + NO_SHADOW, + NORMAL_SCREEN, + OK_EXIT_CODE, + OK_LABEL, + OUTPUT_FD, + OUTPUT_SEPARATOR, + PRINT_MAXSIZE, + PRINT_SIZE, + PRINT_VERSION, + QUOTED, + RIGHT1_BUTTON, + RIGHT1_EXIT_CODE, + RIGHT2_BUTTON, + RIGHT2_EXIT_CODE, + RIGHT3_BUTTON, + RIGHT3_EXIT_CODE, + SAVE_THEME, + SEPARATE_OUTPUT, + SHADOW, + SINGLE_QUOTED, + SLEEP, + STDERR, + STDOUT, + SWITCH_BUTTONS, + TAB_ESCAPE, + TAB_LEN, + TEXT_ESCAPE, + TEXT_UNCHANGED, + THEME, + TIMEOUT_EXIT_CODE, + TIME_FORMAT, + TITLE, + /* Dialogs */ + CALENDAR, + CHECKLIST, + DATEBOX, + FORM, + GAUGE, + INFOBOX, + INPUTBOX, + MENU, + MIXEDFORM, + MIXEDGAUGE, + MSGBOX, + PASSWORDBOX, + PASSWORDFORM, + PAUSE, + RADIOLIST, + RANGEBOX, + TEXTBOX, + TIMEBOX, + TREEVIEW, + YESNO +}; + +/* options descriptor */ +static struct option longopts[] = { + /* Options */ + {"alternate-screen", no_argument, NULL, ALTERNATE_SCREEN}, + {"and-dialog", no_argument, NULL, AND_DIALOG}, + {"and-widget", no_argument, NULL, AND_DIALOG}, + {"ascii-lines", no_argument, NULL, ASCII_LINES}, + {"backtitle", required_argument, NULL, BACKTITLE}, + {"begin-x", required_argument, NULL, BEGIN_X}, + {"begin-y", required_argument, NULL, BEGIN_Y}, + {"bikeshed", no_argument, NULL, BIKESHED}, + {"cancel-exit-code", required_argument, NULL, CANCEL_EXIT_CODE}, + {"cancel-label", required_argument, NULL, CANCEL_LABEL}, + {"clear", no_argument, NULL, CLEAR_SCREEN}, + {"clear-dialog", no_argument, NULL, CLEAR_DIALOG}, + {"clear-screen", no_argument, NULL, CLEAR_SCREEN}, + {"colors", no_argument, NULL, TEXT_ESCAPE}, + {"columns-per-row", required_argument, NULL, COLUMNS_PER_ROW}, + {"cr-wrap", no_argument, NULL, CR_WRAP}, + {"datebox-format", required_argument, NULL, DATEBOX_FORMAT}, + {"date-format", required_argument, NULL, DATE_FORMAT}, + {"defaultno", no_argument, NULL, DEFAULT_NO}, + {"default-button", required_argument, NULL, DEFAULT_BUTTON}, + {"default-item", required_argument, NULL, DEFAULT_ITEM}, + {"default-no", no_argument, NULL, DEFAULT_NO}, + {"disable-esc", no_argument, NULL, DISABLE_ESC}, + {"error-exit-code", required_argument, NULL, ERROR_EXIT_CODE}, + {"esc-exit-code", required_argument, NULL, ESC_EXIT_CODE}, + {"exit-label", required_argument, NULL, EXIT_LABEL}, + {"extra-button", no_argument, NULL, EXTRA_BUTTON}, + {"extra-exit-code", required_argument, NULL, EXTRA_EXIT_CODE}, + {"extra-label", required_argument, NULL, EXTRA_LABEL}, + {"help-button", no_argument, NULL, HELP_BUTTON}, + {"help-exit-code", required_argument, NULL, HELP_EXIT_CODE}, + {"help-label", required_argument, NULL, HELP_LABEL}, + {"help-print-items", no_argument, NULL, HELP_PRINT_ITEMS}, + {"help-print-name", no_argument, NULL, HELP_PRINT_NAME}, + {"help-status", no_argument, NULL, HELP_PRINT_ITEMS}, + {"help-tags", no_argument, NULL, HELP_PRINT_NAME}, + {"hfile", required_argument, NULL, HFILE}, + {"hline", required_argument, NULL, HLINE}, + {"hmsg", required_argument, NULL, HMSG}, + {"ignore", no_argument, NULL, IGNORE}, + {"insecure", no_argument, NULL, INSECURE}, + {"item-bottom-desc", no_argument, NULL, ITEM_BOTTOM_DESC}, + {"item-depth", no_argument, NULL, ITEM_DEPTH}, + {"item-help", no_argument, NULL, ITEM_BOTTOM_DESC}, + {"item-prefix", no_argument, NULL, ITEM_PREFIX}, + {"keep-tite", no_argument, NULL, ALTERNATE_SCREEN}, + {"left1-button", required_argument, NULL, LEFT1_BUTTON}, + {"left1-exit-code", required_argument, NULL, LEFT1_EXIT_CODE}, + {"left2-button", required_argument, NULL, LEFT2_BUTTON}, + {"left2-exit-code", required_argument, NULL, LEFT2_EXIT_CODE}, + {"left3-button", required_argument, NULL, LEFT3_BUTTON}, + {"left3-exit-code", required_argument, NULL, LEFT3_EXIT_CODE}, + {"load-theme", required_argument, NULL, LOAD_THEME}, + {"max-input", required_argument, NULL, MAX_INPUT_FORM}, + {"no-cancel", no_argument, NULL, NO_CANCEL}, + {"nocancel", no_argument, NULL, NO_CANCEL}, + {"no-descriptions", no_argument, NULL, NO_DESCRIPTIONS}, + {"no-items", no_argument, NULL, NO_DESCRIPTIONS}, + {"no-label", required_argument, NULL, CANCEL_LABEL}, + {"no-lines", no_argument, NULL, NO_LINES}, + {"no-names", no_argument, NULL, NO_NAMES}, + {"no-ok", no_argument, NULL, NO_OK}, + {"nook", no_argument, NULL, NO_OK}, + {"no-shadow", no_argument, NULL, NO_SHADOW}, + {"no-tags", no_argument, NULL, NO_NAMES}, + {"normal-screen", no_argument, NULL, NORMAL_SCREEN}, + {"ok-exit-code", required_argument, NULL, OK_EXIT_CODE}, + {"ok-label", required_argument, NULL, OK_LABEL}, + {"output-fd", required_argument, NULL, OUTPUT_FD}, + {"output-separator", required_argument, NULL, OUTPUT_SEPARATOR}, + {"print-maxsize", no_argument, NULL, PRINT_MAXSIZE}, + {"print-size", no_argument, NULL, PRINT_SIZE}, + {"print-version", no_argument, NULL, PRINT_VERSION}, + {"quoted", no_argument, NULL, QUOTED}, + {"right1-button", required_argument, NULL, RIGHT1_BUTTON}, + {"right1-exit-code", required_argument, NULL, RIGHT1_EXIT_CODE}, + {"right2-button", required_argument, NULL, RIGHT2_BUTTON}, + {"right2-exit-code", required_argument, NULL, RIGHT2_EXIT_CODE}, + {"right3-button", required_argument, NULL, RIGHT3_BUTTON}, + {"right3-exit-code", required_argument, NULL, RIGHT3_EXIT_CODE}, + {"save-theme", required_argument, NULL, SAVE_THEME}, + {"separate-output", no_argument, NULL, SEPARATE_OUTPUT}, + {"separator", required_argument, NULL, OUTPUT_SEPARATOR}, + {"shadow", no_argument, NULL, SHADOW}, + {"single-quoted", no_argument, NULL, SINGLE_QUOTED}, + {"sleep", required_argument, NULL, SLEEP}, + {"stderr", no_argument, NULL, STDERR}, + {"stdout", no_argument, NULL, STDOUT}, + {"switch-buttons", no_argument, NULL, SWITCH_BUTTONS}, + {"tab-escape", no_argument, NULL, TAB_ESCAPE}, + {"tab-len", required_argument, NULL, TAB_LEN}, + {"text-escape", no_argument, NULL, TEXT_ESCAPE}, + {"text-unchanged", no_argument, NULL, TEXT_UNCHANGED}, + {"theme", required_argument, NULL, THEME}, + {"timeout-exit-code", required_argument, NULL, TIMEOUT_EXIT_CODE}, + {"time-format", required_argument, NULL, TIME_FORMAT}, + {"title", required_argument, NULL, TITLE}, + {"yes-label", required_argument, NULL, OK_LABEL}, + /* Dialogs */ + {"calendar", no_argument, NULL, CALENDAR}, + {"checklist", no_argument, NULL, CHECKLIST}, + {"datebox", no_argument, NULL, DATEBOX}, + {"form", no_argument, NULL, FORM}, + {"gauge", no_argument, NULL, GAUGE}, + {"infobox", no_argument, NULL, INFOBOX}, + {"inputbox", no_argument, NULL, INPUTBOX}, + {"menu", no_argument, NULL, MENU}, + {"mixedform", no_argument, NULL, MIXEDFORM}, + {"mixedgauge", no_argument, NULL, MIXEDGAUGE}, + {"msgbox", no_argument, NULL, MSGBOX}, + {"passwordbox", no_argument, NULL, PASSWORDBOX}, + {"passwordform", no_argument, NULL, PASSWORDFORM}, + {"pause", no_argument, NULL, PAUSE}, + {"radiolist", no_argument, NULL, RADIOLIST}, + {"rangebox", no_argument, NULL, RANGEBOX}, + {"textbox", no_argument, NULL, TEXTBOX}, + {"timebox", no_argument, NULL, TIMEBOX}, + {"treeview", no_argument, NULL, TREEVIEW}, + {"yesno", no_argument, NULL, YESNO}, + /* END */ + { NULL, 0, NULL, 0} +}; + +void usage(void) +{ + printf("usage: bsddialog --help | --version\n"); + printf(" bsddialog [--<opt>] --<dialog> <text> <rows> <cols> " + "[<arg>] [--<opt>]\n"); + printf(" bsddialog ... --<dialog1> ... [--and-dialog --<dialog2> " + "...] ...\n"); + printf("\n"); + + printf("Options:\n"); + printf(" --alternate-screen, --ascii-lines, --backtitle <backtitle>," + " --begin-x <x>,\n --begin-y <y>, --bikeshed," + " --cancel-exit-code <retval>, --cancel-label <label>,\n" + " --clear-dialog, --clear-screen, --columns-per-row <columns>," + " --cr-wrap,\n --datebox-format d/m/y|m/d/y|y/m/d," + " --date-format <format>,\n --default-button <label>," + " --default-item <name>, --default-no, --disable-esc,\n" + " --error-exit-code <retval>, --esc-exit-code <retval>," + " --exit-label <label>,\n --extra-button," + " --extra-exit-code <retval>, --extra-label <label>,\n" + " --left1-button <label>, --left1-exit-code <retval>," + " --left2-button <label>,\n --left2-exit-code <retval>," + " --left3-button <label>, --left3-exit-code <retval>,\n" + " --help-button, --help-exit-code <retval>, --help-label <label>,\n" + " --help-print-items, --help-print-name, --hfile <file>," + " --hline <string>,\n --hmsg <string>, --ignore, --insecure," + " --item-bottom-desc, --item-depth,\n --item-prefix," + " --load-theme <file>, --max-input <size>, --no-cancel,\n" + " --no-descriptions, --no-label <label>, --no-lines, --no-names," + " --no-ok,\n --no-shadow, --normal-screen, --ok-exit-code <retval>," + " --ok-label <label>,\n --output-fd <fd>, --output-separator <sep>," + " --print-maxsize, --print-size,\n --print-version, --quoted," + " --right1-button <label>,\n --right1-exit-code <retval>," + " --right2-button <label>,\n --right2-exit-code <retval>," + " --right3-button <label>,\n --right3-exit-code <retval>," + " --save-theme <file>, --separate-output,\n --separator <sep>," + " --shadow, --single-quoted, --sleep <secs>, --stderr,\n --stdout," + " --switch-buttons, --tab-escape, --tab-len <spaces>," + " --text-escape,\n --text-unchanged, --theme 3d|blackwhite|flat," + " --timeout-exit-code <retval>,\n --time-format <format>," + " --title <title>, --yes-label <label>."); + printf("\n\n"); + + printf("Dialogs:\n"); + printf(" --calendar <text> <rows> <cols> [<dd> <mm> <yy>]\n"); + printf(" --checklist <text> <rows> <cols> <menurows> [<name> <desc> " + "on|off] ...\n"); + printf(" --datebox <text> <rows> <cols> [<dd> <mm> <yy>]\n"); + printf(" --form <text> <rows> <cols> <formrows> [<label> <ylabel> " + "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxletters>] " + "...\n"); + printf(" --gauge <text> <rows> <cols> [<perc>]\n"); + printf(" --infobox <text> <rows> <cols>\n"); + printf(" --inputbox <text> <rows> <cols> [<init>]\n"); + printf(" --menu <text> <rows> <cols> <menurows> [<name> <desc>] ...\n"); + printf(" --mixedform <text> <rows> <cols> <formrows> [<label> <ylabel> " + "<xlabel> <init> <yfield> <xfield> <fieldlen> <maxletters> " + "0|1|2] ...\n"); + printf(" --mixedgauge <text> <rows> <cols> <mainperc> [<minilabel> " + "<miniperc>] ...\n"); + printf(" --msgbox <text> <rows> <cols>\n"); + printf(" --passwordbox <text> <rows> <cols> [<init>]\n"); + printf(" --passwordform <text> <rows> <cols> <formrows> [<label> " + "<ylabel> <xlabel> <init> <yfield> <xfield> <fieldlen> " + "<maxletters>] ...\n"); + printf(" --pause <text> <rows> <cols> <secs>\n"); + printf(" --radiolist <text> <rows> <cols> <menurows> [<name> <desc> " + "on|off] ...\n"); + printf(" --rangebox <text> <rows> <cols> <min> <max> [<init>]\n"); + printf(" --textbox <file> <rows> <cols>\n"); + printf(" --timebox <text> <rows> <cols> [<hh> <mm> <ss>]\n"); + printf(" --treeview <text> <rows> <cols> <menurows> [<depth> <name> " + "<desc> on|off] ...\n"); + printf(" --yesno <text> <rows> <cols>\n"); + printf("\n"); + + printf("See 'man 1 bsddialog' for more information.\n"); +} + +int +parseargs(int argc, char **argv, struct bsddialog_conf *conf, + struct options *opt) +{ + int arg, parsed, i; + struct winsize ws; + + bsddialog_initconf(conf); + conf->key.enable_esc = true; + conf->button.always_active = true; + + memset(opt, 0, sizeof(struct options)); + opt->theme = -1; + opt->output_fd = STDERR_FILENO; + opt->max_input_form = 2048; + opt->mandatory_dialog = true; + + for (i = 0; i < argc; i++) { + if (strcmp(argv[i], "--and-dialog") == 0 || + strcmp(argv[i], "--and-widget") == 0) { + argc = i + 1; + break; + } + } + parsed = argc; + while ((arg = getopt_long(argc, argv, "", longopts, NULL)) != -1) { + switch (arg) { + /* Options */ + case ALTERNATE_SCREEN: + opt->screen_mode = "smcup"; + break; + case AND_DIALOG: + if (opt->dialogbuilder == NULL) + exit_error(true,"--and-dialog without " + "previous --<dialog>"); + break; + case ASCII_LINES: + conf->ascii_lines = true; + break; + case BACKTITLE: + opt->backtitle = optarg; + if (conf->y == BSDDIALOG_CENTER) + conf->auto_topmargin = 2; + break; + case BEGIN_X: + conf->x = (int)strtol(optarg, NULL, 10); + if (conf->x < BSDDIALOG_CENTER) + exit_error(false, "--begin-x %d is < %d", + conf->x, BSDDIALOG_CENTER); + break; + case BEGIN_Y: + conf->y = (int)strtol(optarg, NULL, 10); + if (conf->y < BSDDIALOG_CENTER) + exit_error(false, "--begin-y %d is < %d", + conf->y, BSDDIALOG_CENTER); + conf->auto_topmargin = 0; + break; + case BIKESHED: + opt->bikeshed = true; + break; + case CANCEL_EXIT_CODE: + set_exit_code(BSDDIALOG_CANCEL, + (int)strtol(optarg, NULL, 10)); + break; + case CANCEL_LABEL: + conf->button.cancel_label = optarg; + break; + case CLEAR_DIALOG: + conf->clear = true; + break; + case CLEAR_SCREEN: + opt->mandatory_dialog = false; + opt->clearscreen = true; + break; + case COLUMNS_PER_ROW: + conf->text.cols_per_row = + (u_int)strtoul(optarg, NULL, 10); + break; + case CR_WRAP: + opt->cr_wrap = true; + break; + case DATEBOX_FORMAT: + if (strcasecmp(optarg, "d/m/y") == 0) + conf->date.format = "d/m/y"; + else if (strcasecmp(optarg, "m/d/y") == 0) + conf->date.format = "m/d/y"; + else if (strcasecmp(optarg, "y/m/d") == 0) + conf->date.format = "y/m/d"; + else + exit_error(true, + "date format \"%s\" is invalid", optarg); + break; + case DATE_FORMAT: + opt->date_fmt = optarg; + break; + case DEFAULT_BUTTON: + conf->button.default_label = optarg; + break; + case DEFAULT_ITEM: + opt->item_default = optarg; + break; + case DEFAULT_NO: + conf->button.default_cancel = true; + break; + case DISABLE_ESC: + conf->key.enable_esc = false; + break; + case ERROR_EXIT_CODE: + set_exit_code(BSDDIALOG_ERROR, + (int)strtol(optarg, NULL, 10)); + break; + case ESC_EXIT_CODE: + set_exit_code(BSDDIALOG_ESC, + (int)strtol(optarg, NULL, 10)); + break; + case EXIT_LABEL: + conf->button.ok_label = optarg; + break; + case EXTRA_BUTTON: + conf->button.with_extra = true; + break; + case EXTRA_EXIT_CODE: + set_exit_code(BSDDIALOG_EXTRA, + (int)strtol(optarg, NULL, 10)); + break; + case EXTRA_LABEL: + conf->button.extra_label = optarg; + break; + case HELP_BUTTON: + conf->button.with_help = true; + break; + case HELP_EXIT_CODE: + i = (int)strtol(optarg, NULL, 10); + set_exit_code(BSDDIALOG_HELP, i); + /* _TEM_HELP follows _HELP */ + set_exit_code(BSDDIALOG_ITEM_HELP, i); + break; + case HELP_LABEL: + conf->button.help_label = optarg; + break; + case HELP_PRINT_ITEMS: + opt->help_print_items = true; + break; + case HELP_PRINT_NAME: + opt->help_print_item_name = true; + break; + case HFILE: + conf->key.f1_file = optarg; + break; + case HLINE: + if (optarg[0] != '\0') + conf->bottomtitle = optarg; + break; + case HMSG: + conf->key.f1_message = optarg; + break; + case IGNORE: + opt->ignore = true; + break; + case INSECURE: + conf->form.securech = '*'; + break; + case ITEM_BOTTOM_DESC: + opt->item_bottomdesc = true; + break; + case ITEM_DEPTH: + opt->item_depth = true; + break; + case ITEM_PREFIX: + opt->item_prefix = true; + break; + case LEFT1_BUTTON: + conf->button.left1_label = optarg; + break; + case LEFT1_EXIT_CODE: + set_exit_code(BSDDIALOG_LEFT1, + (int)strtol(optarg, NULL, 10)); + break; + case LEFT2_BUTTON: + conf->button.left2_label = optarg; + break; + case LEFT2_EXIT_CODE: + set_exit_code(BSDDIALOG_LEFT2, + (int)strtol(optarg, NULL, 10)); + break; + case LEFT3_BUTTON: + conf->button.left3_label = optarg; + break; + case LEFT3_EXIT_CODE: + set_exit_code(BSDDIALOG_LEFT3, + (int)strtol(optarg, NULL, 10)); + break; + case LOAD_THEME: + opt->loadthemefile = optarg; + break; + case MAX_INPUT_FORM: + opt->max_input_form = (u_int)strtoul(optarg, NULL, 10); + break; + case NO_CANCEL: + conf->button.without_cancel = true; + break; + case NO_DESCRIPTIONS: + conf->menu.no_desc = true; + break; + case NO_LINES: + conf->no_lines = true; + break; + case NO_NAMES: + conf->menu.no_name = true; + break; + case NO_OK: + conf->button.without_ok = true; + break; + case NO_SHADOW: + conf->shadow = false; + break; + case NORMAL_SCREEN: + opt->screen_mode = "rmcup"; + break; + case OK_EXIT_CODE: + set_exit_code(BSDDIALOG_OK, + (int)strtol(optarg, NULL, 10)); + break; + case OK_LABEL: + conf->button.ok_label = optarg; + break; + case OUTPUT_FD: + opt->output_fd = (int)strtol(optarg, NULL, 10); + break; + case OUTPUT_SEPARATOR: + opt->item_output_sep = optarg; + break; + case QUOTED: + opt->item_always_quote = true; + break; + case PRINT_MAXSIZE: + opt->mandatory_dialog = false; + ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws); + dprintf(opt->output_fd, "MaxSize: %d, %d\n", + ws.ws_row, ws.ws_col); + break; + case PRINT_SIZE: + conf->get_height = &opt->getH; + conf->get_width = &opt->getW; + break; + case PRINT_VERSION: + opt->mandatory_dialog = false; + dprintf(opt->output_fd, "Version: %s\n", + LIBBSDDIALOG_VERSION); + break; + case RIGHT1_BUTTON: + conf->button.right1_label = optarg; + break; + case RIGHT1_EXIT_CODE: + set_exit_code(BSDDIALOG_RIGHT1, + (int)strtol(optarg, NULL, 10)); + break; + case RIGHT2_BUTTON: + conf->button.right2_label = optarg; + break; + case RIGHT2_EXIT_CODE: + set_exit_code(BSDDIALOG_RIGHT2, + (int)strtol(optarg, NULL, 10)); + break; + case RIGHT3_BUTTON: + conf->button.right3_label = optarg; + break; + case RIGHT3_EXIT_CODE: + set_exit_code(BSDDIALOG_RIGHT3, + (int)strtol(optarg, NULL, 10)); + break; + case SAVE_THEME: + opt->mandatory_dialog = false; + opt->savethemefile = optarg; + break; + case SEPARATE_OUTPUT: + opt->item_output_sepnl = true; + break; + case SHADOW: + conf->shadow = true; + break; + case SINGLE_QUOTED: + opt->item_singlequote = true; + break; + case SLEEP: + conf->sleep = (u_int)strtoul(optarg, NULL, 10); + break; + case STDERR: + opt->output_fd = STDERR_FILENO; + break; + case STDOUT: + opt->output_fd = STDOUT_FILENO; + break; + case SWITCH_BUTTONS: + conf->button.always_active = false; + break; + case TAB_ESCAPE: + opt->tab_escape = true; + break; + case TAB_LEN: + conf->text.tablen = (u_int)strtoul(optarg, NULL, 10); + break; + case TEXT_ESCAPE: + conf->text.escape = true; + break; + case TEXT_UNCHANGED: + opt->text_unchanged = true; + break; + case THEME: + if (strcasecmp(optarg, "blackwhite") == 0) + opt->theme = BSDDIALOG_THEME_BLACKWHITE; + else if (strcasecmp(optarg, "flat") == 0) + opt->theme = BSDDIALOG_THEME_FLAT; + else if (strcasecmp(optarg, "3d") == 0) + opt->theme = BSDDIALOG_THEME_3D; + else + exit_error(true, + "--theme: \"%s\" is unknown", optarg); + break; + case TIMEOUT_EXIT_CODE: + set_exit_code(BSDDIALOG_TIMEOUT, + (int)strtol(optarg, NULL, 10)); + break; + case TIME_FORMAT: + opt->time_fmt = optarg; + break; + case TITLE: + conf->title = optarg; + break; + /* Dialogs */ + case CALENDAR: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --calendar without " + "--and-dialog", opt->name); + opt->name = "--calendar"; + opt->dialogbuilder = calendar_builder; + break; + case CHECKLIST: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --checklist without " + "--and-dialog", opt->name); + opt->name = "--checklist"; + opt->dialogbuilder = checklist_builder; + conf->auto_downmargin = 1; + break; + case DATEBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --datebox without " + "--and-dialog", opt->name); + opt->name = "--datebox"; + opt->dialogbuilder = datebox_builder; + break; + case FORM: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --form without " + "--and-dialog", opt->name); + opt->name = "--form"; + opt->dialogbuilder = form_builder; + conf->auto_downmargin = 1; + break; + case GAUGE: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --gauge without " + "--and-dialog", opt->name); + opt->name = "--gauge"; + opt->dialogbuilder = gauge_builder; + break; + case INFOBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --infobox without " + "--and-dialog", opt->name); + opt->name = "--infobox"; + opt->dialogbuilder = infobox_builder; + break; + case INPUTBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --inputbox without " + "--and-dialog", opt->name); + opt->name = "--inputbox"; + opt->dialogbuilder = inputbox_builder; + conf->auto_downmargin = 1; + break; + case MENU: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --menu without " + "--and-dialog", opt->name); + opt->name = "--menu"; + opt->dialogbuilder = menu_builder; + conf->auto_downmargin = 1; + break; + case MIXEDFORM: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --mixedform without " + "--and-dialog", opt->name); + opt->name = "--mixedform"; + opt->dialogbuilder = mixedform_builder; + conf->auto_downmargin = 1; + break; + case MIXEDGAUGE: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --mixedgauge without " + "--and-dialog", opt->name); + opt->name = "--mixedgauge"; + opt->dialogbuilder = mixedgauge_builder; + break; + case MSGBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --msgbox without " + "--and-dialog", opt->name); + opt->name = "--"; + opt->dialogbuilder = msgbox_builder; + break; + case PAUSE: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --pause without " + "--and-dialog", opt->name); + opt->name = "--pause"; + opt->dialogbuilder = pause_builder; + break; + case PASSWORDBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --passwordbox without " + "--and-dialog", opt->name); + opt->name = "--passwordbox"; + opt->dialogbuilder = passwordbox_builder; + conf->auto_downmargin = 1; + break; + case PASSWORDFORM: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --passwordform " + "without --and-dialog", opt->name); + opt->name = "--passwordform"; + opt->dialogbuilder = passwordform_builder; + conf->auto_downmargin = 1; + break; + case RADIOLIST: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --radiolist without " + "--and-dialog", opt->name); + opt->name = "--radiolist"; + opt->dialogbuilder = radiolist_builder; + conf->auto_downmargin = 1; + break; + case RANGEBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --rangebox without " + "--and-dialog", opt->name); + opt->name = "--rangebox"; + opt->dialogbuilder = rangebox_builder; + break; + case TEXTBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --textbox without " + "--and-dialog", opt->name); + opt->name = "--textbox"; + opt->dialogbuilder = textbox_builder; + break; + case TIMEBOX: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --timebox without " + "--and-dialog", opt->name); + opt->name = "--timebox"; + opt->dialogbuilder = timebox_builder; + break; + case TREEVIEW: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --treeview without " + "--and-dialog", opt->name); + opt->name = "--treeview"; + opt->dialogbuilder = treeview_builder; + conf->auto_downmargin = 1; + break; + case YESNO: + if (opt->dialogbuilder != NULL) + exit_error(true, "%s and --yesno without " + "--and-dialog", opt->name); + opt->name = "--yesno"; + opt->dialogbuilder = yesno_builder; + break; + default: /* Error */ + if (opt->ignore == true) + break; + exit_error(true, "--ignore to continue"); + } + } + + return (parsed); +} diff --git a/contrib/bsddialog/utility/util_theme.c b/contrib/bsddialog/utility/util_theme.c new file mode 100644 index 000000000000..cca79e83b97d --- /dev/null +++ b/contrib/bsddialog/utility/util_theme.c @@ -0,0 +1,451 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022-2025 Alfonso Sabato Siciliano + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/time.h> + +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> + +#include <bsddialog.h> +#include <bsddialog_theme.h> + +#include "util.h" + +static struct bsddialog_theme t; +static char title[1024]; + +#define NPROPERTY 41 +#define NCOLOR 8 +#define NATTR 6 + +#define PROP_ERROR(name, error) do { \ + fclose(fp); \ + exit_error(false, "%s for \"%s\"", error, name); \ +} while (0) + +enum typeproperty { + BOOL, + CHAR, + INT, + UINT, + COLOR, + COMPAT +}; + +struct property { + const char *comment; + const char *name; + enum typeproperty type; + void *value; +}; + +struct namevalue { + const char *name; + unsigned int value; +}; + +static struct namevalue color[NCOLOR] = { + {"black", BSDDIALOG_BLACK}, + {"red", BSDDIALOG_RED}, + {"green", BSDDIALOG_GREEN}, + {"yellow", BSDDIALOG_YELLOW}, + {"blue", BSDDIALOG_BLUE}, + {"magenta", BSDDIALOG_MAGENTA}, + {"cyan", BSDDIALOG_CYAN}, + {"white", BSDDIALOG_WHITE} +}; + +static struct namevalue attr[NATTR] = { + {"bold", BSDDIALOG_BOLD}, + {"reverse", BSDDIALOG_REVERSE}, + {"underline", BSDDIALOG_UNDERLINE}, + {"blink", BSDDIALOG_BLINK}, + {"halfbright", BSDDIALOG_HALFBRIGHT}, + {"highlight", BSDDIALOG_HIGHLIGHT} +}; + +static struct property p[NPROPERTY] = { + {"\n#Terminal\n", "theme.screen.color", COLOR, &t.screen.color}, + + {"\n# Shadow\n", + "theme.shadow.color", COLOR, &t.shadow.color}, + {"# shift down right from main widget\n", + "theme.shadow.y", UINT, &t.shadow.y}, + {"", "theme.shadow.x", UINT, &t.shadow.x}, + + {"\n# Main widget\n", + "theme.dialog.color", COLOR, &t.dialog.color}, + {"", "theme.dialog.delimtitle", BOOL, &t.dialog.delimtitle}, + {"", "theme.dialog.titlecolor", COLOR, &t.dialog.titlecolor}, + {"", "theme.dialog.lineraisecolor", COLOR, &t.dialog.lineraisecolor}, + {"", "theme.dialog.linelowercolor", COLOR, &t.dialog.linelowercolor}, + {"", "theme.dialog.bottomtitlecolor", COLOR, + &t.dialog.bottomtitlecolor}, + {"", "theme.dialog.arrowcolor", COLOR, &t.dialog.arrowcolor}, + + {"\n# Menus: --checklist, --menu, --radiolist\n" + "# prefix [selector] shortcut name desc bottomdesc\n", + "theme.menu.f_prefixcolor", COLOR, &t.menu.f_prefixcolor}, + {"", "theme.menu.prefixcolor", COLOR, &t.menu.prefixcolor}, + {"", "theme.menu.f_selectorcolor", COLOR, &t.menu.f_selectorcolor}, + {"", "theme.menu.selectorcolor", COLOR, &t.menu.selectorcolor}, + {"", "theme.menu.f_namecolor", COLOR, &t.menu.f_namecolor}, + {"", "theme.menu.namecolor", COLOR, &t.menu.namecolor}, + {"", "theme.menu.f_desccolor", COLOR, &t.menu.f_desccolor}, + {"", "theme.menu.desccolor", COLOR, &t.menu.desccolor}, + {"", "theme.menu.f_shortcutcolor", COLOR, &t.menu.f_shortcutcolor}, + {"", "theme.menu.shortcutcolor", COLOR, &t.menu.shortcutcolor}, + {"", "theme.menu.bottomdesccolor", COLOR, &t.menu.bottomdesccolor}, + {"# bsddialog_menutype BSDDIALOG_SEPARATOR\n", + "theme.menu.sepnamecolor", COLOR, &t.menu.sepnamecolor}, + {"", "theme.menu.sepdesccolor", COLOR, &t.menu.sepdesccolor}, + + {"\n# Forms\n", + "theme.form.f_fieldcolor", COLOR, &t.form.f_fieldcolor}, + {"", "theme.form.fieldcolor", COLOR, &t.form.fieldcolor}, + {"", "theme.form.readonlycolor", COLOR, &t.form.readonlycolor}, + {"", "theme.form.bottomdesccolor", COLOR, &t.form.bottomdesccolor}, + + {"\n# Bar of --gauge, --mixedgauge, --pause, --rangebox\n", + "theme.bar.f_color", COLOR, &t.bar.f_color}, + {"", "theme.bar.color", COLOR, &t.bar.color}, + + {"\n# Buttons\n", + "theme.button.minmargin", UINT, &t.button.minmargin}, + {"", "theme.button.maxmargin", UINT, &t.button.maxmargin}, + {"", "theme.button.leftdelim", CHAR, &t.button.leftdelim}, + {"", "theme.button.rightdelim", CHAR, &t.button.rightdelim}, + {"", "theme.button.f_delimcolor", COLOR, &t.button.f_delimcolor}, + {"", "theme.button.delimcolor", COLOR, &t.button.delimcolor}, + {"", "theme.button.f_color", COLOR, &t.button.f_color}, + {"", "theme.button.color", COLOR, &t.button.color}, + {"", "theme.button.f_shortcutcolor", COLOR, &t.button.f_shortcutcolor}, + {"", "theme.button.shortcutcolor", COLOR, &t.button.shortcutcolor}, + + {"\n#Compatibility. Do not use, can be deleted\n", + "use_shadow", COMPAT, NULL} +}; + +void savetheme(const char *file) +{ + int i, j; + unsigned int flags; + enum bsddialog_color bg, fg; + time_t clock; + FILE *fp; + + if (bsddialog_get_theme(&t) != BSDDIALOG_OK) + exit_error(false, + "cannot save theme: %s", bsddialog_geterror()); + + if (time(&clock) < 0) + exit_error(false, "cannot save profile getting current time"); + if ((fp = fopen(file, "w")) == NULL) + exit_error(false, "cannot open %s to save profile", file); + + fprintf(fp, "### bsddialog theme - %s\n", ctime(&clock)); + + fputs("# Colors: ", fp); + fputs("black red green yellow blue magenta cyan white.\n", fp); + fputs("# Attributes: ", fp); + fputs("bold reverse underline blink halfbright highlight.\n", fp); + fputs("# f_* refers to focus for an element with selected or ", fp); + fputs("unselected state.\n\n", fp); + + fprintf(fp, "version %s\n", LIBBSDDIALOG_VERSION); + + for (i = 0; i < NPROPERTY; i++) { + if (p[i].type == COMPAT) + continue; + fprintf(fp, "%s%s", p[i].comment, p[i].name); + switch (p[i].type) { + case CHAR: + fprintf(fp, " %c\n", *((char*)p[i].value)); + break; + case INT: + fprintf(fp, " %d\n", *((int*)p[i].value)); + break; + case UINT: + fprintf(fp, " %u\n", *((unsigned int*)p[i].value)); + break; + case BOOL: + fprintf(fp, " %s\n", + *((bool*)p[i].value) ? "true" : "false"); + break; + case COLOR: + bsddialog_color_attrs(*(int*)p[i].value, &fg, &bg, + &flags); + fprintf(fp, " %s %s", color[fg].name, color[bg].name); + for (j = 0; j < NATTR; j++) + if (flags & attr[j].value) + fprintf(fp, " %s", attr[j].name); + fputs("\n", fp); + break; + case COMPAT: + /* Do not save compat property for now */ + break; + } + } + + fclose(fp); +} + +void loadtheme(const char *file, bool compatibility) +{ + bool boolvalue; + char charvalue, *value; + char line[BUFSIZ], name[BUFSIZ], c1[BUFSIZ], c2[BUFSIZ]; + int i, j, intvalue; + unsigned int uintvalue, flags; + enum bsddialog_color bg, fg; + FILE *fp; + + if (bsddialog_hascolors() == false) + return; + + if (bsddialog_get_theme(&t) != BSDDIALOG_OK) + exit_error(false, "Cannot get current theme: %s", + bsddialog_geterror()); + + if ((fp = fopen(file, "r")) == NULL) + exit_error(false, "Cannot open theme \"%s\" file", file); + + while (fgets(line, BUFSIZ, fp) != NULL) { + if (line[0] == '#' || line[0] == '\n') + continue; /* superfluous, only for efficiency */ + sscanf(line, "%s", name); + value = NULL; /* useless init, fix compiler warning */ + for (i = 0; i < NPROPERTY; i++) { + if (strcmp(name, p[i].name) == 0) { + value = &line[strlen(name)]; + break; + } + } + if (i >= NPROPERTY) { + /* unknown name in property p[] */ + if (strcmp(name, "version") == 0) + continue; /* nothing for now */ + else if (compatibility) + continue; /* just ignore */ + else + PROP_ERROR(name, "Unknown theme property name"); + } + switch (p[i].type) { + case CHAR: + while (value[0] == ' ' || value[0] == '\n' || + value[0] == '\0') + value++; + if (sscanf(value, "%c", &charvalue) != 1) + PROP_ERROR(p[i].name, "Cannot get a char"); + *((int*)p[i].value) = charvalue; + break; + case INT: + if (sscanf(value, "%d", &intvalue) != 1) + PROP_ERROR(p[i].name, "Cannot get a int"); + *((int*)p[i].value) = intvalue; + break; + case UINT: + if (sscanf(value, "%u", &uintvalue) != 1) + PROP_ERROR(p[i].name, "Cannot get a uint"); + *((unsigned int*)p[i].value) = uintvalue; + break; + case BOOL: + boolvalue = (strstr(value, "true") != NULL) ? + true : false; + *((bool*)p[i].value) = boolvalue; + break; + case COLOR: + if (sscanf(value, "%s %s", c1, c2) != 2) + PROP_ERROR(p[i].name, "Cannot get 2 colors"); + /* Foreground */ + for (j = 0; j < NCOLOR ; j++) + if ((strstr(c1, color[j].name)) != NULL) + break; + if (j >= NCOLOR) + PROP_ERROR(p[i].name, "Bad foreground"); + fg = color[j].value; + /* Background */ + for (j = 0; j < NCOLOR ; j++) + if ((strstr(c2, color[j].name)) != NULL) + break; + if (j >= NCOLOR) + PROP_ERROR(p[i].name, "Bad background"); + bg = color[j].value; + /* Flags */ + flags = 0; + for (j = 0; j < NATTR; j++) + if (strstr(value, attr[j].name) != NULL) + flags |= attr[j].value; + *((int*)p[i].value) = bsddialog_color(fg, bg, flags); + break; + case COMPAT: + /* + * usr.sbin/bsdconfig/share/dialog.subr:2255 + * uses this parameter to set NO_SHADOW. + * Set t.shadow.[y|x] for compatibilty. + */ + if (strcmp(name, "use_shadow") == 0) { + if (strcasestr(value, "off") != NULL) + t.shadow.y = t.shadow.x = 0; + } + break; + } + } + + fclose(fp); + + if (bsddialog_set_theme(&t) != BSDDIALOG_OK) + exit_error(false, bsddialog_geterror()); +} + +void setdeftheme(enum bsddialog_default_theme theme) +{ + if (bsddialog_hascolors() == false) + return; + if (bsddialog_set_default_theme(theme) != BSDDIALOG_OK) + exit_error(false, bsddialog_geterror()); +} + +void startuptheme(void) +{ + bool sep; + char *env, *file, *home, path[PATH_MAX]; + + env = getenv("NO_COLOR"); + if (env != NULL && env[0] != '\0') + setdeftheme(BSDDIALOG_THEME_BLACKWHITE); + + if ((home = getenv("HOME")) != NULL) { + sep = (strcmp(home, "/") == 0) ? false : true; + + snprintf(path, PATH_MAX, "%s%s.bsddialog.conf", + home, sep ? "/" : ""); + if (access(path, F_OK) == 0) + loadtheme(path, false); + + if ((file = getenv("BSDDIALOG_COMPATRC")) != NULL) { + snprintf(path, PATH_MAX, "%s%s%s", + home, sep ? "/" : "", file); + if (access(path, F_OK) == 0) + loadtheme(path, true); + } + } + if ((file = getenv("BSDDIALOG_THEMEFILE")) != NULL) { + if (access(file, F_OK) == 0) + loadtheme(file, false); + } +} + +void bikeshed(struct bsddialog_conf *conf) +{ + int margin, i; + int colors[8] = {0, 0, 0, 0, 0, 0, 0, 0}; + char delim[8] = {'[', '<', '(', '|', ']', '>', ')', '|'}; + enum bsddialog_color col[6]; + struct timeval tv; + + /* theme */ + if (bsddialog_get_theme(&t) != BSDDIALOG_OK) + exit_error(false, bsddialog_geterror()); + + gettimeofday(&tv, NULL); + srand(tv.tv_usec); + for (i = 0; i < 6; i++) { + do { + col[i] = rand() % 8; + } while (colors[col[i]] == 1); + colors[col[i]] = 1; + } + + t.screen.color = bsddialog_color(col[4], col[3], 0); + + t.shadow.color = bsddialog_color(col[0], col[0], 0); + t.shadow.y = 1, + t.shadow.x = 2, + + t.dialog.delimtitle = (~rand() & 1) ? true : false; + t.dialog.titlecolor = bsddialog_color(col[3], col[5], 0); + t.dialog.lineraisecolor = bsddialog_color(col[0], col[5], 0); + t.dialog.linelowercolor = bsddialog_color(col[0], col[5], 0); + t.dialog.color = bsddialog_color(col[0], col[5], 0); + t.dialog.bottomtitlecolor = bsddialog_color(col[0], col[5], 0); + t.dialog.arrowcolor = bsddialog_color(col[3], col[5], 0); + + t.menu.f_prefixcolor = bsddialog_color(col[5], col[3], 0); + t.menu.prefixcolor = bsddialog_color(col[0], col[5], 0); + t.menu.f_selectorcolor = bsddialog_color(col[5], col[3], 0); + t.menu.selectorcolor = bsddialog_color(col[0], col[5], 0); + t.menu.f_desccolor = bsddialog_color(col[5], col[3], 0); + t.menu.desccolor = bsddialog_color(col[0], col[5], 0); + t.menu.f_namecolor = bsddialog_color(col[5], col[3], 0); + t.menu.namecolor = bsddialog_color(col[3], col[5], 0); + t.menu.f_shortcutcolor = bsddialog_color(col[1], col[3], 0); + t.menu.shortcutcolor = bsddialog_color(col[1], col[5], 0); + t.menu.bottomdesccolor = bsddialog_color(col[4], col[3], 0); + t.menu.sepnamecolor = bsddialog_color(col[1], col[5], 0); + t.menu.sepdesccolor = bsddialog_color(col[1], col[5], 0); + + t.form.f_fieldcolor = bsddialog_color(col[5], col[3], 0); + t.form.fieldcolor = bsddialog_color(col[5], col[4], 0); + t.form.readonlycolor = bsddialog_color(col[4], col[5], 0); + t.form.bottomdesccolor = bsddialog_color(col[4], col[3], 0); + + t.bar.f_color = bsddialog_color(col[5], col[3], 0); + t.bar.color = bsddialog_color(col[3], col[5], 0); + + t.button.minmargin = 1, + t.button.maxmargin = 5, + i = rand() % 4; + t.button.leftdelim = delim[i]; + t.button.rightdelim = delim[i + 4]; + t.button.f_delimcolor = bsddialog_color(col[5], col[3], 0); + t.button.delimcolor = bsddialog_color(col[0], col[5], 0); + t.button.f_color = bsddialog_color(col[2], col[3], 0); + t.button.color = bsddialog_color(col[0], col[5], 0); + t.button.f_shortcutcolor = bsddialog_color(col[5], col[3], 0); + t.button.shortcutcolor = bsddialog_color(col[1], col[5], 0); + + if (bsddialog_set_theme(&t)) + exit_error(false, bsddialog_geterror()); + + /* conf */ + conf->button.always_active = (~rand() & 1) ? true : false; + if ((i = rand() % 3) != 0) /* default "d/m/y" */ + conf->date.format = (i & 1) ? "m/d/y" : "y/m/d" ; + if (conf->title != NULL) { + memset(title, 0, 1024); + margin = rand() % 5; + memset(title, ' ', margin); + strcpy(title + margin, conf->title); + memset(title + strlen(title), ' ', margin); + conf->title = title; + } +} |
