Marco Gerards added ext2fs support, grub-emu, a new command-line
engine, and fixed many bugs.
+
+Omniflux added terminfo and serial support.
+
+Vincent Pelletier added Sparc64 support.
+
+Hollis Blanchard implemented many parts of PowerPC support.
+
+Tomas Ebenlendr added the command chainloader into the normal mode,
+fixed some bugs.
+
+Guillem Jover merged architecture-independent ELF support code.
+
+Vesa Jaaskelainen added VBE support.
+2005-09-03 Yoshinori K. Okuji <okuji@enbug.org>
+
+ * normal/completion.c (complete_arguments): Add the qualifier
+ const into OPTIONS.
+
+ From Omniflux <omniflux+lists@omniflux.com>:
+ * include/grub/terminfo.h: New file.
+ * include/grub/tparm.h: Likewise.
+ * include/grub/i386/pc/serial.h: Likewise.
+ * term/terminfo.c: Likewise.
+ * term/tparm.c: Likewise.
+ * term/i386/pc/serial.c: Likewise.
+ * conf/i386-pc.rmk (pkgdata_MODULES): Added terminfo.mod and
+ serial.mod.
+ (terminfo_mod_SOURCES): New variable.
+ (terminfo_mod_CFLAGS): Likewise.
+ (serial_mod_SOURCES): Likewise.
+ (serial_mod_CFLAGS): Likewise.
+
2005-08-31 Yoshinori K. Okuji <okuji@enbug.org>
* DISTLIST: Replaced boot/powerpc/ieee1275/crt0.S and
Hollis Blanchard <hollis@penguinppc.org>
Marco Gerards <metgerards@student.han.nl>
NIIBE Yutaka <gniibe@m17n.org>
+Omniflux <omniflux@omniflux.com>
Robert Bihlmeyer <robbe@orcus.priv.at>
Ruslan Nikolaev <nruslan@mail.com>
Timothy Baldwin <T.E.Baldwin99@members.leeds.ac.uk>
Tomas Ebenlendr <ebik@ucw.cz>
Tsuneyoshi Yasuo <tuneyoshi@naic.co.jp>
+Vesa Jaaskelainen <chaac@nic.fi>
Vincent Guffens <guffens@inma.ucl.ac.be>
Vincent Pelletier <subdino2004@yahoo.fr>
Vladimir Serbinenko <phcoder@gmail.com>
terminal.mod fshelp.mod chain.mod multiboot.mod amiga.mod \
apple.mod pc.mod sun.mod loopback.mod reboot.mod halt.mod \
help.mod default.mod timeout.mod configfile.mod vbe.mod \
- vesafb.mod vbetest.mod vbeinfo.mod search.mod gzio.mod
+ vesafb.mod vbetest.mod vbeinfo.mod search.mod gzio.mod \
+ terminfo.mod serial.mod
# For _chain.mod.
_chain_mod_SOURCES = loader/i386/pc/chainloader.c
font_mod_CFLAGS = $(COMMON_CFLAGS)
+# For terminfo.mod.
+terminfo_mod_SOURCES = term/terminfo.c term/tparm.c
+CLEANFILES += terminfo.mod mod-terminfo.o mod-terminfo.c pre-terminfo.o terminfo_mod-term_terminfo.o terminfo_mod-term_tparm.o def-terminfo.lst und-terminfo.lst
+MOSTLYCLEANFILES += terminfo_mod-term_terminfo.d terminfo_mod-term_tparm.d
+DEFSYMFILES += def-terminfo.lst
+UNDSYMFILES += und-terminfo.lst
+
+terminfo.mod: pre-terminfo.o mod-terminfo.o
+ -rm -f $@
+ $(LD) -r -d -o $@ $^
+ $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@
+
+pre-terminfo.o: terminfo_mod-term_terminfo.o terminfo_mod-term_tparm.o
+ -rm -f $@
+ $(LD) -r -d -o $@ $^
+
+mod-terminfo.o: mod-terminfo.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(terminfo_mod_CFLAGS) -c -o $@ $<
+
+mod-terminfo.c: moddep.lst genmodsrc.sh
+ sh $(srcdir)/genmodsrc.sh 'terminfo' $< > $@ || (rm -f $@; exit 1)
+
+def-terminfo.lst: pre-terminfo.o
+ $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 terminfo/' > $@
+
+und-terminfo.lst: pre-terminfo.o
+ echo 'terminfo' > $@
+ $(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+
+terminfo_mod-term_terminfo.o: term/terminfo.c
+ $(CC) -Iterm -I$(srcdir)/term $(CPPFLAGS) $(CFLAGS) $(terminfo_mod_CFLAGS) -c -o $@ $<
+
+terminfo_mod-term_terminfo.d: term/terminfo.c
+ set -e; $(CC) -Iterm -I$(srcdir)/term $(CPPFLAGS) $(CFLAGS) $(terminfo_mod_CFLAGS) -M $< | sed 's,terminfo\.o[ :]*,terminfo_mod-term_terminfo.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@
+
+-include terminfo_mod-term_terminfo.d
+
+CLEANFILES += cmd-terminfo.lst fs-terminfo.lst
+COMMANDFILES += cmd-terminfo.lst
+FSFILES += fs-terminfo.lst
+
+cmd-terminfo.lst: term/terminfo.c gencmdlist.sh
+ set -e; $(CC) -Iterm -I$(srcdir)/term $(CPPFLAGS) $(CFLAGS) $(terminfo_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh terminfo > $@ || (rm -f $@; exit 1)
+
+fs-terminfo.lst: term/terminfo.c genfslist.sh
+ set -e; $(CC) -Iterm -I$(srcdir)/term $(CPPFLAGS) $(CFLAGS) $(terminfo_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh terminfo > $@ || (rm -f $@; exit 1)
+
+
+terminfo_mod-term_tparm.o: term/tparm.c
+ $(CC) -Iterm -I$(srcdir)/term $(CPPFLAGS) $(CFLAGS) $(terminfo_mod_CFLAGS) -c -o $@ $<
+
+terminfo_mod-term_tparm.d: term/tparm.c
+ set -e; $(CC) -Iterm -I$(srcdir)/term $(CPPFLAGS) $(CFLAGS) $(terminfo_mod_CFLAGS) -M $< | sed 's,tparm\.o[ :]*,terminfo_mod-term_tparm.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@
+
+-include terminfo_mod-term_tparm.d
+
+CLEANFILES += cmd-tparm.lst fs-tparm.lst
+COMMANDFILES += cmd-tparm.lst
+FSFILES += fs-tparm.lst
+
+cmd-tparm.lst: term/tparm.c gencmdlist.sh
+ set -e; $(CC) -Iterm -I$(srcdir)/term $(CPPFLAGS) $(CFLAGS) $(terminfo_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh terminfo > $@ || (rm -f $@; exit 1)
+
+fs-tparm.lst: term/tparm.c genfslist.sh
+ set -e; $(CC) -Iterm -I$(srcdir)/term $(CPPFLAGS) $(CFLAGS) $(terminfo_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh terminfo > $@ || (rm -f $@; exit 1)
+
+
+terminfo_mod_CFLAGS = $(COMMON_CFLAGS)
+
+# For serial.mod.
+serial_mod_SOURCES = term/i386/pc/serial.c
+CLEANFILES += serial.mod mod-serial.o mod-serial.c pre-serial.o serial_mod-term_i386_pc_serial.o def-serial.lst und-serial.lst
+MOSTLYCLEANFILES += serial_mod-term_i386_pc_serial.d
+DEFSYMFILES += def-serial.lst
+UNDSYMFILES += und-serial.lst
+
+serial.mod: pre-serial.o mod-serial.o
+ -rm -f $@
+ $(LD) -r -d -o $@ $^
+ $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -R .note -R .comment $@
+
+pre-serial.o: serial_mod-term_i386_pc_serial.o
+ -rm -f $@
+ $(LD) -r -d -o $@ $^
+
+mod-serial.o: mod-serial.c
+ $(CC) $(CPPFLAGS) $(CFLAGS) $(serial_mod_CFLAGS) -c -o $@ $<
+
+mod-serial.c: moddep.lst genmodsrc.sh
+ sh $(srcdir)/genmodsrc.sh 'serial' $< > $@ || (rm -f $@; exit 1)
+
+def-serial.lst: pre-serial.o
+ $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 serial/' > $@
+
+und-serial.lst: pre-serial.o
+ echo 'serial' > $@
+ $(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+
+serial_mod-term_i386_pc_serial.o: term/i386/pc/serial.c
+ $(CC) -Iterm/i386/pc -I$(srcdir)/term/i386/pc $(CPPFLAGS) $(CFLAGS) $(serial_mod_CFLAGS) -c -o $@ $<
+
+serial_mod-term_i386_pc_serial.d: term/i386/pc/serial.c
+ set -e; $(CC) -Iterm/i386/pc -I$(srcdir)/term/i386/pc $(CPPFLAGS) $(CFLAGS) $(serial_mod_CFLAGS) -M $< | sed 's,serial\.o[ :]*,serial_mod-term_i386_pc_serial.o $@ : ,g' > $@; [ -s $@ ] || rm -f $@
+
+-include serial_mod-term_i386_pc_serial.d
+
+CLEANFILES += cmd-serial.lst fs-serial.lst
+COMMANDFILES += cmd-serial.lst
+FSFILES += fs-serial.lst
+
+cmd-serial.lst: term/i386/pc/serial.c gencmdlist.sh
+ set -e; $(CC) -Iterm/i386/pc -I$(srcdir)/term/i386/pc $(CPPFLAGS) $(CFLAGS) $(serial_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh serial > $@ || (rm -f $@; exit 1)
+
+fs-serial.lst: term/i386/pc/serial.c genfslist.sh
+ set -e; $(CC) -Iterm/i386/pc -I$(srcdir)/term/i386/pc $(CPPFLAGS) $(CFLAGS) $(serial_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh serial > $@ || (rm -f $@; exit 1)
+
+
+serial_mod_CFLAGS = $(COMMON_CFLAGS)
+
# For _multiboot.mod.
_multiboot_mod_SOURCES = loader/i386/pc/multiboot.c
CLEANFILES += _multiboot.mod mod-_multiboot.o mod-_multiboot.c pre-_multiboot.o _multiboot_mod-loader_i386_pc_multiboot.o def-_multiboot.lst und-_multiboot.lst
terminal.mod fshelp.mod chain.mod multiboot.mod amiga.mod \
apple.mod pc.mod sun.mod loopback.mod reboot.mod halt.mod \
help.mod default.mod timeout.mod configfile.mod vbe.mod \
- vesafb.mod vbetest.mod vbeinfo.mod search.mod gzio.mod
+ vesafb.mod vbetest.mod vbeinfo.mod search.mod gzio.mod \
+ terminfo.mod serial.mod
# For _chain.mod.
_chain_mod_SOURCES = loader/i386/pc/chainloader.c
font_mod_SOURCES = font/manager.c
font_mod_CFLAGS = $(COMMON_CFLAGS)
+# For terminfo.mod.
+terminfo_mod_SOURCES = term/terminfo.c term/tparm.c
+terminfo_mod_CFLAGS = $(COMMON_CFLAGS)
+
+# For serial.mod.
+serial_mod_SOURCES = term/i386/pc/serial.c
+serial_mod_CFLAGS = $(COMMON_CFLAGS)
+
# For _multiboot.mod.
_multiboot_mod_SOURCES = loader/i386/pc/multiboot.c
_multiboot_mod_CFLAGS = $(COMMON_CFLAGS)
--- /dev/null
+/* serial.h - serial device interface */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2000,2001,2002,2005 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.
+ */
+
+#ifndef GRUB_SERIAL_MACHINE_HEADER
+#define GRUB_SERIAL_MACHINE_HEADER 1
+
+/* Macros. */
+
+/* The offsets of UART registers. */
+#define UART_TX 0
+#define UART_RX 0
+#define UART_DLL 0
+#define UART_IER 1
+#define UART_DLH 1
+#define UART_IIR 2
+#define UART_FCR 2
+#define UART_LCR 3
+#define UART_MCR 4
+#define UART_LSR 5
+#define UART_MSR 6
+#define UART_SR 7
+
+/* For LSR bits. */
+#define UART_DATA_READY 0x01
+#define UART_EMPTY_TRANSMITTER 0x20
+
+/* The type of parity. */
+#define UART_NO_PARITY 0x00
+#define UART_ODD_PARITY 0x08
+#define UART_EVEN_PARITY 0x18
+
+/* The type of word length. */
+#define UART_5BITS_WORD 0x00
+#define UART_6BITS_WORD 0x01
+#define UART_7BITS_WORD 0x02
+#define UART_8BITS_WORD 0x03
+
+/* The type of the length of stop bit. */
+#define UART_1_STOP_BIT 0x00
+#define UART_2_STOP_BITS 0x04
+
+/* the switch of DLAB. */
+#define UART_DLAB 0x80
+
+/* Enable the FIFO. */
+#define UART_ENABLE_FIFO 0xC7
+
+/* Turn on DTR, RTS, and OUT2. */
+#define UART_ENABLE_MODEM 0x0B
+
+#endif /* ! GRUB_SERIAL_MACHINE_HEADER */
--- /dev/null
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2003,2005 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 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 GRUB; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef GRUB_TERMINFO_HEADER
+#define GRUB_TERMINFO_HEADER 1
+
+#include <grub/err.h>
+#include <grub/types.h>
+
+char *grub_terminfo_get_current (void);
+grub_err_t grub_terminfo_set_current (const char *);
+
+void grub_terminfo_gotoxy (grub_uint8_t x, grub_uint8_t y);
+void grub_terminfo_cls (void);
+void grub_terminfo_reverse_video_on (void);
+void grub_terminfo_reverse_video_off (void);
+void grub_terminfo_cursor_on (void);
+void grub_terminfo_cursor_off (void);
+
+#endif /* ! GRUB_TERMINFO_HEADER */
--- /dev/null
+/* tparm.h - parameter formatting of terminfo */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2002,2005 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.
+ */
+
+#ifndef GRUB_TPARM_HEADER
+#define GRUB_TPARM_HEADER 1
+
+/* Function prototypes. */
+char *grub_terminfo_tparm (const char *string, ...);
+
+#endif /* ! GRUB_TPARM_HEADER */
complete_arguments (char *command)
{
grub_command_t cmd;
- struct grub_arg_option *option;
+ const struct grub_arg_option *option;
char shortarg[] = "- ";
cmd = grub_command_find (command);
/* Add the short arguments. */
for (option = cmd->options; option->doc; option++)
{
- if (!option->shortarg)
+ if (! option->shortarg)
continue;
shortarg[1] = option->shortarg;
--- /dev/null
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2000,2001,2002,2003,2004,2005 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 <grub/machine/serial.h>
+#include <grub/machine/console.h>
+#include <grub/term.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/normal.h>
+#include <grub/arg.h>
+#include <grub/terminfo.h>
+
+#define TEXT_WIDTH 80
+#define TEXT_HEIGHT 25
+
+static unsigned int xpos, ypos;
+static unsigned int keep_track = 1;
+static unsigned int registered = 0;
+
+/* An input buffer. */
+static char input_buf[8];
+static unsigned int npending = 0;
+
+/* Argument options. */
+static const struct grub_arg_option options[] =
+{
+ {"unit", 'u', 0, "Set the serial unit", 0, ARG_TYPE_INT},
+ {"port", 'p', 0, "Set the serial port address", 0, ARG_TYPE_STRING},
+ {"speed", 's', 0, "Set the serial port speed", 0, ARG_TYPE_INT},
+ {"word", 'w', 0, "Set the serial port word length", 0, ARG_TYPE_INT},
+ {"parity", 'r', 0, "Set the serial port parity", 0, ARG_TYPE_STRING},
+ {"stop", 't', 0, "Set the serial port stop bits", 0, ARG_TYPE_INT},
+ {0, 0, 0, 0, 0, 0}
+};
+
+/* Serial port settings. */
+struct serial_port
+{
+ unsigned short port;
+ unsigned short divisor;
+ unsigned short word_len;
+ unsigned int parity;
+ unsigned short stop_bits;
+};
+
+/* Serial port settings. */
+static struct serial_port serial_settings;
+
+/* Read a byte from a port. */
+static inline unsigned char
+inb (const unsigned short port)
+{
+ unsigned char value;
+
+ asm volatile ("inb %w1, %0" : "=a" (value) : "Nd" (port));
+ asm volatile ("outb %%al, $0x80" : : );
+
+ return value;
+}
+
+/* Write a byte to a port. */
+static inline void
+outb (const unsigned short port, const unsigned char value)
+{
+ asm volatile ("outb %b0, %w1" : : "a" (value), "Nd" (port));
+ asm volatile ("outb %%al, $0x80" : : );
+}
+
+/* Return the port number for the UNITth serial device. */
+static inline unsigned short
+serial_hw_get_port (const unsigned short unit)
+{
+ /* The BIOS data area. */
+ const unsigned short *addr = (const unsigned short *) 0x0400;
+ return addr[unit];
+}
+
+/* Fetch a key. */
+static int
+serial_hw_fetch (void)
+{
+ if (inb (serial_settings.port + UART_LSR) & UART_DATA_READY)
+ return inb (serial_settings.port + UART_RX);
+
+ return -1;
+}
+
+/* Put a chararacter. */
+static void
+serial_hw_put (const int c)
+{
+ unsigned int timeout = 100000;
+
+ /* Wait until the transmitter holding register is empty. */
+ while ((inb (serial_settings.port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
+ {
+ if (--timeout == 0)
+ /* There is something wrong. But what can I do? */
+ return;
+ }
+
+ outb (serial_settings.port + UART_TX, c);
+}
+
+static void
+serial_translate_key_sequence (void)
+{
+ static struct
+ {
+ char key;
+ char ascii;
+ }
+ three_code_table[] =
+ {
+ {'A', 16},
+ {'B', 14},
+ {'C', 6},
+ {'D', 2},
+ {'F', 5},
+ {'H', 1},
+ {'4', 4}
+ };
+
+ static struct
+ {
+ short key;
+ char ascii;
+ }
+ four_code_table[] =
+ {
+ {('1' | ('~' << 8)), 1},
+ {('3' | ('~' << 8)), 4},
+ {('5' | ('~' << 8)), 7},
+ {('6' | ('~' << 8)), 3}
+ };
+
+ /* The buffer must start with "ESC [". */
+ if (*((unsigned short *) input_buf) != ('\e' | ('[' << 8)))
+ return;
+
+ if (npending >= 3)
+ {
+ unsigned int i;
+
+ for (i = 0;
+ i < sizeof (three_code_table) / sizeof (three_code_table[0]);
+ i++)
+ if (three_code_table[i].key == input_buf[2])
+ {
+ input_buf[0] = three_code_table[i].ascii;
+ npending -= 2;
+ grub_memmove (input_buf + 1, input_buf + 3, npending - 1);
+ return;
+ }
+ }
+
+ if (npending >= 4)
+ {
+ unsigned int i;
+ short key = *((short *) (input_buf + 2));
+
+ for (i = 0;
+ i < sizeof (four_code_table) / sizeof (four_code_table[0]);
+ i++)
+ if (four_code_table[i].key == key)
+ {
+ input_buf[0] = four_code_table[i].ascii;
+ npending -= 3;
+ grub_memmove (input_buf + 1, input_buf + 4, npending - 1);
+ return;
+ }
+ }
+}
+
+static int
+fill_input_buf (const int nowait)
+{
+ int i;
+
+ for (i = 0; i < 10000 && npending < sizeof (input_buf); i++)
+ {
+ int c;
+
+ c = serial_hw_fetch ();
+ if (c >= 0)
+ {
+ input_buf[npending++] = c;
+
+ /* Reset the counter to zero, to wait for the same interval. */
+ i = 0;
+ }
+
+ if (nowait)
+ break;
+ }
+
+ /* Translate some key sequences. */
+ serial_translate_key_sequence ();
+
+ return npending;
+}
+
+/* Convert speed to divisor. */
+static unsigned short
+serial_get_divisor (unsigned int speed)
+{
+ unsigned int i;
+
+ /* The structure for speed vs. divisor. */
+ struct divisor
+ {
+ unsigned int speed;
+ unsigned short div;
+ };
+
+ /* The table which lists common configurations. */
+ /* 1843200 / (speed * 16) */
+ static struct divisor divisor_tab[] =
+ {
+ { 2400, 0x0030 },
+ { 4800, 0x0018 },
+ { 9600, 0x000C },
+ { 19200, 0x0006 },
+ { 38400, 0x0003 },
+ { 57600, 0x0002 },
+ { 115200, 0x0001 }
+ };
+
+ /* Set the baud rate. */
+ for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++)
+ if (divisor_tab[i].speed == speed)
+ return divisor_tab[i].div;
+ return 0;
+}
+
+/* The serial version of checkkey. */
+static int
+grub_serial_checkkey (void)
+{
+ if (fill_input_buf (1))
+ return input_buf[0];
+ else
+ return -1;
+}
+
+/* The serial version of getkey. */
+static int
+grub_serial_getkey (void)
+{
+ int c;
+
+ while (! fill_input_buf (0))
+ ;
+
+ c = input_buf[0];
+ grub_memmove (input_buf, input_buf + 1, --npending);
+
+ return c;
+}
+
+/* Initialize a serial device. PORT is the port number for a serial device.
+ SPEED is a DTE-DTE speed which must be one of these: 2400, 4800, 9600,
+ 19200, 38400, 57600 and 115200. WORD_LEN is the word length to be used
+ for the device. Likewise, PARITY is the type of the parity and
+ STOP_BIT_LEN is the length of the stop bit. The possible values for
+ WORD_LEN, PARITY and STOP_BIT_LEN are defined in the header file as
+ macros. */
+static grub_err_t
+serial_hw_init (void)
+{
+ unsigned char status = 0;
+
+ /* Turn off the interupt. */
+ outb (serial_settings.port + UART_IER, 0);
+
+ /* Set DLAB. */
+ outb (serial_settings.port + UART_LCR, UART_DLAB);
+
+ /* Set the baud rate. */
+ outb (serial_settings.port + UART_DLL, serial_settings.divisor & 0xFF);
+ outb (serial_settings.port + UART_DLH, serial_settings.divisor >> 8 );
+
+ /* Set the line status. */
+ status |= (serial_settings.parity
+ | serial_settings.word_len
+ | serial_settings.stop_bits);
+ outb (serial_settings.port + UART_LCR, status);
+
+ /* Enable the FIFO. */
+ outb (serial_settings.port + UART_FCR, UART_ENABLE_FIFO);
+
+ /* Turn on DTR, RTS, and OUT2. */
+ outb (serial_settings.port + UART_MCR, UART_ENABLE_MODEM);
+
+ /* Drain the input buffer. */
+ while (grub_serial_checkkey () != -1)
+ (void) grub_serial_getkey ();
+
+ /* FIXME: should check if the serial terminal was found. */
+
+ return GRUB_ERR_NONE;
+}
+
+/* The serial version of putchar. */
+static void
+grub_serial_putchar (grub_uint32_t c)
+{
+ /* Keep track of the cursor. */
+ if (keep_track)
+ {
+ /* The serial terminal does not have VGA fonts. */
+ if (c > 0x7F)
+ {
+ /* Better than nothing. */
+ switch (c)
+ {
+ case GRUB_TERM_DISP_LEFT:
+ c = '<';
+ break;
+
+ case GRUB_TERM_DISP_UP:
+ c = '^';
+ break;
+
+ case GRUB_TERM_DISP_RIGHT:
+ c = '>';
+ break;
+
+ case GRUB_TERM_DISP_DOWN:
+ c = 'v';
+ break;
+
+ case GRUB_TERM_DISP_HLINE:
+ c = '-';
+ break;
+
+ case GRUB_TERM_DISP_VLINE:
+ c = '|';
+ break;
+
+ case GRUB_TERM_DISP_UL:
+ case GRUB_TERM_DISP_UR:
+ case GRUB_TERM_DISP_LL:
+ case GRUB_TERM_DISP_LR:
+ c = '+';
+ break;
+
+ default:
+ c = '?';
+ break;
+ }
+ }
+
+ switch (c)
+ {
+ case '\a':
+ break;
+
+ case '\b':
+ case 127:
+ if (xpos > 0)
+ xpos--;
+ break;
+
+ case '\n':
+ if (ypos < TEXT_HEIGHT)
+ ypos++;
+ break;
+
+ case '\r':
+ xpos = 0;
+ break;
+
+ default:
+ if (xpos >= TEXT_WIDTH)
+ {
+ grub_putchar ('\r');
+ grub_putchar ('\n');
+ }
+ xpos++;
+ break;
+ }
+ }
+
+ serial_hw_put (c);
+}
+
+static grub_ssize_t
+grub_serial_getcharwidth (grub_uint32_t c __attribute__ ((unused)))
+{
+ return 1;
+}
+
+static grub_uint16_t
+grub_serial_getwh (void)
+{
+ return (TEXT_WIDTH << 8) | TEXT_HEIGHT;
+}
+
+static grub_uint16_t
+grub_serial_getxy (void)
+{
+ return ((xpos << 8) | ypos);
+}
+
+static void
+grub_serial_gotoxy (grub_uint8_t x, grub_uint8_t y)
+{
+ if (x > TEXT_WIDTH || y > TEXT_HEIGHT)
+ {
+ grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid point (%u,%u)", x, y);
+ }
+ else
+ {
+ keep_track = 0;
+ grub_terminfo_gotoxy (x, y);
+ keep_track = 1;
+
+ xpos = x;
+ ypos = y;
+ }
+}
+
+static void
+grub_serial_cls (void)
+{
+ keep_track = 0;
+ grub_terminfo_cls ();
+ keep_track = 1;
+
+ xpos = ypos = 0;
+}
+
+static void
+grub_serial_setcolorstate (const grub_term_color_state state)
+{
+ keep_track = 0;
+ switch (state)
+ {
+ case GRUB_TERM_COLOR_STANDARD:
+ case GRUB_TERM_COLOR_NORMAL:
+ grub_terminfo_reverse_video_off ();
+ break;
+ case GRUB_TERM_COLOR_HIGHLIGHT:
+ grub_terminfo_reverse_video_on ();
+ break;
+ default:
+ break;
+ }
+ keep_track = 1;
+}
+
+static void
+grub_serial_setcolor (grub_uint8_t normal_color __attribute__ ((unused)),
+ grub_uint8_t highlight_color __attribute__ ((unused)))
+{
+ /* FIXME */
+}
+
+static void
+grub_serial_setcursor (const int on)
+{
+ if (on)
+ grub_terminfo_cursor_on ();
+ else
+ grub_terminfo_cursor_off ();
+}
+
+static struct grub_term grub_serial_term =
+{
+ .name = "serial",
+ .init = 0,
+ .fini = 0,
+ .putchar = grub_serial_putchar,
+ .getcharwidth = grub_serial_getcharwidth,
+ .checkkey = grub_serial_checkkey,
+ .getkey = grub_serial_getkey,
+ .getwh = grub_serial_getwh,
+ .getxy = grub_serial_getxy,
+ .gotoxy = grub_serial_gotoxy,
+ .cls = grub_serial_cls,
+ .setcolorstate = grub_serial_setcolorstate,
+ .setcolor = grub_serial_setcolor,
+ .setcursor = grub_serial_setcursor,
+ .flags = 0,
+ .next = 0
+};
+
+\f
+
+static grub_err_t
+grub_cmd_serial (struct grub_arg_list *state,
+ int argc __attribute__ ((unused)),
+ char **args __attribute__ ((unused)))
+{
+ struct serial_port backup_settings = serial_settings;
+ grub_err_t hwiniterr;
+ int arg;
+
+ if (state[0].set)
+ {
+ arg = grub_strtoul (state[0].arg, 0, 0);
+ if (arg >= 0 && arg < 4)
+ serial_settings.port = serial_hw_get_port ((int) arg);
+ else
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad unit number.");
+ }
+
+ if (state[1].set)
+ serial_settings.port = (unsigned short) grub_strtoul (state[1].arg, 0, 0);
+
+ if (state[2].set)
+ {
+ unsigned long speed;
+
+ speed = grub_strtoul (state[2].arg, 0, 0);
+ serial_settings.divisor = serial_get_divisor ((unsigned int) speed);
+ if (serial_settings.divisor == 0)
+ {
+ serial_settings = backup_settings;
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad speed");
+ }
+ }
+
+ if (state[3].set)
+ {
+ if (! grub_strcmp (state[3].arg, "5"))
+ serial_settings.word_len = UART_5BITS_WORD;
+ else if (! grub_strcmp (state[3].arg, "6"))
+ serial_settings.word_len = UART_6BITS_WORD;
+ else if (! grub_strcmp (state[3].arg, "7"))
+ serial_settings.word_len = UART_7BITS_WORD;
+ else if (! grub_strcmp (state[3].arg, "8"))
+ serial_settings.word_len = UART_8BITS_WORD;
+ else
+ {
+ serial_settings = backup_settings;
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad word length");
+ }
+ }
+
+ if (state[4].set)
+ {
+ if (! grub_strcmp (state[4].arg, "no"))
+ serial_settings.parity = UART_NO_PARITY;
+ else if (! grub_strcmp (state[4].arg, "odd"))
+ serial_settings.parity = UART_ODD_PARITY;
+ else if (! grub_strcmp (state[4].arg, "even"))
+ serial_settings.parity = UART_EVEN_PARITY;
+ else
+ {
+ serial_settings = backup_settings;
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad parity");
+ }
+ }
+
+ if (state[5].set)
+ {
+ if (! grub_strcmp (state[5].arg, "1"))
+ serial_settings.stop_bits = UART_1_STOP_BIT;
+ else if (! grub_strcmp (state[5].arg, "2"))
+ serial_settings.stop_bits = UART_2_STOP_BITS;
+ else
+ {
+ serial_settings = backup_settings;
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad number of stop bits");
+ }
+ }
+
+ /* Initialize with new settings. */
+ hwiniterr = serial_hw_init ();
+
+ if (hwiniterr == GRUB_ERR_NONE)
+ {
+ /* Register terminal if not yet registered. */
+ if (registered == 0)
+ {
+ grub_term_register (&grub_serial_term);
+ registered = 1;
+ }
+ }
+ else
+ {
+ /* Initialization with new settings failed. */
+ if (registered == 1)
+ {
+ /* If the terminal is registered, attempt to restore previous
+ settings. */
+ serial_settings = backup_settings;
+ if (serial_hw_init () != GRUB_ERR_NONE)
+ {
+ /* If unable to restore settings, unregister terminal. */
+ grub_term_unregister (&grub_serial_term);
+ registered = 0;
+ }
+ }
+ }
+
+ return hwiniterr;
+}
+
+GRUB_MOD_INIT
+{
+ (void) mod; /* To stop warning. */
+ grub_register_command ("serial", grub_cmd_serial, GRUB_COMMAND_FLAG_BOTH,
+ "serial [OPTIONS...]", "Configure serial port.", options);
+ /* Set default settings. */
+ serial_settings.port = serial_hw_get_port (0);
+ serial_settings.divisor = serial_get_divisor (9600);
+ serial_settings.word_len = UART_8BITS_WORD;
+ serial_settings.parity = UART_NO_PARITY;
+ serial_settings.stop_bits = UART_1_STOP_BIT;
+}
+
+GRUB_MOD_FINI
+{
+ grub_unregister_command ("serial");
+ if (registered == 1) /* Unregister terminal only if registered. */
+ grub_term_unregister (&grub_serial_term);
+}
--- /dev/null
+/* terminfo.c - simple terminfo module */
+/*
+ * GRUB -- GRand Unified Bootloader
+ * Copyright (C) 2003,2004,2005 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.
+ */
+
+/*
+ * This file contains various functions dealing with different
+ * terminal capabilities. For example, vt52 and vt100.
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/normal.h>
+#include <grub/term.h>
+#include <grub/terminfo.h>
+#include <grub/tparm.h>
+
+struct terminfo
+{
+ char *name;
+
+ char *gotoxy;
+ char *cls;
+ char *reverse_video_on;
+ char *reverse_video_off;
+ char *cursor_on;
+ char *cursor_off;
+};
+
+static struct terminfo term;
+
+/* Get current terminfo name. */
+char *
+grub_terminfo_get_current (void)
+{
+ return term.name;
+}
+
+/* Free *PTR and set *PTR to NULL, to prevent double-free. */
+static void
+grub_terminfo_free (char **ptr)
+{
+ grub_free (*ptr);
+ *ptr = 0;
+}
+
+/* Set current terminfo type. */
+grub_err_t
+grub_terminfo_set_current (const char *str)
+{
+ /* TODO
+ * Lookup user specified terminfo type. If found, set term variables
+ * as appropriate. Otherwise return an error.
+ *
+ * How should this be done?
+ * a. A static table included in this module.
+ * - I do not like this idea.
+ * b. A table stored in the configuration directory.
+ * - Users must convert their terminfo settings if we have not already.
+ * c. Look for terminfo files in the configuration directory.
+ * - /usr/share/terminfo is 6.3M on my system.
+ * - /usr/share/terminfo is not on most users boot partition.
+ * + Copying the terminfo files you want to use to the grub
+ * configuration directory is easier then (b).
+ * d. Your idea here.
+ */
+
+ /* Free previously allocated memory. */
+ grub_terminfo_free (&term.name);
+ grub_terminfo_free (&term.gotoxy);
+ grub_terminfo_free (&term.cls);
+ grub_terminfo_free (&term.reverse_video_on);
+ grub_terminfo_free (&term.reverse_video_off);
+ grub_terminfo_free (&term.cursor_on);
+ grub_terminfo_free (&term.cursor_off);
+
+ if (grub_strcmp ("vt100", str) == 0)
+ {
+ term.name = grub_strdup ("vt100");
+ term.gotoxy = grub_strdup ("\e[%i%p1%d;%p2%dH");
+ term.cls = grub_strdup ("\e[H\e[J");
+ term.reverse_video_on = grub_strdup ("\e[7m");
+ term.reverse_video_off = grub_strdup ("\e[m");
+ term.cursor_on = grub_strdup ("\e[?25l");
+ term.cursor_off = grub_strdup ("\e[?25h");
+ return grub_errno;
+ }
+
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "unknown terminfo type.");
+}
+
+/* Wrapper for grub_putchar to write strings. */
+static void
+putstr (const char *str)
+{
+ while (*str)
+ grub_putchar (*str++);
+}
+
+/* Move the cursor to the given position starting with "0". */
+void
+grub_terminfo_gotoxy (grub_uint8_t x, grub_uint8_t y)
+{
+ putstr (grub_terminfo_tparm (term.gotoxy, y, x));
+}
+
+/* Clear the screen. */
+void
+grub_terminfo_cls (void)
+{
+ putstr (grub_terminfo_tparm (term.cls));
+}
+
+/* Set reverse video mode on. */
+void
+grub_terminfo_reverse_video_on (void)
+{
+ putstr (grub_terminfo_tparm (term.reverse_video_on));
+}
+
+/* Set reverse video mode off. */
+void
+grub_terminfo_reverse_video_off (void)
+{
+ putstr (grub_terminfo_tparm (term.reverse_video_off));
+}
+
+/* Show cursor. */
+void
+grub_terminfo_cursor_on (void)
+{
+ putstr (grub_terminfo_tparm (term.cursor_on));
+}
+
+/* Hide cursor. */
+void
+grub_terminfo_cursor_off (void)
+{
+ putstr (grub_terminfo_tparm (term.cursor_off));
+}
+
+/* GRUB Command. */
+
+static grub_err_t
+grub_cmd_terminfo (struct grub_arg_list *state __attribute__ ((unused)),
+ int argc, char **args)
+{
+ if (argc == 0)
+ {
+ grub_printf ("Current terminfo type: %s\n", grub_terminfo_get_current());
+ return GRUB_ERR_NONE;
+ }
+ else if (argc != 1)
+ return grub_error (GRUB_ERR_BAD_ARGUMENT, "too many parameters.");
+ else
+ return grub_terminfo_set_current (args[0]);
+}
+
+GRUB_MOD_INIT
+{
+ (void) mod; /* To stop warning. */
+ grub_register_command ("terminfo", grub_cmd_terminfo, GRUB_COMMAND_FLAG_BOTH,
+ "terminfo [TERM]", "Set terminfo type.", 0);
+ grub_terminfo_set_current ("vt100");
+}
+
+GRUB_MOD_FINI
+{
+ grub_unregister_command ("terminfo");
+}
--- /dev/null
+/****************************************************************************
+ * Copyright (c) 1998-2003,2004,2005 Free Software Foundation, Inc. *
+ * *
+ * Permission is hereby granted, free of charge, to any person obtaining a *
+ * copy of this software and associated documentation files (the *
+ * "Software"), to deal in the Software without restriction, including *
+ * without limitation the rights to use, copy, modify, merge, publish, *
+ * distribute, distribute with modifications, sublicense, and/or sell *
+ * copies of the Software, and to permit persons to whom the Software is *
+ * furnished to do so, subject to the following conditions: *
+ * *
+ * The above copyright notice and this permission notice shall be included *
+ * in all copies or substantial portions of the Software. *
+ * *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
+ * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
+ * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
+ * *
+ * Except as contained in this notice, the name(s) of the above copyright *
+ * holders shall not be used in advertising or otherwise to promote the *
+ * sale, use or other dealings in this Software without prior written *
+ * authorization. *
+ ****************************************************************************/
+
+/**********************************************************************
+ * This code is a modification of lib_tparm.c found in ncurses-5.2. The
+ * modification are for use in grub by replacing all libc function through
+ * special grub functions. This also meant to delete all dynamic memory
+ * allocation and replace it by a number of fixed buffers.
+ *
+ * Modifications by Tilmann Bubeck <t.bubeck@reinform.de> 2002
+ *
+ * Resync with ncurses-5.4 by Omniflux <omniflux+devel@omniflux.com> 2005
+ **********************************************************************/
+
+/****************************************************************************
+ * Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995 *
+ * and: Eric S. Raymond <esr@snark.thyrsus.com> *
+ * and: Thomas E. Dickey, 1996 on *
+ ****************************************************************************/
+
+/*
+ * tparm.c
+ *
+ */
+
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/tparm.h>
+
+/*
+ * Common/troublesome character definitions
+ */
+typedef char grub_bool_t;
+#ifndef FALSE
+# define FALSE (0)
+#endif
+#ifndef TRUE
+# define TRUE (!FALSE)
+#endif
+
+#define NUM_PARM 9
+#define NUM_VARS 26
+#define STACKSIZE 20
+#define MAX_FORMAT_LEN 256
+
+#define max(a,b) ((a) > (b) ? (a) : (b))
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
+#define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
+
+#define UChar(c) ((unsigned char)(c))
+
+//MODULE_ID("$Id$")
+
+/*
+ * char *
+ * tparm(string, ...)
+ *
+ * Substitute the given parameters into the given string by the following
+ * rules (taken from terminfo(5)):
+ *
+ * Cursor addressing and other strings requiring parame-
+ * ters in the terminal are described by a parameterized string
+ * capability, with like escapes %x in it. For example, to
+ * address the cursor, the cup capability is given, using two
+ * parameters: the row and column to address to. (Rows and
+ * columns are numbered from zero and refer to the physical
+ * screen visible to the user, not to any unseen memory.) If
+ * the terminal has memory relative cursor addressing, that can
+ * be indicated by
+ *
+ * The parameter mechanism uses a stack and special %
+ * codes to manipulate it. Typically a sequence will push one
+ * of the parameters onto the stack and then print it in some
+ * format. Often more complex operations are necessary.
+ *
+ * The % encodings have the following meanings:
+ *
+ * %% outputs `%'
+ * %c print pop() like %c in printf()
+ * %s print pop() like %s in printf()
+ * %[[:]flags][width[.precision]][doxXs]
+ * as in printf, flags are [-+#] and space
+ * The ':' is used to avoid making %+ or %-
+ * patterns (see below).
+ *
+ * %p[1-9] push ith parm
+ * %P[a-z] set dynamic variable [a-z] to pop()
+ * %g[a-z] get dynamic variable [a-z] and push it
+ * %P[A-Z] set static variable [A-Z] to pop()
+ * %g[A-Z] get static variable [A-Z] and push it
+ * %l push strlen(pop)
+ * %'c' push char constant c
+ * %{nn} push integer constant nn
+ *
+ * %+ %- %* %/ %m
+ * arithmetic (%m is mod): push(pop() op pop())
+ * %& %| %^ bit operations: push(pop() op pop())
+ * %= %> %< logical operations: push(pop() op pop())
+ * %A %O logical and & or operations for conditionals
+ * %! %~ unary operations push(op pop())
+ * %i add 1 to first two parms (for ANSI terminals)
+ *
+ * %? expr %t thenpart %e elsepart %;
+ * if-then-else, %e elsepart is optional.
+ * else-if's are possible ala Algol 68:
+ * %? c1 %t b1 %e c2 %t b2 %e c3 %t b3 %e c4 %t b4 %e b5 %;
+ *
+ * For those of the above operators which are binary and not commutative,
+ * the stack works in the usual way, with
+ * %gx %gy %m
+ * resulting in x mod y, not the reverse.
+ */
+
+typedef struct {
+ union {
+ int num;
+ char *str;
+ } data;
+ grub_bool_t num_type;
+} stack_frame;
+
+static stack_frame stack[STACKSIZE];
+static int stack_ptr;
+static const char *tparam_base = "";
+
+static char *out_buff;
+static grub_size_t out_size;
+static grub_size_t out_used;
+
+static char *fmt_buff;
+static grub_size_t fmt_size;
+
+static inline void
+get_space(grub_size_t need)
+{
+ need += out_used;
+ if (need > out_size) {
+ out_size = need * 2;
+ out_buff = grub_realloc(out_buff, out_size*sizeof(char));
+ if (out_buff == 0)
+ // FIX ME! OOM, what now?
+ ;
+ }
+}
+
+static inline void
+save_text(const char *fmt, const char *s, int len)
+{
+ grub_size_t s_len = grub_strlen(s);
+ if (len > (int) s_len)
+ s_len = len;
+
+ get_space(s_len + 1);
+
+ (void) grub_sprintf(out_buff + out_used, fmt, s);
+ out_used += grub_strlen(out_buff + out_used);
+}
+
+static inline void
+save_number(const char *fmt, int number, int len)
+{
+ if (len < 30)
+ len = 30; /* actually log10(MAX_INT)+1 */
+
+ get_space((unsigned) len + 1);
+
+ (void) grub_sprintf(out_buff + out_used, fmt, number);
+ out_used += grub_strlen(out_buff + out_used);
+}
+
+static inline void
+save_char(int c)
+{
+ if (c == 0)
+ c = 0200;
+ get_space(1);
+ out_buff[out_used++] = c;
+}
+
+static inline void
+npush(int x)
+{
+ if (stack_ptr < STACKSIZE) {
+ stack[stack_ptr].num_type = TRUE;
+ stack[stack_ptr].data.num = x;
+ stack_ptr++;
+ }
+}
+
+static inline int
+npop(void)
+{
+ int result = 0;
+ if (stack_ptr > 0) {
+ stack_ptr--;
+ if (stack[stack_ptr].num_type)
+ result = stack[stack_ptr].data.num;
+ }
+ return result;
+}
+
+static inline void
+spush(char *x)
+{
+ if (stack_ptr < STACKSIZE) {
+ stack[stack_ptr].num_type = FALSE;
+ stack[stack_ptr].data.str = x;
+ stack_ptr++;
+ }
+}
+
+static inline char *
+spop(void)
+{
+ static char dummy[] = ""; /* avoid const-cast */
+ char *result = dummy;
+ if (stack_ptr > 0) {
+ stack_ptr--;
+ if (!stack[stack_ptr].num_type && stack[stack_ptr].data.str != 0)
+ result = stack[stack_ptr].data.str;
+ }
+ return result;
+}
+
+static inline const char *
+parse_format(const char *s, char *format, int *len)
+{
+ *len = 0;
+ if (format != 0) {
+ grub_bool_t done = FALSE;
+ grub_bool_t allowminus = FALSE;
+ grub_bool_t dot = FALSE;
+ grub_bool_t err = FALSE;
+ char *fmt = format;
+ int my_width = 0;
+ int my_prec = 0;
+ int value = 0;
+
+ *len = 0;
+ *format++ = '%';
+ while (*s != '\0' && !done) {
+ switch (*s) {
+ case 'c': /* FALLTHRU */
+ case 'd': /* FALLTHRU */
+ case 'o': /* FALLTHRU */
+ case 'x': /* FALLTHRU */
+ case 'X': /* FALLTHRU */
+ case 's':
+ *format++ = *s;
+ done = TRUE;
+ break;
+ case '.':
+ *format++ = *s++;
+ if (dot) {
+ err = TRUE;
+ } else { /* value before '.' is the width */
+ dot = TRUE;
+ my_width = value;
+ }
+ value = 0;
+ break;
+ case '#':
+ *format++ = *s++;
+ break;
+ case ' ':
+ *format++ = *s++;
+ break;
+ case ':':
+ s++;
+ allowminus = TRUE;
+ break;
+ case '-':
+ if (allowminus) {
+ *format++ = *s++;
+ } else {
+ done = TRUE;
+ }
+ break;
+ default:
+ if (isdigit(UChar(*s))) {
+ value = (value * 10) + (*s - '0');
+ if (value > 10000)
+ err = TRUE;
+ *format++ = *s++;
+ } else {
+ done = TRUE;
+ }
+ }
+ }
+
+ /*
+ * If we found an error, ignore (and remove) the flags.
+ */
+ if (err) {
+ my_width = my_prec = value = 0;
+ format = fmt;
+ *format++ = '%';
+ *format++ = *s;
+ }
+
+ /*
+ * Any value after '.' is the precision. If we did not see '.', then
+ * the value is the width.
+ */
+ if (dot)
+ my_prec = value;
+ else
+ my_width = value;
+
+ *format = '\0';
+ /* return maximum string length in print */
+ *len = (my_width > my_prec) ? my_width : my_prec;
+ }
+ return s;
+}
+
+/*
+ * Analyze the string to see how many parameters we need from the varargs list,
+ * and what their types are. We will only accept string parameters if they
+ * appear as a %l or %s format following an explicit parameter reference (e.g.,
+ * %p2%s). All other parameters are numbers.
+ *
+ * 'number' counts coarsely the number of pop's we see in the string, and
+ * 'popcount' shows the highest parameter number in the string. We would like
+ * to simply use the latter count, but if we are reading termcap strings, there
+ * may be cases that we cannot see the explicit parameter numbers.
+ */
+static inline int
+analyze(const char *string, char *p_is_s[NUM_PARM], int *popcount)
+{
+ grub_size_t len2;
+ int i;
+ int lastpop = -1;
+ int len;
+ int number = 0;
+ const char *cp = string;
+ static char dummy[] = "";
+
+ if (cp == 0)
+ return 0;
+
+ if ((len2 = grub_strlen(cp)) > fmt_size) {
+ fmt_size = len2 + fmt_size + 2;
+ if ((fmt_buff = grub_realloc(fmt_buff, fmt_size*sizeof(char))) == 0)
+ return 0;
+ }
+
+ grub_memset(p_is_s, 0, sizeof(p_is_s[0]) * NUM_PARM);
+ *popcount = 0;
+
+ while ((cp - string) < (int) len2) {
+ if (*cp == '%') {
+ cp++;
+ cp = parse_format(cp, fmt_buff, &len);
+ switch (*cp) {
+ default:
+ break;
+
+ case 'd': /* FALLTHRU */
+ case 'o': /* FALLTHRU */
+ case 'x': /* FALLTHRU */
+ case 'X': /* FALLTHRU */
+ case 'c': /* FALLTHRU */
+ if (lastpop <= 0)
+ number++;
+ lastpop = -1;
+ break;
+
+ case 'l':
+ case 's':
+ if (lastpop > 0)
+ p_is_s[lastpop - 1] = dummy;
+ ++number;
+ break;
+
+ case 'p':
+ cp++;
+ i = (UChar(*cp) - '0');
+ if (i >= 0 && i <= NUM_PARM) {
+ lastpop = i;
+ if (lastpop > *popcount)
+ *popcount = lastpop;
+ }
+ break;
+
+ case 'P':
+ ++number;
+ ++cp;
+ break;
+
+ case 'g':
+ cp++;
+ break;
+
+ case '\'':
+ cp += 2;
+ lastpop = -1;
+ break;
+
+ case '{':
+ cp++;
+ while (isdigit(UChar(*cp))) {
+ cp++;
+ }
+ break;
+
+ case '+':
+ case '-':
+ case '*':
+ case '/':
+ case 'm':
+ case 'A':
+ case 'O':
+ case '&':
+ case '|':
+ case '^':
+ case '=':
+ case '<':
+ case '>':
+ lastpop = -1;
+ number += 2;
+ break;
+
+ case '!':
+ case '~':
+ lastpop = -1;
+ ++number;
+ break;
+
+ case 'i':
+ /* will add 1 to first (usually two) parameters */
+ break;
+ }
+ }
+ if (*cp != '\0')
+ cp++;
+ }
+
+ if (number > NUM_PARM)
+ number = NUM_PARM;
+ return number;
+}
+
+static inline char *
+tparam_internal(const char *string, va_list ap)
+{
+ char *p_is_s[NUM_PARM];
+ long param[NUM_PARM];
+ int popcount;
+ int number;
+ int len;
+ int level;
+ int x, y;
+ int i;
+ const char *cp = string;
+ grub_size_t len2;
+ static int dynamic_var[NUM_VARS];
+ static int static_vars[NUM_VARS];
+
+ if (cp == 0)
+ return 0;
+
+ out_used = out_size = fmt_size = 0;
+
+ len2 = (int) grub_strlen(cp);
+
+ /*
+ * Find the highest parameter-number referred to in the format string.
+ * Use this value to limit the number of arguments copied from the
+ * variable-length argument list.
+ */
+ number = analyze(cp, p_is_s, &popcount);
+ if (fmt_buff == 0)
+ return 0;
+
+ for (i = 0; i < max(popcount, number); i++) {
+ /*
+ * A few caps (such as plab_norm) have string-valued parms.
+ * We'll have to assume that the caller knows the difference, since
+ * a char* and an int may not be the same size on the stack.
+ */
+ if (p_is_s[i] != 0) {
+ p_is_s[i] = va_arg(ap, char *);
+ } else {
+ param[i] = va_arg(ap, long int);
+ }
+ }
+
+ /*
+ * This is a termcap compatibility hack. If there are no explicit pop
+ * operations in the string, load the stack in such a way that
+ * successive pops will grab successive parameters. That will make
+ * the expansion of (for example) \E[%d;%dH work correctly in termcap
+ * style, which means tparam() will expand termcap strings OK.
+ */
+ stack_ptr = 0;
+ if (popcount == 0) {
+ popcount = number;
+ for (i = number - 1; i >= 0; i--)
+ npush(param[i]);
+ }
+
+ while ((cp - string) < (int) len2) {
+ if (*cp != '%') {
+ save_char(UChar(*cp));
+ } else {
+ tparam_base = cp++;
+ cp = parse_format(cp, fmt_buff, &len);
+ switch (*cp) {
+ default:
+ break;
+ case '%':
+ save_char('%');
+ break;
+
+ case 'd': /* FALLTHRU */
+ case 'o': /* FALLTHRU */
+ case 'x': /* FALLTHRU */
+ case 'X': /* FALLTHRU */
+ save_number(fmt_buff, npop(), len);
+ break;
+
+ case 'c': /* FALLTHRU */
+ save_char(npop());
+ break;
+
+ case 'l':
+ save_number("%d", (int) grub_strlen(spop()), 0);
+ break;
+
+ case 's':
+ save_text(fmt_buff, spop(), len);
+ break;
+
+ case 'p':
+ cp++;
+ i = (UChar(*cp) - '1');
+ if (i >= 0 && i < NUM_PARM) {
+ if (p_is_s[i])
+ spush(p_is_s[i]);
+ else
+ npush(param[i]);
+ }
+ break;
+
+ case 'P':
+ cp++;
+ if (isUPPER(*cp)) {
+ i = (UChar(*cp) - 'A');
+ static_vars[i] = npop();
+ } else if (isLOWER(*cp)) {
+ i = (UChar(*cp) - 'a');
+ dynamic_var[i] = npop();
+ }
+ break;
+
+ case 'g':
+ cp++;
+ if (isUPPER(*cp)) {
+ i = (UChar(*cp) - 'A');
+ npush(static_vars[i]);
+ } else if (isLOWER(*cp)) {
+ i = (UChar(*cp) - 'a');
+ npush(dynamic_var[i]);
+ }
+ break;
+
+ case '\'':
+ cp++;
+ npush(UChar(*cp));
+ cp++;
+ break;
+
+ case '{':
+ number = 0;
+ cp++;
+ while (isdigit(UChar(*cp))) {
+ number = (number * 10) + (UChar(*cp) - '0');
+ cp++;
+ }
+ npush(number);
+ break;
+
+ case '+':
+ npush(npop() + npop());
+ break;
+
+ case '-':
+ y = npop();
+ x = npop();
+ npush(x - y);
+ break;
+
+ case '*':
+ npush(npop() * npop());
+ break;
+
+ case '/':
+ y = npop();
+ x = npop();
+ npush(y ? (x / y) : 0);
+ break;
+
+ case 'm':
+ y = npop();
+ x = npop();
+ npush(y ? (x % y) : 0);
+ break;
+
+ case 'A':
+ npush(npop() && npop());
+ break;
+
+ case 'O':
+ npush(npop() || npop());
+ break;
+
+ case '&':
+ npush(npop() & npop());
+ break;
+
+ case '|':
+ npush(npop() | npop());
+ break;
+
+ case '^':
+ npush(npop() ^ npop());
+ break;
+
+ case '=':
+ y = npop();
+ x = npop();
+ npush(x == y);
+ break;
+
+ case '<':
+ y = npop();
+ x = npop();
+ npush(x < y);
+ break;
+
+ case '>':
+ y = npop();
+ x = npop();
+ npush(x > y);
+ break;
+
+ case '!':
+ npush(!npop());
+ break;
+
+ case '~':
+ npush(~npop());
+ break;
+
+ case 'i':
+ if (p_is_s[0] == 0)
+ param[0]++;
+ if (p_is_s[1] == 0)
+ param[1]++;
+ break;
+
+ case '?':
+ break;
+
+ case 't':
+ x = npop();
+ if (!x) {
+ /* scan forward for %e or %; at level zero */
+ cp++;
+ level = 0;
+ while (*cp) {
+ if (*cp == '%') {
+ cp++;
+ if (*cp == '?')
+ level++;
+ else if (*cp == ';') {
+ if (level > 0)
+ level--;
+ else
+ break;
+ } else if (*cp == 'e' && level == 0)
+ break;
+ }
+
+ if (*cp)
+ cp++;
+ }
+ }
+ break;
+
+ case 'e':
+ /* scan forward for a %; at level zero */
+ cp++;
+ level = 0;
+ while (*cp) {
+ if (*cp == '%') {
+ cp++;
+ if (*cp == '?')
+ level++;
+ else if (*cp == ';') {
+ if (level > 0)
+ level--;
+ else
+ break;
+ }
+ }
+
+ if (*cp)
+ cp++;
+ }
+ break;
+
+ case ';':
+ break;
+
+ } /* endswitch (*cp) */
+ } /* endelse (*cp == '%') */
+
+ if (*cp == '\0')
+ break;
+
+ cp++;
+ } /* endwhile (*cp) */
+
+ get_space(1);
+ out_buff[out_used] = '\0';
+
+ return (out_buff);
+}
+
+char *
+grub_terminfo_tparm (const char *string, ...)
+{
+ va_list ap;
+ char *result;
+
+ va_start (ap, string);
+ result = tparam_internal (string, ap);
+ va_end (ap);
+ return result;
+}