grub/normal/completion.c
okuji 992ffbbebb 2005-08-18 Yoshinori K. Okuji <okuji@enbug.org>
* normal/misc.c: New file.

        * DISTLIST: Added normal/misc.c.

        * partmap/amiga.c (amiga_partition_map_iterate): Add an argument
        DISK to HOOK. Call HOOK with DISK.
        * partmap/apple.c (apple_partition_map_iterate): Likewise.
        * partmap/pc.c (pc_partition_map_iterate): Likewise.
        * partmap/sun.c (sun_partition_map_iterate): Likewise.

        * normal/menu_entry.c (struct screen): Added a new member
        "completion_shown".
        (completion_buffer): New global variable.
        (make_screen): Set SCREEN->COMPLETION_SHOWN to zero.
        (store_completion): New function.
        (complete): Likewise.
        (clear_completions): Likewise.
        (grub_menu_entry_run): If SCREEN->COMPLETION_SHOWN is non-zero,
        call clear_completions and reset SCREEN->COMPLETION_SHOWN. If C is
        a tab, call complete.

        * normal/completion.c (disk_dev): Removed.
        (print_simple_completion): Likewise.
        (print_partition_completion): Likewise.
        (print_func): New global variable.
        (add_completion): Do not take the arguments WHAT or PRINT any
        longer. Added a new argument TYPE. Instead of printing directly,
        call PRINT_FUNC if not NULL.
        All callers changed.
        (complete_device): Use a local variable DEV instead of
        DISK_DEV. Do not move CURRENT_WORD to the end of a device name.
        (grub_normal_do_completion): Take a new argument HOOK. Do not
        initialize DISK_DEV. Initialize PRINT_FUNC to HOOK. If RET is an
        empty string, return NULL instead.
        All callers changed.

        * normal/cmdline.c (print_completion): New function.

        * kern/partition.c (grub_partition_iterate): Add an argument DISK
        to HOOK.
        All callers changed.

        * kern/disk.c (grub_print_partinfo): Removed.

        * include/grub/partition.h (struct grub_partition_map): Add a new
        argument DISK into HOOK of ITERATE.
        (grub_partition_iterate): Add a new argument DISK to HOOK.

        * include/grub/normal.h (enum grub_completion_type): New enum.
        (grub_completion_type_t): New type.
        (GRUB_COMPLETION_TYPE_COMMAND): New constant.
        (GRUB_COMPLETION_TYPE_DEVICE): Likewise.
        (GRUB_COMPLETION_TYPE_PARTITION): Likewise.
        (GRUB_COMPLETION_TYPE_FILE): Likewise.
        (grub_normal_do_completion): Added a new argument HOOK.
        (grub_normal_print_device_info): New prototype.

        * include/grub/disk.h (grub_print_partinfo): Removed.

        * conf/i386-pc.rmk (grub_emu_SOURCES): Added normal/misc.c.
        (normal_mod_SOURCES): Likewise.
        * conf/powerpc-ieee1275.rmk (grub_emu_SOURCES): Likewise.
        (normal_mod_SOURCES): Likewise.

        * commands/ls.c (grub_ls_list_disks): Use
        grub_normal_print_device_info instead of grub_print_partinfo. Free
        PNAME.
        (grub_ls_list_files): Use grub_normal_print_device_info instead of
        duplicating the code.
2005-08-18 03:14:39 +00:00

396 lines
7.9 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 Free Software Foundation, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <grub/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>
/* 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);
/* 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)
{
if (add_completion (filename, " ", GRUB_COMPLETION_TYPE_FILE))
return 1;
}
else
{
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 (grub_command_find (cmd->name))
{
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;
}
/* 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))
{
char *first_word;
/* Initialize variables. */
match = 0;
num_found = 0;
suffix = "";
print_func = hook;
*restore = 1;
/* Find the first word. */
for (first_word = buf; *first_word == ' '; first_word++)
;
/* Find the delimeter of the current word. */
for (current_word = first_word + grub_strlen (first_word);
current_word > first_word;
current_word--)
if (*current_word == ' ' || *current_word == '=')
break;
if (current_word == first_word)
{
/* Complete a command. */
if (grub_iterate_commands (iterate_command))
goto fail;
}
else
{
current_word++;
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;
int current_len;
int match_len;
current_len = grub_strlen (current_word);
match_len = grub_strlen (match);
ret = grub_malloc (match_len - current_len + grub_strlen (suffix) + 1);
grub_strcpy (ret, match + current_len);
if (num_found == 1)
grub_strcat (ret, suffix);
grub_free (match);
if (*ret == '\0')
{
grub_free (ret);
return 0;
}
return ret;
}
fail:
grub_free (match);
grub_errno = GRUB_ERR_NONE;
return 0;
}