diff --git a/ChangeLog b/ChangeLog index fee952996..d16f2ff57 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +2010-09-04 BVK Chaitanya + + Support for updating environment variables with matched substrings + of regexp. + + * tests/grub_cmd_regexp.in: New test. + * Makefile.util.def: Rule for new test. + + * grub-core/commands/regexp.c: New option -s to update environment + variables with regexp matches. + 2010-09-04 Szymon Janc * include/grub/file.h (grub_file): New member not_easly_seekable. diff --git a/Makefile.util.def b/Makefile.util.def index 79541b5ed..a3d5381d4 100644 --- a/Makefile.util.def +++ b/Makefile.util.def @@ -519,6 +519,12 @@ script = { common = tests/grub_script_return.in; }; +script = { + testcase; + name = grub_cmd_regexp; + common = tests/grub_cmd_regexp.in; +}; + program = { testcase; name = example_unit_test; diff --git a/grub-core/commands/regexp.c b/grub-core/commands/regexp.c index e8e8243b5..8edf818a3 100644 --- a/grub-core/commands/regexp.c +++ b/grub-core/commands/regexp.c @@ -20,37 +20,103 @@ #include #include #include -#include +#include +#include +#include #include #include +static const struct grub_arg_option options[] = + { + { "set", 's', GRUB_ARG_OPTION_REPEATABLE, + N_("Variable names to update with matches."), + N_("[NUMBER:]VARNAME"), ARG_TYPE_STRING }, + { 0, 0, 0, 0, 0, 0 } + }; + static grub_err_t -grub_cmd_regexp (grub_command_t cmd __attribute__ ((unused)), - int argc, char **args) +set_matches (char **varnames, char *str, grub_size_t nmatches, + regmatch_t *matches) +{ + int i; + char ch; + char *p; + char *q; + grub_err_t err; + unsigned long j; + + auto void setvar (char *v, regmatch_t *m); + void setvar (char *v, regmatch_t *m) + { + ch = str[m->rm_eo]; + str[m->rm_eo] = '\0'; + err = grub_env_set (v, str + m->rm_so); + str[m->rm_eo] = ch; + } + + for (i = 0; varnames && varnames[i]; i++) + { + if (! (p = grub_strchr (varnames[i], ':'))) + { + /* varname w/o index defaults to 1 */ + if (nmatches < 2 || matches[1].rm_so == -1) + grub_env_unset (varnames[i]); + else + setvar (varnames[i], &matches[1]); + } + else + { + j = grub_strtoul (varnames[i], &q, 10); + if (q != p) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "invalid variable name format %s", varnames[i]); + + if (nmatches <= j || matches[j].rm_so == -1) + grub_env_unset (p + 1); + else + setvar (p + 1, &matches[j]); + } + + if (err != GRUB_ERR_NONE) + return err; + } + return GRUB_ERR_NONE; +} + +static grub_err_t +grub_cmd_regexp (grub_extcmd_context_t ctxt, int argc, char **args) { int argn = 0; - int matches = 0; regex_t regex; int ret; grub_size_t s; char *comperr; grub_err_t err; + regmatch_t *matches = 0; if (argc != 2) return grub_error (GRUB_ERR_BAD_ARGUMENT, "2 arguments expected"); - ret = regcomp (®ex, args[0], RE_SYNTAX_GNU_AWK); + ret = regcomp (®ex, args[0], REG_EXTENDED); if (ret) goto fail; - ret = regexec (®ex, args[1], 0, 0, 0); + matches = grub_zalloc (sizeof (*matches) * (regex.re_nsub + 1)); + if (! matches) + goto fail; + + ret = regexec (®ex, args[1], regex.re_nsub + 1, matches, 0); if (!ret) { + err = set_matches (ctxt->state[0].args, args[1], + regex.re_nsub + 1, matches); regfree (®ex); - return GRUB_ERR_NONE; + grub_free (matches); + return err; } fail: + grub_free (matches); s = regerror (ret, ®ex, 0, 0); comperr = grub_malloc (s); if (!comperr) @@ -65,16 +131,16 @@ grub_cmd_regexp (grub_command_t cmd __attribute__ ((unused)), return err; } -static grub_command_t cmd; +static grub_extcmd_t cmd; GRUB_MOD_INIT(regexp) { - cmd = grub_register_command ("regexp", grub_cmd_regexp, - N_("REGEXP STRING"), - N_("Test if REGEXP matches STRING.")); + cmd = grub_register_extcmd ("regexp", grub_cmd_regexp, + GRUB_COMMAND_FLAG_BOTH, N_("REGEXP STRING"), + N_("Test if REGEXP matches STRING."), options); } GRUB_MOD_FINI(regexp) { - grub_unregister_command (cmd); + grub_unregister_extcmd (cmd); } diff --git a/tests/grub_cmd_regexp.in b/tests/grub_cmd_regexp.in new file mode 100644 index 000000000..43b479fec --- /dev/null +++ b/tests/grub_cmd_regexp.in @@ -0,0 +1,41 @@ +#! /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 . + +cmd='regexp -s version "vm-(.*)" vm-1.2.3; echo $version' +v=`echo "$cmd" | @builddir@/grub-shell` +if test "$v" != 1.2.3; then echo "error: $cmd" >&2; exit 1; fi + +cmd='regexp -s 1:version "vm-(.*)" vm-1.2.3; echo $version' +v=`echo "$cmd" | @builddir@/grub-shell` +if test "$v" != 1.2.3; then echo "error: $cmd" >&2; exit 1; fi + +cmd='regexp -s 0:match "vm-(.*)" vm-1.2.3; echo $match' +v=`echo "$cmd" | @builddir@/grub-shell` +if test "$v" != vm-1.2.3; then echo "error: $cmd" >&2; exit 1; fi + +cmd='regexp -s 2:match "vm-(.*)" vm-1.2.3; echo $match' +v=`echo "$cmd" | @builddir@/grub-shell` +if test -n "$v"; then echo "error: $cmd" >&2; exit 1; fi + +cmd='regexp -s match "\\\((.*)\\\)" (hd0,msdos1); echo $match' +v=`echo "$cmd" | @builddir@/grub-shell` +if test "$v" != "hd0,msdos1"; then echo "error: $cmd" >&2; exit 1; fi + +cmd='regexp -s match "hd([0-9]+)" hd0; echo $match' +v=`echo "$cmd" | @builddir@/grub-shell` +if test "$v" != "0"; then echo "error: $cmd" >&2; exit 1; fi