c04da07444
* include/pupa/i386/pc/linux.h: New file. * loader/i386/pc/linux.c: Likewise. * loader/i386/pc/chainloader.c (pupa_chainloader_boot_sector): Removed. (pupa_chainloader_unload): Return PUPA_ERR_NONE. (pupa_rescue_cmd_chainloader): Read the image to 0x7C00 instead of PUPA_CHAINLOADER_BOOT_SECTOR. * kern/i386/pc/startup.S: Include pupa/machine/linux.h. (pupa_linux_prot_size): New variable. (pupa_linux_tmp_addr): Likewise. (pupa_linux_real_addr): Likewise. (pupa_linux_boot_zimage): New function. (pupa_linux_boot_bzimage): Likewise. * kern/i386/pc/init.c (struct mem_region): New structure. (MAX_REGIONS): New macro. (mem_regions): New variable. (num_regions): Likewise. (pupa_os_area_addr): Likewise. (pupa_os_area_size): Likewise. (pupa_lower_mem): Likewise. (pupa_upper_mem): Likewise. (add_mem_region): New function. (compact_mem_regions): Likewise. (pupa_machine_init): Set PUPA_LOWER_MEM and PUPA_UPPER_MEM to the size of the conventional memory and that of so-called upper memory (before the first memory hole). Instead of adding each found region to free memory, use add_mem_region and add them after removing overlaps. Also, add only 1/4 of the upper memory to free memory. The rest is used for loading OS images. Maybe this is ad hoc, but this makes it much easier to relocate OS images when booting. * kern/rescue.c (pupa_rescue_cmd_module): Removed. (pupa_enter_rescue_mode): Don't register initrd and module. * kern/mm.c: Include pupa/dl.h. * kern/main.c: Include pupa/file.h and pupa/device.h. * kern/loader.c (pupa_loader_load_module_func): Removed. (pupa_loader_load_module): Likewise. * kern/dl.c (pupa_dl_load): Use the suffix ``.mod'' instead of ``.o''. * include/pupa/i386/pc/loader.h (pupa_linux_prot_size): Declared. (pupa_linux_tmp_addr): Likewise. (pupa_linux_real_addr): Likewise. (pupa_linux_boot_zimage): Likewise. (pupa_linux_boot_bzimage): Likewise. * include/pupa/i386/pc/init.h (pupa_lower_mem): Declared. (pupa_upper_mem): Likewise. (pupa_gate_a20): Don't export, because turning off Gate A20 in a module is too dangerous. * include/pupa/loader.h (pupa_os_area_addr): Declared. (pupa_os_area_size): Likewise. (pupa_loader_set): Remove the first argument. Loader doesn't manage modules or initrd any longer. (pupa_loader_load_module): Removed. * conf/i386-pc.rmk (pkgdata_MODULES): Added linux.mod. (linux_mod_SOURCES): New variable. (linux_mod_CFLAGS): Likewise.
669 lines
14 KiB
C
669 lines
14 KiB
C
/* rescue.c - rescue mode */
|
|
/*
|
|
* PUPA -- Preliminary Universal Programming Architecture for GRUB
|
|
* Copyright (C) 2002 Yoshinori K. Okuji <okuji@enbug.org>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <pupa/kernel.h>
|
|
#include <pupa/rescue.h>
|
|
#include <pupa/term.h>
|
|
#include <pupa/misc.h>
|
|
#include <pupa/disk.h>
|
|
#include <pupa/file.h>
|
|
#include <pupa/mm.h>
|
|
#include <pupa/err.h>
|
|
#include <pupa/loader.h>
|
|
#include <pupa/dl.h>
|
|
#include <pupa/machine/partition.h>
|
|
|
|
#define PUPA_RESCUE_BUF_SIZE 256
|
|
#define PUPA_RESCUE_MAX_ARGS 20
|
|
|
|
struct pupa_rescue_command
|
|
{
|
|
const char *name;
|
|
void (*func) (int argc, char *argv[]);
|
|
const char *message;
|
|
struct pupa_rescue_command *next;
|
|
};
|
|
typedef struct pupa_rescue_command *pupa_rescue_command_t;
|
|
|
|
static char linebuf[PUPA_RESCUE_BUF_SIZE];
|
|
|
|
static pupa_rescue_command_t pupa_rescue_command_list;
|
|
|
|
void
|
|
pupa_rescue_register_command (const char *name,
|
|
void (*func) (int argc, char *argv[]),
|
|
const char *message)
|
|
{
|
|
pupa_rescue_command_t cmd;
|
|
|
|
cmd = (pupa_rescue_command_t) pupa_malloc (sizeof (*cmd));
|
|
if (! cmd)
|
|
return;
|
|
|
|
cmd->name = name;
|
|
cmd->func = func;
|
|
cmd->message = message;
|
|
|
|
cmd->next = pupa_rescue_command_list;
|
|
pupa_rescue_command_list = cmd;
|
|
}
|
|
|
|
void
|
|
pupa_rescue_unregister_command (const char *name)
|
|
{
|
|
pupa_rescue_command_t *p, q;
|
|
|
|
for (p = &pupa_rescue_command_list, q = *p; q; p = &(q->next), q = q->next)
|
|
if (pupa_strcmp (name, q->name) == 0)
|
|
{
|
|
*p = q->next;
|
|
pupa_free (q);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Prompt to input a command and read the line. */
|
|
static void
|
|
pupa_rescue_get_command_line (const char *prompt)
|
|
{
|
|
int c;
|
|
int pos = 0;
|
|
|
|
pupa_printf (prompt);
|
|
pupa_memset (linebuf, 0, PUPA_RESCUE_BUF_SIZE);
|
|
|
|
while ((c = PUPA_TERM_ASCII_CHAR (pupa_getkey ())) != '\n' && c != '\r')
|
|
{
|
|
if (pupa_isprint (c))
|
|
{
|
|
if (pos < PUPA_RESCUE_BUF_SIZE - 1)
|
|
{
|
|
linebuf[pos++] = c;
|
|
pupa_putchar (c);
|
|
}
|
|
}
|
|
else if (c == '\b')
|
|
{
|
|
if (pos > 0)
|
|
{
|
|
linebuf[--pos] = 0;
|
|
pupa_putchar (c);
|
|
pupa_putchar (' ');
|
|
pupa_putchar (c);
|
|
}
|
|
}
|
|
}
|
|
|
|
pupa_putchar ('\n');
|
|
}
|
|
|
|
/* 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)),
|
|
char *argv[] __attribute__ ((unused)))
|
|
{
|
|
pupa_loader_boot ();
|
|
}
|
|
|
|
/* cat FILE */
|
|
static void
|
|
pupa_rescue_cmd_cat (int argc, char *argv[])
|
|
{
|
|
pupa_file_t file;
|
|
char buf[PUPA_DISK_SECTOR_SIZE];
|
|
pupa_ssize_t size;
|
|
|
|
if (argc < 1)
|
|
{
|
|
pupa_error (PUPA_ERR_BAD_ARGUMENT, "no file specified");
|
|
return;
|
|
}
|
|
|
|
file = pupa_file_open (argv[0]);
|
|
if (! file)
|
|
return;
|
|
|
|
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_file_close (file);
|
|
}
|
|
|
|
static int
|
|
pupa_rescue_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)
|
|
{
|
|
pupa_printf ("(%s,%s) ", name, pname);
|
|
pupa_free (pname);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
dev = pupa_device_open (name);
|
|
pupa_errno = PUPA_ERR_NONE;
|
|
|
|
if (dev)
|
|
{
|
|
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;
|
|
}
|
|
|
|
static int
|
|
pupa_rescue_print_files (const char *filename, int dir)
|
|
{
|
|
pupa_printf ("%s%s ", filename, dir ? "/" : "");
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* ls [ARG] */
|
|
static void
|
|
pupa_rescue_cmd_ls (int argc, char *argv[])
|
|
{
|
|
if (argc < 1)
|
|
{
|
|
pupa_disk_dev_iterate (pupa_rescue_print_disks);
|
|
pupa_putchar ('\n');
|
|
}
|
|
else
|
|
{
|
|
char *device_name;
|
|
pupa_device_t dev;
|
|
pupa_fs_t fs;
|
|
char *path;
|
|
|
|
device_name = pupa_file_get_device_name (argv[0]);
|
|
dev = pupa_device_open (device_name);
|
|
if (! dev)
|
|
goto fail;
|
|
|
|
fs = pupa_fs_probe (dev);
|
|
path = pupa_strchr (argv[0], '/');
|
|
|
|
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)
|
|
{
|
|
(fs->dir) (dev, path, pupa_rescue_print_files);
|
|
pupa_putchar ('\n');
|
|
}
|
|
|
|
fail:
|
|
if (dev)
|
|
pupa_device_close (dev);
|
|
|
|
pupa_free (device_name);
|
|
}
|
|
}
|
|
|
|
/* help */
|
|
static void
|
|
pupa_rescue_cmd_help (int argc __attribute__ ((unused)),
|
|
char *argv[] __attribute__ ((unused)))
|
|
{
|
|
pupa_rescue_command_t p, q;
|
|
|
|
/* Sort the commands. This is not a good algorithm, but this is enough,
|
|
because rescue mode has a small number of commands. */
|
|
for (p = pupa_rescue_command_list; p; p = p->next)
|
|
for (q = p->next; q; q = q->next)
|
|
if (pupa_strcmp (p->name, q->name) > 0)
|
|
{
|
|
struct pupa_rescue_command tmp;
|
|
|
|
tmp.name = p->name;
|
|
tmp.func = p->func;
|
|
tmp.message = p->message;
|
|
|
|
p->name = q->name;
|
|
p->func = q->func;
|
|
p->message = q->message;
|
|
|
|
q->name = tmp.name;
|
|
q->func = tmp.func;
|
|
q->message = tmp.message;
|
|
}
|
|
|
|
/* Print them. */
|
|
for (p = pupa_rescue_command_list; p; p = p->next)
|
|
pupa_printf ("%s\t%s\n", p->name, p->message);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
pupa_rescue_cmd_info (void)
|
|
{
|
|
extern void pupa_disk_cache_get_performance (unsigned long *,
|
|
unsigned long *);
|
|
unsigned long hits, misses;
|
|
|
|
pupa_disk_cache_get_performance (&hits, &misses);
|
|
pupa_printf ("Disk cache: hits = %u, misses = %u ", hits, misses);
|
|
if (hits + misses)
|
|
{
|
|
unsigned long ratio = hits * 10000 / (hits + misses);
|
|
pupa_printf ("(%u.%u%%)\n", ratio / 100, ratio % 100);
|
|
}
|
|
else
|
|
pupa_printf ("(N/A)\n");
|
|
}
|
|
#endif
|
|
|
|
/* root [DEVICE] */
|
|
static void
|
|
pupa_rescue_cmd_root (int argc, char *argv[])
|
|
{
|
|
pupa_device_t dev;
|
|
pupa_fs_t fs;
|
|
|
|
if (argc > 0)
|
|
{
|
|
char *device_name = pupa_file_get_device_name (argv[0]);
|
|
if (! device_name)
|
|
return;
|
|
|
|
pupa_device_set_root (device_name);
|
|
pupa_free (device_name);
|
|
}
|
|
|
|
dev = pupa_device_open (0);
|
|
if (! dev)
|
|
return;
|
|
|
|
fs = pupa_fs_probe (dev);
|
|
if (pupa_errno == PUPA_ERR_UNKNOWN_FS)
|
|
pupa_errno = PUPA_ERR_NONE;
|
|
|
|
pupa_printf ("(%s): Filesystem is %s.\n",
|
|
pupa_device_get_root (), fs ? fs->name : "unknown");
|
|
|
|
pupa_device_close (dev);
|
|
}
|
|
|
|
#if 0
|
|
static void
|
|
pupa_rescue_cmd_testload (int argc, char *argv[])
|
|
{
|
|
pupa_file_t file;
|
|
char *buf;
|
|
pupa_ssize_t size;
|
|
pupa_ssize_t pos;
|
|
auto void read_func (unsigned long sector, unsigned offset, unsigned len);
|
|
|
|
void read_func (unsigned long sector __attribute__ ((unused)),
|
|
unsigned offset __attribute__ ((unused)),
|
|
unsigned len __attribute__ ((unused)))
|
|
{
|
|
pupa_putchar ('.');
|
|
}
|
|
|
|
if (argc < 1)
|
|
{
|
|
pupa_error (PUPA_ERR_BAD_ARGUMENT, "no file specified");
|
|
return;
|
|
}
|
|
|
|
file = pupa_file_open (argv[0]);
|
|
if (! file)
|
|
return;
|
|
|
|
size = pupa_file_size (file) & ~(PUPA_DISK_SECTOR_SIZE - 1);
|
|
if (size == 0)
|
|
{
|
|
pupa_file_close (file);
|
|
return;
|
|
}
|
|
|
|
buf = pupa_malloc (size);
|
|
if (! buf)
|
|
goto fail;
|
|
|
|
pupa_printf ("Reading %s sequentially", argv[0]);
|
|
file->read_hook = read_func;
|
|
if (pupa_file_read (file, buf, size) != size)
|
|
goto fail;
|
|
pupa_printf (" Done.\n");
|
|
|
|
/* Read sequentially again. */
|
|
pupa_printf ("Reading %s sequentially again", argv[0]);
|
|
if (pupa_file_seek (file, 0) < 0)
|
|
goto fail;
|
|
|
|
for (pos = 0; pos < size; pos += PUPA_DISK_SECTOR_SIZE)
|
|
{
|
|
char sector[PUPA_DISK_SECTOR_SIZE];
|
|
|
|
if (pupa_file_read (file, sector, PUPA_DISK_SECTOR_SIZE)
|
|
!= PUPA_DISK_SECTOR_SIZE)
|
|
goto fail;
|
|
|
|
if (pupa_memcmp (sector, buf + pos, PUPA_DISK_SECTOR_SIZE) != 0)
|
|
{
|
|
pupa_printf ("\nDiffers in %d\n", pos);
|
|
goto fail;
|
|
}
|
|
}
|
|
pupa_printf (" Done.\n");
|
|
|
|
/* Read backwards and compare. */
|
|
pupa_printf ("Reading %s backwards", argv[0]);
|
|
pos = size;
|
|
while (pos > 0)
|
|
{
|
|
char sector[PUPA_DISK_SECTOR_SIZE];
|
|
|
|
pos -= PUPA_DISK_SECTOR_SIZE;
|
|
|
|
if (pupa_file_seek (file, pos) < 0)
|
|
goto fail;
|
|
|
|
if (pupa_file_read (file, sector, PUPA_DISK_SECTOR_SIZE)
|
|
!= PUPA_DISK_SECTOR_SIZE)
|
|
goto fail;
|
|
|
|
if (pupa_memcmp (sector, buf + pos, PUPA_DISK_SECTOR_SIZE) != 0)
|
|
{
|
|
int i;
|
|
|
|
pupa_printf ("\nDiffers in %d\n", pos);
|
|
|
|
for (i = 0; i < PUPA_DISK_SECTOR_SIZE; i++)
|
|
pupa_putchar (buf[pos + i]);
|
|
|
|
goto fail;
|
|
}
|
|
}
|
|
pupa_printf (" Done.\n");
|
|
|
|
fail:
|
|
|
|
pupa_file_close (file);
|
|
pupa_free (buf);
|
|
}
|
|
#endif
|
|
|
|
/* dump ADDRESS [SIZE] */
|
|
static void
|
|
pupa_rescue_cmd_dump (int argc, char *argv[])
|
|
{
|
|
pupa_uint8_t *addr;
|
|
pupa_size_t size = 4;
|
|
|
|
if (argc == 0)
|
|
{
|
|
pupa_error (PUPA_ERR_BAD_ARGUMENT, "no address specified");
|
|
return;
|
|
}
|
|
|
|
addr = (pupa_uint8_t *) pupa_strtoul (argv[0], 0, 0);
|
|
if (pupa_errno)
|
|
return;
|
|
|
|
if (argc > 1)
|
|
size = (pupa_size_t) pupa_strtoul (argv[1], 0, 0);
|
|
|
|
while (size--)
|
|
{
|
|
pupa_printf ("%x%x ", *addr >> 4, *addr & 0xf);
|
|
addr++;
|
|
}
|
|
}
|
|
|
|
/* 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[])
|
|
{
|
|
char *p;
|
|
pupa_dl_t mod;
|
|
|
|
if (argc == 0)
|
|
{
|
|
pupa_error (PUPA_ERR_BAD_ARGUMENT, "no module specified");
|
|
return;
|
|
}
|
|
|
|
p = pupa_strchr (argv[0], '/');
|
|
if (! p)
|
|
mod = pupa_dl_load (argv[0]);
|
|
else
|
|
mod = pupa_dl_load_file (argv[0]);
|
|
|
|
if (mod)
|
|
pupa_dl_ref (mod);
|
|
}
|
|
|
|
/* rmmod MODULE */
|
|
static void
|
|
pupa_rescue_cmd_rmmod (int argc, char *argv[])
|
|
{
|
|
pupa_dl_t mod;
|
|
|
|
if (argc == 0)
|
|
{
|
|
pupa_error (PUPA_ERR_BAD_ARGUMENT, "no module specified");
|
|
return;
|
|
}
|
|
|
|
mod = pupa_dl_get (argv[0]);
|
|
if (! mod)
|
|
{
|
|
pupa_error (PUPA_ERR_BAD_ARGUMENT, "no such module");
|
|
return;
|
|
}
|
|
|
|
pupa_dl_unref (mod);
|
|
}
|
|
|
|
/* lsmod */
|
|
static void
|
|
pupa_rescue_cmd_lsmod (int argc __attribute__ ((unused)),
|
|
char *argv[] __attribute__ ((unused)))
|
|
{
|
|
auto int print_module (pupa_dl_t mod);
|
|
|
|
int print_module (pupa_dl_t mod)
|
|
{
|
|
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_printf ("%s", dep->mod->name);
|
|
}
|
|
pupa_putchar ('\n');
|
|
return 0;
|
|
}
|
|
|
|
pupa_printf ("Name\tRef Count\tDependencies\n");
|
|
pupa_dl_iterate (print_module);
|
|
}
|
|
|
|
/* Enter the rescue mode. */
|
|
void
|
|
pupa_enter_rescue_mode (void)
|
|
{
|
|
pupa_rescue_register_command ("boot", pupa_rescue_cmd_boot,
|
|
"boot an operating system");
|
|
pupa_rescue_register_command ("cat", pupa_rescue_cmd_cat,
|
|
"show the contents of a file");
|
|
pupa_rescue_register_command ("help", pupa_rescue_cmd_help,
|
|
"show this message");
|
|
pupa_rescue_register_command ("ls", pupa_rescue_cmd_ls,
|
|
"list devices or files");
|
|
pupa_rescue_register_command ("root", pupa_rescue_cmd_root,
|
|
"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");
|
|
|
|
while (1)
|
|
{
|
|
char *line = linebuf;
|
|
char *name;
|
|
int n;
|
|
pupa_rescue_command_t cmd;
|
|
char *args[PUPA_RESCUE_MAX_ARGS + 1];
|
|
|
|
/* Get a command line. */
|
|
pupa_rescue_get_command_line ("pupa rescue> ");
|
|
|
|
/* Get the command name. */
|
|
name = next_word (&line);
|
|
|
|
/* 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);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* If not found, print an error message. */
|
|
if (! cmd)
|
|
{
|
|
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;
|
|
}
|
|
}
|