# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: address@hidden # target_branch: ../mainline/ # testament_sha1: c3ba37e16d26006af32fc9ee46b2de8e3319125c # timestamp: 2010-01-23 10:34:33 +0530 # base_revision_id: address@hidden # # Begin patch === added file 'ChangeLog.lexer-rewrite' --- ChangeLog.lexer-rewrite 1970-01-01 00:00:00 +0000 +++ ChangeLog.lexer-rewrite 2010-01-22 13:37:27 +0000 @@ -0,0 +1,70 @@ +2010-01-10 BVK Chaitanya + + * conf/any-emu.rmk: Build rule updates. + * conf/common.rmk: Likewise. + * conf/i386-coreboot.rmk: Likewise. + * conf/i386-efi.rmk: Likewise. + * conf/i386-ieee1275.rmk: Likewise. + * conf/i386-pc.rmk: Likewise. + * conf/powerpc-ieee1275.rmk: Likewise. + * conf/x86_64-efi.rmk: Likewise. + + * configure.ac: Configure check for flex. + + * include/grub/script_sh.h (grub_script_arg_type_t): More argument + types. + (grub_lexer_param): Struct member updates. + (grub_parser_param): Likewise. + (GRUB_LEXER_TOKEN_MAX): Maximum token size. + (GRUB_LEXER_RECORD_INCREMENT): Memory increments' size. + (grub_script_lexer_init): Prototype update. + (grub_script_lexer_record_start): Likewise. + (grub_script_lexer_record_stop): Likewise. + (grub_script_lexer_yywrap): New function prototype. + (grub_script_lexer_fini): Likewise. + (grub_script_execute_argument_to_string): Removed by... + (grub_script_execute_argument_to_argv): ...better version. + + * script/execute.c (ROUND_UPTO): New macro. + (grub_script_execute_cmdline): Out of memory fixes. + (grub_script_execute_menuentry): Likewise. + (grub_script_execute_argument_to_string): Removed. Update all + users by... + (grub_script_execute_argument_to_argv): ...better version. + * script/function.c (grub_script_function_create): Use + grub_script_execute_argument_to_argv instead of + grub_script_execute_argument_to_string. + + * script/lexer.c (check_varstate): Removed. + (check_textstate): Removed. + (grub_script_lexer_record_start): Likewise. + (grub_script_lexer_record_stop): Likewise. + (recordchar): Replaced with... + (grub_script_lexer_record): ...new function. + (nextchar): Removed. + (grub_script_lexer_init): Rewritten. + (grub_script_yylex): Rewritten. + (append_newline): New function. + (grub_script_lexer_yywrap): New function. + (grub_script_lexer_fini): New function. + (grub_script_yyerror): Sets error flag. + + * script/yylex.l: New file. + (grub_lexer_yyfree): Wrapper for flex yyffre. + (grub_lexer_yyalloc): Likewise. + (grub_lexer_yyrealloc): Likewise. + * script/parser.y: Refactored. + + * script/script.c (grub_script_arg_add): Out of memory fixes. + (grub_script_add_arglist): Likewise. + (grub_script_create_cmdline): Likewise. + (grub_script_create_cmdmenu): Likewise. + (grub_script_add_cmd): Likewise. + (grub_script_parse): Use grub_script_lexer_fini to deallocated. + * util/grub-script-check.c (grub_script_execute_menuentry): Remove + unnecessary code. + + * tests/grub_script_echo1.in: New testcase. + * tests/grub_script_vars1.in: New testcase. + * tests/grub_script_echo_keywords.in: New testcase. + === modified file 'conf/any-emu.rmk' --- conf/any-emu.rmk 2010-01-20 20:31:39 +0000 +++ conf/any-emu.rmk 2010-01-22 13:37:27 +0000 @@ -1,7 +1,7 @@ # -*- makefile -*- # Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h sbin_UTILITIES += grub-emu util/grub-emu.c_DEPENDENCIES = grub_emu_init.h @@ -33,6 +33,8 @@ commands/terminal.c normal/context.c lib/charset.c \ script/main.c script/execute.c script/function.c \ script/lexer.c script/script.c grub_script.tab.c \ + grub_script.yy.c \ + \ partmap/amiga.c partmap/apple.c partmap/msdos.c partmap/sun.c \ partmap/acorn.c partmap/gpt.c \ \ @@ -99,5 +101,11 @@ $(YACC) -d -p grub_script_yy -b grub_script $(srcdir)/script/parser.y DISTCLEANFILES += grub_script.tab.c grub_script.tab.h +grub_script.yy.c grub_script.yy.h: script/yylex.l + $(LEX) -o grub_script.yy.c --header-file=grub_script.yy.h $(srcdir)/script/yylex.l + sed -i 's/^#include.*\(\|\|\|\|\)//g' grub_script.yy.h + sed -i 's/^#include.*\(\|\|\|\|\)//g' grub_script.yy.c +DISTCLEANFILES += grub_script.yy.c grub_script.yy.h + bin_UTILITIES += grub-bin2h grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c === modified file 'conf/common.rmk' --- conf/common.rmk 2010-01-20 20:31:39 +0000 +++ conf/common.rmk 2010-01-22 13:37:27 +0000 @@ -91,13 +91,21 @@ bin_UTILITIES += grub-bin2h grub_bin2h_SOURCES = gnulib/progname.c util/bin2h.c +# For the lexer. +grub_script.yy.c grub_script.yy.h: script/yylex.l + $(LEX) -o grub_script.yy.c --header-file=grub_script.yy.h $(srcdir)/script/yylex.l + sed -i 's/^#include.*\(\|\|\|\|\)//g' grub_script.yy.h + sed -i 's/^#include.*\(\|\|\|\|\)//g' grub_script.yy.c +DISTCLEANFILES += grub_script.yy.c grub_script.yy.h + # For grub-script-check. bin_UTILITIES += grub-script-check util/grub-script-check.c_DEPENDENCIES = grub_script_check_init.h grub_script_check_SOURCES = gnulib/progname.c util/grub-script-check.c util/misc.c \ script/main.c script/script.c script/function.c script/lexer.c \ kern/handler.c kern/err.c kern/parser.c kern/list.c \ - kern/misc.c kern/env.c grub_script_check_init.c grub_script.tab.c + kern/misc.c kern/env.c grub_script_check_init.c grub_script.tab.c \ + grub_script.yy.c # For the parser. grub_script.tab.c grub_script.tab.h: script/parser.y @@ -594,7 +602,7 @@ # For sh.mod. sh_mod_SOURCES = script/main.c script/script.c script/execute.c \ - script/function.c script/lexer.c grub_script.tab.c + script/function.c script/lexer.c grub_script.tab.c grub_script.yy.c sh_mod_CFLAGS = $(COMMON_CFLAGS) sh_mod_LDFLAGS = $(COMMON_LDFLAGS) === modified file 'conf/i386-coreboot.rmk' --- conf/i386-coreboot.rmk 2010-01-20 20:31:39 +0000 +++ conf/i386-coreboot.rmk 2010-01-22 13:37:27 +0000 @@ -5,7 +5,7 @@ COMMON_LDFLAGS = -m32 -nostdlib # Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h # Images. === modified file 'conf/i386-efi.rmk' --- conf/i386-efi.rmk 2010-01-20 20:31:39 +0000 +++ conf/i386-efi.rmk 2010-01-22 13:37:27 +0000 @@ -5,7 +5,7 @@ COMMON_LDFLAGS = -melf_i386 -nostdlib # Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h # Utilities. bin_UTILITIES = grub-mkimage === modified file 'conf/i386-ieee1275.rmk' --- conf/i386-ieee1275.rmk 2010-01-20 20:31:39 +0000 +++ conf/i386-ieee1275.rmk 2010-01-22 13:37:27 +0000 @@ -5,7 +5,7 @@ COMMON_LDFLAGS = -nostdlib # Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h # Images. pkglib_PROGRAMS = kernel.img === modified file 'conf/i386-pc.rmk' --- conf/i386-pc.rmk 2010-01-20 20:31:39 +0000 +++ conf/i386-pc.rmk 2010-01-22 13:37:27 +0000 @@ -7,7 +7,7 @@ COMMON_LDFLAGS = -m32 -nostdlib # Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h # Images. pkglib_IMAGES = boot.img cdboot.img diskboot.img kernel.img lnxboot.img \ === modified file 'conf/powerpc-ieee1275.rmk' --- conf/powerpc-ieee1275.rmk 2010-01-20 20:31:39 +0000 +++ conf/powerpc-ieee1275.rmk 2010-01-22 13:37:27 +0000 @@ -6,7 +6,7 @@ COMMON_LDFLAGS += -nostdlib # Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h # Images. === modified file 'conf/sparc64-ieee1275.rmk' --- conf/sparc64-ieee1275.rmk 2010-01-20 20:31:39 +0000 +++ conf/sparc64-ieee1275.rmk 2010-01-22 13:37:27 +0000 @@ -6,7 +6,7 @@ COMMON_LDFLAGS = -melf64_sparc -nostdlib -mno-relax # Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h # Images. pkglib_IMAGES = boot.img diskboot.img kernel.img === modified file 'conf/tests.rmk' --- conf/tests.rmk 2010-01-12 10:19:40 +0000 +++ conf/tests.rmk 2010-01-22 13:37:27 +0000 @@ -37,12 +37,28 @@ check_SCRIPTS += example_grub_script_test example_grub_script_test_SOURCES = tests/example_grub_script_test.in +# +# Rules for real tests +# + +check_SCRIPTS += grub_script_echo1 +grub_script_echo1_SOURCES = tests/grub_script_echo1.in + +check_SCRIPTS += grub_script_echo_keywords +grub_script_echo_keywords_SOURCES = tests/grub_script_echo_keywords.in + +check_SCRIPTS += grub_script_vars1 +grub_script_vars1_SOURCES = tests/grub_script_vars1.in # List of tests to execute on "make check" -SCRIPTED_TESTS = example_scripted_test -SCRIPTED_TESTS += example_grub_script_test -UNIT_TESTS = example_unit_test -FUNCTIONAL_TESTS = example_functional_test.mod +# SCRIPTED_TESTS = example_scripted_test +# SCRIPTED_TESTS += example_grub_script_test +# UNIT_TESTS = example_unit_test +# FUNCTIONAL_TESTS = example_functional_test.mod + +SCRIPTED_TESTS = grub_script_echo1 +SCRIPTED_TESTS += grub_script_echo_keywords +SCRIPTED_TESTS += grub_script_vars1 # dependencies between tests and testing-tools $(SCRIPTED_TESTS): grub-shell grub-shell-tester === modified file 'conf/x86_64-efi.rmk' --- conf/x86_64-efi.rmk 2010-01-20 20:31:39 +0000 +++ conf/x86_64-efi.rmk 2010-01-22 13:37:27 +0000 @@ -5,7 +5,7 @@ COMMON_LDFLAGS = -melf_x86_64 -nostdlib # Used by various components. These rules need to precede them. -script/lexer.c_DEPENDENCIES = grub_script.tab.h +script/lexer.c_DEPENDENCIES = grub_script.tab.h grub_script.yy.h # Utilities. bin_UTILITIES = grub-mkimage === modified file 'configure.ac' --- configure.ac 2010-01-20 12:27:23 +0000 +++ configure.ac 2010-01-22 13:37:27 +0000 @@ -163,6 +163,11 @@ AC_MSG_ERROR([cmp is not found]) fi +AC_CHECK_PROGS([LEX], [flex]) +if test "x$LEX" = x; then + AC_MSG_ERROR([flex is not found]) +fi + AC_CHECK_PROGS([YACC], [bison]) if test "x$YACC" = x; then AC_MSG_ERROR([bison is not found]) === modified file 'include/grub/script_sh.h' --- include/grub/script_sh.h 2010-01-20 07:18:35 +0000 +++ include/grub/script_sh.h 2010-01-23 03:42:46 +0000 @@ -1,7 +1,7 @@ /* normal_parser.h */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2007,2009,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 @@ -45,8 +45,11 @@ typedef enum { - GRUB_SCRIPT_ARG_TYPE_STR, - GRUB_SCRIPT_ARG_TYPE_VAR + GRUB_SCRIPT_ARG_TYPE_VAR, + GRUB_SCRIPT_ARG_TYPE_TEXT, + GRUB_SCRIPT_ARG_TYPE_DQVAR, + GRUB_SCRIPT_ARG_TYPE_DQSTR, + GRUB_SCRIPT_ARG_TYPE_SQSTR } grub_script_arg_type_t; /* A part of an argument. */ @@ -121,12 +124,6 @@ /* State of the lexer as passed to the lexer. */ struct grub_lexer_param { - /* Set to 0 when the lexer is done. */ - int done; - - /* State of the state machine. */ - grub_parser_state_t state; - /* Function used by the lexer to get a new line when more input is expected, but not available. */ grub_reader_getline_t getline; @@ -137,10 +134,6 @@ depleted. */ int refs; - /* The character stream that has to be parsed. */ - char *script; - char *newscript; /* XXX */ - /* While walking through the databuffer, `record' the characters to this other buffer. It can be used to edit the menu entry at a later moment. */ @@ -157,13 +150,31 @@ /* Size of RECORDING. */ int recordlen; - /* The token that is already parsed but not yet returned. */ - int tokenonhold; - - /* Was the last token a newline? */ - int was_newline; + /* End of file reached. */ + int eof; + + /* Merge multiple word tokens. */ + int merge_start; + int merge_end; + + /* Part of a multi-part token. */ + char *text; + unsigned used; + unsigned size; + + /* Type of text. */ + grub_script_arg_type_t type; + + /* Flex scanner. */ + void *yyscanner; + + /* Flex scanner buffer. */ + void *buffer; }; +#define GRUB_LEXER_INITIAL_TEXT_SIZE 32 +#define GRUB_LEXER_INITIAL_RECORD_SIZE 256 + /* State of the parser as passes to the parser. */ struct grub_parser_param { @@ -223,12 +234,16 @@ struct grub_script *grub_script_create (struct grub_script_cmd *cmd, struct grub_script_mem *mem); -struct grub_lexer_param *grub_script_lexer_init (char *s, +struct grub_lexer_param *grub_script_lexer_init (struct grub_parser_param *parser, + char *script, grub_reader_getline_t getline); +void grub_script_lexer_fini (struct grub_lexer_param *); void grub_script_lexer_ref (struct grub_lexer_param *); void grub_script_lexer_deref (struct grub_lexer_param *); -void grub_script_lexer_record_start (struct grub_lexer_param *); -char *grub_script_lexer_record_stop (struct grub_lexer_param *); +void grub_script_lexer_record_start (struct grub_parser_param *); +char *grub_script_lexer_record_stop (struct grub_parser_param *); +int grub_script_lexer_yywrap (struct grub_parser_param *); +void grub_script_lexer_record (struct grub_parser_param *, char *); /* Functions to track allocated memory. */ struct grub_script_mem *grub_script_mem_record (struct grub_parser_param *state); @@ -284,7 +299,7 @@ int grub_script_function_call (grub_script_function_t func, int argc, char **args); -char * -grub_script_execute_argument_to_string (struct grub_script_arg *arg); +char ** +grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *count); #endif /* ! GRUB_NORMAL_PARSER_HEADER */ === modified file 'script/execute.c' --- script/execute.c 2009-12-29 09:04:06 +0000 +++ script/execute.c 2010-01-23 04:58:52 +0000 @@ -1,7 +1,7 @@ /* execute.c -- Execute a GRUB script. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2007,2008,2009,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 @@ -35,49 +35,146 @@ return cmd->exec (cmd); } -/* Parse ARG and return the textual representation. Add strings are - concatenated and all values of the variables are filled in. */ -char * -grub_script_execute_argument_to_string (struct grub_script_arg *arg) +/* Expand arguments in ARGLIST into multiple arguments. */ +char ** +grub_script_execute_arglist_to_argv (struct grub_script_arglist *arglist, int *count) { - int size = 0; - char *val; - char *chararg; - struct grub_script_arg *argi; - - /* First determine the size of the argument. */ - for (argi = arg; argi; argi = argi->next) - { - if (argi->type == 1) - { - val = grub_env_get (argi->str); - if (val) - size += grub_strlen (val); - } - else - size += grub_strlen (argi->str); - } - - /* Create the argument. */ - chararg = grub_malloc (size + 1); - if (! chararg) - return 0; - - *chararg = '\0'; - /* First determine the size of the argument. */ - for (argi = arg; argi; argi = argi->next) - { - if (argi->type == 1) - { - val = grub_env_get (argi->str); - if (val) - grub_strcat (chararg, val); - } - else - grub_strcat (chararg, argi->str); - } - - return chararg; + int i; + int oom; + int argc; + int empty; + char *ptr; + char **argv; + char *value; + struct grub_script_arg *arg; + + auto void push (char *str); + void push (char *str) + { + char **p; + + if (oom) + return; + + p = grub_realloc (argv, ALIGN_UP (sizeof(char*) * (argc + 1), 32)); + if (!p) + oom = 1; + else + { + p[argc++] = str; + argv = p; + } + } + + auto char* append (const char *str, grub_size_t nchar); + char* append (const char *str, grub_size_t nchar) + { + int len; + int old; + char *p; + + if (oom || !str) + return 0; + + len = nchar ?: grub_strlen (str); + old = argv[argc - 1] ? grub_strlen (argv[argc - 1]) : 0; + p = grub_realloc (argv[argc - 1], ALIGN_UP(old + len + 1, 32)); + + if (p) + { + grub_strncpy (p + old, str, len); + p[old + len] = '\0'; + } + else + { + oom = 1; + grub_free (argv[argc - 1]); + } + argv[argc - 1] = p; + return argv[argc - 1]; + } + + /* Move *STR to the begining of next word, but return current word. */ + auto char* move_to_next (char **str); + char* move_to_next (char **str) + { + char *end; + char *start; + + if (oom || !str || !*str) + return 0; + + start = *str; + while (*start && grub_isspace (*start)) start++; + if (*start == '\0') + return 0; + + end = start + 1; + while (*end && !grub_isspace (*end)) end++; + + *str = end; + return start; + } + + oom = 0; + argv = 0; + argc = 0; + push (0); + for (; arglist; arglist = arglist->next) + { + empty = 1; + arg = arglist->arg; + while (arg) + { + switch (arg->type) + { + case GRUB_SCRIPT_ARG_TYPE_VAR: + value = grub_env_get (arg->str); + while (value && *value && (ptr = move_to_next(&value))) + { + empty = 0; + append (ptr, value - ptr); + if (*value) push(0); + } + break; + + case GRUB_SCRIPT_ARG_TYPE_TEXT: + if (grub_strlen (arg->str) > 0) + { + empty = 0; + append (arg->str, 0); + } + break; + + case GRUB_SCRIPT_ARG_TYPE_DQSTR: + case GRUB_SCRIPT_ARG_TYPE_SQSTR: + empty = 0; + append (arg->str, 0); + break; + + case GRUB_SCRIPT_ARG_TYPE_DQVAR: + empty = 0; + append (grub_env_get (arg->str), 0); + break; + } + arg = arg->next; + } + if (!empty) + push (0); + } + + if (oom) + { + for (i = 0; i < argc; i++) + grub_free (argv[i]); + grub_free (argv); + argv = 0; + } + + if (argv) + *count = argc - 1; + + return argv; } /* Execute a single command line. */ @@ -85,7 +182,6 @@ grub_script_execute_cmdline (struct grub_script_cmd *cmd) { struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd; - struct grub_script_arglist *arglist; char **args = 0; int i = 0; grub_command_t grubcmd; @@ -96,7 +192,11 @@ char *cmdname; /* Lookup the command. */ - cmdname = grub_script_execute_argument_to_string (cmdline->arglist->arg); + args = grub_script_execute_arglist_to_argv (cmdline->arglist, &argcount); + if (!args) + return grub_errno; + + cmdname = args[0]; grubcmd = grub_command_find (cmdname); if (! grubcmd) { @@ -129,27 +229,12 @@ return 0; } } - grub_free (cmdname); - - if (cmdline->arglist->next) - { - argcount = cmdline->arglist->argcount - 1; - - /* Create argv from the arguments. */ - args = grub_malloc (sizeof (char *) * argcount); - for (arglist = cmdline->arglist->next; arglist; arglist = arglist->next) - { - char *str; - str = grub_script_execute_argument_to_string (arglist->arg); - args[i++] = str; - } - } /* Execute the GRUB command or function. */ if (grubcmd) - ret = (grubcmd->func) (grubcmd, argcount, args); + ret = (grubcmd->func) (grubcmd, argcount - 1, args + 1); else - ret = grub_script_function_call (func, argcount, args); + ret = grub_script_function_call (func, argcount - 1, args + 1); /* Free arguments. */ for (i = 0; i < argcount; i++) @@ -202,7 +287,6 @@ grub_script_execute_menuentry (struct grub_script_cmd *cmd) { struct grub_script_cmd_menuentry *cmd_menuentry; - struct grub_script_arglist *arglist; char **args = 0; int argcount = 0; int i = 0; @@ -211,22 +295,9 @@ if (cmd_menuentry->arglist) { - argcount = cmd_menuentry->arglist->argcount; - - /* Create argv from the arguments. */ - args = grub_malloc (sizeof (char *) * argcount); - - if (! args) - { - return grub_errno; - } - - for (arglist = cmd_menuentry->arglist; arglist; arglist = arglist->next) - { - char *str; - str = grub_script_execute_argument_to_string (arglist->arg); - args[i++] = str; - } + args = grub_script_execute_arglist_to_argv (cmd_menuentry->arglist, &argcount); + if (!args) + return grub_errno; } grub_normal_add_menu_entry (argcount, (const char **) args, === modified file 'script/function.c' --- script/function.c 2009-11-23 15:37:33 +0000 +++ script/function.c 2010-01-22 13:37:27 +0000 @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2007,2009,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 @@ -34,7 +34,7 @@ if (! func) return 0; - func->name = grub_script_execute_argument_to_string (functionname_arg); + func->name = grub_strdup (functionname_arg->str); if (! func->name) { grub_free (func); === modified file 'script/lexer.c' --- script/lexer.c 2009-12-24 22:53:05 +0000 +++ script/lexer.c 2010-01-23 04:44:32 +0000 @@ -1,7 +1,7 @@ /* lexer.c - The scripting lexer. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2006,2007,2008,2009,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 @@ -23,42 +23,7 @@ #include #include "grub_script.tab.h" - -static int -check_varstate (grub_parser_state_t state) -{ - return (state == GRUB_PARSER_STATE_VARNAME - || state == GRUB_PARSER_STATE_VAR - || state == GRUB_PARSER_STATE_QVAR - || state == GRUB_PARSER_STATE_VARNAME2 - || state == GRUB_PARSER_STATE_QVARNAME - || state == GRUB_PARSER_STATE_QVARNAME2); -} - -static int -check_textstate (grub_parser_state_t state) -{ - return (state == GRUB_PARSER_STATE_TEXT - || state == GRUB_PARSER_STATE_ESC - || state == GRUB_PARSER_STATE_QUOTE - || state == GRUB_PARSER_STATE_DQUOTE); -} - -struct grub_lexer_param * -grub_script_lexer_init (char *script, grub_reader_getline_t getline) -{ - struct grub_lexer_param *param; - - param = grub_zalloc (sizeof (*param)); - if (! param) - return 0; - - param->state = GRUB_PARSER_STATE_TEXT; - param->getline = getline; - param->script = script; - - return param; -} +#include "grub_script.yy.h" void grub_script_lexer_ref (struct grub_lexer_param *state) @@ -74,360 +39,308 @@ /* Start recording all characters passing through the lexer. */ void -grub_script_lexer_record_start (struct grub_lexer_param *state) +grub_script_lexer_record_start (struct grub_parser_param *parser) { - state->record = 1; - state->recordlen = 100; - state->recording = grub_malloc (state->recordlen); - state->recordpos = 0; + struct grub_lexer_param *lexer = parser->lexerstate; + + lexer->record = 1; + lexer->recordpos = 0; + if (lexer->recording) /* reuse last record */ + return; + + lexer->recordlen = GRUB_LEXER_INITIAL_RECORD_SIZE; + lexer->recording = grub_malloc (lexer->recordlen); + if (!lexer->recording) + { + grub_script_yyerror (parser, 0); + lexer->record = 0; + lexer->recordlen = 0; + } } char * -grub_script_lexer_record_stop (struct grub_lexer_param *state) +grub_script_lexer_record_stop (struct grub_parser_param *parser) { - state->record = 0; - - /* Delete the last character, it is a `}'. */ - if (state->recordpos > 0) - { - if (state->recording[--state->recordpos] != '}') - { - grub_printf ("Internal error while parsing menu entry"); - for (;;); /* XXX */ - } - state->recording[state->recordpos] = '\0'; - } - - return state->recording; + char *ptr; + char *result; + struct grub_lexer_param *lexer = parser->lexerstate; + + auto char *compact (char *start, char *end); + char *compact (char *start, char *end) + { + /* Delete '{' and '}' characters and whitespaces. */ + while (*start && grub_isspace (*start)) start++; + if (*start == '{') start++; + while (*start && grub_isspace (*start)) start++; + + while (*end && grub_isspace (*end)) end--; + if (*end == '}') end--; + while (*end && grub_isspace (*end)) end--; + *end = '\0'; + + return start; + } + + if (!lexer->record || !lexer->recording) + return 0; + + /* XXX This is not necessary in BASH. */ + + ptr = compact (lexer->recording, lexer->recording + lexer->recordpos - 1); + lexer->record = 0; + lexer->recordpos = 0; + + /* This memory would be freed by, grub_script_free. */ + result = grub_script_malloc (parser, grub_strlen (ptr) + 1); + if (result) + grub_strcpy (result, ptr); + + return result; } -/* When recording is enabled, record the character C as the next item - in the character stream. */ -static void -recordchar (struct grub_lexer_param *state, char c) +#define MAX(a,b) ((a) < (b) ? (b) : (a)) + +/* Record STR if input recording is enabled. */ +void +grub_script_lexer_record (struct grub_parser_param *parser, char *str) { - if (state->recordpos == state->recordlen) + int len; + char *old; + struct grub_lexer_param *lexer = parser->lexerstate; + + if (!lexer->record) + return; + + len = grub_strlen (str); + if (lexer->recordpos + len + 1 > lexer->recordlen) { - char *old = state->recording; - state->recordlen += 100; - state->recording = grub_realloc (state->recording, state->recordlen); - if (! state->recording) + old = lexer->recording; + lexer->recordlen = MAX (len, lexer->recordlen) * 2; + lexer->recording = grub_realloc (lexer->recording, lexer->recordlen); + if (!lexer->recording) { grub_free (old); - state->record = 0; - } - } - state->recording[state->recordpos++] = c; -} - -/* Fetch the next character for the lexer. */ -static void -nextchar (struct grub_lexer_param *state) -{ - if (state->record) - recordchar (state, *state->script); - state->script++; -} - -int -grub_script_yylex (union YYSTYPE *yylval, struct grub_parser_param *parsestate) -{ - grub_parser_state_t newstate; - char use; - struct grub_lexer_param *state = parsestate->lexerstate; - int firstrun = 1; - - yylval->arg = 0; - - if (state->tokenonhold) - { - int token = state->tokenonhold; - state->tokenonhold = 0; - return token; - } - - for (;! state->done; firstrun = 0) - { - if (! state->script || ! *state->script) - { - /* Check if more tokens are requested by the parser. */ - if (((state->refs && ! parsestate->err) - || state->state == GRUB_PARSER_STATE_ESC - || state->state == GRUB_PARSER_STATE_QUOTE - || state->state == GRUB_PARSER_STATE_DQUOTE) - && state->getline) - { - int doexit = 0; - if (state->state != GRUB_PARSER_STATE_ESC - && state->state != GRUB_PARSER_STATE_QUOTE - && state->state != GRUB_PARSER_STATE_DQUOTE - && ! state->was_newline) - { - state->was_newline = 1; - state->tokenonhold = '\n'; - break; - } - while (! state->script || ! *state->script) - { - grub_free (state->newscript); - state->newscript = 0; - state->getline (&state->newscript, 1); - state->script = state->newscript; - if (! state->script) - { - doexit = 1; - break; - } - } - if (doexit) - break; - grub_dprintf ("scripting", "token=`\\n'\n"); - recordchar (state, '\n'); - if (state->state == GRUB_PARSER_STATE_VARNAME) - state->state = GRUB_PARSER_STATE_TEXT; - if (state->state == GRUB_PARSER_STATE_QVARNAME) - state->state = GRUB_PARSER_STATE_DQUOTE; - if (state->state == GRUB_PARSER_STATE_DQUOTE - || state->state == GRUB_PARSER_STATE_QUOTE) - yylval->arg = grub_script_arg_add (parsestate, yylval->arg, - GRUB_SCRIPT_ARG_TYPE_STR, - "\n"); - } - else - { - grub_free (state->newscript); - state->newscript = 0; - state->done = 1; - grub_dprintf ("scripting", "token=`\\n'\n"); - state->tokenonhold = '\n'; - break; - } - } - state->was_newline = 0; - - newstate = grub_parser_cmdline_state (state->state, *state->script, &use); - - /* Check if it is a text. */ - if (check_textstate (newstate)) - { - char *buffer = NULL; - int bufpos = 0; - /* Buffer is initially large enough to hold most commands - but extends automatically when needed. */ - int bufsize = 128; - - buffer = grub_malloc (bufsize); - - /* In case the string is not quoted, this can be a one char - length symbol. */ - if (newstate == GRUB_PARSER_STATE_TEXT) - { - int doexit = 0; - switch (*state->script) - { - case ' ': - while (*state->script) - { - newstate = grub_parser_cmdline_state (state->state, - *state->script, &use); - if (! (state->state == GRUB_PARSER_STATE_TEXT - && *state->script == ' ')) - { - grub_dprintf ("scripting", "token=` '\n"); - if (! firstrun) - doexit = 1; - break; - } - state->state = newstate; - nextchar (state); - } - grub_dprintf ("scripting", "token=` '\n"); - if (! firstrun) - doexit = 1; - break; - case '{': - case '}': - case ';': - case '\n': - { - char c; - grub_dprintf ("scripting", "token=`%c'\n", *state->script); - c = *state->script; - nextchar (state); - state->tokenonhold = c; - doexit = 1; - break; - } - } - if (doexit) - { - grub_free (buffer); - break; - } - } - - /* Read one token, possible quoted. */ - while (*state->script) - { - newstate = grub_parser_cmdline_state (state->state, - *state->script, &use); - - /* Check if a variable name starts. */ - if (check_varstate (newstate)) - break; - - /* If the string is not quoted or escaped, stop processing - when a special token was found. It will be recognized - next time when this function is called. */ - if (newstate == GRUB_PARSER_STATE_TEXT - && state->state != GRUB_PARSER_STATE_ESC - && state->state != GRUB_PARSER_STATE_QUOTE - && state->state != GRUB_PARSER_STATE_DQUOTE) - { - int breakout = 0; - - switch (use) - { - case ' ': - case '{': - case '}': - case ';': - case '\n': - breakout = 1; - } - if (breakout) - break; - } - - if (use) - { - if (bufsize <= bufpos + 1) - { - bufsize <<= 1; - buffer = grub_realloc (buffer, bufsize); - } - buffer[bufpos++] = use; - } - - state->state = newstate; - nextchar (state); - } - - /* A string of text was read in. */ - if (bufsize <= bufpos + 1) - { - bufsize <<= 1; - buffer = grub_realloc (buffer, bufsize); - } - - buffer[bufpos++] = 0; - - grub_dprintf ("scripting", "token=`%s'\n", buffer); - yylval->arg = grub_script_arg_add (parsestate, yylval->arg, - GRUB_SCRIPT_ARG_TYPE_STR, buffer); - - grub_free (buffer); - } - else if (newstate == GRUB_PARSER_STATE_VAR - || newstate == GRUB_PARSER_STATE_QVAR) - { - char *buffer = NULL; - int bufpos = 0; - /* Buffer is initially large enough to hold most commands - but extends automatically when needed. */ - int bufsize = 128; - - buffer = grub_malloc (bufsize); - - /* This is a variable, read the variable name. */ - while (*state->script) - { - newstate = grub_parser_cmdline_state (state->state, - *state->script, &use); - - /* Check if this character is not part of the variable name - anymore. */ - if (! (check_varstate (newstate))) - { - if (state->state == GRUB_PARSER_STATE_VARNAME2 - || state->state == GRUB_PARSER_STATE_QVARNAME2) - nextchar (state); - state->state = newstate; - break; - } - - if (use) - { - if (bufsize <= bufpos + 1) - { - bufsize <<= 1; - buffer = grub_realloc (buffer, bufsize); - } - buffer[bufpos++] = use; - } - - nextchar (state); - state->state = newstate; - } - - if (bufsize <= bufpos + 1) - { - bufsize <<= 1; - buffer = grub_realloc (buffer, bufsize); - } - - buffer[bufpos++] = 0; - - state->state = newstate; - yylval->arg = grub_script_arg_add (parsestate, yylval->arg, - GRUB_SCRIPT_ARG_TYPE_VAR, buffer); - grub_dprintf ("scripting", "vartoken=`%s'\n", buffer); - - grub_free (buffer); + lexer->record = 0; + lexer->recordpos = 0; + lexer->recordlen /= 2; + grub_script_yyerror (parser, 0); + return; + } + } + grub_strcpy (lexer->recording + lexer->recordpos, str); + lexer->recordpos += len; +} + +/* Append '\n' to SRC, before '\0' */ +static char * +append_newline (const char *src) +{ + char *line; + grub_size_t len; + + len = grub_strlen (src); + line = grub_malloc (len + 2); + if (!line) + return 0; + + grub_strcpy (line, src); + + line[len] = '\n'; + line[len + 1] = '\0'; + return line; +} + +/* Read next line of input if necessary, and set yyscanner buffers. */ +int +grub_script_lexer_yywrap (struct grub_parser_param *parserstate) +{ + int len; + char *line; + char *line2; + YY_BUFFER_STATE buffer; + struct grub_lexer_param *lexerstate = parserstate->lexerstate; + + if (!lexerstate->refs) + return 0; + + if (!lexerstate->getline) + { + grub_script_yyerror (parserstate, "unexpected end of file"); + return 0; + } + + line = 0; + buffer = 0; + lexerstate->getline (&line, 1); + if (!line) + { + grub_script_yyerror (parserstate, 0); /* XXX this could be for ^C case? */ + return 0; + } + + len = grub_strlen (line); + if (line[len - 1] == '\n') + { + buffer = yy_scan_string (line, lexerstate->yyscanner); + } + else + { + line2 = append_newline (line); + if (line2) + { + buffer = yy_scan_string (line2, lexerstate->yyscanner); + grub_free (line2); + } + } + + grub_free (line); + if (!buffer) + { + grub_script_yyerror (parserstate, 0); + return 0; + } + + return 1; +} + +struct grub_lexer_param * +grub_script_lexer_init (struct grub_parser_param *parser, char *script, + grub_reader_getline_t getline) +{ + int len; + char *script2; + YY_BUFFER_STATE buffer; + struct grub_lexer_param *lexerstate; + + lexerstate = grub_zalloc (sizeof (*lexerstate)); + if (!lexerstate) + return 0; + + lexerstate->size = GRUB_LEXER_INITIAL_TEXT_SIZE; + lexerstate->text = grub_malloc (lexerstate->size); + if (!lexerstate->text) + { + grub_free (lexerstate); + return 0; + } + + lexerstate->getline = getline; /* rest are all zeros already */ + if (yylex_init (&lexerstate->yyscanner)) + { + grub_free (lexerstate->text); + grub_free (lexerstate); + return 0; + } + + buffer = 0; + script = script ? : "\n"; + len = grub_strlen (script); + + if (script[len - 1] == '\n') + { + buffer = yy_scan_string (script, lexerstate->yyscanner); + } + else + { + script2 = append_newline (script); + if (script2) + { + buffer = yy_scan_string (script2, lexerstate->yyscanner); + grub_free (script2); + } + } + + if (!buffer) + { + yylex_destroy (lexerstate->yyscanner); + grub_free (lexerstate->yyscanner); + + grub_free (lexerstate->text); + grub_free (lexerstate); + return 0; + } + yyset_extra (parser, lexerstate->yyscanner); + + return lexerstate; +} + +void +grub_script_lexer_fini (struct grub_lexer_param *lexerstate) +{ + if (!lexerstate) + return; + + yylex_destroy (lexerstate->yyscanner); + + grub_free (lexerstate->recording); + grub_free (lexerstate->text); + grub_free (lexerstate); +} + +int +grub_script_yylex (union YYSTYPE *value, + struct grub_parser_param *parserstate) +{ + char *str; + int token; + grub_script_arg_type_t type; + struct grub_lexer_param *lexerstate = parserstate->lexerstate; + + value->arg = 0; + if (parserstate->err) + return GRUB_PARSER_TOKEN_BAD; + + if (lexerstate->eof) + return GRUB_PARSER_TOKEN_EOF; + + /* + * Words with environment variables, like foo${bar}baz needs + * multiple tokens to be merged into a single grub_script_arg. We + * use two variables to achieve this: lexerstate->merge_start and + * lexerstate->merge_end + */ + + lexerstate->merge_start = 0; + lexerstate->merge_end = 0; + do + { + /* Empty lexerstate->text. */ + lexerstate->used = 1; + lexerstate->text[0] = '\0'; + + token = yylex (value, lexerstate->yyscanner); + if (token == GRUB_PARSER_TOKEN_BAD) + break; + + /* Merging feature uses lexerstate->text instead of yytext. */ + if (lexerstate->merge_start) + { + str = lexerstate->text; + type = lexerstate->type; } else { - /* There is either text or a variable name. In the case you - arrive here there is a serious problem with the lexer. */ - grub_error (GRUB_ERR_BAD_ARGUMENT, "internal error"); - return 0; + str = yyget_text (lexerstate->yyscanner); + type = GRUB_SCRIPT_ARG_TYPE_TEXT; } - } - - if (yylval->arg == 0) - { - int token = state->tokenonhold; - state->tokenonhold = 0; - return token; - } - - if (yylval->arg->next == 0 && yylval->arg->type == GRUB_SCRIPT_ARG_TYPE_STR) - { - /* Detect some special tokens. */ - if (! grub_strcmp (yylval->arg->str, "while")) - return GRUB_PARSER_TOKEN_WHILE; - else if (! grub_strcmp (yylval->arg->str, "if")) - return GRUB_PARSER_TOKEN_IF; - else if (! grub_strcmp (yylval->arg->str, "function")) - return GRUB_PARSER_TOKEN_FUNCTION; - else if (! grub_strcmp (yylval->arg->str, "menuentry")) - return GRUB_PARSER_TOKEN_MENUENTRY; - else if (! grub_strcmp (yylval->arg->str, "@")) - return GRUB_PARSER_TOKEN_MENUENTRY; - else if (! grub_strcmp (yylval->arg->str, "else")) - return GRUB_PARSER_TOKEN_ELSE; - else if (! grub_strcmp (yylval->arg->str, "then")) - return GRUB_PARSER_TOKEN_THEN; - else if (! grub_strcmp (yylval->arg->str, "fi")) - return GRUB_PARSER_TOKEN_FI; - } - - return GRUB_PARSER_TOKEN_ARG; + grub_dprintf("lexer", "token %u text [%s]\n", token, str); + + value->arg = grub_script_arg_add (parserstate, value->arg, type, str); + } + while (lexerstate->merge_start && !lexerstate->merge_end); + + if (!value->arg || parserstate->err) + return GRUB_PARSER_TOKEN_BAD; + + return token; } void -grub_script_yyerror (struct grub_parser_param *lex __attribute__ ((unused)), - char const *err) +grub_script_yyerror (struct grub_parser_param *state, char const *err) { - grub_printf ("%s\n", err); + if (err) + grub_error (GRUB_ERR_INVALID_COMMAND, err); + + grub_print_error (); + state->err++; } === modified file 'script/parser.y' --- script/parser.y 2009-11-23 15:37:33 +0000 +++ script/parser.y 2010-01-23 04:46:05 +0000 @@ -1,7 +1,7 @@ /* parser.y - The scripting parser. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2008,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2006,2007,2008,2009,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 @@ -21,10 +21,10 @@ #include #include -#define YYFREE grub_free -#define YYMALLOC grub_malloc +#define YYFREE grub_free +#define YYMALLOC grub_malloc #define YYLTYPE_IS_TRIVIAL 0 -#define YYENABLE_NLS 0 +#define YYENABLE_NLS 0 %} @@ -35,163 +35,206 @@ char *string; } -%token GRUB_PARSER_TOKEN_IF "if" -%token GRUB_PARSER_TOKEN_WHILE "while" -%token GRUB_PARSER_TOKEN_FUNCTION "function" -%token GRUB_PARSER_TOKEN_MENUENTRY "menuentry" -%token GRUB_PARSER_TOKEN_ELSE "else" -%token GRUB_PARSER_TOKEN_THEN "then" -%token GRUB_PARSER_TOKEN_FI "fi" -%token GRUB_PARSER_TOKEN_ARG -%type script_init script grubcmd command commands commandblock menuentry if -%type arguments; -%type GRUB_PARSER_TOKEN_ARG; +%token GRUB_PARSER_TOKEN_BAD +%token GRUB_PARSER_TOKEN_EOF 0 "end-of-input" + +%token GRUB_PARSER_TOKEN_NEWLINE "\n" +%token GRUB_PARSER_TOKEN_AND "&&" +%token GRUB_PARSER_TOKEN_OR "||" +%token GRUB_PARSER_TOKEN_SEMI2 ";;" +%token GRUB_PARSER_TOKEN_PIPE "|" +%token GRUB_PARSER_TOKEN_AMP "&" +%token GRUB_PARSER_TOKEN_SEMI ";" +%token GRUB_PARSER_TOKEN_LPAR "(" +%token GRUB_PARSER_TOKEN_RPAR ")" +%token GRUB_PARSER_TOKEN_LBR "{" +%token GRUB_PARSER_TOKEN_RBR "}" +%token GRUB_PARSER_TOKEN_NOT "!" +%token GRUB_PARSER_TOKEN_LSQBR2 "[" +%token GRUB_PARSER_TOKEN_RSQBR2 "]" +%token GRUB_PARSER_TOKEN_LT "<" +%token GRUB_PARSER_TOKEN_GT ">" + +%token GRUB_PARSER_TOKEN_CASE "case" +%token GRUB_PARSER_TOKEN_DO "do" +%token GRUB_PARSER_TOKEN_DONE "done" +%token GRUB_PARSER_TOKEN_ELIF "elif" +%token GRUB_PARSER_TOKEN_ELSE "else" +%token GRUB_PARSER_TOKEN_ESAC "esac" +%token GRUB_PARSER_TOKEN_FI "fi" +%token GRUB_PARSER_TOKEN_FOR "for" +%token GRUB_PARSER_TOKEN_IF "if" +%token GRUB_PARSER_TOKEN_IN "in" +%token GRUB_PARSER_TOKEN_SELECT "select" +%token GRUB_PARSER_TOKEN_THEN "then" +%token GRUB_PARSER_TOKEN_UNTIL "until" +%token GRUB_PARSER_TOKEN_WHILE "while" +%token GRUB_PARSER_TOKEN_TIME "time" +%token GRUB_PARSER_TOKEN_FUNCTION "function" +%token GRUB_PARSER_TOKEN_MENUENTRY "menuentry" +%token GRUB_PARSER_TOKEN_NAME "name" +%token GRUB_PARSER_TOKEN_WORD "word" + +%type word argument arguments0 arguments1 +%type script_init script grubcmd ifcmd command +%type commands1 menuentry statement %pure-parser -%lex-param { struct grub_parser_param *state }; +%lex-param { struct grub_parser_param *state }; %parse-param { struct grub_parser_param *state }; +%start script_init + %% /* It should be possible to do this in a clean way... */ -script_init: { state->err = 0; } script - { - state->parsed = $2; - } -; - -script: { $$ = 0; } - | '\n' { $$ = 0; } - | commands { $$ = $1; } - | function '\n' { $$ = 0; } - | menuentry '\n' { $$ = $1; } - | error - { - $$ = 0; - yyerror (state, "Incorrect command"); - state->err = 1; - yyerrok; - } -; - -delimiter: '\n' - | ';' - | delimiter '\n' -; - -newlines: /* Empty */ - | newlines '\n' -; - - - -arguments: GRUB_PARSER_TOKEN_ARG - { - $$ = grub_script_add_arglist (state, 0, $1); - } - | arguments GRUB_PARSER_TOKEN_ARG - { - $$ = grub_script_add_arglist (state, $1, $2); - } -; - -grubcmd: arguments - { - $$ = grub_script_create_cmdline (state, $1); - } +script_init: { state->err = 0; } script { state->parsed = $2; state->err = 0; } +; + +script: newlines0 + { + $$ = 0; + } + | script statement delimiter + { + struct grub_script_cmdblock *cmdblock; + cmdblock = (struct grub_script_cmdblock *) $1; + $$ = grub_script_add_cmd (state, cmdblock, $2); + } + | error + { + $$ = 0; + yyerror (state, "Incorrect command"); + yyerrok; + } +; + +newlines0: /* Empty */ | newlines1 ; +newlines1: newlines0 "\n" ; + +delimiter: ";" + | "\n" +; +delimiters0: /* Empty */ | delimiters1 ; +delimiters1: delimiter + | delimiters1 "\n" +; + +word: GRUB_PARSER_TOKEN_NAME { $$ = grub_script_add_arglist (state, 0, $1); } + | GRUB_PARSER_TOKEN_WORD { $$ = grub_script_add_arglist (state, 0, $1); } +; + +statement: command { $$ = $1; } + | function { $$ = 0; } + | menuentry { $$ = $1; } + +argument : "case" { $$ = grub_script_add_arglist (state, 0, $1); } + | "do" { $$ = grub_script_add_arglist (state, 0, $1); } + | "done" { $$ = grub_script_add_arglist (state, 0, $1); } + | "elif" { $$ = grub_script_add_arglist (state, 0, $1); } + | "else" { $$ = grub_script_add_arglist (state, 0, $1); } + | "esac" { $$ = grub_script_add_arglist (state, 0, $1); } + | "fi" { $$ = grub_script_add_arglist (state, 0, $1); } + | "for" { $$ = grub_script_add_arglist (state, 0, $1); } + | "if" { $$ = grub_script_add_arglist (state, 0, $1); } + | "in" { $$ = grub_script_add_arglist (state, 0, $1); } + | "select" { $$ = grub_script_add_arglist (state, 0, $1); } + | "then" { $$ = grub_script_add_arglist (state, 0, $1); } + | "until" { $$ = grub_script_add_arglist (state, 0, $1); } + | "while" { $$ = grub_script_add_arglist (state, 0, $1); } + | "function" { $$ = grub_script_add_arglist (state, 0, $1); } + | "menuentry" { $$ = grub_script_add_arglist (state, 0, $1); } + | word { $$ = $1; } +; + +arguments0: /* Empty */ { $$ = 0; } + | arguments1 { $$ = $1; } +; +arguments1: argument arguments0 + { + if ($1 && $2) + { + $1->next = $2; + $1->argcount += $2->argcount; + $2->argcount = 0; + } + $$ = $1; + } +; + +grubcmd: word arguments0 + { + if ($1 && $2) { + $1->next = $2; + $1->argcount += $2->argcount; + $2->argcount = 0; + } + $$ = grub_script_create_cmdline (state, $1); + } ; /* A single command. */ -command: grubcmd delimiter { $$ = $1; } - | if delimiter { $$ = $1; } - | commandblock delimiter { $$ = $1; } -; - -/* A block of commands. */ -commands: command - { - $$ = grub_script_add_cmd (state, 0, $1); - } - | command commands - { - struct grub_script_cmdblock *cmd; - cmd = (struct grub_script_cmdblock *) $2; - $$ = grub_script_add_cmd (state, cmd, $1); - } -; - -/* A function. Carefully save the memory that is allocated. Don't - change any stuff because it might seem like a fun thing to do! - Special care was take to make sure the mid-rule actions are - executed on the right moment. So the `commands' rule should be - recognized after executing the `grub_script_mem_record; and before - `grub_script_mem_record_stop'. */ -function: "function" GRUB_PARSER_TOKEN_ARG - { - grub_script_lexer_ref (state->lexerstate); - } newlines '{' - { - /* The first part of the function was recognized. - Now start recording the memory usage to store - this function. */ - state->func_mem = grub_script_mem_record (state); - } newlines commands '}' - { - struct grub_script *script; - - /* All the memory usage for parsing this function - was recorded. */ - state->func_mem = grub_script_mem_record_stop (state, - state->func_mem); - script = grub_script_create ($8, state->func_mem); - if (script) - grub_script_function_create ($2, script); - grub_script_lexer_deref (state->lexerstate); - } -; - -/* Carefully designed, together with `menuentry' so everything happens - just in the expected order. */ -commandblock: '{' - { - grub_script_lexer_ref (state->lexerstate); - } - newlines commands '}' - { - grub_script_lexer_deref (state->lexerstate); - $$ = $4; - } -; - -/* A menu entry. Carefully save the memory that is allocated. */ -menuentry: "menuentry" - { - grub_script_lexer_ref (state->lexerstate); - } arguments newlines '{' - { - grub_script_lexer_record_start (state->lexerstate); - } newlines commands '}' - { - char *menu_entry; - menu_entry = grub_script_lexer_record_stop (state->lexerstate); - grub_script_lexer_deref (state->lexerstate); - $$ = grub_script_create_cmdmenu (state, $3, menu_entry, 0); - } -; - -/* The first part of the if statement. It's used to switch the lexer - to a state in which it demands more tokens. */ -if_statement: "if" { grub_script_lexer_ref (state->lexerstate); } -; - -/* The if statement. */ -if: if_statement commands "then" newlines commands "fi" - { - $$ = grub_script_create_cmdif (state, $2, $5, 0); - grub_script_lexer_deref (state->lexerstate); - } - | if_statement commands "then" newlines commands "else" newlines commands "fi" - { - $$ = grub_script_create_cmdif (state, $2, $5, $8); - grub_script_lexer_deref (state->lexerstate); - } +command: grubcmd { $$ = $1; } + | ifcmd { $$ = $1; } +; + +/* A list of commands. */ +commands1: newlines0 command + { + $$ = grub_script_add_cmd (state, 0, $2); + } + | commands1 delimiters1 command + { + struct grub_script_cmdblock *cmdblock; + cmdblock = (struct grub_script_cmdblock *) $1; + $$ = grub_script_add_cmd (state, cmdblock, $3); + } +; + +function: "function" "name" + { + grub_script_lexer_ref (state->lexerstate); + state->func_mem = grub_script_mem_record (state); + } + delimiters0 "{" commands1 delimiters1 "}" + { + struct grub_script *script; + state->func_mem = grub_script_mem_record_stop (state, + state->func_mem); + script = grub_script_create ($6, state->func_mem); + if (script) + grub_script_function_create ($2, script); + + grub_script_lexer_deref (state->lexerstate); + } +; + +menuentry: "menuentry" + { + grub_script_lexer_ref (state->lexerstate); + } + arguments1 + { + grub_script_lexer_record_start (state); + } + delimiters0 "{" commands1 delimiters1 "}" + { + char *menu_entry; + menu_entry = grub_script_lexer_record_stop (state); + grub_script_lexer_deref (state->lexerstate); + $$ = grub_script_create_cmdmenu (state, $3, menu_entry, 0); + } +; + +if: "if" { grub_script_lexer_ref (state->lexerstate); } +; +ifcmd: if commands1 delimiters1 "then" commands1 delimiters1 "fi" + { + $$ = grub_script_create_cmdif (state, $2, $5, 0); + grub_script_lexer_deref (state->lexerstate); + } + | if commands1 delimiters1 "then" commands1 delimiters1 "else" commands1 delimiters1 "fi" + { + $$ = grub_script_create_cmdif (state, $2, $5, $8); + grub_script_lexer_deref (state->lexerstate); + } ; === modified file 'script/script.c' --- script/script.c 2009-11-23 15:37:33 +0000 +++ script/script.c 2010-01-22 13:37:27 +0000 @@ -1,7 +1,7 @@ /* script.c -- Functions to create an in memory description of the script. */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2009 Free Software Foundation, Inc. + * Copyright (C) 2005,2006,2007,2009,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 @@ -24,12 +24,10 @@ /* 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 - allocations. The memory is freed in case of an error, or - assigned to the parsed script when parsing was successful. */ - -/* XXX */ - -/* In case of the normal malloc, some additional bytes are allocated + allocations. The memory is freed in case of an error, or assigned + to the parsed script when parsing was successful. + + In case of the normal malloc, some additional bytes are allocated for this datastructure. All reserved memory is stored in a linked list so it can be easily freed. The original memory can be found from &mem. */ @@ -46,6 +44,8 @@ struct grub_script_mem *mem; mem = (struct grub_script_mem *) grub_malloc (size + sizeof (*mem) - sizeof (char)); + if (!mem) + return 0; grub_dprintf ("scripting", "malloc %p\n", mem); mem->next = state->memused; @@ -94,32 +94,40 @@ void grub_script_free (struct grub_script *script) { - if (! script) + if (!script) return; grub_script_mem_free (script->mem); grub_free (script); } - + /* Extend the argument arg with a variable or string of text. If ARG is zero a new list is created. */ struct grub_script_arg * -grub_script_arg_add (struct grub_parser_param *state, struct grub_script_arg *arg, - grub_script_arg_type_t type, char *str) +grub_script_arg_add (struct grub_parser_param *state, + struct grub_script_arg *arg, grub_script_arg_type_t type, + char *str) { struct grub_script_arg *argpart; struct grub_script_arg *ll; int len; - argpart = (struct grub_script_arg *) grub_script_malloc (state, sizeof (*arg)); + argpart = + (struct grub_script_arg *) grub_script_malloc (state, sizeof (*arg)); + if (!argpart) + return arg; + argpart->type = type; len = grub_strlen (str) + 1; argpart->str = grub_script_malloc (state, len); + if (!argpart->str) + return arg; /* argpart is freed later, during grub_script_free. */ + grub_memcpy (argpart->str, str, len); argpart->next = 0; - if (! arg) + if (!arg) return argpart; for (ll = arg; ll->next; ll = ll->next); @@ -132,19 +140,24 @@ is zero, a new list will be created. */ struct grub_script_arglist * grub_script_add_arglist (struct grub_parser_param *state, - struct grub_script_arglist *list, struct grub_script_arg *arg) + struct grub_script_arglist *list, + struct grub_script_arg *arg) { struct grub_script_arglist *link; struct grub_script_arglist *ll; grub_dprintf ("scripting", "arglist\n"); - link = (struct grub_script_arglist *) grub_script_malloc (state, sizeof (*link)); + link = + (struct grub_script_arglist *) grub_script_malloc (state, sizeof (*link)); + if (!link) + return list; + link->next = 0; link->arg = arg; link->argcount = 0; - if (! list) + if (!list) { link->argcount++; return link; @@ -171,6 +184,9 @@ grub_dprintf ("scripting", "cmdline\n"); cmd = grub_script_malloc (state, sizeof (*cmd)); + if (!cmd) + return 0; + cmd->cmd.exec = grub_script_execute_cmdline; cmd->cmd.next = 0; cmd->arglist = arglist; @@ -193,6 +209,9 @@ grub_dprintf ("scripting", "cmdif\n"); cmd = grub_script_malloc (state, sizeof (*cmd)); + if (!cmd) + return 0; + cmd->cmd.exec = grub_script_execute_cmdif; cmd->cmd.next = 0; cmd->exec_to_evaluate = exec_to_evaluate; @@ -209,30 +228,16 @@ struct grub_script_cmd * grub_script_create_cmdmenu (struct grub_parser_param *state, struct grub_script_arglist *arglist, - char *sourcecode, - int options) + char *sourcecode, int options) { struct grub_script_cmd_menuentry *cmd; - int i; - - /* Skip leading newlines to make the sourcecode better readable when - using the editor. */ - while (*sourcecode == '\n') - sourcecode++; - - /* Having trailing returns can some some annoying conflicts, remove - them. XXX: Can the parser be improved to handle this? */ - for (i = grub_strlen (sourcecode) - 1; i > 0; i--) - { - if (sourcecode[i] != '\n') - break; - sourcecode[i] = '\0'; - } cmd = grub_script_malloc (state, sizeof (*cmd)); + if (!cmd) + return 0; + cmd->cmd.exec = grub_script_execute_menuentry; cmd->cmd.next = 0; - /* XXX: Check if this memory is properly freed. */ cmd->sourcecode = sourcecode; cmd->arglist = arglist; cmd->options = options; @@ -248,15 +253,19 @@ struct grub_script_cmdblock *cmdblock, struct grub_script_cmd *cmd) { + struct grub_script_cmd *ptr; + grub_dprintf ("scripting", "cmdblock\n"); - if (! cmd) + if (!cmd) return (struct grub_script_cmd *) cmdblock; - if (! cmdblock) + if (!cmdblock) { - cmdblock = (struct grub_script_cmdblock *) grub_script_malloc (state, - sizeof (*cmdblock)); + cmdblock = grub_script_malloc (state, sizeof (*cmdblock)); + if (!cmdblock) + return 0; + cmdblock->cmd.exec = grub_script_execute_cmdblock; cmdblock->cmd.next = 0; cmdblock->cmdlist = cmd; @@ -264,22 +273,29 @@ } else { - cmd->next = cmdblock->cmdlist; - cmdblock->cmdlist = cmd; + if (!cmdblock->cmdlist) + cmdblock->cmdlist = cmd; + else + { + ptr = cmdblock->cmdlist; + while (ptr->next) + ptr = ptr->next; + ptr->next = cmd; + } } return (struct grub_script_cmd *) cmdblock; } - + 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)); - if (! parsed) + if (!parsed) { grub_script_mem_free (mem); grub_free (cmd); @@ -304,16 +320,16 @@ struct grub_parser_param *parsestate; parsed = grub_malloc (sizeof (*parsed)); - if (! parsed) + if (!parsed) return 0; parsestate = grub_zalloc (sizeof (*parsestate)); - if (! parsestate) + if (!parsestate) return 0; /* Initialize the lexer. */ - lexstate = grub_script_lexer_init (script, getline); - if (! lexstate) + lexstate = grub_script_lexer_init (parsestate, script, getline); + if (!lexstate) { grub_free (parsed); grub_free (parsestate); @@ -330,7 +346,7 @@ struct grub_script_mem *memfree; memfree = grub_script_mem_record_stop (parsestate, membackup); grub_script_mem_free (memfree); - grub_free (lexstate); + grub_script_lexer_fini (lexstate); grub_free (parsestate); return 0; } @@ -338,7 +354,7 @@ parsed->mem = grub_script_mem_record_stop (parsestate, membackup); parsed->cmd = parsestate->parsed; - grub_free (lexstate); + grub_script_lexer_fini (lexstate); grub_free (parsestate); return parsed; === added file 'script/yylex.l' --- script/yylex.l 1970-01-01 00:00:00 +0000 +++ script/yylex.l 2010-01-23 04:44:32 +0000 @@ -0,0 +1,342 @@ +%{ +/* yylex.l The scripting lexer. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009,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 . + */ + +#include +#include +#include +#include +#include "grub_script.tab.h" + +#define yyfree grub_lexer_yyfree +#define yyalloc grub_lexer_yyalloc +#define yyrealloc grub_lexer_yyrealloc + +/* + * As we don't have access to yyscanner, we cannot do much except to + * print the fatal error. + */ +#define YY_FATAL_ERROR(msg) \ + do { \ + grub_printf ("fatal error: %s\n", msg); \ + } while (0) + +#define COPY(str, hint) \ + do { \ + copy_string (yyextra, str, hint); \ + } while (0) + + +#define RECORD \ + do { \ + grub_script_lexer_record (yyextra, yytext); \ + } while (0) + +#define ARG(t) \ + do { \ + yyextra->lexerstate->type = t; \ + return GRUB_PARSER_TOKEN_WORD; \ + } while (0) + +/* We don't need YY_INPUT, as we rely on yy_scan_strings */ +#define YY_INPUT(buf,res,max) do { res = 0; } while (0) + +/* forward declarations */ +static void grub_lexer_yyfree (void *, yyscan_t yyscanner); +static void* grub_lexer_yyalloc (yy_size_t, yyscan_t yyscanner); +static void* grub_lexer_yyrealloc (void*, yy_size_t, yyscan_t yyscanner); +static void copy_string (struct grub_parser_param *, const char *, + unsigned hint); + +%} + +%top{ + +/* + * Some flex hacks for -nostdinc; XXX We need to fix these when libc + * support becomes availble in GRUB. + */ + +#include + +typedef grub_size_t size_t; +typedef grub_size_t yy_size_t; +#define YY_TYPEDEF_YY_SIZE_T 1 + +#define FILE void +#define stdin 0 +#define stdout 0 +#define EOF 0 + +#define errno grub_errno +#define EINVAL GRUB_ERR_BAD_NUMBER +#define ENOMEM GRUB_ERR_OUT_OF_MEMORY + +#define strlen grub_strlen +#define memset grub_memset + +#define fprintf(...) 0 +#define exit(...) + +#pragma GCC diagnostic warning "-Wunused-variable" +#pragma GCC diagnostic warning "-Wunused-function" +#pragma GCC diagnostic warning "-Wunused-parameter" +#pragma GCC diagnostic warning "-Wstrict-prototypes" +#pragma GCC diagnostic warning "-Wmissing-prototypes" + +} + +%option ecs +%option meta-ecs + +%option warn +%option array +%option stack +%option reentrant +%option bison-bridge +%option never-interactive + +%option noyyfree noyyalloc noyyrealloc +%option nounistd nostdinit nodefault noyylineno noyywrap + +/* Reduce lexer size, by not defining these. */ +%option noyy_top_state +%option noinput nounput +%option noyyget_in noyyset_in +%option noyyget_out noyyset_out +%option noyyget_debug noyyset_debug +%option noyyget_lineno noyyset_lineno + +%option extra-type="struct grub_parser_param*" + +BLANK [ \t] +COMMENT ^[ \t]*#.*$ + +CHAR [^|&$;()<> \t\n\'\"\\] +DIGITS [[:digit:]]+ +NAME [[:alpha:]_][[:alnum:][:digit:]_]* + +ESC \\. +VARIABLE ${NAME}|$\{{NAME}\}|${DIGITS}|$\{{DIGITS}\}|$\?|$\{\?\} +DQSTR \"([^\\\"]|{ESC})*\" +SQSTR \'[^\']*\' +WORD ({CHAR}|{DQSTR}|{SQSTR}|{ESC}|{VARIABLE})+ + +%x SPLIT +%x DQUOTE +%x SQUOTE +%x VAR + +%% + + /* White spaces */ +{BLANK}+ { RECORD; } +{COMMENT} { RECORD; } + + /* Special symbols */ +"\n" { RECORD; return GRUB_PARSER_TOKEN_NEWLINE; } +"||" { RECORD; return GRUB_PARSER_TOKEN_OR; } +"&&" { RECORD; return GRUB_PARSER_TOKEN_AND; } +";;" { RECORD; return GRUB_PARSER_TOKEN_SEMI2; } +"|" { RECORD; return GRUB_PARSER_TOKEN_PIPE; } +"&" { RECORD; return GRUB_PARSER_TOKEN_AMP; } +";" { RECORD; return GRUB_PARSER_TOKEN_SEMI; } +"(" { RECORD; return GRUB_PARSER_TOKEN_LPAR; } +")" { RECORD; return GRUB_PARSER_TOKEN_RPAR; } +"<" { RECORD; return GRUB_PARSER_TOKEN_LT; } +">" { RECORD; return GRUB_PARSER_TOKEN_GT; } + + /* Reserved words */ +"!" { RECORD; return GRUB_PARSER_TOKEN_NOT; } +"{" { RECORD; return GRUB_PARSER_TOKEN_LBR; } +"}" { RECORD; return GRUB_PARSER_TOKEN_RBR; } +"[[" { RECORD; return GRUB_PARSER_TOKEN_RSQBR2; } +"]]" { RECORD; return GRUB_PARSER_TOKEN_LSQBR2; } +"time" { RECORD; return GRUB_PARSER_TOKEN_TIME; } +"case" { RECORD; return GRUB_PARSER_TOKEN_CASE; } +"do" { RECORD; return GRUB_PARSER_TOKEN_DO; } +"done" { RECORD; return GRUB_PARSER_TOKEN_DONE; } +"elif" { RECORD; return GRUB_PARSER_TOKEN_ELIF; } +"else" { RECORD; return GRUB_PARSER_TOKEN_ELSE; } +"esac" { RECORD; return GRUB_PARSER_TOKEN_ESAC; } +"fi" { RECORD; return GRUB_PARSER_TOKEN_FI; } +"for" { RECORD; return GRUB_PARSER_TOKEN_FOR; } +"if" { RECORD; return GRUB_PARSER_TOKEN_IF; } +"in" { RECORD; return GRUB_PARSER_TOKEN_IN; } +"select" { RECORD; return GRUB_PARSER_TOKEN_SELECT; } +"then" { RECORD; return GRUB_PARSER_TOKEN_THEN; } +"until" { RECORD; return GRUB_PARSER_TOKEN_UNTIL; } +"while" { RECORD; return GRUB_PARSER_TOKEN_WHILE; } +"function" { RECORD; return GRUB_PARSER_TOKEN_FUNCTION; } +"menuentry" { RECORD; return GRUB_PARSER_TOKEN_MENUENTRY; } + +{NAME} { RECORD; return GRUB_PARSER_TOKEN_NAME; } +{WORD} { + RECORD; + /* resplit yytext */ + grub_dprintf ("lexer", "word: [%s]\n", yytext); + yypush_buffer_state (YY_CURRENT_BUFFER, yyscanner); + if (yy_scan_string (yytext, yyscanner)) + { + yyextra->lexerstate->merge_start = 1; + yy_push_state (SPLIT, yyscanner); + } + else + { + grub_script_yyerror (yyextra, 0); + yypop_buffer_state (yyscanner); + return GRUB_PARSER_TOKEN_WORD; + } + } + +.|\n { + grub_script_yyerror (yyextra, "unrecognized token"); + return GRUB_PARSER_TOKEN_BAD; + } + + /* Split word into multiple args */ + +{ + \\. { COPY (yytext + 1, yyleng - 1); } + \" { + yy_push_state (DQUOTE, yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); + } + \' { + yy_push_state (SQUOTE, yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); + } + \$ { + yy_push_state (VAR, yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); + } + \\ | + [^\"\'$\\]+ { COPY (yytext, yyleng); } + <> { + yy_pop_state (yyscanner); + yypop_buffer_state (yyscanner); + yyextra->lexerstate->merge_end = 1; + ARG (GRUB_SCRIPT_ARG_TYPE_TEXT); + } +} + +{ + \? | + {DIGITS} | + {NAME} { + COPY (yytext, yyleng); + yy_pop_state (yyscanner); + if (YY_START == SPLIT) + ARG (GRUB_SCRIPT_ARG_TYPE_VAR); + else + ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); + } + \{\?\} | + \{{DIGITS}\} | + \{{NAME}\} { + yytext[yyleng - 1] = '\0'; + COPY (yytext + 1, yyleng - 2); + yy_pop_state (yyscanner); + if (YY_START == SPLIT) + ARG (GRUB_SCRIPT_ARG_TYPE_VAR); + else + ARG (GRUB_SCRIPT_ARG_TYPE_DQVAR); + } + .|\n { return GRUB_PARSER_TOKEN_BAD; } +} + +{ + \' { + yy_pop_state (yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_SQSTR); + } + [^\']+ { COPY (yytext, yyleng); } +} + +{ + \" { + yy_pop_state (yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR); + } + \$ { + yy_push_state (VAR, yyscanner); + ARG (GRUB_SCRIPT_ARG_TYPE_DQSTR); + } + \\\\ { COPY ("\\", 1); } + \\\" { COPY ("\"", 1); } + \\\n { /* ignore */ } + [^\"$\\\n]+ { COPY (yytext, yyleng); } + (.|\n) { COPY (yytext, yyleng); } +} + +<> { + yypop_buffer_state (yyscanner); + if (! grub_script_lexer_yywrap (yyextra)) + { + yyextra->lexerstate->eof = 1; + return GRUB_PARSER_TOKEN_EOF; + } + } + +%% + +static void +grub_lexer_yyfree (void *ptr, yyscan_t yyscanner __attribute__ ((unused))) +{ + grub_free(ptr); +} + +static void* +grub_lexer_yyalloc (yy_size_t size, yyscan_t yyscanner __attribute__ ((unused))) +{ + return grub_malloc (size); +} + +static void* +grub_lexer_yyrealloc (void *ptr, yy_size_t size, + yyscan_t yyscanner __attribute__ ((unused))) +{ + return grub_realloc (ptr, size); +} + +#define MAX(a,b) ((a) < (b) ? (b) : (a)) + +static void copy_string (struct grub_parser_param *parser, const char *str, unsigned hint) +{ + int size; + char *ptr; + unsigned len; + + len = hint ? hint : grub_strlen (str); + if (parser->lexerstate->used + len >= parser->lexerstate->size) + { + size = MAX (len, parser->lexerstate->size) * 2; + ptr = grub_realloc (parser->lexerstate->text, size); + if (!ptr) + { + grub_script_yyerror (parser, 0); + return; + } + + parser->lexerstate->text = ptr; + parser->lexerstate->size = size; + } + grub_strcpy (parser->lexerstate->text + parser->lexerstate->used - 1, str); + parser->lexerstate->used += len; +} === added file 'tests/grub_script_echo1.in' --- tests/grub_script_echo1.in 1970-01-01 00:00:00 +0000 +++ tests/grub_script_echo1.in 2010-01-22 13:37:27 +0000 @@ -0,0 +1,32 @@ +#! @builddir@/grub-shell-tester + +# 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 . + +foo=bar +echo $foo ${foo} +echo "$foo" "${foo}" +echo '$foo' '${foo}' +echo a$foob a${foo}b +echo ab"cd"ef$foo'gh'ij${foo}kl\ mn\"op\'qr\$st\(uv\yz\) + +foo=c +bar=h +echo e"$foo"${bar}o +e"$foo"${bar}o hello world + +foo=echo +$foo 1234 === added file 'tests/grub_script_echo_keywords.in' --- tests/grub_script_echo_keywords.in 1970-01-01 00:00:00 +0000 +++ tests/grub_script_echo_keywords.in 2010-01-22 13:37:27 +0000 @@ -0,0 +1,3 @@ +#! @builddir@/grub-shell-tester + +echo if then else fi for do done === added file 'tests/grub_script_vars1.in' --- tests/grub_script_vars1.in 1970-01-01 00:00:00 +0000 +++ tests/grub_script_vars1.in 2010-01-22 13:37:27 +0000 @@ -0,0 +1,34 @@ +#! @builddir@/grub-shell-tester + +# 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 . + +var=foo +echo $var +echo "$var" +echo ${var} +echo "${var}" + +echo $1 $2 $? + +foo=foo +echo "" $foo + +echo $bar $foo + +bar="" +echo $bar $foo + === modified file 'tests/util/grub-shell-tester.in' --- tests/util/grub-shell-tester.in 2010-01-12 03:30:55 +0000 +++ tests/util/grub-shell-tester.in 2010-01-22 13:37:27 +0000 @@ -1,7 +1,7 @@ #! /bin/bash -e # Compares GRUB script output with BASH output. -# Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. +# Copyright (C) 2009,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 === modified file 'tests/util/grub-shell.in' --- tests/util/grub-shell.in 2010-01-14 13:09:12 +0000 +++ tests/util/grub-shell.in 2010-01-22 13:37:27 +0000 @@ -1,7 +1,7 @@ #! /bin/bash -e # Run GRUB script in a Qemu instance -# Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. +# Copyright (C) 2009,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 === modified file 'util/grub-script-check.c' --- util/grub-script-check.c 2010-01-18 11:28:31 +0000 +++ util/grub-script-check.c 2010-01-22 13:37:27 +0000 @@ -1,7 +1,7 @@ /* grub-script-check.c - check grub script file for syntax errors */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc. + * Copyright (C) 2009,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 @@ -82,16 +82,8 @@ } grub_err_t -grub_script_execute_menuentry (struct grub_script_cmd *cmd) +grub_script_execute_menuentry (struct grub_script_cmd *cmd __attribute__ ((unused))) { - struct grub_script_cmd_menuentry *menu; - menu = (struct grub_script_cmd_menuentry *)cmd; - - if (menu->sourcecode) - { - grub_free (menu->sourcecode); - menu->sourcecode = 0; - } return 0; } # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWSpztYIAO2J/gH/1dwV///// /+///7////5gTR3oOueVQ19a53cnYPnWncb3aed3s2e7x9SHd3fPntd7JvuClVX29WXN7b7o++Tv p8fd6W6u683ueTZvu+ytPvffZ6SIpbffe6l61tLSh3YGruvvA6Ae2eh0A3MFd3x3veL75CDd333n vL7t3vGez32aPvVnR9Kt3ya69vpncvvvfPd986unx9874+r7q+7z5vc3zm2HzBa1X3fC8PRPMGl2 aPoOve2qHpzrHTLQ92BrpxBXRzsZMpTe+jd52zrQNfQUO9c+56cEkQmgRoaZAQ00IMpk0xMTKYU/ TU1P00FP0UG0gHqfqn6U9TIephKEBoJoIQUxExNQx6ppoyM01PUAAZDQAAAAASmmiEQ1NJ6KnptI nqnpgnlJ6htR5TymmTIDIGgGgDR6g0GjQSaUQmk0RiJpgQxGSanjU2SniT0h6Tyj1NNNHqD0hoae p6gaA0ESiaE0EaNEyYJpo0aT1NomCTwgBGamqep+lP0p+qeKPSP0SfqjRsQSJAgE0IYmmpqemhkm kw0jKaA0aaaaaPIagGmmgANDTt8f5ywnmGGFEk/qEkJKMIoAxVYCBCAsiIsRT+/mOie3c/Kz/n+W FbUhhExQFhATCjSCDBWKoqqgKIxJGJGJEiyMDcoxkhM4rKYwLMIWIoRZYwpChYM/ByfhdzgKcJg3 sRrWBxpt8GZMRXWMnE+RzkzNmK6GUxtnJiel5sFltn3H4ZAOLIxVjOvPMLtDb+p4qTlNMf2Fz/ez SRloB9OETUW/zg9qcr/f99kIvmmSTg8Hi/iQygqqKIky1I21algxf4v1KyxiPot/x8dhR/dZpa9/ +5kW+XNDn2whW3z/rAgZGI2Z1Cz0fP7o2cd7N6vU7b9m0j0TIUR9UGgSFCWerig59eXHT1WBtBLT lP8aPF0mjzoYcd7HthMX15g+NzzxgVFeCaqpEL30Norz1B8SPyLMHmUZGl5gxzw6ehy26emORoCK 2RaELoPEiUvD03HTjr5N/JtjLAwe3V/8jv6qPLUmz2qKHKk/6LkW9r/w+M3rGy7/a6+zlwu+2ZUu Fmm3CLx0y0U7tNNU3ITjQHwTW/M8Wvvh9pe+/ryh+pIc1dQmDB22u0qUXEFoaKmfeTYqkqdrIPWI 2whAJAp215r7cps8iZwqZKTlsOTPHeTogEsY+pzrZTCwgm8Kytmr6Gw7jqOR8eWVQaEVnMQDoRMy kfJVYomL9N81dCctKDvaiqi8bwEneTLv0tBx3nBrbatJkmGMfku995uJFQ1CF8Ud/d8eZ69J4gZ0 aG0em8OGJ2frtdo0xxECkUqUi81cNhacFEO5i15wNE1mbSwqdK6TfZFNwph4kmB8UMab6ZHKseFH o4TxZyzRYrrbFV3bbLU6Upb4ZoVIQeCDWpw7keWvAFwVKOIW9bK0m09i52fT+xsOXuVrMGe5AzCR uiF7GUuMh18jncDQNabaCBI0GgLd2+o8z6eznlMzck4QiuZ4ZNR3VvfweEpVfP14Mk34g9WaPDsg 7sDDSdD/q36uFNzzBHL5bDCAnV0/54+0xToSHn7fkgbq5h6+3cedlqSuFEpA17bkECCLfqyempsW bp4H7pjnu/XnWc7Z0szUmC5frrBm3rC+Q9J3V8V/Kn6q7I+v+H2ZPf05sQCdtAFJ2mSp2UDqtgVn btkMshWGWVAyyE6GSTCYZhO7njibZshJg8po62jnaEYGb+fjo2ZdbJgzpwug7QdmdxKSNPBt+rfh 6LJj0ZaZ8KFtty6SizdUV09pzJV+ice4qwtGaTu6vCsEgVs0nay7JKm4xG0B1e+zdlLcMm4qJMmM PZCbT31nNlvObu3VF7yOrVRNIanC33uneaKUq1m8m3fXo15N3yAcQOdgNgKlYW0tmBUpRVvpW8Bf k1cokER5gBQODdaBaAiiE8J/Po6Lh/j/U+UM5dRIEi9cRKRAIEV06aVIhCLZc359X+mjUY4SbzfA O5qc7/e/c83Hjwn47KCDBGAKRGIwiSSQISBnXVR0RiF/XUtajYPz3dWnT/x4a6oN+QfH6Ght1HQF HZHcsCAjZHJRHyaV9pIlYVuT88t1qB1F9B38rFVu66Zq1+coJ6/kvHZBuE/wFAV3oBCejTGnf52i b23Wp5+al3YcR/fVTWctSgql1DhsTMkGpn/4kktkB/ZnHqXwNWA4RDEP1cZwzQ/K6GGEM3aYkEtg HUwMREY/LQo6ZofHkoKSRjCKSasKxYpBYCqCyKIk0S4xTEKhSKRKHZq46tz0QN4rRf6fTJIaXCfK n7cPonJpx3r7SoTnEDJufvIh2j8csWWKBA995M8BC/cNKpBkdJ9SU85IJRQ6OeZ9GVnDNq+m+W7R lIqBBZFaV8aMq4R/aFaVn61Q7REuMp/Y2P+4bV5ZkYS0aDA2VOBipUgoD9fvEDQSmGA+Ey4q4lZT OopdxrPYeA9xaFhP9gpyFnc/CFE2Jacc26R6D7ZmWI5kBJKVDVHkSeHw1mnMDW8a/QqaqO6d/eN8 XvjSt7uZnB977uNb9jnvsfyWQGI3iojUdxexjm2htzv2BpvMPp+/9PTbN6t1GZGKM8yhVC8UEz0h 8at9Md0EIGFDSwnUY7MMYxYaLzyRxPfS4j0Y0DfhNDVs9SVcznOU2Dc5znMDHnrIyoUBmmlJokml wZ6PfFdvdwNDCU2QmSoNmRwoqa/Qla3ppfO+4rhJ1GKNuEigZXntVCGQnwYuxNOUPLbP5FQ01v+j QIHmECIBJIyLIyIsigsgDAVAUFgoxJBYRBgCMFkUUPo9Mknr8WRIpOhhRQF1C9LAkHZ8bj+xxez2 eyLt9lEyDXEvs+xmeVaDChBcg+195MW5wQQQmVuzwuDFmWEBNUhVcMnnHQoyjooyPDpH21nPcrN7 zVYpLdHLEzqeSjk5jO87XEjUVNynq6doytlpAtpMqMCJUm1FKa0tlS3744UcKDzNqAzAF536cirb 45S5xPZyMJC1L328wS2XrXLRlIwpVVy05ynFCC1i3RQkazJl6vFVtjoLfEpRt2+Bxaxvs91pY3mB JLxpCS3JNKjHz8qbIiVpe1AGR3hC7UuK1OUo0FUdXmwqqtR2Rk2v60l3BbKUuzruuJTxem1CC7Yb 2Vrkp2mIuIk2LV1MRcAhRjE1UlQq2RMG+6JBZgMbIecZzeJjW7cWaxmtjFF1pZW631O1XQtiw1m8 1sG0SE7YBhlEJBS56JNawDCVMcpaJuTbU2uSWMLFXykWJJUon8GrSm3xIFmXQkHF/IgcRT2fUk51 fcMwPEbvjMT9p9zGV95anyTpb3R7reUXyNYkW+emFw84nOM8m2+hnJVhLeBdOZEFJt9kGl9kDSCE JMFNBCuzdbkdAlwc2h8dqqELROHb4jz1BrOBZl/AHn9rehmF8x/q/a9O73z2aWroNF5K6uvsKxLz 7hLrh+UhDEsxZqdjdcS5WwMedINKkISGd0v3/pf44Ki6QUnxKqqwVVWKsBXnskrBVVX6rOOqqutr +uAh2gnR5oGk4hCxBXQmVG2kf8LgusVqU++g3kygIMobqqgSGZM4eng0tsFcGkqT1wxv9t58jyuO GMLT2TJ3qkGDjEvLpLTqwkkuB/4QumLWRC+PdJ/mexsLbB/Yz1/B1px8MY7C85vxjN8YQpr97c7R Yr33sZdlePIZXPt/FWGHWiqlu0w2sde1LUq9Qf6BYkO5OyKt1tl+BXUxPr5346PZIgB/ZE5NQoc2 +OmOZUdff1fHbsx5/ImM49+eXNBy2HqWufD7eZx66am3Xck3quKMVlsugD4Zl91yySQZSdc1Dn68 q22rZ/YlEcHrNeV5P42RECGvQbiKi8Gzfdj9yCRaiB56lJFUavOEIdcS4ZuBRH7kGCCeS9U4gxZA XXOyLvAwF9U7DJGVgMQQiQTeysiKTCiheSvTOM9VUHG7I+MhtZmElY5EKWH7MUZ6G3pH4YMlvtWV m9F5lsUAuOPyTxnfwvUdvOViU9jVa7TyP2LtTYkNlx9P1rk1hbLPRX+4ou6ZZY/gVkMztNo4E7VV XNNe2RFGnTSS3s4OrNMsJOYu2TiZJZThfbjj4+VeRsyeN3t52KT/N5qTW42l+nuZOvxJIK4LRz/X l1rTla1LT7NxVUq6tEdvGV0WqqUJt3GiQcm8lWvUoUbpqjc+KbFeRNpI4JIK5E7XyW9m3sdD8Noo aqDYeeObDYtcV1Ofw6GE5x5jpAARc0qm4AWXiT2V9Rvr+TeYLtZ3Du9WtVdz81w+eRTrbHv9dZfI lCatTM0Jbgc17NVD7q6iRJKohBKv5Yb6dceVrL1eanOrbfZTZ3iSC4V/wlEHhrYXCcXV8Ofd57Kt t54tsu/ml8Waqe0cg1MYPGZJ2RzvAI52xugIpR77rAT9KY3OAwaL1tuzMcPEDAtJfVcrYSxXTnps 6uqBPPx5aVJXBAL9BGzw0b4ToSbVJRiiYNDYmYziS2/BQKB6oDTZMbPq+7Hp+yCLb85osdN/Ed/Q O8QizVhRhEi9Xkn6zHONkixQZhJhvByZLdXhlz8OkywNUb83M2Lbm6K3ojRS10n3vxh+7lu+P38f dj7/T6Y3ueXs6trheOWOl72K+PE4u9cCklebPI2tXTO9rs19O7Tp1Y04aqrLm1ev6xvmf64eb7vR R9kPtoej7nlOcplNAwwzq6X+4vuXwXJkwpDFrXgK0lQYbMxIKHw8vyEPbDHjTypVQbezFNzlvXic +BGpjH0GChlFUnXA/s7Hu1eCSp0ofNjy4ga/i1xrdW/jF8FvGlXP5062n+uu8odTpF5GIuZ2W3bP 8/8H3vte8X2i/UGVq3T+qIIiPudLtD5T1VoGCMkCCIwwh/id+7p6Jbd/TvvvzxY4+LY3JSZDiCaE Kk5y7WIPzznSInlnObalbmE4ODVz+m1UzARgGbhrXQogkawtqgA73bAttEzhh507ht3JRBOcisUc icAkfQxCP/gaIvPsBBawv7xgj6rv/ifqzNJ0CEKqAVrAr8j9Px+L+KzJOzmZ7/Trvthv96pdMcP8 3Xf+TOiV9rQRKMHULaTPcjQmYO4On0I+Pj5q9gfd9ediTWM3yqC090ZAlBJl6aVC8iiYEd6AfMRM YnrIqVuopjAxCK8JFDmID+WR92cuPaYUUfX/Og6C6gMWEAhBYCmFPf/3uVPRG6K6SAJ+EZADsGSy U/6ECAMs7CBfn5vmx5fJHSfSefled/3P/b8v7+qolm1lD+ZmFybfJMitH+e6miI5RW7fihCY9HX9 6Mz4qNO8qfaJ1XFpOouv66wKwavgctFP1PwHpiapVjpNoH0Fv0FLwKKf8LZd012bP4grS7zFUC1e fOscxmgocneK65PnZyDs6xWCqHCkNuiL+KxqtJzA5zFe/b9WW328YF9LZ9v1XRrXr0727PhTPoue Lie1Se6o/RFBVSUUcSNN93E5Ssyekt81X18Shn0rSIrmVbJVHjJkLJ8e+xq+MjrLOszlG/vVwsWW j79dhxwHYS5RIjW3iw14STxXWd58p0PLTF4r2LxEKyVttO5OqEsAesvtDrg3H+j3g7ByLiMCIoQU BTkPogOglzkTAYJWLe1t5tDUQDZkkOk00DHFJxSofAllaxkFBYYQKrBgEGsP3S4i3SRwwO/Cojyv d2GnTSkde/eqyEylCibyfLtZ9al59zhxHMQebgh959eJbLqaok/VKKEVnJNm6cekE7lOOMy/sjZh mLiCg5avhl0ZURItE6uN450fx+zfJUuracBOFq/qrF+3r1pifFpiXs23maJTibW/mfzjZGeL5LUY 3+QPcNv6hFDf4vF/DpEjUmON/7/bSFJGEYhrWthcolA6RhDB5AgdPMUHiQKA/IBBMAuOx88obHE7 oYFhDAGDYT3jIEgYOQgeApYUIgkGIPfeThd4NvdO+GFxNtonFHigtB7xISMIlUJ1cDBu9zSyRZII TGCUAUA9mHXk1NmEflb0PgWJpQRtO8MUGKCd+sO8SS6sN6RwEsAQjcgtWXc2y0xurK0FiRwANfIY IGR3Bw4oL9LyBbpgDEQRoY9pxu6STGvmGfvyiiu5lQ0oVIiVDXi/ClR3eUB/OYnT/F32WWfapFa4 +88eg6PDdN6JTK3j1w+FXU/p0Eweg+r8p6oqNmzRsH54x3WBMsQ+i/qwkcsZPAPDtopaibEBhqWA qXFwlxAOx5ERT8W5UEyF3tCFKAw4xdl7fQCPEqx0xYyQpeAg6LXDfqsuJHrvdp72VGQTIGTKlR0l OlXEkjNJglzPxdF8yBGEMVB+Zn2JhCpYh96VBxcfjrn5g/RMgQ6ZPg9PV4PZgS2y2y220tttLbbc GXPZjz7KTJ/uh0/Ur8aNtNtWBhYd/l3/NQDvWniWTu9dcJQ+opFcds5j2q6VwWtlOfKb8yPe9dq7 F8PwlKVSCvqqVA0URxxhAQOvCgl0UyiFYYRcokrMa2Q0TdDDUJqhonlTVCurJxYBzJIsNU4pA4sk qVN3dCFiEnMbXzsgep8/wgXeoBjabExgiuABjIakZNgDQwCAlzAl1QwOMiYFTuKggJMeJXMUIhY8 BLk89ARHMrnhQgKsgi3Z9WHu+vMBZv2Ga6EmjYiGOPDrjO1wFZJKPUcKZsblcqVQLjCFrkfQiCZF EGyS3uURyiq0+SBiZogiCb4Ni5IulKXXExGiIQ2GDaN5MczmPa46IHeTcZ0swy104xgi4uUEEHy7 4gqZSROIyCohUdEJIo5k2KyJ4qk3EIwONKoKYQxKpcYFTaM+sxOBvQXBQ30R/ChgwaHc6Tc66vDo Y3TexeF+xcoVO5Q2GPsCIiSLQMFClE8FCBuSOiR8rGTRIoOayNGKBYIt1CVESRlJmNsjRVUg7EHq WkRwBCO8rxLU7UuyOvPXXZ0W1tscxktuCCrV7XBoAljBJN87XB8iLDV0GZak1ZswDSB9cuvc2xA6 GuFNYxyvG9moarD1xcCp8ikQPpxGwcpAup6IuS0aTQDbSwgMsdjaadahfYFBp0QSkmkgFlnJHTbE xi1HbYQovBCHBekNKQORAkep4OCuO6Bs4tya7iIIRO5kgmp99qXJ6wL6BqTmg2oNSRuL8bE2kZjU +hoUJiSFVtiEqqynuqQVEDOm9BFcDokSN2GsjbNRZ8RjFOJb87Rm13G6m23flcxxy9HpW8nhd6Nb PCA7liM6ljcpt4NAghMQkGZsMcskBEQHCe0iBgmXtbUEHzoN2Gswoao3nX1yS13LYWMMKaJCwxQJ aK9oM34Pog4oOBXLv2Z9d8nVVo5N6rsOBIkcIWJiQ0FTYbWcjmgvsmx6bKyUQIp3FYUTusrLxubM PYWT1GVE0KDmWqLwUPB4KFydxrqVEPEM4lDerIBly0IN2IS+F9GYj3pxJgCSOYY4HZORZ9iHsQIw nI5U9oZiCp6GkxoYutJBAHg1xrx2GwyLoO8mcDsSmL5+h3awwrEtxS5IsVgIkzg6FHFIl4Ci+YTs FiJ3zQno0WIGxWRY9EQ+wfXRDug5ijnjudm2c6Z3JddxCUx7mGrmMDAxizn1QCUH0/bPTxG+3LW6 svfn0rFGcEmU5befH175o69eUHCY7+eI75y2wQh0RCEGQiiFi1RFIcOm8y056qP8nWQbQKkSGtRT bOa4S48llQHYYRBYbS8qkg2lyB5lauTODZWUCLjLqx0wpz6Z5YCSCCGug9XjFABvGcIDqMTMZxKE EqbKRiIiIVUAVqDTH+rORKZLRzCbt0sOjk5NyRYqtN9ZFwhLMghNDBrEmCETMSZR7jRKJY4aZdQj MZSYI8ChoYvQ5OollutlJruREBj8SuLo6tAwY1B1zgg2hCMtEoCoC3DLMtBx2kSK0k5ErxJLXORW gisNSSN3jt3nCvS1nzm0UXMdyPE0a8AggyWs0iIeRzSMTUvJQMgZwqd+gIWF4hLnYHh3dD0T3GJt GeC5xjM5kDwKR5Oih7BSRCxIkKUueaDERyRcYFq2OvkaZFxcdq9rEfQcUsWjjfvesdue8qB0Rajk 240iA6JIbyxQHW8jvGhteEvaXeNbRKNOdptaSRZ70uUjFVTNO5RJAghZRC6pK5TvJ0UMAEWSQR7b OeqHMLOvIz6UnjE5Fz9oq44iu+AZRmJOrBINCBCdCnm5CYQ02KjGRfAqGMjEhBVWwiTgeW0ZGTJA wlIVM8jUmTGJbIdymXgGNS6zJiSVDLzxMzIefYw92IN7muQoWBNhT0FEI4MHfc5JbBs1+UAi7N23 ocEauUkCCFerUlcmVfyQNypAoF1RoTGyAkTEkmtCDeQei46jEruOvDZQwhxtt8gBNy5yKVolEmpC CBM3jYsdSFx3ahAdhe1kQhHBx7yhQ2MmGMJzOC3BTEtyA549PQlTyFubksnBeZMgXIHBDqRnMyJU lBBy5dxbyNCl77kEFFNuCmjg41kOIkC0vlPygD7HmC+VI7yDjurz4X80kSGJXPtzPgwOOQhTVnnF oj0KVh1UmrvF7ZpZpXtTIM9lGnjatRy5a8dniKxECPdVe3sJgE4GIxSKAiOTIZCKVL2iHj3RNrVU vtFvcQT20PQtfcVCOTCQQgWOCwmtyxGYI6lJjFKknVWG9i2RC3sPZcbKWOR9yIY713AOHO9jZEHR DQKgKib4PfyPslU6rEO49hKc+6ZU0czvWGiaCRQIHdMeXi5aumdEgviWSBE132TpGJm+jcEnlE7H YQ2IndVch9c2JfCcQLPja5SStuErjV2C0oNLINYAmvM0ywrLhmIGaRS5UZSpueZNH3CC8Ha5o8iR cmZLm5bRAkZC7IOXFtxkiRHMxAzGqcDvKk4dVvd6OHzCypERDphtNJcXzDNom3M63WjTHvurYqnc 2OjVK1uwkKva2kxvne8vMrDxe+cazrbGXVMgF8RRDQiiArYFXDw7ShbJQcTtvKbAy0MoKnMawMSR sapx4EeLwOJx8MJ30ikUFBQWLEz6+w55yM25cqNchAoaT2cHcqlTRe6y4FoRIDg+eC2HOjYwXKlr wAH+IJmxub0nnkkPRSy7S73ibE0homSryd2FLm2qErJGJUaA1SuRIVHgxNOCSqyFgQQjNJT0RLDl QzOBOeCBuTLzwbHM7nu0fCT5C/Jc1TEiRMsTODBwFgB6wFIFGKbmlkESRccY7+GQPCBOoJTc5TK5 5fHbxrBM2pBZmTqpZIsVxqnvdFKjG8vZppKsGnONphSIrErUaD8ec0kIk0BrBcQUGKykSXFxzgqM XOjayBaxYkQNozShMsPDGKqVMlomKC1KHiCeYhjL3exsYL4OCY6oVeEEh/gSknhRKePHRexg5HI+ jSNzyY2KDGxTfdVI20a12c8SAlQ3xudYlnZiComzwSPWIMXOdeLSFKjd5Mc4Jc7nAxgpM7jGwt0D wgdkASgZLnO2J9pECxvIU6SIuzO7zJm5KUjA5sbhC5UUtDGKDnVFMY4sOOQsR31IsZNsCm5osUmv RSAd0kHUCEMtyDtAHoLdFoWw2kNN7kFEG8jQZVeWNFlY802bKgvDvGPHMYuamsztRMi1YSDvFfSK BIRER6uDqmL+Bej1iOP6qlPMkUOIlpO3Au8d+IXJcevq1txzRZOW5gSoCCEoEaCSVipUoqaOq7kB lucArjUmakjbmdWRdXA+ZRE7Y32CdCt2RDRTsdzR05A3IQDCjDXKIoGQyUIdCoJWBE46GlJUTUun qa3F6zcjdOChbbRvtadawbWSQMUIGiI8blvgQIoCJsdZSpggVTrY6qnEIlDwpcqTLm89wuRLnUAZ iETLYWkKTDTMiXK8IAnQduoAzCSWT5gQQ10ePm32XXNIs0xudKUYUhEVSUGopusawpSE+9ZrNINd pI4SpVyohzK8PjsjopEdA8R8ik0Qyd9R21UaFxZ528SSAhc7lIHc2WpkxhVFdEJkjBGS4Km5AzWa 7QN/LU9hzdepSLvQJSAN9xqm5E+BEcsu0S4kWK3MGjk5++CX3VQKkyhRTnnwFCnDHR5DB7cPI3Mj nbI3ka5Ni/RFw8HYcoMNk8eLEeeDIvAus1I7oFfgQHNGXltTcoWUrDePBMsYNJrw45sMT5hxseeg 2N3kQWWe0mbADC7aZ6668UHFBAkl7jUvQhD83HXmdfqZMQ3hXUobb4MNd7wTmczt4OxZna7mdYna Nb7rMzg5rRByIjEAzzuNdizDRztDPE58GnLOPlkREQ3NAGtAggspr3KGyKg6GnSj70iUIjmC490p wYAVi+zFNFhzLvstWI31gsOLlMn1EQsnihckaHL7VKTbWTyONFDLzrBi5A5OtFKt1dYN2OiaikyU XaBqCAxwcOYJm3BE5LmxYu4rjqNDAyeaBgRFQJEgsz34NHYRyJawHcZMjYjbgZERHiZOtGSdiRzL QrEzgsMRNpRoXZy9xWNhPZ5eaB8iBwVzdZ0AHeX0NTtPLm7IxPKt6fn1apAoCZmXKyK0yvYlTu9z /RhM1pQ9xpTtVPatQ9L5oGAHFB7KnpuPQR0oYgjTGHdQjYiGZDuoSyHd7yEQqAPevQqguItEK1tm oZIRtigNwi5sUIhv7n103Jxz8/q01ky3aP26iikhfHmwlxVBSbTVjE4eanulVPqs9Fqulj6jwe7T t9ucl1bhfj1bor7t9iqmZnquDepV1Fun85tv5igwKMqDzKIVLyZS6o6LPp9czwN6+rMBPszF04WF sKNk5znfQlMzMPRFmC166N23URN8coM3blCxiCTTaXZOL5R58IAbVGgjtiTbqWWctcXfOXZQq1Ks IJNN+vuz91r/mt1O/RndLqcocYX35t9lOdV2yfNxDJMus4WUp9mfn7xqztV/ZOf50TaC2t7PdIk3 32zlZbjCC4bnxM/pmpZxAb+r7Jj1uMMNZIuyTrwYxXTNLk8J/SPz/bTwRHkxJ/oRqAH5BRUKARDW FQf2XIeIj/SDCQgSIp90Kz991GBFa/b91USpAWMkEZETRAWSHn7Z3oHuSqigiMVijEYJBUYIh5y2 3zwJWLFUVRVbHDILZQoUlsEERBBBgwRFICoIKsUFQQ1kPiDbJOveQ2kH3JBdBj3VDfM9ZqxMG57x mBPiEM6EABN7mPD1ct2Y3IFHccmrT5EiMUBKBg9XVPyO5b6kP7lH+uOBjGIe9ums8p/B3XM5eYuL NRaku/fJ3//ZKVgUKlXJFsf4cBRNoI5HpNfHY2wMj/VHtu8EgBIKQVkQiSRCMiCMvepf6ur9P7Az 0qn2oLEgA1QKfRu996PCCyrif4IkLU1J9AM+olSmgdExVQkWDRCClninDkakPMkmxyztQttgyoQL wwBsBe4KMCiiM2x9aImHB1HQdC8Nj2eNpr5kKt+Z3hmS0GYZWLrCU4KPL3N4IgZq4HH0c9zuCuQQ B6jzzAqApkjcUuv1EJEawvKQOHCakj80EDKAJwYSDMYgcioNm0xWCTEwBxD3gnxIDi0LclsKNd+Y wYzLVdigtkNQx7iXXlYAdwIMN4WwLEk77IoIkhBBEGIoMQZpqdesJMwLA6wOk9I1s2NOxIDWoDvb 4IhTO2zZxUioqDS1dXYZIhbMQDQ7qZPMlzXaHbAOWvQJuiKaIYczXmtXkIMUGkc69ZQhe2ZVMZXm jECtRvqNLiAmluQB4T5860Gxm62DG9863A4LKiJhpGmckxrpba58SeKGQNA3nCYAsNsr4JsYHBcj wiiYHFEkwjrAIuyocQEFQockOLFJt3Ljx2EU18DncSxVG/jCpSld4kJCCJNzec2smsDMnG8jn1ma 8mvCSwolna35NwCItxkBe0smZIAGCeqJoNKNLQThDqFVQRFRFJTt9rnp3TgZmCxlKUqIqjFMuQwU M4W885zfjOE2CE3OhgQUCxsMQrsBCdCDCgiTDfAtU01qHFEqReAAQ05zNekIADdmQLyxvQbESKbT fRvO25ABUxxyaUuHIUTtaALXIXXhmem+8datc4qVtkuNhM9gT/TCRRLnNJG8J9+duvXC8B0negzh L5UIQGIFntSck7uBEYUTtJoULwNDgVMm5slRFMVegvKBaNpJKLVrYVuRa6dLftLcJkAIQ4nO07bp S/E6gGBJZKzAhMdAscalgY3xIjjPlkIxbEeApaBa4PHCBvBuOFCEIqUbmms2lNTwNCNeFoqy8duO igUqDHYP9vDw96z8QfDKv05X0/r+mJb7KUuP2L75nLrV6zRJfD0n53+j8/8XfZWUn6WK19lfkZU1 +X/38v5vTt01vvXH4/1CTJ6fc8uRY2KfwYPvak1eqCZqmGr9PWFK/rD1h9YHuDMBzfjn8J9NZBle 08YkBH4e0xnKsLSHB8H1/pT3C48DOwbISXYA+n+1MpUVe3u/374N+Lsx/BtE8+7VL8mrZjx5MKWu yzPPS8z21ZTLBh+Idrs9AGAfmHtD1osA9lPYVPr++vbAuD5QYB7/f9bUvgOAZJUlS5GCfbDKJmhh noYYCQiD8zJBgWJB+dVQlArXNvHjwdwSDAit8yjVaOIWEUv1ItQvSXjF1cp0Bvh5HiUawDoEIMoA eRHgD+HnHu4YMJAwWtJAPSHgPM1ALD2NUrIiWWiBRLvMXUqO0BKQSIJAYAkYsYM2zKnguw6QLxFq AVUghAYBBkjzBClXONguMEWWEP5rAgEA/K5SReXbstlUJvsEkLMCMxvGCPZ1A4FFFVXJL01Bgwzs qpipQYma+fqmqI3BHHdCtQM8QsIgiQ3+82wwOxDtgcNGn1GqMBGkXP9P+J5BG0S/Sv8yRuX+RMFm eWkP5SeJcbjigS/MZJBXI9ypJkSouHN45H3FZZQFI5cU+HxV7mZTgcDzGNoykRFwNcxYiZI8M4Yk HOSht/qgV1sTKQIHFzHQiImxOZXc4OCjlbCqaJ8FpIoGMg1cD01bNaQ8qSi6oynycGLlxX6Ke1Th oLugXQmNUNEMeDrmV81pL05AwHO2IW0to4SDaaR4LyxM2nPu514Ma4F+pW/kmSKXGJpnw5IOpJJb UZNSEJHa2raI3qtEAClBBxqKZQfXi0ZdDtaCLEfEymBRua+yxeUk5iScQkHmgdoJWAECKCoiw3w8 HIMmBXKYAqAiuny1CSmxCy/pjh+yEn5Q6r9fM4Kpx1oJWkobarzcCzzgFGWBCe4H0Z57Xl/19cxn M1Y3Ob/bIFRFj+3EJIVkiGfzDGUWBDzKoEOM0QpwGbiUQ6kyRhf5KXAGmPy4vKjLv+a4BHJ1zvdM DTAHSEAe8BWR/RarIKHuHCSxwODjMyViFO0cnC6miHjNHQQR7IpSCgFItBzFHELi9CqFFdVtYYY5 0zhm/pj/JeG70+yHH4u/4IfyMQ9CRBIYUTx+Q1FRqyt44W0tXDUeTaqPaMG3MwfRR+nZfmzYr2Nc Wb4oVUfOIlHV384owd5gOpna4zFM4AA5UZ6kjoPs8WM+cNIgBFBZLTnVxI8qyL/r7zvn4K/e1Gyv Aa4z7ZmJoPM5LnZCQw5Wncc9J/IJknImR8pliB8f0F0u74t6Ty5hqC3IDK8Kncg+UPl/RtvFgvHM s2/nH72fAs0wnU0DjE1J+o6RGINEIUCNFhQGWRKNgliWCUbBLEoJRoJRvxkP3TWT2HwfgV9qlPPS oIMA/eIU85IkJ8iCDRdAzOhYA4Lhju+aY5TCEyYCh8YwX3CAvPWeXsI8Tzg+ODuLdUs7/ad3PlUd i8kYjOhYz2WbWXZO4SUdBknQ0ewkyWbhRwjBM3yeEuli31hT5saLoqrgMbjkdip0bEggUFUsImDJ MMEuBiTlj2+nkp5gMDm3e/Q3xunokJMa/KhzavGn3rPZq1lwklsIhVZMmpGBgQHG/HrCx6HJgU9T 1FPBEmV17DzMgZPM5LE4y5WY5kl80TlkmuV5ylhUYGhyI5syZ0GXq07TiNv4kIiUL3fDExpN0Ij7 JECwby5RIJjXxFDBeNDkbEAdofoSg7gOGhA44oNYC6KUdj9B8ARcg1x2GQkGj/LCorpMYPAmik/c nI9IK5/MJP2AKkqm/BJPChx8kmguqJAJWUdDB5MRV4/ETgt1gZUlCQdouh3RV1oV60EQ46ZyjO6i keaFS4Uhoy0NkA5x5sO5dwHhPLYLtFGP4aUh7b15iYTKOEXU1WTXQVrcSmHM2gbVDIq6ZMVElIik d3NZFkzdI5IygaNOOUcu3v5u7+xnQovAMJfFHOBOubh4lQosqaopjUV93sOsvO92HFn3IL9OUZMV 2EC5ysMSxgoZImxZyRMiOZLkBaERXPyRy3grA8IEzRokQCYtCI1ipChA0WNHSAJbcsRGNDsQNcoC Jwe8qAcD7FD4/lybIFpGRSiXMDR7Q3nAw4p8hkdnBJCIOYT1SRIgLJFc7Cm+Ojz4GPeFkROinJso yscmkAyMWMZPM1ovE4cKZlEkSZwz4KXgl6Bo9GxtjeBrdbv9I627Zl/+UC1rUObr91KiTLiY5hZn Gz9CcXKcTxHQaTBXwCwcqc42I5TgAK5bjicABkZTarVUn5xeRDBTAM6l4PcYxrcBvvQZVDgjBJmQ QLoVZ/iEdSPlYap0vfzvpHHLDuZnwNe1XSVmK5ogxgYgPCZQrCpbW0xCba8/DuzjDZVvc3wlVl8n SkDhzAvSWiCEaIhQjN4JJRMWtUhllV0dp/S0VRt3JyoiIFPzfiik2zONBGwkCMSc15tHakt2ja2k 2xJ0k6QR0ilHdYthNRhVHltdzQpalSmq8fdxy3s0hHmmJntDcKNsWxVIQomUJYnDkIipmPB1mKB4 nHUwFlQBBOueECunSSSveE3EgsFEKySCXqKnkcSR6SKRzxl/O6ns1vt5uoydeQ68QkdUDPSVWk5l ZFqxyBnIH4x7SRHhl+XrvFfozTv5+NrueLdvXOVpevmsHH8TaRatKQUoaFOwtGFIETz7Eztk6mxE 9ACpIlcfl53l51HO5QMu2QcjYS53clz6bRdpDk3HKJzSQc6AWg9s5doNMRwjCQYCUAizhzOawYxU uHFBIItGlFtYczJA7oW4+Ems27dJfo9VxnNx0qRQyQb2QS1GQsjyWhtMsQqNpxDeaCwoWtfJ0pLr AN2lItspsZI1MIF8PbfcqjrQBz2eCDz5d4aqIqOpsnNeETuZHhF6tdVA1dnhpu18xynOcplluFXO 7e9REvYof2YGRZTmMOVyjh6c3G2UJ042pjFI1FqCSDr7Xdw5enoyQ2VIMyC4CJ3aVebjUdogQN1h TM7fQMUjkQQTs2J6xJLWg6FxWFOlscMdVeKDVLRyzbCGqYjHt1ZqKnq8iCkQYL2bloQHUI4ni1rz sOe9PMOTvAcU7nqY6IDMwam8/cuh955fiHse/6kIMgJBESBalnvRsHEidB8FMCxScfR800kbO1co YaHXjGNA+9DAD93W5HMruDvkgkioQdtR5PFqiEjLlLILBD3VNXkDnISlLziz7yc3wHnYGtN6tz5O 24Dcd4xMRQot0KRG4UMDZQG+1BG17Fv01Ete3C3qSLekAgMkEKrBC1kMC8OfdFmN7vky8Ybrxe6h vTUb9YfOHtYwWEq/9oc3O9aSdQG8MqhEGIxFJJYoH1bKNPLJqBpPKY04ULUKfBfGg2lW+uyy1Ahg MOIHMWB3xhMkx5yllRVTTScrhfVymAimndduogH/JKQOUEoJVHaM4Aaw9nrBBDuhDJCEmdignSRj B+xhQSSRUSRC3bRL65zTB1ofJukblQswooySeWRhYIhRqL98tkHNpNPRbiy4+H5CDggQOTCI18l1 L+Uon5Y+bx+4lk+gs+0XdonPl81nyHaEQJlWUuXUM49pDKTMAQiZkpJnznBxY6KXmS+4kxsmxYgd i8YGcJY0InRYqWKFB0gOasdGql6FTtxkyYc3GOxuX4KGihk4LkabVH0ZaYox5iIiQKjeUyXtENyd e2SZsjJ117WjUU1vydoDpdKDHd78p+HwaJliBx0SOSwadx3FSatAApmnZoKjmEkpUAisMhpYwkY5 9gA8gJwa/k18KYgYcqeNULN6ocpRUL1A7qyi/fCc+Oh9785EvhkE96VPkIhtOegnQhHh1ZyKxq0p CgW7whHuYIef0X0CG7nCgdZSTbzhh3kNh2n1Cd0eLgF7AiRhCUJFkryBqidLUKhUfTAQKeK9uM96 QvPjPnMdpC8zERIEFDn7tM47aEbVGgiwCjunEnC8DX/tiDhx0MagDmT3oily7sqn2tg1AFFQTjDf kYd9pDVzzkgfN+y77iNwiofSyTV8bdrjFrfuskKmU2SBzUsmohPdM0IasUmbYsAVTllXLcFrhggR iwSaHEz+I+m+eDCwL9psc8h4qmTbZWbKRQiF5Sb8klb37lDc/fX/21qwOiJQGiNC7NlJJvduKeDo BErUE7aQAPEJ19sHtQ8D6IU9KVlAJB+i6gFyF1VfCKvP6i9R9zbCAI9Iq/T2v4qWKRRc7QLhdwAq A/NCR1nnwUyZFxhY8xBMRPJ4jCypgOWEJ993mLwAvcm93getr5RPYh29YbYc6qJl49MsEx4TlkD8 ksD14D4/IqqKqv74TuG/syfSm9TOkEiGQv2g0a6kJ5xTcQ7FRK0Q59zBq5HyG0VNCHs6tfkj5Isk ISA9kaQfz4mJkTLBHAoz04vbLdNLDwPY7qqc5IlGI1IAsWWxBgCc5kqMES+TtiLdwIwF3kIpJ7Q7 QJmASMEE8Dc2BMFDO5vheXauQpHY3AF37lLjsEXFDQiyDBQ3t5O3A4B7tvRlfWxDIy2iDbwk1eiG jDec3jeCBoYbCBwwZHPKhS25Rbnp8fiQ8aGSGoN3cd2iCUQgRIkQkVCMQgmSCIeJCyfbtGlCEJ4T jBNjYQD6bt8blR0ovE0Xp0HApndIOQ+0A6CMtmjwaBoCKr1sdGovL0Nd7xp3QDcHYihqgGvwUONT OAej5dxuF3DePpQ536nOJ46wNUTYJCkkKAkXbVBIhpQzgieVSwGm0DGi/UZVRHj9dQTlA3xV+MoI uABl+HO/Y6OlD2g7QB1hySUQjFmpqHwtGgFQrVEUozq6RLiCDnQksgihBmiCCqddq41oMuIsnHWO 2laMeZCAmYiodPfpW+VqCm6kKkVBHWK2IN/Yj5p4JFA4PjRbJRZJBfqA56Nt8nSfaN/NaQFn0Tj6 B+NPpHocXEZgwiilJCD5eKIUPYYJEiQDvKrYuzGYhi/YQlCopDJbYpG96kKmeykSuobLUiUT/Cpc xkuDCUL7j3O+F28hpeB3m5iB5vO4XVjvKTpbjXRDOJji0aGn3nhsRW+eI9yAjlatwWg1uoDqOd5G ba+hCUUCHIhTv7cUDSgWLnKe4ESdGT5RvQMkfHDJSAHSDiraKOLQl0KQqUJQVE+HqB8qF9yl5uJq iIp08KlcyBVQ/G/t6VR0mTroSAJkcmGKG872n87hJtByPfTnaF6wgOfiibEzT2IbSFdqiG6+fvHz XHaiBELCh3bZKubwkbwgbFgRc6FnQANAyfybJIcAPZpanTlSJNgpm/pB5swhcyBiYgBg7o3KrILI sVVWKqqqqIwM4lhS/y4xXOWF1KQ4gbmoVMmSahQ3Mg5zCblNjgxBsDBbGxjJEMAUhWKRiQLiVaFY xkWqoarFSVBvRr3Gt84t/hNKocep1A6qBoUs1Buyp8N4pzYOqECQGGylGKRvEWUbQIIUU1XChIsY i58TwSamgmpiSLIIFPh0CF0bwJG1TIIDsyVMUDMwSgRaSBBNEhCnnAOUSq/Gh+mxKmkYpNdHIrkA OV5CDqMJmlEmuKENtwd+CrJKSkwHVk44hoGLYJStKwlFLRiJSCFsJBgWMoSKCJVhiwA6bLarFGe2 9Axy36HD13e312K1QQxzIGKkRqVlSlCo16no5gqC3WSxQP3mYsEDaDFGwEX1i5eXDGoI9B3zKC0d HaDYMVI7k/BX9T82Rm+IUz9p16IiPMer19nq10nq3nNA0iokgm2a6KBwoYRGSROIghaKm+EVWvZH mQwEP1wN8NGAVHYuyKW+LRgENAVflNFIvh5ROyXBUIPQgKgyCzApyjHdYwjYrU9UQ+dmJNCEmwT2 4KIhFUj6YFub/p2wvDD4nC/BQ9gIl+sriYoRghVc82GKvMNwWDzv8nLRvOjWrkWQ2RTJUEzPc8/c yafRub0B6zYGR8zqF2J4/JRpI9zqD20sFix6XQhx10nl43rO6eAyV4AK5O95e0iyz1oXqhEFu9Vd 0zu6xhXxQpSFE8HUQhMRC8slnClPOaIcRT5Seep6uJ3QB6F3mI/FvlDnQooHD4tQl7diDIvCFiVk c1QsUrTtgbNg3D9Ipun2Jw5jGE+GbqF3CYKgm4BuxAEN9EdKno6Hsn3yihHgyRKHA2dqRCvQUhHD aB5I+BNKhAXaIg8oInf9X1AibCyHkO+CJtg9IB1v6+hD4EOJ24SQ7gfBRQoikqZGZLEQc5DEUtCp OqYuFCZbhKY0Roy0YZSx9cwaYyRYLDRrGIshmko0jjFGRFBzZUYRiEJYgQ2kJVSagYhzF9LyzQgw wcWgvwxBS5UuKWEXpSiFVXzIWxcl9FS10LzqaMNX0A3hnFtFAKQogS6SM6YF9zzcvJqSvMXLA4kK 5BHmslDsGgPRQKwSRQ131DUCedWKU3HEkwrl4XM7SCNC76Y6CdM8IeDqlIkxPrwNkxj8+JCYuN0y utoKiaWfzYLIfShQAdJ0KakHeYiu6AQEXmI05BGDbdoJ0HIwHBBrQIQVIVgBZJIscE2n2ChpbshB KEGX1TUBlWnF0bepC4Bx/jV8SoJiXBqBEiG1oCuluESpBIkiIEQJAFIAVgpRCl4qiXoo31QU0r7q l0khsZBDAhkCBnV9qaK+dIgxfqj1YwYJRARtkPeK/4pyRKcSa0WQSv9nzTzoX8tu5c87pGHRHXHP eGWZlnvhSVHQtbfhuBYhWWQiVla0wG2xDkANfAqCY6X3BlnudwHA4lzIcMASSn7agfF1Uh1FKedA 5j0UkxVCx1Q6QIFidWLmtKYAA2LqKpfiECA4AXiOmqDiH3X0aXRd6GnEKfMmUisqttnSy7EgmN0d VuCsNMDBUhaKYgXlKBE4vandMOBPM9u49ZiO4LhRDhNCSDIBIpIpIEI3KhttU2Be82WodVLqOCES ILT0TvjCnepSHNJAqQsMKfUexAPZQiwWCxYsUUWGQ7G/i5Ez63c8L34GNdOU8mSPHktAipuxpRKV ILWB+MEwqhY15mN3ZZgfK2JkLLRkh8/cmeGMSddLDoRRTSlQRkCpozAbCPtj4cTMjCYQhE8ol7qS vQfeBToANZeNk2h4RF1YHTpqclaNHLm4pS4ExiVE1hyJ8lCc4Mho8x0cHMKsgqne6rj+5sFU83p4 QmUj2eqmpejS+xH3BqOwKfeBVOR8qc6VEDIIkwsM+KSo5hf8EFUbSxIQs0kZv6zeBY1FFbRqwcwb +mkjKM3iIhdRALS986RL2ylpoRdiA74tlbZtAeYihjvYtc5SaHFRNSGXziw6KLxrpGiu6eXJclrn VjvCqMI30ZMixhRJLmXKMA6ESTItcRgjQioAsxJmA3sHsoxoJAA2/bILpVUWLN3xatJyZgbbbEDu Rc1H4wvJpIWowCoQrCyQKWQgiRGSaEkDmuNQLMI1VK+hS6jYAxKqUEggLMLzpG5AtfEXccCkgghR G4AouCFQSgJChk944nWCCY94xDJHGlUOArl0hbDInj2L2Jqs98OovhDztJpwDyBhLGKgNHySoQlR UMELaB6wRJnGksIGZKNAjfoQ4YhtpwIZnYGY5BDezbtBZGJHMrRCxZEnMJdaWWZizFMgah7ARkeS QfF0gQWboJrG+6gi58yjkHA7TaSBAJCQgqQRAVJ6pClFj3pIa9RgDUGAFYKnzUAKa6FGRCAb0QXD AsRDnd4Ox1UsAp5hDcNSGTDW1OLclCkpOXYHhLHT3PIZ7nykc9zdTgaNguOUTnF9CFe8p5nwgHDl nzvGAonpQ32g8vnduqKbYPfYLtPJShkjyoP1x5A30zf9aocaGruQNUgbBJn6gTExRcgDmDp8FfKH d562v/zAqiVcA8zw+6re6zBUE7xhGzvYXQAkc10QcUHkYeQdiUBudUlk5xMaE/+FKyOSMCAkd1v4 g634DZ86Gnm2KD9BdBIeBV4Cmz4Ye0aMY+Z4+g8GSJzoPyLvC2RmW5wqx44G4XOtRmRkBWogHAPA qCWGxpIbkhCjBpXL8FPsdB4DOJD52YAFOBDHnEM6N4YhtiVPlXhoBITlaBoBO9QSgn4ZVS0dynpK wIfvPUDVcVdbp+Nu8fDuHpDF44yaeSnagJxmnv36SfEh0obt+KHxiRRANDzIa+fmEZqHOb8IAjey BIO6accGGKuviMFffrAcA0tKGFGz6rl9pLoFucW4NRQUC4xIhY5gWiG4bEOWLjfbNMsVHQzISDtB vVKghrEmtimsT8VKIB3s/bHQP8IoWR0g+8AoqOwA10ROoXdGi/XsBftNEkZ4vtrVEPfEVe18NSv1 JFkyQztttjGM/Zj0abKbqCS0Q0vYE+wJBApzrMs3N1q0UkUgPlTZYKUliDKCs9WtY4ioyKG2NhUc 4ufeazmFhGbioEjkYADuNkddexB6RHnAIHiAKFg2xedft9Z+UXiihdMwkkpn8Qp4H5UMDxWyFP+0 9Q++gfedBMuoL+DLRV5SiXTDTFgMkNZpIE80xCWbbShYUs4N72KUe+H8Rw/rTP3D9j/WHVFUP+MZ Ag45ZnrkOADzcSsW+/rNZt9WL+P9o/6moA/PsG6kckwMtW+z/OzdwtLMjIX0vv4aUiH17/Sy9ph7 ZgewrjK3GAeSjxUxo2ZukkCFmVUQtpSPEU7zJugVVOFZd3YkbfUXLVBf2dUEZI/j5f6vvj7oPNBT qVbNzn+c/+ZvaNRSzFUYvYqf6C7kinChIFTnawQ=