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