pupa-devel
[Top][All Lists]
Advanced

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

Normal mode patch


From: Marco Gerards
Subject: Normal mode patch
Date: 28 Dec 2003 21:36:18 +0100
User-agent: Gnus/5.09 (Gnus v5.9.0) Emacs/21.2

Hi,

This patch adds a lot of features to PUPA, especially to normal mode.
For details you will have to look at the changelog entry, but here is
a small and incomplete summary of features and changes:

- The new argument parser for normal mode.
- The new commands ls, boot, cmp and cat.
- Environment variable support.
- Improved splitting of a command line into arguments.  It deals with
  inserting variables (using $foo or ${foo}), escaping and quoting.
- pupa_vsprintf can better format variable output and can print doubles.
- All warnings were fixed.
- Fixed some minor bugs involving error detection.

This patch is quite big, if you would like more features I think I can
better add in a later patch (I think of the env. variable `root' for
example).

I've also added the set and unset commands although you didn't
answered my questions about this yet.  Just have a look what is
changed and if you like it or not.

I haven't implemented the wildcard support yet, I think it's better to
wait with this until we know what the scripting language will look
like.

I will have a look at the portability of PUPA tomorrow and how
difficult it will be to port it to the apple.  Is there anything
really crucial missing on the pc version of PUPA that I should work on
first?

Thanks,
Marco

2003-12-28  Marco Gerards  <address@hidden>

        * commands/boot.c: New file.
        * commands/cat.c: Likewise.
        * commands/cmp.c: Likewise.
        * commands/ls.c: Likewise.
        * commands/terminal.c: Likewise.
        * normal/command.c: Include <pupa/env.h> and <pupa/dl.h>.
        (pupa_register_command): Changed interface to match the new
        argument parser.
        (pupa_command_execute): Changed (almost rewritten) so it uses
        pupa_split_command.  Added support for setting variables using the
        syntax `foo=bar'.
        (rescue_command): Changed to work with the new argument parser.
        (terminal_command): Moved from here to commands/terminal.c.
        (set_command): New function.
        (unset_command): New function.
        (insmod_command): New function.
        (rmmod_command): New function.
        (lsmod_command): New function.
        (pupa_command_init): Don't initialize the command terminal
        anymore.  Initialize the commands set, unset, insmod, rmmod and
        lsmod.
        * conf/i386-pc.rmk (kernel_img_SOURCES): Add kern/env.c.
        (kernel_img_HEADERS): Add arg.h and env.h.
        (pupa_mkimage_LDFLAGS): Add kern/env.c.
        (pupa_emu_SOURCES): Add kern/env.c, commands/ls.c,
        commands/terminal.c commands/boot.c commands/cmp.c commands/cat.c,
        normal/arg.c.
        (pkgdata_MODULES): Add ls.mod, boot.mod, cmp.mod, cat.mod and
        terminal.mod.
        (normal_mod_SOURCES): Add normal/arg.c and normal/arg.c.
        (boot_mod_SOURCES): New variable.
        (terminal_mod_SOURCES): Likewise.
        (ls_mod_SOURCES): Likewise.
        (cmp_mod_SOURCES): Likewise.
        (cat_mod_SOURCES): Likewise.

        * normal/arg.c: New file.
        * pupa/kern/env.c: New file.
        * include/pupa/arg.h: New file.
        * include/pupa/env.h: Likewise.
        * font/manager.c (font_command): Changed to match argument parsing
        interface changes.
        (PUPA_MOD_INIT): Likewise.
        * hello/hello.c (pupa_cmd_hello): Likewise.
        (PUPA_MOD_INIT): Likewise.
        * include/pupa/disk.h: Include <pupa/device.h>.
        (pupa_print_partinfo): New prototype.
        * include/pupa/dl.h (pupa_dl_set_prefix): Prototype removed.
        (pupa_dl_get_prefix): Likewise.
        * include/pupa/misc.h: Include <pupa/err.h>.
        (pupa_isgraph): New prototype.
        (pupa_isdigit): Likewise.
        (pupa_split_cmdline): Likewise.
        * include/pupa/normal.h: Include <pupa/arg.h>.
        (pupa_command): Changed the prototype of the member `func' to
        match the argument parsing interface.  Added member `options'.
        (pupa_register_command): Updated to match function.
        (pupa_arg_parse): New prototype.
        (pupa_hello_init) [PUPA_UTIL]: New prototype.
        (pupa_hello_fini) [PUPA_UTIL]: Likewise.
        (pupa_ls_init) [PUPA_UTIL]: Likewise.
        (pupa_ls_fini) [PUPA_UTIL]: Likewise.
        (pupa_cat_init) [PUPA_UTIL]: Likewise.
        (pupa_cat_fini) [PUPA_UTIL]: Likewise.
        (pupa_boot_init) [PUPA_UTIL]: Likewise.
        (pupa_boot_fini) [PUPA_UTIL]: Likewise.
        (pupa_cmp_init) [PUPA_UTIL]: Likewise.
        (pupa_cmp_fini) [PUPA_UTIL]: Likewise.
        (pupa_terminal_init) [PUPA_UTIL]: Likewise.
        (pupa_terminal_fini) [PUPA_UTIL]: Likewise.
        * kern/disk.c: Include <pupa/file.h>.
        (pupa_print_partinfo): New function.
        * kern/dl.c: Include <pupa/env.h>.
        (pupa_dl_dir): Variable removed.
        (pupa_dl_load): Use the environment variable `prefix' instead of
        the variable pupa_dl_dir.
        (pupa_dl_set_prefix): Function removed.
        (pupa_dl_get_prefix): Likewise.
        * kern/i386/pc/init.c: Include <pupa/env.h>.
        (pupa_machine_init): Use the environment variable `prefix' instead of
        using pupa_dl_set_prefix to set the prefix.
        * kern/main.c: Include <pupa/env.h>.
        (pupa_set_root_dev): Use the environment variable `prefix' instead of
        using pupa_dl_get_prefix to get the prefix.
        * kern/misc.c: Include <pupa/env.h>.
        (pupa_isdigit): New function.
        (pupa_isgraph): Likewise.
        (pupa_ftoa): Likewise.
        (pupa_vsprintf): Added support for printing values of the type
        `double'.  Make it possible to format variable output when using
        formatting like `%1.2%f'.
        (pupa_split_cmdline): New function.
        * kern/rescue.c: Include <pupa/env.h>.
        (next_word): Removed function.
        (pupa_rescue_cmd_prefix): Likewise.
        (pupa_rescue_cmd_set): New function.
        (pupa_rescue_cmd_unset): New function.
        (pupa_enter_rescue_mode): Use the `pupa_split_cmdline' function to
        split the command line instead of splitting it here.  Added
        support for setting variables using the syntax `foo=bar'.  Don't
        initialize the prefix command anymore.  Initialized the set and
        unset commands.
        * normal/cmdline.c: Include <pupa/env.h>.
        (pupa_tab_complete): Added prototypes for print_simple_completion,
        print_partition_completion, add_completion, iterate_commands,
        iterate_dev, iterate_part and iterate_dir. Moved code to print
        partition information from here to kern/disk.c.
        (pupa_cmdline_run): Don't check if the funtion exists anymore.
        * normal/main.c: Include <pupa/env.h>.
        (pupa_rescue_cmd_normal): Use the environment variable `prefix'
        instead of using pupa_dl_get_prefix to get the prefix.
        * term/i386/pc/vga.c: Include <pupa/arg.h>.
        (check_vga_mem): Cast pointers to `void *' to silence a gcc
        warning.
        (pupa_vga_putchar) [! DEBUG_VGA]: Removed for this case.
        (pupa_vga_setcolor): Declare unused variables with `__attribute__
        ((unused))' to silence a gcc warning.
        (pupa_vga_setcolor): Likewise.
        (debug_command): Changed to match argument parsing
        interface changes.
        * util/pupa-emu.c: Include <pupa/env.h>.
        (options): Added 0's for unused fields to silence a gcc warning.
        (argp): Likewise.
        (main): Use the environment variable `prefix' instead of using
        pupa_dl_set_prefix to set the prefix.  Initialize the commands ls,
        boot, cmp, cat and terminal.  Finish the commands boot, cmp, cat
        and terminal.

        * util/i386/pc/getroot.c: Include <pupa/i386/pc/util/biosdisk.h>.
        * util/misc.c: Include <malloc.h>.
        (pupa_malloc): Rewritten so errors are correctly reported.
        (pupa_realloc): Likewise.
        (pupa_memalign): Likewise.
        (pupa_mm_init_region): Declare unused variables with
        `__attribute__ ((unused))' to silence a gcc warning.
        * normal/i386/setjmp.S: Remove tab at the end of the file to
        silence a gcc warning.
        * loader/i386/pc/linux.c (pupa_rescue_cmd_initrd): Declare unused
        variables with `__attribute__ ((unused))' to silence a gcc
        warning.
        * loader/i386/pc/multiboot.c (pupa_multiboot_unload): Make the
        local variable i unsigned to silence a gcc warning.

        * kern/term.c: Include <pupa/misc.h>.
        (pupa_more_lines): New variable.
        (pupa_more): Likewise.
        (pupa_putcode): When the pager is active pause at the end of every
        screen.
        (pupa_set_mode): New function.
        * include/pupa/term.h (pupa_set_more): New prototype.

diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/commands/boot.c ./commands/boot.c
--- /home/marco/hurd/hurdnew/pupa/pupa/commands/boot.c  1970-01-01 
01:00:00.000000000 +0100
+++ ./commands/boot.c   2003-12-28 19:40:28.000000000 +0100
@@ -0,0 +1,66 @@
+/* boot.c - command to boot an operating system */
+/*
+ *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2002  Yoshinori K. Okuji <address@hidden>
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
+ *
+ *  PUPA 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 PUPA; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <pupa/normal.h>
+#include <pupa/dl.h>
+#include <pupa/arg.h>
+#include <pupa/misc.h>
+#include <pupa/loader.h>
+
+static pupa_err_t
+pupa_cmd_boot (struct pupa_arg_list *state __attribute__ ((unused)),
+              int argc, char **args __attribute__ ((unused)))
+{
+  if (argc)
+    return pupa_error (PUPA_ERR_BAD_ARGUMENT, "too many arguments");
+  
+  pupa_loader_boot ();
+  
+  return 0;
+}
+
+
+#ifdef PUPA_UTIL
+void
+pupa_boot_init (void)
+{
+  pupa_register_command ("boot", pupa_cmd_boot, PUPA_COMMAND_FLAG_BOTH,
+                        "boot", "Boot an operating system", 0);
+}
+
+void
+pupa_boot_fini (void)
+{
+  pupa_unregister_command ("boot");
+}
+#else /* ! PUPA_UTIL */
+PUPA_MOD_INIT
+{
+  (void)mod;                   /* To stop warning. */
+  pupa_register_command ("boot", pupa_cmd_boot, PUPA_COMMAND_FLAG_BOTH,
+                        "boot", "Boot an operating system", 0);
+}
+
+PUPA_MOD_FINI
+{
+  pupa_unregister_command ("boot");
+}
+#endif /* ! PUPA_UTIL */
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/commands/cat.c ./commands/cat.c
--- /home/marco/hurd/hurdnew/pupa/pupa/commands/cat.c   1970-01-01 
01:00:00.000000000 +0100
+++ ./commands/cat.c    2003-12-25 22:05:52.000000000 +0100
@@ -0,0 +1,97 @@
+/* cat.c - command to show the contents of a file  */
+/*
+ *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2002,2003  Yoshinori K. Okuji <address@hidden>
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
+ *
+ *  PUPA 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 PUPA; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <pupa/normal.h>
+#include <pupa/dl.h>
+#include <pupa/arg.h>
+#include <pupa/file.h>
+#include <pupa/disk.h>
+#include <pupa/term.h>
+#include <pupa/misc.h>
+
+static pupa_err_t
+pupa_cmd_cat (struct pupa_arg_list *state __attribute__ ((unused)),
+             int argc, char **args)
+{
+  pupa_file_t file;
+  char buf[PUPA_DISK_SECTOR_SIZE];
+  pupa_ssize_t size;
+
+  if (argc != 1)
+    return pupa_error (PUPA_ERR_BAD_ARGUMENT, "file name required");
+
+  file = pupa_file_open (args[0]);
+  if (! file)
+    return 0;
+  
+  while ((size = pupa_file_read (file, buf, sizeof (buf))) > 0)
+    {
+      int i;
+      
+      for (i = 0; i < size; i++)
+       {
+         unsigned char c = buf[i];
+         
+         if (pupa_isprint (c) || pupa_isspace (c))
+           pupa_putchar (c);
+         else
+           {
+             pupa_setcolorstate (PUPA_TERM_COLOR_HIGHLIGHT);
+             pupa_printf ("<%x>", (int) c);
+             pupa_setcolorstate (PUPA_TERM_COLOR_STANDARD);
+           }
+       }
+    }
+
+  pupa_putchar ('\n');
+  pupa_refresh ();
+  pupa_file_close (file);
+  
+  return 0;
+}
+
+
+#ifdef PUPA_UTIL
+void
+pupa_cat_init (void)
+{
+  pupa_register_command ("cat", pupa_cmd_cat, PUPA_COMMAND_FLAG_BOTH,
+                        "cat FILE", "Show the contents of a file", 0);
+}
+
+void
+pupa_cat_fini (void)
+{
+  pupa_unregister_command ("cat");
+}
+#else /* ! PUPA_UTIL */
+PUPA_MOD_INIT
+{
+  (void)mod;                   /* To stop warning. */
+  pupa_register_command ("cat", pupa_cmd_cat, PUPA_COMMAND_FLAG_BOTH,
+                        "cat FILE", "Show the contents of a file", 0);
+}
+
+PUPA_MOD_FINI
+{
+  pupa_unregister_command ("cat");
+}
+#endif /* ! PUPA_UTIL */
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/commands/cmp.c ./commands/cmp.c
--- /home/marco/hurd/hurdnew/pupa/pupa/commands/cmp.c   1970-01-01 
01:00:00.000000000 +0100
+++ ./commands/cmp.c    2003-12-28 20:21:01.000000000 +0100
@@ -0,0 +1,124 @@
+/* cmd.c - command to cmp an operating system */
+/*
+ *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
+ *
+ *  PUPA 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 PUPA; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <pupa/normal.h>
+#include <pupa/dl.h>
+#include <pupa/arg.h>
+#include <pupa/misc.h>
+#include <pupa/file.h>
+
+static pupa_err_t
+pupa_cmd_cmp (struct pupa_arg_list *state __attribute__ ((unused)),
+             int argc, char **args)
+{
+  pupa_file_t file1;
+  pupa_file_t file2;
+
+  if (argc != 2)
+    return pupa_error (PUPA_ERR_BAD_ARGUMENT, "two arguments required");
+
+  pupa_printf ("Compare `%s' and `%s':\n", args[0],
+              args[1]);
+
+  file1 = pupa_file_open (args[0]);
+  if (! file1)
+    return pupa_errno;
+
+  file2 = pupa_file_open (args[1]);
+  if (! file2)
+    {
+      pupa_file_close (file2);
+      return pupa_errno;
+    }
+
+  if (pupa_file_size (file1) != pupa_file_size (file2))
+    pupa_printf ("Differ in size: %d [%s], %d [%s]\n", 
+                pupa_file_size (file1), args[0], 
+                pupa_file_size (file2), args[1]);
+  
+  else
+    {
+      char buf1[512];
+      char buf2[512];
+      pupa_ssize_t rd1, rd2;
+      pupa_uint32_t pos = 0;
+     
+      do
+       {
+         int i;
+         rd1 = pupa_file_read (file1, buf1, 512);
+         rd2 = pupa_file_read (file2, buf2, 512);
+
+         if (rd1 != rd2)
+           return 0;
+
+         for (i = 0; i < 512; i++)
+           {
+             if (buf1[i] != buf2[i])
+               {
+                 pupa_printf ("Differ at the offset %d: 0x%x [%s], 0x%x 
[%s]\n",
+                              i + pos, buf1[i], args[0],
+                              buf2[i], args[1]);
+
+                 pupa_file_close (file1);
+                 pupa_file_close (file2);
+                 return 0;
+               }
+           }
+         pos += 512;
+         
+       } while (rd2);
+    }
+
+  pupa_file_close (file1);
+  pupa_file_close (file2);
+
+  pupa_printf ("The files are identical.\n");
+
+  return 0;
+}
+
+
+#ifdef PUPA_UTIL
+void
+pupa_cmp_init (void)
+{
+  pupa_register_command ("cmp", pupa_cmd_cmp, PUPA_COMMAND_FLAG_BOTH,
+                        "cmp FILE1 FILE2", "Compare two files", 0);
+}
+
+void
+pupa_cmp_fini (void)
+{
+  pupa_unregister_command ("cmp");
+}
+#else /* ! PUPA_UTIL */
+PUPA_MOD_INIT
+{
+  (void)mod;                   /* To stop warning. */
+  pupa_register_command ("cmp", pupa_cmd_cmp, PUPA_COMMAND_FLAG_BOTH,
+                        "cmp FILE1 FILE2", "Compare two files", 0);
+}
+
+PUPA_MOD_FINI
+{
+  pupa_unregister_command ("cmp");
+}
+#endif /* ! PUPA_UTIL */
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/commands/ls.c ./commands/ls.c
--- /home/marco/hurd/hurdnew/pupa/pupa/commands/ls.c    1970-01-01 
01:00:00.000000000 +0100
+++ ./commands/ls.c     2003-12-28 16:53:00.000000000 +0100
@@ -0,0 +1,263 @@
+/* ls.c - command to list files and devices */
+/*
+ *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2002,2003  Yoshinori K. Okuji <address@hidden>
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
+ *
+ *  PUPA 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 PUPA; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <pupa/types.h>
+#include <pupa/misc.h>
+#include <pupa/mm.h>
+#include <pupa/err.h>
+#include <pupa/dl.h>
+#include <pupa/normal.h>
+#include <pupa/arg.h>
+#include <pupa/disk.h>
+#include <pupa/device.h>
+#include <pupa/term.h>
+#include <pupa/machine/partition.h>
+#include <pupa/file.h>
+
+static const struct pupa_arg_option options[] =
+  {
+    {"long", 'l', 0, "Show a long list with more detailed information", 0, 0},
+    {"human-readable", 'h', 0, "Print sizes in a human readable format", 0, 0},
+    {"all", 'a', 0, "List all files", 0, 0},
+    {0, 0, 0, 0, 0, 0}
+  };
+
+static const char pupa_human_sizes[] = {' ', 'K', 'M', 'G', 'T'};
+
+static pupa_err_t
+pupa_ls_list_disks (int longlist)
+{
+  auto int pupa_ls_print_disks (const char *name);
+  int pupa_ls_print_disks (const char *name)
+    {
+      pupa_device_t dev;
+      auto int print_partition (const pupa_partition_t p);
+      
+      int print_partition (const pupa_partition_t p)
+       {
+         char *pname = pupa_partition_get_name (p);
+
+         if (pname)
+           {
+             if (longlist)
+               pupa_print_partinfo (dev, pname);
+             else
+               pupa_printf ("(%s,%s) ", name, pname);
+           }
+
+         return 0;
+       }
+      
+      dev = pupa_device_open (name);
+      pupa_errno = PUPA_ERR_NONE;
+      
+      if (dev)
+       {
+         if (longlist)
+           pupa_printf ("Disk: %s\n", name);
+         else
+           pupa_printf ("(%s) ", name);
+
+         if (dev->disk && dev->disk->has_partitions)
+           {
+             pupa_partition_iterate (dev->disk, print_partition);
+             pupa_errno = PUPA_ERR_NONE;
+           }
+
+         pupa_device_close (dev);
+       }
+  
+      return 0;
+    }
+  
+  pupa_disk_dev_iterate (pupa_ls_print_disks);
+  pupa_putchar ('\n');
+  pupa_refresh ();
+
+ 
+  return 0;
+}
+
+static pupa_err_t
+pupa_ls_list_files (const char *dirname, int longlist, int all, int human)
+{
+  char *device_name;
+  pupa_fs_t fs;
+  char *path;
+  pupa_device_t dev;
+
+  static int print_files (const char *filename, int dir)
+    {
+      if (all || filename[0] != '.')
+       pupa_printf ("%s%s ", filename, dir ? "/" : "");
+      
+      return 0;
+    }
+     
+  static int print_files_long (const char *filename, int dir)
+    {
+      char pathname[pupa_strlen (dirname) + pupa_strlen (filename) + 1];
+
+      if ((! all) && (filename[0] == '.'))
+       return 0;
+
+      if (! dir)
+       {
+         pupa_file_t file;
+         
+         if (dirname[pupa_strlen (dirname) - 1] == '/')
+           pupa_sprintf (pathname, "%s%s", dirname, filename);
+         else
+           pupa_sprintf (pathname, "%s/%s", dirname, filename);
+
+         /* XXX: For ext2fs symlinks are detected as files while they
+            should be reported as directories.  */
+         file = pupa_file_open (pathname);
+         if (! file)
+           {
+             pupa_errno = 0;
+             return 0;
+           }
+
+         if (! human)
+           pupa_printf ("%-12d", file->size);
+         else
+           {
+             float fsize = file->size;
+             int fsz = file->size;
+             int units = 0;
+             char buf[20];
+             
+             while (fsz / 1024)
+               {
+                 fsize /= 1024;
+                 fsz /= 1024;
+                 units++;
+               }
+
+             if (units)
+               {
+                 pupa_sprintf (buf, "%0.2f%c", fsize, pupa_human_sizes[units]);
+                 pupa_printf ("%-12s", buf);
+               }
+             else
+               pupa_printf ("%-12d", file->size);
+             
+           }
+         (fs->close) (file);
+       }
+      else
+       pupa_printf ("%-12s", "DIR");
+
+      pupa_printf ("%s%s\n", filename, dir ? "/" : "");
+
+      return 0;
+    }
+
+  device_name = pupa_file_get_device_name (dirname);
+  dev = pupa_device_open (device_name);
+  if (! dev)
+    goto fail;
+
+  fs = pupa_fs_probe (dev);
+  path = pupa_strchr (dirname, '/');
+
+  if (! path && ! device_name)
+    {
+      pupa_error (PUPA_ERR_BAD_ARGUMENT, "invalid argument");
+      goto fail;
+    }
+      
+  if (! path)
+    {
+      if (pupa_errno == PUPA_ERR_UNKNOWN_FS)
+       pupa_errno = PUPA_ERR_NONE;
+         
+      pupa_printf ("(%s): Filesystem is %s.\n",
+                  device_name, fs ? fs->name : "unknown");
+    }
+  else if (fs)
+    {
+      if (longlist)
+       (fs->dir) (dev, path, print_files_long);
+      else
+       (fs->dir) (dev, path, print_files);
+      pupa_putchar ('\n');
+      pupa_refresh ();
+    }
+
+ fail:
+  if (dev)
+    pupa_device_close (dev);
+      
+  pupa_free (device_name);
+
+  return 0;
+}
+
+static pupa_err_t
+pupa_cmd_ls (struct pupa_arg_list *state, int argc, char **args)
+{
+  static int pupa_ls_print_files (const char *filename, int dir)
+    {
+      if (state[2].set/*all*/ || filename[0] != '.')
+       pupa_printf ("%s%s ", filename, dir ? "/" : "");
+      
+      return 0;
+    }
+
+  if (argc == 0)
+  pupa_ls_list_disks (state[0].set);
+  else
+    pupa_ls_list_files (args[0], state[0].set, state[2].set,
+                       state[1].set);
+
+  return 0;
+}
+
+#ifdef PUPA_UTIL
+void
+pupa_ls_init (void)
+{
+  pupa_register_command ("ls", pupa_cmd_ls, PUPA_COMMAND_FLAG_BOTH,
+                        "ls [OPTIONS...] [DIR]",
+                        "List devices and files", options);
+}
+
+void
+pupa_ls_fini (void)
+{
+  pupa_unregister_command ("ls");
+}
+#else /* ! PUPA_UTIL */
+PUPA_MOD_INIT
+{
+  (void)mod;                   /* To stop warning. */
+  pupa_register_command ("ls", pupa_cmd_ls, PUPA_COMMAND_FLAG_BOTH,
+                        "ls [OPTIONS...] [DIR]",
+                        "List devices and files", options);
+}
+
+PUPA_MOD_FINI
+{
+  pupa_unregister_command ("ls");
+}
+#endif /* ! PUPA_UTIL */
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/commands/terminal.c 
./commands/terminal.c
--- /home/marco/hurd/hurdnew/pupa/pupa/commands/terminal.c      1970-01-01 
01:00:00.000000000 +0100
+++ ./commands/terminal.c       2003-12-28 19:58:40.000000000 +0100
@@ -0,0 +1,100 @@
+/* terminal.c - command to show and select a terminal */
+/*
+ *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2002  Yoshinori K. Okuji <address@hidden>
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
+ *
+ *  PUPA 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 PUPA; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <pupa/normal.h>
+#include <pupa/dl.h>
+#include <pupa/arg.h>
+#include <pupa/misc.h>
+#include <pupa/term.h>
+
+static pupa_err_t
+pupa_cmd_terminal (struct pupa_arg_list *state __attribute__ ((unused)),
+                  int argc, char **args)
+{
+  pupa_term_t term = 0;
+  
+  auto int print_terminal (pupa_term_t);
+  auto int find_terminal (pupa_term_t);
+  
+  int print_terminal (pupa_term_t t)
+    {
+      pupa_printf (" %s", t->name);
+      return 0;
+    }
+
+  int find_terminal (pupa_term_t t)
+    {
+      if (pupa_strcmp (t->name, args[0]) == 0)
+       {
+         term = t;
+         return 1;
+       }
+
+      return 0;
+    }
+  
+  if (argc == 0)
+    {
+      pupa_printf ("Available terminal(s):");
+      pupa_term_iterate (print_terminal);
+      pupa_putchar ('\n');
+      
+      pupa_printf ("Current terminal: %s\n", pupa_term_get_current ()->name);
+    }
+  else
+    {
+      pupa_term_iterate (find_terminal);
+      if (! term)
+       return pupa_error (PUPA_ERR_BAD_ARGUMENT, "no such terminal");
+
+      pupa_term_set_current (term);
+    }
+
+  return PUPA_ERR_NONE;
+}
+
+
+#ifdef PUPA_UTIL
+void
+pupa_terminal_init (void)
+{
+  pupa_register_command ("terminal", pupa_cmd_terminal, PUPA_COMMAND_FLAG_BOTH,
+                        "terminal [TERM...]", "Select a terminal.", 0);
+}
+
+void
+pupa_terminal_fini (void)
+{
+  pupa_unregister_command ("terminal");
+}
+#else /* ! PUPA_UTIL */
+PUPA_MOD_INIT
+{
+  (void)mod;                   /* To stop warning. */
+  pupa_register_command ("terminal", pupa_cmd_terminal, PUPA_COMMAND_FLAG_BOTH,
+                        "terminal [TERM...]", "Select a terminal.", 0);
+}
+
+PUPA_MOD_FINI
+{
+  pupa_unregister_command ("terminal");
+}
+#endif /* ! PUPA_UTIL */
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/conf/i386-pc.mk ./conf/i386-pc.mk
--- /home/marco/hurd/hurdnew/pupa/pupa/conf/i386-pc.mk  2003-11-19 
20:29:06.000000000 +0100
+++ ./conf/i386-pc.mk   2003-12-28 19:52:26.000000000 +0100
@@ -55,16 +55,16 @@ kernel_img_SOURCES = kern/i386/pc/startu
        kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \
        kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \
        kern/i386/dl.c kern/i386/pc/init.c disk/i386/pc/partition.c \
-       disk/i386/pc/biosdisk.c \
+       kern/env.c disk/i386/pc/biosdisk.c \
        term/i386/pc/console.c \
        symlist.c
-CLEANFILES += kernel.img kernel.exec kernel_img-kern_i386_pc_startup.o 
kernel_img-kern_main.o kernel_img-kern_device.o kernel_img-kern_disk.o 
kernel_img-kern_dl.o kernel_img-kern_file.o kernel_img-kern_fs.o 
kernel_img-kern_err.o kernel_img-kern_misc.o kernel_img-kern_mm.o 
kernel_img-kern_loader.o kernel_img-kern_rescue.o kernel_img-kern_term.o 
kernel_img-kern_i386_dl.o kernel_img-kern_i386_pc_init.o 
kernel_img-disk_i386_pc_partition.o kernel_img-disk_i386_pc_biosdisk.o 
kernel_img-term_i386_pc_console.o kernel_img-symlist.o
-MOSTLYCLEANFILES += kernel_img-kern_i386_pc_startup.d kernel_img-kern_main.d 
kernel_img-kern_device.d kernel_img-kern_disk.d kernel_img-kern_dl.d 
kernel_img-kern_file.d kernel_img-kern_fs.d kernel_img-kern_err.d 
kernel_img-kern_misc.d kernel_img-kern_mm.d kernel_img-kern_loader.d 
kernel_img-kern_rescue.d kernel_img-kern_term.d kernel_img-kern_i386_dl.d 
kernel_img-kern_i386_pc_init.d kernel_img-disk_i386_pc_partition.d 
kernel_img-disk_i386_pc_biosdisk.d kernel_img-term_i386_pc_console.d 
kernel_img-symlist.d
+CLEANFILES += kernel.img kernel.exec kernel_img-kern_i386_pc_startup.o 
kernel_img-kern_main.o kernel_img-kern_device.o kernel_img-kern_disk.o 
kernel_img-kern_dl.o kernel_img-kern_file.o kernel_img-kern_fs.o 
kernel_img-kern_err.o kernel_img-kern_misc.o kernel_img-kern_mm.o 
kernel_img-kern_loader.o kernel_img-kern_rescue.o kernel_img-kern_term.o 
kernel_img-kern_i386_dl.o kernel_img-kern_i386_pc_init.o 
kernel_img-disk_i386_pc_partition.o kernel_img-kern_env.o 
kernel_img-disk_i386_pc_biosdisk.o kernel_img-term_i386_pc_console.o 
kernel_img-symlist.o
+MOSTLYCLEANFILES += kernel_img-kern_i386_pc_startup.d kernel_img-kern_main.d 
kernel_img-kern_device.d kernel_img-kern_disk.d kernel_img-kern_dl.d 
kernel_img-kern_file.d kernel_img-kern_fs.d kernel_img-kern_err.d 
kernel_img-kern_misc.d kernel_img-kern_mm.d kernel_img-kern_loader.d 
kernel_img-kern_rescue.d kernel_img-kern_term.d kernel_img-kern_i386_dl.d 
kernel_img-kern_i386_pc_init.d kernel_img-disk_i386_pc_partition.d 
kernel_img-kern_env.d kernel_img-disk_i386_pc_biosdisk.d 
kernel_img-term_i386_pc_console.d kernel_img-symlist.d
 
 kernel.img: kernel.exec
        $(OBJCOPY) -O binary -R .note -R .comment $< $@
 
-kernel.exec: kernel_img-kern_i386_pc_startup.o kernel_img-kern_main.o 
kernel_img-kern_device.o kernel_img-kern_disk.o kernel_img-kern_dl.o 
kernel_img-kern_file.o kernel_img-kern_fs.o kernel_img-kern_err.o 
kernel_img-kern_misc.o kernel_img-kern_mm.o kernel_img-kern_loader.o 
kernel_img-kern_rescue.o kernel_img-kern_term.o kernel_img-kern_i386_dl.o 
kernel_img-kern_i386_pc_init.o kernel_img-disk_i386_pc_partition.o 
kernel_img-disk_i386_pc_biosdisk.o kernel_img-term_i386_pc_console.o 
kernel_img-symlist.o
+kernel.exec: kernel_img-kern_i386_pc_startup.o kernel_img-kern_main.o 
kernel_img-kern_device.o kernel_img-kern_disk.o kernel_img-kern_dl.o 
kernel_img-kern_file.o kernel_img-kern_fs.o kernel_img-kern_err.o 
kernel_img-kern_misc.o kernel_img-kern_mm.o kernel_img-kern_loader.o 
kernel_img-kern_rescue.o kernel_img-kern_term.o kernel_img-kern_i386_dl.o 
kernel_img-kern_i386_pc_init.o kernel_img-disk_i386_pc_partition.o 
kernel_img-kern_env.o kernel_img-disk_i386_pc_biosdisk.o 
kernel_img-term_i386_pc_console.o kernel_img-symlist.o
        $(CC) -o $@ $^ $(LDFLAGS) $(kernel_img_LDFLAGS)
 
 kernel_img-kern_i386_pc_startup.o: kern/i386/pc/startup.S
@@ -195,6 +195,14 @@ kernel_img-disk_i386_pc_partition.d: dis
 
 -include kernel_img-disk_i386_pc_partition.d
 
+kernel_img-kern_env.o: kern/env.c
+       $(CC) -Ikern -I$(srcdir)/kern $(CPPFLAGS)  $(CFLAGS) 
$(kernel_img_CFLAGS) -c -o $@ $<
+
+kernel_img-kern_env.d: kern/env.c
+       set -e;           $(CC) -Ikern -I$(srcdir)/kern $(CPPFLAGS)  $(CFLAGS) 
$(kernel_img_CFLAGS) -M $<         | sed 's,env\.o[ :]*,kernel_img-kern_env.o 
$@ : ,g' > $@;       [ -s $@ ] || rm -f $@
+
+-include kernel_img-kern_env.d
+
 kernel_img-disk_i386_pc_biosdisk.o: disk/i386/pc/biosdisk.c
        $(CC) -Idisk/i386/pc -I$(srcdir)/disk/i386/pc $(CPPFLAGS)  $(CFLAGS) 
$(kernel_img_CFLAGS) -c -o $@ $<
 
@@ -223,7 +231,7 @@ kernel_img_HEADERS = boot.h device.h dis
        file.h fs.h kernel.h loader.h misc.h mm.h net.h rescue.h symbol.h \
        term.h types.h machine/biosdisk.h machine/boot.h \
        machine/console.h machine/init.h machine/memory.h \
-       machine/loader.h machine/partition.h machine/vga.h
+       machine/loader.h machine/partition.h machine/vga.h arg.h env.h
 kernel_img_CFLAGS = $(COMMON_CFLAGS)
 kernel_img_ASFLAGS = $(COMMON_ASFLAGS)
 kernel_img_LDFLAGS = -nostdlib -Wl,-N,-Ttext,8200
@@ -281,11 +289,11 @@ pupa_mkimage_LDFLAGS = -llzo
 pupa_setup_SOURCES = util/i386/pc/pupa-setup.c util/i386/pc/biosdisk.c \
        util/misc.c util/i386/pc/getroot.c kern/device.c kern/disk.c \
        kern/err.c kern/misc.c disk/i386/pc/partition.c fs/fat.c fs/ext2.c \
-       kern/file.c kern/fs.c
-CLEANFILES += pupa-setup pupa_setup-util_i386_pc_pupa_setup.o 
pupa_setup-util_i386_pc_biosdisk.o pupa_setup-util_misc.o 
pupa_setup-util_i386_pc_getroot.o pupa_setup-kern_device.o 
pupa_setup-kern_disk.o pupa_setup-kern_err.o pupa_setup-kern_misc.o 
pupa_setup-disk_i386_pc_partition.o pupa_setup-fs_fat.o pupa_setup-fs_ext2.o 
pupa_setup-kern_file.o pupa_setup-kern_fs.o
-MOSTLYCLEANFILES += pupa_setup-util_i386_pc_pupa_setup.d 
pupa_setup-util_i386_pc_biosdisk.d pupa_setup-util_misc.d 
pupa_setup-util_i386_pc_getroot.d pupa_setup-kern_device.d 
pupa_setup-kern_disk.d pupa_setup-kern_err.d pupa_setup-kern_misc.d 
pupa_setup-disk_i386_pc_partition.d pupa_setup-fs_fat.d pupa_setup-fs_ext2.d 
pupa_setup-kern_file.d pupa_setup-kern_fs.d
+       kern/file.c kern/fs.c kern/env.c
+CLEANFILES += pupa-setup pupa_setup-util_i386_pc_pupa_setup.o 
pupa_setup-util_i386_pc_biosdisk.o pupa_setup-util_misc.o 
pupa_setup-util_i386_pc_getroot.o pupa_setup-kern_device.o 
pupa_setup-kern_disk.o pupa_setup-kern_err.o pupa_setup-kern_misc.o 
pupa_setup-disk_i386_pc_partition.o pupa_setup-fs_fat.o pupa_setup-fs_ext2.o 
pupa_setup-kern_file.o pupa_setup-kern_fs.o pupa_setup-kern_env.o
+MOSTLYCLEANFILES += pupa_setup-util_i386_pc_pupa_setup.d 
pupa_setup-util_i386_pc_biosdisk.d pupa_setup-util_misc.d 
pupa_setup-util_i386_pc_getroot.d pupa_setup-kern_device.d 
pupa_setup-kern_disk.d pupa_setup-kern_err.d pupa_setup-kern_misc.d 
pupa_setup-disk_i386_pc_partition.d pupa_setup-fs_fat.d pupa_setup-fs_ext2.d 
pupa_setup-kern_file.d pupa_setup-kern_fs.d pupa_setup-kern_env.d
 
-pupa-setup: pupa_setup-util_i386_pc_pupa_setup.o 
pupa_setup-util_i386_pc_biosdisk.o pupa_setup-util_misc.o 
pupa_setup-util_i386_pc_getroot.o pupa_setup-kern_device.o 
pupa_setup-kern_disk.o pupa_setup-kern_err.o pupa_setup-kern_misc.o 
pupa_setup-disk_i386_pc_partition.o pupa_setup-fs_fat.o pupa_setup-fs_ext2.o 
pupa_setup-kern_file.o pupa_setup-kern_fs.o
+pupa-setup: pupa_setup-util_i386_pc_pupa_setup.o 
pupa_setup-util_i386_pc_biosdisk.o pupa_setup-util_misc.o 
pupa_setup-util_i386_pc_getroot.o pupa_setup-kern_device.o 
pupa_setup-kern_disk.o pupa_setup-kern_err.o pupa_setup-kern_misc.o 
pupa_setup-disk_i386_pc_partition.o pupa_setup-fs_fat.o pupa_setup-fs_ext2.o 
pupa_setup-kern_file.o pupa_setup-kern_fs.o pupa_setup-kern_env.o
        $(BUILD_CC) -o $@ $^ $(BUILD_LDFLAGS) $(pupa_setup_LDFLAGS)
 
 pupa_setup-util_i386_pc_pupa_setup.o: util/i386/pc/pupa-setup.c
@@ -392,19 +400,28 @@ pupa_setup-kern_fs.d: kern/fs.c
 
 -include pupa_setup-kern_fs.d
 
+pupa_setup-kern_env.o: kern/env.c
+       $(BUILD_CC) -Ikern -I$(srcdir)/kern $(BUILD_CPPFLAGS) $(BUILD_CFLAGS) 
-DPUPA_UTIL=1 $(pupa_setup_CFLAGS) -c -o $@ $<
+
+pupa_setup-kern_env.d: kern/env.c
+       set -e;           $(BUILD_CC) -Ikern -I$(srcdir)/kern $(BUILD_CPPFLAGS) 
$(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_setup_CFLAGS) -M $<          | sed 
's,env\.o[ :]*,pupa_setup-kern_env.o $@ : ,g' > $@;       [ -s $@ ] || rm -f $@
+
+-include pupa_setup-kern_env.d
+
 
 # For pupa
 pupa_emu_SOURCES = kern/main.c kern/device.c                           \
        kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c          \
         kern/misc.c kern/loader.c kern/rescue.c kern/term.c            \
-       disk/i386/pc/partition.c                                        \
+       disk/i386/pc/partition.c kern/env.c commands/ls.c               \
+       commands/terminal.c commands/boot.c commands/cmp.c commands/cat.c       
        \
        util/i386/pc/biosdisk.c fs/fat.c fs/ext2.c                      \
-       normal/cmdline.c normal/command.c normal/main.c normal/menu.c   \
+       normal/cmdline.c normal/command.c normal/main.c normal/menu.c 
normal/arg.c      \
        util/console.c util/pupa-emu.c util/misc.c util/i386/pc/getroot.c
-CLEANFILES += pupa-emu pupa_emu-kern_main.o pupa_emu-kern_device.o 
pupa_emu-kern_disk.o pupa_emu-kern_dl.o pupa_emu-kern_file.o pupa_emu-kern_fs.o 
pupa_emu-kern_err.o pupa_emu-kern_misc.o pupa_emu-kern_loader.o 
pupa_emu-kern_rescue.o pupa_emu-kern_term.o pupa_emu-disk_i386_pc_partition.o 
pupa_emu-util_i386_pc_biosdisk.o pupa_emu-fs_fat.o pupa_emu-fs_ext2.o 
pupa_emu-normal_cmdline.o pupa_emu-normal_command.o pupa_emu-normal_main.o 
pupa_emu-normal_menu.o pupa_emu-util_console.o pupa_emu-util_pupa_emu.o 
pupa_emu-util_misc.o pupa_emu-util_i386_pc_getroot.o
-MOSTLYCLEANFILES += pupa_emu-kern_main.d pupa_emu-kern_device.d 
pupa_emu-kern_disk.d pupa_emu-kern_dl.d pupa_emu-kern_file.d pupa_emu-kern_fs.d 
pupa_emu-kern_err.d pupa_emu-kern_misc.d pupa_emu-kern_loader.d 
pupa_emu-kern_rescue.d pupa_emu-kern_term.d pupa_emu-disk_i386_pc_partition.d 
pupa_emu-util_i386_pc_biosdisk.d pupa_emu-fs_fat.d pupa_emu-fs_ext2.d 
pupa_emu-normal_cmdline.d pupa_emu-normal_command.d pupa_emu-normal_main.d 
pupa_emu-normal_menu.d pupa_emu-util_console.d pupa_emu-util_pupa_emu.d 
pupa_emu-util_misc.d pupa_emu-util_i386_pc_getroot.d
+CLEANFILES += pupa-emu pupa_emu-kern_main.o pupa_emu-kern_device.o 
pupa_emu-kern_disk.o pupa_emu-kern_dl.o pupa_emu-kern_file.o pupa_emu-kern_fs.o 
pupa_emu-kern_err.o pupa_emu-kern_misc.o pupa_emu-kern_loader.o 
pupa_emu-kern_rescue.o pupa_emu-kern_term.o pupa_emu-disk_i386_pc_partition.o 
pupa_emu-kern_env.o pupa_emu-commands_ls.o pupa_emu-commands_terminal.o 
pupa_emu-commands_boot.o pupa_emu-commands_cmp.o pupa_emu-commands_cat.o 
pupa_emu-util_i386_pc_biosdisk.o pupa_emu-fs_fat.o pupa_emu-fs_ext2.o 
pupa_emu-normal_cmdline.o pupa_emu-normal_command.o pupa_emu-normal_main.o 
pupa_emu-normal_menu.o pupa_emu-normal_arg.o pupa_emu-util_console.o 
pupa_emu-util_pupa_emu.o pupa_emu-util_misc.o pupa_emu-util_i386_pc_getroot.o
+MOSTLYCLEANFILES += pupa_emu-kern_main.d pupa_emu-kern_device.d 
pupa_emu-kern_disk.d pupa_emu-kern_dl.d pupa_emu-kern_file.d pupa_emu-kern_fs.d 
pupa_emu-kern_err.d pupa_emu-kern_misc.d pupa_emu-kern_loader.d 
pupa_emu-kern_rescue.d pupa_emu-kern_term.d pupa_emu-disk_i386_pc_partition.d 
pupa_emu-kern_env.d pupa_emu-commands_ls.d pupa_emu-commands_terminal.d 
pupa_emu-commands_boot.d pupa_emu-commands_cmp.d pupa_emu-commands_cat.d 
pupa_emu-util_i386_pc_biosdisk.d pupa_emu-fs_fat.d pupa_emu-fs_ext2.d 
pupa_emu-normal_cmdline.d pupa_emu-normal_command.d pupa_emu-normal_main.d 
pupa_emu-normal_menu.d pupa_emu-normal_arg.d pupa_emu-util_console.d 
pupa_emu-util_pupa_emu.d pupa_emu-util_misc.d pupa_emu-util_i386_pc_getroot.d
 
-pupa-emu: pupa_emu-kern_main.o pupa_emu-kern_device.o pupa_emu-kern_disk.o 
pupa_emu-kern_dl.o pupa_emu-kern_file.o pupa_emu-kern_fs.o pupa_emu-kern_err.o 
pupa_emu-kern_misc.o pupa_emu-kern_loader.o pupa_emu-kern_rescue.o 
pupa_emu-kern_term.o pupa_emu-disk_i386_pc_partition.o 
pupa_emu-util_i386_pc_biosdisk.o pupa_emu-fs_fat.o pupa_emu-fs_ext2.o 
pupa_emu-normal_cmdline.o pupa_emu-normal_command.o pupa_emu-normal_main.o 
pupa_emu-normal_menu.o pupa_emu-util_console.o pupa_emu-util_pupa_emu.o 
pupa_emu-util_misc.o pupa_emu-util_i386_pc_getroot.o
+pupa-emu: pupa_emu-kern_main.o pupa_emu-kern_device.o pupa_emu-kern_disk.o 
pupa_emu-kern_dl.o pupa_emu-kern_file.o pupa_emu-kern_fs.o pupa_emu-kern_err.o 
pupa_emu-kern_misc.o pupa_emu-kern_loader.o pupa_emu-kern_rescue.o 
pupa_emu-kern_term.o pupa_emu-disk_i386_pc_partition.o pupa_emu-kern_env.o 
pupa_emu-commands_ls.o pupa_emu-commands_terminal.o pupa_emu-commands_boot.o 
pupa_emu-commands_cmp.o pupa_emu-commands_cat.o 
pupa_emu-util_i386_pc_biosdisk.o pupa_emu-fs_fat.o pupa_emu-fs_ext2.o 
pupa_emu-normal_cmdline.o pupa_emu-normal_command.o pupa_emu-normal_main.o 
pupa_emu-normal_menu.o pupa_emu-normal_arg.o pupa_emu-util_console.o 
pupa_emu-util_pupa_emu.o pupa_emu-util_misc.o pupa_emu-util_i386_pc_getroot.o
        $(BUILD_CC) -o $@ $^ $(BUILD_LDFLAGS) $(pupa_emu_LDFLAGS)
 
 pupa_emu-kern_main.o: kern/main.c
@@ -503,6 +520,54 @@ pupa_emu-disk_i386_pc_partition.d: disk/
 
 -include pupa_emu-disk_i386_pc_partition.d
 
+pupa_emu-kern_env.o: kern/env.c
+       $(BUILD_CC) -Ikern -I$(srcdir)/kern $(BUILD_CPPFLAGS) $(BUILD_CFLAGS) 
-DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -c -o $@ $<
+
+pupa_emu-kern_env.d: kern/env.c
+       set -e;           $(BUILD_CC) -Ikern -I$(srcdir)/kern $(BUILD_CPPFLAGS) 
$(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -M $<    | sed 's,env\.o[ 
:]*,pupa_emu-kern_env.o $@ : ,g' > $@;         [ -s $@ ] || rm -f $@
+
+-include pupa_emu-kern_env.d
+
+pupa_emu-commands_ls.o: commands/ls.c
+       $(BUILD_CC) -Icommands -I$(srcdir)/commands $(BUILD_CPPFLAGS) 
$(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -c -o $@ $<
+
+pupa_emu-commands_ls.d: commands/ls.c
+       set -e;           $(BUILD_CC) -Icommands -I$(srcdir)/commands 
$(BUILD_CPPFLAGS) $(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -M $<    | 
sed 's,ls\.o[ :]*,pupa_emu-commands_ls.o $@ : ,g' > $@;       [ -s $@ ] || rm 
-f $@
+
+-include pupa_emu-commands_ls.d
+
+pupa_emu-commands_terminal.o: commands/terminal.c
+       $(BUILD_CC) -Icommands -I$(srcdir)/commands $(BUILD_CPPFLAGS) 
$(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -c -o $@ $<
+
+pupa_emu-commands_terminal.d: commands/terminal.c
+       set -e;           $(BUILD_CC) -Icommands -I$(srcdir)/commands 
$(BUILD_CPPFLAGS) $(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -M $<    | 
sed 's,terminal\.o[ :]*,pupa_emu-commands_terminal.o $@ : ,g' > $@;           [ 
-s $@ ] || rm -f $@
+
+-include pupa_emu-commands_terminal.d
+
+pupa_emu-commands_boot.o: commands/boot.c
+       $(BUILD_CC) -Icommands -I$(srcdir)/commands $(BUILD_CPPFLAGS) 
$(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -c -o $@ $<
+
+pupa_emu-commands_boot.d: commands/boot.c
+       set -e;           $(BUILD_CC) -Icommands -I$(srcdir)/commands 
$(BUILD_CPPFLAGS) $(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -M $<    | 
sed 's,boot\.o[ :]*,pupa_emu-commands_boot.o $@ : ,g' > $@;           [ -s $@ ] 
|| rm -f $@
+
+-include pupa_emu-commands_boot.d
+
+pupa_emu-commands_cmp.o: commands/cmp.c
+       $(BUILD_CC) -Icommands -I$(srcdir)/commands $(BUILD_CPPFLAGS) 
$(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -c -o $@ $<
+
+pupa_emu-commands_cmp.d: commands/cmp.c
+       set -e;           $(BUILD_CC) -Icommands -I$(srcdir)/commands 
$(BUILD_CPPFLAGS) $(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -M $<    | 
sed 's,cmp\.o[ :]*,pupa_emu-commands_cmp.o $@ : ,g' > $@;     [ -s $@ ] || rm 
-f $@
+
+-include pupa_emu-commands_cmp.d
+
+pupa_emu-commands_cat.o: commands/cat.c
+       $(BUILD_CC) -Icommands -I$(srcdir)/commands $(BUILD_CPPFLAGS) 
$(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -c -o $@ $<
+
+pupa_emu-commands_cat.d: commands/cat.c
+       set -e;           $(BUILD_CC) -Icommands -I$(srcdir)/commands 
$(BUILD_CPPFLAGS) $(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -M $<    | 
sed 's,cat\.o[ :]*,pupa_emu-commands_cat.o $@ : ,g' > $@;     [ -s $@ ] || rm 
-f $@
+
+-include pupa_emu-commands_cat.d
+
 pupa_emu-util_i386_pc_biosdisk.o: util/i386/pc/biosdisk.c
        $(BUILD_CC) -Iutil/i386/pc -I$(srcdir)/util/i386/pc $(BUILD_CPPFLAGS) 
$(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -c -o $@ $<
 
@@ -559,6 +624,14 @@ pupa_emu-normal_menu.d: normal/menu.c
 
 -include pupa_emu-normal_menu.d
 
+pupa_emu-normal_arg.o: normal/arg.c
+       $(BUILD_CC) -Inormal -I$(srcdir)/normal $(BUILD_CPPFLAGS) 
$(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -c -o $@ $<
+
+pupa_emu-normal_arg.d: normal/arg.c
+       set -e;           $(BUILD_CC) -Inormal -I$(srcdir)/normal 
$(BUILD_CPPFLAGS) $(BUILD_CFLAGS) -DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -M $<        
| sed 's,arg\.o[ :]*,pupa_emu-normal_arg.o $@ : ,g' > $@;       [ -s $@ ] || rm 
-f $@
+
+-include pupa_emu-normal_arg.d
+
 pupa_emu-util_console.o: util/console.c
        $(BUILD_CC) -Iutil -I$(srcdir)/util $(BUILD_CPPFLAGS) $(BUILD_CFLAGS) 
-DPUPA_UTIL=1 $(pupa_emu_CFLAGS) -c -o $@ $<
 
@@ -612,7 +685,7 @@ genmoddep-util_genmoddep.d: util/genmodd
 
 # Modules.
 pkgdata_MODULES = _chain.mod _linux.mod fat.mod ext2.mod normal.mod hello.mod \
-       vga.mod font.mod _multiboot.mod
+        vga.mod font.mod _multiboot.mod ls.mod boot.mod cmp.mod cat.mod 
terminal.mod
 
 # For _chain.mod.
 _chain_mod_SOURCES = loader/i386/pc/chainloader.c
@@ -772,9 +845,9 @@ _linux_mod_CFLAGS = $(COMMON_CFLAGS)
 
 # For normal.mod.
 normal_mod_SOURCES = normal/cmdline.c normal/command.c normal/main.c \
-       normal/menu.c normal/i386/setjmp.S
-CLEANFILES += normal.mod mod-normal.o mod-normal.c pre-normal.o 
normal_mod-normal_cmdline.o normal_mod-normal_command.o 
normal_mod-normal_main.o normal_mod-normal_menu.o 
normal_mod-normal_i386_setjmp.o def-normal.lst und-normal.lst
-MOSTLYCLEANFILES += normal_mod-normal_cmdline.d normal_mod-normal_command.d 
normal_mod-normal_main.d normal_mod-normal_menu.d 
normal_mod-normal_i386_setjmp.d
+       normal/menu.c normal/arg.c normal/i386/setjmp.S
+CLEANFILES += normal.mod mod-normal.o mod-normal.c pre-normal.o 
normal_mod-normal_cmdline.o normal_mod-normal_command.o 
normal_mod-normal_main.o normal_mod-normal_menu.o normal_mod-normal_arg.o 
normal_mod-normal_i386_setjmp.o def-normal.lst und-normal.lst
+MOSTLYCLEANFILES += normal_mod-normal_cmdline.d normal_mod-normal_command.d 
normal_mod-normal_main.d normal_mod-normal_menu.d normal_mod-normal_arg.d 
normal_mod-normal_i386_setjmp.d
 DEFSYMFILES += def-normal.lst
 UNDSYMFILES += und-normal.lst
 
@@ -783,7 +856,7 @@ normal.mod: pre-normal.o mod-normal.o
        $(LD) -r -o $@ $^
        $(STRIP) --strip-unneeded -K pupa_mod_init -K pupa_mod_fini -R .note -R 
.comment $@
 
-pre-normal.o: normal_mod-normal_cmdline.o normal_mod-normal_command.o 
normal_mod-normal_main.o normal_mod-normal_menu.o 
normal_mod-normal_i386_setjmp.o
+pre-normal.o: normal_mod-normal_cmdline.o normal_mod-normal_command.o 
normal_mod-normal_main.o normal_mod-normal_menu.o normal_mod-normal_arg.o 
normal_mod-normal_i386_setjmp.o
        -rm -f $@
        $(LD) -r -o $@ $^
 
@@ -832,6 +905,14 @@ normal_mod-normal_menu.d: normal/menu.c
 
 -include normal_mod-normal_menu.d
 
+normal_mod-normal_arg.o: normal/arg.c
+       $(CC) -Inormal -I$(srcdir)/normal $(CPPFLAGS) $(CFLAGS) 
$(normal_mod_CFLAGS) -c -o $@ $<
+
+normal_mod-normal_arg.d: normal/arg.c
+       set -e;           $(CC) -Inormal -I$(srcdir)/normal $(CPPFLAGS) 
$(CFLAGS) $(normal_mod_CFLAGS) -M $<      | sed 's,arg\.o[ 
:]*,normal_mod-normal_arg.o $@ : ,g' > $@;     [ -s $@ ] || rm -f $@
+
+-include normal_mod-normal_arg.d
+
 normal_mod-normal_i386_setjmp.o: normal/i386/setjmp.S
        $(CC) -Inormal/i386 -I$(srcdir)/normal/i386 $(CPPFLAGS) $(ASFLAGS) 
$(normal_mod_ASFLAGS) -c -o $@ $<
 
@@ -882,6 +963,201 @@ hello_mod-hello_hello.d: hello/hello.c
 
 hello_mod_CFLAGS = $(COMMON_CFLAGS)
 
+# For boot.mod.
+boot_mod_SOURCES = commands/boot.c
+CLEANFILES += boot.mod mod-boot.o mod-boot.c pre-boot.o 
boot_mod-commands_boot.o def-boot.lst und-boot.lst
+MOSTLYCLEANFILES += boot_mod-commands_boot.d
+DEFSYMFILES += def-boot.lst
+UNDSYMFILES += und-boot.lst
+
+boot.mod: pre-boot.o mod-boot.o
+       -rm -f $@
+       $(LD) -r -o $@ $^
+       $(STRIP) --strip-unneeded -K pupa_mod_init -K pupa_mod_fini -R .note -R 
.comment $@
+
+pre-boot.o: boot_mod-commands_boot.o
+       -rm -f $@
+       $(LD) -r -o $@ $^
+
+mod-boot.o: mod-boot.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(boot_mod_CFLAGS) -c -o $@ $<
+
+mod-boot.c: moddep.lst genmodsrc.sh
+       sh $(srcdir)/genmodsrc.sh 'boot' $< > $@ || (rm -f $@; exit 1)
+
+def-boot.lst: pre-boot.o
+       $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 boot/' > $@
+
+und-boot.lst: pre-boot.o
+       echo 'boot' > $@
+       $(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+
+boot_mod-commands_boot.o: commands/boot.c
+       $(CC) -Icommands -I$(srcdir)/commands $(CPPFLAGS) $(CFLAGS) 
$(boot_mod_CFLAGS) -c -o $@ $<
+
+boot_mod-commands_boot.d: commands/boot.c
+       set -e;           $(CC) -Icommands -I$(srcdir)/commands $(CPPFLAGS) 
$(CFLAGS) $(boot_mod_CFLAGS) -M $<    | sed 's,boot\.o[ 
:]*,boot_mod-commands_boot.o $@ : ,g' > $@;           [ -s $@ ] || rm -f $@
+
+-include boot_mod-commands_boot.d
+
+boot_mod_CFLAGS = $(COMMON_CFLAGS)
+
+# For terminal.mod.
+terminal_mod_SOURCES = commands/terminal.c
+CLEANFILES += terminal.mod mod-terminal.o mod-terminal.c pre-terminal.o 
terminal_mod-commands_terminal.o def-terminal.lst und-terminal.lst
+MOSTLYCLEANFILES += terminal_mod-commands_terminal.d
+DEFSYMFILES += def-terminal.lst
+UNDSYMFILES += und-terminal.lst
+
+terminal.mod: pre-terminal.o mod-terminal.o
+       -rm -f $@
+       $(LD) -r -o $@ $^
+       $(STRIP) --strip-unneeded -K pupa_mod_init -K pupa_mod_fini -R .note -R 
.comment $@
+
+pre-terminal.o: terminal_mod-commands_terminal.o
+       -rm -f $@
+       $(LD) -r -o $@ $^
+
+mod-terminal.o: mod-terminal.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(terminal_mod_CFLAGS) -c -o $@ $<
+
+mod-terminal.c: moddep.lst genmodsrc.sh
+       sh $(srcdir)/genmodsrc.sh 'terminal' $< > $@ || (rm -f $@; exit 1)
+
+def-terminal.lst: pre-terminal.o
+       $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 terminal/' > 
$@
+
+und-terminal.lst: pre-terminal.o
+       echo 'terminal' > $@
+       $(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+
+terminal_mod-commands_terminal.o: commands/terminal.c
+       $(CC) -Icommands -I$(srcdir)/commands $(CPPFLAGS) $(CFLAGS) 
$(terminal_mod_CFLAGS) -c -o $@ $<
+
+terminal_mod-commands_terminal.d: commands/terminal.c
+       set -e;           $(CC) -Icommands -I$(srcdir)/commands $(CPPFLAGS) 
$(CFLAGS) $(terminal_mod_CFLAGS) -M $<        | sed 's,terminal\.o[ 
:]*,terminal_mod-commands_terminal.o $@ : ,g' > $@;       [ -s $@ ] || rm -f $@
+
+-include terminal_mod-commands_terminal.d
+
+terminal_mod_CFLAGS = $(COMMON_CFLAGS)
+
+# For ls.mod.
+ls_mod_SOURCES = commands/ls.c
+CLEANFILES += ls.mod mod-ls.o mod-ls.c pre-ls.o ls_mod-commands_ls.o 
def-ls.lst und-ls.lst
+MOSTLYCLEANFILES += ls_mod-commands_ls.d
+DEFSYMFILES += def-ls.lst
+UNDSYMFILES += und-ls.lst
+
+ls.mod: pre-ls.o mod-ls.o
+       -rm -f $@
+       $(LD) -r -o $@ $^
+       $(STRIP) --strip-unneeded -K pupa_mod_init -K pupa_mod_fini -R .note -R 
.comment $@
+
+pre-ls.o: ls_mod-commands_ls.o
+       -rm -f $@
+       $(LD) -r -o $@ $^
+
+mod-ls.o: mod-ls.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(ls_mod_CFLAGS) -c -o $@ $<
+
+mod-ls.c: moddep.lst genmodsrc.sh
+       sh $(srcdir)/genmodsrc.sh 'ls' $< > $@ || (rm -f $@; exit 1)
+
+def-ls.lst: pre-ls.o
+       $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 ls/' > $@
+
+und-ls.lst: pre-ls.o
+       echo 'ls' > $@
+       $(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+
+ls_mod-commands_ls.o: commands/ls.c
+       $(CC) -Icommands -I$(srcdir)/commands $(CPPFLAGS) $(CFLAGS) 
$(ls_mod_CFLAGS) -c -o $@ $<
+
+ls_mod-commands_ls.d: commands/ls.c
+       set -e;           $(CC) -Icommands -I$(srcdir)/commands $(CPPFLAGS) 
$(CFLAGS) $(ls_mod_CFLAGS) -M $<      | sed 's,ls\.o[ :]*,ls_mod-commands_ls.o 
$@ : ,g' > $@;         [ -s $@ ] || rm -f $@
+
+-include ls_mod-commands_ls.d
+
+ls_mod_CFLAGS = $(COMMON_CFLAGS)
+
+# For cmp.mod.
+cmp_mod_SOURCES = commands/cmp.c
+CLEANFILES += cmp.mod mod-cmp.o mod-cmp.c pre-cmp.o cmp_mod-commands_cmp.o 
def-cmp.lst und-cmp.lst
+MOSTLYCLEANFILES += cmp_mod-commands_cmp.d
+DEFSYMFILES += def-cmp.lst
+UNDSYMFILES += und-cmp.lst
+
+cmp.mod: pre-cmp.o mod-cmp.o
+       -rm -f $@
+       $(LD) -r -o $@ $^
+       $(STRIP) --strip-unneeded -K pupa_mod_init -K pupa_mod_fini -R .note -R 
.comment $@
+
+pre-cmp.o: cmp_mod-commands_cmp.o
+       -rm -f $@
+       $(LD) -r -o $@ $^
+
+mod-cmp.o: mod-cmp.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(cmp_mod_CFLAGS) -c -o $@ $<
+
+mod-cmp.c: moddep.lst genmodsrc.sh
+       sh $(srcdir)/genmodsrc.sh 'cmp' $< > $@ || (rm -f $@; exit 1)
+
+def-cmp.lst: pre-cmp.o
+       $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 cmp/' > $@
+
+und-cmp.lst: pre-cmp.o
+       echo 'cmp' > $@
+       $(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+
+cmp_mod-commands_cmp.o: commands/cmp.c
+       $(CC) -Icommands -I$(srcdir)/commands $(CPPFLAGS) $(CFLAGS) 
$(cmp_mod_CFLAGS) -c -o $@ $<
+
+cmp_mod-commands_cmp.d: commands/cmp.c
+       set -e;           $(CC) -Icommands -I$(srcdir)/commands $(CPPFLAGS) 
$(CFLAGS) $(cmp_mod_CFLAGS) -M $<     | sed 's,cmp\.o[ 
:]*,cmp_mod-commands_cmp.o $@ : ,g' > $@;      [ -s $@ ] || rm -f $@
+
+-include cmp_mod-commands_cmp.d
+
+cmp_mod_CFLAGS = $(COMMON_CFLAGS)
+
+# For cat.mod.
+cat_mod_SOURCES = commands/cat.c
+CLEANFILES += cat.mod mod-cat.o mod-cat.c pre-cat.o cat_mod-commands_cat.o 
def-cat.lst und-cat.lst
+MOSTLYCLEANFILES += cat_mod-commands_cat.d
+DEFSYMFILES += def-cat.lst
+UNDSYMFILES += und-cat.lst
+
+cat.mod: pre-cat.o mod-cat.o
+       -rm -f $@
+       $(LD) -r -o $@ $^
+       $(STRIP) --strip-unneeded -K pupa_mod_init -K pupa_mod_fini -R .note -R 
.comment $@
+
+pre-cat.o: cat_mod-commands_cat.o
+       -rm -f $@
+       $(LD) -r -o $@ $^
+
+mod-cat.o: mod-cat.c
+       $(CC) $(CPPFLAGS) $(CFLAGS) $(cat_mod_CFLAGS) -c -o $@ $<
+
+mod-cat.c: moddep.lst genmodsrc.sh
+       sh $(srcdir)/genmodsrc.sh 'cat' $< > $@ || (rm -f $@; exit 1)
+
+def-cat.lst: pre-cat.o
+       $(NM) -g --defined-only -P -p $< | sed 's/^\([^ ]*\).*/\1 cat/' > $@
+
+und-cat.lst: pre-cat.o
+       echo 'cat' > $@
+       $(NM) -u -P -p $< | cut -f1 -d' ' >> $@
+
+cat_mod-commands_cat.o: commands/cat.c
+       $(CC) -Icommands -I$(srcdir)/commands $(CPPFLAGS) $(CFLAGS) 
$(cat_mod_CFLAGS) -c -o $@ $<
+
+cat_mod-commands_cat.d: commands/cat.c
+       set -e;           $(CC) -Icommands -I$(srcdir)/commands $(CPPFLAGS) 
$(CFLAGS) $(cat_mod_CFLAGS) -M $<     | sed 's,cat\.o[ 
:]*,cat_mod-commands_cat.o $@ : ,g' > $@;      [ -s $@ ] || rm -f $@
+
+-include cat_mod-commands_cat.d
+
+cat_mod_CFLAGS = $(COMMON_CFLAGS)
+
 # For vga.mod.
 vga_mod_SOURCES = term/i386/pc/vga.c
 CLEANFILES += vga.mod mod-vga.o mod-vga.c pre-vga.o vga_mod-term_i386_pc_vga.o 
def-vga.lst und-vga.lst
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/conf/i386-pc.rmk 
./conf/i386-pc.rmk
--- /home/marco/hurd/hurdnew/pupa/pupa/conf/i386-pc.rmk 2003-11-19 
20:29:06.000000000 +0100
+++ ./conf/i386-pc.rmk  2003-12-28 19:48:22.000000000 +0100
@@ -21,14 +21,14 @@ kernel_img_SOURCES = kern/i386/pc/startu
        kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \
        kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \
        kern/i386/dl.c kern/i386/pc/init.c disk/i386/pc/partition.c \
-       disk/i386/pc/biosdisk.c \
+       kern/env.c disk/i386/pc/biosdisk.c \
        term/i386/pc/console.c \
        symlist.c
 kernel_img_HEADERS = boot.h device.h disk.h dl.h elf.h err.h \
        file.h fs.h kernel.h loader.h misc.h mm.h net.h rescue.h symbol.h \
        term.h types.h machine/biosdisk.h machine/boot.h \
        machine/console.h machine/init.h machine/memory.h \
-       machine/loader.h machine/partition.h machine/vga.h
+       machine/loader.h machine/partition.h machine/vga.h arg.h env.h
 kernel_img_CFLAGS = $(COMMON_CFLAGS)
 kernel_img_ASFLAGS = $(COMMON_ASFLAGS)
 kernel_img_LDFLAGS = -nostdlib -Wl,-N,-Ttext,8200
@@ -56,15 +56,16 @@ pupa_mkimage_LDFLAGS = -llzo
 pupa_setup_SOURCES = util/i386/pc/pupa-setup.c util/i386/pc/biosdisk.c \
        util/misc.c util/i386/pc/getroot.c kern/device.c kern/disk.c \
        kern/err.c kern/misc.c disk/i386/pc/partition.c fs/fat.c fs/ext2.c \
-       kern/file.c kern/fs.c
+       kern/file.c kern/fs.c kern/env.c
 
 # For pupa
 pupa_emu_SOURCES = kern/main.c kern/device.c                           \
        kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c          \
         kern/misc.c kern/loader.c kern/rescue.c kern/term.c            \
-       disk/i386/pc/partition.c                                        \
+       disk/i386/pc/partition.c kern/env.c commands/ls.c               \
+       commands/terminal.c commands/boot.c commands/cmp.c commands/cat.c       
        \
        util/i386/pc/biosdisk.c fs/fat.c fs/ext2.c                      \
-       normal/cmdline.c normal/command.c normal/main.c normal/menu.c   \
+       normal/cmdline.c normal/command.c normal/main.c normal/menu.c 
normal/arg.c      \
        util/console.c util/pupa-emu.c util/misc.c util/i386/pc/getroot.c
 pupa_emu_LDFLAGS = -lncurses
 
@@ -73,7 +74,7 @@ genmoddep_SOURCES = util/genmoddep.c
 
 # Modules.
 pkgdata_MODULES = _chain.mod _linux.mod fat.mod ext2.mod normal.mod hello.mod \
-       vga.mod font.mod _multiboot.mod
+        vga.mod font.mod _multiboot.mod ls.mod boot.mod cmp.mod cat.mod 
terminal.mod
 
 # For _chain.mod.
 _chain_mod_SOURCES = loader/i386/pc/chainloader.c
@@ -93,7 +94,7 @@ _linux_mod_CFLAGS = $(COMMON_CFLAGS)
 
 # For normal.mod.
 normal_mod_SOURCES = normal/cmdline.c normal/command.c normal/main.c \
-       normal/menu.c normal/i386/setjmp.S
+       normal/menu.c normal/arg.c normal/i386/setjmp.S
 normal_mod_CFLAGS = $(COMMON_CFLAGS)
 normal_mod_ASFLAGS = $(COMMON_ASFLAGS)
 
@@ -101,6 +102,26 @@ normal_mod_ASFLAGS = $(COMMON_ASFLAGS)
 hello_mod_SOURCES = hello/hello.c
 hello_mod_CFLAGS = $(COMMON_CFLAGS)
 
+# For boot.mod.
+boot_mod_SOURCES = commands/boot.c
+boot_mod_CFLAGS = $(COMMON_CFLAGS)
+
+# For terminal.mod.
+terminal_mod_SOURCES = commands/terminal.c
+terminal_mod_CFLAGS = $(COMMON_CFLAGS)
+
+# For ls.mod.
+ls_mod_SOURCES = commands/ls.c
+ls_mod_CFLAGS = $(COMMON_CFLAGS)
+
+# For cmp.mod.
+cmp_mod_SOURCES = commands/cmp.c
+cmp_mod_CFLAGS = $(COMMON_CFLAGS)
+
+# For cat.mod.
+cat_mod_SOURCES = commands/cat.c
+cat_mod_CFLAGS = $(COMMON_CFLAGS)
+
 # For vga.mod.
 vga_mod_SOURCES = term/i386/pc/vga.c
 vga_mod_CFLAGS = $(COMMON_CFLAGS)
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/font/manager.c ./font/manager.c
--- /home/marco/hurd/hurdnew/pupa/pupa/font/manager.c   2003-09-25 
22:15:52.000000000 +0200
+++ ./font/manager.c    2003-12-28 19:41:45.000000000 +0100
@@ -1,5 +1,6 @@
 /*
  *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
  *  Copyright (C) 2003  Yoshinori K. Okuji <address@hidden>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -217,14 +218,16 @@ pupa_font_get_glyph (pupa_uint32_t code,
   return 0;
 }
 
-static int
-font_command (int argc, char *argv[])
+static pupa_err_t
+font_command (struct pupa_arg_list *state __attribute__ ((unused)),
+             int argc  __attribute__ ((unused)),
+             char **args __attribute__ ((unused)))
 {
   if (argc == 0)
     return pupa_error (PUPA_ERR_BAD_ARGUMENT, "no font specified");
 
   while (argc--)
-    if (! add_font (*argv++))
+    if (! add_font (*args++))
       return 1;
 
   return 0;
@@ -232,9 +235,9 @@ font_command (int argc, char *argv[])
 
 PUPA_MOD_INIT
 {
+  (void) mod; /* Stop warning.  */
   pupa_register_command ("font", font_command, PUPA_COMMAND_FLAG_BOTH,
-                        "font FILE...",
-                        "Specify a font file to display.");
+                        "font FILE...", "Specify a font file to display.", 0);
 }
 
 PUPA_MOD_FINI
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/hello/hello.c ./hello/hello.c
--- /home/marco/hurd/hurdnew/pupa/pupa/hello/hello.c    2003-02-08 
09:15:43.000000000 +0100
+++ ./hello/hello.c     2003-12-28 19:42:04.000000000 +0100
@@ -1,6 +1,7 @@
 /* hello.c - test module for dynamic loading */
 /*
  *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
  *  Copyright (C) 2003  NIIBE Yutaka <address@hidden>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -25,10 +26,11 @@
 #include <pupa/dl.h>
 #include <pupa/normal.h>
 
-static int
-pupa_cmd_hello (int argc, char *argv[])
+static pupa_err_t
+pupa_cmd_hello (struct pupa_arg_list *state __attribute__ ((unused)),
+               int argc __attribute__ ((unused)),
+               char **args __attribute__ ((unused)))
 {
-  (void)argc;  (void)argv;     /* To stop warning. */
   pupa_printf ("Hello World\n");
   return 0;
 }
@@ -37,7 +39,7 @@ PUPA_MOD_INIT
 {
   (void)mod;                   /* To stop warning. */
   pupa_register_command ("hello", pupa_cmd_hello, PUPA_COMMAND_FLAG_BOTH,
-                        "hello", "Say hello");
+                        "hello", "Say hello", 0);
 }
 
 PUPA_MOD_FINI
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/arg.h 
./include/pupa/arg.h
--- /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/arg.h       1970-01-01 
01:00:00.000000000 +0100
+++ ./include/pupa/arg.h        2003-12-28 20:31:08.000000000 +0100
@@ -0,0 +1,66 @@
+/*
+ *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
+ *
+ *  PUPA 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 PUPA; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef PUPA_ARG_HEADER
+#define PUPA_ARG_HEADER        1
+
+#include <pupa/symbol.h>
+#include <pupa/err.h>
+#include <pupa/types.h>
+
+enum pupa_arg_type
+  {
+    ARG_TYPE_NONE,
+    ARG_TYPE_STRING,
+    ARG_TYPE_INT,
+    ARG_TYPE_DEVICE,
+    ARG_TYPE_FILE,
+    ARG_TYPE_DIR,
+    ARG_TYPE_PATHNAME
+  };
+
+typedef enum pupa_arg_type pupa_arg_type_t;
+
+/* Flags for the option field op pupa_arg_option.  */
+#define PUPA_ARG_OPTION_OPTIONAL       1 << 1
+
+enum pupa_key_type
+  {
+    PUPA_KEY_ARG = -1,
+    PUPA_KEY_END = -2
+  };
+typedef enum pupa_key_type pupa_arg_key_type_t;
+
+struct pupa_arg_option
+{
+  char *longarg;
+  char shortarg;
+  int flags;
+  char *doc;
+  char *arg;
+  pupa_arg_type_t type;
+};
+
+struct pupa_arg_list
+{
+  int set;
+  char *arg;
+};
+
+#endif /* ! PUPA_ARG_HEADER */
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/disk.h 
./include/pupa/disk.h
--- /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/disk.h      2002-12-27 
09:53:09.000000000 +0100
+++ ./include/pupa/disk.h       2003-12-28 19:42:37.000000000 +0100
@@ -1,5 +1,6 @@
 /*
  *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
  *  Copyright (C) 2002 Yoshinori K. Okuji <address@hidden>
  *
  *  PUPA is free software; you can redistribute it and/or modify
@@ -23,6 +24,7 @@
 #include <pupa/symbol.h>
 #include <pupa/err.h>
 #include <pupa/types.h>
+#include <pupa/device.h>
 
 struct pupa_disk;
 
@@ -116,4 +118,7 @@ pupa_err_t EXPORT_FUNC(pupa_disk_write) 
                                         unsigned long size,
                                         const char *buf);
 
+pupa_err_t EXPORT_FUNC(pupa_print_partinfo) (pupa_device_t disk,
+                                            char *partname);
+
 #endif /* ! PUPA_DISK_HEADER */
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/dl.h 
./include/pupa/dl.h
--- /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/dl.h        2003-01-20 
05:13:46.000000000 +0100
+++ ./include/pupa/dl.h 2003-12-27 21:14:47.000000000 +0100
@@ -83,8 +83,6 @@ pupa_dl_t EXPORT_FUNC(pupa_dl_get) (cons
 pupa_err_t EXPORT_FUNC(pupa_dl_register_symbol) (const char *name, void *addr,
                                            pupa_dl_t mod);
 void *EXPORT_FUNC(pupa_dl_resolve_symbol) (const char *name);
-void EXPORT_FUNC(pupa_dl_set_prefix) (const char *dir);
-const char *EXPORT_FUNC(pupa_dl_get_prefix) (void);
 
 int pupa_arch_dl_check_header (void *ehdr, pupa_size_t size);
 pupa_err_t pupa_arch_dl_relocate_symbols (pupa_dl_t mod, void *ehdr);
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/env.h 
./include/pupa/env.h
--- /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/env.h       1970-01-01 
01:00:00.000000000 +0100
+++ ./include/pupa/env.h        2003-12-27 21:46:02.000000000 +0100
@@ -0,0 +1,49 @@
+/*
+ *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
+ *
+ *  PUPA 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 PUPA; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef PUPA_ENV_HEADER
+#define PUPA_ENV_HEADER        1
+
+#include <pupa/symbol.h>
+#include <pupa/err.h>
+#include <pupa/types.h>
+
+struct pupa_env_var
+{
+  char *name;
+  char *value;
+  pupa_err_t (*read_hook) (struct pupa_env_var *var, char **val);
+  pupa_err_t (*write_hook) (struct pupa_env_var *var);
+  struct pupa_env_var *next;
+  struct pupa_env_var **prevp;
+  struct pupa_env_var *sort_next;
+  struct pupa_env_var **sort_prevp;
+};
+
+pupa_err_t EXPORT_FUNC(pupa_env_set) (const char *var, const char *val);
+char *EXPORT_FUNC(pupa_env_get) (const char *name);
+void EXPORT_FUNC(pupa_env_unset) (const char *name);
+void EXPORT_FUNC(pupa_env_iterate) (int (* func) (struct pupa_env_var *var));
+pupa_err_t EXPORT_FUNC(pupa_register_variable_hook) (const char *var,
+                                                    pupa_err_t (*read_hook) 
+                                                    (struct pupa_env_var *var, 
char **val),
+                                                    pupa_err_t (*write_hook)
+                                                    (struct pupa_env_var 
*var));
+
+#endif /* ! PUPA_ENV_HEADER */
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/misc.h 
./include/pupa/misc.h
--- /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/misc.h      2003-12-03 
20:23:35.000000000 +0100
+++ ./include/pupa/misc.h       2003-12-27 21:56:48.000000000 +0100
@@ -25,6 +25,7 @@
 #include <stdarg.h>
 #include <pupa/types.h>
 #include <pupa/symbol.h>
+#include <pupa/err.h>
 
 /* XXX: If pupa_memmove is too slow, we must implement pupa_memcpy.  */
 #define pupa_memcpy(d,s,n)     pupa_memmove ((d), (s), (n))
@@ -44,6 +45,8 @@ char *EXPORT_FUNC(pupa_strrchr) (const c
 int EXPORT_FUNC(pupa_isspace) (int c);
 int EXPORT_FUNC(pupa_isprint) (int c);
 int EXPORT_FUNC(pupa_isalpha) (int c);
+int EXPORT_FUNC(pupa_isgraph) (int c);
+int EXPORT_FUNC(pupa_isdigit) (int c);
 int EXPORT_FUNC(pupa_tolower) (int c);
 unsigned long EXPORT_FUNC(pupa_strtoul) (const char *str, char **end, int 
base);
 char *EXPORT_FUNC(pupa_strdup) (const char *s);
@@ -55,5 +58,8 @@ int EXPORT_FUNC(pupa_vprintf) (const cha
 int EXPORT_FUNC(pupa_sprintf) (char *str, const char *fmt, ...) __attribute__ 
((format (printf, 2, 3)));
 int EXPORT_FUNC(pupa_vsprintf) (char *str, const char *fmt, va_list args);
 void EXPORT_FUNC(pupa_stop) (void) __attribute__ ((noreturn));
+pupa_err_t EXPORT_FUNC(pupa_split_cmdline) (const char *str, 
+                                           pupa_err_t (* getline) (char **),
+                                           int *argc, char ***argv);
 
 #endif /* ! PUPA_MISC_HEADER */
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/normal.h 
./include/pupa/normal.h
--- /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/normal.h    2003-12-03 
20:23:35.000000000 +0100
+++ ./include/pupa/normal.h     2003-12-28 02:13:47.000000000 +0100
@@ -25,6 +25,7 @@
 #include <pupa/setjmp.h>
 #include <pupa/symbol.h>
 #include <pupa/err.h>
+#include <pupa/arg.h>
 
 /* The maximum size of a command-line.  */
 #define PUPA_MAX_CMDLINE       1600
@@ -47,7 +48,7 @@ struct pupa_command
   const char *name;
 
   /* The callback function.  */
-  int (*func) (int argc, char *argv[]);
+  pupa_err_t (*func) (struct pupa_arg_list *state, int argc, char **args);
 
   /* The flags.  */
   unsigned flags;
@@ -58,6 +59,9 @@ struct pupa_command
   /* The description of the command.  */
   const char *description;
 
+  /* The argument parser optionlist.  */
+  const struct pupa_arg_option *options;
+
   /* The next element.  */
   struct pupa_command *next;
 };
@@ -121,10 +125,12 @@ void pupa_cmdline_run (int nested);
 int pupa_cmdline_get (const char *prompt, char cmdline[], unsigned max_len,
                      int echo_char, int readline);
 void EXPORT_FUNC(pupa_register_command) (const char *name,
-                           int (*func) (int argc, char *argv[]),
+                           pupa_err_t (*func) (struct pupa_arg_list *state,
+                                               int argc, char **args),
                            unsigned flags,
                            const char *summary,
-                           const char *description);
+                           const char *description,
+                           const struct pupa_arg_option *parser);
 void EXPORT_FUNC(pupa_unregister_command) (const char *name);
 pupa_command_t pupa_command_find (char *cmdline);
 pupa_err_t pupa_set_history (int newsize);
@@ -132,10 +138,25 @@ int pupa_iterate_commands (int (*iterate
 int pupa_command_execute (char *cmdline);
 void pupa_command_init (void);
 void pupa_normal_init_page (void);
+int pupa_arg_parse (pupa_command_t parser, int argc, char **argv,
+                   struct pupa_arg_list *usr, char ***args, int *argnum);
+
 
 #ifdef PUPA_UTIL
 void pupa_normal_init (void);
 void pupa_normal_fini (void);
+void pupa_hello_init (void);
+void pupa_hello_fini (void);
+void pupa_ls_init (void);
+void pupa_ls_fini (void);
+void pupa_cat_init (void);
+void pupa_cat_fini (void);
+void pupa_boot_init (void);
+void pupa_boot_fini (void);
+void pupa_cmp_init (void);
+void pupa_cmp_fini (void);
+void pupa_terminal_init (void);
+void pupa_terminal_fini (void);
 #endif
 
 #endif /* ! PUPA_NORMAL_HEADER */
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/term.h 
./include/pupa/term.h
--- /home/marco/hurd/hurdnew/pupa/pupa/include/pupa/term.h      2003-11-19 
20:29:07.000000000 +0100
+++ ./include/pupa/term.h       2003-12-25 15:42:28.000000000 +0100
@@ -125,6 +125,7 @@ void EXPORT_FUNC(pupa_setcolor) (pupa_ui
                                 pupa_uint8_t highlight_color);
 int EXPORT_FUNC(pupa_setcursor) (int on);
 void EXPORT_FUNC(pupa_refresh) (void);
+void EXPORT_FUNC(pupa_set_more) (int onoff);
 
 /* For convenience.  */
 #define PUPA_TERM_ASCII_CHAR(c)        ((c) & 0xff)
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/kern/disk.c ./kern/disk.c
--- /home/marco/hurd/hurdnew/pupa/pupa/kern/disk.c      2003-11-12 
21:38:57.000000000 +0100
+++ ./kern/disk.c       2003-12-08 12:34:41.000000000 +0100
@@ -25,6 +25,7 @@
 #include <pupa/machine/partition.h>
 #include <pupa/misc.h>
 #include <pupa/machine/time.h>
+#include <pupa/file.h>
 
 #define        PUPA_CACHE_TIMEOUT      2
 
@@ -499,3 +500,44 @@ pupa_disk_write (pupa_disk_t disk, unsig
 
   return pupa_errno;
 }
+
+pupa_err_t
+pupa_print_partinfo (pupa_device_t disk, char *partname)
+{
+  pupa_fs_t fs = 0;
+  pupa_device_t part;
+  char devname[20];
+      
+  pupa_sprintf (devname, "%s,%s", disk->disk->name, partname);
+  part = pupa_device_open (devname);
+  if (!part)
+    pupa_printf ("\tPartition num:%s, Filesystem cannot be accessed",
+                partname);
+  else
+    {
+      char *label;
+
+      fs = pupa_fs_probe (part);
+      /* Ignore all errors.  */
+      pupa_errno = 0;
+
+      pupa_printf ("\tPartition num:%s, Filesystem type %s",
+                  partname, fs ? fs->name : "Unknown");
+         
+      if (fs)
+       {
+         (fs->label) (part, &label);
+         if (pupa_errno == PUPA_ERR_NONE)
+           {
+             if (label && pupa_strlen (label))
+               pupa_printf (", Label: %s", label);
+             pupa_free (label);
+           }
+         pupa_errno = PUPA_ERR_NONE;
+       }
+      pupa_device_close (part);
+    }
+
+  pupa_printf ("\n");
+  return pupa_errno;
+}
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/kern/dl.c ./kern/dl.c
--- /home/marco/hurd/hurdnew/pupa/pupa/kern/dl.c        2003-01-20 
05:13:46.000000000 +0100
+++ ./kern/dl.c 2003-12-28 19:43:57.000000000 +0100
@@ -1,6 +1,7 @@
 /* dl.c - loadable module support */
 /*
  *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
  *  Copyright (C) 2002 Yoshinori K. Okuji <address@hidden>
  *
  *  PUPA is free software; you can redistribute it and/or modify
@@ -27,6 +28,7 @@
 #include <pupa/types.h>
 #include <pupa/symbol.h>
 #include <pupa/file.h>
+#include <pupa/env.h>
 
 #if PUPA_HOST_SIZEOF_VOID_P == 4
 
@@ -556,14 +558,13 @@ pupa_dl_load_file (const char *filename)
   return mod;
 }
 
-static char *pupa_dl_dir;
-
 /* Load a module using a symbolic name.  */
 pupa_dl_t
 pupa_dl_load (const char *name)
 {
   char *filename;
   pupa_dl_t mod;
+  char *pupa_dl_dir = pupa_env_get ("prefix");
 
   mod = pupa_dl_get (name);
   if (mod)
@@ -664,15 +665,3 @@ pupa_dl_unload_all (void)
        p->mod->ref_count--;
     }
 }
-
-void
-pupa_dl_set_prefix (const char *dir)
-{
-  pupa_dl_dir = (char *) dir;
-}
-
-const char *
-pupa_dl_get_prefix (void)
-{
-  return pupa_dl_dir;
-}
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/kern/env.c ./kern/env.c
--- /home/marco/hurd/hurdnew/pupa/pupa/kern/env.c       1970-01-01 
01:00:00.000000000 +0100
+++ ./kern/env.c        2003-12-28 20:16:48.000000000 +0100
@@ -0,0 +1,209 @@
+/* env.c - Environment variables */
+/*
+ *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
+ *
+ *  PUPA 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 PUPA; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <pupa/env.h>
+#include <pupa/misc.h>
+#include <pupa/mm.h>
+
+/* XXX: What would be a good size for the hashtable?  */
+#define        HASHSZ  123
+
+/* A hashtable for quick lookup of variables.  */
+static struct pupa_env_var *pupa_env[HASHSZ];
+
+/* The variables in a sorted list.  */
+static struct pupa_env_var *pupa_env_sorted;
+
+/* Return the hash representation of the string S.  */
+static unsigned int pupa_env_hashval (const char *s)
+{
+  unsigned int i = 0;
+
+  /* XXX: This can be done much more effecient.  */
+  while (*s)
+    i += 5 * *(s++);
+
+  return i % HASHSZ;
+}
+
+static struct pupa_env_var *
+pupa_env_find (const char *name)
+{
+  struct pupa_env_var *var;
+  int idx = pupa_env_hashval (name);
+
+  for (var = pupa_env[idx]; var; var = var->next)
+    if (! pupa_strcmp (var->name, name))
+      return var;
+  return 0;
+}
+
+pupa_err_t
+pupa_env_set (const char *var, const char *val)
+{
+  int idx = pupa_env_hashval (var);
+  struct pupa_env_var *env;
+  struct pupa_env_var *sort;
+  struct pupa_env_var **sortp;
+  
+  /* If the variable does already exist, just update the variable.  */
+  env = pupa_env_find (var);
+  if (env)
+    {
+      char *old = env->value;
+      env->value = pupa_strdup (val);
+      if (! env->name)
+       {
+         env->value = old;
+         return pupa_errno;
+       }
+
+      if (env->write_hook)
+       (env->write_hook) (env);
+
+      pupa_free (old);
+      return 0;
+    }
+
+  /* The variable does not exist, create it.  */
+  env = pupa_malloc (sizeof (struct pupa_env_var));
+  if (! env)
+    return pupa_errno;
+  
+  pupa_memset (env, 0, sizeof (struct pupa_env_var));
+  
+  env->name = pupa_strdup (var);
+  if (! env->name)
+    goto fail;
+  
+  env->value = pupa_strdup (val);
+  if (! env->name)
+    goto fail;
+  
+  /* Insert it in the hashtable.  */
+  env->prevp = &pupa_env[idx];
+  env->next = pupa_env[idx];
+  if (pupa_env[idx])
+    pupa_env[idx]->prevp = &env->next;
+  pupa_env[idx] = env;
+  
+  /* Insert it in the sorted list.  */
+  sortp = &pupa_env_sorted;
+  sort = pupa_env_sorted;
+  while (sort)
+    {
+      if (pupa_strcmp (sort->name, var) > 0)
+       break;
+      
+      sortp = &sort->sort_next;
+      sort = sort->sort_next;
+    }
+  env->sort_prevp = sortp;
+  env->sort_next = sort;
+  if (sort)
+    sort->sort_prevp = &env->sort_next;
+  *sortp = env;
+
+ fail:
+  if (pupa_errno)
+    {
+      pupa_free (env->name);
+      pupa_free (env->value);
+      pupa_free (env);
+    }
+  
+  return 0;
+}
+
+char *
+pupa_env_get (const char *name)
+{
+  struct pupa_env_var *env;
+  env = pupa_env_find (name);
+  if (! env)
+    return 0;
+
+  if (env->read_hook)
+    {
+      char *val;
+      env->read_hook (env, &val);
+
+      return val;
+    }
+
+  return env->value;
+}
+
+void
+pupa_env_unset (const char *name)
+{
+  struct pupa_env_var *env;
+  env = pupa_env_find (name);
+  if (! env)
+    return;
+
+  /* XXX: It is not possible to unset variables with a read or write
+     hook.  */
+  if (env->read_hook || env->write_hook)
+    return;
+
+  *env->prevp = env->next;
+  if (env->next)
+    env->next->prevp = env->prevp;
+
+  *env->sort_prevp = env->sort_next;
+  if (env->sort_next)
+    env->sort_next->sort_prevp = env->sort_prevp;
+
+  pupa_free (env->name);
+  pupa_free (env->value);
+  pupa_free (env);
+  return;
+}
+
+void
+pupa_env_iterate (int (* func) (struct pupa_env_var *var))
+{
+  struct pupa_env_var *env;
+  
+  for (env = pupa_env_sorted; env; env = env->sort_next)
+    if (func (env))
+      return;
+}
+
+pupa_err_t
+pupa_register_variable_hook (const char *var,
+                            pupa_err_t (*read_hook) (struct pupa_env_var *var, 
char **),
+                            pupa_err_t (*write_hook) (struct pupa_env_var 
*var))
+{
+  struct pupa_env_var *env = pupa_env_find (var);
+
+  if (! env)
+    if (pupa_env_set (var, "") != PUPA_ERR_NONE)
+      return pupa_errno;
+  
+  env = pupa_env_find (var);
+  /* XXX Insert an assertion?  */
+  
+  env->read_hook = read_hook;
+  env->write_hook = write_hook;
+
+  return PUPA_ERR_NONE;
+}
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/kern/i386/pc/init.c 
./kern/i386/pc/init.c
--- /home/marco/hurd/hurdnew/pupa/pupa/kern/i386/pc/init.c      2003-01-31 
04:26:56.000000000 +0100
+++ ./kern/i386/pc/init.c       2003-12-27 15:12:39.000000000 +0100
@@ -1,6 +1,7 @@
 /*
  *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
  *  Copyright (C) 2002  Yoshinori K. Okuji <address@hidden>
+ *  Copyright (C) 2003  Marco Gerards <address@hidden>.
  *
  *  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
@@ -29,6 +30,7 @@
 #include <pupa/dl.h>
 #include <pupa/misc.h>
 #include <pupa/loader.h>
+#include <pupa/env.h>
 
 struct mem_region
 {
@@ -222,6 +224,7 @@ pupa_machine_init (void)
   /* The memory system was initialized, thus register built-in devices.  */
   pupa_biosdisk_init ();
 
+
   /* Initialize the prefix.  */
-  pupa_dl_set_prefix (make_install_device ());
+  pupa_env_set ("prefix", make_install_device ());
 }
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/kern/main.c ./kern/main.c
--- /home/marco/hurd/hurdnew/pupa/pupa/kern/main.c      2003-01-20 
05:13:46.000000000 +0100
+++ ./kern/main.c       2003-12-27 15:13:18.000000000 +0100
@@ -2,6 +2,7 @@
 /*
  *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
  *  Copyright (C) 2002  Yoshinori K. Okuji <address@hidden>
+ *  Copyright (C) 2003  Marco Gerards <address@hidden>.
  *
  *  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
@@ -27,6 +28,7 @@
 #include <pupa/rescue.h>
 #include <pupa/file.h>
 #include <pupa/device.h>
+#include <pupa/env.h>
 
 /* Return the end of the core image.  */
 pupa_addr_t
@@ -65,7 +67,7 @@ pupa_set_root_dev (void)
 {
   const char *prefix;
 
-  prefix = pupa_dl_get_prefix ();
+  prefix = pupa_env_get ("prefix");
   
   if (prefix)
     {
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/kern/misc.c ./kern/misc.c
--- /home/marco/hurd/hurdnew/pupa/pupa/kern/misc.c      2003-12-03 
20:23:37.000000000 +0100
+++ ./kern/misc.c       2003-12-28 19:57:28.000000000 +0100
@@ -25,6 +25,7 @@
 #include <pupa/mm.h>
 #include <stdarg.h>
 #include <pupa/term.h>
+#include <pupa/env.h>
 
 void *
 pupa_memmove (void *dest, const void *src, pupa_size_t n)
@@ -214,6 +215,18 @@ pupa_isalpha (int c)
 }
 
 int
+pupa_isdigit (int c)
+{
+  return (c >= '0' && c <= '9');
+}
+
+int
+pupa_isgraph (int c)
+{
+  return (c >= '!' && c <= '~');
+}
+
+int
 pupa_tolower (int c)
 {
   if (c >= 'A' && c <= 'Z')
@@ -382,6 +395,24 @@ pupa_itoa (char *str, int c, unsigned n)
   return p;
 }
 
+static char *
+pupa_ftoa (char *str, double f, int round)
+{
+  unsigned int intp;
+  unsigned int fractp;
+  unsigned int power = 1;
+  int i;
+
+  for (i = 0; i < round; i++)
+    power *= 10;
+
+  intp = f;
+  fractp = (f - (float) intp) * power;
+
+  pupa_sprintf (str, "%d.%d", intp, fractp);
+  return str;
+}
+
 int
 pupa_vsprintf (char *str, const char *fmt, va_list args)
 {
@@ -389,6 +420,7 @@ pupa_vsprintf (char *str, const char *fm
   int count = 0;
   auto void write_char (unsigned char ch);
   auto void write_str (const char *s);
+  auto void write_fill (const char ch, int n);
   
   void write_char (unsigned char ch)
     {
@@ -405,6 +437,13 @@ pupa_vsprintf (char *str, const char *fm
       while (*s)
        write_char (*s++);
     }
+
+  void write_fill (const char ch, int n)
+    {
+      int i;
+      for (i = 0; i < n; i++)
+       write_char (ch);
+    }
   
   while ((c = *fmt++) != 0)
     {
@@ -414,10 +453,50 @@ pupa_vsprintf (char *str, const char *fm
        {
          char tmp[16];
          char *p;
+         unsigned int format1 = 0;
+         unsigned int format2 = 3;
+         char zerofill = ' ';
+         int rightfill = 0;
          int n;
          
-         c = *fmt++;
+         if (*fmt && *fmt =='-')
+           {
+             rightfill = 1;
+             fmt++;
+           }
          
+         p = (char *) fmt;
+         /* Read formatting parameters.  */
+         while (*p && pupa_isdigit (*p))
+           p++;
+
+         if (p > fmt)
+           {
+             char s[p - fmt];
+             pupa_strncpy (s, fmt, p - fmt);
+             if (s[0] == '0')
+               zerofill = '0';
+             format1 = pupa_strtoul (s, 0, 10);
+             fmt = p;
+             if (*p && *p == '.')
+               {
+                 p++;
+                 fmt++;
+                 while (*p && pupa_isdigit (*p))
+                   p++;
+                 
+                 if (p > fmt)
+                   {
+                     char fstr[p - fmt];
+                     pupa_strncpy (fstr, fmt, p - fmt);
+                     format2 = pupa_strtoul (fstr, 0, 10);
+                     fmt = p;
+                   }
+               }
+           }
+
+         c = *fmt++;
+
          switch (c)
            {
            case 'p':
@@ -429,14 +508,31 @@ pupa_vsprintf (char *str, const char *fm
            case 'd':
              n = va_arg (args, int);
              pupa_itoa (tmp, c, n);
+             if (!rightfill && pupa_strlen (tmp) < format1)
+               write_fill (zerofill, format1 - pupa_strlen (tmp));
              write_str (tmp);
+             if (rightfill && pupa_strlen (tmp) < format1)
+               write_fill (zerofill, format1 - pupa_strlen (tmp));
              break;
-
+             
            case 'c':
              n = va_arg (args, int);
              write_char (n & 0xff);
              break;
 
+           case 'f':
+             {
+               float f;
+               f = va_arg (args, double);
+               pupa_ftoa (tmp, f, format2);
+               if (!rightfill && pupa_strlen (tmp) < format1)
+                 write_fill (zerofill, format1 - pupa_strlen (tmp));
+               write_str (tmp);
+               if (rightfill && pupa_strlen (tmp) < format1)
+                 write_fill (zerofill, format1 - pupa_strlen (tmp));
+               break;
+             }
+             
            case 'C':
              {
                pupa_uint32_t code = va_arg (args, pupa_uint32_t);
@@ -490,9 +586,18 @@ pupa_vsprintf (char *str, const char *fm
            case 's':
              p = va_arg (args, char *);
              if (p)
-               write_str (p);
+               {
+                 if (!rightfill && pupa_strlen (p) < format1)
+                   write_fill (zerofill, format1 - pupa_strlen (p));
+                 
+                 write_str (p);
+                 
+                 if (rightfill && pupa_strlen (p) < format1)
+                   write_fill (zerofill, format1 - pupa_strlen (p));
+               }
              else
                write_str ("(null)");
+             
              break;
 
            default:
@@ -523,3 +628,224 @@ pupa_sprintf (char *str, const char *fmt
 
   return ret;
 }
+
+pupa_err_t
+pupa_split_cmdline (const char *cmdline, pupa_err_t (* getline) (char **), int 
*argc, char ***argv)
+{
+  /* XXX: Fixed size buffer, perhaps this buffer should be dynamically
+     allocated.  */
+  char buffer[1024];
+  char *bp = buffer;
+  char *rd = (char *) cmdline;
+  char unputbuf;
+  int unput = 0;
+  char *args;
+  int i;
+
+  auto char getchar (void);
+  auto void unputc (char c);
+  auto void getenvvar (void);
+  auto int getarg (void);
+
+  /* Get one character from the commandline.  If the caller reads
+     beyond the end of the string a new line will be read.  This
+     function will not chech for errors, the caller has to check for
+     pupa_errno.  */
+  char getchar (void)
+    {
+      int c;
+      if (unput)
+       {
+         unput = 0;
+         return unputbuf;
+       }
+
+      if (! rd)
+       {
+         getline (&rd);
+         /* Error is ignored here, the caller will check for this
+            when it reads beyond the EOL.  */
+         c = *(rd)++;
+         return c;
+       }
+
+      c = *(rd)++;
+      if (! c)
+       {
+         rd = 0;
+         return '\n';
+       }
+
+      return c;
+    }
+
+  void unputc (char c)
+    {
+      unputbuf = c;
+      unput = 1;
+    }
+
+  /* Read a variable name from the commandline and insert its content
+     into the buffer.  */
+  void getenvvar (void)
+    {
+      char varname[100];
+      char *p = varname;
+      char *val;
+      char c;
+
+      c = getchar ();
+      if (c == '{')
+       while ((c = getchar ()) != '}')
+         *(p++) = c;
+      else
+       {
+         /* XXX: An env. variable can have characters and digits in
+            its name, are more characters allowed here?  */
+         while (c && (pupa_isalpha (c) || pupa_isdigit (c)))
+           {
+             *(p++) = c;
+             c = getchar ();
+           }
+         unputc (c);
+       }
+      *p = '\0';
+
+      /* The variable does not exist.  */
+      val = pupa_env_get (varname);
+      if (! val)
+       return;
+
+      /* Copy the contents of the variable into the buffer.  */
+      for (p = val; *p; p++)
+       *(bp++) = *p;
+    }
+
+  /* Read one argument.  Return 1 if no variables can be read anymore,
+     otherwise return 0.  If there is an error, return 1, the caller
+     has to check pupa_errno.  */
+  int getarg (void)
+    {
+      char c;
+
+      /* Skip all whitespaces before an argument.  */
+      do {
+       c = getchar ();
+      } while (c == ' ' || c == '\t');
+
+      do {
+       switch (c)
+         {
+         case '"':
+           /* Double quote.  */
+           while ((c = getchar ()))
+             {
+               if (pupa_errno)
+                 return 1;
+               /* Read in an escaped character.  */
+               if (c == '\\')
+                 {
+                   c = getchar ();
+                   *(bp++) = c;
+                   continue;
+                 }
+               else if (c == '"')
+                 break;
+               /* Read a variable.  */
+               if (c == '$')
+                 {
+                   getenvvar ();
+                   continue;
+                 }
+               *(bp++) = c;
+             }
+           break;
+
+         case '\'':
+           /* Single quote.  */
+           while ((c = getchar ()) != '\'')
+             {
+               if (pupa_errno)
+                 return 1;
+
+               *(bp++) = c;
+             }
+           break;
+
+         case '\n':
+           /* This was not a argument afterall.  */
+           return 1;
+
+         default:
+           /* A normal option.  */
+           while (c && (pupa_isalpha (c)
+                        || pupa_isdigit (c) || pupa_isgraph (c)))
+             {
+               /* Read in an escaped character.  */
+               if (c == '\\')
+                 {
+                   c = getchar ();
+                   *(bp++) = c;
+                   c = getchar ();
+                   continue;
+                 }
+               /* Read a variable.  */
+               if (c == '$')
+                 {
+                   getenvvar ();
+                   c = getchar ();
+                   continue;
+                 }
+               *(bp++) = c;
+               c = getchar ();
+             }
+           unputc (c);
+
+           break;
+         }
+      } while (! pupa_isspace (c) && c != '\'' && c != '"');
+
+      return 0;
+    }
+
+  /* Read in all arguments and count them.  */
+  *argc = 0;
+  while (1)
+    {
+      if (getarg ())
+       break;
+      *(bp++) = '\0';
+      (*argc)++;
+    }
+
+  /* Check if there were no errors.  */
+  if (pupa_errno)
+    return pupa_errno;
+
+  /* Reserve memory for the return values.  */
+  args = pupa_malloc (bp - buffer);
+  if (! args)
+    return pupa_errno;
+  pupa_memcpy (args, buffer, bp - buffer);
+  
+  *argv = pupa_malloc (sizeof (char *) * (*argc + 1));
+  if (! *argv)
+    {
+      pupa_free (args);
+      return pupa_errno;
+    }
+
+  /* The arguments are separated with 0's, setup argv so it points to
+     the right values.  */
+  bp = args;
+  for (i = 0; i < *argc; i++)
+    {
+      (*argv)[i] = bp;
+      while (*bp)
+       bp++;
+      bp++;
+    }
+
+  (*argc)--;
+  return 0;
+}
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/kern/rescue.c ./kern/rescue.c
--- /home/marco/hurd/hurdnew/pupa/pupa/kern/rescue.c    2003-11-19 
20:29:09.000000000 +0100
+++ ./kern/rescue.c     2003-12-28 20:22:20.000000000 +0100
@@ -1,6 +1,7 @@
 /* rescue.c - rescue mode */
 /*
  *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
  *  Copyright (C) 2002  Yoshinori K. Okuji <address@hidden>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -29,6 +30,7 @@
 #include <pupa/loader.h>
 #include <pupa/dl.h>
 #include <pupa/machine/partition.h>
+#include <pupa/env.h>
 
 #define PUPA_RESCUE_BUF_SIZE   256
 #define PUPA_RESCUE_MAX_ARGS   20
@@ -116,29 +118,6 @@ pupa_rescue_get_command_line (const char
   pupa_refresh ();
 }
 
-/* Get the next word in STR and return a next pointer.  */
-static char *
-next_word (char **str)
-{
-  char *word;
-  char *p = *str;
-  
-  /* Skip spaces.  */
-  while (*p && pupa_isspace (*p))
-    p++;
-
-  word = p;
-
-  /* Find a space.  */
-  while (*p && ! pupa_isspace (*p))
-    p++;
-
-  *p = '\0';
-  *str = p + 1;
-  
-  return word;
-}
-
 /* boot */
 static void
 pupa_rescue_cmd_boot (int argc __attribute__ ((unused)),
@@ -506,27 +485,6 @@ pupa_rescue_cmd_dump (int argc, char *ar
     }
 }
 
-/* prefix [DIR] */
-static void
-pupa_rescue_cmd_prefix (int argc, char *argv[])
-{
-  static char prefix[100];
-  
-  if (argc == 0)
-    pupa_printf ("%s\n", pupa_dl_get_prefix ());
-  else
-    {
-      if (pupa_strlen (argv[0]) >= sizeof (prefix))
-       {
-         pupa_error (PUPA_ERR_BAD_ARGUMENT, "too long prefix");
-         return;
-       }
-      
-      pupa_strcpy (prefix, argv[0]);
-      pupa_dl_set_prefix (prefix);
-    }
-}
-
 /* insmod MODULE */
 static void
 pupa_rescue_cmd_insmod (int argc, char *argv[])
@@ -602,6 +560,52 @@ pupa_rescue_cmd_lsmod (int argc __attrib
   pupa_dl_iterate (print_module);
 }
 
+/* set ENVVAR=VALUE */
+static void
+pupa_rescue_cmd_set (int argc, char *argv[])
+{
+  char *var;
+  char *val;
+
+  auto int print_env (struct pupa_env_var *env);
+
+  int print_env (struct pupa_env_var *env)
+    {
+      pupa_printf ("%s=%s\n", env->name, env->value);
+      return 0;
+    }
+
+  if (argc < 1)
+    {
+      pupa_env_iterate (print_env);
+      return;
+    }
+
+  var = argv[0];
+  val = pupa_strchr (var, '=');
+  if (! val)
+    {
+      pupa_error (PUPA_ERR_BAD_ARGUMENT, "not an assignment");
+      return;
+    }
+
+  val[0] = 0;
+  pupa_env_set (var, val + 1);
+  val[0] = '=';
+}
+
+static void
+pupa_rescue_cmd_unset (int argc, char *argv[])
+{
+  if (argc < 1)
+    {
+      pupa_error (PUPA_ERR_BAD_ARGUMENT, "no environment variable specified");
+      return;
+    }
+
+  pupa_env_unset (argv[0]);
+}
+
 static void
 attempt_normal_mode (void)
 {
@@ -621,6 +625,15 @@ attempt_normal_mode (void)
 void
 pupa_enter_rescue_mode (void)
 {
+  auto pupa_err_t getline (char **line);
+  
+  pupa_err_t getline (char **line)
+    {
+      pupa_rescue_get_command_line ("> ");
+      *line = linebuf;
+      return 0;
+    }
+
   /* First of all, attempt to execute the normal mode.  */
   attempt_normal_mode ();
 
@@ -638,14 +651,16 @@ pupa_enter_rescue_mode (void)
                                "set the root device");
   pupa_rescue_register_command ("dump", pupa_rescue_cmd_dump,
                                "dump memory");
-  pupa_rescue_register_command ("prefix", pupa_rescue_cmd_prefix,
-                               "set the prefix");
   pupa_rescue_register_command ("insmod", pupa_rescue_cmd_insmod,
                                "insert a module");
   pupa_rescue_register_command ("rmmod", pupa_rescue_cmd_rmmod,
                                "remove a module");
   pupa_rescue_register_command ("lsmod", pupa_rescue_cmd_lsmod,
                                "show loaded modules");
+  pupa_rescue_register_command ("set", pupa_rescue_cmd_set,
+                               "set an environment variable");
+  pupa_rescue_register_command ("unset", pupa_rescue_cmd_unset,
+                               "remove an environment variable");
   
   while (1)
     {
@@ -653,36 +668,42 @@ pupa_enter_rescue_mode (void)
       char *name;
       int n;
       pupa_rescue_command_t cmd;
-      char *args[PUPA_RESCUE_MAX_ARGS + 1];
+      char **args;
+
+      /* Print an error, if any.  */
+      pupa_print_error ();
+      pupa_errno = PUPA_ERR_NONE;
 
       /* Get a command line.  */
       pupa_rescue_get_command_line ("pupa rescue> ");
 
+      if (pupa_split_cmdline (line, getline, &n, &args))
+       continue;
+
+      /* In case of an assignment set the environment accordingly
+        instead of calling a function.  */
+      if (n == 0 && pupa_strchr (line, '='))
+       {
+         char *val = pupa_strchr (args[0], '=');
+         val[0] = 0;
+         pupa_env_set (args[0], val + 1);
+         val[0] = '=';
+         continue;
+       }
+
       /* Get the command name.  */
-      name = next_word (&line);
+      name = args[0];
 
       /* If nothing is specified, restart.  */
       if (*name == '\0')
        continue;
 
-      /* Get arguments.  */
-      for (n = 0; n <= PUPA_RESCUE_MAX_ARGS; n++)
-       {
-         char *arg = next_word (&line);
-         
-         if (*arg)
-           args[n] = arg;
-         else
-           break;
-       }
-      args[n] = 0;
-
       /* Find the command and execute it.  */
       for (cmd = pupa_rescue_command_list; cmd; cmd = cmd->next)
        {
          if (pupa_strcmp (name, cmd->name) == 0)
            {
-             (cmd->func) (n, args);
+             (cmd->func) (n, &args[1]);
              break;
            }
        }
@@ -693,9 +714,5 @@ pupa_enter_rescue_mode (void)
          pupa_printf ("Unknown command `%s'\n", name);
          pupa_printf ("Try `help' for usage\n");
        }
-      
-      /* Print an error, if any.  */
-      pupa_print_error ();
-      pupa_errno = PUPA_ERR_NONE;
     }
 }
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/kern/term.c ./kern/term.c
--- /home/marco/hurd/hurdnew/pupa/pupa/kern/term.c      2003-11-19 
20:29:09.000000000 +0100
+++ ./kern/term.c       2003-12-28 19:45:10.000000000 +0100
@@ -1,6 +1,7 @@
 /*
  *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
  *  Copyright (C) 2002  Free Software Foundation, Inc.
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
  *  Copyright (C) 2002,2003  Yoshinori K. Okuji <address@hidden>
  *
  *  PUPA is free software; you can redistribute it and/or modify
@@ -21,6 +22,7 @@
 #include <pupa/term.h>
 #include <pupa/err.h>
 #include <pupa/mm.h>
+#include <pupa/misc.h>
 
 /* The list of terminals.  */
 static pupa_term_t pupa_term_list;
@@ -28,6 +30,12 @@ static pupa_term_t pupa_term_list;
 /* The current terminal.  */
 static pupa_term_t pupa_cur_term;
 
+/* The amount of lines counted by the pager.  */
+static int pupa_more_lines;
+
+/* If the more pager is active.  */
+static int pupa_more;
+
 void
 pupa_term_register (pupa_term_t term)
 {
@@ -98,7 +106,36 @@ pupa_putcode (pupa_uint32_t code)
   (pupa_cur_term->putchar) (code);
   
   if (code == '\n')
-    pupa_putcode ('\r');
+    {
+      pupa_putcode ('\r');
+
+      pupa_more_lines++;
+      /* XXX: Don't use a fixed height!  */
+      if (pupa_more && pupa_more_lines == 24 - 1)
+       {
+         char key;
+         int pos = pupa_getxy ();
+
+         /* Show --MORE-- on the lower left side of the screen.  */
+         pupa_gotoxy (1, 24 - 1);
+         pupa_setcolorstate (PUPA_TERM_COLOR_HIGHLIGHT);
+         pupa_printf ("--MORE--");
+         pupa_setcolorstate (PUPA_TERM_COLOR_STANDARD);
+
+         key = pupa_getkey ();
+         
+         /* Remove the message.  */
+         pupa_gotoxy (1, 24 -1);
+         pupa_printf ("        ");
+         pupa_gotoxy (pos >> 8, pos & 0xFF);
+         
+         /* Scroll one lines or an entire page, depending on the key.  */
+         if (key == '\r' || key =='\n')
+           pupa_more_lines--;
+         else
+           pupa_more_lines = 0;
+       }
+    }
 }
 
 /* Put a character. C is one byte of a UTF-8 stream.
@@ -236,3 +273,14 @@ pupa_refresh (void)
   if (pupa_cur_term->refresh)
     (pupa_cur_term->refresh) ();
 }
+
+void
+pupa_set_more (int onoff)
+{
+  if (onoff == 1)
+    pupa_more++;
+  else
+    pupa_more--;
+
+  pupa_more_lines = 0;
+}
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/loader/i386/pc/linux.c 
./loader/i386/pc/linux.c
--- /home/marco/hurd/hurdnew/pupa/pupa/loader/i386/pc/linux.c   2003-01-20 
05:13:46.000000000 +0100
+++ ./loader/i386/pc/linux.c    2003-12-28 19:45:36.000000000 +0100
@@ -2,6 +2,7 @@
 /*
  *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
  *  Copyright (C) 1999,2000,2001,2002  Free Software Foundation, Inc.
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
  *  Copyright (C) 2003  Yoshinori K. Okuji <address@hidden>
  *
  *  This program is free software; you can redistribute it and/or modify
@@ -283,7 +284,8 @@ pupa_rescue_cmd_linux (int argc, char *a
 }
 
 void
-pupa_rescue_cmd_initrd (int argc, char *argv[])
+pupa_rescue_cmd_initrd (int argc __attribute__ ((unused)),
+                       char *argv[] __attribute__ ((unused)))
 {
   pupa_error (PUPA_ERR_NOT_IMPLEMENTED_YET, "not implemented yet");
 }
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/loader/i386/pc/multiboot.c 
./loader/i386/pc/multiboot.c
--- /home/marco/hurd/hurdnew/pupa/pupa/loader/i386/pc/multiboot.c       
2003-11-16 17:36:39.000000000 +0100
+++ ./loader/i386/pc/multiboot.c        2003-12-27 22:10:36.000000000 +0100
@@ -61,7 +61,7 @@ pupa_multiboot_unload (void)
 {
   if (mbi)
     {
-      int i;
+      unsigned int i;
       for (i = 0; i < mbi->mods_count; i++)
        {
          pupa_free ((void *)
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/normal/arg.c ./normal/arg.c
--- /home/marco/hurd/hurdnew/pupa/pupa/normal/arg.c     1970-01-01 
01:00:00.000000000 +0100
+++ ./normal/arg.c      2003-12-28 20:40:57.000000000 +0100
@@ -0,0 +1,354 @@
+/* arg.c - argument parser */
+/*
+ *  PUPA  --  Preliminary Universal Programming Architecture for GRUB
+ *  Copyright (C) 2003 Marco Gerards <address@hidden>
+ *
+ *  PUPA 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 PUPA; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include "pupa/arg.h"
+#include "pupa/misc.h"
+#include "pupa/mm.h"
+#include "pupa/err.h"
+#include "pupa/normal.h"
+
+/* Build in parser for default options.  */
+static const struct pupa_arg_option help_options[] =
+  {
+    {"help", 'h', 0, "Display help", 0, ARG_TYPE_NONE},
+    {"usage", 'u', 0, "Show how to use this command", 0, ARG_TYPE_NONE},
+    {0, 0, 0, 0, 0, 0}
+  };
+
+static struct pupa_arg_option *
+find_short (const struct pupa_arg_option *options, char c)
+{
+  struct pupa_arg_option *found = 0;
+  auto struct pupa_arg_option *fnd_short (const struct pupa_arg_option *opt);
+
+  struct pupa_arg_option *fnd_short (const struct pupa_arg_option *opt)
+    {
+      while (opt->doc)
+       {
+         if (opt->shortarg == c)
+           return (struct pupa_arg_option *) opt;
+         opt++;
+       }
+      return 0;
+    }
+
+  if (options)
+    found = fnd_short (options);
+  if (! found)
+    found = fnd_short (help_options);
+    
+  return found;
+}
+
+static char *
+find_long_option (char *s)
+{
+  char *argpos = pupa_strchr (s, '=');
+
+  if (argpos)
+    {
+      *argpos = '\0';
+      return ++argpos;
+    }
+  return 0;
+}
+
+static struct pupa_arg_option *
+find_long (const struct pupa_arg_option *options, char *s)
+{
+  struct pupa_arg_option *found = 0;
+  auto struct pupa_arg_option *fnd_long (const struct pupa_arg_option *opt);
+
+  struct pupa_arg_option *fnd_long (const struct pupa_arg_option *opt)
+    {
+      while (opt->doc)
+       {
+         if (opt->longarg && !pupa_strcmp (opt->longarg, s))
+           return (struct pupa_arg_option *) opt;
+         opt++;
+       }
+      return 0;
+    }
+
+  if (options)
+    found = fnd_long (options);
+  if (!found)
+    found = fnd_long (help_options);
+    
+  return found;
+}
+
+static void
+show_usage (pupa_command_t cmd)
+{
+  pupa_printf ("Usage: %s\n", cmd->summary);
+}
+
+static void
+show_help (pupa_command_t cmd)
+{
+  static void showargs (const struct pupa_arg_option *opt)
+    {
+      for (; opt->doc; opt++)
+       {
+         if (opt->shortarg && pupa_isgraph (opt->shortarg))
+           pupa_printf ("-%c%c ", opt->shortarg, opt->longarg ? ',':' ');
+         else
+           pupa_printf ("    ");
+         if (opt->longarg)
+           {
+             pupa_printf ("--%s", opt->longarg);
+             if (opt->arg)
+               pupa_printf ("=%s", opt->arg);
+           }
+         else
+           pupa_printf ("\t");
+
+         pupa_printf ("\t\t%s\n", opt->doc);
+       }
+    }  
+
+  show_usage (cmd);
+  pupa_printf ("%s\n\n", cmd->description);
+  if (cmd->options)
+    showargs (cmd->options);
+  showargs (help_options);
+  pupa_printf ("\nReport bugs to <%s>.\n", PACKAGE_BUGREPORT);
+}
+
+
+static int
+parse_option (pupa_command_t cmd, int key, char *arg, struct pupa_arg_list 
*usr)
+{
+  switch (key)
+    {
+    case 'h':
+      show_help (cmd);
+      return -1;
+      
+    case 'u':
+      show_usage (cmd);
+      return -1;
+
+    default:
+      {
+       int found = -1;
+       int i = 0;
+       const struct pupa_arg_option *opt = cmd->options;
+
+       while (opt->doc)
+         {
+           if (opt->shortarg && key == opt->shortarg)
+             {
+               found = i;
+               break;
+             }
+           opt++;
+           i++;
+         }
+       
+       if (found == -1)
+         return -1;
+
+       usr[found].set = 1;
+       usr[found].arg = arg;
+      }
+    }
+  
+  return 0;
+}
+
+int
+pupa_arg_parse (pupa_command_t cmd, int argc, char **argv,
+               struct pupa_arg_list *usr, char ***args, int *argnum)
+{
+  int curarg;
+  char *longarg = 0;
+  int complete = 0;
+  char **argl = 0;
+  int num = 0;
+  auto pupa_err_t add_arg (char *s);
+
+  pupa_err_t add_arg (char *s)
+    {
+      argl = pupa_realloc (argl, (++num) * sizeof (char *));
+      if (! args)
+       return pupa_errno;
+      argl[num - 1] = s;
+      return 0;
+    }
+
+
+  for (curarg = 0; curarg < argc; curarg++)
+    {
+      char *arg = argv[curarg];
+      struct pupa_arg_option *opt;
+      char *option = 0;
+
+      /* No option is used.  */
+      if (arg[0] != '-' || pupa_strlen (arg) == 1)
+       {
+         if (add_arg (arg) != 0)
+           goto fail;
+  
+         continue;
+       }
+
+      /* One or more short options.  */
+      if (arg[1] != '-')
+       {
+         char *curshort = arg + 1;
+
+         while (1)
+           {
+             opt = find_short (cmd->options, *curshort);
+             if (!opt)
+               {
+                 pupa_error (PUPA_ERR_BAD_ARGUMENT,
+                             "Unknown argument `-%c'\n", *curshort);
+                 goto fail;
+               }
+             
+             curshort++;
+
+             /* Parse all arguments here except the last one because
+                it can have an argument value.  */
+             if (*curshort)
+               {
+                 if (parse_option (cmd, opt->shortarg, 0, usr) || pupa_errno)
+                   goto fail;
+               }
+             else
+               {
+                 if (opt->type != ARG_TYPE_NONE)
+                   {
+                     if (curarg + 1 < argc)
+                       {
+                         char *nextarg = argv[curarg + 1];
+                         if (!(opt->flags & PUPA_ARG_OPTION_OPTIONAL) 
+                             || (pupa_strlen (nextarg) < 2 || nextarg[0] != 
'-'))
+                           option = argv[++curarg];
+                       }
+                   }
+                 break;
+               }
+           }
+         
+       }
+      else /* The argument starts with "--".  */
+       {
+         /* If the argument "--" is used just pass the other
+            arguments.  */
+         if (pupa_strlen (arg) == 2)
+           {
+             for (curarg++; curarg < argc; curarg++)
+               if (add_arg (arg) != 0)
+                 goto fail;
+             break;
+           }
+
+         longarg = (char *) pupa_strdup (arg);
+         if (! longarg)
+           goto fail;
+
+         option = find_long_option (longarg);
+         arg = longarg;
+
+         opt = find_long (cmd->options, arg + 2);
+         if (!opt)
+           {
+             pupa_error (PUPA_ERR_BAD_ARGUMENT, "Unknown argument `%s'\n", 
arg);
+             goto fail;
+           }
+       }
+
+      if (! (opt->type == ARG_TYPE_NONE 
+            || (!option && (opt->flags & PUPA_ARG_OPTION_OPTIONAL))))
+       {
+         if (!option)
+           {
+             pupa_error (PUPA_ERR_BAD_ARGUMENT, 
+                         "Missing mandatory option for `%s'\n", opt->longarg);
+             goto fail;
+           }
+         
+         switch (opt->type)
+           {
+           case ARG_TYPE_NONE:
+             /* This will never happen.  */
+             break;
+             
+           case ARG_TYPE_STRING:
+                 /* No need to do anything.  */
+             break;
+             
+           case ARG_TYPE_INT:
+             {
+               char *tail;
+               
+               pupa_strtoul (option, &tail, 0);
+               if (tail == 0 || tail == option || *tail != '\0' || pupa_errno)
+                 {
+                   pupa_error (PUPA_ERR_BAD_ARGUMENT, 
+                               "The argument `%s' requires an integer.", 
+                               arg);
+
+                   goto fail;
+                 }
+               break;
+             }
+             
+           case ARG_TYPE_DEVICE:
+           case ARG_TYPE_DIR:
+           case ARG_TYPE_FILE:
+           case ARG_TYPE_PATHNAME:
+             /* XXX: Not implemented.  */
+             break;
+           }
+         if (parse_option (cmd, opt->shortarg, option, usr) || pupa_errno)
+           goto fail;
+       }
+      else
+       {
+         if (option)
+           {
+             pupa_error (PUPA_ERR_BAD_ARGUMENT, 
+                         "A value was assigned to the argument `%s' while it "
+                         "doesn't require an argument\n", arg);
+             goto fail;
+           }
+
+         if (parse_option (cmd, opt->shortarg, 0, usr) || pupa_errno)
+           goto fail;
+       }
+      pupa_free (longarg);
+      longarg = 0;
+    }      
+
+  complete = 1;
+
+  *args = argl;
+  *argnum = num;
+
+ fail:
+  pupa_free (longarg);
+ 
+  return complete;
+}
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/normal/cmdline.c 
./normal/cmdline.c
--- /home/marco/hurd/hurdnew/pupa/pupa/normal/cmdline.c 2003-12-03 
20:23:38.000000000 +0100
+++ ./normal/cmdline.c  2003-12-28 19:51:53.000000000 +0100
@@ -28,6 +28,7 @@
 #include <pupa/machine/partition.h>
 #include <pupa/disk.h>
 #include <pupa/file.h>
+#include <pupa/env.h>
 
 static char *kill_buf;
 
@@ -158,6 +159,18 @@ pupa_tab_complete (char *buf, int *resto
   /* String that is added when matched.  */
   char *matchstr;
 
+  auto void print_simple_completion (char *comp);
+  auto void print_partition_completion (char *comp);
+  auto int NESTED_FUNC_ATTR add_completion (const char *comp, const char 
*match,
+                                           const char *what, 
+                                           void (*print_completion) (char *));
+  auto int iterate_commands (pupa_command_t cmd);
+  auto int iterate_dev (const char *devname);
+  auto int iterate_part (const pupa_partition_t p);
+  auto int iterate_dir (const char *filename, int dir);
+  
+
+
   void print_simple_completion (char *comp)
     {
       pupa_printf (" %s", comp);
@@ -165,39 +178,8 @@ pupa_tab_complete (char *buf, int *resto
 
   void print_partition_completion (char *comp)
     {
-      pupa_fs_t fs = 0;
-      pupa_device_t part;
-      char devname[20];
-      
-      pupa_sprintf (devname, "%s,%s", partdev->disk->name, comp);
-      part = pupa_device_open (devname);
-      if (!part)
-       pupa_printf ("\n\tPartition num:%s, Filesystem cannot be accessed", 
-                    comp);
-      else
-       {
-         char *label;
-
-         fs = pupa_fs_probe (part);
-         /* Ignore all errors.  */
-         pupa_errno = 0;
-
-         pupa_printf ("\n\tPartition num:%s, Filesystem type %s",
-                      comp, fs ? fs->name : "Unknown");
-         
-         if (fs)
-           {
-             (fs->label) (part, &label);
-             if (pupa_errno == PUPA_ERR_NONE)
-               {
-                 if (label && pupa_strlen (label))
-                   pupa_printf (", Label: %s", label);
-                 pupa_free (label);
-               }
-             pupa_errno = PUPA_ERR_NONE;
-           }
-         pupa_device_close (part);
-       }
+      pupa_print_partinfo (partdev, comp);
+      pupa_errno = 0;
     }
 
   /* Add a string to the list of possible completions.  COMP is the
@@ -208,9 +190,9 @@ pupa_tab_complete (char *buf, int *resto
      multiple matches.  XXX: Because of a bug in gcc it is required to
      use __regparm__ in some cases.  */
 
-  int NESTED_FUNC_ATTR
-    add_completion (const char *comp, const char *match, const char *what,
-                   void (*print_completion) (char *))
+  int NESTED_FUNC_ATTR add_completion (const char *comp, const char *match,
+                                      const char *what, 
+                                      void (*print_completion) (char *))
     {
       /* Bug in strncmp then len ==0.  */
       if (!len || pupa_strncmp (pos, comp, len) == 0)
@@ -462,7 +444,6 @@ pupa_cmdline_run (int nested)
   while (1)
     {
       static char cmdline[PUPA_MAX_CMDLINE];
-      pupa_command_t cmd;
 
       pupa_print_error ();
       pupa_errno = PUPA_ERR_NONE;
@@ -475,17 +456,6 @@ pupa_cmdline_run (int nested)
       if (! *cmdline)
        continue;
 
-      cmd = pupa_command_find (cmdline);
-      if (! cmd)
-       continue;
-
-      if (! (cmd->flags & PUPA_COMMAND_FLAG_CMDLINE))
-       {
-         pupa_error (PUPA_ERR_UNKNOWN_COMMAND,
-                     "invalid command `%s'", cmd->name);
-         continue;
-       }
-
       pupa_command_execute (cmdline);
     }
 }
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/normal/command.c 
./normal/command.c
--- /home/marco/hurd/hurdnew/pupa/pupa/normal/command.c 2003-12-03 
20:23:38.000000000 +0100
+++ ./normal/command.c  2003-12-28 17:34:40.000000000 +0100
@@ -23,15 +23,19 @@
 #include <pupa/mm.h>
 #include <pupa/err.h>
 #include <pupa/term.h>
+#include <pupa/env.h>
+#include <pupa/dl.h>
 
 static pupa_command_t pupa_command_list;
 
 void
 pupa_register_command (const char *name,
-                      int (*func) (int argc, char *argv[]),
+                      pupa_err_t (*func) (struct pupa_arg_list *state,
+                                          int argc, char **args),
                       unsigned flags,
                       const char *summary,
-                      const char *description)
+                      const char *description,
+                      const struct pupa_arg_option *options)
 {
   pupa_command_t cmd, *p;
 
@@ -44,6 +48,7 @@ pupa_register_command (const char *name,
   cmd->flags = flags;
   cmd->summary = summary;
   cmd->description = description;
+  cmd->options = options;
 
   /* Keep the list sorted for simplicity.  */
   p = &pupa_command_list;
@@ -108,66 +113,69 @@ pupa_iterate_commands (int (*iterate) (p
 int
 pupa_command_execute (char *cmdline)
 {
-  pupa_command_t cmd;
-  char *p;
-  char **args;
-  int num = 0;
-  int i;
-  int ret;
-
-  cmd = pupa_command_find (cmdline);
-  if (! cmd)
-    return -1;
-
-  /* Count arguments.  */
-  p = cmdline;
-  while (1)
+  auto pupa_err_t cmdline_get (char **s);
+  pupa_err_t cmdline_get (char **s)
     {
-      while (*p && *p != ' ')
-       p++;
-
-      if (! *p)
-       break;
-      
-      while (*p == ' ')
-       p++;
-
-      num++;
+      *s = pupa_malloc (PUPA_MAX_CMDLINE);
+      *s[0] = '\0';
+      return pupa_cmdline_get (">", *s, PUPA_MAX_CMDLINE, 0, 1);
     }
 
-  args = (char **) pupa_malloc (sizeof (char *) * (num + 1));
-  if (! args)
-    return -1;
+  pupa_command_t cmd;
+  pupa_err_t ret = 0;
+  char *pager;
+  int num;
+  char **args;
+  struct pupa_arg_list *state;
+  struct pupa_arg_option *parser;
+  int maxargs = 0;
+  char **arglist;
+  int numargs;
 
-  /* Fill arguments.  */
-  for (i = 0, p = pupa_strchr (cmdline, ' '); i < num && p; i++)
+  if (pupa_split_cmdline (cmdline, cmdline_get, &num, &args))
+    return 0;
+  
+  /* In case of an assignment set the environment accordingly instead
+     of calling a function.  */
+  if (num == 0 && pupa_strchr (args[0], '='))
     {
-      if (! p)
-       break;
-      
-      while (*p == ' ')
-       p++;
-
-      args[i] = p;
-
-      while (*p && *p != ' ')
-       p++;
-
-      *p++ = '\0';
+      char *val = pupa_strchr (args[0], '=');
+      val[0] = 0;
+      pupa_env_set (args[0], val + 1);
+      val[0] = '=';
+      return 0;
     }
+  
+  cmd = pupa_command_find (args[0]);
+  if (! cmd)
+    return -1;
 
-  /* Terminate the array with NULL.  */
-  args[i] = 0;
-
-  ret = (cmd->func) (num, args);
+  /* Enable the pager if the environment pager is set to 1.  */
+  pager = pupa_env_get ("pager");
+  if (pager && (! pupa_strcmp (pager, "1")))
+    pupa_set_more (1);
+  
+  parser = (struct pupa_arg_option *) cmd->options;
+  while (parser && (parser++)->doc)
+    maxargs++;
+
+  state = pupa_malloc (sizeof (struct pupa_arg_list) * maxargs);
+  pupa_memset (state, 0, sizeof (struct pupa_arg_list) * maxargs);
+  if (pupa_arg_parse (cmd, num, &args[1], state, &arglist, &numargs))
+    ret = (cmd->func) (state, numargs, arglist);
+  pupa_free (state);
 
+  if (pager && (! pupa_strcmp (pager, "1")))
+    pupa_set_more (0);
+  
   pupa_free (args);
   return ret;
 }
 
-static int
-rescue_command (int argc __attribute__ ((unused)),
-               char *argv[] __attribute__ ((unused)))
+static pupa_err_t
+rescue_command (struct pupa_arg_list *state __attribute__ ((unused)),
+               int argc __attribute__ ((unused)),
+               char **args __attribute__ ((unused)))
 {
   pupa_longjmp (pupa_exit_env, 0);
 
@@ -175,61 +183,145 @@ rescue_command (int argc __attribute__ (
   return 0;
 }
 
-static int
-terminal_command (int argc, char *argv[])
+
+static pupa_err_t
+set_command (struct pupa_arg_list *state __attribute__ ((unused)),
+            int argc, char **args)
 {
-  pupa_term_t term = 0;
-  
-  auto int print_terminal (pupa_term_t);
-  auto int find_terminal (pupa_term_t);
-  
-  int print_terminal (pupa_term_t t)
+  char *var;
+  char *val;
+
+  auto int print_env (struct pupa_env_var *env);
+  int print_env (struct pupa_env_var *env)
     {
-      pupa_printf (" %s", t->name);
+      pupa_printf ("%s=%s\n", env->name, env->value);
       return 0;
     }
-
-  int find_terminal (pupa_term_t t)
+  
+  if (! argc)
     {
-      if (pupa_strcmp (t->name, argv[0]) == 0)
-       {
-         term = t;
-         return 1;
-       }
-
+      pupa_env_iterate (print_env);
       return 0;
     }
   
-  if (argc == 0)
+  var = args[0];
+  val = pupa_strchr (var, '=');
+  if (! val)
     {
-      pupa_printf ("Available terminal(s):");
-      pupa_term_iterate (print_terminal);
-      pupa_putchar ('\n');
-      
-      pupa_printf ("Current terminal: %s\n", pupa_term_get_current ()->name);
+      pupa_error (PUPA_ERR_BAD_ARGUMENT, "not an assignment");
+      return pupa_errno;
     }
+  
+  val[0] = 0;
+  pupa_env_set (var, val + 1);
+  val[0] = '=';
+  return 0;
+}
+
+static pupa_err_t
+unset_command (struct pupa_arg_list *state __attribute__ ((unused)),
+              int argc, char **args)
+{
+  if (argc < 1)
+    return pupa_error (PUPA_ERR_BAD_ARGUMENT,
+                      "no environment variable specified");
+
+  pupa_env_unset (args[0]);
+  return 0;
+}
+
+static pupa_err_t
+insmod_command (struct pupa_arg_list *state __attribute__ ((unused)),
+               int argc, char **args)
+{
+  char *p;
+  pupa_dl_t mod;
+  
+  if (argc == 0)
+    return pupa_error (PUPA_ERR_BAD_ARGUMENT, "no module specified");
+
+  p = pupa_strchr (args[0], '/');
+  if (! p)
+    mod = pupa_dl_load (args[0]);
   else
+    mod = pupa_dl_load_file (args[0]);
+
+  if (mod)
+    pupa_dl_ref (mod);
+
+  return 0;
+}
+
+static pupa_err_t
+rmmod_command (struct pupa_arg_list *state __attribute__ ((unused)),
+               int argc, char **args)
+{
+  pupa_dl_t mod;
+  
+  if (argc == 0)
+    return pupa_error (PUPA_ERR_BAD_ARGUMENT, "no module specified");
+  
+  mod = pupa_dl_get (args[0]);
+  if (! mod)
+    return pupa_error (PUPA_ERR_BAD_ARGUMENT, "no such module");
+
+  if (! pupa_dl_unref (mod))
+    pupa_dl_unload (mod);
+
+  return 0;
+}
+
+static pupa_err_t
+lsmod_command (struct pupa_arg_list *state __attribute__ ((unused)),
+              int argc __attribute__ ((unused)),
+              char **args __attribute__ ((unused)))
+{
+  auto int print_module (pupa_dl_t mod);
+
+  int print_module (pupa_dl_t mod)
     {
-      pupa_term_iterate (find_terminal);
-      if (! term)
-       return pupa_error (PUPA_ERR_BAD_ARGUMENT, "no such terminal");
+      pupa_dl_dep_t dep;
+      
+      pupa_printf ("%s\t%d\t\t", mod->name, mod->ref_count);
+      for (dep = mod->dep; dep; dep = dep->next)
+       {
+         if (dep != mod->dep)
+           pupa_putchar (',');
 
-      pupa_term_set_current (term);
+         pupa_printf ("%s", dep->mod->name);
+       }
+      pupa_putchar ('\n');
+      pupa_refresh ();
+
+      return 0;
     }
 
-  return PUPA_ERR_NONE;
+  pupa_printf ("Name\tRef Count\tDependencies\n");
+  pupa_dl_iterate (print_module);
+  return 0;
 }
 
 void
 pupa_command_init (void)
 {
   /* This is a special command, because this never be called actually.  */
-  pupa_register_command ("title", 0, PUPA_COMMAND_FLAG_TITLE, 0, 0);
+  pupa_register_command ("title", 0, PUPA_COMMAND_FLAG_TITLE, 0, 0, 0);
 
   pupa_register_command ("rescue", rescue_command, PUPA_COMMAND_FLAG_BOTH,
-                        "rescue",
-                        "Enter into the rescue mode.");
-  pupa_register_command ("terminal", terminal_command, PUPA_COMMAND_FLAG_BOTH,
-                        "terminal [TERM...]",
-                        "Select a terminal.");
+                        "rescue", "Enter into the rescue mode.", 0);
+
+  pupa_register_command ("set", set_command, PUPA_COMMAND_FLAG_BOTH,
+                        "unset ENVVAR", "Set an environment variable.", 0);
+
+  pupa_register_command ("unset", unset_command, PUPA_COMMAND_FLAG_BOTH,
+                        "set [ENVVAR=VALUE]", "Remove an environment 
variable.", 0);
+
+  pupa_register_command ("insmod", insmod_command, PUPA_COMMAND_FLAG_BOTH,
+                        "insmod MODULE|FILE", "Insert a module.", 0);
+
+  pupa_register_command ("rmmod", rmmod_command, PUPA_COMMAND_FLAG_BOTH,
+                        "rmmod MODULE", "Remove a module.", 0);
+
+  pupa_register_command ("lsmod", lsmod_command, PUPA_COMMAND_FLAG_BOTH,
+                        "lsmod", "Show loaded modules.", 0);
 }
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/normal/i386/setjmp.S 
./normal/i386/setjmp.S
--- /home/marco/hurd/hurdnew/pupa/pupa/normal/i386/setjmp.S     2003-01-20 
05:13:46.000000000 +0100
+++ ./normal/i386/setjmp.S      2003-12-27 22:19:37.000000000 +0100
@@ -54,4 +54,3 @@ FUNCTION(pupa_longjmp)
        jnz     1f
        incl    %eax
 1:     jmp     *%ecx
-       
\ No newline at end of file
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/normal/main.c ./normal/main.c
--- /home/marco/hurd/hurdnew/pupa/pupa/normal/main.c    2003-12-03 
20:23:38.000000000 +0100
+++ ./normal/main.c     2003-12-27 15:13:38.000000000 +0100
@@ -28,6 +28,7 @@
 #include <pupa/file.h>
 #include <pupa/mm.h>
 #include <pupa/term.h>
+#include <pupa/env.h>
 
 pupa_jmp_buf pupa_exit_env;
 
@@ -317,7 +318,7 @@ pupa_rescue_cmd_normal (int argc, char *
       char *config;
       const char *prefix;
       
-      prefix = pupa_dl_get_prefix ();
+      prefix = pupa_env_get ("prefix");
       if (prefix)
        {
          config = pupa_malloc (pupa_strlen (prefix) + sizeof ("/pupa.cfg"));
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/term/i386/pc/vga.c 
./term/i386/pc/vga.c
--- /home/marco/hurd/hurdnew/pupa/pupa/term/i386/pc/vga.c       2003-09-25 
22:15:52.000000000 +0200
+++ ./term/i386/pc/vga.c        2003-12-27 22:22:04.000000000 +0100
@@ -26,6 +26,7 @@
 #include <pupa/misc.h>
 #include <pupa/normal.h>
 #include <pupa/font.h>
+#include <pupa/arg.h>
 
 #define DEBUG_VGA      0
 
@@ -216,7 +217,7 @@ invalidate_char (struct colored_char *p)
 static int
 check_vga_mem (void *p)
 {
-  return p >= VGA_MEM && p <= VGA_MEM + VGA_WIDTH * VGA_HEIGHT / 8;
+  return p >= (void *) VGA_MEM && p <= (void *) (VGA_MEM + VGA_WIDTH * 
VGA_HEIGHT / 8);
 }
 
 static void
@@ -311,7 +312,9 @@ scroll_up (void)
 static void
 pupa_vga_putchar (pupa_uint32_t c)
 {
+#if DEBUG_VGA
   static int show = 1;
+#endif
   
   if (c == '\a')
     /* FIXME */
@@ -470,7 +473,8 @@ pupa_vga_putchar (pupa_uint32_t c)
  }
 
  static void
- pupa_vga_setcolor (pupa_uint8_t normal_color, pupa_uint8_t highlight_color)
+ pupa_vga_setcolor (pupa_uint8_t normal_color __attribute__ ((unused)),
+                   pupa_uint8_t highlight_color __attribute__ ((unused)))
  {
    /* FIXME */
  }
@@ -507,8 +511,10 @@ pupa_vga_putchar (pupa_uint32_t c)
      .next = 0
    };
 
-static int
-debug_command (int argc, char *argv[])
+static pupa_err_t
+debug_command (struct pupa_arg_list *state __attribute__ ((unused)),
+              int argc  __attribute__ ((unused)),
+              char **args __attribute__ ((unused)))
 {
   pupa_printf ("こんにちは\n");
 
@@ -520,7 +526,7 @@ PUPA_MOD_INIT
   my_mod = mod;
   pupa_term_register (&pupa_vga_term);
   pupa_register_command ("debug", debug_command, PUPA_COMMAND_FLAG_CMDLINE,
-                        "debug", "Debug it!");
+                        "debug", "Debug it!", 0);
 }
 
 PUPA_MOD_FINI
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/util/i386/pc/getroot.c 
./util/i386/pc/getroot.c
--- /home/marco/hurd/hurdnew/pupa/pupa/util/i386/pc/getroot.c   2003-11-17 
19:07:09.000000000 +0100
+++ ./util/i386/pc/getroot.c    2003-12-25 15:50:16.000000000 +0100
@@ -26,6 +26,7 @@
 #include <dirent.h>
 
 #include <pupa/util/misc.h>
+#include <pupa/i386/pc/util/biosdisk.h>
 
 static void
 strip_extra_slashes (char *dir)
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/util/misc.c ./util/misc.c
--- /home/marco/hurd/hurdnew/pupa/pupa/util/misc.c      2003-11-19 
20:29:11.000000000 +0100
+++ ./util/misc.c       2003-12-28 19:53:54.000000000 +0100
@@ -25,6 +25,7 @@
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/times.h>
+#include <malloc.h>
 
 #include <pupa/util/misc.h>
 #include <pupa/mm.h>
@@ -174,7 +175,7 @@ pupa_util_write_image (const char *img, 
 void *
 pupa_malloc (unsigned size)
 {
-  return malloc (size);
+  return xmalloc (size);
 }
 
 void
@@ -186,18 +187,25 @@ pupa_free (void *ptr)
 void *
 pupa_realloc (void *ptr, unsigned size)
 {
-  return realloc (ptr, size);
+  return xrealloc (ptr, size);
 }
 
 void *
 pupa_memalign (pupa_size_t align, pupa_size_t size)
 {
-  return memalign (align, size);
+  void *p;
+  
+  p = memalign (align, size);
+  if (! p)
+    pupa_util_error ("out of memory");
+  
+  return p;
 }
 
 /* Some functions that we don't use.  */
 void
-pupa_mm_init_region (void *addr, pupa_size_t size)
+pupa_mm_init_region (void *addr __attribute__ ((unused)),
+                    pupa_size_t size __attribute__ ((unused)))
 {
 }
 
diff -uprN /home/marco/hurd/hurdnew/pupa/pupa/util/pupa-emu.c ./util/pupa-emu.c
--- /home/marco/hurd/hurdnew/pupa/pupa/util/pupa-emu.c  2003-11-17 
19:07:08.000000000 +0100
+++ ./util/pupa-emu.c   2003-12-28 20:38:11.000000000 +0100
@@ -33,6 +33,7 @@
 #include <pupa/kernel.h>
 #include <pupa/normal.h>
 #include <pupa/util/getroot.h>
+#include <pupa/env.h>
 
 #ifdef __NetBSD__
 /* NetBSD uses /boot for its boot block.  */
@@ -77,11 +78,11 @@ const char *argp_program_bug_address = P
 static char doc[] = "PUPA emulator";
 
 static struct argp_option options[] = {
-  {"root-device", 'r', "DEV",  0, "use DEV as the root device 
[default=guessed]"},
-  {"device-map",  'm', "FILE", 0, "use FILE as the device map"},
-  {"directory",   'd', "DIR",  0, "use PUPA files in the directory DIR"},
-  {"verbose",     'v', 0     , 0, "print verbose messages"},
-  { 0 }
+  {"root-device", 'r', "DEV",  0, "use DEV as the root device 
[default=guessed]", 0},
+  {"device-map",  'm', "FILE", 0, "use FILE as the device map", 0},
+  {"directory",   'd', "DIR",  0, "use PUPA files in the directory DIR", 0},
+  {"verbose",     'v', 0     , 0, "print verbose messages", 0},
+  { 0, 0, 0, 0, 0, 0 }
 };
 
 struct arguments
@@ -118,7 +119,7 @@ parse_opt (int key, char *arg, struct ar
   return 0;
 }
 
-static struct argp argp = {options, parse_opt, 0, doc};
+static struct argp argp = {options, parse_opt, 0, doc, 0, 0, 0};

 
 int
@@ -148,7 +149,8 @@ main (int argc, char *argv[])
 
   prefix = pupa_get_prefix (args.dir ? : DEFAULT_DIRECTORY);
   sprintf (rootprefix, "%s%s", args.root_dev, prefix);
-  pupa_dl_set_prefix (rootprefix);
+
+  pupa_env_set ("prefix", rootprefix);
   
   /* XXX: This is a bit unportable.  */
   pupa_util_biosdisk_init (args.dev_map);
@@ -156,6 +158,11 @@ main (int argc, char *argv[])
   /* Initialize the default modules.  */
   pupa_fat_init ();
   pupa_ext2_init ();
+  pupa_ls_init ();
+  pupa_boot_init ();
+  pupa_cmp_init ();
+  pupa_cat_init ();
+  pupa_terminal_init ();
 
   /* XXX: Should normal mode be started by default?  */
   pupa_normal_init ();
@@ -167,6 +174,10 @@ main (int argc, char *argv[])
   pupa_normal_fini ();
   pupa_ext2_fini ();
   pupa_fat_fini ();
+  pupa_boot_fini ();
+  pupa_cmp_fini ();
+  pupa_cat_fini ();
+  pupa_terminal_fini ();
 
   return 0;
 }





reply via email to

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