grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] Test command


From: phcoder
Subject: Re: [PATCH] Test command
Date: Thu, 16 Apr 2009 15:15:45 +0200
User-agent: Thunderbird 2.0.0.21 (X11/20090409)

BTW, I think you can simplify test_parse. For example, you write "if (*argn + 2 < argc ...)" many times, but it should be possible to test this condition only once per loop.
Optimised. Perhaps compiler optimised this anyway but it made code more readable

Regards,
Okuji


_______________________________________________
Grub-devel mailing list
address@hidden
http://lists.gnu.org/mailman/listinfo/grub-devel


--

Regards
Vladimir 'phcoder' Serbinenko
diff --git a/commands/test.c b/commands/test.c
index a9c8281..8a15d39 100644
--- a/commands/test.c
+++ b/commands/test.c
@@ -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;
-
-  /* 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], "]") == 0)
+    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;

reply via email to

[Prev in Thread] Current Thread [Next in Thread]