Sorry forgot to attach the file
phcoder wrote:
Hello. Here is an implementation of bash-like "test" command. Many file
tests are omitted because they are useless in grub (e.g. -c test). I
also added 3 extension: lexicographical comparing, prefixed -gt and -lt
(it skips common prefix. Useful for comparing versions. e.g. [ vmlinuz-3
-plt vmlinuz-11 ] is true) and biased -nt/-ot which adds s specified
amount of seconds to mtime.
Regards
Vladimir 'phcoder' Serbinenko
Index: commands/test.c
===================================================================
--- commands/test.c (revision 1989)
+++ commands/test.c (working copy)
@@ -23,33 +23,385 @@
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/env.h>
+#include <grub/fs.h>
+#include <grub/device.h>
+#include <grub/file.h>
+/* A simple implementation for signed numbers*/
+static int
+grub_strtosl (char *arg, char **end, int base)
+{
+ if (arg[0] == '-')
+ return -grub_strtoul (arg + 1, end, base);
+ return grub_strtoul (arg, end, base);
+}
+
+/* Parse a test expression startion from *argn*/
+static int
+test_parse (char **args, int *argn, int argc)
+{
+ int ret = 0, discard = 0, invert = 0;
+ int file_exists;
+ struct grub_dirhook_info file_info;
+
+ auto void update_val (int val);
+ auto void get_fileinfo (char *pathname);
+
+ /*Take care of discarding and inverting*/
+ void update_val (int val)
+ {
+ if (!discard)
+ ret = invert ? !val : val;
+ invert = discard = 0;
+ }
+
+ /* Check if file exists and fetch its information */
+ void get_fileinfo (char *pathname)
+ {
+ char *filename, *path;
+ char *device_name;
+ grub_fs_t fs;
+ grub_device_t dev;
+
+ /* A hook for iterating directories */
+ auto int find_file (const char *cur_filename,
+ struct grub_dirhook_info info);
+ int find_file (const char *cur_filename, struct grub_dirhook_info info)
+ {
+ if (info.case_insensitive ? !grub_strcasecmp (cur_filename, filename)
+ :!grub_strcmp (cur_filename, filename))
+ {
+ file_info = info;
+ file_exists = 1;
+ return 1;
+ }
+ return 0;
+ }
+
+
+ file_exists = 0;
+ device_name = grub_file_get_device_name (pathname);
+ dev = grub_device_open (device_name);
+ if (! dev)
+ {
+ grub_free (device_name);
+ return;
+ }
+
+ fs = grub_fs_probe (dev);
+ path = grub_strchr (pathname, ')');
+ if (! path)
+ path = pathname;
+ else
+ path++;
+
+ /* Remove trailing / */
+ while (*pathname && pathname[grub_strlen (pathname) - 1] == '/')
+ pathname[grub_strlen (pathname) - 1] = 0;
+
+ /* Split into path and filename*/
+ filename = grub_strrchr (pathname, '/');
+ if (!filename)
+ {
+ path = grub_strdup ("/");
+ filename = pathname;
+ }
+ else
+ {
+ filename++;
+ path = grub_strdup (pathname);
+ path[filename - pathname] = 0;
+ }
+
+ /* It's the whole device*/
+ if (!*pathname)
+ {
+ file_exists = 1;
+ grub_memset (&file_info, 0, sizeof (file_info));
+ /* Root is always a directory */
+ file_info.dir = 1;
+
+ /* Fetch writing time */
+ file_info.mtimeset = 0;
+ if (fs->mtime)
+ {
+ if (! fs->mtime (dev, &file_info.mtime))
+ file_info.mtimeset = 1;
+ grub_errno = GRUB_ERR_NONE;
+ }
+ }
+ else
+ (fs->dir) (dev, path, find_file);
+
+ grub_device_close (dev);
+ grub_free (path);
+ grub_free (device_name);
+ }
+
+ /* Here we have the real parsing */
+ while (*argn < argc)
+ {
+ /* First try 3 argument tests */
+ /* String tests */
+ if (*argn + 2 < argc && (!grub_strcmp (args[*argn + 1], "=")
+ || !grub_strcmp (args[*argn + 1], "==")))
+ {
+ update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0);
+ (*argn) += 3;
+ continue;
+ }
+
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "!="))
+ {
+ update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0);
+ (*argn) += 3;
+ continue;
+ }
+
+ /* GRUB extension: lexicographical sorting */
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "<"))
+ {
+ update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0);
+ (*argn) += 3;
+ continue;
+ }
+
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "<="))
+ {
+ update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0);
+ (*argn) += 3;
+ continue;
+ }
+
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], ">"))
+ {
+ update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0);
+ (*argn) += 3;
+ continue;
+ }
+
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], ">="))
+ {
+ update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0);
+ (*argn) += 3;
+ continue;
+ }
+
+ /* Number tests */
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-eq"))
+ {
+ update_val (grub_strtosl (args[*argn], 0, 0)
+ == grub_strtosl (args[*argn + 2], 0, 0));
+ (*argn) += 3;
+ continue;
+ }
+
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-ge"))
+ {
+ update_val (grub_strtosl (args[*argn], 0, 0)
+ >= grub_strtosl (args[*argn + 2], 0, 0));
+ (*argn) += 3;
+ continue;
+ }
+
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-gt"))
+ {
+ update_val (grub_strtosl (args[*argn], 0, 0)
+ > grub_strtosl (args[*argn + 2], 0, 0));
+ (*argn) += 3;
+ continue;
+ }
+
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-le"))
+ {
+ update_val (grub_strtosl (args[*argn], 0, 0)
+ <= grub_strtosl (args[*argn + 2], 0, 0));
+ (*argn) += 3;
+ continue;
+ }
+
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-lt"))
+ {
+ update_val (grub_strtosl (args[*argn], 0, 0)
+ < grub_strtosl (args[*argn + 2], 0, 0));
+ (*argn) += 3;
+ continue;
+ }
+
+ if (*argn + 2 < argc && !grub_strcmp (args[*argn + 1], "-ne"))
+ {
+ update_val (grub_strtosl (args[*argn], 0, 0)
+ != grub_strtosl (args[*argn + 2], 0, 0));
+ (*argn) += 3;
+ continue;
+ }
+
+ /* GRUB extension: compare numbers skipping prefixes.
+ Useful for comparing versions. E.g. vmlinuz-2 -plt vmlinuz-11*/
+ if (*argn + 2 < argc && (!grub_strcmp (args[*argn + 1], "-pgt")
+ || !grub_strcmp (args[*argn + 1], "-plt")))
+ {
+ int i;
+ /* Skip common prefix */
+ for (i = 0; args[*argn][i] == args[*argn + 2][i] && args[*argn][i];
+ i++);
+
+ /*Go the digits back*/
+ i--;
+ while (grub_isdigit (args[*argn][i]) && i > 0)
+ i--;
+ i++;
+
+ if (!grub_strcmp (args[*argn + 1], "-pgt"))
+ update_val (grub_strtoul (args[*argn] + i, 0, 0)
+ > grub_strtoul (args[*argn + 2] + i, 0, 0));
+ else
+ update_val (grub_strtoul (args[*argn] + i, 0, 0)
+ < grub_strtoul (args[*argn + 2] + i, 0, 0));
+ (*argn) += 3;
+ continue;
+ }
+
+ /* -nt and -ot tests. GRUB extension: when doing -?t<bias> bias
+ will be added to the first mtime */
+ if (*argn + 2 < argc && (!grub_memcmp (args[*argn + 1], "-nt", 3)
+ || !grub_memcmp (args[*argn + 1], "-ot", 3)))
+ {
+ struct grub_dirhook_info file1;
+ int file1exists;
+ int bias = 0;
+
+ /* Fetch fileinfo */
+ get_fileinfo (args[*argn]);
+ file1 = file_info;
+ file1exists = file_exists;
+ get_fileinfo (args[*argn + 2]);
+
+ if (args[*argn + 1][3])
+ bias = grub_strtosl (args[*argn + 1] + 3, 0, 0);
+
+ if (!grub_memcmp (args[*argn + 1], "-nt", 3))
+ update_val ((file1exists && ! file_exists)
+ || (file1.mtimeset && file_info.mtimeset
+ && file1.mtime + bias > file_info.mtime));
+ else
+ update_val ((!file1exists && file_exists)
+ || (file1.mtimeset && file_info.mtimeset
+ && file1.mtime + bias < file_info.mtime));
+ (*argn) += 3;
+ continue;
+ }
+
+ /* Two-argument tests */
+ /* file tests*/
+ if (*argn + 1 < argc && !grub_strcmp (args[*argn], "-d"))
+ {
+ get_fileinfo (args[*argn + 1]);
+ update_val (file_exists && file_info.dir);
+ (*argn) += 2;
+ return ret;
+ }
+
+ if (*argn + 1 < argc && !grub_strcmp (args[*argn], "-e"))
+ {
+ get_fileinfo (args[*argn + 1]);
+ update_val (file_exists);
+ (*argn) += 2;
+ return ret;
+ }
+
+ if (*argn + 1 < argc && !grub_strcmp (args[*argn], "-f"))
+ {
+ get_fileinfo (args[*argn + 1]);
+ /* FIXME: check for other types */
+ update_val (file_exists && !file_info.dir);
+ (*argn) += 2;
+ return ret;
+ }
+
+ if (*argn + 1 < argc && !grub_strcmp (args[*argn], "-s"))
+ {
+ grub_file_t file;
+ file = grub_file_open (args[*argn + 1]);
+ update_val (file && grub_file_size (file));
+ if (file)
+ grub_file_close (file);
+ grub_errno = GRUB_ERR_NONE;
+ (*argn) += 2;
+ return ret;
+ }
+
+ /* string tests */
+ if (!grub_strcmp (args[*argn], "-n") && *argn + 1 < argc)
+ {
+ update_val (args[*argn + 1][0]);
+
+ (*argn)+=2;
+ continue;
+ }
+ if (!grub_strcmp (args[*argn], "-z") && *argn + 1 < argc)
+ {
+ update_val (!args[*argn + 1][0]);
+ (*argn)+=2;
+ continue;
+ }
+
+ /* Special modifiers*/
+
+ /* End of expression. return to parent*/
+ if (!grub_strcmp (args[*argn], ")"))
+ {
+ (*argn)++;
+ return ret;
+ }
+ /* Recursively invoke if parenthesis */
+ if (!grub_strcmp (args[*argn], "("))
+ {
+ (*argn)++;
+ update_val (test_parse (args, argn, argc));
+ continue;
+ }
+
+ if (!grub_strcmp (args[*argn], "!"))
+ {
+ invert = !invert;
+ (*argn)++;
+ continue;
+ }
+ if (!grub_strcmp (args[*argn], "-a"))
+ {
+ /* if current value is 0 second value is to be discarded */
+ discard = !ret;
+ (*argn)++;
+ continue;
+ }
+ if (!grub_strcmp (args[*argn], "-o"))
+ {
+ /* if current value is 1 second value is to be discarded */
+ discard = ret;
+ (*argn)++;
+ continue;
+ }
+
+ /* To test found. Interpret if as just a string*/
+ update_val (args[*argn][0]);
+ (*argn)++;
+ }
+ return ret;
+}
+
static grub_err_t
grub_cmd_test (struct grub_arg_list *state __attribute__ ((unused)), int argc,
char **args)
{
- char *eq;
- char *eqis;
+ int argn = 0;
- /* XXX: No fancy expression evaluation yet. */
-
- if (argc == 0)
- return 0;
-
- eq = grub_strdup (args[0]);
- eqis = grub_strchr (eq, '=');
- if (! eqis)
- return 0;
+ if (argc >= 1 && !grub_strcmp (args[argc-1], "]"))
+ argc--;
- *eqis = '\0';
- eqis++;
- /* Check an expression in the form `A=B'. */
- if (grub_strcmp (eq, eqis))
- grub_error (GRUB_ERR_TEST_FAILURE, "false");
- grub_free (eq);
-
- return grub_errno;
+ return test_parse (args, &argn, argc) ? GRUB_ERR_NONE
+ : grub_error (GRUB_ERR_TEST_FAILURE, "false");
}
@@ -57,9 +409,11 @@
GRUB_MOD_INIT(test)
{
(void)mod; /* To stop warning. */
- grub_register_command ("[", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE,
+ grub_register_command ("[", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE
+ | GRUB_COMMAND_FLAG_NO_ARG_PARSE,
"[ EXPRESSION ]", "Evaluate an expression", 0);
- grub_register_command ("test", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE,
+ grub_register_command ("test", grub_cmd_test, GRUB_COMMAND_FLAG_CMDLINE
+ | GRUB_COMMAND_FLAG_NO_ARG_PARSE,
"test EXPRESSION", "Evaluate an expression", 0);
}
Index: include/grub/fs.h
===================================================================
--- include/grub/fs.h (revision 1989)
+++ include/grub/fs.h (working copy)
@@ -27,13 +27,14 @@
/* Forward declaration is required, because of mutual reference. */
struct grub_file;
struct grub_dirhook_info
{
int dir:1;
int mtimeset:1;
+ int case_insensitive:1;
grub_int32_t mtime;
};
/* Filesystem descriptor. */
struct grub_fs
{
Index: fs/fat.c
===================================================================
--- fs/fat.c (revision 1989)
+++ fs/fat.c (working copy)
@@ -594,9 +557,10 @@
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
info.dir = (dir->attr & GRUB_FAT_ATTR_DIRECTORY);
+ info.case_insensitive = 1;
if (dir->attr & GRUB_FAT_ATTR_VOLUME_ID)
return 0;
if (*dirname == '\0' && call_hook)
return hook (filename, info);
Index: fs/hfsplus.c
===================================================================
--- fs/hfsplus.c (revision 1989)
+++ fs/hfsplus.c (working copy)
@@ -898,11 +899,12 @@
enum grub_fshelp_filetype filetype,
grub_fshelp_node_t node)
{
struct grub_dirhook_info info;
grub_memset (&info, 0, sizeof (info));
info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+ info.case_insensitive = !! (filetype & GRUB_FSHELP_CASE_INSENSITIVE);
grub_free (node);
return hook (filename, info);
}
#ifndef GRUB_UTIL
Index: ChangeLog
===================================================================
--- ChangeLog (revision 1989)
+++ ChangeLog (working copy)
@@ -1,3 +1,13 @@
+2009-02-13 Vladimir Serbinenko <phco...@gmail.com>
+
+ Test command
+
+ * commands/test.c: rewritten to use bash-like test
+ * include/grub/fs.h (struct grub_dirhook_info): new field
+ case_insensitive
+ * fs/hfsplus.c: declare FS as case-insensitive if necessary
+ * fs/fat.c: likewise
+
2009-02-11 Robert Millan <r...@aybabtu.com>
* util/grub.d/00_header.in: Update old reference to `font' command.
_______________________________________________
Grub-devel mailing list
Grub-devel@gnu.org
http://lists.gnu.org/mailman/listinfo/grub-devel