xref: /freebsd/contrib/bsddialog/lib/lib_util.c (revision 3e42d7194bdcd4fbfd781518258f919d6ac803d3)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2025 Alfonso Sabato Siciliano
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <stdarg.h>
29 #include <curses.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <wctype.h>
34 
35 #include "bsddialog.h"
36 #include "bsddialog_theme.h"
37 #include "lib_util.h"
38 
39 /*
40  * -1- Error and diagnostic
41  *
42  *	get_error_string();
43  *	set_error_string();
44  *	set_fmt_error_string();
45  *
46  * ----------------------------------------------------
47  * -2- (Unicode) Multicolumn character strings
48  *
49  *	alloc_mbstows();
50  *	mvwaddwch();
51  *	str_props();
52  *	strcols();
53  *
54  * ----------------------------------------------------
55  * -3- Buttons
56  *
57  * [static] buttons_min_width();
58  * [static] draw_button();
59  *          draw_buttons();
60  *          set_buttons(); (to call 1 time after prepare_dialog()).
61  *          shortcut_buttons();
62  *
63  * ----------------------------------------------------
64  * -4- (Auto) Sizing and (Auto) Position
65  *
66  * [static] widget_max_height(conf);
67  * [static] widget_max_width(struct bsddialog_conf *conf)
68  * [static] is_wtext_attr();
69  * [static] text_properties();
70  * [static] text_autosize();
71  * [static] text_size();
72  * [static] widget_min_height(conf, htext, hnotext, bool buttons);
73  * [static] widget_min_width(conf, wtext, minw, buttons);
74  *          set_widget_size();
75  *          set_widget_autosize();  (not for all dialogs).
76  *          widget_checksize();     (not for all dialogs).
77  *          set_widget_position();
78  *          dialog_size_position(struct dialog); (not for all dialogs).
79  *
80  * ----------------------------------------------------
81  * -5- (Dialog) Widget components and utils
82  *
83  *	hide_dialog(struct dialog);
84  *	f1help_dialog(conf);
85  *	draw_borders(conf, win, elev);
86  *	update_box(conf, win, y, x, h, w, elev);
87  *	rtextpad(); (helper for pnoutrefresh(textpad)).
88  *
89  * ----------------------------------------------------
90  * -6- Dialog init/build, update/draw, destroy
91  *
92  *          end_dialog(struct dialog);
93  * [static] check_set_wtext_attr();
94  * [static] print_string(); (word wrapping).
95  * [static] print_textpad();
96  *          draw_dialog(struct dialog);
97  *          prepare_dialog(struct dialog);
98  */
99 
100 /*
101  * -1- Error and diagnostic
102  */
103 #define ERRBUFLEN    1024
104 
105 static char errorbuffer[ERRBUFLEN];
106 
get_error_string(void)107 const char *get_error_string(void)
108 {
109 	return (errorbuffer);
110 }
111 
set_error_string(const char * str)112 void set_error_string(const char *str)
113 {
114 	strncpy(errorbuffer, str, ERRBUFLEN-1);
115 }
116 
set_fmt_error_string(const char * fmt,...)117 void set_fmt_error_string(const char *fmt, ...)
118 {
119    va_list arg_ptr;
120 
121    va_start(arg_ptr, fmt);
122    vsnprintf(errorbuffer, ERRBUFLEN-1, fmt, arg_ptr);
123    va_end(arg_ptr);
124 }
125 
126 /*
127  * -2- (Unicode) Multicolumn character strings
128  */
alloc_mbstows(const char * mbstring)129 wchar_t* alloc_mbstows(const char *mbstring)
130 {
131 	size_t charlen, nchar;
132 	mbstate_t mbs;
133 	const char *pmbstring;
134 	wchar_t *wstring;
135 
136 	nchar = 1;
137 	pmbstring = mbstring;
138 	memset(&mbs, 0, sizeof(mbs));
139 	while ((charlen = mbrlen(pmbstring, MB_CUR_MAX, &mbs)) != 0 &&
140 	    charlen != (size_t)-1 && charlen != (size_t)-2) {
141 		pmbstring += charlen;
142 		nchar++;
143 	}
144 
145 	if ((wstring = calloc(nchar, sizeof(wchar_t))) == NULL)
146 		return (NULL);
147 	mbstowcs(wstring, mbstring, nchar);
148 
149 	return (wstring);
150 }
151 
mvwaddwch(WINDOW * w,int y,int x,wchar_t wch)152 void mvwaddwch(WINDOW *w, int y, int x, wchar_t wch)
153 {
154 	wchar_t ws[2];
155 
156 	ws[0] = wch;
157 	ws[1] = L'\0';
158 	mvwaddwstr(w, y, x, ws);
159 }
160 
str_props(const char * mbstring,unsigned int * cols,bool * has_multi_col)161 int str_props(const char *mbstring, unsigned int *cols, bool *has_multi_col)
162 {
163 	bool multicol;
164 	int w;
165 	unsigned int ncol;
166 	size_t charlen, mb_cur_max;
167 	wchar_t wch;
168 	mbstate_t mbs;
169 
170 	multicol = false;
171 	mb_cur_max = MB_CUR_MAX;
172 	ncol = 0;
173 	memset(&mbs, 0, sizeof(mbs));
174 	while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 &&
175 	    charlen != (size_t)-1 && charlen != (size_t)-2) {
176 		if (mbtowc(&wch, mbstring, mb_cur_max) < 0)
177 			return (-1);
178 		w = (wch == L'\t') ? TABSIZE : wcwidth(wch);
179 		ncol += (w < 0) ? 0 : w;
180 		if (w > 1 && wch != L'\t')
181 			multicol = true;
182 		mbstring += charlen;
183 	}
184 
185 	if (cols != NULL)
186 		*cols = ncol;
187 	if (has_multi_col != NULL)
188 		*has_multi_col = multicol;
189 
190 	return (0);
191 }
192 
strcols(const char * mbstring)193 unsigned int strcols(const char *mbstring)
194 {
195 	int w;
196 	unsigned int ncol;
197 	size_t charlen, mb_cur_max;
198 	wchar_t wch;
199 	mbstate_t mbs;
200 
201 	mb_cur_max = MB_CUR_MAX;
202 	ncol = 0;
203 	memset(&mbs, 0, sizeof(mbs));
204 	while ((charlen = mbrlen(mbstring, mb_cur_max, &mbs)) != 0 &&
205 	    charlen != (size_t)-1 && charlen != (size_t)-2) {
206 		if (mbtowc(&wch, mbstring, mb_cur_max) < 0)
207 			return (0);
208 		w = (wch == L'\t') ? TABSIZE : wcwidth(wch);
209 		ncol += (w < 0) ? 0 : w;
210 		mbstring += charlen;
211 	}
212 
213 	return (ncol);
214 }
215 
216 /*
217  * -3- Buttons
218  */
buttons_min_width(struct buttons * bs)219 static int buttons_min_width(struct buttons *bs)
220 {
221 	unsigned int width;
222 
223 	width = bs->nbuttons * bs->sizebutton;
224 	if (bs->nbuttons > 0)
225 		width += (bs->nbuttons - 1) * t.button.minmargin;
226 
227 	return (width);
228 }
229 
230 static void
draw_button(WINDOW * window,int y,int x,int size,const char * text,wchar_t first,bool selected,bool shortcut)231 draw_button(WINDOW *window, int y, int x, int size, const char *text,
232     wchar_t first, bool selected, bool shortcut)
233 {
234 	int i, color_arrows, color_shortkey, color_button;
235 
236 	if (selected) {
237 		color_arrows = t.button.f_delimcolor;
238 		color_shortkey = t.button.f_shortcutcolor;
239 		color_button = t.button.f_color;
240 	} else {
241 		color_arrows = t.button.delimcolor;
242 		color_shortkey = t.button.shortcutcolor;
243 		color_button = t.button.color;
244 	}
245 
246 	wattron(window, color_arrows);
247 	mvwaddch(window, y, x, t.button.leftdelim);
248 	wattroff(window, color_arrows);
249 	wattron(window, color_button);
250 	for (i = 1; i < size - 1; i++)
251 		waddch(window, ' ');
252 	wattroff(window, color_button);
253 	wattron(window, color_arrows);
254 	mvwaddch(window, y, x + i, t.button.rightdelim);
255 	wattroff(window, color_arrows);
256 
257 	x = x + 1 + ((size - 2 - strcols(text))/2);
258 	wattron(window, color_button);
259 	mvwaddstr(window, y, x, text);
260 	wattroff(window, color_button);
261 
262 	if (shortcut) {
263 		wattron(window, color_shortkey);
264 		mvwaddwch(window, y, x, first);
265 		wattroff(window, color_shortkey);
266 	}
267 }
268 
draw_buttons(struct dialog * d)269 void draw_buttons(struct dialog *d)
270 {
271 	int i, x, startx, y;
272 	unsigned int newmargin, margin, wbuttons;
273 
274 	y = d->h - 2;
275 
276 	newmargin = d->w - BORDERS - (d->bs.nbuttons * d->bs.sizebutton);
277 	newmargin /= (d->bs.nbuttons + 1);
278 	newmargin = MIN(newmargin, t.button.maxmargin);
279 	if (newmargin == 0) {
280 		margin = t.button.minmargin;
281 		wbuttons = buttons_min_width(&d->bs);
282 	} else {
283 		margin = newmargin;
284 		wbuttons = d->bs.nbuttons * d->bs.sizebutton;
285 		wbuttons += (d->bs.nbuttons + 1) * margin;
286 	}
287 
288 	startx = d->w/2 - wbuttons/2 + newmargin;
289 	for (i = 0; i < (int)d->bs.nbuttons; i++) {
290 		x = i * (d->bs.sizebutton + margin);
291 		draw_button(d->widget, y, startx + x, d->bs.sizebutton,
292 		    d->bs.label[i], d->bs.first[i],  i == d->bs.curr,
293 		    d->bs.shortcut);
294 	}
295 }
296 
297 void
set_buttons(struct dialog * d,bool shortcut,const char * oklabel,const char * cancellabel)298 set_buttons(struct dialog *d, bool shortcut, const char *oklabel,
299     const char *cancellabel)
300 {
301 	int i;
302 #define SIZEBUTTON              8
303 #define DEFAULT_BUTTON_LABEL	OK_LABEL
304 #define DEFAULT_BUTTON_VALUE	BSDDIALOG_OK
305 	wchar_t first;
306 
307 	d->bs.nbuttons = 0;
308 	d->bs.curr = 0;
309 	d->bs.sizebutton = 0;
310 	d->bs.shortcut = shortcut;
311 
312 	if (d->conf->button.left1_label != NULL) {
313 		d->bs.label[d->bs.nbuttons] = d->conf->button.left1_label;
314 		d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT1;
315 		d->bs.nbuttons += 1;
316 	}
317 
318 	if (d->conf->button.left2_label != NULL) {
319 		d->bs.label[d->bs.nbuttons] = d->conf->button.left2_label;
320 		d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT2;
321 		d->bs.nbuttons += 1;
322 	}
323 
324 	if (d->conf->button.left3_label != NULL) {
325 		d->bs.label[d->bs.nbuttons] = d->conf->button.left3_label;
326 		d->bs.value[d->bs.nbuttons] = BSDDIALOG_LEFT3;
327 		d->bs.nbuttons += 1;
328 	}
329 
330 	if (oklabel != NULL && d->conf->button.without_ok == false) {
331 		d->bs.label[d->bs.nbuttons] = d->conf->button.ok_label != NULL ?
332 		    d->conf->button.ok_label : oklabel;
333 		d->bs.value[d->bs.nbuttons] = BSDDIALOG_OK;
334 		d->bs.nbuttons += 1;
335 	}
336 
337 	if (d->conf->button.with_extra) {
338 		d->bs.label[d->bs.nbuttons] = d->conf->button.extra_label != NULL ?
339 		    d->conf->button.extra_label : "Extra";
340 		d->bs.value[d->bs.nbuttons] = BSDDIALOG_EXTRA;
341 		d->bs.nbuttons += 1;
342 	}
343 
344 	if (cancellabel != NULL && d->conf->button.without_cancel == false) {
345 		d->bs.label[d->bs.nbuttons] = d->conf->button.cancel_label ?
346 		    d->conf->button.cancel_label : cancellabel;
347 		d->bs.value[d->bs.nbuttons] = BSDDIALOG_CANCEL;
348 		if (d->conf->button.default_cancel)
349 			d->bs.curr = d->bs.nbuttons;
350 		d->bs.nbuttons += 1;
351 	}
352 
353 	if (d->conf->button.with_help) {
354 		d->bs.label[d->bs.nbuttons] = d->conf->button.help_label != NULL ?
355 		    d->conf->button.help_label : "Help";
356 		d->bs.value[d->bs.nbuttons] = BSDDIALOG_HELP;
357 		d->bs.nbuttons += 1;
358 	}
359 
360 	if (d->conf->button.right1_label != NULL) {
361 		d->bs.label[d->bs.nbuttons] = d->conf->button.right1_label;
362 		d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT1;
363 		d->bs.nbuttons += 1;
364 	}
365 
366 	if (d->conf->button.right2_label != NULL) {
367 		d->bs.label[d->bs.nbuttons] = d->conf->button.right2_label;
368 		d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT2;
369 		d->bs.nbuttons += 1;
370 	}
371 
372 	if (d->conf->button.right3_label != NULL) {
373 		d->bs.label[d->bs.nbuttons] = d->conf->button.right3_label;
374 		d->bs.value[d->bs.nbuttons] = BSDDIALOG_RIGHT3;
375 		d->bs.nbuttons += 1;
376 	}
377 
378 	if (d->bs.nbuttons == 0) {
379 		d->bs.label[0] = DEFAULT_BUTTON_LABEL;
380 		d->bs.value[0] = DEFAULT_BUTTON_VALUE;
381 		d->bs.nbuttons = 1;
382 	}
383 
384 	for (i = 0; i < (int)d->bs.nbuttons; i++) {
385 		mbtowc(&first, d->bs.label[i], MB_CUR_MAX);
386 		d->bs.first[i] = first;
387 	}
388 
389 	if (d->conf->button.default_label != NULL) {
390 		for (i = 0; i < (int)d->bs.nbuttons; i++) {
391 			if (strcmp(d->conf->button.default_label,
392 			    d->bs.label[i]) == 0)
393 				d->bs.curr = i;
394 		}
395 	}
396 
397 	d->bs.sizebutton = MAX(SIZEBUTTON - 2, strcols(d->bs.label[0]));
398 	for (i = 1; i < (int)d->bs.nbuttons; i++)
399 		d->bs.sizebutton = MAX(d->bs.sizebutton, strcols(d->bs.label[i]));
400 	d->bs.sizebutton += 2;
401 }
402 
shortcut_buttons(wint_t key,struct buttons * bs)403 bool shortcut_buttons(wint_t key, struct buttons *bs)
404 {
405 	bool match;
406 	unsigned int i;
407 
408 	match = false;
409 	for (i = 0; i < bs->nbuttons; i++) {
410 		if (towlower(key) == towlower(bs->first[i])) {
411 			bs->curr = i;
412 			match = true;
413 			break;
414 		}
415 	}
416 
417 	return (match);
418 }
419 
420 /*
421  * -4- (Auto) Sizing and (Auto) Position
422  */
widget_max_height(struct bsddialog_conf * conf)423 static int widget_max_height(struct bsddialog_conf *conf)
424 {
425 	int maxheight;
426 
427 	maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.y : SCREENLINES;
428 	if (maxheight <= 0)
429 		RETURN_ERROR("Terminal too small, screen lines - shadow <= 0");
430 
431 	if (conf->y != BSDDIALOG_CENTER && conf->auto_topmargin > 0)
432 		RETURN_ERROR("conf.y > 0 and conf->auto_topmargin > 0");
433 	else if (conf->y == BSDDIALOG_CENTER) {
434 		maxheight -= conf->auto_topmargin;
435 		if (maxheight <= 0)
436 			RETURN_ERROR("Terminal too small, screen lines - top "
437 			    "margins <= 0");
438 	} else if (conf->y > 0) {
439 		maxheight -= conf->y;
440 		if (maxheight <= 0)
441 			RETURN_ERROR("Terminal too small, screen lines - "
442 			    "shadow - y <= 0");
443 	}
444 
445 	maxheight -= conf->auto_downmargin;
446 	if (maxheight <= 0)
447 		RETURN_ERROR("Terminal too small, screen lines - Down margins "
448 		    "<= 0");
449 
450 	return (maxheight);
451 }
452 
widget_max_width(struct bsddialog_conf * conf)453 static int widget_max_width(struct bsddialog_conf *conf)
454 {
455 	int maxwidth;
456 
457 	maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.x : SCREENCOLS;
458 	if (maxwidth <= 0)
459 		RETURN_ERROR("Terminal too small, screen cols - shadow <= 0");
460 
461 	if (conf->x > 0) {
462 		maxwidth -= conf->x;
463 		if (maxwidth <= 0)
464 			RETURN_ERROR("Terminal too small, screen cols - shadow "
465 			    "- x <= 0");
466 	}
467 
468 	return (maxwidth);
469 }
470 
is_wtext_attr(const wchar_t * wtext)471 static bool is_wtext_attr(const wchar_t *wtext)
472 {
473 	bool att;
474 
475 	if (wcsnlen(wtext, 3) < 3)
476 		return (false);
477 	if (wtext[0] != L'\\' || wtext[1] != L'Z')
478 		return (false);
479 
480 	att = wcschr(L"nbBdDkKrRsSuU01234567", wtext[2]) == NULL ? false : true;
481 
482 	return (att);
483 }
484 
485 #define NL  -1
486 #define WS  -2
487 #define TB  -3
488 
489 struct textproperties {
490 	int nword;
491 	int *words;
492 	uint8_t *wletters;
493 	int maxwordcols;
494 	int maxline;
495 	bool hasnewline;
496 };
497 
498 static int
text_properties(struct bsddialog_conf * conf,const char * text,struct textproperties * tp)499 text_properties(struct bsddialog_conf *conf, const char *text,
500     struct textproperties *tp)
501 {
502 	int i, l, currlinecols, maxwords, wtextlen, tablen, wordcols;
503 	wchar_t *wtext;
504 
505 	tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen;
506 
507 	maxwords = 1024;
508 	if ((tp->words = calloc(maxwords, sizeof(int))) == NULL)
509 		RETURN_ERROR("Cannot alloc memory for text autosize");
510 
511 	if ((wtext = alloc_mbstows(text)) == NULL)
512 		RETURN_ERROR("Cannot allocate/autosize text in wchar_t*");
513 	wtextlen = wcslen(wtext);
514 	if ((tp->wletters = calloc(wtextlen, sizeof(uint8_t))) == NULL)
515 		RETURN_ERROR("Cannot allocate wletters for text autosizing");
516 
517 	tp->nword = 0;
518 	tp->maxline = 0;
519 	tp->maxwordcols = 0;
520 	tp->hasnewline = false;
521 	currlinecols = 0;
522 	wordcols = 0;
523 	l = 0;
524 	for (i = 0; i < wtextlen; i++) {
525 		if (conf->text.escape && is_wtext_attr(wtext + i)) {
526 			i += 2; /* +1 for update statement */
527 			continue;
528 		}
529 
530 		if (tp->nword + 1 >= maxwords) {
531 			maxwords += 1024;
532 			tp->words = realloc(tp->words, maxwords * sizeof(int));
533 			if (tp->words == NULL)
534 				RETURN_ERROR("Cannot realloc memory for text "
535 				    "autosize");
536 		}
537 
538 		if (wcschr(L"\t\n  ", wtext[i]) != NULL) {
539 			tp->maxwordcols = MAX(wordcols, tp->maxwordcols);
540 
541 			if (wordcols != 0) {
542 				/* line */
543 				currlinecols += wordcols;
544 				/* word */
545 				tp->words[tp->nword] = wordcols;
546 				tp->nword += 1;
547 				wordcols = 0;
548 			}
549 
550 			switch (wtext[i]) {
551 			case L'\t':
552 				/* line */
553 				currlinecols += tablen;
554 				/* word */
555 				tp->words[tp->nword] = TB;
556 				break;
557 			case L'\n':
558 				/* line */
559 				tp->hasnewline = true;
560 				tp->maxline = MAX(tp->maxline, currlinecols);
561 				currlinecols = 0;
562 				/* word */
563 				tp->words[tp->nword] = NL;
564 				break;
565 			case L' ':
566 				/* line */
567 				currlinecols += 1;
568 				/* word */
569 				tp->words[tp->nword] = WS;
570 				break;
571 			}
572 			tp->nword += 1;
573 		} else {
574 			tp->wletters[l] = wcwidth(wtext[i]);
575 			wordcols += tp->wletters[l];
576 			l++;
577 		}
578 	}
579 	/* word */
580 	if (wordcols != 0) {
581 		tp->words[tp->nword] = wordcols;
582 		tp->nword += 1;
583 		tp->maxwordcols = MAX(wordcols, tp->maxwordcols);
584 	}
585 	/* line */
586 	tp->maxline = MAX(tp->maxline, currlinecols);
587 
588 	free(wtext);
589 
590 	return (0);
591 }
592 
593 static int
text_autosize(struct bsddialog_conf * conf,struct textproperties * tp,int maxrows,int mincols,bool increasecols,int * h,int * w)594 text_autosize(struct bsddialog_conf *conf, struct textproperties *tp,
595     int maxrows, int mincols, bool increasecols, int *h, int *w)
596 {
597 	int i, j, x, y, z, l, line, maxwidth, tablen;
598 
599 	maxwidth = widget_max_width(conf) - BORDERS - TEXTHMARGINS;
600 	tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen;
601 
602 	if (increasecols) {
603 		mincols = MAX(mincols, tp->maxwordcols);
604 		mincols = MAX(mincols,
605 		    (int)conf->auto_minwidth - BORDERS - TEXTHMARGINS);
606 		mincols = MIN(mincols, maxwidth);
607 	}
608 
609 	while (true) {
610 		x = 0;
611 		y = 1;
612 		line=0;
613 		l = 0;
614 		for (i = 0; i < tp->nword; i++) {
615 			switch (tp->words[i]) {
616 			case TB:
617 				for (j = 0; j < tablen; j++) {
618 					if (x >= mincols) {
619 						x = 0;
620 						y++;
621 					}
622 				x++;
623 				}
624 				break;
625 			case NL:
626 				y++;
627 				x = 0;
628 				break;
629 			case WS:
630 				x++;
631 				if (x >= mincols) {
632 					x = 0;
633 					y++;
634 				}
635 				break;
636 			default:
637 				if (tp->words[i] + x <= mincols) {
638 					x += tp->words[i];
639 					for (z = 0 ; z != tp->words[i]; l++ )
640 						z += tp->wletters[l];
641 				} else if (tp->words[i] <= mincols) {
642 					y++;
643 					x = tp->words[i];
644 					for (z = 0 ; z != tp->words[i]; l++ )
645 						z += tp->wletters[l];
646 				} else {
647 					for (j = tp->words[i]; j > 0; ) {
648 						y = (x == 0) ? y : y + 1;
649 						z = 0;
650 						while (z != j && z < mincols) {
651 							z += tp->wletters[l];
652 							l++;
653 						}
654 						x = z;
655 						line = MAX(line, x);
656 						j -= z;
657 					}
658 				}
659 			}
660 			line = MAX(line, x);
661 		}
662 
663 		if (increasecols == false)
664 			break;
665 		if (mincols >= maxwidth)
666 			break;
667 		if (line >= y * (int)conf->text.cols_per_row && y <= maxrows)
668 			break;
669 		mincols++;
670 	}
671 
672 	*h = (tp->nword == 0) ? 0 : y;
673 	*w = MIN(mincols, line); /* wtext can be less than mincols */
674 
675 	return (0);
676 }
677 
678 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)679 text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text,
680     struct buttons *bs, int rowsnotext, int startwtext, int *htext, int *wtext)
681 {
682 	bool changewtext;
683 	int wbuttons, maxhtext;
684 	struct textproperties tp;
685 
686 	wbuttons = 0;
687 	if (bs->nbuttons > 0)
688 		wbuttons = buttons_min_width(bs);
689 
690 	/* Rows */
691 	if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) {
692 		maxhtext = widget_max_height(conf) - BORDERS - rowsnotext;
693 	} else { /* fixed */
694 		maxhtext = rows - BORDERS - rowsnotext;
695 	}
696 	if (bs->nbuttons > 0)
697 		maxhtext -= 2;
698 	if (maxhtext <= 0)
699 		maxhtext = 1; /* text_autosize() computes always htext */
700 
701 	/* Cols */
702 	if (cols == BSDDIALOG_AUTOSIZE) {
703 		startwtext = MAX(startwtext, wbuttons - TEXTHMARGINS);
704 		changewtext = true;
705 	} else if (cols == BSDDIALOG_FULLSCREEN) {
706 		startwtext = widget_max_width(conf) - BORDERS - TEXTHMARGINS;
707 		changewtext = false;
708 	} else { /* fixed */
709 		startwtext = cols - BORDERS - TEXTHMARGINS;
710 		changewtext = false;
711 	}
712 
713 	if (startwtext <= 0 && changewtext)
714 		startwtext = 1;
715 
716 	/* Sizing calculation */
717 	if (text_properties(conf, text, &tp) != 0)
718 		return (BSDDIALOG_ERROR);
719 	if (tp.nword > 0 && startwtext <= 0)
720 		RETURN_FMTERROR("(fixed cols or fullscreen) "
721 		    "needed at least %d cols to draw text",
722 		    BORDERS + TEXTHMARGINS + 1);
723 	if (text_autosize(conf, &tp, maxhtext, startwtext, changewtext, htext,
724 	    wtext) != 0)
725 		return (BSDDIALOG_ERROR);
726 
727 	free(tp.words);
728 	free(tp.wletters);
729 
730 	return (0);
731 }
732 
733 static int
widget_min_height(struct bsddialog_conf * conf,int htext,int hnotext,bool withbuttons)734 widget_min_height(struct bsddialog_conf *conf, int htext, int hnotext,
735     bool withbuttons)
736 {
737 	int min;
738 
739 	/* dialog borders */
740 	min = BORDERS;
741 
742 	/* text */
743 	min += htext;
744 
745 	/* specific widget lines without text */
746 	min += hnotext;
747 
748 	/* buttons */
749 	if (withbuttons)
750 		min += HBUTTONS; /* buttons and their up-border */
751 
752 	/* conf.auto_minheight */
753 	min = MAX(min, (int)conf->auto_minheight);
754 
755 	return (min);
756 }
757 
758 static int
widget_min_width(struct bsddialog_conf * conf,int wtext,int minwidget,struct buttons * bs)759 widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget,
760     struct buttons *bs)
761 
762 {
763 	int min, delimtitle, wbottomtitle, wtitle;
764 
765 	min = 0;
766 
767 	/* buttons */
768 	if (bs->nbuttons > 0)
769 		min += buttons_min_width(bs);
770 
771 	/* text */
772 	if (wtext > 0)
773 		min = MAX(min, wtext + TEXTHMARGINS);
774 
775 	/* specific widget min width */
776 	min = MAX(min, minwidget);
777 
778 	/* title */
779 	if (conf->title != NULL) {
780 		delimtitle = t.dialog.delimtitle ? 2 : 0;
781 		wtitle = strcols(conf->title);
782 		min = MAX(min, wtitle + 2 + delimtitle);
783 	}
784 
785 	/* bottom title */
786 	if (conf->bottomtitle != NULL) {
787 		wbottomtitle = strcols(conf->bottomtitle);
788 		min = MAX(min, wbottomtitle + 4);
789 	}
790 
791 	/* dialog borders */
792 	min += BORDERS;
793 	/* conf.auto_minwidth */
794 	min = MAX(min, (int)conf->auto_minwidth);
795 
796 	return (min);
797 }
798 
799 int
set_widget_size(struct bsddialog_conf * conf,int rows,int cols,int * h,int * w)800 set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w)
801 {
802 	int maxheight, maxwidth;
803 
804 	if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR)
805 		return (BSDDIALOG_ERROR);
806 
807 	if (rows == BSDDIALOG_FULLSCREEN)
808 		*h = maxheight;
809 	else if (rows < BSDDIALOG_FULLSCREEN)
810 		RETURN_ERROR("Negative (less than -1) height");
811 	else if (rows > BSDDIALOG_AUTOSIZE) /* fixed rows */
812 		*h = MIN(rows, maxheight); /* rows is at most maxheight */
813 	/* rows == AUTOSIZE: each widget has to set its size */
814 
815 	if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR)
816 		return (BSDDIALOG_ERROR);
817 
818 	if (cols == BSDDIALOG_FULLSCREEN)
819 		*w = maxwidth;
820 	else if (cols < BSDDIALOG_FULLSCREEN)
821 		RETURN_ERROR("Negative (less than -1) width");
822 	else if (cols > BSDDIALOG_AUTOSIZE) /* fixed cols */
823 		*w = MIN(cols, maxwidth); /* cols is at most maxwidth */
824 	/* cols == AUTOSIZE: each widget has to set its size */
825 
826 	return (0);
827 }
828 
829 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)830 set_widget_autosize(struct bsddialog_conf *conf, int rows, int cols, int *h,
831     int *w, const char *text, int *rowstext, struct buttons *bs, int hnotext,
832     int minw)
833 {
834 	int htext, wtext;
835 
836 	if (rows == BSDDIALOG_AUTOSIZE || cols == BSDDIALOG_AUTOSIZE ||
837 	    rowstext != NULL) {
838 		if (text_size(conf, rows, cols, text, bs, hnotext, minw,
839 		    &htext, &wtext) != 0)
840 			return (BSDDIALOG_ERROR);
841 		if (rowstext != NULL)
842 			*rowstext = htext;
843 	}
844 
845 	if (rows == BSDDIALOG_AUTOSIZE) {
846 		*h = widget_min_height(conf, htext, hnotext, bs->nbuttons > 0);
847 		*h = MIN(*h, widget_max_height(conf));
848 	}
849 
850 	if (cols == BSDDIALOG_AUTOSIZE) {
851 		*w = widget_min_width(conf, wtext, minw, bs);
852 		*w = MIN(*w, widget_max_width(conf));
853 	}
854 
855 	return (0);
856 }
857 
widget_checksize(int h,int w,struct buttons * bs,int hnotext,int minw)858 int widget_checksize(int h, int w, struct buttons *bs, int hnotext, int minw)
859 {
860 	int minheight, minwidth;
861 
862 	minheight = BORDERS + hnotext;
863 	if (bs->nbuttons > 0)
864 		minheight += HBUTTONS;
865 	if (h < minheight)
866 		RETURN_FMTERROR("Current rows: %d, needed at least: %d",
867 		    h, minheight);
868 
869 	minwidth = 0;
870 	if (bs->nbuttons > 0)
871 		minwidth = buttons_min_width(bs);
872 	minwidth = MAX(minwidth, minw);
873 	minwidth += BORDERS;
874 	if (w < minwidth)
875 		RETURN_FMTERROR("Current cols: %d, nedeed at least %d",
876 		    w, minwidth);
877 
878 	return (0);
879 }
880 
881 int
set_widget_position(struct bsddialog_conf * conf,int * y,int * x,int h,int w)882 set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w)
883 {
884 	int hshadow = conf->shadow ? (int)t.shadow.y : 0;
885 	int wshadow = conf->shadow ? (int)t.shadow.x : 0;
886 
887 	if (conf->y == BSDDIALOG_CENTER) {
888 		*y = SCREENLINES/2 - (h + hshadow)/2;
889 		if (*y < (int)conf->auto_topmargin)
890 			*y = conf->auto_topmargin;
891 		if (*y + h + hshadow > SCREENLINES - (int)conf->auto_downmargin)
892 			*y = SCREENLINES - h - hshadow - conf->auto_downmargin;
893 	}
894 	else if (conf->y < BSDDIALOG_CENTER)
895 		RETURN_ERROR("Negative begin y (less than -1)");
896 	else if (conf->y >= SCREENLINES)
897 		RETURN_ERROR("Begin Y under the terminal");
898 	else
899 		*y = conf->y;
900 
901 	if (*y + h + hshadow > SCREENLINES)
902 		RETURN_ERROR("The lower of the box under the terminal "
903 		    "(begin Y + height (+ shadow) > terminal lines)");
904 
905 
906 	if (conf->x == BSDDIALOG_CENTER)
907 		*x = SCREENCOLS/2 - (w + wshadow)/2;
908 	else if (conf->x < BSDDIALOG_CENTER)
909 		RETURN_ERROR("Negative begin x (less than -1)");
910 	else if (conf->x >= SCREENCOLS)
911 		RETURN_ERROR("Begin X over the right of the terminal");
912 	else
913 		*x = conf->x;
914 
915 	if ((*x + w + wshadow) > SCREENCOLS)
916 		RETURN_ERROR("The right of the box over the terminal "
917 		    "(begin X + width (+ shadow) > terminal cols)");
918 
919 	return (0);
920 }
921 
dialog_size_position(struct dialog * d,int hnotext,int minw,int * htext)922 int dialog_size_position(struct dialog *d, int hnotext, int minw, int *htext)
923 {
924 	if (set_widget_size(d->conf, d->rows, d->cols, &d->h, &d->w) != 0)
925 		return (BSDDIALOG_ERROR);
926 	if (set_widget_autosize(d->conf, d->rows, d->cols, &d->h, &d->w,
927 	    d->text, htext, &d->bs, hnotext, minw) != 0)
928 		return (BSDDIALOG_ERROR);
929 	if (widget_checksize(d->h, d->w, &d->bs, hnotext, minw) != 0)
930 		return (BSDDIALOG_ERROR);
931 	if (set_widget_position(d->conf, &d->y, &d->x, d->h, d->w) != 0)
932 		return (BSDDIALOG_ERROR);
933 
934 	return (0);
935 }
936 
937 /*
938  * -5- Widget components and utilities
939  */
hide_dialog(struct dialog * d)940 int hide_dialog(struct dialog *d)
941 {
942 	WINDOW *clear;
943 
944 	if ((clear = newwin(d->h, d->w, d->y, d->x)) == NULL)
945 		RETURN_ERROR("Cannot hide the widget");
946 	wbkgd(clear, t.screen.color);
947 	wrefresh(clear);
948 
949 	if (d->conf->shadow) {
950 		mvwin(clear, d->y + t.shadow.y, d->x + t.shadow.x);
951 		wrefresh(clear);
952 	}
953 
954 	delwin(clear);
955 
956 	return (0);
957 }
958 
f1help_dialog(struct bsddialog_conf * conf)959 int f1help_dialog(struct bsddialog_conf *conf)
960 {
961 	int output;
962 	struct bsddialog_conf hconf;
963 
964 	bsddialog_initconf(&hconf);
965 	hconf.title           = "HELP";
966 	hconf.button.ok_label = "EXIT";
967 	hconf.clear           = true;
968 	hconf.ascii_lines     = conf->ascii_lines;
969 	hconf.no_lines        = conf->no_lines;
970 	hconf.shadow          = conf->shadow;
971 	hconf.text.escape     = conf->text.escape;
972 
973 	output = BSDDIALOG_OK;
974 	if (conf->key.f1_message != NULL)
975 		output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0);
976 
977 	if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL)
978 		output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0);
979 
980 	return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0);
981 }
982 
draw_borders(struct bsddialog_conf * conf,WINDOW * win,enum elevation elev)983 void draw_borders(struct bsddialog_conf *conf, WINDOW *win, enum elevation elev)
984 {
985 	int h, w;
986 	int leftcolor, rightcolor;
987 	cchar_t *ls, *rs, *ts, *bs, *tl, *tr, *bl, *br;
988 	cchar_t hline, vline, corner;
989 
990 	if (conf->no_lines)
991 		return;
992 
993 	if (conf->ascii_lines) {
994 		setcchar(&hline, L"|", 0, 0, NULL);
995 		ls = rs = &hline;
996 		setcchar(&vline, L"-", 0, 0, NULL);
997 		ts = bs = &vline;
998 		setcchar(&corner, L"+", 0, 0, NULL);
999 		tl = tr = bl = br = &corner;
1000 	} else {
1001 		ls = rs = WACS_VLINE;
1002 		ts = bs = WACS_HLINE;
1003 		tl = WACS_ULCORNER;
1004 		tr = WACS_URCORNER;
1005 		bl = WACS_LLCORNER;
1006 		br = WACS_LRCORNER;
1007 	}
1008 
1009 	getmaxyx(win, h, w);
1010 	leftcolor = (elev == RAISED) ?
1011 	    t.dialog.lineraisecolor : t.dialog.linelowercolor;
1012 	rightcolor = (elev == RAISED) ?
1013 	    t.dialog.linelowercolor : t.dialog.lineraisecolor;
1014 
1015 	wattron(win, leftcolor);
1016 	wborder_set(win, ls, rs, ts, bs, tl, tr, bl, br);
1017 	wattroff(win, leftcolor);
1018 
1019 	wattron(win, rightcolor);
1020 	mvwadd_wch(win, 0, w-1, tr);
1021 	mvwvline_set(win, 1, w-1, rs, h-2);
1022 	mvwadd_wch(win, h-1, w-1, br);
1023 	mvwhline_set(win, h-1, 1, bs, w-2);
1024 	wattroff(win, rightcolor);
1025 }
1026 
1027 void
update_box(struct bsddialog_conf * conf,WINDOW * win,int y,int x,int h,int w,enum elevation elev)1028 update_box(struct bsddialog_conf *conf, WINDOW *win, int y, int x, int h, int w,
1029     enum elevation elev)
1030 {
1031 	wclear(win);
1032 	wresize(win, h, w);
1033 	mvwin(win, y, x);
1034 	draw_borders(conf, win, elev);
1035 }
1036 
1037 void
rtextpad(struct dialog * d,int ytext,int xtext,int upnotext,int downnotext)1038 rtextpad(struct dialog *d, int ytext, int xtext, int upnotext, int downnotext)
1039 {
1040 	pnoutrefresh(d->textpad, ytext, xtext,
1041 	    d->y + BORDER + upnotext,
1042 	    d->x + BORDER + TEXTHMARGIN,
1043 	    d->y + d->h - 1 - downnotext - BORDER,
1044 	    d->x + d->w - TEXTHMARGIN - BORDER);
1045 }
1046 
1047 /*
1048  * -6- Dialog init/build, update/draw, destroy
1049  */
end_dialog(struct dialog * d)1050 void end_dialog(struct dialog *d)
1051 {
1052 	if (d->conf->sleep > 0)
1053 		sleep(d->conf->sleep);
1054 
1055 	delwin(d->textpad);
1056 	delwin(d->widget);
1057 	if (d->conf->shadow)
1058 		delwin(d->shadow);
1059 
1060 	if (d->conf->clear)
1061 		hide_dialog(d);
1062 
1063 	if (d->conf->get_height != NULL)
1064 		*d->conf->get_height = d->h;
1065 	if (d->conf->get_width != NULL)
1066 		*d->conf->get_width = d->w;
1067 }
1068 
check_set_wtext_attr(WINDOW * win,wchar_t * wtext)1069 static bool check_set_wtext_attr(WINDOW *win, wchar_t *wtext)
1070 {
1071 	enum bsddialog_color bg;
1072 
1073 	if (is_wtext_attr(wtext) == false)
1074 		return (false);
1075 
1076 	if ((wtext[2] >= L'0') && (wtext[2] <= L'7')) {
1077 		bsddialog_color_attrs(t.dialog.color, NULL, &bg, NULL);
1078 		wattron(win, bsddialog_color(wtext[2] - L'0', bg, 0));
1079 		return (true);
1080 	}
1081 
1082 	switch (wtext[2]) {
1083 	case L'n':
1084 		wattron(win, t.dialog.color);
1085 		wattrset(win, A_NORMAL);
1086 		break;
1087 	case L'b':
1088 		wattron(win, A_BOLD);
1089 		break;
1090 	case L'B':
1091 		wattroff(win, A_BOLD);
1092 		break;
1093 	case L'd':
1094 		wattron(win, A_DIM);
1095 		break;
1096 	case L'D':
1097 		wattroff(win, A_DIM);
1098 		break;
1099 	case L'k':
1100 		wattron(win, A_BLINK);
1101 		break;
1102 	case L'K':
1103 		wattroff(win, A_BLINK);
1104 		break;
1105 	case L'r':
1106 		wattron(win, A_REVERSE);
1107 		break;
1108 	case L'R':
1109 		wattroff(win, A_REVERSE);
1110 		break;
1111 	case L's':
1112 		wattron(win, A_STANDOUT);
1113 		break;
1114 	case L'S':
1115 		wattroff(win, A_STANDOUT);
1116 		break;
1117 	case L'u':
1118 		wattron(win, A_UNDERLINE);
1119 		break;
1120 	case L'U':
1121 		wattroff(win, A_UNDERLINE);
1122 		break;
1123 	}
1124 
1125 	return (true);
1126 }
1127 
1128 static void
print_string(WINDOW * win,int * rows,int cols,int * y,int * x,wchar_t * str,bool color)1129 print_string(WINDOW *win, int *rows, int cols, int *y, int *x, wchar_t *str,
1130     bool color)
1131 {
1132 	int charwidth, i, j, strlen, strwidth;
1133 	wchar_t ws[2];
1134 
1135 	ws[1] = L'\0';
1136 
1137 	strlen = wcslen(str);
1138 	if (color) {
1139 		strwidth = 0;
1140 		i=0;
1141 		while (i < strlen) {
1142 			if (is_wtext_attr(str+i) == false) {
1143 				strwidth += wcwidth(str[i]);
1144 				i++;
1145 			} else {
1146 				i += 3;
1147 			}
1148 		}
1149 	} else
1150 		strwidth = wcswidth(str, strlen);
1151 
1152 	i = 0;
1153 	while (i < strlen) {
1154 		if (*x + strwidth > cols) {
1155 			if (*x != 0)
1156 				*y = *y + 1;
1157 			if (*y >= *rows) {
1158 				*rows = *y + 1;
1159 				wresize(win, *rows, cols);
1160 			}
1161 			*x = 0;
1162 		}
1163 		j = *x;
1164 		while (i < strlen) {
1165 			if (color && check_set_wtext_attr(win, str+i)) {
1166 				i += 3;
1167 				continue;
1168 			}
1169 
1170 			charwidth = wcwidth(str[i]);
1171 			if (j + wcwidth(str[i]) > cols)
1172 				break;
1173 			/* inline mvwaddwch() for efficiency */
1174 			ws[0] = str[i];
1175 			mvwaddwstr(win, *y, j, ws);
1176 			strwidth -= charwidth;
1177 			j += charwidth;
1178 			*x = j;
1179 			i++;
1180 		}
1181 	}
1182 }
1183 
1184 static int
print_textpad(struct bsddialog_conf * conf,WINDOW * pad,const char * text)1185 print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text)
1186 {
1187 	bool loop;
1188 	int i, j, z, rows, cols, x, y, tablen;
1189 	wchar_t *wtext, *string;
1190 
1191 	if ((wtext = alloc_mbstows(text)) == NULL)
1192 		RETURN_ERROR("Cannot allocate/print text in wchar_t*");
1193 
1194 	if ((string = calloc(wcslen(wtext) + 1, sizeof(wchar_t))) == NULL)
1195 		RETURN_ERROR("Cannot build (analyze) text");
1196 
1197 	getmaxyx(pad, rows, cols);
1198 	tablen = (conf->text.tablen == 0) ? TABSIZE : (int)conf->text.tablen;
1199 
1200 	i = j = x = y = 0;
1201 	loop = true;
1202 	while (loop) {
1203 		string[j] = wtext[i];
1204 
1205 		if (wcschr(L"\n\t  ", string[j]) != NULL || string[j] == L'\0') {
1206 			string[j] = L'\0';
1207 			print_string(pad, &rows, cols, &y, &x, string,
1208 			    conf->text.escape);
1209 		}
1210 
1211 		switch (wtext[i]) {
1212 		case L'\0':
1213 			loop = false;
1214 			break;
1215 		case L'\n':
1216 			x = 0;
1217 			y++;
1218 			j = -1;
1219 			break;
1220 		case L'\t':
1221 			for (z = 0; z < tablen; z++) {
1222 				if (x >= cols) {
1223 					x = 0;
1224 					y++;
1225 				}
1226 				x++;
1227 			}
1228 			j = -1;
1229 			break;
1230 		case L' ':
1231 			x++;
1232 			if (x >= cols) {
1233 				x = 0;
1234 				y++;
1235 			}
1236 			j = -1;
1237 		}
1238 
1239 		if (y >= rows) {
1240 			rows = y + 1;
1241 			wresize(pad, rows, cols);
1242 		}
1243 
1244 		j++;
1245 		i++;
1246 	}
1247 
1248 	free(wtext);
1249 	free(string);
1250 
1251 	return (0);
1252 }
1253 
draw_dialog(struct dialog * d)1254 int draw_dialog(struct dialog *d)
1255 {
1256 	int wtitle, wbottomtitle;
1257 	cchar_t ts, ltee, rtee;
1258 
1259 	if (d->conf->ascii_lines) {
1260 		setcchar(&ts, L"-", 0, 0, NULL);
1261 		setcchar(&ltee, L"+", 0, 0,NULL);
1262 		setcchar(&rtee, L"+", 0, 0, NULL);
1263 	} else {
1264 		ts = *WACS_HLINE;
1265 		ltee = *WACS_LTEE;
1266 		rtee = *WACS_RTEE;
1267 	}
1268 
1269 	if (d->conf->shadow) {
1270 		wclear(d->shadow);
1271 		wresize(d->shadow, d->h, d->w);
1272 		mvwin(d->shadow, d->y + t.shadow.y, d->x + t.shadow.x);
1273 		wnoutrefresh(d->shadow);
1274 	}
1275 
1276 	wclear(d->widget);
1277 	wresize(d->widget, d->h, d->w);
1278 	mvwin(d->widget, d->y, d->x);
1279 	draw_borders(d->conf, d->widget, RAISED);
1280 
1281 	if (d->conf->title != NULL) {
1282 		if ((wtitle = strcols(d->conf->title)) < 0)
1283 			return (BSDDIALOG_ERROR);
1284 		if (t.dialog.delimtitle && d->conf->no_lines == false) {
1285 			wattron(d->widget, t.dialog.lineraisecolor);
1286 			mvwadd_wch(d->widget, 0, d->w/2 - wtitle/2 -1, &rtee);
1287 			wattroff(d->widget, t.dialog.lineraisecolor);
1288 		}
1289 		wattron(d->widget, t.dialog.titlecolor);
1290 		mvwaddstr(d->widget, 0, d->w/2 - wtitle/2, d->conf->title);
1291 		wattroff(d->widget, t.dialog.titlecolor);
1292 		if (t.dialog.delimtitle && d->conf->no_lines == false) {
1293 			wattron(d->widget, t.dialog.lineraisecolor);
1294 			wadd_wch(d->widget, &ltee);
1295 			wattroff(d->widget, t.dialog.lineraisecolor);
1296 		}
1297 	}
1298 
1299 	if (d->bs.nbuttons > 0) {
1300 		if (d->conf->no_lines == false) {
1301 			wattron(d->widget, t.dialog.lineraisecolor);
1302 			mvwadd_wch(d->widget, d->h-3, 0, &ltee);
1303 			mvwhline_set(d->widget, d->h-3, 1, &ts, d->w-2);
1304 			wattroff(d->widget, t.dialog.lineraisecolor);
1305 
1306 			wattron(d->widget, t.dialog.linelowercolor);
1307 			mvwadd_wch(d->widget, d->h-3, d->w-1, &rtee);
1308 			wattroff(d->widget, t.dialog.linelowercolor);
1309 		}
1310 		draw_buttons(d);
1311 	}
1312 
1313 	if (d->conf->bottomtitle != NULL) {
1314 		if ((wbottomtitle = strcols(d->conf->bottomtitle)) < 0)
1315 			return (BSDDIALOG_ERROR);
1316 		wattron(d->widget, t.dialog.bottomtitlecolor);
1317 		wmove(d->widget, d->h - 1, d->w/2 - wbottomtitle/2 - 1);
1318 		waddch(d->widget, ' ');
1319 		waddstr(d->widget, d->conf->bottomtitle);
1320 		waddch(d->widget, ' ');
1321 		wattroff(d->widget, t.dialog.bottomtitlecolor);
1322 	}
1323 
1324 	wnoutrefresh(d->widget);
1325 
1326 	wclear(d->textpad);
1327 	/* `infobox "" 0 2` fails but text is empty and textpad remains 1 1 */
1328 	wresize(d->textpad, 1, d->w - BORDERS - TEXTHMARGINS);
1329 
1330 	if (print_textpad(d->conf, d->textpad, d->text) != 0)
1331 		return (BSDDIALOG_ERROR);
1332 
1333 	d->built = true;
1334 
1335 	return (0);
1336 }
1337 
1338 int
prepare_dialog(struct bsddialog_conf * conf,const char * text,int rows,int cols,struct dialog * d)1339 prepare_dialog(struct bsddialog_conf *conf, const char *text, int rows,
1340     int cols, struct dialog *d)
1341 {
1342 	CHECK_PTR(conf);
1343 
1344 	d->built = false;
1345 	d->conf = conf;
1346 	d->rows = rows;
1347 	d->cols = cols;
1348 	d->text = CHECK_STR(text);
1349 	d->bs.nbuttons = 0;
1350 
1351 	if (d->conf->shadow) {
1352 		if ((d->shadow = newwin(1, 1, 1, 1)) == NULL)
1353 			RETURN_ERROR("Cannot build WINDOW shadow");
1354 		wbkgd(d->shadow, t.shadow.color);
1355 	}
1356 
1357 	if ((d->widget = newwin(1, 1, 1, 1)) == NULL)
1358 		RETURN_ERROR("Cannot build WINDOW widget");
1359 	wbkgd(d->widget, t.dialog.color);
1360 
1361 	/* fake for textpad */
1362 	if ((d->textpad = newpad(1, 1)) == NULL)
1363 		RETURN_ERROR("Cannot build the pad WINDOW for text");
1364 	wbkgd(d->textpad, t.dialog.color);
1365 
1366 	return (0);
1367 }