All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH] GRUB script shell expansion for *
@ 2010-05-18 16:07 BVK Chaitanya
  2010-05-18 17:22 ` Vladimir 'φ-coder/phcoder' Serbinenko
  0 siblings, 1 reply; 3+ messages in thread
From: BVK Chaitanya @ 2010-05-18 16:07 UTC (permalink / raw)
  To: The development of GRUB 2

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

Hi,


Attached patch adds support for path name expansion for * in
grub-script.  So, we can now use *, (*,msdos*), */boot/grub/*.mod,
etc. in grub scripts.  It is available in people/bvk/shell-expansion
branch.



-- 
bvk.chaitanya

[-- Attachment #2: shell-expansion.patch --]
[-- Type: text/x-diff, Size: 23372 bytes --]

# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: bvk.groups@gmail.com-20100518153335-a1fw3v34xm56zdpu
# target_branch: ../cmdlist/
# testament_sha1: d7f4cd00982ced62f3dc9a8e039ca883acc23046
# timestamp: 2010-05-18 21:26:14 +0530
# base_revision_id: bvk.groups@gmail.com-20100512082350-\
#   en6z2k5s1ux46lg3
# 
# Begin patch
=== modified file 'conf/tests.rmk'
--- conf/tests.rmk	2010-05-05 09:17:50 +0000
+++ conf/tests.rmk	2010-05-18 15:33:35 +0000
@@ -74,6 +74,9 @@
 check_SCRIPTS += grub_script_functions
 grub_script_functions_SOURCES = tests/grub_script_functions.in
 
+check_SCRIPTS += grub_script_expansion
+grub_script_expansion_SOURCES = tests/grub_script_expansion.in
+
 # List of tests to execute on "make check"
 # SCRIPTED_TESTS    = example_scripted_test
 # SCRIPTED_TESTS   += example_grub_script_test
@@ -91,6 +94,7 @@
 SCRIPTED_TESTS += grub_script_dollar
 SCRIPTED_TESTS += grub_script_comments
 SCRIPTED_TESTS += grub_script_functions
+SCRIPTED_TESTS += grub_script_expansion
 
 # dependencies between tests and testing-tools
 $(SCRIPTED_TESTS): grub-shell grub-shell-tester

=== modified file 'include/grub/script_sh.h'
--- include/grub/script_sh.h	2010-05-12 04:49:12 +0000
+++ include/grub/script_sh.h	2010-05-18 15:33:35 +0000
@@ -225,7 +225,12 @@
 void grub_script_argv_free    (struct grub_script_argv *argv);
 int grub_script_argv_next     (struct grub_script_argv *argv);
 int grub_script_argv_append   (struct grub_script_argv *argv, const char *s);
+int grub_script_argv_append_escaped (struct grub_script_argv *argv,
+				     const char *s);
+int grub_script_argv_append_unescaped (struct grub_script_argv *argv,
+				       const char *s);
 int grub_script_argv_split_append (struct grub_script_argv *argv, char *s);
+int grub_script_argv_expand   (struct grub_script_argv *argv);
 
 struct grub_script_arglist *
 grub_script_create_arglist (struct grub_parser_param *state);

=== modified file 'script/argv.c'
--- script/argv.c	2010-05-12 04:49:12 +0000
+++ script/argv.c	2010-05-18 15:33:35 +0000
@@ -18,11 +18,32 @@
  */
 
 #include <grub/mm.h>
+#include <grub/fs.h>
+#include <grub/env.h>
+#include <grub/file.h>
+#include <grub/device.h>
 #include <grub/script_sh.h>
 
+#include <regex.h>
+
 #define ARG_ALLOCATION_UNIT  (32 * sizeof (char))
 #define ARGV_ALLOCATION_UNIT (8 * sizeof (void*))
 
+static inline int regexop (char ch);
+static char ** merge (char **lhs, char **rhs);
+static char *make_dir (const char *prefix, const char *start, const char *end);
+static int make_regex (const char *regex_start, const char *regex_end,
+		       regex_t *regexp);
+static void split_path (char *path, char **suffix_end, char **regex_end);
+static char ** match_devices (const regex_t *regexp);
+static char ** match_files (const char *prefix, const char *suffix_start,
+			    const char *suffix_end, const regex_t *regexp);
+static char ** match_paths_with_escaped_suffix (char **paths,
+						const char *suffix_start,
+						const char *suffix_end,
+						const regex_t *regexp);
+static int expand (char *arg, struct grub_script_argv *argv);
+
 void
 grub_script_argv_free (struct grub_script_argv *argv)
 {
@@ -73,29 +94,85 @@
   return 0;
 }
 
-/* Append `s' to the last argument.  */
-int
-grub_script_argv_append (struct grub_script_argv *argv, const char *s)
+enum append_type {
+  APPEND_RAW,
+  APPEND_ESCAPED,
+  APPEND_UNESCAPED
+};
+
+static int
+append (struct grub_script_argv *argv, const char *s, enum append_type type)
 {
-  int a, b;
+  int a;
+  int b;
+  char ch;
   char *p = argv->args[argv->argc - 1];
 
   if (! s)
     return 0;
 
   a = p ? grub_strlen (p) : 0;
-  b = grub_strlen (s);
+  b = grub_strlen (s) * (type == APPEND_ESCAPED ? 2 : 1);
 
   p = grub_realloc (p, ALIGN_UP ((a + b + 1) * sizeof (char),
 				 ARG_ALLOCATION_UNIT));
   if (! p)
     return 1;
 
-  grub_strcpy (p + a, s);
+  switch (type)
+    {
+    case APPEND_RAW:
+      grub_strcpy (p + a, s);
+      break;
+
+    case APPEND_ESCAPED:
+      while ((ch = *s++))
+	{
+	  if (regexop (ch))
+	    p[a++] = '\\';
+	  p[a++] = ch;
+	}
+      p[a] = '\0';
+      break;
+
+    case APPEND_UNESCAPED:
+      while ((ch = *s++))
+	{
+	  if (ch == '\\' && regexop (*s))
+	    p[a++] = *s++;
+	  else
+	    p[a++] = ch;
+	}
+      p[a] = '\0';
+      break;
+    }
+
   argv->args[argv->argc - 1] = p;
   return 0;
 }
 
+
+/* Append `s' to the last argument.  */
+int
+grub_script_argv_append (struct grub_script_argv *argv, const char *s)
+{
+  return append (argv, s, APPEND_RAW);
+}
+
+/* Append `s' to the last argument, but escape any shell regex ops.  */
+int
+grub_script_argv_append_escaped (struct grub_script_argv *argv, const char *s)
+{
+  return append (argv, s, APPEND_ESCAPED);
+}
+
+/* Append `s' to the last argument, but unescape any escaped shell regex ops.  */
+int
+grub_script_argv_append_unescaped (struct grub_script_argv *argv, const char *s)
+{
+  return append (argv, s, APPEND_UNESCAPED);
+}
+
 /* Split `s' and append words as multiple arguments.  */
 int
 grub_script_argv_split_append (struct grub_script_argv *argv, char *s)
@@ -126,3 +203,452 @@
     }
   return errors;
 }
+
+/* Expand `argv' as per shell expansion rules.  */
+int
+grub_script_argv_expand (struct grub_script_argv *argv)
+{
+  int i;
+  struct grub_script_argv result = { 0, 0 };
+
+  for (i = 0; argv->args[i]; i++)
+    if (expand (argv->args[i], &result))
+      goto fail;
+
+  grub_script_argv_free (argv);
+  *argv = result;
+  return 0;
+
+ fail:
+
+  grub_script_argv_free (&result);
+  return 1;
+}
+
+static char **
+merge (char **dest, char **ps)
+{
+  int i;
+  int j;
+  char **p;
+
+  if (! dest)
+    return ps;
+
+  if (! ps)
+    return dest;
+
+  for (i = 0; dest[i]; i++)
+    ;
+  for (j = 0; ps[j]; j++)
+    ;
+
+  p = grub_realloc (dest, sizeof (char*) * (i + j + 1));
+  if (! p)
+    {
+      grub_free (dest);
+      grub_free (ps);
+      return 0;
+    }
+
+  for (j = 0; ps[j]; j++)
+    dest[i++] = ps[j];
+  dest[i] = 0;
+
+  grub_free (ps);
+  return dest;
+}
+
+static inline int
+regexop (char ch)
+{
+  return grub_strchr ("*.\\", ch) ? 1 : 0;
+}
+
+static char *
+make_dir (const char *prefix, const char *start, const char *end)
+{
+  char ch;
+  unsigned i;
+  unsigned n;
+  char *result;
+
+  i = grub_strlen (prefix);
+  n = i + end - start;
+
+  result = grub_malloc (n + 1);
+  if (! result)
+    return 0;
+
+  grub_strcpy (result, prefix);
+  while (start < end && (ch = *start++))
+    if (ch == '\\' && regexop (*start))
+      result[i++] = *start++;
+    else
+      result[i++] = ch;
+
+  result[i] = '\0';
+  return result;
+}
+
+static int
+make_regex (const char *start, const char *end, regex_t *regexp)
+{
+  char ch;
+  int i = 0;
+  unsigned len = end - start;
+  char *buffer = grub_malloc (len * 2 + 1); /* worst case size. */
+
+  while (start < end)
+    {
+      /* XXX Only * expansion for now.  */
+      switch ((ch = *start++))
+	{
+	case '\\':
+	  buffer[i++] = ch;
+	  if (*start != '\0')
+	    buffer[i++] = *start++;
+	  break;
+
+	case '.':
+	  buffer[i++] = '\\';
+	  buffer[i++] = '.';
+	  break;
+
+	case '*':
+	  buffer[i++] = '.';
+	  buffer[i++] = '*';
+	  break;
+
+	default:
+	  buffer[i++] = ch;
+	}
+    }
+  buffer[i] = '\0';
+
+  if (regcomp (regexp, buffer, RE_SYNTAX_GNU_AWK))
+    {
+      grub_free (buffer);
+      return 1;
+    }
+
+  grub_free (buffer);
+  return 0;
+}
+
+static void
+split_path (char *str, char **suffix_end, char **regex_end)
+{
+  char ch = 0;
+  int regex = 0;
+
+  char *end;
+  char *split;
+
+  split = end = str;
+  while ((ch = *end))
+    {
+      if (ch == '\\' && end[1])
+	end++;
+      else if (regexop (ch))
+	regex = 1;
+      else if (ch == '/' && ! regex)
+	split = end + 1;
+      else if (ch == '/' && regex)
+	break;
+
+      end++;
+    }
+
+  *regex_end = end;
+  if (! regex)
+    *suffix_end = end;
+  else
+    *suffix_end = split;
+}
+
+static char **
+match_devices (const regex_t *regexp)
+{
+  int i;
+  int ndev;
+  char **devs;
+  char *buffer;
+
+  auto int match (const char *name);
+  int match (const char *name)
+  {
+    void *t;
+    unsigned n;
+
+    n = grub_strlen (name);
+    t = grub_realloc (buffer, n + 3);
+    if (! t)
+      return 1;
+
+    buffer = (char *) t;
+    grub_snprintf (buffer, n + 3, "(%s)", name);
+
+    grub_dprintf ("expand", "matching: %s\n", buffer);
+    if (regexec (regexp, buffer, 0, 0, 0))
+      return 0;
+
+    t = grub_realloc (devs, sizeof (char*) * (ndev + 2));
+    if (! t)
+      return 1;
+
+    devs = (char **) t;
+    devs[ndev++] = buffer;
+    devs[ndev] = 0;
+    buffer = 0;
+    return 0;
+  }
+
+  ndev = 0;
+  devs = 0;
+  buffer = 0;
+
+  if (grub_device_iterate (match))
+    goto fail;
+
+  if (buffer)
+    grub_free (buffer);
+
+  return devs;
+
+ fail:
+
+  for (i = 0; devs && devs[i]; i++)
+    grub_free (devs[i]);
+
+  if (devs)
+    grub_free (devs);
+
+  if (buffer)
+    grub_free (buffer);
+
+  return 0;
+}
+
+static char **
+match_files (const char *prefix, const char *suffix, const char *end,
+	     const regex_t *regexp)
+{
+  int i;
+  int error;
+  char **files;
+  char *buffer;
+  unsigned nfile;
+  char *dir;
+  unsigned dirlen;
+  const char *path;
+  char *device_name;
+  grub_fs_t fs;
+  grub_device_t dev;
+
+  auto int match (const char *name, const struct grub_dirhook_info *info);
+  int match (const char *name, const struct grub_dirhook_info *info)
+  {
+    void *t;
+    unsigned n;
+
+    /* skip hidden files, . and .. */
+    if (name[0] == '.')
+      return 0;
+
+    grub_dprintf ("expand", "matching: %s in %s\n", name, dir);
+    if (regexec (regexp, name, 0, 0, 0))
+      return 0;
+
+    n = dirlen + grub_strlen (name);
+    t = grub_realloc (buffer, n + 1);
+    if (! t)
+      return 1;
+
+    buffer = (char *) t;
+    grub_snprintf (buffer, n + 1, "%s%s", dir, name);
+
+    t = grub_realloc (files, sizeof (char*) * (nfile + 2));
+    if (! t)
+      return 1;
+
+    files = (char **) t;
+    files[nfile++] = buffer;
+    files[nfile] = 0;
+    buffer = 0;
+    return 0;
+  }
+
+  nfile = 0;
+  files = 0;
+  dev = 0;
+  buffer = 0;
+  device_name = 0;
+  grub_error_push ();
+
+  dir = make_dir (prefix, suffix, end);
+  if (! dir)
+    goto fail;
+  dirlen = grub_strlen (dir);
+
+  device_name = grub_file_get_device_name (dir);
+  dev = grub_device_open (device_name);
+  if (! dev)
+    goto fail;
+
+  fs = grub_fs_probe (dev);
+  if (! fs)
+    goto fail;
+
+  path = grub_strchr (dir, ')');
+  if (! path)
+    goto fail;
+  path++;
+
+  if (fs->dir (dev, path, match))
+    goto fail;
+
+  if (buffer)
+    grub_free (buffer);
+
+  grub_free (dir);
+  grub_device_close (dev);
+  grub_free (device_name);
+  grub_error_pop ();
+  return files;
+
+ fail:
+
+  if (dir)
+    grub_free (dir);
+
+  for (i = 0; files && files[i]; i++)
+    grub_free (files[i]);
+
+  if (files)
+    grub_free (files);
+
+  if (dev)
+    grub_device_close (dev);
+
+  if (device_name)
+    grub_free (device_name);
+
+  if (buffer)
+    grub_free (buffer);
+
+  grub_error_pop ();
+  return 0;
+}
+
+static char **
+match_paths_with_escaped_suffix (char **paths,
+				 const char *suffix, const char *end,
+				 const regex_t *regexp)
+{
+  if (paths == 0 && suffix == end)
+    return match_devices (regexp);
+
+  else if (paths == 0 && suffix[0] == '(')
+    return match_files ("", suffix, end, regexp);
+
+  else if (paths == 0 && suffix[0] == '/')
+    {
+      char **r;
+      unsigned n;
+      char *root;
+      char *prefix;
+
+      root = grub_env_get ("root");
+      if (! root)
+	return 0;
+
+      n = grub_strlen (root) + 2;
+      prefix = grub_malloc (n + 1);
+      if (! prefix)
+	return 0;
+
+      grub_snprintf (prefix, n + 1, "(%s)", root);
+      r = match_files (prefix, suffix, end, regexp);
+      grub_free (prefix);
+      return r;
+    }
+  else if (paths)
+    {
+      int i, j;
+      char **r = 0;
+
+      for (i = 0; paths[i]; i++)
+	{
+	  char **p;
+
+	  p = match_files (paths[i], suffix, end, regexp);
+	  if (! p)
+	    continue;
+
+	  r = merge (r, p);
+	  if (! r)
+	    return 0;
+	}
+      return r;
+    }
+
+  return 0;
+}
+
+static int
+expand (char *arg, struct grub_script_argv *argv)
+{
+  char *p;
+  char *dir;
+  char *reg;
+  char **paths = 0;
+
+  unsigned i;
+  regex_t regex;
+
+  p = arg;
+  while (*p)
+    {
+      /* split `p' into two components: (p..dir), (dir...reg)
+
+	 (p...dir):  path that doesn't need expansion
+
+	 (dir...reg): part of path that needs expansion
+       */
+      split_path (p, &dir, &reg);
+      if (dir < reg)
+	{
+	  if (make_regex (dir, reg, &regex))
+	    goto fail;
+
+	  paths = match_paths_with_escaped_suffix (paths, p, dir, &regex);
+	  regfree (&regex);
+
+	  if (! paths)
+	    goto done;
+	}
+      p = reg;
+    }
+
+  if (! paths)
+    {
+      grub_script_argv_next (argv);
+      grub_script_argv_append_unescaped (argv, arg);
+    }
+  else
+    for (i = 0; paths[i]; i++)
+      {
+	grub_script_argv_next (argv);
+	grub_script_argv_append (argv, paths[i]);
+      }
+
+ done:
+
+  return 0;
+
+ fail:
+
+  regfree (&regex);
+  return 1;
+}

=== modified file 'script/execute.c'
--- script/execute.c	2010-05-12 07:42:49 +0000
+++ script/execute.c	2010-05-18 15:33:35 +0000
@@ -177,7 +177,13 @@
 		{
 		  if (i != 0)
 		    error += grub_script_argv_next (&result);
-		  error += grub_script_argv_append (&result, values[i]);
+
+		  if (arg->type == GRUB_SCRIPT_ARG_TYPE_VAR)
+		    error += grub_script_argv_append (&result, values[i]);
+		  else
+		    error += grub_script_argv_append_escaped (&result, values[i]);
+
+		  grub_free (values[i]);
 		}
 	      grub_free (values);
 	      break;
@@ -189,19 +195,23 @@
 
 	    case GRUB_SCRIPT_ARG_TYPE_DQSTR:
 	    case GRUB_SCRIPT_ARG_TYPE_SQSTR:
-	      error += grub_script_argv_append (&result, arg->str);
+	      error += grub_script_argv_append_escaped (&result, arg->str);
 	      break;
 	    }
 	  arg = arg->next;
 	}
     }
 
-  if (error)
-    return 1;
-
   if (! result.args[result.argc - 1])
     result.argc--;
 
+  error += grub_script_argv_expand (&result);
+  if (error)
+    {
+      grub_script_argv_free (&result);
+      return 1;
+    }
+
   *argv = result;
   return 0;
 }
@@ -287,6 +297,7 @@
 	  grub_snprintf (errnobuf, sizeof (errnobuf), "%d", grub_errno);
 	  grub_env_set ("?", errnobuf);
 
+	  grub_script_argv_free (&argv);
 	  grub_print_error ();
 
 	  return 0;
@@ -411,7 +422,6 @@
 			      cmd_menuentry->sourcecode);
 
   grub_script_argv_free (&argv);
-
   return grub_errno;
 }
 

=== added file 'tests/grub_script_expansion.in'
--- tests/grub_script_expansion.in	1970-01-01 00:00:00 +0000
+++ tests/grub_script_expansion.in	2010-05-18 15:33:35 +0000
@@ -0,0 +1,35 @@
+#! /bin/bash -e
+
+# Run GRUB script in a Qemu instance
+# Copyright (C) 2010  Free Software Foundation, Inc.
+#
+# GRUB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# GRUB is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+
+disks=`echo ls | @builddir@/grub-shell`
+other=`echo echo '*' | @builddir@/grub-shell`
+for d in $disks; do
+  if ! echo "$other" | grep "$d" >/dev/null; then
+    echo "$d missing from * expansion" >&2
+    exit 1
+  fi
+done
+
+other=`echo echo '(*)' | @builddir@/grub-shell`
+for d in $disks; do
+  if ! echo "$other" | grep "$d" >/dev/null; then
+    echo "$d missing from (*) expansion" >&2
+    exit 1
+  fi
+done
+

=== modified file 'tests/util/grub-shell.in'
--- tests/util/grub-shell.in	2010-04-06 06:51:11 +0000
+++ tests/util/grub-shell.in	2010-05-18 15:33:35 +0000
@@ -95,7 +95,7 @@
 if [ "x${source}" = x ] ; then
     tmpfile=`mktemp`
     while read; do
-	echo $REPLY >> ${tmpfile}
+	echo "$REPLY" >> ${tmpfile}
     done
     source=${tmpfile}
 fi

# Begin bundle
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

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] GRUB script shell expansion for *
  2010-05-18 16:07 [PATCH] GRUB script shell expansion for * BVK Chaitanya
@ 2010-05-18 17:22 ` Vladimir 'φ-coder/phcoder' Serbinenko
  2010-07-29 14:53   ` BVK Chaitanya
  0 siblings, 1 reply; 3+ messages in thread
From: Vladimir 'φ-coder/phcoder' Serbinenko @ 2010-05-18 17:22 UTC (permalink / raw)
  To: The development of GNU GRUB

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

BVK Chaitanya wrote:
> Hi,
>
>
> Attached patch adds support for path name expansion for * in
> grub-script.  So, we can now use *, (*,msdos*), */boot/grub/*.mod,
> etc. in grub scripts.  It is available in people/bvk/shell-expansion
> branch.
>
>
>   
1) regexp expansion is better to be moved into regexp.mod to avoid
normal.mod depending on regexp.mod. Hooks can be used for this.
2) I would prefer dotglob behaviour to be default.
Other than that and few other problems discussed on IRC it's ok for exp
>   
> ------------------------------------------------------------------------
>
> _______________________________________________
> Grub-devel mailing list
> Grub-devel@gnu.org
> http://lists.gnu.org/mailman/listinfo/grub-devel


-- 
Regards
Vladimir 'φ-coder/phcoder' Serbinenko



[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 293 bytes --]

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] GRUB script shell expansion for *
  2010-05-18 17:22 ` Vladimir 'φ-coder/phcoder' Serbinenko
@ 2010-07-29 14:53   ` BVK Chaitanya
  0 siblings, 0 replies; 3+ messages in thread
From: BVK Chaitanya @ 2010-07-29 14:53 UTC (permalink / raw)
  To: The development of GNU GRUB

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

Attached is an updated version for GRUB script expansion for wildcard
character '*'.  This feature is enabled only after "insmod regexp" and
hidden files (files starting with '.') are included in filename
expansion, except for "." and ".." names.




regards,
bvk.chaitanya

[-- Attachment #2: shell-expansion.patch --]
[-- Type: text/x-patch, Size: 36699 bytes --]

# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: bvk.groups@gmail.com-20100729221033-hcql8glan9wm05zh
# target_branch: ../cmdlist/
# testament_sha1: 34e1d2ffaa9793e3440e6d6b1e3ec0b590a8ea6f
# timestamp: 2010-07-30 03:40:48 +0530
# base_revision_id: bvk.groups@gmail.com-20100722170920-\
#   q6dgo7psbg1vzkkd
# 
# Begin patch
=== modified file 'commands/regexp.c'
--- commands/regexp.c	2010-05-01 18:28:07 +0000
+++ commands/regexp.c	2010-07-29 18:02:56 +0000
@@ -22,6 +22,7 @@
 #include <grub/mm.h>
 #include <grub/command.h>
 #include <grub/i18n.h>
+#include <grub/script_sh.h>
 #include <regex.h>
 
 static grub_err_t
@@ -69,9 +70,14 @@
 \f
 GRUB_MOD_INIT(regexp)
 {
+  extern struct grub_script_wildcard_translator translator;
+
   cmd = grub_register_command ("regexp", grub_cmd_regexp,
 			       N_("REGEXP STRING"),
 			       N_("Test if REGEXP matches STRING."));
+
+  /* Setup GRUB script wildcard translator.  */
+  wildcard_translator = &translator;
 }
 
 GRUB_MOD_FINI(regexp)

=== added file 'commands/wildcard.c'
--- commands/wildcard.c	1970-01-01 00:00:00 +0000
+++ commands/wildcard.c	2010-07-29 22:10:33 +0000
@@ -0,0 +1,493 @@
+/* wildcard.c - Wildcard character expansion for GRUB script.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2010  Free Software Foundation, Inc.
+ *
+ *  GRUB is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, either version 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/mm.h>
+#include <grub/fs.h>
+#include <grub/env.h>
+#include <grub/file.h>
+#include <grub/device.h>
+#include <grub/script_sh.h>
+
+#include <regex.h>
+
+static inline int isregexop (char ch);
+static char ** merge (char **lhs, char **rhs);
+static char *make_dir (const char *prefix, const char *start, const char *end);
+static int make_regex (const char *regex_start, const char *regex_end,
+		       regex_t *regexp);
+static void split_path (const char *path, const char **suffix_end, const char **regex_end);
+static char ** match_devices (const regex_t *regexp, int noparts);
+static char ** match_files (const char *prefix, const char *suffix_start,
+			    const char *suffix_end, const regex_t *regexp);
+
+static char* wildcard_escape (const char *s);
+static char* wildcard_unescape (const char *s);
+static grub_err_t wildcard_expand (const char *s, char ***strs);
+
+struct grub_script_wildcard_translator translator = {
+  .expand = wildcard_expand,
+  .escape = wildcard_escape,
+  .unescape = wildcard_unescape
+};
+
+static char **
+merge (char **dest, char **ps)
+{
+  int i;
+  int j;
+  char **p;
+
+  if (! dest)
+    return ps;
+
+  if (! ps)
+    return dest;
+
+  for (i = 0; dest[i]; i++)
+    ;
+  for (j = 0; ps[j]; j++)
+    ;
+
+  p = grub_realloc (dest, sizeof (char*) * (i + j + 1));
+  if (! p)
+    {
+      grub_free (dest);
+      grub_free (ps);
+      return 0;
+    }
+
+  for (j = 0; ps[j]; j++)
+    dest[i++] = ps[j];
+  dest[i] = 0;
+
+  grub_free (ps);
+  return dest;
+}
+
+static inline int
+isregexop (char ch)
+{
+  return grub_strchr ("*.\\", ch) ? 1 : 0;
+}
+
+static char *
+make_dir (const char *prefix, const char *start, const char *end)
+{
+  char ch;
+  unsigned i;
+  unsigned n;
+  char *result;
+
+  i = grub_strlen (prefix);
+  n = i + end - start;
+
+  result = grub_malloc (n + 1);
+  if (! result)
+    return 0;
+
+  grub_strcpy (result, prefix);
+  while (start < end && (ch = *start++))
+    if (ch == '\\' && isregexop (*start))
+      result[i++] = *start++;
+    else
+      result[i++] = ch;
+
+  result[i] = '\0';
+  return result;
+}
+
+static int
+make_regex (const char *start, const char *end, regex_t *regexp)
+{
+  char ch;
+  int i = 0;
+  unsigned len = end - start;
+  char *buffer = grub_malloc (len * 2 + 2 + 1); /* worst case size. */
+
+  if (! buffer)
+    return 1;
+
+  buffer[i++] = '^';
+  while (start < end)
+    {
+      /* XXX Only * expansion for now.  */
+      switch ((ch = *start++))
+	{
+	case '\\':
+	  buffer[i++] = ch;
+	  if (*start != '\0')
+	    buffer[i++] = *start++;
+	  break;
+
+	case '.':
+	  buffer[i++] = '\\';
+	  buffer[i++] = '.';
+	  break;
+
+	case '*':
+	  buffer[i++] = '.';
+	  buffer[i++] = '*';
+	  break;
+
+	default:
+	  buffer[i++] = ch;
+	}
+    }
+  buffer[i++] = '$';
+  buffer[i] = '\0';
+
+  if (regcomp (regexp, buffer, RE_SYNTAX_GNU_AWK))
+    {
+      grub_free (buffer);
+      return 1;
+    }
+
+  grub_free (buffer);
+  return 0;
+}
+
+/* Split `str' into two parts: (1) dirname that is regexop free (2)
+   dirname that has a regexop.  */
+static void
+split_path (const char *str, const char **noregexop, const char **regexop)
+{
+  char ch = 0;
+  int regex = 0;
+
+  const char *end;
+  const char *split;  /* points till the end of dirnaname that doesn't
+			 need expansion.  */
+
+  split = end = str;
+  while ((ch = *end))
+    {
+      if (ch == '\\' && end[1])
+	end++;
+
+      else if (isregexop (ch))
+	regex = 1;
+
+      else if (ch == '/' && ! regex)
+	split = end + 1;  /* forward to next regexop-free dirname */
+
+      else if (ch == '/' && regex)
+	break;  /* stop at the first dirname with a regexop */
+
+      end++;
+    }
+
+  *regexop = end;
+  if (! regex)
+    *noregexop = end;
+  else
+    *noregexop = split;
+}
+
+static char **
+match_devices (const regex_t *regexp, int noparts)
+{
+  int i;
+  int ndev;
+  char **devs;
+
+  auto int match (const char *name);
+  int match (const char *name)
+  {
+    char **t;
+    char *buffer;
+
+    /* skip partitions if asked to. */
+    if (noparts && grub_strchr(name, ','))
+      return 0;
+
+    buffer = grub_xasprintf ("(%s)", name);
+    if (! buffer)
+      return 1;
+
+    grub_dprintf ("expand", "matching: %s\n", buffer);
+    if (regexec (regexp, buffer, 0, 0, 0))
+      {
+	grub_free (buffer);
+	return 0;
+      }
+
+    t = grub_realloc (devs, sizeof (char*) * (ndev + 2));
+    if (! t)
+      return 1;
+
+    devs = t;
+    devs[ndev++] = buffer;
+    devs[ndev] = 0;
+    return 0;
+  }
+
+  ndev = 0;
+  devs = 0;
+
+  if (grub_device_iterate (match))
+    goto fail;
+
+  return devs;
+
+ fail:
+
+  for (i = 0; devs && devs[i]; i++)
+    grub_free (devs[i]);
+
+  if (devs)
+    grub_free (devs);
+
+  return 0;
+}
+
+static char **
+match_files (const char *prefix, const char *suffix, const char *end,
+	     const regex_t *regexp)
+{
+  int i;
+  int error;
+  char **files;
+  unsigned nfile;
+  char *dir;
+  const char *path;
+  char *device_name;
+  grub_fs_t fs;
+  grub_device_t dev;
+
+  auto int match (const char *name, const struct grub_dirhook_info *info);
+  int match (const char *name, const struct grub_dirhook_info *info)
+  {
+    char **t;
+    char *buffer;
+
+    /* skip hidden files, . and .. */
+    if (grub_strcmp(".", name) == 0 || grub_strcmp("..", name) == 0)
+      return 0;
+
+    grub_dprintf ("expand", "matching: %s in %s\n", name, dir);
+    if (regexec (regexp, name, 0, 0, 0))
+      return 0;
+
+    buffer = grub_xasprintf ("%s%s", dir, name);
+    if (! buffer)
+      return 1;
+
+    t = grub_realloc (files, sizeof (char*) * (nfile + 2));
+    if (! t)
+      {
+	grub_free (buffer);
+	return 1;
+      }
+
+    files = t;
+    files[nfile++] = buffer;
+    files[nfile] = 0;
+    return 0;
+  }
+
+  nfile = 0;
+  files = 0;
+  dev = 0;
+  device_name = 0;
+  grub_error_push ();
+
+  dir = make_dir (prefix, suffix, end);
+  if (! dir)
+    goto fail;
+
+  device_name = grub_file_get_device_name (dir);
+  dev = grub_device_open (device_name);
+  if (! dev)
+    goto fail;
+
+  fs = grub_fs_probe (dev);
+  if (! fs)
+    goto fail;
+
+  path = grub_strchr (dir, ')');
+  if (! path)
+    goto fail;
+  path++;
+
+  if (fs->dir (dev, path, match))
+    goto fail;
+
+  grub_free (dir);
+  grub_device_close (dev);
+  grub_free (device_name);
+  grub_error_pop ();
+  return files;
+
+ fail:
+
+  if (dir)
+    grub_free (dir);
+
+  for (i = 0; files && files[i]; i++)
+    grub_free (files[i]);
+
+  if (files)
+    grub_free (files);
+
+  if (dev)
+    grub_device_close (dev);
+
+  if (device_name)
+    grub_free (device_name);
+
+  grub_error_pop ();
+  return 0;
+}
+
+static char*
+wildcard_escape (const char *s)
+{
+  int i;
+  int len;
+  char ch;
+  char *p;
+
+  len = grub_strlen (s);
+  p = grub_malloc (len * 2 + 1);
+  if (! p)
+    return NULL;
+
+  i = 0;
+  while ((ch = *s++))
+    {
+      if (isregexop (ch))
+	p[i++] = '\\';
+      p[i++] = ch;
+    }
+  p[i] = '\0';
+  return p;
+}
+
+static char*
+wildcard_unescape (const char *s)
+{
+  int i;
+  int len;
+  char ch;
+  char *p;
+
+  len = grub_strlen (s);
+  p = grub_malloc (len + 1);
+  if (! p)
+    return NULL;
+
+  i = 0;
+  while ((ch = *s++))
+    {
+      if (ch == '\\' && isregexop (*s))
+	p[i++] = *s++;
+      else
+	p[i++] = ch;
+    }
+  p[i] = '\0';
+  return p;
+}
+
+static grub_err_t
+wildcard_expand (const char *s, char ***strs)
+{
+  const char *start;
+  const char *regexop;
+  const char *noregexop;
+  char **paths = 0;
+
+  unsigned i;
+  regex_t regexp;
+
+  start = s;
+  while (*start)
+    {
+      split_path (start, &noregexop, &regexop);
+      if (noregexop >= regexop) /* no more wildcards */
+	break;
+
+      if (make_regex (noregexop, regexop, &regexp))
+	goto fail;
+
+      if (paths == 0)
+	{
+	  if (start == noregexop) /* device part has regexop */
+	    paths = match_devices (&regexp, *start != '(');
+
+	  else if (*start == '(') /* device part explicit wo regexop */
+	    paths = match_files ("", start, noregexop, &regexp);
+
+	  else if (*start == '/') /* no device part */
+	    {
+	      char **r;
+	      unsigned n;
+	      char *root;
+	      char *prefix;
+
+	      root = grub_env_get ("root");
+	      if (! root)
+		goto fail;
+
+	      prefix = grub_xasprintf ("(%s)", root);
+	      if (! prefix)
+		goto fail;
+
+	      paths = match_files (prefix, start, noregexop, &regexp);
+	      grub_free (prefix);
+	    }
+	}
+      else
+	{
+	  char **r = 0;
+
+	  for (i = 0; paths[i]; i++)
+	    {
+	      char **p;
+
+	      p = match_files (paths[i], start, noregexop, &regexp);
+	      if (! p)
+		continue;
+
+	      r = merge (r, p);
+	      if (! r)
+		goto fail;
+	    }
+	  paths = r;
+	}
+
+      regfree (&regexp);
+      if (! paths)
+	goto done;
+
+      start = regexop;
+    }
+
+ done:
+
+  *strs = paths;
+  return 0;
+
+ fail:
+
+  for (i = 0; paths && paths[i]; i++)
+    grub_free (paths[i]);
+  grub_free (paths[i]);
+  regfree (&regexp);
+  return grub_errno;
+}

=== modified file 'conf/common.rmk'
--- conf/common.rmk	2010-07-11 15:44:18 +0000
+++ conf/common.rmk	2010-07-29 18:02:56 +0000
@@ -764,7 +764,7 @@
 setjmp_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 pkglib_MODULES += regexp.mod
-regexp_mod_SOURCES = gnulib/regex.c commands/regexp.c
+regexp_mod_SOURCES = gnulib/regex.c commands/regexp.c commands/wildcard.c
 regexp_mod_CFLAGS = $(COMMON_CFLAGS) $(GNULIB_CFLAGS)
 regexp_mod_LDFLAGS = $(COMMON_LDFLAGS)
 

=== modified file 'conf/tests.rmk'
--- conf/tests.rmk	2010-05-05 09:17:50 +0000
+++ conf/tests.rmk	2010-05-18 15:33:35 +0000
@@ -74,6 +74,9 @@
 check_SCRIPTS += grub_script_functions
 grub_script_functions_SOURCES = tests/grub_script_functions.in
 
+check_SCRIPTS += grub_script_expansion
+grub_script_expansion_SOURCES = tests/grub_script_expansion.in
+
 # List of tests to execute on "make check"
 # SCRIPTED_TESTS    = example_scripted_test
 # SCRIPTED_TESTS   += example_grub_script_test
@@ -91,6 +94,7 @@
 SCRIPTED_TESTS += grub_script_dollar
 SCRIPTED_TESTS += grub_script_comments
 SCRIPTED_TESTS += grub_script_functions
+SCRIPTED_TESTS += grub_script_expansion
 
 # dependencies between tests and testing-tools
 $(SCRIPTED_TESTS): grub-shell grub-shell-tester

=== modified file 'include/grub/script_sh.h'
--- include/grub/script_sh.h	2010-06-12 11:02:06 +0000
+++ include/grub/script_sh.h	2010-07-29 17:22:09 +0000
@@ -70,6 +70,15 @@
   char **args;
 };
 
+/* Pluggable wildcard translator.  */
+struct grub_script_wildcard_translator
+{
+  char *(*escape) (const char *str);
+  char *(*unescape) (const char *str);
+  grub_err_t (*expand) (const char *str, char ***expansions);
+};
+extern struct grub_script_wildcard_translator *wildcard_translator;
+
 /* A complete argument.  It consists of a list of one or more `struct
    grub_script_arg's.  */
 struct grub_script_arglist

=== modified file 'script/argv.c'
--- script/argv.c	2010-05-21 10:04:36 +0000
+++ script/argv.c	2010-07-29 18:02:56 +0000
@@ -18,6 +18,7 @@
  */
 
 #include <grub/mm.h>
+#include <grub/misc.h>
 #include <grub/script_sh.h>
 
 static unsigned
@@ -82,7 +83,8 @@
 int
 grub_script_argv_append (struct grub_script_argv *argv, const char *s)
 {
-  int a, b;
+  int a;
+  int b;
   char *p = argv->args[argv->argc - 1];
 
   if (! s)
@@ -97,6 +99,7 @@
 
   grub_strcpy (p + a, s);
   argv->args[argv->argc - 1] = p;
+
   return 0;
 }
 

=== modified file 'script/execute.c'
--- script/execute.c	2010-05-21 10:13:24 +0000
+++ script/execute.c	2010-07-29 18:02:56 +0000
@@ -37,6 +37,9 @@
 };
 static struct grub_script_scope *scope = 0;
 
+/* Wildcard translator for GRUB script.  */
+struct grub_script_wildcard_translator *wildcard_translator;
+
 static int
 grub_env_special (const char *name)
 {
@@ -161,7 +164,7 @@
   return grub_env_set (name, val);
 }
 
-/* Expand arguments in ARGLIST into multiple arguments.  */
+/* Convert arguments in ARGLIST into ARGV form.  */
 static int
 grub_script_arglist_to_argv (struct grub_script_arglist *arglist,
 			     struct grub_script_argv *argv)
@@ -171,6 +174,28 @@
   struct grub_script_arg *arg = 0;
   struct grub_script_argv result = { 0, 0 };
 
+  auto int append (char *s, int escape_type);
+  int append (char *s, int escape_type)
+  {
+    int r;
+    char *p = 0;
+
+    if (! wildcard_translator || escape_type == 0)
+      return grub_script_argv_append (&result, s);
+
+    if (escape_type > 0)
+      p = wildcard_translator->escape (s);
+    else if (escape_type < 0)
+      p = wildcard_translator->unescape (s);
+
+    if (! p)
+      return 1;
+
+    r = grub_script_argv_append (&result, p);
+    grub_free (p);
+    return r;
+  }
+
   for (; arglist && arglist->arg; arglist = arglist->next)
     {
       if (grub_script_argv_next (&result))
@@ -189,8 +214,18 @@
 		  if (i != 0 && grub_script_argv_next (&result))
 		    goto fail;
 
-		  if (grub_script_argv_append (&result, values[i]))
-		    goto fail;
+		  if (arg->type == GRUB_SCRIPT_ARG_TYPE_VAR)
+		    {
+		      if (grub_script_argv_append (&result, values[i]))
+			goto fail;
+		    }
+		  else
+		    {
+		      if (append (values[i], 1))
+			goto fail;
+		    }
+
+		  grub_free (values[i]);
 		}
 	      grub_free (values);
 	      break;
@@ -214,6 +249,50 @@
   if (! result.args[result.argc - 1])
     result.argc--;
 
+  /* Perform wildcard expansion.  */
+
+  if (wildcard_translator)
+    {
+      int j;
+      int failed = 0;
+      char **expansions = 0;
+      struct grub_script_argv unexpanded = result;
+
+      result.argc = 0;
+      result.args = 0;
+      for (i = 0; unexpanded.args[i]; i++)
+	{
+	  if (wildcard_translator->expand (unexpanded.args[i], &expansions))
+	    {
+	      grub_script_argv_free (&unexpanded);
+	      goto fail;
+	    }
+
+	  if (! expansions)
+	    {
+	      grub_script_argv_next (&result);
+	      append (unexpanded.args[i], -1);
+	    }
+	  else
+	    {
+	      for (j = 0; expansions[j]; j++)
+		{
+		  failed = (failed || grub_script_argv_next (&result) ||
+			    append (expansions[j], -1));
+		  grub_free (expansions[j]);
+		}
+	      grub_free (expansions);
+
+	      if (failed)
+		{
+		  grub_script_argv_free (&unexpanded);
+		  goto fail;
+		}
+	    }
+	}
+      grub_script_argv_free (&unexpanded);
+    }
+
   *argv = result;
   return 0;
 
@@ -429,7 +508,6 @@
 			      cmd_menuentry->sourcecode);
 
   grub_script_argv_free (&argv);
-
   return grub_errno;
 }
 

=== added file 'tests/grub_script_expansion.in'
--- tests/grub_script_expansion.in	1970-01-01 00:00:00 +0000
+++ tests/grub_script_expansion.in	2010-07-29 18:02:56 +0000
@@ -0,0 +1,42 @@
+#! /bin/bash -e
+
+# Run GRUB script in a Qemu instance
+# Copyright (C) 2010  Free Software Foundation, Inc.
+#
+# GRUB is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# GRUB is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
+
+disks=`echo ls | @builddir@/grub-shell`
+other=`echo insmod regexp\; echo \* | @builddir@/grub-shell`
+for d in $disks; do
+    if echo "$d" |grep ',' >/dev/null; then
+	if echo "$other" | grep "$d" >/dev/null; then
+	    echo "$d should not occur in * expansion" >&2
+	    exit 1
+	fi
+    else
+	if ! echo "$other" | grep "$d" >/dev/null; then
+	    echo "$d missing from * expansion" >&2
+	    exit 1
+	fi
+    fi
+done
+
+other=`echo insmod regexp\; echo '(*)' | @builddir@/grub-shell`
+for d in $disks; do
+    if ! echo "$other" | grep "$d" >/dev/null; then
+	echo "$d missing from (*) expansion" >&2
+	exit 1
+    fi
+done
+

=== modified file 'tests/util/grub-shell.in'
--- tests/util/grub-shell.in	2010-07-07 15:51:26 +0000
+++ tests/util/grub-shell.in	2010-07-22 21:27:02 +0000
@@ -95,7 +95,7 @@
 if [ "x${source}" = x ] ; then
     tmpfile=`mktemp`
     while read REPLY; do
-	echo $REPLY >> ${tmpfile}
+	echo "$REPLY" >> ${tmpfile}
     done
     source=${tmpfile}
 fi

# Begin bundle
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

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2010-07-29 14:54 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-05-18 16:07 [PATCH] GRUB script shell expansion for * BVK Chaitanya
2010-05-18 17:22 ` Vladimir 'φ-coder/phcoder' Serbinenko
2010-07-29 14:53   ` BVK Chaitanya

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.