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: Sat, 11 Apr 2009 17:11:45 +0200
User-agent: Thunderbird 2.0.0.21 (X11/20090318)

Updated. Same changelog
+       {
+         update_val (grub_strcmp (args[*argn], args[*argn + 2]) == 0);
+         (*argn) += 3;

I myself feel that these parentheses are redundant, but I don't know how others think. For C programmers, it is well known that * has a very high priority.
These parenthesis are necessary if doing sth like
(*argn)++
since ++ and += are logically and visually similar I prefer to put the parenthesis
Getting tired, so I skip the same criticism from here.
Actually it would have been enough to say "same applies further on in patch"
+      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));

This is not very safe, because grub_file_size returns grub_off_t which is a 64-bit unsigned int. By converting it into 32-bit signed int implicitly, the result can be zero, even when the size is not zero. So it is better to say explicitly, != 0.


--

Regards
Vladimir 'phcoder' Serbinenko
diff --git a/commands/test.c b/commands/test.c
index a9c8281..1ccfe48 100644
--- a/commands/test.c
+++ b/commands/test.c
@@ -21,33 +21,384 @@
 #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. */
+      /* String tests. */
+      if (*argn + 2 < argc && (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 (*argn + 2 < argc && grub_strcmp (args[*argn + 1], "!=") == 0)
+       {
+         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], "<") == 0)
+       {
+         update_val (grub_strcmp (args[*argn], args[*argn + 2]) < 0);
+         (*argn) += 3;
+         continue;
+       }
+
+      if (*argn + 2 < argc && grub_strcmp (args[*argn + 1], "<=") == 0)
+       {
+         update_val (grub_strcmp (args[*argn], args[*argn + 2]) <= 0);
+         (*argn) += 3;
+         continue;
+       }
+
+      if (*argn + 2 < argc && grub_strcmp (args[*argn + 1], ">") == 0)
+       {
+         update_val (grub_strcmp (args[*argn], args[*argn + 2]) > 0);
+         (*argn) += 3;
+         continue;
+       }
+
+      if (*argn + 2 < argc && grub_strcmp (args[*argn + 1], ">=") == 0)
+       {
+         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") == 0)
+       {
+         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") == 0)
+       {
+         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") == 0)
+       {
+         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") == 0)
+       {
+         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") == 0)
+       {
+         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") == 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 (*argn + 2 < argc && (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 (*argn + 2 < argc && (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. */
+      /* file tests. */
+      if (*argn + 1 < argc && grub_strcmp (args[*argn], "-d") == 0)
+       {
+         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") == 0)
+       {
+         get_fileinfo (args[*argn + 1]);
+         update_val (file_exists);
+         (*argn) += 2;
+         return ret;
+       }
+
+      if (*argn + 1 < argc && 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 (*argn + 1 < argc && 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 && *argn + 1 < argc)
+       {
+         update_val (args[*argn + 1][0]);
+         
+         (*argn) += 2;
+         continue;
+       }
+      if (grub_strcmp (args[*argn], "-z") == 0 && *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], ")") == 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]