123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461 |
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <grub/normal.h>
- #include <grub/term.h>
- #include <grub/misc.h>
- #include <grub/mm.h>
- #include <grub/loader.h>
- #include <grub/command.h>
- #include <grub/parser.h>
- #include <grub/script_sh.h>
- #include <grub/auth.h>
- #include <grub/i18n.h>
- #include <grub/charset.h>
- #include <grub/safemath.h>
- enum update_mode
- {
- NO_LINE,
- SINGLE_LINE,
- ALL_LINES
- };
- struct line
- {
- /* The line buffer. */
- grub_uint32_t *buf;
- /* The length of the line. */
- int len;
- /* The maximum length of the line. */
- int max_len;
- struct grub_term_pos **pos;
- };
- struct per_term_screen
- {
- struct grub_term_output *term;
- int y_line_start;
- struct grub_term_screen_geometry geo;
- /* Scratch variables used when updating. Having them here avoids
- loads of small mallocs. */
- int orig_num;
- int down;
- enum update_mode mode;
- };
- struct screen
- {
- /* The array of lines. */
- struct line *lines;
- /* The number of lines. */
- int num_lines;
- /* The current column. */
- int column;
- /* The real column. */
- int real_column;
- /* The current line. */
- int line;
- /* The kill buffer. */
- char *killed_text;
- /* The flag of a completion window. */
- int completion_shown;
- int submenu;
- struct per_term_screen *terms;
- unsigned nterms;
- };
- /* Used for storing completion items temporarily. */
- static struct {
- char *buf;
- grub_size_t len;
- grub_size_t max_len;
- } completion_buffer;
- static int completion_type;
- /* Initialize a line. */
- static int
- init_line (struct screen *screen, struct line *linep)
- {
- linep->len = 0;
- linep->max_len = 80;
- linep->buf = grub_calloc (linep->max_len + 1, sizeof (linep->buf[0]));
- linep->pos = grub_calloc (screen->nterms, sizeof (linep->pos[0]));
- if (! linep->buf || !linep->pos)
- {
- grub_free (linep->buf);
- grub_free (linep->pos);
- return 0;
- }
- return 1;
- }
- /* Allocate extra space if necessary. */
- static int
- ensure_space (struct line *linep, int extra)
- {
- if (linep->max_len < linep->len + extra)
- {
- grub_size_t sz0, sz1;
- if (grub_add (linep->len, extra, &sz0) ||
- grub_mul (sz0, 2, &sz0) ||
- grub_add (sz0, 1, &sz1) ||
- grub_mul (sz1, sizeof (linep->buf[0]), &sz1))
- return 0;
- linep->buf = grub_realloc (linep->buf, sz1);
- if (! linep->buf)
- return 0;
- linep->max_len = sz0;
- }
- return 1;
- }
- /* Return the number of lines occupied by this line on the screen. */
- static int
- get_logical_num_lines (struct line *linep, struct per_term_screen *term_screen)
- {
- grub_size_t width = grub_getstringwidth (linep->buf, linep->buf + linep->len,
- term_screen->term);
- /* Empty line still consumes space on screen */
- return width ? (width + (unsigned) term_screen->geo.entry_width - 1) /
- (unsigned) term_screen->geo.entry_width
- : 1;
- }
- static void
- advance_to (struct screen *screen, int c)
- {
- if (c > screen->lines[screen->line].len)
- c = screen->lines[screen->line].len;
- screen->column = grub_unicode_get_comb_end (screen->lines[screen->line].buf
- + screen->lines[screen->line].len,
- screen->lines[screen->line].buf
- + c)
- - screen->lines[screen->line].buf;
- }
- /* Print an empty line. */
- static void
- print_empty_line (int y, struct per_term_screen *term_screen)
- {
- int i;
- grub_term_gotoxy (term_screen->term,
- (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
- y + term_screen->geo.first_entry_y });
- for (i = 0; i < term_screen->geo.entry_width + 1; i++)
- grub_putcode (' ', term_screen->term);
- }
- static void
- print_updown (int upflag, int downflag, struct per_term_screen *term_screen)
- {
- grub_term_gotoxy (term_screen->term,
- (struct grub_term_coordinate) { term_screen->geo.first_entry_x
- + term_screen->geo.entry_width + 1
- + term_screen->geo.border,
- term_screen->geo.first_entry_y });
- if (upflag && downflag)
- grub_putcode (GRUB_UNICODE_UPDOWNARROW, term_screen->term);
- else if (upflag)
- grub_putcode (GRUB_UNICODE_UPARROW, term_screen->term);
- else if (downflag)
- grub_putcode (GRUB_UNICODE_DOWNARROW, term_screen->term);
- else
- grub_putcode (' ', term_screen->term);
- }
- /* Print an up arrow. */
- static void
- print_up (int flag, struct per_term_screen *term_screen)
- {
- grub_term_gotoxy (term_screen->term,
- (struct grub_term_coordinate) { term_screen->geo.first_entry_x
- + term_screen->geo.entry_width + 1
- + term_screen->geo.border,
- term_screen->geo.first_entry_y });
- if (flag)
- grub_putcode (GRUB_UNICODE_UPARROW, term_screen->term);
- else
- grub_putcode (' ', term_screen->term);
- }
- /* Print a down arrow. */
- static void
- print_down (int flag, struct per_term_screen *term_screen)
- {
- grub_term_gotoxy (term_screen->term,
- (struct grub_term_coordinate) { term_screen->geo.first_entry_x
- + term_screen->geo.entry_width + 1
- + term_screen->geo.border,
- term_screen->geo.first_entry_y
- + term_screen->geo.num_entries - 1 });
- if (flag)
- grub_putcode (GRUB_UNICODE_DOWNARROW, term_screen->term);
- else
- grub_putcode (' ', term_screen->term);
- }
- /* Draw the lines of the screen SCREEN. */
- static void
- update_screen (struct screen *screen, struct per_term_screen *term_screen,
- int region_start, int region_column __attribute__ ((unused)),
- int up, int down, enum update_mode mode)
- {
- int up_flag = 0;
- int down_flag = 0;
- int y;
- int i;
- struct line *linep;
- y = term_screen->y_line_start;
- linep = screen->lines;
- for (i = 0; i < screen->line; i++, linep++)
- y += get_logical_num_lines (linep, term_screen);
- linep = screen->lines + screen->line;
- grub_size_t t = grub_getstringwidth (linep->buf, linep->buf + screen->column,
- term_screen->term);
- y += t / (unsigned) term_screen->geo.entry_width;
- if (t % (unsigned) term_screen->geo.entry_width == 0
- && t != 0 && screen->column == linep->len)
- y--;
- /* Check if scrolling is necessary. */
- if (y < 0 || y >= term_screen->geo.num_entries)
- {
- int delta;
- if (y < 0)
- delta = -y;
- else
- delta = term_screen->geo.num_entries - 1 - y;
- term_screen->y_line_start += delta;
- region_start = 0;
- up = 1;
- down = 1;
- mode = ALL_LINES;
- }
- grub_term_setcursor (term_screen->term, 0);
- if (mode != NO_LINE)
- {
- /* Draw lines. This code is tricky, because this must calculate logical
- positions. */
- y = term_screen->y_line_start;
- i = 0;
- linep = screen->lines;
- while (1)
- {
- int add;
- add = get_logical_num_lines (linep, term_screen);
- if (y + add > 0)
- break;
- i++;
- linep++;
- y += add;
- }
- if (y < 0 || i > 0)
- up_flag = 1;
- do
- {
- struct grub_term_pos **pos;
- if (linep >= screen->lines + screen->num_lines)
- break;
- pos = linep->pos + (term_screen - screen->terms);
- if (!*pos)
- *pos = grub_calloc (linep->len + 1, sizeof (**pos));
- if (i == region_start || linep == screen->lines + screen->line
- || (i > region_start && mode == ALL_LINES))
- {
- grub_term_gotoxy (term_screen->term,
- (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
- (y < 0 ? 0 : y)
- + term_screen->geo.first_entry_y });
- grub_print_ucs4_menu (linep->buf,
- linep->buf + linep->len,
- term_screen->geo.first_entry_x,
- term_screen->geo.right_margin,
- term_screen->term,
- (y < 0) ? -y : 0,
- term_screen->geo.num_entries
- - ((y > 0) ? y : 0), '\\',
- *pos);
- }
- y += get_logical_num_lines (linep, term_screen);
- if (y >= term_screen->geo.num_entries)
- {
- if (i + 1 < screen->num_lines)
- down_flag = 1;
- }
- linep++;
- i++;
- if (mode == ALL_LINES && i == screen->num_lines)
- for (; y < term_screen->geo.num_entries; y++)
- print_empty_line (y, term_screen);
- }
- while (y < term_screen->geo.num_entries);
- /* Draw up and down arrows. */
- if (term_screen->geo.num_entries == 1)
- {
- if (up || down)
- print_updown (up_flag, down_flag, term_screen);
- }
- else
- {
- if (up)
- print_up (up_flag, term_screen);
- if (down)
- print_down (down_flag, term_screen);
- }
- }
- /* Place the cursor. */
- if (screen->lines[screen->line].pos[term_screen - screen->terms])
- {
- const struct grub_term_pos *cpos;
- for (cpos = &(screen->lines[screen->line].pos[term_screen - screen->terms])[screen->column];
- cpos >= &(screen->lines[screen->line].pos[term_screen - screen->terms])[0];
- cpos--)
- if (cpos->valid)
- break;
- y = term_screen->y_line_start;
- for (i = 0; i < screen->line; i++)
- y += get_logical_num_lines (screen->lines + i, term_screen);
- if (cpos >= &(screen->lines[screen->line].pos[term_screen - screen->terms])[0])
- grub_term_gotoxy (term_screen->term,
- (struct grub_term_coordinate) { cpos->x + term_screen->geo.first_entry_x,
- cpos->y + y
- + term_screen->geo.first_entry_y });
- else
- grub_term_gotoxy (term_screen->term,
- (struct grub_term_coordinate) { term_screen->geo.first_entry_x,
- y + term_screen->geo.first_entry_y });
- }
- grub_term_setcursor (term_screen->term, 1);
- grub_term_refresh (term_screen->term);
- }
- static void
- update_screen_all (struct screen *screen,
- int region_start, int region_column,
- int up, int down, enum update_mode mode)
- {
- unsigned i;
- for (i = 0; i < screen->nterms; i++)
- update_screen (screen, &screen->terms[i], region_start, region_column,
- up, down, mode);
- }
- static int
- insert_string (struct screen *screen, const char *s, int update)
- {
- int region_start = screen->num_lines;
- int region_column = 0;
- unsigned i;
- for (i = 0; i < screen->nterms; i++)
- {
- screen->terms[i].down = 0;
- screen->terms[i].mode = NO_LINE;
- }
- while (*s)
- {
- if (*s == '\n')
- {
- /* LF is special because it creates a new line. */
- struct line *current_linep;
- struct line *next_linep;
- int size;
- /* Make a new line. */
- screen->num_lines++;
- screen->lines = grub_realloc (screen->lines,
- screen->num_lines
- * sizeof (screen->lines[0]));
- if (! screen->lines)
- return 0;
- /* Shift down if not appending after the last line. */
- if (screen->line < screen->num_lines - 2)
- grub_memmove (screen->lines + screen->line + 2,
- screen->lines + screen->line + 1,
- ((screen->num_lines - screen->line - 2)
- * sizeof (struct line)));
- if (! init_line (screen, screen->lines + screen->line + 1))
- return 0;
- /* Fold the line. */
- current_linep = screen->lines + screen->line;
- next_linep = current_linep + 1;
- size = current_linep->len - screen->column;
- if (! ensure_space (next_linep, size))
- return 0;
- grub_memmove (next_linep->buf,
- current_linep->buf + screen->column,
- size * sizeof (next_linep->buf[0]));
- current_linep->len = screen->column;
- for (i = 0; i < screen->nterms; i++)
- {
- grub_free (current_linep->pos[i]);
- current_linep->pos[i] = 0;
- }
- next_linep->len = size;
- /* Update a dirty region. */
- if (region_start > screen->line)
- {
- region_start = screen->line;
- region_column = screen->column;
- }
- for (i = 0; i < screen->nterms; i++)
- {
- screen->terms[i].mode = ALL_LINES;
- screen->terms[i].down = 1; /* XXX not optimal. */
- }
- /* Move the cursor. */
- screen->column = screen->real_column = 0;
- screen->line++;
- s++;
- }
- else
- {
- /* All but LF. */
- const char *p;
- struct line *current_linep;
- int size;
- grub_uint32_t *unicode_msg;
- /* Find a string delimited by LF. */
- p = grub_strchr (s, '\n');
- if (! p)
- p = s + grub_strlen (s);
- /* Insert the string. */
- current_linep = screen->lines + screen->line;
- unicode_msg = grub_calloc (p - s, sizeof (grub_uint32_t));
- if (!unicode_msg)
- return 0;
- size = grub_utf8_to_ucs4 (unicode_msg, (p - s),
- (grub_uint8_t *) s, (p - s), 0);
- if (! ensure_space (current_linep, size))
- {
- grub_free (unicode_msg);
- return 0;
- }
- grub_memmove (current_linep->buf + screen->column + size,
- current_linep->buf + screen->column,
- (current_linep->len - screen->column)
- * sizeof (current_linep->buf[0]));
- grub_memmove (current_linep->buf + screen->column,
- unicode_msg,
- size * sizeof (current_linep->buf[0]));
- grub_free (unicode_msg);
- for (i = 0; i < screen->nterms; i++)
- {
- grub_free (current_linep->pos[i]);
- current_linep->pos[i] = 0;
- }
- for (i = 0; i < screen->nterms; i++)
- screen->terms[i].orig_num = get_logical_num_lines (current_linep,
- &screen->terms[i]);
- current_linep->len += size;
- /* Update the dirty region. */
- if (region_start > screen->line)
- {
- region_start = screen->line;
- region_column = screen->column;
- }
- for (i = 0; i < screen->nterms; i++)
- {
- int new_num = get_logical_num_lines (current_linep,
- &screen->terms[i]);
- if (screen->terms[i].orig_num != new_num)
- {
- screen->terms[i].mode = ALL_LINES;
- screen->terms[i].down = 1; /* XXX not optimal. */
- }
- else if (screen->terms[i].mode != ALL_LINES)
- screen->terms[i].mode = SINGLE_LINE;
- }
- /* Move the cursor. */
- advance_to (screen, screen->column + size);
- screen->real_column = screen->column;
- s = p;
- }
- }
- if (update)
- for (i = 0; i < screen->nterms; i++)
- update_screen (screen, &screen->terms[i],
- region_start, region_column, 0, screen->terms[i].down,
- screen->terms[i].mode);
- return 1;
- }
- /* Release the resource allocated for SCREEN. */
- static void
- destroy_screen (struct screen *screen)
- {
- int i;
- if (screen->lines)
- for (i = 0; i < screen->num_lines; i++)
- {
- struct line *linep = screen->lines + i;
- if (linep)
- {
- unsigned j;
- if (linep->pos)
- for (j = 0; j < screen->nterms; j++)
- grub_free (linep->pos[j]);
- grub_free (linep->buf);
- grub_free (linep->pos);
- }
- }
- grub_free (screen->killed_text);
- grub_free (screen->lines);
- grub_free (screen->terms);
- grub_free (screen);
- }
- /* Make a new screen. */
- static struct screen *
- make_screen (grub_menu_entry_t entry)
- {
- struct screen *screen;
- unsigned i;
- /* Initialize the screen. */
- screen = grub_zalloc (sizeof (*screen));
- if (! screen)
- return 0;
- screen->submenu = entry->submenu;
- screen->num_lines = 1;
- screen->lines = grub_malloc (sizeof (struct line));
- if (! screen->lines)
- goto fail;
- /* Initialize the first line which must be always present. */
- if (! init_line (screen, screen->lines))
- goto fail;
- insert_string (screen, (char *) entry->sourcecode, 0);
- /* Reset the cursor position. */
- screen->column = 0;
- screen->real_column = 0;
- screen->line = 0;
- for (i = 0; i < screen->nterms; i++)
- {
- screen->terms[i].y_line_start = 0;
- }
- return screen;
- fail:
- destroy_screen (screen);
- return 0;
- }
- static int
- forward_char (struct screen *screen, int update)
- {
- struct line *linep;
- linep = screen->lines + screen->line;
- if (screen->column < linep->len)
- {
- screen->column = grub_unicode_get_comb_end (screen->lines[screen->line].buf
- + screen->lines[screen->line].len,
- screen->lines[screen->line].buf
- + screen->column + 1)
- - screen->lines[screen->line].buf;
- }
- else if (screen->num_lines > screen->line + 1)
- {
- screen->column = 0;
- screen->line++;
- }
- screen->real_column = screen->column;
- if (update)
- update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
- return 1;
- }
- static int
- backward_char (struct screen *screen, int update)
- {
- if (screen->column > 0)
- {
- struct grub_unicode_glyph glyph;
- struct line *linep;
- linep = screen->lines + screen->line;
- screen->column--;
- screen->column = grub_unicode_get_comb_start (linep->buf,
- linep->buf + screen->column)
- - linep->buf;
- grub_unicode_aglomerate_comb (screen->lines[screen->line].buf + screen->column,
- screen->lines[screen->line].len - screen->column,
- &glyph);
- screen->column = grub_unicode_get_comb_start (linep->buf,
- linep->buf + screen->column)
- - linep->buf;
- grub_unicode_destroy_glyph (&glyph);
- }
- else if (screen->line > 0)
- {
- screen->line--;
- screen->column = screen->lines[screen->line].len;
- }
- screen->real_column = screen->column;
- if (update)
- update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
- return 1;
- }
- static int
- previous_line (struct screen *screen, int update)
- {
- if (screen->line > 0)
- {
- struct line *linep;
- int col;
- screen->line--;
- linep = screen->lines + screen->line;
- if (linep->len < screen->real_column)
- col = linep->len;
- else
- col = screen->real_column;
- screen->column = 0;
- advance_to (screen, col);
- }
- else
- {
- screen->column = 0;
- }
- if (update)
- update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
- return 1;
- }
- static int
- next_line (struct screen *screen, int update)
- {
- if (screen->line < screen->num_lines - 1)
- {
- struct line *linep;
- int c;
- /* How many physical lines from the current position
- to the last physical line? */
- linep = screen->lines + screen->line;
- screen->line++;
- if ((linep + 1)->len < screen->real_column)
- c = (linep + 1)->len;
- else
- c = screen->real_column;
- screen->column = 0;
- advance_to (screen, c);
- }
- else
- advance_to (screen, screen->lines[screen->line].len);
- if (update)
- update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
- return 1;
- }
- static int
- beginning_of_line (struct screen *screen, int update)
- {
- screen->column = screen->real_column = 0;
- if (update)
- update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
- return 1;
- }
- static int
- end_of_line (struct screen *screen, int update)
- {
- advance_to (screen, screen->lines[screen->line].len);
- if (update)
- update_screen_all (screen, screen->num_lines, 0, 0, 0, NO_LINE);
- return 1;
- }
- static int
- delete_char (struct screen *screen, int update)
- {
- struct line *linep;
- int start = screen->num_lines;
- int column = 0;
- linep = screen->lines + screen->line;
- if (linep->len > screen->column)
- {
- unsigned i;
- for (i = 0; i < screen->nterms; i++)
- screen->terms[i].orig_num = get_logical_num_lines (linep, &screen->terms[i]);
- grub_memmove (linep->buf + screen->column,
- linep->buf + screen->column + 1,
- (linep->len - screen->column - 1)
- * sizeof (linep->buf[0]));
- linep->len--;
- for (i = 0; i < screen->nterms; i++)
- {
- grub_free (linep->pos[i]);
- linep->pos[i] = 0;
- }
- start = screen->line;
- column = screen->column;
- screen->real_column = screen->column;
- if (update)
- {
- for (i = 0; i < screen->nterms; i++)
- {
- int new_num;
- new_num = get_logical_num_lines (linep, &screen->terms[i]);
- if (screen->terms[i].orig_num != new_num)
- update_screen (screen, &screen->terms[i],
- start, column, 0, 0, ALL_LINES);
- else
- update_screen (screen, &screen->terms[i],
- start, column, 0, 0, SINGLE_LINE);
- }
- }
- }
- else if (screen->num_lines > screen->line + 1)
- {
- struct line *next_linep;
- unsigned i;
- next_linep = linep + 1;
- if (! ensure_space (linep, next_linep->len))
- return 0;
- grub_memmove (linep->buf + linep->len, next_linep->buf,
- next_linep->len * sizeof (linep->buf[0]));
- linep->len += next_linep->len;
- for (i = 0; i < screen->nterms; i++)
- {
- grub_free (linep->pos[i]);
- linep->pos[i] = 0;
- }
- grub_free (next_linep->buf);
- grub_memmove (next_linep,
- next_linep + 1,
- (screen->num_lines - screen->line - 2)
- * sizeof (struct line));
- screen->num_lines--;
- start = screen->line;
- column = screen->column;
- screen->real_column = screen->column;
- if (update)
- update_screen_all (screen, start, column, 0, 1, ALL_LINES);
- }
- return 1;
- }
- static int
- backward_delete_char (struct screen *screen, int update)
- {
- int saved_column;
- int saved_line;
- saved_column = screen->column;
- saved_line = screen->line;
- if (! backward_char (screen, 0))
- return 0;
- if (saved_column != screen->column || saved_line != screen->line)
- if (! delete_char (screen, update))
- return 0;
- return 1;
- }
- static int
- kill_line (struct screen *screen, int continuous, int update)
- {
- struct line *linep;
- char *p;
- int size;
- int offset;
- p = screen->killed_text;
- if (! continuous && p)
- p[0] = '\0';
- linep = screen->lines + screen->line;
- size = linep->len - screen->column;
- if (p)
- offset = grub_strlen (p);
- else
- offset = 0;
- if (size > 0)
- {
- unsigned i;
- p = grub_realloc (p, offset + size + 1);
- if (! p)
- return 0;
- grub_memmove (p + offset, linep->buf + screen->column, size);
- p[offset + size] = '\0';
- screen->killed_text = p;
- for (i = 0; i < screen->nterms; i++)
- screen->terms[i].orig_num = get_logical_num_lines (linep, &screen->terms[i]);
- linep->len = screen->column;
- if (update)
- {
- for (i = 0; i < screen->nterms; i++)
- {
- int new_num;
- new_num = get_logical_num_lines (linep, &screen->terms[i]);
- if (screen->terms[i].orig_num != new_num)
- update_screen (screen, &screen->terms[i],
- screen->line, screen->column, 0, 1, ALL_LINES);
- else
- update_screen (screen, &screen->terms[i],
- screen->line, screen->column, 0, 0, SINGLE_LINE);
- }
- }
- }
- else if (screen->line + 1 < screen->num_lines)
- {
- p = grub_realloc (p, offset + 1 + 1);
- if (! p)
- return 0;
- p[offset] = '\n';
- p[offset + 1] = '\0';
- screen->killed_text = p;
- return delete_char (screen, update);
- }
- return 1;
- }
- static int
- yank (struct screen *screen, int update)
- {
- if (screen->killed_text)
- return insert_string (screen, screen->killed_text, update);
- return 1;
- }
- static int
- open_line (struct screen *screen, int update)
- {
- if (! insert_string (screen, "\n", 0))
- return 0;
- if (! backward_char (screen, 0))
- return 0;
- if (update)
- update_screen_all (screen, screen->line, screen->column, 0, 1, ALL_LINES);
- return 1;
- }
- /* A completion hook to print items. */
- static void
- store_completion (const char *item, grub_completion_type_t type,
- int count __attribute__ ((unused)))
- {
- char *p;
- completion_type = type;
- /* Make sure that the completion buffer has enough room. */
- if (completion_buffer.max_len < (completion_buffer.len
- + (int) grub_strlen (item) + 1 + 1))
- {
- grub_size_t new_len;
- new_len = completion_buffer.len + grub_strlen (item) + 80;
- p = grub_realloc (completion_buffer.buf, new_len);
- if (! p)
- {
- /* Possibly not fatal. */
- grub_errno = GRUB_ERR_NONE;
- return;
- }
- p[completion_buffer.len] = 0;
- completion_buffer.buf = p;
- completion_buffer.max_len = new_len;
- }
- p = completion_buffer.buf + completion_buffer.len;
- if (completion_buffer.len != 0)
- {
- *p++ = ' ';
- completion_buffer.len++;
- }
- grub_strcpy (p, item);
- completion_buffer.len += grub_strlen (item);
- }
- static int
- complete (struct screen *screen, int continuous, int update)
- {
- struct line *linep;
- int restore;
- char *insert;
- static int count = -1;
- unsigned i;
- grub_uint32_t *ucs4;
- grub_size_t buflen;
- grub_ssize_t ucs4len;
- char *u8;
- if (continuous)
- count++;
- else
- count = 0;
- completion_buffer.buf = 0;
- completion_buffer.len = 0;
- completion_buffer.max_len = 0;
- linep = screen->lines + screen->line;
- u8 = grub_ucs4_to_utf8_alloc (linep->buf, screen->column);
- if (!u8)
- return 1;
- insert = grub_normal_do_completion (u8, &restore, store_completion);
- if (completion_buffer.buf)
- {
- buflen = grub_strlen (completion_buffer.buf);
- ucs4 = grub_calloc (buflen + 1, sizeof (grub_uint32_t));
- if (!ucs4)
- {
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
- return 1;
- }
- ucs4len = grub_utf8_to_ucs4 (ucs4, buflen,
- (grub_uint8_t *) completion_buffer.buf,
- buflen, 0);
- ucs4[ucs4len] = 0;
- if (restore)
- for (i = 0; i < screen->nterms; i++)
- {
- unsigned width = grub_term_width (screen->terms[i].term);
- if (width > 2)
- width -= 2;
- if (width > 15)
- width -= 6;
- unsigned num_sections = ((completion_buffer.len
- + width - 1)
- / width);
- grub_uint32_t *endp;
- struct grub_term_coordinate pos;
- grub_uint32_t *p = ucs4;
- pos = grub_term_getxy (screen->terms[i].term);
- screen->completion_shown = 1;
- grub_term_gotoxy (screen->terms[i].term,
- (struct grub_term_coordinate) { 0,
- screen->terms[i].geo.timeout_y });
- if (screen->terms[i].geo.timeout_lines >= 2)
- {
- grub_puts_terminal (" ", screen->terms[i].term);
- switch (completion_type)
- {
- case GRUB_COMPLETION_TYPE_COMMAND:
- grub_puts_terminal (_("Possible commands are:"),
- screen->terms[i].term);
- break;
- case GRUB_COMPLETION_TYPE_DEVICE:
- grub_puts_terminal (_("Possible devices are:"),
- screen->terms[i].term);
- break;
- case GRUB_COMPLETION_TYPE_FILE:
- grub_puts_terminal (_("Possible files are:"),
- screen->terms[i].term);
- break;
- case GRUB_COMPLETION_TYPE_PARTITION:
- grub_puts_terminal (_("Possible partitions are:"),
- screen->terms[i].term);
- break;
- case GRUB_COMPLETION_TYPE_ARGUMENT:
- grub_puts_terminal (_("Possible arguments are:"),
- screen->terms[i].term);
- break;
- default:
- grub_puts_terminal (_("Possible things are:"),
- screen->terms[i].term);
- break;
- }
- grub_puts_terminal ("\n ", screen->terms[i].term);
- }
- p += ((unsigned) count % num_sections) * width;
- endp = p + width;
- if (p != ucs4)
- grub_putcode (GRUB_UNICODE_LEFTARROW, screen->terms[i].term);
- else
- grub_putcode (' ', screen->terms[i].term);
- grub_print_ucs4 (p, ucs4 + ucs4len < endp ? ucs4 + ucs4len : endp,
- 0, 0, screen->terms[i].term);
- if (ucs4 + ucs4len > endp)
- grub_putcode (GRUB_UNICODE_RIGHTARROW, screen->terms[i].term);
- grub_term_gotoxy (screen->terms[i].term, pos);
- }
- }
- if (insert)
- {
- insert_string (screen, insert, update);
- count = -1;
- grub_free (insert);
- }
- else if (update)
- grub_refresh ();
- grub_free (completion_buffer.buf);
- return 1;
- }
- /* Clear displayed completions. */
- static void
- clear_completions (struct per_term_screen *term_screen)
- {
- struct grub_term_coordinate pos;
- unsigned j;
- int i;
- pos = grub_term_getxy (term_screen->term);
- grub_term_gotoxy (term_screen->term,
- (struct grub_term_coordinate) { 0,
- term_screen->geo.timeout_y });
- for (i = 0; i < term_screen->geo.timeout_lines; i++)
- {
- for (j = 0; j < grub_term_width (term_screen->term) - 1; j++)
- grub_putcode (' ', term_screen->term);
- if (i + 1 < term_screen->geo.timeout_lines)
- grub_putcode ('\n', term_screen->term);
- }
- grub_term_gotoxy (term_screen->term, pos);
- grub_term_refresh (term_screen->term);
- }
- static void
- clear_completions_all (struct screen *screen)
- {
- unsigned i;
- for (i = 0; i < screen->nterms; i++)
- clear_completions (&screen->terms[i]);
- }
- /* Execute the command list in the screen SCREEN. */
- static int
- run (struct screen *screen)
- {
- char *script;
- int errs_before;
- grub_menu_t menu = NULL;
- char *dummy[1] = { NULL };
- grub_cls ();
- grub_printf (" ");
- grub_printf_ (N_("Booting a command list"));
- grub_printf ("\n\n");
- errs_before = grub_err_printed_errors;
- if (screen->submenu)
- {
- grub_env_context_open ();
- menu = grub_zalloc (sizeof (*menu));
- if (! menu)
- return 0;
- grub_env_set_menu (menu);
- }
- /* Execute the script, line for line. */
- {
- int i;
- grub_size_t size = 0, tot_size = 0;
- for (i = 0; i < screen->num_lines; i++)
- tot_size += grub_get_num_of_utf8_bytes (screen->lines[i].buf,
- screen->lines[i].len) + 1;
- script = grub_malloc (tot_size + 1);
- if (! script)
- return 0;
- for (i = 0; i < screen->num_lines; i++)
- {
- size += grub_ucs4_to_utf8 (screen->lines[i].buf, screen->lines[i].len,
- (grub_uint8_t *) script + size,
- tot_size - size);
- script[size++] = '\n';
- }
- script[size] = '\0';
- }
- grub_script_execute_new_scope (script, 0, dummy);
- grub_free (script);
- if (errs_before != grub_err_printed_errors)
- grub_wait_after_message ();
- if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
- /* Implicit execution of boot, only if something is loaded. */
- grub_command_execute ("boot", 0, 0);
- if (screen->submenu)
- {
- if (menu && menu->size)
- {
- grub_show_menu (menu, 1, 0);
- grub_normal_free_menu (menu);
- }
- grub_env_context_close ();
- }
- if (grub_errno != GRUB_ERR_NONE)
- {
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
- grub_wait_after_message ();
- }
- return 1;
- }
- /* Edit a menu entry with an Emacs-like interface. */
- void
- grub_menu_entry_run (grub_menu_entry_t entry)
- {
- struct screen *screen;
- int prev_c;
- grub_err_t err = GRUB_ERR_NONE;
- unsigned i;
- grub_term_output_t term;
- err = grub_auth_check_authentication (NULL);
- if (err)
- {
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
- return;
- }
- screen = make_screen (entry);
- if (! screen)
- return;
- screen->terms = NULL;
- refresh:
- grub_free (screen->terms);
- screen->nterms = 0;
- FOR_ACTIVE_TERM_OUTPUTS(term)
- screen->nterms++;
- for (i = 0; i < (unsigned) screen->num_lines; i++)
- {
- grub_free (screen->lines[i].pos);
- screen->lines[i].pos = grub_calloc (screen->nterms, sizeof (screen->lines[i].pos[0]));
- if (! screen->lines[i].pos)
- {
- grub_print_error ();
- destroy_screen (screen);
- grub_errno = GRUB_ERR_NONE;
- return;
- }
- }
- screen->terms = grub_calloc (screen->nterms, sizeof (screen->terms[0]));
- if (!screen->terms)
- {
- grub_print_error ();
- destroy_screen (screen);
- grub_errno = GRUB_ERR_NONE;
- return;
- }
- i = 0;
- FOR_ACTIVE_TERM_OUTPUTS(term)
- {
- screen->terms[i].term = term;
- screen->terms[i].y_line_start = 0;
- i++;
- }
- /* Draw the screen. */
- for (i = 0; i < screen->nterms; i++)
- grub_menu_init_page (0, 1, &screen->terms[i].geo,
- screen->terms[i].term);
- update_screen_all (screen, 0, 0, 1, 1, ALL_LINES);
- for (i = 0; i < screen->nterms; i++)
- grub_term_setcursor (screen->terms[i].term, 1);
- prev_c = '\0';
- while (1)
- {
- int c = grub_getkey ();
- if (screen->completion_shown)
- {
- clear_completions_all (screen);
- screen->completion_shown = 0;
- }
- if (grub_normal_exit_level)
- {
- destroy_screen (screen);
- return;
- }
- switch (c)
- {
- case GRUB_TERM_KEY_UP:
- case GRUB_TERM_CTRL | 'p':
- if (! previous_line (screen, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'n':
- case GRUB_TERM_KEY_DOWN:
- if (! next_line (screen, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'f':
- case GRUB_TERM_KEY_RIGHT:
- if (! forward_char (screen, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'b':
- case GRUB_TERM_KEY_LEFT:
- if (! backward_char (screen, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'a':
- case GRUB_TERM_KEY_HOME:
- if (! beginning_of_line (screen, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'e':
- case GRUB_TERM_KEY_END:
- if (! end_of_line (screen, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'i':
- case '\t':
- if (! complete (screen, prev_c == c, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'd':
- case GRUB_TERM_KEY_DC:
- if (! delete_char (screen, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'h':
- case '\b':
- if (! backward_delete_char (screen, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'k':
- if (! kill_line (screen, prev_c == c, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'u':
- /* FIXME: What behavior is good for this key? */
- break;
- case GRUB_TERM_CTRL | 'y':
- if (! yank (screen, 1))
- goto fail;
- break;
- case GRUB_TERM_CTRL | 'l':
- /* FIXME: centering. */
- goto refresh;
- case GRUB_TERM_CTRL | 'o':
- if (! open_line (screen, 1))
- goto fail;
- break;
- case '\n':
- case '\r':
- if (! insert_string (screen, "\n", 1))
- goto fail;
- break;
- case GRUB_TERM_ESC:
- destroy_screen (screen);
- return;
- case GRUB_TERM_CTRL | 'c':
- case GRUB_TERM_KEY_F2:
- grub_cmdline_run (1, 0);
- goto refresh;
- case GRUB_TERM_CTRL | 'x':
- case GRUB_TERM_KEY_F10:
- run (screen);
- goto refresh;
- case GRUB_TERM_CTRL | 'r':
- case GRUB_TERM_CTRL | 's':
- case GRUB_TERM_CTRL | 't':
- /* FIXME */
- break;
- default:
- if (grub_isprint (c))
- {
- char buf[2];
- buf[0] = c;
- buf[1] = '\0';
- if (! insert_string (screen, buf, 1))
- goto fail;
- }
- break;
- }
- prev_c = c;
- }
- fail:
- destroy_screen (screen);
- grub_cls ();
- grub_print_error ();
- grub_errno = GRUB_ERR_NONE;
- grub_xputs ("\n");
- grub_printf_ (N_("Press any key to continue..."));
- (void) grub_getkey ();
- }
|