root/scripts/kconfig/nconf.gui.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. set_normal_colors
  2. normal_color_theme
  3. no_colors_theme
  4. set_colors
  5. print_in_middle
  6. get_line_no
  7. get_line
  8. get_line_length
  9. fill_window
  10. btn_dialog
  11. dialog_inputbox
  12. refresh_all_windows
  13. show_scroll_win

   1 // SPDX-License-Identifier: GPL-2.0
   2 /*
   3  * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com>
   4  *
   5  * Derived from menuconfig.
   6  */
   7 #include "nconf.h"
   8 #include "lkc.h"
   9 
  10 /* a list of all the different widgets we use */
  11 attributes_t attributes[ATTR_MAX+1] = {0};
  12 
  13 /* available colors:
  14    COLOR_BLACK   0
  15    COLOR_RED     1
  16    COLOR_GREEN   2
  17    COLOR_YELLOW  3
  18    COLOR_BLUE    4
  19    COLOR_MAGENTA 5
  20    COLOR_CYAN    6
  21    COLOR_WHITE   7
  22    */
  23 static void set_normal_colors(void)
  24 {
  25         init_pair(NORMAL, -1, -1);
  26         init_pair(MAIN_HEADING, COLOR_MAGENTA, -1);
  27 
  28         /* FORE is for the selected item */
  29         init_pair(MAIN_MENU_FORE, -1, -1);
  30         /* BACK for all the rest */
  31         init_pair(MAIN_MENU_BACK, -1, -1);
  32         init_pair(MAIN_MENU_GREY, -1, -1);
  33         init_pair(MAIN_MENU_HEADING, COLOR_GREEN, -1);
  34         init_pair(MAIN_MENU_BOX, COLOR_YELLOW, -1);
  35 
  36         init_pair(SCROLLWIN_TEXT, -1, -1);
  37         init_pair(SCROLLWIN_HEADING, COLOR_GREEN, -1);
  38         init_pair(SCROLLWIN_BOX, COLOR_YELLOW, -1);
  39 
  40         init_pair(DIALOG_TEXT, -1, -1);
  41         init_pair(DIALOG_BOX, COLOR_YELLOW, -1);
  42         init_pair(DIALOG_MENU_BACK, COLOR_YELLOW, -1);
  43         init_pair(DIALOG_MENU_FORE, COLOR_RED, -1);
  44 
  45         init_pair(INPUT_BOX, COLOR_YELLOW, -1);
  46         init_pair(INPUT_HEADING, COLOR_GREEN, -1);
  47         init_pair(INPUT_TEXT, -1, -1);
  48         init_pair(INPUT_FIELD, -1, -1);
  49 
  50         init_pair(FUNCTION_HIGHLIGHT, -1, -1);
  51         init_pair(FUNCTION_TEXT, COLOR_YELLOW, -1);
  52 }
  53 
  54 /* available attributes:
  55    A_NORMAL        Normal display (no highlight)
  56    A_STANDOUT      Best highlighting mode of the terminal.
  57    A_UNDERLINE     Underlining
  58    A_REVERSE       Reverse video
  59    A_BLINK         Blinking
  60    A_DIM           Half bright
  61    A_BOLD          Extra bright or bold
  62    A_PROTECT       Protected mode
  63    A_INVIS         Invisible or blank mode
  64    A_ALTCHARSET    Alternate character set
  65    A_CHARTEXT      Bit-mask to extract a character
  66    COLOR_PAIR(n)   Color-pair number n
  67    */
  68 static void normal_color_theme(void)
  69 {
  70         /* automatically add color... */
  71 #define mkattr(name, attr) do { \
  72 attributes[name] = attr | COLOR_PAIR(name); } while (0)
  73         mkattr(NORMAL, NORMAL);
  74         mkattr(MAIN_HEADING, A_BOLD | A_UNDERLINE);
  75 
  76         mkattr(MAIN_MENU_FORE, A_REVERSE);
  77         mkattr(MAIN_MENU_BACK, A_NORMAL);
  78         mkattr(MAIN_MENU_GREY, A_NORMAL);
  79         mkattr(MAIN_MENU_HEADING, A_BOLD);
  80         mkattr(MAIN_MENU_BOX, A_NORMAL);
  81 
  82         mkattr(SCROLLWIN_TEXT, A_NORMAL);
  83         mkattr(SCROLLWIN_HEADING, A_BOLD);
  84         mkattr(SCROLLWIN_BOX, A_BOLD);
  85 
  86         mkattr(DIALOG_TEXT, A_BOLD);
  87         mkattr(DIALOG_BOX, A_BOLD);
  88         mkattr(DIALOG_MENU_FORE, A_STANDOUT);
  89         mkattr(DIALOG_MENU_BACK, A_NORMAL);
  90 
  91         mkattr(INPUT_BOX, A_NORMAL);
  92         mkattr(INPUT_HEADING, A_BOLD);
  93         mkattr(INPUT_TEXT, A_NORMAL);
  94         mkattr(INPUT_FIELD, A_UNDERLINE);
  95 
  96         mkattr(FUNCTION_HIGHLIGHT, A_BOLD);
  97         mkattr(FUNCTION_TEXT, A_REVERSE);
  98 }
  99 
 100 static void no_colors_theme(void)
 101 {
 102         /* automatically add highlight, no color */
 103 #define mkattrn(name, attr) { attributes[name] = attr; }
 104 
 105         mkattrn(NORMAL, NORMAL);
 106         mkattrn(MAIN_HEADING, A_BOLD | A_UNDERLINE);
 107 
 108         mkattrn(MAIN_MENU_FORE, A_STANDOUT);
 109         mkattrn(MAIN_MENU_BACK, A_NORMAL);
 110         mkattrn(MAIN_MENU_GREY, A_NORMAL);
 111         mkattrn(MAIN_MENU_HEADING, A_BOLD);
 112         mkattrn(MAIN_MENU_BOX, A_NORMAL);
 113 
 114         mkattrn(SCROLLWIN_TEXT, A_NORMAL);
 115         mkattrn(SCROLLWIN_HEADING, A_BOLD);
 116         mkattrn(SCROLLWIN_BOX, A_BOLD);
 117 
 118         mkattrn(DIALOG_TEXT, A_NORMAL);
 119         mkattrn(DIALOG_BOX, A_BOLD);
 120         mkattrn(DIALOG_MENU_FORE, A_STANDOUT);
 121         mkattrn(DIALOG_MENU_BACK, A_NORMAL);
 122 
 123         mkattrn(INPUT_BOX, A_BOLD);
 124         mkattrn(INPUT_HEADING, A_BOLD);
 125         mkattrn(INPUT_TEXT, A_NORMAL);
 126         mkattrn(INPUT_FIELD, A_UNDERLINE);
 127 
 128         mkattrn(FUNCTION_HIGHLIGHT, A_BOLD);
 129         mkattrn(FUNCTION_TEXT, A_REVERSE);
 130 }
 131 
 132 void set_colors(void)
 133 {
 134         start_color();
 135         use_default_colors();
 136         set_normal_colors();
 137         if (has_colors()) {
 138                 normal_color_theme();
 139         } else {
 140                 /* give defaults */
 141                 no_colors_theme();
 142         }
 143 }
 144 
 145 
 146 /* this changes the windows attributes !!! */
 147 void print_in_middle(WINDOW *win,
 148                 int starty,
 149                 int startx,
 150                 int width,
 151                 const char *string,
 152                 chtype color)
 153 {      int length, x, y;
 154         float temp;
 155 
 156 
 157         if (win == NULL)
 158                 win = stdscr;
 159         getyx(win, y, x);
 160         if (startx != 0)
 161                 x = startx;
 162         if (starty != 0)
 163                 y = starty;
 164         if (width == 0)
 165                 width = 80;
 166 
 167         length = strlen(string);
 168         temp = (width - length) / 2;
 169         x = startx + (int)temp;
 170         (void) wattrset(win, color);
 171         mvwprintw(win, y, x, "%s", string);
 172         refresh();
 173 }
 174 
 175 int get_line_no(const char *text)
 176 {
 177         int i;
 178         int total = 1;
 179 
 180         if (!text)
 181                 return 0;
 182 
 183         for (i = 0; text[i] != '\0'; i++)
 184                 if (text[i] == '\n')
 185                         total++;
 186         return total;
 187 }
 188 
 189 const char *get_line(const char *text, int line_no)
 190 {
 191         int i;
 192         int lines = 0;
 193 
 194         if (!text)
 195                 return NULL;
 196 
 197         for (i = 0; text[i] != '\0' && lines < line_no; i++)
 198                 if (text[i] == '\n')
 199                         lines++;
 200         return text+i;
 201 }
 202 
 203 int get_line_length(const char *line)
 204 {
 205         int res = 0;
 206         while (*line != '\0' && *line != '\n') {
 207                 line++;
 208                 res++;
 209         }
 210         return res;
 211 }
 212 
 213 /* print all lines to the window. */
 214 void fill_window(WINDOW *win, const char *text)
 215 {
 216         int x, y;
 217         int total_lines = get_line_no(text);
 218         int i;
 219 
 220         getmaxyx(win, y, x);
 221         /* do not go over end of line */
 222         total_lines = min(total_lines, y);
 223         for (i = 0; i < total_lines; i++) {
 224                 char tmp[x+10];
 225                 const char *line = get_line(text, i);
 226                 int len = get_line_length(line);
 227                 strncpy(tmp, line, min(len, x));
 228                 tmp[len] = '\0';
 229                 mvwprintw(win, i, 0, "%s", tmp);
 230         }
 231 }
 232 
 233 /* get the message, and buttons.
 234  * each button must be a char*
 235  * return the selected button
 236  *
 237  * this dialog is used for 2 different things:
 238  * 1) show a text box, no buttons.
 239  * 2) show a dialog, with horizontal buttons
 240  */
 241 int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...)
 242 {
 243         va_list ap;
 244         char *btn;
 245         int btns_width = 0;
 246         int msg_lines = 0;
 247         int msg_width = 0;
 248         int total_width;
 249         int win_rows = 0;
 250         WINDOW *win;
 251         WINDOW *msg_win;
 252         WINDOW *menu_win;
 253         MENU *menu;
 254         ITEM *btns[btn_num+1];
 255         int i, x, y;
 256         int res = -1;
 257 
 258 
 259         va_start(ap, btn_num);
 260         for (i = 0; i < btn_num; i++) {
 261                 btn = va_arg(ap, char *);
 262                 btns[i] = new_item(btn, "");
 263                 btns_width += strlen(btn)+1;
 264         }
 265         va_end(ap);
 266         btns[btn_num] = NULL;
 267 
 268         /* find the widest line of msg: */
 269         msg_lines = get_line_no(msg);
 270         for (i = 0; i < msg_lines; i++) {
 271                 const char *line = get_line(msg, i);
 272                 int len = get_line_length(line);
 273                 if (msg_width < len)
 274                         msg_width = len;
 275         }
 276 
 277         total_width = max(msg_width, btns_width);
 278         /* place dialog in middle of screen */
 279         y = (getmaxy(stdscr)-(msg_lines+4))/2;
 280         x = (getmaxx(stdscr)-(total_width+4))/2;
 281 
 282 
 283         /* create the windows */
 284         if (btn_num > 0)
 285                 win_rows = msg_lines+4;
 286         else
 287                 win_rows = msg_lines+2;
 288 
 289         win = newwin(win_rows, total_width+4, y, x);
 290         keypad(win, TRUE);
 291         menu_win = derwin(win, 1, btns_width, win_rows-2,
 292                         1+(total_width+2-btns_width)/2);
 293         menu = new_menu(btns);
 294         msg_win = derwin(win, win_rows-2, msg_width, 1,
 295                         1+(total_width+2-msg_width)/2);
 296 
 297         set_menu_fore(menu, attributes[DIALOG_MENU_FORE]);
 298         set_menu_back(menu, attributes[DIALOG_MENU_BACK]);
 299 
 300         (void) wattrset(win, attributes[DIALOG_BOX]);
 301         box(win, 0, 0);
 302 
 303         /* print message */
 304         (void) wattrset(msg_win, attributes[DIALOG_TEXT]);
 305         fill_window(msg_win, msg);
 306 
 307         set_menu_win(menu, win);
 308         set_menu_sub(menu, menu_win);
 309         set_menu_format(menu, 1, btn_num);
 310         menu_opts_off(menu, O_SHOWDESC);
 311         menu_opts_off(menu, O_SHOWMATCH);
 312         menu_opts_on(menu, O_ONEVALUE);
 313         menu_opts_on(menu, O_NONCYCLIC);
 314         set_menu_mark(menu, "");
 315         post_menu(menu);
 316 
 317 
 318         touchwin(win);
 319         refresh_all_windows(main_window);
 320         while ((res = wgetch(win))) {
 321                 switch (res) {
 322                 case KEY_LEFT:
 323                         menu_driver(menu, REQ_LEFT_ITEM);
 324                         break;
 325                 case KEY_RIGHT:
 326                         menu_driver(menu, REQ_RIGHT_ITEM);
 327                         break;
 328                 case 10: /* ENTER */
 329                 case 27: /* ESCAPE */
 330                 case ' ':
 331                 case KEY_F(F_BACK):
 332                 case KEY_F(F_EXIT):
 333                         break;
 334                 }
 335                 touchwin(win);
 336                 refresh_all_windows(main_window);
 337 
 338                 if (res == 10 || res == ' ') {
 339                         res = item_index(current_item(menu));
 340                         break;
 341                 } else if (res == 27 || res == KEY_F(F_BACK) ||
 342                                 res == KEY_F(F_EXIT)) {
 343                         res = KEY_EXIT;
 344                         break;
 345                 }
 346         }
 347 
 348         unpost_menu(menu);
 349         free_menu(menu);
 350         for (i = 0; i < btn_num; i++)
 351                 free_item(btns[i]);
 352 
 353         delwin(win);
 354         return res;
 355 }
 356 
 357 int dialog_inputbox(WINDOW *main_window,
 358                 const char *title, const char *prompt,
 359                 const char *init, char **resultp, int *result_len)
 360 {
 361         int prompt_lines = 0;
 362         int prompt_width = 0;
 363         WINDOW *win;
 364         WINDOW *prompt_win;
 365         WINDOW *form_win;
 366         PANEL *panel;
 367         int i, x, y, lines, columns, win_lines, win_cols;
 368         int res = -1;
 369         int cursor_position = strlen(init);
 370         int cursor_form_win;
 371         char *result = *resultp;
 372 
 373         getmaxyx(stdscr, lines, columns);
 374 
 375         if (strlen(init)+1 > *result_len) {
 376                 *result_len = strlen(init)+1;
 377                 *resultp = result = xrealloc(result, *result_len);
 378         }
 379 
 380         /* find the widest line of msg: */
 381         prompt_lines = get_line_no(prompt);
 382         for (i = 0; i < prompt_lines; i++) {
 383                 const char *line = get_line(prompt, i);
 384                 int len = get_line_length(line);
 385                 prompt_width = max(prompt_width, len);
 386         }
 387 
 388         if (title)
 389                 prompt_width = max(prompt_width, strlen(title));
 390 
 391         win_lines = min(prompt_lines+6, lines-2);
 392         win_cols = min(prompt_width+7, columns-2);
 393         prompt_lines = max(win_lines-6, 0);
 394         prompt_width = max(win_cols-7, 0);
 395 
 396         /* place dialog in middle of screen */
 397         y = (lines-win_lines)/2;
 398         x = (columns-win_cols)/2;
 399 
 400         strncpy(result, init, *result_len);
 401 
 402         /* create the windows */
 403         win = newwin(win_lines, win_cols, y, x);
 404         prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2);
 405         form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2);
 406         keypad(form_win, TRUE);
 407 
 408         (void) wattrset(form_win, attributes[INPUT_FIELD]);
 409 
 410         (void) wattrset(win, attributes[INPUT_BOX]);
 411         box(win, 0, 0);
 412         (void) wattrset(win, attributes[INPUT_HEADING]);
 413         if (title)
 414                 mvwprintw(win, 0, 3, "%s", title);
 415 
 416         /* print message */
 417         (void) wattrset(prompt_win, attributes[INPUT_TEXT]);
 418         fill_window(prompt_win, prompt);
 419 
 420         mvwprintw(form_win, 0, 0, "%*s", prompt_width, " ");
 421         cursor_form_win = min(cursor_position, prompt_width-1);
 422         mvwprintw(form_win, 0, 0, "%s",
 423                   result + cursor_position-cursor_form_win);
 424 
 425         /* create panels */
 426         panel = new_panel(win);
 427 
 428         /* show the cursor */
 429         curs_set(1);
 430 
 431         touchwin(win);
 432         refresh_all_windows(main_window);
 433         while ((res = wgetch(form_win))) {
 434                 int len = strlen(result);
 435                 switch (res) {
 436                 case 10: /* ENTER */
 437                 case 27: /* ESCAPE */
 438                 case KEY_F(F_HELP):
 439                 case KEY_F(F_EXIT):
 440                 case KEY_F(F_BACK):
 441                         break;
 442                 case 8:   /* ^H */
 443                 case 127: /* ^? */
 444                 case KEY_BACKSPACE:
 445                         if (cursor_position > 0) {
 446                                 memmove(&result[cursor_position-1],
 447                                                 &result[cursor_position],
 448                                                 len-cursor_position+1);
 449                                 cursor_position--;
 450                                 cursor_form_win--;
 451                                 len--;
 452                         }
 453                         break;
 454                 case KEY_DC:
 455                         if (cursor_position >= 0 && cursor_position < len) {
 456                                 memmove(&result[cursor_position],
 457                                                 &result[cursor_position+1],
 458                                                 len-cursor_position+1);
 459                                 len--;
 460                         }
 461                         break;
 462                 case KEY_UP:
 463                 case KEY_RIGHT:
 464                         if (cursor_position < len) {
 465                                 cursor_position++;
 466                                 cursor_form_win++;
 467                         }
 468                         break;
 469                 case KEY_DOWN:
 470                 case KEY_LEFT:
 471                         if (cursor_position > 0) {
 472                                 cursor_position--;
 473                                 cursor_form_win--;
 474                         }
 475                         break;
 476                 case KEY_HOME:
 477                         cursor_position = 0;
 478                         cursor_form_win = 0;
 479                         break;
 480                 case KEY_END:
 481                         cursor_position = len;
 482                         cursor_form_win = min(cursor_position, prompt_width-1);
 483                         break;
 484                 default:
 485                         if ((isgraph(res) || isspace(res))) {
 486                                 /* one for new char, one for '\0' */
 487                                 if (len+2 > *result_len) {
 488                                         *result_len = len+2;
 489                                         *resultp = result = realloc(result,
 490                                                                 *result_len);
 491                                 }
 492                                 /* insert the char at the proper position */
 493                                 memmove(&result[cursor_position+1],
 494                                                 &result[cursor_position],
 495                                                 len-cursor_position+1);
 496                                 result[cursor_position] = res;
 497                                 cursor_position++;
 498                                 cursor_form_win++;
 499                                 len++;
 500                         } else {
 501                                 mvprintw(0, 0, "unknown key: %d\n", res);
 502                         }
 503                         break;
 504                 }
 505                 if (cursor_form_win < 0)
 506                         cursor_form_win = 0;
 507                 else if (cursor_form_win > prompt_width-1)
 508                         cursor_form_win = prompt_width-1;
 509 
 510                 wmove(form_win, 0, 0);
 511                 wclrtoeol(form_win);
 512                 mvwprintw(form_win, 0, 0, "%*s", prompt_width, " ");
 513                 mvwprintw(form_win, 0, 0, "%s",
 514                         result + cursor_position-cursor_form_win);
 515                 wmove(form_win, 0, cursor_form_win);
 516                 touchwin(win);
 517                 refresh_all_windows(main_window);
 518 
 519                 if (res == 10) {
 520                         res = 0;
 521                         break;
 522                 } else if (res == 27 || res == KEY_F(F_BACK) ||
 523                                 res == KEY_F(F_EXIT)) {
 524                         res = KEY_EXIT;
 525                         break;
 526                 } else if (res == KEY_F(F_HELP)) {
 527                         res = 1;
 528                         break;
 529                 }
 530         }
 531 
 532         /* hide the cursor */
 533         curs_set(0);
 534         del_panel(panel);
 535         delwin(prompt_win);
 536         delwin(form_win);
 537         delwin(win);
 538         return res;
 539 }
 540 
 541 /* refresh all windows in the correct order */
 542 void refresh_all_windows(WINDOW *main_window)
 543 {
 544         update_panels();
 545         touchwin(main_window);
 546         refresh();
 547 }
 548 
 549 /* layman's scrollable window... */
 550 void show_scroll_win(WINDOW *main_window,
 551                 const char *title,
 552                 const char *text)
 553 {
 554         int res;
 555         int total_lines = get_line_no(text);
 556         int x, y, lines, columns;
 557         int start_x = 0, start_y = 0;
 558         int text_lines = 0, text_cols = 0;
 559         int total_cols = 0;
 560         int win_cols = 0;
 561         int win_lines = 0;
 562         int i = 0;
 563         WINDOW *win;
 564         WINDOW *pad;
 565         PANEL *panel;
 566 
 567         getmaxyx(stdscr, lines, columns);
 568 
 569         /* find the widest line of msg: */
 570         total_lines = get_line_no(text);
 571         for (i = 0; i < total_lines; i++) {
 572                 const char *line = get_line(text, i);
 573                 int len = get_line_length(line);
 574                 total_cols = max(total_cols, len+2);
 575         }
 576 
 577         /* create the pad */
 578         pad = newpad(total_lines+10, total_cols+10);
 579         (void) wattrset(pad, attributes[SCROLLWIN_TEXT]);
 580         fill_window(pad, text);
 581 
 582         win_lines = min(total_lines+4, lines-2);
 583         win_cols = min(total_cols+2, columns-2);
 584         text_lines = max(win_lines-4, 0);
 585         text_cols = max(win_cols-2, 0);
 586 
 587         /* place window in middle of screen */
 588         y = (lines-win_lines)/2;
 589         x = (columns-win_cols)/2;
 590 
 591         win = newwin(win_lines, win_cols, y, x);
 592         keypad(win, TRUE);
 593         /* show the help in the help window, and show the help panel */
 594         (void) wattrset(win, attributes[SCROLLWIN_BOX]);
 595         box(win, 0, 0);
 596         (void) wattrset(win, attributes[SCROLLWIN_HEADING]);
 597         mvwprintw(win, 0, 3, " %s ", title);
 598         panel = new_panel(win);
 599 
 600         /* handle scrolling */
 601         do {
 602 
 603                 copywin(pad, win, start_y, start_x, 2, 2, text_lines,
 604                                 text_cols, 0);
 605                 print_in_middle(win,
 606                                 text_lines+2,
 607                                 0,
 608                                 text_cols,
 609                                 "<OK>",
 610                                 attributes[DIALOG_MENU_FORE]);
 611                 wrefresh(win);
 612 
 613                 res = wgetch(win);
 614                 switch (res) {
 615                 case KEY_NPAGE:
 616                 case ' ':
 617                 case 'd':
 618                         start_y += text_lines-2;
 619                         break;
 620                 case KEY_PPAGE:
 621                 case 'u':
 622                         start_y -= text_lines+2;
 623                         break;
 624                 case KEY_HOME:
 625                         start_y = 0;
 626                         break;
 627                 case KEY_END:
 628                         start_y = total_lines-text_lines;
 629                         break;
 630                 case KEY_DOWN:
 631                 case 'j':
 632                         start_y++;
 633                         break;
 634                 case KEY_UP:
 635                 case 'k':
 636                         start_y--;
 637                         break;
 638                 case KEY_LEFT:
 639                 case 'h':
 640                         start_x--;
 641                         break;
 642                 case KEY_RIGHT:
 643                 case 'l':
 644                         start_x++;
 645                         break;
 646                 }
 647                 if (res == 10 || res == 27 || res == 'q' ||
 648                         res == KEY_F(F_HELP) || res == KEY_F(F_BACK) ||
 649                         res == KEY_F(F_EXIT))
 650                         break;
 651                 if (start_y < 0)
 652                         start_y = 0;
 653                 if (start_y >= total_lines-text_lines)
 654                         start_y = total_lines-text_lines;
 655                 if (start_x < 0)
 656                         start_x = 0;
 657                 if (start_x >= total_cols-text_cols)
 658                         start_x = total_cols-text_cols;
 659         } while (res);
 660 
 661         del_panel(panel);
 662         delwin(win);
 663         refresh_all_windows(main_window);
 664 }

/* [<][>][^][v][top][bottom][index][help] */