diff -ruN grub-0.94.orig/configure.ac configure.ac
--- grub-0.94.orig/configure.ac	Wed Feb 11 00:22:12 2004
+++ configure.ac	Wed Feb 11 00:22:29 2004
@@ -227,6 +227,13 @@
   FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_FFS=1"
 fi
 
+AC_ARG_ENABLE(ufs2,
+  [  --disable-ufs2          disable UFS2 support in Stage 2])
+
+if test x"$enable_ufs2" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_UFS2=1"
+fi
+
 AC_ARG_ENABLE(minix,
   [  --disable-minix         disable Minix fs support in Stage 2])
 
diff -ruN grub-0.94.orig/configure.ac.orig configure.ac.orig
--- grub-0.94.orig/configure.ac.orig	Thu Jan  1 03:00:00 1970
+++ configure.ac.orig	Sun Oct 19 21:25:30 2003
@@ -0,0 +1,640 @@
+dnl Configure script for GRUB.
+dnl Copyright 1999,2000,2001,2002,2003 Free Software Foundation, Inc.
+
+dnl Permission to use, copy, modify and distribute this software and its
+dnl documentation is hereby granted, provided that both the copyright
+dnl notice and this permission notice appear in all copies of the
+dnl software, derivative works or modified versions, and any portions
+dnl thereof, and that both notices appear in supporting documentation.
+dnl
+dnl THE FREE SOFTWARE FOUNDATION ALLOWS FREE USE OF THIS SOFTWARE IN ITS
+dnl "AS IS" CONDITION.  THE FREE SOFTWARE FOUNDATION DISCLAIMS ANY
+dnl LIABILITY OF ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE
+dnl USE OF THIS SOFTWARE.
+
+AC_PREREQ(2.57)
+AC_INIT([GRUB], [0.94], [bug-grub@gnu.org])
+AC_CONFIG_SRCDIR([stage2/stage2.c])
+AC_CONFIG_HEADER([config.h])
+AM_INIT_AUTOMAKE
+
+AC_CANONICAL_HOST
+
+case "$host_cpu" in
+i[[3456]]86) host_cpu=i386 ;;
+x86_64) host_cpu=x86_64 ;;
+*) AC_MSG_ERROR([unsupported CPU type]) ;;
+esac
+
+AC_SUBST(host_cpu)
+AC_SUBST(host_vendor)
+
+#
+# Options
+#
+
+AM_MAINTAINER_MODE
+if test "x$enable_maintainer_mode" = xyes; then
+  AC_PATH_PROG(PERL,perl)
+  if test -z "$PERL"; then
+    AC_MSG_ERROR([perl not found])
+  fi
+fi
+
+# This should be checked before AC_PROG_CC
+if test "x$CFLAGS" = x; then
+  default_CFLAGS=yes
+fi
+
+if test "x$host_cpu" = xx86_64; then
+  CFLAGS="-m32 $CFLAGS"
+fi
+
+#
+# Programs
+#
+
+AC_CHECK_TOOL(CC, gcc)
+AC_PROG_CC
+# We need this for older versions of Autoconf.
+_AM_DEPENDENCIES(CC)
+
+dnl Because recent automake complains about AS, set it here.
+CCAS="$CC"
+AC_SUBST(CCAS)
+
+AC_ARG_WITH(binutils,
+  [  --with-binutils=DIR     search the directory DIR to find binutils])
+
+if test "x$with_binutils" != x; then
+dnl AC_PATH_TOOL is not seen in autoconf 2.13, so use AC_PATH_PROG
+dnl instead for now. It is preferable when you cross-compile GRUB.
+dnl  AC_PATH_TOOL(RANLIB, ranlib, :, "$with_binutils:$PATH")
+  AC_PATH_PROG(RANLIB, ranlib, :, "$with_binutils:$PATH")
+else
+  AC_PROG_RANLIB
+fi
+
+# optimization flags
+if test "x$ac_cv_prog_gcc" = xyes; then
+  if test "x$default_CFLAGS" = xyes; then
+    # Autoconf may set CFLAGS to -O2 and/or -g. So eliminate them.
+    CFLAGS="`echo $CFLAGS | sed -e 's/-g//g' -e 's/-O[[0-9]]//g'` -g"
+    # If the user specify the directory for binutils, add the option `-B'.
+    if test "x$with_binutils" != x; then
+      CFLAGS="-B$with_binutils/ $CFLAGS"
+    fi
+    STAGE1_CFLAGS="-O2"
+    GRUB_CFLAGS="-O2"
+    AC_CACHE_CHECK([whether optimization for size works], size_flag, [
+      saved_CFLAGS=$CFLAGS
+      CFLAGS="-Os -g"
+      AC_TRY_COMPILE(, , size_flag=yes, size_flag=no)
+      CFLAGS=$saved_CFLAGS
+    ])
+    if test "x$size_flag" = xyes; then
+      STAGE2_CFLAGS="-Os"
+    else
+      STAGE2_CFLAGS="-O2 -fno-strength-reduce -fno-unroll-loops"
+    fi
+  fi
+fi
+
+AC_SUBST(STAGE1_CFLAGS)
+AC_SUBST(STAGE2_CFLAGS)
+AC_SUBST(GRUB_CFLAGS)
+
+# Enforce coding standards.
+CPPFLAGS="$CPPFLAGS -Wall -Wmissing-prototypes -Wunused -Wshadow"
+CPPFLAGS="$CPPFLAGS -Wpointer-arith"
+
+AC_CACHE_CHECK([whether -Wundef works], undef_flag, [
+  saved_CPPFLAGS="$CPPFLAGS"
+  CPPFLAGS="-Wundef"
+  AC_TRY_COMPILE(, , undef_flag=yes, undef_flag=no)
+  CPPFLAGS="$saved_CPPFLAGS"
+])
+
+# The options `-falign-*' are supported by gcc 3.0 or later.
+# Probably it is sufficient to only check for -falign-loops.
+AC_CACHE_CHECK([whether -falign-loops works], [falign_loop_flag], [
+  saved_CPPFLAGS="$CPPFLAGS"
+  CPPFLAGS="-falign-loops=1"
+  AC_TRY_COMPILE(, , [falign_loop_flag=yes], [falign_loop_flag=no])
+  CPPFLAGS="$saved_CPPFLAGS"
+])
+
+# Force no alignment to save space.
+if test "x$falign_loop_flag" = xyes; then
+  CPPFLAGS="$CPPFLAGS -falign-jumps=1 -falign-loops=1 -falign-functions=1"
+else
+  CPPFLAGS="$CPPFLAGS -malign-jumps=1 -malign-loops=1 -malign-functions=1"
+fi
+
+if test "x$undef_flag" = xyes; then
+  CPPFLAGS="$CPPFLAGS -Wundef"
+fi
+
+if test "x$with_binutils" != x; then
+dnl  AC_PATH_TOOL(OBJCOPY, objcopy, , "$with_binutils:$PATH")
+  AC_PATH_PROG(OBJCOPY, objcopy, , "$with_binutils:$PATH")
+else
+  AC_CHECK_TOOL(OBJCOPY, objcopy)
+fi
+
+# Defined in acinclude.m4.
+grub_ASM_USCORE
+grub_PROG_OBJCOPY_ABSOLUTE
+if test "x$grub_cv_prog_objcopy_absolute" != xyes; then
+  AC_MSG_ERROR([GRUB requires a working absolute objcopy; upgrade your binutils])
+fi
+
+grub_ASM_PREFIX_REQUIREMENT
+
+grub_ASM_ADDR32
+if test "x$grub_cv_asm_addr32" != xyes; then
+  AC_MSG_ERROR([GRUB requires GAS .code16 addr32 support; upgrade your binutils])
+fi
+
+grub_ASM_ABSOLUTE_WITHOUT_ASTERISK
+
+grub_CHECK_START_SYMBOL
+grub_CHECK_USCORE_START_SYMBOL
+if test "x$grub_cv_check_start_symbol" != "xyes" \
+	-a "x$grub_cv_check_uscore_start_symbol" != "xyes"; then
+  AC_MSG_ERROR([Neither start nor _start is defined])
+fi
+
+grub_CHECK_USCORE_USCORE_BSS_START_SYMBOL
+grub_CHECK_USCORE_EDATA_SYMBOL
+grub_CHECK_EDATA_SYMBOL
+if test "x$grub_cv_check_uscore_uscore_bss_start_symbol" != "xyes" \
+	-a "x$grub_cv_check_uscore_edata_symbol" != "xyes" \
+	-a "x$grub_cv_check_edata_symbol" != "xyes"; then
+  AC_MSG_ERROR([None of __bss_start, _edata, edata defined])
+fi
+
+grub_CHECK_END_SYMBOL
+grub_CHECK_USCORE_END_SYMBOL
+if test "x$grub_cv_check_end_symbol" != "xyes" \
+	-a "x$grub_cv_check_uscore_end_symbol" != "xyes"; then
+  AC_MSG_ERROR([Neither end nor _end is defined])
+fi
+
+# Check for curses libraries.
+AC_ARG_WITH(curses,
+  [  --without-curses        do not use curses])
+
+# Get the filename or the whole disk and open it.
+# Known to work on NetBSD.
+AC_CHECK_LIB(util, opendisk, [GRUB_LIBS="$GRUB_LIBS -lutil"
+  AC_DEFINE(HAVE_OPENDISK, 1, [Define if opendisk() in -lutil can be used])])
+
+# Unless the user specify --without-curses, check for curses.
+if test "x$with_curses" != "xno"; then
+  AC_CHECK_LIB(ncurses, wgetch, [GRUB_LIBS="$GRUB_LIBS -lncurses"
+  AC_DEFINE(HAVE_LIBCURSES)],
+    [AC_CHECK_LIB(curses, wgetch, [GRUB_LIBS="$GRUB_LIBS -lcurses"
+       AC_DEFINE(HAVE_LIBCURSES)])])
+fi
+
+AC_SUBST(GRUB_LIBS)
+
+# Check for headers.
+AC_CHECK_HEADERS(string.h strings.h ncurses/curses.h ncurses.h curses.h)
+
+# Check for user options.
+
+# filesystems support.
+AC_ARG_ENABLE(ext2fs,
+  [  --disable-ext2fs        disable ext2fs support in Stage 2])
+
+if test x"$enable_ext2fs" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_EXT2FS=1"
+fi
+
+AC_ARG_ENABLE(fat,
+  [  --disable-fat           disable FAT support in Stage 2])
+
+if test x"$enable_fat" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_FAT=1"
+fi
+
+AC_ARG_ENABLE(ffs,
+  [  --disable-ffs           disable FFS support in Stage 2])
+
+if test x"$enable_ffs" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_FFS=1"
+fi
+
+AC_ARG_ENABLE(minix,
+  [  --disable-minix         disable Minix fs support in Stage 2])
+
+if test x"$enable_minix" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_MINIX=1"
+fi
+
+AC_ARG_ENABLE(reiserfs,
+  [  --disable-reiserfs      disable ReiserFS support in Stage 2])
+
+if test x"$enable_reiserfs" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_REISERFS=1"
+fi
+
+AC_ARG_ENABLE(vstafs,
+  [  --disable-vstafs        disable VSTa FS support in Stage 2])
+
+if test x"$enable_vstafs" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_VSTAFS=1"
+fi
+
+AC_ARG_ENABLE(jfs,
+  [  --disable-jfs           disable IBM JFS support in Stage 2])
+
+if test x"$enable_jfs" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_JFS=1"
+fi
+
+AC_ARG_ENABLE(xfs,
+  [  --disable-xfs           disable SGI XFS support in Stage 2])
+
+if test x"$enable_xfs" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_XFS=1"
+fi
+
+dnl AC_ARG_ENABLE(tftp,
+dnl [  --enable-tftp           enable TFTP support in Stage 2])
+dnl 
+dnl #if test x"$enable_tftp" = xyes; then
+dnl FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_TFTP=1"
+dnl fi
+
+AC_ARG_ENABLE(gunzip,
+  [  --disable-gunzip        disable decompression in Stage 2])
+
+if test x"$enable_gunzip" = xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DNO_DECOMPRESSION=1"
+fi
+
+AC_ARG_ENABLE(md5-password,
+  [  --disable-md5-password  disable MD5 password support in Stage 2])
+if test "x$enable_md5_password" != xno; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DUSE_MD5_PASSWORDS=1"
+fi
+
+dnl The netboot support.
+dnl General options.
+AC_ARG_ENABLE(packet-retransmission,
+  [  --disable-packet-retransmission
+                          turn off packet retransmission])
+if test "x$enable_packet_retransmission" != xno; then
+  NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DCONGESTED=1"
+fi
+
+AC_ARG_ENABLE(pci-direct,
+  [  --enable-pci-direct     access PCI directly instead of using BIOS])
+if test "x$enable_pci_direct" = xyes; then
+  NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DCONFIG_PCI_DIRECT=1"
+fi
+
+dnl Device drivers.
+AC_ARG_ENABLE(3c509,
+  [  --enable-3c509          enable 3Com509 driver])
+if test "x$enable_3c509" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_3C509"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS 3c509.o"
+fi
+
+AC_ARG_ENABLE(3c529,
+  [  --enable-3c529          enable 3Com529 driver])
+if test "x$enable_3c529" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_3C529=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS 3c529.o"
+fi
+
+AC_ARG_ENABLE(3c595,
+  [  --enable-3c595          enable 3Com595 driver])
+if test "x$enable_3c595" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_3C595=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS 3c595.o"
+fi
+
+AC_ARG_ENABLE(3c90x,
+  [  --enable-3c90x          enable 3Com90x driver])
+if test "x$enable_3c90x" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_3C90X=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS 3c90x.o"
+fi
+
+AC_ARG_ENABLE(cs89x0,
+  [  --enable-cs89x0         enable CS89x0 driver])
+if test "x$enable_cs89x0" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_CS89X0=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS cs89x0.o"
+fi
+
+AC_ARG_ENABLE(davicom,
+  [  --enable-davicom        enable Davicom driver])
+if test "x$enable_davicom" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_DAVICOM=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS davicom.o"
+fi
+
+AC_ARG_ENABLE(depca,
+  [  --enable-depca          enable DEPCA and EtherWORKS driver])
+if test "x$enable_depca" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_DEPCA=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS depca.o"
+fi
+
+AC_ARG_ENABLE(eepro,
+  [  --enable-eepro          enable Etherexpress Pro/10 driver])
+if test "x$enable_eepro" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_EEPRO=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS eepro.o"
+fi
+
+AC_ARG_ENABLE(eepro100,
+  [  --enable-eepro100       enable Etherexpress Pro/100 driver])
+if test "x$enable_eepro100" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_EEPRO100=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS eepro100.o"
+fi
+
+AC_ARG_ENABLE(epic100,
+  [  --enable-epic100        enable SMC 83c170 EPIC/100 driver])
+if test "x$enable_epic100" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_EPIC100=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS epic100.o"
+fi
+
+AC_ARG_ENABLE(3c507,
+  [  --enable-3c507          enable 3Com507 driver])
+if test "x$enable_3c507" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_3C507=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS 3c507.o"
+fi
+
+AC_ARG_ENABLE(exos205,
+  [  --enable-exos205        enable EXOS205 driver])
+if test "x$enable_exos205" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_EXOS205=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS exos205.o"
+fi
+
+AC_ARG_ENABLE(ni5210,
+  [  --enable-ni5210         enable Racal-Interlan NI5210 driver])
+if test "x$enable_ni5210" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_NI5210=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS ni5210.o"
+fi
+
+AC_ARG_ENABLE(lance,
+  [  --enable-lance          enable Lance PCI PCNet/32 driver])
+if test "x$enable_lance" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_LANCE=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS lance.o"
+fi
+
+AC_ARG_ENABLE(ne2100,
+  [  --enable-ne2100         enable Novell NE2100 driver])
+if test "x$enable_ne2100" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_NE2100=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS ne2100.o"
+fi
+
+AC_ARG_ENABLE(ni6510,
+  [  --enable-ni6510         enable Racal-Interlan NI6510 driver])
+if test "x$enable_ni6510" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_NI6510=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS ni6510.o"
+fi
+
+AC_ARG_ENABLE(natsemi,
+  [  --enable-natsemi        enable NatSemi DP8381x driver])
+if test "x$enable_natsemi" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_NATSEMI=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS natsemi.o"
+fi
+
+AC_ARG_ENABLE(ni5010,
+  [  --enable-ni5010         enable Racal-Interlan NI5010 driver])
+if test "x$enable_ni5010" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_NI5010=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS ni5010.o"
+fi
+
+AC_ARG_ENABLE(3c503,
+  [  --enable-3c503          enable 3Com503 driver])
+if test "x$enable_3c503" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_3C503=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS 3c503.o"
+fi
+
+AC_ARG_ENABLE(ne,
+  [  --enable-ne             enable NE1000/2000 ISA driver])
+if test "x$enable_ne" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_NE=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS ne.o"
+fi
+
+AC_ARG_ENABLE(ns8390,
+  [  --enable-ns8390         enable NE2000 PCI driver])
+if test "x$enable_ns8390" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_NS8390=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS ns8390.o"
+fi
+
+AC_ARG_ENABLE(wd,
+  [  --enable-wd             enable WD8003/8013, SMC8216/8416 driver])
+if test "x$enable_wd" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_WD=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS wd.o"
+fi
+
+AC_ARG_ENABLE(otulip,
+  [  --enable-otulip         enable old Tulip driver])
+if test "x$enable_otulip" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_OTULIP=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS otulip.o"
+fi
+
+AC_ARG_ENABLE(rtl8139,
+  [  --enable-rtl8139        enable Realtek 8139 driver])
+if test "x$enable_rtl8139" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_RTL8139=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS rtl8139.o"
+fi
+
+AC_ARG_ENABLE(sis900,
+  [  --enable-sis900         enable SIS 900 and SIS 7016 driver])
+if test "x$enable_sis900" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_SIS900=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS sis900.o"
+fi
+
+AC_ARG_ENABLE(sk-g16,
+  [  --enable-sk-g16         enable Schneider and Koch G16 driver])
+if test "x$enable_sk_g16" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_SK_G16=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS sk_g16.o"
+fi
+
+AC_ARG_ENABLE(smc9000,
+  [  --enable-smc9000        enable SMC9000 driver])
+if test "x$enable_smc9000" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_SMC9000=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS smc9000.o"
+fi
+
+AC_ARG_ENABLE(tiara,
+  [  --enable-tiara          enable Tiara driver])
+if test "x$enable_tiara" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_TIARA=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS tiara.o"
+fi
+
+AC_ARG_ENABLE(tulip,
+  [  --enable-tulip          enable Tulip driver])
+if test "x$enable_tulip" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_TULIP=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS tulip.o"
+fi
+
+AC_ARG_ENABLE(via-rhine,
+  [  --enable-via-rhine      enable Rhine-I/II driver])
+if test "x$enable_via_rhine" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_VIA_RHINE=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS via_rhine.o"
+fi
+
+AC_ARG_ENABLE(w89c840,
+  [  --enable-w89c840        enable Winbond W89c840, Compex RL100-ATX driver])
+if test "x$enable_w89c840" = xyes; then
+  NET_CFLAGS="$NET_CFLAGS -DINCLUDE_W89C840=1"
+  NETBOOT_DRIVERS="$NETBOOT_DRIVERS w89c840.o"
+fi
+
+dnl Check if the netboot support is turned on.
+AM_CONDITIONAL(NETBOOT_SUPPORT, test "x$NET_CFLAGS" != x)
+if test "x$NET_CFLAGS" != x; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_TFTP=1"
+fi
+
+dnl Extra options.
+AC_ARG_ENABLE(3c503-shmem,
+  [  --enable-3c503-shmem    use 3c503 shared memory mode])
+if test "x$enable_3c503_shmem" = xyes; then
+  NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DT503_SHMEM=1"
+fi
+
+AC_ARG_ENABLE(3c503-aui,
+  [  --enable-3c503-aui      use AUI by default on 3c503 cards])
+if test "x$enable_3c503_aui" = xyes; then
+  NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DT503_AUI=1"
+fi
+
+AC_ARG_ENABLE(compex-rl2000-fix,
+  [  --enable-compex-rl2000-fix
+                          specify this if you have a Compex RL2000 PCI])
+if test "x$enable_compex_rl2000_fix" = xyes; then
+  NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DCOMPEX_RL2000_FIX=1"
+fi
+
+AC_ARG_ENABLE(smc9000-scan,
+  [  --enable-smc9000-scan=LIST
+                          probe for SMC9000 I/O addresses using LIST],
+  [NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DSMC9000_SCAN=$enable_smc9000_scan"])
+
+AC_ARG_ENABLE(ne-scan,
+  [  --enable-ne-scan=LIST   probe for NE base address using LIST],
+  [NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DNE_SCAN=$enable_ne_scan"],
+  [NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DNE_SCAN=0x280,0x300,0x320,0x340"])
+
+AC_ARG_ENABLE(wd-default-mem,
+  [  --enable-wd-default-mem=MEM
+                          set the default memory location for WD/SMC],
+  [NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DWD_DEFAULT_MEM=$enable_wd_default_mem"],
+  [NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DWD_DEFAULT_MEM=0xCC000"])
+
+AC_ARG_ENABLE(cs-scan,
+  [  --enable-cs-scan=LIST   probe for CS89x0 base address using LIST],
+  [NET_EXTRAFLAGS="$NET_EXTRAFLAGS -DCS_SCAN=$enable_cs_scan"])
+
+dnl Diskless
+AC_ARG_ENABLE(diskless,
+  [  --enable-diskless       enable diskless support])
+AM_CONDITIONAL(DISKLESS_SUPPORT, test "x$enable_diskless" = xyes)
+
+dnl Hercules terminal
+AC_ARG_ENABLE(hercules,
+  [  --disable-hercules      disable hercules terminal support])
+AM_CONDITIONAL(HERCULES_SUPPORT, test "x$enable_hercules" != xno)
+
+dnl Serial terminal
+AC_ARG_ENABLE(serial,
+  [  --disable-serial        disable serial terminal support])
+AM_CONDITIONAL(SERIAL_SUPPORT, test "x$enable_serial" != xno)
+
+dnl Simulation of the slowness of a serial device.
+AC_ARG_ENABLE(serial-speed-simulation,
+  [  --enable-serial-speed-simulation
+                          simulate the slowness of a serial device])
+AM_CONDITIONAL(SERIAL_SPEED_SIMULATION,
+  test "x$enable_serial_speed_simulation" = xyes)
+
+# Sanity check.
+if test "x$enable_diskless" = xyes; then
+  if test "x$NET_CFLAGS" = x; then
+    AC_MSG_ERROR([You must enable at least one network driver])
+  fi
+fi
+
+dnl Embed a menu string in GRUB itself.
+AC_ARG_ENABLE(preset-menu,
+  [  --enable-preset-menu=FILE
+                          preset a menu file FILE in Stage 2])
+if test "x$enable_preset_menu" = x; then
+  :
+else
+  if test -r $enable_preset_menu; then
+    grub_DEFINE_FILE(PRESET_MENU_STRING, [$enable_preset_menu])
+  else
+    AC_MSG_ERROR([Cannot read the preset menu file $enable_preset_menu])
+  fi
+fi
+
+dnl Build the example Multiboot kernel.
+AC_ARG_ENABLE(example-kernel,
+  [  --enable-example-kernel
+                          build the example Multiboot kernel])
+AM_CONDITIONAL(BUILD_EXAMPLE_KERNEL, test "x$enable_example_kernel" = xyes)
+
+dnl Automatic Linux mem= option.
+AC_ARG_ENABLE(auto-linux-mem-opt,
+  [  --disable-auto-linux-mem-opt
+                          don't pass Linux mem= option automatically])
+if test "x$enable_auto_linux_mem_opt" = xno; then
+  :
+else
+  AC_DEFINE(AUTO_LINUX_MEM_OPT)
+fi
+
+dnl Now substitute the variables.
+AC_SUBST(FSYS_CFLAGS)
+AC_SUBST(NET_CFLAGS)
+AC_SUBST(NET_EXTRAFLAGS)
+AC_SUBST(NETBOOT_DRIVERS)
+
+dnl Because recent automake complains about CCASFLAGS, set it here.
+CCASFLAGS='$(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(CPPFLAGS) $(CFLAGS)'
+AC_SUBST(CCASFLAGS)
+
+
+dnl Output.
+AC_CONFIG_FILES([Makefile stage1/Makefile stage2/Makefile \
+		 docs/Makefile lib/Makefile util/Makefile \
+		 grub/Makefile netboot/Makefile util/grub-image \
+		 util/grub-install util/grub-md5-crypt \
+		 util/grub-terminfo])
+AC_OUTPUT
diff -ruN grub-0.94.orig/grub/Makefile.am grub/Makefile.am
--- grub-0.94.orig/grub/Makefile.am	Wed Feb 11 00:22:12 2004
+++ grub/Makefile.am	Wed Feb 11 00:22:29 2004
@@ -7,6 +7,7 @@
 endif
 
 AM_CPPFLAGS = -DGRUB_UTIL=1 -DFSYS_EXT2FS=1 -DFSYS_FAT=1 \
+	-DFSYS_UFS2=1 \
 	-DFSYS_FFS=1 -DFSYS_MINIX=1 -DSUPPORT_HERCULES=1 \
 	$(SERIAL_FLAGS) -I$(top_srcdir)/stage2 \
 	-I$(top_srcdir)/stage1 -I$(top_srcdir)/lib
diff -ruN grub-0.94.orig/grub/Makefile.am.orig grub/Makefile.am.orig
--- grub-0.94.orig/grub/Makefile.am.orig	Thu Jan  1 03:00:00 1970
+++ grub/Makefile.am.orig	Sun Jan 18 22:34:24 2004
@@ -0,0 +1,17 @@
+sbin_PROGRAMS = grub
+
+if SERIAL_SPEED_SIMULATION
+SERIAL_FLAGS = -DSUPPORT_SERIAL=1 -DSIMULATE_SLOWNESS_OF_SERIAL=1
+else
+SERIAL_FLAGS = -DSUPPORT_SERIAL=1 
+endif
+
+AM_CPPFLAGS = -DGRUB_UTIL=1 -DFSYS_EXT2FS=1 -DFSYS_FAT=1 \
+	-DFSYS_FFS=1 -DFSYS_MINIX=1 -DSUPPORT_HERCULES=1 \
+	$(SERIAL_FLAGS) -I$(top_srcdir)/stage2 \
+	-I$(top_srcdir)/stage1 -I$(top_srcdir)/lib
+
+AM_CFLAGS = $(GRUB_CFLAGS) -fwritable-strings
+
+grub_SOURCES = main.c asmstub.c
+grub_LDADD = ../stage2/libgrub.a  ../lib/libcommon.a $(GRUB_LIBS)
diff -ruN grub-0.94.orig/stage2/Makefile.am stage2/Makefile.am
--- grub-0.94.orig/stage2/Makefile.am	Wed Feb 11 00:22:12 2004
+++ stage2/Makefile.am	Wed Feb 11 00:22:29 2004
@@ -17,10 +17,12 @@
 noinst_LIBRARIES = libgrub.a
 libgrub_a_SOURCES = boot.c builtins.c char_io.c cmdline.c common.c \
 	disk_io.c fsys_ext2fs.c fsys_fat.c fsys_ffs.c fsys_jfs.c \
+	fsys_ufs2.c \
 	fsys_minix.c fsys_reiserfs.c fsys_vstafs.c fsys_xfs.c gunzip.c \
 	md5.c serial.c stage2.c terminfo.c tparm.c
 libgrub_a_CFLAGS = $(GRUB_CFLAGS) -I$(top_srcdir)/lib \
 	-DGRUB_UTIL=1 -DFSYS_EXT2FS=1 -DFSYS_FAT=1 -DFSYS_FFS=1 \
+	-DFSYS_UFS2=1 \
 	-DFSYS_JFS=1 -DFSYS_MINIX=1 -DFSYS_REISERFS=1 -DFSYS_VSTAFS=1 \
 	-DFSYS_XFS=1 -DUSE_MD5_PASSWORDS=1 \
 	-DSUPPORT_SERIAL=1 -DSUPPORT_HERCULES=1 -fwritable-strings
@@ -33,21 +35,25 @@
 if DISKLESS_SUPPORT
 pkgdata_DATA = stage2 e2fs_stage1_5 fat_stage1_5 ffs_stage1_5 \
 	jfs_stage1_5 minix_stage1_5 reiserfs_stage1_5 vstafs_stage1_5 \
+	ufs2_stage1_5 \
 	xfs_stage1_5 nbgrub pxegrub
 noinst_DATA = pre_stage2 start nbloader pxeloader diskless
 noinst_PROGRAMS = pre_stage2.exec start.exec e2fs_stage1_5.exec \
 	fat_stage1_5.exec ffs_stage1_5.exec jfs_stage1_5.exec \
 	minix_stage1_5.exec reiserfs_stage1_5.exec \
+	ufs2_stage1_5.exec \
 	vstafs_stage1_5.exec xfs_stage1_5.exec nbloader.exec \
 	pxeloader.exec diskless.exec
 else
 pkgdata_DATA = stage2 e2fs_stage1_5 fat_stage1_5 ffs_stage1_5 \
 	jfs_stage1_5 minix_stage1_5 reiserfs_stage1_5 vstafs_stage1_5 \
+	ufs2_stage1_5 \
 	xfs_stage1_5
 noinst_DATA = pre_stage2 start
 noinst_PROGRAMS = pre_stage2.exec start.exec e2fs_stage1_5.exec \
 	fat_stage1_5.exec ffs_stage1_5.exec jfs_stage1_5.exec \
 	minix_stage1_5.exec reiserfs_stage1_5.exec \
+	ufs2_stage1_5.exec \
 	vstafs_stage1_5.exec xfs_stage1_5.exec
 endif
 MOSTLYCLEANFILES = $(noinst_PROGRAMS)
@@ -84,6 +90,7 @@
 # For stage2 target.
 pre_stage2_exec_SOURCES = asm.S bios.c boot.c builtins.c char_io.c \
 	cmdline.c common.c console.c disk_io.c fsys_ext2fs.c \
+	fsys_ufs2.c \
 	fsys_fat.c fsys_ffs.c fsys_jfs.c fsys_minix.c fsys_reiserfs.c \
 	fsys_vstafs.c fsys_xfs.c gunzip.c hercules.c md5.c serial.c \
 	smp-imps.c stage2.c terminfo.c tparm.c
@@ -148,6 +155,15 @@
 ffs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FFS=1 \
 	-DNO_BLOCK_FILES=1
 ffs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
+# For ufs2_stage1_5 target.
+ufs2_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \
+	stage1_5.c fsys_ufs2.c bios.c
+ufs2_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_UFS2=1 \
+	-DNO_BLOCK_FILES=1
+ufs2_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_UFS2=1 \
+	-DNO_BLOCK_FILES=1
+ufs2_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
 
 # For minix_stage1_5 target.
 minix_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \
diff -ruN grub-0.94.orig/stage2/Makefile.am.orig stage2/Makefile.am.orig
--- grub-0.94.orig/stage2/Makefile.am.orig	Thu Jan  1 03:00:00 1970
+++ stage2/Makefile.am.orig	Sun Oct 19 20:45:18 2003
@@ -0,0 +1,239 @@
+# For test target.
+TESTS = size_test
+noinst_SCRIPTS = $(TESTS)
+
+# For dist target.
+noinst_HEADERS = apic.h defs.h dir.h disk_inode.h disk_inode_ffs.h \
+        fat.h filesys.h freebsd.h fs.h hercules.h i386-elf.h \
+	imgact_aout.h jfs.h mb_header.h mb_info.h md5.h nbi.h \
+	pc_slice.h serial.h shared.h smp-imps.h term.h terminfo.h \
+	tparm.h nbi.h vstafs.h xfs.h
+EXTRA_DIST = setjmp.S apm.S $(noinst_SCRIPTS)
+
+# For <stage1.h>.
+INCLUDES = -I$(top_srcdir)/stage1
+
+# The library for /sbin/grub.
+noinst_LIBRARIES = libgrub.a
+libgrub_a_SOURCES = boot.c builtins.c char_io.c cmdline.c common.c \
+	disk_io.c fsys_ext2fs.c fsys_fat.c fsys_ffs.c fsys_jfs.c \
+	fsys_minix.c fsys_reiserfs.c fsys_vstafs.c fsys_xfs.c gunzip.c \
+	md5.c serial.c stage2.c terminfo.c tparm.c
+libgrub_a_CFLAGS = $(GRUB_CFLAGS) -I$(top_srcdir)/lib \
+	-DGRUB_UTIL=1 -DFSYS_EXT2FS=1 -DFSYS_FAT=1 -DFSYS_FFS=1 \
+	-DFSYS_JFS=1 -DFSYS_MINIX=1 -DFSYS_REISERFS=1 -DFSYS_VSTAFS=1 \
+	-DFSYS_XFS=1 -DUSE_MD5_PASSWORDS=1 \
+	-DSUPPORT_SERIAL=1 -DSUPPORT_HERCULES=1 -fwritable-strings
+
+# Stage 2 and Stage 1.5's.
+pkgdatadir = $(datadir)/$(PACKAGE)/$(host_cpu)-$(host_vendor)
+
+EXTRA_PROGRAMS = nbloader.exec pxeloader.exec diskless.exec
+
+if DISKLESS_SUPPORT
+pkgdata_DATA = stage2 e2fs_stage1_5 fat_stage1_5 ffs_stage1_5 \
+	jfs_stage1_5 minix_stage1_5 reiserfs_stage1_5 vstafs_stage1_5 \
+	xfs_stage1_5 nbgrub pxegrub
+noinst_DATA = pre_stage2 start nbloader pxeloader diskless
+noinst_PROGRAMS = pre_stage2.exec start.exec e2fs_stage1_5.exec \
+	fat_stage1_5.exec ffs_stage1_5.exec jfs_stage1_5.exec \
+	minix_stage1_5.exec reiserfs_stage1_5.exec \
+	vstafs_stage1_5.exec xfs_stage1_5.exec nbloader.exec \
+	pxeloader.exec diskless.exec
+else
+pkgdata_DATA = stage2 e2fs_stage1_5 fat_stage1_5 ffs_stage1_5 \
+	jfs_stage1_5 minix_stage1_5 reiserfs_stage1_5 vstafs_stage1_5 \
+	xfs_stage1_5
+noinst_DATA = pre_stage2 start
+noinst_PROGRAMS = pre_stage2.exec start.exec e2fs_stage1_5.exec \
+	fat_stage1_5.exec ffs_stage1_5.exec jfs_stage1_5.exec \
+	minix_stage1_5.exec reiserfs_stage1_5.exec \
+	vstafs_stage1_5.exec xfs_stage1_5.exec
+endif
+MOSTLYCLEANFILES = $(noinst_PROGRAMS)
+
+PRE_STAGE2_LINK = -nostdlib -Wl,-N -Wl,-Ttext -Wl,8200
+START_LINK = -nostdlib -Wl,-N -Wl,-Ttext -Wl,8000
+NBLOADER_LINK = -nostdlib -Wl,-N -Wl,-Ttext -Wl,0
+PXELOADER_LINK = -nostdlib -Wl,-N -Wl,-Ttext -Wl,7C00
+
+if NETBOOT_SUPPORT
+NETBOOT_FLAGS = -I$(top_srcdir)/netboot -DSUPPORT_NETBOOT=1
+else
+NETBOOT_FLAGS =
+endif
+
+if SERIAL_SUPPORT
+SERIAL_FLAGS = -DSUPPORT_SERIAL=1
+else
+SERIAL_FLAGS =
+endif
+
+if HERCULES_SUPPORT
+HERCULES_FLAGS = -DSUPPORT_HERCULES=1
+else
+HERCULES_FLAGS =
+endif
+
+STAGE2_COMPILE = $(STAGE2_CFLAGS) -fno-builtin -nostdinc \
+	$(NETBOOT_FLAGS) $(SERIAL_FLAGS) $(HERCULES_FLAGS)
+
+STAGE1_5_LINK = -nostdlib -Wl,-N -Wl,-Ttext -Wl,2000
+STAGE1_5_COMPILE = $(STAGE2_COMPILE) -DNO_DECOMPRESSION=1 -DSTAGE1_5=1
+
+# For stage2 target.
+pre_stage2_exec_SOURCES = asm.S bios.c boot.c builtins.c char_io.c \
+	cmdline.c common.c console.c disk_io.c fsys_ext2fs.c \
+	fsys_fat.c fsys_ffs.c fsys_jfs.c fsys_minix.c fsys_reiserfs.c \
+	fsys_vstafs.c fsys_xfs.c gunzip.c hercules.c md5.c serial.c \
+	smp-imps.c stage2.c terminfo.c tparm.c
+pre_stage2_exec_CFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS)
+pre_stage2_exec_CCASFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS)
+pre_stage2_exec_LDFLAGS = $(PRE_STAGE2_LINK)
+
+if NETBOOT_SUPPORT
+pre_stage2_exec_LDADD = ../netboot/libdrivers.a
+endif
+
+if DISKLESS_SUPPORT
+BUILT_SOURCES = stage2_size.h diskless_size.h
+else
+BUILT_SOURCES = stage2_size.h
+endif
+
+CLEANFILES = $(pkgdata_DATA) $(noinst_DATA) $(BUILT_SOURCES)
+
+stage2_size.h: pre_stage2
+	-rm -f stage2_size.h
+	set dummy `ls -l pre_stage2`; \
+	echo "#define STAGE2_SIZE $$6" > stage2_size.h
+
+start_exec_SOURCES = start.S
+start_exec_CCASFLAGS = $(STAGE2_COMPILE)
+start_exec_LDFLAGS = $(START_LINK)
+
+# XXX: automake doesn't provide a way to specify dependencies for object
+# files explicitly, so we must write this by a general Makefile scheme.
+# If automake change the naming scheme for per-executable objects, this
+# will be broken.
+start_exec-start.$(OBJEXT): stage2_size.h
+
+stage2: pre_stage2 start
+	-rm -f stage2
+	cat start pre_stage2 > stage2
+
+# For e2fs_stage1_5 target.
+e2fs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \
+	stage1_5.c fsys_ext2fs.c bios.c
+e2fs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_EXT2FS=1 \
+	-DNO_BLOCK_FILES=1
+e2fs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_EXT2FS=1 \
+	-DNO_BLOCK_FILES=1
+e2fs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
+# For fat_stage1_5 target.
+fat_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \
+	stage1_5.c fsys_fat.c bios.c
+fat_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FAT=1 \
+	-DNO_BLOCK_FILES=1
+fat_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FAT=1 \
+	-DNO_BLOCK_FILES=1
+fat_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
+# For ffs_stage1_5 target.
+ffs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \
+	stage1_5.c fsys_ffs.c bios.c
+ffs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FFS=1 \
+	-DNO_BLOCK_FILES=1
+ffs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_FFS=1 \
+	-DNO_BLOCK_FILES=1
+ffs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
+# For minix_stage1_5 target.
+minix_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c disk_io.c \
+	stage1_5.c fsys_minix.c bios.c
+minix_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_MINIX=1 \
+	-DNO_BLOCK_FILES=1
+minix_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_MINIX=1 \
+	-DNO_BLOCK_FILES=1
+minix_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
+# For reiserfs_stage1_5 target.
+reiserfs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c \
+	disk_io.c stage1_5.c fsys_reiserfs.c bios.c
+reiserfs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_REISERFS=1 \
+	-DNO_BLOCK_FILES=1
+reiserfs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_REISERFS=1 \
+	-DNO_BLOCK_FILES=1
+reiserfs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
+# For vstafs_stage1_5 target.
+vstafs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c \
+	disk_io.c stage1_5.c fsys_vstafs.c bios.c
+vstafs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_VSTAFS=1 \
+	-DNO_BLOCK_FILES=1
+vstafs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_VSTAFS=1 \
+	-DNO_BLOCK_FILES=1
+vstafs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
+# For jfs_stage1_5 target.
+jfs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c \
+	disk_io.c stage1_5.c fsys_jfs.c bios.c
+jfs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_JFS=1 \
+	-DNO_BLOCK_FILES=1
+jfs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_JFS=1 \
+	-DNO_BLOCK_FILES=1
+jfs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
+# For xfs_stage1_5 target.
+xfs_stage1_5_exec_SOURCES = start.S asm.S common.c char_io.c \
+	disk_io.c stage1_5.c fsys_xfs.c bios.c
+xfs_stage1_5_exec_CFLAGS = $(STAGE1_5_COMPILE) -DFSYS_XFS=1 \
+	-DNO_BLOCK_FILES=1
+xfs_stage1_5_exec_CCASFLAGS = $(STAGE1_5_COMPILE) -DFSYS_XFS=1 \
+	-DNO_BLOCK_FILES=1
+xfs_stage1_5_exec_LDFLAGS = $(STAGE1_5_LINK)
+
+# For diskless target.
+diskless_exec_SOURCES = $(pre_stage2_exec_SOURCES)
+diskless_exec_CFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS) \
+	-DSUPPORT_DISKLESS=1
+diskless_exec_CCASFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS) \
+	-DSUPPORT_DISKLESS=1
+diskless_exec_LDFLAGS = $(PRE_STAGE2_LINK)
+diskless_exec_LDADD = ../netboot/libdrivers.a
+
+diskless_size.h: diskless
+	-rm -f $@
+	set dummy `ls -l $^`; \
+	echo "#define DISKLESS_SIZE $$6" > $@
+
+# For nbloader target.
+nbloader_exec_SOURCES = nbloader.S
+nbloader_exec_CCASFLAGS = $(STAGE2_COMPILE)
+nbloader_exec_LDFLAGS = $(NBLOADER_LINK)
+
+# XXX: See the comment for start_exec-start.o.
+nbloader_exec-nbloader.$(OBJEXT): diskless_size.h
+
+# For nbgrub target.
+nbgrub: nbloader diskless
+	-rm -f $@
+	cat $^ > $@
+
+# For pxeloader target.
+pxeloader_exec_SOURCES = pxeloader.S
+pxeloader_exec_CCASFLAGS = $(STAGE2_COMPILE)
+pxeloader_exec_LDFLAGS = $(PXELOADER_LINK)
+
+# XXX: See the comment for start_exec-start.o.
+pxeloader_exec-pxeloader.$(OBJEXT): diskless_size.h
+
+# For pxegrub target.
+pxegrub: pxeloader diskless
+	-rm -f $@
+	cat $^ > $@
+
+# General rule for making a raw binary.
+%: %.exec
+	$(OBJCOPY) -O binary $< $@
diff -ruN grub-0.94.orig/stage2/builtins.c stage2/builtins.c
--- grub-0.94.orig/stage2/builtins.c	Wed Feb 11 00:22:12 2004
+++ stage2/builtins.c	Wed Feb 11 00:22:29 2004
@@ -3747,6 +3747,7 @@
   {
     {"ext2fs",   "/e2fs_stage1_5"},
     {"fat",      "/fat_stage1_5"},
+    {"ufs2",     "/ufs2_stage1_5"},
     {"ffs",      "/ffs_stage1_5"},
     {"jfs",      "/jfs_stage1_5"},
     {"minix",    "/minix_stage1_5"},
diff -ruN grub-0.94.orig/stage2/builtins.c.orig stage2/builtins.c.orig
--- grub-0.94.orig/stage2/builtins.c.orig	Thu Jan  1 03:00:00 1970
+++ stage2/builtins.c.orig	Sun Jan 11 12:39:22 2004
@@ -0,0 +1,4755 @@
+/* builtins.c - the GRUB builtin commands */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,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 stdio.h before shared.h, because we can't define
+   WITHOUT_LIBC_STUBS here.  */
+#ifdef GRUB_UTIL
+# include <stdio.h>
+#endif
+
+#include <shared.h>
+#include <filesys.h>
+#include <term.h>
+
+#ifdef SUPPORT_NETBOOT
+# define GRUB	1
+# include <etherboot.h>
+#endif
+
+#ifdef SUPPORT_SERIAL
+# include <serial.h>
+# include <terminfo.h>
+#endif
+
+#ifdef GRUB_UTIL
+# include <device.h>
+#else /* ! GRUB_UTIL */
+# include <apic.h>
+# include <smp-imps.h>
+#endif /* ! GRUB_UTIL */
+
+#ifdef USE_MD5_PASSWORDS
+# include <md5.h>
+#endif
+
+/* The type of kernel loaded.  */
+kernel_t kernel_type;
+/* The boot device.  */
+static int bootdev;
+/* True when the debug mode is turned on, and false
+   when it is turned off.  */
+int debug = 0;
+/* The default entry.  */
+int default_entry = 0;
+/* The fallback entry.  */
+int fallback_entry = -1;
+/* The number of current entry.  */
+int current_entryno;
+/* The address for Multiboot command-line buffer.  */
+static char *mb_cmdline;
+/* The password.  */
+char *password;
+/* The password type.  */
+password_t password_type;
+/* The flag for indicating that the user is authoritative.  */
+int auth = 0;
+/* The timeout.  */
+int grub_timeout = -1;
+/* Whether to show the menu or not.  */
+int show_menu = 1;
+/* The BIOS drive map.  */
+static unsigned short bios_drive_map[DRIVE_MAP_SIZE + 1];
+
+/* Prototypes for allowing straightfoward calling of builtins functions
+   inside other functions.  */
+static int configfile_func (char *arg, int flags);
+
+/* Initialize the data for builtins.  */
+void
+init_builtins (void)
+{
+  kernel_type = KERNEL_TYPE_NONE;
+  /* BSD and chainloading evil hacks!  */
+  bootdev = set_bootdev (0);
+  mb_cmdline = (char *) MB_CMDLINE_BUF;
+}
+
+/* Initialize the data for the configuration file.  */
+void
+init_config (void)
+{
+  default_entry = 0;
+  password = 0;
+  fallback_entry = -1;
+  grub_timeout = -1;
+}
+
+/* Check a password for correctness.  Returns 0 if password was
+   correct, and a value != 0 for error, similarly to strcmp. */
+int
+check_password (char *entered, char* expected, password_t type)
+{
+  switch (type)
+    {
+    case PASSWORD_PLAIN:
+      return strcmp (entered, expected);
+
+#ifdef USE_MD5_PASSWORDS
+    case PASSWORD_MD5:
+      return check_md5_password (entered, expected);
+#endif
+    default: 
+      /* unsupported password type: be secure */
+      return 1;
+    }
+}
+
+/* Print which sector is read when loading a file.  */
+static void
+disk_read_print_func (int sector, int offset, int length)
+{
+  grub_printf ("[%d,%d,%d]", sector, offset, length);
+}
+
+
+/* blocklist */
+static int
+blocklist_func (char *arg, int flags)
+{
+  char *dummy = (char *) RAW_ADDR (0x100000);
+  int start_sector;
+  int num_sectors = 0;
+  int num_entries = 0;
+  int last_length = 0;
+
+  /* Collect contiguous blocks into one entry as many as possible,
+     and print the blocklist notation on the screen.  */
+  static void disk_read_blocklist_func (int sector, int offset, int length)
+    {
+      if (num_sectors > 0)
+	{
+	  if (start_sector + num_sectors == sector
+	      && offset == 0 && last_length == SECTOR_SIZE)
+	    {
+	      num_sectors++;
+	      last_length = length;
+	      return;
+	    }
+	  else
+	    {
+	      if (last_length == SECTOR_SIZE)
+		grub_printf ("%s%d+%d", num_entries ? "," : "",
+			     start_sector - part_start, num_sectors);
+	      else if (num_sectors > 1)
+		grub_printf ("%s%d+%d,%d[0-%d]", num_entries ? "," : "",
+			     start_sector - part_start, num_sectors-1,
+			     start_sector + num_sectors-1 - part_start, 
+			     last_length);
+	      else
+		grub_printf ("%s%d[0-%d]", num_entries ? "," : "",
+			     start_sector - part_start, last_length);
+	      num_entries++;
+	      num_sectors = 0;
+	    }
+	}
+
+      if (offset > 0)
+	{
+	  grub_printf("%s%d[%d-%d]", num_entries ? "," : "",
+		      sector-part_start, offset, offset+length);
+	  num_entries++;
+	}
+      else
+	{
+	  start_sector = sector;
+	  num_sectors = 1;
+	  last_length = length;
+	}
+    }
+
+  /* Open the file.  */
+  if (! grub_open (arg))
+    return 1;
+
+  /* Print the device name.  */
+  grub_printf ("(%cd%d",
+	       (current_drive & 0x80) ? 'h' : 'f',
+	       current_drive & ~0x80);
+  
+  if ((current_partition & 0xFF0000) != 0xFF0000)
+    grub_printf (",%d", (current_partition >> 16) & 0xFF);
+  
+  if ((current_partition & 0x00FF00) != 0x00FF00)
+    grub_printf (",%c", 'a' + ((current_partition >> 8) & 0xFF));
+  
+  grub_printf (")");
+
+  /* Read in the whole file to DUMMY.  */
+  disk_read_hook = disk_read_blocklist_func;
+  if (! grub_read (dummy, -1))
+    goto fail;
+
+  /* The last entry may not be printed yet.  Don't check if it is a
+   * full sector, since it doesn't matter if we read too much. */
+  if (num_sectors > 0)
+    grub_printf ("%s%d+%d", num_entries ? "," : "",
+		 start_sector - part_start, num_sectors);
+
+  grub_printf ("\n");
+  
+ fail:
+  disk_read_hook = 0;
+  grub_close ();
+  return errnum;
+}
+
+static struct builtin builtin_blocklist =
+{
+  "blocklist",
+  blocklist_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "blocklist FILE",
+  "Print the blocklist notation of the file FILE."
+};
+
+/* boot */
+static int
+boot_func (char *arg, int flags)
+{
+  /* Clear the int15 handler if we can boot the kernel successfully.
+     This assumes that the boot code never fails only if KERNEL_TYPE is
+     not KERNEL_TYPE_NONE. Is this assumption is bad?  */
+  if (kernel_type != KERNEL_TYPE_NONE)
+    unset_int15_handler ();
+
+#ifdef SUPPORT_NETBOOT
+  /* Shut down the networking.  */
+  cleanup_net ();
+#endif
+  
+  switch (kernel_type)
+    {
+    case KERNEL_TYPE_FREEBSD:
+    case KERNEL_TYPE_NETBSD:
+      /* *BSD */
+      bsd_boot (kernel_type, bootdev, (char *) mbi.cmdline);
+      break;
+
+    case KERNEL_TYPE_LINUX:
+      /* Linux */
+      linux_boot ();
+      break;
+
+    case KERNEL_TYPE_BIG_LINUX:
+      /* Big Linux */
+      big_linux_boot ();
+      break;
+
+    case KERNEL_TYPE_CHAINLOADER:
+      /* Chainloader */
+      
+      /* Check if we should set the int13 handler.  */
+      if (bios_drive_map[0] != 0)
+	{
+	  int i;
+	  
+	  /* Search for SAVED_DRIVE.  */
+	  for (i = 0; i < DRIVE_MAP_SIZE; i++)
+	    {
+	      if (! bios_drive_map[i])
+		break;
+	      else if ((bios_drive_map[i] & 0xFF) == saved_drive)
+		{
+		  /* Exchage SAVED_DRIVE with the mapped drive.  */
+		  saved_drive = (bios_drive_map[i] >> 8) & 0xFF;
+		  break;
+		}
+	    }
+	  
+	  /* Set the handler. This is somewhat dangerous.  */
+	  set_int13_handler (bios_drive_map);
+	}
+      
+      gateA20 (0);
+      boot_drive = saved_drive;
+      chain_stage1 (0, BOOTSEC_LOCATION, boot_part_addr);
+      break;
+
+    case KERNEL_TYPE_MULTIBOOT:
+      /* Multiboot */
+      multi_boot ((int) entry_addr, (int) &mbi);
+      break;
+
+    default:
+      errnum = ERR_BOOT_COMMAND;
+      return 1;
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_boot =
+{
+  "boot",
+  boot_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "boot",
+  "Boot the OS/chain-loader which has been loaded."
+};
+
+
+#ifdef SUPPORT_NETBOOT
+/* bootp */
+static int
+bootp_func (char *arg, int flags)
+{
+  int with_configfile = 0;
+
+  if (grub_memcmp (arg, "--with-configfile", sizeof ("--with-configfile") - 1)
+      == 0)
+    {
+      with_configfile = 1;
+      arg = skip_to (0, arg);
+    }
+  
+  if (! bootp ())
+    {
+      if (errnum == ERR_NONE)
+	errnum = ERR_DEV_VALUES;
+
+      return 1;
+    }
+
+  /* Notify the configuration.  */
+  print_network_configuration ();
+
+  /* XXX: this can cause an endless loop, but there is no easy way to
+     detect such a loop unfortunately.  */
+  if (with_configfile)
+    configfile_func (config_file, flags);
+  
+  return 0;
+}
+
+static struct builtin builtin_bootp =
+{
+  "bootp",
+  bootp_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "bootp [--with-configfile]",
+  "Initialize a network device via BOOTP. If the option `--with-configfile'"
+  " is given, try to load a configuration file specified by the 150 vendor"
+  " tag."
+};
+#endif /* SUPPORT_NETBOOT */
+
+
+/* cat */
+static int
+cat_func (char *arg, int flags)
+{
+  char c;
+
+  if (! grub_open (arg))
+    return 1;
+
+  while (grub_read (&c, 1))
+    {
+      /* Because running "cat" with a binary file can confuse the terminal,
+	 print only some characters as they are.  */
+      if (grub_isspace (c) || (c >= ' ' && c <= '~'))
+	grub_putchar (c);
+      else
+	grub_putchar ('?');
+    }
+  
+  grub_close ();
+  return 0;
+}
+
+static struct builtin builtin_cat =
+{
+  "cat",
+  cat_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "cat FILE",
+  "Print the contents of the file FILE."
+};
+
+
+/* chainloader */
+static int
+chainloader_func (char *arg, int flags)
+{
+  int force = 0;
+  char *file = arg;
+
+  /* If the option `--force' is specified?  */
+  if (substring ("--force", arg) <= 0)
+    {
+      force = 1;
+      file = skip_to (0, arg);
+    }
+
+  /* Open the file.  */
+  if (! grub_open (file))
+    {
+      kernel_type = KERNEL_TYPE_NONE;
+      return 1;
+    }
+
+  /* Read the first block.  */
+  if (grub_read ((char *) BOOTSEC_LOCATION, SECTOR_SIZE) != SECTOR_SIZE)
+    {
+      grub_close ();
+      kernel_type = KERNEL_TYPE_NONE;
+
+      /* This below happens, if a file whose size is less than 512 bytes
+	 is loaded.  */
+      if (errnum == ERR_NONE)
+	errnum = ERR_EXEC_FORMAT;
+      
+      return 1;
+    }
+
+  /* If not loading it forcibly, check for the signature.  */
+  if (! force
+      && (*((unsigned short *) (BOOTSEC_LOCATION + BOOTSEC_SIG_OFFSET))
+	  != BOOTSEC_SIGNATURE))
+    {
+      grub_close ();
+      errnum = ERR_EXEC_FORMAT;
+      kernel_type = KERNEL_TYPE_NONE;
+      return 1;
+    }
+
+  grub_close ();
+  kernel_type = KERNEL_TYPE_CHAINLOADER;
+
+  /* XXX: Windows evil hack. For now, only the first five letters are
+     checked.  */
+  if (IS_PC_SLICE_TYPE_FAT (current_slice)
+      && ! grub_memcmp ((char *) BOOTSEC_LOCATION + BOOTSEC_BPB_SYSTEM_ID,
+			"MSWIN", 5))
+    *((unsigned long *) (BOOTSEC_LOCATION + BOOTSEC_BPB_HIDDEN_SECTORS))
+      = part_start;
+
+  errnum = ERR_NONE;
+  
+  return 0;
+}
+
+static struct builtin builtin_chainloader =
+{
+  "chainloader",
+  chainloader_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "chainloader [--force] FILE",
+  "Load the chain-loader FILE. If --force is specified, then load it"
+  " forcibly, whether the boot loader signature is present or not."
+};
+
+
+/* This function could be used to debug new filesystem code. Put a file
+   in the new filesystem and the same file in a well-tested filesystem.
+   Then, run "cmp" with the files. If no output is obtained, probably
+   the code is good, otherwise investigate what's wrong...  */
+/* cmp FILE1 FILE2 */
+static int
+cmp_func (char *arg, int flags)
+{
+  /* The filenames.  */
+  char *file1, *file2;
+  /* The addresses.  */
+  char *addr1, *addr2;
+  int i;
+  /* The size of the file.  */
+  int size;
+
+  /* Get the filenames from ARG.  */
+  file1 = arg;
+  file2 = skip_to (0, arg);
+  if (! *file1 || ! *file2)
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+
+  /* Terminate the filenames for convenience.  */
+  nul_terminate (file1);
+  nul_terminate (file2);
+
+  /* Read the whole data from FILE1.  */
+  addr1 = (char *) RAW_ADDR (0x100000);
+  if (! grub_open (file1))
+    return 1;
+  
+  /* Get the size.  */
+  size = filemax;
+  if (grub_read (addr1, -1) != size)
+    {
+      grub_close ();
+      return 1;
+    }
+  
+  grub_close ();
+
+  /* Read the whole data from FILE2.  */
+  addr2 = addr1 + size;
+  if (! grub_open (file2))
+    return 1;
+
+  /* Check if the size of FILE2 is equal to the one of FILE2.  */
+  if (size != filemax)
+    {
+      grub_printf ("Differ in size: 0x%x [%s], 0x%x [%s]\n",
+		   size, file1, filemax, file2);
+      grub_close ();
+      return 0;
+    }
+  
+  if (! grub_read (addr2, -1))
+    {
+      grub_close ();
+      return 1;
+    }
+  
+  grub_close ();
+
+  /* Now compare ADDR1 with ADDR2.  */
+  for (i = 0; i < size; i++)
+    {
+      if (addr1[i] != addr2[i])
+	grub_printf ("Differ at the offset %d: 0x%x [%s], 0x%x [%s]\n",
+		     i, (unsigned) addr1[i], file1,
+		     (unsigned) addr2[i], file2);
+    }
+  
+  return 0;
+}
+
+static struct builtin builtin_cmp =
+{
+  "cmp",
+  cmp_func,
+  BUILTIN_CMDLINE,
+  "cmp FILE1 FILE2",
+  "Compare the file FILE1 with the FILE2 and inform the different values"
+  " if any."
+};
+
+
+/* color */
+/* Set new colors used for the menu interface. Support two methods to
+   specify a color name: a direct integer representation and a symbolic
+   color name. An example of the latter is "blink-light-gray/blue".  */
+static int
+color_func (char *arg, int flags)
+{
+  char *normal;
+  char *highlight;
+  int new_normal_color;
+  int new_highlight_color;
+  static char *color_list[16] =
+  {
+    "black",
+    "blue",
+    "green",
+    "cyan",
+    "red",
+    "magenta",
+    "brown",
+    "light-gray",
+    "dark-gray",
+    "light-blue",
+    "light-green",
+    "light-cyan",
+    "light-red",
+    "light-magenta",
+    "yellow",
+    "white"
+  };
+
+  /* Convert the color name STR into the magical number.  */
+  static int color_number (char *str)
+    {
+      char *ptr;
+      int i;
+      int color = 0;
+      
+      /* Find the separator.  */
+      for (ptr = str; *ptr && *ptr != '/'; ptr++)
+	;
+
+      /* If not found, return -1.  */
+      if (! *ptr)
+	return -1;
+
+      /* Terminate the string STR.  */
+      *ptr++ = 0;
+
+      /* If STR contains the prefix "blink-", then set the `blink' bit
+	 in COLOR.  */
+      if (substring ("blink-", str) <= 0)
+	{
+	  color = 0x80;
+	  str += 6;
+	}
+      
+      /* Search for the color name.  */
+      for (i = 0; i < 16; i++)
+	if (grub_strcmp (color_list[i], str) == 0)
+	  {
+	    color |= i;
+	    break;
+	  }
+
+      if (i == 16)
+	return -1;
+
+      str = ptr;
+      nul_terminate (str);
+
+      /* Search for the color name.  */      
+      for (i = 0; i < 8; i++)
+	if (grub_strcmp (color_list[i], str) == 0)
+	  {
+	    color |= i << 4;
+	    break;
+	  }
+
+      if (i == 8)
+	return -1;
+
+      return color;
+    }
+      
+  normal = arg;
+  highlight = skip_to (0, arg);
+
+  new_normal_color = color_number (normal);
+  if (new_normal_color < 0 && ! safe_parse_maxint (&normal, &new_normal_color))
+    return 1;
+  
+  /* The second argument is optional, so set highlight_color
+     to inverted NORMAL_COLOR.  */
+  if (! *highlight)
+    new_highlight_color = ((new_normal_color >> 4)
+			   | ((new_normal_color & 0xf) << 4));
+  else
+    {
+      new_highlight_color = color_number (highlight);
+      if (new_highlight_color < 0
+	  && ! safe_parse_maxint (&highlight, &new_highlight_color))
+	return 1;
+    }
+
+  if (current_term->setcolor)
+    current_term->setcolor (new_normal_color, new_highlight_color);
+  
+  return 0;
+}
+
+static struct builtin builtin_color =
+{
+  "color",
+  color_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "color NORMAL [HIGHLIGHT]",
+  "Change the menu colors. The color NORMAL is used for most"
+  " lines in the menu, and the color HIGHLIGHT is used to highlight the"
+  " line where the cursor points. If you omit HIGHLIGHT, then the"
+  " inverted color of NORMAL is used for the highlighted line."
+  " The format of a color is \"FG/BG\". FG and BG are symbolic color names."
+  " A symbolic color name must be one of these: black, blue, green,"
+  " cyan, red, magenta, brown, light-gray, dark-gray, light-blue,"
+  " light-green, light-cyan, light-red, light-magenta, yellow and white."
+  " But only the first eight names can be used for BG. You can prefix"
+  " \"blink-\" to FG if you want a blinking foreground color."
+};
+
+
+/* configfile */
+static int
+configfile_func (char *arg, int flags)
+{
+  char *new_config = config_file;
+
+  /* Check if the file ARG is present.  */
+  if (! grub_open (arg))
+    return 1;
+
+  grub_close ();
+  
+  /* Copy ARG to CONFIG_FILE.  */
+  while ((*new_config++ = *arg++) != 0)
+    ;
+
+#ifdef GRUB_UTIL
+  /* Force to load the configuration file.  */
+  use_config_file = 1;
+#endif
+
+  /* Make sure that the user will not be authoritative.  */
+  auth = 0;
+  
+  /* Restart cmain.  */
+  grub_longjmp (restart_env, 0);
+
+  /* Never reach here.  */
+  return 0;
+}
+
+static struct builtin builtin_configfile =
+{
+  "configfile",
+  configfile_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "configfile FILE",
+  "Load FILE as the configuration file."
+};
+
+
+/* debug */
+static int
+debug_func (char *arg, int flags)
+{
+  if (debug)
+    {
+      debug = 0;
+      grub_printf (" Debug mode is turned off\n");
+    }
+  else
+    {
+      debug = 1;
+      grub_printf (" Debug mode is turned on\n");
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_debug =
+{
+  "debug",
+  debug_func,
+  BUILTIN_CMDLINE,
+  "debug",
+  "Turn on/off the debug mode."
+};
+
+
+/* default */
+static int
+default_func (char *arg, int flags)
+{
+#ifndef SUPPORT_DISKLESS
+  if (grub_strcmp (arg, "saved") == 0)
+    {
+      default_entry = saved_entryno;
+      return 0;
+    }
+#endif /* SUPPORT_DISKLESS */
+  
+  if (! safe_parse_maxint (&arg, &default_entry))
+    return 1;
+
+  return 0;
+}
+
+static struct builtin builtin_default =
+{
+  "default",
+  default_func,
+  BUILTIN_MENU,
+#if 0
+  "default [NUM | `saved']",
+  "Set the default entry to entry number NUM (if not specified, it is"
+  " 0, the first entry) or the entry number saved by savedefault."
+#endif
+};
+
+
+#ifdef GRUB_UTIL
+/* device */
+static int
+device_func (char *arg, int flags)
+{
+  char *drive = arg;
+  char *device;
+
+  /* Get the drive number from DRIVE.  */
+  if (! set_device (drive))
+    return 1;
+
+  /* Get the device argument.  */
+  device = skip_to (0, drive);
+  
+  /* Terminate DEVICE.  */
+  nul_terminate (device);
+
+  if (! *device || ! check_device (device))
+    {
+      errnum = ERR_FILE_NOT_FOUND;
+      return 1;
+    }
+
+  assign_device_name (current_drive, device);
+  
+  return 0;
+}
+
+static struct builtin builtin_device =
+{
+  "device",
+  device_func,
+  BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "device DRIVE DEVICE",
+  "Specify DEVICE as the actual drive for a BIOS drive DRIVE. This command"
+  " can be used only in the grub shell."
+};
+#endif /* GRUB_UTIL */
+
+
+#ifdef SUPPORT_NETBOOT
+/* dhcp */
+static int
+dhcp_func (char *arg, int flags)
+{
+  /* For now, this is an alias for bootp.  */
+  return bootp_func (arg, flags);
+}
+
+static struct builtin builtin_dhcp =
+{
+  "dhcp",
+  dhcp_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "dhcp",
+  "Initialize a network device via DHCP."
+};
+#endif /* SUPPORT_NETBOOT */
+
+
+/* displayapm */
+static int
+displayapm_func (char *arg, int flags)
+{
+  if (mbi.flags & MB_INFO_APM_TABLE)
+    {
+      grub_printf ("APM BIOS information:\n"
+		   " Version:          0x%x\n"
+		   " 32-bit CS:        0x%x\n"
+		   " Offset:           0x%x\n"
+		   " 16-bit CS:        0x%x\n"
+		   " 16-bit DS:        0x%x\n"
+		   " 32-bit CS length: 0x%x\n"
+		   " 16-bit CS length: 0x%x\n"
+		   " 16-bit DS length: 0x%x\n",
+		   (unsigned) apm_bios_info.version,
+		   (unsigned) apm_bios_info.cseg,
+		   apm_bios_info.offset,
+		   (unsigned) apm_bios_info.cseg_16,
+		   (unsigned) apm_bios_info.dseg_16,
+		   (unsigned) apm_bios_info.cseg_len,
+		   (unsigned) apm_bios_info.cseg_16_len,
+		   (unsigned) apm_bios_info.dseg_16_len);
+    }
+  else
+    {
+      grub_printf ("No APM BIOS found or probe failed\n");
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_displayapm =
+{
+  "displayapm",
+  displayapm_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "displayapm",
+  "Display APM BIOS information."
+};
+
+
+/* displaymem */
+static int
+displaymem_func (char *arg, int flags)
+{
+  if (get_eisamemsize () != -1)
+    grub_printf (" EISA Memory BIOS Interface is present\n");
+  if (get_mmap_entry ((void *) SCRATCHADDR, 0) != 0
+      || *((int *) SCRATCHADDR) != 0)
+    grub_printf (" Address Map BIOS Interface is present\n");
+
+  grub_printf (" Lower memory: %uK, "
+	       "Upper memory (to first chipset hole): %uK\n",
+	       mbi.mem_lower, mbi.mem_upper);
+
+  if (mbi.flags & MB_INFO_MEM_MAP)
+    {
+      struct AddrRangeDesc *map = (struct AddrRangeDesc *) mbi.mmap_addr;
+      int end_addr = mbi.mmap_addr + mbi.mmap_length;
+
+      grub_printf (" [Address Range Descriptor entries "
+		   "immediately follow (values are 64-bit)]\n");
+      while (end_addr > (int) map)
+	{
+	  char *str;
+
+	  if (map->Type == MB_ARD_MEMORY)
+	    str = "Usable RAM";
+	  else
+	    str = "Reserved";
+	  grub_printf ("   %s:  Base Address:  0x%x X 4GB + 0x%x,\n"
+		       "      Length:   0x%x X 4GB + 0x%x bytes\n",
+		       str,
+		       (unsigned long) (map->BaseAddr >> 32),
+		       (unsigned long) (map->BaseAddr & 0xFFFFFFFF),
+		       (unsigned long) (map->Length >> 32),
+		       (unsigned long) (map->Length & 0xFFFFFFFF));
+
+	  map = ((struct AddrRangeDesc *) (((int) map) + 4 + map->size));
+	}
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_displaymem =
+{
+  "displaymem",
+  displaymem_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "displaymem",
+  "Display what GRUB thinks the system address space map of the"
+  " machine is, including all regions of physical RAM installed."
+};
+
+
+/* dump FROM TO */
+#ifdef GRUB_UTIL
+static int
+dump_func (char *arg, int flags)
+{
+  char *from, *to;
+  FILE *fp;
+  char c;
+  
+  from = arg;
+  to = skip_to (0, arg);
+  if (! *from || ! *to)
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+
+  nul_terminate (from);
+  nul_terminate (to);
+  
+  if (! grub_open (from))
+    return 1;
+
+  fp = fopen (to, "w");
+  if (! fp)
+    {
+      errnum = ERR_WRITE;
+      return 1;
+    }
+
+  while (grub_read (&c, 1))
+    if (fputc (c, fp) == EOF)
+      {
+	errnum = ERR_WRITE;
+	fclose (fp);
+	return 1;
+      }
+
+  if (fclose (fp) == EOF)
+    {
+      errnum = ERR_WRITE;
+      return 1;
+    }
+
+  grub_close ();
+  return 0;
+}
+
+static struct builtin builtin_dump =
+  {
+    "dump",
+    dump_func,
+    BUILTIN_CMDLINE,
+    "dump FROM TO",
+    "Dump the contents of the file FROM to the file TO. FROM must be"
+    " a GRUB file and TO must be an OS file."
+  };
+#endif /* GRUB_UTIL */
+
+
+static char embed_info[32];
+/* embed */
+/* Embed a Stage 1.5 in the first cylinder after MBR or in the
+   bootloader block in a FFS.  */
+static int
+embed_func (char *arg, int flags)
+{
+  char *stage1_5;
+  char *device;
+  char *stage1_5_buffer = (char *) RAW_ADDR (0x100000);
+  int len, size;
+  int sector;
+  
+  stage1_5 = arg;
+  device = skip_to (0, stage1_5);
+
+  /* Open a Stage 1.5.  */
+  if (! grub_open (stage1_5))
+    return 1;
+
+  /* Read the whole of the Stage 1.5.  */
+  len = grub_read (stage1_5_buffer, -1);
+  grub_close ();
+  
+  if (errnum)
+    return 1;
+  
+  size = (len + SECTOR_SIZE - 1) / SECTOR_SIZE;
+  
+  /* Get the device where the Stage 1.5 will be embedded.  */
+  set_device (device);
+  if (errnum)
+    return 1;
+
+  if (current_partition == 0xFFFFFF)
+    {
+      /* Embed it after the MBR.  */
+      
+      char mbr[SECTOR_SIZE];
+      char ezbios_check[2*SECTOR_SIZE];
+      int i;
+      
+      /* Open the partition.  */
+      if (! open_partition ())
+	return 1;
+
+      /* No floppy has MBR.  */
+      if (! (current_drive & 0x80))
+	{
+	  errnum = ERR_DEV_VALUES;
+	  return 1;
+	}
+      
+      /* Read the MBR of CURRENT_DRIVE.  */
+      if (! rawread (current_drive, PC_MBR_SECTOR, 0, SECTOR_SIZE, mbr))
+	return 1;
+      
+      /* Sanity check.  */
+      if (! PC_MBR_CHECK_SIG (mbr))
+	{
+	  errnum = ERR_BAD_PART_TABLE;
+	  return 1;
+	}
+
+      /* Check if the disk can store the Stage 1.5.  */
+      for (i = 0; i < 4; i++)
+	if (PC_SLICE_TYPE (mbr, i) && PC_SLICE_START (mbr, i) - 1 < size)
+	  {
+	    errnum = ERR_NO_DISK_SPACE;
+	    return 1;
+	  }
+      
+      /* Check for EZ-BIOS signature. It should be in the third
+       * sector, but due to remapping it can appear in the second, so
+       * load and check both.  
+       */
+      if (! rawread (current_drive, 1, 0, 2 * SECTOR_SIZE, ezbios_check))
+	return 1;
+
+      if (! memcmp (ezbios_check + 3, "AERMH", 5)
+	  || ! memcmp (ezbios_check + 512 + 3, "AERMH", 5))
+	{
+	  /* The space after the MBR is used by EZ-BIOS which we must 
+	   * not overwrite.
+	   */
+	  errnum = ERR_NO_DISK_SPACE;
+	  return 1;
+	}
+
+      sector = 1;
+    }
+  else
+    {
+      /* Embed it in the bootloader block in the filesystem.  */
+      int start_sector;
+      
+      /* Open the partition.  */
+      if (! open_device ())
+	return 1;
+
+      /* Check if the current slice supports embedding.  */
+      if (fsys_table[fsys_type].embed_func == 0
+	  || ! fsys_table[fsys_type].embed_func (&start_sector, size))
+	{
+	  errnum = ERR_DEV_VALUES;
+	  return 1;
+	}
+
+      sector = part_start + start_sector;
+    }
+
+  /* Clear the cache.  */
+  buf_track = -1;
+
+  /* Now perform the embedding.  */
+  if (! devwrite (sector - part_start, size, stage1_5_buffer))
+    return 1;
+  
+  grub_printf (" %d sectors are embedded.\n", size);
+  grub_sprintf (embed_info, "%d+%d", sector - part_start, size);
+  return 0;
+}
+
+static struct builtin builtin_embed =
+{
+  "embed",
+  embed_func,
+  BUILTIN_CMDLINE,
+  "embed STAGE1_5 DEVICE",
+  "Embed the Stage 1.5 STAGE1_5 in the sectors after MBR if DEVICE"
+  " is a drive, or in the \"bootloader\" area if DEVICE is a FFS partition."
+  " Print the number of sectors which STAGE1_5 occupies if successful."
+};
+
+
+/* fallback */
+static int
+fallback_func (char *arg, int flags)
+{
+  if (! safe_parse_maxint (&arg, &fallback_entry))
+    return 1;
+
+  return 0;
+}
+
+static struct builtin builtin_fallback =
+{
+  "fallback",
+  fallback_func,
+  BUILTIN_MENU,
+#if 0
+  "fallback NUM",
+  "Go into unattended boot mode: if the default boot entry has any"
+  " errors, instead of waiting for the user to do anything, it"
+  " immediately starts over using the NUM entry (same numbering as the"
+  " `default' command). This obviously won't help if the machine"
+  " was rebooted by a kernel that GRUB loaded."
+#endif
+};
+
+
+/* find */
+/* Search for the filename ARG in all of partitions.  */
+static int
+find_func (char *arg, int flags)
+{
+  char *filename = arg;
+  unsigned long drive;
+  unsigned long tmp_drive = saved_drive;
+  unsigned long tmp_partition = saved_partition;
+  int got_file = 0;
+  
+  /* Floppies.  */
+  for (drive = 0; drive < 8; drive++)
+    {
+      current_drive = drive;
+      current_partition = 0xFFFFFF;
+      
+      if (open_device ())
+	{
+	  saved_drive = current_drive;
+	  saved_partition = current_partition;
+	  if (grub_open (filename))
+	    {
+	      grub_close ();
+	      grub_printf (" (fd%d)\n", drive);
+	      got_file = 1;
+	    }
+	}
+
+      errnum = ERR_NONE;
+    }
+
+  /* Hard disks.  */
+  for (drive = 0x80; drive < 0x88; drive++)
+    {
+      unsigned long part = 0xFFFFFF;
+      unsigned long start, len, offset, ext_offset;
+      int type, entry;
+      char buf[SECTOR_SIZE];
+
+      current_drive = drive;
+      while (next_partition (drive, 0xFFFFFF, &part, &type,
+			     &start, &len, &offset, &entry,
+			     &ext_offset, buf))
+	{
+	  if (type != PC_SLICE_TYPE_NONE
+	      && ! IS_PC_SLICE_TYPE_BSD (type)
+	      && ! IS_PC_SLICE_TYPE_EXTENDED (type))
+	    {
+	      current_partition = part;
+	      if (open_device ())
+		{
+		  saved_drive = current_drive;
+		  saved_partition = current_partition;
+		  if (grub_open (filename))
+		    {
+		      int bsd_part = (part >> 8) & 0xFF;
+		      int pc_slice = part >> 16;
+		      
+		      grub_close ();
+		      
+		      if (bsd_part == 0xFF)
+			grub_printf (" (hd%d,%d)\n",
+				     drive - 0x80, pc_slice);
+		      else
+			grub_printf (" (hd%d,%d,%c)\n",
+				     drive - 0x80, pc_slice, bsd_part + 'a');
+
+		      got_file = 1;
+		    }
+		}
+	    }
+
+	  /* We want to ignore any error here.  */
+	  errnum = ERR_NONE;
+	}
+
+      /* next_partition always sets ERRNUM in the last call, so clear
+	 it.  */
+      errnum = ERR_NONE;
+    }
+
+  saved_drive = tmp_drive;
+  saved_partition = tmp_partition;
+
+  if (got_file)
+    {
+      errnum = ERR_NONE;
+      return 0;
+    }
+
+  errnum = ERR_FILE_NOT_FOUND;
+  return 1;
+}
+
+static struct builtin builtin_find =
+{
+  "find",
+  find_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "find FILENAME",
+  "Search for the filename FILENAME in all of partitions and print the list of"
+  " the devices which contain the file."
+};
+
+
+/* fstest */
+static int
+fstest_func (char *arg, int flags)
+{
+  if (disk_read_hook)
+    {
+      disk_read_hook = NULL;
+      printf (" Filesystem tracing is now off\n");
+    }
+  else
+    {
+      disk_read_hook = disk_read_print_func;
+      printf (" Filesystem tracing is now on\n");
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_fstest =
+{
+  "fstest",
+  fstest_func,
+  BUILTIN_CMDLINE,
+  "fstest",
+  "Toggle filesystem test mode."
+};
+
+
+/* geometry */
+static int
+geometry_func (char *arg, int flags)
+{
+  struct geometry geom;
+  char *msg;
+  char *device = arg;
+#ifdef GRUB_UTIL
+  char *ptr;
+#endif
+
+  /* Get the device number.  */
+  set_device (device);
+  if (errnum)
+    return 1;
+
+  /* Check for the geometry.  */
+  if (get_diskinfo (current_drive, &geom))
+    {
+      errnum = ERR_NO_DISK;
+      return 1;
+    }
+
+  /* Attempt to read the first sector, because some BIOSes turns out not
+     to support LBA even though they set the bit 0 in the support
+     bitmap, only after reading something actually.  */
+  if (biosdisk (BIOSDISK_READ, current_drive, &geom, 0, 1, SCRATCHSEG))
+    {
+      errnum = ERR_READ;
+      return 1;
+    }
+
+#ifdef GRUB_UTIL
+  ptr = skip_to (0, device);
+  if (*ptr)
+    {
+      char *cylinder, *head, *sector, *total_sector;
+      int num_cylinder, num_head, num_sector, num_total_sector;
+
+      cylinder = ptr;
+      head = skip_to (0, cylinder);
+      sector = skip_to (0, head);
+      total_sector = skip_to (0, sector);
+      if (! safe_parse_maxint (&cylinder, &num_cylinder)
+	  || ! safe_parse_maxint (&head, &num_head)
+	  || ! safe_parse_maxint (&sector, &num_sector))
+	return 1;
+
+      disks[current_drive].cylinders = num_cylinder;
+      disks[current_drive].heads = num_head;
+      disks[current_drive].sectors = num_sector;
+
+      if (safe_parse_maxint (&total_sector, &num_total_sector))
+	disks[current_drive].total_sectors = num_total_sector;
+      else
+	disks[current_drive].total_sectors
+	  = num_cylinder * num_head * num_sector;
+      errnum = 0;
+
+      geom = disks[current_drive];
+      buf_drive = -1;
+    }
+#endif /* GRUB_UTIL */
+
+#ifdef GRUB_UTIL
+  msg = device_map[current_drive];
+#else
+  if (geom.flags & BIOSDISK_FLAG_LBA_EXTENSION)
+    msg = "LBA";
+  else
+    msg = "CHS";
+#endif
+
+  grub_printf ("drive 0x%x: C/H/S = %d/%d/%d, "
+	       "The number of sectors = %d, %s\n",
+	       current_drive,
+	       geom.cylinders, geom.heads, geom.sectors,
+	       geom.total_sectors, msg);
+  real_open_partition (1);
+
+  return 0;
+}
+
+static struct builtin builtin_geometry =
+{
+  "geometry",
+  geometry_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "geometry DRIVE [CYLINDER HEAD SECTOR [TOTAL_SECTOR]]",
+  "Print the information for a drive DRIVE. In the grub shell, you can"
+  " set the geometry of the drive arbitrarily. The number of the cylinders,"
+  " the one of the heads, the one of the sectors and the one of the total"
+  " sectors are set to CYLINDER, HEAD, SECTOR and TOTAL_SECTOR,"
+  " respectively. If you omit TOTAL_SECTOR, then it will be calculated based"
+  " on the C/H/S values automatically."
+};
+
+
+/* halt */
+static int
+halt_func (char *arg, int flags)
+{
+  int no_apm;
+
+  no_apm = (grub_memcmp (arg, "--no-apm", 8) == 0);
+  grub_halt (no_apm);
+  
+  /* Never reach here.  */
+  return 1;
+}
+
+static struct builtin builtin_halt =
+{
+  "halt",
+  halt_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "halt [--no-apm]",
+  "Halt your system. If APM is avaiable on it, turn off the power using"
+  " the APM BIOS, unless you specify the option `--no-apm'."
+};
+
+
+/* help */
+#define MAX_SHORT_DOC_LEN	39
+#define MAX_LONG_DOC_LEN	66
+
+static int
+help_func (char *arg, int flags)
+{
+  int all = 0;
+  
+  if (grub_memcmp (arg, "--all", sizeof ("--all") - 1) == 0)
+    {
+      all = 1;
+      arg = skip_to (0, arg);
+    }
+  
+  if (! *arg)
+    {
+      /* Invoked with no argument. Print the list of the short docs.  */
+      struct builtin **builtin;
+      int left = 1;
+
+      for (builtin = builtin_table; *builtin != 0; builtin++)
+	{
+	  int len;
+	  int i;
+
+	  /* If this cannot be used in the command-line interface,
+	     skip this.  */
+	  if (! ((*builtin)->flags & BUILTIN_CMDLINE))
+	    continue;
+	  
+	  /* If this doesn't need to be listed automatically and "--all"
+	     is not specified, skip this.  */
+	  if (! all && ! ((*builtin)->flags & BUILTIN_HELP_LIST))
+	    continue;
+
+	  len = grub_strlen ((*builtin)->short_doc);
+	  /* If the length of SHORT_DOC is too long, truncate it.  */
+	  if (len > MAX_SHORT_DOC_LEN - 1)
+	    len = MAX_SHORT_DOC_LEN - 1;
+
+	  for (i = 0; i < len; i++)
+	    grub_putchar ((*builtin)->short_doc[i]);
+
+	  for (; i < MAX_SHORT_DOC_LEN; i++)
+	    grub_putchar (' ');
+
+	  if (! left)
+	    grub_putchar ('\n');
+
+	  left = ! left;
+	}
+
+      /* If the last entry was at the left column, no newline was printed
+	 at the end.  */
+      if (! left)
+	grub_putchar ('\n');
+    }
+  else
+    {
+      /* Invoked with one or more patterns.  */
+      do
+	{
+	  struct builtin **builtin;
+	  char *next_arg;
+
+	  /* Get the next argument.  */
+	  next_arg = skip_to (0, arg);
+
+	  /* Terminate ARG.  */
+	  nul_terminate (arg);
+
+	  for (builtin = builtin_table; *builtin; builtin++)
+	    {
+	      /* Skip this if this is only for the configuration file.  */
+	      if (! ((*builtin)->flags & BUILTIN_CMDLINE))
+		continue;
+
+	      if (substring (arg, (*builtin)->name) < 1)
+		{
+		  char *doc = (*builtin)->long_doc;
+
+		  /* At first, print the name and the short doc.  */
+		  grub_printf ("%s: %s\n",
+			       (*builtin)->name, (*builtin)->short_doc);
+
+		  /* Print the long doc.  */
+		  while (*doc)
+		    {
+		      int len = grub_strlen (doc);
+		      int i;
+
+		      /* If LEN is too long, fold DOC.  */
+		      if (len > MAX_LONG_DOC_LEN)
+			{
+			  /* Fold this line at the position of a space.  */
+			  for (len = MAX_LONG_DOC_LEN; len > 0; len--)
+			    if (doc[len - 1] == ' ')
+			      break;
+			}
+
+		      grub_printf ("    ");
+		      for (i = 0; i < len; i++)
+			grub_putchar (*doc++);
+		      grub_putchar ('\n');
+		    }
+		}
+	    }
+
+	  arg = next_arg;
+	}
+      while (*arg);
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_help =
+{
+  "help",
+  help_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "help [--all] [PATTERN ...]",
+  "Display helpful information about builtin commands. Not all commands"
+  " aren't shown without the option `--all'."
+};
+
+
+/* hiddenmenu */
+static int
+hiddenmenu_func (char *arg, int flags)
+{
+  show_menu = 0;
+  return 0;
+}
+
+static struct builtin builtin_hiddenmenu =
+{
+  "hiddenmenu",
+  hiddenmenu_func,
+  BUILTIN_MENU,
+#if 0
+  "hiddenmenu",
+  "Hide the menu."
+#endif
+};
+
+
+/* hide */
+static int
+hide_func (char *arg, int flags)
+{
+  if (! set_device (arg))
+    return 1;
+
+  if (! set_partition_hidden_flag (1))
+    return 1;
+
+  return 0;
+}
+
+static struct builtin builtin_hide =
+{
+  "hide",
+  hide_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "hide PARTITION",
+  "Hide PARTITION by setting the \"hidden\" bit in"
+  " its partition type code."
+};
+
+
+#ifdef SUPPORT_NETBOOT
+/* ifconfig */
+static int
+ifconfig_func (char *arg, int flags)
+{
+  char *svr = 0, *ip = 0, *gw = 0, *sm = 0;
+  
+  if (! eth_probe ())
+    {
+      grub_printf ("No ethernet card found.\n");
+      errnum = ERR_DEV_VALUES;
+      return 1;
+    }
+  
+  while (*arg) 
+    {
+      if (! grub_memcmp ("--server=", arg, sizeof ("--server=") - 1))
+	svr = arg + sizeof("--server=") - 1;
+      else if (! grub_memcmp ("--address=", arg, sizeof ("--address=") - 1))
+	ip = arg + sizeof ("--address=") - 1;
+      else if (! grub_memcmp ("--gateway=", arg, sizeof ("--gateway=") - 1))
+	gw = arg + sizeof ("--gateway=") - 1;
+      else if (! grub_memcmp ("--mask=", arg, sizeof("--mask=") - 1))
+	sm = arg + sizeof ("--mask=") - 1;
+      else
+	{
+	  errnum = ERR_BAD_ARGUMENT;
+	  return 1;
+	}
+      
+      arg = skip_to (0, arg);
+    }
+  
+  if (! ifconfig (ip, sm, gw, svr))
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+  
+  print_network_configuration ();
+  return 0;
+}
+
+static struct builtin builtin_ifconfig =
+{
+  "ifconfig",
+  ifconfig_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "ifconfig [--address=IP] [--gateway=IP] [--mask=MASK] [--server=IP]",
+  "Configure the IP address, the netmask, the gateway and the server"
+  " address or print current network configuration."
+};
+#endif /* SUPPORT_NETBOOT */
+
+
+/* impsprobe */
+static int
+impsprobe_func (char *arg, int flags)
+{
+#ifdef GRUB_UTIL
+  /* In the grub shell, we cannot probe IMPS.  */
+  errnum = ERR_UNRECOGNIZED;
+  return 1;
+#else /* ! GRUB_UTIL */
+  if (!imps_probe ())
+    printf (" No MPS information found or probe failed\n");
+
+  return 0;
+#endif /* ! GRUB_UTIL */
+}
+
+static struct builtin builtin_impsprobe =
+{
+  "impsprobe",
+  impsprobe_func,
+  BUILTIN_CMDLINE,
+  "impsprobe",
+  "Probe the Intel Multiprocessor Specification 1.1 or 1.4"
+  " configuration table and boot the various CPUs which are found into"
+  " a tight loop."
+};
+
+
+/* initrd */
+static int
+initrd_func (char *arg, int flags)
+{
+  switch (kernel_type)
+    {
+    case KERNEL_TYPE_LINUX:
+    case KERNEL_TYPE_BIG_LINUX:
+      if (! load_initrd (arg))
+	return 1;
+      break;
+
+    default:
+      errnum = ERR_NEED_LX_KERNEL;
+      return 1;
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_initrd =
+{
+  "initrd",
+  initrd_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "initrd FILE [ARG ...]",
+  "Load an initial ramdisk FILE for a Linux format boot image and set the"
+  " appropriate parameters in the Linux setup area in memory."
+};
+
+
+/* install */
+static int
+install_func (char *arg, int flags)
+{
+  char *stage1_file, *dest_dev, *file, *addr;
+  char *stage1_buffer = (char *) RAW_ADDR (0x100000);
+  char *stage2_buffer = stage1_buffer + SECTOR_SIZE;
+  char *old_sect = stage2_buffer + SECTOR_SIZE;
+  char *stage2_first_buffer = old_sect + SECTOR_SIZE;
+  char *stage2_second_buffer = stage2_first_buffer + SECTOR_SIZE;
+  /* XXX: Probably SECTOR_SIZE is reasonable.  */
+  char *config_filename = stage2_second_buffer + SECTOR_SIZE;
+  char *dummy = config_filename + SECTOR_SIZE;
+  int new_drive = 0xFF;
+  int dest_drive, dest_partition, dest_sector;
+  int src_drive, src_partition, src_part_start;
+  int i;
+  struct geometry dest_geom, src_geom;
+  int saved_sector;
+  int stage2_first_sector, stage2_second_sector;
+  char *ptr;
+  int installaddr, installlist;
+  /* Point to the location of the name of a configuration file in Stage 2.  */
+  char *config_file_location;
+  /* If FILE is a Stage 1.5?  */
+  int is_stage1_5 = 0;
+  /* Must call grub_close?  */
+  int is_open = 0;
+  /* If LBA is forced?  */
+  int is_force_lba = 0;
+  /* Was the last sector full? */
+  int last_length = SECTOR_SIZE;
+  
+#ifdef GRUB_UTIL
+  /* If the Stage 2 is in a partition mounted by an OS, this will store
+     the filename under the OS.  */
+  char *stage2_os_file = 0;
+#endif /* GRUB_UTIL */
+  
+  /* Save the first sector of Stage2 in STAGE2_SECT.  */
+  static void disk_read_savesect_func (int sector, int offset, int length)
+    {
+      if (debug)
+	printf ("[%d]", sector);
+
+      /* ReiserFS has files which sometimes contain data not aligned
+         on sector boundaries.  Returning an error is better than
+         silently failing. */
+      if (offset != 0 || length != SECTOR_SIZE)
+	errnum = ERR_UNALIGNED;
+
+      saved_sector = sector;
+    }
+
+  /* Write SECTOR to INSTALLLIST, and update INSTALLADDR and
+     INSTALLSECT.  */
+  static void disk_read_blocklist_func (int sector, int offset, int length)
+    {
+      if (debug)
+	printf("[%d]", sector);
+
+      if (offset != 0 || last_length != SECTOR_SIZE)
+	{
+	  /* We found a non-sector-aligned data block. */
+	  errnum = ERR_UNALIGNED;
+	  return;
+	}
+
+      last_length = length;
+
+      if (*((unsigned long *) (installlist - 4))
+	  + *((unsigned short *) installlist) != sector
+	  || installlist == (int) stage2_first_buffer + SECTOR_SIZE + 4)
+	{
+	  installlist -= 8;
+
+	  if (*((unsigned long *) (installlist - 8)))
+	    errnum = ERR_WONT_FIT;
+	  else
+	    {
+	      *((unsigned short *) (installlist + 2)) = (installaddr >> 4);
+	      *((unsigned long *) (installlist - 4)) = sector;
+	    }
+	}
+
+      *((unsigned short *) installlist) += 1;
+      installaddr += 512;
+    }
+
+  /* First, check the GNU-style long option.  */
+  while (1)
+    {
+      if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0)
+	{
+	  is_force_lba = 1;
+	  arg = skip_to (0, arg);
+	}
+#ifdef GRUB_UTIL
+      else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0)
+	{
+	  stage2_os_file = arg + sizeof ("--stage2=") - 1;
+	  arg = skip_to (0, arg);
+	  nul_terminate (stage2_os_file);
+	}
+#endif /* GRUB_UTIL */
+      else
+	break;
+    }
+  
+  stage1_file = arg;
+  dest_dev = skip_to (0, stage1_file);
+  if (*dest_dev == 'd')
+    {
+      new_drive = 0;
+      dest_dev = skip_to (0, dest_dev);
+    }
+  file = skip_to (0, dest_dev);
+  addr = skip_to (0, file);
+
+  /* Get the installation address.  */
+  if (! safe_parse_maxint (&addr, &installaddr))
+    {
+      /* ADDR is not specified.  */
+      installaddr = 0;
+      ptr = addr;
+      errnum = 0;
+    }
+  else
+    ptr = skip_to (0, addr);
+
+#ifndef NO_DECOMPRESSION
+  /* Do not decompress Stage 1 or Stage 2.  */
+  no_decompression = 1;
+#endif
+
+  /* Read Stage 1.  */
+  is_open = grub_open (stage1_file);
+  if (! is_open
+      || ! grub_read (stage1_buffer, SECTOR_SIZE) == SECTOR_SIZE)
+    goto fail;
+
+  /* Read the old sector from DEST_DEV.  */
+  if (! set_device (dest_dev)
+      || ! open_partition ()
+      || ! devread (0, 0, SECTOR_SIZE, old_sect))
+    goto fail;
+
+  /* Store the information for the destination device.  */
+  dest_drive = current_drive;
+  dest_partition = current_partition;
+  dest_geom = buf_geom;
+  dest_sector = part_start;
+
+  /* Copy the possible DOS BPB, 59 bytes at byte offset 3.  */
+  grub_memmove (stage1_buffer + BOOTSEC_BPB_OFFSET,
+		old_sect + BOOTSEC_BPB_OFFSET,
+		BOOTSEC_BPB_LENGTH);
+
+  /* If for a hard disk, copy the possible MBR/extended part table.  */
+  if (dest_drive & 0x80)
+    grub_memmove (stage1_buffer + STAGE1_WINDOWS_NT_MAGIC,
+		  old_sect + STAGE1_WINDOWS_NT_MAGIC,
+		  STAGE1_PARTEND - STAGE1_WINDOWS_NT_MAGIC);
+
+  /* Check for the version and the signature of Stage 1.  */
+  if (*((short *)(stage1_buffer + STAGE1_VER_MAJ_OFFS)) != COMPAT_VERSION
+      || (*((unsigned short *) (stage1_buffer + BOOTSEC_SIG_OFFSET))
+	  != BOOTSEC_SIGNATURE))
+    {
+      errnum = ERR_BAD_VERSION;
+      goto fail;
+    }
+
+  /* This below is not true any longer. But should we leave this alone?  */
+  
+  /* If DEST_DRIVE is a floppy, Stage 2 must have the iteration probe
+     routine.  */
+  if (! (dest_drive & 0x80)
+      && (*((unsigned char *) (stage1_buffer + BOOTSEC_PART_OFFSET)) == 0x80
+	  || stage1_buffer[BOOTSEC_PART_OFFSET] == 0))
+    {
+      errnum = ERR_BAD_VERSION;
+      goto fail;
+    }
+
+  grub_close ();
+  
+  /* Open Stage 2.  */
+  is_open = grub_open (file);
+  if (! is_open)
+    goto fail;
+
+  src_drive = current_drive;
+  src_partition = current_partition;
+  src_part_start = part_start;
+  src_geom = buf_geom;
+  
+  if (! new_drive)
+    new_drive = src_drive;
+  else if (src_drive != dest_drive)
+    grub_printf ("Warning: the option `d' was not used, but the Stage 1 will"
+		 " be installed on a\ndifferent drive than the drive where"
+		 " the Stage 2 resides.\n");
+
+  /* Set the boot drive.  */
+  *((unsigned char *) (stage1_buffer + STAGE1_BOOT_DRIVE)) = new_drive;
+
+  /* Set the "force LBA" flag.  */
+  *((unsigned char *) (stage1_buffer + STAGE1_FORCE_LBA)) = is_force_lba;
+
+  /* Set the boot drive mask. This is a workaround for buggy BIOSes which
+     don't pass boot drive correctly. Instead, they pass 0x00 even when
+     booted from 0x80.  */
+  *((unsigned char *) (stage1_buffer + STAGE1_BOOT_DRIVE_MASK))
+    = (dest_drive & BIOS_FLAG_FIXED_DISK);
+  
+  /* Read the first sector of Stage 2.  */
+  disk_read_hook = disk_read_savesect_func;
+  if (grub_read (stage2_first_buffer, SECTOR_SIZE) != SECTOR_SIZE)
+    goto fail;
+
+  stage2_first_sector = saved_sector;
+  
+  /* Read the second sector of Stage 2.  */
+  if (grub_read (stage2_second_buffer, SECTOR_SIZE) != SECTOR_SIZE)
+    goto fail;
+
+  stage2_second_sector = saved_sector;
+  
+  /* Check for the version of Stage 2.  */
+  if (*((short *) (stage2_second_buffer + STAGE2_VER_MAJ_OFFS))
+      != COMPAT_VERSION)
+    {
+      errnum = ERR_BAD_VERSION;
+      goto fail;
+    }
+
+  /* Check for the Stage 2 id.  */
+  if (stage2_second_buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2)
+    is_stage1_5 = 1;
+
+  /* If INSTALLADDR is not specified explicitly in the command-line,
+     determine it by the Stage 2 id.  */
+  if (! installaddr)
+    {
+      if (! is_stage1_5)
+	/* Stage 2.  */
+	installaddr = 0x8000;
+      else
+	/* Stage 1.5.  */
+	installaddr = 0x2000;
+    }
+
+  *((unsigned long *) (stage1_buffer + STAGE1_STAGE2_SECTOR))
+    = stage2_first_sector;
+  *((unsigned short *) (stage1_buffer + STAGE1_STAGE2_ADDRESS))
+    = installaddr;
+  *((unsigned short *) (stage1_buffer + STAGE1_STAGE2_SEGMENT))
+    = installaddr >> 4;
+
+  i = (int) stage2_first_buffer + SECTOR_SIZE - 4;
+  while (*((unsigned long *) i))
+    {
+      if (i < (int) stage2_first_buffer
+	  || (*((int *) (i - 4)) & 0x80000000)
+	  || *((unsigned short *) i) >= 0xA00
+	  || *((short *) (i + 2)) == 0)
+	{
+	  errnum = ERR_BAD_VERSION;
+	  goto fail;
+	}
+
+      *((int *) i) = 0;
+      *((int *) (i - 4)) = 0;
+      i -= 8;
+    }
+
+  installlist = (int) stage2_first_buffer + SECTOR_SIZE + 4;
+  installaddr += SECTOR_SIZE;
+  
+  /* Read the whole of Stage2 except for the first sector.  */
+  grub_seek (SECTOR_SIZE);
+
+  disk_read_hook = disk_read_blocklist_func;
+  if (! grub_read (dummy, -1))
+    goto fail;
+  
+  disk_read_hook = 0;
+  
+  /* Find a string for the configuration filename.  */
+  config_file_location = stage2_second_buffer + STAGE2_VER_STR_OFFS;
+  while (*(config_file_location++))
+    ;
+
+  /* Set the "force LBA" flag for Stage2.  */
+  *((unsigned char *) (stage2_second_buffer + STAGE2_FORCE_LBA))
+    = is_force_lba;
+  
+  if (*ptr == 'p')
+    {
+      *((long *) (stage2_second_buffer + STAGE2_INSTALLPART))
+	= src_partition;
+      if (is_stage1_5)
+	{
+	  /* Reset the device information in FILE if it is a Stage 1.5.  */
+	  unsigned long device = 0xFFFFFFFF;
+
+	  grub_memmove (config_file_location, (char *) &device,
+			sizeof (device));
+	}
+
+      ptr = skip_to (0, ptr);
+    }
+
+  if (*ptr)
+    {
+      grub_strcpy (config_filename, ptr);
+      nul_terminate (config_filename);
+	
+      if (! is_stage1_5)
+	/* If it is a Stage 2, just copy PTR to CONFIG_FILE_LOCATION.  */
+	grub_strcpy (config_file_location, ptr);
+      else
+	{
+	  char *real_config;
+	  unsigned long device;
+
+	  /* Translate the external device syntax to the internal device
+	     syntax.  */
+	  if (! (real_config = set_device (ptr)))
+	    {
+	      /* The Stage 2 PTR does not contain the device name, so
+		 use the root device instead.  */
+	      errnum = ERR_NONE;
+	      current_drive = saved_drive;
+	      current_partition = saved_partition;
+	      real_config = ptr;
+	    }
+	  
+	  if (current_drive == src_drive)
+	    {
+	      /* If the drive where the Stage 2 resides is the same as
+		 the one where the Stage 1.5 resides, do not embed the
+		 drive number.  */
+	      current_drive = 0xFF;
+	    }
+
+	  device = (current_drive << 24) | current_partition;
+	  grub_memmove (config_file_location, (char *) &device,
+			sizeof (device));
+	  grub_strcpy (config_file_location + sizeof (device),
+		       real_config);
+	}
+
+      /* If a Stage 1.5 is used, then we need to modify the Stage2.  */
+      if (is_stage1_5)
+	{
+	  char *real_config_filename = skip_to (0, ptr);
+	  
+	  is_open = grub_open (config_filename);
+	  if (! is_open)
+	    goto fail;
+
+	  /* Skip the first sector.  */
+	  grub_seek (SECTOR_SIZE);
+	  
+	  disk_read_hook = disk_read_savesect_func;
+	  if (grub_read (stage2_buffer, SECTOR_SIZE) != SECTOR_SIZE)
+	    goto fail;
+	  
+	  disk_read_hook = 0;
+	  grub_close ();
+	  is_open = 0;
+	  
+	  /* Sanity check.  */
+	  if (*(stage2_buffer + STAGE2_STAGE2_ID) != STAGE2_ID_STAGE2)
+	    {
+	      errnum = ERR_BAD_VERSION;
+	      goto fail;
+	    }
+
+	  /* Set the "force LBA" flag for Stage2.  */
+	  *(stage2_buffer + STAGE2_FORCE_LBA) = is_force_lba;
+
+	  /* If REAL_CONFIG_FILENAME is specified, copy it to the Stage2.  */
+	  if (*real_config_filename)
+	    {
+	      /* Specified */
+	      char *location;
+	      
+	      /* Find a string for the configuration filename.  */
+	      location = stage2_buffer + STAGE2_VER_STR_OFFS;
+	      while (*(location++))
+		;
+	      
+	      /* Copy the name.  */
+	      grub_strcpy (location, real_config_filename);
+	    }
+	  
+	  /* Write it to the disk.  */
+	  buf_track = -1;
+
+#ifdef GRUB_UTIL
+	  /* In the grub shell, access the Stage 2 via the OS filesystem
+	     service, if possible.  */
+	  if (stage2_os_file)
+	    {
+	      FILE *fp;
+
+	      fp = fopen (stage2_os_file, "r+");
+	      if (! fp)
+		{
+		  errnum = ERR_FILE_NOT_FOUND;
+		  goto fail;
+		}
+
+	      if (fseek (fp, SECTOR_SIZE, SEEK_SET) != 0)
+		{
+		  fclose (fp);
+		  errnum = ERR_BAD_VERSION;
+		  goto fail;
+		}
+
+	      if (fwrite (stage2_buffer, 1, SECTOR_SIZE, fp)
+		  != SECTOR_SIZE)
+		{
+		  fclose (fp);
+		  errnum = ERR_WRITE;
+		  goto fail;
+		}
+
+	      fclose (fp);
+	    }
+	  else
+#endif /* GRUB_UTIL */
+	    {
+	      if (! devwrite (saved_sector - part_start, 1, stage2_buffer))
+		goto fail;
+	    }
+	}
+    }
+
+  /* Clear the cache.  */
+  buf_track = -1;
+
+  /* Write the modified sectors of Stage2 to the disk.  */
+#ifdef GRUB_UTIL
+  if (! is_stage1_5 && stage2_os_file)
+    {
+      FILE *fp;
+
+      fp = fopen (stage2_os_file, "r+");
+      if (! fp)
+	{
+	  errnum = ERR_FILE_NOT_FOUND;
+	  goto fail;
+	}
+
+      if (fwrite (stage2_first_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE)
+	{
+	  fclose (fp);
+	  errnum = ERR_WRITE;
+	  goto fail;
+	}
+
+      if (fwrite (stage2_second_buffer, 1, SECTOR_SIZE, fp) != SECTOR_SIZE)
+	{
+	  fclose (fp);
+	  errnum = ERR_WRITE;
+	  goto fail;
+	}
+
+      fclose (fp);
+    }
+  else
+#endif /* GRUB_UTIL */
+    {
+      /* The first.  */
+      current_drive = src_drive;
+      current_partition = src_partition;
+
+      if (! open_partition ())
+	goto fail;
+
+      if (! devwrite (stage2_first_sector - src_part_start, 1,
+		      stage2_first_buffer))
+	goto fail;
+
+      if (! devwrite (stage2_second_sector - src_part_start, 1,
+		      stage2_second_buffer))
+	goto fail;
+    }
+  
+  /* Write the modified sector of Stage 1 to the disk.  */
+  current_drive = dest_drive;
+  current_partition = dest_partition;
+  if (! open_partition ())
+    goto fail;
+
+  devwrite (0, 1, stage1_buffer);
+
+ fail:
+  if (is_open)
+    grub_close ();
+  
+  disk_read_hook = 0;
+  
+#ifndef NO_DECOMPRESSION
+  no_decompression = 0;
+#endif
+
+  return errnum;
+}
+
+static struct builtin builtin_install =
+{
+  "install",
+  install_func,
+  BUILTIN_CMDLINE,
+  "install [--stage2=STAGE2_FILE] [--force-lba] STAGE1 [d] DEVICE STAGE2 [ADDR] [p] [CONFIG_FILE] [REAL_CONFIG_FILE]",
+  "Install STAGE1 on DEVICE, and install a blocklist for loading STAGE2"
+  " as a Stage 2. If the option `d' is present, the Stage 1 will always"
+  " look for the disk where STAGE2 was installed, rather than using"
+  " the booting drive. The Stage 2 will be loaded at address ADDR, which"
+  " will be determined automatically if you don't specify it. If"
+  " the option `p' or CONFIG_FILE is present, then the first block"
+  " of Stage 2 is patched with new values of the partition and name"
+  " of the configuration file used by the true Stage 2 (for a Stage 1.5,"
+  " this is the name of the true Stage 2) at boot time. If STAGE2 is a Stage"
+  " 1.5 and REAL_CONFIG_FILE is present, then the Stage 2 CONFIG_FILE is"
+  " patched with the configuration filename REAL_CONFIG_FILE."
+  " If the option `--force-lba' is specified, disable some sanity checks"
+  " for LBA mode. If the option `--stage2' is specified, rewrite the Stage"
+  " 2 via your OS's filesystem instead of the raw device."
+};
+
+
+/* ioprobe */
+static int
+ioprobe_func (char *arg, int flags)
+{
+#ifdef GRUB_UTIL
+  
+  errnum = ERR_UNRECOGNIZED;
+  return 1;
+  
+#else /* ! GRUB_UTIL */
+  
+  unsigned short *port;
+  
+  /* Get the drive number.  */
+  set_device (arg);
+  if (errnum)
+    return 1;
+
+  /* Clean out IO_MAP.  */
+  grub_memset ((char *) io_map, 0, IO_MAP_SIZE * sizeof (unsigned short));
+
+  /* Track the int13 handler.  */
+  track_int13 (current_drive);
+  
+  /* Print out the result.  */
+  for (port = io_map; *port != 0; port++)
+    grub_printf (" 0x%x", (unsigned int) *port);
+
+  return 0;
+  
+#endif /* ! GRUB_UTIL */
+}
+
+static struct builtin builtin_ioprobe =
+{
+  "ioprobe",
+  ioprobe_func,
+  BUILTIN_CMDLINE,
+  "ioprobe DRIVE",
+  "Probe I/O ports used for the drive DRIVE."
+};
+
+
+/* kernel */
+static int
+kernel_func (char *arg, int flags)
+{
+  int len;
+  kernel_t suggested_type = KERNEL_TYPE_NONE;
+  unsigned long load_flags = 0;
+
+#ifndef AUTO_LINUX_MEM_OPT
+  load_flags |= KERNEL_LOAD_NO_MEM_OPTION;
+#endif
+
+  /* Deal with GNU-style long options.  */
+  while (1)
+    {
+      /* If the option `--type=TYPE' is specified, convert the string to
+	 a kernel type.  */
+      if (grub_memcmp (arg, "--type=", 7) == 0)
+	{
+	  arg += 7;
+	  
+	  if (grub_memcmp (arg, "netbsd", 6) == 0)
+	    suggested_type = KERNEL_TYPE_NETBSD;
+	  else if (grub_memcmp (arg, "freebsd", 7) == 0)
+	    suggested_type = KERNEL_TYPE_FREEBSD;
+	  else if (grub_memcmp (arg, "openbsd", 7) == 0)
+	    /* XXX: For now, OpenBSD is identical to NetBSD, from GRUB's
+	       point of view.  */
+	    suggested_type = KERNEL_TYPE_NETBSD;
+	  else if (grub_memcmp (arg, "linux", 5) == 0)
+	    suggested_type = KERNEL_TYPE_LINUX;
+	  else if (grub_memcmp (arg, "biglinux", 8) == 0)
+	    suggested_type = KERNEL_TYPE_BIG_LINUX;
+	  else if (grub_memcmp (arg, "multiboot", 9) == 0)
+	    suggested_type = KERNEL_TYPE_MULTIBOOT;
+	  else
+	    {
+	      errnum = ERR_BAD_ARGUMENT;
+	      return 1;
+	    }
+	}
+      /* If the `--no-mem-option' is specified, don't pass a Linux's mem
+	 option automatically. If the kernel is another type, this flag
+	 has no effect.  */
+      else if (grub_memcmp (arg, "--no-mem-option", 15) == 0)
+	load_flags |= KERNEL_LOAD_NO_MEM_OPTION;
+      else
+	break;
+
+      /* Try the next.  */
+      arg = skip_to (0, arg);
+    }
+      
+  len = grub_strlen (arg);
+
+  /* Reset MB_CMDLINE.  */
+  mb_cmdline = (char *) MB_CMDLINE_BUF;
+  if (len + 1 > MB_CMDLINE_BUFLEN)
+    {
+      errnum = ERR_WONT_FIT;
+      return 1;
+    }
+
+  /* Copy the command-line to MB_CMDLINE.  */
+  grub_memmove (mb_cmdline, arg, len + 1);
+  kernel_type = load_image (arg, mb_cmdline, suggested_type, load_flags);
+  if (kernel_type == KERNEL_TYPE_NONE)
+    return 1;
+
+  mb_cmdline += len + 1;
+  return 0;
+}
+
+static struct builtin builtin_kernel =
+{
+  "kernel",
+  kernel_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "kernel [--no-mem-option] [--type=TYPE] FILE [ARG ...]",
+  "Attempt to load the primary boot image from FILE. The rest of the"
+  " line is passed verbatim as the \"kernel command line\".  Any modules"
+  " must be reloaded after using this command. The option --type is used"
+  " to suggest what type of kernel to be loaded. TYPE must be either of"
+  " \"netbsd\", \"freebsd\", \"openbsd\", \"linux\", \"biglinux\" and"
+  " \"multiboot\". The option --no-mem-option tells GRUB not to pass a"
+  " Linux's mem option automatically."
+};
+
+
+/* lock */
+static int
+lock_func (char *arg, int flags)
+{
+  if (! auth && password)
+    {
+      errnum = ERR_PRIVILEGED;
+      return 1;
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_lock =
+{
+  "lock",
+  lock_func,
+  BUILTIN_CMDLINE,
+  "lock",
+  "Break a command execution unless the user is authenticated."
+};
+  
+
+/* makeactive */
+static int
+makeactive_func (char *arg, int flags)
+{
+  if (! make_saved_active ())
+    return 1;
+
+  return 0;
+}
+
+static struct builtin builtin_makeactive =
+{
+  "makeactive",
+  makeactive_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "makeactive",
+  "Set the active partition on the root disk to GRUB's root device."
+  " This command is limited to _primary_ PC partitions on a hard disk."
+};
+
+
+/* map */
+/* Map FROM_DRIVE to TO_DRIVE.  */
+static int
+map_func (char *arg, int flags)
+{
+  char *to_drive;
+  char *from_drive;
+  unsigned long to, from;
+  int i;
+  
+  to_drive = arg;
+  from_drive = skip_to (0, arg);
+
+  /* Get the drive number for TO_DRIVE.  */
+  set_device (to_drive);
+  if (errnum)
+    return 1;
+  to = current_drive;
+
+  /* Get the drive number for FROM_DRIVE.  */
+  set_device (from_drive);
+  if (errnum)
+    return 1;
+  from = current_drive;
+
+  /* Search for an empty slot in BIOS_DRIVE_MAP.  */
+  for (i = 0; i < DRIVE_MAP_SIZE; i++)
+    {
+      /* Perhaps the user wants to override the map.  */
+      if ((bios_drive_map[i] & 0xff) == from)
+	break;
+      
+      if (! bios_drive_map[i])
+	break;
+    }
+
+  if (i == DRIVE_MAP_SIZE)
+    {
+      errnum = ERR_WONT_FIT;
+      return 1;
+    }
+
+  if (to == from)
+    /* If TO is equal to FROM, delete the entry.  */
+    grub_memmove ((char *) &bios_drive_map[i], (char *) &bios_drive_map[i + 1],
+		  sizeof (unsigned short) * (DRIVE_MAP_SIZE - i));
+  else
+    bios_drive_map[i] = from | (to << 8);
+  
+  return 0;
+}
+
+static struct builtin builtin_map =
+{
+  "map",
+  map_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "map TO_DRIVE FROM_DRIVE",
+  "Map the drive FROM_DRIVE to the drive TO_DRIVE. This is necessary"
+  " when you chain-load some operating systems, such as DOS, if such an"
+  " OS resides at a non-first drive."
+};
+
+
+#ifdef USE_MD5_PASSWORDS
+/* md5crypt */
+static int
+md5crypt_func (char *arg, int flags)
+{
+  char crypted[36];
+  char key[32];
+  unsigned int seed;
+  int i;
+  const char *const seedchars =
+    "./0123456789ABCDEFGHIJKLMNOPQRST"
+    "UVWXYZabcdefghijklmnopqrstuvwxyz";
+  
+  /* First create a salt.  */
+
+  /* The magical prefix.  */
+  grub_memset (crypted, 0, sizeof (crypted));
+  grub_memmove (crypted, "$1$", 3);
+
+  /* Create the length of a salt.  */
+  seed = currticks ();
+
+  /* Generate a salt.  */
+  for (i = 0; i < 8 && seed; i++)
+    {
+      /* FIXME: This should be more random.  */
+      crypted[3 + i] = seedchars[seed & 0x3f];
+      seed >>= 6;
+    }
+
+  /* A salt must be terminated with `$', if it is less than 8 chars.  */
+  crypted[3 + i] = '$';
+
+#ifdef DEBUG_MD5CRYPT
+  grub_printf ("salt = %s\n", crypted);
+#endif
+  
+  /* Get a password.  */
+  grub_memset (key, 0, sizeof (key));
+  get_cmdline ("Password: ", key, sizeof (key) - 1, '*', 0);
+
+  /* Crypt the key.  */
+  make_md5_password (key, crypted);
+
+  grub_printf ("Encrypted: %s\n", crypted);
+  return 0;
+}
+
+static struct builtin builtin_md5crypt =
+{
+  "md5crypt",
+  md5crypt_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "md5crypt",
+  "Generate a password in MD5 format."
+};
+#endif /* USE_MD5_PASSWORDS */
+
+
+/* module */
+static int
+module_func (char *arg, int flags)
+{
+  int len = grub_strlen (arg);
+
+  switch (kernel_type)
+    {
+    case KERNEL_TYPE_MULTIBOOT:
+      if (mb_cmdline + len + 1 > (char *) MB_CMDLINE_BUF + MB_CMDLINE_BUFLEN)
+	{
+	  errnum = ERR_WONT_FIT;
+	  return 1;
+	}
+      grub_memmove (mb_cmdline, arg, len + 1);
+      if (! load_module (arg, mb_cmdline))
+	return 1;
+      mb_cmdline += len + 1;
+      break;
+
+    case KERNEL_TYPE_LINUX:
+    case KERNEL_TYPE_BIG_LINUX:
+      if (! load_initrd (arg))
+	return 1;
+      break;
+
+    default:
+      errnum = ERR_NEED_MB_KERNEL;
+      return 1;
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_module =
+{
+  "module",
+  module_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "module FILE [ARG ...]",
+  "Load a boot module FILE for a Multiboot format boot image (no"
+  " interpretation of the file contents is made, so users of this"
+  " command must know what the kernel in question expects). The"
+  " rest of the line is passed as the \"module command line\", like"
+  " the `kernel' command."
+};
+
+
+/* modulenounzip */
+static int
+modulenounzip_func (char *arg, int flags)
+{
+  int ret;
+
+#ifndef NO_DECOMPRESSION
+  no_decompression = 1;
+#endif
+
+  ret = module_func (arg, flags);
+
+#ifndef NO_DECOMPRESSION
+  no_decompression = 0;
+#endif
+
+  return ret;
+}
+
+static struct builtin builtin_modulenounzip =
+{
+  "modulenounzip",
+  modulenounzip_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "modulenounzip FILE [ARG ...]",
+  "The same as `module', except that automatic decompression is"
+  " disabled."
+};
+
+
+/* pager [on|off] */
+static int
+pager_func (char *arg, int flags)
+{
+  /* If ARG is empty, toggle the flag.  */
+  if (! *arg)
+    use_pager = ! use_pager;
+  else if (grub_memcmp (arg, "on", 2) == 0)
+    use_pager = 1;
+  else if (grub_memcmp (arg, "off", 3) == 0)
+    use_pager = 0;
+  else
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+
+  grub_printf (" Internal pager is now %s\n", use_pager ? "on" : "off");
+  return 0;
+}
+
+static struct builtin builtin_pager =
+{
+  "pager",
+  pager_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "pager [FLAG]",
+  "Toggle pager mode with no argument. If FLAG is given and its value"
+  " is `on', turn on the mode. If FLAG is `off', turn off the mode."
+};
+
+
+/* partnew PART TYPE START LEN */
+static int
+partnew_func (char *arg, int flags)
+{
+  int new_type, new_start, new_len;
+  int start_cl, start_ch, start_dh;
+  int end_cl, end_ch, end_dh;
+  int entry;
+  char mbr[512];
+
+  /* Convert a LBA address to a CHS address in the INT 13 format.  */
+  auto void lba_to_chs (int lba, int *cl, int *ch, int *dh);
+  void lba_to_chs (int lba, int *cl, int *ch, int *dh)
+    {
+      int cylinder, head, sector;
+
+      sector = lba % buf_geom.sectors + 1;
+      head = (lba / buf_geom.sectors) % buf_geom.heads;
+      cylinder = lba / (buf_geom.sectors * buf_geom.heads);
+
+      if (cylinder >= buf_geom.cylinders)
+	cylinder = buf_geom.cylinders - 1;
+      
+      *cl = sector | ((cylinder & 0x300) >> 2);
+      *ch = cylinder & 0xFF;
+      *dh = head;
+    }
+      
+  /* Get the drive and the partition.  */
+  if (! set_device (arg))
+    return 1;
+
+  /* The drive must be a hard disk.  */
+  if (! (current_drive & 0x80))
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+
+  /* The partition must a primary partition.  */
+  if ((current_partition >> 16) > 3
+      || (current_partition & 0xFFFF) != 0xFFFF)
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+
+  entry = current_partition >> 16;
+  
+  /* Get the new partition type.  */
+  arg = skip_to (0, arg);
+  if (! safe_parse_maxint (&arg, &new_type))
+    return 1;
+
+  /* The partition type is unsigned char.  */
+  if (new_type > 0xFF)
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+
+  /* Get the new partition start.  */
+  arg = skip_to (0, arg);
+  if (! safe_parse_maxint (&arg, &new_start))
+    return 1;
+  
+  /* Get the new partition length.  */
+  arg = skip_to (0, arg);
+  if (! safe_parse_maxint (&arg, &new_len))
+    return 1;
+
+  /* Read the MBR.  */
+  if (! rawread (current_drive, 0, 0, SECTOR_SIZE, mbr))
+    return 1;
+
+  /* Check if the new partition will fit in the disk.  */
+  if (new_start + new_len > buf_geom.total_sectors)
+    {
+      errnum = ERR_GEOM;
+      return 1;
+    }
+
+  /* Store the partition information in the MBR.  */
+  lba_to_chs (new_start, &start_cl, &start_ch, &start_dh);
+  lba_to_chs (new_start + new_len - 1, &end_cl, &end_ch, &end_dh);
+
+  PC_SLICE_FLAG (mbr, entry) = 0;
+  PC_SLICE_HEAD (mbr, entry) = start_dh;
+  PC_SLICE_SEC (mbr, entry) = start_cl;
+  PC_SLICE_CYL (mbr, entry) = start_ch;
+  PC_SLICE_TYPE (mbr, entry) = new_type;
+  PC_SLICE_EHEAD (mbr, entry) = end_dh;
+  PC_SLICE_ESEC (mbr, entry) = end_cl;
+  PC_SLICE_ECYL (mbr, entry) = end_ch;
+  PC_SLICE_START (mbr, entry) = new_start;
+  PC_SLICE_LENGTH (mbr, entry) = new_len;
+
+  /* Make sure that the MBR has a valid signature.  */
+  PC_MBR_SIG (mbr) = PC_MBR_SIGNATURE;
+  
+  /* Write back the MBR to the disk.  */
+  buf_track = -1;
+  if (! rawwrite (current_drive, 0, mbr))
+    return 1;
+
+  return 0;
+}
+
+static struct builtin builtin_partnew =
+{
+  "partnew",
+  partnew_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "partnew PART TYPE START LEN",
+  "Create a primary partition at the starting address START with the"
+  " length LEN, with the type TYPE. START and LEN are in sector units."
+};
+
+
+/* parttype PART TYPE */
+static int
+parttype_func (char *arg, int flags)
+{
+  int new_type;
+  unsigned long part = 0xFFFFFF;
+  unsigned long start, len, offset, ext_offset;
+  int entry, type;
+  char mbr[512];
+
+  /* Get the drive and the partition.  */
+  if (! set_device (arg))
+    return 1;
+
+  /* The drive must be a hard disk.  */
+  if (! (current_drive & 0x80))
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+  
+  /* The partition must be a PC slice.  */
+  if ((current_partition >> 16) == 0xFF
+      || (current_partition & 0xFFFF) != 0xFFFF)
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+
+  /* Get the new partition type.  */
+  arg = skip_to (0, arg);
+  if (! safe_parse_maxint (&arg, &new_type))
+    return 1;
+
+  /* The partition type is unsigned char.  */
+  if (new_type > 0xFF)
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+
+  /* Look for the partition.  */
+  while (next_partition (current_drive, 0xFFFFFF, &part, &type,
+			 &start, &len, &offset, &entry,
+			 &ext_offset, mbr))
+    {
+      if (part == current_partition)
+	{
+	  /* Found.  */
+
+	  /* Set the type to NEW_TYPE.  */
+	  PC_SLICE_TYPE (mbr, entry) = new_type;
+	  
+	  /* Write back the MBR to the disk.  */
+	  buf_track = -1;
+	  if (! rawwrite (current_drive, offset, mbr))
+	    return 1;
+
+	  /* Succeed.  */
+	  return 0;
+	}
+    }
+
+  /* The partition was not found.  ERRNUM was set by next_partition.  */
+  return 1;
+}
+
+static struct builtin builtin_parttype =
+{
+  "parttype",
+  parttype_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "parttype PART TYPE",
+  "Change the type of the partition PART to TYPE."
+};
+
+
+/* password */
+static int
+password_func (char *arg, int flags)
+{
+  int len;
+  password_t type = PASSWORD_PLAIN;
+
+#ifdef USE_MD5_PASSWORDS
+  if (grub_memcmp (arg, "--md5", 5) == 0)
+    {
+      type = PASSWORD_MD5;
+      arg = skip_to (0, arg);
+    }
+#endif
+  if (grub_memcmp (arg, "--", 2) == 0)
+    {
+      type = PASSWORD_UNSUPPORTED;
+      arg = skip_to (0, arg);
+    }
+
+  if ((flags & (BUILTIN_CMDLINE | BUILTIN_SCRIPT)) != 0)
+    {
+      /* Do password check! */
+      char entered[32];
+      
+      /* Wipe out any previously entered password */
+      entered[0] = 0;
+      get_cmdline ("Password: ", entered, 31, '*', 0);
+
+      nul_terminate (arg);
+      if (check_password (entered, arg, type) != 0)
+	{
+	  errnum = ERR_PRIVILEGED;
+	  return 1;
+	}
+    }
+  else
+    {
+      len = grub_strlen (arg);
+      
+      /* PASSWORD NUL NUL ... */
+      if (len + 2 > PASSWORD_BUFLEN)
+	{
+	  errnum = ERR_WONT_FIT;
+	  return 1;
+	}
+      
+      /* Copy the password and clear the rest of the buffer.  */
+      password = (char *) PASSWORD_BUF;
+      grub_memmove (password, arg, len);
+      grub_memset (password + len, 0, PASSWORD_BUFLEN - len);
+      password_type = type;
+    }
+  return 0;
+}
+
+static struct builtin builtin_password =
+{
+  "password",
+  password_func,
+  BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_NO_ECHO,
+  "password [--md5] PASSWD [FILE]",
+  "If used in the first section of a menu file, disable all"
+  " interactive editing control (menu entry editor and"
+  " command line). If the password PASSWD is entered, it loads the"
+  " FILE as a new config file and restarts the GRUB Stage 2. If you"
+  " omit the argument FILE, then GRUB just unlocks privileged"
+  " instructions.  You can also use it in the script section, in"
+  " which case it will ask for the password, before continueing."
+  " The option --md5 tells GRUB that PASSWD is encrypted with"
+  " md5crypt."
+};
+
+
+/* pause */
+static int
+pause_func (char *arg, int flags)
+{
+  printf("%s\n", arg);
+
+  /* If ESC is returned, then abort this entry.  */
+  if (ASCII_CHAR (getkey ()) == 27)
+    return 1;
+
+  return 0;
+}
+
+static struct builtin builtin_pause =
+{
+  "pause",
+  pause_func,
+  BUILTIN_CMDLINE | BUILTIN_NO_ECHO,
+  "pause [MESSAGE ...]",
+  "Print MESSAGE, then wait until a key is pressed."
+};
+
+
+#ifdef GRUB_UTIL
+/* quit */
+static int
+quit_func (char *arg, int flags)
+{
+  stop ();
+  
+  /* Never reach here.  */
+  return 0;
+}
+
+static struct builtin builtin_quit =
+{
+  "quit",
+  quit_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "quit",
+  "Exit from the GRUB shell."
+};
+#endif /* GRUB_UTIL */
+
+
+#ifdef SUPPORT_NETBOOT
+/* rarp */
+static int
+rarp_func (char *arg, int flags)
+{
+  if (! rarp ())
+    {
+      if (errnum == ERR_NONE)
+	errnum = ERR_DEV_VALUES;
+
+      return 1;
+    }
+
+  /* Notify the configuration.  */
+  print_network_configuration ();
+  return 0;
+}
+
+static struct builtin builtin_rarp =
+{
+  "rarp",
+  rarp_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "rarp",
+  "Initialize a network device via RARP."
+};
+#endif /* SUPPORT_NETBOOT */
+
+
+static int
+read_func (char *arg, int flags)
+{
+  int addr;
+
+  if (! safe_parse_maxint (&arg, &addr))
+    return 1;
+
+  grub_printf ("Address 0x%x: Value 0x%x\n",
+	       addr, *((unsigned *) RAW_ADDR (addr)));
+  return 0;
+}
+
+static struct builtin builtin_read =
+{
+  "read",
+  read_func,
+  BUILTIN_CMDLINE,
+  "read ADDR",
+  "Read a 32-bit value from memory at address ADDR and"
+  " display it in hex format."
+};
+
+
+/* reboot */
+static int
+reboot_func (char *arg, int flags)
+{
+  grub_reboot ();
+
+  /* Never reach here.  */
+  return 1;
+}
+
+static struct builtin builtin_reboot =
+{
+  "reboot",
+  reboot_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "reboot",
+  "Reboot your system."
+};
+
+
+/* Print the root device information.  */
+static void
+print_root_device (void)
+{
+  if (saved_drive == NETWORK_DRIVE)
+    {
+      /* Network drive.  */
+      grub_printf (" (nd):");
+    }
+  else if (saved_drive & 0x80)
+    {
+      /* Hard disk drive.  */
+      grub_printf (" (hd%d", saved_drive - 0x80);
+      
+      if ((saved_partition & 0xFF0000) != 0xFF0000)
+	grub_printf (",%d", saved_partition >> 16);
+
+      if ((saved_partition & 0x00FF00) != 0x00FF00)
+	grub_printf (",%c", ((saved_partition >> 8) & 0xFF) + 'a');
+
+      grub_printf ("):");
+    }
+  else
+    {
+      /* Floppy disk drive.  */
+      grub_printf (" (fd%d):", saved_drive);
+    }
+
+  /* Print the filesystem information.  */
+  current_partition = saved_partition;
+  current_drive = saved_drive;
+  print_fsys_type ();
+}
+
+static int
+real_root_func (char *arg, int attempt_mount)
+{
+  int hdbias = 0;
+  char *biasptr;
+  char *next;
+
+  /* If ARG is empty, just print the current root device.  */
+  if (! *arg)
+    {
+      print_root_device ();
+      return 0;
+    }
+  
+  /* Call set_device to get the drive and the partition in ARG.  */
+  next = set_device (arg);
+  if (! next)
+    return 1;
+
+  /* Ignore ERR_FSYS_MOUNT.  */
+  if (attempt_mount)
+    {
+      if (! open_device () && errnum != ERR_FSYS_MOUNT)
+	return 1;
+    }
+  else
+    {
+      /* This is necessary, because the location of a partition table
+	 must be set appropriately.  */
+      if (open_partition ())
+	{
+	  set_bootdev (0);
+	  if (errnum)
+	    return 1;
+	}
+    }
+  
+  /* Clear ERRNUM.  */
+  errnum = 0;
+  saved_partition = current_partition;
+  saved_drive = current_drive;
+
+  if (attempt_mount)
+    {
+      /* BSD and chainloading evil hacks !!  */
+      biasptr = skip_to (0, next);
+      safe_parse_maxint (&biasptr, &hdbias);
+      errnum = 0;
+      bootdev = set_bootdev (hdbias);
+      if (errnum)
+	return 1;
+      
+      /* Print the type of the filesystem.  */
+      print_fsys_type ();
+    }
+  
+  return 0;
+}
+
+static int
+root_func (char *arg, int flags)
+{
+  return real_root_func (arg, 1);
+}
+
+static struct builtin builtin_root =
+{
+  "root",
+  root_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "root [DEVICE [HDBIAS]]",
+  "Set the current \"root device\" to the device DEVICE, then"
+  " attempt to mount it to get the partition size (for passing the"
+  " partition descriptor in `ES:ESI', used by some chain-loaded"
+  " bootloaders), the BSD drive-type (for booting BSD kernels using"
+  " their native boot format), and correctly determine "
+  " the PC partition where a BSD sub-partition is located. The"
+  " optional HDBIAS parameter is a number to tell a BSD kernel"
+  " how many BIOS drive numbers are on controllers before the current"
+  " one. For example, if there is an IDE disk and a SCSI disk, and your"
+  " FreeBSD root partition is on the SCSI disk, then use a `1' for HDBIAS."
+};
+
+
+/* rootnoverify */
+static int
+rootnoverify_func (char *arg, int flags)
+{
+  return real_root_func (arg, 0);
+}
+
+static struct builtin builtin_rootnoverify =
+{
+  "rootnoverify",
+  rootnoverify_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "rootnoverify [DEVICE [HDBIAS]]",
+  "Similar to `root', but don't attempt to mount the partition. This"
+  " is useful for when an OS is outside of the area of the disk that"
+  " GRUB can read, but setting the correct root device is still"
+  " desired. Note that the items mentioned in `root' which"
+  " derived from attempting the mount will NOT work correctly."
+};
+
+
+/* savedefault */
+static int
+savedefault_func (char *arg, int flags)
+{
+#if !defined(SUPPORT_DISKLESS) && !defined(GRUB_UTIL)
+  char buffer[512];
+  int *entryno_ptr;
+  
+  /* This command is only useful when you boot an entry from the menu
+     interface.  */
+  if (! (flags & BUILTIN_SCRIPT))
+    {
+      errnum = ERR_UNRECOGNIZED;
+      return 1;
+    }
+  
+  /* Get the geometry of the boot drive (i.e. the disk which contains
+     this stage2).  */
+  if (get_diskinfo (boot_drive, &buf_geom))
+    {
+      errnum = ERR_NO_DISK;
+      return 1;
+    }
+
+  /* Load the second sector of this stage2.  */
+  if (! rawread (boot_drive, install_second_sector, 0, SECTOR_SIZE, buffer))
+    {
+      return 1;
+    }
+
+  /* Sanity check.  */
+  if (buffer[STAGE2_STAGE2_ID] != STAGE2_ID_STAGE2
+      || *((short *) (buffer + STAGE2_VER_MAJ_OFFS)) != COMPAT_VERSION)
+    {
+      errnum = ERR_BAD_VERSION;
+      return 1;
+    }
+  
+  entryno_ptr = (int *) (buffer + STAGE2_SAVED_ENTRYNO);
+
+  /* Check if the saved entry number differs from current entry number.  */
+  if (*entryno_ptr != current_entryno)
+    {
+      /* Overwrite the saved entry number.  */
+      *entryno_ptr = current_entryno;
+      
+      /* Save the image in the disk.  */
+      if (! rawwrite (boot_drive, install_second_sector, buffer))
+	return 1;
+      
+      /* Clear the cache.  */
+      buf_track = -1;
+    }
+
+  return 0;
+#else /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
+  errnum = ERR_UNRECOGNIZED;
+  return 1;
+#endif /* ! SUPPORT_DISKLESS && ! GRUB_UTIL */
+}
+
+static struct builtin builtin_savedefault =
+{
+  "savedefault",
+  savedefault_func,
+  BUILTIN_CMDLINE,
+  "savedefault",
+  "Save the current entry as the default boot entry."
+};
+
+
+#ifdef SUPPORT_SERIAL
+/* serial */
+static int
+serial_func (char *arg, int flags)
+{
+  unsigned short port = serial_hw_get_port (0);
+  unsigned int speed = 9600;
+  int word_len = UART_8BITS_WORD;
+  int parity = UART_NO_PARITY;
+  int stop_bit_len = UART_1_STOP_BIT;
+
+  /* Process GNU-style long options.
+     FIXME: We should implement a getopt-like function, to avoid
+     duplications.  */
+  while (1)
+    {
+      if (grub_memcmp (arg, "--unit=", sizeof ("--unit=") - 1) == 0)
+	{
+	  char *p = arg + sizeof ("--unit=") - 1;
+	  int unit;
+	  
+	  if (! safe_parse_maxint (&p, &unit))
+	    return 1;
+	  
+	  if (unit < 0 || unit > 3)
+	    {
+	      errnum = ERR_DEV_VALUES;
+	      return 1;
+	    }
+
+	  port = serial_hw_get_port (unit);
+	}
+      else if (grub_memcmp (arg, "--speed=", sizeof ("--speed=") - 1) == 0)
+	{
+	  char *p = arg + sizeof ("--speed=") - 1;
+	  int num;
+	  
+	  if (! safe_parse_maxint (&p, &num))
+	    return 1;
+
+	  speed = (unsigned int) num;
+	}
+      else if (grub_memcmp (arg, "--port=", sizeof ("--port=") - 1) == 0)
+	{
+	  char *p = arg + sizeof ("--port=") - 1;
+	  int num;
+	  
+	  if (! safe_parse_maxint (&p, &num))
+	    return 1;
+
+	  port = (unsigned short) num;
+	}
+      else if (grub_memcmp (arg, "--word=", sizeof ("--word=") - 1) == 0)
+	{
+	  char *p = arg + sizeof ("--word=") - 1;
+	  int len;
+	  
+	  if (! safe_parse_maxint (&p, &len))
+	    return 1;
+
+	  switch (len)
+	    {
+	    case 5: word_len = UART_5BITS_WORD; break;
+	    case 6: word_len = UART_6BITS_WORD; break;
+	    case 7: word_len = UART_7BITS_WORD; break;
+	    case 8: word_len = UART_8BITS_WORD; break;
+	    default:
+	      errnum = ERR_BAD_ARGUMENT;
+	      return 1;
+	    }
+	}
+      else if (grub_memcmp (arg, "--stop=", sizeof ("--stop=") - 1) == 0)
+	{
+	  char *p = arg + sizeof ("--stop=") - 1;
+	  int len;
+	  
+	  if (! safe_parse_maxint (&p, &len))
+	    return 1;
+
+	  switch (len)
+	    {
+	    case 1: stop_bit_len = UART_1_STOP_BIT; break;
+	    case 2: stop_bit_len = UART_2_STOP_BITS; break;
+	    default:
+	      errnum = ERR_BAD_ARGUMENT;
+	      return 1;
+	    }
+	}
+      else if (grub_memcmp (arg, "--parity=", sizeof ("--parity=") - 1) == 0)
+	{
+	  char *p = arg + sizeof ("--parity=") - 1;
+
+	  if (grub_memcmp (p, "no", sizeof ("no") - 1) == 0)
+	    parity = UART_NO_PARITY;
+	  else if (grub_memcmp (p, "odd", sizeof ("odd") - 1) == 0)
+	    parity = UART_ODD_PARITY;
+	  else if (grub_memcmp (p, "even", sizeof ("even") - 1) == 0)
+	    parity = UART_EVEN_PARITY;
+	  else
+	    {
+	      errnum = ERR_BAD_ARGUMENT;
+	      return 1;
+	    }
+	}
+# ifdef GRUB_UTIL
+      /* In the grub shell, don't use any port number but open a tty
+	 device instead.  */
+      else if (grub_memcmp (arg, "--device=", sizeof ("--device=") - 1) == 0)
+	{
+	  char *p = arg + sizeof ("--device=") - 1;
+	  char dev[256];	/* XXX */
+	  char *q = dev;
+	  
+	  while (*p && ! grub_isspace (*p))
+	    *q++ = *p++;
+	  
+	  *q = 0;
+	  serial_set_device (dev);
+	}
+# endif /* GRUB_UTIL */
+      else
+	break;
+
+      arg = skip_to (0, arg);
+    }
+
+  /* Initialize the serial unit.  */
+  if (! serial_hw_init (port, speed, word_len, parity, stop_bit_len))
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+  
+  return 0;
+}
+
+static struct builtin builtin_serial =
+{
+  "serial",
+  serial_func,
+  BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "serial [--unit=UNIT] [--port=PORT] [--speed=SPEED] [--word=WORD] [--parity=PARITY] [--stop=STOP] [--device=DEV]",
+  "Initialize a serial device. UNIT is a digit that specifies which serial"
+  " device is used (e.g. 0 == COM1). If you need to specify the port number,"
+  " set it by --port. SPEED is the DTE-DTE speed. WORD is the word length,"
+  " PARITY is the type of parity, which is one of `no', `odd' and `even'."
+  " STOP is the length of stop bit(s). The option --device can be used only"
+  " in the grub shell, which specifies the file name of a tty device. The"
+  " default values are COM1, 9600, 8N1."
+};
+#endif /* SUPPORT_SERIAL */
+
+
+/* setkey */
+struct keysym
+{
+  char *unshifted_name;			/* the name in unshifted state */
+  char *shifted_name;			/* the name in shifted state */
+  unsigned char unshifted_ascii;	/* the ascii code in unshifted state */
+  unsigned char shifted_ascii;		/* the ascii code in shifted state */
+  unsigned char keycode;		/* keyboard scancode */
+};
+
+/* The table for key symbols. If the "shifted" member of an entry is
+   NULL, the entry does not have shifted state.  */
+static struct keysym keysym_table[] =
+{
+  {"escape",		0,		0x1b,	0,	0x01},
+  {"1",			"exclam",	'1',	'!',	0x02},
+  {"2",			"at",		'2',	'@',	0x03},
+  {"3",			"numbersign",	'3',	'#',	0x04},
+  {"4",			"dollar",	'4',	'$',	0x05},
+  {"5",			"percent",	'5',	'%',	0x06},
+  {"6",			"caret",	'6',	'^',	0x07},
+  {"7",			"ampersand",	'7',	'&',	0x08},
+  {"8",			"asterisk",	'8',	'*',	0x09},
+  {"9",			"parenleft",	'9',	'(',	0x0a},
+  {"0",			"parenright",	'0',	')',	0x0b},
+  {"minus",		"underscore",	'-',	'_',	0x0c},
+  {"equal",		"plus",		'=',	'+',	0x0d},
+  {"backspace",		0,		'\b',	0,	0x0e},
+  {"tab",		0,		'\t',	0,	0x0f},
+  {"q",			"Q",		'q',	'Q',	0x10},
+  {"w",			"W",		'w',	'W',	0x11},
+  {"e",			"E",		'e',	'E',	0x12},
+  {"r",			"R",		'r',	'R',	0x13},
+  {"t",			"T",		't',	'T',	0x14},
+  {"y",			"Y",		'y',	'Y',	0x15},
+  {"u",			"U",		'u',	'U',	0x16},
+  {"i",			"I",		'i',	'I',	0x17},
+  {"o",			"O",		'o',	'O',	0x18},
+  {"p",			"P",		'p',	'P',	0x19},
+  {"bracketleft",	"braceleft",	'[',	'{',	0x1a},
+  {"bracketright",	"braceright",	']',	'}',	0x1b},
+  {"enter",		0,		'\n',	0,	0x1c},
+  {"control",		0,		0,	0,	0x1d},
+  {"a",			"A",		'a',	'A',	0x1e},
+  {"s",			"S",		's',	'S',	0x1f},
+  {"d",			"D",		'd',	'D',	0x20},
+  {"f",			"F",		'f',	'F',	0x21},
+  {"g",			"G",		'g',	'G',	0x22},
+  {"h",			"H",		'h',	'H',	0x23},
+  {"j",			"J",		'j',	'J',	0x24},
+  {"k",			"K",		'k',	'K',	0x25},
+  {"l",			"L",		'l',	'L',	0x26},
+  {"semicolon",		"colon",	';',	':',	0x27},
+  {"quote",		"doublequote",	'\'',	'"',	0x28},
+  {"backquote",		"tilde",	'`',	'~',	0x29},
+  {"shift",		0,		0,	0,	0x2a},
+  {"backslash",		"bar",		'\\',	'|',	0x2b},
+  {"z",			"Z",		'z',	'Z',	0x2c},
+  {"x",			"X",		'x',	'X',	0x2d},
+  {"c",			"C",		'c',	'C',	0x2e},
+  {"v",			"V",		'v',	'V',	0x2f},
+  {"b",			"B",		'b',	'B',	0x30},
+  {"n",			"N",		'n',	'N',	0x31},
+  {"m",			"M",		'm',	'M',	0x32},
+  {"comma",		"less",		',',	'<',	0x33},
+  {"period",		"greater",	'.',	'>',	0x34},
+  {"slash",		"question",	'/',	'?',	0x35},
+  {"alt",		0,		0,	0,	0x38},
+  {"space",		0,		' ',	0,	0x39},
+  {"capslock",		0,		0,	0,	0x3a},
+  {"F1",		0,		0,	0,	0x3b},
+  {"F2",		0,		0,	0,	0x3c},
+  {"F3",		0,		0,	0,	0x3d},
+  {"F4",		0,		0,	0,	0x3e},
+  {"F5",		0,		0,	0,	0x3f},
+  {"F6",		0,		0,	0,	0x40},
+  {"F7",		0,		0,	0,	0x41},
+  {"F8",		0,		0,	0,	0x42},
+  {"F9",		0,		0,	0,	0x43},
+  {"F10",		0,		0,	0,	0x44},
+  /* Caution: do not add NumLock here! we cannot deal with it properly.  */
+  {"delete",		0,		0x7f,	0,	0x53}
+};
+
+static int
+setkey_func (char *arg, int flags)
+{
+  char *to_key, *from_key;
+  int to_code, from_code;
+  int map_in_interrupt = 0;
+  
+  static int find_key_code (char *key)
+    {
+      int i;
+
+      for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
+	{
+	  if (keysym_table[i].unshifted_name &&
+	      grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
+	    return keysym_table[i].keycode;
+	  else if (keysym_table[i].shifted_name &&
+		   grub_strcmp (key, keysym_table[i].shifted_name) == 0)
+	    return keysym_table[i].keycode;
+	}
+      
+      return 0;
+    }
+  
+  static int find_ascii_code (char *key)
+    {
+      int i;
+      
+      for (i = 0; i < sizeof (keysym_table) / sizeof (keysym_table[0]); i++)
+	{
+	  if (keysym_table[i].unshifted_name &&
+	      grub_strcmp (key, keysym_table[i].unshifted_name) == 0)
+	    return keysym_table[i].unshifted_ascii;
+	  else if (keysym_table[i].shifted_name &&
+		   grub_strcmp (key, keysym_table[i].shifted_name) == 0)
+	    return keysym_table[i].shifted_ascii;
+	}
+      
+      return 0;
+    }
+  
+  to_key = arg;
+  from_key = skip_to (0, to_key);
+
+  if (! *to_key)
+    {
+      /* If the user specifies no argument, reset the key mappings.  */
+      grub_memset (bios_key_map, 0, KEY_MAP_SIZE * sizeof (unsigned short));
+      grub_memset (ascii_key_map, 0, KEY_MAP_SIZE * sizeof (unsigned short));
+
+      return 0;
+    }
+  else if (! *from_key)
+    {
+      /* The user must specify two arguments or zero argument.  */
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+  
+  nul_terminate (to_key);
+  nul_terminate (from_key);
+  
+  to_code = find_ascii_code (to_key);
+  from_code = find_ascii_code (from_key);
+  if (! to_code || ! from_code)
+    {
+      map_in_interrupt = 1;
+      to_code = find_key_code (to_key);
+      from_code = find_key_code (from_key);
+      if (! to_code || ! from_code)
+	{
+	  errnum = ERR_BAD_ARGUMENT;
+	  return 1;
+	}
+    }
+  
+  if (map_in_interrupt)
+    {
+      int i;
+      
+      /* Find an empty slot.  */
+      for (i = 0; i < KEY_MAP_SIZE; i++)
+	{
+	  if ((bios_key_map[i] & 0xff) == from_code)
+	    /* Perhaps the user wants to overwrite the map.  */
+	    break;
+	  
+	  if (! bios_key_map[i])
+	    break;
+	}
+      
+      if (i == KEY_MAP_SIZE)
+	{
+	  errnum = ERR_WONT_FIT;
+	  return 1;
+	}
+      
+      if (to_code == from_code)
+	/* If TO is equal to FROM, delete the entry.  */
+	grub_memmove ((char *) &bios_key_map[i],
+		      (char *) &bios_key_map[i + 1],
+		      sizeof (unsigned short) * (KEY_MAP_SIZE - i));
+      else
+	bios_key_map[i] = (to_code << 8) | from_code;
+      
+      /* Ugly but should work.  */
+      unset_int15_handler ();
+      set_int15_handler ();
+    }
+  else
+    {
+      int i;
+      
+      /* Find an empty slot.  */
+      for (i = 0; i < KEY_MAP_SIZE; i++)
+	{
+	  if ((ascii_key_map[i] & 0xff) == from_code)
+	    /* Perhaps the user wants to overwrite the map.  */
+	    break;
+	  
+	  if (! ascii_key_map[i])
+	    break;
+	}
+      
+      if (i == KEY_MAP_SIZE)
+	{
+	  errnum = ERR_WONT_FIT;
+	  return 1;
+	}
+      
+      if (to_code == from_code)
+	/* If TO is equal to FROM, delete the entry.  */
+	grub_memmove ((char *) &ascii_key_map[i],
+		      (char *) &ascii_key_map[i + 1],
+		      sizeof (unsigned short) * (KEY_MAP_SIZE - i));
+      else
+	ascii_key_map[i] = (to_code << 8) | from_code;
+    }
+      
+  return 0;
+}
+
+static struct builtin builtin_setkey =
+{
+  "setkey",
+  setkey_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "setkey [TO_KEY FROM_KEY]",
+  "Change the keyboard map. The key FROM_KEY is mapped to the key TO_KEY."
+  " A key must be an alphabet, a digit, or one of these: escape, exclam,"
+  " at, numbersign, dollar, percent, caret, ampersand, asterisk, parenleft,"
+  " parenright, minus, underscore, equal, plus, backspace, tab, bracketleft,"
+  " braceleft, bracketright, braceright, enter, control, semicolon, colon,"
+  " quote, doublequote, backquote, tilde, shift, backslash, bar, comma,"
+  " less, period, greater, slash, question, alt, space, capslock, FX (X"
+  " is a digit), and delete. If no argument is specified, reset key"
+  " mappings."
+};
+
+
+/* setup */
+static int
+setup_func (char *arg, int flags)
+{
+  /* Point to the string of the installed drive/partition.  */
+  char *install_ptr;
+  /* Point to the string of the drive/parition where the GRUB images
+     reside.  */
+  char *image_ptr;
+  unsigned long installed_drive, installed_partition;
+  unsigned long image_drive, image_partition;
+  unsigned long tmp_drive, tmp_partition;
+  char stage1[64];
+  char stage2[64];
+  char config_filename[64];
+  char real_config_filename[64];
+  char cmd_arg[256];
+  char device[16];
+  char *buffer = (char *) RAW_ADDR (0x100000);
+  int is_force_lba = 0;
+  char *stage2_arg = 0;
+  char *prefix = 0;
+
+  auto int check_file (char *file);
+  auto void sprint_device (int drive, int partition);
+  auto int embed_stage1_5 (char * stage1_5, int drive, int partition);
+  
+  /* Check if the file FILE exists like Autoconf.  */
+  int check_file (char *file)
+    {
+      int ret;
+      
+      grub_printf (" Checking if \"%s\" exists... ", file);
+      ret = grub_open (file);
+      if (ret)
+	{
+	  grub_close ();
+	  grub_printf ("yes\n");
+	}
+      else
+	grub_printf ("no\n");
+
+      return ret;
+    }
+  
+  /* Construct a device name in DEVICE.  */
+  void sprint_device (int drive, int partition)
+    {
+      grub_sprintf (device, "(%cd%d",
+		    (drive & 0x80) ? 'h' : 'f',
+		    drive & ~0x80);
+      if ((partition & 0xFF0000) != 0xFF0000)
+	{
+	  char tmp[16];
+	  grub_sprintf (tmp, ",%d", (partition >> 16) & 0xFF);
+	  grub_strncat (device, tmp, 256);
+	}
+      if ((partition & 0x00FF00) != 0x00FF00)
+	{
+	  char tmp[16];
+	  grub_sprintf (tmp, ",%c", 'a' + ((partition >> 8) & 0xFF));
+	  grub_strncat (device, tmp, 256);
+	}
+      grub_strncat (device, ")", 256);
+    }
+  
+  int embed_stage1_5 (char *stage1_5, int drive, int partition)
+    {
+      /* We install GRUB into the MBR, so try to embed the
+	 Stage 1.5 in the sectors right after the MBR.  */
+      sprint_device (drive, partition);
+      grub_sprintf (cmd_arg, "%s %s", stage1_5, device);
+	      
+      /* Notify what will be run.  */
+      grub_printf (" Running \"embed %s\"... ", cmd_arg);
+      
+      embed_func (cmd_arg, flags);
+      if (! errnum)
+	{
+	  /* Construct the blocklist representation.  */
+	  grub_sprintf (buffer, "%s%s", device, embed_info);
+	  grub_printf ("succeeded\n");
+	  return 1;
+	}
+      else
+	{
+	  grub_printf ("failed (this is not fatal)\n");
+	  return 0;
+	}
+    }
+	  
+  struct stage1_5_map {
+    char *fsys;
+    char *name;
+  };
+  struct stage1_5_map stage1_5_map[] =
+  {
+    {"ext2fs",   "/e2fs_stage1_5"},
+    {"fat",      "/fat_stage1_5"},
+    {"ffs",      "/ffs_stage1_5"},
+    {"jfs",      "/jfs_stage1_5"},
+    {"minix",    "/minix_stage1_5"},
+    {"reiserfs", "/reiserfs_stage1_5"},
+    {"vstafs",   "/vstafs_stage1_5"},
+    {"xfs",      "/xfs_stage1_5"}
+  };
+
+  tmp_drive = saved_drive;
+  tmp_partition = saved_partition;
+
+  /* Check if the user specifies --force-lba.  */
+  while (1)
+    {
+      if (grub_memcmp ("--force-lba", arg, sizeof ("--force-lba") - 1) == 0)
+	{
+	  is_force_lba = 1;
+	  arg = skip_to (0, arg);
+	}
+      else if (grub_memcmp ("--prefix=", arg, sizeof ("--prefix=") - 1) == 0)
+	{
+	  prefix = arg + sizeof ("--prefix=") - 1;
+	  arg = skip_to (0, arg);
+	  nul_terminate (prefix);
+	}
+#ifdef GRUB_UTIL
+      else if (grub_memcmp ("--stage2=", arg, sizeof ("--stage2=") - 1) == 0)
+	{
+	  stage2_arg = arg;
+	  arg = skip_to (0, arg);
+	  nul_terminate (stage2_arg);
+	}
+#endif /* GRUB_UTIL */
+      else
+	break;
+    }
+  
+  install_ptr = arg;
+  image_ptr = skip_to (0, install_ptr);
+
+  /* Make sure that INSTALL_PTR is valid.  */
+  set_device (install_ptr);
+  if (errnum)
+    return 1;
+
+  installed_drive = current_drive;
+  installed_partition = current_partition;
+  
+  /* Mount the drive pointed by IMAGE_PTR.  */
+  if (*image_ptr)
+    {
+      /* If the drive/partition where the images reside is specified,
+	 get the drive and the partition.  */
+      set_device (image_ptr);
+      if (errnum)
+	return 1;
+    }
+  else
+    {
+      /* If omitted, use SAVED_PARTITION and SAVED_DRIVE.  */
+      current_drive = saved_drive;
+      current_partition = saved_partition;
+    }
+
+  image_drive = saved_drive = current_drive;
+  image_partition = saved_partition = current_partition;
+
+  /* Open it.  */
+  if (! open_device ())
+    goto fail;
+
+  /* Check if stage1 exists. If the user doesn't specify the option
+     `--prefix', attempt /boot/grub and /grub.  */
+  /* NOTE: It is dangerous to run this command without `--prefix' in the
+     grub shell, since that affects `--stage2'.  */
+  if (! prefix)
+    {
+      prefix = "/boot/grub";
+      grub_sprintf (stage1, "%s%s", prefix, "/stage1");
+      if (! check_file (stage1))
+	{
+	  errnum = ERR_NONE;
+	  prefix = "/grub";
+	  grub_sprintf (stage1, "%s%s", prefix, "/stage1");
+	  if (! check_file (stage1))
+	    goto fail;
+	}
+    }
+  else
+    {
+      grub_sprintf (stage1, "%s%s", prefix, "/stage1");
+      if (! check_file (stage1))
+	goto fail;
+    }
+
+  /* The prefix was determined.  */
+  grub_sprintf (stage2, "%s%s", prefix, "/stage2");
+  grub_sprintf (config_filename, "%s%s", prefix, "/menu.lst");
+  *real_config_filename = 0;
+
+  /* Check if stage2 exists.  */
+  if (! check_file (stage2))
+    goto fail;
+
+  {
+    char *fsys = fsys_table[fsys_type].name;
+    int i;
+    int size = sizeof (stage1_5_map) / sizeof (stage1_5_map[0]);
+    
+    /* Iterate finding the same filesystem name as FSYS.  */
+    for (i = 0; i < size; i++)
+      if (grub_strcmp (fsys, stage1_5_map[i].fsys) == 0)
+	{
+	  /* OK, check if the Stage 1.5 exists.  */
+	  char stage1_5[64];
+	  
+	  grub_sprintf (stage1_5, "%s%s", prefix, stage1_5_map[i].name);
+	  if (check_file (stage1_5))
+	    {
+	      if (embed_stage1_5 (stage1_5, 
+				    installed_drive, installed_partition)
+		  || embed_stage1_5 (stage1_5, 
+				     image_drive, image_partition))
+		{
+		  grub_strcpy (real_config_filename, config_filename);
+		  sprint_device (image_drive, image_partition);
+		  grub_sprintf (config_filename, "%s%s", device, stage2);
+		  grub_strcpy (stage2, buffer);
+		}
+	    }
+	  errnum = 0;
+	  break;
+	}
+  }
+
+  /* Construct a string that is used by the command "install" as its
+     arguments.  */
+  sprint_device (installed_drive, installed_partition);
+  
+#if 1
+  /* Don't embed a drive number unnecessarily.  */
+  grub_sprintf (cmd_arg, "%s%s%s%s %s%s %s p %s %s",
+		is_force_lba? "--force-lba " : "",
+		stage2_arg? stage2_arg : "",
+		stage2_arg? " " : "",
+		stage1,
+		(installed_drive != image_drive) ? "d " : "",
+		device,
+		stage2,
+		config_filename,
+		real_config_filename);
+#else /* NOT USED */
+  /* This code was used, because we belived some BIOSes had a problem
+     that they didn't pass a booting drive correctly. It turned out,
+     however, stage1 could trash a booting drive when checking LBA support,
+     because some BIOSes modified the register %dx in INT 13H, AH=48H.
+     So it becamed unclear whether GRUB should use a pre-defined booting
+     drive or not. If the problem still exists, it would be necessary to
+     switch back to this code.  */
+  grub_sprintf (cmd_arg, "%s%s%s%s d %s %s p %s %s",
+		is_force_lba? "--force-lba " : "",
+		stage2_arg? stage2_arg : "",
+		stage2_arg? " " : "",
+		stage1,
+		device,
+		stage2,
+		config_filename,
+		real_config_filename);
+#endif /* NOT USED */
+  
+  /* Notify what will be run.  */
+  grub_printf (" Running \"install %s\"... ", cmd_arg);
+
+  /* Make sure that SAVED_DRIVE and SAVED_PARTITION are identical
+     with IMAGE_DRIVE and IMAGE_PARTITION, respectively.  */
+  saved_drive = image_drive;
+  saved_partition = image_partition;
+  
+  /* Run the command.  */
+  if (! install_func (cmd_arg, flags))
+    grub_printf ("succeeded\nDone.\n");
+  else
+    grub_printf ("failed\n");
+
+ fail:
+  saved_drive = tmp_drive;
+  saved_partition = tmp_partition;
+  return errnum;
+}
+
+static struct builtin builtin_setup =
+{
+  "setup",
+  setup_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "setup [--prefix=DIR] [--stage2=STAGE2_FILE] [--force-lba] INSTALL_DEVICE [IMAGE_DEVICE]",
+  "Set up the installation of GRUB automatically. This command uses"
+  " the more flexible command \"install\" in the backend and installs"
+  " GRUB into the device INSTALL_DEVICE. If IMAGE_DEVICE is specified,"
+  " then find the GRUB images in the device IMAGE_DEVICE, otherwise"
+  " use the current \"root device\", which can be set by the command"
+  " \"root\". If you know that your BIOS should support LBA but GRUB"
+  " doesn't work in LBA mode, specify the option `--force-lba'."
+  " If you install GRUB under the grub shell and you cannot unmount the"
+  " partition where GRUB images reside, specify the option `--stage2'"
+  " to tell GRUB the file name under your OS."
+};
+
+
+#if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES)
+/* terminal */
+static int
+terminal_func (char *arg, int flags)
+{
+  /* The index of the default terminal in TERM_TABLE.  */
+  int default_term = -1;
+  struct term_entry *prev_term = current_term;
+  int to = -1;
+  int lines = 0;
+  int no_message = 0;
+  unsigned long term_flags = 0;
+  /* XXX: Assume less than 32 terminals.  */
+  unsigned long term_bitmap = 0;
+
+  /* Get GNU-style long options.  */
+  while (1)
+    {
+      if (grub_memcmp (arg, "--dumb", sizeof ("--dumb") - 1) == 0)
+	term_flags |= TERM_DUMB;
+      else if (grub_memcmp (arg, "--no-echo", sizeof ("--no-echo") - 1) == 0)
+	/* ``--no-echo'' implies ``--no-edit''.  */
+	term_flags |= (TERM_NO_ECHO | TERM_NO_EDIT);
+      else if (grub_memcmp (arg, "--no-edit", sizeof ("--no-edit") - 1) == 0)
+	term_flags |= TERM_NO_EDIT;
+      else if (grub_memcmp (arg, "--timeout=", sizeof ("--timeout=") - 1) == 0)
+	{
+	  char *val = arg + sizeof ("--timeout=") - 1;
+	  
+	  if (! safe_parse_maxint (&val, &to))
+	    return 1;
+	}
+      else if (grub_memcmp (arg, "--lines=", sizeof ("--lines=") - 1) == 0)
+	{
+	  char *val = arg + sizeof ("--lines=") - 1;
+
+	  if (! safe_parse_maxint (&val, &lines))
+	    return 1;
+
+	  /* Probably less than four is meaningless....  */
+	  if (lines < 4)
+	    {
+	      errnum = ERR_BAD_ARGUMENT;
+	      return 1;
+	    }
+	}
+      else if (grub_memcmp (arg, "--silent", sizeof ("--silent") - 1) == 0)
+	no_message = 1;
+      else
+	break;
+
+      arg = skip_to (0, arg);
+    }
+  
+  /* If no argument is specified, show current setting.  */
+  if (! *arg)
+    {
+      grub_printf ("%s%s%s%s\n",
+		   current_term->name,
+		   current_term->flags & TERM_DUMB ? " (dumb)" : "",
+		   current_term->flags & TERM_NO_EDIT ? " (no edit)" : "",
+		   current_term->flags & TERM_NO_ECHO ? " (no echo)" : "");
+      return 0;
+    }
+
+  while (*arg)
+    {
+      int i;
+      char *next = skip_to (0, arg);
+      
+      nul_terminate (arg);
+
+      for (i = 0; term_table[i].name; i++)
+	{
+	  if (grub_strcmp (arg, term_table[i].name) == 0)
+	    {
+	      if (term_table[i].flags & TERM_NEED_INIT)
+		{
+		  errnum = ERR_DEV_NEED_INIT;
+		  return 1;
+		}
+	      
+	      if (default_term < 0)
+		default_term = i;
+
+	      term_bitmap |= (1 << i);
+	      break;
+	    }
+	}
+
+      if (! term_table[i].name)
+	{
+	  errnum = ERR_BAD_ARGUMENT;
+	  return 1;
+	}
+
+      arg = next;
+    }
+
+  /* If multiple terminals are specified, wait until the user pushes any
+     key on one of the terminals.  */
+  if (term_bitmap & ~(1 << default_term))
+    {
+      int time1, time2 = -1;
+
+      /* XXX: Disable the pager.  */
+      count_lines = -1;
+      
+      /* Get current time.  */
+      while ((time1 = getrtsecs ()) == 0xFF)
+	;
+
+      /* Wait for a key input.  */
+      while (to)
+	{
+	  int i;
+
+	  for (i = 0; term_table[i].name; i++)
+	    {
+	      if (term_bitmap & (1 << i))
+		{
+		  if (term_table[i].checkkey () >= 0)
+		    {
+		      (void) term_table[i].getkey ();
+		      default_term = i;
+		      
+		      goto end;
+		    }
+		}
+	    }
+	  
+	  /* Prompt the user, once per sec.  */
+	  if ((time1 = getrtsecs ()) != time2 && time1 != 0xFF)
+	    {
+	      if (! no_message)
+		{
+		  /* Need to set CURRENT_TERM to each of selected
+		     terminals.  */
+		  for (i = 0; term_table[i].name; i++)
+		    if (term_bitmap & (1 << i))
+		      {
+			current_term = term_table + i;
+			grub_printf ("\rPress any key to continue.\n");
+		      }
+		  
+		  /* Restore CURRENT_TERM.  */
+		  current_term = prev_term;
+		}
+	      
+	      time2 = time1;
+	      if (to > 0)
+		to--;
+	    }
+	}
+    }
+
+ end:
+  current_term = term_table + default_term;
+  current_term->flags = term_flags;
+  
+  if (lines)
+    max_lines = lines;
+  else
+    /* 24 would be a good default value.  */
+    max_lines = 24;
+  
+  /* If the interface is currently the command-line,
+     restart it to repaint the screen.  */
+  if (current_term != prev_term && (flags & BUILTIN_CMDLINE))
+    grub_longjmp (restart_cmdline_env, 0);
+  
+  return 0;
+}
+
+static struct builtin builtin_terminal =
+{
+  "terminal",
+  terminal_func,
+  BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "terminal [--dumb] [--no-echo] [--no-edit] [--timeout=SECS] [--lines=LINES] [--silent] [console] [serial] [hercules]",
+  "Select a terminal. When multiple terminals are specified, wait until"
+  " you push any key to continue. If both console and serial are specified,"
+  " the terminal to which you input a key first will be selected. If no"
+  " argument is specified, print current setting. The option --dumb"
+  " specifies that your terminal is dumb, otherwise, vt100-compatibility"
+  " is assumed. If you specify --no-echo, input characters won't be echoed."
+  " If you specify --no-edit, the BASH-like editing feature will be disabled."
+  " If --timeout is present, this command will wait at most for SECS"
+  " seconds. The option --lines specifies the maximum number of lines."
+  " The option --silent is used to suppress messages."
+};
+#endif /* SUPPORT_SERIAL || SUPPORT_HERCULES */
+
+
+#ifdef SUPPORT_SERIAL
+static int
+terminfo_func (char *arg, int flags)
+{
+  struct terminfo term;
+
+  if (*arg)
+    {
+      struct
+      {
+	const char *name;
+	char *var;
+      }
+      options[] =
+	{
+	  {"--name=", term.name},
+	  {"--cursor-address=", term.cursor_address},
+	  {"--clear-screen=", term.clear_screen},
+	  {"--enter-standout-mode=", term.enter_standout_mode},
+	  {"--exit-standout-mode=", term.exit_standout_mode}
+	};
+
+      grub_memset (&term, 0, sizeof (term));
+      
+      while (*arg)
+	{
+	  int i;
+	  char *next = skip_to (0, arg);
+	      
+	  nul_terminate (arg);
+	  
+	  for (i = 0; i < sizeof (options) / sizeof (options[0]); i++)
+	    {
+	      const char *name = options[i].name;
+	      int len = grub_strlen (name);
+	      
+	      if (! grub_memcmp (arg, name, len))
+		{
+		  grub_strcpy (options[i].var, ti_unescape_string (arg + len));
+		  break;
+		}
+	    }
+
+	  if (i == sizeof (options) / sizeof (options[0]))
+	    {
+	      errnum = ERR_BAD_ARGUMENT;
+	      return errnum;
+	    }
+
+	  arg = next;
+	}
+
+      if (term.name[0] == 0 || term.cursor_address[0] == 0)
+	{
+	  errnum = ERR_BAD_ARGUMENT;
+	  return errnum;
+	}
+
+      ti_set_term (&term);
+    }
+  else
+    {
+      /* No option specifies printing out current settings.  */
+      ti_get_term (&term);
+
+      grub_printf ("name=%s\n",
+		   ti_escape_string (term.name));
+      grub_printf ("cursor_address=%s\n",
+		   ti_escape_string (term.cursor_address));
+      grub_printf ("clear_screen=%s\n",
+		   ti_escape_string (term.clear_screen));
+      grub_printf ("enter_standout_mode=%s\n",
+		   ti_escape_string (term.enter_standout_mode));
+      grub_printf ("exit_standout_mode=%s\n",
+		   ti_escape_string (term.exit_standout_mode));
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_terminfo =
+{
+  "terminfo",
+  terminfo_func,
+  BUILTIN_MENU | BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "terminfo [--name=NAME --cursor-address=SEQ [--clear-screen=SEQ]"
+  " [--enter-standout-mode=SEQ] [--exit-standout-mode=SEQ]]",
+  
+  "Define the capabilities of your terminal. Use this command to"
+  " define escape sequences, if it is not vt100-compatible."
+  " You may use \\e for ESC and ^X for a control character."
+  " If no option is specified, the current settings are printed."
+};
+#endif /* SUPPORT_SERIAL */
+	  
+
+/* testload */
+static int
+testload_func (char *arg, int flags)
+{
+  int i;
+
+  kernel_type = KERNEL_TYPE_NONE;
+
+  if (! grub_open (arg))
+    return 1;
+
+  disk_read_hook = disk_read_print_func;
+
+  /* Perform filesystem test on the specified file.  */
+  /* Read whole file first. */
+  grub_printf ("Whole file: ");
+
+  grub_read ((char *) RAW_ADDR (0x100000), -1);
+
+  /* Now compare two sections of the file read differently.  */
+
+  for (i = 0; i < 0x10ac0; i++)
+    {
+      *((unsigned char *) RAW_ADDR (0x200000 + i)) = 0;
+      *((unsigned char *) RAW_ADDR (0x300000 + i)) = 1;
+    }
+
+  /* First partial read.  */
+  grub_printf ("\nPartial read 1: ");
+
+  grub_seek (0);
+  grub_read ((char *) RAW_ADDR (0x200000), 0x7);
+  grub_read ((char *) RAW_ADDR (0x200007), 0x100);
+  grub_read ((char *) RAW_ADDR (0x200107), 0x10);
+  grub_read ((char *) RAW_ADDR (0x200117), 0x999);
+  grub_read ((char *) RAW_ADDR (0x200ab0), 0x10);
+  grub_read ((char *) RAW_ADDR (0x200ac0), 0x10000);
+
+  /* Second partial read.  */
+  grub_printf ("\nPartial read 2: ");
+
+  grub_seek (0);
+  grub_read ((char *) RAW_ADDR (0x300000), 0x10000);
+  grub_read ((char *) RAW_ADDR (0x310000), 0x10);
+  grub_read ((char *) RAW_ADDR (0x310010), 0x7);
+  grub_read ((char *) RAW_ADDR (0x310017), 0x10);
+  grub_read ((char *) RAW_ADDR (0x310027), 0x999);
+  grub_read ((char *) RAW_ADDR (0x3109c0), 0x100);
+
+  grub_printf ("\nHeader1 = 0x%x, next = 0x%x, next = 0x%x, next = 0x%x\n",
+	       *((int *) RAW_ADDR (0x200000)),
+	       *((int *) RAW_ADDR (0x200004)),
+	       *((int *) RAW_ADDR (0x200008)),
+	       *((int *) RAW_ADDR (0x20000c)));
+
+  grub_printf ("Header2 = 0x%x, next = 0x%x, next = 0x%x, next = 0x%x\n",
+	       *((int *) RAW_ADDR (0x300000)),
+	       *((int *) RAW_ADDR (0x300004)),
+	       *((int *) RAW_ADDR (0x300008)),
+	       *((int *) RAW_ADDR (0x30000c)));
+
+  for (i = 0; i < 0x10ac0; i++)
+    if (*((unsigned char *) RAW_ADDR (0x200000 + i))
+	!= *((unsigned char *) RAW_ADDR (0x300000 + i)))
+      break;
+
+  grub_printf ("Max is 0x10ac0: i=0x%x, filepos=0x%x\n", i, filepos);
+  disk_read_hook = 0;
+  grub_close ();
+  return 0;
+}
+
+static struct builtin builtin_testload =
+{
+  "testload",
+  testload_func,
+  BUILTIN_CMDLINE,
+  "testload FILE",
+  "Read the entire contents of FILE in several different ways and"
+  " compares them, to test the filesystem code. The output is somewhat"
+  " cryptic, but if no errors are reported and the final `i=X,"
+  " filepos=Y' reading has X and Y equal, then it is definitely"
+  " consistent, and very likely works correctly subject to a"
+  " consistent offset error. If this test succeeds, then a good next"
+  " step is to try loading a kernel."
+};
+
+
+/* testvbe MODE */
+static int
+testvbe_func (char *arg, int flags)
+{
+  int mode_number;
+  struct vbe_controller controller;
+  struct vbe_mode mode;
+  
+  if (! *arg)
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+
+  if (! safe_parse_maxint (&arg, &mode_number))
+    return 1;
+
+  /* Preset `VBE2'.  */
+  grub_memmove (controller.signature, "VBE2", 4);
+
+  /* Detect VBE BIOS.  */
+  if (get_vbe_controller_info (&controller) != 0x004F)
+    {
+      grub_printf (" VBE BIOS is not present.\n");
+      return 0;
+    }
+  
+  if (controller.version < 0x0200)
+    {
+      grub_printf (" VBE version %d.%d is not supported.\n",
+		   (int) (controller.version >> 8),
+		   (int) (controller.version & 0xFF));
+      return 0;
+    }
+
+  if (get_vbe_mode_info (mode_number, &mode) != 0x004F
+      || (mode.mode_attributes & 0x0091) != 0x0091)
+    {
+      grub_printf (" Mode 0x%x is not supported.\n", mode_number);
+      return 0;
+    }
+
+  /* Now trip to the graphics mode.  */
+  if (set_vbe_mode (mode_number | (1 << 14)) != 0x004F)
+    {
+      grub_printf (" Switching to Mode 0x%x failed.\n", mode_number);
+      return 0;
+    }
+
+  /* Draw something on the screen...  */
+  {
+    unsigned char *base_buf = (unsigned char *) mode.phys_base;
+    int scanline = controller.version >= 0x0300
+      ? mode.linear_bytes_per_scanline : mode.bytes_per_scanline;
+    /* FIXME: this assumes that any depth is a modulo of 8.  */
+    int bpp = mode.bits_per_pixel / 8;
+    int width = mode.x_resolution;
+    int height = mode.y_resolution;
+    int x, y;
+    unsigned color = 0;
+
+    /* Iterate drawing on the screen, until the user hits any key.  */
+    while (checkkey () == -1)
+      {
+	for (y = 0; y < height; y++)
+	  {
+	    unsigned char *line_buf = base_buf + scanline * y;
+	    
+	    for (x = 0; x < width; x++)
+	      {
+		unsigned char *buf = line_buf + bpp * x;
+		int i;
+
+		for (i = 0; i < bpp; i++, buf++)
+		  *buf = (color >> (i * 8)) & 0xff;
+	      }
+
+	    color++;
+	  }
+      }
+
+    /* Discard the input.  */
+    getkey ();
+  }
+  
+  /* Back to the default text mode.  */
+  if (set_vbe_mode (0x03) != 0x004F)
+    {
+      /* Why?!  */
+      grub_reboot ();
+    }
+
+  return 0;
+}
+
+static struct builtin builtin_testvbe =
+{
+  "testvbe",
+  testvbe_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "testvbe MODE",
+  "Test the VBE mode MODE. Hit any key to return."
+};
+
+
+#ifdef SUPPORT_NETBOOT
+/* tftpserver */
+static int
+tftpserver_func (char *arg, int flags)
+{
+  if (! *arg || ! ifconfig (0, 0, 0, arg))
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+
+  print_network_configuration ();
+  return 0;
+}
+
+static struct builtin builtin_tftpserver =
+{
+  "tftpserver",
+  tftpserver_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "tftpserver IPADDR",
+  "Override the TFTP server address."
+};
+#endif /* SUPPORT_NETBOOT */
+
+
+/* timeout */
+static int
+timeout_func (char *arg, int flags)
+{
+  if (! safe_parse_maxint (&arg, &grub_timeout))
+    return 1;
+
+  return 0;
+}
+
+static struct builtin builtin_timeout =
+{
+  "timeout",
+  timeout_func,
+  BUILTIN_MENU,
+#if 0
+  "timeout SEC",
+  "Set a timeout, in SEC seconds, before automatically booting the"
+  " default entry (normally the first entry defined)."
+#endif
+};
+
+
+/* title */
+static int
+title_func (char *arg, int flags)
+{
+  /* This function is not actually used at least currently.  */
+  return 0;
+}
+
+static struct builtin builtin_title =
+{
+  "title",
+  title_func,
+  BUILTIN_TITLE,
+#if 0
+  "title [NAME ...]",
+  "Start a new boot entry, and set its name to the contents of the"
+  " rest of the line, starting with the first non-space character."
+#endif
+};
+
+
+/* unhide */
+static int
+unhide_func (char *arg, int flags)
+{
+  if (! set_device (arg))
+    return 1;
+
+  if (! set_partition_hidden_flag (0))
+    return 1;
+
+  return 0;
+}
+
+static struct builtin builtin_unhide =
+{
+  "unhide",
+  unhide_func,
+  BUILTIN_CMDLINE | BUILTIN_MENU | BUILTIN_HELP_LIST,
+  "unhide PARTITION",
+  "Unhide PARTITION by clearing the \"hidden\" bit in its"
+  " partition type code."
+};
+
+
+/* uppermem */
+static int
+uppermem_func (char *arg, int flags)
+{
+  if (! safe_parse_maxint (&arg, (int *) &mbi.mem_upper))
+    return 1;
+
+  mbi.flags &= ~MB_INFO_MEM_MAP;
+  return 0;
+}
+
+static struct builtin builtin_uppermem =
+{
+  "uppermem",
+  uppermem_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "uppermem KBYTES",
+  "Force GRUB to assume that only KBYTES kilobytes of upper memory are"
+  " installed.  Any system address range maps are discarded."
+};
+
+
+/* vbeprobe */
+static int
+vbeprobe_func (char *arg, int flags)
+{
+  struct vbe_controller controller;
+  unsigned short *mode_list;
+  int mode_number = -1;
+  
+  auto unsigned long vbe_far_ptr_to_linear (unsigned long);
+  
+  unsigned long vbe_far_ptr_to_linear (unsigned long ptr)
+    {
+      unsigned short seg = (ptr >> 16);
+      unsigned short off = (ptr & 0xFFFF);
+
+      return (seg << 4) + off;
+    }
+  
+  if (*arg)
+    {
+      if (! safe_parse_maxint (&arg, &mode_number))
+	return 1;
+    }
+  
+  /* Set the signature to `VBE2', to obtain VBE 3.0 information.  */
+  grub_memmove (controller.signature, "VBE2", 4);
+  
+  if (get_vbe_controller_info (&controller) != 0x004F)
+    {
+      grub_printf (" VBE BIOS is not present.\n");
+      return 0;
+    }
+
+  /* Check the version.  */
+  if (controller.version < 0x0200)
+    {
+      grub_printf (" VBE version %d.%d is not supported.\n",
+		   (int) (controller.version >> 8),
+		   (int) (controller.version & 0xFF));
+      return 0;
+    }
+
+  /* Print some information.  */
+  grub_printf (" VBE version %d.%d\n",
+	       (int) (controller.version >> 8),
+	       (int) (controller.version & 0xFF));
+
+  /* Iterate probing modes.  */
+  for (mode_list
+	 = (unsigned short *) vbe_far_ptr_to_linear (controller.video_mode);
+       *mode_list != 0xFFFF;
+       mode_list++)
+    {
+      struct vbe_mode mode;
+      
+      if (get_vbe_mode_info (*mode_list, &mode) != 0x004F)
+	continue;
+
+      /* Skip this, if this is not supported or linear frame buffer
+	 mode is not support.  */
+      if ((mode.mode_attributes & 0x0081) != 0x0081)
+	continue;
+
+      if (mode_number == -1 || mode_number == *mode_list)
+	{
+	  char *model;
+	  switch (mode.memory_model)
+	    {
+	    case 0x00: model = "Text"; break;
+	    case 0x01: model = "CGA graphics"; break;
+	    case 0x02: model = "Hercules graphics"; break;
+	    case 0x03: model = "Planar"; break;
+	    case 0x04: model = "Packed pixel"; break;
+	    case 0x05: model = "Non-chain 4, 256 color"; break;
+	    case 0x06: model = "Direct Color"; break;
+	    case 0x07: model = "YUV"; break;
+	    default: model = "Unknown"; break;
+	    }
+	  
+	  grub_printf ("  0x%x: %s, %ux%ux%u\n",
+		       (unsigned) *mode_list,
+		       model,
+		       (unsigned) mode.x_resolution,
+		       (unsigned) mode.y_resolution,
+		       (unsigned) mode.bits_per_pixel);
+	  
+	  if (mode_number != -1)
+	    break;
+	}
+    }
+
+  if (mode_number != -1 && mode_number != *mode_list)
+    grub_printf ("  Mode 0x%x is not found or supported.\n", mode_number);
+  
+  return 0;
+}
+
+static struct builtin builtin_vbeprobe =
+{
+  "vbeprobe",
+  vbeprobe_func,
+  BUILTIN_CMDLINE | BUILTIN_HELP_LIST,
+  "vbeprobe [MODE]",
+  "Probe VBE information. If the mode number MODE is specified, show only"
+  " the information about only the mode."
+};
+  
+
+/* The table of builtin commands. Sorted in dictionary order.  */
+struct builtin *builtin_table[] =
+{
+  &builtin_blocklist,
+  &builtin_boot,
+#ifdef SUPPORT_NETBOOT
+  &builtin_bootp,
+#endif /* SUPPORT_NETBOOT */
+  &builtin_cat,
+  &builtin_chainloader,
+  &builtin_cmp,
+  &builtin_color,
+  &builtin_configfile,
+  &builtin_debug,
+  &builtin_default,
+#ifdef GRUB_UTIL
+  &builtin_device,
+#endif /* GRUB_UTIL */
+#ifdef SUPPORT_NETBOOT
+  &builtin_dhcp,
+#endif /* SUPPORT_NETBOOT */
+  &builtin_displayapm,
+  &builtin_displaymem,
+#ifdef GRUB_UTIL
+  &builtin_dump,
+#endif /* GRUB_UTIL */
+  &builtin_embed,
+  &builtin_fallback,
+  &builtin_find,
+  &builtin_fstest,
+  &builtin_geometry,
+  &builtin_halt,
+  &builtin_help,
+  &builtin_hiddenmenu,
+  &builtin_hide,
+#ifdef SUPPORT_NETBOOT
+  &builtin_ifconfig,
+#endif /* SUPPORT_NETBOOT */
+  &builtin_impsprobe,
+  &builtin_initrd,
+  &builtin_install,
+  &builtin_ioprobe,
+  &builtin_kernel,
+  &builtin_lock,
+  &builtin_makeactive,
+  &builtin_map,
+#ifdef USE_MD5_PASSWORDS
+  &builtin_md5crypt,
+#endif /* USE_MD5_PASSWORDS */
+  &builtin_module,
+  &builtin_modulenounzip,
+  &builtin_pager,
+  &builtin_partnew,
+  &builtin_parttype,
+  &builtin_password,
+  &builtin_pause,
+#ifdef GRUB_UTIL
+  &builtin_quit,
+#endif /* GRUB_UTIL */
+#ifdef SUPPORT_NETBOOT
+  &builtin_rarp,
+#endif /* SUPPORT_NETBOOT */
+  &builtin_read,
+  &builtin_reboot,
+  &builtin_root,
+  &builtin_rootnoverify,
+  &builtin_savedefault,
+#ifdef SUPPORT_SERIAL
+  &builtin_serial,
+#endif /* SUPPORT_SERIAL */
+  &builtin_setkey,
+  &builtin_setup,
+#if defined(SUPPORT_SERIAL) || defined(SUPPORT_HERCULES)
+  &builtin_terminal,
+#endif /* SUPPORT_SERIAL || SUPPORT_HERCULES */
+#ifdef SUPPORT_SERIAL
+  &builtin_terminfo,
+#endif /* SUPPORT_SERIAL */
+  &builtin_testload,
+  &builtin_testvbe,
+#ifdef SUPPORT_NETBOOT
+  &builtin_tftpserver,
+#endif /* SUPPORT_NETBOOT */
+  &builtin_timeout,
+  &builtin_title,
+  &builtin_unhide,
+  &builtin_uppermem,
+  &builtin_vbeprobe,
+  0
+};
diff -ruN grub-0.94.orig/stage2/disk_io.c stage2/disk_io.c
--- grub-0.94.orig/stage2/disk_io.c	Wed Feb 11 00:22:12 2004
+++ stage2/disk_io.c	Wed Feb 11 00:22:29 2004
@@ -72,6 +72,9 @@
 # ifdef FSYS_XFS
   {"xfs", xfs_mount, xfs_read, xfs_dir, 0, 0},
 # endif
+# ifdef FSYS_UFS2
+  {"ufs2", ufs2_mount, ufs2_read, ufs2_dir, 0, ufs2_embed},
+# endif
   /* XX FFS should come last as it's superblock is commonly crossing tracks
      on floppies from track 1 to 2, while others only use 1.  */
 # ifdef FSYS_FFS
diff -ruN grub-0.94.orig/stage2/disk_io.c.orig stage2/disk_io.c.orig
--- grub-0.94.orig/stage2/disk_io.c.orig	Thu Jan  1 03:00:00 1970
+++ stage2/disk_io.c.orig	Sun Oct 19 19:58:03 2003
@@ -0,0 +1,1741 @@
+/* disk_io.c - implement abstract BIOS disk input and output */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003  Free Software Foundation, Inc.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+
+#include <shared.h>
+#include <filesys.h>
+
+#ifdef SUPPORT_NETBOOT
+# define GRUB	1
+# include <etherboot.h>
+#endif
+
+#ifdef GRUB_UTIL
+# include <device.h>
+#endif
+
+/* instrumentation variables */
+void (*disk_read_hook) (int, int, int) = NULL;
+void (*disk_read_func) (int, int, int) = NULL;
+
+#ifndef STAGE1_5
+int print_possibilities;
+
+static int do_completion;
+static int unique;
+static char *unique_string;
+
+#endif
+
+int fsmax;
+struct fsys_entry fsys_table[NUM_FSYS + 1] =
+{
+  /* TFTP should come first because others don't handle net device.  */
+# ifdef FSYS_TFTP
+  {"tftp", tftp_mount, tftp_read, tftp_dir, tftp_close, 0},
+# endif
+# ifdef FSYS_FAT
+  {"fat", fat_mount, fat_read, fat_dir, 0, 0},
+# endif
+# ifdef FSYS_EXT2FS
+  {"ext2fs", ext2fs_mount, ext2fs_read, ext2fs_dir, 0, 0},
+# endif
+# ifdef FSYS_MINIX
+  {"minix", minix_mount, minix_read, minix_dir, 0, 0},
+# endif
+# ifdef FSYS_REISERFS
+  {"reiserfs", reiserfs_mount, reiserfs_read, reiserfs_dir, 0, reiserfs_embed},
+# endif
+# ifdef FSYS_VSTAFS
+  {"vstafs", vstafs_mount, vstafs_read, vstafs_dir, 0, 0},
+# endif
+# ifdef FSYS_JFS
+  {"jfs", jfs_mount, jfs_read, jfs_dir, 0, jfs_embed},
+# endif
+# ifdef FSYS_XFS
+  {"xfs", xfs_mount, xfs_read, xfs_dir, 0, 0},
+# endif
+  /* XX FFS should come last as it's superblock is commonly crossing tracks
+     on floppies from track 1 to 2, while others only use 1.  */
+# ifdef FSYS_FFS
+  {"ffs", ffs_mount, ffs_read, ffs_dir, 0, ffs_embed},
+# endif
+  {0, 0, 0, 0, 0, 0}
+};
+
+
+/* These have the same format as "boot_drive" and "install_partition", but
+   are meant to be working values. */
+unsigned long current_drive = 0xFF;
+unsigned long current_partition;
+
+#ifndef STAGE1_5
+/* The register ESI should contain the address of the partition to be
+   used for loading a chain-loader when chain-loading the loader.  */
+unsigned long boot_part_addr = 0;
+#endif
+
+/*
+ *  Global variables describing details of the filesystem
+ */
+
+/* FIXME: BSD evil hack */
+#include "freebsd.h"
+int bsd_evil_hack;
+
+/* filesystem type */
+int fsys_type = NUM_FSYS;
+#ifndef NO_BLOCK_FILES
+static int block_file = 0;
+#endif /* NO_BLOCK_FILES */
+
+/* these are the translated numbers for the open partition */
+unsigned long part_start;
+unsigned long part_length;
+
+int current_slice;
+
+/* disk buffer parameters */
+int buf_drive = -1;
+int buf_track;
+struct geometry buf_geom;
+
+/* filesystem common variables */
+int filepos;
+int filemax;
+
+int
+rawread (int drive, int sector, int byte_offset, int byte_len, char *buf)
+{
+  int slen = (byte_offset + byte_len + SECTOR_SIZE - 1) >> SECTOR_BITS;
+
+  if (byte_len <= 0)
+    return 1;
+
+  while (byte_len > 0 && !errnum)
+    {
+      int soff, num_sect, bufaddr, track, size = byte_len;
+
+      /*
+       *  Check track buffer.  If it isn't valid or it is from the
+       *  wrong disk, then reset the disk geometry.
+       */
+      if (buf_drive != drive)
+	{
+	  if (get_diskinfo (drive, &buf_geom))
+	    {
+	      errnum = ERR_NO_DISK;
+	      return 0;
+	    }
+	  buf_drive = drive;
+	  buf_track = -1;
+	}
+
+      /* Make sure that SECTOR is valid.  */
+      if (sector < 0 || sector >= buf_geom.total_sectors)
+	{
+	  errnum = ERR_GEOM;
+	  return 0;
+	}
+      
+      /*  Get first sector of track  */
+      soff = sector % buf_geom.sectors;
+      track = sector - soff;
+      num_sect = buf_geom.sectors - soff;
+      bufaddr = BUFFERADDR + (soff * SECTOR_SIZE) + byte_offset;
+
+      if (track != buf_track)
+	{
+	  int bios_err, read_start = track, read_len = buf_geom.sectors;
+
+	  /*
+	   *  If there's more than one read in this entire loop, then
+	   *  only make the earlier reads for the portion needed.  This
+	   *  saves filling the buffer with data that won't be used!
+	   */
+	  if (slen > num_sect)
+	    {
+	      read_start = sector;
+	      read_len = num_sect;
+	      bufaddr = BUFFERADDR + byte_offset;
+	    }
+
+	  bios_err = biosdisk (BIOSDISK_READ, drive, &buf_geom,
+			       read_start, read_len, BUFFERSEG);
+	  if (bios_err)
+	    {
+	      buf_track = -1;
+
+	      if (bios_err == BIOSDISK_ERROR_GEOMETRY)
+		errnum = ERR_GEOM;
+	      else
+		{
+		  /*
+		   *  If there was an error, try to load only the
+		   *  required sector(s) rather than failing completely.
+		   */
+		  if (slen > num_sect
+		      || biosdisk (BIOSDISK_READ, drive, &buf_geom,
+				   sector, slen, BUFFERSEG))
+		    errnum = ERR_READ;
+
+		  bufaddr = BUFFERADDR + byte_offset;
+		}
+	    }
+	  else
+	    buf_track = track;
+
+	  if ((buf_track == 0 || sector == 0)
+	      && (PC_SLICE_TYPE (BUFFERADDR, 0) == PC_SLICE_TYPE_EZD
+		  || PC_SLICE_TYPE (BUFFERADDR, 1) == PC_SLICE_TYPE_EZD
+		  || PC_SLICE_TYPE (BUFFERADDR, 2) == PC_SLICE_TYPE_EZD
+		  || PC_SLICE_TYPE (BUFFERADDR, 3) == PC_SLICE_TYPE_EZD))
+	    {
+	      /* This is a EZD disk map sector 0 to sector 1 */
+	      if (buf_track == 0 || slen >= 2)
+		{
+		  /* We already read the sector 1, copy it to sector 0 */
+		  memmove ((char *) BUFFERADDR, 
+			   (char *) BUFFERADDR + SECTOR_SIZE, SECTOR_SIZE);
+		}
+	      else
+		{
+		  if (biosdisk (BIOSDISK_READ, drive, &buf_geom,
+				1, 1, BUFFERSEG))
+		    errnum = ERR_READ;
+		}
+	    }
+	}
+	  
+      if (size > ((num_sect * SECTOR_SIZE) - byte_offset))
+	size = (num_sect * SECTOR_SIZE) - byte_offset;
+
+      /*
+       *  Instrumentation to tell which sectors were read and used.
+       */
+      if (disk_read_func)
+	{
+	  int sector_num = sector;
+	  int length = SECTOR_SIZE - byte_offset;
+	  if (length > size)
+	    length = size;
+	  (*disk_read_func) (sector_num++, byte_offset, length);
+	  length = size - length;
+	  if (length > 0)
+	    {
+	      while (length > SECTOR_SIZE)
+		{
+		  (*disk_read_func) (sector_num++, 0, SECTOR_SIZE);
+		  length -= SECTOR_SIZE;
+		}
+	      (*disk_read_func) (sector_num, 0, length);
+	    }
+	}
+
+      memmove (buf, (char *) bufaddr, size);
+
+      buf += size;
+      byte_len -= size;
+      sector += num_sect;
+      slen -= num_sect;
+      byte_offset = 0;
+    }
+
+  return (!errnum);
+}
+
+
+int
+devread (int sector, int byte_offset, int byte_len, char *buf)
+{
+  /*
+   *  Check partition boundaries
+   */
+  if (sector < 0
+      || ((sector + ((byte_offset + byte_len - 1) >> SECTOR_BITS))
+	  >= part_length))
+    {
+      errnum = ERR_OUTSIDE_PART;
+      return 0;
+    }
+
+  /*
+   *  Get the read to the beginning of a partition.
+   */
+  sector += byte_offset >> SECTOR_BITS;
+  byte_offset &= SECTOR_SIZE - 1;
+
+#if !defined(STAGE1_5)
+  if (disk_read_hook && debug)
+    printf ("<%d, %d, %d>", sector, byte_offset, byte_len);
+#endif /* !STAGE1_5 */
+
+  /*
+   *  Call RAWREAD, which is very similar, but:
+   *
+   *    --  It takes an extra parameter, the drive number.
+   *    --  It requires that "sector" is relative to the beginning
+   *            of the disk.
+   *    --  It doesn't handle offsets of more than 511 bytes into the
+   *            sector.
+   */
+  return rawread (current_drive, part_start + sector, byte_offset,
+		  byte_len, buf);
+}
+
+#ifndef STAGE1_5
+int
+rawwrite (int drive, int sector, char *buf)
+{
+  if (sector == 0)
+    {
+      if (biosdisk (BIOSDISK_READ, drive, &buf_geom, 0, 1, SCRATCHSEG))
+	{
+	  errnum = ERR_WRITE;
+	  return 0;
+	}
+
+      if (PC_SLICE_TYPE (SCRATCHADDR, 0) == PC_SLICE_TYPE_EZD
+	  || PC_SLICE_TYPE (SCRATCHADDR, 1) == PC_SLICE_TYPE_EZD
+	  || PC_SLICE_TYPE (SCRATCHADDR, 2) == PC_SLICE_TYPE_EZD
+	  || PC_SLICE_TYPE (SCRATCHADDR, 3) == PC_SLICE_TYPE_EZD)
+	sector = 1;
+    }
+  
+  memmove ((char *) SCRATCHADDR, buf, SECTOR_SIZE);
+  if (biosdisk (BIOSDISK_WRITE, drive, &buf_geom,
+		sector, 1, SCRATCHSEG))
+    {
+      errnum = ERR_WRITE;
+      return 0;
+    }
+
+  if (sector - sector % buf_geom.sectors == buf_track)
+    /* Clear the cache.  */
+    buf_track = -1;
+
+  return 1;
+}
+
+int
+devwrite (int sector, int sector_count, char *buf)
+{
+#if defined(GRUB_UTIL) && defined(__linux__)
+  if (current_partition != 0xFFFFFF)
+    {
+      /* If the grub shell is running under Linux and the user wants to
+	 embed a Stage 1.5 into a partition instead of a MBR, use system
+	 calls directly instead of biosdisk, because of the bug in
+	 Linux. *sigh*  */
+      return write_to_partition (device_map, current_drive, current_partition,
+				 sector, sector_count, buf);
+    }
+  else
+#endif /* GRUB_UTIL && __linux__ */
+    {
+      int i;
+      
+      for (i = 0; i < sector_count; i++)
+	{
+	  if (! rawwrite (current_drive, part_start + sector + i, 
+			  buf + (i << SECTOR_BITS)))
+	      return 0;
+
+	}
+      return 1;
+    }
+}
+
+static int
+sane_partition (void)
+{
+  /* network drive */
+  if (current_drive == NETWORK_DRIVE)
+    return 1;
+  
+  if (!(current_partition & 0xFF000000uL)
+      && (current_drive & 0xFFFFFF7F) < 8
+      && (current_partition & 0xFF) == 0xFF
+      && ((current_partition & 0xFF00) == 0xFF00
+	  || (current_partition & 0xFF00) < 0x800)
+      && ((current_partition >> 16) == 0xFF
+	  || (current_drive & 0x80)))
+    return 1;
+
+  errnum = ERR_DEV_VALUES;
+  return 0;
+}
+#endif /* ! STAGE1_5 */
+
+static void
+attempt_mount (void)
+{
+#ifndef STAGE1_5
+  for (fsys_type = 0; fsys_type < NUM_FSYS; fsys_type++)
+    if ((fsys_table[fsys_type].mount_func) ())
+      break;
+
+  if (fsys_type == NUM_FSYS && errnum == ERR_NONE)
+    errnum = ERR_FSYS_MOUNT;
+#else
+  fsys_type = 0;
+  if ((*(fsys_table[fsys_type].mount_func)) () != 1)
+    {
+      fsys_type = NUM_FSYS;
+      errnum = ERR_FSYS_MOUNT;
+    }
+#endif
+}
+
+
+#ifndef STAGE1_5
+/* Turn on the active flag for the partition SAVED_PARTITION in the
+   drive SAVED_DRIVE. If an error occurs, return zero, otherwise return
+   non-zero.  */
+int
+make_saved_active (void)
+{
+  char mbr[512];
+
+  if (saved_drive & 0x80)
+    {
+      /* Hard disk */
+      int part = saved_partition >> 16;
+
+      /* If the partition is not a primary partition, the active flag is
+	 meaningless. (XXX: Really?)  */
+      if (part > 3)
+	{
+	  errnum = ERR_DEV_VALUES;
+	  return 0;
+	}
+
+      /* Read the MBR in the scratch space.  */
+      if (! rawread (saved_drive, 0, 0, SECTOR_SIZE, mbr))
+	return 0;
+
+      /* If the partition is an extended partition, setting the active
+	 flag violates the specification by IBM.  */
+      if (IS_PC_SLICE_TYPE_EXTENDED (PC_SLICE_TYPE (mbr, part)))
+	{
+	  errnum = ERR_DEV_VALUES;
+	  return 0;
+	}
+
+      /* Check if the active flag is disabled.  */
+      if (PC_SLICE_FLAG (mbr, part) != PC_SLICE_FLAG_BOOTABLE)
+	{
+	  int i;
+
+	  /* Clear all the active flags in this table.  */
+	  for (i = 0; i < 4; i++)
+	    PC_SLICE_FLAG (mbr, i) = 0;
+
+	  /* Set the flag.  */
+	  PC_SLICE_FLAG (mbr, part) = PC_SLICE_FLAG_BOOTABLE;
+
+	  /* Write back the MBR.  */
+	  if (! rawwrite (saved_drive, 0, mbr))
+	    return 0;
+	}
+    }
+  else
+    {
+      /* If the drive is not a hard disk drive, you shouldn't call this
+	 function. (XXX: Should I just ignore this error?)  */
+      errnum = ERR_DEV_VALUES;
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Hide/Unhide CURRENT_PARTITION.  */
+int
+set_partition_hidden_flag (int hidden)
+{
+  unsigned long part = 0xFFFFFF;
+  unsigned long start, len, offset, ext_offset;
+  int entry, type;
+  char mbr[512];
+  
+  /* The drive must be a hard disk.  */
+  if (! (current_drive & 0x80))
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+  
+  /* The partition must be a PC slice.  */
+  if ((current_partition >> 16) == 0xFF
+      || (current_partition & 0xFFFF) != 0xFFFF)
+    {
+      errnum = ERR_BAD_ARGUMENT;
+      return 1;
+    }
+  
+  /* Look for the partition.  */
+  while (next_partition (current_drive, 0xFFFFFF, &part, &type,           
+			 &start, &len, &offset, &entry,
+			 &ext_offset, mbr))
+    {                                                                       
+      if (part == current_partition)
+	{
+	  /* Found.  */
+	  if (hidden)
+	    PC_SLICE_TYPE (mbr, entry) |= PC_SLICE_TYPE_HIDDEN_FLAG;
+	  else
+	    PC_SLICE_TYPE (mbr, entry) &= ~PC_SLICE_TYPE_HIDDEN_FLAG;       
+	  
+	  /* Write back the MBR to the disk.  */
+	  buf_track = -1;
+	  if (! rawwrite (current_drive, offset, mbr))
+	    return 1;
+	  
+	  /* Succeed.  */
+	  return 0;
+	}
+    }
+  
+  return 1;
+}
+
+
+static void
+check_and_print_mount (void)
+{
+  attempt_mount ();
+  if (errnum == ERR_FSYS_MOUNT)
+    errnum = ERR_NONE;
+  if (!errnum)
+    print_fsys_type ();
+  print_error ();
+}
+#endif /* STAGE1_5 */
+
+
+/* Get the information on next partition on the drive DRIVE.
+   The caller must not modify the contents of the arguments when
+   iterating this function. The partition representation in GRUB will
+   be stored in *PARTITION. Likewise, the partition type in *TYPE, the
+   start sector in *START, the length in *LEN, the offset of the
+   partition table in *OFFSET, the entry number in the table in *ENTRY,
+   the offset of the extended partition in *EXT_OFFSET.
+   BUF is used to store a MBR, the boot sector of a partition, or
+   a BSD label sector, and it must be at least 512 bytes length.
+   When calling this function first, *PARTITION must be initialized to
+   0xFFFFFF. The return value is zero if fails, otherwise non-zero.  */
+int
+next_partition (unsigned long drive, unsigned long dest,
+		unsigned long *partition, int *type,
+		unsigned long *start, unsigned long *len,
+		unsigned long *offset, int *entry,
+		unsigned long *ext_offset, char *buf)
+{
+  /* Forward declarations.  */
+  auto int next_bsd_partition (void);
+  auto int next_pc_slice (void);
+
+  /* Get next BSD partition in current PC slice.  */
+  int next_bsd_partition (void)
+    {
+      int i;
+      int bsd_part_no = (*partition & 0xFF00) >> 8;
+
+      /* If this is the first time...  */
+      if (bsd_part_no == 0xFF)
+	{
+	  /* Check if the BSD label is within current PC slice.  */
+	  if (*len < BSD_LABEL_SECTOR + 1)
+	    {
+	      errnum = ERR_BAD_PART_TABLE;
+	      return 0;
+	    }
+
+	  /* Read the BSD label.  */
+	  if (! rawread (drive, *start + BSD_LABEL_SECTOR,
+			 0, SECTOR_SIZE, buf))
+	    return 0;
+
+	  /* Check if it is valid.  */
+	  if (! BSD_LABEL_CHECK_MAG (buf))
+	    {
+	      errnum = ERR_BAD_PART_TABLE;
+	      return 0;
+	    }
+	  
+	  bsd_part_no = -1;
+	}
+
+      /* Search next valid BSD partition.  */
+      for (i = bsd_part_no + 1; i < BSD_LABEL_NPARTS (buf); i++)
+	{
+	  if (BSD_PART_TYPE (buf, i))
+	    {
+	      /* Note that *TYPE and *PARTITION were set
+		 for current PC slice.  */
+	      *type = (BSD_PART_TYPE (buf, i) << 8) | (*type & 0xFF);
+	      *start = BSD_PART_START (buf, i);
+	      *len = BSD_PART_LENGTH (buf, i);
+	      *partition = (*partition & 0xFF00FF) | (i << 8);
+
+#ifndef STAGE1_5
+	      /* XXX */
+	      if ((drive & 0x80) && BSD_LABEL_DTYPE (buf) == DTYPE_SCSI)
+		bsd_evil_hack = 4;
+#endif /* ! STAGE1_5 */
+	      
+	      return 1;
+	    }
+	}
+
+      errnum = ERR_NO_PART;
+      return 0;
+    }
+
+  /* Get next PC slice. Be careful of that this function may return
+     an empty PC slice (i.e. a partition whose type is zero) as well.  */
+  int next_pc_slice (void)
+    {
+      int pc_slice_no = (*partition & 0xFF0000) >> 16;
+
+      /* If this is the first time...  */
+      if (pc_slice_no == 0xFF)
+	{
+	  *offset = 0;
+	  *ext_offset = 0;
+	  *entry = -1;
+	  pc_slice_no = -1;
+	}
+
+      /* Read the MBR or the boot sector of the extended partition.  */
+      if (! rawread (drive, *offset, 0, SECTOR_SIZE, buf))
+	return 0;
+
+      /* Check if it is valid.  */
+      if (! PC_MBR_CHECK_SIG (buf))
+	{
+	  errnum = ERR_BAD_PART_TABLE;
+	  return 0;
+	}
+
+      /* Increase the entry number.  */
+      (*entry)++;
+
+      /* If this is out of current partition table...  */
+      if (*entry == PC_SLICE_MAX)
+	{
+	  int i;
+
+	  /* Search the first extended partition in current table.  */
+	  for (i = 0; i < PC_SLICE_MAX; i++)
+	    {
+	      if (IS_PC_SLICE_TYPE_EXTENDED (PC_SLICE_TYPE (buf, i)))
+		{
+		  /* Found. Set the new offset and the entry number,
+		     and restart this function.  */
+		  *offset = *ext_offset + PC_SLICE_START (buf, i);
+		  if (! *ext_offset)
+		    *ext_offset = *offset;
+		  *entry = -1;
+		  return next_pc_slice ();
+		}
+	    }
+
+	  errnum = ERR_NO_PART;
+	  return 0;
+	}
+      
+      *type = PC_SLICE_TYPE (buf, *entry);
+      *start = *offset + PC_SLICE_START (buf, *entry);
+      *len = PC_SLICE_LENGTH (buf, *entry);
+
+      /* The calculation of a PC slice number is complicated, because of
+	 the rather odd definition of extended partitions. Even worse,
+	 there is no guarantee that this is consistent with every
+	 operating systems. Uggh.  */
+      if (pc_slice_no < PC_SLICE_MAX
+	  || (! IS_PC_SLICE_TYPE_EXTENDED (*type)
+	      && *type != PC_SLICE_TYPE_NONE))
+	pc_slice_no++;
+      
+      *partition = (pc_slice_no << 16) | 0xFFFF;
+      return 1;
+    }
+
+  /* Start the body of this function.  */
+  
+#ifndef STAGE1_5
+  if (current_drive == NETWORK_DRIVE)
+    return 0;
+#endif
+
+  /* If previous partition is a BSD partition or a PC slice which
+     contains BSD partitions...  */
+  if ((*partition != 0xFFFFFF && IS_PC_SLICE_TYPE_BSD (*type & 0xff))
+      || ! (drive & 0x80))
+    {
+      if (*type == PC_SLICE_TYPE_NONE)
+	*type = PC_SLICE_TYPE_FREEBSD;
+      
+      /* Get next BSD partition, if any.  */
+      if (next_bsd_partition ())
+	return 1;
+
+      /* If the destination partition is a BSD partition and current
+	 BSD partition has any error, abort the operation.  */
+      if ((dest & 0xFF00) != 0xFF00
+	  && ((dest & 0xFF0000) == 0xFF0000
+	      || (dest & 0xFF0000) == (*partition & 0xFF0000)))
+	return 0;
+
+      /* Ignore the error.  */
+      errnum = ERR_NONE;
+    }
+	  
+  return next_pc_slice ();
+}
+
+#ifndef STAGE1_5
+static unsigned long cur_part_offset;
+static unsigned long cur_part_addr;
+#endif
+
+/* Open a partition.  */
+int
+real_open_partition (int flags)
+{
+  unsigned long dest_partition = current_partition;
+  unsigned long part_offset;
+  unsigned long ext_offset;
+  int entry;
+  char buf[SECTOR_SIZE];
+  int bsd_part, pc_slice;
+
+  /* For simplicity.  */
+  auto int next (void);
+  int next (void)
+    {
+      int ret = next_partition (current_drive, dest_partition,
+				&current_partition, &current_slice,
+				&part_start, &part_length,
+				&part_offset, &entry, &ext_offset, buf);
+      bsd_part = (current_partition >> 8) & 0xFF;
+      pc_slice = current_partition >> 16;
+      return ret;
+    }
+  
+#ifndef STAGE1_5
+  /* network drive */
+  if (current_drive == NETWORK_DRIVE)
+    return 1;
+  
+  if (! sane_partition ())
+    return 0;
+#endif
+
+  bsd_evil_hack = 0;
+  current_slice = 0;
+  part_start = 0;
+
+  /* Make sure that buf_geom is valid. */
+  if (buf_drive != current_drive)
+    {
+      if (get_diskinfo (current_drive, &buf_geom))
+	{
+	  errnum = ERR_NO_DISK;
+	  return 0;
+	}
+      buf_drive = current_drive;
+      buf_track = -1;
+    }
+  part_length = buf_geom.total_sectors;
+
+  /* If this is the whole disk, return here.  */
+  if (! flags && current_partition == 0xFFFFFF)
+    return 1;
+
+  if (flags)
+    dest_partition = 0xFFFFFF;
+
+  /* Initialize CURRENT_PARTITION for next_partition.  */
+  current_partition = 0xFFFFFF;
+  
+  while (next ())
+    {
+#ifndef STAGE1_5
+    loop_start:
+      
+      cur_part_offset = part_offset;
+      cur_part_addr = BOOT_PART_TABLE + (entry << 4);
+#endif /* ! STAGE1_5 */
+
+      /* If this is a valid partition...  */
+      if (current_slice)
+	{
+#ifndef STAGE1_5
+	  /* Display partition information.  */
+	  if (flags && ! IS_PC_SLICE_TYPE_EXTENDED (current_slice))
+	    {
+	      if (! do_completion)
+		{
+		  if (current_drive & 0x80)
+		    grub_printf ("   Partition num: %d, ",
+				 current_partition >> 16);
+
+		  if (! IS_PC_SLICE_TYPE_BSD (current_slice))
+		    check_and_print_mount ();
+		  else
+		    {
+		      int got_part = 0;
+		      int saved_slice = current_slice;
+		      
+		      while (next ())
+			{
+			  if (bsd_part == 0xFF)
+			    break;
+			  
+			  if (! got_part)
+			    {
+			      grub_printf ("[BSD sub-partitions immediately follow]\n");
+			      got_part = 1;
+			    }
+			  
+			  grub_printf ("     BSD Partition num: \'%c\', ",
+				       bsd_part + 'a');
+			  check_and_print_mount ();
+			}
+
+		      if (! got_part)
+			grub_printf (" No BSD sub-partition found, partition type 0x%x\n",
+				     saved_slice);
+		      
+		      if (errnum)
+			{
+			  errnum = ERR_NONE;
+			  break;
+			}
+		      
+		      goto loop_start;
+		    }
+		}
+	      else
+		{
+		  if (bsd_part != 0xFF)
+		    {
+		      char str[16];
+		      
+		      if (! (current_drive & 0x80)
+			  || (dest_partition >> 16) == pc_slice)
+			grub_sprintf (str, "%c)", bsd_part + 'a');
+		      else
+			grub_sprintf (str, "%d,%c)",
+				      pc_slice, bsd_part + 'a');
+		      print_a_completion (str);
+		    }
+		  else if (! IS_PC_SLICE_TYPE_BSD (current_slice))
+		    {
+		      char str[8];
+		      
+		      grub_sprintf (str, "%d)", pc_slice);
+		      print_a_completion (str);
+		    }
+		}
+	    }
+	  
+	  errnum = ERR_NONE;
+#endif /* ! STAGE1_5 */
+	  
+	  /* Check if this is the destination partition.  */
+	  if (! flags
+	      && (dest_partition == current_partition
+		  || ((dest_partition >> 16) == 0xFF
+		      && ((dest_partition >> 8) & 0xFF) == bsd_part)))
+	    return 1;
+	}
+    }
+
+#ifndef STAGE1_5
+  if (flags)
+    {
+      if (! (current_drive & 0x80))
+	{
+	  current_partition = 0xFFFFFF;
+	  check_and_print_mount ();
+	}
+      
+      errnum = ERR_NONE;
+      return 1;
+    }
+#endif /* ! STAGE1_5 */
+  
+  return 0;
+}
+
+
+int
+open_partition (void)
+{
+  return real_open_partition (0);
+}
+
+
+#ifndef STAGE1_5
+/* XX used for device completion in 'set_device' and 'print_completions' */
+static int incomplete, disk_choice;
+static enum
+{
+  PART_UNSPECIFIED = 0,
+  PART_DISK,
+  PART_CHOSEN,
+}
+part_choice;
+#endif /* ! STAGE1_5 */
+
+char *
+set_device (char *device)
+{
+#ifdef STAGE1_5
+    /* In Stage 1.5, the first 4 bytes of FILENAME has a device number.  */
+  unsigned long dev = *((unsigned long *) device);
+  int drive = (dev >> 24) & 0xFF;
+  int partition = dev & 0xFFFFFF;
+
+  /* If DRIVE is disabled (0xFF), use SAVED_DRIVE instead.  */
+  if (drive == 0xFF)
+    current_drive = saved_drive;
+  else
+    current_drive = drive;
+
+  /* The `partition' part must always have a valid number.  */
+  current_partition = partition;
+  
+  return device + sizeof (unsigned long);
+  
+#else /* ! STAGE1_5 */
+  
+  int result = 0;
+
+  incomplete = 0;
+  disk_choice = 1;
+  part_choice = PART_UNSPECIFIED;
+  current_drive = saved_drive;
+  current_partition = 0xFFFFFF;
+
+  if (*device == '(' && !*(device + 1))
+    /* user has given '(' only, let disk_choice handle what disks we have */
+    return device + 1;
+
+  if (*device == '(' && *(++device))
+    {
+      if (*device != ',' && *device != ')')
+	{
+	  char ch = *device;
+#ifdef SUPPORT_NETBOOT
+	  if (*device == 'f' || *device == 'h' ||
+	      (*device == 'n' && network_ready))
+#else
+	  if (*device == 'f' || *device == 'h')
+#endif /* SUPPORT_NETBOOT */
+	    {
+	      /* user has given '([fhn]', check for resp. add 'd' and
+		 let disk_choice handle what disks we have */
+	      if (!*(device + 1))
+		{
+		  device++;
+		  *device++ = 'd';
+		  *device = '\0';
+		  return device;
+		}
+	      else if (*(device + 1) == 'd' && !*(device + 2))
+		return device + 2;
+	    }
+
+#ifdef SUPPORT_NETBOOT
+	  if ((*device == 'f' || *device == 'h' ||
+	       (*device == 'n' && network_ready))
+#else
+	  if ((*device == 'f' || *device == 'h')
+#endif /* SUPPORT_NETBOOT */
+	      && (device += 2, (*(device - 1) != 'd')))
+	    errnum = ERR_NUMBER_PARSING;
+
+#ifdef SUPPORT_NETBOOT
+	  if (ch == 'n' && network_ready)
+	    current_drive = NETWORK_DRIVE;
+	  else
+#endif /* SUPPORT_NETBOOT */
+	    {
+	      safe_parse_maxint (&device, (int *) &current_drive);
+	      
+	      disk_choice = 0;
+	      if (ch == 'h')
+		current_drive += 0x80;
+	    }
+	}
+
+      if (errnum)
+	return 0;
+
+      if (*device == ')')
+	{
+	  part_choice = PART_CHOSEN;
+	  result = 1;
+	}
+      else if (*device == ',')
+	{
+	  /* Either an absolute PC or BSD partition. */
+	  disk_choice = 0;
+	  part_choice ++;
+	  device++;
+
+	  if (*device >= '0' && *device <= '9')
+	    {
+	      part_choice ++;
+	      current_partition = 0;
+
+	      if (!(current_drive & 0x80)
+		  || !safe_parse_maxint (&device, (int *) &current_partition)
+		  || current_partition > 254)
+		{
+		  errnum = ERR_DEV_FORMAT;
+		  return 0;
+		}
+
+	      current_partition = (current_partition << 16) + 0xFFFF;
+
+	      if (*device == ',')
+		device++;
+	      
+	      if (*device >= 'a' && *device <= 'h')
+		{
+		  current_partition = (((*(device++) - 'a') << 8)
+				       | (current_partition & 0xFF00FF));
+		}
+	    }
+	  else if (*device >= 'a' && *device <= 'h')
+	    {
+	      part_choice ++;
+	      current_partition = ((*(device++) - 'a') << 8) | 0xFF00FF;
+	    }
+
+	  if (*device == ')')
+	    {
+	      if (part_choice == PART_DISK)
+		{
+		  current_partition = saved_partition;
+		  part_choice ++;
+		}
+
+	      result = 1;
+	    }
+	}
+    }
+
+  if (! sane_partition ())
+    return 0;
+  
+  if (result)
+    return device + 1;
+  else
+    {
+      if (!*device)
+	incomplete = 1;
+      errnum = ERR_DEV_FORMAT;
+    }
+
+  return 0;
+  
+#endif /* ! STAGE1_5 */
+}
+
+/*
+ *  This performs a "mount" on the current device, both drive and partition
+ *  number.
+ */
+
+int
+open_device (void)
+{
+  if (open_partition ())
+    attempt_mount ();
+
+  if (errnum != ERR_NONE)
+    return 0;
+
+  return 1;
+}
+
+
+#ifndef STAGE1_5
+int
+set_bootdev (int hdbias)
+{
+  int i, j;
+
+  /* Copy the boot partition information to 0x7be-0x7fd for chain-loading.  */
+  if ((saved_drive & 0x80) && cur_part_addr)
+    {
+      if (rawread (saved_drive, cur_part_offset,
+		   0, SECTOR_SIZE, (char *) SCRATCHADDR))
+	{
+	  char *dst, *src;
+      
+	  /* Need only the partition table.
+	     XXX: We cannot use grub_memmove because BOOT_PART_TABLE
+	     (0x07be) is less than 0x1000.  */
+	  dst = (char *) BOOT_PART_TABLE;
+	  src = (char *) SCRATCHADDR + BOOTSEC_PART_OFFSET;
+	  while (dst < (char *) BOOT_PART_TABLE + BOOTSEC_PART_LENGTH)
+	    *dst++ = *src++;
+	  
+	  /* Set the active flag of the booted partition.  */
+	  for (i = 0; i < 4; i++)
+	    PC_SLICE_FLAG (BOOT_PART_TABLE, i) = 0;
+	  
+	  *((unsigned char *) cur_part_addr) = PC_SLICE_FLAG_BOOTABLE;
+	  boot_part_addr = cur_part_addr;
+	}
+      else
+	return 0;
+    }
+  
+  /*
+   *  Set BSD boot device.
+   */
+  i = (saved_partition >> 16) + 2;
+  if (saved_partition == 0xFFFFFF)
+    i = 1;
+  else if ((saved_partition >> 16) == 0xFF)
+    i = 0;
+
+  /* FIXME: extremely evil hack!!! */
+  j = 2;
+  if (saved_drive & 0x80)
+    j = bsd_evil_hack;
+
+  return MAKEBOOTDEV (j, (i >> 4), (i & 0xF),
+		      ((saved_drive - hdbias) & 0x7F),
+		      ((saved_partition >> 8) & 0xFF));
+}
+#endif /* STAGE1_5 */
+
+
+static char *
+setup_part (char *filename)
+{
+#ifdef STAGE1_5
+
+  if (! (filename = set_device (filename)))
+    {
+      current_drive = 0xFF;
+      return 0;
+    }
+  
+# ifndef NO_BLOCK_FILES
+  if (*filename != '/')
+    open_partition ();
+  else
+# endif /* ! NO_BLOCK_FILES */
+    open_device ();
+  
+#else /* ! STAGE1_5 */
+  
+  if (*filename == '(')
+    {
+      if ((filename = set_device (filename)) == 0)
+	{
+	  current_drive = 0xFF;
+	  return 0;
+	}
+# ifndef NO_BLOCK_FILES
+      if (*filename != '/')
+	open_partition ();
+      else
+# endif /* ! NO_BLOCK_FILES */
+	open_device ();
+    }
+  else if (saved_drive != current_drive
+	   || saved_partition != current_partition
+	   || (*filename == '/' && fsys_type == NUM_FSYS)
+	   || buf_drive == -1)
+    {
+      current_drive = saved_drive;
+      current_partition = saved_partition;
+      /* allow for the error case of "no filesystem" after the partition
+         is found.  This makes block files work fine on no filesystem */
+# ifndef NO_BLOCK_FILES
+      if (*filename != '/')
+	open_partition ();
+      else
+# endif /* ! NO_BLOCK_FILES */
+	open_device ();
+    }
+  
+#endif /* ! STAGE1_5 */
+  
+  if (errnum && (*filename == '/' || errnum != ERR_FSYS_MOUNT))
+    return 0;
+  else
+    errnum = 0;
+
+#ifndef STAGE1_5
+  if (!sane_partition ())
+    return 0;
+#endif
+
+  return filename;
+}
+
+
+#ifndef STAGE1_5
+/*
+ *  This prints the filesystem type or gives relevant information.
+ */
+
+void
+print_fsys_type (void)
+{
+  if (! do_completion)
+    {
+      printf (" Filesystem type ");
+      
+      if (fsys_type != NUM_FSYS)
+	printf ("is %s, ", fsys_table[fsys_type].name);
+      else
+	printf ("unknown, ");
+      
+      if (current_partition == 0xFFFFFF)
+	printf ("using whole disk\n");
+      else
+	printf ("partition type 0x%x\n", current_slice & 0xFF);
+    }
+}
+#endif /* STAGE1_5 */
+
+#ifndef STAGE1_5
+/* If DO_COMPLETION is true, just print NAME. Otherwise save the unique
+   part into UNIQUE_STRING.  */
+void
+print_a_completion (char *name)
+{
+  /* If NAME is "." or "..", do not count it.  */
+  if (grub_strcmp (name, ".") == 0 || grub_strcmp (name, "..") == 0)
+    return;
+  
+  if (do_completion)
+    {
+      char *buf = unique_string;
+      
+      if (! unique)
+	while ((*buf++ = *name++))
+	  ;
+      else
+	{
+	  while (*buf && (*buf == *name))
+	    {
+	      buf++;
+	      name++;
+	    }
+	  /* mismatch, strip it.  */
+	  *buf = '\0';
+	}
+    }
+  else
+    grub_printf (" %s", name);
+  
+  unique++;
+}
+
+/*
+ *  This lists the possible completions of a device string, filename, or
+ *  any sane combination of the two.
+ */
+
+int
+print_completions (int is_filename, int is_completion)
+{
+  char *buf = (char *) COMPLETION_BUF;
+  char *ptr = buf;
+
+  unique_string = (char *) UNIQUE_BUF;
+  *unique_string = 0;
+  unique = 0;
+  do_completion = is_completion;
+
+  if (! is_filename)
+    {
+      /* Print the completions of builtin commands.  */
+      struct builtin **builtin;
+
+      if (! is_completion)
+	grub_printf (" Possible commands are:");
+      
+      for (builtin = builtin_table; (*builtin); builtin++)
+	{
+	  /* If *BUILTIN cannot be run in the command-line, skip it.  */
+	  if (! ((*builtin)->flags & BUILTIN_CMDLINE))
+	    continue;
+
+	  if (substring (buf, (*builtin)->name) <= 0)
+	    print_a_completion ((*builtin)->name);
+	}
+
+      if (is_completion && *unique_string)
+	{
+	  if (unique == 1)
+	    {
+	      char *u = unique_string + grub_strlen (unique_string);
+
+	      *u++ = ' ';
+	      *u = 0;
+	    }
+	  
+	  grub_strcpy (buf, unique_string);
+	}
+
+      if (! is_completion)
+	grub_putchar ('\n');
+      
+      print_error ();
+      do_completion = 0;
+      if (errnum)
+	return -1;
+      else
+	return unique - 1;
+    }
+
+  if (*buf == '/' || (ptr = set_device (buf)) || incomplete)
+    {
+      errnum = 0;
+
+      if (*buf == '(' && (incomplete || ! *ptr))
+	{
+	  if (! part_choice)
+	    {
+	      /* disk completions */
+	      int disk_no, i, j;
+	      struct geometry geom;
+
+	      if (! is_completion)
+		grub_printf (" Possible disks are: ");
+
+#ifdef SUPPORT_NETBOOT
+	      if (!ptr || *(ptr-1) != 'd' || *(ptr-2) != 'n')
+#endif /* SUPPORT_NETBOOT */
+		{
+		  for (i = (ptr && (*(ptr-1) == 'd' && *(ptr-2) == 'h') ? 1:0);
+		       i < (ptr && (*(ptr-1) == 'd' && *(ptr-2) == 'f') ? 1:2);
+		       i++)
+		    {
+		      for (j = 0; j < 8; j++)
+			{
+			  disk_no = (i * 0x80) + j;
+			  if ((disk_choice || disk_no == current_drive)
+			      && ! get_diskinfo (disk_no, &geom))
+			    {
+			      char dev_name[8];
+
+			      grub_sprintf (dev_name, "%cd%d", i ? 'h':'f', j);
+			      print_a_completion (dev_name);
+			    }
+			}
+		    }
+		}
+# ifdef SUPPORT_NETBOOT
+	      if (network_ready &&
+		  (disk_choice || NETWORK_DRIVE == current_drive) &&
+		  (!ptr || *(ptr-1) == '(' ||
+		   (*(ptr-1) == 'd' && *(ptr-2) == 'n')))
+		print_a_completion ("nd");
+# endif /* SUPPORT_NETBOOT */
+
+	      if (is_completion && *unique_string)
+		{
+		  ptr = buf;
+		  while (*ptr != '(')
+		    ptr--;
+		  ptr++;
+		  grub_strcpy (ptr, unique_string);
+		  if (unique == 1)
+		    {
+		      ptr += grub_strlen (ptr);
+		      if (*unique_string == 'h')
+			{
+			  *ptr++ = ',';
+			  *ptr = 0;
+			}
+		      else
+			{
+			  *ptr++ = ')';
+			  *ptr = 0;
+			}
+		    }
+		}
+
+	      if (! is_completion)
+		grub_putchar ('\n');
+	    }
+	  else
+	    {
+	      /* partition completions */
+	      if (part_choice == PART_CHOSEN
+		  && open_partition ()
+		  && ! IS_PC_SLICE_TYPE_BSD (current_slice))
+		{
+		  unique = 1;
+		  ptr = buf + grub_strlen (buf);
+		  if (*(ptr - 1) != ')')
+		    {
+		      *ptr++ = ')';
+		      *ptr = 0;
+		    }
+		}
+	      else
+		{
+		  if (! is_completion)
+		    grub_printf (" Possible partitions are:\n");
+		  real_open_partition (1);
+		  
+		  if (is_completion && *unique_string)
+		    {
+		      ptr = buf;
+		      while (*ptr++ != ',')
+			;
+		      grub_strcpy (ptr, unique_string);
+		    }
+		}
+	    }
+	}
+      else if (ptr && *ptr == '/')
+	{
+	  /* filename completions */
+	  if (! is_completion)
+	    grub_printf (" Possible files are:");
+	  
+	  dir (buf);
+	  
+	  if (is_completion && *unique_string)
+	    {
+	      ptr += grub_strlen (ptr);
+	      while (*ptr != '/')
+		ptr--;
+	      ptr++;
+	      
+	      grub_strcpy (ptr, unique_string);
+	      
+	      if (unique == 1)
+		{
+		  ptr += grub_strlen (unique_string);
+
+		  /* Check if the file UNIQUE_STRING is a directory.  */
+		  *ptr = '/';
+		  *(ptr + 1) = 0;
+		  
+		  dir (buf);
+		  
+		  /* Restore the original unique value.  */
+		  unique = 1;
+		  
+		  if (errnum)
+		    {
+		      /* Regular file */
+		      errnum = 0;
+		      *ptr = ' ';
+		      *(ptr + 1) = 0;
+		    }
+		}
+	    }
+	  
+	  if (! is_completion)
+	    grub_putchar ('\n');
+	}
+      else
+	errnum = ERR_BAD_FILENAME;
+    }
+
+  print_error ();
+  do_completion = 0;
+  if (errnum)
+    return -1;
+  else
+    return unique - 1;
+}
+#endif /* STAGE1_5 */
+
+
+/*
+ *  This is the generic file open function.
+ */
+
+int
+grub_open (char *filename)
+{
+#ifndef NO_DECOMPRESSION
+  compressed_file = 0;
+#endif /* NO_DECOMPRESSION */
+
+  /* if any "dir" function uses/sets filepos, it must
+     set it to zero before returning if opening a file! */
+  filepos = 0;
+
+  if (!(filename = setup_part (filename)))
+    return 0;
+
+#ifndef NO_BLOCK_FILES
+  block_file = 0;
+#endif /* NO_BLOCK_FILES */
+
+  /* This accounts for partial filesystem implementations. */
+  fsmax = MAXINT;
+
+  if (*filename != '/')
+    {
+#ifndef NO_BLOCK_FILES
+      char *ptr = filename;
+      int tmp, list_addr = BLK_BLKLIST_START;
+      filemax = 0;
+
+      while (list_addr < BLK_MAX_ADDR)
+	{
+	  tmp = 0;
+	  safe_parse_maxint (&ptr, &tmp);
+	  errnum = 0;
+
+	  if (*ptr != '+')
+	    {
+	      if ((*ptr && *ptr != '/' && !isspace (*ptr))
+		  || tmp == 0 || tmp > filemax)
+		errnum = ERR_BAD_FILENAME;
+	      else
+		filemax = tmp;
+
+	      break;
+	    }
+
+	  /* since we use the same filesystem buffer, mark it to
+	     be remounted */
+	  fsys_type = NUM_FSYS;
+
+	  BLK_BLKSTART (list_addr) = tmp;
+	  ptr++;
+
+	  if (!safe_parse_maxint (&ptr, &tmp)
+	      || tmp == 0
+	      || (*ptr && *ptr != ',' && *ptr != '/' && !isspace (*ptr)))
+	    {
+	      errnum = ERR_BAD_FILENAME;
+	      break;
+	    }
+
+	  BLK_BLKLENGTH (list_addr) = tmp;
+
+	  filemax += (tmp * SECTOR_SIZE);
+	  list_addr += BLK_BLKLIST_INC_VAL;
+
+	  if (*ptr != ',')
+	    break;
+
+	  ptr++;
+	}
+
+      if (list_addr < BLK_MAX_ADDR && ptr != filename && !errnum)
+	{
+	  block_file = 1;
+	  BLK_CUR_FILEPOS = 0;
+	  BLK_CUR_BLKLIST = BLK_BLKLIST_START;
+	  BLK_CUR_BLKNUM = 0;
+
+#ifndef NO_DECOMPRESSION
+	  return gunzip_test_header ();
+#else /* NO_DECOMPRESSION */
+	  return 1;
+#endif /* NO_DECOMPRESSION */
+	}
+#else /* NO_BLOCK_FILES */
+      errnum = ERR_BAD_FILENAME;
+#endif /* NO_BLOCK_FILES */
+    }
+
+  if (!errnum && fsys_type == NUM_FSYS)
+    errnum = ERR_FSYS_MOUNT;
+
+# ifndef STAGE1_5
+  /* set "dir" function to open a file */
+  print_possibilities = 0;
+# endif
+
+  if (!errnum && (*(fsys_table[fsys_type].dir_func)) (filename))
+    {
+#ifndef NO_DECOMPRESSION
+      return gunzip_test_header ();
+#else /* NO_DECOMPRESSION */
+      return 1;
+#endif /* NO_DECOMPRESSION */
+    }
+
+  return 0;
+}
+
+
+int
+grub_read (char *buf, int len)
+{
+  /* Make sure "filepos" is a sane value */
+  if ((filepos < 0) || (filepos > filemax))
+    filepos = filemax;
+
+  /* Make sure "len" is a sane value */
+  if ((len < 0) || (len > (filemax - filepos)))
+    len = filemax - filepos;
+
+  /* if target file position is past the end of
+     the supported/configured filesize, then
+     there is an error */
+  if (filepos + len > fsmax)
+    {
+      errnum = ERR_FILELENGTH;
+      return 0;
+    }
+
+#ifndef NO_DECOMPRESSION
+  if (compressed_file)
+    return gunzip_read (buf, len);
+#endif /* NO_DECOMPRESSION */
+
+#ifndef NO_BLOCK_FILES
+  if (block_file)
+    {
+      int size, off, ret = 0;
+
+      while (len && !errnum)
+	{
+	  /* we may need to look for the right block in the list(s) */
+	  if (filepos < BLK_CUR_FILEPOS)
+	    {
+	      BLK_CUR_FILEPOS = 0;
+	      BLK_CUR_BLKLIST = BLK_BLKLIST_START;
+	      BLK_CUR_BLKNUM = 0;
+	    }
+
+	  /* run BLK_CUR_FILEPOS up to filepos */
+	  while (filepos > BLK_CUR_FILEPOS)
+	    {
+	      if ((filepos - (BLK_CUR_FILEPOS & ~(SECTOR_SIZE - 1)))
+		  >= SECTOR_SIZE)
+		{
+		  BLK_CUR_FILEPOS += SECTOR_SIZE;
+		  BLK_CUR_BLKNUM++;
+
+		  if (BLK_CUR_BLKNUM >= BLK_BLKLENGTH (BLK_CUR_BLKLIST))
+		    {
+		      BLK_CUR_BLKLIST += BLK_BLKLIST_INC_VAL;
+		      BLK_CUR_BLKNUM = 0;
+		    }
+		}
+	      else
+		BLK_CUR_FILEPOS = filepos;
+	    }
+
+	  off = filepos & (SECTOR_SIZE - 1);
+	  size = ((BLK_BLKLENGTH (BLK_CUR_BLKLIST) - BLK_CUR_BLKNUM)
+		  * SECTOR_SIZE) - off;
+	  if (size > len)
+	    size = len;
+
+	  disk_read_func = disk_read_hook;
+
+	  /* read current block and put it in the right place in memory */
+	  devread (BLK_BLKSTART (BLK_CUR_BLKLIST) + BLK_CUR_BLKNUM,
+		   off, size, buf);
+
+	  disk_read_func = NULL;
+
+	  len -= size;
+	  filepos += size;
+	  ret += size;
+	  buf += size;
+	}
+
+      if (errnum)
+	ret = 0;
+
+      return ret;
+    }
+#endif /* NO_BLOCK_FILES */
+
+  if (fsys_type == NUM_FSYS)
+    {
+      errnum = ERR_FSYS_MOUNT;
+      return 0;
+    }
+
+  return (*(fsys_table[fsys_type].read_func)) (buf, len);
+}
+
+#ifndef STAGE1_5
+/* Reposition a file offset.  */
+int
+grub_seek (int offset)
+{
+  if (offset > filemax || offset < 0)
+    return -1;
+
+  filepos = offset;
+  return offset;
+}
+
+int
+dir (char *dirname)
+{
+#ifndef NO_DECOMPRESSION
+  compressed_file = 0;
+#endif /* NO_DECOMPRESSION */
+
+  if (!(dirname = setup_part (dirname)))
+    return 0;
+
+  if (*dirname != '/')
+    errnum = ERR_BAD_FILENAME;
+
+  if (fsys_type == NUM_FSYS)
+    errnum = ERR_FSYS_MOUNT;
+
+  if (errnum)
+    return 0;
+
+  /* set "dir" function to list completions */
+  print_possibilities = 1;
+
+  return (*(fsys_table[fsys_type].dir_func)) (dirname);
+}
+#endif /* STAGE1_5 */
+
+void 
+grub_close (void)
+{
+#ifndef NO_BLOCK_FILES
+  if (block_file)
+    return;
+#endif /* NO_BLOCK_FILES */
+  
+  if (fsys_table[fsys_type].close_func != 0)
+    (*(fsys_table[fsys_type].close_func)) ();
+}
diff -ruN grub-0.94.orig/stage2/filesys.h stage2/filesys.h
--- grub-0.94.orig/stage2/filesys.h	Wed Feb 11 00:22:12 2004
+++ stage2/filesys.h	Wed Feb 11 00:22:29 2004
@@ -30,6 +30,16 @@
 #define FSYS_FFS_NUM 0
 #endif
 
+#ifdef FSYS_UFS2
+#define FSYS_UFS2_NUM 1
+int ufs2_mount (void);
+int ufs2_read (char *buf, int len);
+int ufs2_dir (char *dirname);
+int ufs2_embed (int *start_sector, int needed_sectors);
+#else
+#define FSYS_UFS2_NUM 0
+#endif
+
 #ifdef FSYS_FAT
 #define FSYS_FAT_NUM 1
 int fat_mount (void);
@@ -109,6 +119,7 @@
 #define NUM_FSYS	\
   (FSYS_FFS_NUM + FSYS_FAT_NUM + FSYS_EXT2FS_NUM + FSYS_MINIX_NUM	\
    + FSYS_REISERFS_NUM + FSYS_VSTAFS_NUM + FSYS_JFS_NUM + FSYS_XFS_NUM	\
+   + FSYS_UFS2_NUM \
    + FSYS_TFTP_NUM)
 #endif
 
diff -ruN grub-0.94.orig/stage2/filesys.h.orig stage2/filesys.h.orig
--- grub-0.94.orig/stage2/filesys.h.orig	Thu Jan  1 03:00:00 1970
+++ stage2/filesys.h.orig	Wed Jul  9 15:45:52 2003
@@ -0,0 +1,146 @@
+/* filesys.h - abstract filesystem interface */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999, 2000, 2001  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 "pc_slice.h"
+
+#ifdef FSYS_FFS
+#define FSYS_FFS_NUM 1
+int ffs_mount (void);
+int ffs_read (char *buf, int len);
+int ffs_dir (char *dirname);
+int ffs_embed (int *start_sector, int needed_sectors);
+#else
+#define FSYS_FFS_NUM 0
+#endif
+
+#ifdef FSYS_FAT
+#define FSYS_FAT_NUM 1
+int fat_mount (void);
+int fat_read (char *buf, int len);
+int fat_dir (char *dirname);
+#else
+#define FSYS_FAT_NUM 0
+#endif
+
+#ifdef FSYS_EXT2FS
+#define FSYS_EXT2FS_NUM 1
+int ext2fs_mount (void);
+int ext2fs_read (char *buf, int len);
+int ext2fs_dir (char *dirname);
+#else
+#define FSYS_EXT2FS_NUM 0
+#endif
+
+#ifdef FSYS_MINIX
+#define FSYS_MINIX_NUM 1
+int minix_mount (void);
+int minix_read (char *buf, int len);
+int minix_dir (char *dirname);
+#else
+#define FSYS_MINIX_NUM 0
+#endif
+
+#ifdef FSYS_REISERFS
+#define FSYS_REISERFS_NUM 1
+int reiserfs_mount (void);
+int reiserfs_read (char *buf, int len);
+int reiserfs_dir (char *dirname);
+int reiserfs_embed (int *start_sector, int needed_sectors);
+#else
+#define FSYS_REISERFS_NUM 0
+#endif
+
+#ifdef FSYS_VSTAFS
+#define FSYS_VSTAFS_NUM 1
+int vstafs_mount (void);
+int vstafs_read (char *buf, int len);
+int vstafs_dir (char *dirname);
+#else
+#define FSYS_VSTAFS_NUM 0
+#endif
+
+#ifdef FSYS_JFS
+#define FSYS_JFS_NUM 1
+int jfs_mount (void);
+int jfs_read (char *buf, int len);
+int jfs_dir (char *dirname);
+int jfs_embed (int *start_sector, int needed_sectors);
+#else
+#define FSYS_JFS_NUM 0
+#endif
+
+#ifdef FSYS_XFS
+#define FSYS_XFS_NUM 1
+int xfs_mount (void);
+int xfs_read (char *buf, int len);
+int xfs_dir (char *dirname);
+#else
+#define FSYS_XFS_NUM 0
+#endif
+
+#ifdef FSYS_TFTP
+#define FSYS_TFTP_NUM 1
+int tftp_mount (void);
+int tftp_read (char *buf, int len);
+int tftp_dir (char *dirname);
+void tftp_close (void);
+#else
+#define FSYS_TFTP_NUM 0
+#endif
+
+#ifndef NUM_FSYS
+#define NUM_FSYS	\
+  (FSYS_FFS_NUM + FSYS_FAT_NUM + FSYS_EXT2FS_NUM + FSYS_MINIX_NUM	\
+   + FSYS_REISERFS_NUM + FSYS_VSTAFS_NUM + FSYS_JFS_NUM + FSYS_XFS_NUM	\
+   + FSYS_TFTP_NUM)
+#endif
+
+/* defines for the block filesystem info area */
+#ifndef NO_BLOCK_FILES
+#define BLK_CUR_FILEPOS      (*((int*)FSYS_BUF))
+#define BLK_CUR_BLKLIST      (*((int*)(FSYS_BUF+4)))
+#define BLK_CUR_BLKNUM       (*((int*)(FSYS_BUF+8)))
+#define BLK_MAX_ADDR         (FSYS_BUF+0x7FF9)
+#define BLK_BLKSTART(l)      (*((int*)l))
+#define BLK_BLKLENGTH(l)     (*((int*)(l+4)))
+#define BLK_BLKLIST_START    (FSYS_BUF+12)
+#define BLK_BLKLIST_INC_VAL  8
+#endif /* NO_BLOCK_FILES */
+
+/* this next part is pretty ugly, but it keeps it in one place! */
+
+struct fsys_entry
+{
+  char *name;
+  int (*mount_func) (void);
+  int (*read_func) (char *buf, int len);
+  int (*dir_func) (char *dirname);
+  void (*close_func) (void);
+  int (*embed_func) (int *start_sector, int needed_sectors);
+};
+
+#ifdef STAGE1_5
+# define print_possibilities 0
+#else
+extern int print_possibilities;
+#endif
+
+extern int fsmax;
+extern struct fsys_entry fsys_table[NUM_FSYS + 1];
diff -ruN grub-0.94.orig/stage2/fsys_ufs2.c stage2/fsys_ufs2.c
--- grub-0.94.orig/stage2/fsys_ufs2.c	Thu Jan  1 03:00:00 1970
+++ stage2/fsys_ufs2.c	Wed Feb 11 00:22:29 2004
@@ -0,0 +1,305 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000, 2001  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.
+ */
+
+/*
+ * Elements of this file were originally from the FreeBSD "biosboot"
+ * bootloader file "disk.c" dated 4/12/95.
+ *
+ * The license and header comments from that file are included here.
+ */
+
+/*
+ * Mach Operating System
+ * Copyright (c) 1992, 1991 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
+ *  School of Computer Science
+ *  Carnegie Mellon University
+ *  Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ *
+ *	from: Mach, Revision 2.2  92/04/04  11:35:49  rpd
+ *
+ */
+
+#ifdef FSYS_UFS2
+
+#include "shared.h"
+#include "filesys.h"
+
+#include "ufs2.h"
+
+/* used for filesystem map blocks */
+static int mapblock;
+static int mapblock_offset;
+static int mapblock_bsize;
+
+/* pointer to superblock */
+#define SUPERBLOCK ((struct fs *) ( FSYS_BUF + 8192 ))
+#define INODE ((struct ufs2_dinode *) ( FSYS_BUF + 16384 ))
+#define MAPBUF ( FSYS_BUF + 24576 )
+#define MAPBUF_LEN 8192
+
+  
+int
+ufs2_mount (void)
+{
+  int retval = 1;
+
+  if ((((current_drive & 0x80) || (current_slice != 0))
+       && ! IS_PC_SLICE_TYPE_BSD_WITH_FS (current_slice, FS_BSDFFS))
+      || part_length < (SBLOCK_UFS2 + (SBLOCKSIZE / DEV_BSIZE))
+      || !devread (0, SBLOCK_UFS2, SBLOCKSIZE, (char *) SUPERBLOCK)
+      || SUPERBLOCK->fs_magic != FS_UFS2_MAGIC)
+    retval = 0;
+
+  mapblock = -1;
+  mapblock_offset = -1;
+  
+  return retval;
+}
+
+static int64_t
+block_map (int file_block)
+{
+  int bnum, offset, bsize;
+  
+  if (file_block < NDADDR)
+    return (INODE->di_db[file_block]);
+  
+  /* If the blockmap loaded does not include FILE_BLOCK,
+     load a new blockmap.  */
+  if ((bnum = fsbtodb (SUPERBLOCK, INODE->di_ib[0])) != mapblock
+      || (mapblock_offset <= bnum && bnum <= mapblock_offset + mapblock_bsize))
+    {
+      if (MAPBUF_LEN < SUPERBLOCK->fs_bsize)
+	{
+	  offset = ((file_block - NDADDR) % NINDIR (SUPERBLOCK));
+	  bsize = MAPBUF_LEN;
+	  
+	  if (offset + MAPBUF_LEN > SUPERBLOCK->fs_bsize)
+	    offset = (SUPERBLOCK->fs_bsize - MAPBUF_LEN) / sizeof (int);
+	}
+      else
+	{
+	  bsize = SUPERBLOCK->fs_bsize;
+	  offset = 0;
+	}
+      
+      if (! devread (bnum, offset * sizeof (int), bsize, (char *) MAPBUF))
+	{
+	  mapblock = -1;
+	  mapblock_bsize = -1;
+	  mapblock_offset = -1;
+	  errnum = ERR_FSYS_CORRUPT;
+	  return -1;
+	}
+      
+      mapblock = bnum;
+      mapblock_bsize = bsize;
+      mapblock_offset = offset;
+    }
+  
+  return (((int64_t *) MAPBUF)[((file_block - NDADDR) % NINDIR (SUPERBLOCK))
+			  - mapblock_offset]);
+}
+
+int
+ufs2_read (char *buf, int len)
+{
+  int logno, off, size, ret = 0;
+  int64_t map;
+
+  while (len && !errnum)
+    {
+      off = blkoff (SUPERBLOCK, filepos);
+      logno = lblkno (SUPERBLOCK, filepos);
+      size = blksize (SUPERBLOCK, INODE, logno);
+
+      if ((map = block_map (logno)) < 0)
+	break; 
+
+      size -= off;
+
+      if (size > len)
+	size = len;
+
+      disk_read_func = disk_read_hook;
+
+      devread (fsbtodb (SUPERBLOCK, map), off, size, buf);
+
+      disk_read_func = NULL;
+
+      buf += size;
+      len -= size;
+      filepos += size;
+      ret += size;
+    }
+
+  if (errnum)
+    ret = 0;
+
+  return ret;
+}
+
+int
+ufs2_dir (char *dirname)
+{
+  char *rest, ch;
+  int block, off, loc, ino = ROOTINO;
+  int64_t map;
+  struct direct *dp;
+
+/* main loop to find destination inode */
+loop:
+
+  /* load current inode (defaults to the root inode) */
+
+	if (!devread (fsbtodb (SUPERBLOCK, ino_to_fsba (SUPERBLOCK, ino)),
+								ino % (SUPERBLOCK->fs_inopb) * sizeof (struct ufs2_dinode),
+								sizeof (struct ufs2_dinode), (char *) INODE))
+			return 0;			/* XXX what return value? */
+
+  /* if we have a real file (and we're not just printing possibilities),
+     then this is where we want to exit */
+
+  if (!*dirname || isspace (*dirname))
+    {
+      if ((INODE->di_mode & IFMT) != IFREG)
+	{
+	  errnum = ERR_BAD_FILETYPE;
+	  return 0;
+	}
+
+      filemax = INODE->di_size;
+
+      /* incomplete implementation requires this! */
+      fsmax = (NDADDR + NINDIR (SUPERBLOCK)) * SUPERBLOCK->fs_bsize;
+      return 1;
+    }
+
+  /* continue with file/directory name interpretation */
+
+  while (*dirname == '/')
+    dirname++;
+
+  if (!(INODE->di_size) || ((INODE->di_mode & IFMT) != IFDIR))
+    {
+      errnum = ERR_BAD_FILETYPE;
+      return 0;
+    }
+
+  for (rest = dirname; (ch = *rest) && !isspace (ch) && ch != '/'; rest++);
+
+  *rest = 0;
+  loc = 0;
+
+  /* loop for reading a the entries in a directory */
+
+  do
+    {
+      if (loc >= INODE->di_size)
+	{
+#if 0
+	  putchar ('\n');
+#endif
+
+	  if (print_possibilities < 0)
+	    return 1;
+
+	  errnum = ERR_FILE_NOT_FOUND;
+	  *rest = ch;
+	  return 0;
+	}
+
+      if (!(off = blkoff (SUPERBLOCK, loc)))
+	{
+	  block = lblkno (SUPERBLOCK, loc);
+
+	  if ((map = block_map (block)) < 0
+	      || !devread (fsbtodb (SUPERBLOCK, map), 0,
+			   blksize (SUPERBLOCK, INODE, block),
+			   (char *) FSYS_BUF))
+	    {
+	      errnum = ERR_FSYS_CORRUPT;
+	      *rest = ch;
+	      return 0;
+	    }
+	}
+
+      dp = (struct direct *) (FSYS_BUF + off);
+      loc += dp->d_reclen;
+
+#ifndef STAGE1_5
+      if (dp->d_ino && print_possibilities && ch != '/'
+	  && (!*dirname || substring (dirname, dp->d_name) <= 0))
+	{
+	  if (print_possibilities > 0)
+	    print_possibilities = -print_possibilities;
+
+	  print_a_completion (dp->d_name);
+	}
+#endif /* STAGE1_5 */
+    }
+  while (!dp->d_ino || (substring (dirname, dp->d_name) != 0
+			|| (print_possibilities && ch != '/')));
+
+  /* only get here if we have a matching directory entry */
+
+  ino = dp->d_ino;
+  *(dirname = rest) = ch;
+
+  /* go back to main loop at top of function */
+  goto loop;
+}
+
+int
+ufs2_embed (int *start_sector, int needed_sectors)
+{
+  /* XXX: I don't know if this is really correct. Someone who is
+     familiar with BSD should check for this.  */
+  if (needed_sectors > 14)
+    return 0;
+  
+  *start_sector = 1;
+#if 1
+  /* FIXME: Disable the embedding in FFS until someone checks if
+     the code above is correct.  */
+  return 0;
+#else
+  return 1;
+#endif
+}
+
+#endif /* FSYS_UFS2 */
diff -ruN grub-0.94.orig/stage2/shared.h stage2/shared.h
--- grub-0.94.orig/stage2/shared.h	Wed Feb 11 00:22:12 2004
+++ stage2/shared.h	Wed Feb 11 00:22:29 2004
@@ -205,6 +205,7 @@
 #define STAGE2_ID_VSTAFS_STAGE1_5	6
 #define STAGE2_ID_JFS_STAGE1_5		7
 #define STAGE2_ID_XFS_STAGE1_5		8
+#define STAGE2_ID_UFS2_STAGE1_5         9
 
 #ifndef STAGE1_5
 # define STAGE2_ID	STAGE2_ID_STAGE2
@@ -225,6 +226,8 @@
 #  define STAGE2_ID	STAGE2_ID_JFS_STAGE1_5
 # elif defined(FSYS_XFS)
 #  define STAGE2_ID	STAGE2_ID_XFS_STAGE1_5
+# elif defined(FSYS_UFS2)
+#  define STAGE2_ID	STAGE2_ID_UFS2_STAGE1_5
 # else
 #  error "unknown Stage 2"
 # endif
diff -ruN grub-0.94.orig/stage2/shared.h.orig stage2/shared.h.orig
--- grub-0.94.orig/stage2/shared.h.orig	Thu Jan  1 03:00:00 1970
+++ stage2/shared.h.orig	Sun Jan 11 12:39:22 2004
@@ -0,0 +1,980 @@
+/* shared.h - definitions used in all GRUB-specific code */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,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.
+ */
+
+/*
+ *  Generic defines to use anywhere
+ */
+
+#ifndef GRUB_SHARED_HEADER
+#define GRUB_SHARED_HEADER	1
+
+#include <config.h>
+
+/* Add an underscore to a C symbol in assembler code if needed. */
+#ifdef HAVE_ASM_USCORE
+# define EXT_C(sym) _ ## sym
+#else
+# define EXT_C(sym) sym
+#endif
+
+/* Maybe redirect memory requests through grub_scratch_mem. */
+#ifdef GRUB_UTIL
+extern char *grub_scratch_mem;
+# define RAW_ADDR(x) ((x) + (int) grub_scratch_mem)
+# define RAW_SEG(x) (RAW_ADDR ((x) << 4) >> 4)
+#else
+# define RAW_ADDR(x) (x)
+# define RAW_SEG(x) (x)
+#endif
+
+/*
+ *  Integer sizes
+ */
+
+#define MAXINT     0x7FFFFFFF
+
+/* Maximum command line size. Before you blindly increase this value,
+   see the comment in char_io.c (get_cmdline).  */
+#define MAX_CMDLINE 1600
+#define NEW_HEAPSIZE 1500
+
+/* 512-byte scratch area */
+#define SCRATCHADDR  RAW_ADDR (0x77e00)
+#define SCRATCHSEG   RAW_SEG (0x77e0)
+
+/*
+ *  This is the location of the raw device buffer.  It is 31.5K
+ *  in size.
+ */
+
+#define BUFFERLEN   0x7e00
+#define BUFFERADDR  RAW_ADDR (0x70000)
+#define BUFFERSEG   RAW_SEG (0x7000)
+
+#define BOOT_PART_TABLE	RAW_ADDR (0x07be)
+
+/*
+ *  BIOS disk defines
+ */
+#define BIOSDISK_READ		    0x0
+#define BIOSDISK_WRITE		    0x1
+#define BIOSDISK_ERROR_GEOMETRY     0x100
+#define BIOSDISK_FLAG_LBA_EXTENSION 0x1
+
+/*
+ *  This is the filesystem (not raw device) buffer.
+ *  It is 32K in size, do not overrun!
+ */
+
+#define FSYS_BUFLEN  0x8000
+#define FSYS_BUF RAW_ADDR (0x68000)
+
+/* Command-line buffer for Multiboot kernels and modules. This area
+   includes the area into which Stage 1.5 and Stage 1 are loaded, but
+   that's no problem.  */
+#define MB_CMDLINE_BUF		RAW_ADDR (0x2000)
+#define MB_CMDLINE_BUFLEN	0x6000
+
+/* The buffer for the password.  */
+#define PASSWORD_BUF		RAW_ADDR (0x78000)
+#define PASSWORD_BUFLEN		0x200
+
+/* The buffer for the command-line.  */
+#define CMDLINE_BUF		(PASSWORD_BUF + PASSWORD_BUFLEN)
+#define CMDLINE_BUFLEN		MAX_CMDLINE
+
+/* The kill buffer for the command-line.  */
+#define KILL_BUF		(CMDLINE_BUF + CMDLINE_BUFLEN)
+#define KILL_BUFLEN		MAX_CMDLINE
+
+/* The history buffer for the command-line.  */
+#define HISTORY_BUF		(KILL_BUF + KILL_BUFLEN)
+#define HISTORY_SIZE		5
+#define HISTORY_BUFLEN		(MAX_CMDLINE * HISTORY_SIZE)
+
+/* The buffer for the completion.  */
+#define COMPLETION_BUF		(HISTORY_BUF + HISTORY_BUFLEN)
+#define COMPLETION_BUFLEN	MAX_CMDLINE
+
+/* The buffer for the unique string.  */
+#define UNIQUE_BUF		(COMPLETION_BUF + COMPLETION_BUFLEN)
+#define UNIQUE_BUFLEN		MAX_CMDLINE
+
+/* The buffer for the menu entries.  */
+#define MENU_BUF		(UNIQUE_BUF + UNIQUE_BUFLEN)
+#define MENU_BUFLEN		(0x8000 + PASSWORD_BUF - UNIQUE_BUF)
+
+/* The size of the drive map.  */
+#define DRIVE_MAP_SIZE		8
+
+/* The size of the key map.  */
+#define KEY_MAP_SIZE		128
+
+/* The size of the io map.  */
+#define IO_MAP_SIZE		128
+
+/*
+ *  Linux setup parameters
+ */
+
+#define LINUX_MAGIC_SIGNATURE		0x53726448	/* "HdrS" */
+#define LINUX_DEFAULT_SETUP_SECTS	4
+#define LINUX_FLAG_CAN_USE_HEAP		0x80
+#define LINUX_INITRD_MAX_ADDRESS	0x38000000
+#define LINUX_MAX_SETUP_SECTS		64
+#define LINUX_BOOT_LOADER_TYPE		0x71
+#define LINUX_HEAP_END_OFFSET		(0x9000 - 0x200)
+
+#define LINUX_BZIMAGE_ADDR		RAW_ADDR (0x100000)
+#define LINUX_ZIMAGE_ADDR		RAW_ADDR (0x10000)
+#define LINUX_OLD_REAL_MODE_ADDR	RAW_ADDR (0x90000)
+#define LINUX_SETUP_STACK		0x9000
+
+#define LINUX_FLAG_BIG_KERNEL		0x1
+
+/* Linux's video mode selection support. Actually I hate it!  */
+#define LINUX_VID_MODE_NORMAL		0xFFFF
+#define LINUX_VID_MODE_EXTENDED		0xFFFE
+#define LINUX_VID_MODE_ASK		0xFFFD
+
+#define LINUX_CL_OFFSET			0x9000
+#define LINUX_CL_END_OFFSET		0x90FF
+#define LINUX_SETUP_MOVE_SIZE		0x9100
+#define LINUX_CL_MAGIC			0xA33F
+
+/*
+ *  General disk stuff
+ */
+
+#define SECTOR_SIZE		0x200
+#define SECTOR_BITS		9
+#define BIOS_FLAG_FIXED_DISK	0x80
+
+#define BOOTSEC_LOCATION		RAW_ADDR (0x7C00)
+#define BOOTSEC_SIGNATURE		0xAA55
+#define BOOTSEC_BPB_OFFSET		0x3
+#define BOOTSEC_BPB_LENGTH		0x3B
+#define BOOTSEC_BPB_SYSTEM_ID		0x3
+#define BOOTSEC_BPB_HIDDEN_SECTORS	0x1C
+#define BOOTSEC_PART_OFFSET		0x1BE
+#define BOOTSEC_PART_LENGTH		0x40
+#define BOOTSEC_SIG_OFFSET		0x1FE
+#define BOOTSEC_LISTSIZE		8
+
+/* Not bad, perhaps.  */
+#define NETWORK_DRIVE	0x20
+
+/*
+ *  GRUB specific information
+ *    (in LSB order)
+ */
+
+#include <stage1.h>
+
+#define STAGE2_VER_MAJ_OFFS	0x6
+#define STAGE2_INSTALLPART	0x8
+#define STAGE2_SAVED_ENTRYNO	0xc
+#define STAGE2_STAGE2_ID	0x10
+#define STAGE2_FORCE_LBA	0x11
+#define STAGE2_VER_STR_OFFS	0x12
+
+/* Stage 2 identifiers */
+#define STAGE2_ID_STAGE2		0
+#define STAGE2_ID_FFS_STAGE1_5		1
+#define STAGE2_ID_E2FS_STAGE1_5		2
+#define STAGE2_ID_FAT_STAGE1_5		3
+#define STAGE2_ID_MINIX_STAGE1_5	4
+#define STAGE2_ID_REISERFS_STAGE1_5	5
+#define STAGE2_ID_VSTAFS_STAGE1_5	6
+#define STAGE2_ID_JFS_STAGE1_5		7
+#define STAGE2_ID_XFS_STAGE1_5		8
+
+#ifndef STAGE1_5
+# define STAGE2_ID	STAGE2_ID_STAGE2
+#else
+# if defined(FSYS_FFS)
+#  define STAGE2_ID	STAGE2_ID_FFS_STAGE1_5
+# elif defined(FSYS_EXT2FS)
+#  define STAGE2_ID	STAGE2_ID_E2FS_STAGE1_5
+# elif defined(FSYS_FAT)
+#  define STAGE2_ID	STAGE2_ID_FAT_STAGE1_5
+# elif defined(FSYS_MINIX)
+#  define STAGE2_ID	STAGE2_ID_MINIX_STAGE1_5
+# elif defined(FSYS_REISERFS)
+#  define STAGE2_ID	STAGE2_ID_REISERFS_STAGE1_5
+# elif defined(FSYS_VSTAFS)
+#  define STAGE2_ID	STAGE2_ID_VSTAFS_STAGE1_5
+# elif defined(FSYS_JFS)
+#  define STAGE2_ID	STAGE2_ID_JFS_STAGE1_5
+# elif defined(FSYS_XFS)
+#  define STAGE2_ID	STAGE2_ID_XFS_STAGE1_5
+# else
+#  error "unknown Stage 2"
+# endif
+#endif
+
+/*
+ *  defines for use when switching between real and protected mode
+ */
+
+#define CR0_PE_ON	0x1
+#define CR0_PE_OFF	0xfffffffe
+#define PROT_MODE_CSEG	0x8
+#define PROT_MODE_DSEG  0x10
+#define PSEUDO_RM_CSEG	0x18
+#define PSEUDO_RM_DSEG	0x20
+#define STACKOFF	(0x2000 - 0x10)
+#define PROTSTACKINIT   (FSYS_BUF - 0x10)
+
+
+/*
+ * Assembly code defines
+ *
+ * "EXT_C" is assumed to be defined in the Makefile by the configure
+ *   command.
+ */
+
+#define ENTRY(x) .globl EXT_C(x) ; EXT_C(x):
+#define VARIABLE(x) ENTRY(x)
+
+
+#define K_RDWR  	0x60	/* keyboard data & cmds (read/write) */
+#define K_STATUS	0x64	/* keyboard status */
+#define K_CMD		0x64	/* keybd ctlr command (write-only) */
+
+#define K_OBUF_FUL 	0x01	/* output buffer full */
+#define K_IBUF_FUL 	0x02	/* input buffer full */
+
+#define KC_CMD_WIN	0xd0	/* read  output port */
+#define KC_CMD_WOUT	0xd1	/* write output port */
+#define KB_OUTPUT_MASK  0xdd	/* enable output buffer full interrupt
+				   enable data line
+				   enable clock line */
+#define KB_A20_ENABLE   0x02
+
+/* Codes for getchar. */
+#define ASCII_CHAR(x)   ((x) & 0xFF)
+#if !defined(GRUB_UTIL) || !defined(HAVE_LIBCURSES)
+# define KEY_LEFT        0x4B00
+# define KEY_RIGHT       0x4D00
+# define KEY_UP          0x4800
+# define KEY_DOWN        0x5000
+# define KEY_IC          0x5200	/* insert char */
+# define KEY_DC          0x5300	/* delete char */
+# define KEY_BACKSPACE   0x0008
+# define KEY_HOME        0x4700
+# define KEY_END         0x4F00
+# define KEY_NPAGE       0x5100
+# define KEY_PPAGE       0x4900
+# define A_NORMAL        0x7
+# define A_REVERSE       0x70
+#elif defined(HAVE_NCURSES_CURSES_H)
+# include <ncurses/curses.h>
+#elif defined(HAVE_NCURSES_H)
+# include <ncurses.h>
+#elif defined(HAVE_CURSES_H)
+# include <curses.h>
+#endif
+
+/* In old BSD curses, A_NORMAL and A_REVERSE are not defined, so we
+   define them here if they are undefined.  */
+#ifndef A_NORMAL
+# define A_NORMAL	0
+#endif /* ! A_NORMAL */
+#ifndef A_REVERSE
+# ifdef A_STANDOUT
+#  define A_REVERSE	A_STANDOUT
+# else /* ! A_STANDOUT */
+#  define A_REVERSE	0
+# endif /* ! A_STANDOUT */
+#endif /* ! A_REVERSE */
+
+/* Define ACS_* ourselves, since the definitions are not consistent among
+   various curses implementations.  */
+#undef ACS_ULCORNER
+#undef ACS_URCORNER
+#undef ACS_LLCORNER
+#undef ACS_LRCORNER
+#undef ACS_HLINE
+#undef ACS_VLINE
+#undef ACS_LARROW
+#undef ACS_RARROW
+#undef ACS_UARROW
+#undef ACS_DARROW
+
+#define ACS_ULCORNER	'+'
+#define ACS_URCORNER	'+'
+#define ACS_LLCORNER	'+'
+#define ACS_LRCORNER	'+'
+#define ACS_HLINE	'-'
+#define ACS_VLINE	'|'
+#define ACS_LARROW	'<'
+#define ACS_RARROW	'>'
+#define ACS_UARROW	'^'
+#define ACS_DARROW	'v'
+
+/* Special graphics characters for IBM displays. */
+#define DISP_UL		218
+#define DISP_UR		191
+#define DISP_LL		192
+#define DISP_LR		217
+#define DISP_HORIZ	196
+#define DISP_VERT	179
+#define DISP_LEFT	0x1b
+#define DISP_RIGHT	0x1a
+#define DISP_UP		0x18
+#define DISP_DOWN	0x19
+
+/* Remap some libc-API-compatible function names so that we prevent
+   circularararity. */
+#ifndef WITHOUT_LIBC_STUBS
+#define memmove grub_memmove
+#define memcpy grub_memmove	/* we don't need a separate memcpy */
+#define memset grub_memset
+#define isspace grub_isspace
+#define printf grub_printf
+#define sprintf grub_sprintf
+#undef putchar
+#define putchar grub_putchar
+#define strncat grub_strncat
+#define strstr grub_strstr
+#define memcmp grub_memcmp
+#define strcmp grub_strcmp
+#define tolower grub_tolower
+#define strlen grub_strlen
+#define strcpy grub_strcpy
+#endif /* WITHOUT_LIBC_STUBS */
+
+
+#ifndef ASM_FILE
+/*
+ *  Below this should be ONLY defines and other constructs for C code.
+ */
+
+/* multiboot stuff */
+
+#include "mb_header.h"
+#include "mb_info.h"
+
+/* For the Linux/i386 boot protocol version 2.03.  */
+struct linux_kernel_header
+{
+  char code1[0x0020];
+  unsigned short cl_magic;		/* Magic number 0xA33F */
+  unsigned short cl_offset;		/* The offset of command line */
+  char code2[0x01F1 - 0x0020 - 2 - 2];
+  unsigned char setup_sects;		/* The size of the setup in sectors */
+  unsigned short root_flags;		/* If the root is mounted readonly */
+  unsigned short syssize;		/* obsolete */
+  unsigned short swap_dev;		/* obsolete */
+  unsigned short ram_size;		/* obsolete */
+  unsigned short vid_mode;		/* Video mode control */
+  unsigned short root_dev;		/* Default root device number */
+  unsigned short boot_flag;		/* 0xAA55 magic number */
+  unsigned short jump;			/* Jump instruction */
+  unsigned long header;			/* Magic signature "HdrS" */
+  unsigned short version;		/* Boot protocol version supported */
+  unsigned long realmode_swtch;		/* Boot loader hook */
+  unsigned long start_sys;		/* Points to kernel version string */
+  unsigned char type_of_loader;		/* Boot loader identifier */
+  unsigned char loadflags;		/* Boot protocol option flags */
+  unsigned short setup_move_size;	/* Move to high memory size */
+  unsigned long code32_start;		/* Boot loader hook */
+  unsigned long ramdisk_image;		/* initrd load address */
+  unsigned long ramdisk_size;		/* initrd size */
+  unsigned long bootsect_kludge;	/* obsolete */
+  unsigned short heap_end_ptr;		/* Free memory after setup end */
+  unsigned short pad1;			/* Unused */
+  char *cmd_line_ptr;			/* Points to the kernel command line */
+  unsigned long initrd_addr_max;	/* The highest address of initrd */
+} __attribute__ ((packed));
+
+/* Memory map address range descriptor used by GET_MMAP_ENTRY. */
+struct mmar_desc
+{
+  unsigned long desc_len;	/* Size of this descriptor. */
+  unsigned long long addr;	/* Base address. */
+  unsigned long long length;	/* Length in bytes. */
+  unsigned long type;		/* Type of address range. */
+} __attribute__ ((packed));
+
+/* VBE controller information.  */
+struct vbe_controller
+{
+  unsigned char signature[4];
+  unsigned short version;
+  unsigned long oem_string;
+  unsigned long capabilities;
+  unsigned long video_mode;
+  unsigned short total_memory;
+  unsigned short oem_software_rev;
+  unsigned long oem_vendor_name;
+  unsigned long oem_product_name;
+  unsigned long oem_product_rev;
+  unsigned char reserved[222];
+  unsigned char oem_data[256];
+} __attribute__ ((packed));
+
+/* VBE mode information.  */
+struct vbe_mode
+{
+  unsigned short mode_attributes;
+  unsigned char win_a_attributes;
+  unsigned char win_b_attributes;
+  unsigned short win_granularity;
+  unsigned short win_size;
+  unsigned short win_a_segment;
+  unsigned short win_b_segment;
+  unsigned long win_func;
+  unsigned short bytes_per_scanline;
+
+  /* >=1.2 */
+  unsigned short x_resolution;
+  unsigned short y_resolution;
+  unsigned char x_char_size;
+  unsigned char y_char_size;
+  unsigned char number_of_planes;
+  unsigned char bits_per_pixel;
+  unsigned char number_of_banks;
+  unsigned char memory_model;
+  unsigned char bank_size;
+  unsigned char number_of_image_pages;
+  unsigned char reserved0;
+
+  /* direct color */
+  unsigned char red_mask_size;
+  unsigned char red_field_position;
+  unsigned char green_mask_size;
+  unsigned char green_field_position;
+  unsigned char blue_mask_size;
+  unsigned char blue_field_position;
+  unsigned char reserved_mask_size;
+  unsigned char reserved_field_position;
+  unsigned char direct_color_mode_info;
+
+  /* >=2.0 */
+  unsigned long phys_base;
+  unsigned long reserved1;
+  unsigned short reversed2;
+
+  /* >=3.0 */
+  unsigned short linear_bytes_per_scanline;
+  unsigned char banked_number_of_image_pages;
+  unsigned char linear_number_of_image_pages;
+  unsigned char linear_red_mask_size;
+  unsigned char linear_red_field_position;
+  unsigned char linear_green_mask_size;
+  unsigned char linear_green_field_position;
+  unsigned char linear_blue_mask_size;
+  unsigned char linear_blue_field_position;
+  unsigned char linear_reserved_mask_size;
+  unsigned char linear_reserved_field_position;
+  unsigned long max_pixel_clock;
+
+  unsigned char reserved3[189];
+} __attribute__ ((packed));
+
+
+#undef NULL
+#define NULL         ((void *) 0)
+
+/* Error codes (descriptions are in common.c) */
+typedef enum
+{
+  ERR_NONE = 0,
+  ERR_BAD_FILENAME,
+  ERR_BAD_FILETYPE,
+  ERR_BAD_GZIP_DATA,
+  ERR_BAD_GZIP_HEADER,
+  ERR_BAD_PART_TABLE,
+  ERR_BAD_VERSION,
+  ERR_BELOW_1MB,
+  ERR_BOOT_COMMAND,
+  ERR_BOOT_FAILURE,
+  ERR_BOOT_FEATURES,
+  ERR_DEV_FORMAT,
+  ERR_DEV_VALUES,
+  ERR_EXEC_FORMAT,
+  ERR_FILELENGTH,
+  ERR_FILE_NOT_FOUND,
+  ERR_FSYS_CORRUPT,
+  ERR_FSYS_MOUNT,
+  ERR_GEOM,
+  ERR_NEED_LX_KERNEL,
+  ERR_NEED_MB_KERNEL,
+  ERR_NO_DISK,
+  ERR_NO_PART,
+  ERR_NUMBER_PARSING,
+  ERR_OUTSIDE_PART,
+  ERR_READ,
+  ERR_SYMLINK_LOOP,
+  ERR_UNRECOGNIZED,
+  ERR_WONT_FIT,
+  ERR_WRITE,
+  ERR_BAD_ARGUMENT,
+  ERR_UNALIGNED,
+  ERR_PRIVILEGED,
+  ERR_DEV_NEED_INIT,
+  ERR_NO_DISK_SPACE,
+  ERR_NUMBER_OVERFLOW,
+
+  MAX_ERR_NUM
+} grub_error_t;
+
+extern unsigned long install_partition;
+extern unsigned long boot_drive;
+extern unsigned long install_second_sector;
+extern struct apm_info apm_bios_info;
+extern unsigned long boot_part_addr;
+extern int saved_entryno;
+extern unsigned char force_lba;
+extern char version_string[];
+extern char config_file[];
+extern unsigned long linux_text_len;
+extern char *linux_data_tmp_addr;
+extern char *linux_data_real_addr;
+
+#ifdef GRUB_UTIL
+/* If not using config file, this variable is set to zero,
+   otherwise non-zero.  */
+extern int use_config_file;
+/* If using the preset menu, this variable is set to non-zero,
+   otherwise zero.  */
+extern int use_preset_menu;
+/* If not using curses, this variable is set to zero, otherwise non-zero.  */
+extern int use_curses;
+/* The flag for verbose messages.  */
+extern int verbose;
+/* The flag for read-only.  */
+extern int read_only;
+/* The number of floppies to be probed.  */
+extern int floppy_disks;
+/* The map between BIOS drives and UNIX device file names.  */
+extern char **device_map;
+/* The filename which stores the information about a device map.  */
+extern char *device_map_file;
+/* The array of geometries.  */
+extern struct geometry *disks;
+/* Assign DRIVE to a device name DEVICE.  */
+extern void assign_device_name (int drive, const char *device);
+#endif
+
+#ifndef STAGE1_5
+/* GUI interface variables. */
+extern int fallback_entry;
+extern int default_entry;
+extern int current_entryno;
+
+/* The constants for password types.  */
+typedef enum
+{
+  PASSWORD_PLAIN,
+  PASSWORD_MD5,
+  PASSWORD_UNSUPPORTED
+}
+password_t;
+
+extern char *password;
+extern password_t password_type;
+extern int auth;
+extern char commands[];
+
+/* For `more'-like feature.  */
+extern int max_lines;
+extern int count_lines;
+extern int use_pager;
+#endif
+
+#ifndef NO_DECOMPRESSION
+extern int no_decompression;
+extern int compressed_file;
+#endif
+
+/* instrumentation variables */
+extern void (*disk_read_hook) (int, int, int);
+extern void (*disk_read_func) (int, int, int);
+
+#ifndef STAGE1_5
+/* The flag for debug mode.  */
+extern int debug;
+#endif /* STAGE1_5 */
+
+extern unsigned long current_drive;
+extern unsigned long current_partition;
+
+extern int fsys_type;
+
+/* The information for a disk geometry. The CHS information is only for
+   DOS/Partition table compatibility, and the real number of sectors is
+   stored in TOTAL_SECTORS.  */
+struct geometry
+{
+  /* The number of cylinders */
+  unsigned long cylinders;
+  /* The number of heads */
+  unsigned long heads;
+  /* The number of sectors */
+  unsigned long sectors;
+  /* The total number of sectors */
+  unsigned long total_sectors;
+  /* Flags */
+  unsigned long flags;
+};
+
+extern unsigned long part_start;
+extern unsigned long part_length;
+
+extern int current_slice;
+
+extern int buf_drive;
+extern int buf_track;
+extern struct geometry buf_geom;
+
+/* these are the current file position and maximum file position */
+extern int filepos;
+extern int filemax;
+
+/*
+ *  Common BIOS/boot data.
+ */
+
+extern struct multiboot_info mbi;
+extern unsigned long saved_drive;
+extern unsigned long saved_partition;
+#ifndef STAGE1_5
+extern unsigned long saved_mem_upper;
+extern unsigned long extended_memory;
+#endif
+
+/*
+ *  Error variables.
+ */
+
+extern grub_error_t errnum;
+extern char *err_list[];
+
+/* Simplify declaration of entry_addr. */
+typedef void (*entry_func) (int, int, int, int, int, int)
+     __attribute__ ((noreturn));
+
+extern entry_func entry_addr;
+
+/* Enter the stage1.5/stage2 C code after the stack is set up. */
+void cmain (void);
+
+/* Halt the processor (called after an unrecoverable error). */
+void stop (void) __attribute__ ((noreturn));
+
+/* Reboot the system.  */
+void grub_reboot (void) __attribute__ ((noreturn));
+
+/* Halt the system, using APM if possible. If NO_APM is true, don't use
+   APM even if it is available.  */
+void grub_halt (int no_apm) __attribute__ ((noreturn));
+
+/* Copy MAP to the drive map and set up int13_handler.  */
+void set_int13_handler (unsigned short *map);
+
+/* Set up int15_handler.  */
+void set_int15_handler (void);
+
+/* Restore the original int15 handler.  */
+void unset_int15_handler (void);
+
+/* Track the int13 handler to probe I/O address space.  */
+void track_int13 (int drive);
+
+/* The key map.  */
+extern unsigned short bios_key_map[];
+extern unsigned short ascii_key_map[];
+extern unsigned short io_map[];
+
+/* calls for direct boot-loader chaining */
+void chain_stage1 (unsigned long segment, unsigned long offset,
+		   unsigned long part_table_addr)
+     __attribute__ ((noreturn));
+void chain_stage2 (unsigned long segment, unsigned long offset,
+		   int second_sector)
+     __attribute__ ((noreturn));
+
+/* do some funky stuff, then boot linux */
+void linux_boot (void) __attribute__ ((noreturn));
+
+/* do some funky stuff, then boot bzImage linux */
+void big_linux_boot (void) __attribute__ ((noreturn));
+
+/* booting a multiboot executable */
+void multi_boot (int start, int mb_info) __attribute__ ((noreturn));
+
+/* If LINEAR is nonzero, then set the Intel processor to linear mode.
+   Otherwise, bit 20 of all memory accesses is always forced to zero,
+   causing a wraparound effect for bugwards compatibility with the
+   8086 CPU. */
+void gateA20 (int linear);
+
+/* memory probe routines */
+int get_memsize (int type);
+int get_eisamemsize (void);
+
+/* Fetch the next entry in the memory map and return the continuation
+   value.  DESC is a pointer to the descriptor buffer, and CONT is the
+   previous continuation value (0 to get the first entry in the
+   map). */
+int get_mmap_entry (struct mmar_desc *desc, int cont);
+
+/* Get the linear address of a ROM configuration table. Return zero,
+   if fails.  */
+unsigned long get_rom_config_table (void);
+
+/* Get APM BIOS information.  */
+void get_apm_info (void);
+
+/* Get VBE controller information.  */
+int get_vbe_controller_info (struct vbe_controller *controller);
+
+/* Get VBE mode information.  */
+int get_vbe_mode_info (int mode_number, struct vbe_mode *mode);
+
+/* Set VBE mode.  */
+int set_vbe_mode (int mode_number);
+
+/* Return the data area immediately following our code. */
+int get_code_end (void);
+
+/* low-level timing info */
+int getrtsecs (void);
+int currticks (void);
+
+/* Clear the screen. */
+void cls (void);
+
+/* Turn on/off cursor. */
+int setcursor (int on);
+
+/* Get the current cursor position (where 0,0 is the top left hand
+   corner of the screen).  Returns packed values, (RET >> 8) is x,
+   (RET & 0xff) is y. */
+int getxy (void);
+
+/* Set the cursor position. */
+void gotoxy (int x, int y);
+
+/* Displays an ASCII character.  IBM displays will translate some
+   characters to special graphical ones (see the DISP_* constants). */
+void grub_putchar (int c);
+
+/* Wait for a keypress, and return its packed BIOS/ASCII key code.
+   Use ASCII_CHAR(ret) to extract the ASCII code. */
+int getkey (void);
+
+/* Like GETKEY, but doesn't block, and returns -1 if no keystroke is
+   available. */
+int checkkey (void);
+
+/* Low-level disk I/O */
+int get_diskinfo (int drive, struct geometry *geometry);
+int biosdisk (int subfunc, int drive, struct geometry *geometry,
+	      int sector, int nsec, int segment);
+void stop_floppy (void);
+
+/* Command-line interface functions. */
+#ifndef STAGE1_5
+
+/* The flags for the builtins.  */
+#define BUILTIN_CMDLINE		0x1	/* Run in the command-line.  */
+#define BUILTIN_MENU		0x2	/* Run in the menu.  */
+#define BUILTIN_TITLE		0x4	/* Only for the command title.  */
+#define BUILTIN_SCRIPT		0x8	/* Run in the script.  */
+#define BUILTIN_NO_ECHO		0x10	/* Don't print command on booting. */
+#define BUILTIN_HELP_LIST	0x20	/* Show help in listing.  */
+
+/* The table for a builtin.  */
+struct builtin
+{
+  /* The command name.  */
+  char *name;
+  /* The callback function.  */
+  int (*func) (char *, int);
+  /* The combination of the flags defined above.  */
+  int flags;
+  /* The short version of the documentation.  */
+  char *short_doc;
+  /* The long version of the documentation.  */
+  char *long_doc;
+};
+
+/* All the builtins are registered in this.  */
+extern struct builtin *builtin_table[];
+
+/* The constants for kernel types.  */
+typedef enum
+{
+  KERNEL_TYPE_NONE,		/* None is loaded.  */
+  KERNEL_TYPE_MULTIBOOT,	/* Multiboot.  */
+  KERNEL_TYPE_LINUX,		/* Linux.  */
+  KERNEL_TYPE_BIG_LINUX,	/* Big Linux.  */
+  KERNEL_TYPE_FREEBSD,		/* FreeBSD.  */
+  KERNEL_TYPE_NETBSD,		/* NetBSD.  */
+  KERNEL_TYPE_CHAINLOADER	/* Chainloader.  */
+}
+kernel_t;
+
+extern kernel_t kernel_type;
+extern int show_menu;
+extern int grub_timeout;
+
+void init_builtins (void);
+void init_config (void);
+char *skip_to (int after_equal, char *cmdline);
+struct builtin *find_command (char *command);
+void print_cmdline_message (int forever);
+void enter_cmdline (char *heap, int forever);
+int run_script (char *script, char *heap);
+#endif
+
+/* C library replacement functions with identical semantics. */
+void grub_printf (const char *format,...);
+int grub_sprintf (char *buffer, const char *format, ...);
+int grub_tolower (int c);
+int grub_isspace (int c);
+int grub_strncat (char *s1, const char *s2, int n);
+void *grub_memmove (void *to, const void *from, int len);
+void *grub_memset (void *start, int c, int len);
+int grub_strncat (char *s1, const char *s2, int n);
+char *grub_strstr (const char *s1, const char *s2);
+int grub_memcmp (const char *s1, const char *s2, int n);
+int grub_strcmp (const char *s1, const char *s2);
+int grub_strlen (const char *str);
+char *grub_strcpy (char *dest, const char *src);
+
+#ifndef GRUB_UTIL
+typedef unsigned long grub_jmp_buf[6];
+#else
+/* In the grub shell, use the libc jmp_buf instead.  */
+# include <setjmp.h>
+# define grub_jmp_buf jmp_buf
+#endif
+
+#ifdef GRUB_UTIL
+# define grub_setjmp	setjmp
+# define grub_longjmp	longjmp
+#else /* ! GRUB_UTIL */
+int grub_setjmp (grub_jmp_buf env);
+void grub_longjmp (grub_jmp_buf env, int val);
+#endif /* ! GRUB_UTIL */
+
+/* The environment for restarting Stage 2.  */
+extern grub_jmp_buf restart_env;
+/* The environment for restarting the command-line interface.  */
+extern grub_jmp_buf restart_cmdline_env;
+
+/* misc */
+void init_page (void);
+void print_error (void);
+char *convert_to_ascii (char *buf, int c, ...);
+int get_cmdline (char *prompt, char *cmdline, int maxlen,
+		 int echo_char, int history);
+int substring (const char *s1, const char *s2);
+int nul_terminate (char *str);
+int get_based_digit (int c, int base);
+int safe_parse_maxint (char **str_ptr, int *myint_ptr);
+int memcheck (int start, int len);
+void grub_putstr (const char *str);
+
+#ifndef NO_DECOMPRESSION
+/* Compression support. */
+int gunzip_test_header (void);
+int gunzip_read (char *buf, int len);
+#endif /* NO_DECOMPRESSION */
+
+int rawread (int drive, int sector, int byte_offset, int byte_len, char *buf);
+int devread (int sector, int byte_offset, int byte_len, char *buf);
+int rawwrite (int drive, int sector, char *buf);
+int devwrite (int sector, int sector_len, char *buf);
+
+/* Parse a device string and initialize the global parameters. */
+char *set_device (char *device);
+int open_device (void);
+int real_open_partition (int flags);
+int open_partition (void);
+int next_partition (unsigned long drive, unsigned long dest,
+		    unsigned long *partition, int *type,
+		    unsigned long *start, unsigned long *len,
+		    unsigned long *offset, int *entry,
+		    unsigned long *ext_offset, char *buf);
+
+/* Sets device to the one represented by the SAVED_* parameters. */
+int make_saved_active (void);
+
+/* Set or clear the current root partition's hidden flag.  */
+int set_partition_hidden_flag (int hidden);
+
+/* Open a file or directory on the active device, using GRUB's
+   internal filesystem support. */
+int grub_open (char *filename);
+
+/* Read LEN bytes into BUF from the file that was opened with
+   GRUB_OPEN.  If LEN is -1, read all the remaining data in the file.  */
+int grub_read (char *buf, int len);
+
+/* Reposition a file offset.  */
+int grub_seek (int offset);
+
+/* Close a file.  */
+void grub_close (void);
+
+/* List the contents of the directory that was opened with GRUB_OPEN,
+   printing all completions. */
+int dir (char *dirname);
+
+int set_bootdev (int hdbias);
+
+/* Display statistics on the current active device. */
+void print_fsys_type (void);
+
+/* Display device and filename completions. */
+void print_a_completion (char *filename);
+int print_completions (int is_filename, int is_completion);
+
+/* Copies the current partition data to the desired address. */
+void copy_current_part_entry (char *buf);
+
+#ifndef STAGE1_5
+void bsd_boot (kernel_t type, int bootdev, char *arg)
+     __attribute__ ((noreturn));
+
+/* Define flags for load_image here.  */
+/* Don't pass a Linux's mem option automatically.  */
+#define KERNEL_LOAD_NO_MEM_OPTION	(1 << 0)
+
+kernel_t load_image (char *kernel, char *arg, kernel_t suggested_type,
+		     unsigned long load_flags);
+
+int load_module (char *module, char *arg);
+int load_initrd (char *initrd);
+
+int check_password(char *entered, char* expected, password_t type);
+#endif
+
+void init_bios_info (void);
+
+#endif /* ASM_FILE */
+
+#endif /* ! GRUB_SHARED_HEADER */
diff -ruN grub-0.94.orig/stage2/size_test stage2/size_test
--- grub-0.94.orig/stage2/size_test	Wed Feb 11 00:22:12 2004
+++ stage2/size_test	Wed Feb 11 00:22:29 2004
@@ -40,6 +40,8 @@
 # The bootloader area of a FFS partition is 14 sectors.
 check ffs_stage1_5 7168
 
+check ufs2_stage1_5 7168
+
 # Stage 1.5 can be installed in the sectors immediately after MBR in the
 # first cylinder, so the size is (63 - 1) sectors.
 check fat_stage1_5 31744
diff -ruN grub-0.94.orig/stage2/size_test.orig stage2/size_test.orig
--- grub-0.94.orig/stage2/size_test.orig	Thu Jan  1 03:00:00 1970
+++ stage2/size_test.orig	Wed Jul  9 15:45:53 2003
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# Check the sizes of Stage 2 and Stage 1.5's.
+# Copyright (C) 1999  Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+# Written by OKUJI Yoshinori <okuji@kuicr.kyoto-u.ac.jp>
+
+
+# This function checks if the size of the first argument (filename) is
+# greater than the second argument (limit). If so, then exit with the
+# status 1, otherwise do nothing.
+check ()
+{
+    local file size limit
+
+    file=$1
+    limit=$2
+    set dummy `ls -l $file`
+    size=$6
+    if test $size -gt $limit; then
+	echo "$file is too big ($size > $limit)."
+	exit 1
+    fi
+}
+
+# The bootloader area of a FFS partition is 14 sectors.
+check ffs_stage1_5 7168
+
+# Stage 1.5 can be installed in the sectors immediately after MBR in the
+# first cylinder, so the size is (63 - 1) sectors.
+check fat_stage1_5 31744
+
+# Likewise.
+check e2fs_stage1_5 31744
+
+# Likewise.
+check minix_stage1_5 31744
+
+# Success.
+exit 0
diff -ruN grub-0.94.orig/stage2/ufs2.h stage2/ufs2.h
--- grub-0.94.orig/stage2/ufs2.h	Thu Jan  1 03:00:00 1970
+++ stage2/ufs2.h	Wed Feb 11 00:23:16 2004
@@ -0,0 +1,410 @@
+/*
+ * Copyright (c) 2002 Networks Associates Technology, Inc.
+ * All rights reserved.
+ *
+ * This software was developed for the FreeBSD Project by Marshall
+ * Kirk McKusick and Network Associates Laboratories, the Security
+ * Research Division of Network Associates, Inc. under DARPA/SPAWAR
+ * contract N66001-01-C-8035 ("CBOSS"), as part of the DARPA CHATS
+ * research program
+ *
+ * Copyright (c) 1982, 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The names of the authors may not be used to endorse or promote
+ *    products derived from this software without specific prior written
+ *    permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *	@(#)dinode.h	8.3 (Berkeley) 1/21/94
+ * $FreeBSD: ports/sysutils/grub/files/patch-ufs2,v 1.1 2004/02/10 22:00:02 krion Exp $
+ */
+
+#ifndef _GRUB_UFS2_H_
+#define _GRUB_UFS2_H_
+
+typedef signed char            int8_t;
+typedef signed short           int16_t;
+typedef signed int             int32_t;
+typedef signed long long int   int64_t;
+typedef unsigned char          uint8_t;
+typedef unsigned short         uint16_t;
+typedef unsigned int           uint32_t;
+typedef unsigned long long int uint64_t;
+
+typedef uint8_t                u_char;
+typedef uint32_t               u_int;
+
+typedef uint8_t                u_int8_t;
+typedef uint16_t               u_int16_t;
+typedef uint32_t               u_int32_t;
+typedef uint64_t               u_int64_t;
+
+/*
+ * __uint* constants already defined in
+ * FreeBSD 5.x /usr/include/machine/_types.h
+ * or
+ * FreeBSD 4.x /usr/include/machine/ansi.h
+ */
+#if !defined(_MACHINE__TYPES_H_) && !defined(_MACHINE_ANSI_H_)
+typedef uint8_t                __uint8_t;
+typedef uint16_t               __uint16_t;
+typedef uint32_t               __uint32_t;
+typedef uint64_t               __uint64_t;
+#endif /* _MACHINE__TYPES_H_ */
+
+#define i_size di_size
+
+
+#define DEV_BSIZE 512
+
+/*
+ * The root inode is the root of the filesystem.  Inode 0 can't be used for
+ * normal purposes and historically bad blocks were linked to inode 1, thus
+ * the root inode is 2.  (Inode 1 is no longer used for this purpose, however
+ * numerous dump tapes make this assumption, so we are stuck with it).
+ */
+#define	ROOTINO	((ino_t)2)
+
+/*
+ * The size of physical and logical block numbers and time fields in UFS.
+ */
+typedef	int64_t	ufs2_daddr_t;
+typedef int64_t ufs_lbn_t;
+typedef int64_t ufs_time_t;
+
+/* inode number */
+typedef __uint32_t      ino_t;
+
+/* File permissions. */
+#define	IEXEC		0000100		/* Executable. */
+#define	IWRITE		0000200		/* Writeable. */
+#define	IREAD		0000400		/* Readable. */
+#define	ISVTX		0001000		/* Sticky bit. */
+#define	ISGID		0002000		/* Set-gid. */
+#define	ISUID		0004000		/* Set-uid. */
+
+/* File types. */
+#define	IFMT		0170000		/* Mask of file type. */
+#define	IFIFO		0010000		/* Named pipe (fifo). */
+#define	IFCHR		0020000		/* Character device. */
+#define	IFDIR		0040000		/* Directory file. */
+#define	IFBLK		0060000		/* Block device. */
+#define	IFREG		0100000		/* Regular file. */
+#define	IFLNK		0120000		/* Symbolic link. */
+#define	IFSOCK		0140000		/* UNIX domain socket. */
+#define	IFWHT		0160000		/* Whiteout. */
+
+/*
+ * A dinode contains all the meta-data associated with a UFS2 file.
+ * This structure defines the on-disk format of a dinode. Since
+ * this structure describes an on-disk structure, all its fields
+ * are defined by types with precise widths.
+ */
+
+#define	NXADDR	2			/* External addresses in inode. */
+#define	NDADDR	12			/* Direct addresses in inode. */
+#define	NIADDR	3			/* Indirect addresses in inode. */
+
+struct ufs2_dinode {
+	u_int16_t	di_mode;	/*   0: IFMT, permissions; see below. */
+	int16_t		di_nlink;	/*   2: File link count. */
+	u_int32_t	di_uid;		/*   4: File owner. */
+	u_int32_t	di_gid;		/*   8: File group. */
+	u_int32_t	di_blksize;	/*  12: Inode blocksize. */
+	u_int64_t	di_size;	/*  16: File byte count. */
+	u_int64_t	di_blocks;	/*  24: Bytes actually held. */
+	ufs_time_t	di_atime;	/*  32: Last access time. */
+	ufs_time_t	di_mtime;	/*  40: Last modified time. */
+	ufs_time_t	di_ctime;	/*  48: Last inode change time. */
+	ufs_time_t	di_birthtime;	/*  56: Inode creation time. */
+	int32_t		di_mtimensec;	/*  64: Last modified time. */
+	int32_t		di_atimensec;	/*  68: Last access time. */
+	int32_t		di_ctimensec;	/*  72: Last inode change time. */
+	int32_t		di_birthnsec;	/*  76: Inode creation time. */
+	int32_t		di_gen;		/*  80: Generation number. */
+	u_int32_t	di_kernflags;	/*  84: Kernel flags. */
+	u_int32_t	di_flags;	/*  88: Status flags (chflags). */
+	int32_t		di_extsize;	/*  92: External attributes block. */
+	ufs2_daddr_t	di_extb[NXADDR];/*  96: External attributes block. */
+	ufs2_daddr_t	di_db[NDADDR];	/* 112: Direct disk blocks. */
+	ufs2_daddr_t	di_ib[NIADDR];	/* 208: Indirect disk blocks. */
+	int64_t		di_spare[3];	/* 232: Reserved; currently unused */
+};
+
+#define	MAXNAMLEN	255
+
+struct	direct {
+	u_int32_t d_ino;		/* inode number of entry */
+	u_int16_t d_reclen;		/* length of this record */
+	u_int8_t  d_type; 		/* file type, see below */
+	u_int8_t  d_namlen;		/* length of string in d_name */
+	char	  d_name[MAXNAMLEN + 1];/* name with length <= MAXNAMLEN */
+};
+
+/*
+ * File types
+ */
+#define DT_UNKNOWN       0
+#define DT_FIFO          1
+#define DT_CHR           2
+#define DT_DIR           4
+#define DT_BLK           6
+#define DT_REG           8
+#define DT_LNK          10
+#define DT_SOCK         12
+#define DT_WHT          14
+
+#define SBLOCK_UFS2	 65536
+#define SBLOCKSIZE	  8192
+
+#define MAXMNTLEN	512
+
+#define	NOCSPTRS	((128 / sizeof(void *)) - 4)
+
+/*
+ * The maximum number of snapshot nodes that can be associated
+ * with each filesystem. This limit affects only the number of
+ * snapshot files that can be recorded within the superblock so
+ * that they can be found when the filesystem is mounted. However,
+ * maintaining too many will slow the filesystem performance, so
+ * having this limit is a good idea.
+ */
+#define FSMAXSNAP 20
+	
+/*
+ * Per cylinder group information; summarized in blocks allocated
+ * from first cylinder group data blocks.  These blocks have to be
+ * read in from fs_csaddr (size fs_cssize) in addition to the
+ * super block.
+ */
+struct csum {
+	int32_t	cs_ndir;		/* number of directories */
+	int32_t	cs_nbfree;		/* number of free blocks */
+	int32_t	cs_nifree;		/* number of free inodes */
+	int32_t	cs_nffree;		/* number of free frags */
+};
+
+struct csum_total {
+	int64_t	cs_ndir;		/* number of directories */
+	int64_t	cs_nbfree;		/* number of free blocks */
+	int64_t	cs_nifree;		/* number of free inodes */
+	int64_t	cs_nffree;		/* number of free frags */
+	int64_t	cs_numclusters;		/* number of free clusters */
+	int64_t	cs_spare[3];		/* future expansion */
+};
+
+/*
+ * Super block for an FFS filesystem.
+ */
+struct fs {
+	int32_t	 fs_firstfield;		/* historic filesystem linked list, */
+	int32_t	 fs_unused_1;		/*     used for incore super blocks */
+	int32_t	 fs_sblkno;		/* offset of super-block in filesys */
+	int32_t	 fs_cblkno;		/* offset of cyl-block in filesys */
+	int32_t	 fs_iblkno;		/* offset of inode-blocks in filesys */
+	int32_t	 fs_dblkno;		/* offset of first data after cg */
+	int32_t	 fs_old_cgoffset;	/* cylinder group offset in cylinder */
+	int32_t	 fs_old_cgmask;		/* used to calc mod fs_ntrak */
+	int32_t  fs_old_time;		/* last time written */
+	int32_t	 fs_old_size;		/* number of blocks in fs */
+	int32_t	 fs_old_dsize;		/* number of data blocks in fs */
+	int32_t	 fs_ncg;		/* number of cylinder groups */
+	int32_t	 fs_bsize;		/* size of basic blocks in fs */
+	int32_t	 fs_fsize;		/* size of frag blocks in fs */
+	int32_t	 fs_frag;		/* number of frags in a block in fs */
+/* these are configuration parameters */
+	int32_t	 fs_minfree;		/* minimum percentage of free blocks */
+	int32_t	 fs_old_rotdelay;	/* num of ms for optimal next block */
+	int32_t	 fs_old_rps;		/* disk revolutions per second */
+/* these fields can be computed from the others */
+	int32_t	 fs_bmask;		/* ``blkoff'' calc of blk offsets */
+	int32_t	 fs_fmask;		/* ``fragoff'' calc of frag offsets */
+	int32_t	 fs_bshift;		/* ``lblkno'' calc of logical blkno */
+	int32_t	 fs_fshift;		/* ``numfrags'' calc number of frags */
+/* these are configuration parameters */
+	int32_t	 fs_maxcontig;		/* max number of contiguous blks */
+	int32_t	 fs_maxbpg;		/* max number of blks per cyl group */
+/* these fields can be computed from the others */
+	int32_t	 fs_fragshift;		/* block to frag shift */
+	int32_t	 fs_fsbtodb;		/* fsbtodb and dbtofsb shift constant */
+	int32_t	 fs_sbsize;		/* actual size of super block */
+	int32_t	 fs_spare1[2];		/* old fs_csmask */
+					/* old fs_csshift */
+	int32_t	 fs_nindir;		/* value of NINDIR */
+	int32_t	 fs_inopb;		/* value of INOPB */
+	int32_t	 fs_old_nspf;		/* value of NSPF */
+/* yet another configuration parameter */
+	int32_t	 fs_optim;		/* optimization preference, see below */
+	int32_t	 fs_old_npsect;		/* # sectors/track including spares */
+	int32_t	 fs_old_interleave;	/* hardware sector interleave */
+	int32_t	 fs_old_trackskew;	/* sector 0 skew, per track */
+	int32_t	 fs_id[2];		/* unique filesystem id */
+/* sizes determined by number of cylinder groups and their sizes */
+	int32_t	 fs_old_csaddr;		/* blk addr of cyl grp summary area */
+	int32_t	 fs_cssize;		/* size of cyl grp summary area */
+	int32_t	 fs_cgsize;		/* cylinder group size */
+	int32_t	 fs_spare2;		/* old fs_ntrak */
+	int32_t	 fs_old_nsect;		/* sectors per track */
+	int32_t  fs_old_spc;		/* sectors per cylinder */
+	int32_t	 fs_old_ncyl;		/* cylinders in filesystem */
+	int32_t	 fs_old_cpg;		/* cylinders per group */
+	int32_t	 fs_ipg;		/* inodes per group */
+	int32_t	 fs_fpg;		/* blocks per group * fs_frag */
+/* this data must be re-computed after crashes */
+	struct	csum fs_old_cstotal;	/* cylinder summary information */
+/* these fields are cleared at mount time */
+	int8_t   fs_fmod;		/* super block modified flag */
+	int8_t   fs_clean;		/* filesystem is clean flag */
+	int8_t 	 fs_ronly;		/* mounted read-only flag */
+	int8_t   fs_old_flags;		/* old FS_ flags */
+	u_char	 fs_fsmnt[MAXMNTLEN];	/* name mounted on */
+/* these fields retain the current block allocation info */
+	int32_t	 fs_cgrotor;		/* last cg searched */
+	void 	*fs_ocsp[NOCSPTRS];	/* padding; was list of fs_cs buffers */
+	u_int8_t *fs_contigdirs;	/* # of contiguously allocated dirs */
+	struct	csum *fs_csp;		/* cg summary info buffer for fs_cs */
+	int32_t	*fs_maxcluster;		/* max cluster in each cyl group */
+	u_int	*fs_active;		/* used by snapshots to track fs */
+	int32_t	 fs_old_cpc;		/* cyl per cycle in postbl */
+	int32_t	 fs_maxbsize;		/* maximum blocking factor permitted */
+	int64_t	 fs_sparecon64[17];	/* old rotation block list head */
+	int64_t	 fs_sblockloc;		/* byte offset of standard superblock */
+	struct	csum_total fs_cstotal;	/* cylinder summary information */
+	ufs_time_t fs_time;		/* last time written */
+	int64_t	 fs_size;		/* number of blocks in fs */
+	int64_t	 fs_dsize;		/* number of data blocks in fs */
+	ufs2_daddr_t fs_csaddr;		/* blk addr of cyl grp summary area */
+	int64_t	 fs_pendingblocks;	/* blocks in process of being freed */
+	int32_t	 fs_pendinginodes;	/* inodes in process of being freed */
+	int32_t	 fs_snapinum[FSMAXSNAP];/* list of snapshot inode numbers */
+	int32_t	 fs_avgfilesize;	/* expected average file size */
+	int32_t	 fs_avgfpdir;		/* expected # of files per directory */
+	int32_t	 fs_save_cgsize;	/* save real cg size to use fs_bsize */
+	int32_t	 fs_sparecon32[26];	/* reserved for future constants */
+	int32_t  fs_flags;		/* see FS_ flags below */
+	int32_t	 fs_contigsumsize;	/* size of cluster summary array */ 
+	int32_t	 fs_maxsymlinklen;	/* max length of an internal symlink */
+	int32_t	 fs_old_inodefmt;	/* format of on-disk inodes */
+	u_int64_t fs_maxfilesize;	/* maximum representable file size */
+	int64_t	 fs_qbmask;		/* ~fs_bmask for use with 64-bit size */
+	int64_t	 fs_qfmask;		/* ~fs_fmask for use with 64-bit size */
+	int32_t	 fs_state;		/* validate fs_clean field */
+	int32_t	 fs_old_postblformat;	/* format of positional layout tables */
+	int32_t	 fs_old_nrpos;		/* number of rotational positions */
+	int32_t	 fs_spare5[2];		/* old fs_postbloff */
+					/* old fs_rotbloff */
+	int32_t	 fs_magic;		/* magic number */
+};
+
+/*
+ * Filesystem identification
+ */
+#define	FS_UFS2_MAGIC	0x19540119	/* UFS2 fast filesystem magic number */
+
+/*
+ * Turn filesystem block numbers into disk block addresses.
+ * This maps filesystem blocks to device size blocks.
+ */
+#define fsbtodb(fs, b)	((b) << (fs)->fs_fsbtodb)
+#define	dbtofsb(fs, b)	((b) >> (fs)->fs_fsbtodb)
+
+/*
+ * Cylinder group macros to locate things in cylinder groups.
+ * They calc filesystem addresses of cylinder group data structures.
+ */
+#define	cgbase(fs, c)	((ufs2_daddr_t)((fs)->fs_fpg * (c)))
+#define	cgimin(fs, c)	(cgstart(fs, c) + (fs)->fs_iblkno)	/* inode blk */
+#define cgstart(fs, c)							\
+       ((fs)->fs_magic == FS_UFS2_MAGIC ? cgbase(fs, c) :		\
+       (cgbase(fs, c) + (fs)->fs_old_cgoffset * ((c) & ~((fs)->fs_old_cgmask))))
+
+/*
+ * Macros for handling inode numbers:
+ *     inode number to filesystem block offset.
+ *     inode number to cylinder group number.
+ *     inode number to filesystem block address.
+ */
+#define	ino_to_cg(fs, x)	((x) / (fs)->fs_ipg)
+#define	ino_to_fsba(fs, x)						\
+	((ufs2_daddr_t)(cgimin(fs, ino_to_cg(fs, x)) +			\
+	    (blkstofrags((fs), (((x) % (fs)->fs_ipg) / INOPB(fs))))))
+#define	ino_to_fsbo(fs, x)	((x) % INOPB(fs))
+
+/*
+ * The following macros optimize certain frequently calculated
+ * quantities by using shifts and masks in place of divisions
+ * modulos and multiplications.
+ */
+#define blkoff(fs, loc)		/* calculates (loc % fs->fs_bsize) */ \
+	((loc) & (fs)->fs_qbmask)
+
+/* Use this only when `blk' is known to be small, e.g., < NDADDR. */
+#define smalllblktosize(fs, blk)    /* calculates (blk * fs->fs_bsize) */ \
+	((blk) << (fs)->fs_bshift)
+
+
+#define lblkno(fs, loc)		/* calculates (loc / fs->fs_bsize) */ \
+	((loc) >> (fs)->fs_bshift)
+
+#define fragroundup(fs, size)	/* calculates roundup(size, fs->fs_fsize) */ \
+	(((size) + (fs)->fs_qfmask) & (fs)->fs_fmask)
+
+#define fragstoblks(fs, frags)	/* calculates (frags / fs->fs_frag) */ \
+	((frags) >> (fs)->fs_fragshift)
+#define blkstofrags(fs, blks)	/* calculates (blks * fs->fs_frag) */ \
+	((blks) << (fs)->fs_fragshift)
+#define fragnum(fs, fsb)	/* calculates (fsb % fs->fs_frag) */ \
+	((fsb) & ((fs)->fs_frag - 1))
+#define blknum(fs, fsb)		/* calculates rounddown(fsb, fs->fs_frag) */ \
+	((fsb) &~ ((fs)->fs_frag - 1))
+
+/*
+ * Determining the size of a file block in the filesystem.
+ */
+#define blksize(fs, ip, lbn) \
+	(((lbn) >= NDADDR || (ip)->i_size >= smalllblktosize(fs, (lbn) + 1)) \
+	    ? (fs)->fs_bsize \
+	    : (fragroundup(fs, blkoff(fs, (ip)->i_size))))
+#define sblksize(fs, size, lbn) \
+	(((lbn) >= NDADDR || (size) >= ((lbn) + 1) << (fs)->fs_bshift) \
+	  ? (fs)->fs_bsize \
+	  : (fragroundup(fs, blkoff(fs, (size)))))
+
+
+/*
+ * Number of inodes in a secondary storage block/fragment.
+ */
+#define	INOPB(fs)	((fs)->fs_inopb)
+#define	INOPF(fs)	((fs)->fs_inopb >> (fs)->fs_fragshift)
+
+/*
+ * Number of indirects in a filesystem block.
+ */
+#define	NINDIR(fs)	((fs)->fs_nindir)
+
+#endif /* _GRUB_UFS2_H_ */
--- util/grub-install.in.orig	Wed Feb 11 00:31:26 2004
+++ util/grub-install.in	Wed Feb 11 00:31:58 2004
@@ -105,11 +105,11 @@
 	tmp_disk=`echo "$1" | sed 's%\([sh]d[0-9]*\).*%\1%'`
 	tmp_part=`echo "$1" | sed "s%$tmp_disk%%"` ;;
     freebsd*)
-	tmp_disk=`echo "$1" | sed 's%r\{0,1\}\([saw]d[0-9]*\).*$%r\1%' \
-			    | sed 's%r\{0,1\}\(da[0-9]*\).*$%r\1%'`
+	tmp_disk=`echo "$1" | sed 's%\([saw]d[0-9]*\).*$%\1%' \
+			    | sed 's%\(da[0-9]*\).*$%\1%'`
 	tmp_part=`echo "$1" \
-	    | sed "s%.*/r\{0,1\}[saw]d[0-9]\(s[0-9]*[a-h]\)%\1%" \
-       	    | sed "s%.*/r\{0,1\}da[0-9]\(s[0-9]*[a-h]\)%\1%"`
+	    | sed "s%.*/[saw]d[0-9]\(s[0-9]*[a-h]\)%\1%" \
+       	    | sed "s%.*/da[0-9]\(s[0-9]*[a-h]\)%\1%"`
 	;;
     netbsd*)
 	tmp_disk=`echo "$1" | sed 's%r\{0,1\}\([sw]d[0-9]*\).*$%r\1d%' \
