/* * VAS_EBOOT -- GRand Unified Bootloader * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2009 Free Software Foundation, Inc. * * VAS_EBOOT 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. * * VAS_EBOOT 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 VAS_EBOOT. If not, see . */ #include #include #include #include #include #include #include #include #include #include #include #include #include static VasEBoot_uint32_t *kill_buf; static int hist_size; static VasEBoot_uint32_t **hist_lines = 0; static int hist_pos = 0; static int hist_end = 0; static int hist_used = 0; VasEBoot_err_t VasEBoot_set_history (int newsize) { VasEBoot_uint32_t **old_hist_lines = hist_lines; hist_lines = VasEBoot_calloc (newsize, sizeof (VasEBoot_uint32_t *)); if (hist_lines == NULL) { /* We need to restore hist_lines to avoid memory leak and state loss. */ hist_lines = old_hist_lines; return VasEBoot_errno; } /* Copy the old lines into the new buffer. */ if (old_hist_lines) { /* Remove the lines that don't fit in the new buffer. */ if (newsize < hist_used) { VasEBoot_size_t i; VasEBoot_size_t delsize = hist_used - newsize; hist_used = newsize; for (i = 1; i < delsize + 1; i++) { VasEBoot_ssize_t pos = hist_end - i; if (pos < 0) pos += hist_size; VasEBoot_free (old_hist_lines[pos]); } hist_end -= delsize; if (hist_end < 0) hist_end += hist_size; } if (hist_pos < hist_end) VasEBoot_memmove (hist_lines, old_hist_lines + hist_pos, (hist_end - hist_pos) * sizeof (VasEBoot_uint32_t *)); else if (hist_used) { /* Copy the older part. */ VasEBoot_memmove (hist_lines, old_hist_lines + hist_pos, (hist_size - hist_pos) * sizeof (VasEBoot_uint32_t *)); /* Copy the newer part. */ VasEBoot_memmove (hist_lines + hist_size - hist_pos, old_hist_lines, hist_end * sizeof (VasEBoot_uint32_t *)); } } VasEBoot_free (old_hist_lines); hist_size = newsize; hist_pos = 0; hist_end = hist_used; return 0; } /* Get the entry POS from the history where `0' is the newest entry. */ static VasEBoot_uint32_t * VasEBoot_history_get (unsigned pos) { pos = (hist_pos + pos) % hist_size; return hist_lines[pos]; } static VasEBoot_size_t strlen_ucs4 (const VasEBoot_uint32_t *s) { const VasEBoot_uint32_t *p = s; while (*p) p++; return p - s; } /* Replace the history entry on position POS with the string S. */ static void VasEBoot_history_set (int pos, VasEBoot_uint32_t *s, VasEBoot_size_t len) { VasEBoot_free (hist_lines[pos]); hist_lines[pos] = VasEBoot_calloc (len + 1, sizeof (VasEBoot_uint32_t)); if (!hist_lines[pos]) { VasEBoot_print_error (); VasEBoot_errno = VAS_EBOOT_ERR_NONE; return ; } VasEBoot_memcpy (hist_lines[pos], s, len * sizeof (VasEBoot_uint32_t)); hist_lines[pos][len] = 0; } /* Insert a new history line S on the top of the history. */ static void VasEBoot_history_add (VasEBoot_uint32_t *s, VasEBoot_size_t len) { /* Remove the oldest entry in the history to make room for a new entry. */ if (hist_used + 1 > hist_size) { hist_end--; if (hist_end < 0) hist_end = hist_size + hist_end; VasEBoot_free (hist_lines[hist_end]); } else hist_used++; /* Move to the next position. */ hist_pos--; if (hist_pos < 0) hist_pos = hist_size + hist_pos; /* Insert into history. */ hist_lines[hist_pos] = NULL; VasEBoot_history_set (hist_pos, s, len); } /* Replace the history entry on position POS with the string S. */ static void VasEBoot_history_replace (unsigned pos, VasEBoot_uint32_t *s, VasEBoot_size_t len) { VasEBoot_history_set ((hist_pos + pos) % hist_size, s, len); } /* A completion hook to print items. */ static void print_completion (const char *item, VasEBoot_completion_type_t type, int count) { if (count == 0) { /* If this is the first time, print a label. */ VasEBoot_puts (""); switch (type) { case VAS_EBOOT_COMPLETION_TYPE_COMMAND: VasEBoot_puts_ (N_("Possible commands are:")); break; case VAS_EBOOT_COMPLETION_TYPE_DEVICE: VasEBoot_puts_ (N_("Possible devices are:")); break; case VAS_EBOOT_COMPLETION_TYPE_FILE: VasEBoot_puts_ (N_("Possible files are:")); break; case VAS_EBOOT_COMPLETION_TYPE_PARTITION: VasEBoot_puts_ (N_("Possible partitions are:")); break; case VAS_EBOOT_COMPLETION_TYPE_ARGUMENT: VasEBoot_puts_ (N_("Possible arguments are:")); break; default: /* TRANSLATORS: this message is used if none of above matches. This shouldn't happen but please use the general term for "thing" or "object". */ VasEBoot_puts_ (N_("Possible things are:")); break; } VasEBoot_puts (""); } if (type == VAS_EBOOT_COMPLETION_TYPE_PARTITION) { VasEBoot_normal_print_device_info (item); VasEBoot_errno = VAS_EBOOT_ERR_NONE; } else VasEBoot_printf (" %s", item); } struct cmdline_term { struct VasEBoot_term_coordinate pos; unsigned ystart, width, height; unsigned prompt_len; struct VasEBoot_term_output *term; }; static inline void cl_set_pos (struct cmdline_term *cl_term, VasEBoot_size_t lpos) { cl_term->pos.x = (cl_term->prompt_len + lpos) % cl_term->width; cl_term->pos.y = cl_term->ystart + (cl_term->prompt_len + lpos) / cl_term->width; if (cl_term->pos.y >= cl_term->height) cl_term->pos.y = cl_term->height - 1; VasEBoot_term_gotoxy (cl_term->term, cl_term->pos); } static void cl_set_pos_all (struct cmdline_term *cl_terms, unsigned nterms, VasEBoot_size_t lpos) { unsigned i; for (i = 0; i < nterms; i++) cl_set_pos (&cl_terms[i], lpos); } static inline void __attribute__ ((always_inline)) cl_print (struct cmdline_term *cl_term, VasEBoot_uint32_t c, VasEBoot_uint32_t *start, VasEBoot_uint32_t *end) { VasEBoot_uint32_t *p; for (p = start; p < end; p++) { if (c) VasEBoot_putcode (c, cl_term->term); else VasEBoot_putcode (*p, cl_term->term); cl_term->pos.x++; if (cl_term->pos.x >= cl_term->width - 1) { cl_term->pos.x = 0; if (cl_term->pos.y >= (unsigned) (cl_term->height - 1)) { if (cl_term->ystart > 0) cl_term->ystart--; } else cl_term->pos.y++; VasEBoot_putcode ('\n', cl_term->term); } } } static void cl_print_all (struct cmdline_term *cl_terms, unsigned nterms, VasEBoot_uint32_t c, VasEBoot_uint32_t *start, VasEBoot_uint32_t *end) { unsigned i; for (i = 0; i < nterms; i++) cl_print (&cl_terms[i], c, start, end); } static void init_clterm (struct cmdline_term *cl_term_cur) { cl_term_cur->pos.x = cl_term_cur->prompt_len; cl_term_cur->pos.y = VasEBoot_term_getxy (cl_term_cur->term).y; cl_term_cur->ystart = cl_term_cur->pos.y; cl_term_cur->width = VasEBoot_term_width (cl_term_cur->term); cl_term_cur->height = VasEBoot_term_height (cl_term_cur->term); } static void cl_delete (struct cmdline_term *cl_terms, unsigned nterms, VasEBoot_uint32_t *buf, VasEBoot_size_t lpos, VasEBoot_size_t *llen, unsigned len) { if (lpos + len <= (*llen)) { cl_set_pos_all (cl_terms, nterms, (*llen) - len); cl_print_all (cl_terms, nterms, ' ', buf + (*llen) - len, buf + (*llen)); cl_set_pos_all (cl_terms, nterms, lpos); VasEBoot_memmove (buf + lpos, buf + lpos + len, sizeof (VasEBoot_uint32_t) * ((*llen) - lpos + 1)); (*llen) -= len; cl_print_all (cl_terms, nterms, 0, buf + lpos, buf + (*llen)); cl_set_pos_all (cl_terms, nterms, lpos); } } static void cl_insert (struct cmdline_term *cl_terms, unsigned nterms, VasEBoot_size_t *lpos, VasEBoot_size_t *llen, VasEBoot_size_t *max_len, VasEBoot_uint32_t **buf, const VasEBoot_uint32_t *str) { VasEBoot_size_t len = strlen_ucs4 (str); if (len + (*llen) >= (*max_len)) { VasEBoot_uint32_t *nbuf; VasEBoot_size_t sz; if (VasEBoot_mul (*max_len, 2, max_len) || VasEBoot_mul (*max_len, sizeof (VasEBoot_uint32_t), &sz)) { VasEBoot_errno = VAS_EBOOT_ERR_OUT_OF_RANGE; goto fail; } nbuf = VasEBoot_realloc ((*buf), sz); if (nbuf) (*buf) = nbuf; else { fail: VasEBoot_print_error (); VasEBoot_errno = VAS_EBOOT_ERR_NONE; (*max_len) /= 2; } } if (len + (*llen) < (*max_len)) { VasEBoot_memmove ((*buf) + (*lpos) + len, (*buf) + (*lpos), ((*llen) - (*lpos) + 1) * sizeof (VasEBoot_uint32_t)); VasEBoot_memmove ((*buf) + (*lpos), str, len * sizeof (VasEBoot_uint32_t)); (*llen) += len; cl_set_pos_all (cl_terms, nterms, (*lpos)); cl_print_all (cl_terms, nterms, 0, *buf + (*lpos), *buf + (*llen)); (*lpos) += len; cl_set_pos_all (cl_terms, nterms, (*lpos)); } } /* Get a command-line. If ESC is pushed, return zero, otherwise return command line. */ /* FIXME: The dumb interface is not supported yet. */ char * VasEBoot_cmdline_get (const char *prompt_translated) { VasEBoot_size_t lpos, llen; VasEBoot_uint32_t *buf; VasEBoot_size_t max_len = 256; int key; int histpos = 0; struct cmdline_term *cl_terms; char *ret; unsigned nterms; buf = VasEBoot_calloc (max_len, sizeof (VasEBoot_uint32_t)); if (!buf) return 0; lpos = llen = 0; buf[0] = '\0'; { VasEBoot_term_output_t term; FOR_ACTIVE_TERM_OUTPUTS(term) if ((VasEBoot_term_getxy (term).x) != 0) VasEBoot_putcode ('\n', term); } VasEBoot_xputs (prompt_translated); VasEBoot_xputs (" "); VasEBoot_normal_reset_more (); { struct cmdline_term *cl_term_cur; struct VasEBoot_term_output *cur; VasEBoot_uint32_t *unicode_msg; VasEBoot_size_t msg_len = VasEBoot_strlen (prompt_translated) + 3; nterms = 0; FOR_ACTIVE_TERM_OUTPUTS(cur) nterms++; cl_terms = VasEBoot_calloc (nterms, sizeof (cl_terms[0])); if (!cl_terms) { VasEBoot_free (buf); return 0; } cl_term_cur = cl_terms; unicode_msg = VasEBoot_calloc (msg_len, sizeof (VasEBoot_uint32_t)); if (!unicode_msg) { VasEBoot_free (buf); VasEBoot_free (cl_terms); return 0; } msg_len = VasEBoot_utf8_to_ucs4 (unicode_msg, msg_len - 1, (VasEBoot_uint8_t *) prompt_translated, -1, 0); unicode_msg[msg_len++] = ' '; FOR_ACTIVE_TERM_OUTPUTS(cur) { cl_term_cur->term = cur; cl_term_cur->prompt_len = VasEBoot_getstringwidth (unicode_msg, unicode_msg + msg_len, cur); init_clterm (cl_term_cur); cl_term_cur++; } VasEBoot_free (unicode_msg); } if (hist_used == 0) VasEBoot_history_add (buf, llen); VasEBoot_refresh (); while ((key = VasEBoot_getkey ()) != '\n' && key != '\r') { switch (key) { case VAS_EBOOT_TERM_CTRL | 'a': case VAS_EBOOT_TERM_KEY_HOME: lpos = 0; cl_set_pos_all (cl_terms, nterms, lpos); break; case VAS_EBOOT_TERM_CTRL | 'b': case VAS_EBOOT_TERM_KEY_LEFT: if (lpos > 0) { lpos--; cl_set_pos_all (cl_terms, nterms, lpos); } break; case VAS_EBOOT_TERM_CTRL | 'e': case VAS_EBOOT_TERM_KEY_END: lpos = llen; cl_set_pos_all (cl_terms, nterms, lpos); break; case VAS_EBOOT_TERM_CTRL | 'f': case VAS_EBOOT_TERM_KEY_RIGHT: if (lpos < llen) { lpos++; cl_set_pos_all (cl_terms, nterms, lpos); } break; case VAS_EBOOT_TERM_CTRL | 'i': case '\t': { int restore; char *insertu8; char *bufu8; VasEBoot_uint32_t c; c = buf[lpos]; buf[lpos] = '\0'; bufu8 = VasEBoot_ucs4_to_utf8_alloc (buf, lpos); buf[lpos] = c; if (!bufu8) { VasEBoot_print_error (); VasEBoot_errno = VAS_EBOOT_ERR_NONE; break; } insertu8 = VasEBoot_normal_do_completion (bufu8, &restore, print_completion); VasEBoot_free (bufu8); VasEBoot_normal_reset_more (); if (restore) { unsigned i; /* Restore the prompt. */ VasEBoot_xputs ("\n"); VasEBoot_xputs (prompt_translated); VasEBoot_xputs (" "); for (i = 0; i < nterms; i++) init_clterm (&cl_terms[i]); cl_print_all (cl_terms, nterms, 0, buf, buf + llen); } if (insertu8) { VasEBoot_size_t insertlen; VasEBoot_ssize_t t; VasEBoot_uint32_t *insert; insertlen = VasEBoot_strlen (insertu8); insert = VasEBoot_calloc (insertlen + 1, sizeof (VasEBoot_uint32_t)); if (!insert) { VasEBoot_free (insertu8); VasEBoot_print_error (); VasEBoot_errno = VAS_EBOOT_ERR_NONE; break; } t = VasEBoot_utf8_to_ucs4 (insert, insertlen, (VasEBoot_uint8_t *) insertu8, insertlen, 0); if (t > 0) { if (insert[t-1] == ' ' && buf[lpos] == ' ') { insert[t-1] = 0; if (t != 1) cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, insert); lpos++; } else { insert[t] = 0; cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, insert); } } VasEBoot_free (insertu8); VasEBoot_free (insert); } cl_set_pos_all (cl_terms, nterms, lpos); } break; case VAS_EBOOT_TERM_CTRL | 'k': if (lpos < llen) { VasEBoot_free (kill_buf); kill_buf = VasEBoot_malloc ((llen - lpos + 1) * sizeof (VasEBoot_uint32_t)); if (VasEBoot_errno) { VasEBoot_print_error (); VasEBoot_errno = VAS_EBOOT_ERR_NONE; } else { VasEBoot_memcpy (kill_buf, buf + lpos, (llen - lpos + 1) * sizeof (VasEBoot_uint32_t)); kill_buf[llen - lpos] = 0; } cl_delete (cl_terms, nterms, buf, lpos, &llen, llen - lpos); } break; case VAS_EBOOT_TERM_CTRL | 'n': case VAS_EBOOT_TERM_KEY_DOWN: { VasEBoot_uint32_t *hist; lpos = 0; if (histpos > 0) { VasEBoot_history_replace (histpos, buf, llen); histpos--; } cl_delete (cl_terms, nterms, buf, lpos, &llen, llen); hist = VasEBoot_history_get (histpos); cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, hist); break; } case VAS_EBOOT_TERM_KEY_UP: case VAS_EBOOT_TERM_CTRL | 'p': { VasEBoot_uint32_t *hist; lpos = 0; if (histpos < hist_used - 1) { VasEBoot_history_replace (histpos, buf, llen); histpos++; } cl_delete (cl_terms, nterms, buf, lpos, &llen, llen); hist = VasEBoot_history_get (histpos); cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, hist); } break; case VAS_EBOOT_TERM_CTRL | 'u': if (lpos > 0) { VasEBoot_size_t n = lpos; VasEBoot_free (kill_buf); kill_buf = VasEBoot_calloc (n + 1, sizeof (VasEBoot_uint32_t)); if (VasEBoot_errno) { VasEBoot_print_error (); VasEBoot_errno = VAS_EBOOT_ERR_NONE; } if (kill_buf) { VasEBoot_memcpy (kill_buf, buf, n * sizeof(VasEBoot_uint32_t)); kill_buf[n] = 0; } lpos = 0; cl_set_pos_all (cl_terms, nterms, lpos); cl_delete (cl_terms, nterms, buf, lpos, &llen, n); } break; case VAS_EBOOT_TERM_CTRL | 'y': if (kill_buf) cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, kill_buf); break; case VAS_EBOOT_TERM_ESC: VasEBoot_free (cl_terms); VasEBoot_free (buf); return 0; case VAS_EBOOT_TERM_BACKSPACE: if (lpos > 0) { lpos--; cl_set_pos_all (cl_terms, nterms, lpos); } else break; /* fall through */ case VAS_EBOOT_TERM_CTRL | 'd': case VAS_EBOOT_TERM_KEY_DC: if (lpos < llen) cl_delete (cl_terms, nterms, buf, lpos, &llen, 1); break; default: if (VasEBoot_isprint (key)) { VasEBoot_uint32_t str[2]; str[0] = key; str[1] = '\0'; cl_insert (cl_terms, nterms, &lpos, &llen, &max_len, &buf, str); } break; } VasEBoot_refresh (); } VasEBoot_xputs ("\n"); VasEBoot_refresh (); histpos = 0; if (strlen_ucs4 (buf) > 0) { VasEBoot_uint32_t empty[] = { 0 }; VasEBoot_history_replace (histpos, buf, llen); VasEBoot_history_add (empty, 0); } ret = VasEBoot_ucs4_to_utf8_alloc (buf, llen + 1); VasEBoot_free (buf); VasEBoot_free (cl_terms); return ret; }