kconfig/lxdialog/util.c
author Arnaud Lacombe <lacombar@gmail.com>
Tue Aug 03 06:17:51 2010 +0200 (2010-08-03)
changeset 2064 f5ebe8c429dc
parent 1 eeea35fbf182
child 2448 a103abae1560
permissions -rw-r--r--
libc/uClibc: add uClibc 0.9.30.3

This version has been released a couple of month ago, but it never reached
crosstool-ng tree. This may be linked to the fact that the current 0.9.30.2,
once patched, has nothing much different from 0.9.30.3, released.

I'm not including any patch with this upgrade, on purpose.

Signed-off-by: Arnaud Lacombe <lacombar@gmail.com>
     1 /*
     2  *  util.c
     3  *
     4  *  ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
     5  *  MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
     6  *
     7  *  This program is free software; you can redistribute it and/or
     8  *  modify it under the terms of the GNU General Public License
     9  *  as published by the Free Software Foundation; either version 2
    10  *  of the License, or (at your option) any later version.
    11  *
    12  *  This program is distributed in the hope that it will be useful,
    13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    15  *  GNU General Public License for more details.
    16  *
    17  *  You should have received a copy of the GNU General Public License
    18  *  along with this program; if not, write to the Free Software
    19  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
    20  */
    21 
    22 #include "dialog.h"
    23 
    24 struct dialog_info dlg;
    25 
    26 static void set_mono_theme(void)
    27 {
    28 	dlg.screen.atr = A_NORMAL;
    29 	dlg.shadow.atr = A_NORMAL;
    30 	dlg.dialog.atr = A_NORMAL;
    31 	dlg.title.atr = A_BOLD;
    32 	dlg.border.atr = A_NORMAL;
    33 	dlg.button_active.atr = A_REVERSE;
    34 	dlg.button_inactive.atr = A_DIM;
    35 	dlg.button_key_active.atr = A_REVERSE;
    36 	dlg.button_key_inactive.atr = A_BOLD;
    37 	dlg.button_label_active.atr = A_REVERSE;
    38 	dlg.button_label_inactive.atr = A_NORMAL;
    39 	dlg.inputbox.atr = A_NORMAL;
    40 	dlg.inputbox_border.atr = A_NORMAL;
    41 	dlg.searchbox.atr = A_NORMAL;
    42 	dlg.searchbox_title.atr = A_BOLD;
    43 	dlg.searchbox_border.atr = A_NORMAL;
    44 	dlg.position_indicator.atr = A_BOLD;
    45 	dlg.menubox.atr = A_NORMAL;
    46 	dlg.menubox_border.atr = A_NORMAL;
    47 	dlg.item.atr = A_NORMAL;
    48 	dlg.item_selected.atr = A_REVERSE;
    49 	dlg.tag.atr = A_BOLD;
    50 	dlg.tag_selected.atr = A_REVERSE;
    51 	dlg.tag_key.atr = A_BOLD;
    52 	dlg.tag_key_selected.atr = A_REVERSE;
    53 	dlg.check.atr = A_BOLD;
    54 	dlg.check_selected.atr = A_REVERSE;
    55 	dlg.uarrow.atr = A_BOLD;
    56 	dlg.darrow.atr = A_BOLD;
    57 }
    58 
    59 #define DLG_COLOR(dialog, f, b, h) \
    60 do {                               \
    61 	dlg.dialog.fg = (f);       \
    62 	dlg.dialog.bg = (b);       \
    63 	dlg.dialog.hl = (h);       \
    64 } while (0)
    65 
    66 static void set_classic_theme(void)
    67 {
    68 	DLG_COLOR(screen,                COLOR_CYAN,   COLOR_BLUE,   true);
    69 	DLG_COLOR(shadow,                COLOR_BLACK,  COLOR_BLACK,  true);
    70 	DLG_COLOR(dialog,                COLOR_BLACK,  COLOR_WHITE,  false);
    71 	DLG_COLOR(title,                 COLOR_YELLOW, COLOR_WHITE,  true);
    72 	DLG_COLOR(border,                COLOR_WHITE,  COLOR_WHITE,  true);
    73 	DLG_COLOR(button_active,         COLOR_WHITE,  COLOR_BLUE,   true);
    74 	DLG_COLOR(button_inactive,       COLOR_BLACK,  COLOR_WHITE,  false);
    75 	DLG_COLOR(button_key_active,     COLOR_WHITE,  COLOR_BLUE,   true);
    76 	DLG_COLOR(button_key_inactive,   COLOR_RED,    COLOR_WHITE,  false);
    77 	DLG_COLOR(button_label_active,   COLOR_YELLOW, COLOR_BLUE,   true);
    78 	DLG_COLOR(button_label_inactive, COLOR_BLACK,  COLOR_WHITE,  true);
    79 	DLG_COLOR(inputbox,              COLOR_BLACK,  COLOR_WHITE,  false);
    80 	DLG_COLOR(inputbox_border,       COLOR_BLACK,  COLOR_WHITE,  false);
    81 	DLG_COLOR(searchbox,             COLOR_BLACK,  COLOR_WHITE,  false);
    82 	DLG_COLOR(searchbox_title,       COLOR_YELLOW, COLOR_WHITE,  true);
    83 	DLG_COLOR(searchbox_border,      COLOR_WHITE,  COLOR_WHITE,  true);
    84 	DLG_COLOR(position_indicator,    COLOR_YELLOW, COLOR_WHITE,  true);
    85 	DLG_COLOR(menubox,               COLOR_BLACK,  COLOR_WHITE,  false);
    86 	DLG_COLOR(menubox_border,        COLOR_WHITE,  COLOR_WHITE,  true);
    87 	DLG_COLOR(item,                  COLOR_BLACK,  COLOR_WHITE,  false);
    88 	DLG_COLOR(item_selected,         COLOR_WHITE,  COLOR_BLUE,   true);
    89 	DLG_COLOR(tag,                   COLOR_YELLOW, COLOR_WHITE,  true);
    90 	DLG_COLOR(tag_selected,          COLOR_YELLOW, COLOR_BLUE,   true);
    91 	DLG_COLOR(tag_key,               COLOR_YELLOW, COLOR_WHITE,  true);
    92 	DLG_COLOR(tag_key_selected,      COLOR_YELLOW, COLOR_BLUE,   true);
    93 	DLG_COLOR(check,                 COLOR_BLACK,  COLOR_WHITE,  false);
    94 	DLG_COLOR(check_selected,        COLOR_WHITE,  COLOR_BLUE,   true);
    95 	DLG_COLOR(uarrow,                COLOR_GREEN,  COLOR_WHITE,  true);
    96 	DLG_COLOR(darrow,                COLOR_GREEN,  COLOR_WHITE,  true);
    97 }
    98 
    99 static void set_blackbg_theme(void)
   100 {
   101 	DLG_COLOR(screen, COLOR_RED,   COLOR_BLACK, true);
   102 	DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, false);
   103 	DLG_COLOR(dialog, COLOR_WHITE, COLOR_BLACK, false);
   104 	DLG_COLOR(title,  COLOR_RED,   COLOR_BLACK, false);
   105 	DLG_COLOR(border, COLOR_BLACK, COLOR_BLACK, true);
   106 
   107 	DLG_COLOR(button_active,         COLOR_YELLOW, COLOR_RED,   false);
   108 	DLG_COLOR(button_inactive,       COLOR_YELLOW, COLOR_BLACK, false);
   109 	DLG_COLOR(button_key_active,     COLOR_YELLOW, COLOR_RED,   true);
   110 	DLG_COLOR(button_key_inactive,   COLOR_RED,    COLOR_BLACK, false);
   111 	DLG_COLOR(button_label_active,   COLOR_WHITE,  COLOR_RED,   false);
   112 	DLG_COLOR(button_label_inactive, COLOR_BLACK,  COLOR_BLACK, true);
   113 
   114 	DLG_COLOR(inputbox,         COLOR_YELLOW, COLOR_BLACK, false);
   115 	DLG_COLOR(inputbox_border,  COLOR_YELLOW, COLOR_BLACK, false);
   116 
   117 	DLG_COLOR(searchbox,        COLOR_YELLOW, COLOR_BLACK, false);
   118 	DLG_COLOR(searchbox_title,  COLOR_YELLOW, COLOR_BLACK, true);
   119 	DLG_COLOR(searchbox_border, COLOR_BLACK,  COLOR_BLACK, true);
   120 
   121 	DLG_COLOR(position_indicator, COLOR_RED, COLOR_BLACK,  false);
   122 
   123 	DLG_COLOR(menubox,          COLOR_YELLOW, COLOR_BLACK, false);
   124 	DLG_COLOR(menubox_border,   COLOR_BLACK,  COLOR_BLACK, true);
   125 
   126 	DLG_COLOR(item,             COLOR_WHITE, COLOR_BLACK, false);
   127 	DLG_COLOR(item_selected,    COLOR_WHITE, COLOR_RED,   false);
   128 
   129 	DLG_COLOR(tag,              COLOR_RED,    COLOR_BLACK, false);
   130 	DLG_COLOR(tag_selected,     COLOR_YELLOW, COLOR_RED,   true);
   131 	DLG_COLOR(tag_key,          COLOR_RED,    COLOR_BLACK, false);
   132 	DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_RED,   true);
   133 
   134 	DLG_COLOR(check,            COLOR_YELLOW, COLOR_BLACK, false);
   135 	DLG_COLOR(check_selected,   COLOR_YELLOW, COLOR_RED,   true);
   136 
   137 	DLG_COLOR(uarrow, COLOR_RED, COLOR_BLACK, false);
   138 	DLG_COLOR(darrow, COLOR_RED, COLOR_BLACK, false);
   139 }
   140 
   141 static void set_bluetitle_theme(void)
   142 {
   143 	set_classic_theme();
   144 	DLG_COLOR(title,               COLOR_BLUE,   COLOR_WHITE, true);
   145 	DLG_COLOR(button_key_active,   COLOR_YELLOW, COLOR_BLUE,  true);
   146 	DLG_COLOR(button_label_active, COLOR_WHITE,  COLOR_BLUE,  true);
   147 	DLG_COLOR(searchbox_title,     COLOR_BLUE,   COLOR_WHITE, true);
   148 	DLG_COLOR(position_indicator,  COLOR_BLUE,   COLOR_WHITE, true);
   149 	DLG_COLOR(tag,                 COLOR_BLUE,   COLOR_WHITE, true);
   150 	DLG_COLOR(tag_key,             COLOR_BLUE,   COLOR_WHITE, true);
   151 
   152 }
   153 
   154 /*
   155  * Select color theme
   156  */
   157 static int set_theme(const char *theme)
   158 {
   159 	int use_color = 1;
   160 	if (!theme)
   161 		set_bluetitle_theme();
   162 	else if (strcmp(theme, "classic") == 0)
   163 		set_classic_theme();
   164 	else if (strcmp(theme, "bluetitle") == 0)
   165 		set_bluetitle_theme();
   166 	else if (strcmp(theme, "blackbg") == 0)
   167 		set_blackbg_theme();
   168 	else if (strcmp(theme, "mono") == 0)
   169 		use_color = 0;
   170 
   171 	return use_color;
   172 }
   173 
   174 static void init_one_color(struct dialog_color *color)
   175 {
   176 	static int pair = 0;
   177 
   178 	pair++;
   179 	init_pair(pair, color->fg, color->bg);
   180 	if (color->hl)
   181 		color->atr = A_BOLD | COLOR_PAIR(pair);
   182 	else
   183 		color->atr = COLOR_PAIR(pair);
   184 }
   185 
   186 static void init_dialog_colors(void)
   187 {
   188 	init_one_color(&dlg.screen);
   189 	init_one_color(&dlg.shadow);
   190 	init_one_color(&dlg.dialog);
   191 	init_one_color(&dlg.title);
   192 	init_one_color(&dlg.border);
   193 	init_one_color(&dlg.button_active);
   194 	init_one_color(&dlg.button_inactive);
   195 	init_one_color(&dlg.button_key_active);
   196 	init_one_color(&dlg.button_key_inactive);
   197 	init_one_color(&dlg.button_label_active);
   198 	init_one_color(&dlg.button_label_inactive);
   199 	init_one_color(&dlg.inputbox);
   200 	init_one_color(&dlg.inputbox_border);
   201 	init_one_color(&dlg.searchbox);
   202 	init_one_color(&dlg.searchbox_title);
   203 	init_one_color(&dlg.searchbox_border);
   204 	init_one_color(&dlg.position_indicator);
   205 	init_one_color(&dlg.menubox);
   206 	init_one_color(&dlg.menubox_border);
   207 	init_one_color(&dlg.item);
   208 	init_one_color(&dlg.item_selected);
   209 	init_one_color(&dlg.tag);
   210 	init_one_color(&dlg.tag_selected);
   211 	init_one_color(&dlg.tag_key);
   212 	init_one_color(&dlg.tag_key_selected);
   213 	init_one_color(&dlg.check);
   214 	init_one_color(&dlg.check_selected);
   215 	init_one_color(&dlg.uarrow);
   216 	init_one_color(&dlg.darrow);
   217 }
   218 
   219 /*
   220  * Setup for color display
   221  */
   222 static void color_setup(const char *theme)
   223 {
   224 	int use_color;
   225 
   226 	use_color = set_theme(theme);
   227 	if (use_color && has_colors()) {
   228 		start_color();
   229 		init_dialog_colors();
   230 	} else
   231 		set_mono_theme();
   232 }
   233 
   234 /*
   235  * Set window to attribute 'attr'
   236  */
   237 void attr_clear(WINDOW * win, int height, int width, chtype attr)
   238 {
   239 	int i, j;
   240 
   241 	wattrset(win, attr);
   242 	for (i = 0; i < height; i++) {
   243 		wmove(win, i, 0);
   244 		for (j = 0; j < width; j++)
   245 			waddch(win, ' ');
   246 	}
   247 	touchwin(win);
   248 }
   249 
   250 void dialog_clear(void)
   251 {
   252 	attr_clear(stdscr, LINES, COLS, dlg.screen.atr);
   253 	/* Display background title if it exists ... - SLH */
   254 	if (dlg.backtitle != NULL) {
   255 		int i;
   256 
   257 		wattrset(stdscr, dlg.screen.atr);
   258 		mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle);
   259 		wmove(stdscr, 1, 1);
   260 		for (i = 1; i < COLS - 1; i++)
   261 			waddch(stdscr, ACS_HLINE);
   262 	}
   263 	wnoutrefresh(stdscr);
   264 }
   265 
   266 /*
   267  * Do some initialization for dialog
   268  */
   269 int init_dialog(const char *backtitle)
   270 {
   271 	int height, width;
   272 
   273 	initscr();		/* Init curses */
   274 	getmaxyx(stdscr, height, width);
   275 	if (height < 19 || width < 80) {
   276 		endwin();
   277 		return -ERRDISPLAYTOOSMALL;
   278 	}
   279 
   280 	dlg.backtitle = backtitle;
   281 	color_setup(getenv("MENUCONFIG_COLOR"));
   282 
   283 	keypad(stdscr, TRUE);
   284 	cbreak();
   285 	noecho();
   286 	dialog_clear();
   287 
   288 	return 0;
   289 }
   290 
   291 void set_dialog_backtitle(const char *backtitle)
   292 {
   293 	dlg.backtitle = backtitle;
   294 }
   295 
   296 /*
   297  * End using dialog functions.
   298  */
   299 void end_dialog(int x, int y)
   300 {
   301 	/* move cursor back to original position */
   302 	move(y, x);
   303 	refresh();
   304 	endwin();
   305 }
   306 
   307 /* Print the title of the dialog. Center the title and truncate
   308  * tile if wider than dialog (- 2 chars).
   309  **/
   310 void print_title(WINDOW *dialog, const char *title, int width)
   311 {
   312 	if (title) {
   313 		int tlen = MIN(width - 2, strlen(title));
   314 		wattrset(dialog, dlg.title.atr);
   315 		mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' ');
   316 		mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen);
   317 		waddch(dialog, ' ');
   318 	}
   319 }
   320 
   321 /*
   322  * Print a string of text in a window, automatically wrap around to the
   323  * next line if the string is too long to fit on one line. Newline
   324  * characters '\n' are replaced by spaces.  We start on a new line
   325  * if there is no room for at least 4 nonblanks following a double-space.
   326  */
   327 void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
   328 {
   329 	int newl, cur_x, cur_y;
   330 	int i, prompt_len, room, wlen;
   331 	char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
   332 
   333 	strcpy(tempstr, prompt);
   334 
   335 	prompt_len = strlen(tempstr);
   336 
   337 	/*
   338 	 * Remove newlines
   339 	 */
   340 	for (i = 0; i < prompt_len; i++) {
   341 		if (tempstr[i] == '\n')
   342 			tempstr[i] = ' ';
   343 	}
   344 
   345 	if (prompt_len <= width - x * 2) {	/* If prompt is short */
   346 		wmove(win, y, (width - prompt_len) / 2);
   347 		waddstr(win, tempstr);
   348 	} else {
   349 		cur_x = x;
   350 		cur_y = y;
   351 		newl = 1;
   352 		word = tempstr;
   353 		while (word && *word) {
   354 			sp = strchr(word, ' ');
   355 			if (sp)
   356 				*sp++ = 0;
   357 
   358 			/* Wrap to next line if either the word does not fit,
   359 			   or it is the first word of a new sentence, and it is
   360 			   short, and the next word does not fit. */
   361 			room = width - cur_x;
   362 			wlen = strlen(word);
   363 			if (wlen > room ||
   364 			    (newl && wlen < 4 && sp
   365 			     && wlen + 1 + strlen(sp) > room
   366 			     && (!(sp2 = strchr(sp, ' '))
   367 				 || wlen + 1 + (sp2 - sp) > room))) {
   368 				cur_y++;
   369 				cur_x = x;
   370 			}
   371 			wmove(win, cur_y, cur_x);
   372 			waddstr(win, word);
   373 			getyx(win, cur_y, cur_x);
   374 			cur_x++;
   375 			if (sp && *sp == ' ') {
   376 				cur_x++;	/* double space */
   377 				while (*++sp == ' ') ;
   378 				newl = 1;
   379 			} else
   380 				newl = 0;
   381 			word = sp;
   382 		}
   383 	}
   384 }
   385 
   386 /*
   387  * Print a button
   388  */
   389 void print_button(WINDOW * win, const char *label, int y, int x, int selected)
   390 {
   391 	int i, temp;
   392 
   393 	wmove(win, y, x);
   394 	wattrset(win, selected ? dlg.button_active.atr
   395 		 : dlg.button_inactive.atr);
   396 	waddstr(win, "<");
   397 	temp = strspn(label, " ");
   398 	label += temp;
   399 	wattrset(win, selected ? dlg.button_label_active.atr
   400 		 : dlg.button_label_inactive.atr);
   401 	for (i = 0; i < temp; i++)
   402 		waddch(win, ' ');
   403 	wattrset(win, selected ? dlg.button_key_active.atr
   404 		 : dlg.button_key_inactive.atr);
   405 	waddch(win, label[0]);
   406 	wattrset(win, selected ? dlg.button_label_active.atr
   407 		 : dlg.button_label_inactive.atr);
   408 	waddstr(win, (char *)label + 1);
   409 	wattrset(win, selected ? dlg.button_active.atr
   410 		 : dlg.button_inactive.atr);
   411 	waddstr(win, ">");
   412 	wmove(win, y, x + temp + 1);
   413 }
   414 
   415 /*
   416  * Draw a rectangular box with line drawing characters
   417  */
   418 void
   419 draw_box(WINDOW * win, int y, int x, int height, int width,
   420 	 chtype box, chtype border)
   421 {
   422 	int i, j;
   423 
   424 	wattrset(win, 0);
   425 	for (i = 0; i < height; i++) {
   426 		wmove(win, y + i, x);
   427 		for (j = 0; j < width; j++)
   428 			if (!i && !j)
   429 				waddch(win, border | ACS_ULCORNER);
   430 			else if (i == height - 1 && !j)
   431 				waddch(win, border | ACS_LLCORNER);
   432 			else if (!i && j == width - 1)
   433 				waddch(win, box | ACS_URCORNER);
   434 			else if (i == height - 1 && j == width - 1)
   435 				waddch(win, box | ACS_LRCORNER);
   436 			else if (!i)
   437 				waddch(win, border | ACS_HLINE);
   438 			else if (i == height - 1)
   439 				waddch(win, box | ACS_HLINE);
   440 			else if (!j)
   441 				waddch(win, border | ACS_VLINE);
   442 			else if (j == width - 1)
   443 				waddch(win, box | ACS_VLINE);
   444 			else
   445 				waddch(win, box | ' ');
   446 	}
   447 }
   448 
   449 /*
   450  * Draw shadows along the right and bottom edge to give a more 3D look
   451  * to the boxes
   452  */
   453 void draw_shadow(WINDOW * win, int y, int x, int height, int width)
   454 {
   455 	int i;
   456 
   457 	if (has_colors()) {	/* Whether terminal supports color? */
   458 		wattrset(win, dlg.shadow.atr);
   459 		wmove(win, y + height, x + 2);
   460 		for (i = 0; i < width; i++)
   461 			waddch(win, winch(win) & A_CHARTEXT);
   462 		for (i = y + 1; i < y + height + 1; i++) {
   463 			wmove(win, i, x + width);
   464 			waddch(win, winch(win) & A_CHARTEXT);
   465 			waddch(win, winch(win) & A_CHARTEXT);
   466 		}
   467 		wnoutrefresh(win);
   468 	}
   469 }
   470 
   471 /*
   472  *  Return the position of the first alphabetic character in a string.
   473  */
   474 int first_alpha(const char *string, const char *exempt)
   475 {
   476 	int i, in_paren = 0, c;
   477 
   478 	for (i = 0; i < strlen(string); i++) {
   479 		c = tolower(string[i]);
   480 
   481 		if (strchr("<[(", c))
   482 			++in_paren;
   483 		if (strchr(">])", c) && in_paren > 0)
   484 			--in_paren;
   485 
   486 		if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0)
   487 			return i;
   488 	}
   489 
   490 	return 0;
   491 }
   492 
   493 /*
   494  * ncurses uses ESC to detect escaped char sequences. This resutl in
   495  * a small timeout before ESC is actually delivered to the application.
   496  * lxdialog suggest <ESC> <ESC> which is correctly translated to two
   497  * times esc. But then we need to ignore the second esc to avoid stepping
   498  * out one menu too much. Filter away all escaped key sequences since
   499  * keypad(FALSE) turn off ncurses support for escape sequences - and thats
   500  * needed to make notimeout() do as expected.
   501  */
   502 int on_key_esc(WINDOW *win)
   503 {
   504 	int key;
   505 	int key2;
   506 	int key3;
   507 
   508 	nodelay(win, TRUE);
   509 	keypad(win, FALSE);
   510 	key = wgetch(win);
   511 	key2 = wgetch(win);
   512 	do {
   513 		key3 = wgetch(win);
   514 	} while (key3 != ERR);
   515 	nodelay(win, FALSE);
   516 	keypad(win, TRUE);
   517 	if (key == KEY_ESC && key2 == ERR)
   518 		return KEY_ESC;
   519 	else if (key != ERR && key != KEY_ESC && key2 == ERR)
   520 		ungetch(key);
   521 
   522 	return -1;
   523 }
   524 
   525 /* redraw screen in new size */
   526 int on_key_resize(void)
   527 {
   528 	dialog_clear();
   529 	return KEY_RESIZE;
   530 }
   531 
   532 struct dialog_list *item_cur;
   533 struct dialog_list item_nil;
   534 struct dialog_list *item_head;
   535 
   536 void item_reset(void)
   537 {
   538 	struct dialog_list *p, *next;
   539 
   540 	for (p = item_head; p; p = next) {
   541 		next = p->next;
   542 		free(p);
   543 	}
   544 	item_head = NULL;
   545 	item_cur = &item_nil;
   546 }
   547 
   548 void item_make(const char *fmt, ...)
   549 {
   550 	va_list ap;
   551 	struct dialog_list *p = malloc(sizeof(*p));
   552 
   553 	if (item_head)
   554 		item_cur->next = p;
   555 	else
   556 		item_head = p;
   557 	item_cur = p;
   558 	memset(p, 0, sizeof(*p));
   559 
   560 	va_start(ap, fmt);
   561 	vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap);
   562 	va_end(ap);
   563 }
   564 
   565 void item_add_str(const char *fmt, ...)
   566 {
   567 	va_list ap;
   568         size_t avail;
   569 
   570 	avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str);
   571 
   572 	va_start(ap, fmt);
   573 	vsnprintf(item_cur->node.str + strlen(item_cur->node.str),
   574 		  avail, fmt, ap);
   575 	item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0';
   576 	va_end(ap);
   577 }
   578 
   579 void item_set_tag(char tag)
   580 {
   581 	item_cur->node.tag = tag;
   582 }
   583 void item_set_data(void *ptr)
   584 {
   585 	item_cur->node.data = ptr;
   586 }
   587 
   588 void item_set_selected(int val)
   589 {
   590 	item_cur->node.selected = val;
   591 }
   592 
   593 int item_activate_selected(void)
   594 {
   595 	item_foreach()
   596 		if (item_is_selected())
   597 			return 1;
   598 	return 0;
   599 }
   600 
   601 void *item_data(void)
   602 {
   603 	return item_cur->node.data;
   604 }
   605 
   606 char item_tag(void)
   607 {
   608 	return item_cur->node.tag;
   609 }
   610 
   611 int item_count(void)
   612 {
   613 	int n = 0;
   614 	struct dialog_list *p;
   615 
   616 	for (p = item_head; p; p = p->next)
   617 		n++;
   618 	return n;
   619 }
   620 
   621 void item_set(int n)
   622 {
   623 	int i = 0;
   624 	item_foreach()
   625 		if (i++ == n)
   626 			return;
   627 }
   628 
   629 int item_n(void)
   630 {
   631 	int n = 0;
   632 	struct dialog_list *p;
   633 
   634 	for (p = item_head; p; p = p->next) {
   635 		if (p == item_cur)
   636 			return n;
   637 		n++;
   638 	}
   639 	return 0;
   640 }
   641 
   642 const char *item_str(void)
   643 {
   644 	return item_cur->node.str;
   645 }
   646 
   647 int item_is_selected(void)
   648 {
   649 	return (item_cur->node.selected != 0);
   650 }
   651 
   652 int item_is_tag(char tag)
   653 {
   654 	return (item_cur->node.tag == tag);
   655 }