blob: 9805d120b370c826d125b97914f20d4b1f94818d [file] [log] [blame] [raw]
/* char_io.c - basic console input and output */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1996 Erich Boleyn <erich@uruk.org>
* Copyright (C) 1999 Free Software Foundation, Inc.
*
* This program 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 2 of the License, or
* (at your option) any later version.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "shared.h"
void
print_error (void)
{
if (errnum > ERR_NONE && errnum < MAX_ERR_NUM)
#ifndef STAGE1_5
/* printf("\7\n %s\n", err_list[errnum]); */
printf ("\n %s\n", err_list[errnum]);
#else /* STAGE1_5 */
printf ("Error: %d\n", errnum);
#endif /* STAGE1_5 */
errnum = ERR_NONE;
}
char *
convert_to_ascii (char *buf, int c,...)
{
unsigned long num = *((&c) + 1), mult = 10;
char *ptr = buf;
if (c == 'x')
mult = 16;
if ((num & 0x80000000uL) && c == 'd')
{
num = (~num) + 1;
*(ptr++) = '-';
buf++;
}
do
{
int dig = num % mult;
*(ptr++) = ((dig > 9) ? dig + 'a' - 10 : '0' + dig);
}
while (num /= mult);
/* reorder to correct direction!! */
{
char *ptr1 = ptr - 1;
char *ptr2 = buf;
while (ptr1 > ptr2)
{
int c = *ptr1;
*ptr1 = *ptr2;
*ptr2 = c;
ptr1--;
ptr2++;
}
}
return ptr;
}
void
grub_printf (char *format,...)
{
int *dataptr = (int *) &format;
char c, *ptr, str[16];
dataptr++;
while ((c = *(format++)) != 0)
{
if (c != '%')
putchar (c);
else
switch (c = *(format++))
{
case 'd':
case 'u':
case 'x':
*convert_to_ascii (str, c, *((unsigned long *) dataptr++)) = 0;
ptr = str;
while (*ptr)
putchar (*(ptr++));
break;
#ifndef STAGE1_5
case 'c':
putchar ((*(dataptr++)) & 0xff);
break;
case 's':
ptr = (char *) (*(dataptr++));
while ((c = *(ptr++)) != 0)
putchar (c);
break;
#endif
}
}
}
#ifndef STAGE1_5
void
init_page (void)
{
cls ();
printf ("\n GRUB version %s (%dK lower / %dK upper memory)\n\n",
version_string, mbi.mem_lower, mbi.mem_upper);
}
/* Don't use this with a MAXLEN greater than 1600 or so! The problem
is that GET_CMDLINE depends on the everything fitting on the screen
at once. So, the whole screen is about 2000 characters, minus the
PROMPT, and space for error and status lines, etc. MAXLEN must be
at least 1, and PROMPT and CMDLINE must be valid strings (not NULL
or zero-length).
If ECHO_CHAR is nonzero, echo it instead of the typed character. */
int
get_cmdline (char *prompt, char *commands, char *cmdline, int maxlen,
int echo_char)
{
int ystart, yend, xend, lpos, c;
int plen = 0;
int llen = 0;
/* nested function definition for code simplicity */
static void cl_print (char *str, int echo_char)
{
while (*str != 0)
{
putchar (echo_char ? echo_char : *str);
str ++;
if (++xend > 78)
{
xend = 0;
putchar (' ');
if (yend == (getxy () & 0xff))
ystart--;
else
yend++;
}
}
}
/* nested function definition for code simplicity */
static void cl_setcpos (void)
{
yend = ((lpos + plen) / 79) + ystart;
xend = ((lpos + plen) % 79);
gotoxy (xend, yend);
}
/* nested function definition for initial command-line printing */
static void cl_init ()
{
/* distinguish us from other lines and error messages! */
putchar ('\n');
/* print full line and set position here */
ystart = (getxy () & 0xff);
yend = ystart;
xend = 0;
cl_print (prompt, 0);
cl_print (cmdline, echo_char);
cl_setcpos ();
}
/* nested function definition for erasing to the end of the line */
static void cl_kill_to_end ()
{
int i;
cmdline[lpos] = 0;
for (i = lpos; i <= llen; i++)
{
if (i && ((i + plen) % 79) == 0)
putchar (' ');
putchar (' ');
}
llen = lpos;
cl_setcpos ();
}
while (prompt[plen])
plen++;
while (cmdline[llen])
llen++;
if (maxlen > MAX_CMDLINE)
{
maxlen = MAX_CMDLINE;
if (llen >= MAX_CMDLINE)
{
llen = MAX_CMDLINE - 1;
cmdline[MAX_CMDLINE] = 0;
}
}
lpos = llen;
cl_init ();
while (ASCII_CHAR (c = getkey ()) != '\n' && ASCII_CHAR (c) != '\r')
{
switch (c)
{
case KEY_LEFT:
c = 2;
break;
case KEY_RIGHT:
c = 6;
break;
case KEY_HOME:
c = 1;
break;
case KEY_END:
c = 5;
break;
case KEY_DC:
c = 4;
case KEY_BACKSPACE:
c = 8;
default:
}
c = ASCII_CHAR (c);
switch (c)
{
case 27: /* ESC immediately return 1 */
return 1;
case 9: /* TAB lists completions */
{
int i, j = 0, llen_old = llen;
while (cmdline[j] && cmdline[j] != '=')
j++;
/* since the command line cannot have a '\n', we're OK to use c */
c = cmdline[lpos];
cl_kill_to_end ();
/* goto part after line here */
yend = ((llen + plen) / 79) + ystart;
putchar ('\n');
gotoxy (0, getxy () & 0xff);
if (lpos > j)
{
for (i = lpos; i > 0 && cmdline[i - 1] != ' '; i--);
if (i <= j)
i = j + 1;
/* print possible completions */
print_completions (cmdline + i);
}
else if (commands)
printf (commands);
else
break;
/* restore command-line */
cmdline[lpos] = c;
llen = llen_old;
cl_init ();
}
break;
case 1: /* C-a go to beginning of line */
lpos = 0;
cl_setcpos ();
break;
case 5: /* C-e go to end of line */
lpos = llen;
cl_setcpos ();
break;
case 6: /* C-f forward one character */
if (lpos < llen)
{
lpos ++;
cl_setcpos ();
}
break;
case 2: /* C-b backward one character */
if (lpos > 0)
{
lpos--;
cl_setcpos ();
}
break;
case 4: /* C-d delete character under cursor */
if (lpos == llen)
break;
lpos ++;
/* fallthrough is on purpose! */
case 8: /* C-h backspace */
if (lpos > 0)
{
int i;
for (i = lpos - 1; i < llen; i++)
cmdline[i] = cmdline[i + 1];
i = lpos;
lpos = llen - 1;
cl_setcpos ();
putchar (' ');
lpos = i - 1; /* restore lpos and decrement */
llen--;
cl_setcpos ();
if (lpos != llen)
{
cl_print (cmdline + lpos, echo_char);
cl_setcpos ();
}
}
break;
case 21: /* C-u kill to beginning of line */
if (lpos == 0)
break;
{
int i;
for (i = 0; i < (llen - lpos); i++)
cmdline[i] = cmdline[lpos + i];
}
lpos = llen - lpos;
cl_setcpos ();
/* fallthrough on purpose! */
case 11: /* C-k kill to end of line */
if (lpos < llen)
{
cl_kill_to_end ();
if (c == 21)
{
lpos = 0;
cl_setcpos ();
cl_print (cmdline, echo_char);
cl_setcpos ();
}
}
break;
default: /* insert printable character into line */
if (llen < (maxlen - 1) && c >= ' ' && c <= '~')
{
if (lpos == llen)
{
cmdline[lpos] = c;
cmdline[lpos + 1] = 0;
cl_print (cmdline + lpos, echo_char);
lpos ++;
cl_setcpos ();
}
else
{
int i;
for (i = llen; i >= lpos; i--)
cmdline[i + 1] = cmdline[i];
cmdline[lpos] = c;
cl_setcpos ();
cl_print (cmdline + lpos, echo_char);
lpos++;
cl_setcpos ();
}
llen++;
}
}
}
/* goto part after line here */
yend = ((llen + plen) / 79) + ystart;
putchar ('\n');
gotoxy (0, getxy () & 0xff);
/* remove leading spaces */
/* use c and lpos as indexes now */
for (lpos = 0; cmdline[lpos] == ' '; lpos++);
if (lpos != 0)
{
c = 0;
do
{
cmdline[c] = cmdline[lpos];
c++;
lpos++;
}
while (cmdline[lpos]);
/* Zero-terminate the string. */
cmdline[c] = 0;
}
return 0;
}
#endif /* STAGE1_5 */
int
get_based_digit (int c, int base)
{
int digit = -1;
/* make sure letter in the the range we can check! */
c = tolower (c);
/*
* Is it in the range between zero and nine?
*/
if (base > 0 && c >= '0' && c <= '9' && c < (base + '0'))
{
digit = c - '0';
}
/*
* Is it in the range used by a letter?
*/
if (base > 10 && c >= 'a' && c <= 'z' && c < ((base - 10) + 'a'))
{
digit = c - 'a' + 10;
}
return digit;
}
int
safe_parse_maxint (char **str_ptr, int *myint_ptr)
{
register char *ptr = *str_ptr;
register int myint = 0, digit;
int mult = 10, found = 0;
/*
* Is this a hex number?
*/
if (*ptr == '0' && tolower (*(ptr + 1)) == 'x')
{
ptr += 2;
mult = 16;
}
while ((digit = get_based_digit (*ptr, mult)) != -1)
{
found = 1;
if (myint > ((MAXINT - digit) / mult))
{
errnum = ERR_NUMBER_PARSING;
return 0;
}
myint = (myint * mult) + digit;
ptr++;
}
if (!found)
{
errnum = ERR_NUMBER_PARSING;
return 0;
}
*str_ptr = ptr;
*myint_ptr = myint;
return 1;
}
int
grub_tolower (int c)
{
if (c >= 'A' && c <= 'Z')
return (c + ('a' - 'A'));
return c;
}
int
grub_isspace (int c)
{
if (c == ' ' || c == '\t' || c == '\n')
return 1;
return 0;
}
#ifndef STAGE1_5
int
grub_strncat (char *s1, char *s2, int n)
{
int i = -1;
while (++i < n && s1[i] != 0);
while (i < n && (s1[i++] = *(s2++)) != 0);
s1[n - 1] = 0;
if (i >= n)
return 0;
s1[i] = 0;
return 1;
}
int
grub_strcmp (char *s1, char *s2)
{
while (*s1 || *s2)
{
if (*s1 < *s2)
return -1;
else if (*s1 > *s2)
return 1;
s1 ++;
s2 ++;
}
return 0;
}
#endif /* ! STAGE1_5 */
int
substring (char *s1, char *s2)
{
while (*s1 == *s2)
{
/* The strings match exactly. */
if (! *(s1++))
return 0;
s2 ++;
}
/* S1 is a substring of S2. */
if (*s1 == 0)
return -1;
/* S1 isn't a substring. */
return 1;
}
#ifndef STAGE1_5
char *
grub_strstr (char *s1, char *s2)
{
char *ptr, *tmp;
while (*s1)
{
ptr = s1;
tmp = s2;
while (*s1 && *s1++ == *tmp++);
if (tmp > s2 && !*(tmp - 1))
return ptr;
}
return 0;
}
#endif /* ! STAGE1_5 */
int
memcheck (int start, int len)
{
#ifdef GRUB_UTIL
/* FIXME: cur_part_desc is the only global variable that we memmove
to. We should fix this so that we don't need a special case
(i.e. so that it lives on the stack, or somewhere inside
grub_scratch_mem). */
extern char cur_part_desc[];
if (start >= (int) cur_part_desc && start + len <= (int) cur_part_desc + 16)
return ! errnum;
#endif /* GRUB_UTIL */
if ((start < RAW_ADDR (0x1000)) ||
(start < RAW_ADDR (0x100000) &&
RAW_ADDR (mbi.mem_lower * 1024) < (start + len)) ||
(start >= RAW_ADDR (0x100000) &&
RAW_ADDR (mbi.mem_upper * 1024) < ((start - 0x100000) + len)))
errnum = ERR_WONT_FIT;
return ! errnum;
}
char *
grub_memmove (char *to, char *from, int len)
{
char *ret = to;
if (memcheck ((int) to, len))
{
if ((to >= from + len) || (to <= from))
{
while (len >= sizeof (unsigned long))
{
len -= sizeof (unsigned long);
*(((unsigned long *) to)++) = *(((unsigned long *) from)++);
}
while (len-- > 0)
*(to++) = *(from++);
}
else
{
/* We have a region that overlaps, but would be overwritten
if we copied it forward. */
while (len-- > 0)
to[len] = from[len];
}
}
return errnum ? NULL : ret;
}
#ifndef STAGE1_5
void *
grub_memset (void *start, int c, int len)
{
char *p = start;
if (memcheck ((int) start, len))
{
while (len -- > 0)
*p ++ = c;
}
return errnum ? NULL : start;
}
#endif /* ! STAGE1_5 */