Merge mainline into mbivid
This commit is contained in:
commit
9ba27423f5
931 changed files with 122070 additions and 40781 deletions
250
grub-core/normal/auth.c
Normal file
250
grub-core/normal/auth.c
Normal file
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 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/auth.h>
|
||||
#include <grub/list.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/i18n.h>
|
||||
|
||||
struct grub_auth_user
|
||||
{
|
||||
struct grub_auth_user *next;
|
||||
char *name;
|
||||
grub_auth_callback_t callback;
|
||||
void *arg;
|
||||
int authenticated;
|
||||
};
|
||||
|
||||
struct grub_auth_user *users = NULL;
|
||||
|
||||
grub_err_t
|
||||
grub_auth_register_authentication (const char *user,
|
||||
grub_auth_callback_t callback,
|
||||
void *arg)
|
||||
{
|
||||
struct grub_auth_user *cur;
|
||||
|
||||
cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
|
||||
if (!cur)
|
||||
cur = grub_zalloc (sizeof (*cur));
|
||||
if (!cur)
|
||||
return grub_errno;
|
||||
cur->callback = callback;
|
||||
cur->arg = arg;
|
||||
if (! cur->name)
|
||||
{
|
||||
cur->name = grub_strdup (user);
|
||||
if (!cur->name)
|
||||
{
|
||||
grub_free (cur);
|
||||
return grub_errno;
|
||||
}
|
||||
grub_list_push (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
|
||||
}
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_auth_unregister_authentication (const char *user)
|
||||
{
|
||||
struct grub_auth_user *cur;
|
||||
cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
|
||||
if (!cur)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "user '%s' not found", user);
|
||||
if (!cur->authenticated)
|
||||
{
|
||||
grub_free (cur->name);
|
||||
grub_list_remove (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
|
||||
grub_free (cur);
|
||||
}
|
||||
else
|
||||
{
|
||||
cur->callback = NULL;
|
||||
cur->arg = NULL;
|
||||
}
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_auth_authenticate (const char *user)
|
||||
{
|
||||
struct grub_auth_user *cur;
|
||||
|
||||
cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
|
||||
if (!cur)
|
||||
cur = grub_zalloc (sizeof (*cur));
|
||||
if (!cur)
|
||||
return grub_errno;
|
||||
|
||||
cur->authenticated = 1;
|
||||
|
||||
if (! cur->name)
|
||||
{
|
||||
cur->name = grub_strdup (user);
|
||||
if (!cur->name)
|
||||
{
|
||||
grub_free (cur);
|
||||
return grub_errno;
|
||||
}
|
||||
grub_list_push (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_auth_deauthenticate (const char *user)
|
||||
{
|
||||
struct grub_auth_user *cur;
|
||||
cur = grub_named_list_find (GRUB_AS_NAMED_LIST (users), user);
|
||||
if (!cur)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "user '%s' not found", user);
|
||||
if (!cur->callback)
|
||||
{
|
||||
grub_free (cur->name);
|
||||
grub_list_remove (GRUB_AS_LIST_P (&users), GRUB_AS_LIST (cur));
|
||||
grub_free (cur);
|
||||
}
|
||||
else
|
||||
cur->authenticated = 0;
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static int
|
||||
is_authenticated (const char *userlist)
|
||||
{
|
||||
const char *superusers;
|
||||
struct grub_auth_user *user;
|
||||
|
||||
superusers = grub_env_get ("superusers");
|
||||
|
||||
if (!superusers)
|
||||
return 1;
|
||||
|
||||
FOR_LIST_ELEMENTS (user, users)
|
||||
{
|
||||
if (!(user->authenticated))
|
||||
continue;
|
||||
|
||||
if ((userlist && grub_strword (userlist, user->name))
|
||||
|| grub_strword (superusers, user->name))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
grub_username_get (char buf[], unsigned buf_size)
|
||||
{
|
||||
unsigned cur_len = 0;
|
||||
int key;
|
||||
|
||||
while (1)
|
||||
{
|
||||
key = GRUB_TERM_ASCII_CHAR (grub_getkey ());
|
||||
if (key == '\n' || key == '\r')
|
||||
break;
|
||||
|
||||
if (key == '\e')
|
||||
{
|
||||
cur_len = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
if (key == '\b')
|
||||
{
|
||||
cur_len--;
|
||||
grub_printf ("\b");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!grub_isprint (key))
|
||||
continue;
|
||||
|
||||
if (cur_len + 2 < buf_size)
|
||||
{
|
||||
buf[cur_len++] = key;
|
||||
grub_printf ("%c", key);
|
||||
}
|
||||
}
|
||||
|
||||
grub_memset (buf + cur_len, 0, buf_size - cur_len);
|
||||
|
||||
grub_xputs ("\n");
|
||||
grub_refresh ();
|
||||
|
||||
return (key != '\e');
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_auth_check_authentication (const char *userlist)
|
||||
{
|
||||
char login[1024];
|
||||
struct grub_auth_user *cur = NULL;
|
||||
grub_err_t err;
|
||||
static unsigned long punishment_delay = 1;
|
||||
char entered[GRUB_AUTH_MAX_PASSLEN];
|
||||
struct grub_auth_user *user;
|
||||
|
||||
grub_memset (login, 0, sizeof (login));
|
||||
|
||||
if (is_authenticated (userlist))
|
||||
{
|
||||
punishment_delay = 1;
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
grub_puts_ (N_("Enter username: "));
|
||||
|
||||
if (!grub_username_get (login, sizeof (login) - 1))
|
||||
goto access_denied;
|
||||
|
||||
grub_puts_ (N_("Enter password: "));
|
||||
|
||||
if (!grub_password_get (entered, GRUB_AUTH_MAX_PASSLEN))
|
||||
goto access_denied;
|
||||
|
||||
FOR_LIST_ELEMENTS (user, users)
|
||||
{
|
||||
if (grub_strcmp (login, user->name) == 0)
|
||||
cur = user;
|
||||
}
|
||||
|
||||
if (!cur || ! cur->callback)
|
||||
goto access_denied;
|
||||
|
||||
err = cur->callback (login, entered, cur->arg);
|
||||
if (is_authenticated (userlist))
|
||||
{
|
||||
punishment_delay = 1;
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
access_denied:
|
||||
grub_sleep (punishment_delay);
|
||||
|
||||
if (punishment_delay < GRUB_ULONG_MAX / 2)
|
||||
punishment_delay *= 2;
|
||||
|
||||
return GRUB_ACCESS_DENIED;
|
||||
}
|
136
grub-core/normal/autofs.c
Normal file
136
grub-core/normal/autofs.c
Normal file
|
@ -0,0 +1,136 @@
|
|||
/* autofs.c - support auto-loading from fs.lst */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 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/mm.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/fs.h>
|
||||
#include <grub/normal.h>
|
||||
|
||||
/* This is used to store the names of filesystem modules for auto-loading. */
|
||||
static grub_named_list_t fs_module_list;
|
||||
|
||||
/* The auto-loading hook for filesystems. */
|
||||
static int
|
||||
autoload_fs_module (void)
|
||||
{
|
||||
grub_named_list_t p;
|
||||
|
||||
while ((p = fs_module_list) != NULL)
|
||||
{
|
||||
if (! grub_dl_get (p->name) && grub_dl_load (p->name))
|
||||
return 1;
|
||||
|
||||
if (grub_errno)
|
||||
grub_print_error ();
|
||||
|
||||
fs_module_list = p->next;
|
||||
grub_free (p->name);
|
||||
grub_free (p);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Read the file fs.lst for auto-loading. */
|
||||
void
|
||||
read_fs_list (const char *prefix)
|
||||
{
|
||||
if (prefix)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
filename = grub_xasprintf ("%s/fs.lst", prefix);
|
||||
if (filename)
|
||||
{
|
||||
grub_file_t file;
|
||||
grub_fs_autoload_hook_t tmp_autoload_hook;
|
||||
|
||||
/* This rules out the possibility that read_fs_list() is invoked
|
||||
recursively when we call grub_file_open() below. */
|
||||
tmp_autoload_hook = grub_fs_autoload_hook;
|
||||
grub_fs_autoload_hook = NULL;
|
||||
|
||||
file = grub_file_open (filename);
|
||||
if (file)
|
||||
{
|
||||
/* Override previous fs.lst. */
|
||||
while (fs_module_list)
|
||||
{
|
||||
grub_named_list_t tmp;
|
||||
tmp = fs_module_list->next;
|
||||
grub_free (fs_module_list);
|
||||
fs_module_list = tmp;
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
char *buf;
|
||||
char *p;
|
||||
char *q;
|
||||
grub_named_list_t fs_mod;
|
||||
|
||||
buf = grub_file_getline (file);
|
||||
if (! buf)
|
||||
break;
|
||||
|
||||
p = buf;
|
||||
q = buf + grub_strlen (buf) - 1;
|
||||
|
||||
/* Ignore space. */
|
||||
while (grub_isspace (*p))
|
||||
p++;
|
||||
|
||||
while (p < q && grub_isspace (*q))
|
||||
*q-- = '\0';
|
||||
|
||||
/* If the line is empty, skip it. */
|
||||
if (p >= q)
|
||||
continue;
|
||||
|
||||
fs_mod = grub_malloc (sizeof (*fs_mod));
|
||||
if (! fs_mod)
|
||||
continue;
|
||||
|
||||
fs_mod->name = grub_strdup (p);
|
||||
if (! fs_mod->name)
|
||||
{
|
||||
grub_free (fs_mod);
|
||||
continue;
|
||||
}
|
||||
|
||||
fs_mod->next = fs_module_list;
|
||||
fs_module_list = fs_mod;
|
||||
}
|
||||
|
||||
grub_file_close (file);
|
||||
grub_fs_autoload_hook = tmp_autoload_hook;
|
||||
}
|
||||
|
||||
grub_free (filename);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ignore errors. */
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
/* Set the hook. */
|
||||
grub_fs_autoload_hook = autoload_fs_module;
|
||||
}
|
1293
grub-core/normal/charset.c
Normal file
1293
grub-core/normal/charset.c
Normal file
File diff suppressed because it is too large
Load diff
641
grub-core/normal/cmdline.c
Normal file
641
grub-core/normal/cmdline.c
Normal file
|
@ -0,0 +1,641 @@
|
|||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,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/misc.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/types.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/partition.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/charset.h>
|
||||
|
||||
static grub_uint32_t *kill_buf;
|
||||
|
||||
static int hist_size;
|
||||
static grub_uint32_t **hist_lines = 0;
|
||||
static int hist_pos = 0;
|
||||
static int hist_end = 0;
|
||||
static int hist_used = 0;
|
||||
|
||||
grub_err_t
|
||||
grub_set_history (int newsize)
|
||||
{
|
||||
grub_uint32_t **old_hist_lines = hist_lines;
|
||||
hist_lines = grub_malloc (sizeof (grub_uint32_t *) * newsize);
|
||||
|
||||
/* 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)
|
||||
{
|
||||
int i;
|
||||
int delsize = hist_used - newsize;
|
||||
hist_used = newsize;
|
||||
|
||||
for (i = 1; i <= delsize; i++)
|
||||
{
|
||||
int pos = hist_end - i;
|
||||
if (pos < 0)
|
||||
pos += hist_size;
|
||||
grub_free (old_hist_lines[pos]);
|
||||
}
|
||||
|
||||
hist_end -= delsize;
|
||||
if (hist_end < 0)
|
||||
hist_end += hist_size;
|
||||
}
|
||||
|
||||
if (hist_pos < hist_end)
|
||||
grub_memmove (hist_lines, old_hist_lines + hist_pos,
|
||||
(hist_end - hist_pos) * sizeof (grub_uint32_t *));
|
||||
else if (hist_used)
|
||||
{
|
||||
/* Copy the older part. */
|
||||
grub_memmove (hist_lines, old_hist_lines + hist_pos,
|
||||
(hist_size - hist_pos) * sizeof (grub_uint32_t *));
|
||||
|
||||
/* Copy the newer part. */
|
||||
grub_memmove (hist_lines + hist_size - hist_pos, old_hist_lines,
|
||||
hist_end * sizeof (grub_uint32_t *));
|
||||
}
|
||||
}
|
||||
|
||||
grub_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 grub_uint32_t *
|
||||
grub_history_get (int pos)
|
||||
{
|
||||
pos = (hist_pos + pos) % hist_size;
|
||||
return hist_lines[pos];
|
||||
}
|
||||
|
||||
static grub_size_t
|
||||
strlen_ucs4 (const grub_uint32_t *s)
|
||||
{
|
||||
const grub_uint32_t *p = s;
|
||||
|
||||
while (*p)
|
||||
p++;
|
||||
|
||||
return p - s;
|
||||
}
|
||||
|
||||
/* Replace the history entry on position POS with the string S. */
|
||||
static void
|
||||
grub_history_set (int pos, grub_uint32_t *s, grub_size_t len)
|
||||
{
|
||||
grub_free (hist_lines[pos]);
|
||||
hist_lines[pos] = grub_malloc ((len + 1) * sizeof (grub_uint32_t));
|
||||
if (!hist_lines[pos])
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return ;
|
||||
}
|
||||
grub_memcpy (hist_lines[pos], s, len * sizeof (grub_uint32_t));
|
||||
hist_lines[pos][len] = 0;
|
||||
}
|
||||
|
||||
/* Insert a new history line S on the top of the history. */
|
||||
static void
|
||||
grub_history_add (grub_uint32_t *s, grub_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;
|
||||
|
||||
grub_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;
|
||||
grub_history_set (hist_pos, s, len);
|
||||
}
|
||||
|
||||
/* Replace the history entry on position POS with the string S. */
|
||||
static void
|
||||
grub_history_replace (int pos, grub_uint32_t *s, grub_size_t len)
|
||||
{
|
||||
grub_history_set ((hist_pos + pos) % hist_size, s, len);
|
||||
}
|
||||
|
||||
/* A completion hook to print items. */
|
||||
static void
|
||||
print_completion (const char *item, grub_completion_type_t type, int count)
|
||||
{
|
||||
if (count == 0)
|
||||
{
|
||||
/* If this is the first time, print a label. */
|
||||
|
||||
grub_puts ("");
|
||||
switch (type)
|
||||
{
|
||||
case GRUB_COMPLETION_TYPE_COMMAND:
|
||||
grub_puts_ (N_("Possible commands are:"));
|
||||
break;
|
||||
case GRUB_COMPLETION_TYPE_DEVICE:
|
||||
grub_puts_ (N_("Possible devices are:"));
|
||||
break;
|
||||
case GRUB_COMPLETION_TYPE_FILE:
|
||||
grub_puts_ (N_("Possible files are:"));
|
||||
break;
|
||||
case GRUB_COMPLETION_TYPE_PARTITION:
|
||||
grub_puts_ (N_("Possible partitions are:"));
|
||||
break;
|
||||
case GRUB_COMPLETION_TYPE_ARGUMENT:
|
||||
grub_puts_ (N_("Possible arguments are:"));
|
||||
break;
|
||||
default:
|
||||
grub_puts_ (N_("Possible things are:"));
|
||||
break;
|
||||
}
|
||||
grub_puts ("");
|
||||
}
|
||||
|
||||
if (type == GRUB_COMPLETION_TYPE_PARTITION)
|
||||
{
|
||||
grub_normal_print_device_info (item);
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
else
|
||||
grub_printf (" %s", item);
|
||||
}
|
||||
|
||||
struct cmdline_term
|
||||
{
|
||||
unsigned xpos, ypos, ystart, width, height;
|
||||
struct grub_term_output *term;
|
||||
};
|
||||
|
||||
/* Get a command-line. If ESC is pushed, return zero,
|
||||
otherwise return command line. */
|
||||
/* FIXME: The dumb interface is not supported yet. */
|
||||
char *
|
||||
grub_cmdline_get (const char *prompt)
|
||||
{
|
||||
grub_size_t lpos, llen;
|
||||
grub_size_t plen;
|
||||
grub_uint32_t *buf;
|
||||
grub_size_t max_len = 256;
|
||||
int key;
|
||||
int histpos = 0;
|
||||
auto void cl_insert (const grub_uint32_t *str);
|
||||
auto void cl_delete (unsigned len);
|
||||
auto inline void __attribute__ ((always_inline)) cl_print (struct cmdline_term *cl_term, int pos,
|
||||
grub_uint32_t c);
|
||||
auto void cl_set_pos (struct cmdline_term *cl_term);
|
||||
auto void cl_print_all (int pos, grub_uint32_t c);
|
||||
auto void cl_set_pos_all (void);
|
||||
auto void init_clterm (struct cmdline_term *cl_term_cur);
|
||||
auto void init_clterm_all (void);
|
||||
const char *prompt_translated = _(prompt);
|
||||
struct cmdline_term *cl_terms;
|
||||
char *ret;
|
||||
unsigned nterms;
|
||||
|
||||
void cl_set_pos (struct cmdline_term *cl_term)
|
||||
{
|
||||
cl_term->xpos = (plen + lpos) % (cl_term->width - 1);
|
||||
cl_term->ypos = cl_term->ystart + (plen + lpos) / (cl_term->width - 1);
|
||||
grub_term_gotoxy (cl_term->term, cl_term->xpos, cl_term->ypos);
|
||||
}
|
||||
|
||||
void cl_set_pos_all ()
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < nterms; i++)
|
||||
cl_set_pos (&cl_terms[i]);
|
||||
}
|
||||
|
||||
inline void __attribute__ ((always_inline)) cl_print (struct cmdline_term *cl_term, int pos, grub_uint32_t c)
|
||||
{
|
||||
grub_uint32_t *p;
|
||||
|
||||
for (p = buf + pos; p < buf + llen; p++)
|
||||
{
|
||||
if (c)
|
||||
grub_putcode (c, cl_term->term);
|
||||
else
|
||||
grub_putcode (*p, cl_term->term);
|
||||
cl_term->xpos++;
|
||||
if (cl_term->xpos >= cl_term->width - 1)
|
||||
{
|
||||
cl_term->xpos = 0;
|
||||
if (cl_term->ypos >= (unsigned) (cl_term->height - 1))
|
||||
cl_term->ystart--;
|
||||
else
|
||||
cl_term->ypos++;
|
||||
grub_putcode ('\n', cl_term->term);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void cl_print_all (int pos, grub_uint32_t c)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < nterms; i++)
|
||||
cl_print (&cl_terms[i], pos, c);
|
||||
}
|
||||
|
||||
void cl_insert (const grub_uint32_t *str)
|
||||
{
|
||||
grub_size_t len = strlen_ucs4 (str);
|
||||
|
||||
if (len + llen >= max_len)
|
||||
{
|
||||
grub_uint32_t *nbuf;
|
||||
max_len *= 2;
|
||||
nbuf = grub_realloc (buf, sizeof (grub_uint32_t) * max_len);
|
||||
if (nbuf)
|
||||
buf = nbuf;
|
||||
else
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
max_len /= 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (len + llen < max_len)
|
||||
{
|
||||
grub_memmove (buf + lpos + len, buf + lpos,
|
||||
(llen - lpos + 1) * sizeof (grub_uint32_t));
|
||||
grub_memmove (buf + lpos, str, len * sizeof (grub_uint32_t));
|
||||
|
||||
llen += len;
|
||||
cl_set_pos_all ();
|
||||
cl_print_all (lpos, 0);
|
||||
lpos += len;
|
||||
cl_set_pos_all ();
|
||||
}
|
||||
}
|
||||
|
||||
void cl_delete (unsigned len)
|
||||
{
|
||||
if (lpos + len <= llen)
|
||||
{
|
||||
grub_size_t saved_lpos = lpos;
|
||||
|
||||
lpos = llen - len;
|
||||
cl_set_pos_all ();
|
||||
cl_print_all (lpos, ' ');
|
||||
lpos = saved_lpos;
|
||||
cl_set_pos_all ();
|
||||
|
||||
grub_memmove (buf + lpos, buf + lpos + len,
|
||||
sizeof (grub_uint32_t) * (llen - lpos + 1));
|
||||
llen -= len;
|
||||
cl_print_all (lpos, 0);
|
||||
cl_set_pos_all ();
|
||||
}
|
||||
}
|
||||
|
||||
void init_clterm (struct cmdline_term *cl_term_cur)
|
||||
{
|
||||
cl_term_cur->xpos = plen;
|
||||
cl_term_cur->ypos = (grub_term_getxy (cl_term_cur->term) & 0xFF);
|
||||
cl_term_cur->ystart = cl_term_cur->ypos;
|
||||
cl_term_cur->width = grub_term_width (cl_term_cur->term);
|
||||
cl_term_cur->height = grub_term_height (cl_term_cur->term);
|
||||
}
|
||||
|
||||
void init_clterm_all (void)
|
||||
{
|
||||
unsigned i;
|
||||
for (i = 0; i < nterms; i++)
|
||||
init_clterm (&cl_terms[i]);
|
||||
}
|
||||
|
||||
buf = grub_malloc (max_len * sizeof (grub_uint32_t));
|
||||
if (!buf)
|
||||
return 0;
|
||||
|
||||
plen = grub_strlen (prompt_translated) + sizeof (" ") - 1;
|
||||
lpos = llen = 0;
|
||||
buf[0] = '\0';
|
||||
|
||||
{
|
||||
grub_term_output_t term;
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
if ((grub_term_getxy (term) >> 8) != 0)
|
||||
grub_putcode ('\n', term);
|
||||
}
|
||||
grub_printf ("%s ", prompt_translated);
|
||||
grub_normal_reset_more ();
|
||||
|
||||
{
|
||||
struct cmdline_term *cl_term_cur;
|
||||
struct grub_term_output *cur;
|
||||
nterms = 0;
|
||||
FOR_ACTIVE_TERM_OUTPUTS(cur)
|
||||
nterms++;
|
||||
|
||||
cl_terms = grub_malloc (sizeof (cl_terms[0]) * nterms);
|
||||
if (!cl_terms)
|
||||
return 0;
|
||||
cl_term_cur = cl_terms;
|
||||
FOR_ACTIVE_TERM_OUTPUTS(cur)
|
||||
{
|
||||
cl_term_cur->term = cur;
|
||||
init_clterm (cl_term_cur);
|
||||
cl_term_cur++;
|
||||
}
|
||||
}
|
||||
|
||||
if (hist_used == 0)
|
||||
grub_history_add (buf, llen);
|
||||
|
||||
grub_refresh ();
|
||||
|
||||
while ((key = GRUB_TERM_ASCII_CHAR (grub_getkey ())) != '\n' && key != '\r')
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 1: /* Ctrl-a */
|
||||
lpos = 0;
|
||||
cl_set_pos_all ();
|
||||
break;
|
||||
|
||||
case 2: /* Ctrl-b */
|
||||
if (lpos > 0)
|
||||
{
|
||||
lpos--;
|
||||
cl_set_pos_all ();
|
||||
}
|
||||
break;
|
||||
|
||||
case 5: /* Ctrl-e */
|
||||
lpos = llen;
|
||||
cl_set_pos_all ();
|
||||
break;
|
||||
|
||||
case 6: /* Ctrl-f */
|
||||
if (lpos < llen)
|
||||
{
|
||||
lpos++;
|
||||
cl_set_pos_all ();
|
||||
}
|
||||
break;
|
||||
|
||||
case 9: /* Ctrl-i or TAB */
|
||||
{
|
||||
int restore;
|
||||
char *insertu8;
|
||||
char *bufu8;
|
||||
grub_uint32_t c;
|
||||
|
||||
c = buf[lpos];
|
||||
buf[lpos] = '\0';
|
||||
|
||||
bufu8 = grub_ucs4_to_utf8_alloc (buf, lpos);
|
||||
buf[lpos] = c;
|
||||
if (!bufu8)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
break;
|
||||
}
|
||||
|
||||
insertu8 = grub_normal_do_completion (bufu8, &restore,
|
||||
print_completion);
|
||||
grub_free (bufu8);
|
||||
|
||||
grub_normal_reset_more ();
|
||||
|
||||
if (restore)
|
||||
{
|
||||
/* Restore the prompt. */
|
||||
grub_printf ("\n%s ", prompt_translated);
|
||||
init_clterm_all ();
|
||||
cl_print_all (0, 0);
|
||||
}
|
||||
|
||||
if (insertu8)
|
||||
{
|
||||
grub_size_t insertlen;
|
||||
grub_ssize_t t;
|
||||
grub_uint32_t *insert;
|
||||
|
||||
insertlen = grub_strlen (insertu8);
|
||||
insert = grub_malloc ((insertlen + 1) * sizeof (grub_uint32_t));
|
||||
if (!insert)
|
||||
{
|
||||
grub_free (insertu8);
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
break;
|
||||
}
|
||||
t = grub_utf8_to_ucs4 (insert, insertlen,
|
||||
(grub_uint8_t *) insertu8,
|
||||
insertlen, 0);
|
||||
if (t > 0)
|
||||
{
|
||||
if (insert[t-1] == ' ' && buf[lpos] == ' ')
|
||||
{
|
||||
insert[t-1] = 0;
|
||||
if (t != 1)
|
||||
cl_insert (insert);
|
||||
lpos++;
|
||||
}
|
||||
else
|
||||
{
|
||||
insert[t] = 0;
|
||||
cl_insert (insert);
|
||||
}
|
||||
}
|
||||
|
||||
grub_free (insertu8);
|
||||
grub_free (insert);
|
||||
}
|
||||
cl_set_pos_all ();
|
||||
}
|
||||
break;
|
||||
|
||||
case 11: /* Ctrl-k */
|
||||
if (lpos < llen)
|
||||
{
|
||||
if (kill_buf)
|
||||
grub_free (kill_buf);
|
||||
|
||||
kill_buf = grub_malloc ((llen - lpos + 1)
|
||||
* sizeof (grub_uint32_t));
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_memcpy (kill_buf, buf + lpos,
|
||||
(llen - lpos + 1) * sizeof (grub_uint32_t));
|
||||
kill_buf[llen - lpos] = 0;
|
||||
}
|
||||
|
||||
cl_delete (llen - lpos);
|
||||
}
|
||||
break;
|
||||
|
||||
case 14: /* Ctrl-n */
|
||||
{
|
||||
grub_uint32_t *hist;
|
||||
|
||||
lpos = 0;
|
||||
|
||||
if (histpos > 0)
|
||||
{
|
||||
grub_history_replace (histpos, buf, llen);
|
||||
histpos--;
|
||||
}
|
||||
|
||||
cl_delete (llen);
|
||||
hist = grub_history_get (histpos);
|
||||
cl_insert (hist);
|
||||
|
||||
break;
|
||||
}
|
||||
case 16: /* Ctrl-p */
|
||||
{
|
||||
grub_uint32_t *hist;
|
||||
|
||||
lpos = 0;
|
||||
|
||||
if (histpos < hist_used - 1)
|
||||
{
|
||||
grub_history_replace (histpos, buf, llen);
|
||||
histpos++;
|
||||
}
|
||||
|
||||
cl_delete (llen);
|
||||
hist = grub_history_get (histpos);
|
||||
|
||||
cl_insert (hist);
|
||||
}
|
||||
break;
|
||||
|
||||
case 21: /* Ctrl-u */
|
||||
if (lpos > 0)
|
||||
{
|
||||
grub_size_t n = lpos;
|
||||
|
||||
if (kill_buf)
|
||||
grub_free (kill_buf);
|
||||
|
||||
kill_buf = grub_malloc (n + 1);
|
||||
if (grub_errno)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
if (kill_buf)
|
||||
{
|
||||
grub_memcpy (kill_buf, buf, n);
|
||||
kill_buf[n] = '\0';
|
||||
}
|
||||
|
||||
lpos = 0;
|
||||
cl_set_pos_all ();
|
||||
cl_delete (n);
|
||||
}
|
||||
break;
|
||||
|
||||
case 25: /* Ctrl-y */
|
||||
if (kill_buf)
|
||||
cl_insert (kill_buf);
|
||||
break;
|
||||
|
||||
case '\e':
|
||||
grub_free (cl_terms);
|
||||
return 0;
|
||||
|
||||
case '\b':
|
||||
if (lpos > 0)
|
||||
{
|
||||
lpos--;
|
||||
cl_set_pos_all ();
|
||||
}
|
||||
else
|
||||
break;
|
||||
/* fall through */
|
||||
|
||||
case 4: /* Ctrl-d */
|
||||
if (lpos < llen)
|
||||
cl_delete (1);
|
||||
break;
|
||||
|
||||
default:
|
||||
if (grub_isprint (key))
|
||||
{
|
||||
grub_uint32_t str[2];
|
||||
|
||||
str[0] = key;
|
||||
str[1] = '\0';
|
||||
cl_insert (str);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
grub_refresh ();
|
||||
}
|
||||
|
||||
grub_xputs ("\n");
|
||||
grub_refresh ();
|
||||
|
||||
/* Remove leading spaces. */
|
||||
lpos = 0;
|
||||
while (buf[lpos] == ' ')
|
||||
lpos++;
|
||||
|
||||
histpos = 0;
|
||||
if (strlen_ucs4 (buf) > 0)
|
||||
{
|
||||
grub_uint32_t empty[] = { 0 };
|
||||
grub_history_replace (histpos, buf, llen);
|
||||
grub_history_add (empty, 0);
|
||||
}
|
||||
|
||||
ret = grub_ucs4_to_utf8_alloc (buf + lpos, llen - lpos + 1);
|
||||
grub_free (buf);
|
||||
grub_free (cl_terms);
|
||||
return ret;
|
||||
}
|
150
grub-core/normal/color.c
Normal file
150
grub-core/normal/color.c
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 1999,2000,2001,2002,2003,2004,2008 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/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/i18n.h>
|
||||
|
||||
/* Borrowed from GRUB Legacy */
|
||||
static char *color_list[16] =
|
||||
{
|
||||
"black",
|
||||
"blue",
|
||||
"green",
|
||||
"cyan",
|
||||
"red",
|
||||
"magenta",
|
||||
"brown",
|
||||
"light-gray",
|
||||
"dark-gray",
|
||||
"light-blue",
|
||||
"light-green",
|
||||
"light-cyan",
|
||||
"light-red",
|
||||
"light-magenta",
|
||||
"yellow",
|
||||
"white"
|
||||
};
|
||||
|
||||
static int
|
||||
parse_color_name (grub_uint8_t *ret, char *name)
|
||||
{
|
||||
grub_uint8_t i;
|
||||
for (i = 0; i < sizeof (color_list) / sizeof (*color_list); i++)
|
||||
if (! grub_strcmp (name, color_list[i]))
|
||||
{
|
||||
*ret = i;
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
grub_parse_color_name_pair (grub_uint8_t *color, const char *name)
|
||||
{
|
||||
int result = 1;
|
||||
grub_uint8_t fg, bg;
|
||||
char *fg_name, *bg_name;
|
||||
|
||||
/* nothing specified by user */
|
||||
if (name == NULL)
|
||||
return result;
|
||||
|
||||
fg_name = grub_strdup (name);
|
||||
if (fg_name == NULL)
|
||||
{
|
||||
/* "out of memory" message was printed by grub_strdup() */
|
||||
grub_wait_after_message ();
|
||||
return result;
|
||||
}
|
||||
|
||||
bg_name = grub_strchr (fg_name, '/');
|
||||
if (bg_name == NULL)
|
||||
{
|
||||
grub_printf_ (N_("Warning: syntax error (missing slash) in `%s'\n"), fg_name);
|
||||
grub_wait_after_message ();
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
*(bg_name++) = '\0';
|
||||
|
||||
if (parse_color_name (&fg, fg_name) == -1)
|
||||
{
|
||||
grub_printf_ (N_("Warning: invalid foreground color `%s'\n"), fg_name);
|
||||
grub_wait_after_message ();
|
||||
goto free_and_return;
|
||||
}
|
||||
if (parse_color_name (&bg, bg_name) == -1)
|
||||
{
|
||||
grub_printf_ (N_("Warning: invalid background color `%s'\n"), bg_name);
|
||||
grub_wait_after_message ();
|
||||
goto free_and_return;
|
||||
}
|
||||
|
||||
*color = (bg << 4) | fg;
|
||||
result = 0;
|
||||
|
||||
free_and_return:
|
||||
grub_free (fg_name);
|
||||
return result;
|
||||
}
|
||||
|
||||
static grub_uint8_t color_normal, color_highlight;
|
||||
|
||||
static void
|
||||
set_colors (void)
|
||||
{
|
||||
struct grub_term_output *term;
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
/* Reloads terminal `normal' and `highlight' colors. */
|
||||
grub_term_setcolor (term, color_normal, color_highlight);
|
||||
|
||||
/* Propagates `normal' color to terminal current color. */
|
||||
grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Replace default `normal' colors with the ones specified by user (if any). */
|
||||
char *
|
||||
grub_env_write_color_normal (struct grub_env_var *var __attribute__ ((unused)),
|
||||
const char *val)
|
||||
{
|
||||
if (grub_parse_color_name_pair (&color_normal, val))
|
||||
return NULL;
|
||||
|
||||
set_colors ();
|
||||
|
||||
return grub_strdup (val);
|
||||
}
|
||||
|
||||
/* Replace default `highlight' colors with the ones specified by user (if any). */
|
||||
char *
|
||||
grub_env_write_color_highlight (struct grub_env_var *var __attribute__ ((unused)),
|
||||
const char *val)
|
||||
{
|
||||
if (grub_parse_color_name_pair (&color_highlight, val))
|
||||
return NULL;
|
||||
|
||||
set_colors ();
|
||||
|
||||
return grub_strdup (val);
|
||||
}
|
517
grub-core/normal/completion.c
Normal file
517
grub-core/normal/completion.c
Normal file
|
@ -0,0 +1,517 @@
|
|||
/* completion.c - complete a command, a disk, a partition or a file */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005,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/misc.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/partition.h>
|
||||
#include <grub/disk.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/parser.h>
|
||||
#include <grub/extcmd.h>
|
||||
|
||||
/* The current word. */
|
||||
static char *current_word;
|
||||
|
||||
/* The matched string. */
|
||||
static char *match;
|
||||
|
||||
/* The count of candidates. */
|
||||
static int num_found;
|
||||
|
||||
/* The string to be appended. */
|
||||
static const char *suffix;
|
||||
|
||||
/* The callback function to print items. */
|
||||
static void (*print_func) (const char *, grub_completion_type_t, int);
|
||||
|
||||
/* The state the command line is in. */
|
||||
static grub_parser_state_t cmdline_state;
|
||||
|
||||
|
||||
/* Add a string to the list of possible completions. COMPLETION is the
|
||||
string that should be added. EXTRA will be appended if COMPLETION
|
||||
matches uniquely. The type TYPE specifies what kind of data is added. */
|
||||
static int
|
||||
add_completion (const char *completion, const char *extra,
|
||||
grub_completion_type_t type)
|
||||
{
|
||||
if (grub_strncmp (current_word, completion, grub_strlen (current_word)) == 0)
|
||||
{
|
||||
num_found++;
|
||||
|
||||
switch (num_found)
|
||||
{
|
||||
case 1:
|
||||
match = grub_strdup (completion);
|
||||
if (! match)
|
||||
return 1;
|
||||
suffix = extra;
|
||||
break;
|
||||
|
||||
case 2:
|
||||
if (print_func)
|
||||
print_func (match, type, 0);
|
||||
|
||||
/* Fall through. */
|
||||
|
||||
default:
|
||||
{
|
||||
char *s = match;
|
||||
const char *t = completion;
|
||||
|
||||
if (print_func)
|
||||
print_func (completion, type, num_found - 1);
|
||||
|
||||
/* Detect the matched portion. */
|
||||
while (*s && *t && *s == *t)
|
||||
{
|
||||
s++;
|
||||
t++;
|
||||
}
|
||||
|
||||
*s = '\0';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iterate_partition (grub_disk_t disk, const grub_partition_t p)
|
||||
{
|
||||
const char *disk_name = disk->name;
|
||||
char *partition_name = grub_partition_get_name (p);
|
||||
char *name;
|
||||
int ret;
|
||||
|
||||
if (! partition_name)
|
||||
return 1;
|
||||
|
||||
name = grub_xasprintf ("%s,%s", disk_name, partition_name);
|
||||
grub_free (partition_name);
|
||||
|
||||
if (! name)
|
||||
return 1;
|
||||
|
||||
ret = add_completion (name, ")", GRUB_COMPLETION_TYPE_PARTITION);
|
||||
grub_free (name);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
iterate_dir (const char *filename, const struct grub_dirhook_info *info)
|
||||
{
|
||||
if (! info->dir)
|
||||
{
|
||||
const char *prefix;
|
||||
if (cmdline_state == GRUB_PARSER_STATE_DQUOTE)
|
||||
prefix = "\" ";
|
||||
else if (cmdline_state == GRUB_PARSER_STATE_QUOTE)
|
||||
prefix = "\' ";
|
||||
else
|
||||
prefix = " ";
|
||||
|
||||
if (add_completion (filename, prefix, GRUB_COMPLETION_TYPE_FILE))
|
||||
return 1;
|
||||
}
|
||||
else if (grub_strcmp (filename, ".") && grub_strcmp (filename, ".."))
|
||||
{
|
||||
char *fname;
|
||||
|
||||
fname = grub_xasprintf ("%s/", filename);
|
||||
if (add_completion (fname, "", GRUB_COMPLETION_TYPE_FILE))
|
||||
{
|
||||
grub_free (fname);
|
||||
return 1;
|
||||
}
|
||||
grub_free (fname);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iterate_dev (const char *devname)
|
||||
{
|
||||
grub_device_t dev;
|
||||
|
||||
/* Complete the partition part. */
|
||||
dev = grub_device_open (devname);
|
||||
|
||||
if (dev)
|
||||
{
|
||||
char tmp[grub_strlen (devname) + sizeof (",")];
|
||||
|
||||
grub_memcpy (tmp, devname, grub_strlen (devname));
|
||||
|
||||
if (grub_strcmp (devname, current_word) == 0)
|
||||
{
|
||||
if (add_completion (devname, ")", GRUB_COMPLETION_TYPE_PARTITION))
|
||||
return 1;
|
||||
|
||||
if (dev->disk)
|
||||
if (grub_partition_iterate (dev->disk, iterate_partition))
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_memcpy (tmp + grub_strlen (devname), "", sizeof (""));
|
||||
if (add_completion (tmp, "", GRUB_COMPLETION_TYPE_DEVICE))
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Complete a device. */
|
||||
static int
|
||||
complete_device (void)
|
||||
{
|
||||
/* Check if this is a device or a partition. */
|
||||
char *p = grub_strchr (++current_word, ',');
|
||||
grub_device_t dev;
|
||||
|
||||
if (! p)
|
||||
{
|
||||
/* Complete the disk part. */
|
||||
if (grub_disk_dev_iterate (iterate_dev))
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Complete the partition part. */
|
||||
*p = '\0';
|
||||
dev = grub_device_open (current_word);
|
||||
*p = ',';
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
if (dev)
|
||||
{
|
||||
if (dev->disk)
|
||||
{
|
||||
if (grub_partition_iterate (dev->disk, iterate_partition))
|
||||
{
|
||||
grub_device_close (dev);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
grub_device_close (dev);
|
||||
}
|
||||
else
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Complete a file. */
|
||||
static int
|
||||
complete_file (void)
|
||||
{
|
||||
char *device;
|
||||
char *dir;
|
||||
char *last_dir;
|
||||
grub_fs_t fs;
|
||||
grub_device_t dev;
|
||||
int ret = 0;
|
||||
|
||||
device = grub_file_get_device_name (current_word);
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
return 1;
|
||||
|
||||
dev = grub_device_open (device);
|
||||
if (! dev)
|
||||
{
|
||||
ret = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
fs = grub_fs_probe (dev);
|
||||
if (! fs)
|
||||
{
|
||||
ret = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
dir = grub_strchr (current_word + (device ? 2 + grub_strlen (device) : 0),
|
||||
'/');
|
||||
last_dir = grub_strrchr (current_word, '/');
|
||||
if (dir)
|
||||
{
|
||||
char *dirfile;
|
||||
|
||||
current_word = last_dir + 1;
|
||||
|
||||
dir = grub_strdup (dir);
|
||||
if (! dir)
|
||||
{
|
||||
ret = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* Cut away the filename part. */
|
||||
dirfile = grub_strrchr (dir, '/');
|
||||
dirfile[1] = '\0';
|
||||
|
||||
/* Iterate the directory. */
|
||||
(fs->dir) (dev, dir, iterate_dir);
|
||||
|
||||
grub_free (dir);
|
||||
|
||||
if (grub_errno)
|
||||
{
|
||||
ret = 1;
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
current_word += grub_strlen (current_word);
|
||||
match = grub_strdup ("/");
|
||||
if (! match)
|
||||
{
|
||||
ret = 1;
|
||||
goto fail;
|
||||
}
|
||||
|
||||
suffix = "";
|
||||
num_found = 1;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (dev)
|
||||
grub_device_close (dev);
|
||||
grub_free (device);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Complete an argument. */
|
||||
static int
|
||||
complete_arguments (char *command)
|
||||
{
|
||||
grub_command_t cmd;
|
||||
grub_extcmd_t ext;
|
||||
const struct grub_arg_option *option;
|
||||
char shortarg[] = "- ";
|
||||
|
||||
cmd = grub_command_find (command);
|
||||
|
||||
if (!cmd || !(cmd->flags & GRUB_COMMAND_FLAG_EXTCMD))
|
||||
return 0;
|
||||
|
||||
ext = cmd->data;
|
||||
if (!ext->options)
|
||||
return 0;
|
||||
|
||||
if (add_completion ("-u", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
|
||||
return 1;
|
||||
|
||||
/* Add the short arguments. */
|
||||
for (option = ext->options; option->doc; option++)
|
||||
{
|
||||
if (! option->shortarg)
|
||||
continue;
|
||||
|
||||
shortarg[1] = option->shortarg;
|
||||
if (add_completion (shortarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
/* First add the built-in arguments. */
|
||||
if (add_completion ("--help", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
|
||||
return 1;
|
||||
if (add_completion ("--usage", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
|
||||
return 1;
|
||||
|
||||
/* Add the long arguments. */
|
||||
for (option = ext->options; option->doc; option++)
|
||||
{
|
||||
char *longarg;
|
||||
if (!option->longarg)
|
||||
continue;
|
||||
|
||||
longarg = grub_xasprintf ("--%s", option->longarg);
|
||||
if (!longarg)
|
||||
return 1;
|
||||
|
||||
if (add_completion (longarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
|
||||
{
|
||||
grub_free (longarg);
|
||||
return 1;
|
||||
}
|
||||
grub_free (longarg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static grub_parser_state_t
|
||||
get_state (const char *cmdline)
|
||||
{
|
||||
grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
|
||||
char use;
|
||||
|
||||
while (*cmdline)
|
||||
state = grub_parser_cmdline_state (state, *(cmdline++), &use);
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
/* Try to complete the string in BUF. Return the characters that
|
||||
should be added to the string. This command outputs the possible
|
||||
completions by calling HOOK, in that case set RESTORE to 1 so the
|
||||
caller can restore the prompt. */
|
||||
char *
|
||||
grub_normal_do_completion (char *buf, int *restore,
|
||||
void (*hook) (const char *, grub_completion_type_t, int))
|
||||
{
|
||||
int argc;
|
||||
char **argv;
|
||||
|
||||
/* Initialize variables. */
|
||||
match = 0;
|
||||
num_found = 0;
|
||||
suffix = "";
|
||||
print_func = hook;
|
||||
|
||||
*restore = 1;
|
||||
|
||||
if (grub_parser_split_cmdline (buf, 0, &argc, &argv))
|
||||
return 0;
|
||||
|
||||
if (argc == 0)
|
||||
current_word = "";
|
||||
else
|
||||
current_word = argv[argc - 1];
|
||||
|
||||
if (argc > 1 && ! grub_strcmp (argv[0], "set"))
|
||||
{
|
||||
char *equals = grub_strchr (current_word, '=');
|
||||
if (equals)
|
||||
/* Complete the value of the variable. */
|
||||
current_word = equals + 1;
|
||||
}
|
||||
|
||||
/* Determine the state the command line is in, depending on the
|
||||
state, it can be determined how to complete. */
|
||||
cmdline_state = get_state (buf);
|
||||
|
||||
if (argc == 1 || argc == 0)
|
||||
{
|
||||
/* Complete a command. */
|
||||
grub_command_t cmd;
|
||||
FOR_COMMANDS(cmd)
|
||||
{
|
||||
if (cmd->prio & GRUB_PRIO_LIST_FLAG_ACTIVE)
|
||||
{
|
||||
if (add_completion (cmd->name, " ", GRUB_COMPLETION_TYPE_COMMAND))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (*current_word == '-')
|
||||
{
|
||||
if (complete_arguments (buf))
|
||||
goto fail;
|
||||
}
|
||||
else if (*current_word == '(' && ! grub_strchr (current_word, ')'))
|
||||
{
|
||||
/* Complete a device. */
|
||||
if (complete_device ())
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Complete a file. */
|
||||
if (complete_file ())
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/* If more than one match is found those matches will be printed and
|
||||
the prompt should be restored. */
|
||||
if (num_found > 1)
|
||||
*restore = 1;
|
||||
else
|
||||
*restore = 0;
|
||||
|
||||
/* Return the part that matches. */
|
||||
if (match)
|
||||
{
|
||||
char *ret;
|
||||
char *escstr;
|
||||
char *newstr;
|
||||
int current_len;
|
||||
int match_len;
|
||||
int spaces = 0;
|
||||
|
||||
current_len = grub_strlen (current_word);
|
||||
match_len = grub_strlen (match);
|
||||
|
||||
/* Count the number of spaces that have to be escaped. XXX:
|
||||
More than just spaces have to be escaped. */
|
||||
for (escstr = match + current_len; *escstr; escstr++)
|
||||
if (*escstr == ' ')
|
||||
spaces++;
|
||||
|
||||
ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + spaces + 1);
|
||||
newstr = ret;
|
||||
for (escstr = match + current_len; *escstr; escstr++)
|
||||
{
|
||||
if (*escstr == ' ' && cmdline_state != GRUB_PARSER_STATE_QUOTE
|
||||
&& cmdline_state != GRUB_PARSER_STATE_QUOTE)
|
||||
*(newstr++) = '\\';
|
||||
*(newstr++) = *escstr;
|
||||
}
|
||||
*newstr = '\0';
|
||||
|
||||
if (num_found == 1)
|
||||
grub_strcat (ret, suffix);
|
||||
|
||||
if (*ret == '\0')
|
||||
{
|
||||
grub_free (ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (argc != 0)
|
||||
grub_free (argv[0]);
|
||||
grub_free (match);
|
||||
return ret;
|
||||
}
|
||||
|
||||
fail:
|
||||
if (argc != 0)
|
||||
{
|
||||
grub_free (argv[0]);
|
||||
grub_free (argv);
|
||||
}
|
||||
grub_free (match);
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
return 0;
|
||||
}
|
188
grub-core/normal/context.c
Normal file
188
grub-core/normal/context.c
Normal file
|
@ -0,0 +1,188 @@
|
|||
/* env.c - Environment variables */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2003,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/env.h>
|
||||
#include <grub/env_private.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/command.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/i18n.h>
|
||||
|
||||
struct menu_pointer
|
||||
{
|
||||
grub_menu_t menu;
|
||||
struct menu_pointer *prev;
|
||||
};
|
||||
|
||||
struct menu_pointer initial_menu;
|
||||
struct menu_pointer *current_menu = &initial_menu;
|
||||
|
||||
void
|
||||
grub_env_unset_menu (void)
|
||||
{
|
||||
current_menu->menu = NULL;
|
||||
}
|
||||
|
||||
grub_menu_t
|
||||
grub_env_get_menu (void)
|
||||
{
|
||||
return current_menu->menu;
|
||||
}
|
||||
|
||||
void
|
||||
grub_env_set_menu (grub_menu_t nmenu)
|
||||
{
|
||||
current_menu->menu = nmenu;
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_env_context_open (int export)
|
||||
{
|
||||
struct grub_env_context *context;
|
||||
int i;
|
||||
struct menu_pointer *menu;
|
||||
|
||||
context = grub_zalloc (sizeof (*context));
|
||||
if (! context)
|
||||
return grub_errno;
|
||||
menu = grub_zalloc (sizeof (*menu));
|
||||
if (! menu)
|
||||
return grub_errno;
|
||||
|
||||
context->prev = grub_current_context;
|
||||
grub_current_context = context;
|
||||
|
||||
menu->prev = current_menu;
|
||||
current_menu = menu;
|
||||
|
||||
/* Copy exported variables. */
|
||||
for (i = 0; i < HASHSZ; i++)
|
||||
{
|
||||
struct grub_env_var *var;
|
||||
|
||||
for (var = context->prev->vars[i]; var; var = var->next)
|
||||
{
|
||||
if (export && var->global)
|
||||
{
|
||||
if (grub_env_set (var->name, var->value) != GRUB_ERR_NONE)
|
||||
{
|
||||
grub_env_context_close ();
|
||||
return grub_errno;
|
||||
}
|
||||
grub_env_export (var->name);
|
||||
grub_register_variable_hook (var->name, var->read_hook, var->write_hook);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_env_context_close (void)
|
||||
{
|
||||
struct grub_env_context *context;
|
||||
int i;
|
||||
struct menu_pointer *menu;
|
||||
|
||||
if (! grub_current_context->prev)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
"cannot close the initial context");
|
||||
|
||||
/* Free the variables associated with this context. */
|
||||
for (i = 0; i < HASHSZ; i++)
|
||||
{
|
||||
struct grub_env_var *p, *q;
|
||||
|
||||
for (p = grub_current_context->vars[i]; p; p = q)
|
||||
{
|
||||
q = p->next;
|
||||
grub_free (p->name);
|
||||
grub_free (p->value);
|
||||
grub_free (p);
|
||||
}
|
||||
}
|
||||
|
||||
/* Restore the previous context. */
|
||||
context = grub_current_context->prev;
|
||||
grub_free (grub_current_context);
|
||||
grub_current_context = context;
|
||||
|
||||
menu = current_menu->prev;
|
||||
grub_free (current_menu);
|
||||
current_menu = menu;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_env_export (const char *name)
|
||||
{
|
||||
struct grub_env_var *var;
|
||||
|
||||
var = grub_env_find (name);
|
||||
if (! var)
|
||||
{
|
||||
grub_err_t err;
|
||||
|
||||
err = grub_env_set (name, "");
|
||||
if (err)
|
||||
return err;
|
||||
var = grub_env_find (name);
|
||||
}
|
||||
var->global = 1;
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_command_t export_cmd;
|
||||
|
||||
static grub_err_t
|
||||
grub_cmd_export (struct grub_command *cmd __attribute__ ((unused)),
|
||||
int argc, char **args)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (argc < 1)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
||||
"no environment variable specified");
|
||||
|
||||
for (i = 0; i < argc; i++)
|
||||
grub_env_export (args[i]);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void
|
||||
grub_context_init (void)
|
||||
{
|
||||
grub_env_export ("root");
|
||||
grub_env_export ("prefix");
|
||||
|
||||
export_cmd = grub_register_command ("export", grub_cmd_export,
|
||||
N_("ENVVAR [ENVVAR] ..."),
|
||||
N_("Export variables."));
|
||||
}
|
||||
|
||||
void
|
||||
grub_context_fini (void)
|
||||
{
|
||||
grub_unregister_command (export_cmd);
|
||||
}
|
151
grub-core/normal/crypto.c
Normal file
151
grub-core/normal/crypto.c
Normal file
|
@ -0,0 +1,151 @@
|
|||
/* crypto.c - support crypto autoload */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 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/dl.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/crypto.h>
|
||||
#include <grub/normal.h>
|
||||
|
||||
struct load_spec
|
||||
{
|
||||
struct load_spec *next;
|
||||
char *name;
|
||||
char *modname;
|
||||
};
|
||||
|
||||
struct load_spec *crypto_specs = NULL;
|
||||
|
||||
static void
|
||||
grub_crypto_autoload (const char *name)
|
||||
{
|
||||
struct load_spec *cur;
|
||||
grub_dl_t mod;
|
||||
|
||||
for (cur = crypto_specs; cur; cur = cur->next)
|
||||
if (grub_strcasecmp (name, cur->name) == 0)
|
||||
{
|
||||
mod = grub_dl_load (cur->modname);
|
||||
if (mod)
|
||||
grub_dl_ref (mod);
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
grub_crypto_spec_free (void)
|
||||
{
|
||||
struct load_spec *cur, *next;
|
||||
for (cur = crypto_specs; cur; cur = next)
|
||||
{
|
||||
next = cur->next;
|
||||
grub_free (cur->name);
|
||||
grub_free (cur->modname);
|
||||
grub_free (cur);
|
||||
}
|
||||
crypto_specs = NULL;
|
||||
}
|
||||
|
||||
|
||||
/* Read the file crypto.lst for auto-loading. */
|
||||
void
|
||||
read_crypto_list (const char *prefix)
|
||||
{
|
||||
char *filename;
|
||||
grub_file_t file;
|
||||
char *buf = NULL;
|
||||
|
||||
if (!prefix)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
filename = grub_xasprintf ("%s/crypto.lst", prefix);
|
||||
if (!filename)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
file = grub_file_open (filename);
|
||||
grub_free (filename);
|
||||
if (!file)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Override previous crypto.lst. */
|
||||
grub_crypto_spec_free ();
|
||||
|
||||
for (;; grub_free (buf))
|
||||
{
|
||||
char *p, *name;
|
||||
struct load_spec *cur;
|
||||
|
||||
buf = grub_file_getline (file);
|
||||
|
||||
if (! buf)
|
||||
break;
|
||||
|
||||
name = buf;
|
||||
|
||||
p = grub_strchr (name, ':');
|
||||
if (! p)
|
||||
continue;
|
||||
|
||||
*p = '\0';
|
||||
while (*++p == ' ')
|
||||
;
|
||||
|
||||
cur = grub_malloc (sizeof (*cur));
|
||||
if (!cur)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
cur->name = grub_strdup (name);
|
||||
if (! name)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
grub_free (cur);
|
||||
continue;
|
||||
}
|
||||
|
||||
cur->modname = grub_strdup (p);
|
||||
if (! cur->modname)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
grub_free (cur);
|
||||
grub_free (cur->name);
|
||||
continue;
|
||||
}
|
||||
cur->next = crypto_specs;
|
||||
crypto_specs = cur;
|
||||
}
|
||||
|
||||
grub_file_close (file);
|
||||
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
grub_crypto_autoload_hook = grub_crypto_autoload;
|
||||
}
|
100
grub-core/normal/datetime.c
Normal file
100
grub-core/normal/datetime.c
Normal file
|
@ -0,0 +1,100 @@
|
|||
/* datetime.c - Module for common datetime function. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2008 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/datetime.h>
|
||||
|
||||
static char *grub_weekday_names[] =
|
||||
{
|
||||
"Sunday",
|
||||
"Monday",
|
||||
"Tuesday",
|
||||
"Wednesday",
|
||||
"Thursday",
|
||||
"Friday",
|
||||
"Saturday",
|
||||
};
|
||||
|
||||
int
|
||||
grub_get_weekday (struct grub_datetime *datetime)
|
||||
{
|
||||
int a, y, m;
|
||||
|
||||
a = (14 - datetime->month) / 12;
|
||||
y = datetime->year - a;
|
||||
m = datetime->month + 12 * a - 2;
|
||||
|
||||
return (datetime->day + y + y / 4 - y / 100 + y / 400 + (31 * m / 12)) % 7;
|
||||
}
|
||||
|
||||
char *
|
||||
grub_get_weekday_name (struct grub_datetime *datetime)
|
||||
{
|
||||
return grub_weekday_names[grub_get_weekday (datetime)];
|
||||
}
|
||||
|
||||
#define SECPERMIN 60
|
||||
#define SECPERHOUR (60*SECPERMIN)
|
||||
#define SECPERDAY (24*SECPERHOUR)
|
||||
#define SECPERYEAR (365*SECPERDAY)
|
||||
#define SECPER4YEARS (4*SECPERYEAR+SECPERDAY)
|
||||
|
||||
|
||||
void
|
||||
grub_unixtime2datetime (grub_int32_t nix, struct grub_datetime *datetime)
|
||||
{
|
||||
int i;
|
||||
int div;
|
||||
grub_uint8_t months[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
|
||||
/* In the period of validity of unixtime all years divisible by 4
|
||||
are bissextile*/
|
||||
/* Convenience: let's have 3 consecutive non-bissextile years
|
||||
at the beginning of the epoch. So count from 1973 instead of 1970 */
|
||||
nix -= 3*SECPERYEAR + SECPERDAY;
|
||||
/* Transform C divisions and modulos to mathematical ones */
|
||||
div = nix / SECPER4YEARS;
|
||||
if (nix < 0)
|
||||
div--;
|
||||
datetime->year = 1973 + 4 * div;
|
||||
nix -= div * SECPER4YEARS;
|
||||
|
||||
/* On 31st December of bissextile years 365 days from the beginning
|
||||
of the year elapsed but year isn't finished yet */
|
||||
if (nix / SECPERYEAR == 4)
|
||||
{
|
||||
datetime->year += 3;
|
||||
nix -= 3*SECPERYEAR;
|
||||
}
|
||||
else
|
||||
{
|
||||
datetime->year += nix / SECPERYEAR;
|
||||
nix %= SECPERYEAR;
|
||||
}
|
||||
for (i = 0; i < 12
|
||||
&& nix >= ((grub_int32_t) (i==1 && datetime->year % 4 == 0
|
||||
? 29 : months[i]))*SECPERDAY; i++)
|
||||
nix -= ((grub_int32_t) (i==1 && datetime->year % 4 == 0
|
||||
? 29 : months[i]))*SECPERDAY;
|
||||
datetime->month = i + 1;
|
||||
datetime->day = 1 + (nix / SECPERDAY);
|
||||
nix %= SECPERDAY;
|
||||
datetime->hour = (nix / SECPERHOUR);
|
||||
nix %= SECPERHOUR;
|
||||
datetime->minute = nix / SECPERMIN;
|
||||
datetime->second = nix % SECPERMIN;
|
||||
}
|
182
grub-core/normal/dyncmd.c
Normal file
182
grub-core/normal/dyncmd.c
Normal file
|
@ -0,0 +1,182 @@
|
|||
/* dyncmd.c - support dynamic command */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 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/dl.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/command.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/extcmd.h>
|
||||
#include <grub/script_sh.h>
|
||||
#include <grub/i18n.h>
|
||||
|
||||
static grub_err_t
|
||||
grub_dyncmd_dispatcher (struct grub_extcmd_context *ctxt,
|
||||
int argc, char **args)
|
||||
{
|
||||
char *modname;
|
||||
grub_dl_t mod;
|
||||
grub_err_t ret;
|
||||
grub_extcmd_t extcmd = ctxt->extcmd;
|
||||
grub_command_t cmd = extcmd->cmd;
|
||||
|
||||
modname = extcmd->data;
|
||||
mod = grub_dl_load (modname);
|
||||
if (mod)
|
||||
{
|
||||
char *name;
|
||||
|
||||
grub_free (modname);
|
||||
grub_dl_ref (mod);
|
||||
|
||||
name = (char *) cmd->name;
|
||||
grub_unregister_extcmd (extcmd);
|
||||
|
||||
cmd = grub_command_find (name);
|
||||
if (cmd)
|
||||
{
|
||||
if (cmd->flags & GRUB_COMMAND_FLAG_BLOCKS &&
|
||||
cmd->flags & GRUB_COMMAND_FLAG_EXTCMD)
|
||||
ret = grub_extcmd_dispatcher (cmd, argc, args, ctxt->script);
|
||||
else
|
||||
ret = (cmd->func) (cmd, argc, args);
|
||||
}
|
||||
else
|
||||
ret = grub_errno;
|
||||
|
||||
grub_free (name);
|
||||
}
|
||||
else
|
||||
ret = grub_errno;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Read the file command.lst for auto-loading. */
|
||||
void
|
||||
read_command_list (const char *prefix)
|
||||
{
|
||||
if (prefix)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
filename = grub_xasprintf ("%s/command.lst", prefix);
|
||||
if (filename)
|
||||
{
|
||||
grub_file_t file;
|
||||
|
||||
file = grub_file_open (filename);
|
||||
if (file)
|
||||
{
|
||||
char *buf = NULL;
|
||||
grub_command_t ptr, last = 0, next;
|
||||
|
||||
/* Override previous commands.lst. */
|
||||
for (ptr = grub_command_list; ptr; ptr = next)
|
||||
{
|
||||
next = ptr->next;
|
||||
if (ptr->flags & GRUB_COMMAND_FLAG_DYNCMD)
|
||||
{
|
||||
if (last)
|
||||
last->next = ptr->next;
|
||||
else
|
||||
grub_command_list = ptr->next;
|
||||
grub_free (ptr);
|
||||
grub_free (ptr->data); /* extcmd struct */
|
||||
}
|
||||
else
|
||||
last = ptr;
|
||||
}
|
||||
|
||||
for (;; grub_free (buf))
|
||||
{
|
||||
char *p, *name, *modname;
|
||||
grub_extcmd_t cmd;
|
||||
int prio = 0;
|
||||
|
||||
buf = grub_file_getline (file);
|
||||
|
||||
if (! buf)
|
||||
break;
|
||||
|
||||
name = buf;
|
||||
if (*name == '*')
|
||||
{
|
||||
name++;
|
||||
prio++;
|
||||
}
|
||||
|
||||
if (! grub_isgraph (name[0]))
|
||||
continue;
|
||||
|
||||
p = grub_strchr (name, ':');
|
||||
if (! p)
|
||||
continue;
|
||||
|
||||
*p = '\0';
|
||||
while (*++p == ' ')
|
||||
;
|
||||
|
||||
if (! grub_isgraph (*p))
|
||||
continue;
|
||||
|
||||
if (grub_dl_get (p))
|
||||
continue;
|
||||
|
||||
name = grub_strdup (name);
|
||||
if (! name)
|
||||
continue;
|
||||
|
||||
modname = grub_strdup (p);
|
||||
if (! modname)
|
||||
{
|
||||
grub_free (name);
|
||||
continue;
|
||||
}
|
||||
|
||||
cmd = grub_register_extcmd_prio (name,
|
||||
grub_dyncmd_dispatcher,
|
||||
GRUB_COMMAND_FLAG_BLOCKS
|
||||
| GRUB_COMMAND_FLAG_EXTCMD
|
||||
| GRUB_COMMAND_FLAG_DYNCMD,
|
||||
0, N_("not loaded"), 0,
|
||||
prio);
|
||||
if (! cmd)
|
||||
{
|
||||
grub_free (name);
|
||||
grub_free (modname);
|
||||
continue;
|
||||
}
|
||||
cmd->data = modname;
|
||||
|
||||
/* Update the active flag. */
|
||||
grub_command_find (name);
|
||||
}
|
||||
|
||||
grub_file_close (file);
|
||||
}
|
||||
|
||||
grub_free (filename);
|
||||
}
|
||||
}
|
||||
|
||||
/* Ignore errors. */
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
232
grub-core/normal/handler.c
Normal file
232
grub-core/normal/handler.c
Normal file
|
@ -0,0 +1,232 @@
|
|||
/* handler.c - support handler loading */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 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/dl.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/command.h>
|
||||
#include <grub/handler.h>
|
||||
#include <grub/normal.h>
|
||||
|
||||
struct grub_handler_list
|
||||
{
|
||||
struct grub_handler_list *next;
|
||||
char *name;
|
||||
grub_command_t cmd;
|
||||
};
|
||||
|
||||
static grub_list_t handler_list;
|
||||
|
||||
static grub_err_t
|
||||
grub_handler_cmd (struct grub_command *cmd,
|
||||
int argc __attribute__ ((unused)),
|
||||
char **args __attribute__ ((unused)))
|
||||
{
|
||||
char *p;
|
||||
grub_handler_class_t class;
|
||||
grub_handler_t handler;
|
||||
|
||||
p = grub_strchr (cmd->name, '.');
|
||||
if (! p)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid command name");
|
||||
|
||||
if (cmd->data)
|
||||
{
|
||||
if (! grub_dl_get (cmd->data))
|
||||
{
|
||||
grub_dl_t mod;
|
||||
|
||||
mod = grub_dl_load (cmd->data);
|
||||
if (mod)
|
||||
grub_dl_ref (mod);
|
||||
else
|
||||
return grub_errno;
|
||||
}
|
||||
grub_free (cmd->data);
|
||||
cmd->data = 0;
|
||||
}
|
||||
|
||||
*p = 0;
|
||||
class = grub_named_list_find (GRUB_AS_NAMED_LIST (grub_handler_class_list),
|
||||
cmd->name);
|
||||
*p = '.';
|
||||
|
||||
if (! class)
|
||||
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "class not found");
|
||||
|
||||
|
||||
handler = grub_named_list_find (GRUB_AS_NAMED_LIST (class->handler_list),
|
||||
p + 1);
|
||||
if (! handler)
|
||||
return grub_error (GRUB_ERR_FILE_NOT_FOUND, "handler not found");
|
||||
|
||||
grub_handler_set_current (class, handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
insert_handler (char *name, char *module)
|
||||
{
|
||||
struct grub_handler_list *item;
|
||||
char *data;
|
||||
|
||||
if (grub_command_find (name))
|
||||
return;
|
||||
|
||||
item = grub_malloc (sizeof (*item));
|
||||
if (! item)
|
||||
return;
|
||||
|
||||
item->name = grub_strdup (name);
|
||||
if (! item->name)
|
||||
{
|
||||
grub_free (item);
|
||||
return;
|
||||
}
|
||||
|
||||
if (module)
|
||||
{
|
||||
data = grub_strdup (module);
|
||||
if (! data)
|
||||
{
|
||||
grub_free (item->name);
|
||||
grub_free (item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
data = 0;
|
||||
|
||||
item->cmd = grub_register_command (item->name, grub_handler_cmd, 0,
|
||||
"Set active handler.");
|
||||
if (! item->cmd)
|
||||
{
|
||||
grub_free (data);
|
||||
grub_free (item->name);
|
||||
grub_free (item);
|
||||
return;
|
||||
}
|
||||
|
||||
item->cmd->data = data;
|
||||
grub_list_push (&handler_list, GRUB_AS_LIST (item));
|
||||
}
|
||||
|
||||
/* Read the file handler.lst for auto-loading. */
|
||||
void
|
||||
read_handler_list (void)
|
||||
{
|
||||
const char *prefix;
|
||||
static int first_time = 1;
|
||||
const char *class_name;
|
||||
|
||||
auto int iterate_handler (grub_handler_t handler);
|
||||
int iterate_handler (grub_handler_t handler)
|
||||
{
|
||||
char name[grub_strlen (class_name) + grub_strlen (handler->name) + 2];
|
||||
|
||||
grub_strcpy (name, class_name);
|
||||
grub_strcat (name, ".");
|
||||
grub_strcat (name, handler->name);
|
||||
|
||||
insert_handler (name, 0);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto int iterate_class (grub_handler_class_t class);
|
||||
int iterate_class (grub_handler_class_t class)
|
||||
{
|
||||
class_name = class->name;
|
||||
grub_list_iterate (GRUB_AS_LIST (class->handler_list),
|
||||
(grub_list_hook_t) iterate_handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Make sure that this function does not get executed twice. */
|
||||
if (! first_time)
|
||||
return;
|
||||
first_time = 0;
|
||||
|
||||
prefix = grub_env_get ("prefix");
|
||||
if (prefix)
|
||||
{
|
||||
char *filename;
|
||||
|
||||
filename = grub_malloc (grub_strlen (prefix) + sizeof ("/handler.lst"));
|
||||
if (filename)
|
||||
{
|
||||
grub_file_t file;
|
||||
|
||||
grub_sprintf (filename, "%s/handler.lst", prefix);
|
||||
file = grub_file_open (filename);
|
||||
if (file)
|
||||
{
|
||||
char *buf = NULL;
|
||||
for (;; grub_free (buf))
|
||||
{
|
||||
char *p;
|
||||
|
||||
buf = grub_file_getline (file);
|
||||
|
||||
if (! buf)
|
||||
break;
|
||||
|
||||
if (! grub_isgraph (buf[0]))
|
||||
continue;
|
||||
|
||||
p = grub_strchr (buf, ':');
|
||||
if (! p)
|
||||
continue;
|
||||
|
||||
*p = '\0';
|
||||
while (*++p == ' ')
|
||||
;
|
||||
|
||||
insert_handler (buf, p);
|
||||
}
|
||||
grub_file_close (file);
|
||||
}
|
||||
grub_free (filename);
|
||||
}
|
||||
}
|
||||
|
||||
grub_list_iterate (GRUB_AS_LIST (grub_handler_class_list),
|
||||
(grub_list_hook_t) iterate_class);
|
||||
|
||||
/* Ignore errors. */
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
void
|
||||
free_handler_list (void)
|
||||
{
|
||||
struct grub_handler_list *item;
|
||||
|
||||
while ((item = grub_list_pop (&handler_list)) != 0)
|
||||
{
|
||||
grub_free (item->cmd->data);
|
||||
grub_unregister_command (item->cmd);
|
||||
grub_free (item->name);
|
||||
grub_free (item);
|
||||
}
|
||||
}
|
530
grub-core/normal/main.c
Normal file
530
grub-core/normal/main.c
Normal file
|
@ -0,0 +1,530 @@
|
|||
/* main.c - the normal mode main routine */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2000,2001,2002,2003,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/kernel.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/parser.h>
|
||||
#include <grub/reader.h>
|
||||
#include <grub/menu_viewer.h>
|
||||
#include <grub/auth.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/charset.h>
|
||||
#include <grub/script_sh.h>
|
||||
|
||||
#define GRUB_DEFAULT_HISTORY_SIZE 50
|
||||
|
||||
static int nested_level = 0;
|
||||
int grub_normal_exit_level = 0;
|
||||
|
||||
/* Read a line from the file FILE. */
|
||||
char *
|
||||
grub_file_getline (grub_file_t file)
|
||||
{
|
||||
char c;
|
||||
int pos = 0;
|
||||
int literal = 0;
|
||||
char *cmdline;
|
||||
int max_len = 64;
|
||||
|
||||
/* Initially locate some space. */
|
||||
cmdline = grub_malloc (max_len);
|
||||
if (! cmdline)
|
||||
return 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (grub_file_read (file, &c, 1) != 1)
|
||||
break;
|
||||
|
||||
/* Skip all carriage returns. */
|
||||
if (c == '\r')
|
||||
continue;
|
||||
|
||||
/* Replace tabs with spaces. */
|
||||
if (c == '\t')
|
||||
c = ' ';
|
||||
|
||||
/* The previous is a backslash, then... */
|
||||
if (literal)
|
||||
{
|
||||
/* If it is a newline, replace it with a space and continue. */
|
||||
if (c == '\n')
|
||||
{
|
||||
c = ' ';
|
||||
|
||||
/* Go back to overwrite the backslash. */
|
||||
if (pos > 0)
|
||||
pos--;
|
||||
}
|
||||
|
||||
literal = 0;
|
||||
}
|
||||
|
||||
if (c == '\\')
|
||||
literal = 1;
|
||||
|
||||
if (pos == 0)
|
||||
{
|
||||
if (! grub_isspace (c))
|
||||
cmdline[pos++] = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pos >= max_len)
|
||||
{
|
||||
char *old_cmdline = cmdline;
|
||||
max_len = max_len * 2;
|
||||
cmdline = grub_realloc (cmdline, max_len);
|
||||
if (! cmdline)
|
||||
{
|
||||
grub_free (old_cmdline);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (c == '\n')
|
||||
break;
|
||||
|
||||
cmdline[pos++] = c;
|
||||
}
|
||||
}
|
||||
|
||||
cmdline[pos] = '\0';
|
||||
|
||||
/* If the buffer is empty, don't return anything at all. */
|
||||
if (pos == 0)
|
||||
{
|
||||
grub_free (cmdline);
|
||||
cmdline = 0;
|
||||
}
|
||||
|
||||
return cmdline;
|
||||
}
|
||||
|
||||
static void
|
||||
free_menu (grub_menu_t menu)
|
||||
{
|
||||
grub_menu_entry_t entry = menu->entry_list;
|
||||
|
||||
while (entry)
|
||||
{
|
||||
grub_menu_entry_t next_entry = entry->next;
|
||||
|
||||
grub_free ((void *) entry->title);
|
||||
grub_free ((void *) entry->sourcecode);
|
||||
entry = next_entry;
|
||||
}
|
||||
|
||||
grub_free (menu);
|
||||
grub_env_unset_menu ();
|
||||
}
|
||||
|
||||
static grub_menu_t
|
||||
read_config_file (const char *config)
|
||||
{
|
||||
grub_file_t file;
|
||||
|
||||
auto grub_err_t getline (char **line, int cont);
|
||||
grub_err_t getline (char **line, int cont __attribute__ ((unused)))
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
char *buf;
|
||||
|
||||
*line = buf = grub_file_getline (file);
|
||||
if (! buf)
|
||||
return grub_errno;
|
||||
|
||||
if (buf[0] == '#')
|
||||
grub_free (*line);
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
grub_menu_t newmenu;
|
||||
|
||||
newmenu = grub_env_get_menu ();
|
||||
if (! newmenu)
|
||||
{
|
||||
newmenu = grub_zalloc (sizeof (*newmenu));
|
||||
if (! newmenu)
|
||||
return 0;
|
||||
|
||||
grub_env_set_menu (newmenu);
|
||||
}
|
||||
|
||||
/* Try to open the config file. */
|
||||
file = grub_file_open (config);
|
||||
if (! file)
|
||||
return 0;
|
||||
|
||||
while (1)
|
||||
{
|
||||
char *line;
|
||||
|
||||
/* Print an error, if any. */
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
if ((getline (&line, 0)) || (! line))
|
||||
break;
|
||||
|
||||
grub_normal_parse_line (line, getline);
|
||||
grub_free (line);
|
||||
}
|
||||
|
||||
grub_file_close (file);
|
||||
|
||||
return newmenu;
|
||||
}
|
||||
|
||||
/* Initialize the screen. */
|
||||
void
|
||||
grub_normal_init_page (struct grub_term_output *term)
|
||||
{
|
||||
int msg_len;
|
||||
int posx;
|
||||
const char *msg = _("GNU GRUB version %s");
|
||||
char *msg_formatted;
|
||||
grub_uint32_t *unicode_msg;
|
||||
grub_uint32_t *last_position;
|
||||
|
||||
grub_term_cls (term);
|
||||
|
||||
msg_formatted = grub_xasprintf (msg, PACKAGE_VERSION);
|
||||
if (!msg_formatted)
|
||||
return;
|
||||
|
||||
msg_len = grub_utf8_to_ucs4_alloc (msg_formatted,
|
||||
&unicode_msg, &last_position);
|
||||
grub_free (msg_formatted);
|
||||
|
||||
if (msg_len < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
posx = grub_getstringwidth (unicode_msg, last_position, term);
|
||||
posx = (grub_term_width (term) - posx) / 2;
|
||||
grub_term_gotoxy (term, posx, 1);
|
||||
|
||||
grub_print_ucs4 (unicode_msg, last_position, 0, 0, term);
|
||||
grub_putcode ('\n', term);
|
||||
grub_putcode ('\n', term);
|
||||
grub_free (unicode_msg);
|
||||
}
|
||||
|
||||
static void
|
||||
read_lists (const char *val)
|
||||
{
|
||||
if (! grub_no_autoload)
|
||||
{
|
||||
read_command_list (val);
|
||||
read_fs_list (val);
|
||||
read_crypto_list (val);
|
||||
read_terminal_list (val);
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
read_lists_hook (struct grub_env_var *var __attribute__ ((unused)),
|
||||
const char *val)
|
||||
{
|
||||
read_lists (val);
|
||||
return val ? grub_strdup (val) : NULL;
|
||||
}
|
||||
|
||||
/* Read the config file CONFIG and execute the menu interface or
|
||||
the command line interface if BATCH is false. */
|
||||
void
|
||||
grub_normal_execute (const char *config, int nested, int batch)
|
||||
{
|
||||
grub_menu_t menu = 0;
|
||||
const char *prefix;
|
||||
|
||||
if (! nested)
|
||||
{
|
||||
prefix = grub_env_get ("prefix");
|
||||
read_lists (prefix);
|
||||
grub_register_variable_hook ("prefix", NULL, read_lists_hook);
|
||||
grub_command_execute ("parser.grub", 0, 0);
|
||||
}
|
||||
|
||||
if (config)
|
||||
{
|
||||
menu = read_config_file (config);
|
||||
|
||||
/* Ignore any error. */
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
if (! batch)
|
||||
{
|
||||
if (menu && menu->size)
|
||||
{
|
||||
grub_show_menu (menu, nested);
|
||||
if (nested)
|
||||
free_menu (menu);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This starts the normal mode. */
|
||||
void
|
||||
grub_enter_normal_mode (const char *config)
|
||||
{
|
||||
nested_level++;
|
||||
grub_normal_execute (config, 0, 0);
|
||||
grub_cmdline_run (0);
|
||||
nested_level--;
|
||||
if (grub_normal_exit_level)
|
||||
grub_normal_exit_level--;
|
||||
}
|
||||
|
||||
/* Enter normal mode from rescue mode. */
|
||||
static grub_err_t
|
||||
grub_cmd_normal (struct grub_command *cmd __attribute__ ((unused)),
|
||||
int argc, char *argv[])
|
||||
{
|
||||
if (argc == 0)
|
||||
{
|
||||
/* Guess the config filename. It is necessary to make CONFIG static,
|
||||
so that it won't get broken by longjmp. */
|
||||
char *config;
|
||||
const char *prefix;
|
||||
|
||||
prefix = grub_env_get ("prefix");
|
||||
if (prefix)
|
||||
{
|
||||
config = grub_xasprintf ("%s/grub.cfg", prefix);
|
||||
if (! config)
|
||||
goto quit;
|
||||
|
||||
grub_enter_normal_mode (config);
|
||||
grub_free (config);
|
||||
}
|
||||
else
|
||||
grub_enter_normal_mode (0);
|
||||
}
|
||||
else
|
||||
grub_enter_normal_mode (argv[0]);
|
||||
|
||||
quit:
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Exit from normal mode to rescue mode. */
|
||||
static grub_err_t
|
||||
grub_cmd_normal_exit (struct grub_command *cmd __attribute__ ((unused)),
|
||||
int argc __attribute__ ((unused)),
|
||||
char *argv[] __attribute__ ((unused)))
|
||||
{
|
||||
if (nested_level <= grub_normal_exit_level)
|
||||
return grub_error (GRUB_ERR_BAD_ARGUMENT, "not in normal environment");
|
||||
grub_normal_exit_level++;
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_normal_reader_init (int nested)
|
||||
{
|
||||
struct grub_term_output *term;
|
||||
const char *msg = _("Minimal BASH-like line editing is supported. For "
|
||||
"the first word, TAB lists possible command completions. Anywhere "
|
||||
"else TAB lists possible device or file completions. %s");
|
||||
const char *msg_esc = _("ESC at any time exits.");
|
||||
char *msg_formatted;
|
||||
|
||||
msg_formatted = grub_xasprintf (msg, nested ? msg_esc : "");
|
||||
if (!msg_formatted)
|
||||
return grub_errno;
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
grub_normal_init_page (term);
|
||||
grub_term_setcursor (term, 1);
|
||||
|
||||
grub_print_message_indented (msg_formatted, 3, STANDARD_MARGIN, term);
|
||||
grub_putcode ('\n', term);
|
||||
grub_putcode ('\n', term);
|
||||
}
|
||||
grub_free (msg_formatted);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_normal_read_line_real (char **line, int cont, int nested)
|
||||
{
|
||||
const char *prompt;
|
||||
|
||||
if (cont)
|
||||
prompt = ">";
|
||||
else
|
||||
prompt = "grub>";
|
||||
|
||||
if (!prompt)
|
||||
return grub_errno;
|
||||
|
||||
while (1)
|
||||
{
|
||||
*line = grub_cmdline_get (prompt);
|
||||
if (*line)
|
||||
break;
|
||||
|
||||
if (cont || nested)
|
||||
{
|
||||
grub_free (*line);
|
||||
*line = 0;
|
||||
return grub_errno;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_err_t
|
||||
grub_normal_read_line (char **line, int cont)
|
||||
{
|
||||
return grub_normal_read_line_real (line, cont, 0);
|
||||
}
|
||||
|
||||
void
|
||||
grub_cmdline_run (int nested)
|
||||
{
|
||||
grub_err_t err = GRUB_ERR_NONE;
|
||||
|
||||
err = grub_auth_check_authentication (NULL);
|
||||
|
||||
if (err)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
grub_normal_reader_init (nested);
|
||||
|
||||
while (1)
|
||||
{
|
||||
char *line;
|
||||
|
||||
if (grub_normal_exit_level)
|
||||
break;
|
||||
|
||||
/* Print an error, if any. */
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
grub_normal_read_line_real (&line, 0, nested);
|
||||
if (! line)
|
||||
break;
|
||||
|
||||
grub_normal_parse_line (line, grub_normal_read_line);
|
||||
grub_free (line);
|
||||
}
|
||||
}
|
||||
|
||||
static char *
|
||||
grub_env_write_pager (struct grub_env_var *var __attribute__ ((unused)),
|
||||
const char *val)
|
||||
{
|
||||
grub_set_more ((*val == '1'));
|
||||
return grub_strdup (val);
|
||||
}
|
||||
|
||||
/* clear */
|
||||
static grub_err_t
|
||||
grub_mini_cmd_clear (struct grub_command *cmd __attribute__ ((unused)),
|
||||
int argc __attribute__ ((unused)),
|
||||
char *argv[] __attribute__ ((unused)))
|
||||
{
|
||||
grub_cls ();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static grub_command_t cmd_clear;
|
||||
|
||||
static void (*grub_xputs_saved) (const char *str);
|
||||
|
||||
GRUB_MOD_INIT(normal)
|
||||
{
|
||||
/* Previously many modules depended on gzio. Be nice to user and load it. */
|
||||
grub_dl_load ("gzio");
|
||||
|
||||
grub_context_init ();
|
||||
grub_script_init ();
|
||||
grub_menu_init ();
|
||||
|
||||
grub_xputs_saved = grub_xputs;
|
||||
grub_xputs = grub_xputs_normal;
|
||||
|
||||
/* Normal mode shouldn't be unloaded. */
|
||||
if (mod)
|
||||
grub_dl_ref (mod);
|
||||
|
||||
cmd_clear =
|
||||
grub_register_command ("clear", grub_mini_cmd_clear,
|
||||
0, N_("Clear the screen."));
|
||||
|
||||
grub_set_history (GRUB_DEFAULT_HISTORY_SIZE);
|
||||
|
||||
grub_register_variable_hook ("pager", 0, grub_env_write_pager);
|
||||
|
||||
/* Register a command "normal" for the rescue mode. */
|
||||
grub_register_command ("normal", grub_cmd_normal,
|
||||
0, N_("Enter normal mode."));
|
||||
grub_register_command ("normal_exit", grub_cmd_normal_exit,
|
||||
0, N_("Exit from normal mode."));
|
||||
|
||||
/* Reload terminal colors when these variables are written to. */
|
||||
grub_register_variable_hook ("color_normal", NULL, grub_env_write_color_normal);
|
||||
grub_register_variable_hook ("color_highlight", NULL, grub_env_write_color_highlight);
|
||||
|
||||
/* Preserve hooks after context changes. */
|
||||
grub_env_export ("color_normal");
|
||||
grub_env_export ("color_highlight");
|
||||
|
||||
/* Set default color names. */
|
||||
grub_env_set ("color_normal", "white/black");
|
||||
grub_env_set ("color_highlight", "black/white");
|
||||
}
|
||||
|
||||
GRUB_MOD_FINI(normal)
|
||||
{
|
||||
grub_context_fini ();
|
||||
grub_script_fini ();
|
||||
grub_menu_fini ();
|
||||
|
||||
grub_xputs = grub_xputs_saved;
|
||||
|
||||
grub_set_history (0);
|
||||
grub_register_variable_hook ("pager", 0, 0);
|
||||
grub_fs_autoload_hook = 0;
|
||||
grub_unregister_command (cmd_clear);
|
||||
}
|
623
grub-core/normal/menu.c
Normal file
623
grub-core/normal/menu.c
Normal file
|
@ -0,0 +1,623 @@
|
|||
/* menu.c - General supporting functionality for menus. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010 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/misc.h>
|
||||
#include <grub/loader.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/menu_viewer.h>
|
||||
#include <grub/command.h>
|
||||
#include <grub/parser.h>
|
||||
#include <grub/auth.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/script_sh.h>
|
||||
|
||||
/* Time to delay after displaying an error message about a default/fallback
|
||||
entry failing to boot. */
|
||||
#define DEFAULT_ENTRY_ERROR_DELAY_MS 2500
|
||||
|
||||
grub_err_t (*grub_gfxmenu_try_hook) (int entry, grub_menu_t menu,
|
||||
int nested) = NULL;
|
||||
|
||||
/* Wait until the user pushes any key so that the user
|
||||
can see what happened. */
|
||||
void
|
||||
grub_wait_after_message (void)
|
||||
{
|
||||
grub_uint64_t endtime;
|
||||
grub_xputs ("\n");
|
||||
grub_printf_ (N_("Press any key to continue..."));
|
||||
grub_refresh ();
|
||||
|
||||
endtime = grub_get_time_ms () + 10000;
|
||||
|
||||
while (grub_get_time_ms () < endtime)
|
||||
if (grub_checkkey () >= 0)
|
||||
{
|
||||
grub_getkey ();
|
||||
break;
|
||||
}
|
||||
|
||||
grub_xputs ("\n");
|
||||
}
|
||||
|
||||
/* Get a menu entry by its index in the entry list. */
|
||||
grub_menu_entry_t
|
||||
grub_menu_get_entry (grub_menu_t menu, int no)
|
||||
{
|
||||
grub_menu_entry_t e;
|
||||
|
||||
for (e = menu->entry_list; e && no > 0; e = e->next, no--)
|
||||
;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
/* Return the current timeout. If the variable "timeout" is not set or
|
||||
invalid, return -1. */
|
||||
int
|
||||
grub_menu_get_timeout (void)
|
||||
{
|
||||
char *val;
|
||||
int timeout;
|
||||
|
||||
val = grub_env_get ("timeout");
|
||||
if (! val)
|
||||
return -1;
|
||||
|
||||
grub_error_push ();
|
||||
|
||||
timeout = (int) grub_strtoul (val, 0, 0);
|
||||
|
||||
/* If the value is invalid, unset the variable. */
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
{
|
||||
grub_env_unset ("timeout");
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
timeout = -1;
|
||||
}
|
||||
|
||||
grub_error_pop ();
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/* Set current timeout in the variable "timeout". */
|
||||
void
|
||||
grub_menu_set_timeout (int timeout)
|
||||
{
|
||||
/* Ignore TIMEOUT if it is zero, because it will be unset really soon. */
|
||||
if (timeout > 0)
|
||||
{
|
||||
char buf[16];
|
||||
|
||||
grub_snprintf (buf, sizeof (buf), "%d", timeout);
|
||||
grub_env_set ("timeout", buf);
|
||||
}
|
||||
}
|
||||
|
||||
/* Get the first entry number from the value of the environment variable NAME,
|
||||
which is a space-separated list of non-negative integers. The entry number
|
||||
which is returned is stripped from the value of NAME. If no entry number
|
||||
can be found, -1 is returned. */
|
||||
static int
|
||||
get_and_remove_first_entry_number (const char *name)
|
||||
{
|
||||
char *val;
|
||||
char *tail;
|
||||
int entry;
|
||||
|
||||
val = grub_env_get (name);
|
||||
if (! val)
|
||||
return -1;
|
||||
|
||||
grub_error_push ();
|
||||
|
||||
entry = (int) grub_strtoul (val, &tail, 0);
|
||||
|
||||
if (grub_errno == GRUB_ERR_NONE)
|
||||
{
|
||||
/* Skip whitespace to find the next digit. */
|
||||
while (*tail && grub_isspace (*tail))
|
||||
tail++;
|
||||
grub_env_set (name, tail);
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_env_unset (name);
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
entry = -1;
|
||||
}
|
||||
|
||||
grub_error_pop ();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
/* Run a menu entry. */
|
||||
void
|
||||
grub_menu_execute_entry(grub_menu_entry_t entry)
|
||||
{
|
||||
grub_err_t err = GRUB_ERR_NONE;
|
||||
|
||||
if (entry->restricted)
|
||||
err = grub_auth_check_authentication (entry->users);
|
||||
|
||||
if (err)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
grub_env_set ("chosen", entry->title);
|
||||
grub_script_execute_sourcecode (entry->sourcecode, entry->argc, entry->args);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* Execute ENTRY from the menu MENU, falling back to entries specified
|
||||
in the environment variable "fallback" if it fails. CALLBACK is a
|
||||
pointer to a struct of function pointers which are used to allow the
|
||||
caller provide feedback to the user. */
|
||||
void
|
||||
grub_menu_execute_with_fallback (grub_menu_t menu,
|
||||
grub_menu_entry_t entry,
|
||||
grub_menu_execute_callback_t callback,
|
||||
void *callback_data)
|
||||
{
|
||||
int fallback_entry;
|
||||
|
||||
callback->notify_booting (entry, callback_data);
|
||||
|
||||
grub_menu_execute_entry (entry);
|
||||
|
||||
/* Deal with fallback entries. */
|
||||
while ((fallback_entry = get_and_remove_first_entry_number ("fallback"))
|
||||
>= 0)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
entry = grub_menu_get_entry (menu, fallback_entry);
|
||||
callback->notify_fallback (entry, callback_data);
|
||||
grub_menu_execute_entry (entry);
|
||||
/* If the function call to execute the entry returns at all, then this is
|
||||
taken to indicate a boot failure. For menu entries that do something
|
||||
other than actually boot an operating system, this could assume
|
||||
incorrectly that something failed. */
|
||||
}
|
||||
|
||||
callback->notify_failure (callback_data);
|
||||
}
|
||||
|
||||
static struct grub_menu_viewer *viewers;
|
||||
|
||||
static void
|
||||
menu_set_chosen_entry (int entry)
|
||||
{
|
||||
struct grub_menu_viewer *cur;
|
||||
for (cur = viewers; cur; cur = cur->next)
|
||||
cur->set_chosen_entry (entry, cur->data);
|
||||
}
|
||||
|
||||
static void
|
||||
menu_print_timeout (int timeout)
|
||||
{
|
||||
struct grub_menu_viewer *cur;
|
||||
for (cur = viewers; cur; cur = cur->next)
|
||||
cur->print_timeout (timeout, cur->data);
|
||||
}
|
||||
|
||||
static void
|
||||
menu_fini (void)
|
||||
{
|
||||
struct grub_menu_viewer *cur, *next;
|
||||
for (cur = viewers; cur; cur = next)
|
||||
{
|
||||
next = cur->next;
|
||||
cur->fini (cur->data);
|
||||
grub_free (cur);
|
||||
}
|
||||
viewers = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
menu_init (int entry, grub_menu_t menu, int nested)
|
||||
{
|
||||
struct grub_term_output *term;
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
grub_err_t err;
|
||||
|
||||
if (grub_gfxmenu_try_hook && grub_strcmp (term->name, "gfxterm") == 0)
|
||||
{
|
||||
err = grub_gfxmenu_try_hook (entry, menu, nested);
|
||||
if(!err)
|
||||
continue;
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
err = grub_menu_try_text (term, entry, menu, nested);
|
||||
if(!err)
|
||||
continue;
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
clear_timeout (void)
|
||||
{
|
||||
struct grub_menu_viewer *cur;
|
||||
for (cur = viewers; cur; cur = cur->next)
|
||||
cur->clear_timeout (cur->data);
|
||||
}
|
||||
|
||||
void
|
||||
grub_menu_register_viewer (struct grub_menu_viewer *viewer)
|
||||
{
|
||||
viewer->next = viewers;
|
||||
viewers = viewer;
|
||||
}
|
||||
|
||||
/* Get the entry number from the variable NAME. */
|
||||
static int
|
||||
get_entry_number (grub_menu_t menu, const char *name)
|
||||
{
|
||||
char *val;
|
||||
int entry;
|
||||
|
||||
val = grub_env_get (name);
|
||||
if (! val)
|
||||
return -1;
|
||||
|
||||
grub_error_push ();
|
||||
|
||||
entry = (int) grub_strtoul (val, 0, 0);
|
||||
|
||||
if (grub_errno == GRUB_ERR_BAD_NUMBER)
|
||||
{
|
||||
/* See if the variable matches the title of a menu entry. */
|
||||
grub_menu_entry_t e = menu->entry_list;
|
||||
int i;
|
||||
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
for (i = 0; e; i++)
|
||||
{
|
||||
if (grub_strcmp (e->title, val) == 0)
|
||||
{
|
||||
entry = i;
|
||||
break;
|
||||
}
|
||||
e = e->next;
|
||||
}
|
||||
|
||||
if (! e)
|
||||
entry = -1;
|
||||
}
|
||||
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
entry = -1;
|
||||
}
|
||||
|
||||
grub_error_pop ();
|
||||
|
||||
return entry;
|
||||
}
|
||||
|
||||
#define GRUB_MENU_PAGE_SIZE 10
|
||||
|
||||
/* Show the menu and handle menu entry selection. Returns the menu entry
|
||||
index that should be executed or -1 if no entry should be executed (e.g.,
|
||||
Esc pressed to exit a sub-menu or switching menu viewers).
|
||||
If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
|
||||
entry to be executed is a result of an automatic default selection because
|
||||
of the timeout. */
|
||||
static int
|
||||
run_menu (grub_menu_t menu, int nested, int *auto_boot)
|
||||
{
|
||||
grub_uint64_t saved_time;
|
||||
int default_entry, current_entry;
|
||||
int timeout;
|
||||
|
||||
default_entry = get_entry_number (menu, "default");
|
||||
|
||||
/* If DEFAULT_ENTRY is not within the menu entries, fall back to
|
||||
the first entry. */
|
||||
if (default_entry < 0 || default_entry >= menu->size)
|
||||
default_entry = 0;
|
||||
|
||||
/* If timeout is 0, drawing is pointless (and ugly). */
|
||||
if (grub_menu_get_timeout () == 0)
|
||||
{
|
||||
*auto_boot = 1;
|
||||
return default_entry;
|
||||
}
|
||||
|
||||
current_entry = default_entry;
|
||||
|
||||
/* Initialize the time. */
|
||||
saved_time = grub_get_time_ms ();
|
||||
|
||||
refresh:
|
||||
menu_init (current_entry, menu, nested);
|
||||
|
||||
timeout = grub_menu_get_timeout ();
|
||||
|
||||
if (timeout > 0)
|
||||
menu_print_timeout (timeout);
|
||||
else
|
||||
clear_timeout ();
|
||||
|
||||
while (1)
|
||||
{
|
||||
int c;
|
||||
timeout = grub_menu_get_timeout ();
|
||||
|
||||
if (grub_normal_exit_level)
|
||||
return -1;
|
||||
|
||||
if (timeout > 0)
|
||||
{
|
||||
grub_uint64_t current_time;
|
||||
|
||||
current_time = grub_get_time_ms ();
|
||||
if (current_time - saved_time >= 1000)
|
||||
{
|
||||
timeout--;
|
||||
grub_menu_set_timeout (timeout);
|
||||
saved_time = current_time;
|
||||
menu_print_timeout (timeout);
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout == 0)
|
||||
{
|
||||
grub_env_unset ("timeout");
|
||||
*auto_boot = 1;
|
||||
menu_fini ();
|
||||
return default_entry;
|
||||
}
|
||||
|
||||
if (grub_checkkey () >= 0 || timeout < 0)
|
||||
{
|
||||
c = GRUB_TERM_ASCII_CHAR (grub_getkey ());
|
||||
|
||||
if (timeout >= 0)
|
||||
{
|
||||
grub_env_unset ("timeout");
|
||||
grub_env_unset ("fallback");
|
||||
clear_timeout ();
|
||||
}
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case GRUB_TERM_HOME:
|
||||
current_entry = 0;
|
||||
menu_set_chosen_entry (current_entry);
|
||||
break;
|
||||
|
||||
case GRUB_TERM_END:
|
||||
current_entry = menu->size - 1;
|
||||
menu_set_chosen_entry (current_entry);
|
||||
break;
|
||||
|
||||
case GRUB_TERM_UP:
|
||||
case '^':
|
||||
if (current_entry > 0)
|
||||
current_entry--;
|
||||
menu_set_chosen_entry (current_entry);
|
||||
break;
|
||||
|
||||
case GRUB_TERM_DOWN:
|
||||
case 'v':
|
||||
if (current_entry < menu->size - 1)
|
||||
current_entry++;
|
||||
menu_set_chosen_entry (current_entry);
|
||||
break;
|
||||
|
||||
case GRUB_TERM_PPAGE:
|
||||
if (current_entry < GRUB_MENU_PAGE_SIZE)
|
||||
current_entry = 0;
|
||||
else
|
||||
current_entry -= GRUB_MENU_PAGE_SIZE;
|
||||
menu_set_chosen_entry (current_entry);
|
||||
break;
|
||||
|
||||
case GRUB_TERM_NPAGE:
|
||||
if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size)
|
||||
current_entry += GRUB_MENU_PAGE_SIZE;
|
||||
else
|
||||
current_entry = menu->size - 1;
|
||||
menu_set_chosen_entry (current_entry);
|
||||
break;
|
||||
|
||||
case '\n':
|
||||
case '\r':
|
||||
case 6:
|
||||
menu_fini ();
|
||||
*auto_boot = 0;
|
||||
return current_entry;
|
||||
|
||||
case '\e':
|
||||
if (nested)
|
||||
{
|
||||
menu_fini ();
|
||||
return -1;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'c':
|
||||
menu_fini ();
|
||||
grub_cmdline_run (1);
|
||||
goto refresh;
|
||||
|
||||
case 'e':
|
||||
menu_fini ();
|
||||
{
|
||||
grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
|
||||
if (e)
|
||||
grub_menu_entry_run (e);
|
||||
}
|
||||
goto refresh;
|
||||
|
||||
default:
|
||||
{
|
||||
grub_menu_entry_t entry;
|
||||
int i;
|
||||
for (i = 0, entry = menu->entry_list; i < menu->size;
|
||||
i++, entry = entry->next)
|
||||
if (entry->hotkey == c)
|
||||
{
|
||||
menu_fini ();
|
||||
*auto_boot = 0;
|
||||
return i;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Never reach here. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Callback invoked immediately before a menu entry is executed. */
|
||||
static void
|
||||
notify_booting (grub_menu_entry_t entry,
|
||||
void *userdata __attribute__((unused)))
|
||||
{
|
||||
grub_printf (" ");
|
||||
grub_printf_ (N_("Booting \'%s\'"), entry->title);
|
||||
grub_printf ("\n\n");
|
||||
}
|
||||
|
||||
/* Callback invoked when a default menu entry executed because of a timeout
|
||||
has failed and an attempt will be made to execute the next fallback
|
||||
entry, ENTRY. */
|
||||
static void
|
||||
notify_fallback (grub_menu_entry_t entry,
|
||||
void *userdata __attribute__((unused)))
|
||||
{
|
||||
grub_printf ("\n ");
|
||||
grub_printf_ (N_("Falling back to \'%s\'"), entry->title);
|
||||
grub_printf ("\n\n");
|
||||
grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
|
||||
}
|
||||
|
||||
/* Callback invoked when a menu entry has failed and there is no remaining
|
||||
fallback entry to attempt. */
|
||||
static void
|
||||
notify_execution_failure (void *userdata __attribute__((unused)))
|
||||
{
|
||||
if (grub_errno != GRUB_ERR_NONE)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
grub_printf ("\n ");
|
||||
grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
|
||||
grub_wait_after_message ();
|
||||
}
|
||||
|
||||
/* Callbacks used by the text menu to provide user feedback when menu entries
|
||||
are executed. */
|
||||
static struct grub_menu_execute_callback execution_callback =
|
||||
{
|
||||
.notify_booting = notify_booting,
|
||||
.notify_fallback = notify_fallback,
|
||||
.notify_failure = notify_execution_failure
|
||||
};
|
||||
|
||||
static grub_err_t
|
||||
show_menu (grub_menu_t menu, int nested)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
int boot_entry;
|
||||
grub_menu_entry_t e;
|
||||
int auto_boot;
|
||||
|
||||
boot_entry = run_menu (menu, nested, &auto_boot);
|
||||
if (boot_entry < 0)
|
||||
break;
|
||||
|
||||
e = grub_menu_get_entry (menu, boot_entry);
|
||||
if (! e)
|
||||
continue; /* Menu is empty. */
|
||||
|
||||
grub_cls ();
|
||||
|
||||
if (auto_boot)
|
||||
{
|
||||
grub_menu_execute_with_fallback (menu, e, &execution_callback, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
int chars_before = grub_normal_get_char_counter ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
grub_menu_execute_entry (e);
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
|
||||
if (chars_before != grub_normal_get_char_counter ())
|
||||
grub_wait_after_message ();
|
||||
}
|
||||
}
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_show_menu (grub_menu_t menu, int nested)
|
||||
{
|
||||
grub_err_t err1, err2;
|
||||
|
||||
while (1)
|
||||
{
|
||||
err1 = show_menu (menu, nested);
|
||||
grub_print_error ();
|
||||
|
||||
if (grub_normal_exit_level)
|
||||
break;
|
||||
|
||||
err2 = grub_auth_check_authentication (NULL);
|
||||
if (err2)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return err1;
|
||||
}
|
1414
grub-core/normal/menu_entry.c
Normal file
1414
grub-core/normal/menu_entry.c
Normal file
File diff suppressed because it is too large
Load diff
459
grub-core/normal/menu_text.c
Normal file
459
grub-core/normal/menu_text.c
Normal file
|
@ -0,0 +1,459 @@
|
|||
/* menu_text.c - Basic text menu implementation. */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2003,2004,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/loader.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/time.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/menu_viewer.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/charset.h>
|
||||
|
||||
static grub_uint8_t grub_color_menu_normal;
|
||||
static grub_uint8_t grub_color_menu_highlight;
|
||||
|
||||
struct menu_viewer_data
|
||||
{
|
||||
int first, offset;
|
||||
grub_menu_t menu;
|
||||
struct grub_term_output *term;
|
||||
};
|
||||
|
||||
grub_ssize_t
|
||||
grub_getstringwidth (grub_uint32_t * str, const grub_uint32_t * last_position,
|
||||
struct grub_term_output *term)
|
||||
{
|
||||
grub_ssize_t width = 0;
|
||||
|
||||
while (str < last_position)
|
||||
{
|
||||
struct grub_unicode_glyph glyph;
|
||||
str += grub_unicode_aglomerate_comb (str, last_position - str, &glyph);
|
||||
width += grub_term_getcharwidth (term, &glyph);
|
||||
}
|
||||
return width;
|
||||
}
|
||||
|
||||
void
|
||||
grub_print_message_indented (const char *msg, int margin_left, int margin_right,
|
||||
struct grub_term_output *term)
|
||||
{
|
||||
grub_uint32_t *unicode_msg;
|
||||
grub_uint32_t *last_position;
|
||||
|
||||
int msg_len;
|
||||
|
||||
msg_len = grub_utf8_to_ucs4_alloc (msg, &unicode_msg, &last_position);
|
||||
|
||||
if (msg_len < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
grub_print_ucs4 (unicode_msg, last_position, margin_left, margin_right, term);
|
||||
|
||||
grub_free (unicode_msg);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
draw_border (struct grub_term_output *term)
|
||||
{
|
||||
unsigned i;
|
||||
|
||||
grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
|
||||
|
||||
grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y);
|
||||
grub_putcode (GRUB_UNICODE_CORNER_UL, term);
|
||||
for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++)
|
||||
grub_putcode (GRUB_UNICODE_HLINE, term);
|
||||
grub_putcode (GRUB_UNICODE_CORNER_UR, term);
|
||||
|
||||
for (i = 0; i < (unsigned) grub_term_num_entries (term); i++)
|
||||
{
|
||||
grub_term_gotoxy (term, GRUB_TERM_MARGIN, GRUB_TERM_TOP_BORDER_Y + i + 1);
|
||||
grub_putcode (GRUB_UNICODE_VLINE, term);
|
||||
grub_term_gotoxy (term, GRUB_TERM_MARGIN + grub_term_border_width (term)
|
||||
- 1,
|
||||
GRUB_TERM_TOP_BORDER_Y + i + 1);
|
||||
grub_putcode (GRUB_UNICODE_VLINE, term);
|
||||
}
|
||||
|
||||
grub_term_gotoxy (term, GRUB_TERM_MARGIN,
|
||||
GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term) + 1);
|
||||
grub_putcode (GRUB_UNICODE_CORNER_LL, term);
|
||||
for (i = 0; i < (unsigned) grub_term_border_width (term) - 2; i++)
|
||||
grub_putcode (GRUB_UNICODE_HLINE, term);
|
||||
grub_putcode (GRUB_UNICODE_CORNER_LR, term);
|
||||
|
||||
grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
|
||||
|
||||
grub_term_gotoxy (term, GRUB_TERM_MARGIN,
|
||||
(GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term)
|
||||
+ GRUB_TERM_MARGIN + 1));
|
||||
}
|
||||
|
||||
static void
|
||||
print_message (int nested, int edit, struct grub_term_output *term)
|
||||
{
|
||||
grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
|
||||
|
||||
if (edit)
|
||||
{
|
||||
grub_putcode ('\n', term);
|
||||
#ifdef GRUB_MACHINE_EFI
|
||||
grub_print_message_indented (_("Minimum Emacs-like screen editing is \
|
||||
supported. TAB lists completions. Press F1 to boot, F2=Ctrl-a, F3=Ctrl-e, \
|
||||
F4 for a command-line or ESC to discard edits and return to the GRUB menu."),
|
||||
STANDARD_MARGIN, STANDARD_MARGIN, term);
|
||||
#else
|
||||
grub_print_message_indented (_("Minimum Emacs-like screen editing is \
|
||||
supported. TAB lists completions. Press Ctrl-x to boot, Ctrl-c for a \
|
||||
command-line or ESC to discard edits and return to the GRUB menu."),
|
||||
STANDARD_MARGIN, STANDARD_MARGIN, term);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
const char *msg = _("Use the %C and %C keys to select which "
|
||||
"entry is highlighted.\n");
|
||||
char *msg_translated;
|
||||
|
||||
msg_translated = grub_xasprintf (msg, GRUB_UNICODE_UPARROW,
|
||||
GRUB_UNICODE_DOWNARROW);
|
||||
if (!msg_translated)
|
||||
return;
|
||||
grub_putcode ('\n', term);
|
||||
grub_print_message_indented (msg_translated, STANDARD_MARGIN,
|
||||
STANDARD_MARGIN, term);
|
||||
|
||||
grub_free (msg_translated);
|
||||
|
||||
if (nested)
|
||||
{
|
||||
grub_print_message_indented
|
||||
(_("Press enter to boot the selected OS, "
|
||||
"\'e\' to edit the commands before booting "
|
||||
"or \'c\' for a command-line. ESC to return previous menu.\n"),
|
||||
STANDARD_MARGIN, STANDARD_MARGIN, term);
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_print_message_indented
|
||||
(_("Press enter to boot the selected OS, "
|
||||
"\'e\' to edit the commands before booting "
|
||||
"or \'c\' for a command-line.\n"),
|
||||
STANDARD_MARGIN, STANDARD_MARGIN, term);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
print_entry (int y, int highlight, grub_menu_entry_t entry,
|
||||
struct grub_term_output *term)
|
||||
{
|
||||
int x;
|
||||
const char *title;
|
||||
grub_size_t title_len;
|
||||
grub_ssize_t len;
|
||||
grub_uint32_t *unicode_title;
|
||||
grub_ssize_t i;
|
||||
grub_uint8_t old_color_normal, old_color_highlight;
|
||||
|
||||
title = entry ? entry->title : "";
|
||||
title_len = grub_strlen (title);
|
||||
unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
|
||||
if (! unicode_title)
|
||||
/* XXX How to show this error? */
|
||||
return;
|
||||
|
||||
len = grub_utf8_to_ucs4 (unicode_title, title_len,
|
||||
(grub_uint8_t *) title, -1, 0);
|
||||
if (len < 0)
|
||||
{
|
||||
/* It is an invalid sequence. */
|
||||
grub_free (unicode_title);
|
||||
return;
|
||||
}
|
||||
|
||||
grub_term_getcolor (term, &old_color_normal, &old_color_highlight);
|
||||
grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight);
|
||||
grub_term_setcolorstate (term, highlight
|
||||
? GRUB_TERM_COLOR_HIGHLIGHT
|
||||
: GRUB_TERM_COLOR_NORMAL);
|
||||
|
||||
grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN, y);
|
||||
|
||||
int last_printed = 0;
|
||||
for (x = GRUB_TERM_LEFT_BORDER_X + GRUB_TERM_MARGIN + 1, i = 0;
|
||||
x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
|
||||
- GRUB_TERM_MARGIN);)
|
||||
{
|
||||
if (i < len
|
||||
&& x <= (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
|
||||
- GRUB_TERM_MARGIN - 1))
|
||||
{
|
||||
grub_ssize_t width;
|
||||
struct grub_unicode_glyph glyph;
|
||||
|
||||
i += grub_unicode_aglomerate_comb (unicode_title + i,
|
||||
len - i, &glyph);
|
||||
|
||||
width = grub_term_getcharwidth (term, &glyph);
|
||||
grub_free (glyph.combining);
|
||||
|
||||
if (x + width <= (int) (GRUB_TERM_LEFT_BORDER_X
|
||||
+ grub_term_border_width (term)
|
||||
- GRUB_TERM_MARGIN - 1))
|
||||
last_printed = i;
|
||||
x += width;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
grub_print_ucs4 (unicode_title,
|
||||
unicode_title + last_printed, 0, 0, term);
|
||||
|
||||
if (last_printed != len)
|
||||
{
|
||||
grub_putcode (GRUB_UNICODE_RIGHTARROW, term);
|
||||
struct grub_unicode_glyph pseudo_glyph = {
|
||||
.base = GRUB_UNICODE_RIGHTARROW,
|
||||
.variant = 0,
|
||||
.attributes = 0,
|
||||
.ncomb = 0,
|
||||
.combining = 0,
|
||||
.estimated_width = 1
|
||||
};
|
||||
x += grub_term_getcharwidth (term, &pseudo_glyph);
|
||||
}
|
||||
|
||||
for (; x < (int) (GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term)
|
||||
- GRUB_TERM_MARGIN); x++)
|
||||
grub_putcode (' ', term);
|
||||
|
||||
grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
|
||||
grub_putcode (' ', term);
|
||||
|
||||
grub_term_gotoxy (term, grub_term_cursor_x (term), y);
|
||||
|
||||
grub_term_setcolor (term, old_color_normal, old_color_highlight);
|
||||
grub_term_setcolorstate (term, GRUB_TERM_COLOR_NORMAL);
|
||||
grub_free (unicode_title);
|
||||
}
|
||||
|
||||
static void
|
||||
print_entries (grub_menu_t menu, int first, int offset,
|
||||
struct grub_term_output *term)
|
||||
{
|
||||
grub_menu_entry_t e;
|
||||
int i;
|
||||
|
||||
grub_term_gotoxy (term,
|
||||
GRUB_TERM_LEFT_BORDER_X + grub_term_border_width (term),
|
||||
GRUB_TERM_FIRST_ENTRY_Y);
|
||||
|
||||
if (first)
|
||||
grub_putcode (GRUB_UNICODE_UPARROW, term);
|
||||
else
|
||||
grub_putcode (' ', term);
|
||||
|
||||
e = grub_menu_get_entry (menu, first);
|
||||
|
||||
for (i = 0; i < grub_term_num_entries (term); i++)
|
||||
{
|
||||
print_entry (GRUB_TERM_FIRST_ENTRY_Y + i, offset == i, e, term);
|
||||
if (e)
|
||||
e = e->next;
|
||||
}
|
||||
|
||||
grub_term_gotoxy (term, GRUB_TERM_LEFT_BORDER_X
|
||||
+ grub_term_border_width (term),
|
||||
GRUB_TERM_TOP_BORDER_Y + grub_term_num_entries (term));
|
||||
|
||||
if (e)
|
||||
grub_putcode (GRUB_UNICODE_DOWNARROW, term);
|
||||
else
|
||||
grub_putcode (' ', term);
|
||||
|
||||
grub_term_gotoxy (term, grub_term_cursor_x (term),
|
||||
GRUB_TERM_FIRST_ENTRY_Y + offset);
|
||||
}
|
||||
|
||||
/* Initialize the screen. If NESTED is non-zero, assume that this menu
|
||||
is run from another menu or a command-line. If EDIT is non-zero, show
|
||||
a message for the menu entry editor. */
|
||||
void
|
||||
grub_menu_init_page (int nested, int edit,
|
||||
struct grub_term_output *term)
|
||||
{
|
||||
grub_uint8_t old_color_normal, old_color_highlight;
|
||||
|
||||
grub_term_getcolor (term, &old_color_normal, &old_color_highlight);
|
||||
|
||||
/* By default, use the same colors for the menu. */
|
||||
grub_color_menu_normal = old_color_normal;
|
||||
grub_color_menu_highlight = old_color_highlight;
|
||||
|
||||
/* Then give user a chance to replace them. */
|
||||
grub_parse_color_name_pair (&grub_color_menu_normal,
|
||||
grub_env_get ("menu_color_normal"));
|
||||
grub_parse_color_name_pair (&grub_color_menu_highlight,
|
||||
grub_env_get ("menu_color_highlight"));
|
||||
|
||||
grub_normal_init_page (term);
|
||||
grub_term_setcolor (term, grub_color_menu_normal, grub_color_menu_highlight);
|
||||
draw_border (term);
|
||||
grub_term_setcolor (term, old_color_normal, old_color_highlight);
|
||||
print_message (nested, edit, term);
|
||||
}
|
||||
|
||||
static void
|
||||
menu_text_print_timeout (int timeout, void *dataptr)
|
||||
{
|
||||
const char *msg =
|
||||
_("The highlighted entry will be executed automatically in %ds.");
|
||||
struct menu_viewer_data *data = dataptr;
|
||||
char *msg_translated;
|
||||
int posx;
|
||||
|
||||
grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3);
|
||||
|
||||
msg_translated = grub_xasprintf (msg, timeout);
|
||||
if (!msg_translated)
|
||||
{
|
||||
grub_print_error ();
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
grub_print_message_indented (msg_translated, 3, 0, data->term);
|
||||
|
||||
posx = grub_term_getxy (data->term) >> 8;
|
||||
grub_print_spaces (data->term, grub_term_width (data->term) - posx - 1);
|
||||
|
||||
grub_term_gotoxy (data->term,
|
||||
grub_term_cursor_x (data->term),
|
||||
GRUB_TERM_FIRST_ENTRY_Y + data->offset);
|
||||
grub_term_refresh (data->term);
|
||||
}
|
||||
|
||||
static void
|
||||
menu_text_set_chosen_entry (int entry, void *dataptr)
|
||||
{
|
||||
struct menu_viewer_data *data = dataptr;
|
||||
int oldoffset = data->offset;
|
||||
int complete_redraw = 0;
|
||||
|
||||
data->offset = entry - data->first;
|
||||
if (data->offset > grub_term_num_entries (data->term) - 1)
|
||||
{
|
||||
data->first = entry - (grub_term_num_entries (data->term) - 1);
|
||||
data->offset = grub_term_num_entries (data->term) - 1;
|
||||
complete_redraw = 1;
|
||||
}
|
||||
if (data->offset < 0)
|
||||
{
|
||||
data->offset = 0;
|
||||
data->first = entry;
|
||||
complete_redraw = 1;
|
||||
}
|
||||
if (complete_redraw)
|
||||
print_entries (data->menu, data->first, data->offset, data->term);
|
||||
else
|
||||
{
|
||||
print_entry (GRUB_TERM_FIRST_ENTRY_Y + oldoffset, 0,
|
||||
grub_menu_get_entry (data->menu, data->first + oldoffset),
|
||||
data->term);
|
||||
print_entry (GRUB_TERM_FIRST_ENTRY_Y + data->offset, 1,
|
||||
grub_menu_get_entry (data->menu, data->first + data->offset),
|
||||
data->term);
|
||||
}
|
||||
grub_term_refresh (data->term);
|
||||
}
|
||||
|
||||
static void
|
||||
menu_text_fini (void *dataptr)
|
||||
{
|
||||
struct menu_viewer_data *data = dataptr;
|
||||
|
||||
grub_term_setcursor (data->term, 1);
|
||||
grub_term_cls (data->term);
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
menu_text_clear_timeout (void *dataptr)
|
||||
{
|
||||
struct menu_viewer_data *data = dataptr;
|
||||
|
||||
grub_term_gotoxy (data->term, 0, grub_term_height (data->term) - 3);
|
||||
grub_print_spaces (data->term, grub_term_width (data->term) - 1);
|
||||
grub_term_gotoxy (data->term, grub_term_cursor_x (data->term),
|
||||
GRUB_TERM_FIRST_ENTRY_Y + data->offset);
|
||||
grub_term_refresh (data->term);
|
||||
}
|
||||
|
||||
grub_err_t
|
||||
grub_menu_try_text (struct grub_term_output *term,
|
||||
int entry, grub_menu_t menu, int nested)
|
||||
{
|
||||
struct menu_viewer_data *data;
|
||||
struct grub_menu_viewer *instance;
|
||||
|
||||
instance = grub_zalloc (sizeof (*instance));
|
||||
if (!instance)
|
||||
return grub_errno;
|
||||
|
||||
data = grub_zalloc (sizeof (*data));
|
||||
if (!data)
|
||||
{
|
||||
grub_free (instance);
|
||||
return grub_errno;
|
||||
}
|
||||
|
||||
data->term = term;
|
||||
instance->data = data;
|
||||
instance->set_chosen_entry = menu_text_set_chosen_entry;
|
||||
instance->print_timeout = menu_text_print_timeout;
|
||||
instance->clear_timeout = menu_text_clear_timeout;
|
||||
instance->fini = menu_text_fini;
|
||||
|
||||
data->menu = menu;
|
||||
|
||||
data->offset = entry;
|
||||
data->first = 0;
|
||||
if (data->offset > grub_term_num_entries (data->term) - 1)
|
||||
{
|
||||
data->first = data->offset - (grub_term_num_entries (data->term) - 1);
|
||||
data->offset = grub_term_num_entries (data->term) - 1;
|
||||
}
|
||||
|
||||
grub_term_setcursor (data->term, 0);
|
||||
grub_menu_init_page (nested, 0, data->term);
|
||||
print_entries (menu, data->first, data->offset, data->term);
|
||||
grub_term_refresh (data->term);
|
||||
grub_menu_register_viewer (instance);
|
||||
|
||||
return GRUB_ERR_NONE;
|
||||
}
|
129
grub-core/normal/misc.c
Normal file
129
grub-core/normal/misc.c
Normal file
|
@ -0,0 +1,129 @@
|
|||
/* misc.c - miscellaneous functions */
|
||||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2005,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/disk.h>
|
||||
#include <grub/fs.h>
|
||||
#include <grub/err.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/datetime.h>
|
||||
#include <grub/term.h>
|
||||
#include <grub/i18n.h>
|
||||
#include <grub/partition.h>
|
||||
|
||||
/* Print the information on the device NAME. */
|
||||
grub_err_t
|
||||
grub_normal_print_device_info (const char *name)
|
||||
{
|
||||
grub_device_t dev;
|
||||
char *p;
|
||||
|
||||
p = grub_strchr (name, ',');
|
||||
if (p)
|
||||
{
|
||||
grub_xputs ("\t");
|
||||
grub_printf_ (N_("Partition %s:"), name);
|
||||
grub_xputs (" ");
|
||||
}
|
||||
else
|
||||
{
|
||||
grub_printf_ (N_("Device %s:"), name);
|
||||
grub_xputs (" ");
|
||||
}
|
||||
|
||||
dev = grub_device_open (name);
|
||||
if (! dev)
|
||||
grub_printf ("%s", _("Filesystem cannot be accessed"));
|
||||
else if (dev->disk)
|
||||
{
|
||||
grub_fs_t fs;
|
||||
|
||||
fs = grub_fs_probe (dev);
|
||||
/* Ignore all errors. */
|
||||
grub_errno = 0;
|
||||
|
||||
if (fs)
|
||||
{
|
||||
grub_printf_ (N_("Filesystem type %s"), fs->name);
|
||||
if (fs->label)
|
||||
{
|
||||
char *label;
|
||||
(fs->label) (dev, &label);
|
||||
if (grub_errno == GRUB_ERR_NONE)
|
||||
{
|
||||
if (label && grub_strlen (label))
|
||||
{
|
||||
grub_xputs (" ");
|
||||
grub_printf_ (N_("- Label \"%s\""), label);
|
||||
}
|
||||
grub_free (label);
|
||||
}
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
if (fs->mtime)
|
||||
{
|
||||
grub_int32_t tm;
|
||||
struct grub_datetime datetime;
|
||||
(fs->mtime) (dev, &tm);
|
||||
if (grub_errno == GRUB_ERR_NONE)
|
||||
{
|
||||
grub_unixtime2datetime (tm, &datetime);
|
||||
grub_xputs (" ");
|
||||
grub_printf_ (N_("- Last modification time %d-%02d-%02d "
|
||||
"%02d:%02d:%02d %s"),
|
||||
datetime.year, datetime.month, datetime.day,
|
||||
datetime.hour, datetime.minute, datetime.second,
|
||||
grub_get_weekday_name (&datetime));
|
||||
|
||||
}
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
if (fs->uuid)
|
||||
{
|
||||
char *uuid;
|
||||
(fs->uuid) (dev, &uuid);
|
||||
if (grub_errno == GRUB_ERR_NONE)
|
||||
{
|
||||
if (uuid && grub_strlen (uuid))
|
||||
grub_printf (", UUID %s", uuid);
|
||||
grub_free (uuid);
|
||||
}
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
}
|
||||
else
|
||||
grub_printf ("%s", _("Not a known filesystem"));
|
||||
|
||||
if (dev->disk->partition)
|
||||
grub_printf (_(" - Partition start at %u"),
|
||||
grub_partition_get_start (dev->disk->partition));
|
||||
if (grub_disk_get_size (dev->disk) == GRUB_DISK_SIZE_UNKNOWN)
|
||||
grub_printf (_(" - Total size unknown"),
|
||||
grub_disk_get_size (dev->disk));
|
||||
else
|
||||
grub_printf (_(" - Total size %u sectors"),
|
||||
grub_disk_get_size (dev->disk));
|
||||
|
||||
grub_device_close (dev);
|
||||
}
|
||||
|
||||
grub_xputs ("\n");
|
||||
return grub_errno;
|
||||
}
|
860
grub-core/normal/term.c
Normal file
860
grub-core/normal/term.c
Normal file
|
@ -0,0 +1,860 @@
|
|||
/*
|
||||
* GRUB -- GRand Unified Bootloader
|
||||
* Copyright (C) 2002,2003,2005,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/term.h>
|
||||
#include <grub/misc.h>
|
||||
#include <grub/mm.h>
|
||||
#include <grub/file.h>
|
||||
#include <grub/dl.h>
|
||||
#include <grub/env.h>
|
||||
#include <grub/normal.h>
|
||||
#include <grub/charset.h>
|
||||
|
||||
struct term_state
|
||||
{
|
||||
struct term_state *next;
|
||||
const struct grub_unicode_glyph *backlog_glyphs;
|
||||
const grub_uint32_t *backlog_ucs4;
|
||||
grub_size_t backlog_len;
|
||||
|
||||
void *free;
|
||||
int num_lines;
|
||||
char *term_name;
|
||||
};
|
||||
|
||||
static struct term_state *term_states = NULL;
|
||||
|
||||
/* If the more pager is active. */
|
||||
static int grub_more;
|
||||
|
||||
static int grub_normal_char_counter = 0;
|
||||
|
||||
static void
|
||||
putcode_real (grub_uint32_t code, struct grub_term_output *term);
|
||||
|
||||
int
|
||||
grub_normal_get_char_counter (void)
|
||||
{
|
||||
return grub_normal_char_counter;
|
||||
}
|
||||
|
||||
void
|
||||
grub_normal_reset_more (void)
|
||||
{
|
||||
static struct term_state *state;
|
||||
for (state = term_states; state; state = state->next)
|
||||
state->num_lines = 0;
|
||||
}
|
||||
|
||||
static void
|
||||
print_more (void)
|
||||
{
|
||||
char key;
|
||||
grub_uint16_t *pos;
|
||||
grub_term_output_t term;
|
||||
grub_uint32_t *unicode_str, *unicode_last_position;
|
||||
|
||||
pos = grub_term_save_pos ();
|
||||
|
||||
grub_utf8_to_ucs4_alloc ("--MORE--", &unicode_str,
|
||||
&unicode_last_position);
|
||||
|
||||
if (!unicode_str)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term);
|
||||
}
|
||||
grub_setcolorstate (GRUB_TERM_COLOR_NORMAL);
|
||||
|
||||
grub_free (unicode_str);
|
||||
|
||||
key = grub_getkey ();
|
||||
|
||||
/* Remove the message. */
|
||||
grub_term_restore_pos (pos);
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
grub_print_spaces (term, 8);
|
||||
grub_term_restore_pos (pos);
|
||||
grub_free (pos);
|
||||
|
||||
/* Scroll one lines or an entire page, depending on the key. */
|
||||
|
||||
if (key == '\r' || key =='\n')
|
||||
grub_normal_reset_more ();
|
||||
else
|
||||
{
|
||||
static struct term_state *state;
|
||||
for (state = term_states; state; state = state->next)
|
||||
state->num_lines -= 2;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
grub_set_more (int onoff)
|
||||
{
|
||||
if (onoff == 1)
|
||||
grub_more++;
|
||||
else
|
||||
grub_more--;
|
||||
grub_normal_reset_more ();
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
GRUB_CP437_UPARROW = 0x18,
|
||||
GRUB_CP437_DOWNARROW = 0x19,
|
||||
GRUB_CP437_RIGHTARROW = 0x1a,
|
||||
GRUB_CP437_LEFTARROW = 0x1b,
|
||||
GRUB_CP437_VLINE = 0xb3,
|
||||
GRUB_CP437_CORNER_UR = 0xbf,
|
||||
GRUB_CP437_CORNER_LL = 0xc0,
|
||||
GRUB_CP437_HLINE = 0xc4,
|
||||
GRUB_CP437_CORNER_LR = 0xd9,
|
||||
GRUB_CP437_CORNER_UL = 0xda,
|
||||
};
|
||||
|
||||
static grub_uint32_t
|
||||
map_code (grub_uint32_t in, struct grub_term_output *term)
|
||||
{
|
||||
if (in <= 0x7f)
|
||||
return in;
|
||||
|
||||
switch (term->flags & GRUB_TERM_CODE_TYPE_MASK)
|
||||
{
|
||||
case GRUB_TERM_CODE_TYPE_CP437:
|
||||
switch (in)
|
||||
{
|
||||
case GRUB_UNICODE_LEFTARROW:
|
||||
return GRUB_CP437_LEFTARROW;
|
||||
case GRUB_UNICODE_UPARROW:
|
||||
return GRUB_CP437_UPARROW;
|
||||
case GRUB_UNICODE_RIGHTARROW:
|
||||
return GRUB_CP437_RIGHTARROW;
|
||||
case GRUB_UNICODE_DOWNARROW:
|
||||
return GRUB_CP437_DOWNARROW;
|
||||
case GRUB_UNICODE_HLINE:
|
||||
return GRUB_CP437_HLINE;
|
||||
case GRUB_UNICODE_VLINE:
|
||||
return GRUB_CP437_VLINE;
|
||||
case GRUB_UNICODE_CORNER_UL:
|
||||
return GRUB_CP437_CORNER_UL;
|
||||
case GRUB_UNICODE_CORNER_UR:
|
||||
return GRUB_CP437_CORNER_UR;
|
||||
case GRUB_UNICODE_CORNER_LL:
|
||||
return GRUB_CP437_CORNER_LL;
|
||||
case GRUB_UNICODE_CORNER_LR:
|
||||
return GRUB_CP437_CORNER_LR;
|
||||
}
|
||||
return '?';
|
||||
case GRUB_TERM_CODE_TYPE_ASCII:
|
||||
/* Better than nothing. */
|
||||
switch (in)
|
||||
{
|
||||
case GRUB_UNICODE_LEFTARROW:
|
||||
return '<';
|
||||
|
||||
case GRUB_UNICODE_UPARROW:
|
||||
return '^';
|
||||
|
||||
case GRUB_UNICODE_RIGHTARROW:
|
||||
return '>';
|
||||
|
||||
case GRUB_UNICODE_DOWNARROW:
|
||||
return 'v';
|
||||
|
||||
case GRUB_UNICODE_HLINE:
|
||||
return '-';
|
||||
|
||||
case GRUB_UNICODE_VLINE:
|
||||
return '|';
|
||||
|
||||
case GRUB_UNICODE_CORNER_UL:
|
||||
case GRUB_UNICODE_CORNER_UR:
|
||||
case GRUB_UNICODE_CORNER_LL:
|
||||
case GRUB_UNICODE_CORNER_LR:
|
||||
return '+';
|
||||
|
||||
}
|
||||
return '?';
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
void
|
||||
grub_puts_terminal (const char *str, struct grub_term_output *term)
|
||||
{
|
||||
grub_uint32_t *unicode_str, *unicode_last_position;
|
||||
grub_error_push ();
|
||||
grub_utf8_to_ucs4_alloc (str, &unicode_str,
|
||||
&unicode_last_position);
|
||||
grub_error_pop ();
|
||||
if (!unicode_str)
|
||||
{
|
||||
for (; *str; str++)
|
||||
{
|
||||
struct grub_unicode_glyph c =
|
||||
{
|
||||
.variant = 0,
|
||||
.attributes = 0,
|
||||
.ncomb = 0,
|
||||
.combining = 0,
|
||||
.estimated_width = 1,
|
||||
.base = *str
|
||||
};
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
(term->putchar) (term, &c);
|
||||
}
|
||||
if (*str == '\n')
|
||||
{
|
||||
c.base = '\r';
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
(term->putchar) (term, &c);
|
||||
}
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
grub_print_ucs4 (unicode_str, unicode_last_position, 0, 0, term);
|
||||
grub_free (unicode_str);
|
||||
}
|
||||
|
||||
grub_uint16_t *
|
||||
grub_term_save_pos (void)
|
||||
{
|
||||
struct grub_term_output *cur;
|
||||
unsigned cnt = 0;
|
||||
grub_uint16_t *ret, *ptr;
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(cur)
|
||||
cnt++;
|
||||
|
||||
ret = grub_malloc (cnt * sizeof (ret[0]));
|
||||
if (!ret)
|
||||
return NULL;
|
||||
|
||||
ptr = ret;
|
||||
FOR_ACTIVE_TERM_OUTPUTS(cur)
|
||||
*ptr++ = grub_term_getxy (cur);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
grub_term_restore_pos (grub_uint16_t *pos)
|
||||
{
|
||||
struct grub_term_output *cur;
|
||||
grub_uint16_t *ptr = pos;
|
||||
|
||||
if (!pos)
|
||||
return;
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(cur)
|
||||
{
|
||||
grub_term_gotoxy (cur, (*ptr & 0xff00) >> 8, *ptr & 0xff);
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
grub_terminal_autoload_free (void)
|
||||
{
|
||||
struct grub_term_autoload *cur, *next;
|
||||
unsigned i;
|
||||
for (i = 0; i < 2; i++)
|
||||
for (cur = i ? grub_term_input_autoload : grub_term_output_autoload;
|
||||
cur; cur = next)
|
||||
{
|
||||
next = cur->next;
|
||||
grub_free (cur->name);
|
||||
grub_free (cur->modname);
|
||||
grub_free (cur);
|
||||
}
|
||||
grub_term_input_autoload = NULL;
|
||||
grub_term_output_autoload = NULL;
|
||||
}
|
||||
|
||||
/* Read the file terminal.lst for auto-loading. */
|
||||
void
|
||||
read_terminal_list (const char *prefix)
|
||||
{
|
||||
char *filename;
|
||||
grub_file_t file;
|
||||
char *buf = NULL;
|
||||
|
||||
if (!prefix)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
filename = grub_xasprintf ("%s/terminal.lst", prefix);
|
||||
if (!filename)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
file = grub_file_open (filename);
|
||||
grub_free (filename);
|
||||
if (!file)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Override previous terminal.lst. */
|
||||
grub_terminal_autoload_free ();
|
||||
|
||||
for (;; grub_free (buf))
|
||||
{
|
||||
char *p, *name;
|
||||
struct grub_term_autoload *cur;
|
||||
struct grub_term_autoload **target = NULL;
|
||||
|
||||
buf = grub_file_getline (file);
|
||||
|
||||
if (! buf)
|
||||
break;
|
||||
|
||||
switch (buf[0])
|
||||
{
|
||||
case 'i':
|
||||
target = &grub_term_input_autoload;
|
||||
break;
|
||||
|
||||
case 'o':
|
||||
target = &grub_term_output_autoload;
|
||||
break;
|
||||
}
|
||||
if (!target)
|
||||
continue;
|
||||
|
||||
name = buf + 1;
|
||||
|
||||
p = grub_strchr (name, ':');
|
||||
if (! p)
|
||||
continue;
|
||||
|
||||
*p = '\0';
|
||||
while (*++p == ' ')
|
||||
;
|
||||
|
||||
cur = grub_malloc (sizeof (*cur));
|
||||
if (!cur)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
continue;
|
||||
}
|
||||
|
||||
cur->name = grub_strdup (name);
|
||||
if (! name)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
grub_free (cur);
|
||||
continue;
|
||||
}
|
||||
|
||||
cur->modname = grub_strdup (p);
|
||||
if (! cur->modname)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
grub_free (cur);
|
||||
grub_free (cur->name);
|
||||
continue;
|
||||
}
|
||||
cur->next = *target;
|
||||
*target = cur;
|
||||
}
|
||||
|
||||
grub_file_close (file);
|
||||
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
putglyph (const struct grub_unicode_glyph *c, struct grub_term_output *term)
|
||||
{
|
||||
struct grub_unicode_glyph c2 =
|
||||
{
|
||||
.variant = 0,
|
||||
.attributes = 0,
|
||||
.ncomb = 0,
|
||||
.combining = 0,
|
||||
.estimated_width = 1
|
||||
};
|
||||
|
||||
grub_normal_char_counter++;
|
||||
|
||||
if (c->base == '\t' && term->getxy)
|
||||
{
|
||||
int n;
|
||||
|
||||
n = 8 - ((term->getxy (term) >> 8) & 7);
|
||||
c2.base = ' ';
|
||||
while (n--)
|
||||
(term->putchar) (term, &c2);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
|
||||
== GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
|
||||
|| (term->flags & GRUB_TERM_CODE_TYPE_MASK)
|
||||
== GRUB_TERM_CODE_TYPE_UTF8_VISUAL)
|
||||
{
|
||||
int i;
|
||||
c2.estimated_width = grub_term_getcharwidth (term, c);
|
||||
for (i = -1; i < (int) c->ncomb; i++)
|
||||
{
|
||||
grub_uint8_t u8[20], *ptr;
|
||||
grub_uint32_t code;
|
||||
|
||||
if (i == -1)
|
||||
{
|
||||
code = c->base;
|
||||
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
|
||||
== GRUB_TERM_CODE_TYPE_UTF8_VISUAL)
|
||||
{
|
||||
if ((c->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR))
|
||||
code = grub_unicode_mirror_code (code);
|
||||
code = grub_unicode_shape_code (code, c->attributes);
|
||||
}
|
||||
}
|
||||
else
|
||||
code = c->combining[i].code;
|
||||
|
||||
grub_ucs4_to_utf8 (&code, 1, u8, sizeof (u8));
|
||||
|
||||
for (ptr = u8; *ptr; ptr++)
|
||||
{
|
||||
c2.base = *ptr;
|
||||
(term->putchar) (term, &c2);
|
||||
c2.estimated_width = 0;
|
||||
}
|
||||
}
|
||||
c2.estimated_width = 1;
|
||||
}
|
||||
else
|
||||
(term->putchar) (term, c);
|
||||
|
||||
if (c->base == '\n')
|
||||
{
|
||||
c2.base = '\r';
|
||||
(term->putchar) (term, &c2);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
putcode_real (grub_uint32_t code, struct grub_term_output *term)
|
||||
{
|
||||
struct grub_unicode_glyph c =
|
||||
{
|
||||
.variant = 0,
|
||||
.attributes = 0,
|
||||
.ncomb = 0,
|
||||
.combining = 0,
|
||||
.estimated_width = 1
|
||||
};
|
||||
|
||||
c.base = map_code (code, term);
|
||||
putglyph (&c, term);
|
||||
}
|
||||
|
||||
/* Put a Unicode character. */
|
||||
void
|
||||
grub_putcode (grub_uint32_t code, struct grub_term_output *term)
|
||||
{
|
||||
/* Combining character by itself? */
|
||||
if (grub_unicode_get_comb_type (code) != GRUB_UNICODE_COMB_NONE)
|
||||
return;
|
||||
|
||||
putcode_real (code, term);
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
get_maxwidth (struct grub_term_output *term,
|
||||
int margin_left, int margin_right)
|
||||
{
|
||||
struct grub_unicode_glyph space_glyph = {
|
||||
.base = ' ',
|
||||
.variant = 0,
|
||||
.attributes = 0,
|
||||
.ncomb = 0,
|
||||
.combining = 0
|
||||
};
|
||||
return (grub_term_width (term)
|
||||
- grub_term_getcharwidth (term, &space_glyph)
|
||||
* (margin_left + margin_right) - 1);
|
||||
}
|
||||
|
||||
static grub_ssize_t
|
||||
get_startwidth (struct grub_term_output *term,
|
||||
int margin_left)
|
||||
{
|
||||
return ((term->getxy (term) >> 8) & 0xff) - margin_left;
|
||||
}
|
||||
|
||||
static int
|
||||
print_ucs4_terminal (const grub_uint32_t * str,
|
||||
const grub_uint32_t * last_position,
|
||||
int margin_left, int margin_right,
|
||||
struct grub_term_output *term,
|
||||
struct term_state *state)
|
||||
{
|
||||
const grub_uint32_t *ptr;
|
||||
grub_ssize_t startwidth = get_startwidth (term, margin_left);
|
||||
grub_ssize_t line_width = startwidth;
|
||||
grub_ssize_t lastspacewidth = 0;
|
||||
grub_ssize_t max_width = get_maxwidth (term, margin_left, margin_right);
|
||||
const grub_uint32_t *line_start = str, *last_space = str - 1;
|
||||
|
||||
for (ptr = str; ptr < last_position; ptr++)
|
||||
{
|
||||
grub_ssize_t last_width = 0;
|
||||
if (grub_unicode_get_comb_type (*ptr) == GRUB_UNICODE_COMB_NONE)
|
||||
{
|
||||
struct grub_unicode_glyph c = {
|
||||
.variant = 0,
|
||||
.attributes = 0,
|
||||
.ncomb = 0,
|
||||
.combining = 0
|
||||
};
|
||||
c.base = *ptr;
|
||||
line_width += last_width = grub_term_getcharwidth (term, &c);
|
||||
}
|
||||
|
||||
if (*ptr == ' ')
|
||||
{
|
||||
lastspacewidth = line_width;
|
||||
last_space = ptr;
|
||||
}
|
||||
|
||||
if (line_width > max_width || *ptr == '\n')
|
||||
{
|
||||
const grub_uint32_t *ptr2;
|
||||
|
||||
if (line_width > max_width && last_space > line_start)
|
||||
ptr = last_space;
|
||||
else if (line_width > max_width
|
||||
&& line_start == str && startwidth != 0)
|
||||
{
|
||||
ptr = str;
|
||||
lastspacewidth = startwidth;
|
||||
}
|
||||
else
|
||||
lastspacewidth = line_width - last_width;
|
||||
|
||||
for (ptr2 = line_start; ptr2 < ptr; ptr2++)
|
||||
{
|
||||
/* Skip combining characters on non-UTF8 terminals. */
|
||||
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
|
||||
!= GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
|
||||
&& grub_unicode_get_comb_type (*ptr2)
|
||||
!= GRUB_UNICODE_COMB_NONE)
|
||||
continue;
|
||||
putcode_real (*ptr2, term);
|
||||
}
|
||||
|
||||
grub_print_spaces (term, margin_right);
|
||||
grub_putcode ('\n', term);
|
||||
if (state && ++state->num_lines
|
||||
>= (grub_ssize_t) grub_term_height (term) - 2)
|
||||
{
|
||||
state->backlog_ucs4 = (ptr == last_space || *ptr == '\n')
|
||||
? ptr + 1 : ptr;
|
||||
state->backlog_len = last_position - state->backlog_ucs4;
|
||||
return 1;
|
||||
}
|
||||
|
||||
line_width -= lastspacewidth;
|
||||
grub_print_spaces (term, margin_left);
|
||||
if (ptr == last_space || *ptr == '\n')
|
||||
ptr++;
|
||||
line_start = ptr;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const grub_uint32_t *ptr2;
|
||||
for (ptr2 = line_start; ptr2 < last_position; ptr2++)
|
||||
{
|
||||
/* Skip combining characters on non-UTF8 terminals. */
|
||||
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
|
||||
!= GRUB_TERM_CODE_TYPE_UTF8_LOGICAL
|
||||
&& grub_unicode_get_comb_type (*ptr2)
|
||||
!= GRUB_UNICODE_COMB_NONE)
|
||||
continue;
|
||||
putcode_real (*ptr2, term);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct term_state *
|
||||
find_term_state (struct grub_term_output *term)
|
||||
{
|
||||
struct term_state *state;
|
||||
for (state = term_states; state; state = state->next)
|
||||
if (grub_strcmp (state->term_name, term->name) == 0)
|
||||
return state;
|
||||
|
||||
state = grub_zalloc (sizeof (*state));
|
||||
if (!state)
|
||||
{
|
||||
grub_errno = GRUB_ERR_NONE;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
state->term_name = grub_strdup (term->name);
|
||||
state->next = term_states;
|
||||
term_states = state;
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
static int
|
||||
put_glyphs_terminal (const struct grub_unicode_glyph *visual,
|
||||
grub_ssize_t visual_len,
|
||||
int margin_left, int margin_right,
|
||||
struct grub_term_output *term,
|
||||
struct term_state *state)
|
||||
{
|
||||
const struct grub_unicode_glyph *visual_ptr;
|
||||
for (visual_ptr = visual; visual_ptr < visual + visual_len; visual_ptr++)
|
||||
{
|
||||
if (visual_ptr->base == '\n')
|
||||
grub_print_spaces (term, margin_right);
|
||||
putglyph (visual_ptr, term);
|
||||
if (visual_ptr->base == '\n')
|
||||
{
|
||||
if (state && ++state->num_lines
|
||||
>= (grub_ssize_t) grub_term_height (term) - 2)
|
||||
{
|
||||
state->backlog_glyphs = visual_ptr + 1;
|
||||
state->backlog_len = visual_len - (visual_ptr - visual) - 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
grub_print_spaces (term, margin_left);
|
||||
}
|
||||
grub_free (visual_ptr->combining);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
print_backlog (struct grub_term_output *term,
|
||||
int margin_left, int margin_right)
|
||||
{
|
||||
struct term_state *state = find_term_state (term);
|
||||
|
||||
if (!state)
|
||||
return 0;
|
||||
|
||||
if (state->backlog_ucs4)
|
||||
{
|
||||
int ret;
|
||||
ret = print_ucs4_terminal (state->backlog_ucs4,
|
||||
state->backlog_ucs4 + state->backlog_len,
|
||||
margin_left, margin_right, term, state);
|
||||
if (!ret)
|
||||
{
|
||||
grub_free (state->free);
|
||||
state->free = NULL;
|
||||
state->backlog_len = 0;
|
||||
state->backlog_ucs4 = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (state->backlog_glyphs)
|
||||
{
|
||||
int ret;
|
||||
ret = put_glyphs_terminal (state->backlog_glyphs,
|
||||
state->backlog_len,
|
||||
margin_left, margin_right, term, state);
|
||||
if (!ret)
|
||||
{
|
||||
grub_free (state->free);
|
||||
state->free = NULL;
|
||||
state->backlog_len = 0;
|
||||
state->backlog_glyphs = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
print_ucs4_real (const grub_uint32_t * str,
|
||||
const grub_uint32_t * last_position,
|
||||
int margin_left, int margin_right,
|
||||
struct grub_term_output *term, int backlog)
|
||||
{
|
||||
struct term_state *state = NULL;
|
||||
|
||||
if (backlog)
|
||||
state = find_term_state (term);
|
||||
|
||||
if (((term->getxy (term) >> 8) & 0xff) < margin_left)
|
||||
grub_print_spaces (term, margin_left - ((term->getxy (term) >> 8) & 0xff));
|
||||
|
||||
if ((term->flags & GRUB_TERM_CODE_TYPE_MASK)
|
||||
== GRUB_TERM_CODE_TYPE_VISUAL_GLYPHS
|
||||
|| (term->flags & GRUB_TERM_CODE_TYPE_MASK)
|
||||
== GRUB_TERM_CODE_TYPE_UTF8_VISUAL)
|
||||
{
|
||||
grub_ssize_t visual_len;
|
||||
struct grub_unicode_glyph *visual;
|
||||
int ret;
|
||||
|
||||
auto grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c);
|
||||
grub_ssize_t getcharwidth (const struct grub_unicode_glyph *c)
|
||||
{
|
||||
return grub_term_getcharwidth (term, c);
|
||||
}
|
||||
|
||||
visual_len = grub_bidi_logical_to_visual (str, last_position - str,
|
||||
&visual, getcharwidth,
|
||||
get_maxwidth (term,
|
||||
margin_left,
|
||||
margin_right),
|
||||
get_startwidth (term,
|
||||
margin_left));
|
||||
if (visual_len < 0)
|
||||
{
|
||||
grub_print_error ();
|
||||
return 0;
|
||||
}
|
||||
ret = put_glyphs_terminal (visual, visual_len, margin_left, margin_right,
|
||||
term, state);
|
||||
if (!ret)
|
||||
grub_free (visual);
|
||||
else
|
||||
state->free = visual;
|
||||
return ret;
|
||||
}
|
||||
return print_ucs4_terminal (str, last_position, margin_left, margin_right,
|
||||
term, state);
|
||||
}
|
||||
|
||||
void
|
||||
grub_print_ucs4 (const grub_uint32_t * str,
|
||||
const grub_uint32_t * last_position,
|
||||
int margin_left, int margin_right,
|
||||
struct grub_term_output *term)
|
||||
{
|
||||
print_ucs4_real (str, last_position, margin_left, margin_right,
|
||||
term, 0);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
grub_xputs_normal (const char *str)
|
||||
{
|
||||
grub_uint32_t *unicode_str = NULL, *unicode_last_position;
|
||||
int backlog = 0;
|
||||
grub_term_output_t term;
|
||||
|
||||
grub_error_push ();
|
||||
grub_utf8_to_ucs4_alloc (str, &unicode_str,
|
||||
&unicode_last_position);
|
||||
grub_error_pop ();
|
||||
|
||||
if (!unicode_str)
|
||||
{
|
||||
for (; *str; str++)
|
||||
{
|
||||
struct grub_unicode_glyph c =
|
||||
{
|
||||
.variant = 0,
|
||||
.attributes = 0,
|
||||
.ncomb = 0,
|
||||
.combining = 0,
|
||||
.estimated_width = 1,
|
||||
.base = *str
|
||||
};
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
(term->putchar) (term, &c);
|
||||
}
|
||||
if (*str == '\n')
|
||||
{
|
||||
c.base = '\r';
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
(term->putchar) (term, &c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
int cur;
|
||||
cur = print_ucs4_real (unicode_str, unicode_last_position, 0, 0,
|
||||
term, grub_more);
|
||||
if (cur)
|
||||
backlog = 1;
|
||||
}
|
||||
while (backlog)
|
||||
{
|
||||
print_more ();
|
||||
backlog = 0;
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
int cur;
|
||||
cur = print_backlog (term, 0, 0);
|
||||
if (cur)
|
||||
backlog = 1;
|
||||
}
|
||||
}
|
||||
grub_free (unicode_str);
|
||||
}
|
||||
|
||||
void
|
||||
grub_cls (void)
|
||||
{
|
||||
struct grub_term_output *term;
|
||||
|
||||
FOR_ACTIVE_TERM_OUTPUTS(term)
|
||||
{
|
||||
if ((term->flags & GRUB_TERM_DUMB) || (grub_env_get ("debug")))
|
||||
{
|
||||
grub_putcode ('\n', term);
|
||||
grub_term_refresh (term);
|
||||
}
|
||||
else
|
||||
(term->cls) (term);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue