script.c 9.76 KB
Newer Older
1 2 3
/* script.c -- Functions to create an in memory description of the script. */
/*
 *  GRUB  --  GRand Unified Bootloader
4
 *  Copyright (C) 2005,2006,2007,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
 */

#include <grub/misc.h>
21
#include <grub/script_sh.h>
22 23 24 25 26
#include <grub/parser.h>
#include <grub/mm.h>

/* It is not possible to deallocate the memory when a syntax error was
   found.  Because of that it is required to keep track of all memory
27 28
   allocations.  The memory is freed in case of an error, or assigned
   to the parsed script when parsing was successful.
29

30
   In case of the normal malloc, some additional bytes are allocated
31
   for this datastructure.  All reserved memory is stored in a linked
32
   list so it can be easily freed.  The original memory can be found
33 34 35 36 37 38 39 40 41
   from &mem.  */
struct grub_script_mem
{
  struct grub_script_mem *next;
  char mem;
};

/* Return malloc'ed memory and keep track of the allocation.  */
void *
42
grub_script_malloc (struct grub_parser_param *state, grub_size_t size)
43 44 45 46
{
  struct grub_script_mem *mem;
  mem = (struct grub_script_mem *) grub_malloc (size + sizeof (*mem)
						- sizeof (char));
47 48
  if (!mem)
    return 0;
49 50

  grub_dprintf ("scripting", "malloc %p\n", mem);
51 52
  mem->next = state->memused;
  state->memused = mem;
53 54 55 56
  return (void *) &mem->mem;
}

/* Free all memory described by MEM.  */
57
void
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73
grub_script_mem_free (struct grub_script_mem *mem)
{
  struct grub_script_mem *memfree;

  while (mem)
    {
      memfree = mem->next;
      grub_dprintf ("scripting", "free %p\n", mem);
      grub_free (mem);
      mem = memfree;
    }
}

/* Start recording memory usage.  Returns the memory that should be
   restored when calling stop.  */
struct grub_script_mem *
74
grub_script_mem_record (struct grub_parser_param *state)
75
{
76 77 78
  struct grub_script_mem *mem = state->memused;
  state->memused = 0;

79 80 81 82 83 84
  return mem;
}

/* Stop recording memory usage.  Restore previous recordings using
   RESTORE.  Return the recorded memory.  */
struct grub_script_mem *
85 86
grub_script_mem_record_stop (struct grub_parser_param *state,
			     struct grub_script_mem *restore)
87
{
88 89
  struct grub_script_mem *mem = state->memused;
  state->memused = restore;
90 91 92 93 94 95 96
  return mem;
}

/* Free the memory reserved for CMD and all of it's children.  */
void
grub_script_free (struct grub_script *script)
{
97
  struct grub_script *s;
98
  struct grub_script *t;
99

BVK Chaitanya's avatar
BVK Chaitanya committed
100
  if (! script)
101
    return;
102 103 104 105

  if (script->mem)
    grub_script_mem_free (script->mem);

106 107
  s = script->children;
  while (s) {
108 109
    t = s->next_siblings;
    grub_script_unref (s);
110
    s = t;
111
  }
112 113 114 115
  grub_free (script);
}


116

117 118 119
/* Extend the argument arg with a variable or string of text.  If ARG
   is zero a new list is created.  */
struct grub_script_arg *
120 121 122
grub_script_arg_add (struct grub_parser_param *state,
		     struct grub_script_arg *arg, grub_script_arg_type_t type,
		     char *str)
123 124 125
{
  struct grub_script_arg *argpart;
  struct grub_script_arg *ll;
126
  int len;
127

128 129 130 131 132
  argpart =
    (struct grub_script_arg *) grub_script_malloc (state, sizeof (*arg));
  if (!argpart)
    return arg;

133
  argpart->type = type;
134
  argpart->script = 0;
135

136 137
  len = grub_strlen (str) + 1;
  argpart->str = grub_script_malloc (state, len);
138 139 140
  if (!argpart->str)
    return arg; /* argpart is freed later, during grub_script_free.  */

141
  grub_memcpy (argpart->str, str, len);
142 143
  argpart->next = 0;

144
  if (!arg)
145 146 147 148
    return argpart;

  for (ll = arg; ll->next; ll = ll->next);
  ll->next = argpart;
149

150 151 152 153 154 155
  return arg;
}

/* Add the argument ARG to the end of the argument list LIST.  If LIST
   is zero, a new list will be created.  */
struct grub_script_arglist *
156
grub_script_add_arglist (struct grub_parser_param *state,
157 158
			 struct grub_script_arglist *list,
			 struct grub_script_arg *arg)
159 160 161 162 163 164
{
  struct grub_script_arglist *link;
  struct grub_script_arglist *ll;

  grub_dprintf ("scripting", "arglist\n");

165 166 167 168 169
  link =
    (struct grub_script_arglist *) grub_script_malloc (state, sizeof (*link));
  if (!link)
    return list;

170 171 172 173
  link->next = 0;
  link->arg = arg;
  link->argcount = 0;

174
  if (!list)
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
    {
      link->argcount++;
      return link;
    }

  list->argcount++;

  /* Look up the last link in the chain.  */
  for (ll = list; ll->next; ll = ll->next);
  ll->next = link;

  return list;
}

/* Create a command that describes a single command line.  CMDLINE
   contains the name of the command that should be executed.  ARGLIST
   holds all arguments for this command.  */
struct grub_script_cmd *
193
grub_script_create_cmdline (struct grub_parser_param *state,
194
			    struct grub_script_arglist *arglist)
195 196 197 198 199
{
  struct grub_script_cmdline *cmd;

  grub_dprintf ("scripting", "cmdline\n");

200
  cmd = grub_script_malloc (state, sizeof (*cmd));
201 202 203
  if (!cmd)
    return 0;

204 205 206 207 208 209 210 211
  cmd->cmd.exec = grub_script_execute_cmdline;
  cmd->cmd.next = 0;
  cmd->arglist = arglist;

  return (struct grub_script_cmd *) cmd;
}

/* Create a command that functions as an if statement.  If BOOL is
212
   evaluated to true (the value is returned in envvar '?'), the
213 214 215
   interpreter will run the command TRUE, otherwise the interpreter
   runs the command FALSE.  */
struct grub_script_cmd *
216
grub_script_create_cmdif (struct grub_parser_param *state,
217 218 219
			  struct grub_script_cmd *exec_to_evaluate,
			  struct grub_script_cmd *exec_on_true,
			  struct grub_script_cmd *exec_on_false)
220 221 222 223 224
{
  struct grub_script_cmdif *cmd;

  grub_dprintf ("scripting", "cmdif\n");

225
  cmd = grub_script_malloc (state, sizeof (*cmd));
226 227 228
  if (!cmd)
    return 0;

229 230
  cmd->cmd.exec = grub_script_execute_cmdif;
  cmd->cmd.next = 0;
231 232 233
  cmd->exec_to_evaluate = exec_to_evaluate;
  cmd->exec_on_true = exec_on_true;
  cmd->exec_on_false = exec_on_false;
234 235 236 237

  return (struct grub_script_cmd *) cmd;
}

238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261
/* Create a command that functions as a for statement.  */
struct grub_script_cmd *
grub_script_create_cmdfor (struct grub_parser_param *state,
			   struct grub_script_arg *name,
			   struct grub_script_arglist *words,
			   struct grub_script_cmd *list)
{
  struct grub_script_cmdfor *cmd;

  grub_dprintf ("scripting", "cmdfor\n");

  cmd = grub_script_malloc (state, sizeof (*cmd));
  if (! cmd)
    return 0;

  cmd->cmd.exec = grub_script_execute_cmdfor;
  cmd->cmd.next = 0;
  cmd->name = name;
  cmd->words = words;
  cmd->list = list;

  return (struct grub_script_cmd *) cmd;
}

262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
/* Create a "while" or "until" command.  */
struct grub_script_cmd *
grub_script_create_cmdwhile (struct grub_parser_param *state,
			     struct grub_script_cmd *cond,
			     struct grub_script_cmd *list,
			     int is_an_until_loop)
{
  struct grub_script_cmdwhile *cmd;

  cmd = grub_script_malloc (state, sizeof (*cmd));
  if (! cmd)
    return 0;

  cmd->cmd.exec = grub_script_execute_cmdwhile;
  cmd->cmd.next = 0;
  cmd->cond = cond;
  cmd->list = list;
  cmd->until = is_an_until_loop;

  return (struct grub_script_cmd *) cmd;
}

284 285 286
/* Create a chain of commands.  LAST contains the command that should
   be added at the end of LIST's list.  If LIST is zero, a new list
   will be created.  */
287
struct grub_script_cmd *
288 289 290
grub_script_append_cmd (struct grub_parser_param *state,
			struct grub_script_cmd *list,
			struct grub_script_cmd *last)
291
{
292 293
  struct grub_script_cmd *ptr;

294
  grub_dprintf ("scripting", "append command\n");
295

296 297
  if (! last)
    return list;
298

299
  if (! list)
300
    {
301 302
      list = grub_script_malloc (state, sizeof (*list));
      if (! list)
303 304
	return 0;

305 306
      list->exec = grub_script_execute_cmdlist;
      list->next = last;
307 308 309
    }
  else
    {
310 311 312 313 314
      ptr = list;
      while (ptr->next)
	ptr = ptr->next;

      ptr->next = last;
315 316
    }

317
  return list;
318 319 320
}


321

322 323 324 325 326 327
struct grub_script *
grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem)
{
  struct grub_script *parsed;

  parsed = grub_malloc (sizeof (*parsed));
328 329
  if (! parsed)
    return 0;
330 331 332

  parsed->mem = mem;
  parsed->cmd = cmd;
333
  parsed->refcnt = 0;
334
  parsed->children = 0;
335
  parsed->next_siblings = 0;
336 337 338 339 340 341 342

  return parsed;
}

/* Parse the script passed in SCRIPT and return the parsed
   datastructure that is ready to be interpreted.  */
struct grub_script *
343 344
grub_script_parse (char *script,
		   grub_reader_getline_t getline, void *getline_data)
345 346 347
{
  struct grub_script *parsed;
  struct grub_script_mem *membackup;
348 349
  struct grub_lexer_param *lexstate;
  struct grub_parser_param *parsestate;
350

351
  parsed = grub_script_create (0, 0);
352
  if (!parsed)
353 354
    return 0;

355
  parsestate = grub_zalloc (sizeof (*parsestate));
356
  if (!parsestate)
357 358 359 360
    {
      grub_free (parsed);
      return 0;
    }
361

362
  /* Initialize the lexer.  */
363 364
  lexstate = grub_script_lexer_init (parsestate, script,
				     getline, getline_data);
365
  if (!lexstate)
366 367 368 369 370
    {
      grub_free (parsed);
      grub_free (parsestate);
      return 0;
    }
371

372
  parsestate->lexerstate = lexstate;
373

374
  membackup = grub_script_mem_record (parsestate);
375

376 377
  /* Parse the script.  */
  if (grub_script_yyparse (parsestate) || parsestate->err)
378 379
    {
      struct grub_script_mem *memfree;
380
      memfree = grub_script_mem_record_stop (parsestate, membackup);
381
      grub_script_mem_free (memfree);
382
      grub_script_lexer_fini (lexstate);
383
      grub_free (parsestate);
384
      grub_free (parsed);
385 386 387
      return 0;
    }

388 389
  parsed->mem = grub_script_mem_record_stop (parsestate, membackup);
  parsed->cmd = parsestate->parsed;
390
  parsed->children = parsestate->scripts;
391

392
  grub_script_lexer_fini (lexstate);
393
  grub_free (parsestate);
394 395 396

  return parsed;
}