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