grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

x86 serial support


From: Omniflux
Subject: x86 serial support
Date: Mon, 21 Feb 2005 05:38:05 -0700
User-agent: Mozilla Thunderbird 1.0 (Windows/20041206)

Here is a patch to add serial support to x86.

It is a porting of grub-legacy's serial support, copies of grub-legacy's tparms.{c,h} and terminfo.{c.h} and modifications based on grub2's term/i386/pc/vga.c, disk/loopback.c (to add a grub command), and util/console.c

It still needs a lot of work (it does not even let you specify serial port settings yet), but I don't want to spend any more time on it if it is not going to be used.

If this is headed in the right direction, and likely to be accepted for merging with a little more work, I'll keep working on it.

Please let me know.

--
Omniflux
diff -uNr grub2/conf/i386-pc.mk grub2.newserial/conf/i386-pc.mk
--- grub2/conf/i386-pc.mk       2005-02-19 20:56:06.000000000 +0000
+++ grub2.newserial/conf/i386-pc.mk     2005-02-21 12:20:18.044758032 +0000
@@ -253,7 +253,7 @@
        file.h fs.h kernel.h loader.h misc.h mm.h net.h rescue.h symbol.h \
        term.h types.h machine/biosdisk.h machine/boot.h \
        machine/console.h machine/init.h machine/memory.h \
-       machine/loader.h partition.h pc_partition.h machine/time.h machine/vga.h
+       machine/loader.h partition.h pc_partition.h machine/time.h 
machine/vga.h machine/serial.h
 kernel_img_CFLAGS = $(COMMON_CFLAGS)
 kernel_img_ASFLAGS = $(COMMON_ASFLAGS)
 kernel_img_LDFLAGS = -nostdlib -Wl,-N,-Ttext,8200
@@ -902,7 +902,7 @@
 pkgdata_MODULES = _chain.mod _linux.mod linux.mod fat.mod ufs.mod ext2.mod 
minix.mod \
        hfs.mod jfs.mod normal.mod hello.mod vga.mod font.mod _multiboot.mod 
ls.mod \
        boot.mod cmp.mod cat.mod terminal.mod fshelp.mod chain.mod 
multiboot.mod \
-       amiga.mod apple.mod pc.mod loopback.mod reboot.mod halt.mod help.mod
+       amiga.mod apple.mod pc.mod loopback.mod reboot.mod halt.mod help.mod 
serial.mod
 
 # For _chain.mod.
 _chain_mod_SOURCES = loader/i386/pc/chainloader.c
@@ -1817,6 +1817,51 @@
 
 vga_mod_CFLAGS = $(COMMON_CFLAGS)
 
+# For serial.mod.
+serial_mod_SOURCES = term/i386/pc/serial.c term/tparm.c term/terminfo.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 
serial_mod-term_i386_pc_terminfo.o serial_mod-term_i386_pc_tparm.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 $@
+
+serial_mod-term_i386_pc_terminfo.o: term/terminfo.c
+       $(CC) -Iterm -I$(srcdir)/term $(CPPFLAGS) $(CFLAGS) 
$(serial_mod_CFLAGS) -c -o $@ $<
+
+serial_mod-term_i386_pc_tparm.o: term/tparm.c
+       $(CC) -Iterm -I$(srcdir)/term $(CPPFLAGS) $(CFLAGS) 
$(serial_mod_CFLAGS) -c -o $@ $<
+
+-include serial_mod-term_i386_pc_serial.d
+
+serial_mod_CFLAGS = $(COMMON_CFLAGS)
+
 # For font.mod.
 font_mod_SOURCES = font/manager.c
 CLEANFILES += font.mod mod-font.o mod-font.c pre-font.o 
font_mod-font_manager.o def-font.lst und-font.lst
diff -uNr grub2/conf/i386-pc.rmk grub2.newserial/conf/i386-pc.rmk
--- grub2/conf/i386-pc.rmk      2005-02-19 20:56:06.000000000 +0000
+++ grub2.newserial/conf/i386-pc.rmk    2005-02-21 12:21:03.387864832 +0000
@@ -33,7 +33,8 @@
        file.h fs.h kernel.h loader.h misc.h mm.h net.h rescue.h symbol.h \
        term.h types.h machine/biosdisk.h machine/boot.h \
        machine/console.h machine/init.h machine/memory.h \
-       machine/loader.h partition.h pc_partition.h machine/time.h machine/vga.h
+       machine/loader.h partition.h pc_partition.h machine/time.h 
machine/vga.h machine/serial.h
+
 kernel_img_CFLAGS = $(COMMON_CFLAGS)
 kernel_img_ASFLAGS = $(COMMON_ASFLAGS)
 kernel_img_LDFLAGS = -nostdlib -Wl,-N,-Ttext,8200
@@ -187,6 +188,10 @@
 vga_mod_SOURCES = term/i386/pc/vga.c
 vga_mod_CFLAGS = $(COMMON_CFLAGS)
 
+# For serial.mod.
+serial_mod_SOURCES = term/i386/pc/serial.c term/tparm.c term/terminfo.c
+serial_mod_CFLAGS = $(COMMON_CFLAGS)
+
 # For font.mod.
 font_mod_SOURCES = font/manager.c
 font_mod_CFLAGS = $(COMMON_CFLAGS)
diff -uNr grub2/include/grub/i386/pc/serial.h 
grub2.newserial/include/grub/i386/pc/serial.h
--- grub2/include/grub/i386/pc/serial.h 1970-01-01 00:00:00.000000000 +0000
+++ grub2.newserial/include/grub/i386/pc/serial.h       2005-02-20 
17:43:42.000000000 +0000
@@ -0,0 +1,85 @@
+/* serial.h - serial device interface */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000,2001,2002  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
+
+
+/* Function prototypes.  */
+
+/* Return the port number for the UNITth serial device.  */
+unsigned short serial_hw_get_port (int unit);
+
+/* Initialize a serial device.  */
+int serial_hw_init (void);
+
+int fill_input_buf (int nowait);
+
+int grub_serial_getkey (void);
+
+int grub_serial_checkkey (void);
+
+void grub_setup_defaults (void);
+
+#endif /* ! GRUB_SERIAL_MACHINE_HEADER */
diff -uNr grub2/include/grub/terminfo.h grub2.newserial/include/grub/terminfo.h
--- grub2/include/grub/terminfo.h       1970-01-01 00:00:00.000000000 +0000
+++ grub2.newserial/include/grub/terminfo.h     2005-02-20 17:45:33.000000000 
+0000
@@ -0,0 +1,53 @@
+/* terminfo.h - read a terminfo entry from the command line */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2003,2004  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_TERMCAP_HEADER
+#define GRUB_TERMCAP_HEADER    1
+
+#define TERMINFO_LEN 40
+
+typedef struct terminfo
+{
+  char name[TERMINFO_LEN];
+  char cursor_address[TERMINFO_LEN];
+  char clear_screen[TERMINFO_LEN];
+  char enter_standout_mode[TERMINFO_LEN];
+  char exit_standout_mode[TERMINFO_LEN];
+}
+terminfo;
+
+
+/* Function prototypes.  */
+char *ti_escape_memory (const char *in, const char *end);
+char *ti_escape_string (const char *in);
+char *ti_unescape_memory (const char *in, const char *end);
+char *ti_unescape_string (const char *in);
+
+void ti_set_term (const struct terminfo *new);
+void ti_get_term (struct terminfo *copy);
+
+void ti_cursor_address (int x, int y);
+void ti_clear_screen (void);
+void ti_enter_standout_mode (void);
+void ti_exit_standout_mode (void);
+void ti_cursor_on (void);
+void ti_cursor_off (void);
+
+#endif /* ! GRUB_TERMCAP_HEADER */
diff -uNr grub2/include/grub/tparm.h grub2.newserial/include/grub/tparm.h
--- grub2/include/grub/tparm.h  1970-01-01 00:00:00.000000000 +0000
+++ grub2.newserial/include/grub/tparm.h        2005-02-20 17:46:09.000000000 
+0000
@@ -0,0 +1,28 @@
+/* tparm.h - parameter formatting of terminfo */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002  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_tparm (const char *string, ...);
+
+#endif /* ! GRUB_TERMCAP_HEADER */
diff -uNr grub2/term/i386/pc/serial.c grub2.newserial/term/i386/pc/serial.c
--- grub2/term/i386/pc/serial.c 1970-01-01 00:00:00.000000000 +0000
+++ grub2.newserial/term/i386/pc/serial.c       2005-02-20 18:00:43.000000000 
+0000
@@ -0,0 +1,592 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000,2001,2002,2003,2004  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 DEBUG_SERIAL   0
+
+#define TEXT_WIDTH     80
+#define TEXT_HEIGHT    25
+
+
+static grub_dl_t my_mod;
+static unsigned xpos, ypos;
+static int cursor_state = 1;
+static int keep_track = 1;
+
+/* An input buffer.  */
+static char input_buf[8];
+static unsigned int npending = 0;
+
+/* Argument options.  */
+static const struct grub_arg_option options[] =
+{
+  {"port",    'p', 0, "Set the serial port",             0, ARG_TYPE_INT},
+  {"address", 'a', 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 int speed;
+  int word_len;
+  int parity;
+  int stop_bit_len;
+};
+
+/* The structure for speed vs. divisor.  */
+struct divisor
+{
+  unsigned int speed;
+  unsigned short div;
+};
+
+/* Store the serial port settings.  */
+static struct serial_port serial_settings;
+
+/* Store the port number of a serial unit.  */
+static unsigned short serial_hw_port = 0;
+
+/* 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 },
+};
+
+static grub_err_t
+grub_cmd_serial (struct grub_arg_list *state __attribute__ ((unused)),
+                   int argc, char **args)
+{
+  unsigned short port;
+  unsigned int speed;
+  int word_len, parity, stop_bit_len;
+
+  if (serial_settings.port == 0)
+    grub_setup_defaults ();
+
+  /* Load current settings.  */
+  port = serial_settings.port;
+  speed = serial_settings.speed;
+  word_len = serial_settings.word_len;
+  parity = serial_settings.parity;
+  stop_bit_len = serial_settings.stop_bit_len;
+
+  /* TODO parse cmd line parameters and validate.  */
+
+  /* Save current settings.  */
+  serial_settings.port         = port;
+  serial_settings.speed        = speed;
+  serial_settings.word_len     = word_len;
+  serial_settings.parity       = parity;
+  serial_settings.stop_bit_len = stop_bit_len;
+
+  serial_hw_init ();
+
+  return GRUB_ERR_NONE;
+}
+
+/* Read a byte from a port.  */
+static inline unsigned char
+inb (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 (unsigned short port, unsigned char value)
+{
+  asm volatile ("outb   %b0, %w1" : : "a" (value), "Nd" (port));
+  asm volatile ("outb   %%al, $0x80" : : );
+}
+
+/* Fetch a key.  */
+static int
+serial_hw_fetch (void)
+{
+  if (inb (serial_hw_port + UART_LSR) & UART_DATA_READY)
+    return inb (serial_hw_port + UART_RX);
+
+  return -1;
+}
+
+/* Put a chararacter.  */
+static void
+serial_hw_put (int c)
+{
+  int timeout = 100000;
+
+  /* Wait until the transmitter holding register is empty.  */
+  while ((inb (serial_hw_port + UART_LSR) & UART_EMPTY_TRANSMITTER) == 0)
+    {
+      if (--timeout == 0)
+        /* There is something wrong. But what can I do?  */
+        return;
+    }
+
+  outb (serial_hw_port + UART_TX, c);
+}
+
+/* Return the port number for the UNITth serial device.  */
+unsigned short
+serial_hw_get_port (int unit)
+{
+  /* The BIOS data area.  */
+  const unsigned short *addr = (const unsigned short *) 0x0400;
+
+  return addr[unit];
+}
+
+/* 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.  */
+int
+serial_hw_init (void)
+{
+  unsigned int i;
+  unsigned short port;
+  unsigned short div = 0;
+  unsigned char status = 0;
+
+  /* Select the port.  */
+  port = serial_settings.port;
+
+  /* Turn off the interupt.  */
+  outb (port + UART_IER, 0);
+
+  /* Set DLAB.  */
+  outb (port + UART_LCR, UART_DLAB);
+
+  /* Set the baud rate.  */
+  for (i = 0; i < sizeof (divisor_tab) / sizeof (divisor_tab[0]); i++)
+    if (divisor_tab[i].speed == serial_settings.speed)
+    {
+      div = divisor_tab[i].div;
+      break;
+    }
+
+  if (div == 0)
+    return 0;
+
+  outb (port + UART_DLL, div & 0xFF);
+  outb (port + UART_DLH, div >> 8 );
+
+  /* Set the line status.  */
+  status |= serial_settings.parity
+          | serial_settings.word_len
+          | serial_settings.stop_bit_len;
+  outb (port + UART_LCR, status);
+
+  /* Enable the FIFO.  */
+  outb (port + UART_FCR, UART_ENABLE_FIFO);
+
+  /* Turn on DTR, RTS, and OUT2.  */
+  outb (port + UART_MCR, UART_ENABLE_MODEM);
+
+  /* Store the port number.  */
+  serial_hw_port = port;
+
+  /* Drain the input buffer.  */
+  while (grub_serial_checkkey () != -1)
+    (void) grub_serial_getkey ();
+
+  cursor_state = 1;
+
+  /*  FIXME: should check if the serial terminal was found.  */
+
+  return 1;
+}
+
+static void
+serial_translate_key_sequence (void)
+{
+  const struct
+  {
+    char key;
+    char ascii;
+  }
+  three_code_table[] =
+  {
+    {'A', 16},
+    {'B', 14},
+    {'C', 6},
+    {'D', 2},
+    {'F', 5},
+    {'H', 1},
+    {'4', 4}
+  };
+
+  const 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;
+      }
+  }
+}
+
+int
+fill_input_buf (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;
+}
+
+/* Setup serial port defaults.  */
+void
+grub_setup_defaults (void)
+{
+  serial_settings.port         = serial_hw_get_port (0);
+  serial_settings.speed        = 9600;
+  serial_settings.word_len     = UART_8BITS_WORD;
+  serial_settings.parity       = UART_NO_PARITY;
+  serial_settings.stop_bit_len = UART_1_STOP_BIT;
+}
+
+static grub_err_t
+grub_serial_init (void)
+{
+  if (serial_settings.port == 0)
+  {
+    grub_setup_defaults ();
+    serial_hw_init ();
+  }
+
+  return GRUB_ERR_NONE;
+}
+
+/* The serial version of getkey.  */
+int
+grub_serial_getkey (void)
+{
+  int c;
+
+  while (! fill_input_buf (0))
+    ;
+
+  c = input_buf[0];
+  npending--;
+  grub_memmove (input_buf, input_buf + 1, npending);
+
+  return c;
+}
+
+/* The serial version of checkkey.  */
+int
+grub_serial_checkkey (void)
+{
+  if (fill_input_buf (1))
+    return input_buf[0];
+
+  return -1;
+}
+
+/* The serial version of putchar.  */
+static void
+grub_serial_putchar (grub_uint32_t c)
+{
+#if DEBUG_SERIAL
+  static int show = 1;
+#endif
+
+  /* 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:
+          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);
+
+#if DEBUG_SERIAL
+  if (show)
+    {
+      grub_uint16_t pos = grub_getxy ();
+
+      show = 0;
+      grub_gotoxy (0, 0);
+      grub_printf ("[%u:%u]", (unsigned) (pos >> 8), (unsigned) (pos & 0xff));
+      grub_gotoxy (pos >> 8, pos & 0xff);
+      show = 1;
+    }
+#endif
+}
+
+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)",
+                  (unsigned) x, (unsigned) y);
+      return;
+    }
+
+  keep_track = 0;
+  ti_cursor_address (x,y);
+  keep_track = 1;
+
+  xpos = x;
+  ypos = y;
+}
+
+static void
+grub_serial_cls (void)
+{
+  keep_track = 0;
+  ti_clear_screen ();
+  keep_track = 1;
+
+  xpos = ypos = 0;
+}
+
+static void
+grub_serial_setcolorstate (grub_term_color_state state)
+{
+  keep_track = 0;
+  switch (state)
+    {
+    case GRUB_TERM_COLOR_STANDARD:
+    case GRUB_TERM_COLOR_NORMAL:
+      ti_exit_standout_mode ();
+      break;
+    case GRUB_TERM_COLOR_HIGHLIGHT:
+      ti_enter_standout_mode ();
+      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 (int new_state)
+{ 
+  if (cursor_state != new_state)
+    {
+      if (cursor_state)
+        ti_cursor_off ();
+      else
+        ti_cursor_on ();
+      cursor_state = new_state;
+    }
+}
+
+static struct grub_term grub_serial_term =
+{
+  .name = "serial",
+  .init = grub_serial_init,
+  .fini = 0,
+  .putchar = grub_serial_putchar,
+  .checkkey = grub_serial_checkkey,
+  .getkey = grub_serial_getkey,
+  .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
+};
+
+
+
+GRUB_MOD_INIT
+{
+  my_mod = mod;
+  grub_register_command ("serial", grub_cmd_serial, GRUB_COMMAND_FLAG_BOTH,
+                         "serial [OPTIONS...]", "Configure serial port.", 
options);
+  grub_term_register (&grub_serial_term);
+}
+
+GRUB_MOD_FINI
+{
+  grub_unregister_command ("serial");
+  grub_term_unregister (&grub_serial_term);
+}
diff -uNr grub2/term/terminfo.c grub2.newserial/term/terminfo.c
--- grub2/term/terminfo.c       1970-01-01 00:00:00.000000000 +0000
+++ grub2.newserial/term/terminfo.c     2005-02-20 17:49:05.000000000 +0000
@@ -0,0 +1,279 @@
+/* terminfo.c - read a terminfo entry from the command line */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2004  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. It knows the difference between a vt52 and vt100 
+ * terminal (and much more) and is mainly used the terminal emulation
+ * in the serial driver.
+ */
+
+#include <grub/misc.h>
+#include <grub/terminfo.h>
+#include <grub/tparm.h>
+#include <grub/machine/serial.h>
+
+/* Current terminal capabilities. Default is "vt100".  */
+struct terminfo term =
+  {
+    .name                = "vt100",
+    .cursor_address      = "\e[%i%p1%d;%p2%dH",
+    .clear_screen        = "\e[H\e[J",
+    .enter_standout_mode = "\e[7m",
+    .exit_standout_mode  = "\e[m"
+  };
+
+/* A number of escape sequences are provided in the string valued
+   capabilities for easy encoding of characters there.  Both \E and \e
+   map to an ESCAPE character, ^x maps to a control-x for any
+   appropriate x, and the sequences \n \l \r \t \b \f \s give a
+   newline, line-feed, return, tab, backspace, form-feed, and space.
+   Other escapes include \^ for ^, \\ for \, \, for comma, \: for :,
+   and \0 for null.  (\0 will produce \200, which does not terminate a
+   string but behaves as a null character on most terminals, providĀ­
+   ing CS7 is specified.  See stty(1).)  Finally, characters may be
+   given as three octal digits after a \.  */
+
+void putstr (const char *str)
+{
+  while (*str)
+    grub_putchar (*str++);
+}
+
+char *
+ti_unescape_memory (const char *in, const char *end) 
+{
+  static char out_buffer[256];
+  char c;
+  char *out;
+
+  out = out_buffer;
+  do
+    {
+      c = *(in++);
+      switch (c) 
+       {
+       case '^':
+         if (*in >= 'A' && *in <= 'Z')
+           {
+             *out = (*in) - 'A';
+             in++;
+           }
+         else
+           {
+             *out = '^';
+           }
+         break;
+       case '\\':
+         c = *(in++);
+         if (c >= '0' && c <= '9')
+           {
+             // octal number
+             int n = 0;
+             do
+               {
+                 n = (n << 4) | (c - '0');
+                 c = *(in++);
+               }
+             while (c >= '0' && c <= '9');
+             
+             *out++ = (char)(n & 0xff);
+             
+             // redo last character
+             in--;
+             
+             break;
+           } 
+
+         switch (c) 
+           {
+           case 'e':
+           case 'E':
+             *out++ = '\e';
+             break;
+           case 'n':
+             *out++ = '\n';
+             break;
+           case 'r':
+             *out++ = '\r';
+             break;
+           case 't':
+             *out++ = '\t';
+             break;
+           case 'b':
+             *out++ = '\b';
+             break;
+           case 'f':
+             *out++ = '\f';
+             break;
+           case 's':
+             *out++ = ' ';
+             break;
+           case '\\':
+             *out++ = '\\';
+             break;
+           case '^':
+             *out++ = '^';
+             break;
+           case ',':
+             *out++ = ',';
+             break;
+           case ':':
+             *out++ = ':';
+             break;
+           case '0':
+             *out++ = '\200';
+             break;
+           }
+         break;
+       default:
+         *out++ = c;
+         break;
+       }
+    }
+  while (in <= end);
+  
+  return out_buffer;
+}
+
+char *
+ti_unescape_string (const char *in) 
+{
+  return ti_unescape_memory (in, in + grub_strlen (in));
+}
+
+/* convert a memory region containing binary character into an external
+ * ascii representation. The binary characters will be replaced by an
+ * "ecsape notation". E.g. "033" will become "\e". */
+char *
+ti_escape_memory (const char *in, const char *end) 
+{
+  static char out_buffer[256];
+  char c;
+  char *out;
+
+  out = out_buffer;
+  do
+    {
+      c = *(in++);
+      switch (c)
+       {
+       case '\e':
+         *out++ = '\\'; *out++ = 'e'; break;
+       case ' ':
+         *out++ = '\\'; *out++ = 's'; break;
+       case '\\':
+         *out++ = '\\'; *out++ = '\\'; break;
+       case '0' ... '9':
+       case 'a' ... 'z':
+       case 'A' ... 'Z':
+       case '%':
+       case '+':
+       case '-':
+       case '*':
+       case '/':
+       case ';':
+       case ':':
+       case '{':
+       case '}':
+       case '[':
+       case ']':
+         *out++ = c; break;
+       case 0 ... 25:
+         *out++ = '^'; *out++ = 'A' + c; break;
+       default:
+         *out++ = '\\'; 
+         *out++ = ((c >> 8) & 7) + '0';
+         *out++ = ((c >> 4) & 7) + '0';
+         *out++ = ((c >> 0) & 7) + '0';
+         break;
+       }
+    }
+  while (in < end);
+  
+  *out++ = 0;
+  
+  return out_buffer;
+}
+
+/* convert a string containing binary character into an external ascii
+ * representation. */
+char *
+ti_escape_string (const char *in) 
+{
+  return ti_escape_memory (in, in + grub_strlen (in));
+}
+
+/* move the cursor to the given position starting with "0". */
+void
+ti_cursor_address (int x, int y)
+{
+  putstr (grub_tparm (term.cursor_address, y, x));
+}
+
+/* clear the screen. */
+void 
+ti_clear_screen (void)
+{
+  putstr (grub_tparm (term.clear_screen));
+}
+
+/* enter reverse video */
+void 
+ti_enter_standout_mode (void)
+{
+  putstr (grub_tparm (term.enter_standout_mode));
+}
+
+/* exit reverse video */
+void 
+ti_exit_standout_mode (void)
+{
+  putstr (grub_tparm (term.exit_standout_mode));
+}
+
+/* set the current terminal emulation to use */
+void 
+ti_set_term (const struct terminfo *new)
+{
+  grub_memmove (&term, new, sizeof (struct terminfo));
+}
+
+/* get the current terminal emulation */
+void
+ti_get_term(struct terminfo *copy)
+{
+  grub_memmove (copy, &term, sizeof (struct terminfo));
+}
+
+/* cursor on */
+void
+ti_cursor_on(void)
+{
+  putstr (grub_tparm ("\e[?25l"));
+}
+
+/* cursor off */
+void
+ti_cursor_off(void)
+{
+  putstr (grub_tparm ("\e[?25h"));
+}
+
diff -uNr grub2/term/tparm.c grub2.newserial/term/tparm.c
--- grub2/term/tparm.c  1970-01-01 00:00:00.000000000 +0000
+++ grub2.newserial/term/tparm.c        2005-02-20 17:49:13.000000000 +0000
@@ -0,0 +1,725 @@
+/****************************************************************************
+ * Copyright (c) 1998,2000,2002 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 <address@hidden> 2002
+ **********************************************************************/
+
+/****************************************************************************
+ *  Author: Zeyd M. Ben-Halim <address@hidden> 1992,1995               *
+ *     and: Eric S. Raymond <address@hidden>                         *
+ ****************************************************************************/
+
+/*
+ *     tparm.c
+ *
+ */
+
+#include <grub/misc.h>
+#include <grub/tparm.h>
+
+/*
+ * Common/troublesome character definitions
+ */
+typedef char grub_bool;
+#define isdigit(c) ((c) >= '0' && (c) <= '9')
+#ifndef FALSE
+# define FALSE (0)
+#endif
+#ifndef TRUE
+# define TRUE (!FALSE)
+#endif
+#define MAX_FORMAT_LEN 256
+#define max(a,b) ((a) > (b) ? (a) : (b))
+
+//MODULE_ID("$Id: tparm.c,v 1.1 2002/11/29 20:39:24 okuji Exp $")
+
+/*
+ *     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.
+ */
+
+#define STACKSIZE      20
+
+typedef struct {
+    union {
+       unsigned int num;
+       char *str;
+    } data;
+    grub_bool num_type;
+} stack_frame;
+
+static stack_frame stack[STACKSIZE];
+static int stack_ptr;
+
+static char out_buff[256];
+static int out_size = 256;
+static int out_used;
+
+static inline void
+get_space(int need)
+{
+    need += out_used;
+    if (need > out_size) {
+       // FIX ME! buffer full, what now?
+       ;
+    }
+}
+
+static inline void
+save_text(const char *fmt, const char *s, int len)
+{
+    int 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(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)
+{
+    grub_bool done = FALSE;
+    grub_bool allowminus = FALSE;
+    grub_bool dot = FALSE;
+    grub_bool err = FALSE;
+    char *fmt = format;
+    int prec = 0;
+    int width = 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 {
+               dot = TRUE;
+               prec = 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(*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) {
+       prec = width = value = 0;
+       format = fmt;
+       *format++ = '%';
+       *format++ = *s;
+    }
+
+    if (dot)
+       width = value;
+    else
+       prec = value;
+
+    *format = '\0';
+    /* return maximum string length in print */
+    *len = (prec > width) ? prec : width;
+    return s;
+}
+
+#define isUPPER(c) ((c) >= 'A' && (c) <= 'Z')
+#define isLOWER(c) ((c) >= 'a' && (c) <= 'z')
+
+static inline char *
+tparam_internal(const char *string, int *dataptr)
+{
+#define NUM_VARS 26
+    char *p_is_s[9];
+    int param[9];
+    int lastpop;
+    int popcount;
+    int number;
+    int len;
+    int level;
+    int x, y;
+    int i;
+    int len2;
+    register const char *cp;
+    static int len_fmt = MAX_FORMAT_LEN;
+    static char dummy[] = "";
+    static char format[MAX_FORMAT_LEN];
+    static int dynamic_var[NUM_VARS];
+    static int static_vars[NUM_VARS];
+
+    out_used = 0;
+    if (string == 0)
+       return 0;
+
+    if ((len2 = grub_strlen(string)) > len_fmt) {
+       return 0;
+    }
+
+    /*
+     * 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 = 0;
+    lastpop = -1;
+    popcount = 0;
+    grub_memset(p_is_s, 0, sizeof(p_is_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.
+     */
+    for (cp = string; (cp - string) < (int) len2;) {
+       if (*cp == '%') {
+           cp++;
+           cp = parse_format(cp, format, &len);
+           switch (*cp) {
+           default:
+               break;
+
+           case 'd':           /* FALLTHRU */
+           case 'o':           /* FALLTHRU */
+           case 'x':           /* FALLTHRU */
+           case 'X':           /* FALLTHRU */
+           case 'c':           /* FALLTHRU */
+               number++;
+               lastpop = -1;
+               break;
+
+           case 'l':
+           case 's':
+               if (lastpop > 0)
+                   p_is_s[lastpop - 1] = dummy;
+               ++number;
+               break;
+
+           case 'p':
+               cp++;
+               i = (*cp - '0');
+               if (i >= 0 && i <= 9) {
+                   lastpop = i;
+                   if (lastpop > popcount)
+                       popcount = lastpop;
+               }
+               break;
+
+           case 'P':
+           case 'g':
+               cp++;
+               break;
+
+           case '\'':
+               cp += 2;
+               lastpop = -1;
+               break;
+
+           case '{':
+               cp++;
+               while (*cp >= '0' && *cp <= '9') {
+                   cp++;
+               }
+               break;
+
+           case '+':
+           case '-':
+           case '*':
+           case '/':
+           case 'm':
+           case 'A':
+           case 'O':
+           case '&':
+           case '|':
+           case '^':
+           case '=':
+           case '<':
+           case '>':
+           case '!':
+           case '~':
+               lastpop = -1;
+               number += 2;
+               break;
+
+           case 'i':
+               lastpop = -1;
+               if (popcount < 2)
+                   popcount = 2;
+               break;
+           }
+       }
+       if (*cp != '\0')
+           cp++;
+    }
+
+    if (number > 9)
+       number = 9;
+    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] = (char *)(*(dataptr++));
+       } else {
+         param[i] = (int)(*(dataptr++));
+       }
+    }
+
+    /*
+     * 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 (*string) {
+        /* skip delay timings */
+       if (*string == '$' && *(string + 1) == '<') {
+           while( *string && *string != '>') 
+               string++;
+           if ( *string == '>' ) string++;
+       } else if ( *string == '%') {
+           string++;
+           string = parse_format(string, format, &len);
+           switch (*string) {
+           default:
+               break;
+           case '%':
+               save_char('%');
+               break;
+
+           case 'd':           /* FALLTHRU */
+           case 'o':           /* FALLTHRU */
+           case 'x':           /* FALLTHRU */
+           case 'X':           /* FALLTHRU */
+           case 'c':           /* FALLTHRU */
+               save_number(format, npop(), len);
+               break;
+
+           case 'l':
+               save_number("%d", grub_strlen(spop()), 0);
+               break;
+
+           case 's':
+               save_text(format, spop(), len);
+               break;
+
+           case 'p':
+               string++;
+               i = (*string - '1');
+               if (i >= 0 && i < 9) {
+                   if (p_is_s[i])
+                       spush(p_is_s[i]);
+                   else
+                       npush(param[i]);
+               }
+               break;
+
+           case 'P':
+               string++;
+               if (isUPPER(*string)) {
+                   i = (*string - 'A');
+                   static_vars[i] = npop();
+               } else if (isLOWER(*string)) {
+                   i = (*string - 'a');
+                   dynamic_var[i] = npop();
+               }
+               break;
+
+           case 'g':
+               string++;
+               if (isUPPER(*string)) {
+                   i = (*string - 'A');
+                   npush(static_vars[i]);
+               } else if (isLOWER(*string)) {
+                   i = (*string - 'a');
+                   npush(dynamic_var[i]);
+               }
+               break;
+
+           case '\'':
+               string++;
+               npush(*string);
+               string++;
+               break;
+
+           case '{':
+               number = 0;
+               string++;
+               while (*string >= '0' && *string <= '9') {
+                   number = number * 10 + *string - '0';
+                   string++;
+               }
+               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 */
+                   string++;
+                   level = 0;
+                   while (*string) {
+                       if (*string == '%') {
+                           string++;
+                           if (*string == '?')
+                               level++;
+                           else if (*string == ';') {
+                               if (level > 0)
+                                   level--;
+                               else
+                                   break;
+                           } else if (*string == 'e' && level == 0)
+                               break;
+                       }
+
+                       if (*string)
+                           string++;
+                   }
+               }
+               break;
+
+           case 'e':
+               /* scan forward for a %; at level zero */
+               string++;
+               level = 0;
+               while (*string) {
+                   if (*string == '%') {
+                       string++;
+                       if (*string == '?')
+                           level++;
+                       else if (*string == ';') {
+                           if (level > 0)
+                               level--;
+                           else
+                               break;
+                       }
+                   }
+
+                   if (*string)
+                       string++;
+               }
+               break;
+
+           case ';':
+               break;
+
+           }                   /* endswitch (*string) */
+       } else {                /* endelse (*string == '%') */
+           save_char(*string);
+       }
+
+       if (*string == '\0')
+           break;
+
+       string++;
+    }                          /* endwhile (*string) */
+
+    get_space(1);
+    out_buff[out_used] = '\0';
+
+    return (out_buff);
+}
+
+char *
+grub_tparm(const char *string,...)
+{
+    char *result;
+    int *dataptr = (int *) &string;
+
+    dataptr++;
+
+    result = tparam_internal(string, dataptr);
+
+    return result;
+}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]