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