[2140] 2009-04-25 Vladimir Serbinenko <[email protected]>

Vladimir Serbinenko <[email protected]>
Newsgroups gmane.comp.boot-loaders.grub.cvs
Message-ID <[email protected]>
Revision: 2140
          http://svn.sv.gnu.org/viewvc/?view=rev&root=grub&revision=2140
Author:   phcoder
Date:     2009-04-25 12:29:08 +0000 (Sat, 25 Apr 2009)
Log Message:
-----------
2009-04-25  Vladimir Serbinenko  <[email protected]>

	Test command

	* commands/test.c: rewritten to use bash-like test

Modified Paths:
--------------
    trunk/grub2/ChangeLog
    trunk/grub2/commands/test.c

Modified: trunk/grub2/ChangeLog
===================================================================
--- trunk/grub2/ChangeLog	2009-04-25 12:18:25 UTC (rev 2139)
+++ trunk/grub2/ChangeLog	2009-04-25 12:29:08 UTC (rev 2140)
@@ -1,5 +1,11 @@
 2009-04-25  Vladimir Serbinenko  <[email protected]>
 
+	Test command
+
+	* commands/test.c: rewritten to use bash-like test
+
+2009-04-25  Vladimir Serbinenko  <[email protected]>
+
 	Parttool autoloading and improvements
 
 	* Makefile.in (pkglib_DATA): add parttool.lst 

Modified: trunk/grub2/commands/test.c
===================================================================
--- trunk/grub2/commands/test.c	2009-04-25 12:18:25 UTC (rev 2139)
+++ trunk/grub2/commands/test.c	2009-04-25 12:29:08 UTC (rev 2140)
@@ -21,33 +21,390 @@
 #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)) == 0)
+	{
+	  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. */
+      if (*argn + 2 < argc)
+	{
+	  /* String tests. */
+	  if (grub_strcmp (args[*argn + 1], "=") == 0
+	      || grub_strcmp (args[*argn + 1], "==") == 0)
+	    {
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0);
+	      (*argn) += 3;
+	      continue;
+	    }
+
+	  if (grub_strcmp (args[*argn + 1], "!=") == 0)
+	    {
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) != 0);
+	      (*argn) += 3;
+	      continue;
+	    }
+	  
+	  /* GRUB extension: lexicographical sorting. */
+	  if (grub_strcmp (args[*argn + 1], "<") == 0)
+	    {
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0);
+	      (*argn) += 3;
+	      continue;
+	    }
+	  
+	  if (grub_strcmp (args[*argn + 1], "<=") == 0)
+	    {
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0);
+	      (*argn) += 3;
+	      continue;
+	    }
+	  
+	  if (grub_strcmp (args[*argn + 1], ">") == 0)
+	    {
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0);
+	      (*argn) += 3;
+	      continue;
+	    }
+	  
+	  if (grub_strcmp (args[*argn + 1], ">=") == 0)
+	    {
+	      update_val (grub_strcmp (args[*argn], args[*argn + 2]) >= 0);
+	      (*argn) += 3;
+	      continue;
+	    }
+
+	  /* Number tests. */
+	  if (grub_strcmp (args[*argn + 1], "-eq") == 0)
+	    {
+	      update_val (grub_strtosl (args[*argn], 0, 0) 
+			  == grub_strtosl (args[*argn + 2], 0, 0));
+	      (*argn) += 3;
+	      continue;
+	    }
+
+	  if (grub_strcmp (args[*argn + 1], "-ge") == 0)
+	    {
+	      update_val (grub_strtosl (args[*argn], 0, 0) 
+			  >= grub_strtosl (args[*argn + 2], 0, 0));
+	      (*argn) += 3;
+	      continue;
+	    }
+	  
+	  if (grub_strcmp (args[*argn + 1], "-gt") == 0)
+	    {
+	      update_val (grub_strtosl (args[*argn], 0, 0) 
+			  > grub_strtosl (args[*argn + 2], 0, 0));
+	      (*argn) += 3;
+	      continue;
+	    }
+
+	  if (grub_strcmp (args[*argn + 1], "-le") == 0)
+	    {
+	      update_val (grub_strtosl (args[*argn], 0, 0) 
+		      <= grub_strtosl (args[*argn + 2], 0, 0));
+	      (*argn) += 3;
+	      continue;
+	    }
+	  
+	  if (grub_strcmp (args[*argn + 1], "-lt") == 0)
+	    {
+	      update_val (grub_strtosl (args[*argn], 0, 0) 
+			  < grub_strtosl (args[*argn + 2], 0, 0));
+	      (*argn) += 3;
+	      continue;
+	    }
+	  
+	  if (grub_strcmp (args[*argn + 1], "-ne") == 0)
+	    {
+	      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 (grub_strcmp (args[*argn + 1], "-pgt") == 0
+	      || grub_strcmp (args[*argn + 1], "-plt") == 0)
+	    {
+	      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") == 0)
+		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 (grub_memcmp (args[*argn + 1], "-nt", 3) == 0
+	      || grub_memcmp (args[*argn + 1], "-ot", 3) == 0)
+	    {
+	      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) == 0)
+		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. */
+      if (*argn + 1 < argc)
+	{
+	  /* File tests. */
+	  if (grub_strcmp (args[*argn], "-d") == 0)
+	    {
+	      get_fileinfo (args[*argn + 1]);
+	      update_val (file_exists && file_info.dir);
+	      (*argn) += 2;
+	      return ret;
+	    }
+	  
+	  if (grub_strcmp (args[*argn], "-e") == 0)
+	    {
+	      get_fileinfo (args[*argn + 1]);
+	      update_val (file_exists);
+	      (*argn) += 2;
+	      return ret;
+	    }
+
+	  if (grub_strcmp (args[*argn], "-f") == 0)
+	    {
+	      get_fileinfo (args[*argn + 1]);
+	      /* FIXME: check for other types. */
+	      update_val (file_exists && ! file_info.dir);
+	      (*argn) += 2;
+	      return ret;
+	    }
+	  
+	  if (grub_strcmp (args[*argn], "-s") == 0)
+	    {
+	      grub_file_t file;
+	      file = grub_file_open (args[*argn + 1]);
+	      update_val (file && (grub_file_size (file) != 0));
+	      if (file)
+		grub_file_close (file);
+	      grub_errno = GRUB_ERR_NONE;
+	      (*argn) += 2;
+	      return ret;
+	    }
+	  
+	  /* String tests. */
+	  if (grub_strcmp (args[*argn], "-n") == 0)
+	    {
+	      update_val (args[*argn + 1][0]);
+	      
+	      (*argn) += 2;
+	      continue;
+	    }
+	  if (grub_strcmp (args[*argn], "-z") == 0)
+	    {
+	      update_val (! args[*argn + 1][0]);
+	      (*argn) += 2;
+	      continue;
+	    }
+	}
+
+      /* Special modifiers. */
+      
+      /* End of expression. return to parent. */
+      if (grub_strcmp (args[*argn], ")") == 0)
+	{
+	  (*argn)++;
+	  return ret;
+	}
+      /* Recursively invoke if parenthesis. */
+      if (grub_strcmp (args[*argn], "(") == 0)
+	{
+	  (*argn)++;
+	  update_val (test_parse (args, argn, argc));
+	  continue;
+	}
+      
+      if (grub_strcmp (args[*argn], "!") == 0)
+	{
+	  invert = ! invert;
+	  (*argn)++;
+	  continue;
+	}
+      if (grub_strcmp (args[*argn], "-a") == 0)
+	{
+	  /* If current value is 0 second value is to be discarded. */
+	  discard = ! ret;
+	  (*argn)++;
+	  continue;
+	}
+      if (grub_strcmp (args[*argn], "-o") == 0)
+	{
+	  /* If current value is 1 second value is to be discarded. */
+	  discard = ret;
+	  (*argn)++;
+	  continue;
+	}
+
+      /* No 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;
+  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], "]") == 0)
+    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");
 }
 
 static grub_command_t cmd_1, cmd_2;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.