All of lore.kernel.org
 help / color / mirror / Atom feed
From: phcoder <phcoder@gmail.com>
To: The development of GRUB 2 <grub-devel@gnu.org>
Subject: [PATCH] Test command
Date: Fri, 13 Feb 2009 12:39:50 +0100	[thread overview]
Message-ID: <49955C06.9060706@gmail.com> (raw)
In-Reply-To: <49955BD7.2070206@gmail.com>

[-- Attachment #1: Type: text/plain, Size: 490 bytes --]

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


[-- Attachment #2: test.diff --]
[-- Type: text/x-patch, Size: 12884 bytes --]

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  <phcoder@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  <rmh@aybabtu.com>
 
 	* util/grub.d/00_header.in: Update old reference to `font' command.

  reply	other threads:[~2009-02-13 11:39 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2009-02-13 11:39 Test command phcoder
2009-02-13 11:39 ` phcoder [this message]
2009-04-10 22:18   ` [PATCH] " phcoder
2009-04-11 10:07     ` Yoshinori K. Okuji
2009-04-11 15:11       ` phcoder
2009-04-14 15:55         ` Yoshinori K. Okuji
2009-04-16 13:15           ` phcoder
2009-04-25 12:29             ` Vladimir Serbinenko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=49955C06.9060706@gmail.com \
    --to=phcoder@gmail.com \
    --cc=grub-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.