[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [PATCH] Test command
From: |
phcoder |
Subject: |
Re: [PATCH] Test command |
Date: |
Sat, 11 Apr 2009 00:18:59 +0200 |
User-agent: |
Thunderbird 2.0.0.21 (X11/20090318) |
Rediffed. New changelog
2009-04-11 Vladimir Serbinenko <address@hidden>
Test command
* commands/test.c: rewritten to use bash-like test
phcoder wrote:
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
--
Regards
Vladimir 'phcoder' Serbinenko
diff --git a/commands/test.c b/commands/test.c
index a9c8281..2d8dedd 100644
--- a/commands/test.c
+++ b/commands/test.c
@@ -21,33 +21,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>
#include <grub/command.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 (grub_command_t cmd __attribute__ ((unused)),
int argc, char **args)
{
- char *eq;
- char *eqis;
-
- /* XXX: No fancy expression evaluation yet. */
-
- if (argc == 0)
- return 0;
-
- eq = grub_strdup (args[0]);
- eqis = grub_strchr (eq, '=');
- if (! eqis)
- return 0;
-
- *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;
+ int argn = 0;
+
+ if (argc >= 1 && !grub_strcmp (args[argc-1], "]"))
+ argc--;
+
+ return test_parse (args, &argn, argc) ? GRUB_ERR_NONE
+ : grub_error (GRUB_ERR_TEST_FAILURE, "false");
}
static grub_command_t cmd_1, cmd_2;
- Re: [PATCH] Test command,
phcoder <=