toybox/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 "dialog.h"
  23
  24struct dialog_info dlg;
  25
  26static 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) \
  60do {                               \
  61        dlg.dialog.fg = (f);       \
  62        dlg.dialog.bg = (b);       \
  63        dlg.dialog.hl = (h);       \
  64} while (0)
  65
  66static 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
  99static 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
 141static 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 */
 157static 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
 174static 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
 186static 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 */
 222static void color_setup(const char *theme)
 223{
 224        if (set_theme(theme)) {
 225                if (has_colors()) {     /* Terminal supports color? */
 226                        start_color();
 227                        init_dialog_colors();
 228                }
 229        }
 230        else
 231        {
 232                set_mono_theme();
 233        }
 234}
 235
 236/*
 237 * Set window to attribute 'attr'
 238 */
 239void attr_clear(WINDOW * win, int height, int width, chtype attr)
 240{
 241        int i, j;
 242
 243        wattrset(win, attr);
 244        for (i = 0; i < height; i++) {
 245                wmove(win, i, 0);
 246                for (j = 0; j < width; j++)
 247                        waddch(win, ' ');
 248        }
 249        touchwin(win);
 250}
 251
 252void dialog_clear(void)
 253{
 254        attr_clear(stdscr, LINES, COLS, dlg.screen.atr);
 255        /* Display background title if it exists ... - SLH */
 256        if (dlg.backtitle != NULL) {
 257                int i;
 258
 259                wattrset(stdscr, dlg.screen.atr);
 260                mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle);
 261                wmove(stdscr, 1, 1);
 262                for (i = 1; i < COLS - 1; i++)
 263                        waddch(stdscr, ACS_HLINE);
 264        }
 265        wnoutrefresh(stdscr);
 266}
 267
 268/*
 269 * Do some initialization for dialog
 270 */
 271void init_dialog(const char *backtitle)
 272{
 273        dlg.backtitle = backtitle;
 274        color_setup(getenv("MENUCONFIG_COLOR"));
 275}
 276
 277void reset_dialog(void)
 278{
 279        initscr();              /* Init curses */
 280        keypad(stdscr, TRUE);
 281        cbreak();
 282        noecho();
 283        dialog_clear();
 284}
 285
 286/*
 287 * End using dialog functions.
 288 */
 289void end_dialog(void)
 290{
 291        endwin();
 292}
 293
 294/* Print the title of the dialog. Center the title and truncate
 295 * tile if wider than dialog (- 2 chars).
 296 **/
 297void print_title(WINDOW *dialog, const char *title, int width)
 298{
 299        if (title) {
 300                int tlen = MIN(width - 2, strlen(title));
 301                wattrset(dialog, dlg.title.atr);
 302                mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' ');
 303                mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen);
 304                waddch(dialog, ' ');
 305        }
 306}
 307
 308/*
 309 * Print a string of text in a window, automatically wrap around to the
 310 * next line if the string is too long to fit on one line. Newline
 311 * characters '\n' are replaced by spaces.  We start on a new line
 312 * if there is no room for at least 4 nonblanks following a double-space.
 313 */
 314void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
 315{
 316        int newl, cur_x, cur_y;
 317        int i, prompt_len, room, wlen;
 318        char tempstr[MAX_LEN + 1], *word, *sp, *sp2;
 319
 320        strcpy(tempstr, prompt);
 321
 322        prompt_len = strlen(tempstr);
 323
 324        /*
 325         * Remove newlines
 326         */
 327        for (i = 0; i < prompt_len; i++) {
 328                if (tempstr[i] == '\n')
 329                        tempstr[i] = ' ';
 330        }
 331
 332        if (prompt_len <= width - x * 2) {      /* If prompt is short */
 333                wmove(win, y, (width - prompt_len) / 2);
 334                waddstr(win, tempstr);
 335        } else {
 336                cur_x = x;
 337                cur_y = y;
 338                newl = 1;
 339                word = tempstr;
 340                while (word && *word) {
 341                        sp = index(word, ' ');
 342                        if (sp)
 343                                *sp++ = 0;
 344
 345                        /* Wrap to next line if either the word does not fit,
 346                           or it is the first word of a new sentence, and it is
 347                           short, and the next word does not fit. */
 348                        room = width - cur_x;
 349                        wlen = strlen(word);
 350                        if (wlen > room ||
 351                            (newl && wlen < 4 && sp
 352                             && wlen + 1 + strlen(sp) > room
 353                             && (!(sp2 = index(sp, ' '))
 354                                 || wlen + 1 + (sp2 - sp) > room))) {
 355                                cur_y++;
 356                                cur_x = x;
 357                        }
 358                        wmove(win, cur_y, cur_x);
 359                        waddstr(win, word);
 360                        getyx(win, cur_y, cur_x);
 361                        cur_x++;
 362                        if (sp && *sp == ' ') {
 363                                cur_x++;        /* double space */
 364                                while (*++sp == ' ') ;
 365                                newl = 1;
 366                        } else
 367                                newl = 0;
 368                        word = sp;
 369                }
 370        }
 371}
 372
 373/*
 374 * Print a button
 375 */
 376void print_button(WINDOW * win, const char *label, int y, int x, int selected)
 377{
 378        int i, temp;
 379
 380        wmove(win, y, x);
 381        wattrset(win, selected ? dlg.button_active.atr
 382                 : dlg.button_inactive.atr);
 383        waddstr(win, "<");
 384        temp = strspn(label, " ");
 385        label += temp;
 386        wattrset(win, selected ? dlg.button_label_active.atr
 387                 : dlg.button_label_inactive.atr);
 388        for (i = 0; i < temp; i++)
 389                waddch(win, ' ');
 390        wattrset(win, selected ? dlg.button_key_active.atr
 391                 : dlg.button_key_inactive.atr);
 392        waddch(win, label[0]);
 393        wattrset(win, selected ? dlg.button_label_active.atr
 394                 : dlg.button_label_inactive.atr);
 395        waddstr(win, (char *)label + 1);
 396        wattrset(win, selected ? dlg.button_active.atr
 397                 : dlg.button_inactive.atr);
 398        waddstr(win, ">");
 399        wmove(win, y, x + temp + 1);
 400}
 401
 402/*
 403 * Draw a rectangular box with line drawing characters
 404 */
 405void
 406draw_box(WINDOW * win, int y, int x, int height, int width,
 407         chtype box, chtype border)
 408{
 409        int i, j;
 410
 411        wattrset(win, 0);
 412        for (i = 0; i < height; i++) {
 413                wmove(win, y + i, x);
 414                for (j = 0; j < width; j++)
 415                        if (!i && !j)
 416                                waddch(win, border | ACS_ULCORNER);
 417                        else if (i == height - 1 && !j)
 418                                waddch(win, border | ACS_LLCORNER);
 419                        else if (!i && j == width - 1)
 420                                waddch(win, box | ACS_URCORNER);
 421                        else if (i == height - 1 && j == width - 1)
 422                                waddch(win, box | ACS_LRCORNER);
 423                        else if (!i)
 424                                waddch(win, border | ACS_HLINE);
 425                        else if (i == height - 1)
 426                                waddch(win, box | ACS_HLINE);
 427                        else if (!j)
 428                                waddch(win, border | ACS_VLINE);
 429                        else if (j == width - 1)
 430                                waddch(win, box | ACS_VLINE);
 431                        else
 432                                waddch(win, box | ' ');
 433        }
 434}
 435
 436/*
 437 * Draw shadows along the right and bottom edge to give a more 3D look
 438 * to the boxes
 439 */
 440void draw_shadow(WINDOW * win, int y, int x, int height, int width)
 441{
 442        int i;
 443
 444        if (has_colors()) {     /* Whether terminal supports color? */
 445                wattrset(win, dlg.shadow.atr);
 446                wmove(win, y + height, x + 2);
 447                for (i = 0; i < width; i++)
 448                        waddch(win, winch(win) & A_CHARTEXT);
 449                for (i = y + 1; i < y + height + 1; i++) {
 450                        wmove(win, i, x + width);
 451                        waddch(win, winch(win) & A_CHARTEXT);
 452                        waddch(win, winch(win) & A_CHARTEXT);
 453                }
 454                wnoutrefresh(win);
 455        }
 456}
 457
 458/*
 459 *  Return the position of the first alphabetic character in a string.
 460 */
 461int first_alpha(const char *string, const char *exempt)
 462{
 463        int i, in_paren = 0, c;
 464
 465        for (i = 0; i < strlen(string); i++) {
 466                c = tolower(string[i]);
 467
 468                if (strchr("<[(", c))
 469                        ++in_paren;
 470                if (strchr(">])", c) && in_paren > 0)
 471                        --in_paren;
 472
 473                if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0)
 474                        return i;
 475        }
 476
 477        return 0;
 478}
 479
 480/*
 481 * ncurses uses ESC to detect escaped char sequences. This resutl in
 482 * a small timeout before ESC is actually delivered to the application.
 483 * lxdialog suggest <ESC> <ESC> which is correctly translated to two
 484 * times esc. But then we need to ignore the second esc to avoid stepping
 485 * out one menu too much. Filter away all escaped key sequences since
 486 * keypad(FALSE) turn off ncurses support for escape sequences - and thats
 487 * needed to make notimeout() do as expected.
 488 */
 489int on_key_esc(WINDOW *win)
 490{
 491        int key;
 492        int key2;
 493        int key3;
 494
 495        nodelay(win, TRUE);
 496        keypad(win, FALSE);
 497        key = wgetch(win);
 498        key2 = wgetch(win);
 499        do {
 500                key3 = wgetch(win);
 501        } while (key3 != ERR);
 502        nodelay(win, FALSE);
 503        keypad(win, TRUE);
 504        if (key == KEY_ESC && key2 == ERR)
 505                return KEY_ESC;
 506        else if (key != ERR && key != KEY_ESC && key2 == ERR)
 507                ungetch(key);
 508
 509        return -1;
 510}
 511
 512/* redraw screen in new size */
 513int on_key_resize(void)
 514{
 515        dialog_clear();
 516        return KEY_RESIZE;
 517}
 518
 519struct dialog_list *item_cur;
 520struct dialog_list item_nil;
 521struct dialog_list *item_head;
 522
 523void item_reset(void)
 524{
 525        struct dialog_list *p, *next;
 526
 527        for (p = item_head; p; p = next) {
 528                next = p->next;
 529                free(p);
 530        }
 531        item_head = NULL;
 532        item_cur = &item_nil;
 533}
 534
 535void item_make(const char *fmt, ...)
 536{
 537        va_list ap;
 538        struct dialog_list *p = malloc(sizeof(*p));
 539
 540        if (item_head)
 541                item_cur->next = p;
 542        else
 543                item_head = p;
 544        item_cur = p;
 545        memset(p, 0, sizeof(*p));
 546
 547        va_start(ap, fmt);
 548        vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap);
 549        va_end(ap);
 550}
 551
 552void item_add_str(const char *fmt, ...)
 553{
 554        va_list ap;
 555        size_t avail;
 556
 557        avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str);
 558
 559        va_start(ap, fmt);
 560        vsnprintf(item_cur->node.str + strlen(item_cur->node.str),
 561                  avail, fmt, ap);
 562        item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0';
 563        va_end(ap);
 564}
 565
 566void item_set_tag(char tag)
 567{
 568        item_cur->node.tag = tag;
 569}
 570void item_set_data(void *ptr)
 571{
 572        item_cur->node.data = ptr;
 573}
 574
 575void item_set_selected(int val)
 576{
 577        item_cur->node.selected = val;
 578}
 579
 580int item_activate_selected(void)
 581{
 582        item_foreach()
 583                if (item_is_selected())
 584                        return 1;
 585        return 0;
 586}
 587
 588void *item_data(void)
 589{
 590        return item_cur->node.data;
 591}
 592
 593char item_tag(void)
 594{
 595        return item_cur->node.tag;
 596}
 597
 598int item_count(void)
 599{
 600        int n = 0;
 601        struct dialog_list *p;
 602
 603        for (p = item_head; p; p = p->next)
 604                n++;
 605        return n;
 606}
 607
 608void item_set(int n)
 609{
 610        int i = 0;
 611        item_foreach()
 612                if (i++ == n)
 613                        return;
 614}
 615
 616int item_n(void)
 617{
 618        int n = 0;
 619        struct dialog_list *p;
 620
 621        for (p = item_head; p; p = p->next) {
 622                if (p == item_cur)
 623                        return n;
 624                n++;
 625        }
 626        return 0;
 627}
 628
 629const char *item_str(void)
 630{
 631        return item_cur->node.str;
 632}
 633
 634int item_is_selected(void)
 635{
 636        return (item_cur->node.selected != 0);
 637}
 638
 639int item_is_tag(char tag)
 640{
 641        return (item_cur->node.tag == tag);
 642}
 643