* [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, ®);
+ if (dir < reg)
+ {
+ if (make_regex (dir, reg, ®ex))
+ goto fail;
+
+ paths = match_paths_with_escaped_suffix (paths, p, dir, ®ex);
+ regfree (®ex);
+
+ 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 (®ex);
+ 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, ®exop);
+ if (noregexop >= regexop) /* no more wildcards */
+ break;
+
+ if (make_regex (noregexop, regexop, ®exp))
+ goto fail;
+
+ if (paths == 0)
+ {
+ if (start == noregexop) /* device part has regexop */
+ paths = match_devices (®exp, *start != '(');
+
+ else if (*start == '(') /* device part explicit wo regexop */
+ paths = match_files ("", start, noregexop, ®exp);
+
+ 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, ®exp);
+ grub_free (prefix);
+ }
+ }
+ else
+ {
+ char **r = 0;
+
+ for (i = 0; paths[i]; i++)
+ {
+ char **p;
+
+ p = match_files (paths[i], start, noregexop, ®exp);
+ if (! p)
+ continue;
+
+ r = merge (r, p);
+ if (! r)
+ goto fail;
+ }
+ paths = r;
+ }
+
+ regfree (®exp);
+ 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 (®exp);
+ 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
IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWY9A7ucAQYbfgHgwf//////v
/+/////+YEf/efX0Pt9OAH1pUT5Oja2AQa4+gHd0+4+5b1uraFKfbt956l327DZ9rFmR87J7s555
rE3fN5HiTVtvveeU9Gi5gA7WAGTu+uBXkBrXodN3cAW7OAYb3KY6uQABlRwQcncDcFhXc7ZVubZ9
zklulzjmr060HgcaS5Wapk0qECbZFtqkqqs2JKbW2TJPe7peixrW2YLYxTZhGGQiZT0mmTAETTVG
eTSGgnqGmQAAAMjAQAlCAAIQiYmok2JinonppGgGgAyAAAAGmTQhJVPeqj9RN6NUGhoAAA0DCANA
AA0aAk0oU00IEGlT9T00aaZMKnhRs1I0aaB6gaeoGnqA9Q0BEoImQBGgTJoNAajBNCegVPaBqTwj
SNqB6TzKBUkQIATQBDIU/QamTUwp4ppqepo0GgBtQAGTA7M0dqSUIF5RJCojRJUpCiIkFEYiERAE
SSKiERhBSLFRFkWSIyCIRAYSIwiRHlX84f/Tlf5dh3Gn8zErSGVJiJUj/Y1lAy8IWJbNE/+dx3BD
lblCxA5SCE9gGJWxEqalyTCEJFgojNg3IFghDpjqQoFBs8+GQohkNXgmYwMw3FYetgFQ2zSa20e/
OOdDbRSRZBZh3agYEE6erABRHBYIhWBlDCEwIGEMAyYQUqYSNLJu4N8m3t/iwa7Lr3dng93t93g1
3Y2ztxiRaavngc0jKInSouzOmGU0K/9CRg8WDCuT+98SjOLEqCqJBnEJSGEgwSiz/nxKkE/uycgI
gBxR6IkRqFFKVtVZAn8NSgei3/Rhi1CDFKP6CrNuEx0LTS90X7wMjZ5f/hp+8OwVg3/tH/gXuk9E
Iggev2XXifOukwh09k3Tw4Epi3PyfKI+IQLSA86sVx8bqvnCgmTeh6HrpjJ1s4HDi4D5D5rNi60u
Q5bSTAIBhE+RDCR1krfSGQvPU0ZBIZZZgEyYhlAMSHUiJMxgoWOlimG5wFBy59bkMApSiJS50Jmc
HMcm4yUQGOrnjsUxgeJw3VxDpS8OSvzm4nZi4evAaOB0xbwKEMMlQRghdsAYEDAiMzR7NtjRqTxN
EEFRBERBETCB8ozBEQGIIRSa42b5JiQSpDxiZBGCCcgSiJ12iCKiLN9pTAhRAoL47RCD3dkLx1p4
nS/RP/SlnS95k1wqZ4vxaY2vnm6vnk8CeB8GLTvx3Cd7lO/VybY0iKsNoMkDf4srB6MtX8yRi5xW
k8Rl4VudoNuavxmtU2Hzhv5OOOej57JIdE42l52TbamzzZNmHJnPVg2httYsDCGEbxOO/Q1wFHkX
SaYLpkE6IswASIDIAswGQIojbOb5pndu8gnd4OUrIQPjNdPL0OjBk6umErTEEUDlV84ZA+K+5Nji
Akjzc7nuurxVNXfY6qRRsjC9pCkMmVSl6zvERob3tGxZ0nUtjdE0+aPLZcoY7mRmbu+Chehewaet
7BrY0fCBnfqrHY1SpM9fSSeYo4SJjtoZeHQ6zspUmfxXjA6sIG2IUREw4QxMU68YbXDzOQnmPEI4
oPTJ+ynsEQBMhfZAIRGMEkkCBAYRUCLJFWCQUgEUhFPrpYoIoooyRYjBBiiogiIoKwYwiMiKIjJI
jARJLZvu1sfIedIwT/IoakqZOuUP4spZUozJyIxAUEowKgiEk3LLEGCwjEikBEiiKqQFFILFBiCg
CxisgGBgFEnsSxFigDUigdwfI5D9IepfzSD6F/W86dx5zYFD7znPcsoopR1TVIaoa1leC7bgsuqS
SlHcqOhgHWQNrdbFNMa0kQinUGQewuZxNiUs7CmZkS7EPIfRoMhPhV7xCbk6xidbU3UrFMksjgcD
JmMyzrTJimLY0WTRZY6llmClpeyYNOjuyMTZuvLtSlh3NST23LJilpitNi7Io2Pqym/b1ZJlicJU
1MLtisJLizY5KbGbKVVFGTY4dNDfezGsZ4JtUJzk1GtXYxRDcdjg2L+rgrY+t5hgWSaK/wJYOxSP
AqSGFFKbAojCFI7lhaDBe0OhU/vcT6KUphLa3Bzxb32WpqP5u7Wbm5qnbZgwTtpiIgh6j9MhD9Ph
nKST0fNFUFFhFVSNhKJFkRFBEBBESCJFEYKqixQUBQUJFgKoqwRigsikBGDjmz1/z3AfageEwMFF
EEQYkRgiEGHzCShDxE/IJFgJBR6YfRp9/vr7/fa2/44Y4fG9skDTyA43JIJCkWKkiqDGKK/cd33H
gQnWdXAe3t5Zvb2ZGMS8wzu4ZIOyiMZnEWCQbS1c2TtQiZWrdlaOChhsXWYVNsB7aGa0ZFlNBmAm
pIhl1MiCtkyPAAeBEHsgCOQId312YPEXIfplFNsE4REF9U7OtvF5QgrK0IG3viyZUH56sVqyZtAE
8JtAnhkEURznJgbPaohvk3nVDmHvGx1rsh2wg4p2udu1M0VRDMlvtK0gK3cgLSGNzgNGyDtPFhGs
vYjmS7vdm5rHckXxDDI5sSBepTyg6WiL1thNLSkMhpatYOXI5e28IrdRx0dOoMg7UbpApmaMb4SZ
FzdRxaIkkEE6XDGjymJuKetqrMBmTkoUbOhrnHzWS+VxoiSEQCgt6hAGVZJirQeLcO7y5xHDvJCn
amQqkSDiUXKJAtKWL+TiHj/wrUQ8jU/5VId55e4CzAgZPW/UYLwllHhg+NPYjpOQbnfIe0QEHkRL
WAPTSKKOTCrm49LmCJQUrAGFB2Cpcf2CGYbSlOIcYlWJ1CY3OrpTGFMh1Y6jJSjx97tuidYNKBvP
OjxnVovsA+xOYiYeH5ye8fOePLuzqqoq32cgLh+5JWA8K4HE6AXlCBC8CQmXDA53Xj788j/4hHa9
qGDU1ag/uf0eVzfxQ8uk0ZN8xhyn/L2P32cDHb0OjGBDdR5TxHuL6MjD9Rhd2drOg0hY+w4FqpT5
eXF8B2Gxceh1tQPkmtNhdDldyn9k1PGMFTMVRQ7GhUkwSw0MKI1YglgMBTAkKAhFIiEFkIyFA2Ty
agGSIiREE/ksVSeqaNrqMnS5yxg2tbvMDJTN/jWZtg7a7dJQ6DUcxs5XWWfvNzhrkb1ZNbmj9xse
sgZFcQQYTZw6WE/Ap+ZQ64X067jKdxXo7XRfoqa+K8oLSu6hoUTAoVDeSxsLAFabbNT+/6cGTk9b
Of4Gtyb2udeHXHKy6mJaepdTJZLqkiIwDJI4ZcJaHBpiTJTuNamPKkMpQo7JubuiYsWadKS0jNVL
oMSooJBZERjFDgWYKeK3VnVDrPPTZHiZjzlqI2ItCFiEIWCBbvyz8T+0dRwCGLo9t50OKbbmR2zk
0MDh+5zXRos7p2LKssMkI6A3vNHIxTMutEfYolTN/aTAqYGYWDM0DbyOZfY2KUeZ+195g6H1uoDw
g1XgFUvvUTlH8eU28hi79JQ0nMUahyFrPSThxMG5DcYmAYOhu2LnGEJtY+fK9zzGo0BW5Acmy2tv
fFZqZsWC7HCYOLRPz1s25gQ6USYOgLRbnhDxtXfz6fBPIyxceYKOspm3alx+YLlzcFi+vVKU5bJC
u0cT1SSGp1V3iiVPFFqpm93hDucHBOw6p4J3t73lMFFYzbod+nVvU06cXiGnSOKUcUb980znDlak
L81AsS5d9DQNjUueJo4aEhhLvB2849G7b9uaTo/qpUILBBEQYHtP2B7Hs+z8/2vsWPbsndvcmq0t
D27nLeS50M8LeMcI+rZDXGqNF2hRopHjPyT4JaT0oWToCIa7tuI20Spe1zgzi5tMZuFTlJche/iI
/T630F81T5vc9x07n4QJJ9ZifVToOSjRWu75J3teeeNt+juOZc7nOvb6HcZ9OmIRhjlSgpR1InCU
XkaPE+JQhIM6GhUOs/keB+07gsQPM4npVQy8Hadni7dV+Hjp5OTyalRdYQUOUINGCp9ogenAcolK
ijbLTIXPwhKhP7YvYUpCASKwj/3OgYQ9/w+vaaQ6T25odxp09hQoWNi8zXkjNk2qWUtNCn/Cyf35
EnjKIN6pOgVEnNKQprWgpUmgpIbUUoI7iAo6z9eRY+08Z9/l1GHHJ0YFK00H5FSyCdkU3659INCW
F6tLhfB9PY2InyFxYInYYQEiBEcMMERbD6CQM5gTcQfMYlJwitIdcVMCFUthL2tl6j7NJJ369elh
rKMWVo7D96Yhg7Ex24zi1KU/nNGp3z72tNWsWanW6tMZsWWKchSjkFcfzGhgcnewqS7Ap4PLNPJo
LorB2hyiinsMxDxUOJC5IkY9oCy9SIwOV4ypAmD3KoMzOH9UvaOKsiyB31aCwQgT3N9C7FdxcQiL
Q3qjkSwxIeOLDywXMjFYp6Fi4VL8EtjcsawsV1kqbyg0qQ/0/zLazuO0DqXWFFC5FRS4qSXKe6dE
YmCb2S6yiSygUUOtSJ/dSQupSoMCyWKMh+gKWGLCyCDIICIJEQgiDRijFIo0CBUlAiQIVIoHWLPv
+8UREQOs8YII+HxGCmBzA/MxSeZ3rF1DVE1pSQxZ56WtZSaOvr/GdnsrpO7wa3Z7KwRhZsezCYN7
yhiyYYT3S7lrbGosswZF3JZfrpdkuxPGUaJYYtiZaprmMa+KaDv5HM4JxiiuimxN5mXmbspjmbDN
gtz4SNExDI1mBuKla0uHY/tOlfOD9nw+Ph5KwK0pI9NPopVCxZL2L7QyIF2AQbO5+8fkP4I8XJD+
pJ4I1FmLQyPWMD+mRSFJPuFSRGVCRoqIYFInlqD+SU6z8KgFkbCpSqsLPsegMC6PhDaeQ4k5n7xE
55xNLrEvrA1PI8VfKecdCHOrmr+ApqXHzTFBDYzQC9CsBNoJ0Ox5jSHU9CsJGIIg8ETCI1N4tYnG
IYMzKM7V5hleZi9m3/Uf5KjxjYOYdDcQAwaUgjzdbZlfhzq9psWZdvfFo3kdrppu1+T1Z2LpUc1a
i+spDAaMBkgaD1QCP64EYiocToDwuIiAYdwSKQ101eOlrWPEajx12prB2OwxsrDYdY8iCeFfwAsQ
kf8yhPtC0wJ90ZCHGeTo8vpz19viOzd9y+AltLbC2ltkttttsLbbS2y220lt5cv2uk/pE+oIvjm8
ohOP+SI3vB+3sUb+L8KXqegnskZvpWT6zLRALCAtd4zx7vSO0lbdHJQ7kykEMoejBFcwIcdpCBoZ
J70xvQj77oVk0hskqC12utzJwiTgwlQlTkwykDSTHHNxJxZrFAtnRXGA4QQwgF5IMiBmYkV1fYDZ
Qy0o6kjCuqmV9OnVpFLvlE89UEQ0oCePcrOSbduxnmva0BG1ikXZMkMsmbqSMvlyzuZ77tA1Nl2b
O77ud1s2q+ar5MoXMJtEB0YpEqEwPmYEJiHMrsOfYIC5FeojH2ltRJlnPTkmwqzUQK7LD6PkKlJk
XTRyDk5hY2lobaN9BoSEYRFW65mt1ho0mg5QxH0EzCJcdjAuGZy42XyMVHGEkWs1DLnj0cNqY7rC
GcIVJIY7GepwbZDp1treZ7L0h2ozN6GM3JGOTbaxk1bm5itpJMom/MxLyxqkQveYTLE3ZZNjTcpq
b9UECKIieEREQ/9C5qUKEnEMGTQRY4waGBtCpPRYfcWVhqy/ICRsRqIrjxkuDWREeiJolHAucxY0
ZSeDxYdyCG4U8IkEttzKuzmU39OvEPEMUWKB3du/f2bHPnIvBhJNOLTLUcIQ6UQAHSAEUYFbUI5b
l45BwvGA+Ppm1zEGcOA7BzdiYjCIsiq6AAyAQB2X3hdoSI7w1ueDNF2NMj3K1QaxEELEOapu6n5Z
YKjs2JZqYRPC8u51ZhCFbELtL3L09SSd3fd4dj9JJr1FOimy+8jKEMLzdu3uNg6SwYicvRfCLmCx
2CFNGlxNWAZlqONShcyNyu3erYscpmcNg31OZOk3O5Q2lzgiZkDEq4lLld3uJbItWYESxjQyQvzo
VYJQRDtYMwxUQ0Unby7wMWfy6GNxndXw7z02VhJ7QnilXYpBY98GcZdCc435zfLQuiJqxVeBcrNK
/AOEBAiKSlDIc7kd1QVSiQudXCkaiQXA8RDJV3IIWBdmeOjCVqVqx3lNpTk+CvOrsrozobgyrHVM
GhYIIXEnLuxR4lrgNQaNAm9cwZh7R033IiW4Kp+QA4QaK6VVu9CgBAqiiqQ7Cw8pHbgjU4LFKVg5
Mol0lxdASQ+QzlWVFZduxkwxlpzTw0JjMZaE0BtL9m7kcxAw5VW4xsdv6fAPLkUKDUA5pAcgjINK
q8F9F7N/P3yF3rkm1ZlwXUVJM9h03Bgqc6ieUnCS+g3VOzouGR0N3eVDsyOBu0ksdzgUXguUPI8z
CGCVPIePSKEeSgQgQuneVto3qYqZsXPnceG4Tl8iOTxnKeCPfPSekFwAA+k8wW0YJWynLN4e6YQE
FICPcysoM0KcALwXslIEyRlKAdZfkzKe6UvDu2fJm7gh4ZW0Q8HAw8xgGDi60MgWqhWGda3dVKXk
g0Rq6hTsUQskFVDYIaCuxw19ZY06UQsXPlckFSCIlrDjzsUeex7G05sUFs2EYSCDhUEYESb0QGQN
HXFLlQrbFEwqWgRT4zkFTgHW5ABjqmUxhGoLnbQre44Ihds3KiIjCAHxdF7h3HbmKDyTJM66Oxg2
kTNGyKDYQRhUSISRi90BAgXBjDAVJNcasqioraakZL3YWye9DNdtcJarLYt6/BTLQqhH92sZIlOy
aBBTi9Y9HbF1FLMVEFWehREOCM74WK3sLOGz07ECuGiozlQGNE3CKjGNRgFuQ1UKvLDpSVYIzKKJ
ZURB7mB3l3Hk4yMb5oIWrNM1V1ZYg6IitIPKWJcHMK0wGOuA6Ji+3ICIkxBAtgwrwq5DVpRB1aho
nkeRNJ3GuPKly0CpzIrJZI8vcqiy26KLQjklZqHBWoSJSgVoZj8fsTzPmQT5wQ2HyiETSDRXWIa3
E53n5LmRtktWJTkLlOO+OeEmMbTwtyPfLkgMQOa5pLWGnPBc5N80NZyt4HrKVkci74dWH5yQQYn0
+w8Z2NCMgCANJzLSVhuHHc+HnocbihncxlVVWr9ve0C5IJfDbbY8+RDNYhAhRxw10i9LvfhBNEuS
e0kGx+LPxP4rgUrQcnLsGXwLCY4HPATICSRPZ81WufUc2+OCWMEzinQKjoTdwod9ByY/AAkI6YYz
2RAXpERFonHTiCtxMLmSY50NKWf4WIggN2Jh2NEFoSvxy5y6B43Qpy8Z5HyREiEiWnESaEKhaT0G
CvWtumLYVHCvFHER6LV9TTwFJC1geBaqOJ7cYcdQHvV8mVowLJA4FiCWNDx6GBmsSailaXnC/Ib4
kD6mC5orsun0iHI4FHTzIsdu+jFzZnsXKHKj85eV1CtDgsVORSxI3785HOVIKNMkOcpvB7GiJWti
RaHwPrSVz4JYDSHn5ie4P3nqdvHlwYTCuurO6zurK/G+Fr6ZYgv03AQzDfqLVlYWuanmWTet0rvQ
kBWrG5aRoZ+tlsxiNLWRV7268dEO7XtmCsMlNZg1/QhgQi8dDCAgTSCFwVCKEh5Yq6dofAtnsRcR
FkfpD6SS/vwNaF81sZgq7BEuOB4MaI19nyjUKR5Z5M65DZxcc0BAoSrpYK8vDEjAYdwkeJ0gVq6j
lu6avh7CksJOD6SbirTSMpETJG1SqidnwKoJMe8ckJnJuC2N8GsxTFQZps9mYsDiAfVChSt8O9Ei
lk2LXA9dqWd1trqxIbjAr/d9kvciPisDnTlKodUpOa2iasIfMiBRHyLGwWRYPGbEvjYINu/nHzBR
ER3gQROB2Dv27FCXcWDnknDjR3EYsEh6KU7AvQpTHSPaVZEZUM0IEBwQDIWPrRN2QSxoQMgJ9PGc
BuZ4wsUe5C8Z1R10BBDLCs+E1phQ9Oc4uPOW51RkzguVM2rqJc1arJ4c3LYVbwBxAEkAjCBFgGJ1
FhUgjHoxSdKAcK4eECq8+kIHQWQRkOzju82X4vTiqAjSrfOZpRiVXpKiJOABQiJz9D5OmmZrmUxF
OG0oIbRYuwoXWpgvrbXfDslTjQr3L1ReL0K4xaLeUPnmCGGKCwUFBYKCgnUd51HUY59UTlXblzvh
iw8GrRj0Callia+4p3MVllmwx6DI6J6eSLUqiomRgkDVIh5u9SMnO9bI0Jci9IoNdQuI/WzkkEny
idGa5bAIfRIkcEsnU6PvvHY2ifWAlZURuDY5n9yHTlO/t4lojwnVhzc26ElMxLxKmlDRCvR6B2Mw
lRAQLncipZwY+UQ66qiJDdCgeWzfJIkDCk9nXl2/SBEbAuMBcriBMTnqnHDIaJxzU4OAYYwWJlOP
iIPCp1urZsVzx2N8mTWrU29DhE6SLh5BeST3zwglYiIaORPFqoYq8h5ujGKj0bbKtSMId4HKnNg5
o63qnJIp6V2d1LPLnHiG1A1AwIACUGIGgIB0dZaEpjoKRiHI6mPAbVKxECfHymF+8szuROCsYd6D
3TvogaY0EyKcQ9KSKGCD2GJ2sjkpVWOXOVwlDwpLBQ2bv4QS3C0s5zFXDjZTRUY3gN2KBJT0dwLh
dsYgxctyvIVB5cjfRGTHR1yZnvZvNXQdumh/I+BlCpM2cmjaP3aVy5aj99qIifFESCIInBKTgUbq
oXOQfw95vE7hit4nh8DXClChkcYhlcE+S86PzE7IVJsFiRUqYefeSBEdJQ3UmRIGLXaPDOyQYuOb
a2aXsyu0aKk9xpynfCGnROJ6jaeEqAnnKS7whyQLq5HrB1Fdo4nMrMZYUgtIOGg/WW6IkNjma3eX
ImHp6gfA8J6DAMCF241Ox6UECODyMoji7J0cttbllMTRV3re9EHDpyiNMrHE1aGaDNWqIG6IIZlR
MjqsqhvMGZTCU+gPA6ViG4I3qvYkU9hAn6+rGR4hrsMDGTCZLDHlnvRYdw1YQWNmcCngslClS5y9
+msrEikh5M7FTxCsVdo7BAuQKjuTcleeqb35lirGDc+sjePGiB23g5xeyrIyQOh45A+dpTypamxz
WqQrpYm7DZTqeuSbYQy4N+bBdwcqw4MmC6djsXDVHvC1SZlDtjHExaFzq4sOCLzg2PV1wiaHyJWN
QuX4OHULI4Lj8EzjyXzZXlAidz0oIeRzUMTy+4NnLyNOGFeNnnqVrOfy0RKq4pZ1pwvgnQYBy7zl
l5GzO5vItWBW07b59mETIqXFtt5sW+mckydbIS+SW7LbEDRIsekBEICHhOMesZbXFXXUaeAAsHow
cIsHiBmIyTtM2UlERC3NlFh6scpxl2Y6EwPK4q11kwyQKzS1iNexmdYthy5IB8mpS565vrZwaM+6
PtvlcE/oQSJ2FSodw0SH95tlPy9YO9wW5XOldUJROB5ZtEx3sFaSIHhiIqTBSvYlJ4zMjjc+AimR
RjdS49AYlDcnmAli5sUnSolih3hEpwSBdsTFmbh5ECwI5ES9PdERQ75joto83Bcrovk4HjhwONC1
LHRPpp1OnK4tRynvFAa7ccVEfctHVnWSAascmSAMWOhOQ+ce8k2I+cT5E8p8InkeKPjzhDpcfDwe
roy9cufR6D6lGRPNON2SaSknb2NKHMiVoavCQZZXCtphZKXmALBJeuiNyNLBx6c21CZmSjI7VTKK
NI19+DYpe27czhDBGqWaWxhDBvrbqiWJ4ab14SzYSwluxnseDa1xZWmsZhEk8HhARVKDSXgce2Jj
icW+8IPB0ZPcz2kqQ6oKQdCuykitFhc6bgUQyI/wTBJKirjhxseRJluA6hj76CORAYEo+Kq1mTuH
cwKX7zwFBZYg/gqMsdXJ84IElLc13FSprRJEDAqlWYOmREu1aDxSROy7x6gjkcOCxO6tuKRR6mnF
D3REgMAyIm4jtRFOCFn3JHRcnvFDBDYriU2tiRgxw8ZHRHjujIYiMpBzhDsYJQKk8+KhszAlwVLk
IESxoOPin0gH1VROQCJ3PUj0gm3QZK91B+I+Iz8Cz7PsPs0h1va1wd72u3lVoKHarBO1WwCsoEDo
OlWohdXnVl8VbmCteq4i4q2QQuBRWpjbBXG8EXwK4ZCIY5ZK0VtwNbIwpVc3UtE+Drf1KSrz6L2T
BV1c7ON3NrMXrMhoda16yZ4/gzonjN5wLKcK1dYuKdWsm9FGzex52/UKyJ2QxUYcKiq8ZT/gHmiB
UWKqoGSRwRDP4iRcqclUe6Q6hYUUXDk0KSgyeg4102HHX16OWt1TkcQ0idhnBgtEkyCjF+okBx7y
vNVaoNwS+/bxYsLCSILGR6veoi1kzlZmIMeC8SZh1zgyO8iQ4JNo6+lgkPahB/icG9R14LOxg3ql
1JvUkxUfH8HFSXKZLJqXWabLE5lKYb9Gi7PCXOi7NnjiduCRbu67NipauZdalVjgs7l2Ea36LHSQ
gm4Z+T5kT7VEgBFYB+T/FoUIEWiyLCkkoVAUqRKpB4pUkLIv3kFdivUqbgikjCEEhGEYh1QKD8RF
FgosiqsFUWKpFazEGQslhYUClASIIJERZAiRCRYQkugHMO/qPgD9w0CANGAUQiaTAaME9o/UrEo6
Fi9FMoQIxEGEABNYWf6P4vpU//P5N3+CwSz/WrU/T6n83FqZlw/srzg9iSKMAkCCkUSQYwX/h+9X
/+XDdifvf6L97rNakbNnQ4KGK8HS/5DQxtHF5XZvXfBMTY/5eUy3SEkccDHYGCkVrENxiURNilii
F2zi7SPK/tcW1X/DR3uQ86tywXU4vFh/mjQNxoV/N5HnEu7Xg+sjmpoCFHQ8jk6nSO46HJNC2Ez1
nB4txNziAXdbYMHi7y7Z/8mJR3N2g70S3Ao4m5oBRfBmahhV1yhoBzdHQm++LtY3tRu3rRC6hsaD
ZzLsamRqv0K7WzVjQh4n+4UNTY0O96CO0mD+HYT0n0D5UISEO8gQoEKFAoHcowgLj8ECE+oG5/Mo
ra4e5fkfYQaIw/DZJUQ+kPoLv4MVkSEFg6+j2uidzslep9Cyk+18ft5sfbUtjOiDuCcz+SWUfmi6
WHrCoNhMJlLRVMgwYQbLREY4KaVbtEgxYMRghEgVSVNpRIYwy/Z6sA0SmcMZ7DwU0C61tVUC6oMd
SQB1OIVAMTEo1LKFmEIpfCRkcGgGa9f1rF/krAPAf7BQ0dh2n5H+hpP3BY2n8Mb4X/jqP5cUELkU
TMqfMAlKJ2P5wj+cMmDVywfwfmlwfo/QYMgszFRR6k3oE7FxxKAKswhXAyRcUMYYMSDp+9kXlShp
/V0yFC09jxhxvcJlKGu/giYIQIjxamRQwY+KIl+4gRz/Xg5MhgPBgmcyLEyLEJHyfoPwutOQYPHO
fLzUgd8fL5FjyodGTSIlUrghT85OAahDSbjjt1b3iZQqIYkqXbVwKGWt8avARD9wggcFQ7Wj2q6h
aI3Qhz1Aoh4XsHzKA6FcXe38wp3DgYYCHMjgQ+0lIBGLmkEAsC/0jqLpirkAeMQ1Ap8kuSD/P8Kk
/3/GqXIHo0HQJzIqPXFd4aneSQkYFkADf9Y8385+pRGw6gmk/8pwT9dKKRk0j9/1wGAwNiAefpOh
PICJIwgoRTaaAh53cD5bHwA6g7MVLG1qr3wRPC0wdKIFldQg2GwLrDw9zj3GlOzW/d+eD6ej+5JP
nU4Op6fis+iz7NWGp335/Bopk+D71j+w893omHCkRisyDgz9uEoQMmPuobC4SNliX3/uyQWnA6S8
j9HJeU5m1ViUyJQOZto5NMOsRLPwopM5GMXODEBwaOOHurgZeDQUHm94POJe9Bm3UnUqKQ8dwEQT
B1romHFDmI8fI+fZ2N1OxRE+654lyWPvInjI8gYLhbBgkm3lCAxDFanEC3ckchsndU4OyRuPMmih
swJzORK8dhtJHaYeF5hNHNxeFWwPKC9CvmdvvR2q98D6j8YpuE/dMo/tli09wMQEQQQxCgpDAk/C
TQWHpJ+E5BPCemhSyJLAEsSkSkYUkSxKSJQGSyCUaCUbBKRhSJRsEsSwSgykSjYJZElglG/jJH5D
8xvvPs8kA9AX4EBX0bEKxRhACMJEiJl5+rzlhE6B9/exI+Y+U/YPnLHzFyTsLSZC48goRGHkj6El
eXzOcRPKQ8tUk+dz8qXYpYKn0EiY/5r0PjS7yhy4KTLwD8aCROPpGNGhipwR+rAbLDiMwcOaMDoN
QH5c/4CDixktej4G0mMK+tiiwGvo6FNWBiQ63oBTUKvnHubHv05beQ1cc9vAvXfo3wqVgdyHHI4e
PJEXGB58ilA85MeFMZ2evuBxyLxj+PdkiacT26boxhNbobMHNvYtXR4dkj5E7lCMElCzwyRhCkJ1
UmRkiJET3MolGFEKCE8wYKUFSSKBUJiKEYCUIgSIwYgQZ7TAO7Q0DaI+Seex6j1YpeSVd+RoGbH6
j4pEHnN7SSWHugw8JmkZA7gEREBEQQZ/r+zAGJERAZ4vGNmTNXFCh7DVnpOwOsntRJOaPt7iTr6k
9eIZkY+E7J9ykzjJRSjICIqhRgURYIyMnNET4RN6k/3FQvjPRdhnLZqUVF48/b776GNIhJEWBgjR
84fMenEoZeshYo+r0dCuOvH1aD5j52pU+sW8z1OFPUSx9i4rKNitYJTMSBAXgxGJc+wweECA4fIy
TGLhfNqoiZhqzDYPo4wEQgpvb3R1SJNRzYthozeyHXCF3Ns2uWHPAeJnx2q7LnJiXNxlmjpK/Tgr
mhxRBh1B0ojIKiURUQGFRFDzMj+wZto8HJ4xq4lyeziZnBdFHnBD8bPCfsIiI7t3PQ9DzJj4nbR2
Hdy400E7yTDqkJCpqtQ1C0Xk6ALcYu4D12WlA4ptAHL2vi/JSx8lSZqUouLKfVLrlJZ9hYwGS6S7
KlMEwuqyy5xd1eTu8m8t2rm7m49mh3+MReV4B9hDhwCHuI0IHypQjHi7FwCwvqPRtaveJ6VDuAOC
Fj3kcSJkH0BMfA0a+mPjJO9HU/aeaLvrDLlVo+qnstbwUmwnTIqSdJgLMD5rI/VSP6FJMpkYhW5Q
r0o2YVLBYDQ8BzQ1R0Dcu3aljPQolC7GKxxSzQNQ+9zwNDdqObVmt151sZNzL61dVVVFQVTFZZGu
fuQ39rgAUxCISCE4cBSoCnPMPFtVXVLpF9fiWr3Ot8UnnyCDZ9R4D5BDff7SVlSovyA47UJQqlg7
ilCxPASgyzhLLa0l8Hgyv25YRx7FNGiGFuyFO5K9qxckaLbKhpQOs3NNlGWmMVhrXSlfVWMhpGQF
Y5gOwOvrhojWIRQsH3SV5GTwYgeRK5AjGV/f5DyZ/y0B2S2bELjouJEPY8wHgO5ngYdB1HjUSKJu
B1pFDuISEhGMSRiNBIEqXOoOwaIpQ6BEIQvJT2R0s3Y63c7+l3vU9Tjjrs9e7N1pHJi0rX3bmhvd
xxe04PD2Y7GZ7NeDhMn5T8D0J/CTqk8UcA74dbl5vmZzPplHV5S0itVex8dU2NkqjhWOA3jYMN1B
zk2RrQAADUUJH3GI1Ae1vXnnfJMJTotzXstIks3ON4gC2veSez2Tu0jDpTjypk2c5JoJL/FYsbdw
WLnEJ4FXgxc4AqZq+Vxdt+bD1brpTV13Wm8k9D7udrdrJg521nxJOQIG3fGD5QZ2zZQ9+X0K5Cu1
UPA+j60o0IFApCjOr9Lj4znEeue2fRMQ3NQgyfXEoML+UGiU+noNxERhZIwbqfX+xCvnV7TcrCKJ
rRSnoqghf6P1Gp9nnHAfP9BxLo+vfV52rEsq4DbrFqykmKMkxFkN8MH+/jDVeNw7p4d1c/1pceK7
1fAnOQIMGETdxohrYHrCEIPcbiIxfk/OWdHzjo68qXCKrbPskn7m/mnVzK7RCK8yFVQ+pLPxqiJQ
QNLAUoApEH6S6i9v0pEidJ3QsiLuDh5NNU5uYyDmLHMsAwkkFgzsV7L1fkaSrCsgYocqIOqvFXnO
d6VFwBgMIDQYolIL6IVIf0aCe359I+4+v2Hx+Bb419Z04/OZmB5DVX6LfcqjcuPyFLFjsgIF9lyw
x+qW4FufYGnEjZmrmPrECo4laIk3EjWtkjo2ddECw65UK1dQPtBejgyUyZG3jBQnzIYoig0GkFfx
wKDrFjkK0OQcQLIljU0yViOH0PZBA9ypY7Cw4HGyp8EzfZUOLH4ozNnjx59HHBYoMZOxSkHBUqxy
WSYoxE7+EyPulJiCJVPC64INABA1q2Om7g2bTLqcXqRJ94hp12kTh+iPR9zpaPE/lIin1eciLHoy
iRUTSynkWUp8Vllyvwe2zpLFUipQqKTrVLJL/H9hz6t+xDAHv1Qou4g++IHhVhp4e4YlSCD7FpNY
o5upZifKHOa5JhPcyU9h8BPmc5wG6TXONKegVHlLYvKvR6RPAmZGo1KcxSn6hNNTvLId6PqgKHQl
xRTtPFwdXqH2P3CCBYA7n3vZ7Ho83rC0PPArGBSIVT/GaIHtF6PdJ90TJg+ChYqQ1FjUwJSiRlWw
oiWCSAplz7S4SuWuGuEREc3YOIZLQ1iTY1SbJWBODJRR3D6QhUSHypB+nre5Th7HvfgsBkaklEIw
ME0GlUc5GAMmQlVugXuknv0B7l+kO0S2hHMgGasEoxHOBARf6q0BRd4WUj6Lq+8D3DvCPlDMHHkr
0q379smuVMMFVeSGRHmcze96ugPQrBHmCzoDQ2GKliHzEBOD43ly89X1J3JteT3BCyWSUH9iSVtk
n0kldu+yt1U3m+DEMBBwED/UGrQS1DkV+H0ryL2APuRBv8GqvL5eUzdivr9fAhIRfWiMCsosRGDL
JSVgIUqkqFLSI2liUEsRlBCp9KYzghJ0P0UeDqAMwXWKaR+hEUP7eMkEX4rwV3qoFwiiWcCrBXFd
z3wCjGgBbx7Vny+iIa98edPd3H6nM5G+Nz+i0ZxUTvtLKRGRU6onOrB7uw5n0K8iuSusD0wQuhES
DBAYRWcoZiK+N7FV5jWBu6p7hIxMDiblmRgn0PlGgxh8VpJ1xe16iCeljwdKtXaivIoOjM2jBqef
tar5Ez4yh8ZJ8CTdOh9A4TXzMx+ZJbBD2WTtRAqSbNk7g/WfGEP170mmmo0SPq1eMtM4nY4PvPWi
Fz7TqN5G2c0jSYsjjNzZD1ItPpNEvBSEFEu8keKSBqKKGSIl1bK3AivwGCuKgWHIi946ADtXIF0h
iiB1gEFXrIqmq5JKID0EDO+NzuB8gzMkAmILeK9nDpA8z3mGlD4vwTYC9q2aqnHU8yQiuggPpVoN
FmALdAor0HcZoFniGxBD0nLqfnV1IulBMlelHFDIAoOY/cR3nW+NoScntNjvuXkgLfG82plIZibC
ZyTUhB2dDrdqxZ6pue+G+LBijBTGqVIiAp8AzJNSdwHNgQ7b87a1uJJC3FOCZGSfMgdIr3NN1g0q
+t1iFtKaFgr3ORoaSSo1eGTvcpJ5Yok8WycyM8pvmJq8HKazzhqmJq11ZSxZCt3ZdSxa+wJjPZZn
toGBhz4hSCYlyhKNWqhtYVhVs2slQP9g4tUKgREkEZFe4O8F3AmIpofQC97ylh2LdMlV9b1G0gYF
BoeIxCio6Vi6y6xS6pEXYMonRP4ToapDoAyS9BEIgiCoLARgwSMEiRQhFI1EQoKVGq99HIzPaQhh
gmxpJ02jpR+4LaGK0kmcNYnFMXLFNq7UQhoQ/ECBdgZVV4q5/zPSC63rFNC/7tCTSQ7fTefrO9yX
NXQXXfyUl11PmuejDLwPo5gTmBkeYA8AqmssKjFQLCrWitWDVFJIQRCwWeLZnqG5YvRYxIyvgR97
3PH4PsFjP53nbxXlyokpUVZZcoYmwYS7WHtiZP38Zk3PDvcrwOPzDxAjedwnIl3EYD4X9Y9LyegE
2/Fek6V0zgcUiDVkWSGqRJxklIkwKDgsAPirngPBsiUQF+7q8PWGsQ/IyD0AD2KQeCZT0ZyTujbN
svLzRtIouUXl00VfG1SgXikAgiHIlxT7W5Qir86Q7HC79YaRI6Tsd01WWlULSypUf0xKtP3p6Bq9
ir9T/4egP+B5REAs1eU1HcOQpgSfQfR5cAk+AtMUafSh6T0J/VucZpJsy8CIJKKwd7MGCmBg4IWR
MUlJgsYyT7iPRu7pqhk0lqqmo/VVb39ZSlzJ009whVAHU7/sVuokQQ+08xComoYelCAbGEIQ2BsK
vBIEsSjFg3e/y8+8Rd4vTAgQ1NEoEhgwQuWO4EMqK5uI5rT2p9lST7bTAj7mpbgbN0R/MqRvczOd
EU5iyVM9w+x6psdEk0ZtDmu2IIBB73sPEIrd0iF2Dk5mAnAMHrI73amoIepBD7hPh3nJS7s8b87+
5BD0h18XF2CwhDkmtwPLJH9ban6E/OJ70j5h1zipzke83jCgJQ3iC0gikWkLHawOwh6IlzyNsFxQ
I3jwiEa1CrBmaUSdhpNpk3EEeAlEGaoKUSlBpsSYCl4otPbKxil00iifwlSTfLjrMHgcXQ3nEKNL
QTGCm8zzMUmJEhMk8pOqkEKoUeQQxBbti3MjYGgq+RQgGKqZpPaNafID6pUeckl/yftZx62kl33q
BoPq/lMbQEh+oZOIPpi1iz7XCk13ChSlKblKJBBRwVJglThcOAsExFimr8KrE0p5Kev69UgPzT3d
OUbom1Ual9ypYp77Sc1l5ddcvJcWlS665RSAVhRgQhQpQ0q61aCL1603vWCUKoRUXuDtDUVt4R6f
U8FfUIrYHQ6VA6yGk6oiKYI9rRn7DpnfOidtHKcdJJqDuRA6TjCG9OcpDelksl3Bci9RA3IwaKxS
2KIAYAuqqnsPMDRXgBBXm1HyNJkekoqectSVNXzlbGZdd5C1pKIpHLwGwzyUwUf8KGmMQZg4cLKo
lEPvAUToEyEdYqMnmSORiQSVPSwDVFFHealaivc8hKg99BimOOtYzqFmpnLl1StTBzmZtcJrY3YS
jtpgeET9j4BMlcH5daP1GTr1kNRv21rtK03nU5ogXnhWaplFy1ktdddetDFSjExOs1NdM52yTvmk
79CRueKH4LPSnyr3AXSyRIUX2Bhir0C8HUPlCNBqrQo9PVqm2XWZr2/KR4TP1icOqAe7gB1KiyIq
KskhIMiyEIgVUTrV5HWllbhuAi1C+ekA4hAdbFMlaNAKCLIrBAwM1Eg7DWUC4dD44pIsgSLISKKK
LBYsUFix2CSxO6eQ3m07eAconZMJ7qlK50p8ZvHuidKOJN4UYHJxSSKUGA/GoS2w8zW2rl1ROUss
ll0WbkJndyRD4Mf7aGDMm9KJdLxlIhM82hJIZgXwa0YsBqqGZxEyDQy+P83scJGxyNk9C73SzBcN
kdNy0aTyfVMhUnyzU5sT4wzaAE3E3mDgjHQ6OshaUjFguk9l1lSiOFrG3G0Z6iW3LrhgaXTncqln
JDUYmZuG1qwhChJYlGqKczJgDCRDMwUNwhqSKNEyM1CauPsj6FomSlYKYtbU6ykplgY4slm+7bJL
mClGwJzUG6RGULQZpEIuN3AAurFEYVAkFbBF2qGCsca2z31fEroNjkTzJ5pRUTYrpV9SCGSmhYjG
6Ax0qRqCtIIO5XYNzqFdDtcB2jk6H8HM/QoGiqrNKaSkCMOBmCwRCqKcG5zCWF5wMSQgEJJ7yosz
pBtWlFhS1VCGo3STwlzswqIh74HKZnI9Hy2ZxTxHkPBPiZcQYo+EN5cKrJJ7BT6mjseXggq78UP4
IYNgf4hj3VBfjiAYSRhqZT5UovuAQ6VHVUqvO8A06c8iDLq5KOogNjxJ/rdDIeh5x6Z9RXzlQp4X
rWh83zVY5lPX58XJvRA+hxPheZpEeMTJGeqd/Y2mko1BU+Kdse59f4z+KHqJ8BRRRSikqRSohvWS
2f7CfqYJgC3YPxV3h1E2P3Zu/vZm5opiM9fGwIBqkQMXrVOwwhO05zwRqbHxl5O4j1CWjKJ2o4eb
1w3zpM03Iu8VB9q9iylqq/lMQ8tKHzE9VoOVcCZEDvjJqM7TrgfWPOdJx4OJgd7Qs+3odz0B2OlZ
AiQhCRIQjIAx6Tvfp2FzqAzfQJ7gCzlGSBVKhUR0jw/J9mBFY+aHyDb1kyji/E+6fcJyJMSB/xqQ
jmkYm+4SXen7He7W9VdZzfHlJKnsmEt+9p8shXbDAoC6SdZ6TOSZtFG0fQnnzP4TwnvTjI8Dw2HI
9KWeU5/EIZpYB8JgRRIRfDR5DxnWSEJEntanhV9ux8GXmCoLvMz2nyIBEfanPWJESPzAs/E/nRZE
hEwLgUhfAsVWwJjMKFHrMSBqQwBdIf/i7kinChIR6B3c4A==
^ 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.