1N/A/* char_io.c - basic console input and output */
1N/A/*
1N/A * GRUB -- GRand Unified Bootloader
1N/A * Copyright (C) 1999,2000,2001,2002,2004 Free Software Foundation, Inc.
1N/A *
1N/A * This program is free software; you can redistribute it and/or modify
1N/A * it under the terms of the GNU General Public License as published by
1N/A * the Free Software Foundation; either version 2 of the License, or
1N/A * (at your option) any later version.
1N/A *
1N/A * This program is distributed in the hope that it will be useful,
1N/A * but WITHOUT ANY WARRANTY; without even the implied warranty of
1N/A * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1N/A * GNU General Public License for more details.
1N/A *
1N/A * You should have received a copy of the GNU General Public License
1N/A * along with this program; if not, write to the Free Software
1N/A * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
1N/A */
1N/A/*
1N/A * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
1N/A * Use is subject to license terms.
1N/A */
1N/A
1N/A#include <shared.h>
1N/A#include <term.h>
1N/A
1N/A#ifdef SUPPORT_HERCULES
1N/A# include <hercules.h>
1N/A#endif
1N/A
1N/A#ifdef SUPPORT_SERIAL
1N/A# include <serial.h>
1N/A#endif
1N/A
1N/A#ifndef STAGE1_5
1N/Astruct term_entry term_table[] =
1N/A {
1N/A {
1N/A "console",
1N/A 0,
1N/A 24,
1N/A console_putchar,
1N/A console_checkkey,
1N/A console_getkey,
1N/A console_getxy,
1N/A console_gotoxy,
1N/A console_cls,
1N/A console_setcolorstate,
1N/A console_setcolor,
1N/A console_setcursor,
1N/A 0,
1N/A 0
1N/A },
1N/A#ifdef SUPPORT_SERIAL
1N/A {
1N/A "serial",
1N/A /* A serial device must be initialized. */
1N/A TERM_NEED_INIT,
1N/A 24,
1N/A serial_putchar,
1N/A serial_checkkey,
1N/A serial_getkey,
1N/A serial_getxy,
1N/A serial_gotoxy,
1N/A serial_cls,
1N/A serial_setcolorstate,
1N/A 0,
1N/A 0,
1N/A 0,
1N/A 0
1N/A },
1N/A#endif /* SUPPORT_SERIAL */
1N/A#ifdef SUPPORT_HERCULES
1N/A {
1N/A "hercules",
1N/A 0,
1N/A 24,
1N/A hercules_putchar,
1N/A console_checkkey,
1N/A console_getkey,
1N/A hercules_getxy,
1N/A hercules_gotoxy,
1N/A hercules_cls,
1N/A hercules_setcolorstate,
1N/A hercules_setcolor,
1N/A hercules_setcursor,
1N/A 0,
1N/A 0
1N/A },
1N/A#endif /* SUPPORT_HERCULES */
1N/A#ifdef SUPPORT_GRAPHICS
1N/A { "graphics",
1N/A TERM_NEED_INIT, /* flags */
1N/A 30, /* number of lines */
1N/A graphics_putchar, /* putchar */
1N/A console_checkkey, /* checkkey */
1N/A console_getkey, /* getkey */
1N/A graphics_getxy, /* getxy */
1N/A graphics_gotoxy, /* gotoxy */
1N/A graphics_cls, /* cls */
1N/A graphics_setcolorstate, /* setcolorstate */
1N/A graphics_setcolor, /* setcolor */
1N/A graphics_setcursor, /* nocursor */
1N/A graphics_init, /* initialize */
1N/A graphics_end /* shutdown */
1N/A },
1N/A#endif /* SUPPORT_GRAPHICS */
1N/A /* This must be the last entry. */
1N/A { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
1N/A };
1N/A
1N/A/* This must be console. */
1N/Astruct term_entry *current_term = term_table;
1N/A
1N/Aint max_lines = 24;
1N/Aint count_lines = -1;
1N/Aint use_pager = 1;
1N/A#endif
1N/A
1N/Avoid
1N/Aprint_error (void)
1N/A{
1N/A if (errnum > ERR_NONE && errnum < MAX_ERR_NUM)
1N/A#ifndef STAGE1_5
1N/A /* printf("\7\n %s\n", err_list[errnum]); */
1N/A printf ("\nError %u: %s\n", errnum, err_list[errnum]);
1N/A#else /* STAGE1_5 */
1N/A printf ("Error %u\n", errnum);
1N/A#endif /* STAGE1_5 */
1N/A}
1N/A
1N/Achar *
1N/Aconvert_to_ascii (char *buf, int c,...)
1N/A{
1N/A unsigned long num = *((&c) + 1), mult = 10;
1N/A char *ptr = buf;
1N/A
1N/A#ifndef STAGE1_5
1N/A if (c == 'x' || c == 'X')
1N/A mult = 16;
1N/A
1N/A if ((num & 0x80000000uL) && c == 'd')
1N/A {
1N/A num = (~num) + 1;
1N/A *(ptr++) = '-';
1N/A buf++;
1N/A }
1N/A#endif
1N/A
1N/A do
1N/A {
1N/A int dig = num % mult;
1N/A *(ptr++) = ((dig > 9) ? dig + 'a' - 10 : '0' + dig);
1N/A }
1N/A while (num /= mult);
1N/A
1N/A /* reorder to correct direction!! */
1N/A {
1N/A char *ptr1 = ptr - 1;
1N/A char *ptr2 = buf;
1N/A while (ptr1 > ptr2)
1N/A {
1N/A int tmp = *ptr1;
1N/A *ptr1 = *ptr2;
1N/A *ptr2 = tmp;
1N/A ptr1--;
1N/A ptr2++;
1N/A }
1N/A }
1N/A
1N/A return ptr;
1N/A}
1N/A
1N/Avoid
1N/Agrub_putstr (const char *str)
1N/A{
1N/A while (*str)
1N/A grub_putchar (*str++);
1N/A}
1N/A
1N/Astatic void
1N/Agrub_vprintf (const char *format, int *dataptr)
1N/A{
1N/A char c, str[16];
1N/A
1N/A while ((c = *(format++)) != 0)
1N/A {
1N/A if (c != '%')
1N/A grub_putchar (c);
1N/A else
1N/A switch (c = *(format++))
1N/A {
1N/A#ifndef STAGE1_5
1N/A case 'd':
1N/A case 'x':
1N/A case 'X':
1N/A#endif
1N/A case 'u':
1N/A *convert_to_ascii (str, c, *((unsigned long *) dataptr++)) = 0;
1N/A grub_putstr (str);
1N/A break;
1N/A
1N/A#ifndef STAGE1_5
1N/A case 'c':
1N/A grub_putchar ((*(dataptr++)) & 0xff);
1N/A break;
1N/A
1N/A case 's':
1N/A grub_putstr ((char *) *(dataptr++));
1N/A break;
1N/A#endif
1N/A }
1N/A }
1N/A}
1N/A
1N/A#ifndef STAGE1_5
1N/Avoid
1N/Ainit_page (void)
1N/A{
1N/A cls ();
1N/A
1N/A grub_printf ("\n GNU GRUB version %s (%dK lower / %dK upper memory)\n\n",
1N/A version_string, mbi.mem_lower, mbi.mem_upper);
1N/A}
1N/A
1N/A/* The number of the history entries. */
1N/Astatic int num_history = 0;
1N/A
1N/A/* Get the NOth history. If NO is less than zero or greater than or
1N/A equal to NUM_HISTORY, return NULL. Otherwise return a valid string. */
1N/Astatic char *
1N/Aget_history (int no)
1N/A{
1N/A if (no < 0 || no >= num_history)
1N/A return 0;
1N/A
1N/A return (char *) HISTORY_BUF + MAX_CMDLINE * no;
1N/A}
1N/A
1N/A/* Add CMDLINE to the history buffer. */
1N/Astatic void
1N/Aadd_history (const char *cmdline, int no)
1N/A{
1N/A grub_memmove ((char *) HISTORY_BUF + MAX_CMDLINE * (no + 1),
1N/A (char *) HISTORY_BUF + MAX_CMDLINE * no,
1N/A MAX_CMDLINE * (num_history - no));
1N/A grub_strcpy ((char *) HISTORY_BUF + MAX_CMDLINE * no, cmdline);
1N/A if (num_history < HISTORY_SIZE)
1N/A num_history++;
1N/A}
1N/A
1N/Astatic int
1N/Areal_get_cmdline (char *prompt, char *cmdline, int maxlen,
1N/A int echo_char, int readline)
1N/A{
1N/A /* This is a rather complicated function. So explain the concept.
1N/A
1N/A A command-line consists of ``section''s. A section is a part of the
1N/A line which may be displayed on the screen, but a section is never
1N/A displayed with another section simultaneously.
1N/A
1N/A Each section is basically 77 or less characters, but the exception
1N/A is the first section, which is 78 or less characters, because the
1N/A starting point is special. See below.
1N/A
1N/A The first section contains a prompt and a command-line (or the
1N/A first part of a command-line when it is too long to be fit in the
1N/A screen). So, in the first section, the number of command-line
1N/A characters displayed is 78 minus the length of the prompt (or
1N/A less). If the command-line has more characters, `>' is put at the
1N/A position 78 (zero-origin), to inform the user of the hidden
1N/A characters.
1N/A
1N/A Other sections always have `<' at the first position, since there
1N/A is absolutely a section before each section. If there is a section
1N/A after another section, this section consists of 77 characters and
1N/A `>' at the last position. The last section has 77 or less
1N/A characters and doesn't have `>'.
1N/A
1N/A Each section other than the last shares some characters with the
1N/A previous section. This region is called ``margin''. If the cursor
1N/A is put at the magin which is shared by the first section and the
1N/A second, the first section is displayed. Otherwise, a displayed
1N/A section is switched to another section, only if the cursor is put
1N/A outside that section. */
1N/A
1N/A /* XXX: These should be defined in shared.h, but I leave these here,
1N/A until this code is freezed. */
1N/A#define CMDLINE_WIDTH 78
1N/A#define CMDLINE_MARGIN 10
1N/A
1N/A int xpos, lpos, c, section;
1N/A /* The length of PROMPT. */
1N/A int plen;
1N/A /* The length of the command-line. */
1N/A int llen;
1N/A /* The index for the history. */
1N/A int history = -1;
1N/A /* The working buffer for the command-line. */
1N/A char *buf = (char *) CMDLINE_BUF;
1N/A /* The kill buffer. */
1N/A char *kill_buf = (char *) KILL_BUF;
1N/A
1N/A /* Nested function definitions for code simplicity. */
1N/A
1N/A /* The forward declarations of nested functions are prefixed
1N/A with `auto'. */
1N/A auto void cl_refresh (int full, int len);
1N/A auto void cl_backward (int count);
1N/A auto void cl_forward (int count);
1N/A auto void cl_insert (const char *str);
1N/A auto void cl_delete (int count);
1N/A auto void cl_init (void);
1N/A
1N/A /* Move the cursor backward. */
1N/A void cl_backward (int count)
1N/A {
1N/A lpos -= count;
1N/A
1N/A /* If the cursor is in the first section, display the first section
1N/A instead of the second. */
1N/A if (section == 1 && plen + lpos < CMDLINE_WIDTH)
1N/A cl_refresh (1, 0);
1N/A else if (xpos - count < 1)
1N/A cl_refresh (1, 0);
1N/A else
1N/A {
1N/A xpos -= count;
1N/A
1N/A if (current_term->flags & TERM_DUMB)
1N/A {
1N/A int i;
1N/A
1N/A for (i = 0; i < count; i++)
1N/A grub_putchar ('\b');
1N/A }
1N/A else
1N/A gotoxy (xpos, getxy () & 0xFF);
1N/A }
1N/A }
1N/A
1N/A /* Move the cursor forward. */
1N/A void cl_forward (int count)
1N/A {
1N/A lpos += count;
1N/A
1N/A /* If the cursor goes outside, scroll the screen to the right. */
1N/A if (xpos + count >= CMDLINE_WIDTH)
1N/A cl_refresh (1, 0);
1N/A else
1N/A {
1N/A xpos += count;
1N/A
1N/A if (current_term->flags & TERM_DUMB)
1N/A {
1N/A int i;
1N/A
1N/A for (i = lpos - count; i < lpos; i++)
1N/A {
1N/A if (! echo_char)
1N/A grub_putchar (buf[i]);
1N/A else
1N/A grub_putchar (echo_char);
1N/A }
1N/A }
1N/A else
1N/A gotoxy (xpos, getxy () & 0xFF);
1N/A }
1N/A }
1N/A
1N/A /* Refresh the screen. If FULL is true, redraw the full line, otherwise,
1N/A only LEN characters from LPOS. */
1N/A void cl_refresh (int full, int len)
1N/A {
1N/A int i;
1N/A int start;
1N/A int pos = xpos;
1N/A
1N/A if (full)
1N/A {
1N/A /* Recompute the section number. */
1N/A if (lpos + plen < CMDLINE_WIDTH)
1N/A section = 0;
1N/A else
1N/A section = ((lpos + plen - CMDLINE_WIDTH)
1N/A / (CMDLINE_WIDTH - 1 - CMDLINE_MARGIN) + 1);
1N/A
1N/A /* From the start to the end. */
1N/A len = CMDLINE_WIDTH;
1N/A pos = 0;
1N/A grub_putchar ('\r');
1N/A
1N/A /* If SECTION is the first section, print the prompt, otherwise,
1N/A print `<'. */
1N/A if (section == 0)
1N/A {
1N/A grub_printf ("%s", prompt);
1N/A len -= plen;
1N/A pos += plen;
1N/A }
1N/A else
1N/A {
1N/A grub_putchar ('<');
1N/A len--;
1N/A pos++;
1N/A }
1N/A }
1N/A
1N/A /* Compute the index to start writing BUF and the resulting position
1N/A on the screen. */
1N/A if (section == 0)
1N/A {
1N/A int offset = 0;
1N/A
1N/A if (! full)
1N/A offset = xpos - plen;
1N/A
1N/A start = 0;
1N/A xpos = lpos + plen;
1N/A start += offset;
1N/A }
1N/A else
1N/A {
1N/A int offset = 0;
1N/A
1N/A if (! full)
1N/A offset = xpos - 1;
1N/A
1N/A start = ((section - 1) * (CMDLINE_WIDTH - 1 - CMDLINE_MARGIN)
1N/A + CMDLINE_WIDTH - plen - CMDLINE_MARGIN);
1N/A xpos = lpos + 1 - start;
1N/A start += offset;
1N/A }
1N/A
1N/A /* Print BUF. If ECHO_CHAR is not zero, put it instead. */
1N/A for (i = start; i < start + len && i < llen; i++)
1N/A {
1N/A if (! echo_char)
1N/A grub_putchar (buf[i]);
1N/A else
1N/A grub_putchar (echo_char);
1N/A
1N/A pos++;
1N/A }
1N/A
1N/A /* Fill up the rest of the line with spaces. */
1N/A for (; i < start + len; i++)
1N/A {
1N/A grub_putchar (' ');
1N/A pos++;
1N/A }
1N/A
1N/A /* If the cursor is at the last position, put `>' or a space,
1N/A depending on if there are more characters in BUF. */
1N/A if (pos == CMDLINE_WIDTH)
1N/A {
1N/A if (start + len < llen)
1N/A grub_putchar ('>');
1N/A else
1N/A grub_putchar (' ');
1N/A
1N/A pos++;
1N/A }
1N/A
1N/A /* Back to XPOS. */
1N/A if (current_term->flags & TERM_DUMB)
1N/A {
1N/A for (i = 0; i < pos - xpos; i++)
1N/A grub_putchar ('\b');
1N/A }
1N/A else
1N/A gotoxy (xpos, getxy () & 0xFF);
1N/A }
1N/A
1N/A /* Initialize the command-line. */
1N/A void cl_init (void)
1N/A {
1N/A /* Distinguish us from other lines and error messages! */
1N/A grub_putchar ('\n');
1N/A
1N/A /* Print full line and set position here. */
1N/A cl_refresh (1, 0);
1N/A }
1N/A
1N/A /* Insert STR to BUF. */
1N/A void cl_insert (const char *str)
1N/A {
1N/A int l = grub_strlen (str);
1N/A
1N/A if (llen + l < maxlen)
1N/A {
1N/A if (lpos == llen)
1N/A grub_memmove (buf + lpos, str, l + 1);
1N/A else
1N/A {
1N/A grub_memmove (buf + lpos + l, buf + lpos, llen - lpos + 1);
1N/A grub_memmove (buf + lpos, str, l);
1N/A }
1N/A
1N/A llen += l;
1N/A lpos += l;
1N/A if (xpos + l >= CMDLINE_WIDTH)
1N/A cl_refresh (1, 0);
1N/A else if (xpos + l + llen - lpos > CMDLINE_WIDTH)
1N/A cl_refresh (0, CMDLINE_WIDTH - xpos);
1N/A else
1N/A cl_refresh (0, l + llen - lpos);
1N/A }
1N/A }
1N/A
1N/A /* Delete COUNT characters in BUF. */
1N/A void cl_delete (int count)
1N/A {
1N/A grub_memmove (buf + lpos, buf + lpos + count, llen - count + 1);
1N/A llen -= count;
1N/A
1N/A if (xpos + llen + count - lpos > CMDLINE_WIDTH)
1N/A cl_refresh (0, CMDLINE_WIDTH - xpos);
1N/A else
1N/A cl_refresh (0, llen + count - lpos);
1N/A }
1N/A
1N/A plen = grub_strlen (prompt);
1N/A llen = grub_strlen (cmdline);
1N/A
1N/A if (maxlen > MAX_CMDLINE)
1N/A {
1N/A maxlen = MAX_CMDLINE;
1N/A if (llen >= MAX_CMDLINE)
1N/A {
1N/A llen = MAX_CMDLINE - 1;
1N/A cmdline[MAX_CMDLINE] = 0;
1N/A }
1N/A }
1N/A lpos = llen;
1N/A grub_strcpy (buf, cmdline);
1N/A
1N/A cl_init ();
1N/A
1N/A while ((c = ASCII_CHAR (getkey ())) != '\n' && c != '\r')
1N/A {
1N/A /* If READLINE is non-zero, handle readline-like key bindings. */
1N/A if (readline)
1N/A {
1N/A switch (c)
1N/A {
1N/A case 9: /* TAB lists completions */
1N/A {
1N/A int i;
1N/A /* POS points to the first space after a command. */
1N/A int pos = 0;
1N/A int ret;
1N/A char *completion_buffer = (char *) COMPLETION_BUF;
1N/A int equal_pos = -1;
1N/A int is_filename;
1N/A
1N/A /* Find the first word. */
1N/A while (buf[pos] == ' ')
1N/A pos++;
1N/A while (buf[pos] && buf[pos] != '=' && buf[pos] != ' ')
1N/A pos++;
1N/A
1N/A is_filename = (lpos > pos);
1N/A
1N/A /* Find the position of the equal character after a
1N/A command, and replace it with a space. */
1N/A for (i = pos; buf[i] && buf[i] != ' '; i++)
1N/A if (buf[i] == '=')
1N/A {
1N/A equal_pos = i;
1N/A buf[i] = ' ';
1N/A break;
1N/A }
1N/A
1N/A /* Find the position of the first character in this
1N/A word. */
1N/A for (i = lpos; i > 0 && buf[i - 1] != ' '; i--)
1N/A ;
1N/A
1N/A /* Invalidate the cache, because the user may exchange
1N/A removable disks. */
1N/A buf_drive = -1;
1N/A
1N/A /* Copy this word to COMPLETION_BUFFER and do the
1N/A completion. */
1N/A grub_memmove (completion_buffer, buf + i, lpos - i);
1N/A completion_buffer[lpos - i] = 0;
1N/A ret = print_completions (is_filename, 1);
1N/A errnum = ERR_NONE;
1N/A
1N/A if (ret >= 0)
1N/A {
1N/A /* Found, so insert COMPLETION_BUFFER. */
1N/A cl_insert (completion_buffer + lpos - i);
1N/A
1N/A if (ret > 0)
1N/A {
1N/A /* There are more than one candidates, so print
1N/A the list. */
1N/A grub_putchar ('\n');
1N/A print_completions (is_filename, 0);
1N/A errnum = ERR_NONE;
1N/A }
1N/A }
1N/A
1N/A /* Restore the command-line. */
1N/A if (equal_pos >= 0)
1N/A buf[equal_pos] = '=';
1N/A
1N/A if (ret)
1N/A cl_init ();
1N/A }
1N/A break;
1N/A case 1: /* C-a go to beginning of line */
1N/A cl_backward (lpos);
1N/A break;
1N/A case 5: /* C-e go to end of line */
1N/A cl_forward (llen - lpos);
1N/A break;
1N/A case 6: /* C-f forward one character */
1N/A if (lpos < llen)
1N/A cl_forward (1);
1N/A break;
1N/A case 2: /* C-b backward one character */
1N/A if (lpos > 0)
1N/A cl_backward (1);
1N/A break;
1N/A case 21: /* C-u kill to beginning of line */
1N/A if (lpos == 0)
1N/A break;
1N/A /* Copy the string being deleted to KILL_BUF. */
1N/A grub_memmove (kill_buf, buf, lpos);
1N/A kill_buf[lpos] = 0;
1N/A {
1N/A /* XXX: Not very clever. */
1N/A
1N/A int count = lpos;
1N/A
1N/A cl_backward (lpos);
1N/A cl_delete (count);
1N/A }
1N/A break;
1N/A case 11: /* C-k kill to end of line */
1N/A if (lpos == llen)
1N/A break;
1N/A /* Copy the string being deleted to KILL_BUF. */
1N/A grub_memmove (kill_buf, buf + lpos, llen - lpos + 1);
1N/A cl_delete (llen - lpos);
1N/A break;
1N/A case 25: /* C-y yank the kill buffer */
1N/A cl_insert (kill_buf);
1N/A break;
1N/A case 16: /* C-p fetch the previous command */
1N/A {
1N/A char *p;
1N/A
1N/A if (history < 0)
1N/A /* Save the working buffer. */
1N/A grub_strcpy (cmdline, buf);
1N/A else if (grub_strcmp (get_history (history), buf) != 0)
1N/A /* If BUF is modified, add it into the history list. */
1N/A add_history (buf, history);
1N/A
1N/A history++;
1N/A p = get_history (history);
1N/A if (! p)
1N/A {
1N/A history--;
1N/A break;
1N/A }
1N/A
1N/A grub_strcpy (buf, p);
1N/A llen = grub_strlen (buf);
1N/A lpos = llen;
1N/A cl_refresh (1, 0);
1N/A }
1N/A break;
1N/A case 14: /* C-n fetch the next command */
1N/A {
1N/A char *p;
1N/A
1N/A if (history < 0)
1N/A {
1N/A break;
1N/A }
1N/A else if (grub_strcmp (get_history (history), buf) != 0)
1N/A /* If BUF is modified, add it into the history list. */
1N/A add_history (buf, history);
1N/A
1N/A history--;
1N/A p = get_history (history);
1N/A if (! p)
1N/A p = cmdline;
1N/A
1N/A grub_strcpy (buf, p);
1N/A llen = grub_strlen (buf);
1N/A lpos = llen;
1N/A cl_refresh (1, 0);
1N/A }
1N/A break;
1N/A }
1N/A }
1N/A
1N/A /* ESC, C-d and C-h are always handled. Actually C-d is not
1N/A functional if READLINE is zero, as the cursor cannot go
1N/A backward, but that's ok. */
1N/A switch (c)
1N/A {
1N/A case 27: /* ESC immediately return 1 */
1N/A return 1;
1N/A case 4: /* C-d delete character under cursor */
1N/A if (lpos == llen)
1N/A break;
1N/A cl_delete (1);
1N/A break;
1N/A case 8: /* C-h backspace */
1N/A# ifdef GRUB_UTIL
1N/A case 127: /* also backspace */
1N/A# endif
1N/A if (lpos > 0)
1N/A {
1N/A cl_backward (1);
1N/A cl_delete (1);
1N/A }
1N/A break;
1N/A default: /* insert printable character into line */
1N/A if (c >= ' ' && c <= '~')
1N/A {
1N/A char str[2];
1N/A
1N/A str[0] = c;
1N/A str[1] = 0;
1N/A cl_insert (str);
1N/A }
1N/A }
1N/A }
1N/A
1N/A grub_putchar ('\n');
1N/A
1N/A /* If ECHO_CHAR is NUL, remove the leading spaces. */
1N/A lpos = 0;
1N/A if (! echo_char)
1N/A while (buf[lpos] == ' ')
1N/A lpos++;
1N/A
1N/A /* Copy the working buffer to CMDLINE. */
1N/A grub_memmove (cmdline, buf + lpos, llen - lpos + 1);
1N/A
1N/A /* If the readline-like feature is turned on and CMDLINE is not
1N/A empty, add it into the history list. */
1N/A if (readline && lpos < llen)
1N/A add_history (cmdline, 0);
1N/A
1N/A return 0;
1N/A}
1N/A
1N/A/* Don't use this with a MAXLEN greater than 1600 or so! The problem
1N/A is that GET_CMDLINE depends on the everything fitting on the screen
1N/A at once. So, the whole screen is about 2000 characters, minus the
1N/A PROMPT, and space for error and status lines, etc. MAXLEN must be
1N/A at least 1, and PROMPT and CMDLINE must be valid strings (not NULL
1N/A or zero-length).
1N/A
1N/A If ECHO_CHAR is nonzero, echo it instead of the typed character. */
1N/Aint
1N/Aget_cmdline (char *prompt, char *cmdline, int maxlen,
1N/A int echo_char, int readline)
1N/A{
1N/A int old_cursor;
1N/A int ret;
1N/A
1N/A old_cursor = setcursor (1);
1N/A
1N/A /* Because it is hard to deal with different conditions simultaneously,
1N/A less functional cases are handled here. Assume that TERM_NO_ECHO
1N/A implies TERM_NO_EDIT. */
1N/A if (current_term->flags & (TERM_NO_ECHO | TERM_NO_EDIT))
1N/A {
1N/A char *p = cmdline;
1N/A int c;
1N/A
1N/A /* Make sure that MAXLEN is not too large. */
1N/A if (maxlen > MAX_CMDLINE)
1N/A maxlen = MAX_CMDLINE;
1N/A
1N/A /* Print only the prompt. The contents of CMDLINE is simply discarded,
1N/A even if it is not empty. */
1N/A grub_printf ("%s", prompt);
1N/A
1N/A /* Gather characters until a newline is gotten. */
1N/A while ((c = ASCII_CHAR (getkey ())) != '\n' && c != '\r')
1N/A {
1N/A /* Return immediately if ESC is pressed. */
1N/A if (c == 27)
1N/A {
1N/A setcursor (old_cursor);
1N/A return 1;
1N/A }
1N/A
1N/A /* Printable characters are added into CMDLINE. */
1N/A if (c >= ' ' && c <= '~')
1N/A {
1N/A if (! (current_term->flags & TERM_NO_ECHO))
1N/A grub_putchar (c);
1N/A
1N/A /* Preceding space characters must be ignored. */
1N/A if (c != ' ' || p != cmdline)
1N/A *p++ = c;
1N/A }
1N/A }
1N/A
1N/A *p = 0;
1N/A
1N/A if (! (current_term->flags & TERM_NO_ECHO))
1N/A grub_putchar ('\n');
1N/A
1N/A setcursor (old_cursor);
1N/A return 0;
1N/A }
1N/A
1N/A /* Complicated features are left to real_get_cmdline. */
1N/A ret = real_get_cmdline (prompt, cmdline, maxlen, echo_char, readline);
1N/A setcursor (old_cursor);
1N/A return ret;
1N/A}
1N/A
1N/Aint
1N/Asafe_parse_maxint (char **str_ptr, int *myint_ptr)
1N/A{
1N/A char *ptr = *str_ptr;
1N/A int myint = 0;
1N/A int mult = 10, found = 0;
1N/A
1N/A /*
1N/A * Is this a hex number?
1N/A */
1N/A if (*ptr == '0' && tolower (*(ptr + 1)) == 'x')
1N/A {
1N/A ptr += 2;
1N/A mult = 16;
1N/A }
1N/A
1N/A while (1)
1N/A {
1N/A /* A bit tricky. This below makes use of the equivalence:
1N/A (A >= B && A <= C) <=> ((A - B) <= (C - B))
1N/A when C > B and A is unsigned. */
1N/A unsigned int digit;
1N/A
1N/A digit = tolower (*ptr) - '0';
1N/A if (digit > 9)
1N/A {
1N/A digit -= 'a' - '0';
1N/A if (mult == 10 || digit > 5)
1N/A break;
1N/A digit += 10;
1N/A }
1N/A
1N/A found = 1;
1N/A if (myint > ((MAXINT - digit) / mult))
1N/A {
1N/A errnum = ERR_NUMBER_OVERFLOW;
1N/A return 0;
1N/A }
1N/A myint = (myint * mult) + digit;
1N/A ptr++;
1N/A }
1N/A
1N/A if (!found)
1N/A {
1N/A errnum = ERR_NUMBER_PARSING;
1N/A return 0;
1N/A }
1N/A
1N/A *str_ptr = ptr;
1N/A *myint_ptr = myint;
1N/A
1N/A return 1;
1N/A}
1N/A#endif /* STAGE1_5 */
1N/A
1N/A#if !defined(STAGE1_5) || defined(FSYS_ZFS)
1N/Astatic int
1N/Agrub_vsprintf (char *buffer, const char *format, int *dataptr)
1N/A{
1N/A /* XXX hohmuth
1N/A ugly hack -- should unify with printf() */
1N/A char c, *ptr, str[16];
1N/A char *bp = buffer;
1N/A int len = 0;
1N/A
1N/A while ((c = *format++) != 0)
1N/A {
1N/A if (c != '%') {
1N/A if (buffer)
1N/A *bp++ = c; /* putchar(c); */
1N/A len++;
1N/A } else {
1N/A switch (c = *(format++))
1N/A {
1N/A case 'd': case 'u': case 'x':
1N/A *convert_to_ascii (str, c, *((unsigned long *) dataptr++)) = 0;
1N/A
1N/A ptr = str;
1N/A
1N/A while (*ptr) {
1N/A if (buffer)
1N/A *bp++ = *(ptr++); /* putchar(*(ptr++)); */
1N/A else
1N/A ptr++;
1N/A len++;
1N/A }
1N/A break;
1N/A
1N/A case 'c':
1N/A if (buffer)
1N/A *bp++ = (*(dataptr++))&0xff;
1N/A else
1N/A dataptr++;
1N/A len++;
1N/A /* putchar((*(dataptr++))&0xff); */
1N/A break;
1N/A
1N/A case 's':
1N/A ptr = (char *) (*(dataptr++));
1N/A
1N/A while ((c = *ptr++) != 0) {
1N/A if (buffer)
1N/A *bp++ = c; /* putchar(c); */
1N/A len++;
1N/A }
1N/A break;
1N/A }
1N/A }
1N/A }
1N/A
1N/A *bp = 0;
1N/A return (len);
1N/A}
1N/A
1N/Aint
1N/Agrub_sprintf (char *buffer, const char *format, ...)
1N/A{
1N/A int *dataptr = (int *) &format;
1N/A dataptr++;
1N/A
1N/A return (grub_vsprintf (buffer, format, dataptr));
1N/A}
1N/A
1N/A#endif /* !defined(STAGE1_5) || defined(FSYS_ZFS) */
1N/A
1N/Avoid
1N/Anoisy_printf (const char *format,...)
1N/A{
1N/A int *dataptr = (int *) &format;
1N/A dataptr++;
1N/A
1N/A grub_vprintf(format, dataptr);
1N/A}
1N/A
1N/A/*
1N/A * print to a buffer, unless verbose mode is on
1N/A * if verbos mode is switched on, the buffer is dumped in verbose_func()
1N/A */
1N/Avoid
1N/Agrub_printf (const char *format,...)
1N/A{
1N/A int len;
1N/A int *dataptr = (int *) &format;
1N/A dataptr++;
1N/A
1N/A#ifndef STAGE1_5
1N/A if (silent.status != SILENT)
1N/A#endif
1N/A grub_vprintf(format, dataptr);
1N/A#ifndef STAGE1_5
1N/A else {
1N/A len = grub_vsprintf(NULL, format, dataptr);
1N/A if (silent.buffer_start - silent.buffer + len + 1 >=
1N/A SCREENBUF) {
1N/A silent.buffer_start = silent.buffer;
1N/A silent.looped = 1;
1N/A }
1N/A if (len < SCREENBUF) /* all other cases loop safely */
1N/A silent.buffer_start +=
1N/A grub_vsprintf(silent.buffer_start, format, dataptr);
1N/A }
1N/A#endif
1N/A}
1N/A
1N/A#if !defined(STAGE1_5) || defined(FSYS_FAT)
1N/Aint
1N/Agrub_tolower (int c)
1N/A{
1N/A if (c >= 'A' && c <= 'Z')
1N/A return (c + ('a' - 'A'));
1N/A
1N/A return c;
1N/A}
1N/A#endif /* ! STAGE1_5 || FSYS_FAT */
1N/A
1N/Aint
1N/Agrub_isspace (int c)
1N/A{
1N/A switch (c)
1N/A {
1N/A case ' ':
1N/A case '\t':
1N/A case '\r':
1N/A case '\n':
1N/A return 1;
1N/A default:
1N/A break;
1N/A }
1N/A
1N/A return 0;
1N/A}
1N/A
1N/A#if !defined(STAGE1_5) || defined(FSYS_ISO9660)
1N/Aint
1N/Agrub_memcmp (const char *s1, const char *s2, int n)
1N/A{
1N/A while (n)
1N/A {
1N/A if (*s1 < *s2)
1N/A return -1;
1N/A else if (*s1 > *s2)
1N/A return 1;
1N/A s1++;
1N/A s2++;
1N/A n--;
1N/A }
1N/A
1N/A return 0;
1N/A}
1N/A#endif /* ! STAGE1_5 || FSYS_ISO9660 */
1N/A
1N/A#ifndef STAGE1_5
1N/Aint
1N/Agrub_strncat (char *s1, const char *s2, int n)
1N/A{
1N/A int i = -1;
1N/A
1N/A while (++i < n && s1[i] != 0);
1N/A
1N/A while (i < n && (s1[i++] = *(s2++)) != 0);
1N/A
1N/A s1[n - 1] = 0;
1N/A
1N/A if (i >= n)
1N/A return 0;
1N/A
1N/A s1[i] = 0;
1N/A
1N/A return 1;
1N/A}
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/A/* XXX: This below is an evil hack. Certainly, we should change the
1N/A strategy to determine what should be defined and what shouldn't be
1N/A defined for each image. For example, it would be better to create
1N/A a static library supporting minimal standard C functions and link
1N/A each image with the library. Complicated things should be left to
1N/A computer, definitely. -okuji */
1N/A
1N/A/* Make some grub_str* routines available to ZFS plug-in as well */
1N/A
1N/A#if !defined(STAGE1_5) || defined(FSYS_VSTAFS) || defined(FSYS_ZFS)
1N/Aint
1N/Agrub_strcmp (const char *s1, const char *s2)
1N/A{
1N/A while (*s1 || *s2)
1N/A {
1N/A if (*s1 < *s2)
1N/A return -1;
1N/A else if (*s1 > *s2)
1N/A return 1;
1N/A s1 ++;
1N/A s2 ++;
1N/A }
1N/A
1N/A return 0;
1N/A}
1N/A
1N/Aint
1N/Agrub_strncmp(const char *s1, const char *s2, int n)
1N/A{
1N/A if (s1 == s2)
1N/A return (0);
1N/A n++;
1N/A while (--n != 0 && *s1 == *s2++)
1N/A if (*s1++ == '\0')
1N/A return (0);
1N/A return ((n == 0) ? 0 : *(unsigned char *)s1 - *(unsigned char *)--s2);
1N/A}
1N/A
1N/A#endif /* ! STAGE1_5 || FSYS_VSTAFS || defined(FSYS_ZFS) */
1N/A
1N/A#ifndef STAGE1_5
1N/A/* Wait for a keypress and return its code. */
1N/Aint
1N/Agetkey (void)
1N/A{
1N/A return current_term->getkey ();
1N/A}
1N/A
1N/A/* Check if a key code is available. */
1N/Aint
1N/Acheckkey (void)
1N/A{
1N/A return current_term->checkkey ();
1N/A}
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/A/* Display an ASCII character. */
1N/Avoid
1N/Agrub_putchar (int c)
1N/A{
1N/A if (c == '\n')
1N/A grub_putchar ('\r');
1N/A#ifndef STAGE1_5
1N/A else if (c == '\t' && current_term->getxy)
1N/A {
1N/A int n;
1N/A
1N/A n = 8 - ((current_term->getxy () >> 8) & 3);
1N/A while (n--)
1N/A grub_putchar (' ');
1N/A
1N/A return;
1N/A }
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/A#ifdef STAGE1_5
1N/A
1N/A /* In Stage 1.5, only the normal console is supported. */
1N/A console_putchar (c);
1N/A
1N/A#else /* ! STAGE1_5 */
1N/A
1N/A if (c == '\n')
1N/A {
1N/A /* Internal `more'-like feature. */
1N/A if (count_lines >= 0)
1N/A {
1N/A count_lines++;
1N/A if (count_lines >= max_lines - 2)
1N/A {
1N/A int tmp;
1N/A
1N/A /* It's important to disable the feature temporarily, because
1N/A the following grub_printf call will print newlines. */
1N/A count_lines = -1;
1N/A
1N/A grub_printf("\n");
1N/A if (current_term->setcolorstate)
1N/A current_term->setcolorstate (COLOR_STATE_HIGHLIGHT);
1N/A
1N/A grub_printf ("[Hit return to continue]");
1N/A
1N/A if (current_term->setcolorstate)
1N/A current_term->setcolorstate (COLOR_STATE_NORMAL);
1N/A
1N/A do
1N/A {
1N/A tmp = ASCII_CHAR (getkey ());
1N/A }
1N/A while (tmp != '\n' && tmp != '\r');
1N/A grub_printf ("\r \r");
1N/A
1N/A /* Restart to count lines. */
1N/A count_lines = 0;
1N/A return;
1N/A }
1N/A }
1N/A }
1N/A
1N/A current_term->putchar (c);
1N/A
1N/A#endif /* ! STAGE1_5 */
1N/A}
1N/A
1N/A#ifndef STAGE1_5
1N/Avoid
1N/Agotoxy (int x, int y)
1N/A{
1N/A current_term->gotoxy (x, y);
1N/A}
1N/A
1N/Aint
1N/Agetxy (void)
1N/A{
1N/A return current_term->getxy ();
1N/A}
1N/A
1N/Avoid
1N/Acls (void)
1N/A{
1N/A /* If the terminal is dumb, there is no way to clean the terminal. */
1N/A if (current_term->flags & TERM_DUMB)
1N/A grub_putchar ('\n');
1N/A else
1N/A current_term->cls ();
1N/A}
1N/A
1N/Aint
1N/Asetcursor (int on)
1N/A{
1N/A if (current_term->setcursor)
1N/A return current_term->setcursor (on);
1N/A
1N/A return 1;
1N/A}
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/Aint
1N/Asubstring (const char *s1, const char *s2)
1N/A{
1N/A while (*s1 == *s2)
1N/A {
1N/A /* The strings match exactly. */
1N/A if (! *(s1++))
1N/A return 0;
1N/A s2 ++;
1N/A }
1N/A
1N/A /* S1 is a substring of S2. */
1N/A if (*s1 == 0)
1N/A return -1;
1N/A
1N/A /* S1 isn't a substring. */
1N/A return 1;
1N/A}
1N/A
1N/A#if !defined(STAGE1_5) || defined(FSYS_ZFS)
1N/Achar *
1N/Agrub_strstr (const char *s1, const char *s2)
1N/A{
1N/A while (*s1)
1N/A {
1N/A const char *ptr, *tmp;
1N/A
1N/A ptr = s1;
1N/A tmp = s2;
1N/A
1N/A while (*tmp && *ptr == *tmp)
1N/A ptr++, tmp++;
1N/A
1N/A if (tmp > s2 && ! *tmp)
1N/A return (char *) s1;
1N/A
1N/A s1++;
1N/A }
1N/A
1N/A return 0;
1N/A}
1N/A
1N/Aint
1N/Agrub_strlen (const char *str)
1N/A{
1N/A int len = 0;
1N/A
1N/A while (*str++)
1N/A len++;
1N/A
1N/A return len;
1N/A}
1N/A#endif /* !defined(STAGE1_5) || defined(FSYS_ZFS) */
1N/A
1N/A#ifndef STAGE1_5
1N/A/* Terminate the string STR with NUL. */
1N/Aint
1N/Anul_terminate (char *str)
1N/A{
1N/A int ch;
1N/A
1N/A while (*str && ! grub_isspace (*str))
1N/A str++;
1N/A
1N/A ch = *str;
1N/A *str = 0;
1N/A return ch;
1N/A}
1N/A
1N/Achar *
1N/Agrub_strchr (char *str, char c)
1N/A{
1N/A for (; *str && (*str != c); str++);
1N/A
1N/A return (*str ? str : NULL);
1N/A}
1N/A
1N/Achar *
1N/Agrub_strrchr (char *str, char c)
1N/A{
1N/A char *r = NULL;
1N/A
1N/A do {
1N/A if (*str == c)
1N/A r = str;
1N/A } while (*str++);
1N/A
1N/A return (r);
1N/A}
1N/A
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/Aint
1N/Amemcheck (unsigned long addr, unsigned long len)
1N/A{
1N/A int local_errnum = 0;
1N/A#ifdef GRUB_UTIL
1N/A auto unsigned long start_addr (void);
1N/A auto unsigned long end_addr (void);
1N/A
1N/A auto unsigned long start_addr (void)
1N/A {
1N/A int ret;
1N/A# if defined(HAVE_START_SYMBOL)
1N/A asm volatile ("movl $start, %0" : "=a" (ret));
1N/A# elif defined(HAVE_USCORE_START_SYMBOL)
1N/A asm volatile ("movl $_start, %0" : "=a" (ret));
1N/A# endif
1N/A return ret;
1N/A }
1N/A
1N/A auto unsigned long end_addr (void)
1N/A {
1N/A int ret;
1N/A# if defined(HAVE_END_SYMBOL)
1N/A asm volatile ("movl $end, %0" : "=a" (ret));
1N/A# elif defined(HAVE_USCORE_END_SYMBOL)
1N/A asm volatile ("movl $_end, %0" : "=a" (ret));
1N/A# endif
1N/A return ret;
1N/A }
1N/A
1N/A if (start_addr () <= addr && end_addr () > addr + len)
1N/A return ! local_errnum;
1N/A#endif /* GRUB_UTIL */
1N/A
1N/A if ((addr < RAW_ADDR (0x1000))
1N/A || (addr < RAW_ADDR (0x100000)
1N/A && RAW_ADDR (mbi.mem_lower * 1024) < (addr + len))
1N/A || (addr >= RAW_ADDR (0x100000)
1N/A && RAW_ADDR (mbi.mem_upper * 1024) < ((addr - 0x100000) + len)))
1N/A local_errnum = ERR_WONT_FIT;
1N/A
1N/A if (errnum == 0) /* preserve original errnum */
1N/A errnum = local_errnum;
1N/A return ! local_errnum;
1N/A}
1N/A
1N/Avoid
1N/Agrub_memcpy(void *dest, const void *src, int len)
1N/A{
1N/A int i;
1N/A register char *d = (char*)dest, *s = (char*)src;
1N/A
1N/A for (i = 0; i < len; i++)
1N/A d[i] = s[i];
1N/A}
1N/A
1N/Avoid *
1N/Agrub_memmove (void *to, const void *from, int len)
1N/A{
1N/A if (memcheck ((int) to, len))
1N/A {
1N/A /* This assembly code is stolen from
1N/A linux-2.2.2/include/asm-i386/string.h. This is not very fast
1N/A but compact. */
1N/A int d0, d1, d2;
1N/A
1N/A if (to < from)
1N/A {
1N/A asm volatile ("cld\n\t"
1N/A "rep\n\t"
1N/A "movsb"
1N/A : "=&c" (d0), "=&S" (d1), "=&D" (d2)
1N/A : "0" (len),"1" (from),"2" (to)
1N/A : "memory");
1N/A }
1N/A else
1N/A {
1N/A asm volatile ("std\n\t"
1N/A "rep\n\t"
1N/A "movsb\n\t"
1N/A "cld"
1N/A : "=&c" (d0), "=&S" (d1), "=&D" (d2)
1N/A : "0" (len),
1N/A "1" (len - 1 + (const char *) from),
1N/A "2" (len - 1 + (char *) to)
1N/A : "memory");
1N/A }
1N/A return to;
1N/A }
1N/A
1N/A return NULL;
1N/A}
1N/A
1N/Avoid *
1N/Agrub_memset (void *start, int c, int len)
1N/A{
1N/A char *p = start;
1N/A
1N/A if (memcheck ((int) start, len))
1N/A {
1N/A while (len -- > 0)
1N/A *p ++ = c;
1N/A }
1N/A
1N/A return errnum ? NULL : start;
1N/A}
1N/A
1N/A#ifndef STAGE1_5
1N/Achar *
1N/Agrub_strcpy (char *dest, const char *src)
1N/A{
1N/A grub_memmove (dest, src, grub_strlen (src) + 1);
1N/A return dest;
1N/A}
1N/A#endif /* ! STAGE1_5 */
1N/A
1N/A#ifndef GRUB_UTIL
1N/A# undef memcpy
1N/A/* GCC emits references to memcpy() for struct copies etc. */
1N/Avoid *memcpy (void *dest, const void *src, int n) __attribute__ ((alias ("grub_memmove")));
1N/A#endif