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