execute.c 24.8 KB
Newer Older
1 2 3
/* execute.c -- Execute a GRUB script.  */
/*
 *  GRUB  --  GRand Unified Bootloader
4
 *  Copyright (C) 2005,2007,2008,2009,2010  Free Software Foundation, Inc.
5
 *
6
 *  GRUB is free software: you can redistribute it and/or modify
7
 *  it under the terms of the GNU General Public License as published by
8
 *  the Free Software Foundation, either version 3 of the License, or
9 10
 *  (at your option) any later version.
 *
11
 *  GRUB is distributed in the hope that it will be useful,
12 13 14 15 16
 *  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
17
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
18 19 20 21 22
 */

#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/env.h>
23 24 25
#include <grub/script_sh.h>
#include <grub/command.h>
#include <grub/menu.h>
26
#include <grub/lib/arg.h>
27
#include <grub/normal.h>
28
#include <grub/extcmd.h>
29
#include <grub/i18n.h>
30

31 32 33 34
/* Max digits for a char is 3 (0xFF is 255), similarly for an int it
   is sizeof (int) * 3, and one extra for a possible -ve sign.  */
#define ERRNO_DIGITS_MAX  (sizeof (int) * 3 + 1)

BVK Chaitanya's avatar
BVK Chaitanya committed
35
static unsigned long is_continue;
BVK Chaitanya's avatar
BVK Chaitanya committed
36 37
static unsigned long active_loops;
static unsigned long active_breaks;
38
static unsigned long function_return;
39

40 41
#define GRUB_SCRIPT_SCOPE_MALLOCED      1
#define GRUB_SCRIPT_SCOPE_ARGS_MALLOCED 2
42

43 44 45
/* Scope for grub script functions.  */
struct grub_script_scope
{
46 47
  unsigned flags;
  unsigned shifts;
48
  struct grub_script_argv argv;
49
};
50 51
static struct grub_script_scope *scope = 0;

52
/* Wildcard translator for GRUB script.  */
BVK Chaitanya's avatar
BVK Chaitanya committed
53
struct grub_script_wildcard_translator *grub_wildcard_translator;
54

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
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++))
    {
71
      if (ch == '*' || ch == '\\' || ch == '?')
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
	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 == '\\')
	p[i++] = *s++;
      else
	p[i++] = ch;
    }
  p[i] = '\0';
  return p;
}

104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
static void
replace_scope (struct grub_script_scope *new_scope)
{
  if (scope)
    {
      scope->argv.argc += scope->shifts;
      scope->argv.args -= scope->shifts;

      if (scope->flags & GRUB_SCRIPT_SCOPE_ARGS_MALLOCED)
	grub_script_argv_free (&scope->argv);

      if (scope->flags & GRUB_SCRIPT_SCOPE_MALLOCED)
	grub_free (scope);
    }
  scope = new_scope;
}

BVK Chaitanya's avatar
BVK Chaitanya committed
121
grub_err_t
BVK Chaitanya's avatar
BVK Chaitanya committed
122
grub_script_break (grub_command_t cmd, int argc, char *argv[])
BVK Chaitanya's avatar
BVK Chaitanya committed
123 124 125 126 127 128
{
  char *p = 0;
  unsigned long count;

  if (argc == 0)
    count = 1;
129 130 131 132 133 134 135 136 137 138
  else if (argc > 1)
    return  grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
  else
    {
      count = grub_strtoul (argv[0], &p, 10);
      if (grub_errno)
	return grub_errno;
      if (*p != '\0')
	return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized number"));
      if (count == 0)
139 140 141
	/* TRANSLATORS: 0 is a quantifier. "break" (similar to bash)
	   can be used e.g. to break 3 loops at once.
	   But asking it to break 0 loops makes no sense. */
142 143
	return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't break 0 loops"));
    }
BVK Chaitanya's avatar
BVK Chaitanya committed
144

BVK Chaitanya's avatar
BVK Chaitanya committed
145
  is_continue = grub_strcmp (cmd->name, "break") ? 1 : 0;
146 147 148
  active_breaks = count;
  if (active_breaks > active_loops)
    active_breaks = active_loops;
BVK Chaitanya's avatar
BVK Chaitanya committed
149 150 151
  return GRUB_ERR_NONE;
}

BVK Chaitanya's avatar
BVK Chaitanya committed
152 153 154
grub_err_t
grub_script_shift (grub_command_t cmd __attribute__((unused)),
		   int argc, char *argv[])
BVK Chaitanya's avatar
BVK Chaitanya committed
155 156 157 158 159 160 161 162 163
{
  char *p = 0;
  unsigned long n = 0;

  if (! scope)
    return GRUB_ERR_NONE;

  if (argc == 0)
    n = 1;
164

BVK Chaitanya's avatar
BVK Chaitanya committed
165 166
  else if (argc > 1)
    return GRUB_ERR_BAD_ARGUMENT;
167

BVK Chaitanya's avatar
BVK Chaitanya committed
168 169 170
  else
    {
      n = grub_strtoul (argv[0], &p, 10);
BVK Chaitanya's avatar
BVK Chaitanya committed
171
      if (*p != '\0')
BVK Chaitanya's avatar
BVK Chaitanya committed
172 173 174
	return GRUB_ERR_BAD_ARGUMENT;
    }

BVK Chaitanya's avatar
BVK Chaitanya committed
175 176 177
  if (n > scope->argv.argc)
    return GRUB_ERR_BAD_ARGUMENT;

178
  scope->shifts += n;
179 180
  scope->argv.argc -= n;
  scope->argv.args += n;
BVK Chaitanya's avatar
BVK Chaitanya committed
181 182 183
  return GRUB_ERR_NONE;
}

184 185 186 187 188
grub_err_t
grub_script_setparams (grub_command_t cmd __attribute__((unused)),
		       int argc, char **args)
{
  struct grub_script_scope *new_scope;
BVK Chaitanya's avatar
BVK Chaitanya committed
189
  struct grub_script_argv argv = { 0, 0, 0 };
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212

  if (! scope)
    return GRUB_ERR_INVALID_COMMAND;

  new_scope = grub_malloc (sizeof (*new_scope));
  if (! new_scope)
    return grub_errno;

  if (grub_script_argv_make (&argv, argc, args))
    {
      grub_free (new_scope);
      return grub_errno;
    }

  new_scope->shifts = 0;
  new_scope->argv = argv;
  new_scope->flags = GRUB_SCRIPT_SCOPE_MALLOCED |
    GRUB_SCRIPT_SCOPE_ARGS_MALLOCED;

  replace_scope (new_scope);
  return GRUB_ERR_NONE;
}

213 214 215 216 217 218 219 220
grub_err_t
grub_script_return (grub_command_t cmd __attribute__((unused)),
		    int argc, char *argv[])
{
  char *p;
  unsigned long n;

  if (! scope || argc > 1)
221
    return grub_error (GRUB_ERR_BAD_ARGUMENT,
222 223 224 225
		       /* TRANSLATORS: It's about not being
			  inside a function. "return" can be used only
			  in a function and this error occurs if it's used
			  anywhere else.  */
226
		       N_("not in function body"));
227 228 229

  if (argc == 0)
    {
230
      const char *t;
231
      function_return = 1;
232 233 234 235
      t = grub_env_get ("?");
      if (!t)
	return GRUB_ERR_NONE;
      return grub_strtoul (t, NULL, 10);
236 237 238
    }

  n = grub_strtoul (argv[0], &p, 10);
239 240
  if (grub_errno)
    return grub_errno;
241
  if (*p != '\0')
242 243
    return grub_error (GRUB_ERR_BAD_ARGUMENT,
		       N_("unrecognized number"));
244 245

  function_return = 1;
246
  return n ? grub_error (n, N_("false")) : GRUB_ERR_NONE;
247 248
}

249 250
static int
grub_env_special (const char *name)
251
{
252 253 254 255 256 257 258
  if (grub_isdigit (name[0]) ||
      grub_strcmp (name, "#") == 0 ||
      grub_strcmp (name, "*") == 0 ||
      grub_strcmp (name, "@") == 0)
    return 1;
  return 0;
}
259

260 261 262
static char **
grub_script_env_get (const char *name, grub_script_arg_type_t type)
{
263
  unsigned i;
264
  struct grub_script_argv result = { 0, 0, 0 };
265

266 267 268
  if (grub_script_argv_next (&result))
    goto fail;

269
  if (! grub_env_special (name))
270
    {
271
      const char *v = grub_env_get (name);
272
      if (v && v[0])
273
	{
274
	  if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
275 276 277 278
	    {
	      if (grub_script_argv_split_append (&result, v))
		goto fail;
	    }
279
	  else
280
	    if (grub_script_argv_append (&result, v, grub_strlen (v)))
281
	      goto fail;
282 283
	}
    }
284
  else if (! scope)
285
    {
286
      if (grub_script_argv_append (&result, 0, 0))
287 288
	goto fail;
    }
289 290
  else if (grub_strcmp (name, "#") == 0)
    {
291 292
      char buffer[ERRNO_DIGITS_MAX + 1];
      grub_snprintf (buffer, sizeof (buffer), "%u", scope->argv.argc);
293
      if (grub_script_argv_append (&result, buffer, grub_strlen (buffer)))
294
	goto fail;
295 296 297
    }
  else if (grub_strcmp (name, "*") == 0)
    {
298
      for (i = 0; i < scope->argv.argc; i++)
299 300
	if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
	  {
301 302 303 304 305
	    if (i != 0 && grub_script_argv_next (&result))
	      goto fail;

	    if (grub_script_argv_split_append (&result, scope->argv.args[i]))
	      goto fail;
306 307 308
	  }
	else
	  {
309
	    if (i != 0 && grub_script_argv_append (&result, " ", 1))
310 311
	      goto fail;

312 313
	    if (grub_script_argv_append (&result, scope->argv.args[i],
					 grub_strlen (scope->argv.args[i])))
314
	      goto fail;
315 316 317 318
	  }
    }
  else if (grub_strcmp (name, "@") == 0)
    {
319
      for (i = 0; i < scope->argv.argc; i++)
320
	{
321 322
	  if (i != 0 && grub_script_argv_next (&result))
	    goto fail;
323 324

	  if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
325 326 327 328
	    {
	      if (grub_script_argv_split_append (&result, scope->argv.args[i]))
		goto fail;
	    }
329
	  else
330 331
	    if (grub_script_argv_append (&result, scope->argv.args[i],
					 grub_strlen (scope->argv.args[i])))
332
	      goto fail;
333
	}
334 335
    }
  else
336 337 338 339 340 341 342 343
    {
      unsigned long num = grub_strtoul (name, 0, 10);
      if (num == 0)
	; /* XXX no file name, for now.  */

      else if (num <= scope->argv.argc)
	{
	  if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
344 345 346 347 348
	    {
	      if (grub_script_argv_split_append (&result,
						 scope->argv.args[num - 1]))
		goto fail;
	    }
349
	  else
350 351 352
	    if (grub_script_argv_append (&result, scope->argv.args[num - 1],
					 grub_strlen (scope->argv.args[num - 1])
					 ))
353
	      goto fail;
354 355
	}
    }
BVK Chaitanya's avatar
BVK Chaitanya committed
356

357
  return result.args;
358 359 360 361 362

 fail:

  grub_script_argv_free (&result);
  return 0;
363 364 365 366 367
}

static grub_err_t
grub_script_env_set (const char *name, const char *val)
{
368
  if (grub_env_special (name))
369 370
    return grub_error (GRUB_ERR_BAD_ARGUMENT,
		       N_("invalid variable name `%s'"), name);
371 372 373 374

  return grub_env_set (name, val);
}

375 376 377 378 379 380 381
struct gettext_context
{
  char **allowed_strings;
  grub_size_t nallowed_strings;
  grub_size_t additional_len;
};

382 383
static int
parse_string (const char *str,
384 385 386 387
	      int (*hook) (const char *var, grub_size_t varlen,
			   char **ptr, struct gettext_context *ctx),
	      struct gettext_context *ctx,
	      char *put)
388 389 390 391 392 393 394 395 396 397 398
{
  const char *ptr;
  int escaped = 0;
  const char *optr;

  for (ptr = str; ptr && *ptr; )
    switch (*ptr)
      {
      case '\\':
	escaped = !escaped;
	if (!escaped && put)
399
	  *(put++) = '\\';
400 401 402 403 404 405 406
	ptr++;
	break;
      case '$':
	if (escaped)
	  {
	    escaped = 0;
	    if (put)
407
	      *(put++) = *ptr;
408 409 410 411 412 413 414 415 416 417 418 419 420
	    ptr++;
	    break;
	  }

	ptr++;
	switch (*ptr)
	  {
	  case '{':
	    {
	      optr = ptr + 1;
	      ptr = grub_strchr (optr, '}');
	      if (!ptr)
		break;
421
	      if (hook (optr, ptr - optr, &put, ctx))
422 423 424 425 426 427 428 429
		return 1;
	      ptr++;
	      break;
	    }
	  case '0' ... '9':
	    optr = ptr;
	    while (*ptr >= '0' && *ptr <= '9')
	      ptr++;
430
	    if (hook (optr, ptr - optr, &put, ctx))
431 432 433 434 435 436 437 438 439 440 441
	      return 1;
	    break;
	  case 'a' ... 'z':
	  case 'A' ... 'Z':
	  case '_':
	    optr = ptr;
	    while ((*ptr >= '0' && *ptr <= '9')
		   || (*ptr >= 'a' && *ptr <= 'z')
		   || (*ptr >= 'A' && *ptr <= 'Z')
		   || *ptr == '_')
	      ptr++;
442
	    if (hook (optr, ptr - optr, &put, ctx))
443 444 445 446
	      return 1;
	    break;
	  case '?':
	  case '#':
447
	    if (hook (ptr, 1, &put, ctx))
448 449 450 451 452
	      return 1;
	    ptr++;
	    break;
	  default:
	    if (put)
453
	      *(put++) = '$';
454 455 456 457
	  }
	break;
      default:
	if (escaped && put)
458
	  *(put++) = '\\';
459 460
	escaped = 0;
	if (put)
461
	  *(put++) = *ptr;
462 463 464
	ptr++;
	break;
      }
465 466
  if (put)
    *(put++) = 0;
467 468 469 470
  return 0;
}

static int
471 472
gettext_putvar (const char *str, grub_size_t len,
		char **ptr, struct gettext_context *ctx)
473
{
474 475
  const char *var;
  grub_size_t i;
476

477 478 479 480 481 482 483
  for (i = 0; i < ctx->nallowed_strings; i++)
    if (grub_strncmp (ctx->allowed_strings[i], str, len) == 0
	&& ctx->allowed_strings[i][len] == 0)
      {
	break;
      }
  if (i == ctx->nallowed_strings)
484 485
    return 0;

486 487 488 489 490
  /* Enough for any number.  */
  if (len == 1 && str[0] == '#')
    {
      grub_snprintf (*ptr, 30, "%u", scope->argv.argc);
      *ptr += grub_strlen (*ptr);
491
      return 0;
492 493 494 495 496 497
    }
  var = grub_env_get (ctx->allowed_strings[i]);
  if (var)
    *ptr = grub_stpcpy (*ptr, var);
  return 0;
}
498

499 500 501 502 503 504 505 506 507 508
static int
gettext_save_allow (const char *str, grub_size_t len,
		    char **ptr __attribute__ ((unused)),
		    struct gettext_context *ctx)
{
  ctx->allowed_strings[ctx->nallowed_strings++] = grub_strndup (str, len);
  if (!ctx->allowed_strings[ctx->nallowed_strings - 1])
    return 1;
  return 0;
}
509

510 511 512 513 514 515 516 517 518 519 520 521 522 523
static int
gettext_getlen (const char *str, grub_size_t len,
		char **ptr __attribute__ ((unused)),
		struct gettext_context *ctx)
{
  const char *var;
  grub_size_t i;

  for (i = 0; i < ctx->nallowed_strings; i++)
    if (grub_strncmp (ctx->allowed_strings[i], str, len) == 0
	&& ctx->allowed_strings[i][len] == 0)
      break;
  if (i == ctx->nallowed_strings)
    return 0;
524

525 526 527 528
  /* Enough for any number.  */
  if (len == 1 && str[0] == '#')
    {
      ctx->additional_len += 30;
529
      return 0;
530 531 532 533 534 535
    }
  var = grub_env_get (ctx->allowed_strings[i]);
  if (var)
    ctx->additional_len += grub_strlen (var);
  return 0;
}
536

537 538 539 540 541 542 543 544 545 546 547 548
static int
gettext_append (struct grub_script_argv *result, const char *orig_str)
{
  const char *template;
  char *res = 0;
  struct gettext_context ctx = {
    .allowed_strings = 0,
    .nallowed_strings = 0,
    .additional_len = 1
  };
  int rval = 1;
  const char *iptr;
549 550 551 552 553 554

  grub_size_t dollar_cnt = 0;

  for (iptr = orig_str; *iptr; iptr++)
    if (*iptr == '$')
      dollar_cnt++;
555
  ctx.allowed_strings = grub_malloc (sizeof (ctx.allowed_strings[0]) * dollar_cnt);
556

557
  if (parse_string (orig_str, gettext_save_allow, &ctx, 0))
558 559 560 561
    goto fail;

  template = _(orig_str);

562
  if (parse_string (template, gettext_getlen, &ctx, 0))
563 564
    goto fail;

565
  res = grub_malloc (grub_strlen (template) + ctx.additional_len);
566 567 568
  if (!res)
    goto fail;

569
  if (parse_string (template, gettext_putvar, &ctx, res))
570 571
    goto fail;

572 573 574
  char *escaped = 0;
  escaped = wildcard_escape (res);
  if (grub_script_argv_append (result, escaped, grub_strlen (escaped)))
575 576 577
    {
      grub_free (escaped);
      goto fail;
578 579
    }
  grub_free (escaped);
580 581 582 583 584 585

  rval = 0;
 fail:
  grub_free (res);
  {
    grub_size_t i;
586 587
    for (i = 0; i < ctx.nallowed_strings; i++)
      grub_free (ctx.allowed_strings[i]);
588
  }
589
  grub_free (ctx.allowed_strings);
590 591 592
  return rval;
}

593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
static int
append (struct grub_script_argv *result,
	const char *s, int escape_type)
{
  int r;
  char *p = 0;

  if (escape_type == 0)
    return grub_script_argv_append (result, s, grub_strlen (s));

  if (escape_type > 0)
    p = wildcard_escape (s);
  else if (escape_type < 0)
    p = wildcard_unescape (s);

  if (! p)
    return 1;

  r = grub_script_argv_append (result, p, grub_strlen (p));
  grub_free (p);
  return r;
}

BVK Chaitanya's avatar
BVK Chaitanya committed
616
/* Convert arguments in ARGLIST into ARGV form.  */
617 618 619
static int
grub_script_arglist_to_argv (struct grub_script_arglist *arglist,
			     struct grub_script_argv *argv)
620 621
{
  int i;
622 623
  char **values = 0;
  struct grub_script_arg *arg = 0;
624
  struct grub_script_argv result = { 0, 0, 0 };
625

626
  for (; arglist && arglist->arg; arglist = arglist->next)
627
    {
628 629
      if (grub_script_argv_next (&result))
	goto fail;
630

631 632
      arg = arglist->arg;
      while (arg)
633
	{
634 635 636
	  switch (arg->type)
	    {
	    case GRUB_SCRIPT_ARG_TYPE_VAR:
637
	    case GRUB_SCRIPT_ARG_TYPE_DQVAR:
638 639
	      {
		int need_cleanup = 0;
640

641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
		values = grub_script_env_get (arg->str, arg->type);
		for (i = 0; values && values[i]; i++)
		  {
		    if (!need_cleanup)
		      {
			if (i != 0 && grub_script_argv_next (&result))
			  {
			    need_cleanup = 1;
			    goto cleanup;
			  }

			if (arg->type == GRUB_SCRIPT_ARG_TYPE_VAR)
			  {
			    int len;
			    char ch;
			    char *p;
			    char *op;
			    const char *s = values[i];

			    len = grub_strlen (values[i]);
			    /* \? -> \\\? */
			    /* \* -> \\\* */
			    /* \ -> \\ */
			    p = grub_malloc (len * 2 + 1);
			    if (! p)
			      {
				need_cleanup = 1;
				goto cleanup;
			      }

			    op = p;
			    while ((ch = *s++))
			      {
				if (ch == '\\')
				  {
				    *op++ = '\\';
				    if (*s == '?' || *s == '*')
				      *op++ = '\\';
				  }
				*op++ = ch;
			      }
			    *op = '\0';

684 685 686
			    need_cleanup = grub_script_argv_append (&result, p, op - p);
			    grub_free (p);
			    /* Fall through to cleanup */
687 688 689
			  }
			else
			  {
690 691
			    need_cleanup = append (&result, values[i], 1);
			    /* Fall through to cleanup */
692 693 694 695 696 697 698 699 700 701 702 703 704
			  }
		      }

cleanup:
		    grub_free (values[i]);
		  }
		grub_free (values);

		if (need_cleanup)
		  goto fail;

		break;
	      }
705

BVK Chaitanya's avatar
BVK Chaitanya committed
706
	    case GRUB_SCRIPT_ARG_TYPE_BLOCK:
707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723
	      {
		char *p;
		if (grub_script_argv_append (&result, "{", 1))
		  goto fail;
		p = wildcard_escape (arg->str);
		if (!p)
		  goto fail;
		if (grub_script_argv_append (&result, p,
					     grub_strlen (p)))
		  {
		    grub_free (p);
		    goto fail;
		  }
		grub_free (p);
		if (grub_script_argv_append (&result, "}", 1))
		  goto fail;
	      }
724
	      result.script = arg->script;
725 726
	      break;

727
	    case GRUB_SCRIPT_ARG_TYPE_TEXT:
728
	      if (arg->str[0] &&
729 730
		  grub_script_argv_append (&result, arg->str,
					   grub_strlen (arg->str)))
731
		goto fail;
732 733
	      break;

734 735
	    case GRUB_SCRIPT_ARG_TYPE_GETTEXT:
	      {
736
		if (gettext_append (&result, arg->str))
737 738 739 740
		  goto fail;
	      }
	      break;

741 742
	    case GRUB_SCRIPT_ARG_TYPE_DQSTR:
	    case GRUB_SCRIPT_ARG_TYPE_SQSTR:
743
	      if (append (&result, arg->str, 1))
744
		goto fail;
745 746 747
	      break;
	    }
	  arg = arg->next;
748 749 750
	}
    }

751 752
  if (! result.args[result.argc - 1])
    result.argc--;
753

BVK Chaitanya's avatar
BVK Chaitanya committed
754 755
  /* Perform wildcard expansion.  */

756 757 758 759 760 761 762
  int j;
  int failed = 0;
  struct grub_script_argv unexpanded = result;

  result.argc = 0;
  result.args = 0;
  for (i = 0; unexpanded.args[i]; i++)
BVK Chaitanya's avatar
BVK Chaitanya committed
763 764
    {
      char **expansions = 0;
765 766 767
      if (grub_wildcard_translator
	  && grub_wildcard_translator->expand (unexpanded.args[i],
					       &expansions))
BVK Chaitanya's avatar
BVK Chaitanya committed
768
	{
769 770 771
	  grub_script_argv_free (&unexpanded);
	  goto fail;
	}
BVK Chaitanya's avatar
BVK Chaitanya committed
772

773 774 775
      if (! expansions)
	{
	  grub_script_argv_next (&result);
776
	  append (&result, unexpanded.args[i], -1);
777 778 779 780
	}
      else
	{
	  for (j = 0; expansions[j]; j++)
BVK Chaitanya's avatar
BVK Chaitanya committed
781
	    {
782
	      failed = (failed || grub_script_argv_next (&result) ||
783
			append (&result, expansions[j], 0));
784
	      grub_free (expansions[j]);
BVK Chaitanya's avatar
BVK Chaitanya committed
785
	    }
786 787 788
	  grub_free (expansions);
	  
	  if (failed)
BVK Chaitanya's avatar
BVK Chaitanya committed
789
	    {
790 791
	      grub_script_argv_free (&unexpanded);
	      goto fail;
BVK Chaitanya's avatar
BVK Chaitanya committed
792 793 794
	    }
	}
    }
795
  grub_script_argv_free (&unexpanded);
796

797 798
  *argv = result;
  return 0;
799 800 801 802 803

 fail:

  grub_script_argv_free (&result);
  return 1;
804 805 806 807 808 809 810 811 812 813 814 815
}

static grub_err_t
grub_script_execute_cmd (struct grub_script_cmd *cmd)
{
  int ret;
  char errnobuf[ERRNO_DIGITS_MAX + 1];

  if (cmd == 0)
    return 0;

  ret = cmd->exec (cmd);
816

817 818 819
  grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
  grub_env_set ("?", errnobuf);
  return ret;
820 821
}

822 823 824 825 826
/* Execute a function call.  */
grub_err_t
grub_script_function_call (grub_script_function_t func, int argc, char **args)
{
  grub_err_t ret = 0;
BVK Chaitanya's avatar
BVK Chaitanya committed
827
  unsigned long loops = active_loops;
828
  struct grub_script_scope *old_scope;
829 830
  struct grub_script_scope new_scope;

BVK Chaitanya's avatar
BVK Chaitanya committed
831
  active_loops = 0;
832 833
  new_scope.flags = 0;
  new_scope.shifts = 0;
834 835
  new_scope.argv.argc = argc;
  new_scope.argv.args = args;
836 837 838

  old_scope = scope;
  scope = &new_scope;
839 840 841

  ret = grub_script_execute (func->func);

842
  function_return = 0;
BVK Chaitanya's avatar
BVK Chaitanya committed
843
  active_loops = loops;
844
  replace_scope (old_scope); /* free any scopes by setparams */
845 846 847
  return ret;
}

848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872
/* Helper for grub_script_execute_sourcecode.  */
static grub_err_t
grub_script_execute_sourcecode_getline (char **line,
					int cont __attribute__ ((unused)),
					void *data)
{
  const char **source = data;
  const char *p;

  if (! *source)
    {
      *line = 0;
      return 0;
    }

  p = grub_strchr (*source, '\n');

  if (p)
    *line = grub_strndup (*source, p - *source);
  else
    *line = grub_strdup (*source);
  *source = p ? p + 1 : 0;
  return 0;
}

873 874
/* Execute a source script.  */
grub_err_t
875
grub_script_execute_sourcecode (const char *source)
876 877 878 879 880 881 882 883
{
  grub_err_t ret = 0;
  struct grub_script *parsed_script;

  while (source)
    {
      char *line;

884 885 886
      grub_script_execute_sourcecode_getline (&line, 0, &source);
      parsed_script = grub_script_parse
	(line, grub_script_execute_sourcecode_getline, &source);
887 888 889
      if (! parsed_script)
	{
	  ret = grub_errno;
890
	  grub_free (line);
891 892 893 894
	  break;
	}

      ret = grub_script_execute (parsed_script);
895
      grub_script_free (parsed_script);
896 897 898
      grub_free (line);
    }

899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
  return ret;
}

/* Execute a source script in new scope.  */
grub_err_t
grub_script_execute_new_scope (const char *source, int argc, char **args)
{
  grub_err_t ret = 0;
  struct grub_script_scope new_scope;
  struct grub_script_scope *old_scope;

  new_scope.argv.argc = argc;
  new_scope.argv.args = args;
  new_scope.flags = 0;

  old_scope = scope;
  scope = &new_scope;

  ret = grub_script_execute_sourcecode (source);

919
  scope = old_scope;
920 921 922
  return ret;
}

923 924 925 926 927 928 929 930
/* Execute a single command line.  */
grub_err_t
grub_script_execute_cmdline (struct grub_script_cmd *cmd)
{
  struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd;
  grub_command_t grubcmd;
  grub_err_t ret = 0;
  grub_script_function_t func = 0;
931
  char errnobuf[18];
932
  char *cmdname;
BVK Chaitanya's avatar
BVK Chaitanya committed
933 934 935
  int argc;
  char **args;
  int invert;
936
  struct grub_script_argv argv = { 0, 0, 0 };
937 938

  /* Lookup the command.  */
BVK Chaitanya's avatar
BVK Chaitanya committed
939
  if (grub_script_arglist_to_argv (cmdline->arglist, &argv) || ! argv.args[0])
940 941
    return grub_errno;

BVK Chaitanya's avatar
BVK Chaitanya committed
942 943 944
  invert = 0;
  argc = argv.argc - 1;
  args = argv.args + 1;
945
  cmdname = argv.args[0];
BVK Chaitanya's avatar
BVK Chaitanya committed
946 947 948 949 950
  if (grub_strcmp (cmdname, "!") == 0)
    {
      if (argv.argc < 2 || ! argv.args[1])
	{
	  grub_script_argv_free (&argv);
951 952
	  return grub_error (GRUB_ERR_BAD_ARGUMENT,
			     N_("no command is specified"));
BVK Chaitanya's avatar
BVK Chaitanya committed
953 954 955 956 957 958 959
	}

      invert = 1;
      argc = argv.argc - 2;
      args = argv.args + 2;
      cmdname = argv.args[1];
    }
960
  grubcmd = grub_command_find (cmdname);
961 962
  if (! grubcmd)
    {
963 964
      grub_errno = GRUB_ERR_NONE;

965
      /* It's not a GRUB command, try all functions.  */
966
      func = grub_script_function_find (cmdname);
967
      if (! func)
968 969
	{
	  /* As a last resort, try if it is an assignment.  */
970
	  char *assign = grub_strdup (cmdname);
971 972 973 974
	  char *eq = grub_strchr (assign, '=');

	  if (eq)
	    {
975 976 977
	      /* This was set because the command was not found.  */
	      grub_errno = GRUB_ERR_NONE;

978 979 980
	      /* Create two strings and set the variable.  */
	      *eq = '\0';
	      eq++;
981
	      grub_script_env_set (assign, eq);
982 983
	    }
	  grub_free (assign);
984

985
	  grub_snprintf (errnobuf, sizeof (errnobuf), "%d", grub_errno);
986
	  grub_script_env_set ("?", errnobuf);
987

988
	  grub_script_argv_free (&argv);
989 990
	  grub_print_error ();

991 992
	  return 0;
	}
993 994 995 996
    }

  /* Execute the GRUB command or function.  */
  if (grubcmd)
997
    {
998 999 1000 1001
      if (grub_extractor_level && !(grubcmd->flags
				    & GRUB_COMMAND_FLAG_EXTRACTOR))
	ret = grub_error (GRUB_ERR_EXTRACTOR,
			  "%s isn't allowed to execute in an extractor",
1002 1003 1004
			  cmdname);
      else if ((grubcmd->flags & GRUB_COMMAND_FLAG_BLOCKS) &&
	       (grubcmd->flags & GRUB_COMMAND_FLAG_EXTCMD))
BVK Chaitanya's avatar
BVK Chaitanya committed
1005
	ret = grub_extcmd_dispatcher (grubcmd, argc, args, argv.script);
1006
      else
BVK Chaitanya's avatar
BVK Chaitanya committed
1007
	ret = (grubcmd->func) (grubcmd, argc, args);
1008
    }
1009
  else
BVK Chaitanya's avatar
BVK Chaitanya committed
1010 1011 1012
    ret = grub_script_function_call (func, argc, args);

  if (invert)
1013 1014 1015 1016
    {
      if (ret == GRUB_ERR_TEST_FAILURE)
	grub_errno = ret = GRUB_ERR_NONE;
      else if (ret == GRUB_ERR_NONE)
1017
	ret = grub_error (GRUB_ERR_TEST_FAILURE, N_("false"));
1018 1019 1020
      else
	{
	  grub_print_error ();
1021
	  ret = GRUB_ERR_NONE;
1022 1023
	}
    }
1024 1025

  /* Free arguments.  */
1026
  grub_script_argv_free (&argv);
1027

1028 1029 1030 1031 1032
  if (grub_errno == GRUB_ERR_TEST_FAILURE)
    grub_errno = GRUB_ERR_NONE;

  grub_print_error ();

1033
  grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
1034 1035 1036 1037 1038
  grub_env_set ("?", errnobuf);

  return ret;
}

1039
/* Execute a block of one or more commands.  */
1040
grub_err_t
1041
grub_script_execute_cmdlist (struct grub_script_cmd *list)
1042
{
1043
  int ret = 0;
1044
  struct grub_script_cmd *cmd;
1045 1046

  /* Loop over every command and execute it.  */
1047 1048 1049 1050 1051 1052 1053 1054 1055 1056
  for (cmd = list->next; cmd; cmd = cmd->next)
    {
      if (active_breaks)
	break;

      ret = grub_script_execute_cmd (cmd);

      if (function_return)
	break;
    }
1057

1058
  return ret;
1059 1060 1061 1062 1063 1064
}

/* Execute an if statement.  */
grub_err_t
grub_script_execute_cmdif (struct grub_script_cmd *cmd)
{
1065
  int ret;
1066
  const char *result;
1067
  struct grub_script_cmdif *cmdif = (struct grub_script_cmdif *) cmd;
1068 1069

  /* Check if the commands results in a true or a false.  The value is
1070
     read from the env variable `?'.  */
1071 1072 1073
  ret = grub_script_execute_cmd (cmdif->exec_to_evaluate);
  if (function_return)
    return ret;
1074

1075
  result = grub_env_get ("?");
1076 1077
  grub_errno = GRUB_ERR_NONE;

1078
  /* Execute the `if' or the `else' part depending on the value of
1079 1080 1081
     `?'.  */
  if (result && ! grub_strcmp (result, "0"))
    return grub_script_execute_cmd (cmdif->exec_on_true);
1082
  else
1083
    return grub_script_execute_cmd (cmdif->exec_on_false);
1084 1085
}

1086 1087 1088 1089
/* Execute a for statement.  */
grub_err_t
grub_script_execute_cmdfor (struct grub_script_cmd *cmd)
{
1090 1091
  unsigned i;
  grub_err_t result;
1092
  struct grub_script_argv argv = { 0, 0, 0 };
1093 1094
  struct grub_script_cmdfor *cmdfor = (struct grub_script_cmdfor *) cmd;

1095
  if (grub_script_arglist_to_argv (cmdfor->words, &argv))
1096 1097
    return grub_errno;

BVK Chaitanya's avatar
BVK Chaitanya committed
1098
  active_loops++;
1099
  result = 0;
1100
  for (i = 0; i < argv.argc; i++)
1101
    {
BVK Chaitanya's avatar
BVK Chaitanya committed
1102 1103 1104
      if (is_continue && active_breaks == 1)
	active_breaks = 0;

BVK Chaitanya's avatar
BVK Chaitanya committed
1105 1106
      if (! active_breaks)
	{
1107
	  grub_script_env_set (cmdfor->name->str, argv.args[i]);
BVK Chaitanya's avatar
BVK Chaitanya committed
1108
	  result = grub_script_execute_cmd (cmdfor->list);
1109 1110
	  if (function_return)
	    break;
BVK Chaitanya's avatar
BVK Chaitanya committed
1111
	}
1112 1113
    }

BVK Chaitanya's avatar
BVK Chaitanya committed
1114 1115 1116 1117
  if (active_breaks)
    active_breaks--;

  active_loops--;
1118
  grub_script_argv_free (&argv);
1119 1120 1121
  return result;
}

1122 1123 1124 1125 1126 1127 1128
/* Execute a "while" or "until" command.  */
grub_err_t
grub_script_execute_cmdwhile (struct grub_script_cmd *cmd)
{
  int result;
  struct grub_script_cmdwhile *cmdwhile = (struct grub_script_cmdwhile *) cmd;

BVK Chaitanya's avatar
BVK Chaitanya committed
1129
  active_loops++;
1130
  do {
1131 1132 1133 1134 1135
    result = grub_script_execute_cmd (cmdwhile->cond);
    if (function_return)
      break;

    if (cmdwhile->until ? !result : result)
1136 1137 1138
      break;

    result = grub_script_execute_cmd (cmdwhile->list);
1139 1140
    if (function_return)
      break;
BVK Chaitanya's avatar
BVK Chaitanya committed
1141

BVK Chaitanya's avatar
BVK Chaitanya committed
1142 1143 1144
    if (active_breaks == 1 && is_continue)
      active_breaks = 0;

BVK Chaitanya's avatar
BVK Chaitanya committed
1145
    if (active_breaks)
BVK Chaitanya's avatar
BVK Chaitanya committed
1146
      break;
BVK Chaitanya's avatar
BVK Chaitanya committed
1147

1148 1149
  } while (1); /* XXX Put a check for ^C here */

BVK Chaitanya's avatar
BVK Chaitanya committed
1150 1151 1152
  if (active_breaks)
    active_breaks--;

BVK Chaitanya's avatar
BVK Chaitanya committed
1153
  active_loops--;
1154 1155 1156
  return result;
}

1157 1158 1159 1160 1161 1162 1163 1164 1165
/* Execute any GRUB pre-parsed command or script.  */
grub_err_t
grub_script_execute (struct grub_script *script)
{
  if (script == 0)
    return 0;

  return grub_script_execute_cmd (script->cmd);
}
1166