grub/normal/completion.c
bean b1b797cb1b 2009-03-21 Bean <bean123ch@gmail.com>
* commands/blocklist.c: Add include file <grub/command.h>, remove
	<grub/normal.h> and <grub/arg.h>.
	(grub_cmd_blocklist): Use the new command interface.
	(GRUB_MOD_INIT): Likewise.
	(GRUB_MOD_FINI): Likewise.
	* commands/boot.c: Likewise.
	* commands/cat.c: Likewise.
	* commands/cmp.c: Likewise.
	* commands/configfile.c: Likewise.
	* commands/crc.c: Likewise.
	* commands/echo.c: Likewise.
	* commands/halt.c: Likewise.
	* commands/handler.c: Likewise.
	* commands/hdparm.c: Likewise.
	* commands/help.c: Likewise.
	* commands/hexdump.c: Likewise.
	* commands/loadenv.c: Likewise.
	* commands/ls.c: Likewise.
	* commands/lsmmap.c: Likewise.
	* commands/lspci.c: Likewise.
	* commands/loadenv.c: Likewise.
	* commands/read.c: Likewise.
	* commands/reboot.c: Likewise.
	* commands/search.c: Likewise.
	* commands/sleep.c: Likewise.
	* commands/test.c: Likewise.
	* commands/usbtest.c: Likewise.
	* commands/videotest.c: Likewise.
	* commands/i386/cpuid.c: Likewise.
	* commands/i386/pc/halt.c: Likewise.
	* commands/i386/pc/play.c: Likewise.
	* commands/i386/pc/pxecmd.c: Likewise.
	* commands/i386/pc/vbeinfo.c: Likewise.
	* commands/i386/pc/vbetest.c: Likewise.
	* commands/ieee1275/suspend.c: Likewise.
	* disk/loopback.c: Likewise.
	* font/font_cmd.c: Likewise.
	* hello/hello.c: Likewise.
	* loader/efi/appleloader.c: Likewise.
	* loader/efi/chainloader.c: Likewise.
	* loader/i386/bsd.c: Likewise.
	* loader/i386/efi/linux.c: Likewise.
	* loader/i386/ieee1275/linux.c: Likewise.
	* loader/i386/linux.c: Likewise.
	* loader/i386/pc/chainloader.c: Likewise.
	* loader/i386/pc/linux.c: Likewise.
	* loader/powerpc/ieee1275/linux.c: Likewise.
	* loader/multiboot_loader.c: Likewise.
	* term/gfxterm.c: Likewise.
	* term/i386/pc/serial.c: Likewise.
	* term/terminfo.c: Likewise.

	* term/i386/pc/vesafb.c: Removed <grub/arg.h>.
	* term/i386/pc/vga.c: Likewise.
	* video/readers/jpeg.c: Likewise.
	* video/readers/png.c: Likewise.
	* video/readers/tga.c: Likewise.

	* util/grub-fstest (cmd_loopback): Removed.
	(cmd_blocklist): Likewise.
	(cmd_ls): Likewise.
	(grub_register_command): Likewise.
	(grub_unregister_command): Likewise.
	(execute_command): Use grub_command_find to locate command and execute
	it.

	* include/grub/efi/chainloader.h: Removed.
	* loader/efi/chainloader_normal.c: Likewise.
	* loader/i386/bsd_normal.c: Likewise.
	* loader/i386/pc/chainloader_normal.c: Likewise.
	* loader/i386/pc/multiboot_normal.c: Likewise.
	* loader/linux_normal.c: Likewise.
	* loader/multiboot_loader_normal.c: Likewise.
	* loader/powerpc/ieee1275/linux_normal.c: Likewise.

	* gencmdlist.sh: Scan new registration command grub_register_extcmd
	and grub_register_command_p1.

	* conf/common.rmk (grub_fstest_SOURCES): Add kern/list.c,
	kern/command.c, lib/arg.c and commands/extcmd.c.
	(pkglib_MODULES): Remove boot.mod, and minicmd.mod and extcmd.mod.
	(minicmd_mod_SOURCES): New variable.
	(minicmd_mod_CFLAGS): Likewise.
	(minicmd_mod_LDFLAGS): Likewise.
	(extcmd_mod_SOURCES): Likewise.
	(extcmd_mod_CFLAGS): Likewise.
	(extcmd_mod_LDFLAGS): Likewise.
	(boot_mod_SOURCES): Removed.
	(boot_mod_CFLAGS): Likewise.
	(boot_mod_LDFLAGS): Likewise.

	* conf/i386-pc.rmk (kernel_img_SOURCES): Add kern/command.c and
	kern/corecmd.c.
	(kernel_img_HEADERS): Add command.h.
	(grub_emu_SOURCES): Remove commands/boot.c and normal/arg.c, add
	commands/minicmd.c, kern/command.c, kern/corecmd.c, commands/extcmd.c
	and lib/arg.c.
	(pkglib_MODULES): Change _linux.mod, _chain.mod, _bsd.mod and
	_multiboot.mod as linux.mod, chain.mod, bsd.mod and multiboot.mod,
	remove the corresponding normal mode command.
	(normal_mod_SOURCES): Remove normal/arg.c.
	* conf/i386-coreboot.rmk: Likewise.
	* conf/i386-efi.rmk: Likewise.
	* conf/i386-ieee1275.rmk: Likewise.
	* conf/powerpc-ieee1275.rmk: Likewise.
	* conf/x86_64-efi.rmk: Likewise.

	* include/grub/arg.h: Move from here ...
	* include/grub/lib/arg.h: ... to here.

	* normal/arg.c: Move from here ...
	* lib/arg.c: ... to here.

	* commands/extcmd.c: New file.
	* commands/minicmd.c: Likewise.
	* include/grub/command.h: Likewise.
	* include/grub/extcmd.h: Likewise.
	* kern/command.c: Likewise.
	* kern/corecmd.c: Likewise.

	* kern/list.c (grub_list_iterate): Return int instead of void.
	(grub_list_insert): New function.
	(grub_prio_list_insert): Likewise.

	* kern/rescue.c (grub_rescue_command): Removed.
	(grub_rescue_command_list): Likewise.
	(grub_rescue_register_command): Likewise.
	(grub_rescue_unregister_command): Likewise.
	(grub_rescue_cmd_boot): Move to minicmd.c
	(grub_rescue_cmd_help): Likewise.
	(grub_rescue_cmd_info): Likewise.
	(grub_rescue_cmd_boot): Likewise.
	(grub_rescue_cmd_testload): Likewise.
	(grub_rescue_cmd_dump): Likewise.
	(grub_rescue_cmd_rmmod): Likewise.
	(grub_rescue_cmd_lsmod): Likewise.
	(grub_rescue_cmd_exit): Likewise.
	(grub_rescue_print_devices): Moved to corecmd.c.
	(grub_rescue_print_files): Likewise.
	(grub_rescue_cmd_ls): Likewise.
	(grub_rescue_cmd_insmod): Likewise.
	(grub_rescue_cmd_set): Likewise.
	(grub_rescue_cmd_unset): Likewise.
	(attemp_normal_mode): Use grub_command_find to get normal module.
	(grub_enter_rescue_mode): Use grub_register_core_commands to register
	commands, remove grub_rescue_regiter_command calls.

	* normal/command.c (grub_regiser_command): Removed.
	(grub_unregister_command): Likewise.
	(grub_command_find): Likewise.
	(grub_iterate_commands): Likewise.
	(rescue_command): Likewise.
	(export_command): Moved to corecmd.c.
	(set_command): Removed.
	(unset_command): Likewise.
	(insmod_command): Likewise.
	(rmmod_command): Likewise.
	(lsmod_command): Likewise.
	(grub_command_init): Likewise.

	* normal/completion.c (iterate_command): Use cmd->prio to check for
	active command.
	(complete_arguments): Use grub_extcmd_t structure to find options.
	(grub_normal_do_completion): Change function grub_iterate_commands to
	grub_command_iterate.

	* normal/execute.c (grub_script_execute_cmd): No need to parse
	argument here.

	* normal/main.c (grub_dyncmd_dispatcher): New function.
	(read_command_list): Register unload commands as dyncmd.
	(grub_cmd_normal): Use new command interface, register rescue,
	unregister normal at entry, register normal, unregister rescue at exit.

	* include/grub/list.h (grub_list_test_t): New type.
	(grub_list_iterate): Return int instead of void.
	(grub_list_insert): New function.
	(GRUB_AS_NAMED_LIST_P): New macro.
	(GRUB_AS_PRIO_LIST): Likewise.
	(GRUB_AS_PRIO_LIST_P): Likewise.
	(GRUB_PRIO_LIST_PRIO_MASK): New constant.
	(GRUB_PRIO_LIST_FLAG_ACTIVE): Likewise.
	(grub_prio_list): New structure.
	(grub_prio_list_insert): New function.
	(grub_prio_list_remove): New inline function.

	* include/grub/normal.h: Remove <grub/arg.h>, add <grub/command.h>.
	(GRUB_COMMAND_FLAG_CMDLINE): Moved to command.h.
	(GRUB_COMMAND_FLAG_MENU): Likewise.
	(GRUB_COMMAND_FLAG_BOTH): Likewise.
	(GRUB_COMMAND_FLAG_TITLE): Likewise.
	(GRUB_COMMAND_FLAG_NO_ECHO): Likewise.
	(GRUB_COMMAND_FLAG_NO_ARG_PARSE): Removed.
	(GRUB_COMMAND_FLAG_NOT_LOADED): Likewise.
	(grub_command): Likewise.
	(grub_register_command): Likewise.
	(grub_command_find): Likewise.
	(grub_iterate_commands): Likewise.
	(grub_command_init): Likewise.
	(grub_arg_parse): Likewise.
	(grub_arg_show_help): Likewise.

	* include/grub/rescue.h (grub_rescue_register_command): Removed.
	(grub_rescue_unregister_command): Likewise.

	* include/grub/i386/bsd.h: Remove grub_rescue_cmd_freebsd,
	grub_rescue_cmd_openbsd, grub_rescue_cmd_netbsd,
	grub_rescue_cmd_freebsd_loadenv and grub_rescue_cmd_freebsd_module.

	* include/grub/i386/efi/loader.h: Remove grub_rescue_cmd_linux and
	grub_rescue_cmd_initrd.
	* include/grub/i386/loader.h: Likewise.
	* include/grub/x86_64/loader.h: Likewise.

	* include/grub/i386/pc/chainloader.h: Remove grub_chainloader_cmd.
2009-03-21 08:39:59 +00:00

499 lines
10 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* completion.c - complete a command, a disk, a partition or a file */
/*
* GRUB -- GRand Unified Bootloader
* Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2007,2008 Free Software Foundation, Inc.
*
* GRUB is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* GRUB is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with GRUB. If not, see <http://www.gnu.org/licenses/>.
*/
#include <grub/normal.h>
#include <grub/misc.h>
#include <grub/err.h>
#include <grub/mm.h>
#include <grub/partition.h>
#include <grub/disk.h>
#include <grub/file.h>
#include <grub/parser.h>
#include <grub/extcmd.h>
/* The current word. */
static char *current_word;
/* The matched string. */
static char *match;
/* The count of candidates. */
static int num_found;
/* The string to be appended. */
static const char *suffix;
/* The callback function to print items. */
static void (*print_func) (const char *, grub_completion_type_t, int);
/* The state the command line is in. */
static grub_parser_state_t cmdline_state;
/* Add a string to the list of possible completions. COMPLETION is the
string that should be added. EXTRA will be appended if COMPLETION
matches uniquely. The type TYPE specifies what kind of data is added. */
static int
add_completion (const char *completion, const char *extra,
grub_completion_type_t type)
{
if (grub_strncmp (current_word, completion, grub_strlen (current_word)) == 0)
{
num_found++;
switch (num_found)
{
case 1:
match = grub_strdup (completion);
if (! match)
return 1;
suffix = extra;
break;
case 2:
if (print_func)
print_func (match, type, 0);
/* Fall through. */
default:
{
char *s = match;
const char *t = completion;
if (print_func)
print_func (completion, type, num_found - 1);
/* Detect the matched portion. */
while (*s && *t && *s == *t)
{
s++;
t++;
}
*s = '\0';
}
break;
}
}
return 0;
}
static int
iterate_partition (grub_disk_t disk, const grub_partition_t p)
{
const char *disk_name = disk->name;
char *partition_name = grub_partition_get_name (p);
char *name;
int ret;
if (! partition_name)
return 1;
name = grub_malloc (grub_strlen (disk_name) + 1
+ grub_strlen (partition_name) + 1);
if (! name)
{
grub_free (partition_name);
return 1;
}
grub_sprintf (name, "%s,%s", disk_name, partition_name);
grub_free (partition_name);
ret = add_completion (name, ")", GRUB_COMPLETION_TYPE_PARTITION);
grub_free (name);
return ret;
}
static int
iterate_dir (const char *filename, int dir)
{
if (! dir)
{
const char *prefix;
if (cmdline_state == GRUB_PARSER_STATE_DQUOTE)
prefix = "\" ";
else if (cmdline_state == GRUB_PARSER_STATE_QUOTE)
prefix = "\' ";
else
prefix = " ";
if (add_completion (filename, prefix, GRUB_COMPLETION_TYPE_FILE))
return 1;
}
else if (grub_strcmp (filename, ".") && grub_strcmp (filename, ".."))
{
char fname[grub_strlen (filename) + 2];
grub_sprintf (fname, "%s/", filename);
if (add_completion (fname, "", GRUB_COMPLETION_TYPE_FILE))
return 1;
}
return 0;
}
static int
iterate_dev (const char *devname)
{
grub_device_t dev;
/* Complete the partition part. */
dev = grub_device_open (devname);
if (dev)
{
if (dev->disk && dev->disk->has_partitions)
{
if (add_completion (devname, ",", GRUB_COMPLETION_TYPE_DEVICE))
return 1;
}
else
{
if (add_completion (devname, ")", GRUB_COMPLETION_TYPE_DEVICE))
return 1;
}
}
grub_errno = GRUB_ERR_NONE;
return 0;
}
static int
iterate_command (grub_command_t cmd)
{
if (cmd->prio & GRUB_PRIO_LIST_FLAG_ACTIVE)
{
if (cmd->flags & GRUB_COMMAND_FLAG_CMDLINE)
{
if (add_completion (cmd->name, " ", GRUB_COMPLETION_TYPE_COMMAND))
return 1;
}
}
return 0;
}
/* Complete a device. */
static int
complete_device (void)
{
/* Check if this is a device or a partition. */
char *p = grub_strchr (++current_word, ',');
grub_device_t dev;
if (! p)
{
/* Complete the disk part. */
if (grub_disk_dev_iterate (iterate_dev))
return 1;
}
else
{
/* Complete the partition part. */
*p = '\0';
dev = grub_device_open (current_word);
*p = ',';
grub_errno = GRUB_ERR_NONE;
if (dev)
{
if (dev->disk && dev->disk->has_partitions)
{
if (grub_partition_iterate (dev->disk, iterate_partition))
{
grub_device_close (dev);
return 1;
}
}
grub_device_close (dev);
}
else
return 1;
}
return 0;
}
/* Complete a file. */
static int
complete_file (void)
{
char *device;
char *dir;
char *last_dir;
grub_fs_t fs;
grub_device_t dev;
int ret = 0;
device = grub_file_get_device_name (current_word);
if (grub_errno != GRUB_ERR_NONE)
return 1;
dev = grub_device_open (device);
if (! dev)
{
ret = 1;
goto fail;
}
fs = grub_fs_probe (dev);
if (! fs)
{
ret = 1;
goto fail;
}
dir = grub_strchr (current_word, '/');
last_dir = grub_strrchr (current_word, '/');
if (dir)
{
char *dirfile;
current_word = last_dir + 1;
dir = grub_strdup (dir);
if (! dir)
{
ret = 1;
goto fail;
}
/* Cut away the filename part. */
dirfile = grub_strrchr (dir, '/');
dirfile[1] = '\0';
/* Iterate the directory. */
(fs->dir) (dev, dir, iterate_dir);
grub_free (dir);
if (grub_errno)
{
ret = 1;
goto fail;
}
}
else
{
current_word += grub_strlen (current_word);
match = grub_strdup ("/");
if (! match)
{
ret = 1;
goto fail;
}
suffix = "";
num_found = 1;
}
fail:
if (dev)
grub_device_close (dev);
grub_free (device);
return ret;
}
/* Complete an argument. */
static int
complete_arguments (char *command)
{
grub_command_t cmd;
grub_extcmd_t ext;
const struct grub_arg_option *option;
char shortarg[] = "- ";
cmd = grub_command_find (command);
if (!cmd || !(cmd->flags & GRUB_COMMAND_FLAG_EXTCMD))
return 0;
ext = cmd->data;
if (!ext->options)
return 0;
if (add_completion ("-u", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
return 1;
/* Add the short arguments. */
for (option = ext->options; option->doc; option++)
{
if (! option->shortarg)
continue;
shortarg[1] = option->shortarg;
if (add_completion (shortarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
return 1;
}
/* First add the built-in arguments. */
if (add_completion ("--help", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
return 1;
if (add_completion ("--usage", " ", GRUB_COMPLETION_TYPE_ARGUMENT))
return 1;
/* Add the long arguments. */
for (option = ext->options; option->doc; option++)
{
char *longarg;
if (!option->longarg)
continue;
longarg = grub_malloc (grub_strlen (option->longarg));
grub_sprintf (longarg, "--%s", option->longarg);
if (add_completion (longarg, " ", GRUB_COMPLETION_TYPE_ARGUMENT))
{
grub_free (longarg);
return 1;
}
grub_free (longarg);
}
return 0;
}
static grub_parser_state_t
get_state (const char *cmdline)
{
grub_parser_state_t state = GRUB_PARSER_STATE_TEXT;
char use;
while (*cmdline)
state = grub_parser_cmdline_state (state, *(cmdline++), &use);
return state;
}
/* Try to complete the string in BUF. Return the characters that
should be added to the string. This command outputs the possible
completions by calling HOOK, in that case set RESTORE to 1 so the
caller can restore the prompt. */
char *
grub_normal_do_completion (char *buf, int *restore,
void (*hook) (const char *, grub_completion_type_t, int))
{
int argc;
char **argv;
/* Initialize variables. */
match = 0;
num_found = 0;
suffix = "";
print_func = hook;
*restore = 1;
if (grub_parser_split_cmdline (buf, 0, &argc, &argv))
return 0;
current_word = argv[argc];
/* Determine the state the command line is in, depending on the
state, it can be determined how to complete. */
cmdline_state = get_state (buf);
if (argc == 0)
{
/* Complete a command. */
if (grub_command_iterate (iterate_command))
goto fail;
}
else if (*current_word == '-')
{
if (complete_arguments (buf))
goto fail;
}
else if (*current_word == '(' && ! grub_strchr (current_word, ')'))
{
/* Complete a device. */
if (complete_device ())
goto fail;
}
else
{
/* Complete a file. */
if (complete_file ())
goto fail;
}
/* If more than one match is found those matches will be printed and
the prompt should be restored. */
if (num_found > 1)
*restore = 1;
else
*restore = 0;
/* Return the part that matches. */
if (match)
{
char *ret;
char *escstr;
char *newstr;
int current_len;
int match_len;
int spaces = 0;
current_len = grub_strlen (current_word);
match_len = grub_strlen (match);
/* Count the number of spaces that have to be escaped. XXX:
More than just spaces have to be escaped. */
for (escstr = match + current_len; *escstr; escstr++)
if (*escstr == ' ')
spaces++;
ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + spaces + 1);
newstr = ret;
for (escstr = match + current_len; *escstr; escstr++)
{
if (*escstr == ' ' && cmdline_state != GRUB_PARSER_STATE_QUOTE
&& cmdline_state != GRUB_PARSER_STATE_QUOTE)
*(newstr++) = '\\';
*(newstr++) = *escstr;
}
*newstr = '\0';
if (num_found == 1)
grub_strcat (ret, suffix);
if (*ret == '\0')
{
grub_free (ret);
goto fail;
}
grub_free (argv[0]);
grub_free (match);
return ret;
}
fail:
grub_free (argv[0]);
grub_free (match);
grub_errno = GRUB_ERR_NONE;
return 0;
}