xref: /freebsd/contrib/bsddialog/lib/lib_util.c (revision 1f1e2261)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2021-2022 Alfonso Sabato Siciliano
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 #include <sys/param.h>
29 
30 #include <ctype.h>
31 #include <curses.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "bsddialog.h"
37 #include "bsddialog_theme.h"
38 #include "lib_util.h"
39 
40 #define TABLEN     4    /* Default tab len */
41 #define ERRBUFLEN  1024 /* Error buffer    */
42 
43 /* Error */
44 static char errorbuffer[ERRBUFLEN];
45 
46 const char *get_error_string(void)
47 {
48 	return (errorbuffer);
49 }
50 
51 void set_error_string(const char *str)
52 {
53 	strncpy(errorbuffer, str, ERRBUFLEN-1);
54 }
55 
56 /* Clear */
57 int hide_widget(int y, int x, int h, int w, bool withshadow)
58 {
59 	WINDOW *clear;
60 
61 	if ((clear = newwin(h, w, y + t.shadow.h, x + t.shadow.w)) == NULL)
62 		RETURN_ERROR("Cannot hide the widget");
63 	wbkgd(clear, t.screen.color);
64 
65 	if (withshadow)
66 		wrefresh(clear);
67 
68 	mvwin(clear, y, x);
69 	wrefresh(clear);
70 
71 	delwin(clear);
72 
73 	return (0);
74 }
75 
76 /* F1 help */
77 int f1help(struct bsddialog_conf *conf)
78 {
79 	int output;
80 	struct bsddialog_conf hconf;
81 
82 	bsddialog_initconf(&hconf);
83 	hconf.title           = "HELP";
84 	hconf.button.ok_label = "EXIT";
85 	hconf.clear           = true;
86 	hconf.ascii_lines     = conf->ascii_lines;
87 	hconf.no_lines        = conf->no_lines;
88 	hconf.shadow          = conf->shadow;
89 	hconf.text.highlight  = conf->text.highlight;
90 
91 	output = BSDDIALOG_OK;
92 	if (conf->key.f1_message != NULL)
93 		output = bsddialog_msgbox(&hconf, conf->key.f1_message, 0, 0);
94 
95 	if (output != BSDDIALOG_ERROR && conf->key.f1_file != NULL)
96 		output = bsddialog_textbox(&hconf, conf->key.f1_file, 0, 0);
97 
98 	return (output == BSDDIALOG_ERROR ? BSDDIALOG_ERROR : 0);
99 }
100 
101 /* Buttons */
102 static void
103 draw_button(WINDOW *window, int y, int x, int size, const char *text,
104     bool selected, bool shortcut)
105 {
106 	int i, color_arrows, color_shortkey, color_button;
107 
108 	if (selected) {
109 		color_arrows = t.button.f_delimcolor;
110 		color_shortkey = t.button.f_shortcutcolor;
111 		color_button = t.button.f_color;
112 	} else {
113 		color_arrows = t.button.delimcolor;
114 		color_shortkey = t.button.shortcutcolor;
115 		color_button = t.button.color;
116 	}
117 
118 	wattron(window, color_arrows);
119 	mvwaddch(window, y, x, t.button.leftdelim);
120 	wattroff(window, color_arrows);
121 	wattron(window, color_button);
122 	for (i = 1; i < size - 1; i++)
123 		waddch(window, ' ');
124 	wattroff(window, color_button);
125 	wattron(window, color_arrows);
126 	mvwaddch(window, y, x + i, t.button.rightdelim);
127 	wattroff(window, color_arrows);
128 
129 	x = x + 1 + ((size - 2 - strlen(text))/2);
130 	wattron(window, color_button);
131 	mvwaddstr(window, y, x, text);
132 	wattroff(window, color_button);
133 
134 	if (shortcut) {
135 		wattron(window, color_shortkey);
136 		mvwaddch(window, y, x, text[0]);
137 		wattroff(window, color_shortkey);
138 	}
139 }
140 
141 void
142 draw_buttons(WINDOW *window, struct buttons bs, bool shortcut)
143 {
144 	int i, x, startx, y, rows, cols;
145 
146 	getmaxyx(window, rows, cols);
147 	y = rows - 2;
148 
149 	startx = cols/2 - buttons_width(bs)/2;
150 
151 	for (i = 0; i < (int)bs.nbuttons; i++) {
152 		x = i * (bs.sizebutton + t.button.hmargin);
153 		draw_button(window, y, startx + x, bs.sizebutton, bs.label[i],
154 		    i == bs.curr, shortcut);
155 	}
156 }
157 
158 void
159 get_buttons(struct bsddialog_conf *conf, struct buttons *bs,
160     const char *yesoklabel, const char *nocancellabel)
161 {
162 	int i;
163 #define SIZEBUTTON              8
164 #define DEFAULT_BUTTON_LABEL	BUTTON_OK_LABEL
165 #define DEFAULT_BUTTON_VALUE	BSDDIALOG_OK
166 
167 	bs->nbuttons = 0;
168 	bs->curr = 0;
169 	bs->sizebutton = 0;
170 
171 	if (yesoklabel != NULL && conf->button.without_ok == false) {
172 		bs->label[0] = conf->button.ok_label != NULL ?
173 		    conf->button.ok_label : yesoklabel;
174 		bs->value[0] = BSDDIALOG_OK;
175 		bs->nbuttons += 1;
176 	}
177 
178 	if (conf->button.with_extra) {
179 		bs->label[bs->nbuttons] = conf->button.extra_label != NULL ?
180 		    conf->button.extra_label : "Extra";
181 		bs->value[bs->nbuttons] = BSDDIALOG_EXTRA;
182 		bs->nbuttons += 1;
183 	}
184 
185 	if (nocancellabel != NULL && conf->button.without_cancel == false) {
186 		bs->label[bs->nbuttons] = conf->button.cancel_label ?
187 		    conf->button.cancel_label : nocancellabel;
188 		bs->value[bs->nbuttons] = BSDDIALOG_CANCEL;
189 		if (conf->button.default_cancel)
190 			bs->curr = bs->nbuttons;
191 		bs->nbuttons += 1;
192 	}
193 
194 	if (conf->button.with_help) {
195 		bs->label[bs->nbuttons] = conf->button.help_label != NULL ?
196 		    conf->button.help_label : "Help";
197 		bs->value[bs->nbuttons] = BSDDIALOG_HELP;
198 		bs->nbuttons += 1;
199 	}
200 
201 	if (conf->button.generic1_label != NULL) {
202 		bs->label[bs->nbuttons] = conf->button.generic1_label;
203 		bs->value[bs->nbuttons] = BSDDIALOG_GENERIC1;
204 		bs->nbuttons += 1;
205 	}
206 
207 	if (conf->button.generic2_label != NULL) {
208 		bs->label[bs->nbuttons] = conf->button.generic2_label;
209 		bs->value[bs->nbuttons] = BSDDIALOG_GENERIC2;
210 		bs->nbuttons += 1;
211 	}
212 
213 	if (bs->nbuttons == 0) {
214 		bs->label[0] = DEFAULT_BUTTON_LABEL;
215 		bs->value[0] = DEFAULT_BUTTON_VALUE;
216 		bs->nbuttons = 1;
217 	}
218 
219 	if (conf->button.default_label != NULL) {
220 		for (i = 0; i < (int)bs->nbuttons; i++) {
221 			if (strcmp(conf->button.default_label,
222 			    bs->label[i]) == 0)
223 				bs->curr = i;
224 		}
225 	}
226 
227 	bs->sizebutton = MAX(SIZEBUTTON - 2, strlen(bs->label[0]));
228 	for (i = 1; i < (int)bs->nbuttons; i++)
229 		bs->sizebutton = MAX(bs->sizebutton, strlen(bs->label[i]));
230 	bs->sizebutton += 2;
231 }
232 
233 int buttons_width(struct buttons bs)
234 {
235 	unsigned int width;
236 
237 	width = bs.nbuttons * bs.sizebutton;
238 	if (bs.nbuttons > 0)
239 		width += (bs.nbuttons - 1) * t.button.hmargin;
240 
241 	return (width);
242 }
243 
244 bool shortcut_buttons(int key, struct buttons *bs)
245 {
246 	bool match;
247 	unsigned int i;
248 
249 	match = false;
250 	for (i = 0; i < bs->nbuttons; i++) {
251 		if (tolower(key) == tolower(bs->label[i][0])) {
252 			bs->curr = i;
253 			match = true;
254 			break;
255 		}
256 	}
257 
258 	return (match);
259 }
260 
261 /* Text */
262 static bool is_text_attr(const char *text)
263 {
264 	if (strnlen(text, 3) < 3)
265 		return (false);
266 
267 	if (text[0] != '\\' || text[1] != 'Z')
268 		return (false);
269 
270 	return (strchr("nbBrRuU01234567", text[2]) == NULL ? false : true);
271 }
272 
273 static bool check_set_text_attr(WINDOW *win, char *text)
274 {
275 	if (is_text_attr(text) == false)
276 		return (false);
277 
278 	if ((text[2] - '0') >= 0 && (text[2] - '0') < 8) {
279 		wattron(win, bsddialog_color(text[2] - '0', COLOR_WHITE, 0));
280 		return (true);
281 	}
282 
283 	switch (text[2]) {
284 	case 'n':
285 		wattron(win, t.dialog.color);
286 		wattrset(win, A_NORMAL);
287 		break;
288 	case 'b':
289 		wattron(win, A_BOLD);
290 		break;
291 	case 'B':
292 		wattroff(win, A_BOLD);
293 		break;
294 	case 'r':
295 		wattron(win, A_REVERSE);
296 		break;
297 	case 'R':
298 		wattroff(win, A_REVERSE);
299 		break;
300 	case 'u':
301 		wattron(win, A_UNDERLINE);
302 		break;
303 	case 'U':
304 		wattroff(win, A_UNDERLINE);
305 		break;
306 	}
307 
308 	return (true);
309 }
310 
311 static void
312 print_string(WINDOW *win, int *rows, int cols, int *y, int *x, char *str,
313     bool color)
314 {
315 	int i, j, len, reallen;
316 
317 	len = reallen = strlen(str);
318 	if (color) {
319 		i=0;
320 		while (i < len) {
321 			if (is_text_attr(str+i))
322 				reallen -= 3;
323 			i++;
324 		}
325 	}
326 
327 	i = 0;
328 	while (i < len) {
329 		if (*x + reallen > cols) {
330 			*y = (*x != 0 ? *y+1 : *y);
331 			if (*y >= *rows) {
332 				*rows = *y + 1;
333 				wresize(win, *rows, cols);
334 			}
335 			*x = 0;
336 		}
337 		j = *x;
338 		while (j < cols && i < len) {
339 			if (color && check_set_text_attr(win, str+i)) {
340 				i += 3;
341 			} else {
342 				mvwaddch(win, *y, j, str[i]);
343 				i++;
344 				reallen--;
345 				j++;
346 				*x = j;
347 			}
348 		}
349 	}
350 }
351 
352 static int
353 print_textpad(struct bsddialog_conf *conf, WINDOW *pad, const char *text)
354 {
355 	bool loop;
356 	int i, j, z, rows, cols, x, y, tablen;
357 	char *string;
358 
359 	if ((string = malloc(strlen(text) + 1)) == NULL)
360 		RETURN_ERROR("Cannot build (analyze) text");
361 
362 	getmaxyx(pad, rows, cols);
363 	tablen = (conf->text.tablen == 0) ? TABLEN : (int)conf->text.tablen;
364 
365 	i = j = x = y = 0;
366 	loop = true;
367 	while (loop) {
368 		string[j] = text[i];
369 
370 		if (strchr("\n\t  ", string[j]) != NULL || string[j] == '\0') {
371 			string[j] = '\0';
372 			print_string(pad, &rows, cols, &y, &x, string,
373 			    conf->text.highlight);
374 		}
375 
376 		switch (text[i]) {
377 		case '\0':
378 			loop = false;
379 			break;
380 		case '\n':
381 			x = 0;
382 			y++;
383 			j = -1;
384 			break;
385 		case '\t':
386 			for (z = 0; z < tablen; z++) {
387 				if (x >= cols) {
388 					x = 0;
389 					y++;
390 				}
391 				x++;
392 			}
393 			j = -1;
394 			break;
395 		case ' ':
396 			x++;
397 			if (x >= cols) {
398 				x = 0;
399 				y++;
400 			}
401 			j = -1;
402 		}
403 
404 		if (y >= rows) {
405 			rows = y + 1;
406 			wresize(pad, rows, cols);
407 		}
408 
409 		j++;
410 		i++;
411 	}
412 
413 	free(string);
414 
415 	return (0);
416 }
417 
418 /* Autosize */
419 static int
420 text_autosize(struct bsddialog_conf *conf, const char *text, int maxrows,
421     int mincols, bool increasecols, int *h, int *w)
422 {
423 	int i, j, z, x, y;
424 	int tablen, wordlen, maxwordlen, nword, maxwords, line, maxwidth;
425 	int *words;
426 #define NL -1
427 #define WS -2
428 
429 	maxwords = 1024;
430 	if ((words = calloc(maxwords, sizeof(int))) == NULL)
431 		RETURN_ERROR("Cannot alloc memory for text autosize");
432 
433 	tablen = (conf->text.tablen == 0) ? TABLEN : (int)conf->text.tablen;
434 	maxwidth = widget_max_width(conf) - HBORDERS - TEXTHMARGINS;
435 
436 	nword = 0;
437 	wordlen = 0;
438 	maxwordlen = 0;
439 	i=0;
440 	while (true) {
441 		if (conf->text.highlight && is_text_attr(text + i)) {
442 			i += 3;
443 			continue;
444 		}
445 
446 		if (nword + tablen + 1 >= maxwords) {
447 			maxwords += 1024;
448 			words = realloc(words, maxwords * sizeof(int));
449 			if (words == NULL)
450 				RETURN_ERROR("Cannot realloc memory for text "
451 				    "autosize");
452 		}
453 
454 		if (text[i] == '\0') {
455 			words[nword] = wordlen;
456 			maxwordlen = MAX(wordlen, maxwordlen);
457 			break;
458 		}
459 
460 		if (strchr("\t\n  ", text[i]) != NULL) {
461 			maxwordlen = MAX(wordlen, maxwordlen);
462 
463 			if (wordlen != 0) {
464 				words[nword] = wordlen;
465 				nword++;
466 				wordlen = 0;
467 			}
468 
469 			if (text[i] == '\t') {
470 				for (j = 0; j < tablen; j++)
471 					words[nword + j] = 1;
472 				nword += tablen;
473 			} else {
474 				words[nword] = text[i] == '\n' ? NL : WS;
475 				nword++;
476 			}
477 		}
478 		else
479 			wordlen++;
480 
481 		i++;
482 	}
483 
484 	if (increasecols) {
485 		mincols = MAX(mincols, maxwordlen);
486 		mincols = MAX(mincols,
487 		    (int)conf->auto_minwidth - HBORDERS - TEXTHMARGINS);
488 		mincols = MIN(mincols, maxwidth);
489 	}
490 
491 	while (true) {
492 		x = 0;
493 		y = 1;
494 		line=0;
495 		for (i = 0; i <= nword; i++) {
496 			if (words[i] == NL) {
497 				y++;
498 				x = 0;
499 			}
500 			else if (words[i] == WS) {
501 				x++;
502 				if (x >= mincols) {
503 					x = 0;
504 					y++;
505 				}
506 			}
507 			else {
508 				if (words[i] + x <= mincols)
509 					x += words[i];
510 				else {
511 					for (z = words[i]; z > 0; ) {
512 						y++;
513 						x = MIN(mincols, z);
514 						z -= x;
515 					}
516 				}
517 			}
518 			line = MAX(line, x);
519 		}
520 
521 		if (increasecols == false)
522 			break;
523 		if (y <= maxrows || mincols >= maxwidth)
524 			break;
525 		mincols++;
526 	}
527 
528 	*h = (nword == 0 && words[0] == 0) ? 0 : y;
529 	*w = MIN(mincols, line); /* wtext can be less than mincols */
530 
531 	free(words);
532 
533 	return (0);
534 }
535 
536 int
537 text_size(struct bsddialog_conf *conf, int rows, int cols, const char *text,
538     struct buttons *bs, int rowsnotext, int startwtext, int *htext, int *wtext)
539 {
540 	int wbuttons, maxhtext;
541 	bool changewtext;
542 
543 	wbuttons = 0;
544 	if (bs != NULL)
545 		wbuttons = buttons_width(*bs);
546 
547 	if (cols == BSDDIALOG_AUTOSIZE) {
548 		startwtext = MAX(startwtext, wbuttons - TEXTHMARGINS);
549 		changewtext = true;
550 	} else if (cols == BSDDIALOG_FULLSCREEN) {
551 		startwtext = widget_max_width(conf) - VBORDERS - TEXTHMARGINS;
552 		changewtext = false;
553 	} else { /* fixed */
554 		startwtext = cols - VBORDERS - TEXTHMARGINS;
555 		changewtext = false;
556 	}
557 
558 	if (rows == BSDDIALOG_AUTOSIZE || rows == BSDDIALOG_FULLSCREEN) {
559 		maxhtext = widget_max_height(conf) - VBORDERS - rowsnotext;
560 		if (bs != NULL)
561 			maxhtext -= 2;
562 	} else { /* fixed */
563 		maxhtext = rows - VBORDERS - rowsnotext;
564 		if (bs != NULL)
565 			maxhtext -= 2;
566 	}
567 
568 	if (startwtext <= 0 && changewtext)
569 		startwtext = 1;
570 	if (maxhtext <= 0 || startwtext <= 0) {
571 		*htext = *wtext = 0;
572 		return (0);
573 	}
574 
575 	if (text_autosize(conf, text, maxhtext, startwtext, changewtext,
576 	    htext, wtext) != 0)
577 		return (BSDDIALOG_ERROR);
578 
579 	return (0);
580 }
581 
582 int widget_max_height(struct bsddialog_conf *conf)
583 {
584 	int maxheight;
585 
586 	maxheight = conf->shadow ? SCREENLINES - (int)t.shadow.h : SCREENLINES;
587 	if (maxheight <= 0)
588 		RETURN_ERROR("Terminal too small, screen lines - shadow <= 0");
589 
590 	if (conf->y > 0) {
591 		maxheight -= conf->y;
592 		if (maxheight <= 0)
593 			RETURN_ERROR("Terminal too small, screen lines - "
594 			    "shadow - y <= 0");
595 	}
596 
597 	return (maxheight);
598 }
599 
600 int widget_max_width(struct bsddialog_conf *conf)
601 {
602 	int maxwidth;
603 
604 	maxwidth = conf->shadow ? SCREENCOLS - (int)t.shadow.w : SCREENCOLS;
605 	if (maxwidth <= 0)
606 		RETURN_ERROR("Terminal too small, screen cols - shadow <= 0");
607 
608 	if (conf->x > 0) {
609 		maxwidth -= conf->x;
610 		if (maxwidth <= 0)
611 			RETURN_ERROR("Terminal too small, screen cols - shadow "
612 			    "- x <= 0");
613 	}
614 
615 	return (maxwidth);
616 }
617 
618 int
619 widget_min_height(struct bsddialog_conf *conf, int htext, int minwidget,
620     bool withbuttons)
621 {
622 	int min;
623 
624 	min = 0;
625 
626 	/* buttons */
627 	if (withbuttons)
628 		min += 2; /* buttons and border */
629 
630 	/* text */
631 	min += htext;
632 
633 	/* specific widget min height */
634 	min += minwidget;
635 
636 	/* dialog borders */
637 	min += HBORDERS;
638 	/* conf.auto_minheight */
639 	min = MAX(min, (int)conf->auto_minheight);
640 	/* avoid terminal overflow */
641 	min = MIN(min, widget_max_height(conf));
642 
643 	return (min);
644 }
645 
646 int
647 widget_min_width(struct bsddialog_conf *conf, int wtext, int minwidget,
648     struct buttons *bs)
649 
650 {
651 	int min, delimtitle;
652 
653 	min = 0;
654 
655 	/* buttons */
656 	if (bs != NULL)
657 		min += buttons_width(*bs);
658 
659 	/* text */
660 	if (wtext > 0)
661 		min = MAX(min, wtext + TEXTHMARGINS);
662 
663 	/* specific widget min width */
664 	min = MAX(min, minwidget);
665 
666 	/* title */
667 	if (conf->title != NULL) {
668 		delimtitle = t.dialog.delimtitle ? 2 : 0;
669 		min = MAX(min, (int)strlen(conf->title) + 2 + delimtitle);
670 	}
671 
672 	/* bottom title */
673 	if (conf->bottomtitle != NULL)
674 		min = MAX(min, (int)strlen(conf->bottomtitle) + 4);
675 
676 	/* dialog borders */
677 	min += VBORDERS;
678 	/* conf.auto_minwidth */
679 	min = MAX(min, (int)conf->auto_minwidth);
680 	/* avoid terminal overflow */
681 	min = MIN(min, widget_max_width(conf));
682 
683 	return (min);
684 }
685 
686 int
687 set_widget_size(struct bsddialog_conf *conf, int rows, int cols, int *h, int *w)
688 {
689 	int maxheight, maxwidth;
690 
691 	if ((maxheight = widget_max_height(conf)) == BSDDIALOG_ERROR)
692 		return (BSDDIALOG_ERROR);
693 
694 	if (rows == BSDDIALOG_FULLSCREEN)
695 		*h = maxheight;
696 	else if (rows < BSDDIALOG_FULLSCREEN)
697 		RETURN_ERROR("Negative (less than -1) height");
698 	else if (rows > BSDDIALOG_AUTOSIZE) {
699 		if ((*h = rows) > maxheight)
700 			RETURN_ERROR("Height too big (> terminal height - "
701 			    "shadow)");
702 	}
703 	/* rows == AUTOSIZE: each widget has to set its size */
704 
705 	if ((maxwidth = widget_max_width(conf)) == BSDDIALOG_ERROR)
706 		return (BSDDIALOG_ERROR);
707 
708 	if (cols == BSDDIALOG_FULLSCREEN)
709 		*w = maxwidth;
710 	else if (cols < BSDDIALOG_FULLSCREEN)
711 		RETURN_ERROR("Negative (less than -1) width");
712 	else if (cols > BSDDIALOG_AUTOSIZE) {
713 		if ((*w = cols) > maxwidth)
714 			RETURN_ERROR("Width too big (> terminal width - "
715 			    "shadow)");
716 	}
717 	/* cols == AUTOSIZE: each widget has to set its size */
718 
719 	return (0);
720 }
721 
722 int
723 set_widget_position(struct bsddialog_conf *conf, int *y, int *x, int h, int w)
724 {
725 	if (conf->y == BSDDIALOG_CENTER)
726 		*y = SCREENLINES/2 - (h + t.shadow.h)/2;
727 	else if (conf->y < BSDDIALOG_CENTER)
728 		RETURN_ERROR("Negative begin y (less than -1)");
729 	else if (conf->y >= SCREENLINES)
730 		RETURN_ERROR("Begin Y under the terminal");
731 	else
732 		*y = conf->y;
733 
734 	if ((*y + h + (conf->shadow ? (int) t.shadow.h : 0)) > SCREENLINES)
735 		RETURN_ERROR("The lower of the box under the terminal "
736 		    "(begin Y + height (+ shadow) > terminal lines)");
737 
738 
739 	if (conf->x == BSDDIALOG_CENTER)
740 		*x = SCREENCOLS/2 - (w + t.shadow.w)/2;
741 	else if (conf->x < BSDDIALOG_CENTER)
742 		RETURN_ERROR("Negative begin x (less than -1)");
743 	else if (conf->x >= SCREENCOLS)
744 		RETURN_ERROR("Begin X over the right of the terminal");
745 	else
746 		*x = conf->x;
747 
748 	if ((*x + w + (conf->shadow ? (int) t.shadow.w : 0)) > SCREENCOLS)
749 		RETURN_ERROR("The right of the box over the terminal "
750 		    "(begin X + width (+ shadow) > terminal cols)");
751 
752 	return (0);
753 }
754 
755 /* Widgets builders */
756 void
757 draw_borders(struct bsddialog_conf *conf, WINDOW *win, int rows, int cols,
758     enum elevation elev)
759 {
760 	int leftcolor, rightcolor;
761 	int ls, rs, ts, bs, tl, tr, bl, br, ltee, rtee;
762 
763 	if (conf->no_lines)
764 		return;
765 
766 	if (conf->ascii_lines) {
767 		ls = rs = '|';
768 		ts = bs = '-';
769 		tl = tr = bl = br = ltee = rtee = '+';
770 	} else {
771 		ls = rs = ACS_VLINE;
772 		ts = bs = ACS_HLINE;
773 		tl = ACS_ULCORNER;
774 		tr = ACS_URCORNER;
775 		bl = ACS_LLCORNER;
776 		br = ACS_LRCORNER;
777 		ltee = ACS_LTEE;
778 		rtee = ACS_RTEE;
779 	}
780 
781 	leftcolor = elev == RAISED ?
782 	    t.dialog.lineraisecolor : t.dialog.linelowercolor;
783 	rightcolor = elev == RAISED ?
784 	    t.dialog.linelowercolor : t.dialog.lineraisecolor;
785 	wattron(win, leftcolor);
786 	wborder(win, ls, rs, ts, bs, tl, tr, bl, br);
787 	wattroff(win, leftcolor);
788 
789 	wattron(win, rightcolor);
790 	mvwaddch(win, 0, cols-1, tr);
791 	mvwvline(win, 1, cols-1, rs, rows-2);
792 	mvwaddch(win, rows-1, cols-1, br);
793 	mvwhline(win, rows-1, 1, bs, cols-2);
794 	wattroff(win, rightcolor);
795 }
796 
797 WINDOW *
798 new_boxed_window(struct bsddialog_conf *conf, int y, int x, int rows, int cols,
799     enum elevation elev)
800 {
801 	WINDOW *win;
802 
803 	if ((win = newwin(rows, cols, y, x)) == NULL) {
804 		set_error_string("Cannot build boxed window");
805 		return (NULL);
806 	}
807 
808 	wbkgd(win, t.dialog.color);
809 
810 	draw_borders(conf, win, rows, cols, elev);
811 
812 	return (win);
813 }
814 
815 static int
816 draw_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget,
817     WINDOW *textpad, const char *text, struct buttons *bs, bool shortcutbuttons)
818 {
819 	int h, w, ts, ltee, rtee;
820 
821 	ts = conf->ascii_lines ? '-' : ACS_HLINE;
822 	ltee = conf->ascii_lines ? '+' : ACS_LTEE;
823 	rtee = conf->ascii_lines ? '+' : ACS_RTEE;
824 
825 	getmaxyx(widget, h, w);
826 
827 	if (conf->shadow)
828 		wnoutrefresh(shadow);
829 
830 	draw_borders(conf, widget, h, w, RAISED);
831 
832 	if (conf->title != NULL) {
833 		if (t.dialog.delimtitle && conf->no_lines == false) {
834 			wattron(widget, t.dialog.lineraisecolor);
835 			mvwaddch(widget, 0, w/2-strlen(conf->title)/2-1, rtee);
836 			wattroff(widget, t.dialog.lineraisecolor);
837 		}
838 		wattron(widget, t.dialog.titlecolor);
839 		mvwaddstr(widget, 0, w/2 - strlen(conf->title)/2, conf->title);
840 		wattroff(widget, t.dialog.titlecolor);
841 		if (t.dialog.delimtitle && conf->no_lines == false) {
842 			wattron(widget, t.dialog.lineraisecolor);
843 			waddch(widget, ltee);
844 			wattroff(widget, t.dialog.lineraisecolor);
845 		}
846 	}
847 
848 	if (bs != NULL) {
849 		if (conf->no_lines == false) {
850 			wattron(widget, t.dialog.lineraisecolor);
851 			mvwaddch(widget, h-3, 0, ltee);
852 			mvwhline(widget, h-3, 1, ts, w-2);
853 			wattroff(widget, t.dialog.lineraisecolor);
854 
855 			wattron(widget, t.dialog.linelowercolor);
856 			mvwaddch(widget, h-3, w-1, rtee);
857 			wattroff(widget, t.dialog.linelowercolor);
858 		}
859 		draw_buttons(widget, *bs, shortcutbuttons);
860 	}
861 
862 	if (conf->bottomtitle != NULL) {
863 		wattron(widget, t.dialog.bottomtitlecolor);
864 		wmove(widget, h - 1, w/2 - strlen(conf->bottomtitle)/2 - 1);
865 		waddch(widget, ' ');
866 		waddstr(widget, conf->bottomtitle);
867 		waddch(widget, ' ');
868 		wattroff(widget, t.dialog.bottomtitlecolor);
869 	}
870 
871 	wnoutrefresh(widget);
872 
873 	if (textpad != NULL && text != NULL) /* textbox */
874 		if (print_textpad(conf, textpad, text) !=0)
875 			return (BSDDIALOG_ERROR);
876 
877 	return (0);
878 }
879 
880 int
881 update_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget,
882     int y, int x, int h, int w, WINDOW *textpad, const char *text,
883     struct buttons *bs, bool shortcutbuttons)
884 {
885 	int error;
886 
887 	if (conf->shadow) {
888 		wclear(shadow);
889 		mvwin(shadow, y + t.shadow.h, x + t.shadow.w);
890 		wresize(shadow, h, w);
891 	}
892 
893 	wclear(widget);
894 	mvwin(widget, y, x);
895 	wresize(widget, h, w);
896 
897 	if (textpad != NULL) {
898 		wclear(textpad);
899 		wresize(textpad, 1, w - HBORDERS - TEXTHMARGINS);
900 	}
901 
902 	error = draw_dialog(conf, shadow, widget, textpad, text, bs,
903 	    shortcutbuttons);
904 
905 	return (error);
906 }
907 
908 int
909 new_dialog(struct bsddialog_conf *conf, WINDOW **shadow, WINDOW **widget, int y,
910     int x, int h, int w, WINDOW **textpad, const char *text, struct buttons *bs,
911     bool shortcutbuttons)
912 {
913 	int error;
914 
915 	if (conf->shadow) {
916 		*shadow = newwin(h, w, y + t.shadow.h, x + t.shadow.w);
917 		if (*shadow == NULL)
918 			RETURN_ERROR("Cannot build shadow");
919 		wbkgd(*shadow, t.shadow.color);
920 	}
921 
922 	if ((*widget = new_boxed_window(conf, y, x, h, w, RAISED)) == NULL) {
923 		if (conf->shadow)
924 			delwin(*shadow);
925 		return (BSDDIALOG_ERROR);
926 	}
927 
928 	if (textpad != NULL && text != NULL) { /* textbox */
929 		*textpad = newpad(1, w - HBORDERS - TEXTHMARGINS);
930 		if (*textpad == NULL) {
931 			delwin(*widget);
932 			if (conf->shadow)
933 				delwin(*shadow);
934 			RETURN_ERROR("Cannot build the pad window for text");
935 		}
936 		wbkgd(*textpad, t.dialog.color);
937 	}
938 
939 	error = draw_dialog(conf, *shadow, *widget,
940 	    textpad == NULL ? NULL : *textpad, text, bs, shortcutbuttons);
941 
942 	return (error);
943 }
944 
945 void
946 end_dialog(struct bsddialog_conf *conf, WINDOW *shadow, WINDOW *widget,
947     WINDOW *textpad)
948 {
949 	int y, x, h, w;
950 
951 	getbegyx(widget, y, x);
952 	getmaxyx(widget, h, w);
953 
954 	if (conf->sleep > 0)
955 		sleep(conf->sleep);
956 
957 	if (textpad != NULL)
958 		delwin(textpad);
959 
960 	delwin(widget);
961 
962 	if (conf->shadow)
963 		delwin(shadow);
964 
965 	if (conf->clear)
966 		hide_widget(y, x, h, w, conf->shadow);
967 
968 	if (conf->get_height != NULL)
969 		*conf->get_height = h;
970 	if (conf->get_width != NULL)
971 		*conf->get_width = w;
972 }
973