# Bazaar merge directive format 2 (Bazaar 0.90) # revision_id: colin@gibibit.com-20080804171055-36h7yem24erv5fy8 # target_branch: ../../repo/trunk-clean/ # testament_sha1: 1c89c142bccacc692e7e4d96fe51b2cbe5afc77e # timestamp: 2008-08-04 10:17:09 -0700 # source_branch: http://grub.gibibit.com/bzr/trunk-clean # base_revision_id: colin@gibibit.com-20080803034658-kcm160bcz0e1rt9f # # Begin patch === modified file '.bzrignore' --- .bzrignore 2008-07-19 21:43:42 +0000 +++ .bzrignore 2008-07-19 21:44:04 +0000 @@ -7,3 +7,5 @@ ./conf/sparc64-ieee1275.mk ./conf/i386-coreboot.mk ./build* +./util/fonttool/build +./util/fonttool/fonttool.iws === modified file 'ChangeLog' --- ChangeLog 2008-08-02 22:24:34 +0000 +++ ChangeLog 2008-08-03 03:57:46 +0000 @@ -64,7 +64,6 @@ (memdisk_mod_SOURCES, memdisk_mod_CFLAGS) (memdisk_mod_LDFLAGS): New variables. * conf/i386-coreboot.rmk: Likewise. - * conf/i386-ieee1275.rmk: Likewise. 2008-08-02 Robert Millan @@ -197,6 +196,72 @@ * normal/main.c (get_line): Fix buffer overflow bug. +2008-07-28 Colin D Bennett + + High resolution timer support. Implemented for x86 CPUs using TSC. + Extracted generic grub_millisleep() so it's linked in only as needed. + This requires a Pentium compatible CPU; if the RDTSC instruction is + not supported, then it falls back on the generic grub_get_time_ms() + implementation that uses the machine's RTC. + + * conf/i386-efi.rmk: Added new source files to kernel_elf_SOURCES. + + * conf/i386-pc.rmk: Likewise. + + * conf/sparc64-ieee1275.rmk: Likewise. + + * conf/powerpc-ieee1275.rmk: Likewise. + + + * kern/generic/rtc_get_time_ms.c: New file. + + * kern/generic/millisleep.c: New file. + + * kern/misc.c (grub_millisleep_generic): Removed. + + * commands/sleep.c (grub_interruptible_millisleep): Uses + grub_get_time_ms() instead of grub_get_rtc() to stay in sync with + grub_millisleep() from kern/generic/millisleep.c. + + * include/grub/i386/tsc.h (grub_get_tsc): New file. New inline + function. + (grub_cpu_is_cpuid_supported): New inline function. + (grub_cpu_is_tsc_supported): New inline function. + (grub_tsc_init): New function prototype. + (grub_tsc_get_time_ms): New function prototype. + + * kern/i386/tsc.c (grub_get_time_ms): New file. New function. + (calibrate_tsc): New static function. + (grub_tsc_init): New function. + + * include/grub/time.h (grub_millisleep_generic): Removed. + (grub_get_time_ms): New function. + (grub_install_get_time_ms): New function. + + * kern/time.c (grub_get_time_ms): New function. + (grub_install_get_time_ms): New function. + + * kern/i386/efi/init.c (grub_millisleep): Removed. + (grub_machine_init): Call grub_tsc_init. + + * kern/i386/linuxbios/init.c (grub_machine_init): Install the RTC + get_time_ms() implementation. + + * kern/sparc64/ieee1275/init.c (grub_millisleep): Removed. + (ieee1275_get_time_ms): New function. + (grub_machine_init): Install get_time_ms() implementation. + + * kern/i386/pc/init.c (grub_machine_init): Call grub_tsc_init(). + (grub_millisleep): Removed. + + * kern/ieee1275/init.c (grub_millisleep): Removed. + (grub_machine_init): Install ieee1275_get_time_ms() implementation. + (ieee1275_get_time_ms): New static function. + (grub_get_rtc): Now calls ieee1275_get_time_ms(), which does the real + work. + + * conf/i386-ieee1275.rmk: Likewise. + 2008-07-28 Robert Millan * partmap/apple.c (GRUB_APPLE_HEADER_MAGIC): New macro. === modified file 'Makefile.in' --- Makefile.in 2008-07-24 13:56:30 +0000 +++ Makefile.in 2008-07-28 02:00:30 +0000 @@ -89,6 +89,39 @@ YACC = @YACC@ UNIFONT_HEX = @UNIFONT_HEX@ +### Pretty output control ### +# Set up compiler and linker commands that either is quiet (does not print +# the command line being executed) or verbose (print the command line). +_CC := $(CC) +_TARGET_CC := $(TARGET_CC) +_STRIP := $(STRIP) +_GENMODSRC := sh $(srcdir)/genmodsrc.sh +ifeq ($(V),1) + override V_PREFIX := + override CC = $(_CC) + override TARGET_CC = $(_CC) + override STRIP = $(_STRIP) + override GENMODSRC = $(_GENMODSRC) + override INFO_GENCMDLIST = + override INFO_GENFSLIST = + override INFO_GENPARTMAPLIST = + override INFO_GEN_FINAL_COMMAND_LIST = + override INFO_GEN_FINAL_FS_LIST = + override INFO_GEN_FINAL_PARTMAP_LIST = +else + override V_PREFIX := @ + override CC = @echo "COMPILE $<"; $(_CC) + override TARGET_CC = @echo "COMPILE(TARGET) $<"; $(_TARGET_CC) + override STRIP = @echo "STRIP $@"; $(_STRIP) + override GENMODSRC = @echo "GENMODSRC $@"; $(_GENMODSRC) + override INFO_GENCMDLIST = @echo "GENCMDLIST $@" + override INFO_GENFSLIST = @echo "GENFSLIST $@" + override INFO_GENPARTMAPLIST = @echo "GENPARTMAPLIST $@" + override INFO_GEN_FINAL_COMMAND_LIST = @echo "GENCMDLIST[final] $@" + override INFO_GEN_FINAL_FS_LIST = @echo "GENFSLIST[final] $@" + override INFO_GEN_FINAL_PARTMAP_LIST = @echo "GENPARTMAPLIST[final] $@" +endif + # Options. enable_grub_emu = @enable_grub_emu@ enable_grub_fstest = @enable_grub_fstest@ @@ -136,13 +169,16 @@ || (rm -f $@; exit 1) command.lst: $(COMMANDFILES) - cat $^ /dev/null | sort > $@ + $(INFO_GEN_FINAL_COMMAND_LIST) + $(V_PREFIX)cat $^ /dev/null | sort > $@ fs.lst: $(FSFILES) - cat $^ /dev/null | sort > $@ + $(INFO_GEN_FINAL_FS_LIST) + $(V_PREFIX)cat $^ /dev/null | sort > $@ partmap.lst: $(PARTMAPFILES) - cat $^ /dev/null | sort > $@ + $(INFO_GEN_FINAL_PARTMAP_LIST) + $(V_PREFIX)cat $^ /dev/null | sort > $@ ifeq (, $(UNIFONT_HEX)) else === modified file 'commands/i386/pc/vbeinfo.c' --- commands/i386/pc/vbeinfo.c 2007-07-21 22:32:33 +0000 +++ commands/i386/pc/vbeinfo.c 2008-07-09 14:39:39 +0000 @@ -18,6 +18,7 @@ */ #include +#include #include #include #include @@ -48,12 +49,22 @@ grub_err_t err; char *modevar; - grub_printf ("List of compatible video modes:\n"); - err = grub_vbe_probe (&controller_info); if (err != GRUB_ERR_NONE) return err; + int row = 0; + + grub_printf ("VBE info: version: %d.%d OEM software rev: %d.%d\n", + controller_info.version >> 8, + controller_info.version & 0xFF, + controller_info.oem_software_rev >> 8, + controller_info.oem_software_rev & 0xFF); + row++; + grub_printf (" total memory: %d KiB\n", + (controller_info.total_memory << 16) / 1024); + row++; + /* Because the information on video modes is stored in a temporary place, it is better to copy it to somewhere safe. */ p = video_mode_list = real2pm (controller_info.video_mode_ptr); @@ -67,6 +78,10 @@ grub_memcpy (saved_video_mode_list, video_mode_list, video_mode_list_size); + grub_printf ("List of compatible video modes:\n"); + grub_printf ("Legend: P=Packed pixel, D=Direct color, " + "mask/pos=R/G/B/reserved\n"); + /* Walk through all video modes listed. */ for (p = saved_video_mode_list; *p != 0xFFFF; p++) { @@ -103,10 +118,10 @@ switch (mode_info_tmp.memory_model) { case 0x04: - memory_model = "Packed Pixel"; + memory_model = "Packed"; break; case 0x06: - memory_model = "Direct Color"; + memory_model = "Direct"; break; default: @@ -116,12 +131,31 @@ if (! memory_model) continue; - grub_printf ("0x%03x: %d x %d x %d bpp (%s)\n", - mode, + grub_printf ("0x%03x: %4dx%4dx%2d %s", + mode, mode_info_tmp.x_resolution, mode_info_tmp.y_resolution, mode_info_tmp.bits_per_pixel, - memory_model); + memory_model); + if (memory_model[0] == 'D') + grub_printf (" mask: %d/%d/%d/%d pos: %d/%d/%d/%d", + mode_info_tmp.red_mask_size, + mode_info_tmp.green_mask_size, + mode_info_tmp.blue_mask_size, + mode_info_tmp.rsvd_mask_size, + mode_info_tmp.red_field_position, + mode_info_tmp.green_field_position, + mode_info_tmp.blue_field_position, + mode_info_tmp.rsvd_field_position); + grub_printf ("\n"); + + if (++row >= 25) + { + grub_printf ("-- More --"); + grub_getkey (); + grub_printf ("\r \r"); + row = 0; + } } grub_free (saved_video_mode_list); === added file 'commands/luagfx.c' --- commands/luagfx.c 1970-01-01 00:00:00 +0000 +++ commands/luagfx.c 2008-08-04 17:10:55 +0000 @@ -0,0 +1,152 @@ +/* luagfx.c - Test of extending Lua to access GRUB graphics. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static grub_err_t +grub_cmd_luagfx (struct grub_arg_list *state __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + /* Load the file into ``buf``. */ + const char *filename = "/luagfx.lua"; + grub_file_t f; + grub_errno = GRUB_ERR_NONE; + f = grub_file_open (filename); + if (! f) + return grub_errno; + grub_size_t sz = grub_file_size (f); + char *buf = grub_malloc (sz); + if (! buf) + return grub_errno; + if ((grub_size_t) grub_file_read (f, buf, sz) != sz) + { + grub_free (buf); + grub_file_close (f); + return grub_errno; + } + grub_file_close (f); + + /* Display info on the file and its loaded contents. */ + grub_printf ("Loaded %u bytes from %s.", sz, filename); + + /* Execute some Lua code. */ + int err; + lua_State *L = lua_open (); + luaL_openlibs (L); + + err = luaL_loadstring (L, "print('Hello from a Lua string.')"); + if (err) + { + grub_printf ("Lua load string error: %s\n", lua_tostring (L, -1)); + lua_pop (L, 1); /* Pop the error message. */ + goto end; + } + + err = lua_pcall (L, 0, 0, 0); + if (err) + { + grub_printf ("Lua pcall string chunk error: %s\n", lua_tostring (L, -1)); + lua_pop (L, 1); /* Pop the error message. */ + goto end; + } + + err = luaL_loadbuffer (L, buf, sz, filename); + grub_free (buf); + if (err) + { + grub_printf ("Lua error: %s\n", lua_tostring (L, -1)); + lua_pop (L, 1); /* Pop the error message. */ + goto end; + } + + err = lua_pcall (L, 0, 0, 0); + if (err) + { + grub_printf ("Error in lua_pcall(): %s\n", lua_tostring (L, -1)); + goto end; + } + grub_printf ("Lua chunk executed\n"); + + lua_getglobal (L, "main"); + if (lua_isnoneornil (L, -1)) + { + grub_printf ("Error: symbol 'main' does not exist.\n"); + goto end; + } + + err = lua_pcall (L, 0, 0, 0); + if (err) + { + grub_printf ("Error in lua_pcall(): %s\n", lua_tostring (L, -1)); + goto end; + } + grub_printf ("Lua function 'main' executed\n"); + + grub_lua_load_grub_packages (L); + grub_printf ("GRUB Lua packages loaded\n"); + + lua_getglobal (L, "videotest"); + if (lua_isnoneornil (L, -1)) + { + grub_printf ("Error: symbol 'videotest' does not exist.\n"); + goto end; + } + + err = lua_pcall (L, 0, 0, 0); + if (err) + { + grub_printf ("Error in lua_pcall(): %s\n", lua_tostring (L, -1)); + goto end; + } + grub_printf ("Lua function 'videotest' executed\n"); + +end: + lua_close (L); + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT (luagfx) +{ + (void) mod; /* To stop warning. */ + grub_register_command ("luagfx", + grub_cmd_luagfx, GRUB_COMMAND_FLAG_BOTH, + "luagfx", "Test of Lua graphics", 0); +} + +GRUB_MOD_FINI (luagfx) +{ + grub_unregister_command ("luagfx"); +} === added file 'commands/luagfx.lua' --- commands/luagfx.lua 1970-01-01 00:00:00 +0000 +++ commands/luagfx.lua 2008-08-04 17:10:55 +0000 @@ -0,0 +1,70 @@ +print("This is luagfx.lua, the file chunk.") + +function main() + print("Hello, world!! This is the function 'main' from Lua!") +end + +function delay(ms) + local stop = sys.get_time_ms() + ms + while stop - sys.get_time_ms() > 0 do + end +end + +function videotest() +-- video.set_graphics_mode() + local seed = 42373 + local function rand(min,max) + seed = seed * 97 + 17 * min * max + return seed % (max - min + 1) + min + end + + local strings = + { + "Hello, world!", + "GRUB rocks!", + "What's up?", + "Do the Dew!", + "Lua!" + } + local function new_random_word() + local w={} + w.speed = rand(5,15) + w.x = rand(-200, -100) + w.y = rand(40, 440) + w.text = strings[rand(1, #strings)] + w.color = {r=rand(0,255), g=rand(0,255), b=rand(0,255)} + return w + end + + local words={} + while table.getn(words) < 10 do + table.insert(words, new_random_word()) + end + while not input.checkkey() do + video.fill_rect({r=255, g=255, b=255}, 0, 0, 640, 480) + + -- Draw the words. + for _, w in ipairs(words) do + video.draw_string(w.text, "Helvetica Bold 18", + w.color, w.x, w.y) + end + video.swap_buffers() + + -- Update the word positions and + -- refresh words when they reach the edge. + for i,w in ipairs(words) do + w.x = w.x + w.speed + if w.x >= 640 then + words[i] = new_random_word() + end + end + + -- Delay a moment. + delay(12) + end + + -- Eat the keypress. + input.getkey() +-- video.set_text_mode() +end + === modified file 'commands/sleep.c' --- commands/sleep.c 2008-05-16 20:55:29 +0000 +++ commands/sleep.c 2008-07-04 16:55:48 +0000 @@ -43,15 +43,15 @@ grub_printf ("%d ", n); } -/* Based on grub_millisleep() from kern/misc.c. */ +/* Based on grub_millisleep() from kern/generic/millisleep.c. */ static int grub_interruptible_millisleep (grub_uint32_t ms) { - grub_uint32_t end_at; - - end_at = grub_get_rtc () + grub_div_roundup (ms * GRUB_TICKS_PER_SECOND, 1000); - - while (grub_get_rtc () < end_at) + grub_uint64_t start; + + start = grub_get_time_ms (); + + while (grub_get_time_ms () - start < ms) if (grub_checkkey () >= 0 && GRUB_TERM_ASCII_CHAR (grub_getkey ()) == GRUB_TERM_ESC) return 1; === modified file 'commands/videotest.c' --- commands/videotest.c 2007-07-21 22:32:33 +0000 +++ commands/videotest.c 2008-07-19 18:25:18 +0000 @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -26,15 +27,38 @@ #include #include #include - -static grub_err_t -grub_cmd_videotest (struct grub_arg_list *state __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) -{ +#include +#include +#include /* for grub_vbe_bios_set_display_start () test */ +/* Option array indices. */ +#define ARGINDEX_TEST_TIME 0 +#define ARGINDEX_DOUBLE_BUF 1 + +static const struct grub_arg_option arg_options[] = { + {"time", 't', 0, "Time to run each test, in seconds.", 0, ARG_TYPE_INT}, + {"dbuf", 'd', 0, "Use double buffered graphics.", 0, ARG_TYPE_NONE}, + {0, 0, 0, 0, 0, 0} +}; + +#define DEFAULT_TEST_TIME 5 + +/* Command options -- populated base on command line arguments. */ +struct videotest_options +{ + int test_time; + int double_buffering; +}; + + +static void +basic_video_test (struct videotest_options *vt_opts) +{ + int mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + if (vt_opts->double_buffering) + mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; if (grub_video_setup (1024, 768, GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != GRUB_ERR_NONE) - return grub_errno; + return; grub_getkey (); @@ -44,7 +68,8 @@ unsigned int width; unsigned int height; int i; - struct grub_font_glyph glyph; + grub_font_t font; + struct grub_font_glyph *glyph; struct grub_video_render_target *text_layer; grub_video_color_t palette[16]; @@ -65,8 +90,15 @@ color = grub_video_map_rgb (0, 255, 255); grub_video_fill_rect (color, 100, 100, 100, 100); - grub_font_get_glyph ('*', &glyph); - grub_video_blit_glyph (&glyph, color, 200 ,0); + font = grub_font_get ("Helvetica Bold 14"); + if (! font) + { + grub_error (GRUB_ERR_BAD_FONT, "No font loaded."); + return; + } + + glyph = grub_font_get_glyph (font, '*'); + grub_video_blit_glyph (glyph, color, 200 ,0); grub_video_set_viewport (x + 150, y + 150, width - 150 * 2, height - 150 * 2); @@ -77,18 +109,18 @@ color = grub_video_map_rgb (255, 255, 255); - grub_font_get_glyph ('A', &glyph); - grub_video_blit_glyph (&glyph, color, 16, 16); - grub_font_get_glyph ('B', &glyph); - grub_video_blit_glyph (&glyph, color, 16 * 2, 16); + glyph = grub_font_get_glyph (font, 'A'); + grub_video_blit_glyph (glyph, color, 16, 16); + glyph = grub_font_get_glyph (font, 'B'); + grub_video_blit_glyph (glyph, color, 16 * 2, 16); - grub_font_get_glyph ('*', &glyph); + glyph = grub_font_get_glyph (font, '*'); for (i = 0; i < 16; i++) { color = grub_video_map_color (i); palette[i] = color; - grub_video_blit_glyph (&glyph, color, 16 + i * 16, 32); + grub_video_blit_glyph (glyph, color, 16 + i * 16, 32); } grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); @@ -101,30 +133,1076 @@ 0, 0, width, height); } + grub_video_swap_buffers (); + grub_getkey (); + + /* Test VBE set display start address. */ + /* This should scroll the screen, first vertically, + * then horizontally. The horizontal scrolling seems to + * only have a resolution of about 16 pixels on my VIA Mini-ITX. */ + int vbestatus; + int vbeok = 0; + int vbeerr = 0; + for (i = 0; i < 50; i++) + { + vbestatus = grub_vbe_bios_set_display_start (0, i); + if (vbestatus == GRUB_VBE_STATUS_OK) + vbeok++; + else + vbeerr++; + } + + + grub_getkey (); + + for (i = 0; i < 50; i++) + { + vbestatus = grub_vbe_bios_set_display_start (i, 50); + if (vbestatus == GRUB_VBE_STATUS_OK) + vbeok++; + else + vbeerr++; + } + grub_getkey (); grub_video_delete_render_target (text_layer); - - grub_video_restore (); - - for (i = 0; i < 16; i++) - grub_printf("color %d: %08x\n", i, palette[i]); - - grub_errno = GRUB_ERR_NONE; + grub_video_restore (); + + grub_printf ("VBE set_display_start status: %d\n", vbestatus); + grub_printf ("ok: %d\n", vbeok); + grub_printf ("errors: %d\n", vbeerr); + + grub_errno = GRUB_ERR_NONE; +} + + + +/** + * Simple opaque image blit test. + * Returns the error status in grub_errno. + */ +static void +bitmap_demo (struct videotest_options *vt_opts) +{ + int mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + if (vt_opts->double_buffering) + mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + if (grub_video_setup (1024, 768, mode_type) != GRUB_ERR_NONE) + return; + + grub_video_rect_t view; + grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y, + (unsigned *) &view.width, + (unsigned *) &view.height); + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + const int bm1width = 500, bm1height = 400; + struct grub_video_bitmap *bitmap1; + + if (grub_video_bitmap_create (&bitmap1, bm1width, bm1height, + GRUB_VIDEO_BLIT_FORMAT_RGB_888) + != GRUB_ERR_NONE) + return; + + int offset = 0; + int x; + int y; + grub_uint8_t *data = grub_video_bitmap_get_data (bitmap1); + for (y = 0; y < bm1height; y++) + { + for (x = 0; x < bm1width; x++) + { + data[offset++] = x ^ y; /* red */ + data[offset++] = (x * 3) ^ (y * 3); /* green */ + data[offset++] = (x * 2) ^ (y * 2); /* blue */ + } + } + + /* Blit the entire bitmap in the center of the screen. */ + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_REPLACE, + view.x + (view.width - bm1width) / 2, + view.y + (view.height - bm1height) / 2, + 0, 0, bm1width, bm1height); + grub_video_swap_buffers (); + grub_getkey (); + + /* Blit more copies of the bitmap. */ + /* Upper left. */ + grub_video_blit_bitmap (bitmap1, GRUB_VIDEO_BLIT_REPLACE, + view.x, view.y, 0, 0, bm1width, bm1height); + /* Upper right. */ + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_REPLACE, + view.x + view.width - bm1width, + view.y, 0, 0, bm1width, bm1height); + /* Lower left. */ + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_REPLACE, + view.x, + view.y + view.height - bm1height, + 0, 0, bm1width, bm1height); + /* Lower right. */ + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_REPLACE, + view.x + view.width - bm1width, + view.y + view.height - bm1height, + 0, 0, bm1width, bm1height); + grub_video_swap_buffers (); + grub_getkey (); + + int clearbg = 0; /* Boolean flag: whether to fill background. */ + grub_video_color_t bgcolor = grub_video_map_rgb (16, 16, 96); + + /* Animate the image sliding in. End when a key is pressed. */ + int vscale = 1000; + int velocityx = -5000; + int velocityy = -8000; + int positionx = 100; + int positiony = 300; + + grub_uint32_t frame_count = 0; + grub_uint32_t start_time = grub_get_time_ms (); + + while (grub_checkkey () == -1) + { + /* If the time limit option is set, then check if it's exceeded. */ + if (vt_opts->test_time != 0 + && grub_get_time_ms () >= start_time + vt_opts->test_time * 1000) + break; + + int newx = positionx + velocityx / vscale; + int newy = positiony + velocityy / vscale; + + /* Check collision w/ left */ + if (newx < view.x && velocityx < 0) + { + velocityx = -velocityx; + newx = positionx + velocityx / vscale; + } + /* Check collision w/ right */ + if (newx + bm1width > view.x + view.width && velocityx > 0) + { + velocityx = -velocityx; + newx = positionx + velocityx / vscale; + } + /* Check collision w/ top */ + if (newy < 0 && velocityy < 0) + { + velocityy = -velocityy; + newy = positiony + velocityy / vscale; + } + /* Check collision w/ bottom */ + if (newy + bm1height > view.y + view.height && velocityy > 0) + { + velocityy = -velocityy; + newy = positiony + velocityy / vscale; + } + + positionx = newx; + positiony = newy; + + if (clearbg) + grub_video_fill_rect (bgcolor, view.x, view.y, view.width, + view.height); + + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_REPLACE, + view.x + positionx, + view.y + positiony, 0, 0, bm1width, bm1height); + grub_video_swap_buffers (); + frame_count++; + + /* Acceleration due to gravity... + * note that the y coordinate is increasing downward. */ + velocityy += vscale / 7; + } + + /* Calculate average frame rate. */ + grub_uint32_t duration = grub_get_time_ms () - start_time; + grub_uint32_t fps_x10 = 10 * 1000 * frame_count / duration; + + /* Eat the keystroke. */ + if (grub_checkkey () != -1) + grub_getkey (); + + grub_video_bitmap_destroy (bitmap1); + grub_video_restore (); + + grub_printf ("Average frame rate: %d.%d fps\n", fps_x10 / 10, fps_x10 % 10); + + grub_errno = GRUB_ERR_NONE; +} + + + +/* Configuration settings for a benchmark run. */ +struct benchmark_config +{ + int width; + int height; + unsigned int mode_type; + int use_rgba_bitmaps; +}; + +/* Macro to make the benchmark_configs[] declaration more concise. */ +#define MODE_TYPE_RGB(bpp) (GRUB_VIDEO_MODE_TYPE_RGB \ + | ((bpp) << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)) + +/* The video configurations to use for the benchmark. */ +static struct benchmark_config benchmark_configs[] = { + {320, 200, MODE_TYPE_RGB (0), 0}, + {640, 480, MODE_TYPE_RGB (0), 0}, + {1024, 768, MODE_TYPE_RGB (0), 0}, + {1024, 768, MODE_TYPE_RGB (0), 1}, +}; + +#define NUM_BENCHMARK_CONFIGS (sizeof(benchmark_configs) \ + / sizeof(benchmark_configs[0])) + +struct benchmark_result +{ + /* If set to 1, the test was able to run successfully; + * 0 means there was an error. */ + int test_passed; + + /* Bits per pixel for the video mode used. */ + int bpp; + + /* All fps are in fps * 10 to achieve one decimal place. */ + /* Set to 0 to indicate the test could not be run. */ + grub_int32_t fill_fps; + grub_int32_t blit_fps; + grub_int32_t blend_fps; +}; + +#define BENCHMARK_RESULT_FPS_SCALE 10 + +static void +move_rectangle_one_step (int *x, int *y, + int width, int height, + int *vx, int *vy, const grub_video_rect_t * bounds) +{ + int newx = *x + *vx; + int newy = *y + *vy; + + /* Check collision w/ left */ + if (newx < bounds->x && *vx < 0) + { + *vx = -*vx; + newx = *x + *vx; + } + /* Check collision w/ right */ + if (newx + width > bounds->x + bounds->width && *vx > 0) + { + *vx = -*vx; + newx = *x + *vx; + } + /* Check collision w/ top */ + if (newy < 0 && *vy < 0) + { + *vy = -*vy; + newy = *y + *vy; + } + /* Check collision w/ bottom */ + if (newy + height > bounds->y + bounds->height && *vy > 0) + { + *vy = -*vy; + newy = *y + *vy; + } + + *x = newx; + *y = newy; +} + +/** + * Run the benchmark test for a particular video mode, which is specified + * by ``*config``. The results of the test are stored in ``*result``. + */ +static void +do_benchmark (const struct benchmark_config *config, + struct benchmark_result *result, + struct videotest_options *vt_opts) +{ + struct grub_video_mode_info modeinfo; + + result->test_passed = 0; + result->fill_fps = 0; + result->blit_fps = 0; + result->blend_fps = 0; + result->bpp = 0; + + int mode_type = config->mode_type; + if (vt_opts->double_buffering) + mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + if (grub_video_setup (config->width, config->height, + mode_type) != GRUB_ERR_NONE) + return; + + if (grub_video_get_info (&modeinfo) == GRUB_ERR_NONE) + result->bpp = modeinfo.bpp; + + /* Full screen bitmap blit test. */ + + grub_video_rect_t view; + grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y, + (unsigned *) &view.width, + (unsigned *) &view.height); + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + + /* For measuring timing. */ + grub_uint32_t frame_count; + grub_uint64_t start_time; + grub_uint64_t desired_stop_time; + grub_uint32_t duration; + + + /*** FILL TEST ***/ + + /* Alternates between 0 and 1 to change the color. */ + int color_flag = 0; + grub_video_color_t fillcolors[2]; + fillcolors[0] = grub_video_map_rgb (0, 0, 255); + fillcolors[1] = grub_video_map_rgb (255, 255, 0); + + frame_count = 0; + start_time = grub_get_time_ms (); + desired_stop_time = start_time + vt_opts->test_time * 1000; + + while (grub_get_time_ms () < desired_stop_time && grub_checkkey () == -1) + { + grub_video_fill_rect (fillcolors[color_flag], + view.x, view.y, view.width, view.height); + grub_video_swap_buffers (); + color_flag ^= 1; + frame_count++; + } + if (grub_checkkey () != -1) + grub_getkey (); /* Eat the keypress, if there was one. */ + + /* Calculate average frame rate. */ + duration = grub_get_time_ms () - start_time; + if (duration == 0) + { + result->fill_fps = 0; + } + else + { + result->fill_fps = + (BENCHMARK_RESULT_FPS_SCALE * 1000 + * frame_count / duration); + } + + + /*** BLIT TEST ***/ + + /* Generate two bitmaps, the same size as the screen. */ + const int bitmapwidth = view.width, bitmapheight = view.height; + struct grub_video_bitmap *bitmap1; + struct grub_video_bitmap *bitmap2; + enum grub_video_blit_format bitmap_format = config->use_rgba_bitmaps + ? GRUB_VIDEO_BLIT_FORMAT_RGBA_8888 : GRUB_VIDEO_BLIT_FORMAT_RGB_888; + + if (grub_video_bitmap_create (&bitmap1, bitmapwidth, bitmapheight, + bitmap_format) != GRUB_ERR_NONE) + return; + + if (grub_video_bitmap_create (&bitmap2, bitmapwidth, bitmapheight, + bitmap_format) != GRUB_ERR_NONE) + { + grub_video_bitmap_destroy (bitmap1); + return; + } + + int offset; + int x; + int y; + grub_uint8_t *data; + + offset = 0; + data = grub_video_bitmap_get_data (bitmap1); + for (y = 0; y < bitmapheight; y++) + { + for (x = 0; x < bitmapwidth; x++) + { + data[offset++] = x ^ y; /* red */ + data[offset++] = (x * 3) ^ (y * 3); /* green */ + data[offset++] = (x * 2) ^ (y * 2); /* blue */ + if (config->use_rgba_bitmaps) + data[offset++] = 255; + } + } + + offset = 0; + data = grub_video_bitmap_get_data (bitmap2); + for (y = 0; y < bitmapheight; y++) + { + for (x = 0; x < bitmapwidth; x++) + { + data[offset++] = x + y; /* red */ + data[offset++] = x * x + y * y; /* green */ + data[offset++] = x * x / 4 + y * y / 4; /* blue */ + if (config->use_rgba_bitmaps) + data[offset++] = 255; + } + } + + + /* Now do the blit test, alternating between the two bitmaps. */ + frame_count = 0; + start_time = grub_get_time_ms (); + desired_stop_time = start_time + vt_opts->test_time * 1000; + + int cur = 0; /* Which bitmap to draw this frame. */ + while (grub_get_time_ms () < desired_stop_time && grub_checkkey () == -1) + { + struct grub_video_bitmap *current_bitmap = cur == 0 ? bitmap1 : bitmap2; + grub_video_blit_bitmap (current_bitmap, + GRUB_VIDEO_BLIT_REPLACE, + view.x, view.y, 0, 0, bitmapwidth, + bitmapheight); + grub_video_swap_buffers (); + frame_count++; + cur ^= 1; + } + if (grub_checkkey () != -1) + grub_getkey (); /* Eat the keypress, if there was once. */ + + /* Calculate average frame rate. */ + duration = grub_get_time_ms () - start_time; + if (duration == 0) + { + result->blit_fps = 0; + } + else + { + result->blit_fps = + (BENCHMARK_RESULT_FPS_SCALE * 1000 + * frame_count / duration); + } + + grub_video_bitmap_destroy (bitmap2); + grub_video_bitmap_destroy (bitmap1); + + + + /*** BLEND TEST ***/ + + /* Generate two bitmaps, with alpha translucency. */ + const int bbw = view.width * 2 / 3; + const int bbh = view.height * 2 / 3; + + if (grub_video_bitmap_create (&bitmap1, bbw, bbh, + GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) != + GRUB_ERR_NONE) + return; + + if (grub_video_bitmap_create (&bitmap2, bbw, bbh, + GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) != + GRUB_ERR_NONE) + { + grub_video_bitmap_destroy (bitmap1); + return; + } + + offset = 0; + data = grub_video_bitmap_get_data (bitmap1); + for (y = 0; y < bbh; y++) + { + for (x = 0; x < bbw; x++) + { + /* Calculate a to be increasing away from the center. */ + int dx = 256 * (x - bbw / 2) / (bbw / 2); + int dy = 256 * (y - bbh / 2) / (bbh / 2); + int a = dx * dx + dy * dy; + /* range for a = 0 .. 2*(256^2) = 2*2^16 = 2^17 */ + a >>= 17 - 8; /* Make range 0..256. */ + if (a > 255) + a = 255; + + data[offset++] = x ^ y; /* red */ + data[offset++] = (x * 3) ^ (y * 3); /* green */ + data[offset++] = (x * 2) ^ (y * 2); /* blue */ + data[offset++] = 255 - a; + } + } + + offset = 0; + data = grub_video_bitmap_get_data (bitmap2); + for (y = 0; y < bbh; y++) + { + for (x = 0; x < bbw; x++) + { + data[offset++] = x + y; /* red */ + data[offset++] = x * x + y * y; /* green */ + data[offset++] = x * x / 4 + y * y / 4; /* blue */ + data[offset++] = 255; + } + } + + frame_count = 0; + start_time = grub_get_time_ms (); + desired_stop_time = start_time + vt_opts->test_time * 1000; + + + grub_video_color_t bgcolor = grub_video_map_rgb (80, 80, 80); + + /* Bitmap locations. */ + int b1x = 0; + int b1y = 0; + int b2x = view.width - bbw; + int b2y = 0; + /* Bitmap velocities. */ + int b1vx = 8; + int b1vy = 12; + int b2vx = -10; + int b2vy = 9; + + while (grub_get_time_ms () < desired_stop_time && grub_checkkey () == -1) + { + move_rectangle_one_step (&b1x, &b1y, bbw, bbh, &b1vx, &b1vy, &view); + move_rectangle_one_step (&b2x, &b2y, bbw, bbh, &b2vx, &b2vy, &view); + grub_video_fill_rect (bgcolor, view.x, view.y, view.width, view.height); + grub_video_blit_bitmap (bitmap2, + GRUB_VIDEO_BLIT_BLEND, + b2x, b2y, 0, 0, bbw, bbh); + grub_video_blit_bitmap (bitmap1, + GRUB_VIDEO_BLIT_BLEND, + b1x, b1y, 0, 0, bbw, bbh); + grub_video_swap_buffers (); + frame_count++; + cur ^= 1; + } + if (grub_checkkey () != -1) + grub_getkey (); /* Eat the keypress, if there was once. */ + + /* Calculate average frame rate. */ + duration = grub_get_time_ms () - start_time; + if (duration == 0) + { + result->blend_fps = 0; + } + else + { + result->blend_fps = + (BENCHMARK_RESULT_FPS_SCALE * 1000 + * frame_count / duration); + } + + grub_video_bitmap_destroy (bitmap2); + grub_video_bitmap_destroy (bitmap1); + + grub_video_restore (); + result->test_passed = 1; +} + +/** + * Run a benchmark test in a series of video modes. + * The results are reported in tabular form. This will be helpful to + * determine how effective various optimizations are. + */ +static void +benchmark_test (struct videotest_options *vt_opts) +{ + unsigned int i; + struct benchmark_result results[NUM_BENCHMARK_CONFIGS]; + + /* Set option default values. */ + if (vt_opts->test_time == 0) + vt_opts->test_time = DEFAULT_TEST_TIME; + + /* Run benchmarks. */ + for (i = 0; i < NUM_BENCHMARK_CONFIGS; i++) + { + grub_error_push (); + do_benchmark (&benchmark_configs[i], &results[i], vt_opts); + } + + grub_print_error (); + + /* Display results. */ + grub_printf ("Benchmark results (in frames/s):\n"); + grub_printf ("(W=Width, H=Height, B=Bits per pixel,\n" + " T=Mode Type, A=Bitmap Alpha)\n"); + grub_printf (" W H B T A FILL BLIT BLEND\n"); + for (i = 0; i < NUM_BENCHMARK_CONFIGS; i++) + { + struct benchmark_config *c = &benchmark_configs[i]; + struct benchmark_result *r = &results[i]; + + if (r->test_passed) + { + grub_printf ("%4dx%4d %2d %d %d: %4d.%d %4d.%d %4d.%d\n", + c->width, c->height, r->bpp, + c->mode_type & 0x0F, + c->use_rgba_bitmaps, + r->fill_fps / BENCHMARK_RESULT_FPS_SCALE, + r->fill_fps % BENCHMARK_RESULT_FPS_SCALE, + r->blit_fps / BENCHMARK_RESULT_FPS_SCALE, + r->blit_fps % BENCHMARK_RESULT_FPS_SCALE, + r->blend_fps / BENCHMARK_RESULT_FPS_SCALE, + r->blend_fps % BENCHMARK_RESULT_FPS_SCALE); + } + else + { + grub_printf ("%4dx%4d %2d %d Not supported.\n", + c->width, c->height, + ((c->mode_type & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK) + >> GRUB_VIDEO_MODE_TYPE_DEPTH_POS), + c->mode_type & 0x0F); + } + } + + grub_errno = GRUB_ERR_NONE; +} + + +/** + * Test time functions. + */ +static void +clock_test (struct videotest_options *vt_opts) +{ + int mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + if (vt_opts->double_buffering) + mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + if (grub_video_setup (640, 480, mode_type) != GRUB_ERR_NONE) + return; + + grub_video_rect_t view; + grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y, + (unsigned *) &view.width, + (unsigned *) &view.height); + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + /* Draw a progress bar that animates in sync with time. */ + + grub_video_rect_t bar_frame; + bar_frame.width = view.width - 2 * view.width / 10; + bar_frame.height = view.height / 20; + bar_frame.x = view.x + view.width / 10; + bar_frame.y = view.y + view.height - bar_frame.height - view.height / 10; + + grub_video_color_t bgcolor = grub_video_map_rgb (50, 50, 50); + grub_video_color_t framecolor = grub_video_map_rgb (255, 255, 255); + grub_video_color_t barbgcolor = grub_video_map_rgb (0, 0, 128); + grub_video_color_t barcolor = grub_video_map_rgb (100, 100, 255); + + grub_uint32_t frame_count; + grub_uint64_t start_time; + grub_uint64_t barstart; + grub_uint32_t barlength = 1000; + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + frame_count = 0; + start_time = grub_get_time_ms (); + barstart = grub_get_time_ms (); + + while (grub_checkkey () == -1) + { + grub_uint64_t now; + grub_uint32_t bartime; + now = grub_get_time_ms (); + /* If the time limit option is set, then check if it's exceeded. */ + if (vt_opts->test_time != 0 + && now >= start_time + vt_opts->test_time * 1000) + break; + bartime = now - barstart; + if (bartime > barlength) + { + barstart = grub_get_time_ms (); /* Start over. */ + bartime = barlength; + } + + /* Clear screen. */ + grub_video_fill_rect (bgcolor, view.x, view.y, view.width, view.height); + + /* Border. */ + grub_video_fill_rect (framecolor, + bar_frame.x - 1, bar_frame.y - 1, + bar_frame.width + 2, bar_frame.height + 2); + + /* Bar background. */ + int barwidth = bar_frame.width * bartime / barlength; + grub_video_fill_rect (barbgcolor, bar_frame.x + barwidth, + bar_frame.y, bar_frame.width - barwidth, + bar_frame.height); + /* Bar foreground. */ + grub_video_fill_rect (barcolor, bar_frame.x, bar_frame.y, + barwidth, bar_frame.height); + grub_video_swap_buffers (); + frame_count++; + } + + grub_uint32_t duration = grub_get_time_ms () - start_time; + grub_uint32_t fps_x10 = 10 * 1000 * frame_count / duration; + + if (grub_checkkey () != -1) + grub_getkey (); /* Eat the keypress, if there was one. */ + grub_video_restore (); + grub_printf ("Average frame rate: %d.%d fps\n", fps_x10 / 10, fps_x10 % 10); +} + + +/** + * Test double buffering. + */ +static void +doublebuf_test (struct videotest_options *vt_opts) +{ + int mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + if (vt_opts->double_buffering) + mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + if (grub_video_setup (640, 480, mode_type) != GRUB_ERR_NONE) + return; + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + grub_video_color_t red = grub_video_map_rgb (255, 50, 50); + grub_video_color_t yellow = grub_video_map_rgb (255, 255, 0); + grub_video_color_t green = grub_video_map_rgb (20, 255, 20); + grub_video_color_t blue = grub_video_map_rgb (50, 50, 255); + grub_video_color_t black = grub_video_map_rgb (0, 0, 0); + grub_video_color_t bgcolor = grub_video_map_rgb (255, 255, 255); + + grub_video_fill_rect (bgcolor, 0, 0, 640, 480); + grub_video_fill_rect (red, 100, 100, 200, 200); + grub_video_swap_buffers (); + grub_getkey (); + + grub_video_fill_rect (bgcolor, 0, 0, 640, 480); + grub_video_fill_rect (yellow, 120, 120, 200, 200); + grub_video_swap_buffers (); + grub_getkey (); + + grub_video_fill_rect (bgcolor, 0, 0, 640, 480); + grub_video_fill_rect (green, 140, 140, 200, 200); + grub_video_swap_buffers (); + grub_getkey (); + + grub_video_fill_rect (bgcolor, 0, 0, 640, 480); + grub_video_fill_rect (blue, 160, 160, 200, 200); + grub_video_swap_buffers (); + grub_getkey (); + + grub_video_fill_rect (bgcolor, 0, 0, 640, 480); + grub_video_fill_rect (black, 180, 180, 200, 200); + grub_video_swap_buffers (); + grub_getkey (); + + grub_video_restore (); +} + + +/** + * Test text rendering. + */ +static void +text_test (struct videotest_options *vt_opts) +{ + grub_video_color_t color; + const char *s; + int view_x; + int view_y; + int view_width; + int view_height; + int xpos; + int ypos; + int i; + grub_font_t font1; + grub_font_t font2; + grub_font_t font3; + + int mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + if (vt_opts->double_buffering) + mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + if (grub_video_setup (1024, 768, mode_type) != GRUB_ERR_NONE) + return; + + grub_video_get_viewport ((unsigned int *) &view_x, + (unsigned int *) &view_y, + (unsigned int *) &view_width, + (unsigned int *) &view_height); + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + if (!(font1 = grub_font_get ("New Century Schoolbook 24")) + || !(font2 = grub_font_get ("Helvetica Bold 14")) + || !(font3 = grub_font_get ("Helvetica 10"))) + { + grub_video_restore (); + grub_error (GRUB_ERR_BAD_FONT, "No font loaded."); + return; + } + + color = grub_video_map_rgb (0, 0, 0); + grub_video_fill_rect (color, 0, 0, view_width, view_height); + + color = grub_video_map_rgb (255, 0, 0); + grub_video_fill_rect (color, 0, 0, 100, 100); + + color = grub_video_map_rgb (0, 255, 255); + grub_video_fill_rect (color, 100, 100, 100, 100); + + color = grub_video_map_rgb (255, 255, 255); + + xpos = 10; + ypos = 30; + s = "Hello, World!"; + for (i = 0; i < 40; i++) + { + if (xpos + grub_font_get_string_width (font1, s) >= view_width) + { + /* The string will wrap; go to the beginning of the next line. */ + xpos = 10; + ypos += (grub_font_get_descent (font1) + + grub_font_get_ascent (font1)); + } + grub_video_draw_string ("Hello, World!", + font1, grub_video_map_rgb (255, 255, 0), + view_x + xpos, view_y + ypos); + + xpos += grub_font_get_string_width (font1, s); + } + + xpos = 300; + ypos = 450; + grub_video_draw_string (grub_font_get_name (font1), + font1, grub_video_map_rgb (255, 255, 255), + view_x + xpos, view_y + ypos); + ypos += grub_font_get_descent (font1) + grub_font_get_ascent (font2) + 2; + grub_video_draw_string (grub_font_get_name (font2), + font2, grub_video_map_rgb (255, 255, 255), + view_x + xpos, view_y + ypos); + ypos += grub_font_get_descent (font2) + grub_font_get_ascent (font3) + 2; + grub_video_draw_string (grub_font_get_name (font3), + font3, grub_video_map_rgb (255, 255, 255), + view_x + xpos, view_y + ypos); + + grub_video_swap_buffers (); + grub_getkey (); + grub_video_restore (); + grub_errno = GRUB_ERR_NONE; +} + + +/** + * Test bitmap scaling. + */ +static void +scale_test (struct videotest_options *vt_opts) +{ + int mode_type = GRUB_VIDEO_MODE_TYPE_RGB; + if (vt_opts->double_buffering) + mode_type |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + if (grub_video_setup (1024, 768, mode_type) != GRUB_ERR_NONE) + return; + + grub_errno = GRUB_ERR_NONE; + grub_video_rect_t view; + grub_video_get_viewport ((unsigned *) &view.x, (unsigned *) &view.y, + (unsigned *) &view.width, + (unsigned *) &view.height); + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + grub_font_t font; + if (!(font = grub_font_get ("Helvetica 10"))) + { + grub_video_restore (); + grub_error (GRUB_ERR_BAD_FONT, "No font loaded."); + return; + } + + grub_video_color_t color; + + int text_y = 0; + int text_height = 25; + + color = grub_video_map_rgb (44, 44, 200); + grub_video_fill_rect (color, view.x, view.y, view.width, view.height); + color = grub_video_map_rgb (255, 255, 255); + + grub_video_draw_string ("Loading image", + font, color, 10, text_y += text_height); + + enum grub_video_bitmap_scale_method scale_method = + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST; + const char *bitmap_name = "/boot/images/wallpaper.tga"; + struct grub_video_bitmap *bitmap; + grub_video_bitmap_load (&bitmap, bitmap_name); + if (grub_errno != GRUB_ERR_NONE) + { + grub_video_draw_string ("Error loading bitmap", + font, color, 10, text_y += text_height); + } + else + { + grub_video_draw_string ("Original image", + font, color, 10, text_y += text_height); + grub_video_blit_bitmap (bitmap, GRUB_VIDEO_BLIT_BLEND, + 400, text_y - 10, + 0, 0, grub_video_bitmap_get_width (bitmap), + grub_video_bitmap_get_height (bitmap)); + + struct grub_video_bitmap *bitmap2; + if (grub_video_bitmap_create_scaled (&bitmap2, 40, 40, + bitmap, + scale_method) + != GRUB_ERR_NONE) + { + grub_video_draw_string ("Error scaling down bitmap", + font, color, 10, text_y += text_height); + } + else + { + grub_video_draw_string ("Scaled down version", + font, color, 10, text_y += text_height); + grub_video_blit_bitmap (bitmap2, GRUB_VIDEO_BLIT_BLEND, + 400, text_y + 100, + 0, 0, grub_video_bitmap_get_width (bitmap2), + grub_video_bitmap_get_height (bitmap2)); + grub_video_bitmap_destroy (bitmap2); + } + + struct grub_video_bitmap *bitmap3; + if (grub_video_bitmap_create_scaled (&bitmap3, 500, 300, bitmap, + scale_method) + != GRUB_ERR_NONE) + { + grub_video_draw_string ("Error scaling up bitmap", + font, color, 10, text_y += text_height); + } + else + { + grub_video_draw_string ("Scaled up version", + font, color, 10, text_y += text_height); + grub_video_blit_bitmap (bitmap3, GRUB_VIDEO_BLIT_BLEND, + 400, text_y + 50, + 0, 0, grub_video_bitmap_get_width (bitmap3), + grub_video_bitmap_get_height (bitmap3)); + grub_video_bitmap_destroy (bitmap3); + } + } + + grub_video_swap_buffers (); + grub_getkey (); + grub_video_bitmap_destroy (bitmap); + grub_video_restore (); +} + + +/** Print a list of the available tests. */ +static void list_tests (void); + +/** + * Video test command. Takes an argument specifying the test to run. + */ +static grub_err_t +grub_cmd_videotest (struct grub_arg_list *state, int argc, char **args) +{ + int i; + struct videotest_options vt_opts; + /* Pointer to the test function. */ + void (*test_func) (struct videotest_options *) = NULL; + + vt_opts.test_time = + state[ARGINDEX_TEST_TIME].set + ? grub_strtoul (state[ARGINDEX_TEST_TIME].arg, 0, 0) : 0; + vt_opts.double_buffering = state[ARGINDEX_DOUBLE_BUF].set; + + /* Parse command line arguments to determine the test to run. */ + for (i = 0; i < argc; i++) + { + char *arg = args[i]; + if (grub_strcmp (arg, "list") == 0) + { + list_tests (); + return GRUB_ERR_NONE; + } + else if (grub_strcmp (arg, "basic") == 0) + { + test_func = basic_video_test; + } + else if (grub_strcmp (arg, "bench") == 0) + { + test_func = benchmark_test; + } + else if (grub_strcmp (arg, "bitmaps") == 0) + { + test_func = bitmap_demo; + } + else if (grub_strcmp (arg, "clock") == 0) + { + test_func = clock_test; + } + else if (grub_strcmp (arg, "doublebuf") == 0) + { + test_func = doublebuf_test; + } + else if (grub_strcmp (arg, "text") == 0) + { + test_func = text_test; + } + else if (grub_strcmp (arg, "scale") == 0) + { + test_func = scale_test; + } + else + { + grub_printf ("Error: Unknown test `%s'\n", arg); + grub_errno = GRUB_ERR_BAD_ARGUMENT; + return grub_errno; + } + } + + if (test_func == NULL) + { + grub_printf ("Usage: videotest TESTNAME Run a test.\n"); + grub_printf (" videotest list List available tests.\n"); + grub_printf ("\n"); + list_tests (); + } + else + { + test_func (&vt_opts); + } + return grub_errno; } -GRUB_MOD_INIT(videotest) +static void +list_tests (void) +{ + grub_printf ("Available tests\n"); + grub_printf ("===============\n"); + grub_printf ("basic Basic video test with filled rectangles,\n"); + grub_printf (" offscreen rendering targets, some text.\n"); + grub_printf ("bench Run a performance benchmark.\n"); + grub_printf ("bitmaps Test generating and blitting bitmaps.\n"); + grub_printf ("clock Test time functions w/ animated progress bar.\n"); + grub_printf ("doublebuf Test double buffering.\n"); + grub_printf ("text Test text rendering.\n"); + grub_printf ("scale Test image scaling.\n"); + grub_printf ("\n"); +} + + +GRUB_MOD_INIT (videotest) { grub_register_command ("videotest", grub_cmd_videotest, GRUB_COMMAND_FLAG_BOTH, - "videotest", - "Test video subsystem", - 0); + "videotest TEST", + "Run the specified video subsystem test.", + arg_options); } -GRUB_MOD_FINI(videotest) +GRUB_MOD_FINI (videotest) { grub_unregister_command ("videotest"); } === modified file 'conf/common.rmk' --- conf/common.rmk 2008-08-01 03:06:55 +0000 +++ conf/common.rmk 2008-08-04 17:10:55 +0000 @@ -274,6 +274,7 @@ cmp.mod cat.mod help.mod font.mod search.mod \ loopback.mod fs_uuid.mod configfile.mod echo.mod \ terminfo.mod test.mod blocklist.mod hexdump.mod \ + gfxmenu.mod \ read.mod sleep.mod loadenv.mod crc.mod # For hello.mod. @@ -281,6 +282,16 @@ hello_mod_CFLAGS = $(COMMON_CFLAGS) hello_mod_LDFLAGS = $(COMMON_LDFLAGS) +# For gfxmenu.mod. +gfxmenu_mod_SOURCES = \ + gfxmenu/gfxmenu.c \ + gfxmenu/model.c \ + gfxmenu/view.c \ + gfxmenu/widget-box.c \ + gfxmenu/stringutil.c +gfxmenu_mod_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/lua +gfxmenu_mod_LDFLAGS = $(COMMON_LDFLAGS) + # For boot.mod. boot_mod_SOURCES = commands/boot.c boot_mod_CFLAGS = $(COMMON_CFLAGS) @@ -317,7 +328,7 @@ help_mod_LDFLAGS = $(COMMON_LDFLAGS) # For font.mod. -font_mod_SOURCES = font/manager.c +font_mod_SOURCES = font/font_cmd.c font/font.c font/loader.c font_mod_CFLAGS = $(COMMON_CFLAGS) font_mod_LDFLAGS = $(COMMON_LDFLAGS) @@ -382,7 +393,9 @@ crc_mod_LDFLAGS = $(COMMON_LDFLAGS) # Misc. -pkglib_MODULES += gzio.mod bufio.mod elf.mod +pkglib_MODULES += gzio.mod bufio.mod elf.mod \ + lua.mod luacmd.mod \ + luagfx.mod luagrub.mod # For elf.mod. elf_mod_SOURCES = kern/elf.c @@ -398,3 +411,32 @@ bufio_mod_SOURCES = io/bufio.c bufio_mod_CFLAGS = $(COMMON_CFLAGS) bufio_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For lua.mod. +lua_mod_SOURCES = lua/lapi.c lua/lcode.c lua/ldebug.c lua/ldo.c lua/ldump.c \ + lua/lfunc.c lua/lgc.c lua/llex.c lua/lmem.c lua/lobject.c \ + lua/lopcodes.c lua/lparser.c lua/lstate.c lua/lstring.c \ + lua/ltable.c lua/ltm.c lua/lundump.c lua/lvm.c lua/lzio.c \ + lua/lauxlib.c lua/linit.c lua/lbaselib.c lua/ldblib.c lua/ltablib.c \ + lua/lstrlib.c +lua_mod_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/lua -DUSE_GRUB_LIB +lua_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For luacmd.mod. +luacmd_mod_SOURCES = lua/luacmd.c lua/lua.c +# Omitted lua/liolib.c and lua/loslib.c because they need to be ported to +# use the GRUB library. +luacmd_mod_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/lua \ + -DUSE_GRUB_LIB -DGRUB_COMMAND +luacmd_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For luagfx.mod. +luagfx_mod_SOURCES = commands/luagfx.c +luagfx_mod_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/lua +luagfx_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For luagrub.mod. +luagrub_mod_SOURCES = lib/luagrub.c +luagrub_mod_CFLAGS = $(COMMON_CFLAGS) -I$(srcdir)/lua +luagrub_mod_LDFLAGS = $(COMMON_LDFLAGS) + === modified file 'conf/i386-coreboot.rmk' --- conf/i386-coreboot.rmk 2008-08-02 11:17:44 +0000 +++ conf/i386-coreboot.rmk 2008-08-03 03:57:46 +0000 @@ -16,6 +16,7 @@ kern/main.c kern/device.c \ kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \ + kern/time.c \ kern/i386/dl.c kern/parser.c kern/partition.c \ kern/env.c \ term/i386/pc/console.c \ === modified file 'conf/i386-efi.rmk' --- conf/i386-efi.rmk 2008-07-27 12:51:30 +0000 +++ conf/i386-efi.rmk 2008-07-28 16:26:46 +0000 @@ -84,7 +84,10 @@ kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \ kern/i386/dl.c kern/i386/efi/init.c kern/parser.c kern/partition.c \ kern/env.c symlist.c kern/efi/efi.c kern/efi/init.c kern/efi/mm.c \ - term/efi/console.c disk/efi/efidisk.c + term/efi/console.c disk/efi/efidisk.c \ + kern/i386/tsc.c \ + kern/generic/rtc_get_time_ms.c \ + kern/generic/millisleep.c kernel_mod_HEADERS = arg.h boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ partition.h pc_partition.h rescue.h symbol.h term.h time.h types.h \ === modified file 'conf/i386-ieee1275.rmk' --- conf/i386-ieee1275.rmk 2008-08-02 11:17:44 +0000 +++ conf/i386-ieee1275.rmk 2008-08-03 03:57:46 +0000 @@ -19,6 +19,7 @@ kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \ kern/i386/dl.c kern/parser.c kern/partition.c \ kern/env.c \ + kern/generic/millisleep.c \ kern/ieee1275/ieee1275.c \ term/ieee1275/ofconsole.c term/i386/pc/at_keyboard.c \ disk/ieee1275/ofdisk.c \ === modified file 'conf/i386-pc.rmk' --- conf/i386-pc.rmk 2008-07-27 12:51:30 +0000 +++ conf/i386-pc.rmk 2008-07-28 17:02:56 +0000 @@ -42,13 +42,19 @@ kernel_img_SOURCES = kern/i386/pc/startup.S kern/main.c kern/device.c \ kern/disk.c kern/dl.c kern/file.c kern/fs.c kern/err.c \ kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \ + kern/time.c \ kern/i386/dl.c kern/i386/pc/init.c kern/parser.c kern/partition.c \ + kern/i386/tsc.c \ + kern/generic/rtc_get_time_ms.c \ + kern/generic/millisleep.c \ kern/env.c \ + kern/menu_viewer.c \ term/i386/pc/console.c \ symlist.c kernel_img_HEADERS = arg.h boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ partition.h pc_partition.h rescue.h symbol.h term.h time.h types.h \ + menu_viewer.h \ machine/biosdisk.h machine/boot.h machine/console.h machine/init.h \ machine/memory.h machine/loader.h machine/vga.h machine/vbe.h machine/kernel.h kernel_img_CFLAGS = $(COMMON_CFLAGS) @@ -246,7 +252,7 @@ play_mod_LDFLAGS = $(COMMON_LDFLAGS) # For video.mod. -video_mod_SOURCES = video/video.c +video_mod_SOURCES = video/video.c video/setmode.c video_mod_CFLAGS = $(COMMON_CFLAGS) video_mod_LDFLAGS = $(COMMON_LDFLAGS) @@ -261,7 +267,10 @@ videotest_mod_LDFLAGS = $(COMMON_LDFLAGS) # For bitmap.mod -bitmap_mod_SOURCES = video/bitmap.c +bitmap_mod_SOURCES = video/bitmap.c \ + video/bitmap_scale_nn.c \ + video/bitmap_scale_bilinear.c \ + video/bitmap_scale.c bitmap_mod_CFLAGS = $(COMMON_CFLAGS) bitmap_mod_LDFLAGS = $(COMMON_LDFLAGS) === modified file 'conf/powerpc-ieee1275.rmk' --- conf/powerpc-ieee1275.rmk 2008-08-02 11:17:44 +0000 +++ conf/powerpc-ieee1275.rmk 2008-08-03 03:57:46 +0000 @@ -85,6 +85,7 @@ kern/ieee1275/init.c term/ieee1275/ofconsole.c \ kern/ieee1275/openfw.c disk/ieee1275/ofdisk.c \ kern/parser.c kern/partition.c kern/env.c kern/powerpc/dl.c \ + kern/generic/millisleep.c \ symlist.c kern/powerpc/cache.S kernel_elf_HEADERS = grub/powerpc/ieee1275/ieee1275.h kernel_elf_CFLAGS = $(COMMON_CFLAGS) === modified file 'conf/sparc64-ieee1275.rmk' --- conf/sparc64-ieee1275.rmk 2008-06-19 00:04:59 +0000 +++ conf/sparc64-ieee1275.rmk 2008-07-03 14:16:34 +0000 @@ -73,6 +73,7 @@ kern/rescue.c kern/term.c term/ieee1275/ofconsole.c \ kern/sparc64/ieee1275/openfw.c disk/ieee1275/ofdisk.c \ kern/partition.c kern/env.c kern/sparc64/dl.c symlist.c \ + kern/generic/millisleep.c kern/generic/get_time_ms.c \ kern/sparc64/cache.S kern/parser.c kernel_elf_HEADERS = grub/sparc64/ieee1275/ieee1275.h kernel_elf_CFLAGS = $(COMMON_CFLAGS) @@ -195,7 +196,7 @@ cat_mod_LDFLAGS = $(COMMON_LDFLAGS) # For font.mod. -font_mod_SOURCES = font/manager.c +font_mod_SOURCES = font/font_cmd.c font/font.c font/loader.c font_mod_CFLAGS = $(COMMON_CFLAGS) font_mod_LDFLAGS = $(COMMON_LDFLAGS) === modified file 'conf/x86_64-efi.rmk' --- conf/x86_64-efi.rmk 2008-07-27 12:51:30 +0000 +++ conf/x86_64-efi.rmk 2008-07-28 16:23:46 +0000 @@ -87,6 +87,7 @@ kern/misc.c kern/mm.c kern/loader.c kern/rescue.c kern/term.c \ kern/x86_64/dl.c kern/i386/efi/init.c kern/parser.c kern/partition.c \ kern/env.c symlist.c kern/efi/efi.c kern/efi/init.c kern/efi/mm.c \ + kern/generic/millisleep.c kern/generic/rtc_get_time_ms.c \ term/efi/console.c disk/efi/efidisk.c kernel_mod_HEADERS = arg.h boot.h cache.h device.h disk.h dl.h elf.h elfload.h \ env.h err.h file.h fs.h kernel.h loader.h misc.h mm.h net.h parser.h \ === modified file 'config.h.in' --- config.h.in 2008-07-13 00:55:15 +0000 +++ config.h.in 2008-07-19 21:46:40 +0000 @@ -113,10 +113,37 @@ /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS +/* Define for large files, on AIX-style hosts. */ +#undef _LARGE_FILES + +/* Define to 1 if on MINIX. */ +#undef _MINIX + +/* Define to 2 if the system does not provide POSIX.1 features except with + this defined. */ +#undef _POSIX_1_SOURCE + +/* Define to 1 if you need to in order for `stat' and other things to work. */ +#undef _POSIX_SOURCE + +/* Enable extensions on AIX 3, Interix. */ +#ifndef _ALL_SOURCE +# undef _ALL_SOURCE +#endif /* Enable GNU extensions on systems that have them. */ #ifndef _GNU_SOURCE # undef _GNU_SOURCE #endif +/* Enable threading extensions on Solaris. */ +#ifndef _POSIX_PTHREAD_SEMANTICS +# undef _POSIX_PTHREAD_SEMANTICS +#endif +/* Enable extensions on HP NonStop. */ +#ifndef _TANDEM_SOURCE +# undef _TANDEM_SOURCE +#endif +/* Enable general extensions on Solaris. */ +#ifndef __EXTENSIONS__ +# undef __EXTENSIONS__ +#endif -/* Define for large files, on AIX-style hosts. */ -#undef _LARGE_FILES === added file 'font/font.c' --- font/font.c 1970-01-01 00:00:00 +0000 +++ font/font.c 2008-07-03 14:12:08 +0000 @@ -0,0 +1,92 @@ +/* font.c - Font functions. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 +#include +#include + +grub_font_t +grub_font_get (const char *font_name) +{ + struct font_node *node; + + for (node = grub_font_list; node; node = node->next) + { + grub_font_t font = node->value; + if (grub_strcmp (font->name, font_name) == 0) + return font; + } + + /* If no font by that name is found, return the first font in the list + * as a fallback. */ + return grub_font_list->value; +} + +const char * +grub_font_get_name (grub_font_t font) +{ + return font->name; +} + +int +grub_font_get_max_char_width (grub_font_t font) +{ + return font->max_char_width; +} + +int +grub_font_get_max_char_height (grub_font_t font) +{ + return font->max_char_height; +} + +int +grub_font_get_ascent (grub_font_t font) +{ + return font->ascent; +} + +int +grub_font_get_descent (grub_font_t font) +{ + return font->descent; +} + +int +grub_font_get_string_width (grub_font_t font, const char *str) +{ + int i; + int width; + struct grub_font_glyph *glyph; + grub_size_t len; + + len = grub_strlen (str); + + for (i = 0, width = 0; i < len; i++) + { + glyph = grub_font_get_glyph (font, str[i]); + width += glyph->device_width; + } + + return width; +} === added file 'font/font_cmd.c' --- font/font_cmd.c 1970-01-01 00:00:00 +0000 +++ font/font_cmd.c 2008-07-03 14:12:08 +0000 @@ -0,0 +1,75 @@ +/* font_cmd.c - Font command definition. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2005,2006,2007 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 + +static grub_err_t +loadfont_command (struct grub_arg_list *state __attribute__ ((unused)), + int argc, + char **args) +{ + if (argc == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "no font specified"); + + while (argc--) + if (grub_font_load (*args++) != 0) + return GRUB_ERR_BAD_FONT; + + return GRUB_ERR_NONE; +} + +static grub_err_t +lsfonts_command (struct grub_arg_list *state __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + struct font_node *node; + + grub_printf ("Loaded fonts:\n"); + for (node = grub_font_list; node; node = node->next) + { + grub_font_t font = node->value; + grub_printf ("%s\n", font->name); + } + + return GRUB_ERR_NONE; +} + +GRUB_MOD_INIT(font_manager) +{ + grub_font_loader_init (); + + grub_register_command ("loadfont", loadfont_command, GRUB_COMMAND_FLAG_BOTH, + "loadfont FILE...", + "Specify one or more font files to load.", 0); + + grub_register_command ("lsfonts", lsfonts_command, GRUB_COMMAND_FLAG_BOTH, + "lsfonts", + "List the loaded fonts.", 0); +} + +GRUB_MOD_FINI(font_manager) +{ + /* Should this free fonts, unknown_glyph, etc.? Freeing fonts could + * be a Bad Thing if there are still references to any of them. */ + + grub_unregister_command ("loadfont"); +} + === added file 'font/loader.c' --- font/loader.c 1970-01-01 00:00:00 +0000 +++ font/loader.c 2008-07-29 14:51:29 +0000 @@ -0,0 +1,689 @@ +/* loader.c - Functions to handle loading fonts and glyphs from files. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2005,2006,2007,2008 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 +#include + +/* Definition of font registry. */ +struct font_node *grub_font_list; + +static int register_font (grub_font_t font); +static void free_font (grub_font_t font); +static void remove_font (grub_font_t font); + +struct font_file_section +{ + grub_file_t file; /* The file this section is in. */ + char name[4]; /* FOURCC name of the section. */ + grub_uint32_t length; /* Length of the section contents. */ + int eof; /* Set by open_section() on EOF. */ +}; + +/* Font file format constants. */ +static const char pff2_magic[4] = { 'P', 'F', 'F', '2' }; +static const char section_names_file[4] = { 'F', 'I', 'L', 'E' }; +static const char section_names_font_name[4] = { 'N', 'A', 'M', 'E' }; +static const char section_names_max_char_width[4] = { 'M', 'A', 'X', 'W' }; +static const char section_names_max_char_height[4] = { 'M', 'A', 'X', 'H' }; +static const char section_names_ascent[4] = { 'A', 'S', 'C', 'E' }; +static const char section_names_descent[4] = { 'D', 'E', 'S', 'C' }; +static const char section_names_char_index[4] = { 'C', 'H', 'I', 'X' }; +static const char section_names_data[4] = { 'D', 'A', 'T', 'A' }; + +/* Replace unknown glyphs with a rounded question mark. */ +static grub_uint8_t unknown_glyph_bitmap[] = +{ + /* 76543210 */ + 0x7C, /* ooooo */ + 0x82, /* o o */ + 0xBA, /* o ooo o */ + 0xAA, /* o o o o */ + 0xAA, /* o o o o */ + 0x8A, /* o o o */ + 0x9A, /* o oo o */ + 0x92, /* o o o */ + 0x92, /* o o o */ + 0x92, /* o o o */ + 0x92, /* o o o */ + 0x82, /* o o */ + 0x92, /* o o o */ + 0x82, /* o o */ + 0x7C, /* ooooo */ + 0x00 /* */ +}; + +static struct grub_font_glyph *unknown_glyph; + +void +grub_font_loader_init (void) +{ + unknown_glyph = grub_malloc(sizeof(struct grub_font_glyph) + + sizeof(unknown_glyph_bitmap)); + if (! unknown_glyph) + return; + + unknown_glyph->width = 8; + unknown_glyph->height = 16; + unknown_glyph->offset_x = 0; + unknown_glyph->offset_y = 0; + unknown_glyph->device_width = 8; + grub_memcpy(unknown_glyph->bitmap, + unknown_glyph_bitmap, sizeof(unknown_glyph_bitmap)); +} + +/* + Open the next section in the file. + + On success, the section name is stored in section->name and the length in + section->length, and 0 is returned. On failure, 1 is returned and + grub_errno is set approriately with an error message. + + If 1 is returned due to being at the end of the file, then section->eof is + set to 1; otherwise, section->eof is set to 0. + */ +static int +open_section (grub_file_t file, struct font_file_section *section) +{ + grub_ssize_t retval; + grub_uint32_t raw_length; + + section->file = file; + section->eof = 0; + + /* Read the FOURCC section name. */ + retval = grub_file_read (file, section->name, 4); + if (retval >= 0 && retval < 4) + { + section->eof = 1; + return 1; /* EOF encountered. */ + } + else if (retval < 0) + { + grub_error (GRUB_ERR_BAD_FONT, + "Font format error: can't read section name"); + return 1; /* Read error. */ + } + + /* Read the big-endian 32-bit section length. */ + retval = grub_file_read (file, (char *) &raw_length, 4); + if (retval >= 0 && retval < 4) + { + section->eof = 1; + return 1; /* EOF encountered. */ + } + else if (retval < 0) + { + grub_error (GRUB_ERR_BAD_FONT, + "Font format error: can't read section length"); + return 1; /* Read error. */ + } + + /* Convert byte-order and store in *length. */ + section->length = grub_be_to_cpu32 (raw_length); + + return 0; +} + +/* Size in bytes of each character index (CHIX section) + entry in the font file. */ +#define FONT_CHAR_INDEX_ENTRY_SIZE (4 + 1 + 4) + +/* + Load the character index (CHIX) section contents from the font file. This + presumes that the position of FILE is positioned immediately after the + section length for the CHIX section (i.e., at the start of the section + contents). Returns 0 upon success, nonzero for failure (in which case + grub_errno is set appropriately). + */ +static int +load_font_index (grub_file_t file, grub_uint32_t sect_length, struct + grub_font *font) +{ + unsigned i; + +#if FONT_DEBUG >= 2 + grub_printf("load_font_index(sect_length=%d)\n", sect_length); +#endif + + /* Sanity check: ensure section length is divisible by the entry size. */ + if (sect_length % FONT_CHAR_INDEX_ENTRY_SIZE != 0) + { + grub_error (GRUB_ERR_BAD_FONT, + "Font file format error: character index length %d " + "is not a multiple of the entry size %d", + sect_length, FONT_CHAR_INDEX_ENTRY_SIZE); + return 1; /* Invalid index section length. */ + } + + /* Calculate the number of characters. */ + font->num_chars = sect_length / FONT_CHAR_INDEX_ENTRY_SIZE; + + /* Allocate the character index array. */ + font->char_index = grub_malloc (font->num_chars + * sizeof (struct char_index_entry)); + if (!font->char_index) + return 1; /* Error allocating memory. */ + +#if FONT_DEBUG >= 2 + grub_printf("num_chars=%d)\n", font->num_chars); +#endif + + /* Load the character index data from the file. */ + for (i = 0; i < font->num_chars; i++) + { + struct char_index_entry *entry = &font->char_index[i]; + + /* Read code point value; convert to native byte order. */ + if (grub_file_read (file, (char *) &entry->code, 4) != 4) + return 1; + entry->code = grub_be_to_cpu32 (entry->code); + + /* Read storage flags byte. */ + if (grub_file_read (file, (char *) &entry->storage_flags, 1) != 1) + return 1; + + /* Read glyph data offset; convert to native byte order. */ + if (grub_file_read (file, (char *) &entry->offset, 4) != 4) + return 1; + entry->offset = grub_be_to_cpu32 (entry->offset); + + /* No glyph loaded. Will be loaded on demand and cached thereafter. */ + entry->glyph = 0; + +#if FONT_DEBUG >= 5 + if (i < 10) /* Print the 1st 10 characters. */ + grub_printf("c=%d o=%d\n", entry->code, entry->offset); +#endif + } + + return 0; /* Index loaded OK. */ +} + +/* + Read the contents of the specified section as a string, which is + allocated on the heap. Returns 0 if there is an error. + */ +static char * +read_section_as_string (struct font_file_section *section) +{ + char *str; + grub_ssize_t ret; + + str = grub_malloc (section->length + 1); + if (!str) + return 0; + + ret = grub_file_read (section->file, str, section->length); + if (ret < 0 || ret != (grub_ssize_t) section->length) + { + grub_free (str); + return 0; + } + + str[section->length] = '\0'; + return str; +} + +/* + Read the contents of the current section as a 16-bit integer value, + which is stored into *VALUE. Returns 0 upon success, nonzero upon failure. + */ +static int +read_section_as_short (struct font_file_section *section, grub_int16_t *value) +{ + grub_uint16_t raw_value; + + if (section->length != 2) + { + grub_error (GRUB_ERR_BAD_FONT, + "Font file format error: section %c%c%c%c length " + "is %d but should be 2", + section->name[0], section->name[1], + section->name[2], section->name[3], + section->length); + return 1; /* An error occurred. */ + } + if (grub_file_read (section->file, (char *) &raw_value, 2) != 2) + return 1; /* An error occurred. */ + + *value = grub_be_to_cpu16 (raw_value); + return 0; /* Successfully read the value. */ +} + +/* + Load a font and add it to the beginning of the global font list. + Returns 0 upon success, nonzero upon failure. + */ +int +grub_font_load (const char *filename) +{ + grub_file_t file = 0; + struct font_file_section section; + char magic[4]; + grub_font_t font = 0; + +#if FONT_DEBUG >= 1 + grub_printf("add_font(%s)\n", filename); +#endif + + file = grub_buffile_open (filename, 1024); + if (!file) + goto fail; + +#if FONT_DEBUG >= 3 + grub_printf("file opened\n"); +#endif + + /* Read the FILE section. It indicates the file format. */ + if (open_section (file, §ion) != 0) + goto fail; + +#if FONT_DEBUG >= 3 + grub_printf("opened FILE section\n"); +#endif + if (grub_memcmp (section.name, section_names_file, 4) != 0) + { + grub_error (GRUB_ERR_BAD_FONT, + "Font file format error: 1st section must be FILE"); + goto fail; + } + +#if FONT_DEBUG >= 3 + grub_printf("section name ok\n"); +#endif + if (section.length != 4) + { + grub_error (GRUB_ERR_BAD_FONT, + "Font file format error (file type ID length is %d " + "but should be 4)", section.length); + goto fail; + } + +#if FONT_DEBUG >= 3 + grub_printf("section length ok\n"); +#endif + /* Check the file format type code. */ + if (grub_file_read (file, magic, 4) != 4) + goto fail; + +#if FONT_DEBUG >= 3 + grub_printf("read magic ok\n"); +#endif + + if (grub_memcmp (magic, pff2_magic, 4) != 0) + { + grub_error (GRUB_ERR_BAD_FONT, "Invalid font magic %x %x %x %x", + magic[0], magic[1], magic[2], magic[3]); + goto fail; + } + +#if FONT_DEBUG >= 3 + grub_printf("compare magic ok\n"); +#endif + + /* Allocate the font object. */ + font = (grub_font_t) grub_malloc (sizeof (struct grub_font)); + if (!font) + goto fail; + + font->file = file; + font->name = 0; + font->ascent = 0; + font->descent = 0; + font->max_char_width = 0; + font->max_char_height = 0; + font->num_chars = 0; + font->char_index = 0; + +#if FONT_DEBUG >= 3 + grub_printf("allocate font ok; loading font info\n"); +#endif + + /* Load the font information. */ + while (1) + { + if (open_section (file, §ion) != 0) + { + if (section.eof) + break; /* Done reading the font file. */ + else + goto fail; + } + +#if FONT_DEBUG >= 2 + grub_printf("opened section %c%c%c%c ok\n", + section.name[0], section.name[1], + section.name[2], section.name[3]); +#endif + + if (grub_memcmp (section.name, section_names_font_name, 4) == 0) + { + font->name = read_section_as_string (§ion); + if (!font->name) + goto fail; + } + else if (grub_memcmp (section.name, section_names_max_char_width, 4) == 0) + { + if (read_section_as_short (§ion, &font->max_char_width) != 0) + goto fail; + } + else if (grub_memcmp (section.name, section_names_max_char_height, 4) == 0) + { + if (read_section_as_short (§ion, &font->max_char_height) != 0) + goto fail; + } + else if (grub_memcmp (section.name, section_names_ascent, 4) == 0) + { + if (read_section_as_short (§ion, &font->ascent) != 0) + goto fail; + } + else if (grub_memcmp (section.name, section_names_descent, 4) == 0) + { + if (read_section_as_short (§ion, &font->descent) != 0) + goto fail; + } + else if (grub_memcmp (section.name, section_names_char_index, 4) == 0) + { + /* Load the font index. */ + if (load_font_index (file, section.length, font) != 0) + goto fail; + } + else if (grub_memcmp (section.name, section_names_data, 4) == 0) + { + /* When the DATA section marker is reached, we stop reading. */ + break; + } + else + { + /* Unhandled section type, simply skip past it. */ +#if FONT_DEBUG >= 3 + grub_printf("Unhandled section type, skipping.\n"); +#endif + grub_off_t section_end = grub_file_tell (file) + section.length; + if ((int) grub_file_seek (file, section_end) == -1) + goto fail; + } + } + + if (!font->name) + { + grub_printf ("Note: Font has no name.\n"); + font->name = grub_strdup ("Unknown"); + } + +#if FONT_DEBUG >= 1 + grub_printf ("Loaded font `%s'.\n" + "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n", + font->name, + font->ascent, font->descent, + font->max_char_width, font->max_char_height, + font->num_chars); +#endif + + if (font->max_char_width == 0 + || font->max_char_height == 0 + || font->num_chars == 0 + || font->char_index == 0 + || font->ascent == 0 + || font->descent == 0) + { + grub_error (GRUB_ERR_BAD_FONT, + "Invalid font file: missing some required data."); + goto fail; + } + + /* Add the font to the global font registry. */ + if (register_font (font) != 0) + goto fail; + + return 0; /* Font loaded ok. */ + +fail: + free_font (font); + return 1; /* Failed to load font. */ +} + +/* + Read a 16-bit big-endian integer from FILE, convert it to native byte + order, and store it in *VALUE. + Returns 0 on success, 1 on failure. + */ +static int +read_be_uint16 (grub_file_t file, grub_uint16_t * value) +{ + if (grub_file_read (file, (char *) value, 2) != 2) + return 1; + *value = grub_be_to_cpu16 (*value); + return 0; +} + +static int +read_be_int16 (grub_file_t file, grub_int16_t * value) +{ + /* For the signed integer version, use the same code as for unsigned. */ + return read_be_uint16 (file, (grub_uint16_t *) value); +} + +/* + Return a pointer to the character index entry for the glyph corresponding to + the codepoint CODE in the font FONT. If not found, return zero. + */ +static struct char_index_entry * +find_glyph (const grub_font_t font, grub_uint32_t code) +{ + grub_uint32_t i; + grub_uint32_t len = font->num_chars; + struct char_index_entry *table = font->char_index; + + /* Do a linear search. */ + for (i = 0; i < len; i++) + { + if (table[i].code == code) + return &table[i]; + } + + return 0; /* No entry found for code point CODE. */ +} + +static struct grub_font_glyph * +grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code) +{ + struct char_index_entry *index_entry; + + index_entry = find_glyph (font, code); + if (index_entry) + { + struct grub_font_glyph *glyph = 0; + grub_uint16_t width; + grub_uint16_t height; + grub_int16_t xoff; + grub_int16_t yoff; + grub_int16_t dwidth; + int len; + + if (index_entry->glyph) + return index_entry->glyph; /* Return cached glyph. */ + + /* Make sure we can find glyphs for error messages. Push active + error message to error stack and reset error message. */ + grub_error_push (); + + grub_file_seek (font->file, index_entry->offset); + + /* Read the glyph width, height, and baseline. */ + if (read_be_uint16(font->file, &width) != 0 + || read_be_uint16(font->file, &height) != 0 + || read_be_int16(font->file, &xoff) != 0 + || read_be_int16(font->file, &yoff) != 0 + || read_be_int16(font->file, &dwidth) != 0) + { + //remove_font (font); + return 0; + } + + len = (width * height + 7) / 8; + glyph = grub_malloc (sizeof (struct grub_font_glyph) + len); + if (! glyph) + { + //remove_font (font); + return 0; + } + + glyph->font = font; + glyph->width = width; + glyph->height = height; + glyph->offset_x = xoff; + glyph->offset_y = yoff; + glyph->device_width = dwidth; + + /* Don't try to read empty bitmaps (e.g., space characters). */ + if (len != 0) + { + if (grub_file_read (font->file, (char *) glyph->bitmap, len) != len) + { + //remove_font (font); + return 0; + } + } + + /* Restore old error message. */ + grub_error_pop (); + + /* Cache the glyph. */ + index_entry->glyph = glyph; + + return glyph; /* Glyph loaded ok. */ + } + + return 0; +} + +/* Get the glyph for FONT corresponding to the Unicode code point CODE. + Returns a pointer to an glyph indicating there is no glyph available + if CODE does not exist in the font. The glyphs are cached once loaded. */ +struct grub_font_glyph * +grub_font_get_glyph (grub_font_t font, grub_uint32_t code) +{ + struct grub_font_glyph *glyph; + glyph = grub_font_get_glyph_internal (font, code); + if (glyph == 0) + glyph = unknown_glyph; + return glyph; +} + +/* Get a glyph corresponding to the codepoint CODE. If no glyph is available + for CODE in the available fonts, then a glyph representing an unknown + character is returned. This function never returns NULL. + The returned glyph is owned by the font manager and should not be freed + by the caller. The glyphs are cached. */ +struct grub_font_glyph * +grub_font_get_glyph_any (grub_uint32_t code) +{ + struct font_node *node; + /* Keep track of next node, in case there's an I/O error in + grub_font_get_glyph() and the font is removed from the list. */ + struct font_node *next; + + for (node = grub_font_list; node; node = next) + { + grub_font_t font; + struct grub_font_glyph *glyph; + + font = node->value; + next = node->next; + + glyph = grub_font_get_glyph_internal (font, code); + if (glyph) + return glyph; + } + + /* Uggh... Glyph was not found in any font. */ + return unknown_glyph; /* Failed to load glyph. */ +} + +/* + Free the memory used by a font. + This should not be called if the font has been made available to + users (once it is added to the global font list), since there would + be the possibility of a dangling pointer. + */ +static void +free_font (grub_font_t font) +{ + if (font) + { + if (font->file) + grub_file_close (font->file); + if (font->name) + grub_free (font->name); + if (font->char_index) + grub_free (font->char_index); + grub_free (font); + } +} + +/* + Add FONT to the global font registry. + Returns 0 upon success, nonzero on failure (the font was not registered). + */ +static int +register_font (grub_font_t font) +{ + struct font_node *node = 0; + + node = grub_malloc (sizeof (struct font_node)); + if (!node) + return 1; /* Error. */ + + node->value = font; + node->next = grub_font_list; + grub_font_list = node; + + return 0; /* Success. */ +} + +/* + Remove the font from the global font list. We don't actually free the + font's memory since users could be holding references to the font. + */ +static void +remove_font (grub_font_t font) +{ + struct font_node **nextp, *cur; + + for (nextp = &grub_font_list, cur = *nextp; + cur; + nextp = &cur->next, cur = cur->next) + { + if (cur->value == font) + { + *nextp = cur->next; + + /* Free the node, but not the font itself. */ + grub_free (cur); + + return; + } + } +} + === removed file 'font/manager.c' --- font/manager.c 2008-08-01 03:06:55 +0000 +++ font/manager.c 1970-01-01 00:00:00 +0000 @@ -1,283 +0,0 @@ -/* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2003,2005,2006,2007 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 -#include -#include -#include - -struct entry -{ - grub_uint32_t code; - grub_uint32_t offset; -}; - -struct font -{ - struct font *next; - grub_file_t file; - grub_uint32_t num; - struct entry table[0]; -}; - -static struct font *font_list; - -/* Fill unknown glyph's with rounded question mark. */ -static grub_uint8_t unknown_glyph[16] = -{ /* 76543210 */ - 0x7C, /* ooooo */ - 0x82, /* o o */ - 0xBA, /* o ooo o */ - 0xAA, /* o o o o */ - 0xAA, /* o o o o */ - 0x8A, /* o o o */ - 0x9A, /* o oo o */ - 0x92, /* o o o */ - 0x92, /* o o o */ - 0x92, /* o o o */ - 0x92, /* o o o */ - 0x82, /* o o */ - 0x92, /* o o o */ - 0x82, /* o o */ - 0x7C, /* ooooo */ - 0x00 /* */ -}; - -static int -add_font (const char *filename) -{ - grub_file_t file = 0; - char magic[4]; - grub_uint32_t num, i; - struct font *font = 0; - - file = grub_buffile_open (filename, 0); - if (! file) - goto fail; - - if (grub_file_read (file, magic, 4) != 4) - goto fail; - - if (grub_memcmp (magic, GRUB_FONT_MAGIC, 4) != 0) - { - grub_error (GRUB_ERR_BAD_FONT, "invalid font magic"); - goto fail; - } - - if (grub_file_read (file, (char *) &num, 4) != 4) - goto fail; - - num = grub_le_to_cpu32 (num); - font = (struct font *) grub_malloc (sizeof (struct font) - + sizeof (struct entry) * num); - if (! font) - goto fail; - - font->file = file; - font->num = num; - - for (i = 0; i < num; i++) - { - grub_uint32_t code, offset; - - if (grub_file_read (file, (char *) &code, 4) != 4) - goto fail; - - if (grub_file_read (file, (char *) &offset, 4) != 4) - goto fail; - - font->table[i].code = grub_le_to_cpu32 (code); - font->table[i].offset = grub_le_to_cpu32 (offset); - } - - font->next = font_list; - font_list = font; - - return 1; - - fail: - if (font) - grub_free (font); - - if (file) - grub_file_close (file); - - return 0; -} - -static void -remove_font (struct font *font) -{ - struct font **p, *q; - - for (p = &font_list, q = *p; q; p = &(q->next), q = q->next) - if (q == font) - { - *p = q->next; - - grub_file_close (font->file); - grub_free (font); - - break; - } -} - -/* Return the offset of the glyph corresponding to the codepoint CODE - in the font FONT. If no found, return zero. */ -static grub_uint32_t -find_glyph (const struct font *font, grub_uint32_t code) -{ - grub_uint32_t start = 0; - grub_uint32_t end = font->num - 1; - const struct entry *table = font->table; - - /* This shouldn't happen. */ - if (font->num == 0) - return 0; - - /* Do a binary search. */ - while (start <= end) - { - grub_uint32_t i = (start + end) / 2; - - if (table[i].code < code) - start = i + 1; - else if (table[i].code > code) - end = i - 1; - else - return table[i].offset; - } - - return 0; -} - -/* Set the glyph to something stupid. */ -static void -fill_with_default_glyph (grub_font_glyph_t glyph) -{ - unsigned i; - - /* Use pre-defined pattern to fill unknown glyphs. */ - for (i = 0; i < 16; i++) - glyph->bitmap[i] = unknown_glyph[i]; - - glyph->char_width = 1; - glyph->width = glyph->char_width * 8; - glyph->height = 16; - glyph->baseline = (16 * 3) / 4; -} - -/* Get a glyph corresponding to the codepoint CODE. Always fill glyph - information with something, even if no glyph is found. */ -int -grub_font_get_glyph (grub_uint32_t code, - grub_font_glyph_t glyph) -{ - struct font *font; - grub_uint8_t bitmap[32]; - - /* FIXME: It is necessary to cache glyphs! */ - - restart: - for (font = font_list; font; font = font->next) - { - grub_uint32_t offset; - - offset = find_glyph (font, code); - if (offset) - { - grub_uint32_t w; - int len; - - /* Make sure we can find glyphs for error messages. Push active - error message to error stack and reset error message. */ - grub_error_push (); - - grub_file_seek (font->file, offset); - if ((len = grub_file_read (font->file, (char *) &w, sizeof (w))) - != sizeof (w)) - { - remove_font (font); - goto restart; - } - - w = grub_le_to_cpu32 (w); - if (w != 1 && w != 2) - { - /* grub_error (GRUB_ERR_BAD_FONT, "invalid width"); */ - remove_font (font); - goto restart; - } - - if (grub_file_read (font->file, (char *) bitmap, w * 16) - != (grub_ssize_t) w * 16) - { - remove_font (font); - goto restart; - } - - /* Fill glyph with information. */ - grub_memcpy (glyph->bitmap, bitmap, w * 16); - - glyph->char_width = w; - glyph->width = glyph->char_width * 8; - glyph->height = 16; - glyph->baseline = (16 * 3) / 4; - - /* Restore old error message. */ - grub_error_pop (); - - return 1; - } - } - - /* Uggh... No font was found. */ - fill_with_default_glyph (glyph); - return 0; -} - -static grub_err_t -font_command (struct grub_arg_list *state __attribute__ ((unused)), - int argc __attribute__ ((unused)), - char **args __attribute__ ((unused))) -{ - if (argc == 0) - return grub_error (GRUB_ERR_BAD_ARGUMENT, "no font specified"); - - while (argc--) - if (! add_font (*args++)) - return 1; - - return 0; -} - -GRUB_MOD_INIT(font_manager) -{ - grub_register_command ("font", font_command, GRUB_COMMAND_FLAG_BOTH, - "font FILE...", - "Specify one or more font files to display.", 0); -} - -GRUB_MOD_FINI(font_manager) -{ - grub_unregister_command ("font"); -} === modified file 'genmk.rb' --- genmk.rb 2008-07-28 21:35:40 +0000 +++ genmk.rb 2008-08-03 03:57:46 +0000 @@ -126,7 +126,7 @@ $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(#{prefix}_CFLAGS) -c -o $@ $< #{mod_src}: moddep.lst genmodsrc.sh - sh $(srcdir)/genmodsrc.sh '#{mod_name}' $< > $@ || (rm -f $@; exit 1) + $(GENMODSRC) '#{mod_name}' $< > $@ || (rm -f $@; exit 1) ifneq ($(#{prefix}_EXPORTS),no) #{defsym}: #{pre_obj} @@ -158,18 +158,21 @@ PARTMAPFILES += #{partmap} #{command}: #{src} $(#{src}_DEPENDENCIES) gencmdlist.sh - set -e; \ - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ + $(INFO_GENCMDLIST) + $(V_PREFIX)set -e; \ + $(_TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ | sh $(srcdir)/gencmdlist.sh #{symbolic_name} > $@ || (rm -f $@; exit 1) #{fs}: #{src} $(#{src}_DEPENDENCIES) genfslist.sh - set -e; \ - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ + $(INFO_GENFSLIST) + $(V_PREFIX)set -e; \ + $(_TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ | sh $(srcdir)/genfslist.sh #{symbolic_name} > $@ || (rm -f $@; exit 1) #{partmap}: #{src} $(#{src}_DEPENDENCIES) genpartmaplist.sh - set -e; \ - $(TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ + $(INFO_GENPARTMAPLIST) + $(V_PREFIX)set -e; \ + $(_TARGET_CC) -I#{dir} -I$(srcdir)/#{dir} $(TARGET_CPPFLAGS) $(TARGET_#{flag}) $(#{prefix}_#{flag}) -E $< \ | sh $(srcdir)/genpartmaplist.sh #{symbolic_name} > $@ || (rm -f $@; exit 1) === added directory 'gfxmenu' === added file 'gfxmenu/gfxmenu.c' --- gfxmenu/gfxmenu.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/gfxmenu.c 2008-07-27 00:06:20 +0000 @@ -0,0 +1,221 @@ +/* gfxmenu.c - Graphical menu interface controller. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void switch_to_text_menu (void) +{ + grub_env_set ("menuviewer", "terminal"); +} + +static void +process_key_press (int c, + grub_gfxmenu_model_t model, + grub_gfxmenu_view_t view, + int nested, + int *should_exit) +{ + /* When a key is pressed, stop the timeout. */ + grub_gfxmenu_model_clear_timeout (model); + + if (c == 'j' || c == GRUB_TERM_DOWN) + { + int i = grub_gfxmenu_model_get_selected_index (model); + int num_items = grub_gfxmenu_model_get_num_entries (model); + if (i < num_items - 1) + { + i++; + grub_gfxmenu_model_set_selected_index (model, i); + } + } + else if (c == 'k' || c == GRUB_TERM_UP) + { + int i = grub_gfxmenu_model_get_selected_index (model); + if (i > 0) + { + i--; + grub_gfxmenu_model_set_selected_index (model, i); + } + } + else if (c == '\r' || c == '\n' || c == GRUB_TERM_RIGHT) + { + int selected = grub_gfxmenu_model_get_selected_index (model); + int num_entries = grub_gfxmenu_model_get_num_entries (model); + if (selected >= 0 && selected < num_entries) + { + grub_menu_entry_t entry = + grub_gfxmenu_model_get_entry (model, selected); + grub_gfxmenu_view_execute_entry (view, entry); + } + } + else if (c == 'c') + { + grub_gfxmenu_view_run_terminal (view); + if (grub_errno != GRUB_ERR_NONE) + *should_exit = 1; + } + else if (c == 't') + { + /* The write hook for 'menuviewer' will cause + * grub_menu_viewer_should_return to return nonzero. */ + switch_to_text_menu (); + *should_exit = 1; + } + else if (c == '1') + { + grub_gfxmenu_view_load_theme (view, + "/boot/grub/themes/proto/theme.txt"); + } + else if (c == '2') + { + grub_gfxmenu_view_load_theme (view, + "/boot/grub/themes/winter/theme.txt"); + } + else if (nested && c == GRUB_TERM_ESC) + { + *should_exit = 1; + } +} + +static void +handle_key_events (grub_gfxmenu_model_t model, + grub_gfxmenu_view_t view, + int nested, + int *should_exit) +{ + while (!*should_exit && grub_checkkey () != -1) + { + int key = grub_getkey (); + int c = GRUB_TERM_ASCII_CHAR (key); + process_key_press (c, model, view, nested, should_exit); + } +} + +static grub_err_t +show_menu (grub_menu_t menu, int nested) +{ + grub_gfxmenu_model_t model; + + model = grub_gfxmenu_model_new (menu); + if (! model) + { + grub_print_error (); + grub_printf ("Initializing menu data for graphical menu failed;\n" + "falling back to terminal based menu.\n"); + grub_wait_after_message (); + switch_to_text_menu (); + return grub_errno; + } + + grub_gfxmenu_view_t view; + + /* Create the view. */ + const char *theme_path = grub_env_get ("theme"); + if (! theme_path) + theme_path = "/boot/grub/themes/proto/theme.txt"; + + view = grub_gfxmenu_view_new (theme_path, model); + if (! view) + { + grub_print_error (); + grub_printf ("Starting graphical menu failed;\n" + "falling back to terminal based menu.\n"); + grub_wait_after_message (); + grub_gfxmenu_model_destroy (model); + switch_to_text_menu (); + return grub_errno; + } + + /* Initially select the default menu entry. */ + int default_index = grub_menu_get_default_entry_index (menu); + grub_gfxmenu_model_set_selected_index (model, default_index); + + /* Start the timer to execute the default entry. */ + grub_gfxmenu_model_set_timeout (model); + + /* Main event loop. */ + int exit_requested = 0; + while (!exit_requested && !grub_menu_viewer_should_return ()) + { + if (grub_gfxmenu_model_timeout_expired (model)) + { + grub_gfxmenu_model_clear_timeout (model); + int i = grub_gfxmenu_model_get_selected_index (model); + grub_menu_entry_t e = grub_gfxmenu_model_get_entry (model, i); + grub_gfxmenu_view_execute_with_fallback (view, e); + continue; + } + + grub_gfxmenu_view_draw (view); + grub_video_swap_buffers (); + handle_key_events (model, view, nested, &exit_requested); + } + + grub_gfxmenu_view_destroy (view); + grub_gfxmenu_model_destroy (model); + + return grub_errno; +} + +static grub_err_t +grub_cmd_gfxmenu (struct grub_arg_list *state __attribute__ ((unused)), + int argc __attribute__ ((unused)), + char **args __attribute__ ((unused))) +{ + grub_menu_t menu = grub_env_get_data_slot ("menu"); + if (!menu) + return grub_error (GRUB_ERR_MENU, "No menu context"); + + return show_menu (menu, 1); +} + +static struct grub_menu_viewer menu_viewer = +{ + .name = "gfxmenu", + .show_menu = show_menu +}; + +GRUB_MOD_INIT (gfxmenu) +{ + (void) mod; /* To stop warning. */ + grub_menu_viewer_register (&menu_viewer); + grub_register_command ("gfxmenu", + grub_cmd_gfxmenu, GRUB_COMMAND_FLAG_BOTH, + "gfxmenu", "Show graphical menu interface", 0); +} + +GRUB_MOD_FINI (gfxmenu) +{ + grub_unregister_command ("gfxmenu"); +} === added file 'gfxmenu/model.c' --- gfxmenu/model.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/model.c 2008-07-26 23:37:04 +0000 @@ -0,0 +1,192 @@ +/* model.c - Graphical menu interface MVC model. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 +#include +#include +#include + +/* Model type definition. */ +struct grub_gfxmenu_model +{ + grub_menu_t menu; + int num_entries; + grub_menu_entry_t *entries; + int selected_entry_index; + int timeout_set; + grub_uint64_t timeout_start; + grub_uint64_t timeout_at; +}; + + +grub_gfxmenu_model_t +grub_gfxmenu_model_new (grub_menu_t menu) +{ + grub_gfxmenu_model_t model; + + model = grub_malloc (sizeof (*model)); + if (! model) + return 0; + + model->menu = menu; + model->num_entries = menu->size; + model->entries = 0; + model->selected_entry_index = 0; + model->timeout_set = 0; + model->timeout_at = 0; + if (model->num_entries > 0) + { + model->entries = grub_malloc (model->num_entries + * sizeof (*model->entries)); + if (! model->entries) + goto fail_and_free; + + int i; + grub_menu_entry_t cur; + for (i = 0, cur = menu->entry_list; + i < model->num_entries; + i++, cur = cur->next) + { + model->entries[i] = cur; + } + } + + return model; + +fail_and_free: + grub_free (model->entries); + grub_free (model); + return 0; +} + +void +grub_gfxmenu_model_destroy (grub_gfxmenu_model_t model) +{ + if (! model) + return; + + grub_free (model->entries); + model->entries = 0; + + grub_free (model); +} + +grub_menu_t +grub_gfxmenu_model_get_menu (grub_gfxmenu_model_t model) +{ + return model->menu; +} + +void +grub_gfxmenu_model_set_timeout (grub_gfxmenu_model_t model) +{ + int timeout_sec = grub_menu_get_timeout (); + if (timeout_sec >= 0) + { + model->timeout_start = grub_get_time_ms (); + model->timeout_at = model->timeout_start + timeout_sec * 1000; + model->timeout_set = 1; + } + else + { + model->timeout_set = 0; + } +} + +void +grub_gfxmenu_model_clear_timeout (grub_gfxmenu_model_t model) +{ + model->timeout_set = 0; + grub_menu_set_timeout (-1); +} + +int +grub_gfxmenu_model_get_timeout_ms (grub_gfxmenu_model_t model) +{ + if (!model->timeout_set) + return -1; + + return model->timeout_at - model->timeout_start; +} + +int +grub_gfxmenu_model_get_timeout_remaining_ms (grub_gfxmenu_model_t model) +{ + if (!model->timeout_set) + return -1; + + return model->timeout_at - grub_get_time_ms (); +} + +int +grub_gfxmenu_model_timeout_expired (grub_gfxmenu_model_t model) +{ + if (model->timeout_set + && grub_get_time_ms () >= model->timeout_at) + return 1; + + return 0; +} + +int +grub_gfxmenu_model_get_num_entries (grub_gfxmenu_model_t model) +{ + return model->num_entries; +} + +int +grub_gfxmenu_model_get_selected_index (grub_gfxmenu_model_t model) +{ + return model->selected_entry_index; +} + +void +grub_gfxmenu_model_set_selected_index (grub_gfxmenu_model_t model, int index) +{ + model->selected_entry_index = index; +} + +const char * +grub_gfxmenu_model_get_entry_title (grub_gfxmenu_model_t model, int index) +{ + if (index < 0 || index >= model->num_entries) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid menu index"); + return 0; + } + + return model->entries[index]->title; +} + +grub_menu_entry_t +grub_gfxmenu_model_get_entry (grub_gfxmenu_model_t model, int index) +{ + if (index < 0 || index >= model->num_entries) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "invalid menu index"); + return 0; + } + + return model->entries[index]; +} + === added file 'gfxmenu/stringutil.c' --- gfxmenu/stringutil.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/stringutil.c 2008-07-26 00:44:48 +0000 @@ -0,0 +1,196 @@ +/* stringutil.c - String utilities. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 + +/* Create a new NUL-terminated string on the heap as a substring of BUF. + The range of buf included is the half-open interval [START,END). + The index START is inclusive, END is exclusive. */ +char * +grub_new_substring (const char *buf, + grub_size_t start, grub_size_t end) +{ + char *s = grub_malloc (end - start + 1); + if (! s) + return 0; + grub_memcpy (s, buf + start, end - start); + s[end - start] = '\0'; + return s; +} + +/* Eliminate "." and ".." path elements from PATH. A new heap-allocated + string is returned. */ +static char * +canonicalize_path (const char *path) +{ + int i; + const char *p; + char *newpath = 0; + + /* Count the path components in path. */ + int components = 1; + for (p = path; *p; p++) + if (*p == '/') + components++; + + char **path_array = grub_malloc (components * sizeof (*path_array)); + if (! path_array) + return 0; + + /* Initialize array elements to NULL pointers; in case once of the + allocations fails, the cleanup code can just call grub_free() for all + pointers in the array. */ + for (i = 0; i < components; i++) + path_array[i] = 0; + + /* Parse the path into path_array. */ + p = path; + for (i = 0; i < components && p; i++) + { + /* Find the end of the path element. */ + const char *end = grub_strchr (p, '/'); + if (!end) + end = p + grub_strlen (p); + + /* Copy the element. */ + path_array[i] = grub_new_substring (p, 0, end - p); + if (!path_array[i]) + goto cleanup; + + /* Advance p to point to the start of the next element, or NULL. */ + if (*end) + p = end + 1; + else + p = 0; + } + + /* Eliminate '.' and '..' elements from the path array. */ + int newpath_length = 0; + for (i = components - 1; i >= 0; --i) + { + if (! grub_strcmp (path_array[i], ".")) + { + grub_free (path_array[i]); + path_array[i] = 0; + } + else if (! grub_strcmp (path_array[i], "..") + && i > 0) + { + /* Delete the '..' and the prior path element. */ + grub_free (path_array[i]); + path_array[i] = 0; + --i; + grub_free (path_array[i]); + path_array[i] = 0; + } + else + { + newpath_length += grub_strlen (path_array[i]) + 1; + } + } + + /* Construct a new path string. */ + newpath = grub_malloc (newpath_length + 1); + if (! newpath) + goto cleanup; + + newpath[0] = '\0'; + char *newpath_end = newpath; + int first = 1; + for (i = 0; i < components; i++) + { + char *element = path_array[i]; + if (element) + { + /* For all components but the first, prefix with a slash. */ + if (! first) + newpath_end = grub_stpcpy (newpath_end, "/"); + newpath_end = grub_stpcpy (newpath_end, element); + first = 0; + } + } + +cleanup: + for (i = 0; i < components; i++) + grub_free (path_array[i]); + grub_free (path_array); + + return newpath; +} + +/* Return a new heap-allocated string representing to absolute path + to the file referred to by PATH. If PATH is an absolute path, then + the returned path is a copy of PATH. If PATH is a relative path, then + BASE is with PATH used to construct the absolute path. */ +char * +grub_resolve_relative_path (const char *base, const char *path) +{ + char *abspath; + char *canonpath; + char *p; + + /* If PATH is an absolute path, then just use it as is. */ + if (path[0] == '/' || path[0] == '(') + return canonicalize_path (path); + + abspath = grub_malloc (grub_strlen (base) + grub_strlen (path) + 1); + if (!abspath) + return 0; + + /* Concatenate BASE and PATH. + Note that BASE is expected to have a trailing slash. */ + p = grub_stpcpy (abspath, base); + grub_stpcpy (p, path); + + canonpath = canonicalize_path (abspath); + if (!canonpath) + return abspath; + + grub_free (abspath); + return canonpath; +} + +/* Get the path of the directory where the file at FILE_PATH is located. + FILE_PATH should refer to a file, not a directory. The returned path + includes a trailing slash. + This does not handle GRUB "(hd0,0)" paths properly yet since it only + looks at slashes. */ +char * +grub_get_dirname (const char *file_path) +{ + int i; + int last_slash; + + last_slash = -1; + for (i = grub_strlen (file_path) - 1; i >= 0; --i) + { + if (file_path[i] == '/') + { + last_slash = i; + break; + } + } + if (last_slash == -1) + return grub_strdup ("/"); + + return grub_new_substring (file_path, 0, last_slash + 1); +} === added file 'gfxmenu/view.c' --- gfxmenu/view.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/view.c 2008-08-04 17:10:55 +0000 @@ -0,0 +1,1243 @@ +/* view.c - Graphical menu interface MVC view. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +typedef struct grub_gfxmenu_view_icon +{ + const char *class_name; + struct grub_video_bitmap *bitmap; + struct grub_gfxmenu_view_icon *next; +} *grub_gfxmenu_view_icon_t; + +/* Definition of the private representation of the view. */ +struct grub_gfxmenu_view +{ + grub_video_rect_t screen; + + int icon_width; + int icon_height; + int item_height; + int item_padding; + int item_icon_space; + int item_spacing; + grub_font_t title_font; + grub_font_t item_font; + grub_font_t selected_item_font; + grub_font_t status_font; + char *terminal_font_name; + grub_video_color_t title_color; + grub_video_color_t item_color; + grub_video_color_t selected_item_color; + grub_video_color_t status_color; + grub_video_color_t status_bg_color; + grub_video_color_t progress_bar_border_color; + grub_video_color_t progress_bar_fg_color; + grub_video_color_t progress_bar_bg_color; + struct grub_video_bitmap *desktop_image; + grub_video_color_t desktop_color; + grub_gfxmenu_box_t menu_box; + grub_gfxmenu_box_t selected_item_box; + grub_gfxmenu_box_t terminal_box; + char *title_text; + lua_State *lua_state; + char *progress_message_text; + char *theme_path; + /* Icon cache: linked list w/ dummy head node. */ + struct grub_gfxmenu_view_icon icon_cache; + + grub_gfxmenu_model_t model; +}; + +static void init_terminal (grub_gfxmenu_view_t view); +static void destroy_terminal (void); +static grub_err_t set_graphics_mode (void); +static grub_err_t set_text_mode (void); + +/* Create a new view object, loading the theme specified by THEME_PATH and + associating MODEL with the view. */ +grub_gfxmenu_view_t +grub_gfxmenu_view_new (const char *theme_path, grub_gfxmenu_model_t model) +{ + grub_gfxmenu_view_t view; + + view = grub_malloc (sizeof (*view)); + if (! view) + return 0; + + set_graphics_mode (); + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + grub_video_get_viewport ((unsigned *) &view->screen.x, + (unsigned *) &view->screen.y, + (unsigned *) &view->screen.width, + (unsigned *) &view->screen.height); + + /* Clear the screen; there may be garbage left over in video memory, and + loading the menu style (particularly the background) can take a while. */ + grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), + view->screen.x, view->screen.y, + view->screen.width, view->screen.height); + grub_video_swap_buffers (); + + grub_font_t default_font; + grub_video_color_t default_fg_color; + grub_video_color_t default_bg_color; + + default_font = grub_font_get ("Helvetica 12"); + default_fg_color = grub_video_map_rgb (0, 0, 0); + default_bg_color = grub_video_map_rgb (255, 255, 255); + + view->icon_width = 32; + view->icon_height = 32; + view->item_height = 42; + view->item_padding = 14; + view->item_icon_space = 4; + view->item_spacing = 16; + view->title_font = default_font; + view->item_font = default_font; + view->selected_item_font = default_font; + view->status_font = default_font; + view->terminal_font_name = grub_strdup ("Fixed 10"); + view->title_color = default_fg_color; + view->item_color = default_fg_color; + view->selected_item_color = default_fg_color; + view->status_color = default_bg_color; + view->status_bg_color = default_fg_color; + view->progress_bar_border_color = default_fg_color; + view->progress_bar_fg_color = grub_video_map_rgb (160, 160, 160); + view->progress_bar_bg_color = default_bg_color; + view->desktop_image = 0; + view->desktop_color = default_bg_color; + view->menu_box = 0; + view->selected_item_box = 0; + view->terminal_box = 0; + view->title_text = grub_strdup ("GRUB Boot Menu"); + view->lua_state = 0; + view->progress_message_text = 0; + view->theme_path = 0; + view->icon_cache.class_name = 0; + view->icon_cache.bitmap = 0; + view->icon_cache.next = 0; + + view->model = model; + + if (! grub_gfxmenu_view_load_theme (view, theme_path)) + { + grub_gfxmenu_view_destroy (view); + return 0; + } + + init_terminal (view); + + return view; +} + +static void +free_icon_cache (grub_gfxmenu_view_t view) +{ + grub_gfxmenu_view_icon_t cur; + grub_gfxmenu_view_icon_t next; + for (cur = view->icon_cache.next; cur; cur = next) + { + next = cur->next; + grub_free (cur); + } + view->icon_cache.next = 0; +} + +/* Destroy the view object. All used memory is freed. */ +void +grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view) +{ + grub_video_bitmap_destroy (view->desktop_image); + if (view->menu_box) + view->menu_box->destroy (view->menu_box); + if (view->selected_item_box) + view->selected_item_box->destroy (view->selected_item_box); + if (view->terminal_box) + view->terminal_box->destroy (view->terminal_box); + grub_free (view->terminal_font_name); + grub_free (view->title_text); + if (view->lua_state) + lua_close (view->lua_state); + grub_free (view->progress_message_text); + grub_free (view->theme_path); + free_icon_cache (view); + grub_free (view); + + set_text_mode (); + destroy_terminal (); +} + +/* Sets MESSAGE as the progress message for the view. The string MESSAGE + must be heap-allocated and will be owned by VIEW. MESSAGE can be 0, in + which case no message is displayed. */ +static void +set_progress_message (grub_gfxmenu_view_t view, char *message) +{ + grub_free (view->progress_message_text); + view->progress_message_text = message; +} + +/* Parse a color string of the form "r, g, b". + Whitespace is insignificant. */ +static grub_video_color_t +parse_color (const char *s) +{ + int red = grub_strtoul (s, 0, 0); + if ((s = grub_strchr (s, ',')) == 0) + goto fail; + s++; + int green = grub_strtoul (s, 0, 0); + if ((s = grub_strchr (s, ',')) == 0) + goto fail; + s++; + int blue = grub_strtoul (s, 0, 0); + int alpha; + if ((s = grub_strchr (s, ',')) == 0) + alpha = 255; + else + { + s++; + alpha = grub_strtoul (s, 0, 0); + } + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + return grub_video_map_rgba (red, green, blue, alpha); + +fail: + return 0; +} + +/* Construct a new box widget using PATTERN to find the pixmap files for it, + storing the new widget at *BOXPTR. PATTERN should be of the form: + "somewhere/style*.png". The '*' then gets substituted with the various + pixmap names that the widget uses. + Return zero on failure, nonzero on success. */ +static int +theme_set_box (grub_gfxmenu_box_t *boxptr, + char *pattern, const char *theme_dir) +{ + char *abspattern; + char *prefix; + char *suffix; + char *star; + grub_gfxmenu_box_t box; + + abspattern = grub_resolve_relative_path (theme_dir, pattern); + if (! abspattern) + return 0; + + star = grub_strchr (abspattern, '*'); + if (! star) + { + grub_free (abspattern); + return 0; + } + + /* Prefix: Get the part before the '*'. */ + prefix = grub_malloc (star - abspattern + 1); + if (! prefix) + { + grub_free (abspattern); + return 0; + } + + grub_memcpy (prefix, abspattern, star - abspattern); + prefix[star - abspattern] = '\0'; + + /* Suffix: Everything after the '*' is the suffix. */ + suffix = star + 1; + + box = grub_gfxmenu_create_box (prefix, suffix); + grub_free (abspattern); /* Note: suffix, star point into abspattern. */ + grub_free (prefix); + if (! box) + return 0; + + if (*boxptr) + (*boxptr)->destroy (*boxptr); + *boxptr = box; + return 1; + +} + +/* Load the contents of the file located at PATH. Allocates a buffer and + stores it in *BUFP and returns the size. Returns -1 on error. */ +static grub_ssize_t +load_file (const char *path, char **bufp) +{ + *bufp = 0; + + grub_file_t f; + grub_errno = GRUB_ERR_NONE; + f = grub_file_open (path); + grub_errno = GRUB_ERR_NONE; + if (! f) + return -1; + + grub_size_t sz = grub_file_size (f); + char *buf = grub_malloc (sz); + if (! buf) + { + grub_file_close (f); + return -1; + } + grub_ssize_t n = grub_file_read (f, buf, sz); + if ((grub_size_t) n != sz) + { + grub_free (buf); + grub_file_close (f); + return -1; + } + grub_file_close (f); + *bufp = buf; + return n; +} + +/* Load the Lua script at PATH as a chunk of Lua code. This results in a + function being pushed on the Lua stack. */ +static int +grub_lua_load_file (lua_State *L, const char *path) +{ + /* Load the contents of the file. */ + char *buf; + grub_ssize_t n; + n = load_file (path, &buf); + if (n == -1) + return -1; + + /* Create a Lua function from it. */ + int err; + err = luaL_loadbuffer (L, buf, n, path); + grub_free (buf); + if (err) + { + grub_error (GRUB_ERR_FILE_READ_ERROR, "Error loading Lua code"); + lua_pop (L, 1); /* Pop the error message. */ + return -1; + } + return 0; +} + +/* Error message buffer for debugging -- we should implement something + better, but this really helps debugging the theme loading by providing + some details on errors when things go wrong. */ +static char errbuf[400]; + +/* Set the specified property NAME on the view to the given string VALUE. + This function takes ownership of both NAME and VALUE, so the caller + should pass pointers to new heap-allocated strings. */ +static void +theme_set_string (grub_gfxmenu_view_t view, char *name, char *value, + const char *theme_dir) +{ + if (! grub_strcmp ("title-font", name)) + view->title_font = grub_font_get (value); + else if (! grub_strcmp ("item-font", name)) + view->item_font = grub_font_get (value); + else if (! grub_strcmp ("selected-item-font", name)) + view->selected_item_font = grub_font_get (value); + else if (! grub_strcmp ("status-font", name)) + view->status_font = grub_font_get (value); + else if (! grub_strcmp ("terminal-font", name)) + { + grub_free (view->terminal_font_name); + view->terminal_font_name = value; + value = 0; /* Prevent value from being freed below. */ + } + else if (! grub_strcmp ("title-color", name)) + view->title_color = parse_color (value); + else if (! grub_strcmp ("item-color", name)) + view->item_color = parse_color (value); + else if (! grub_strcmp ("selected-item-color", name)) + view->selected_item_color = parse_color (value); + else if (! grub_strcmp ("status-color", name)) + view->status_color = parse_color (value); + else if (! grub_strcmp ("status-bg-color", name)) + view->status_bg_color = parse_color (value); + else if (! grub_strcmp ("progress-bar-border-color", name)) + view->progress_bar_border_color = parse_color (value); + else if (! grub_strcmp ("progress-bar-fg-color", name)) + view->progress_bar_fg_color = parse_color (value); + else if (! grub_strcmp ("progress-bar-bg-color", name)) + view->progress_bar_bg_color = parse_color (value); + else if (! grub_strcmp ("desktop-image", name)) + { + struct grub_video_bitmap *raw_bitmap; + struct grub_video_bitmap *scaled_bitmap; + char *path; + path = grub_resolve_relative_path (theme_dir, value); + if (! path) + goto fail; + if (grub_video_bitmap_load (&raw_bitmap, path) != GRUB_ERR_NONE) + { + grub_free (path); + goto fail; + } + grub_free(path); + grub_video_bitmap_create_scaled (&scaled_bitmap, + view->screen.width, + view->screen.height, + raw_bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + grub_video_bitmap_destroy (raw_bitmap); + if (!scaled_bitmap) + goto fail; + + grub_video_bitmap_destroy (view->desktop_image); + view->desktop_image = scaled_bitmap; + } + else if (! grub_strcmp ("desktop-color", name)) + view->desktop_color = parse_color (value); + else if (! grub_strcmp ("menu-box", name)) + theme_set_box (&view->menu_box, value, theme_dir); + else if (! grub_strcmp ("selected-item-box", name)) + theme_set_box (&view->selected_item_box, value, theme_dir); + else if (! grub_strcmp ("terminal-box", name)) + theme_set_box (&view->terminal_box, value, theme_dir); + else if (! grub_strcmp ("title-text", name)) + { + grub_free (view->title_text); + view->title_text = value; + value = 0; /* Prevent value from being freed below. */ + } + else if (! grub_strcmp ("lua-script", name)) + { + grub_error_push (); + /* The 'lua-script' attribute specifies a Lua source file to load. */ + if (! view->lua_state) + { + view->lua_state = lua_open (); + if (! view->lua_state) + goto fail; + luaL_openlibs (view->lua_state); + grub_lua_load_grub_packages (view->lua_state); + } + + /* Load the file. */ + char *path; + path = grub_resolve_relative_path (theme_dir, value); + if (! path) + goto fail; + if (grub_lua_load_file (view->lua_state, path) != 0) + { + grub_strcat (errbuf, "lua script load failed. "); + grub_error (GRUB_ERR_FILE_READ_ERROR, "load script failed"); + grub_free (path); + goto fail; + } + grub_free (path); + + /* Execute the loaded chunk of Lua code. */ + int err; + err = lua_pcall (view->lua_state, 0, 0, 0); + if (err) + { + grub_strcat (errbuf, "script failed: "); + grub_strcat (errbuf, lua_tostring (view->lua_state, -1)); + goto fail; + } + } + +fail: + grub_free (value); + grub_free (name); +} + +/* Set the specified property NAME on the view to the given numeric VALUE. + This function takes ownership NAME, so the caller should pass a pointer + to a new heap-allocated string. */ +static void +theme_set_number (grub_gfxmenu_view_t view, char *name, int value) +{ + if (! grub_strcmp ("icon-width", name)) + view->icon_width = value; + else if (! grub_strcmp ("icon-height", name)) + view->icon_height = value; + else if (! grub_strcmp ("item-height", name)) + view->item_height = value; + else if (! grub_strcmp ("item-padding", name)) + view->item_padding = value; + else if (! grub_strcmp ("item-icon-space", name)) + view->item_icon_space = value; + else if (! grub_strcmp ("item-spacing", name)) + view->item_spacing = value; + + grub_free (name); +} + +/* Set properties on the view based on settings from the specified + theme file. Returns nonzero on success, zero on failure. */ +int +grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, const char *theme_path) +{ + char *theme_dir; + grub_file_t file; + char *buf; + int pos; + int len; + + if (view->lua_state) + { + lua_close (view->lua_state); + view->lua_state = 0; + } + + errbuf[0] = '\0'; /* Clear the error messages. */ + theme_dir = grub_get_dirname (theme_path); + + file = grub_file_open (theme_path); + if (!file) + { + grub_free (theme_dir); + return 0; + } + + len = grub_file_size (file); + buf = grub_malloc (len); + if (! buf) + { + grub_file_close (file); + grub_free (theme_dir); + return 0; + } + if (grub_file_read (file, buf, len) != len) + { + grub_free (buf); + grub_file_close (file); + grub_free (theme_dir); + return 0; + } + + pos = 0; + while (pos < len) + { + /* Skip comments (lines beginning with #). */ + if (pos < len && buf[pos] == '#') + goto nextline; + + /* Get name. */ + /* Find a word character. */ + while (pos < len && grub_isspace(buf[pos])) + pos++; + int name_start = pos; + /* Find the end of the name. */ + while (pos < len + && (grub_isalpha(buf[pos]) + || grub_isdigit(buf[pos]) + || buf[pos] == '_' + || buf[pos] == '-')) + pos++; + int name_end = pos; + + if (name_end - name_start < 1) + goto nextline; + + /* Skip whitespace before separator. */ + while (pos < len + && (buf[pos] == ' ' + || buf[pos] == '\t' + || buf[pos] == '\f')) + pos++; + + /* Read separator. */ + if (buf[pos] != ':') + goto nextline; + + pos++; /* Skip separator. */ + + /* Skip whitespace after separator. */ + while (pos < len + && (buf[pos] == ' ' + || buf[pos] == '\t' + || buf[pos] == '\f')) + pos++; + + /* Get the value based on its type. */ + if (pos < len && buf[pos] == '"') + { + /* String value. (e.g., '"My string"') */ + + int value_start; + int value_end; + + /* Skip the opening quotation mark. */ + pos++; + /* Get string value. */ + value_start = pos; + /* Find the ending quotation mark. */ + while (pos < len + && !(buf[pos] == '"' + || buf[pos] == '\n')) + pos++; + value_end = pos; + theme_set_string (view, + grub_new_substring (buf, name_start, name_end), + grub_new_substring (buf, value_start, value_end), + theme_dir); + } + else if (pos < len && grub_isdigit(buf[pos])) + { + /* Numeric value. (e.g., '123') */ + + int value_start; + int value_end; + char *value_str; + int value; + + /* Get numeric value. */ + value_start = pos; + /* Find the end of the digit sequence. */ + while (pos < len && grub_isdigit(buf[pos])) + pos++; + value_end = pos; + value_str = grub_new_substring (buf, value_start, value_end); + if (!value_str) + continue; + value = grub_strtoul (value_str, 0, 0); + grub_free (value_str); + theme_set_number (view, + grub_new_substring (buf, name_start, name_end), + value); + } + +nextline: + /* Eat characters up to the newline. */ + while (pos < len && buf[pos] != '\n') + pos++; + pos++; /* Eat the newline. */ + } + + grub_free (view->theme_path); + view->theme_path = grub_strdup (theme_path); + free_icon_cache (view); /* There may be new icons for this theme. */ + grub_free (buf); + grub_file_close (file); + grub_free (theme_dir); + + return 1; +} + +static void +draw_background (grub_gfxmenu_view_t view) +{ + if (view->desktop_image) + { + struct grub_video_bitmap *img = view->desktop_image; + grub_video_blit_bitmap (img, GRUB_VIDEO_BLIT_REPLACE, + view->screen.x, view->screen.y, 0, 0, + grub_video_bitmap_get_width (img), + grub_video_bitmap_get_height (img)); + } + else + { + grub_video_fill_rect (view->desktop_color, + view->screen.x, view->screen.y, + view->screen.width, view->screen.height); + } +} + +static void +draw_lua_desktop (grub_gfxmenu_view_t view) +{ + lua_State *L = view->lua_state; + if (! L) + return; /* No Lua state, just return. */ + + lua_getglobal (L, "theme"); + if (! lua_isnoneornil (L, -1)) + { + lua_getfield (L, -1, "draw_desktop"); + if (! lua_isnoneornil (L, -1)) + { + lua_getglobal (L, "theme"); + if (! lua_isnoneornil (L, -1)) + { + /* We should check the return value of lua_pcall(), but we don't + have a good way to handle the error anyway, so for now we don't. */ + if (lua_pcall (L, 1, 0, 0) != 0) + { + /* Display the error message. */ + grub_strcpy (errbuf, lua_tostring (L, -1)); + } + } + } + } + + lua_settop (L, 0); /* Clear the stack. */ +} + +static const char icon_extension[] = ".png"; + +static struct grub_video_bitmap * +try_loading_icon (grub_gfxmenu_view_t view, + const char *dir, const char *class_name) +{ + char *path = grub_malloc (grub_strlen (dir) + + grub_strlen (class_name) + + grub_strlen (icon_extension) + + 100); + if (! path) + return 0; + + grub_strcpy (path, dir); + grub_strcat (path, class_name); + grub_strcat (path, icon_extension); + + struct grub_video_bitmap *raw_bitmap; + grub_video_bitmap_load (&raw_bitmap, path); + grub_free (path); + grub_errno = GRUB_ERR_NONE; /* Critical to clear the error!! */ + if (! raw_bitmap) + return 0; + + struct grub_video_bitmap *scaled_bitmap; + grub_video_bitmap_create_scaled (&scaled_bitmap, + view->icon_width, view->icon_height, + raw_bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + grub_video_bitmap_destroy (raw_bitmap); + if (! scaled_bitmap) + return 0; + + return scaled_bitmap; +} + +static struct grub_video_bitmap * +get_icon_by_class (grub_gfxmenu_view_t view, const char *class_name) +{ + + /* First check the view's icon cache. */ + grub_gfxmenu_view_icon_t vicon; + for (vicon = view->icon_cache.next; vicon; vicon = vicon->next) + { + if (grub_strcmp (vicon->class_name, class_name) == 0) + return vicon->bitmap; + } + + /* Otherwise, we search for an icon to load. */ + if (! view->theme_path) + return 0; + + char *theme_dir = grub_get_dirname (view->theme_path); + char *icons_dir; + struct grub_video_bitmap *icon; + icon = 0; + /* First try the theme's own icons, from "grub/themes/NAME/icons/" */ + icons_dir = grub_resolve_relative_path (theme_dir, "icons/"); + if (icons_dir) + { + icon = try_loading_icon (view, icons_dir, class_name); + grub_free (icons_dir); + } + if (! icon) + { + /* If the theme doesn't have an appropriate icon, check in + "grub/themes/icons". */ + icons_dir = grub_resolve_relative_path (theme_dir, "../icons/"); + if (icons_dir) + { + icon = try_loading_icon (view, icons_dir, class_name); + grub_free (icons_dir); + } + } + grub_free (theme_dir); + + if (! icon) + return 0; + + vicon = grub_malloc (sizeof (*vicon)); + if (! vicon) + { + grub_video_bitmap_destroy (icon); + return 0; + } + vicon->class_name = class_name; + vicon->bitmap = icon; + vicon->next = view->icon_cache.next; + view->icon_cache.next = vicon; /* Link it into the cache. */ + return vicon->bitmap; +} + +static struct grub_video_bitmap * +get_item_icon (grub_gfxmenu_view_t view, int item_index) +{ + grub_menu_entry_t entry; + entry = grub_gfxmenu_model_get_entry (view->model, item_index); + if (! entry) + return 0; + + struct grub_menu_entry_class *c; + struct grub_video_bitmap *icon; + icon = 0; + for (c = entry->classes->next; c && ! icon; c = c->next) + icon = get_icon_by_class (view, c->name); + return icon; +} + +static void +draw_menu (grub_gfxmenu_view_t view) +{ + int boxpad = view->item_padding; + int icon_text_space = view->item_icon_space; + int item_vspace = view->item_spacing; + + int ascent = grub_font_get_ascent (view->item_font); + int descent = grub_font_get_descent (view->item_font); + int item_height = view->item_height; + + int num_items = grub_gfxmenu_model_get_num_entries (view->model); + grub_video_rect_t r; + r.width = view->screen.width * 4 / 5; + /* Set the menu box height to fit the items. */ + r.height = (item_height * num_items + + item_vspace * (num_items - 1) + + 2 * boxpad); + r.x = (view->screen.width - r.width) / 2; + r.y = (view->screen.height - r.height) / 2; + view->menu_box->set_content_size (view->menu_box, r.width, r.height); + + int menu_box_left_pad = view->menu_box->get_left_pad (view->menu_box); + int menu_box_top_pad = view->menu_box->get_top_pad (view->menu_box); + view->menu_box->draw (view->menu_box, + r.x - menu_box_left_pad, r.y - menu_box_top_pad); + + int item_top = r.y + boxpad; + int item_left = r.x + boxpad; + int i; + + for (i = 0; i < num_items; i++) + { + int is_selected = + (i == grub_gfxmenu_model_get_selected_index (view->model)); + + if (is_selected) + { + view->selected_item_box->set_content_size (view->selected_item_box, + r.width - 2 * boxpad, + item_height); + int leftpad = view->selected_item_box->get_left_pad (view->selected_item_box); + int toppad = view->selected_item_box->get_top_pad (view->selected_item_box); + view->selected_item_box->draw (view->selected_item_box, + item_left - leftpad, + item_top - toppad); + } + + struct grub_video_bitmap *icon; + if ((icon = get_item_icon (view, i)) != 0) + grub_video_blit_bitmap (icon, GRUB_VIDEO_BLIT_BLEND, + item_left, + item_top + (item_height - view->icon_height) / 2, + 0, 0, view->icon_width, view->icon_height); + + const char *item_title = + grub_gfxmenu_model_get_entry_title (view->model, i); + grub_video_draw_string (item_title, + (is_selected + ? view->selected_item_font + : view->item_font), + (is_selected + ? view->selected_item_color + : view->item_color), + item_left + view->icon_width + icon_text_space, + (item_top + (item_height - (ascent + descent)) + / 2 + ascent)); + + item_top += item_height + item_vspace; + } +} + +static void +draw_title (grub_gfxmenu_view_t view) +{ + if (! view->title_text) + return; + + /* Center the title. */ + int title_width = grub_font_get_string_width (view->title_font, + view->title_text); + int x = (view->screen.width - title_width) / 2; + int y = 40 + grub_font_get_ascent (view->title_font); + grub_video_draw_string (view->title_text, + view->title_font, view->title_color, + x, y); +} + +static void +draw_status (grub_gfxmenu_view_t view) +{ + int descent = grub_font_get_descent (view->status_font); + int ascent = grub_font_get_ascent (view->status_font); + int vpad = 5; + int textheight = descent + ascent + 1; + int h = 2 * vpad + 2 * textheight; + + grub_video_fill_rect (view->status_bg_color, + 0, view->screen.height - h, + view->screen.width, view->screen.height - 1); + + int texty = view->screen.height - h + vpad + ascent; + grub_video_draw_string ("Select an item with the arrow keys and " + "press Enter to boot.", + view->status_font, view->status_color, 30, texty); + texty += textheight; + grub_video_draw_string ("Press: 'c' for command line; 't' to switch to " + "non-graphical menu.", + view->status_font, view->status_color, 30, texty); +} + +static void +draw_timeout (grub_gfxmenu_view_t view) +{ + int timeout = grub_gfxmenu_model_get_timeout_ms (view->model); + if (timeout == -1) + return; + + int remaining = grub_gfxmenu_model_get_timeout_remaining_ms (view->model); + if (remaining < 0) + remaining = 0; + + int t = timeout - remaining; + + /* Set the timeout bar's frame. */ + grub_video_rect_t f; + f.width = view->screen.width * 3 / 5; + f.height = view->screen.height / 25; + f.x = view->screen.x + (view->screen.width - f.width) / 2; + f.y = view->screen.y + view->screen.height - 90; + + /* First attempt just uses filled rectangles; + TODO we should enhance with a pixmap themed progress bar component. */ + + /* Border. */ + grub_video_fill_rect (view->progress_bar_border_color, + f.x - 1, f.y - 1, + f.width + 2, f.height + 2); + + /* Bar background. */ + int barwidth = f.width * t / timeout; + grub_video_fill_rect (view->progress_bar_bg_color, + f.x + barwidth, f.y, + f.width - barwidth, f.height); + + /* Bar foreground. */ + grub_video_fill_rect (view->progress_bar_fg_color, + f.x, f.y, + barwidth, f.height); + + char *text = grub_malloc (200); + if (!text) + return; + int seconds_remaining_rounded_up = (remaining + 999) / 1000; + grub_sprintf (text, + "The highlighted entry will be booted automatically in %d s.", + seconds_remaining_rounded_up); + grub_font_t font = view->status_font; + grub_video_color_t color = view->progress_bar_border_color; + + /* Center the text. */ + int text_width = grub_font_get_string_width (font, text); + int x = f.x + (f.width - text_width) / 2; + int y = (f.y + (f.height - grub_font_get_descent (font)) / 2 + + grub_font_get_ascent (font) / 2); + grub_video_draw_string (text, font, color, x, y); + grub_free (text); +} + +static void +draw_message (grub_gfxmenu_view_t view) +{ + char *text = view->progress_message_text; + if (! text) + return; + + grub_font_t font = view->status_font; + grub_video_color_t color = view->status_color; + + /* Set the timeout bar's frame. */ + grub_video_rect_t f; + f.width = view->screen.width * 4 / 5; + f.height = 50; + f.x = view->screen.x + (view->screen.width - f.width) / 2; + f.y = view->screen.y + view->screen.height - 90 - 20 - f.height; + + /* Border. */ + grub_video_fill_rect (color, + f.x-1, f.y-1, f.width+2, f.height+2); + /* Fill. */ + grub_video_fill_rect (view->status_bg_color, + f.x, f.y, f.width, f.height); + + /* Center the text. */ + int text_width = grub_font_get_string_width (font, text); + int x = f.x + (f.width - text_width) / 2; + int y = (f.y + (f.height - grub_font_get_descent (font)) / 2 + + grub_font_get_ascent (font) / 2); + grub_video_draw_string (text, font, color, x, y); +} + + +void +grub_gfxmenu_view_draw (grub_gfxmenu_view_t view) +{ + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + draw_background (view); + draw_lua_desktop (view); + draw_menu (view); + draw_title (view); + draw_status (view); + draw_timeout (view); + draw_message (view); + + /* Draw error messages if there are any. */ + if (errbuf[0]) + grub_video_draw_string (errbuf, + view->status_font, view->title_color, + 10,20); +} + +static grub_err_t +set_graphics_mode (void) +{ + const char *doublebuf_str = grub_env_get ("doublebuffering"); + int doublebuf_flags = + (doublebuf_str && doublebuf_str[0] == 'n') + ? 0 + : GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + + const char *modestr = grub_env_get ("gfxmode"); + if (grub_video_setup_preferred_mode (modestr, doublebuf_flags, 640, 480) + != GRUB_ERR_NONE) + return grub_errno; + + return GRUB_ERR_NONE; +} + +static grub_err_t +set_text_mode (void) +{ + return grub_video_restore (); +} + +static int term_target_width; +static int term_target_height; +static struct grub_video_render_target *term_target; +static int term_initialized; +static grub_term_t term_original; +static grub_gfxmenu_view_t term_view; + +static void +repaint_terminal (int x __attribute ((unused)), + int y __attribute ((unused)), + int width __attribute ((unused)), + int height __attribute ((unused))) +{ + if (! term_view) + return; + + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + grub_gfxmenu_view_draw (term_view); + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + int termx = term_view->screen.x + + term_view->screen.width * (10 - 7) / 10 / 2; + int termy = term_view->screen.y + + term_view->screen.height * (10 - 7) / 10 / 2; + + grub_gfxmenu_box_t term_box = term_view->terminal_box; + if (term_box) + { + term_box->set_content_size (term_box, + term_target_width, term_target_height); + + term_box->draw (term_box, + termx - term_box->get_left_pad (term_box), + termy - term_box->get_top_pad (term_box)); + } + + grub_video_blit_render_target (term_target, GRUB_VIDEO_BLIT_REPLACE, + termx, termy, + 0, 0, term_target_width, term_target_height); + grub_video_swap_buffers (); +} + +static void +init_terminal (grub_gfxmenu_view_t view) +{ + term_original = grub_term_get_current (); + + term_target_width = view->screen.width * 7 / 10; + term_target_height = view->screen.height * 7 / 10; + + grub_video_create_render_target (&term_target, + term_target_width, + term_target_height, + GRUB_VIDEO_MODE_TYPE_RGB + | GRUB_VIDEO_MODE_TYPE_ALPHA); + if (grub_errno != GRUB_ERR_NONE) + return; + + /* Note: currently there is no API for changing the gfxterm font + on the fly, so whatever font the initially loaded theme specifies + will be permanent. */ + grub_gfxterm_init_window (term_target, 0, 0, + term_target_width, term_target_height, + view->terminal_font_name, 3); + if (grub_errno != GRUB_ERR_NONE) + return; + term_initialized = 1; + + /* XXX: store static pointer to the 'view' object so the repaint callback can access it. */ + term_view = view; + grub_gfxterm_set_repaint_callback (repaint_terminal); + grub_term_set_current (grub_gfxterm_get_term ()); +} + +static void destroy_terminal (void) +{ + term_view = 0; + if (term_initialized) + grub_gfxterm_destroy_window (); + grub_gfxterm_set_repaint_callback (0); + if (term_target) + grub_video_delete_render_target (term_target); + if (term_original) + grub_term_set_current (term_original); +} + + +static void +notify_booting (void *userdata, grub_menu_entry_t entry) +{ + grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata; + + char *s = grub_malloc (100 + grub_strlen (entry->title)); + if (!s) + return; + + grub_sprintf (s, "Booting '%s'", entry->title); + set_progress_message (view, s); + grub_gfxmenu_view_draw (view); + grub_video_swap_buffers (); +} + +static void +notify_fallback (void *userdata, grub_menu_entry_t entry) +{ + grub_gfxmenu_view_t view = (grub_gfxmenu_view_t) userdata; + + char *s = grub_malloc (100 + grub_strlen (entry->title)); + if (!s) + return; + + grub_sprintf (s, "Falling back to '%s'", entry->title); + set_progress_message (view, s); + grub_gfxmenu_view_draw (view); + grub_video_swap_buffers (); +} + +static void +notify_execution_failure (void *userdata __attribute__ ((unused))) +{ +} + + +static struct grub_menu_execute_callback execute_callback = +{ + .notify_booting = notify_booting, + .notify_fallback = notify_fallback, + .notify_failure = notify_execution_failure +}; + +int +grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view, + grub_menu_entry_t entry) +{ + grub_menu_execute_with_fallback (grub_gfxmenu_model_get_menu (view->model), + entry, &execute_callback, (void *) view); + + if (set_graphics_mode () != GRUB_ERR_NONE) + return 0; /* Failure. */ + + /* If we returned, there was a failure. */ + set_progress_message (view, grub_strdup ("Unable to automatically boot. " + "Press SPACE to continue.")); + grub_gfxmenu_view_draw (view); + grub_video_swap_buffers (); + while (GRUB_TERM_ASCII_CHAR(grub_getkey ()) != ' ') + { + /* Wait for SPACE to be pressed. */ + } + + set_progress_message (view, 0); /* Clear the message. */ + + return 1; /* Ok. */ +} + +int +grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view, + grub_menu_entry_t entry) +{ + /* Currently we switch back to text mode by restoring + the original terminal before executing the menu entry. + It is hard to make it work when executing a menu entry + that switches video modes -- it using gfxterm in a + window, the repaint callback seems to crash GRUB. */ + /* TODO: Determine if this works when 'gfxterm' was set as + the current terminal before invoking the gfxmenu. */ + destroy_terminal (); + + grub_menu_execute_entry (entry); + if (grub_errno != GRUB_ERR_NONE) + grub_wait_after_message (); + + if (set_graphics_mode () != GRUB_ERR_NONE) + return 0; /* Failure. */ + + init_terminal (view); + return 1; /* Ok. */ +} + +void +grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view __attribute__((unused))) +{ + grub_cmdline_run (1); +} + === added file 'gfxmenu/widget-box.c' --- gfxmenu/widget-box.c 1970-01-01 00:00:00 +0000 +++ gfxmenu/widget-box.c 2008-07-19 18:25:18 +0000 @@ -0,0 +1,244 @@ +/* widget_box.c - Pixmap-stylized box widget. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 +#include +#include +#include + +enum box_pixmaps +{ + BOX_PIXMAP_NW, BOX_PIXMAP_NE, BOX_PIXMAP_SE, BOX_PIXMAP_SW, + BOX_PIXMAP_N, BOX_PIXMAP_E, BOX_PIXMAP_S, BOX_PIXMAP_W, + BOX_PIXMAP_CENTER +}; + +static const char *box_pixmap_names[] = { + /* Corners: */ + "nw", "ne", "se", "sw", + /* Sides: */ + "n", "e", "s", "w", + /* Center: */ + "c" +}; + +#define BOX_NUM_PIXMAPS (sizeof(box_pixmap_names)/sizeof(*box_pixmap_names)) + +static void +draw (grub_gfxmenu_box_t self, int x, int y) +{ + int height_n; + int height_s; + int height_e; + int height_w; + int width_n; + int width_s; + int width_e; + int width_w; + unsigned i; + + /* Don't try to draw if any pixmaps are null, except the center. */ + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + { + if (i != BOX_PIXMAP_CENTER && self->scaled_pixmaps[i] == 0) + return; + } + + height_n = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_N]); + height_s = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_S]); + height_e = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_E]); + height_w = grub_video_bitmap_get_height (self->scaled_pixmaps[BOX_PIXMAP_W]); + width_n = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_N]); + width_s = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_S]); + width_e = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_E]); + width_w = grub_video_bitmap_get_width (self->scaled_pixmaps[BOX_PIXMAP_W]); + + /* Draw sides. */ + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_N], + GRUB_VIDEO_BLIT_BLEND, + x + width_w, y, 0, 0, width_n, height_n); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_S], + GRUB_VIDEO_BLIT_BLEND, + x + width_w, y + height_n + height_w, + 0, 0, width_s, height_s); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_E], + GRUB_VIDEO_BLIT_BLEND, + x + width_w + width_n, y + height_n, + 0, 0, width_e, height_e); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_W], + GRUB_VIDEO_BLIT_BLEND, + x, y + height_n, 0, 0, width_w, height_w); + + /* Draw corners. */ + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_NW], + GRUB_VIDEO_BLIT_BLEND, + x, y, 0, 0, width_w, height_n); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_NE], + GRUB_VIDEO_BLIT_BLEND, + x + width_w + width_n, y, + 0, 0, width_e, height_n); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_SE], + GRUB_VIDEO_BLIT_BLEND, + x + width_w + width_n, y + height_n + height_e, + 0, 0, width_e, height_s); + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_SW], + GRUB_VIDEO_BLIT_BLEND, + x, y + height_n + height_w, + 0, 0, width_w, height_s); + + /* Draw center. */ + if (self->scaled_pixmaps[BOX_PIXMAP_CENTER]) + grub_video_blit_bitmap (self->scaled_pixmaps[BOX_PIXMAP_CENTER], + GRUB_VIDEO_BLIT_BLEND, + x + width_w, y + height_n, + 0, 0, self->width, self->height); +} + +static void +scale_pixmap (grub_gfxmenu_box_t self, int i, int w, int h) +{ + struct grub_video_bitmap **scaled = &self->scaled_pixmaps[i]; + struct grub_video_bitmap *raw = self->raw_pixmaps[i]; + + if (raw == 0) + return; + + if (w == -1) + w = grub_video_bitmap_get_width (raw); + if (h == -1) + h = grub_video_bitmap_get_height (raw); + + if (*scaled == 0 + || ((int) grub_video_bitmap_get_width (*scaled) != w) + || ((int) grub_video_bitmap_get_height (*scaled) != h)) + { + if (*scaled) + { + grub_video_bitmap_destroy (*scaled); + *scaled = 0; + } + grub_video_bitmap_create_scaled (scaled, w, h, raw, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + } +} + +static void +set_content_size (grub_gfxmenu_box_t self, + int width, int height) +{ + self->width = width; + self->height = height; + /* Resize sides to match the width and height. */ + /* It is assumed that the corners width/height match the adjacent sides. */ + + /* Resize N and S sides to match width. */ + scale_pixmap(self, BOX_PIXMAP_N, width, -1); + scale_pixmap(self, BOX_PIXMAP_S, width, -1); + + /* Resize E and W sides to match height. */ + scale_pixmap(self, BOX_PIXMAP_E, -1, height); + scale_pixmap(self, BOX_PIXMAP_W, -1, height); + + /* Don't scale the corners--they are assumed to match the sides. */ + scale_pixmap(self, BOX_PIXMAP_NW, -1, -1); + scale_pixmap(self, BOX_PIXMAP_SW, -1, -1); + scale_pixmap(self, BOX_PIXMAP_NE, -1, -1); + scale_pixmap(self, BOX_PIXMAP_SE, -1, -1); + + /* Scale the center area. */ + scale_pixmap(self, BOX_PIXMAP_CENTER, width, height); +} + +static int +get_left_pad (grub_gfxmenu_box_t self) +{ + return grub_video_bitmap_get_width (self->raw_pixmaps[BOX_PIXMAP_W]); +} + +static int +get_top_pad (grub_gfxmenu_box_t self) +{ + return grub_video_bitmap_get_height (self->raw_pixmaps[BOX_PIXMAP_N]); +} + +static void +destroy (grub_gfxmenu_box_t self) +{ + unsigned i; + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + { + if (self->raw_pixmaps[i]) + grub_video_bitmap_destroy(self->raw_pixmaps[i]); + self->raw_pixmaps[i] = 0; + + if (self->scaled_pixmaps[i]) + grub_video_bitmap_destroy(self->scaled_pixmaps[i]); + self->scaled_pixmaps[i] = 0; + } + grub_free (self->raw_pixmaps); + self->raw_pixmaps = 0; + grub_free (self->scaled_pixmaps); + self->scaled_pixmaps = 0; + + grub_free (self); /* Free self: must be the last step! */ +} + + +grub_gfxmenu_box_t +grub_gfxmenu_create_box (const char *pixmaps_prefix, + const char *pixmaps_suffix) +{ + char path[200]; + unsigned i; + grub_gfxmenu_box_t box; + + box = (grub_gfxmenu_box_t) grub_malloc (sizeof (*box)); + if (!box) + return 0; + box->width = 0; + box->height = 0; + box->raw_pixmaps = + (struct grub_video_bitmap **) + grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *)); + box->scaled_pixmaps = + (struct grub_video_bitmap **) + grub_malloc (BOX_NUM_PIXMAPS * sizeof (struct grub_video_bitmap *)); + + for (i = 0; i < BOX_NUM_PIXMAPS; i++) + { + /* TODO XXX dynamically allocate PATH, making sure it's large enough */ + grub_sprintf (path, "%s%s%s", + pixmaps_prefix, box_pixmap_names[i], pixmaps_suffix); + grub_video_bitmap_load (&box->raw_pixmaps[i], path); + box->scaled_pixmaps[i] = 0; + } + + box->draw = draw; + box->set_content_size = set_content_size; + box->get_left_pad = get_left_pad; + box->get_top_pad = get_top_pad; + box->destroy = destroy; + + return box; +} + === added file 'include/grub/bitmap_scale.h' --- include/grub/bitmap_scale.h 1970-01-01 00:00:00 +0000 +++ include/grub/bitmap_scale.h 2008-07-03 14:27:43 +0000 @@ -0,0 +1,54 @@ +/* bitmap_scale.h - Bitmap scaling functions. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007 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 . + */ + +#ifndef GRUB_BITMAP_SCALE_HEADER +#define GRUB_BITMAP_SCALE_HEADER 1 + +#include +#include +#include + +enum grub_video_bitmap_scale_method +{ + /* Choose the fastest interpolation algorithm. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST, + /* Choose the highest quality interpolation algorithm. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST, + /* Nearest neighbor interpolation algorithm. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST, + /* Bilinear interpolation algorithm. */ + GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR +}; + +grub_err_t +grub_video_bitmap_scale_nn (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src); + +grub_err_t +grub_video_bitmap_scale_bilinear (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src); + +grub_err_t +grub_video_bitmap_create_scaled (struct grub_video_bitmap **dst, + int dst_width, int dst_height, + struct grub_video_bitmap *src, + enum + grub_video_bitmap_scale_method scale_method); + +#endif /* ! GRUB_BITMAP_SCALE_HEADER */ === modified file 'include/grub/font.h' --- include/grub/font.h 2007-07-21 22:32:33 +0000 +++ include/grub/font.h 2008-07-03 14:17:31 +0000 @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2003,2007 Free Software Foundation, Inc. + * Copyright (C) 2003,2007,2008 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,33 +21,85 @@ #include -#define GRUB_FONT_MAGIC "PPF\x7f" +/* Forward declaration of opaque structure grub_font. + * Users only pass struct grub_font pointers to the font module functions, + * and do not have knowledge of the structure contents. */ +struct grub_font; + +/* Font type used to access font functions. */ +typedef struct grub_font *grub_font_t; + struct grub_font_glyph { - /* Glyph width in pixels. */ - grub_uint8_t width; - - /* Glyph height in pixels. */ - grub_uint8_t height; - - /* Glyph width in characters. */ - grub_uint8_t char_width; - - /* Glyph baseline position in pixels (from up). */ - grub_uint8_t baseline; - - /* Glyph bitmap data array of bytes in ((width + 7) / 8) * height. - Bitmap is formulated by height scanlines, each scanline having - width number of pixels. Pixels are coded as bits, value 1 meaning - of opaque pixel and 0 is transparent. If width does not fit byte - boundary, it will be padded with 0 to make it fit. */ - grub_uint8_t bitmap[32]; + /* Reference to the font this glyph belongs to. */ + grub_font_t font; + + /* Glyph bitmap width in pixels. */ + grub_uint16_t width; + + /* Glyph bitmap height in pixels. */ + grub_uint16_t height; + + /* Glyph bitmap x offset in pixels. Add to screen coordinate. */ + grub_int16_t offset_x; + + /* Glyph bitmap y offset in pixels. Subtract from screen coordinate. */ + grub_int16_t offset_y; + + /* Number of pixels to advance to start the next character. */ + grub_uint16_t device_width; + + /* Row-major order, packed bits (no padding; rows can break within a byte). + * The length of the array is (width * height + 7) / 8. Within a + * byte, the most significant bit is the first (leftmost/uppermost) pixel. + * Pixels are coded as bits, value 1 meaning of opaque pixel and 0 is + * transparent. If the length of the array does not fit byte boundary, it + * will be padded with 0 bits to make it fit. */ + grub_uint8_t bitmap[0]; }; -typedef struct grub_font_glyph *grub_font_glyph_t; - -int grub_font_get_glyph (grub_uint32_t code, - grub_font_glyph_t glyph); + +/****** font/font.c ******/ + +/* Get the font that has the specified name. Font names are in the form + * "Family Name Bold Italic 14", where Bold and Italic are optional. + * If no font matches the name specified, the most recently loaded font + * is returned as a fallback. */ +grub_font_t grub_font_get (const char *font_name); + +const char *grub_font_get_name (grub_font_t font); + +int grub_font_get_max_char_width (grub_font_t font); + +int grub_font_get_max_char_height (grub_font_t font); + +int grub_font_get_ascent (grub_font_t font); + +int grub_font_get_descent (grub_font_t font); + +int grub_font_get_string_width (grub_font_t font, const char *str); + + +/****** font/loader.c ******/ + +/* + * Load a font and add it to the beginning of the global font list. + * Returns: 0 upon success; nonzero upon failure. + */ +int grub_font_load (const char *filename); + +/* Get the glyph for FONT corresponding to the Unicode code point CODE. + * Returns a pointer to an glyph indicating there is no glyph available + * if CODE does not exist in the font. The glyphs are cached once loaded. */ +struct grub_font_glyph *grub_font_get_glyph (grub_font_t font, + grub_uint32_t code); + +/* Get a glyph corresponding to the codepoint CODE. If no glyph is available + * for CODE in the available fonts, then a glyph representing an unknown + * character is returned. This function never returns NULL. + * The returned glyph is owned by the font manager and should not be freed + * by the caller. The glyphs are cached. */ +struct grub_font_glyph *grub_font_get_glyph_any (grub_uint32_t code); #endif /* ! GRUB_FONT_HEADER */ === added file 'include/grub/font_internal.h' --- include/grub/font_internal.h 1970-01-01 00:00:00 +0000 +++ include/grub/font_internal.h 2008-07-03 14:16:11 +0000 @@ -0,0 +1,71 @@ +/* font_internal.h - Font declarations for use internally by the font module. + * Users of the font module should not include this header. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2005,2006,2007,2008 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 . + */ + +#ifndef GRUB_FONT_INTERNAL_HEADER +#define GRUB_FONT_INTERNAL_HEADER 1 + +#include +#include +#include +#include +#include +#include +#include + +#define FONT_DEBUG 0 + +struct char_index_entry +{ + grub_uint32_t code; + grub_uint8_t storage_flags; + grub_uint32_t offset; + struct grub_font_glyph *glyph; /* Glyph if loaded, or null. */ +}; + +struct grub_font +{ + char *name; + grub_file_t file; + short max_char_width; + short max_char_height; + short ascent; + short descent; + grub_uint32_t num_chars; + struct char_index_entry *char_index; +}; + +struct font_node +{ + struct font_node *next; + struct grub_font *value; +}; + +extern struct font_node *grub_font_list; + + +/****** loader.c ******/ + +/* Initialize the font loader module. */ +void +grub_font_loader_init (void); + + +#endif /* ! GRUB_FONT_INTERNAL_HEADER */ + === added file 'include/grub/gfxmenu_model.h' --- include/grub/gfxmenu_model.h 1970-01-01 00:00:00 +0000 +++ include/grub/gfxmenu_model.h 2008-07-26 23:37:04 +0000 @@ -0,0 +1,59 @@ +/* gfxmenu_model.h - gfxmenu model interface. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 . + */ + +#ifndef GRUB_GFXMENU_MODEL_HEADER +#define GRUB_GFXMENU_MODEL_HEADER 1 + +#include + +struct grub_gfxmenu_model; /* Forward declaration of opaque type. */ +typedef struct grub_gfxmenu_model *grub_gfxmenu_model_t; + + +grub_gfxmenu_model_t grub_gfxmenu_model_new (grub_menu_t menu); + +void grub_gfxmenu_model_destroy (grub_gfxmenu_model_t model); + +grub_menu_t grub_gfxmenu_model_get_menu (grub_gfxmenu_model_t model); + +void grub_gfxmenu_model_set_timeout (grub_gfxmenu_model_t model); + +void grub_gfxmenu_model_clear_timeout (grub_gfxmenu_model_t model); + +int grub_gfxmenu_model_get_timeout_ms (grub_gfxmenu_model_t model); + +int grub_gfxmenu_model_get_timeout_remaining_ms (grub_gfxmenu_model_t model); + +int grub_gfxmenu_model_timeout_expired (grub_gfxmenu_model_t model); + +int grub_gfxmenu_model_get_num_entries (grub_gfxmenu_model_t model); + +int grub_gfxmenu_model_get_selected_index (grub_gfxmenu_model_t model); + +void grub_gfxmenu_model_set_selected_index (grub_gfxmenu_model_t model, + int index); + +const char *grub_gfxmenu_model_get_entry_title (grub_gfxmenu_model_t model, + int index); + +grub_menu_entry_t grub_gfxmenu_model_get_entry (grub_gfxmenu_model_t model, + int index); + +#endif /* GRUB_GFXMENU_MODEL_HEADER */ + === added file 'include/grub/gfxmenu_view.h' --- include/grub/gfxmenu_view.h 1970-01-01 00:00:00 +0000 +++ include/grub/gfxmenu_view.h 2008-07-26 23:37:04 +0000 @@ -0,0 +1,53 @@ +/* gfxmenu_view.h - gfxmenu view interface. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 . + */ + +#ifndef GRUB_GFXMENU_VIEW_HEADER +#define GRUB_GFXMENU_VIEW_HEADER 1 + +#include +#include +#include +#include + +struct grub_gfxmenu_view; /* Forward declaration of opaque type. */ +typedef struct grub_gfxmenu_view *grub_gfxmenu_view_t; + + +grub_gfxmenu_view_t grub_gfxmenu_view_new (const char *theme_path, + grub_gfxmenu_model_t model); + +void grub_gfxmenu_view_destroy (grub_gfxmenu_view_t view); + +/* Set properties on the view based on settings from the specified + theme file. */ +int grub_gfxmenu_view_load_theme (grub_gfxmenu_view_t view, + const char *theme_path); + +void grub_gfxmenu_view_draw (grub_gfxmenu_view_t view); + +int grub_gfxmenu_view_execute_with_fallback (grub_gfxmenu_view_t view, + grub_menu_entry_t entry); + +int grub_gfxmenu_view_execute_entry (grub_gfxmenu_view_t view, + grub_menu_entry_t entry); + +void grub_gfxmenu_view_run_terminal (grub_gfxmenu_view_t view); + +#endif /* GRUB_GFXMENU_VIEW_HEADER */ + === added file 'include/grub/gfxterm.h' --- include/grub/gfxterm.h 1970-01-01 00:00:00 +0000 +++ include/grub/gfxterm.h 2008-07-19 19:56:06 +0000 @@ -0,0 +1,41 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 . + */ + +#ifndef GRUB_GFXTERM_HEADER +#define GRUB_GFXTERM_HEADER 1 + +#include +#include +#include +#include + +grub_err_t +grub_gfxterm_init_window (struct grub_video_render_target *target, + int x, int y, int width, int height, + const char *font_name, int border_width); + +void grub_gfxterm_destroy_window (void); + +grub_term_t grub_gfxterm_get_term (void); + +typedef void (*grub_gfxterm_repaint_callback_t)(int x, int y, + int width, int height); + +void grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func); + +#endif /* ! GRUB_GFXTERM_HEADER */ === added file 'include/grub/gfxwidgets.h' --- include/grub/gfxwidgets.h 1970-01-01 00:00:00 +0000 +++ include/grub/gfxwidgets.h 2008-07-19 18:25:18 +0000 @@ -0,0 +1,47 @@ +/* gfxwidgets.h - Widgets for the graphical menu (gfxmenu). */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 . + */ + +#ifndef GRUB_GFXWIDGETS_HEADER +#define GRUB_GFXWIDGETS_HEADER 1 + +#include + +typedef struct grub_gfxmenu_box *grub_gfxmenu_box_t; + +struct grub_gfxmenu_box +{ + /* The size of the content. */ + int width; + int height; + + struct grub_video_bitmap **raw_pixmaps; + struct grub_video_bitmap **scaled_pixmaps; + + void (*draw) (grub_gfxmenu_box_t self, int x, int y); + void (*set_content_size) (grub_gfxmenu_box_t self, + int width, int height); + int (*get_left_pad) (grub_gfxmenu_box_t self); + int (*get_top_pad) (grub_gfxmenu_box_t self); + void (*destroy) (grub_gfxmenu_box_t self); +}; + +grub_gfxmenu_box_t grub_gfxmenu_create_box (const char *pixmaps_prefix, + const char *pixmaps_suffix); + +#endif /* ! GRUB_GFXWIDGETS_HEADER */ === modified file 'include/grub/i386/pc/vbeblit.h' --- include/grub/i386/pc/vbeblit.h 2007-07-21 22:32:33 +0000 +++ include/grub/i386/pc/vbeblit.h 2008-07-09 17:27:53 +0000 @@ -25,55 +25,93 @@ struct grub_video_i386_vbeblit_info; void -grub_video_i386_vbeblit_R8G8B8A8_R8G8B8A8 (struct grub_video_i386_vbeblit_info *dst, - struct grub_video_i386_vbeblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y); - -void -grub_video_i386_vbeblit_R8G8B8X8_R8G8B8X8 (struct grub_video_i386_vbeblit_info *dst, - struct grub_video_i386_vbeblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y); - -void -grub_video_i386_vbeblit_R8G8B8_R8G8B8A8 (struct grub_video_i386_vbeblit_info *dst, - struct grub_video_i386_vbeblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y); - -void -grub_video_i386_vbeblit_R8G8B8_R8G8B8X8 (struct grub_video_i386_vbeblit_info *dst, - struct grub_video_i386_vbeblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y); - -void -grub_video_i386_vbeblit_index_R8G8B8A8 (struct grub_video_i386_vbeblit_info *dst, - struct grub_video_i386_vbeblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y); - -void -grub_video_i386_vbeblit_index_R8G8B8X8 (struct grub_video_i386_vbeblit_info *dst, - struct grub_video_i386_vbeblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y); - -void -grub_video_i386_vbeblit_R8G8B8A8_R8G8B8 (struct grub_video_i386_vbeblit_info *dst, - struct grub_video_i386_vbeblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y); - -void -grub_video_i386_vbeblit_R8G8B8_R8G8B8 (struct grub_video_i386_vbeblit_info *dst, - struct grub_video_i386_vbeblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y); - -void -grub_video_i386_vbeblit_index_R8G8B8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbeblit_BGRX8888_RGBX8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_BGRA8888_RGB888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_BGR888_RGB888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_BGRA8888_RGBA8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_BGR888_RGBA8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_BGR888_RGBX8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_RGBA8888_RGBA8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +/* Direct copy for compatible 32 bpp blit formats. + * (RGBA8888->RGBA8888, BGRA8888->BGRA8888, etc.) */ +void +grub_video_i386_vbeblit_direct32_copy (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_RGB888_RGBA8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_RGB888_RGBX8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_index_RGBA8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_index_RGBX8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_RGBA8888_RGB888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_RGB888_RGB888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y); + +void +grub_video_i386_vbeblit_index_RGB888 (struct grub_video_i386_vbeblit_info *dst, struct grub_video_i386_vbeblit_info *src, int x, int y, int width, int height, int offset_x, int offset_y); === modified file 'include/grub/i386/pc/vbefill.h' --- include/grub/i386/pc/vbefill.h 2007-07-21 22:32:33 +0000 +++ include/grub/i386/pc/vbefill.h 2008-07-03 13:49:18 +0000 @@ -25,14 +25,14 @@ struct grub_video_i386_vbeblit_info; void -grub_video_i386_vbefill_R8G8B8A8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbefill_direct32 (struct grub_video_i386_vbeblit_info *dst, grub_video_color_t color, int x, int y, int width, int height); void -grub_video_i386_vbefill_R8G8B8 (struct grub_video_i386_vbeblit_info *dst, - grub_video_color_t color, int x, int y, - int width, int height); +grub_video_i386_vbefill_direct24 (struct grub_video_i386_vbeblit_info *dst, + grub_video_color_t color, int x, int y, + int width, int height); void grub_video_i386_vbefill_index (struct grub_video_i386_vbeblit_info *dst, === added file 'include/grub/i386/tsc.h' --- include/grub/i386/tsc.h 1970-01-01 00:00:00 +0000 +++ include/grub/i386/tsc.h 2008-07-28 16:23:46 +0000 @@ -0,0 +1,80 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 . + */ + +#ifndef KERNEL_CPU_TSC_HEADER +#define KERNEL_CPU_TSC_HEADER 1 + +#include + +/* Read the TSC value, which increments with each CPU clock cycle. */ +static __inline grub_uint64_t +grub_get_tsc (void) +{ + grub_uint32_t lo, hi; + + /* The CPUID instruction is a 'serializing' instruction, and + avoids out-of-order execution of the RDTSC instruction. */ + __asm__ __volatile__ ("xorl %%eax, %%eax\n\t" + "cpuid":::"%rax", "%rbx", "%rcx", "%rdx"); + /* Read TSC value. We cannot use "=A", since this would use + %rax on x86_64. */ + __asm__ __volatile__ ("rdtsc":"=a" (lo), "=d" (hi)); + + return (((grub_uint64_t) hi) << 32) | lo; +} + +static __inline int +grub_cpu_is_cpuid_supported (void) +{ + grub_uint32_t id_supported; + + __asm__ ("pushfl\n\t" + "popl %%eax /* Get EFLAGS into EAX */\n\t" + "movl %%eax, %%ecx /* Save original flags in ECX */\n\t" + "xorl $0x200000, %%eax /* Flip ID bit in EFLAGS */\n\t" + "pushl %%eax /* Store modified EFLAGS on stack */\n\t" + "popfl /* Replace current EFLAGS */\n\t" + "pushfl /* Read back the EFLAGS */\n\t" + "popl %%eax /* Get EFLAGS into EAX */\n\t" + "xorl %%ecx, %%eax /* Check if flag could be modified */\n\t" + : "=a" (id_supported) + : /* No inputs. */ + : /* Clobbered: */ "%rcx"); + + return id_supported != 0; +} + +static __inline int +grub_cpu_is_tsc_supported (void) +{ + if (! grub_cpu_is_cpuid_supported ()) + return 0; + + grub_uint32_t features; + __asm__ ("movl $1, %%eax\n\t" + "cpuid" + : "=d" (features) + : /* No inputs. */ + : /* Clobbered: */ "%rax", "%rbx", "%rcx"); + return (features & (1 << 4)) != 0; +} + +void grub_tsc_init (void); +grub_uint64_t grub_tsc_get_time_ms (void); + +#endif /* ! KERNEL_CPU_TSC_HEADER */ === modified file 'include/grub/i386/types.h' --- include/grub/i386/types.h 2007-07-21 22:32:33 +0000 +++ include/grub/i386/types.h 2008-08-01 15:27:27 +0000 @@ -25,6 +25,51 @@ /* The size of long. */ #define GRUB_TARGET_SIZEOF_LONG 4 +/* The native word size in bits. */ +#define __WORDSIZE 32 + +/* These assume 8-bit `char's, 16-bit `short int's, + and 32-bit `int's and `long int's. */ + +/* Number of bits in a `char'. */ +#define CHAR_BIT 8 + +/* Minimum and maximum values a `signed char' can hold. */ +#define SCHAR_MIN (-128) +#define SCHAR_MAX 127 + +/* Maximum value an `unsigned char' can hold. (Minimum is 0.) */ +#define UCHAR_MAX 255 + +/* Minimum and maximum values a `char' can hold. */ +#ifdef __CHAR_UNSIGNED__ +# define CHAR_MIN 0 +# define CHAR_MAX UCHAR_MAX +#else +# define CHAR_MIN SCHAR_MIN +# define CHAR_MAX SCHAR_MAX +#endif + +/* Minimum and maximum values for signed short int. */ +#define SHRT_MIN (-32768) +#define SHRT_MAX 32767 + +/* Maximum value for unsigned short int. (Minimum is 0.) */ +#define USHRT_MAX 65535 + +/* Mininum and maximum values for signed int. */ +#define INT_MIN (-INT_MAX - 1) +#define INT_MAX 2147483647 +#define UINT_MAX 4294967295U + +/* Mininum and maximum values for signed long. */ +#if __WORDSIZE == 64 +# define LONG_MAX 9223372036854775807L +#else +# define LONG_MAX 2147483647L +#endif +#define LONG_MIN (-LONG_MAX - 1L) + /* i386 is little-endian. */ #undef GRUB_TARGET_WORDS_BIGENDIAN === added file 'include/grub/luagrub.h' --- include/grub/luagrub.h 1970-01-01 00:00:00 +0000 +++ include/grub/luagrub.h 2008-08-04 17:10:55 +0000 @@ -0,0 +1,27 @@ +/* luagrub.h - prototypes for GRUB Lua package functions. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 . + */ + +#ifndef GRUB_LUAGRUB_HEADER +#define GRUB_LUAGRUB_HEADER 1 + +#include + +int grub_lua_load_grub_packages (lua_State *L); + +#endif /* GRUB_LUAGRUB_HEADER */ === added file 'include/grub/menu.h' --- include/grub/menu.h 1970-01-01 00:00:00 +0000 +++ include/grub/menu.h 2008-08-03 02:14:40 +0000 @@ -0,0 +1,63 @@ +/* menu.h - Menu and menu entry model declarations. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2005,2006,2007,2008 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 . + */ + +#ifndef GRUB_MENU_HEADER +#define GRUB_MENU_HEADER 1 + +struct grub_menu_entry_class +{ + char *name; + struct grub_menu_entry_class *next; +}; + +/* The menu entry. */ +struct grub_menu_entry +{ + /* The title name. */ + const char *title; + + /* The classes associated with the menu entry: + used to choose an icon or other style attributes. + This is a dummy head node for the linked list, so for an entry E, + E.classes->next is the first class if it is not NULL. */ + struct grub_menu_entry_class *classes; + + /* The commands associated with this menu entry. */ + struct grub_script *commands; + + /* The sourcecode of the menu entry, used by the editor. */ + const char *sourcecode; + + /* The next element. */ + struct grub_menu_entry *next; +}; +typedef struct grub_menu_entry *grub_menu_entry_t; + +/* The menu. */ +struct grub_menu +{ + /* The size of a menu. */ + int size; + + /* The list of menu entries. */ + grub_menu_entry_t entry_list; +}; +typedef struct grub_menu *grub_menu_t; + +#endif /* GRUB_MENU_HEADER */ === added file 'include/grub/menu_viewer.h' --- include/grub/menu_viewer.h 1970-01-01 00:00:00 +0000 +++ include/grub/menu_viewer.h 2008-07-19 19:07:47 +0000 @@ -0,0 +1,48 @@ +/* menu_viewer.h - Interface to menu viewer implementations. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2005,2007,2008 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 . + */ + +#ifndef GRUB_MENU_VIEWER_HEADER +#define GRUB_MENU_VIEWER_HEADER 1 + +#include +#include +#include +#include + +struct grub_menu_viewer +{ + /* The menu viewer name. */ + const char *name; + + grub_err_t (*show_menu) (grub_menu_t menu, int nested); + + struct grub_menu_viewer *next; +}; +typedef struct grub_menu_viewer *grub_menu_viewer_t; + +void EXPORT_FUNC(grub_menu_viewer_init) (void); + +void EXPORT_FUNC(grub_menu_viewer_register) (grub_menu_viewer_t viewer); + +grub_err_t EXPORT_FUNC(grub_menu_viewer_show_menu) (grub_menu_t menu, int nested); + +/* Return nonzero iff the menu viewer should clean up and return ASAP. */ +int EXPORT_FUNC(grub_menu_viewer_should_return) (void); + +#endif /* GRUB_MENU_VIEWER_HEADER */ === modified file 'include/grub/misc.h' --- include/grub/misc.h 2008-07-04 01:12:54 +0000 +++ include/grub/misc.h 2008-08-01 15:27:27 +0000 @@ -52,11 +52,14 @@ char *EXPORT_FUNC(grub_strstr) (const char *haystack, const char *needle); int EXPORT_FUNC(grub_iswordseparator) (int c); int EXPORT_FUNC(grub_isspace) (int c); +int EXPORT_FUNC(grub_iscntrl) (int c); int EXPORT_FUNC(grub_isprint) (int c); int EXPORT_FUNC(grub_isalpha) (int c); +int EXPORT_FUNC(grub_isalnum) (int c); int EXPORT_FUNC(grub_isgraph) (int c); int EXPORT_FUNC(grub_isdigit) (int c); int EXPORT_FUNC(grub_tolower) (int c); +long EXPORT_FUNC(grub_strtol) (const char *str, char **end, int base); unsigned long EXPORT_FUNC(grub_strtoul) (const char *str, char **end, int base); unsigned long long EXPORT_FUNC(grub_strtoull) (const char *str, char **end, int base); char *EXPORT_FUNC(grub_strdup) (const char *s); === modified file 'include/grub/normal.h' --- include/grub/normal.h 2008-03-26 12:01:02 +0000 +++ include/grub/normal.h 2008-08-03 04:00:22 +0000 @@ -25,6 +25,7 @@ #include #include #include +#include /* The maximum size of a command-line. */ #define GRUB_MAX_CMDLINE 1600 @@ -39,7 +40,7 @@ #define GRUB_COMMAND_FLAG_TITLE 0x4 /* Don't print the command on booting. */ #define GRUB_COMMAND_FLAG_NO_ECHO 0x8 -/* Don't print the command on booting. */ +/* Pass arguments to the command without parsing options. */ #define GRUB_COMMAND_FLAG_NO_ARG_PARSE 0x10 /* Not loaded yet. Used for auto-loading. */ #define GRUB_COMMAND_FLAG_NOT_LOADED 0x20 @@ -84,34 +85,6 @@ }; typedef struct grub_command *grub_command_t; -/* The menu entry. */ -struct grub_menu_entry -{ - /* The title name. */ - const char *title; - - /* The commands associated with this menu entry. */ - struct grub_script *commands; - - /* The sourcecode of the menu entry, used by the editor. */ - const char *sourcecode; - - /* The next element. */ - struct grub_menu_entry *next; -}; -typedef struct grub_menu_entry *grub_menu_entry_t; - -/* The menu. */ -struct grub_menu -{ - /* The size of a menu. */ - int size; - - /* The list of menu entries. */ - grub_menu_entry_t entry_list; -}; -typedef struct grub_menu *grub_menu_t; - /* This is used to store the names of filesystem modules for auto-loading. */ struct grub_fs_module_list { @@ -123,10 +96,27 @@ /* To exit from the normal mode. */ extern grub_jmp_buf grub_exit_env; +extern struct grub_menu_viewer grub_normal_terminal_menu_viewer; + +typedef struct grub_menu_execute_callback +{ + void (*notify_booting) (void *userdata, grub_menu_entry_t entry); + void (*notify_fallback) (void *userdata, grub_menu_entry_t entry); + void (*notify_failure) (void *userdata); +} +*grub_menu_execute_callback_t; + void grub_enter_normal_mode (const char *config); void grub_normal_execute (const char *config, int nested); -void grub_menu_run (grub_menu_t menu, int nested); +void grub_menu_execute_with_fallback (grub_menu_t menu, + grub_menu_entry_t entry, + grub_menu_execute_callback_t callback, + void *callback_data); void grub_menu_entry_run (grub_menu_entry_t entry); +int grub_menu_get_default_entry_index (grub_menu_t menu); +int grub_menu_get_timeout (void); +void grub_menu_set_timeout (int timeout); +void grub_menu_execute_entry(grub_menu_entry_t entry); void grub_cmdline_run (int nested); int grub_cmdline_get (const char *prompt, char cmdline[], unsigned max_len, int echo_char, int readline); === added file 'include/grub/stringutil.h' --- include/grub/stringutil.h 1970-01-01 00:00:00 +0000 +++ include/grub/stringutil.h 2008-07-25 15:52:57 +0000 @@ -0,0 +1,32 @@ +/* gfxmenu_stringutil.h - String utilities for the graphical menu interface. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 . + */ + +#ifndef GRUB_GFXMENU_STRINGUTIL_HEADER +#define GRUB_GFXMENU_STRINGUTIL_HEADER 1 + +#include + +char *grub_new_substring (const char *buf, + grub_size_t start, grub_size_t end); + +char *grub_resolve_relative_path (const char *base, const char *path); + +char *grub_get_dirname (const char *file_path); + +#endif /* GRUB_GFXMENU_STRINGUTIL_HEADER */ === modified file 'include/grub/time.h' --- include/grub/time.h 2007-10-22 19:02:16 +0000 +++ include/grub/time.h 2008-07-28 16:51:30 +0000 @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2007 Free Software Foundation, Inc. + * Copyright (C) 2007, 2008 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 @@ -19,12 +19,15 @@ #ifndef KERNEL_TIME_HEADER #define KERNEL_TIME_HEADER 1 +#include #include #include #include void EXPORT_FUNC(grub_millisleep) (grub_uint32_t ms); -void EXPORT_FUNC(grub_millisleep_generic) (grub_uint32_t ms); +grub_uint64_t EXPORT_FUNC(grub_get_time_ms) (void); + +grub_uint64_t grub_rtc_get_time_ms (void); static __inline void grub_sleep (grub_uint32_t s) @@ -32,4 +35,6 @@ grub_millisleep (1000 * s); } +void grub_install_get_time_ms (grub_uint64_t (*get_time_ms_func) (void)); + #endif /* ! KERNEL_TIME_HEADER */ === modified file 'include/grub/video.h' --- include/grub/video.h 2008-01-01 12:02:07 +0000 +++ include/grub/video.h 2008-07-19 19:31:46 +0000 @@ -21,6 +21,7 @@ #include #include +#include /* Video color in hardware dependent format. Users should not assume any specific coding format. */ @@ -31,40 +32,41 @@ struct grub_video_render_target; /* Forward declarations for used data structures. */ -struct grub_font_glyph; struct grub_video_bitmap; /* Defines used to describe video mode or rendering target. */ -#define GRUB_VIDEO_MODE_TYPE_ALPHA 0x00000008 -#define GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED 0x00000004 +#define GRUB_VIDEO_MODE_TYPE_ALPHA 0x00000020 +#define GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED 0x00000010 +#define GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP 0x00000004 #define GRUB_VIDEO_MODE_TYPE_INDEX_COLOR 0x00000002 #define GRUB_VIDEO_MODE_TYPE_RGB 0x00000001 /* Defines used to mask flags. */ -#define GRUB_VIDEO_MODE_TYPE_COLOR_MASK 0x00000003 +#define GRUB_VIDEO_MODE_TYPE_COLOR_MASK 0x0000000F /* Defines used to specify requested bit depth. */ #define GRUB_VIDEO_MODE_TYPE_DEPTH_MASK 0x0000ff00 #define GRUB_VIDEO_MODE_TYPE_DEPTH_POS 8 -/* Defined predefined render targets. */ -#define GRUB_VIDEO_RENDER_TARGET_DISPLAY ((struct grub_video_render_target *) 0) -#define GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER ((struct grub_video_render_target *) 0) -#define GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER ((struct grub_video_render_target *) 1) +/* Predefined render target: */ +/* The render target that client code should render to. */ +#define GRUB_VIDEO_RENDER_TARGET_DISPLAY \ + ((struct grub_video_render_target *) 0) + /* Defined blitting formats. */ enum grub_video_blit_format { - /* Follow exactly field & mask information. */ - GRUB_VIDEO_BLIT_FORMAT_RGBA, - /* Make optimization assumption. */ - GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8, - /* Follow exactly field & mask information. */ - GRUB_VIDEO_BLIT_FORMAT_RGB, - /* Make optimization assumption. */ - GRUB_VIDEO_BLIT_FORMAT_R8G8B8, + GRUB_VIDEO_BLIT_FORMAT_RGBA, /* General: Use fields & masks. */ + GRUB_VIDEO_BLIT_FORMAT_RGBA_8888, /* Optimized format. */ + GRUB_VIDEO_BLIT_FORMAT_BGRA_8888, /* Optimized format. */ + GRUB_VIDEO_BLIT_FORMAT_RGB, /* General: Use fields & masks. */ + GRUB_VIDEO_BLIT_FORMAT_RGB_888, /* Optimized format. */ + GRUB_VIDEO_BLIT_FORMAT_BGR_888, /* Optimized format. */ /* When needed, decode color or just use value as is. */ - GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR + GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR, + /* Two color bitmap; bits packed: rows are not padded to byte boundary. */ + GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED }; /* Define blitting operators. */ @@ -127,6 +129,18 @@ /* What is location of reserved color bits. In Index Color mode, this is 0. */ unsigned int reserved_field_pos; + + /* For 1-bit bitmaps, the background color. Used for bits = 0. */ + grub_uint8_t bg_red; + grub_uint8_t bg_green; + grub_uint8_t bg_blue; + grub_uint8_t bg_alpha; + + /* For 1-bit bitmaps, the foreground color. Used for bits = 1. */ + grub_uint8_t fg_red; + grub_uint8_t fg_green; + grub_uint8_t fg_blue; + grub_uint8_t fg_alpha; }; struct grub_video_palette_data @@ -137,6 +151,16 @@ grub_uint8_t a; /* Reserved bits value (0-255). */ }; +/* A 2D rectangle type. */ +struct grub_video_rect +{ + int x; + int y; + int width; + int height; +}; +typedef struct grub_video_rect grub_video_rect_t; + struct grub_video_adapter { /* The video adapter name. */ @@ -183,6 +207,10 @@ grub_err_t (*blit_glyph) (struct grub_font_glyph *glyph, grub_video_color_t color, int x, int y); + grub_err_t (*draw_string) (const char *str, grub_font_t font, + grub_video_color_t color, + int left_x, int baseline_y); + grub_err_t (*blit_bitmap) (struct grub_video_bitmap *bitmap, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, @@ -255,6 +283,10 @@ grub_err_t grub_video_blit_glyph (struct grub_font_glyph *glyph, grub_video_color_t color, int x, int y); +grub_err_t grub_video_draw_string (const char *str, grub_font_t font, + grub_video_color_t color, + int left_x, int baseline_y); + grub_err_t grub_video_blit_bitmap (struct grub_video_bitmap *bitmap, enum grub_video_blit_operators oper, int x, int y, int offset_x, int offset_y, @@ -269,6 +301,9 @@ grub_err_t grub_video_scroll (grub_video_color_t color, int dx, int dy); +/* Swap the pages referred to by the front buffer and back buffer render + * targets, and the page previously referred to by the back buffer is made + * visible on the display. */ grub_err_t grub_video_swap_buffers (void); grub_err_t grub_video_create_render_target (struct grub_video_render_target **result, @@ -282,4 +317,21 @@ grub_err_t grub_video_get_active_render_target (struct grub_video_render_target **target); + +/* Defined in video/setmode.c */ + +/* Set the video mode based on the preferred modes specified in MODE_LIST in + the form: x[x][;...] + + For example: 640x480;800x600x8;400x300x32 + + If MODE_LIST is null, or no modes in it are usable, then DEFAULT_WIDTH and + DEFAULT_HEIGHT are used to set the mode. The MODE_FLAGS argument determines + the video mode flags such as double buffering that are used. */ + +grub_err_t +grub_video_setup_preferred_mode (const char *mode_list, int mode_flags, + int default_width, int default_height); + + #endif /* ! GRUB_VIDEO_HEADER */ === added directory 'kern/generic' === added file 'kern/generic/millisleep.c' --- kern/generic/millisleep.c 1970-01-01 00:00:00 +0000 +++ kern/generic/millisleep.c 2008-07-28 16:51:30 +0000 @@ -0,0 +1,39 @@ +/* millisleep.c - generic millisleep function. + * The generic implementation of these functions can be used for architectures + * or platforms that do not have a more specialized implementation. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008 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 + +void +grub_millisleep (grub_uint32_t ms) +{ + grub_uint64_t start; + + start = grub_get_time_ms (); + + /* Instead of setting an end time and looping while the current time is + less than that, comparing the elapsed sleep time with the desired sleep + time handles the (unlikely!) case that the timer would wrap around + during the sleep. */ + + while (grub_get_time_ms () - start < ms) + grub_cpu_idle (); +} === added file 'kern/generic/rtc_get_time_ms.c' --- kern/generic/rtc_get_time_ms.c 1970-01-01 00:00:00 +0000 +++ kern/generic/rtc_get_time_ms.c 2008-07-28 16:51:30 +0000 @@ -0,0 +1,37 @@ +/* rtc_get_time_ms.c - get_time_ms implementation using platform RTC. + * The generic implementation of these functions can be used for architectures + * or platforms that do not have a more specialized implementation. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 + +/* Calculate the time in milliseconds since the epoch based on the RTC. */ +grub_uint64_t +grub_rtc_get_time_ms (void) +{ + /* By dimensional analysis: + + 1000 ms N rtc ticks 1 s + ------- * ----------- * ----------- = 1000*N/T ms + 1 s 1 T rtc ticks + */ + grub_uint64_t ticks_ms_per_sec = ((grub_uint64_t) 1000) * grub_get_rtc (); + return grub_divmod64 (ticks_ms_per_sec, GRUB_TICKS_PER_SECOND, 0); +} === modified file 'kern/i386/efi/init.c' --- kern/i386/efi/init.c 2007-10-22 18:59:33 +0000 +++ kern/i386/efi/init.c 2008-07-28 16:23:46 +0000 @@ -25,18 +25,13 @@ #include #include #include -#include - -void -grub_millisleep (grub_uint32_t ms) -{ - grub_millisleep_generic (ms); -} +#include void grub_machine_init (void) { grub_efi_init (); + grub_tsc_init (); } void === modified file 'kern/i386/linuxbios/init.c' --- kern/i386/linuxbios/init.c 2008-07-31 18:33:23 +0000 +++ kern/i386/linuxbios/init.c 2008-08-03 03:57:46 +0000 @@ -143,6 +143,8 @@ /* This variable indicates size, not offset. */ grub_upper_mem -= GRUB_MEMORY_MACHINE_UPPER_START; + + grub_install_get_time_ms (grub_rtc_get_time_ms); } void === modified file 'kern/i386/pc/init.c' --- kern/i386/pc/init.c 2008-08-02 22:24:34 +0000 +++ kern/i386/pc/init.c 2008-08-03 03:57:46 +0000 @@ -30,6 +30,7 @@ #include #include #include +#include struct mem_region { @@ -46,12 +47,6 @@ grub_size_t grub_os_area_size; grub_size_t grub_lower_mem, grub_upper_mem; -void -grub_millisleep (grub_uint32_t ms) -{ - grub_millisleep_generic (ms); -} - void grub_arch_sync_caches (void *address __attribute__ ((unused)), grub_size_t len __attribute__ ((unused))) @@ -231,6 +226,8 @@ if (! grub_os_area_addr) grub_fatal ("no upper memory"); + + grub_tsc_init (); } void === added file 'kern/i386/tsc.c' --- kern/i386/tsc.c 1970-01-01 00:00:00 +0000 +++ kern/i386/tsc.c 2008-07-28 16:51:30 +0000 @@ -0,0 +1,102 @@ +/* kern/i386/tsc.c - x86 TSC time source implementation + * Requires Pentium or better x86 CPU that supports the RDTSC instruction. + * This module uses the RTC (via grub_get_rtc()) to calibrate the TSC to + * real time. + * + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 + +/* Calibrated reference for TSC=0. This defines the time since the epoch in + milliseconds that TSC=0 refers to. */ +static grub_uint64_t tsc_boot_time; + +/* Calibrated TSC rate. (In TSC ticks per millisecond.) */ +static grub_uint64_t tsc_ticks_per_ms; + + +grub_uint64_t +grub_tsc_get_time_ms (void) +{ + return tsc_boot_time + grub_divmod64 (grub_get_tsc (), tsc_ticks_per_ms, 0); +} + + +/* How many RTC ticks to use for calibration loop. (>= 1) */ +#define CALIBRATION_TICKS 2 + +/* Calibrate the TSC based on the RTC. */ +static void +calibrate_tsc (void) +{ + /* First calbrate the TSC rate (relative, not absolute time). */ + grub_uint64_t start_tsc; + grub_uint64_t end_tsc; + grub_uint32_t initial_tick; + grub_uint32_t start_tick; + grub_uint32_t end_tick; + + /* Wait for the start of the next tick; + we'll base out timing off this edge. */ + initial_tick = grub_get_rtc (); + do + { + start_tick = grub_get_rtc (); + } + while (start_tick == initial_tick); + start_tsc = grub_get_tsc (); + + /* Wait for the start of the next tick. This will + be the end of the 1-tick period. */ + do + { + end_tick = grub_get_rtc (); + } + while (end_tick - start_tick < CALIBRATION_TICKS); + end_tsc = grub_get_tsc (); + + tsc_ticks_per_ms = + grub_divmod64 (grub_divmod64 + (end_tsc - start_tsc, end_tick - start_tick, 0) + * GRUB_TICKS_PER_SECOND, 1000, 0); + + /* Reference the TSC zero (boot time) to the epoch to + get an absolute real time reference. */ + grub_uint64_t ms_since_boot = grub_divmod64 (end_tsc, tsc_ticks_per_ms, 0); + grub_uint64_t mstime_now = grub_divmod64 ((grub_uint64_t) 1000 * end_tick, + GRUB_TICKS_PER_SECOND, + 0); + tsc_boot_time = mstime_now - ms_since_boot; +} + +void +grub_tsc_init (void) +{ + if (grub_cpu_is_tsc_supported ()) + { + calibrate_tsc (); + grub_install_get_time_ms (grub_tsc_get_time_ms); + } + else + { + grub_install_get_time_ms (grub_rtc_get_time_ms); + } +} === modified file 'kern/ieee1275/init.c' --- kern/ieee1275/init.c 2008-07-30 09:42:11 +0000 +++ kern/ieee1275/init.c 2008-08-03 03:57:46 +0000 @@ -47,12 +47,6 @@ extern char _end[]; void -grub_millisleep (grub_uint32_t ms) -{ - grub_millisleep_generic (ms); -} - -void grub_exit (void) { grub_ieee1275_exit (); @@ -209,6 +203,8 @@ #endif +static grub_uint64_t ieee1275_get_time_ms (void); + void grub_machine_init (void) { @@ -258,6 +254,8 @@ } } } + + grub_install_get_time_ms (ieee1275_get_time_ms); } void @@ -267,8 +265,8 @@ grub_console_fini (); } -grub_uint32_t -grub_get_rtc (void) +static grub_uint64_t +ieee1275_get_time_ms (void) { grub_uint32_t msecs = 0; @@ -277,6 +275,12 @@ return msecs; } +grub_uint32_t +grub_get_rtc (void) +{ + return ieee1275_get_time_ms (); +} + grub_addr_t grub_arch_modules_addr (void) { === added file 'kern/menu_viewer.c' --- kern/menu_viewer.c 1970-01-01 00:00:00 +0000 +++ kern/menu_viewer.c 2008-07-19 19:07:47 +0000 @@ -0,0 +1,99 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2002,2003,2005,2007 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 + +/* The list of menu viewers. */ +static grub_menu_viewer_t menu_viewer_list; + +static int should_return; +static int menu_viewer_changed; + +void +grub_menu_viewer_register (grub_menu_viewer_t viewer) +{ + viewer->next = menu_viewer_list; + menu_viewer_list = viewer; +} + +static grub_menu_viewer_t get_current_menu_viewer (void) +{ + const char *selected_name = grub_env_get ("menuviewer"); + + /* If none selected, pick the last registered one. */ + if (selected_name == 0) + return menu_viewer_list; + + grub_menu_viewer_t cur; + for (cur = menu_viewer_list; cur; cur = cur->next) + { + if (grub_strcmp (cur->name, selected_name) == 0) + return cur; + } + + /* Fall back to the first entry (or null). */ + return menu_viewer_list; +} + +grub_err_t +grub_menu_viewer_show_menu (grub_menu_t menu, int nested) +{ + grub_err_t err; + int repeat = 0; + do + { + repeat = 0; + menu_viewer_changed = 0; + grub_menu_viewer_t cur = get_current_menu_viewer (); + if (!cur) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "No menu viewer available."); + + should_return = 0; + err = cur->show_menu (menu, nested); + if (menu_viewer_changed) + repeat = 1; + } + while (repeat); + return err; +} + +int +grub_menu_viewer_should_return (void) +{ + return should_return; +} + +static char * +menuviewer_write_hook (struct grub_env_var *var __attribute__ ((unused)), + const char *val) +{ + menu_viewer_changed = 1; + should_return = 1; + return grub_strdup (val); +} + +void +grub_menu_viewer_init (void) +{ + grub_register_variable_hook ("menuviewer", 0, menuviewer_write_hook); +} + === modified file 'kern/misc.c' --- kern/misc.c 2008-06-15 23:42:48 +0000 +++ kern/misc.c 2008-08-01 15:27:27 +0000 @@ -23,7 +23,6 @@ #include #include #include -#include void * grub_memmove (void *dest, const void *src, grub_size_t n) @@ -359,6 +358,12 @@ } int +grub_iscntrl (int c) +{ + return (c >= 0x00 && c <= 0x1F) || c == 0x7F; +} + +int grub_isprint (int c) { return (c >= ' ' && c <= '~'); @@ -371,6 +376,12 @@ } int +grub_isalnum (int c) +{ + return grub_isalpha (c) || grub_isdigit (c); +} + +int grub_isdigit (int c) { return (c >= '0' && c <= '9'); @@ -391,6 +402,41 @@ return c; } +long +grub_strtol (const char *str, char **end, int base) +{ + int negative = 0; + + while (*str && grub_isspace (*str)) + str++; + + if (*str == '-') + { + negative = 1; + str++; + } + + unsigned long long magnitude; + magnitude = grub_strtoull (str, end, base); + if (negative) + { + if (magnitude > -((long long) LONG_MIN)) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "negative overflow"); + return LONG_MIN; + } + return -((long long) magnitude); + } + else + { + if (magnitude > LONG_MAX) + { + grub_error (GRUB_ERR_OUT_OF_RANGE, "positive overflow"); + return LONG_MAX; + } + return (long) magnitude; + } +} unsigned long grub_strtoul (const char *str, char **end, int base) @@ -1018,17 +1064,6 @@ return p - dest; } -void -grub_millisleep_generic (grub_uint32_t ms) -{ - grub_uint32_t end_at; - - end_at = grub_get_rtc () + grub_div_roundup (ms * GRUB_TICKS_PER_SECOND, 1000); - - while (grub_get_rtc () < end_at) - grub_cpu_idle (); -} - /* Abort GRUB. This function does not return. */ void grub_abort (void) === modified file 'kern/sparc64/ieee1275/init.c' --- kern/sparc64/ieee1275/init.c 2007-10-22 18:59:33 +0000 +++ kern/sparc64/ieee1275/init.c 2008-07-28 16:23:46 +0000 @@ -66,12 +66,6 @@ /* Never reached. */ } -void -grub_millisleep (grub_uint32_t ms) -{ - grub_millisleep_generic (ms); -} - int grub_ieee1275_test_flag (enum grub_ieee1275_flag flag) { @@ -145,6 +139,8 @@ grub_free (prefix); } +grub_uint64_t ieee1275_get_time_ms (void); + void grub_machine_init (void) { @@ -201,6 +197,7 @@ } } + grub_install_get_time_ms (ieee1275_get_time_ms); } void @@ -216,6 +213,12 @@ grub_ieee1275_enter (); } +grub_uint64_t +ieee1275_get_time_ms (void) +{ + return grub_get_rtc (); +} + grub_uint32_t grub_get_rtc (void) { === added file 'kern/time.c' --- kern/time.c 1970-01-01 00:00:00 +0000 +++ kern/time.c 2008-07-28 16:23:46 +0000 @@ -0,0 +1,37 @@ +/* time.c - kernel time functions */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 + +typedef grub_uint64_t (*get_time_ms_func_t) (void); + +/* Function pointer to the implementation in use. */ +static get_time_ms_func_t get_time_ms_func; + +grub_uint64_t +grub_get_time_ms (void) +{ + return get_time_ms_func (); +} + +void +grub_install_get_time_ms (get_time_ms_func_t func) +{ + get_time_ms_func = func; +} === added file 'lib/luagrub.c' --- lib/luagrub.c 1970-01-01 00:00:00 +0000 +++ lib/luagrub.c 2008-08-04 17:10:55 +0000 @@ -0,0 +1,185 @@ +/* luagrub.c - Lua GRUB package; provides access to GRUB from Lua. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Lua function: get_time_ms() : returns the time in milliseconds. */ +static int +l_sys_get_time_ms (lua_State *L) +{ + lua_pushinteger (L, grub_get_time_ms ()); + return 1; +} + + + +/* Lua function: input.getkey() : returns { ASCII char, scan code }. */ +static int +l_input_getkey (lua_State *L) +{ + int c = grub_getkey(); + lua_pushinteger (L, c & 0xFF); /* Push ASCII character code. */ + lua_pushinteger (L, (c >> 8) & 0xFF); /* Push the scan code. */ + return 2; +} + +/* Lua function: input.checkkey() : returns true if a key is waiting. */ +static int +l_input_checkkey (lua_State *L) +{ + lua_pushboolean (L, grub_checkkey() != -1); + return 1; +} + + + +/* Lua function: video.set_graphics_mode(). */ +static int +l_video_set_graphics_mode (lua_State *L) +{ + const char *doublebuf_str = grub_env_get ("doublebuffering"); + int doublebuf_flags = + (doublebuf_str && doublebuf_str[0] == 'n') + ? 0 + : GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + + const char *modestr = grub_env_get ("gfxmode"); + if (grub_video_setup_preferred_mode (modestr, doublebuf_flags, 640, 480) + != GRUB_ERR_NONE) + return luaL_error (L, "Error setting video mode: %s", grub_errmsg); + + return 0; +} + +/* Lua function: video.set_text_mode(). */ +static int +l_video_set_text_mode (lua_State *L) +{ + if (grub_video_restore () != GRUB_ERR_NONE) + return luaL_error (L, "Error setting text mode: %s", grub_errmsg); + return 0; +} + +/* Lua function: video.swap_buffers(). */ +static int +l_video_swap_buffers (lua_State *L) +{ + if (grub_video_swap_buffers () != GRUB_ERR_NONE) + return luaL_error (L, "Error swapping video buffers: %s", grub_errmsg); + return 0; +} + +static grub_video_color_t +check_grub_color (lua_State *L, int narg) +{ + /* Get the color components. */ + luaL_argcheck (L, lua_istable (L, narg), narg, "should be a color"); + lua_getfield (L, narg, "r"); + lua_getfield (L, narg, "g"); + lua_getfield (L, narg, "b"); + lua_getfield (L, narg, "a"); + grub_video_color_t color; + color = grub_video_map_rgba (luaL_checkint (L, -4), + luaL_checkint (L, -3), + luaL_checkint (L, -2), + luaL_optint (L, -1, 255)); + lua_pop (L, 4); + return color; +} + +/* Lua function: video.fill_rect(color, x, y, width, height). */ +static int +l_video_fill_rect (lua_State *L) +{ + grub_video_color_t color = check_grub_color (L, 1); + int x = luaL_checkint (L, 2); + int y = luaL_checkint (L, 3); + int w = luaL_checkint (L, 4); + int h = luaL_checkint (L, 5); + if (grub_video_fill_rect (color, x, y, w, h) != GRUB_ERR_NONE) + return luaL_error (L, "Error filling rectangle: %s", grub_errmsg); + return 0; +} + +/* Lua function: video.draw_string(text, font, color, x, y). */ +static int +l_video_draw_string (lua_State *L) +{ + grub_video_draw_string (luaL_checkstring (L, 1), + grub_font_get (luaL_checkstring (L, 2)), + check_grub_color (L, 3), + luaL_checkint (L, 4), + luaL_checkint (L, 5)); + return 0; +} + + +static const struct luaL_reg syslib[] = { + {"get_time_ms", l_sys_get_time_ms}, + {0, 0} +}; + +static const struct luaL_reg inputlib[] = { + {"getkey", l_input_getkey}, + {"checkkey", l_input_checkkey}, + {0, 0} +}; + +static const struct luaL_reg videolib[] = { + {"set_graphics_mode", l_video_set_graphics_mode}, + {"set_text_mode", l_video_set_text_mode}, + {"swap_buffers", l_video_swap_buffers}, + {"fill_rect", l_video_fill_rect}, + {"draw_string", l_video_draw_string}, + {0, 0} +}; + +int +grub_lua_load_grub_packages (lua_State *L) +{ + luaL_openlib(L, "sys", syslib, 0); + luaL_openlib(L, "input", inputlib, 0); + luaL_openlib(L, "video", videolib, 0); + return 3; /* 3 items returned on stack: the packages. */ +} + +GRUB_MOD_INIT (luagrub) +{ + (void) mod; /* To stop warning. */ +} + +GRUB_MOD_FINI (luagrub) +{ +} === added directory 'lua' === added file 'lua/Makefile' --- lua/Makefile 1970-01-01 00:00:00 +0000 +++ lua/Makefile 2008-08-01 15:19:23 +0000 @@ -0,0 +1,182 @@ +# makefile for building Lua +# see ../INSTALL for installation instructions +# see ../Makefile and luaconf.h for further customization + +# == CHANGE THE SETTINGS BELOW TO SUIT YOUR ENVIRONMENT ======================= + +# Your platform. See PLATS for possible values. +PLAT= none + +CC= gcc +CFLAGS= -O2 -Wall $(MYCFLAGS) +AR= ar rcu +RANLIB= ranlib +RM= rm -f +LIBS= -lm $(MYLIBS) + +MYCFLAGS= +MYLDFLAGS= +MYLIBS= + +# == END OF USER SETTINGS. NO NEED TO CHANGE ANYTHING BELOW THIS LINE ========= + +PLATS= aix ansi bsd freebsd generic linux macosx mingw posix solaris + +LUA_A= liblua.a +CORE_O= lapi.o lcode.o ldebug.o ldo.o ldump.o lfunc.o lgc.o llex.o lmem.o \ + lobject.o lopcodes.o lparser.o lstate.o lstring.o ltable.o ltm.o \ + lundump.o lvm.o lzio.o +LIB_O= lauxlib.o lbaselib.o ldblib.o liolib.o lmathlib.o loslib.o ltablib.o \ + lstrlib.o loadlib.o linit.o + +LUA_T= lua +LUA_O= lua.o + +LUAC_T= luac +LUAC_O= luac.o print.o + +ALL_O= $(CORE_O) $(LIB_O) $(LUA_O) $(LUAC_O) +ALL_T= $(LUA_A) $(LUA_T) $(LUAC_T) +ALL_A= $(LUA_A) + +default: $(PLAT) + +all: $(ALL_T) + +o: $(ALL_O) + +a: $(ALL_A) + +$(LUA_A): $(CORE_O) $(LIB_O) + $(AR) $@ $? + $(RANLIB) $@ + +$(LUA_T): $(LUA_O) $(LUA_A) + $(CC) -o $@ $(MYLDFLAGS) $(LUA_O) $(LUA_A) $(LIBS) + +$(LUAC_T): $(LUAC_O) $(LUA_A) + $(CC) -o $@ $(MYLDFLAGS) $(LUAC_O) $(LUA_A) $(LIBS) + +clean: + $(RM) $(ALL_T) $(ALL_O) + +depend: + @$(CC) $(CFLAGS) -MM l*.c print.c + +echo: + @echo "PLAT = $(PLAT)" + @echo "CC = $(CC)" + @echo "CFLAGS = $(CFLAGS)" + @echo "AR = $(AR)" + @echo "RANLIB = $(RANLIB)" + @echo "RM = $(RM)" + @echo "MYCFLAGS = $(MYCFLAGS)" + @echo "MYLDFLAGS = $(MYLDFLAGS)" + @echo "MYLIBS = $(MYLIBS)" + +# convenience targets for popular platforms + +none: + @echo "Please choose a platform:" + @echo " $(PLATS)" + +aix: + $(MAKE) all CC="xlc" CFLAGS="-O2 -DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" MYLDFLAGS="-brtl -bexpall" + +ansi: + $(MAKE) all MYCFLAGS=-DLUA_ANSI + +bsd: + $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-Wl,-E" + +freebsd: + $(MAKE) all MYCFLAGS="-DLUA_USE_LINUX" MYLIBS="-Wl,-E -lreadline" + +generic: + $(MAKE) all MYCFLAGS= + +linux: + $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-Wl,-E -ldl -lreadline -lhistory -lncurses" + +macosx: + $(MAKE) all MYCFLAGS=-DLUA_USE_LINUX MYLIBS="-lreadline" +# use this on Mac OS X 10.3- +# $(MAKE) all MYCFLAGS=-DLUA_USE_MACOSX + +mingw: + $(MAKE) "LUA_A=lua51.dll" "LUA_T=lua.exe" \ + "AR=$(CC) -shared -o" "RANLIB=strip --strip-unneeded" \ + "MYCFLAGS=-DLUA_BUILD_AS_DLL" "MYLIBS=" "MYLDFLAGS=-s" lua.exe + $(MAKE) "LUAC_T=luac.exe" luac.exe + +posix: + $(MAKE) all MYCFLAGS=-DLUA_USE_POSIX + +solaris: + $(MAKE) all MYCFLAGS="-DLUA_USE_POSIX -DLUA_USE_DLOPEN" MYLIBS="-ldl" + +# list targets that do not create files (but not all makes understand .PHONY) +.PHONY: all $(PLATS) default o a clean depend echo none + +# DO NOT DELETE + +lapi.o: lapi.c lua.h luaconf.h lapi.h lobject.h llimits.h ldebug.h \ + lstate.h ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h \ + lundump.h lvm.h +lauxlib.o: lauxlib.c lua.h luaconf.h lauxlib.h +lbaselib.o: lbaselib.c lua.h luaconf.h lauxlib.h lualib.h +lcode.o: lcode.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ + lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h lgc.h \ + ltable.h +ldblib.o: ldblib.c lua.h luaconf.h lauxlib.h lualib.h +ldebug.o: ldebug.c lua.h luaconf.h lapi.h lobject.h llimits.h lcode.h \ + llex.h lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \ + lfunc.h lstring.h lgc.h ltable.h lvm.h +ldo.o: ldo.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ + lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lparser.h lstring.h \ + ltable.h lundump.h lvm.h +ldump.o: ldump.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h \ + lzio.h lmem.h lundump.h +lfunc.o: lfunc.c lua.h luaconf.h lfunc.h lobject.h llimits.h lgc.h lmem.h \ + lstate.h ltm.h lzio.h +lgc.o: lgc.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ + lzio.h lmem.h ldo.h lfunc.h lgc.h lstring.h ltable.h +linit.o: linit.c lua.h luaconf.h lualib.h lauxlib.h +liolib.o: liolib.c lua.h luaconf.h lauxlib.h lualib.h +llex.o: llex.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h ltm.h \ + lzio.h lmem.h llex.h lparser.h lstring.h lgc.h ltable.h +lmathlib.o: lmathlib.c lua.h luaconf.h lauxlib.h lualib.h +lmem.o: lmem.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h ldo.h +loadlib.o: loadlib.c lua.h luaconf.h lauxlib.h lualib.h +lobject.o: lobject.c lua.h luaconf.h ldo.h lobject.h llimits.h lstate.h \ + ltm.h lzio.h lmem.h lstring.h lgc.h lvm.h +lopcodes.o: lopcodes.c lopcodes.h llimits.h lua.h luaconf.h +loslib.o: loslib.c lua.h luaconf.h lauxlib.h lualib.h +lparser.o: lparser.c lua.h luaconf.h lcode.h llex.h lobject.h llimits.h \ + lzio.h lmem.h lopcodes.h lparser.h ldebug.h lstate.h ltm.h ldo.h \ + lfunc.h lstring.h lgc.h ltable.h +lstate.o: lstate.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h ldo.h lfunc.h lgc.h llex.h lstring.h ltable.h +lstring.o: lstring.c lua.h luaconf.h lmem.h llimits.h lobject.h lstate.h \ + ltm.h lzio.h lstring.h lgc.h +lstrlib.o: lstrlib.c lua.h luaconf.h lauxlib.h lualib.h +ltable.o: ltable.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h ldo.h lgc.h ltable.h +ltablib.o: ltablib.c lua.h luaconf.h lauxlib.h lualib.h +ltm.o: ltm.c lua.h luaconf.h lobject.h llimits.h lstate.h ltm.h lzio.h \ + lmem.h lstring.h lgc.h ltable.h +lua.o: lua.c lua.h luaconf.h lauxlib.h lualib.h +luac.o: luac.c lua.h luaconf.h lauxlib.h ldo.h lobject.h llimits.h \ + lstate.h ltm.h lzio.h lmem.h lfunc.h lopcodes.h lstring.h lgc.h \ + lundump.h +lundump.o: lundump.c lua.h luaconf.h ldebug.h lstate.h lobject.h \ + llimits.h ltm.h lzio.h lmem.h ldo.h lfunc.h lstring.h lgc.h lundump.h +lvm.o: lvm.c lua.h luaconf.h ldebug.h lstate.h lobject.h llimits.h ltm.h \ + lzio.h lmem.h ldo.h lfunc.h lgc.h lopcodes.h lstring.h ltable.h lvm.h +lzio.o: lzio.c lua.h luaconf.h llimits.h lmem.h lstate.h lobject.h ltm.h \ + lzio.h +print.o: print.c ldebug.h lstate.h lua.h luaconf.h lobject.h llimits.h \ + ltm.h lzio.h lmem.h lopcodes.h lundump.h + +# (end of Makefile) === added file 'lua/grublib.h' --- lua/grublib.h 1970-01-01 00:00:00 +0000 +++ lua/grublib.h 2008-08-01 15:27:27 +0000 @@ -0,0 +1,131 @@ +/* grublib.h - Define aliases to the GRUB library functions for Lua. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 . + */ + +#ifndef GRUB_LUA_GRUBLIB_HEADER +#define GRUB_LUA_GRUBLIB_HEADER 1 + +#include +#include +#include + +#define isalpha grub_isalpha +#define isalnum grub_isalnum +#define isdigit grub_isdigit +#define isprint grub_isprint +#define isspace grub_isspace +#define iscntrl grub_iscntrl +#define tolower grub_tolower + +static __inline int islower(int c) +{ + return c >= 'a' && c <= 'z'; +} + +static __inline int isupper(int c) +{ + return c >= 'A' && c <= 'Z'; +} + +static __inline int toupper(int c) +{ + if (islower (c)) + return (c - 'a') + 'A'; + return c; +} + +static __inline int isxdigit(int c) +{ + return (isdigit (c) + || (c >= 'a' && c <= 'f') + || (c >= 'A' && c <= 'F')); +} + +static __inline int ispunct(int c) +{ + return isprint (c) && !isalnum (c); +} + + +#define memcmp grub_memcmp +#define strcmp grub_strcmp +#define strcoll grub_strcmp +#define strchr grub_strchr +#define strstr grub_strstr +#define strcspn grub_strcspn +#define strlen grub_strlen +#define strcpy grub_strcpy +#define strncpy grub_strncpy +#define strcat grub_strcat +#define strncat grub_strncat + +#define strtol grub_strtol +#define strtoul grub_strtoul +#define sprintf grub_sprintf +#define abs grub_abs + +#define malloc grub_malloc +#define realloc grub_realloc +#define free grub_free + +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 +static __inline void exit (int status __attribute__ ((unused))) +{ + grub_exit (); +} + +static __inline void * +memchr (const char *s, int c, size_t n) +{ + while (n) + { + if (*s == c) + return (void *) s; + + n--; + s++; + } + return 0; +} + +static __inline char * +strpbrk (const char *s, const char *accept) +{ + char *p; + while (*s) + { + p = grub_strchr (accept, *s); + if (p) + return 0; + + s++; + } + return 0; +} + +static __inline size_t +strcspn (const char *s, const char *reject) +{ + size_t n = 0; + while (s[n] && ! grub_strchr (reject, s[n])) + n++; + return n; +} + +#endif /* ! GRUB_LUA_GRUBLIB_HEADER */ === added file 'lua/lapi.c' --- lua/lapi.c 1970-01-01 00:00:00 +0000 +++ lua/lapi.c 2008-08-03 02:57:07 +0000 @@ -0,0 +1,1080 @@ +/* +** $Id: lapi.c,v 2.55.1.3 2008/01/03 15:20:39 roberto Exp $ +** Lua API +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#include +#include +#include +#endif /* ! USE_GRUB_LIB */ + +#define lapi_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" + + + +#define api_checknelems(L, n) api_check(L, (n) <= (L->top - L->base)) + +#define api_checkvalidindex(L, i) api_check(L, (i) != luaO_nilobject) + +#define api_incr_top(L) {api_check(L, L->top < L->ci->top); L->top++;} + + + +static TValue *index2adr (lua_State *L, int idx) { + if (idx > 0) { + TValue *o = L->base + (idx - 1); + api_check(L, idx <= L->ci->top - L->base); + if (o >= L->top) return cast(TValue *, luaO_nilobject); + else return o; + } + else if (idx > LUA_REGISTRYINDEX) { + api_check(L, idx != 0 && -idx <= L->top - L->base); + return L->top + idx; + } + else switch (idx) { /* pseudo-indices */ + case LUA_REGISTRYINDEX: return registry(L); + case LUA_ENVIRONINDEX: { + Closure *func = curr_func(L); + sethvalue(L, &L->env, func->c.env); + return &L->env; + } + case LUA_GLOBALSINDEX: return gt(L); + default: { + Closure *func = curr_func(L); + idx = LUA_GLOBALSINDEX - idx; + return (idx <= func->c.nupvalues) + ? &func->c.upvalue[idx-1] + : cast(TValue *, luaO_nilobject); + } + } +} + + +static Table *getcurrenv (lua_State *L) { + if (L->ci == L->base_ci) /* no enclosing function? */ + return hvalue(gt(L)); /* use global table as environment */ + else { + Closure *func = curr_func(L); + return func->c.env; + } +} + + +void luaA_pushobject (lua_State *L, const TValue *o) { + setobj2s(L, L->top, o); + api_incr_top(L); +} + + +LUA_API int lua_checkstack (lua_State *L, int size) { + int res; + lua_lock(L); + if ((L->top - L->base + size) > LUAI_MAXCSTACK) + res = 0; /* stack overflow */ + else { + luaD_checkstack(L, size); + if (L->ci->top < L->top + size) + L->ci->top = L->top + size; + res = 1; + } + lua_unlock(L); + return res; +} + + +LUA_API void lua_xmove (lua_State *from, lua_State *to, int n) { + int i; + if (from == to) return; + lua_lock(to); + api_checknelems(from, n); + api_check(from, G(from) == G(to)); + api_check(from, to->ci->top - to->top >= n); + from->top -= n; + for (i = 0; i < n; i++) { + setobj2s(to, to->top++, from->top + i); + } + lua_unlock(to); +} + + +LUA_API void lua_setlevel (lua_State *from, lua_State *to) { + to->nCcalls = from->nCcalls; +} + + +LUA_API lua_CFunction lua_atpanic (lua_State *L, lua_CFunction panicf) { + lua_CFunction old; + lua_lock(L); + old = G(L)->panic; + G(L)->panic = panicf; + lua_unlock(L); + return old; +} + + +LUA_API lua_State *lua_newthread (lua_State *L) { + lua_State *L1; + lua_lock(L); + luaC_checkGC(L); + L1 = luaE_newthread(L); + setthvalue(L, L->top, L1); + api_incr_top(L); + lua_unlock(L); + luai_userstatethread(L, L1); + return L1; +} + + + +/* +** basic stack manipulation +*/ + + +LUA_API int lua_gettop (lua_State *L) { + return cast_int(L->top - L->base); +} + + +LUA_API void lua_settop (lua_State *L, int idx) { + lua_lock(L); + if (idx >= 0) { + api_check(L, idx <= L->stack_last - L->base); + while (L->top < L->base + idx) + setnilvalue(L->top++); + L->top = L->base + idx; + } + else { + api_check(L, -(idx+1) <= (L->top - L->base)); + L->top += idx+1; /* `subtract' index (index is negative) */ + } + lua_unlock(L); +} + + +LUA_API void lua_remove (lua_State *L, int idx) { + StkId p; + lua_lock(L); + p = index2adr(L, idx); + api_checkvalidindex(L, p); + while (++p < L->top) setobjs2s(L, p-1, p); + L->top--; + lua_unlock(L); +} + + +LUA_API void lua_insert (lua_State *L, int idx) { + StkId p; + StkId q; + lua_lock(L); + p = index2adr(L, idx); + api_checkvalidindex(L, p); + for (q = L->top; q>p; q--) setobjs2s(L, q, q-1); + setobjs2s(L, p, L->top); + lua_unlock(L); +} + + +LUA_API void lua_replace (lua_State *L, int idx) { + StkId o; + lua_lock(L); + /* explicit test for incompatible code */ + if (idx == LUA_ENVIRONINDEX && L->ci == L->base_ci) + luaG_runerror(L, "no calling environment"); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + if (idx == LUA_ENVIRONINDEX) { + Closure *func = curr_func(L); + api_check(L, ttistable(L->top - 1)); + func->c.env = hvalue(L->top - 1); + luaC_barrier(L, func, L->top - 1); + } + else { + setobj(L, o, L->top - 1); + if (idx < LUA_GLOBALSINDEX) /* function upvalue? */ + luaC_barrier(L, curr_func(L), L->top - 1); + } + L->top--; + lua_unlock(L); +} + + +LUA_API void lua_pushvalue (lua_State *L, int idx) { + lua_lock(L); + setobj2s(L, L->top, index2adr(L, idx)); + api_incr_top(L); + lua_unlock(L); +} + + + +/* +** access functions (stack -> C) +*/ + + +LUA_API int lua_type (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (o == luaO_nilobject) ? LUA_TNONE : ttype(o); +} + + +LUA_API const char *lua_typename (lua_State *L, int t) { + UNUSED(L); + return (t == LUA_TNONE) ? "no value" : luaT_typenames[t]; +} + + +LUA_API int lua_iscfunction (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return iscfunction(o); +} + + +LUA_API int lua_isnumber (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + return tonumber(o, &n); +} + + +LUA_API int lua_isstring (lua_State *L, int idx) { + int t = lua_type(L, idx); + return (t == LUA_TSTRING || t == LUA_TNUMBER); +} + + +LUA_API int lua_isuserdata (lua_State *L, int idx) { + const TValue *o = index2adr(L, idx); + return (ttisuserdata(o) || ttislightuserdata(o)); +} + + +LUA_API int lua_rawequal (lua_State *L, int index1, int index2) { + StkId o1 = index2adr(L, index1); + StkId o2 = index2adr(L, index2); + return (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 + : luaO_rawequalObj(o1, o2); +} + + +LUA_API int lua_equal (lua_State *L, int index1, int index2) { + StkId o1, o2; + int i; + lua_lock(L); /* may call tag method */ + o1 = index2adr(L, index1); + o2 = index2adr(L, index2); + i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 : equalobj(L, o1, o2); + lua_unlock(L); + return i; +} + + +LUA_API int lua_lessthan (lua_State *L, int index1, int index2) { + StkId o1, o2; + int i; + lua_lock(L); /* may call tag method */ + o1 = index2adr(L, index1); + o2 = index2adr(L, index2); + i = (o1 == luaO_nilobject || o2 == luaO_nilobject) ? 0 + : luaV_lessthan(L, o1, o2); + lua_unlock(L); + return i; +} + + + +LUA_API lua_Number lua_tonumber (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + if (tonumber(o, &n)) + return nvalue(o); + else + return 0; +} + + +LUA_API lua_Integer lua_tointeger (lua_State *L, int idx) { + TValue n; + const TValue *o = index2adr(L, idx); + if (tonumber(o, &n)) { + lua_Integer res; + lua_Number num = nvalue(o); + lua_number2integer(res, num); + return res; + } + else + return 0; +} + + +LUA_API int lua_toboolean (lua_State *L, int idx) { + const TValue *o = index2adr(L, idx); + return !l_isfalse(o); +} + + +LUA_API const char *lua_tolstring (lua_State *L, int idx, size_t *len) { + StkId o = index2adr(L, idx); + if (!ttisstring(o)) { + lua_lock(L); /* `luaV_tostring' may create a new string */ + if (!luaV_tostring(L, o)) { /* conversion failed? */ + if (len != NULL) *len = 0; + lua_unlock(L); + return NULL; + } + luaC_checkGC(L); + o = index2adr(L, idx); /* previous call may reallocate the stack */ + lua_unlock(L); + } + if (len != NULL) *len = tsvalue(o)->len; + return svalue(o); +} + + +LUA_API size_t lua_objlen (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TSTRING: return tsvalue(o)->len; + case LUA_TUSERDATA: return uvalue(o)->len; + case LUA_TTABLE: return luaH_getn(hvalue(o)); + case LUA_TNUMBER: { + size_t l; + lua_lock(L); /* `luaV_tostring' may create a new string */ + l = (luaV_tostring(L, o) ? tsvalue(o)->len : 0); + lua_unlock(L); + return l; + } + default: return 0; + } +} + + +LUA_API lua_CFunction lua_tocfunction (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (!iscfunction(o)) ? NULL : clvalue(o)->c.f; +} + + +LUA_API void *lua_touserdata (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TUSERDATA: return (rawuvalue(o) + 1); + case LUA_TLIGHTUSERDATA: return pvalue(o); + default: return NULL; + } +} + + +LUA_API lua_State *lua_tothread (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + return (!ttisthread(o)) ? NULL : thvalue(o); +} + + +LUA_API const void *lua_topointer (lua_State *L, int idx) { + StkId o = index2adr(L, idx); + switch (ttype(o)) { + case LUA_TTABLE: return hvalue(o); + case LUA_TFUNCTION: return clvalue(o); + case LUA_TTHREAD: return thvalue(o); + case LUA_TUSERDATA: + case LUA_TLIGHTUSERDATA: + return lua_touserdata(L, idx); + default: return NULL; + } +} + + + +/* +** push functions (C -> stack) +*/ + + +LUA_API void lua_pushnil (lua_State *L) { + lua_lock(L); + setnilvalue(L->top); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushnumber (lua_State *L, lua_Number n) { + lua_lock(L); + setnvalue(L->top, n); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushinteger (lua_State *L, lua_Integer n) { + lua_lock(L); + setnvalue(L->top, cast_num(n)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushlstring (lua_State *L, const char *s, size_t len) { + lua_lock(L); + luaC_checkGC(L); + setsvalue2s(L, L->top, luaS_newlstr(L, s, len)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushstring (lua_State *L, const char *s) { + if (s == NULL) + lua_pushnil(L); + else + lua_pushlstring(L, s, strlen(s)); +} + + +LUA_API const char *lua_pushvfstring (lua_State *L, const char *fmt, + va_list argp) { + const char *ret; + lua_lock(L); + luaC_checkGC(L); + ret = luaO_pushvfstring(L, fmt, argp); + lua_unlock(L); + return ret; +} + + +LUA_API const char *lua_pushfstring (lua_State *L, const char *fmt, ...) { + const char *ret; + va_list argp; + lua_lock(L); + luaC_checkGC(L); + va_start(argp, fmt); + ret = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + lua_unlock(L); + return ret; +} + + +LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { + Closure *cl; + lua_lock(L); + luaC_checkGC(L); + api_checknelems(L, n); + cl = luaF_newCclosure(L, n, getcurrenv(L)); + cl->c.f = fn; + L->top -= n; + while (n--) + setobj2n(L, &cl->c.upvalue[n], L->top+n); + setclvalue(L, L->top, cl); + lua_assert(iswhite(obj2gco(cl))); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushboolean (lua_State *L, int b) { + lua_lock(L); + setbvalue(L->top, (b != 0)); /* ensure that true is 1 */ + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_pushlightuserdata (lua_State *L, void *p) { + lua_lock(L); + setpvalue(L->top, p); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int lua_pushthread (lua_State *L) { + lua_lock(L); + setthvalue(L, L->top, L); + api_incr_top(L); + lua_unlock(L); + return (G(L)->mainthread == L); +} + + + +/* +** get functions (Lua -> stack) +*/ + + +LUA_API void lua_gettable (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + luaV_gettable(L, t, L->top - 1, L->top - 1); + lua_unlock(L); +} + + +LUA_API void lua_getfield (lua_State *L, int idx, const char *k) { + StkId t; + TValue key; + lua_lock(L); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + setsvalue(L, &key, luaS_new(L, k)); + luaV_gettable(L, t, &key, L->top); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_rawget (lua_State *L, int idx) { + StkId t; + lua_lock(L); + t = index2adr(L, idx); + api_check(L, ttistable(t)); + setobj2s(L, L->top - 1, luaH_get(hvalue(t), L->top - 1)); + lua_unlock(L); +} + + +LUA_API void lua_rawgeti (lua_State *L, int idx, int n) { + StkId o; + lua_lock(L); + o = index2adr(L, idx); + api_check(L, ttistable(o)); + setobj2s(L, L->top, luaH_getnum(hvalue(o), n)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API void lua_createtable (lua_State *L, int narray, int nrec) { + lua_lock(L); + luaC_checkGC(L); + sethvalue(L, L->top, luaH_new(L, narray, nrec)); + api_incr_top(L); + lua_unlock(L); +} + + +LUA_API int lua_getmetatable (lua_State *L, int objindex) { + const TValue *obj; + Table *mt = NULL; + int res; + lua_lock(L); + obj = index2adr(L, objindex); + switch (ttype(obj)) { + case LUA_TTABLE: + mt = hvalue(obj)->metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(obj)->metatable; + break; + default: + mt = G(L)->mt[ttype(obj)]; + break; + } + if (mt == NULL) + res = 0; + else { + sethvalue(L, L->top, mt); + api_incr_top(L); + res = 1; + } + lua_unlock(L); + return res; +} + + +LUA_API void lua_getfenv (lua_State *L, int idx) { + StkId o; + lua_lock(L); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + switch (ttype(o)) { + case LUA_TFUNCTION: + sethvalue(L, L->top, clvalue(o)->c.env); + break; + case LUA_TUSERDATA: + sethvalue(L, L->top, uvalue(o)->env); + break; + case LUA_TTHREAD: + setobj2s(L, L->top, gt(thvalue(o))); + break; + default: + setnilvalue(L->top); + break; + } + api_incr_top(L); + lua_unlock(L); +} + + +/* +** set functions (stack -> Lua) +*/ + + +LUA_API void lua_settable (lua_State *L, int idx) { + StkId t; + lua_lock(L); + api_checknelems(L, 2); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + luaV_settable(L, t, L->top - 2, L->top - 1); + L->top -= 2; /* pop index and value */ + lua_unlock(L); +} + + +LUA_API void lua_setfield (lua_State *L, int idx, const char *k) { + StkId t; + TValue key; + lua_lock(L); + api_checknelems(L, 1); + t = index2adr(L, idx); + api_checkvalidindex(L, t); + setsvalue(L, &key, luaS_new(L, k)); + luaV_settable(L, t, &key, L->top - 1); + L->top--; /* pop value */ + lua_unlock(L); +} + + +LUA_API void lua_rawset (lua_State *L, int idx) { + StkId t; + lua_lock(L); + api_checknelems(L, 2); + t = index2adr(L, idx); + api_check(L, ttistable(t)); + setobj2t(L, luaH_set(L, hvalue(t), L->top-2), L->top-1); + luaC_barriert(L, hvalue(t), L->top-1); + L->top -= 2; + lua_unlock(L); +} + + +LUA_API void lua_rawseti (lua_State *L, int idx, int n) { + StkId o; + lua_lock(L); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_check(L, ttistable(o)); + setobj2t(L, luaH_setnum(L, hvalue(o), n), L->top-1); + luaC_barriert(L, hvalue(o), L->top-1); + L->top--; + lua_unlock(L); +} + + +LUA_API int lua_setmetatable (lua_State *L, int objindex) { + TValue *obj; + Table *mt; + lua_lock(L); + api_checknelems(L, 1); + obj = index2adr(L, objindex); + api_checkvalidindex(L, obj); + if (ttisnil(L->top - 1)) + mt = NULL; + else { + api_check(L, ttistable(L->top - 1)); + mt = hvalue(L->top - 1); + } + switch (ttype(obj)) { + case LUA_TTABLE: { + hvalue(obj)->metatable = mt; + if (mt) + luaC_objbarriert(L, hvalue(obj), mt); + break; + } + case LUA_TUSERDATA: { + uvalue(obj)->metatable = mt; + if (mt) + luaC_objbarrier(L, rawuvalue(obj), mt); + break; + } + default: { + G(L)->mt[ttype(obj)] = mt; + break; + } + } + L->top--; + lua_unlock(L); + return 1; +} + + +LUA_API int lua_setfenv (lua_State *L, int idx) { + StkId o; + int res = 1; + lua_lock(L); + api_checknelems(L, 1); + o = index2adr(L, idx); + api_checkvalidindex(L, o); + api_check(L, ttistable(L->top - 1)); + switch (ttype(o)) { + case LUA_TFUNCTION: + clvalue(o)->c.env = hvalue(L->top - 1); + break; + case LUA_TUSERDATA: + uvalue(o)->env = hvalue(L->top - 1); + break; + case LUA_TTHREAD: + sethvalue(L, gt(thvalue(o)), hvalue(L->top - 1)); + break; + default: + res = 0; + break; + } + if (res) luaC_objbarrier(L, gcvalue(o), hvalue(L->top - 1)); + L->top--; + lua_unlock(L); + return res; +} + + +/* +** `load' and `call' functions (run Lua code) +*/ + + +#define adjustresults(L,nres) \ + { if (nres == LUA_MULTRET && L->top >= L->ci->top) L->ci->top = L->top; } + + +#define checkresults(L,na,nr) \ + api_check(L, (nr) == LUA_MULTRET || (L->ci->top - L->top >= (nr) - (na))) + + +LUA_API void lua_call (lua_State *L, int nargs, int nresults) { + StkId func; + lua_lock(L); + api_checknelems(L, nargs+1); + checkresults(L, nargs, nresults); + func = L->top - (nargs+1); + luaD_call(L, func, nresults); + adjustresults(L, nresults); + lua_unlock(L); +} + + + +/* +** Execute a protected call. +*/ +struct CallS { /* data to `f_call' */ + StkId func; + int nresults; +}; + + +static void f_call (lua_State *L, void *ud) { + struct CallS *c = cast(struct CallS *, ud); + luaD_call(L, c->func, c->nresults); +} + + + +LUA_API int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc) { + struct CallS c; + int status; + ptrdiff_t func; + lua_lock(L); + api_checknelems(L, nargs+1); + checkresults(L, nargs, nresults); + if (errfunc == 0) + func = 0; + else { + StkId o = index2adr(L, errfunc); + api_checkvalidindex(L, o); + func = savestack(L, o); + } + c.func = L->top - (nargs+1); /* function to be called */ + c.nresults = nresults; + status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); + adjustresults(L, nresults); + lua_unlock(L); + return status; +} + + +/* +** Execute a protected C call. +*/ +struct CCallS { /* data to `f_Ccall' */ + lua_CFunction func; + void *ud; +}; + + +static void f_Ccall (lua_State *L, void *ud) { + struct CCallS *c = cast(struct CCallS *, ud); + Closure *cl; + cl = luaF_newCclosure(L, 0, getcurrenv(L)); + cl->c.f = c->func; + setclvalue(L, L->top, cl); /* push function */ + api_incr_top(L); + setpvalue(L->top, c->ud); /* push only argument */ + api_incr_top(L); + luaD_call(L, L->top - 2, 0); +} + + +LUA_API int lua_cpcall (lua_State *L, lua_CFunction func, void *ud) { + struct CCallS c; + int status; + lua_lock(L); + c.func = func; + c.ud = ud; + status = luaD_pcall(L, f_Ccall, &c, savestack(L, L->top), 0); + lua_unlock(L); + return status; +} + + +LUA_API int lua_load (lua_State *L, lua_Reader reader, void *data, + const char *chunkname) { + ZIO z; + int status; + lua_lock(L); + if (!chunkname) chunkname = "?"; + luaZ_init(L, &z, reader, data); + status = luaD_protectedparser(L, &z, chunkname); + lua_unlock(L); + return status; +} + + +LUA_API int lua_dump (lua_State *L, lua_Writer writer, void *data) { + int status; + TValue *o; + lua_lock(L); + api_checknelems(L, 1); + o = L->top - 1; + if (isLfunction(o)) + status = luaU_dump(L, clvalue(o)->l.p, writer, data, 0); + else + status = 1; + lua_unlock(L); + return status; +} + + +LUA_API int lua_status (lua_State *L) { + return L->status; +} + + +/* +** Garbage-collection function +*/ + +LUA_API int lua_gc (lua_State *L, int what, int data) { + int res = 0; + global_State *g; + lua_lock(L); + g = G(L); + switch (what) { + case LUA_GCSTOP: { + g->GCthreshold = MAX_LUMEM; + break; + } + case LUA_GCRESTART: { + g->GCthreshold = g->totalbytes; + break; + } + case LUA_GCCOLLECT: { + luaC_fullgc(L); + break; + } + case LUA_GCCOUNT: { + /* GC values are expressed in Kbytes: #bytes/2^10 */ + res = cast_int(g->totalbytes >> 10); + break; + } + case LUA_GCCOUNTB: { + res = cast_int(g->totalbytes & 0x3ff); + break; + } + case LUA_GCSTEP: { + lu_mem a = (cast(lu_mem, data) << 10); + if (a <= g->totalbytes) + g->GCthreshold = g->totalbytes - a; + else + g->GCthreshold = 0; + while (g->GCthreshold <= g->totalbytes) + luaC_step(L); + if (g->gcstate == GCSpause) /* end of cycle? */ + res = 1; /* signal it */ + break; + } + case LUA_GCSETPAUSE: { + res = g->gcpause; + g->gcpause = data; + break; + } + case LUA_GCSETSTEPMUL: { + res = g->gcstepmul; + g->gcstepmul = data; + break; + } + default: res = -1; /* invalid option */ + } + lua_unlock(L); + return res; +} + + + +/* +** miscellaneous functions +*/ + + +LUA_API int lua_error (lua_State *L) { + lua_lock(L); + api_checknelems(L, 1); + luaG_errormsg(L); + lua_unlock(L); + return 0; /* to avoid warnings */ +} + + +LUA_API int lua_next (lua_State *L, int idx) { + StkId t; + int more; + lua_lock(L); + t = index2adr(L, idx); + api_check(L, ttistable(t)); + more = luaH_next(L, hvalue(t), L->top - 1); + if (more) { + api_incr_top(L); + } + else /* no more elements */ + L->top -= 1; /* remove key */ + lua_unlock(L); + return more; +} + + +LUA_API void lua_concat (lua_State *L, int n) { + lua_lock(L); + api_checknelems(L, n); + if (n >= 2) { + luaC_checkGC(L); + luaV_concat(L, n, cast_int(L->top - L->base) - 1); + L->top -= (n-1); + } + else if (n == 0) { /* push empty string */ + setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); + api_incr_top(L); + } + /* else n == 1; nothing to do */ + lua_unlock(L); +} + + +LUA_API lua_Alloc lua_getallocf (lua_State *L, void **ud) { + lua_Alloc f; + lua_lock(L); + if (ud) *ud = G(L)->ud; + f = G(L)->frealloc; + lua_unlock(L); + return f; +} + + +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) { + lua_lock(L); + G(L)->ud = ud; + G(L)->frealloc = f; + lua_unlock(L); +} + + +LUA_API void *lua_newuserdata (lua_State *L, size_t size) { + Udata *u; + lua_lock(L); + luaC_checkGC(L); + u = luaS_newudata(L, size, getcurrenv(L)); + setuvalue(L, L->top, u); + api_incr_top(L); + lua_unlock(L); + return u + 1; +} + + + + +static const char *aux_upvalue (StkId fi, int n, TValue **val) { + Closure *f; + if (!ttisfunction(fi)) return NULL; + f = clvalue(fi); + if (f->c.isC) { + if (!(1 <= n && n <= f->c.nupvalues)) return NULL; + *val = &f->c.upvalue[n-1]; + return ""; + } + else { + Proto *p = f->l.p; + if (!(1 <= n && n <= p->sizeupvalues)) return NULL; + *val = f->l.upvals[n-1]->v; + return getstr(p->upvalues[n-1]); + } +} + + +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val; + lua_lock(L); + name = aux_upvalue(index2adr(L, funcindex), n, &val); + if (name) { + setobj2s(L, L->top, val); + api_incr_top(L); + } + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n) { + const char *name; + TValue *val; + StkId fi; + lua_lock(L); + fi = index2adr(L, funcindex); + api_checknelems(L, 1); + name = aux_upvalue(fi, n, &val); + if (name) { + L->top--; + setobj(L, val, L->top); + luaC_barrier(L, clvalue(fi), L->top); + } + lua_unlock(L); + return name; +} + === added file 'lua/lapi.h' --- lua/lapi.h 1970-01-01 00:00:00 +0000 +++ lua/lapi.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,16 @@ +/* +** $Id: lapi.h,v 2.2.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions from Lua API +** See Copyright Notice in lua.h +*/ + +#ifndef lapi_h +#define lapi_h + + +#include "lobject.h" + + +LUAI_FUNC void luaA_pushobject (lua_State *L, const TValue *o); + +#endif === added file 'lua/lauxlib.c' --- lua/lauxlib.c 1970-01-01 00:00:00 +0000 +++ lua/lauxlib.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,679 @@ +/* +** $Id: lauxlib.c,v 1.159.1.3 2008/01/21 13:20:51 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#include +#include +#include +#include +#include + +#else +#include +#include +#include +#endif /* USE_GRUB_LIB */ + + +/* This file uses only the official API of Lua. +** Any function declared here could be written as an application function. +*/ + +#define lauxlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" + + +#define FREELIST_REF 0 /* free list of references */ + + +/* convert a stack index to positive */ +#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : \ + lua_gettop(L) + (i) + 1) + + +/* +** {====================================================== +** Error-report functions +** ======================================================= +*/ + + +LUALIB_API int luaL_argerror (lua_State *L, int narg, const char *extramsg) { + lua_Debug ar; + if (!lua_getstack(L, 0, &ar)) /* no stack frame? */ + return luaL_error(L, "bad argument #%d (%s)", narg, extramsg); + lua_getinfo(L, "n", &ar); + if (strcmp(ar.namewhat, "method") == 0) { + narg--; /* do not count `self' */ + if (narg == 0) /* error is in the self argument itself? */ + return luaL_error(L, "calling " LUA_QS " on bad self (%s)", + ar.name, extramsg); + } + if (ar.name == NULL) + ar.name = "?"; + return luaL_error(L, "bad argument #%d to " LUA_QS " (%s)", + narg, ar.name, extramsg); +} + + +LUALIB_API int luaL_typerror (lua_State *L, int narg, const char *tname) { + const char *msg = lua_pushfstring(L, "%s expected, got %s", + tname, luaL_typename(L, narg)); + return luaL_argerror(L, narg, msg); +} + + +static void tag_error (lua_State *L, int narg, int tag) { + luaL_typerror(L, narg, lua_typename(L, tag)); +} + + +LUALIB_API void luaL_where (lua_State *L, int level) { + lua_Debug ar; + if (lua_getstack(L, level, &ar)) { /* check function at level */ + lua_getinfo(L, "Sl", &ar); /* get info about it */ + if (ar.currentline > 0) { /* is there info? */ + lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); + return; + } + } + lua_pushliteral(L, ""); /* else, no information available... */ +} + + +LUALIB_API int luaL_error (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + luaL_where(L, 1); + lua_pushvfstring(L, fmt, argp); + va_end(argp); + lua_concat(L, 2); + return lua_error(L); +} + +/* }====================================================== */ + + +LUALIB_API int luaL_checkoption (lua_State *L, int narg, const char *def, + const char *const lst[]) { + const char *name = (def) ? luaL_optstring(L, narg, def) : + luaL_checkstring(L, narg); + int i; + for (i=0; lst[i]; i++) + if (strcmp(lst[i], name) == 0) + return i; + return luaL_argerror(L, narg, + lua_pushfstring(L, "invalid option " LUA_QS, name)); +} + + +LUALIB_API int luaL_newmetatable (lua_State *L, const char *tname) { + lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */ + if (!lua_isnil(L, -1)) /* name already in use? */ + return 0; /* leave previous value on top, but return 0 */ + lua_pop(L, 1); + lua_newtable(L); /* create metatable */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ + return 1; +} + + +LUALIB_API void *luaL_checkudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */ + if (lua_rawequal(L, -1, -2)) { /* does it have the correct mt? */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + } + luaL_typerror(L, ud, tname); /* else error */ + return NULL; /* to avoid warnings */ +} + + +LUALIB_API void luaL_checkstack (lua_State *L, int space, const char *mes) { + if (!lua_checkstack(L, space)) + luaL_error(L, "stack overflow (%s)", mes); +} + + +LUALIB_API void luaL_checktype (lua_State *L, int narg, int t) { + if (lua_type(L, narg) != t) + tag_error(L, narg, t); +} + + +LUALIB_API void luaL_checkany (lua_State *L, int narg) { + if (lua_type(L, narg) == LUA_TNONE) + luaL_argerror(L, narg, "value expected"); +} + + +LUALIB_API const char *luaL_checklstring (lua_State *L, int narg, size_t *len) { + const char *s = lua_tolstring(L, narg, len); + if (!s) tag_error(L, narg, LUA_TSTRING); + return s; +} + + +LUALIB_API const char *luaL_optlstring (lua_State *L, int narg, + const char *def, size_t *len) { + if (lua_isnoneornil(L, narg)) { + if (len) + *len = (def ? strlen(def) : 0); + return def; + } + else return luaL_checklstring(L, narg, len); +} + + +LUALIB_API lua_Number luaL_checknumber (lua_State *L, int narg) { + lua_Number d = lua_tonumber(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Number luaL_optnumber (lua_State *L, int narg, lua_Number def) { + return luaL_opt(L, luaL_checknumber, narg, def); +} + + +LUALIB_API lua_Integer luaL_checkinteger (lua_State *L, int narg) { + lua_Integer d = lua_tointeger(L, narg); + if (d == 0 && !lua_isnumber(L, narg)) /* avoid extra test when d is not 0 */ + tag_error(L, narg, LUA_TNUMBER); + return d; +} + + +LUALIB_API lua_Integer luaL_optinteger (lua_State *L, int narg, + lua_Integer def) { + return luaL_opt(L, luaL_checkinteger, narg, def); +} + + +LUALIB_API int luaL_getmetafield (lua_State *L, int obj, const char *event) { + if (!lua_getmetatable(L, obj)) /* no metatable? */ + return 0; + lua_pushstring(L, event); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { + lua_pop(L, 2); /* remove metatable and metafield */ + return 0; + } + else { + lua_remove(L, -2); /* remove only metatable */ + return 1; + } +} + + +LUALIB_API int luaL_callmeta (lua_State *L, int obj, const char *event) { + obj = abs_index(L, obj); + if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ + return 0; + lua_pushvalue(L, obj); + lua_call(L, 1, 1); + return 1; +} + + +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l) { + luaI_openlib(L, libname, l, 0); +} + + +static int libsize (const luaL_Reg *l) { + int size = 0; + for (; l->name; l++) size++; + return size; +} + + +LUALIB_API void luaI_openlib (lua_State *L, const char *libname, + const luaL_Reg *l, int nup) { + if (libname) { + int size = libsize(l); + /* check whether lib already exists */ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); + lua_getfield(L, -1, libname); /* get _LOADED[libname] */ + if (!lua_istable(L, -1)) { /* not found? */ + lua_pop(L, 1); /* remove previous result */ + /* try global variable (and create one if it does not exist) */ + if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL) + luaL_error(L, "name conflict for module " LUA_QS, libname); + lua_pushvalue(L, -1); + lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */ + } + lua_remove(L, -2); /* remove _LOADED table */ + lua_insert(L, -(nup+1)); /* move library table to below upvalues */ + } + for (; l->name; l++) { + int i; + for (i=0; ifunc, nup); + lua_setfield(L, -(nup+2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + + + +/* +** {====================================================== +** getn-setn: size for arrays +** ======================================================= +*/ + +#if defined(LUA_COMPAT_GETN) + +static int checkint (lua_State *L, int topop) { + int n = (lua_type(L, -1) == LUA_TNUMBER) ? lua_tointeger(L, -1) : -1; + lua_pop(L, topop); + return n; +} + + +static void getsizes (lua_State *L) { + lua_getfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); + if (lua_isnil(L, -1)) { /* no `size' table? */ + lua_pop(L, 1); /* remove nil */ + lua_newtable(L); /* create it */ + lua_pushvalue(L, -1); /* `size' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "kv"); + lua_setfield(L, -2, "__mode"); /* metatable(N).__mode = "kv" */ + lua_pushvalue(L, -1); + lua_setfield(L, LUA_REGISTRYINDEX, "LUA_SIZES"); /* store in register */ + } +} + + +LUALIB_API void luaL_setn (lua_State *L, int t, int n) { + t = abs_index(L, t); + lua_pushliteral(L, "n"); + lua_rawget(L, t); + if (checkint(L, 1) >= 0) { /* is there a numeric field `n'? */ + lua_pushliteral(L, "n"); /* use it */ + lua_pushinteger(L, n); + lua_rawset(L, t); + } + else { /* use `sizes' */ + getsizes(L); + lua_pushvalue(L, t); + lua_pushinteger(L, n); + lua_rawset(L, -3); /* sizes[t] = n */ + lua_pop(L, 1); /* remove `sizes' */ + } +} + + +LUALIB_API int luaL_getn (lua_State *L, int t) { + int n; + t = abs_index(L, t); + lua_pushliteral(L, "n"); /* try t.n */ + lua_rawget(L, t); + if ((n = checkint(L, 1)) >= 0) return n; + getsizes(L); /* else try sizes[t] */ + lua_pushvalue(L, t); + lua_rawget(L, -2); + if ((n = checkint(L, 2)) >= 0) return n; + return (int)lua_objlen(L, t); +} + +#endif + +/* }====================================================== */ + + + +LUALIB_API const char *luaL_gsub (lua_State *L, const char *s, const char *p, + const char *r) { + const char *wild; + size_t l = strlen(p); + luaL_Buffer b; + luaL_buffinit(L, &b); + while ((wild = strstr(s, p)) != NULL) { + luaL_addlstring(&b, s, wild - s); /* push prefix */ + luaL_addstring(&b, r); /* push replacement in place of pattern */ + s = wild + l; /* continue after `p' */ + } + luaL_addstring(&b, s); /* push last suffix */ + luaL_pushresult(&b); + return lua_tostring(L, -1); +} + + +LUALIB_API const char *luaL_findtable (lua_State *L, int idx, + const char *fname, int szhint) { + const char *e; + lua_pushvalue(L, idx); + do { + e = strchr(fname, '.'); + if (e == NULL) e = fname + strlen(fname); + lua_pushlstring(L, fname, e - fname); + lua_rawget(L, -2); + if (lua_isnil(L, -1)) { /* no such field? */ + lua_pop(L, 1); /* remove this nil */ + lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ + lua_pushlstring(L, fname, e - fname); + lua_pushvalue(L, -2); + lua_settable(L, -4); /* set new table into field */ + } + else if (!lua_istable(L, -1)) { /* field has a non-table value? */ + lua_pop(L, 2); /* remove table and value */ + return fname; /* return problematic part of the name */ + } + lua_remove(L, -2); /* remove previous table */ + fname = e + 1; + } while (*e == '.'); + return NULL; +} + + + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + +#define bufflen(B) ((B)->p - (B)->buffer) +#define bufffree(B) ((size_t)(LUAL_BUFFERSIZE - bufflen(B))) + +#define LIMIT (LUA_MINSTACK/2) + + +static int emptybuffer (luaL_Buffer *B) { + size_t l = bufflen(B); + if (l == 0) return 0; /* put nothing on stack */ + else { + lua_pushlstring(B->L, B->buffer, l); + B->p = B->buffer; + B->lvl++; + return 1; + } +} + + +static void adjuststack (luaL_Buffer *B) { + if (B->lvl > 1) { + lua_State *L = B->L; + int toget = 1; /* number of levels to concat */ + size_t toplen = lua_strlen(L, -1); + do { + size_t l = lua_strlen(L, -(toget+1)); + if (B->lvl - toget + 1 >= LIMIT || toplen > l) { + toplen += l; + toget++; + } + else break; + } while (toget < B->lvl); + lua_concat(L, toget); + B->lvl = B->lvl - toget + 1; + } +} + + +LUALIB_API char *luaL_prepbuffer (luaL_Buffer *B) { + if (emptybuffer(B)) + adjuststack(B); + return B->buffer; +} + + +LUALIB_API void luaL_addlstring (luaL_Buffer *B, const char *s, size_t l) { + while (l--) + luaL_addchar(B, *s++); +} + + +LUALIB_API void luaL_addstring (luaL_Buffer *B, const char *s) { + luaL_addlstring(B, s, strlen(s)); +} + + +LUALIB_API void luaL_pushresult (luaL_Buffer *B) { + emptybuffer(B); + lua_concat(B->L, B->lvl); + B->lvl = 1; +} + + +LUALIB_API void luaL_addvalue (luaL_Buffer *B) { + lua_State *L = B->L; + size_t vl; + const char *s = lua_tolstring(L, -1, &vl); + if (vl <= bufffree(B)) { /* fit into buffer? */ + memcpy(B->p, s, vl); /* put it there */ + B->p += vl; + lua_pop(L, 1); /* remove from stack */ + } + else { + if (emptybuffer(B)) + lua_insert(L, -2); /* put buffer before new value */ + B->lvl++; /* add new value into B stack */ + adjuststack(B); + } +} + + +LUALIB_API void luaL_buffinit (lua_State *L, luaL_Buffer *B) { + B->L = L; + B->p = B->buffer; + B->lvl = 0; +} + +/* }====================================================== */ + + +LUALIB_API int luaL_ref (lua_State *L, int t) { + int ref; + t = abs_index(L, t); + if (lua_isnil(L, -1)) { + lua_pop(L, 1); /* remove from stack */ + return LUA_REFNIL; /* `nil' has a unique fixed reference */ + } + lua_rawgeti(L, t, FREELIST_REF); /* get first free element */ + ref = (int)lua_tointeger(L, -1); /* ref = t[FREELIST_REF] */ + lua_pop(L, 1); /* remove it from stack */ + if (ref != 0) { /* any free element? */ + lua_rawgeti(L, t, ref); /* remove it from list */ + lua_rawseti(L, t, FREELIST_REF); /* (t[FREELIST_REF] = t[ref]) */ + } + else { /* no free elements */ + ref = (int)lua_objlen(L, t); + ref++; /* create new reference */ + } + lua_rawseti(L, t, ref); + return ref; +} + + +LUALIB_API void luaL_unref (lua_State *L, int t, int ref) { + if (ref >= 0) { + t = abs_index(L, t); + lua_rawgeti(L, t, FREELIST_REF); + lua_rawseti(L, t, ref); /* t[ref] = t[FREELIST_REF] */ + lua_pushinteger(L, ref); + lua_rawseti(L, t, FREELIST_REF); /* t[FREELIST_REF] = ref */ + } +} + + + +/* +** {====================================================== +** Load functions +** ======================================================= +*/ + +typedef struct LoadF { + int extraline; + grub_file_t f; + char buff[LUAL_BUFFERSIZE]; +} LoadF; + + +#define EOF (-1) + +static int grub_file_error_flag = 0; + +static int grub_getc(grub_file_t f) +{ + char c; + if (grub_file_read (f, &c, 1) != 1) + { + grub_file_error_flag = 1; + return EOF; + } + grub_file_error_flag = 0; + return c; +} + +static int grub_ferror(void) +{ + return grub_file_error_flag; +} + + +static const char *getF (lua_State *L, void *ud, size_t *size) { + LoadF *lf = (LoadF *)ud; + (void)L; + if (lf->extraline) { + lf->extraline = 0; + *size = 1; + return "\n"; + } + *size = grub_file_read(lf->f, lf->buff, sizeof(lf->buff)); + return (*size > 0) ? lf->buff : NULL; +} + + +static int errfile (lua_State *L, const char *what, int fnameindex) { + const char *serr = grub_errmsg; + const char *filename = lua_tostring(L, fnameindex) + 1; + lua_pushfstring(L, "cannot %s %s: %s", what, filename, serr); + lua_remove(L, fnameindex); + return LUA_ERRFILE; +} + +LUALIB_API int luaL_loadfile (lua_State *L, const char *filename) { + LoadF lf; + int status, readstatus; + int c; + int fnameindex = lua_gettop(L) + 1; /* index of filename on the stack */ + lf.extraline = 0; + if (filename == NULL) { + lua_pushliteral(L, "=stdin"); + grub_error(GRUB_ERR_UNKNOWN_DEVICE, "stdin not supported"); + return errfile(L, "open stdin (not supported)", fnameindex); + } + else { + lua_pushfstring(L, "@%s", filename); + lf.f = grub_buffile_open(filename, -1); + if (lf.f == NULL) return errfile(L, "open", fnameindex); + } + c = grub_getc(lf.f); + if (c == '#') { /* Unix exec. file? */ + lf.extraline = 1; + while ((c = grub_getc(lf.f)) != EOF && c != '\n') ; /* skip first line */ + if (c == '\n') c = grub_getc(lf.f); + } + if (c == LUA_SIGNATURE[0] && filename) { /* binary file? */ + grub_file_seek(lf.f, 0); + /* skip eventual `#!...' */ + while ((c = grub_getc(lf.f)) != EOF && c != LUA_SIGNATURE[0]) ; + lf.extraline = 0; + } + grub_file_seek(lf.f, 0); + status = lua_load(L, getF, &lf, lua_tostring(L, -1)); + readstatus = grub_ferror(); + if (filename) grub_file_close(lf.f); /* close file (even in case of errors) */ + if (readstatus) { + lua_settop(L, fnameindex); /* ignore results from `lua_load' */ + return errfile(L, "read", fnameindex); + } + lua_remove(L, fnameindex); + return status; +} + + +typedef struct LoadS { + const char *s; + size_t size; +} LoadS; + + +static const char *getS (lua_State *L, void *ud, size_t *size) { + LoadS *ls = (LoadS *)ud; + (void)L; + if (ls->size == 0) return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + + +LUALIB_API int luaL_loadbuffer (lua_State *L, const char *buff, size_t size, + const char *name) { + LoadS ls; + ls.s = buff; + ls.size = size; + return lua_load(L, getS, &ls, name); +} + + +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s) { + return luaL_loadbuffer(L, s, strlen(s), s); +} + + + +/* }====================================================== */ + + +static void *l_alloc (void *ud, void *ptr, size_t osize, size_t nsize) { + (void)ud; + (void)osize; + if (nsize == 0) { + grub_free(ptr); + return NULL; + } + else + return grub_realloc(ptr, nsize); +} + + +static int panic (lua_State *L) { + (void)L; /* to avoid warnings */ + grub_printf("PANIC: unprotected error in call to Lua API (%s)\n", + lua_tostring(L, -1)); + return 0; +} + + +LUALIB_API lua_State *luaL_newstate (void) { + lua_State *L = lua_newstate(l_alloc, NULL); + if (L) lua_atpanic(L, &panic); + return L; +} + === added file 'lua/lauxlib.h' --- lua/lauxlib.h 1970-01-01 00:00:00 +0000 +++ lua/lauxlib.h 2008-08-01 15:27:27 +0000 @@ -0,0 +1,176 @@ +/* +** $Id: lauxlib.h,v 1.88.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions for building Lua libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lauxlib_h +#define lauxlib_h + + +#include +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#include "lua.h" + + +#if defined(LUA_COMPAT_GETN) +LUALIB_API int (luaL_getn) (lua_State *L, int t); +LUALIB_API void (luaL_setn) (lua_State *L, int t, int n); +#else +#define luaL_getn(L,i) ((int)lua_objlen(L, i)) +#define luaL_setn(L,i,j) ((void)0) /* no op! */ +#endif + +#if defined(LUA_COMPAT_OPENLIB) +#define luaI_openlib luaL_openlib +#endif + + +/* extra error code for `luaL_load' */ +#define LUA_ERRFILE (LUA_ERRERR+1) + + +typedef struct luaL_Reg { + const char *name; + lua_CFunction func; +} luaL_Reg; + + + +LUALIB_API void (luaI_openlib) (lua_State *L, const char *libname, + const luaL_Reg *l, int nup); +LUALIB_API void (luaL_register) (lua_State *L, const char *libname, + const luaL_Reg *l); +LUALIB_API int (luaL_getmetafield) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_callmeta) (lua_State *L, int obj, const char *e); +LUALIB_API int (luaL_typerror) (lua_State *L, int narg, const char *tname); +LUALIB_API int (luaL_argerror) (lua_State *L, int numarg, const char *extramsg); +LUALIB_API const char *(luaL_checklstring) (lua_State *L, int numArg, + size_t *l); +LUALIB_API const char *(luaL_optlstring) (lua_State *L, int numArg, + const char *def, size_t *l); +LUALIB_API lua_Number (luaL_checknumber) (lua_State *L, int numArg); +LUALIB_API lua_Number (luaL_optnumber) (lua_State *L, int nArg, lua_Number def); + +LUALIB_API lua_Integer (luaL_checkinteger) (lua_State *L, int numArg); +LUALIB_API lua_Integer (luaL_optinteger) (lua_State *L, int nArg, + lua_Integer def); + +LUALIB_API void (luaL_checkstack) (lua_State *L, int sz, const char *msg); +LUALIB_API void (luaL_checktype) (lua_State *L, int narg, int t); +LUALIB_API void (luaL_checkany) (lua_State *L, int narg); + +LUALIB_API int (luaL_newmetatable) (lua_State *L, const char *tname); +LUALIB_API void *(luaL_checkudata) (lua_State *L, int ud, const char *tname); + +LUALIB_API void (luaL_where) (lua_State *L, int lvl); +LUALIB_API int (luaL_error) (lua_State *L, const char *fmt, ...); + +LUALIB_API int (luaL_checkoption) (lua_State *L, int narg, const char *def, + const char *const lst[]); + +LUALIB_API int (luaL_ref) (lua_State *L, int t); +LUALIB_API void (luaL_unref) (lua_State *L, int t, int ref); + +LUALIB_API int (luaL_loadfile) (lua_State *L, const char *filename); +LUALIB_API int (luaL_loadbuffer) (lua_State *L, const char *buff, size_t sz, + const char *name); +LUALIB_API int (luaL_loadstring) (lua_State *L, const char *s); + +LUALIB_API lua_State *(luaL_newstate) (void); + + +LUALIB_API const char *(luaL_gsub) (lua_State *L, const char *s, const char *p, + const char *r); + +LUALIB_API const char *(luaL_findtable) (lua_State *L, int idx, + const char *fname, int szhint); + + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define luaL_argcheck(L, cond,numarg,extramsg) \ + ((void)((cond) || luaL_argerror(L, (numarg), (extramsg)))) +#define luaL_checkstring(L,n) (luaL_checklstring(L, (n), NULL)) +#define luaL_optstring(L,n,d) (luaL_optlstring(L, (n), (d), NULL)) +#define luaL_checkint(L,n) ((int)luaL_checkinteger(L, (n))) +#define luaL_optint(L,n,d) ((int)luaL_optinteger(L, (n), (d))) +#define luaL_checklong(L,n) ((long)luaL_checkinteger(L, (n))) +#define luaL_optlong(L,n,d) ((long)luaL_optinteger(L, (n), (d))) + +#define luaL_typename(L,i) lua_typename(L, lua_type(L,(i))) + +#define luaL_dofile(L, fn) \ + (luaL_loadfile(L, fn) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_dostring(L, s) \ + (luaL_loadstring(L, s) || lua_pcall(L, 0, LUA_MULTRET, 0)) + +#define luaL_getmetatable(L,n) (lua_getfield(L, LUA_REGISTRYINDEX, (n))) + +#define luaL_opt(L,f,n,d) (lua_isnoneornil(L,(n)) ? (d) : f(L,(n))) + +/* +** {====================================================== +** Generic Buffer manipulation +** ======================================================= +*/ + + + +typedef struct luaL_Buffer { + char *p; /* current position in buffer */ + int lvl; /* number of strings in the stack (level) */ + lua_State *L; + char buffer[LUAL_BUFFERSIZE]; +} luaL_Buffer; + +#define luaL_addchar(B,c) \ + ((void)((B)->p < ((B)->buffer+LUAL_BUFFERSIZE) || luaL_prepbuffer(B)), \ + (*(B)->p++ = (char)(c))) + +/* compatibility only */ +#define luaL_putchar(B,c) luaL_addchar(B,c) + +#define luaL_addsize(B,n) ((B)->p += (n)) + +LUALIB_API void (luaL_buffinit) (lua_State *L, luaL_Buffer *B); +LUALIB_API char *(luaL_prepbuffer) (luaL_Buffer *B); +LUALIB_API void (luaL_addlstring) (luaL_Buffer *B, const char *s, size_t l); +LUALIB_API void (luaL_addstring) (luaL_Buffer *B, const char *s); +LUALIB_API void (luaL_addvalue) (luaL_Buffer *B); +LUALIB_API void (luaL_pushresult) (luaL_Buffer *B); + + +/* }====================================================== */ + + +/* compatibility with ref system */ + +/* pre-defined references */ +#define LUA_NOREF (-2) +#define LUA_REFNIL (-1) + +#define lua_ref(L,lock) ((lock) ? luaL_ref(L, LUA_REGISTRYINDEX) : \ + (lua_pushstring(L, "unlocked references are obsolete"), lua_error(L), 0)) + +#define lua_unref(L,ref) luaL_unref(L, LUA_REGISTRYINDEX, (ref)) + +#define lua_getref(L,ref) lua_rawgeti(L, LUA_REGISTRYINDEX, (ref)) + + +#define luaL_reg luaL_Reg + +#endif + + === added file 'lua/lbaselib.c' --- lua/lbaselib.c 1970-01-01 00:00:00 +0000 +++ lua/lbaselib.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,656 @@ +/* +** $Id: lbaselib.c,v 1.191.1.4 2008/01/20 13:53:22 roberto Exp $ +** Basic library +** See Copyright Notice in lua.h +*/ + + + +#ifndef USE_GRUB_LIB +#include +#include +#include +#include +#define PRINT(s) fputs(s, stdout) +#else +#define PRINT(s) grub_printf("%s", s) +#endif /* ! USE_GRUB_LIB */ + +#define lbaselib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + + +/* +** If your system does not support `stdout', you can just remove this function. +** If you need, you can define your own `print' function, following this +** model but changing `fputs' to put the strings at a proper place +** (a console window or a log file, for instance). +*/ +static int luaB_print (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + int i; + lua_getglobal(L, "tostring"); + for (i=1; i<=n; i++) { + const char *s; + lua_pushvalue(L, -1); /* function to be called */ + lua_pushvalue(L, i); /* value to print */ + lua_call(L, 1, 1); + s = lua_tostring(L, -1); /* get result */ + if (s == NULL) + return luaL_error(L, LUA_QL("tostring") " must return a string to " + LUA_QL("print")); + if (i>1) PRINT("\t"); + PRINT(s); + lua_pop(L, 1); /* pop result */ + } + PRINT("\n"); + return 0; +} + + +static int luaB_tonumber (lua_State *L) { + int base = luaL_optint(L, 2, 10); + if (base == 10) { /* standard conversion */ + luaL_checkany(L, 1); + if (lua_isnumber(L, 1)) { + lua_pushnumber(L, lua_tonumber(L, 1)); + return 1; + } + } + else { + const char *s1 = luaL_checkstring(L, 1); + char *s2; + unsigned long n; + luaL_argcheck(L, 2 <= base && base <= 36, 2, "base out of range"); + n = strtoul(s1, &s2, base); + if (s1 != s2) { /* at least one valid digit? */ + while (isspace((unsigned char)(*s2))) s2++; /* skip trailing spaces */ + if (*s2 == '\0') { /* no invalid trailing characters? */ + lua_pushnumber(L, (lua_Number)n); + return 1; + } + } + } + lua_pushnil(L); /* else not a number */ + return 1; +} + + +static int luaB_error (lua_State *L) { + int level = luaL_optint(L, 2, 1); + lua_settop(L, 1); + if (lua_isstring(L, 1) && level > 0) { /* add extra information? */ + luaL_where(L, level); + lua_pushvalue(L, 1); + lua_concat(L, 2); + } + return lua_error(L); +} + + +static int luaB_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); + return 1; /* no metatable */ + } + luaL_getmetafield(L, 1, "__metatable"); + return 1; /* returns either __metatable field (if present) or metatable */ +} + + +static int luaB_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + if (luaL_getmetafield(L, 1, "__metatable")) + luaL_error(L, "cannot change a protected metatable"); + lua_settop(L, 2); + lua_setmetatable(L, 1); + return 1; +} + + +static void getfunc (lua_State *L, int opt) { + if (lua_isfunction(L, 1)) lua_pushvalue(L, 1); + else { + lua_Debug ar; + int level = opt ? luaL_optint(L, 1, 1) : luaL_checkint(L, 1); + luaL_argcheck(L, level >= 0, 1, "level must be non-negative"); + if (lua_getstack(L, level, &ar) == 0) + luaL_argerror(L, 1, "invalid level"); + lua_getinfo(L, "f", &ar); + if (lua_isnil(L, -1)) + luaL_error(L, "no function environment for tail call at level %d", + level); + } +} + + +static int luaB_getfenv (lua_State *L) { + getfunc(L, 1); + if (lua_iscfunction(L, -1)) /* is a C function? */ + lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */ + else + lua_getfenv(L, -1); + return 1; +} + + +static int luaB_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + getfunc(L, 0); + lua_pushvalue(L, 2); + if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) { + /* change environment of current thread */ + lua_pushthread(L); + lua_insert(L, -2); + lua_setfenv(L, -2); + return 0; + } + else if (lua_iscfunction(L, -2) || lua_setfenv(L, -2) == 0) + luaL_error(L, + LUA_QL("setfenv") " cannot change environment of given object"); + return 1; +} + + +static int luaB_rawequal (lua_State *L) { + luaL_checkany(L, 1); + luaL_checkany(L, 2); + lua_pushboolean(L, lua_rawequal(L, 1, 2)); + return 1; +} + + +static int luaB_rawget (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_rawget(L, 1); + return 1; +} + +static int luaB_rawset (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checkany(L, 2); + luaL_checkany(L, 3); + lua_settop(L, 3); + lua_rawset(L, 1); + return 1; +} + + +static int luaB_gcinfo (lua_State *L) { + lua_pushinteger(L, lua_getgccount(L)); + return 1; +} + + +static int luaB_collectgarbage (lua_State *L) { + static const char *const opts[] = {"stop", "restart", "collect", + "count", "step", "setpause", "setstepmul", NULL}; + static const int optsnum[] = {LUA_GCSTOP, LUA_GCRESTART, LUA_GCCOLLECT, + LUA_GCCOUNT, LUA_GCSTEP, LUA_GCSETPAUSE, LUA_GCSETSTEPMUL}; + int o = luaL_checkoption(L, 1, "collect", opts); + int ex = luaL_optint(L, 2, 0); + int res = lua_gc(L, optsnum[o], ex); + switch (optsnum[o]) { + case LUA_GCCOUNT: { + int b = lua_gc(L, LUA_GCCOUNTB, 0); + lua_pushnumber(L, res + ((lua_Number)b/1024)); + return 1; + } + case LUA_GCSTEP: { + lua_pushboolean(L, res); + return 1; + } + default: { + lua_pushnumber(L, res); + return 1; + } + } +} + + +static int luaB_type (lua_State *L) { + luaL_checkany(L, 1); + lua_pushstring(L, luaL_typename(L, 1)); + return 1; +} + + +static int luaB_next (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 2); /* create a 2nd argument if there isn't one */ + if (lua_next(L, 1)) + return 2; + else { + lua_pushnil(L); + return 1; + } +} + + +static int luaB_pairs (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushnil(L); /* and initial value */ + return 3; +} + + +static int ipairsaux (lua_State *L) { + int i = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TTABLE); + i++; /* next value */ + lua_pushinteger(L, i); + lua_rawgeti(L, 1, i); + return (lua_isnil(L, -1)) ? 0 : 2; +} + + +static int luaB_ipairs (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ + lua_pushvalue(L, 1); /* state, */ + lua_pushinteger(L, 0); /* and initial value */ + return 3; +} + + +static int load_aux (lua_State *L, int status) { + if (status == 0) /* OK? */ + return 1; + else { + lua_pushnil(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return nil plus error message */ + } +} + + +static int luaB_loadstring (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + const char *chunkname = luaL_optstring(L, 2, s); + return load_aux(L, luaL_loadbuffer(L, s, l, chunkname)); +} + + +static int luaB_loadfile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + return load_aux(L, luaL_loadfile(L, fname)); +} + + +/* +** Reader for generic `load' function: `lua_load' uses the +** stack for internal stuff, so the reader cannot change the +** stack top. Instead, it keeps its resulting string in a +** reserved slot inside the stack. +*/ +static const char *generic_reader (lua_State *L, void *ud, size_t *size) { + (void)ud; /* to avoid warnings */ + luaL_checkstack(L, 2, "too many nested functions"); + lua_pushvalue(L, 1); /* get function */ + lua_call(L, 0, 1); /* call it */ + if (lua_isnil(L, -1)) { + *size = 0; + return NULL; + } + else if (lua_isstring(L, -1)) { + lua_replace(L, 3); /* save string in a reserved stack slot */ + return lua_tolstring(L, 3, size); + } + else luaL_error(L, "reader function must return a string"); + return NULL; /* to avoid warnings */ +} + + +static int luaB_load (lua_State *L) { + int status; + const char *cname = luaL_optstring(L, 2, "=(load)"); + luaL_checktype(L, 1, LUA_TFUNCTION); + lua_settop(L, 3); /* function, eventual name, plus one reserved slot */ + status = lua_load(L, generic_reader, NULL, cname); + return load_aux(L, status); +} + + +static int luaB_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + int n = lua_gettop(L); + if (luaL_loadfile(L, fname) != 0) lua_error(L); + lua_call(L, 0, LUA_MULTRET); + return lua_gettop(L) - n; +} + + +static int luaB_assert (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_toboolean(L, 1)) + return luaL_error(L, "%s", luaL_optstring(L, 2, "assertion failed!")); + return lua_gettop(L); +} + + +static int luaB_unpack (lua_State *L) { + int i, e, n; + luaL_checktype(L, 1, LUA_TTABLE); + i = luaL_optint(L, 2, 1); + e = luaL_opt(L, luaL_checkint, 3, luaL_getn(L, 1)); + n = e - i + 1; /* number of elements */ + if (n <= 0) return 0; /* empty range */ + luaL_checkstack(L, n, "table too big to unpack"); + for (; i<=e; i++) /* push arg[i...e] */ + lua_rawgeti(L, 1, i); + return n; +} + + +static int luaB_select (lua_State *L) { + int n = lua_gettop(L); + if (lua_type(L, 1) == LUA_TSTRING && *lua_tostring(L, 1) == '#') { + lua_pushinteger(L, n-1); + return 1; + } + else { + int i = luaL_checkint(L, 1); + if (i < 0) i = n + i; + else if (i > n) i = n; + luaL_argcheck(L, 1 <= i, 1, "index out of range"); + return n - i; + } +} + + +static int luaB_pcall (lua_State *L) { + int status; + luaL_checkany(L, 1); + status = lua_pcall(L, lua_gettop(L) - 1, LUA_MULTRET, 0); + lua_pushboolean(L, (status == 0)); + lua_insert(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_xpcall (lua_State *L) { + int status; + luaL_checkany(L, 2); + lua_settop(L, 2); + lua_insert(L, 1); /* put error function under function to be called */ + status = lua_pcall(L, 0, LUA_MULTRET, 1); + lua_pushboolean(L, (status == 0)); + lua_replace(L, 1); + return lua_gettop(L); /* return status + all results */ +} + + +static int luaB_tostring (lua_State *L) { + luaL_checkany(L, 1); + if (luaL_callmeta(L, 1, "__tostring")) /* is there a metafield? */ + return 1; /* use its value */ + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + lua_pushstring(L, lua_tostring(L, 1)); + break; + case LUA_TSTRING: + lua_pushvalue(L, 1); + break; + case LUA_TBOOLEAN: + lua_pushstring(L, (lua_toboolean(L, 1) ? "true" : "false")); + break; + case LUA_TNIL: + lua_pushliteral(L, "nil"); + break; + default: + lua_pushfstring(L, "%s: %p", luaL_typename(L, 1), lua_topointer(L, 1)); + break; + } + return 1; +} + + +static int luaB_newproxy (lua_State *L) { + lua_settop(L, 1); + lua_newuserdata(L, 0); /* create proxy */ + if (lua_toboolean(L, 1) == 0) + return 1; /* no metatable */ + else if (lua_isboolean(L, 1)) { + lua_newtable(L); /* create a new metatable `m' ... */ + lua_pushvalue(L, -1); /* ... and mark `m' as a valid metatable */ + lua_pushboolean(L, 1); + lua_rawset(L, lua_upvalueindex(1)); /* weaktable[m] = true */ + } + else { + int validproxy = 0; /* to check if weaktable[metatable(u)] == true */ + if (lua_getmetatable(L, 1)) { + lua_rawget(L, lua_upvalueindex(1)); + validproxy = lua_toboolean(L, -1); + lua_pop(L, 1); /* remove value */ + } + luaL_argcheck(L, validproxy, 1, "boolean or proxy expected"); + lua_getmetatable(L, 1); /* metatable is valid; get it */ + } + lua_setmetatable(L, 2); + return 1; +} + + +static const luaL_Reg base_funcs[] = { + {"assert", luaB_assert}, + {"collectgarbage", luaB_collectgarbage}, + {"dofile", luaB_dofile}, + {"error", luaB_error}, + {"gcinfo", luaB_gcinfo}, + {"getfenv", luaB_getfenv}, + {"getmetatable", luaB_getmetatable}, + {"loadfile", luaB_loadfile}, + {"load", luaB_load}, + {"loadstring", luaB_loadstring}, + {"next", luaB_next}, + {"pcall", luaB_pcall}, + {"print", luaB_print}, + {"rawequal", luaB_rawequal}, + {"rawget", luaB_rawget}, + {"rawset", luaB_rawset}, + {"select", luaB_select}, + {"setfenv", luaB_setfenv}, + {"setmetatable", luaB_setmetatable}, + {"tonumber", luaB_tonumber}, + {"tostring", luaB_tostring}, + {"type", luaB_type}, + {"unpack", luaB_unpack}, + {"xpcall", luaB_xpcall}, + {NULL, NULL} +}; + + +/* +** {====================================================== +** Coroutine library +** ======================================================= +*/ + +#define CO_RUN 0 /* running */ +#define CO_SUS 1 /* suspended */ +#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */ +#define CO_DEAD 3 + +static const char *const statnames[] = + {"running", "suspended", "normal", "dead"}; + +static int costatus (lua_State *L, lua_State *co) { + if (L == co) return CO_RUN; + switch (lua_status(co)) { + case LUA_YIELD: + return CO_SUS; + case 0: { + lua_Debug ar; + if (lua_getstack(co, 0, &ar) > 0) /* does it have frames? */ + return CO_NOR; /* it is running */ + else if (lua_gettop(co) == 0) + return CO_DEAD; + else + return CO_SUS; /* initial state */ + } + default: /* some error occured */ + return CO_DEAD; + } +} + + +static int luaB_costatus (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + luaL_argcheck(L, co, 1, "coroutine expected"); + lua_pushstring(L, statnames[costatus(L, co)]); + return 1; +} + + +static int auxresume (lua_State *L, lua_State *co, int narg) { + int status = costatus(L, co); + if (!lua_checkstack(co, narg)) + luaL_error(L, "too many arguments to resume"); + if (status != CO_SUS) { + lua_pushfstring(L, "cannot resume %s coroutine", statnames[status]); + return -1; /* error flag */ + } + lua_xmove(L, co, narg); + lua_setlevel(L, co); + status = lua_resume(co, narg); + if (status == 0 || status == LUA_YIELD) { + int nres = lua_gettop(co); + if (!lua_checkstack(L, nres)) + luaL_error(L, "too many results to resume"); + lua_xmove(co, L, nres); /* move yielded values */ + return nres; + } + else { + lua_xmove(co, L, 1); /* move error message */ + return -1; /* error flag */ + } +} + + +static int luaB_coresume (lua_State *L) { + lua_State *co = lua_tothread(L, 1); + int r; + luaL_argcheck(L, co, 1, "coroutine expected"); + r = auxresume(L, co, lua_gettop(L) - 1); + if (r < 0) { + lua_pushboolean(L, 0); + lua_insert(L, -2); + return 2; /* return false + error message */ + } + else { + lua_pushboolean(L, 1); + lua_insert(L, -(r + 1)); + return r + 1; /* return true + `resume' returns */ + } +} + + +static int luaB_auxwrap (lua_State *L) { + lua_State *co = lua_tothread(L, lua_upvalueindex(1)); + int r = auxresume(L, co, lua_gettop(L)); + if (r < 0) { + if (lua_isstring(L, -1)) { /* error object is a string? */ + luaL_where(L, 1); /* add extra info */ + lua_insert(L, -2); + lua_concat(L, 2); + } + lua_error(L); /* propagate error */ + } + return r; +} + + +static int luaB_cocreate (lua_State *L) { + lua_State *NL = lua_newthread(L); + luaL_argcheck(L, lua_isfunction(L, 1) && !lua_iscfunction(L, 1), 1, + "Lua function expected"); + lua_pushvalue(L, 1); /* move function to top */ + lua_xmove(L, NL, 1); /* move function from L to NL */ + return 1; +} + + +static int luaB_cowrap (lua_State *L) { + luaB_cocreate(L); + lua_pushcclosure(L, luaB_auxwrap, 1); + return 1; +} + + +static int luaB_yield (lua_State *L) { + return lua_yield(L, lua_gettop(L)); +} + + +static int luaB_corunning (lua_State *L) { + if (lua_pushthread(L)) + lua_pushnil(L); /* main thread is not a coroutine */ + return 1; +} + + +static const luaL_Reg co_funcs[] = { + {"create", luaB_cocreate}, + {"resume", luaB_coresume}, + {"running", luaB_corunning}, + {"status", luaB_costatus}, + {"wrap", luaB_cowrap}, + {"yield", luaB_yield}, + {NULL, NULL} +}; + +/* }====================================================== */ + + +static void auxopen (lua_State *L, const char *name, + lua_CFunction f, lua_CFunction u) { + lua_pushcfunction(L, u); + lua_pushcclosure(L, f, 1); + lua_setfield(L, -2, name); +} + + +static void base_open (lua_State *L) { + /* set global _G */ + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setglobal(L, "_G"); + /* open lib into global table */ + luaL_register(L, "_G", base_funcs); + lua_pushliteral(L, LUA_VERSION); + lua_setglobal(L, "_VERSION"); /* set global _VERSION */ + /* `ipairs' and `pairs' need auxliliary functions as upvalues */ + auxopen(L, "ipairs", luaB_ipairs, ipairsaux); + auxopen(L, "pairs", luaB_pairs, luaB_next); + /* `newproxy' needs a weaktable as upvalue */ + lua_createtable(L, 0, 1); /* new table `w' */ + lua_pushvalue(L, -1); /* `w' will be its own metatable */ + lua_setmetatable(L, -2); + lua_pushliteral(L, "kv"); + lua_setfield(L, -2, "__mode"); /* metatable(w).__mode = "kv" */ + lua_pushcclosure(L, luaB_newproxy, 1); + lua_setglobal(L, "newproxy"); /* set global `newproxy' */ +} + + +LUALIB_API int luaopen_base (lua_State *L) { + base_open(L); + luaL_register(L, LUA_COLIBNAME, co_funcs); + return 2; +} + === added file 'lua/lcode.c' --- lua/lcode.c 1970-01-01 00:00:00 +0000 +++ lua/lcode.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,841 @@ +/* +** $Id: lcode.c,v 2.25.1.3 2007/12/28 15:32:23 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#define lcode_c +#define LUA_CORE + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "ltable.h" + + +#define hasjumps(e) ((e)->t != (e)->f) + + +static int isnumeral(expdesc *e) { + return (e->k == VKNUM && e->t == NO_JUMP && e->f == NO_JUMP); +} + + +void luaK_nil (FuncState *fs, int from, int n) { + Instruction *previous; + if (fs->pc > fs->lasttarget) { /* no jumps to current position? */ + if (fs->pc == 0) { /* function start? */ + if (from >= fs->nactvar) + return; /* positions are already clean */ + } + else { + previous = &fs->f->code[fs->pc-1]; + if (GET_OPCODE(*previous) == OP_LOADNIL) { + int pfrom = GETARG_A(*previous); + int pto = GETARG_B(*previous); + if (pfrom <= from && from <= pto+1) { /* can connect both? */ + if (from+n-1 > pto) + SETARG_B(*previous, from+n-1); + return; + } + } + } + } + luaK_codeABC(fs, OP_LOADNIL, from, from+n-1, 0); /* else no optimization */ +} + + +int luaK_jump (FuncState *fs) { + int jpc = fs->jpc; /* save list of jumps to here */ + int j; + fs->jpc = NO_JUMP; + j = luaK_codeAsBx(fs, OP_JMP, 0, NO_JUMP); + luaK_concat(fs, &j, jpc); /* keep them on hold */ + return j; +} + + +void luaK_ret (FuncState *fs, int first, int nret) { + luaK_codeABC(fs, OP_RETURN, first, nret+1, 0); +} + + +static int condjump (FuncState *fs, OpCode op, int A, int B, int C) { + luaK_codeABC(fs, op, A, B, C); + return luaK_jump(fs); +} + + +static void fixjump (FuncState *fs, int pc, int dest) { + Instruction *jmp = &fs->f->code[pc]; + int offset = dest-(pc+1); + lua_assert(dest != NO_JUMP); + if (abs(offset) > MAXARG_sBx) + luaX_syntaxerror(fs->ls, "control structure too long"); + SETARG_sBx(*jmp, offset); +} + + +/* +** returns current `pc' and marks it as a jump target (to avoid wrong +** optimizations with consecutive instructions not in the same basic block). +*/ +int luaK_getlabel (FuncState *fs) { + fs->lasttarget = fs->pc; + return fs->pc; +} + + +static int getjump (FuncState *fs, int pc) { + int offset = GETARG_sBx(fs->f->code[pc]); + if (offset == NO_JUMP) /* point to itself represents end of list */ + return NO_JUMP; /* end of list */ + else + return (pc+1)+offset; /* turn offset into absolute position */ +} + + +static Instruction *getjumpcontrol (FuncState *fs, int pc) { + Instruction *pi = &fs->f->code[pc]; + if (pc >= 1 && testTMode(GET_OPCODE(*(pi-1)))) + return pi-1; + else + return pi; +} + + +/* +** check whether list has any jump that do not produce a value +** (or produce an inverted value) +*/ +static int need_value (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) { + Instruction i = *getjumpcontrol(fs, list); + if (GET_OPCODE(i) != OP_TESTSET) return 1; + } + return 0; /* not found */ +} + + +static int patchtestreg (FuncState *fs, int node, int reg) { + Instruction *i = getjumpcontrol(fs, node); + if (GET_OPCODE(*i) != OP_TESTSET) + return 0; /* cannot patch other instructions */ + if (reg != NO_REG && reg != GETARG_B(*i)) + SETARG_A(*i, reg); + else /* no register to put value or register already has the value */ + *i = CREATE_ABC(OP_TEST, GETARG_B(*i), 0, GETARG_C(*i)); + + return 1; +} + + +static void removevalues (FuncState *fs, int list) { + for (; list != NO_JUMP; list = getjump(fs, list)) + patchtestreg(fs, list, NO_REG); +} + + +static void patchlistaux (FuncState *fs, int list, int vtarget, int reg, + int dtarget) { + while (list != NO_JUMP) { + int next = getjump(fs, list); + if (patchtestreg(fs, list, reg)) + fixjump(fs, list, vtarget); + else + fixjump(fs, list, dtarget); /* jump to default target */ + list = next; + } +} + + +static void dischargejpc (FuncState *fs) { + patchlistaux(fs, fs->jpc, fs->pc, NO_REG, fs->pc); + fs->jpc = NO_JUMP; +} + + +void luaK_patchlist (FuncState *fs, int list, int target) { + if (target == fs->pc) + luaK_patchtohere(fs, list); + else { + lua_assert(target < fs->pc); + patchlistaux(fs, list, target, NO_REG, target); + } +} + + +void luaK_patchtohere (FuncState *fs, int list) { + luaK_getlabel(fs); + luaK_concat(fs, &fs->jpc, list); +} + + +void luaK_concat (FuncState *fs, int *l1, int l2) { + if (l2 == NO_JUMP) return; + else if (*l1 == NO_JUMP) + *l1 = l2; + else { + int list = *l1; + int next; + while ((next = getjump(fs, list)) != NO_JUMP) /* find last element */ + list = next; + fixjump(fs, list, l2); + } +} + + +void luaK_checkstack (FuncState *fs, int n) { + int newstack = fs->freereg + n; + if (newstack > fs->f->maxstacksize) { + if (newstack >= MAXSTACK) + luaX_syntaxerror(fs->ls, "function or expression too complex"); + fs->f->maxstacksize = cast_byte(newstack); + } +} + + +void luaK_reserveregs (FuncState *fs, int n) { + luaK_checkstack(fs, n); + fs->freereg += n; +} + + +static void freereg (FuncState *fs, int reg) { + if (!ISK(reg) && reg >= fs->nactvar) { + fs->freereg--; + lua_assert(reg == fs->freereg); + } +} + + +static void freeexp (FuncState *fs, expdesc *e) { + if (e->k == VNONRELOC) + freereg(fs, e->u.s.info); +} + + +static int addk (FuncState *fs, TValue *k, TValue *v) { + lua_State *L = fs->L; + TValue *idx = luaH_set(L, fs->h, k); + Proto *f = fs->f; + int oldsize = f->sizek; + if (ttisnumber(idx)) { + lua_assert(luaO_rawequalObj(&fs->f->k[cast_int(nvalue(idx))], v)); + return cast_int(nvalue(idx)); + } + else { /* constant not found; create a new entry */ + setnvalue(idx, cast_num(fs->nk)); + luaM_growvector(L, f->k, fs->nk, f->sizek, TValue, + MAXARG_Bx, "constant table overflow"); + while (oldsize < f->sizek) setnilvalue(&f->k[oldsize++]); + setobj(L, &f->k[fs->nk], v); + luaC_barrier(L, f, v); + return fs->nk++; + } +} + + +int luaK_stringK (FuncState *fs, TString *s) { + TValue o; + setsvalue(fs->L, &o, s); + return addk(fs, &o, &o); +} + + +int luaK_numberK (FuncState *fs, lua_Number r) { + TValue o; + setnvalue(&o, r); + return addk(fs, &o, &o); +} + + +static int boolK (FuncState *fs, int b) { + TValue o; + setbvalue(&o, b); + return addk(fs, &o, &o); +} + + +static int nilK (FuncState *fs) { + TValue k, v; + setnilvalue(&v); + /* cannot use nil as key; instead use table itself to represent nil */ + sethvalue(fs->L, &k, fs->h); + return addk(fs, &k, &v); +} + + +void luaK_setreturns (FuncState *fs, expdesc *e, int nresults) { + if (e->k == VCALL) { /* expression is an open function call? */ + SETARG_C(getcode(fs, e), nresults+1); + } + else if (e->k == VVARARG) { + SETARG_B(getcode(fs, e), nresults+1); + SETARG_A(getcode(fs, e), fs->freereg); + luaK_reserveregs(fs, 1); + } +} + + +void luaK_setoneret (FuncState *fs, expdesc *e) { + if (e->k == VCALL) { /* expression is an open function call? */ + e->k = VNONRELOC; + e->u.s.info = GETARG_A(getcode(fs, e)); + } + else if (e->k == VVARARG) { + SETARG_B(getcode(fs, e), 2); + e->k = VRELOCABLE; /* can relocate its simple result */ + } +} + + +void luaK_dischargevars (FuncState *fs, expdesc *e) { + switch (e->k) { + case VLOCAL: { + e->k = VNONRELOC; + break; + } + case VUPVAL: { + e->u.s.info = luaK_codeABC(fs, OP_GETUPVAL, 0, e->u.s.info, 0); + e->k = VRELOCABLE; + break; + } + case VGLOBAL: { + e->u.s.info = luaK_codeABx(fs, OP_GETGLOBAL, 0, e->u.s.info); + e->k = VRELOCABLE; + break; + } + case VINDEXED: { + freereg(fs, e->u.s.aux); + freereg(fs, e->u.s.info); + e->u.s.info = luaK_codeABC(fs, OP_GETTABLE, 0, e->u.s.info, e->u.s.aux); + e->k = VRELOCABLE; + break; + } + case VVARARG: + case VCALL: { + luaK_setoneret(fs, e); + break; + } + default: break; /* there is one value available (somewhere) */ + } +} + + +static int code_label (FuncState *fs, int A, int b, int jump) { + luaK_getlabel(fs); /* those instructions may be jump targets */ + return luaK_codeABC(fs, OP_LOADBOOL, A, b, jump); +} + + +static void discharge2reg (FuncState *fs, expdesc *e, int reg) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: { + luaK_nil(fs, reg, 1); + break; + } + case VFALSE: case VTRUE: { + luaK_codeABC(fs, OP_LOADBOOL, reg, e->k == VTRUE, 0); + break; + } + case VK: { + luaK_codeABx(fs, OP_LOADK, reg, e->u.s.info); + break; + } + case VKNUM: { + luaK_codeABx(fs, OP_LOADK, reg, luaK_numberK(fs, e->u.nval)); + break; + } + case VRELOCABLE: { + Instruction *pc = &getcode(fs, e); + SETARG_A(*pc, reg); + break; + } + case VNONRELOC: { + if (reg != e->u.s.info) + luaK_codeABC(fs, OP_MOVE, reg, e->u.s.info, 0); + break; + } + default: { + lua_assert(e->k == VVOID || e->k == VJMP); + return; /* nothing to do... */ + } + } + e->u.s.info = reg; + e->k = VNONRELOC; +} + + +static void discharge2anyreg (FuncState *fs, expdesc *e) { + if (e->k != VNONRELOC) { + luaK_reserveregs(fs, 1); + discharge2reg(fs, e, fs->freereg-1); + } +} + + +static void exp2reg (FuncState *fs, expdesc *e, int reg) { + discharge2reg(fs, e, reg); + if (e->k == VJMP) + luaK_concat(fs, &e->t, e->u.s.info); /* put this jump in `t' list */ + if (hasjumps(e)) { + int final; /* position after whole expression */ + int p_f = NO_JUMP; /* position of an eventual LOAD false */ + int p_t = NO_JUMP; /* position of an eventual LOAD true */ + if (need_value(fs, e->t) || need_value(fs, e->f)) { + int fj = (e->k == VJMP) ? NO_JUMP : luaK_jump(fs); + p_f = code_label(fs, reg, 0, 1); + p_t = code_label(fs, reg, 1, 0); + luaK_patchtohere(fs, fj); + } + final = luaK_getlabel(fs); + patchlistaux(fs, e->f, final, reg, p_f); + patchlistaux(fs, e->t, final, reg, p_t); + } + e->f = e->t = NO_JUMP; + e->u.s.info = reg; + e->k = VNONRELOC; +} + + +void luaK_exp2nextreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + freeexp(fs, e); + luaK_reserveregs(fs, 1); + exp2reg(fs, e, fs->freereg - 1); +} + + +int luaK_exp2anyreg (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + if (e->k == VNONRELOC) { + if (!hasjumps(e)) return e->u.s.info; /* exp is already in a register */ + if (e->u.s.info >= fs->nactvar) { /* reg. is not a local? */ + exp2reg(fs, e, e->u.s.info); /* put value on it */ + return e->u.s.info; + } + } + luaK_exp2nextreg(fs, e); /* default */ + return e->u.s.info; +} + + +void luaK_exp2val (FuncState *fs, expdesc *e) { + if (hasjumps(e)) + luaK_exp2anyreg(fs, e); + else + luaK_dischargevars(fs, e); +} + + +int luaK_exp2RK (FuncState *fs, expdesc *e) { + luaK_exp2val(fs, e); + switch (e->k) { + case VKNUM: + case VTRUE: + case VFALSE: + case VNIL: { + if (fs->nk <= MAXINDEXRK) { /* constant fit in RK operand? */ + e->u.s.info = (e->k == VNIL) ? nilK(fs) : + (e->k == VKNUM) ? luaK_numberK(fs, e->u.nval) : + boolK(fs, (e->k == VTRUE)); + e->k = VK; + return RKASK(e->u.s.info); + } + else break; + } + case VK: { + if (e->u.s.info <= MAXINDEXRK) /* constant fit in argC? */ + return RKASK(e->u.s.info); + else break; + } + default: break; + } + /* not a constant in the right range: put it in a register */ + return luaK_exp2anyreg(fs, e); +} + + +void luaK_storevar (FuncState *fs, expdesc *var, expdesc *ex) { + switch (var->k) { + case VLOCAL: { + freeexp(fs, ex); + exp2reg(fs, ex, var->u.s.info); + return; + } + case VUPVAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABC(fs, OP_SETUPVAL, e, var->u.s.info, 0); + break; + } + case VGLOBAL: { + int e = luaK_exp2anyreg(fs, ex); + luaK_codeABx(fs, OP_SETGLOBAL, e, var->u.s.info); + break; + } + case VINDEXED: { + int e = luaK_exp2RK(fs, ex); + luaK_codeABC(fs, OP_SETTABLE, var->u.s.info, var->u.s.aux, e); + break; + } + default: { + lua_assert(0); /* invalid var kind to store */ + break; + } + } + freeexp(fs, ex); +} + + +void luaK_self (FuncState *fs, expdesc *e, expdesc *key) { + int func; + luaK_exp2anyreg(fs, e); + freeexp(fs, e); + func = fs->freereg; + luaK_reserveregs(fs, 2); + luaK_codeABC(fs, OP_SELF, func, e->u.s.info, luaK_exp2RK(fs, key)); + freeexp(fs, key); + e->u.s.info = func; + e->k = VNONRELOC; +} + + +static void invertjump (FuncState *fs, expdesc *e) { + Instruction *pc = getjumpcontrol(fs, e->u.s.info); + lua_assert(testTMode(GET_OPCODE(*pc)) && GET_OPCODE(*pc) != OP_TESTSET && + GET_OPCODE(*pc) != OP_TEST); + SETARG_A(*pc, !(GETARG_A(*pc))); +} + + +static int jumponcond (FuncState *fs, expdesc *e, int cond) { + if (e->k == VRELOCABLE) { + Instruction ie = getcode(fs, e); + if (GET_OPCODE(ie) == OP_NOT) { + fs->pc--; /* remove previous OP_NOT */ + return condjump(fs, OP_TEST, GETARG_B(ie), 0, !cond); + } + /* else go through */ + } + discharge2anyreg(fs, e); + freeexp(fs, e); + return condjump(fs, OP_TESTSET, NO_REG, e->u.s.info, cond); +} + + +void luaK_goiftrue (FuncState *fs, expdesc *e) { + int pc; /* pc of last jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VK: case VKNUM: case VTRUE: { + pc = NO_JUMP; /* always true; do nothing */ + break; + } + case VFALSE: { + pc = luaK_jump(fs); /* always jump */ + break; + } + case VJMP: { + invertjump(fs, e); + pc = e->u.s.info; + break; + } + default: { + pc = jumponcond(fs, e, 0); + break; + } + } + luaK_concat(fs, &e->f, pc); /* insert last jump in `f' list */ + luaK_patchtohere(fs, e->t); + e->t = NO_JUMP; +} + + +static void luaK_goiffalse (FuncState *fs, expdesc *e) { + int pc; /* pc of last jump */ + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: case VFALSE: { + pc = NO_JUMP; /* always false; do nothing */ + break; + } + case VTRUE: { + pc = luaK_jump(fs); /* always jump */ + break; + } + case VJMP: { + pc = e->u.s.info; + break; + } + default: { + pc = jumponcond(fs, e, 1); + break; + } + } + luaK_concat(fs, &e->t, pc); /* insert last jump in `t' list */ + luaK_patchtohere(fs, e->f); + e->f = NO_JUMP; +} + + +static void codenot (FuncState *fs, expdesc *e) { + luaK_dischargevars(fs, e); + switch (e->k) { + case VNIL: case VFALSE: { + e->k = VTRUE; + break; + } + case VK: case VKNUM: case VTRUE: { + e->k = VFALSE; + break; + } + case VJMP: { + invertjump(fs, e); + break; + } + case VRELOCABLE: + case VNONRELOC: { + discharge2anyreg(fs, e); + freeexp(fs, e); + e->u.s.info = luaK_codeABC(fs, OP_NOT, 0, e->u.s.info, 0); + e->k = VRELOCABLE; + break; + } + default: { + lua_assert(0); /* cannot happen */ + break; + } + } + /* interchange true and false lists */ + { int temp = e->f; e->f = e->t; e->t = temp; } + removevalues(fs, e->f); + removevalues(fs, e->t); +} + + +void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k) { + t->u.s.aux = luaK_exp2RK(fs, k); + t->k = VINDEXED; +} + + +static int constfolding (OpCode op, expdesc *e1, expdesc *e2) { + lua_Number v1, v2, r; + if (!isnumeral(e1) || !isnumeral(e2)) return 0; + v1 = e1->u.nval; + v2 = e2->u.nval; + switch (op) { + case OP_ADD: r = luai_numadd(v1, v2); break; + case OP_SUB: r = luai_numsub(v1, v2); break; + case OP_MUL: r = luai_nummul(v1, v2); break; + case OP_DIV: + if (v2 == 0) return 0; /* do not attempt to divide by 0 */ + r = luai_numdiv(v1, v2); break; + case OP_MOD: + if (v2 == 0) return 0; /* do not attempt to divide by 0 */ + r = luai_nummod(v1, v2); break; + case OP_POW: r = luai_numpow(v1, v2); break; + case OP_UNM: r = luai_numunm(v1); break; + case OP_LEN: return 0; /* no constant folding for 'len' */ + default: lua_assert(0); r = 0; break; + } + if (luai_numisnan(r)) return 0; /* do not attempt to produce NaN */ + e1->u.nval = r; + return 1; +} + + +static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { + if (constfolding(op, e1, e2)) + return; + else { + int o2 = (op != OP_UNM && op != OP_LEN) ? luaK_exp2RK(fs, e2) : 0; + int o1 = luaK_exp2RK(fs, e1); + if (o1 > o2) { + freeexp(fs, e1); + freeexp(fs, e2); + } + else { + freeexp(fs, e2); + freeexp(fs, e1); + } + e1->u.s.info = luaK_codeABC(fs, op, 0, o1, o2); + e1->k = VRELOCABLE; + } +} + + +static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, + expdesc *e2) { + int o1 = luaK_exp2RK(fs, e1); + int o2 = luaK_exp2RK(fs, e2); + freeexp(fs, e2); + freeexp(fs, e1); + if (cond == 0 && op != OP_EQ) { + int temp; /* exchange args to replace by `<' or `<=' */ + temp = o1; o1 = o2; o2 = temp; /* o1 <==> o2 */ + cond = 1; + } + e1->u.s.info = condjump(fs, op, cond, o1, o2); + e1->k = VJMP; +} + + +void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) { + expdesc e2; + e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0; + switch (op) { + case OPR_MINUS: { + if (!isnumeral(e)) + luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */ + codearith(fs, OP_UNM, e, &e2); + break; + } + case OPR_NOT: codenot(fs, e); break; + case OPR_LEN: { + luaK_exp2anyreg(fs, e); /* cannot operate on constants */ + codearith(fs, OP_LEN, e, &e2); + break; + } + default: lua_assert(0); + } +} + + +void luaK_infix (FuncState *fs, BinOpr op, expdesc *v) { + switch (op) { + case OPR_AND: { + luaK_goiftrue(fs, v); + break; + } + case OPR_OR: { + luaK_goiffalse(fs, v); + break; + } + case OPR_CONCAT: { + luaK_exp2nextreg(fs, v); /* operand must be on the `stack' */ + break; + } + case OPR_ADD: case OPR_SUB: case OPR_MUL: case OPR_DIV: + case OPR_MOD: case OPR_POW: { + if (!isnumeral(v)) luaK_exp2RK(fs, v); + break; + } + default: { + luaK_exp2RK(fs, v); + break; + } + } +} + + +void luaK_posfix (FuncState *fs, BinOpr op, expdesc *e1, expdesc *e2) { + switch (op) { + case OPR_AND: { + lua_assert(e1->t == NO_JUMP); /* list must be closed */ + luaK_dischargevars(fs, e2); + luaK_concat(fs, &e2->f, e1->f); + *e1 = *e2; + break; + } + case OPR_OR: { + lua_assert(e1->f == NO_JUMP); /* list must be closed */ + luaK_dischargevars(fs, e2); + luaK_concat(fs, &e2->t, e1->t); + *e1 = *e2; + break; + } + case OPR_CONCAT: { + luaK_exp2val(fs, e2); + if (e2->k == VRELOCABLE && GET_OPCODE(getcode(fs, e2)) == OP_CONCAT) { + lua_assert(e1->u.s.info == GETARG_B(getcode(fs, e2))-1); + freeexp(fs, e1); + SETARG_B(getcode(fs, e2), e1->u.s.info); + e1->k = VRELOCABLE; e1->u.s.info = e2->u.s.info; + } + else { + luaK_exp2nextreg(fs, e2); /* operand must be on the 'stack' */ + codearith(fs, OP_CONCAT, e1, e2); + } + break; + } + case OPR_ADD: codearith(fs, OP_ADD, e1, e2); break; + case OPR_SUB: codearith(fs, OP_SUB, e1, e2); break; + case OPR_MUL: codearith(fs, OP_MUL, e1, e2); break; + case OPR_DIV: codearith(fs, OP_DIV, e1, e2); break; + case OPR_MOD: codearith(fs, OP_MOD, e1, e2); break; + case OPR_POW: codearith(fs, OP_POW, e1, e2); break; + case OPR_EQ: codecomp(fs, OP_EQ, 1, e1, e2); break; + case OPR_NE: codecomp(fs, OP_EQ, 0, e1, e2); break; + case OPR_LT: codecomp(fs, OP_LT, 1, e1, e2); break; + case OPR_LE: codecomp(fs, OP_LE, 1, e1, e2); break; + case OPR_GT: codecomp(fs, OP_LT, 0, e1, e2); break; + case OPR_GE: codecomp(fs, OP_LE, 0, e1, e2); break; + default: lua_assert(0); + } +} + + +void luaK_fixline (FuncState *fs, int line) { + fs->f->lineinfo[fs->pc - 1] = line; +} + + +static int luaK_code (FuncState *fs, Instruction i, int line) { + Proto *f = fs->f; + dischargejpc(fs); /* `pc' will change */ + /* put new instruction in code array */ + luaM_growvector(fs->L, f->code, fs->pc, f->sizecode, Instruction, + MAX_INT, "code size overflow"); + f->code[fs->pc] = i; + /* save corresponding line information */ + luaM_growvector(fs->L, f->lineinfo, fs->pc, f->sizelineinfo, int, + MAX_INT, "code size overflow"); + f->lineinfo[fs->pc] = line; + return fs->pc++; +} + + +int luaK_codeABC (FuncState *fs, OpCode o, int a, int b, int c) { + lua_assert(getOpMode(o) == iABC); + lua_assert(getBMode(o) != OpArgN || b == 0); + lua_assert(getCMode(o) != OpArgN || c == 0); + return luaK_code(fs, CREATE_ABC(o, a, b, c), fs->ls->lastline); +} + + +int luaK_codeABx (FuncState *fs, OpCode o, int a, unsigned int bc) { + lua_assert(getOpMode(o) == iABx || getOpMode(o) == iAsBx); + lua_assert(getCMode(o) == OpArgN); + return luaK_code(fs, CREATE_ABx(o, a, bc), fs->ls->lastline); +} + + +void luaK_setlist (FuncState *fs, int base, int nelems, int tostore) { + int c = (nelems - 1)/LFIELDS_PER_FLUSH + 1; + int b = (tostore == LUA_MULTRET) ? 0 : tostore; + lua_assert(tostore != 0); + if (c <= MAXARG_C) + luaK_codeABC(fs, OP_SETLIST, base, b, c); + else { + luaK_codeABC(fs, OP_SETLIST, base, b, 0); + luaK_code(fs, cast(Instruction, c), fs->ls->lastline); + } + fs->freereg = base + 1; /* free registers with list values */ +} + === added file 'lua/lcode.h' --- lua/lcode.h 1970-01-01 00:00:00 +0000 +++ lua/lcode.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,76 @@ +/* +** $Id: lcode.h,v 1.48.1.1 2007/12/27 13:02:25 roberto Exp $ +** Code generator for Lua +** See Copyright Notice in lua.h +*/ + +#ifndef lcode_h +#define lcode_h + +#include "llex.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" + + +/* +** Marks the end of a patch list. It is an invalid value both as an absolute +** address, and as a list link (would link an element to itself). +*/ +#define NO_JUMP (-1) + + +/* +** grep "ORDER OPR" if you change these enums +*/ +typedef enum BinOpr { + OPR_ADD, OPR_SUB, OPR_MUL, OPR_DIV, OPR_MOD, OPR_POW, + OPR_CONCAT, + OPR_NE, OPR_EQ, + OPR_LT, OPR_LE, OPR_GT, OPR_GE, + OPR_AND, OPR_OR, + OPR_NOBINOPR +} BinOpr; + + +typedef enum UnOpr { OPR_MINUS, OPR_NOT, OPR_LEN, OPR_NOUNOPR } UnOpr; + + +#define getcode(fs,e) ((fs)->f->code[(e)->u.s.info]) + +#define luaK_codeAsBx(fs,o,A,sBx) luaK_codeABx(fs,o,A,(sBx)+MAXARG_sBx) + +#define luaK_setmultret(fs,e) luaK_setreturns(fs, e, LUA_MULTRET) + +LUAI_FUNC int luaK_codeABx (FuncState *fs, OpCode o, int A, unsigned int Bx); +LUAI_FUNC int luaK_codeABC (FuncState *fs, OpCode o, int A, int B, int C); +LUAI_FUNC void luaK_fixline (FuncState *fs, int line); +LUAI_FUNC void luaK_nil (FuncState *fs, int from, int n); +LUAI_FUNC void luaK_reserveregs (FuncState *fs, int n); +LUAI_FUNC void luaK_checkstack (FuncState *fs, int n); +LUAI_FUNC int luaK_stringK (FuncState *fs, TString *s); +LUAI_FUNC int luaK_numberK (FuncState *fs, lua_Number r); +LUAI_FUNC void luaK_dischargevars (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2anyreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2nextreg (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_exp2val (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_exp2RK (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_self (FuncState *fs, expdesc *e, expdesc *key); +LUAI_FUNC void luaK_indexed (FuncState *fs, expdesc *t, expdesc *k); +LUAI_FUNC void luaK_goiftrue (FuncState *fs, expdesc *e); +LUAI_FUNC void luaK_storevar (FuncState *fs, expdesc *var, expdesc *e); +LUAI_FUNC void luaK_setreturns (FuncState *fs, expdesc *e, int nresults); +LUAI_FUNC void luaK_setoneret (FuncState *fs, expdesc *e); +LUAI_FUNC int luaK_jump (FuncState *fs); +LUAI_FUNC void luaK_ret (FuncState *fs, int first, int nret); +LUAI_FUNC void luaK_patchlist (FuncState *fs, int list, int target); +LUAI_FUNC void luaK_patchtohere (FuncState *fs, int list); +LUAI_FUNC void luaK_concat (FuncState *fs, int *l1, int l2); +LUAI_FUNC int luaK_getlabel (FuncState *fs); +LUAI_FUNC void luaK_prefix (FuncState *fs, UnOpr op, expdesc *v); +LUAI_FUNC void luaK_infix (FuncState *fs, BinOpr op, expdesc *v); +LUAI_FUNC void luaK_posfix (FuncState *fs, BinOpr op, expdesc *v1, expdesc *v2); +LUAI_FUNC void luaK_setlist (FuncState *fs, int base, int nelems, int tostore); + + +#endif === added file 'lua/ldblib.c' --- lua/ldblib.c 1970-01-01 00:00:00 +0000 +++ lua/ldblib.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,399 @@ +/* +** $Id: ldblib.c,v 1.104.1.3 2008/01/21 13:11:21 roberto Exp $ +** Interface from Lua to its debug API +** See Copyright Notice in lua.h +*/ + + +#ifdef USE_GRUB_LIB +#include /* for grub_cmdline_get() */ +#else /* ! USE_GRUB_LIB */ +#include +#include +#include +#endif /* ! USE_GRUB_LIB */ + +#define ldblib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +static int db_getregistry (lua_State *L) { + lua_pushvalue(L, LUA_REGISTRYINDEX); + return 1; +} + + +static int db_getmetatable (lua_State *L) { + luaL_checkany(L, 1); + if (!lua_getmetatable(L, 1)) { + lua_pushnil(L); /* no metatable */ + } + return 1; +} + + +static int db_setmetatable (lua_State *L) { + int t = lua_type(L, 2); + luaL_argcheck(L, t == LUA_TNIL || t == LUA_TTABLE, 2, + "nil or table expected"); + lua_settop(L, 2); + lua_pushboolean(L, lua_setmetatable(L, 1)); + return 1; +} + + +static int db_getfenv (lua_State *L) { + lua_getfenv(L, 1); + return 1; +} + + +static int db_setfenv (lua_State *L) { + luaL_checktype(L, 2, LUA_TTABLE); + lua_settop(L, 2); + if (lua_setfenv(L, 1) == 0) + luaL_error(L, LUA_QL("setfenv") + " cannot change environment of given object"); + return 1; +} + + +static void settabss (lua_State *L, const char *i, const char *v) { + lua_pushstring(L, v); + lua_setfield(L, -2, i); +} + + +static void settabsi (lua_State *L, const char *i, int v) { + lua_pushinteger(L, v); + lua_setfield(L, -2, i); +} + + +static lua_State *getthread (lua_State *L, int *arg) { + if (lua_isthread(L, 1)) { + *arg = 1; + return lua_tothread(L, 1); + } + else { + *arg = 0; + return L; + } +} + + +static void treatstackoption (lua_State *L, lua_State *L1, const char *fname) { + if (L == L1) { + lua_pushvalue(L, -2); + lua_remove(L, -3); + } + else + lua_xmove(L1, L, 1); + lua_setfield(L, -2, fname); +} + + +static int db_getinfo (lua_State *L) { + lua_Debug ar; + int arg; + lua_State *L1 = getthread(L, &arg); + const char *options = luaL_optstring(L, arg+2, "flnSu"); + if (lua_isnumber(L, arg+1)) { + if (!lua_getstack(L1, (int)lua_tointeger(L, arg+1), &ar)) { + lua_pushnil(L); /* level out of range */ + return 1; + } + } + else if (lua_isfunction(L, arg+1)) { + lua_pushfstring(L, ">%s", options); + options = lua_tostring(L, -1); + lua_pushvalue(L, arg+1); + lua_xmove(L, L1, 1); + } + else + return luaL_argerror(L, arg+1, "function or level expected"); + if (!lua_getinfo(L1, options, &ar)) + return luaL_argerror(L, arg+2, "invalid option"); + lua_createtable(L, 0, 2); + if (strchr(options, 'S')) { + settabss(L, "source", ar.source); + settabss(L, "short_src", ar.short_src); + settabsi(L, "linedefined", ar.linedefined); + settabsi(L, "lastlinedefined", ar.lastlinedefined); + settabss(L, "what", ar.what); + } + if (strchr(options, 'l')) + settabsi(L, "currentline", ar.currentline); + if (strchr(options, 'u')) + settabsi(L, "nups", ar.nups); + if (strchr(options, 'n')) { + settabss(L, "name", ar.name); + settabss(L, "namewhat", ar.namewhat); + } + if (strchr(options, 'L')) + treatstackoption(L, L1, "activelines"); + if (strchr(options, 'f')) + treatstackoption(L, L1, "func"); + return 1; /* return table */ +} + + +static int db_getlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + const char *name; + if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + name = lua_getlocal(L1, &ar, luaL_checkint(L, arg+2)); + if (name) { + lua_xmove(L1, L, 1); + lua_pushstring(L, name); + lua_pushvalue(L, -2); + return 2; + } + else { + lua_pushnil(L); + return 1; + } +} + + +static int db_setlocal (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + if (!lua_getstack(L1, luaL_checkint(L, arg+1), &ar)) /* out of range? */ + return luaL_argerror(L, arg+1, "level out of range"); + luaL_checkany(L, arg+3); + lua_settop(L, arg+3); + lua_xmove(L, L1, 1); + lua_pushstring(L, lua_setlocal(L1, &ar, luaL_checkint(L, arg+2))); + return 1; +} + + +static int auxupvalue (lua_State *L, int get) { + const char *name; + int n = luaL_checkint(L, 2); + luaL_checktype(L, 1, LUA_TFUNCTION); + if (lua_iscfunction(L, 1)) return 0; /* cannot touch C upvalues from Lua */ + name = get ? lua_getupvalue(L, 1, n) : lua_setupvalue(L, 1, n); + if (name == NULL) return 0; + lua_pushstring(L, name); + lua_insert(L, -(get+1)); + return get + 1; +} + + +static int db_getupvalue (lua_State *L) { + return auxupvalue(L, 1); +} + + +static int db_setupvalue (lua_State *L) { + luaL_checkany(L, 3); + return auxupvalue(L, 0); +} + + + +static const char KEY_HOOK = 'h'; + + +static void hookf (lua_State *L, lua_Debug *ar) { + static const char *const hooknames[] = + {"call", "return", "line", "count", "tail return"}; + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + lua_pushlightuserdata(L, L); + lua_rawget(L, -2); + if (lua_isfunction(L, -1)) { + lua_pushstring(L, hooknames[(int)ar->event]); + if (ar->currentline >= 0) + lua_pushinteger(L, ar->currentline); + else lua_pushnil(L); + lua_assert(lua_getinfo(L, "lS", ar)); + lua_call(L, 2, 0); + } +} + + +static int makemask (const char *smask, int count) { + int mask = 0; + if (strchr(smask, 'c')) mask |= LUA_MASKCALL; + if (strchr(smask, 'r')) mask |= LUA_MASKRET; + if (strchr(smask, 'l')) mask |= LUA_MASKLINE; + if (count > 0) mask |= LUA_MASKCOUNT; + return mask; +} + + +static char *unmakemask (int mask, char *smask) { + int i = 0; + if (mask & LUA_MASKCALL) smask[i++] = 'c'; + if (mask & LUA_MASKRET) smask[i++] = 'r'; + if (mask & LUA_MASKLINE) smask[i++] = 'l'; + smask[i] = '\0'; + return smask; +} + + +static void gethooktable (lua_State *L) { + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_rawget(L, LUA_REGISTRYINDEX); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + lua_createtable(L, 0, 1); + lua_pushlightuserdata(L, (void *)&KEY_HOOK); + lua_pushvalue(L, -2); + lua_rawset(L, LUA_REGISTRYINDEX); + } +} + + +static int db_sethook (lua_State *L) { + int arg, mask, count; + lua_Hook func; + lua_State *L1 = getthread(L, &arg); + if (lua_isnoneornil(L, arg+1)) { + lua_settop(L, arg+1); + func = NULL; mask = 0; count = 0; /* turn off hooks */ + } + else { + const char *smask = luaL_checkstring(L, arg+2); + luaL_checktype(L, arg+1, LUA_TFUNCTION); + count = luaL_optint(L, arg+3, 0); + func = hookf; mask = makemask(smask, count); + } + gethooktable(L); + lua_pushlightuserdata(L, L1); + lua_pushvalue(L, arg+1); + lua_rawset(L, -3); /* set new hook */ + lua_pop(L, 1); /* remove hook table */ + lua_sethook(L1, func, mask, count); /* set hooks */ + return 0; +} + + +static int db_gethook (lua_State *L) { + int arg; + lua_State *L1 = getthread(L, &arg); + char buff[5]; + int mask = lua_gethookmask(L1); + lua_Hook hook = lua_gethook(L1); + if (hook != NULL && hook != hookf) /* external hook? */ + lua_pushliteral(L, "external hook"); + else { + gethooktable(L); + lua_pushlightuserdata(L, L1); + lua_rawget(L, -2); /* get hook */ + lua_remove(L, -2); /* remove hook table */ + } + lua_pushstring(L, unmakemask(mask, buff)); + lua_pushinteger(L, lua_gethookcount(L1)); + return 3; +} + + +static int db_debug (lua_State *L) { + for (;;) { + char buffer[250]; + if (grub_cmdline_get("lua_debug> ", buffer, sizeof(buffer), 0, 1) == 0 || + strcmp(buffer, "cont\n") == 0) + return 0; + if (luaL_loadbuffer(L, buffer, strlen(buffer), "=(debug command)") || + lua_pcall(L, 0, 0, 0)) { + grub_printf("%s\n", lua_tostring(L, -1)); + } + lua_settop(L, 0); /* remove eventual returns */ + } +} + + +#define LEVELS1 12 /* size of the first part of the stack */ +#define LEVELS2 10 /* size of the second part of the stack */ + +static int db_errorfb (lua_State *L) { + int level; + int firstpart = 1; /* still before eventual `...' */ + int arg; + lua_State *L1 = getthread(L, &arg); + lua_Debug ar; + if (lua_isnumber(L, arg+2)) { + level = (int)lua_tointeger(L, arg+2); + lua_pop(L, 1); + } + else + level = (L == L1) ? 1 : 0; /* level 0 may be this own function */ + if (lua_gettop(L) == arg) + lua_pushliteral(L, ""); + else if (!lua_isstring(L, arg+1)) return 1; /* message is not a string */ + else lua_pushliteral(L, "\n"); + lua_pushliteral(L, "stack traceback:"); + while (lua_getstack(L1, level++, &ar)) { + if (level > LEVELS1 && firstpart) { + /* no more than `LEVELS2' more levels? */ + if (!lua_getstack(L1, level+LEVELS2, &ar)) + level--; /* keep going */ + else { + lua_pushliteral(L, "\n\t..."); /* too many levels */ + while (lua_getstack(L1, level+LEVELS2, &ar)) /* find last levels */ + level++; + } + firstpart = 0; + continue; + } + lua_pushliteral(L, "\n\t"); + lua_getinfo(L1, "Snl", &ar); + lua_pushfstring(L, "%s:", ar.short_src); + if (ar.currentline > 0) + lua_pushfstring(L, "%d:", ar.currentline); + if (*ar.namewhat != '\0') /* is there a name? */ + lua_pushfstring(L, " in function " LUA_QS, ar.name); + else { + if (*ar.what == 'm') /* main? */ + lua_pushfstring(L, " in main chunk"); + else if (*ar.what == 'C' || *ar.what == 't') + lua_pushliteral(L, " ?"); /* C function or tail call */ + else + lua_pushfstring(L, " in function <%s:%d>", + ar.short_src, ar.linedefined); + } + lua_concat(L, lua_gettop(L) - arg); + } + lua_concat(L, lua_gettop(L) - arg); + return 1; +} + + +static const luaL_Reg dblib[] = { + {"debug", db_debug}, + {"getfenv", db_getfenv}, + {"gethook", db_gethook}, + {"getinfo", db_getinfo}, + {"getlocal", db_getlocal}, + {"getregistry", db_getregistry}, + {"getmetatable", db_getmetatable}, + {"getupvalue", db_getupvalue}, + {"setfenv", db_setfenv}, + {"sethook", db_sethook}, + {"setlocal", db_setlocal}, + {"setmetatable", db_setmetatable}, + {"setupvalue", db_setupvalue}, + {"traceback", db_errorfb}, + {NULL, NULL} +}; + + +LUALIB_API int luaopen_debug (lua_State *L) { + luaL_register(L, LUA_DBLIBNAME, dblib); + return 1; +} + === added file 'lua/ldebug.c' --- lua/ldebug.c 1970-01-01 00:00:00 +0000 +++ lua/ldebug.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,624 @@ +/* +** $Id: ldebug.c,v 2.29.1.3 2007/12/28 15:32:23 roberto Exp $ +** Debug Interface +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#include +#include +#endif /* ! USE_GRUB_LIB */ + + +#define ldebug_c +#define LUA_CORE + +#include "lua.h" + +#include "lapi.h" +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name); + + +static int currentpc (lua_State *L, CallInfo *ci) { + if (!isLua(ci)) return -1; /* function is not a Lua function? */ + if (ci == L->ci) + ci->savedpc = L->savedpc; + return pcRel(ci->savedpc, ci_func(ci)->l.p); +} + + +static int currentline (lua_State *L, CallInfo *ci) { + int pc = currentpc(L, ci); + if (pc < 0) + return -1; /* only active lua functions have current-line information */ + else + return getline(ci_func(ci)->l.p, pc); +} + + +/* +** this function can be called asynchronous (e.g. during a signal) +*/ +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count) { + if (func == NULL || mask == 0) { /* turn off hooks? */ + mask = 0; + func = NULL; + } + L->hook = func; + L->basehookcount = count; + resethookcount(L); + L->hookmask = cast_byte(mask); + return 1; +} + + +LUA_API lua_Hook lua_gethook (lua_State *L) { + return L->hook; +} + + +LUA_API int lua_gethookmask (lua_State *L) { + return L->hookmask; +} + + +LUA_API int lua_gethookcount (lua_State *L) { + return L->basehookcount; +} + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar) { + int status; + CallInfo *ci; + lua_lock(L); + for (ci = L->ci; level > 0 && ci > L->base_ci; ci--) { + level--; + if (f_isLua(ci)) /* Lua function? */ + level -= ci->tailcalls; /* skip lost tail calls */ + } + if (level == 0 && ci > L->base_ci) { /* level found? */ + status = 1; + ar->i_ci = cast_int(ci - L->base_ci); + } + else if (level < 0) { /* level is of a lost tail call? */ + status = 1; + ar->i_ci = 0; + } + else status = 0; /* no such level */ + lua_unlock(L); + return status; +} + + +static Proto *getluaproto (CallInfo *ci) { + return (isLua(ci) ? ci_func(ci)->l.p : NULL); +} + + +static const char *findlocal (lua_State *L, CallInfo *ci, int n) { + const char *name; + Proto *fp = getluaproto(ci); + if (fp && (name = luaF_getlocalname(fp, n, currentpc(L, ci))) != NULL) + return name; /* is a local variable in a Lua function */ + else { + StkId limit = (ci == L->ci) ? L->top : (ci+1)->func; + if (limit - ci->base >= n && n > 0) /* is 'n' inside 'ci' stack? */ + return "(*temporary)"; + else + return NULL; + } +} + + +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n) { + CallInfo *ci = L->base_ci + ar->i_ci; + const char *name = findlocal(L, ci, n); + lua_lock(L); + if (name) + luaA_pushobject(L, ci->base + (n - 1)); + lua_unlock(L); + return name; +} + + +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n) { + CallInfo *ci = L->base_ci + ar->i_ci; + const char *name = findlocal(L, ci, n); + lua_lock(L); + if (name) + setobjs2s(L, ci->base + (n - 1), L->top - 1); + L->top--; /* pop value */ + lua_unlock(L); + return name; +} + + +static void funcinfo (lua_Debug *ar, Closure *cl) { + if (cl->c.isC) { + ar->source = "=[C]"; + ar->linedefined = -1; + ar->lastlinedefined = -1; + ar->what = "C"; + } + else { + ar->source = getstr(cl->l.p->source); + ar->linedefined = cl->l.p->linedefined; + ar->lastlinedefined = cl->l.p->lastlinedefined; + ar->what = (ar->linedefined == 0) ? "main" : "Lua"; + } + luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); +} + + +static void info_tailcall (lua_Debug *ar) { + ar->name = ar->namewhat = ""; + ar->what = "tail"; + ar->lastlinedefined = ar->linedefined = ar->currentline = -1; + ar->source = "=(tail call)"; + luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE); + ar->nups = 0; +} + + +static void collectvalidlines (lua_State *L, Closure *f) { + if (f == NULL || f->c.isC) { + setnilvalue(L->top); + } + else { + Table *t = luaH_new(L, 0, 0); + int *lineinfo = f->l.p->lineinfo; + int i; + for (i=0; il.p->sizelineinfo; i++) + setbvalue(luaH_setnum(L, t, lineinfo[i]), 1); + sethvalue(L, L->top, t); + } + incr_top(L); +} + + +static int auxgetinfo (lua_State *L, const char *what, lua_Debug *ar, + Closure *f, CallInfo *ci) { + int status = 1; + if (f == NULL) { + info_tailcall(ar); + return status; + } + for (; *what; what++) { + switch (*what) { + case 'S': { + funcinfo(ar, f); + break; + } + case 'l': { + ar->currentline = (ci) ? currentline(L, ci) : -1; + break; + } + case 'u': { + ar->nups = f->c.nupvalues; + break; + } + case 'n': { + ar->namewhat = (ci) ? getfuncname(L, ci, &ar->name) : NULL; + if (ar->namewhat == NULL) { + ar->namewhat = ""; /* not found */ + ar->name = NULL; + } + break; + } + case 'L': + case 'f': /* handled by lua_getinfo */ + break; + default: status = 0; /* invalid option */ + } + } + return status; +} + + +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar) { + int status; + Closure *f = NULL; + CallInfo *ci = NULL; + lua_lock(L); + if (*what == '>') { + StkId func = L->top - 1; + luai_apicheck(L, ttisfunction(func)); + what++; /* skip the '>' */ + f = clvalue(func); + L->top--; /* pop function */ + } + else if (ar->i_ci != 0) { /* no tail call? */ + ci = L->base_ci + ar->i_ci; + lua_assert(ttisfunction(ci->func)); + f = clvalue(ci->func); + } + status = auxgetinfo(L, what, ar, f, ci); + if (strchr(what, 'f')) { + if (f == NULL) setnilvalue(L->top); + else setclvalue(L, L->top, f); + incr_top(L); + } + if (strchr(what, 'L')) + collectvalidlines(L, f); + lua_unlock(L); + return status; +} + + +/* +** {====================================================== +** Symbolic Execution and code checker +** ======================================================= +*/ + +#define check(x) if (!(x)) return 0; + +#define checkjump(pt,pc) check(0 <= pc && pc < pt->sizecode) + +#define checkreg(pt,reg) check((reg) < (pt)->maxstacksize) + + + +static int precheck (const Proto *pt) { + check(pt->maxstacksize <= MAXSTACK); + lua_assert(pt->numparams+(pt->is_vararg & VARARG_HASARG) <= pt->maxstacksize); + lua_assert(!(pt->is_vararg & VARARG_NEEDSARG) || + (pt->is_vararg & VARARG_HASARG)); + check(pt->sizeupvalues <= pt->nups); + check(pt->sizelineinfo == pt->sizecode || pt->sizelineinfo == 0); + check(GET_OPCODE(pt->code[pt->sizecode-1]) == OP_RETURN); + return 1; +} + + +#define checkopenop(pt,pc) luaG_checkopenop((pt)->code[(pc)+1]) + +int luaG_checkopenop (Instruction i) { + switch (GET_OPCODE(i)) { + case OP_CALL: + case OP_TAILCALL: + case OP_RETURN: + case OP_SETLIST: { + check(GETARG_B(i) == 0); + return 1; + } + default: return 0; /* invalid instruction after an open call */ + } +} + + +static int checkArgMode (const Proto *pt, int r, enum OpArgMask mode) { + switch (mode) { + case OpArgN: check(r == 0); break; + case OpArgU: break; + case OpArgR: checkreg(pt, r); break; + case OpArgK: + check(ISK(r) ? INDEXK(r) < pt->sizek : r < pt->maxstacksize); + break; + } + return 1; +} + + +static Instruction symbexec (const Proto *pt, int lastpc, int reg) { + int pc; + int last; /* stores position of last instruction that changed `reg' */ + last = pt->sizecode-1; /* points to final return (a `neutral' instruction) */ + check(precheck(pt)); + for (pc = 0; pc < lastpc; pc++) { + Instruction i = pt->code[pc]; + OpCode op = GET_OPCODE(i); + int a = GETARG_A(i); + int b = 0; + int c = 0; + check(op < NUM_OPCODES); + checkreg(pt, a); + switch (getOpMode(op)) { + case iABC: { + b = GETARG_B(i); + c = GETARG_C(i); + check(checkArgMode(pt, b, getBMode(op))); + check(checkArgMode(pt, c, getCMode(op))); + break; + } + case iABx: { + b = GETARG_Bx(i); + if (getBMode(op) == OpArgK) check(b < pt->sizek); + break; + } + case iAsBx: { + b = GETARG_sBx(i); + if (getBMode(op) == OpArgR) { + int dest = pc+1+b; + check(0 <= dest && dest < pt->sizecode); + if (dest > 0) { + /* cannot jump to a setlist count */ + Instruction d = pt->code[dest-1]; + check(!(GET_OPCODE(d) == OP_SETLIST && GETARG_C(d) == 0)); + } + } + break; + } + } + if (testAMode(op)) { + if (a == reg) last = pc; /* change register `a' */ + } + if (testTMode(op)) { + check(pc+2 < pt->sizecode); /* check skip */ + check(GET_OPCODE(pt->code[pc+1]) == OP_JMP); + } + switch (op) { + case OP_LOADBOOL: { + check(c == 0 || pc+2 < pt->sizecode); /* check its jump */ + break; + } + case OP_LOADNIL: { + if (a <= reg && reg <= b) + last = pc; /* set registers from `a' to `b' */ + break; + } + case OP_GETUPVAL: + case OP_SETUPVAL: { + check(b < pt->nups); + break; + } + case OP_GETGLOBAL: + case OP_SETGLOBAL: { + check(ttisstring(&pt->k[b])); + break; + } + case OP_SELF: { + checkreg(pt, a+1); + if (reg == a+1) last = pc; + break; + } + case OP_CONCAT: { + check(b < c); /* at least two operands */ + break; + } + case OP_TFORLOOP: { + check(c >= 1); /* at least one result (control variable) */ + checkreg(pt, a+2+c); /* space for results */ + if (reg >= a+2) last = pc; /* affect all regs above its base */ + break; + } + case OP_FORLOOP: + case OP_FORPREP: + checkreg(pt, a+3); + /* go through */ + case OP_JMP: { + int dest = pc+1+b; + /* not full check and jump is forward and do not skip `lastpc'? */ + if (reg != NO_REG && pc < dest && dest <= lastpc) + pc += b; /* do the jump */ + break; + } + case OP_CALL: + case OP_TAILCALL: { + if (b != 0) { + checkreg(pt, a+b-1); + } + c--; /* c = num. returns */ + if (c == LUA_MULTRET) { + check(checkopenop(pt, pc)); + } + else if (c != 0) + checkreg(pt, a+c-1); + if (reg >= a) last = pc; /* affect all registers above base */ + break; + } + case OP_RETURN: { + b--; /* b = num. returns */ + if (b > 0) checkreg(pt, a+b-1); + break; + } + case OP_SETLIST: { + if (b > 0) checkreg(pt, a + b); + if (c == 0) pc++; + break; + } + case OP_CLOSURE: { + int nup, j; + check(b < pt->sizep); + nup = pt->p[b]->nups; + check(pc + nup < pt->sizecode); + for (j = 1; j <= nup; j++) { + OpCode op1 = GET_OPCODE(pt->code[pc + j]); + check(op1 == OP_GETUPVAL || op1 == OP_MOVE); + } + if (reg != NO_REG) /* tracing? */ + pc += nup; /* do not 'execute' these pseudo-instructions */ + break; + } + case OP_VARARG: { + check((pt->is_vararg & VARARG_ISVARARG) && + !(pt->is_vararg & VARARG_NEEDSARG)); + b--; + if (b == LUA_MULTRET) check(checkopenop(pt, pc)); + checkreg(pt, a+b-1); + break; + } + default: break; + } + } + return pt->code[last]; +} + +#undef check +#undef checkjump +#undef checkreg + +/* }====================================================== */ + + +int luaG_checkcode (const Proto *pt) { + return (symbexec(pt, pt->sizecode, NO_REG) != 0); +} + + +static const char *kname (Proto *p, int c) { + if (ISK(c) && ttisstring(&p->k[INDEXK(c)])) + return svalue(&p->k[INDEXK(c)]); + else + return "?"; +} + + +static const char *getobjname (lua_State *L, CallInfo *ci, int stackpos, + const char **name) { + if (isLua(ci)) { /* a Lua function? */ + Proto *p = ci_func(ci)->l.p; + int pc = currentpc(L, ci); + Instruction i; + *name = luaF_getlocalname(p, stackpos+1, pc); + if (*name) /* is a local? */ + return "local"; + i = symbexec(p, pc, stackpos); /* try symbolic execution */ + lua_assert(pc != -1); + switch (GET_OPCODE(i)) { + case OP_GETGLOBAL: { + int g = GETARG_Bx(i); /* global index */ + lua_assert(ttisstring(&p->k[g])); + *name = svalue(&p->k[g]); + return "global"; + } + case OP_MOVE: { + int a = GETARG_A(i); + int b = GETARG_B(i); /* move from `b' to `a' */ + if (b < a) + return getobjname(L, ci, b, name); /* get name for `b' */ + break; + } + case OP_GETTABLE: { + int k = GETARG_C(i); /* key index */ + *name = kname(p, k); + return "field"; + } + case OP_GETUPVAL: { + int u = GETARG_B(i); /* upvalue index */ + *name = p->upvalues ? getstr(p->upvalues[u]) : "?"; + return "upvalue"; + } + case OP_SELF: { + int k = GETARG_C(i); /* key index */ + *name = kname(p, k); + return "method"; + } + default: break; + } + } + return NULL; /* no useful name found */ +} + + +static const char *getfuncname (lua_State *L, CallInfo *ci, const char **name) { + Instruction i; + if ((isLua(ci) && ci->tailcalls > 0) || !isLua(ci - 1)) + return NULL; /* calling function is not Lua (or is unknown) */ + ci--; /* calling function */ + i = ci_func(ci)->l.p->code[currentpc(L, ci)]; + if (GET_OPCODE(i) == OP_CALL || GET_OPCODE(i) == OP_TAILCALL || + GET_OPCODE(i) == OP_TFORLOOP) + return getobjname(L, ci, GETARG_A(i), name); + else + return NULL; /* no useful name can be found */ +} + + +/* only ANSI way to check whether a pointer points to an array */ +static int isinstack (CallInfo *ci, const TValue *o) { + StkId p; + for (p = ci->base; p < ci->top; p++) + if (o == p) return 1; + return 0; +} + + +void luaG_typeerror (lua_State *L, const TValue *o, const char *op) { + const char *name = NULL; + const char *t = luaT_typenames[ttype(o)]; + const char *kind = (isinstack(L->ci, o)) ? + getobjname(L, L->ci, cast_int(o - L->base), &name) : + NULL; + if (kind) + luaG_runerror(L, "attempt to %s %s " LUA_QS " (a %s value)", + op, kind, name, t); + else + luaG_runerror(L, "attempt to %s a %s value", op, t); +} + + +void luaG_concaterror (lua_State *L, StkId p1, StkId p2) { + if (ttisstring(p1) || ttisnumber(p1)) p1 = p2; + lua_assert(!ttisstring(p1) && !ttisnumber(p1)); + luaG_typeerror(L, p1, "concatenate"); +} + + +void luaG_aritherror (lua_State *L, const TValue *p1, const TValue *p2) { + TValue temp; + if (luaV_tonumber(p1, &temp) == NULL) + p2 = p1; /* first operand is wrong */ + luaG_typeerror(L, p2, "perform arithmetic on"); +} + + +int luaG_ordererror (lua_State *L, const TValue *p1, const TValue *p2) { + const char *t1 = luaT_typenames[ttype(p1)]; + const char *t2 = luaT_typenames[ttype(p2)]; + if (t1[2] == t2[2]) + luaG_runerror(L, "attempt to compare two %s values", t1); + else + luaG_runerror(L, "attempt to compare %s with %s", t1, t2); + return 0; +} + + +static void addinfo (lua_State *L, const char *msg) { + CallInfo *ci = L->ci; + if (isLua(ci)) { /* is Lua code? */ + char buff[LUA_IDSIZE]; /* add file:line information */ + int line = currentline(L, ci); + luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE); + luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); + } +} + + +void luaG_errormsg (lua_State *L) { + if (L->errfunc != 0) { /* is there an error handling function? */ + StkId errfunc = restorestack(L, L->errfunc); + if (!ttisfunction(errfunc)) luaD_throw(L, LUA_ERRERR); + setobjs2s(L, L->top, L->top - 1); /* move argument */ + setobjs2s(L, L->top - 1, errfunc); /* push function */ + incr_top(L); + luaD_call(L, L->top - 2, 1); /* call it */ + } + luaD_throw(L, LUA_ERRRUN); +} + + +void luaG_runerror (lua_State *L, const char *fmt, ...) { + va_list argp; + va_start(argp, fmt); + addinfo(L, luaO_pushvfstring(L, fmt, argp)); + va_end(argp); + luaG_errormsg(L); +} + === added file 'lua/ldebug.h' --- lua/ldebug.h 1970-01-01 00:00:00 +0000 +++ lua/ldebug.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,33 @@ +/* +** $Id: ldebug.h,v 2.3.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions from Debug Interface module +** See Copyright Notice in lua.h +*/ + +#ifndef ldebug_h +#define ldebug_h + + +#include "lstate.h" + + +#define pcRel(pc, p) (cast(int, (pc) - (p)->code) - 1) + +#define getline(f,pc) (((f)->lineinfo) ? (f)->lineinfo[pc] : 0) + +#define resethookcount(L) (L->hookcount = L->basehookcount) + + +LUAI_FUNC void luaG_typeerror (lua_State *L, const TValue *o, + const char *opname); +LUAI_FUNC void luaG_concaterror (lua_State *L, StkId p1, StkId p2); +LUAI_FUNC void luaG_aritherror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC int luaG_ordererror (lua_State *L, const TValue *p1, + const TValue *p2); +LUAI_FUNC void luaG_runerror (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaG_errormsg (lua_State *L); +LUAI_FUNC int luaG_checkcode (const Proto *pt); +LUAI_FUNC int luaG_checkopenop (Instruction i); + +#endif === added file 'lua/ldo.c' --- lua/ldo.c 1970-01-01 00:00:00 +0000 +++ lua/ldo.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,520 @@ +/* +** $Id: ldo.c,v 2.38.1.3 2008/01/18 22:31:22 roberto Exp $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#include +#include +#endif /* ! USE_GRUB_LIB */ + +#define ldo_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lundump.h" +#include "lvm.h" +#include "lzio.h" + + + + +/* +** {====================================================== +** Error-recovery functions +** ======================================================= +*/ + + +/* chain list of long jump buffers */ +struct lua_longjmp { + struct lua_longjmp *previous; + luai_jmpbuf b; + volatile int status; /* error code */ +}; + + +void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop) { + switch (errcode) { + case LUA_ERRMEM: { + setsvalue2s(L, oldtop, luaS_newliteral(L, MEMERRMSG)); + break; + } + case LUA_ERRERR: { + setsvalue2s(L, oldtop, luaS_newliteral(L, "error in error handling")); + break; + } + case LUA_ERRSYNTAX: + case LUA_ERRRUN: { + setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ + break; + } + } + L->top = oldtop + 1; +} + + +static void restore_stack_limit (lua_State *L) { + lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); + if (L->size_ci > LUAI_MAXCALLS) { /* there was an overflow? */ + int inuse = cast_int(L->ci - L->base_ci); + if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */ + luaD_reallocCI(L, LUAI_MAXCALLS); + } +} + + +static void resetstack (lua_State *L, int status) { + L->ci = L->base_ci; + L->base = L->ci->base; + luaF_close(L, L->base); /* close eventual pending closures */ + luaD_seterrorobj(L, status, L->base); + L->nCcalls = L->baseCcalls; + L->allowhook = 1; + restore_stack_limit(L); + L->errfunc = 0; + L->errorJmp = NULL; +} + + +void luaD_throw (lua_State *L, int errcode) { + if (L->errorJmp) { + L->errorJmp->status = errcode; + LUAI_THROW(L, L->errorJmp); + } + else { + L->status = cast_byte(errcode); + if (G(L)->panic) { + resetstack(L, errcode); + lua_unlock(L); + G(L)->panic(L); + } + exit(EXIT_FAILURE); + } +} + + +int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud) { + struct lua_longjmp lj; + lj.status = 0; + lj.previous = L->errorJmp; /* chain new error handler */ + L->errorJmp = &lj; + LUAI_TRY(L, &lj, + (*f)(L, ud); + ); + L->errorJmp = lj.previous; /* restore old error handler */ + return lj.status; +} + +/* }====================================================== */ + + +static void correctstack (lua_State *L, TValue *oldstack) { + CallInfo *ci; + GCObject *up; + L->top = (L->top - oldstack) + L->stack; + for (up = L->openupval; up != NULL; up = up->gch.next) + gco2uv(up)->v = (gco2uv(up)->v - oldstack) + L->stack; + for (ci = L->base_ci; ci <= L->ci; ci++) { + ci->top = (ci->top - oldstack) + L->stack; + ci->base = (ci->base - oldstack) + L->stack; + ci->func = (ci->func - oldstack) + L->stack; + } + L->base = (L->base - oldstack) + L->stack; +} + + +void luaD_reallocstack (lua_State *L, int newsize) { + TValue *oldstack = L->stack; + int realsize = newsize + 1 + EXTRA_STACK; + lua_assert(L->stack_last - L->stack == L->stacksize - EXTRA_STACK - 1); + luaM_reallocvector(L, L->stack, L->stacksize, realsize, TValue); + L->stacksize = realsize; + L->stack_last = L->stack+newsize; + correctstack(L, oldstack); +} + + +void luaD_reallocCI (lua_State *L, int newsize) { + CallInfo *oldci = L->base_ci; + luaM_reallocvector(L, L->base_ci, L->size_ci, newsize, CallInfo); + L->size_ci = newsize; + L->ci = (L->ci - oldci) + L->base_ci; + L->end_ci = L->base_ci + L->size_ci - 1; +} + + +void luaD_growstack (lua_State *L, int n) { + if (n <= L->stacksize) /* double size is enough? */ + luaD_reallocstack(L, 2*L->stacksize); + else + luaD_reallocstack(L, L->stacksize + n); +} + + +static CallInfo *growCI (lua_State *L) { + if (L->size_ci > LUAI_MAXCALLS) /* overflow while handling overflow? */ + luaD_throw(L, LUA_ERRERR); + else { + luaD_reallocCI(L, 2*L->size_ci); + if (L->size_ci > LUAI_MAXCALLS) + luaG_runerror(L, "stack overflow"); + } + return ++L->ci; +} + + +void luaD_callhook (lua_State *L, int event, int line) { + lua_Hook hook = L->hook; + if (hook && L->allowhook) { + ptrdiff_t top = savestack(L, L->top); + ptrdiff_t ci_top = savestack(L, L->ci->top); + lua_Debug ar; + ar.event = event; + ar.currentline = line; + if (event == LUA_HOOKTAILRET) + ar.i_ci = 0; /* tail call; no debug information about it */ + else + ar.i_ci = cast_int(L->ci - L->base_ci); + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + L->ci->top = L->top + LUA_MINSTACK; + lua_assert(L->ci->top <= L->stack_last); + L->allowhook = 0; /* cannot call hooks inside a hook */ + lua_unlock(L); + (*hook)(L, &ar); + lua_lock(L); + lua_assert(!L->allowhook); + L->allowhook = 1; + L->ci->top = restorestack(L, ci_top); + L->top = restorestack(L, top); + } +} + + +static StkId adjust_varargs (lua_State *L, Proto *p, int actual) { + int i; + int nfixargs = p->numparams; + Table *htab = NULL; + StkId base, fixed; + for (; actual < nfixargs; ++actual) + setnilvalue(L->top++); +#if defined(LUA_COMPAT_VARARG) + if (p->is_vararg & VARARG_NEEDSARG) { /* compat. with old-style vararg? */ + int nvar = actual - nfixargs; /* number of extra arguments */ + lua_assert(p->is_vararg & VARARG_HASARG); + luaC_checkGC(L); + htab = luaH_new(L, nvar, 1); /* create `arg' table */ + for (i=0; itop - nvar + i); + /* store counter in field `n' */ + setnvalue(luaH_setstr(L, htab, luaS_newliteral(L, "n")), cast_num(nvar)); + } +#endif + /* move fixed parameters to final position */ + fixed = L->top - actual; /* first fixed argument */ + base = L->top; /* final position of first argument */ + for (i=0; itop++, fixed+i); + setnilvalue(fixed+i); + } + /* add `arg' parameter */ + if (htab) { + sethvalue(L, L->top++, htab); + lua_assert(iswhite(obj2gco(htab))); + } + return base; +} + + +static StkId tryfuncTM (lua_State *L, StkId func) { + const TValue *tm = luaT_gettmbyobj(L, func, TM_CALL); + StkId p; + ptrdiff_t funcr = savestack(L, func); + if (!ttisfunction(tm)) + luaG_typeerror(L, func, "call"); + /* Open a hole inside the stack at `func' */ + for (p = L->top; p > func; p--) setobjs2s(L, p, p-1); + incr_top(L); + func = restorestack(L, funcr); /* previous call may change stack */ + setobj2s(L, func, tm); /* tag method is the new function to be called */ + return func; +} + + + +#define inc_ci(L) \ + ((L->ci == L->end_ci) ? growCI(L) : \ + (condhardstacktests(luaD_reallocCI(L, L->size_ci)), ++L->ci)) + + +int luaD_precall (lua_State *L, StkId func, int nresults) { + LClosure *cl; + ptrdiff_t funcr; + if (!ttisfunction(func)) /* `func' is not a function? */ + func = tryfuncTM(L, func); /* check the `function' tag method */ + funcr = savestack(L, func); + cl = &clvalue(func)->l; + L->ci->savedpc = L->savedpc; + if (!cl->isC) { /* Lua function? prepare its call */ + CallInfo *ci; + StkId st, base; + Proto *p = cl->p; + luaD_checkstack(L, p->maxstacksize); + func = restorestack(L, funcr); + if (!p->is_vararg) { /* no varargs? */ + base = func + 1; + if (L->top > base + p->numparams) + L->top = base + p->numparams; + } + else { /* vararg function */ + int nargs = cast_int(L->top - func) - 1; + base = adjust_varargs(L, p, nargs); + func = restorestack(L, funcr); /* previous call may change the stack */ + } + ci = inc_ci(L); /* now `enter' new function */ + ci->func = func; + L->base = ci->base = base; + ci->top = L->base + p->maxstacksize; + lua_assert(ci->top <= L->stack_last); + L->savedpc = p->code; /* starting point */ + ci->tailcalls = 0; + ci->nresults = nresults; + for (st = L->top; st < ci->top; st++) + setnilvalue(st); + L->top = ci->top; + if (L->hookmask & LUA_MASKCALL) { + L->savedpc++; /* hooks assume 'pc' is already incremented */ + luaD_callhook(L, LUA_HOOKCALL, -1); + L->savedpc--; /* correct 'pc' */ + } + return PCRLUA; + } + else { /* if is a C function, call it */ + CallInfo *ci; + int n; + luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ + ci = inc_ci(L); /* now `enter' new function */ + ci->func = restorestack(L, funcr); + L->base = ci->base = ci->func + 1; + ci->top = L->top + LUA_MINSTACK; + lua_assert(ci->top <= L->stack_last); + ci->nresults = nresults; + if (L->hookmask & LUA_MASKCALL) + luaD_callhook(L, LUA_HOOKCALL, -1); + lua_unlock(L); + n = (*curr_func(L)->c.f)(L); /* do the actual call */ + lua_lock(L); + if (n < 0) /* yielding? */ + return PCRYIELD; + else { + luaD_poscall(L, L->top - n); + return PCRC; + } + } +} + + +static StkId callrethooks (lua_State *L, StkId firstResult) { + ptrdiff_t fr = savestack(L, firstResult); /* next call may change stack */ + luaD_callhook(L, LUA_HOOKRET, -1); + if (f_isLua(L->ci)) { /* Lua function? */ + while ((L->hookmask & LUA_MASKRET) && L->ci->tailcalls--) /* tail calls */ + luaD_callhook(L, LUA_HOOKTAILRET, -1); + } + return restorestack(L, fr); +} + + +int luaD_poscall (lua_State *L, StkId firstResult) { + StkId res; + int wanted, i; + CallInfo *ci; + if (L->hookmask & LUA_MASKRET) + firstResult = callrethooks(L, firstResult); + ci = L->ci--; + res = ci->func; /* res == final position of 1st result */ + wanted = ci->nresults; + L->base = (ci - 1)->base; /* restore base */ + L->savedpc = (ci - 1)->savedpc; /* restore savedpc */ + /* move results to correct place */ + for (i = wanted; i != 0 && firstResult < L->top; i--) + setobjs2s(L, res++, firstResult++); + while (i-- > 0) + setnilvalue(res++); + L->top = res; + return (wanted - LUA_MULTRET); /* 0 iff wanted == LUA_MULTRET */ +} + + +/* +** Call a function (C or Lua). The function to be called is at *func. +** The arguments are on the stack, right after the function. +** When returns, all the results are on the stack, starting at the original +** function position. +*/ +void luaD_call (lua_State *L, StkId func, int nResults) { + if (++L->nCcalls >= LUAI_MAXCCALLS) { + if (L->nCcalls == LUAI_MAXCCALLS) + luaG_runerror(L, "C stack overflow"); + else if (L->nCcalls >= (LUAI_MAXCCALLS + (LUAI_MAXCCALLS>>3))) + luaD_throw(L, LUA_ERRERR); /* error while handing stack error */ + } + if (luaD_precall(L, func, nResults) == PCRLUA) /* is a Lua function? */ + luaV_execute(L, 1); /* call it */ + L->nCcalls--; + luaC_checkGC(L); +} + + +static void resume (lua_State *L, void *ud) { + StkId firstArg = cast(StkId, ud); + CallInfo *ci = L->ci; + if (L->status == 0) { /* start coroutine? */ + lua_assert(ci == L->base_ci && firstArg > L->base); + if (luaD_precall(L, firstArg - 1, LUA_MULTRET) != PCRLUA) + return; + } + else { /* resuming from previous yield */ + lua_assert(L->status == LUA_YIELD); + L->status = 0; + if (!f_isLua(ci)) { /* `common' yield? */ + /* finish interrupted execution of `OP_CALL' */ + lua_assert(GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_CALL || + GET_OPCODE(*((ci-1)->savedpc - 1)) == OP_TAILCALL); + if (luaD_poscall(L, firstArg)) /* complete it... */ + L->top = L->ci->top; /* and correct top if not multiple results */ + } + else /* yielded inside a hook: just continue its execution */ + L->base = L->ci->base; + } + luaV_execute(L, cast_int(L->ci - L->base_ci)); +} + + +static int resume_error (lua_State *L, const char *msg) { + L->top = L->ci->base; + setsvalue2s(L, L->top, luaS_new(L, msg)); + incr_top(L); + lua_unlock(L); + return LUA_ERRRUN; +} + + +LUA_API int lua_resume (lua_State *L, int nargs) { + int status; + lua_lock(L); + if (L->status != LUA_YIELD && (L->status != 0 || L->ci != L->base_ci)) + return resume_error(L, "cannot resume non-suspended coroutine"); + if (L->nCcalls >= LUAI_MAXCCALLS) + return resume_error(L, "C stack overflow"); + luai_userstateresume(L, nargs); + lua_assert(L->errfunc == 0); + L->baseCcalls = ++L->nCcalls; + status = luaD_rawrunprotected(L, resume, L->top - nargs); + if (status != 0) { /* error? */ + L->status = cast_byte(status); /* mark thread as `dead' */ + luaD_seterrorobj(L, status, L->top); + L->ci->top = L->top; + } + else { + lua_assert(L->nCcalls == L->baseCcalls); + status = L->status; + } + --L->nCcalls; + lua_unlock(L); + return status; +} + + +LUA_API int lua_yield (lua_State *L, int nresults) { + luai_userstateyield(L, nresults); + lua_lock(L); + if (L->nCcalls > L->baseCcalls) + luaG_runerror(L, "attempt to yield across metamethod/C-call boundary"); + L->base = L->top - nresults; /* protect stack slots below */ + L->status = LUA_YIELD; + lua_unlock(L); + return -1; +} + + +int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t old_top, ptrdiff_t ef) { + int status; + unsigned short oldnCcalls = L->nCcalls; + ptrdiff_t old_ci = saveci(L, L->ci); + lu_byte old_allowhooks = L->allowhook; + ptrdiff_t old_errfunc = L->errfunc; + L->errfunc = ef; + status = luaD_rawrunprotected(L, func, u); + if (status != 0) { /* an error occurred? */ + StkId oldtop = restorestack(L, old_top); + luaF_close(L, oldtop); /* close eventual pending closures */ + luaD_seterrorobj(L, status, oldtop); + L->nCcalls = oldnCcalls; + L->ci = restoreci(L, old_ci); + L->base = L->ci->base; + L->savedpc = L->ci->savedpc; + L->allowhook = old_allowhooks; + restore_stack_limit(L); + } + L->errfunc = old_errfunc; + return status; +} + + + +/* +** Execute a protected parser. +*/ +struct SParser { /* data to `f_parser' */ + ZIO *z; + Mbuffer buff; /* buffer to be used by the scanner */ + const char *name; +}; + +static void f_parser (lua_State *L, void *ud) { + int i; + Proto *tf; + Closure *cl; + struct SParser *p = cast(struct SParser *, ud); + int c = luaZ_lookahead(p->z); + luaC_checkGC(L); + tf = ((c == LUA_SIGNATURE[0]) ? luaU_undump : luaY_parser)(L, p->z, + &p->buff, p->name); + cl = luaF_newLclosure(L, tf->nups, hvalue(gt(L))); + cl->l.p = tf; + for (i = 0; i < tf->nups; i++) /* initialize eventual upvalues */ + cl->l.upvals[i] = luaF_newupval(L); + setclvalue(L, L->top, cl); + incr_top(L); +} + + +int luaD_protectedparser (lua_State *L, ZIO *z, const char *name) { + struct SParser p; + int status; + p.z = z; p.name = name; + luaZ_initbuffer(L, &p.buff); + status = luaD_pcall(L, f_parser, &p, savestack(L, L->top), L->errfunc); + luaZ_freebuffer(L, &p.buff); + return status; +} + + === added file 'lua/ldo.h' --- lua/ldo.h 1970-01-01 00:00:00 +0000 +++ lua/ldo.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,57 @@ +/* +** $Id: ldo.h,v 2.7.1.1 2007/12/27 13:02:25 roberto Exp $ +** Stack and Call structure of Lua +** See Copyright Notice in lua.h +*/ + +#ifndef ldo_h +#define ldo_h + + +#include "lobject.h" +#include "lstate.h" +#include "lzio.h" + + +#define luaD_checkstack(L,n) \ + if ((char *)L->stack_last - (char *)L->top <= (n)*(int)sizeof(TValue)) \ + luaD_growstack(L, n); \ + else condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); + + +#define incr_top(L) {luaD_checkstack(L,1); L->top++;} + +#define savestack(L,p) ((char *)(p) - (char *)L->stack) +#define restorestack(L,n) ((TValue *)((char *)L->stack + (n))) + +#define saveci(L,p) ((char *)(p) - (char *)L->base_ci) +#define restoreci(L,n) ((CallInfo *)((char *)L->base_ci + (n))) + + +/* results from luaD_precall */ +#define PCRLUA 0 /* initiated a call to a Lua function */ +#define PCRC 1 /* did a call to a C function */ +#define PCRYIELD 2 /* C funtion yielded */ + + +/* type of protected functions, to be ran by `runprotected' */ +typedef void (*Pfunc) (lua_State *L, void *ud); + +LUAI_FUNC int luaD_protectedparser (lua_State *L, ZIO *z, const char *name); +LUAI_FUNC void luaD_callhook (lua_State *L, int event, int line); +LUAI_FUNC int luaD_precall (lua_State *L, StkId func, int nresults); +LUAI_FUNC void luaD_call (lua_State *L, StkId func, int nResults); +LUAI_FUNC int luaD_pcall (lua_State *L, Pfunc func, void *u, + ptrdiff_t oldtop, ptrdiff_t ef); +LUAI_FUNC int luaD_poscall (lua_State *L, StkId firstResult); +LUAI_FUNC void luaD_reallocCI (lua_State *L, int newsize); +LUAI_FUNC void luaD_reallocstack (lua_State *L, int newsize); +LUAI_FUNC void luaD_growstack (lua_State *L, int n); + +LUAI_FUNC void luaD_throw (lua_State *L, int errcode); +LUAI_FUNC int luaD_rawrunprotected (lua_State *L, Pfunc f, void *ud); + +LUAI_FUNC void luaD_seterrorobj (lua_State *L, int errcode, StkId oldtop); + +#endif + === added file 'lua/ldump.c' --- lua/ldump.c 1970-01-01 00:00:00 +0000 +++ lua/ldump.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,166 @@ +/* +** $Id: ldump.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ +** save precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#define ldump_c +#define LUA_CORE + +#include "lua.h" + +#include "lobject.h" +#include "lstate.h" +#include "lundump.h" + +typedef struct { + lua_State* L; + lua_Writer writer; + void* data; + int strip; + int status; +} DumpState; + +#define DumpMem(b,n,size,D) DumpBlock(b,(n)*(size),D) +#define DumpVar(x,D) DumpMem(&x,1,sizeof(x),D) + +static void DumpBlock(const void* b, size_t size, DumpState* D) +{ + if (D->status==0) + { + lua_unlock(D->L); + D->status=(*D->writer)(D->L,b,size,D->data); + lua_lock(D->L); + } +} + +static void DumpChar(int y, DumpState* D) +{ + char x=(char)y; + DumpVar(x,D); +} + +static void DumpInt(int x, DumpState* D) +{ + DumpVar(x,D); +} + +static void DumpNumber(lua_Number x, DumpState* D) +{ + DumpVar(x,D); +} + +static void DumpVector(const void* b, int n, size_t size, DumpState* D) +{ + DumpInt(n,D); + DumpMem(b,n,size,D); +} + +static void DumpString(const TString* s, DumpState* D) +{ + if (s==NULL || getstr(s)==NULL) + { + size_t size=0; + DumpVar(size,D); + } + else + { + size_t size=s->tsv.len+1; /* include trailing '\0' */ + DumpVar(size,D); + DumpBlock(getstr(s),size,D); + } +} + +#define DumpCode(f,D) DumpVector(f->code,f->sizecode,sizeof(Instruction),D) + +static void DumpFunction(const Proto* f, const TString* p, DumpState* D); + +static void DumpConstants(const Proto* f, DumpState* D) +{ + int i,n=f->sizek; + DumpInt(n,D); + for (i=0; ik[i]; + DumpChar(ttype(o),D); + switch (ttype(o)) + { + case LUA_TNIL: + break; + case LUA_TBOOLEAN: + DumpChar(bvalue(o),D); + break; + case LUA_TNUMBER: + DumpNumber(nvalue(o),D); + break; + case LUA_TSTRING: + DumpString(rawtsvalue(o),D); + break; + default: + lua_assert(0); /* cannot happen */ + break; + } + } + n=f->sizep; + DumpInt(n,D); + for (i=0; ip[i],f->source,D); +} + +static void DumpDebug(const Proto* f, DumpState* D) +{ + int i,n; + n= (D->strip) ? 0 : f->sizelineinfo; + DumpVector(f->lineinfo,n,sizeof(int),D); + n= (D->strip) ? 0 : f->sizelocvars; + DumpInt(n,D); + for (i=0; ilocvars[i].varname,D); + DumpInt(f->locvars[i].startpc,D); + DumpInt(f->locvars[i].endpc,D); + } + n= (D->strip) ? 0 : f->sizeupvalues; + DumpInt(n,D); + for (i=0; iupvalues[i],D); +} + +static void DumpFunction(const Proto* f, const TString* p, DumpState* D) +{ + DumpString((f->source==p || D->strip) ? NULL : f->source,D); + DumpInt(f->linedefined,D); + DumpInt(f->lastlinedefined,D); + DumpChar(f->nups,D); + DumpChar(f->numparams,D); + DumpChar(f->is_vararg,D); + DumpChar(f->maxstacksize,D); + DumpCode(f,D); + DumpConstants(f,D); + DumpDebug(f,D); +} + +static void DumpHeader(DumpState* D) +{ + char h[LUAC_HEADERSIZE]; + luaU_header(h); + DumpBlock(h,LUAC_HEADERSIZE,D); +} + +/* +** dump Lua function as precompiled chunk +*/ +int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip) +{ + DumpState D; + D.L=L; + D.writer=w; + D.data=data; + D.strip=strip; + D.status=0; + DumpHeader(&D); + DumpFunction(f,NULL,&D); + return D.status; +} === added file 'lua/lfunc.c' --- lua/lfunc.c 1970-01-01 00:00:00 +0000 +++ lua/lfunc.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,176 @@ +/* +** $Id: lfunc.c,v 2.12.1.2 2007/12/28 14:58:43 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#define lfunc_c +#define LUA_CORE + +#include "lua.h" + +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e) { + Closure *c = cast(Closure *, luaM_malloc(L, sizeCclosure(nelems))); + luaC_link(L, obj2gco(c), LUA_TFUNCTION); + c->c.isC = 1; + c->c.env = e; + c->c.nupvalues = cast_byte(nelems); + return c; +} + + +Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e) { + Closure *c = cast(Closure *, luaM_malloc(L, sizeLclosure(nelems))); + luaC_link(L, obj2gco(c), LUA_TFUNCTION); + c->l.isC = 0; + c->l.env = e; + c->l.nupvalues = cast_byte(nelems); + while (nelems--) c->l.upvals[nelems] = NULL; + return c; +} + + +UpVal *luaF_newupval (lua_State *L) { + UpVal *uv = luaM_new(L, UpVal); + luaC_link(L, obj2gco(uv), LUA_TUPVAL); + uv->v = &uv->u.value; + setnilvalue(uv->v); + return uv; +} + + +UpVal *luaF_findupval (lua_State *L, StkId level) { + global_State *g = G(L); + GCObject **pp = &L->openupval; + UpVal *p; + UpVal *uv; + while (*pp != NULL && (p = ngcotouv(*pp))->v >= level) { + lua_assert(p->v != &p->u.value); + if (p->v == level) { /* found a corresponding upvalue? */ + if (isdead(g, obj2gco(p))) /* is it dead? */ + changewhite(obj2gco(p)); /* ressurect it */ + return p; + } + pp = &p->next; + } + uv = luaM_new(L, UpVal); /* not found: create a new one */ + uv->tt = LUA_TUPVAL; + uv->marked = luaC_white(g); + uv->v = level; /* current value lives in the stack */ + uv->next = *pp; /* chain it in the proper position */ + *pp = obj2gco(uv); + uv->u.l.prev = &g->uvhead; /* double link it in `uvhead' list */ + uv->u.l.next = g->uvhead.u.l.next; + uv->u.l.next->u.l.prev = uv; + g->uvhead.u.l.next = uv; + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + return uv; +} + + +static void unlinkupval (UpVal *uv) { + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + uv->u.l.next->u.l.prev = uv->u.l.prev; /* remove from `uvhead' list */ + uv->u.l.prev->u.l.next = uv->u.l.next; +} + + +void luaF_freeupval (lua_State *L, UpVal *uv) { + if (uv->v != &uv->u.value) /* is it open? */ + unlinkupval(uv); /* remove from open list */ + luaM_free(L, uv); /* free upvalue */ +} + + +void luaF_close (lua_State *L, StkId level) { + UpVal *uv; + global_State *g = G(L); + while (L->openupval != NULL && (uv = ngcotouv(L->openupval))->v >= level) { + GCObject *o = obj2gco(uv); + lua_assert(!isblack(o) && uv->v != &uv->u.value); + L->openupval = uv->next; /* remove from `open' list */ + if (isdead(g, o)) + luaF_freeupval(L, uv); /* free upvalue */ + else { + unlinkupval(uv); + setobj(L, &uv->u.value, uv->v); + uv->v = &uv->u.value; /* now current value lives here */ + luaC_linkupval(L, uv); /* link upvalue into `gcroot' list */ + } + } +} + + +Proto *luaF_newproto (lua_State *L) { + Proto *f = luaM_new(L, Proto); + luaC_link(L, obj2gco(f), LUA_TPROTO); + f->k = NULL; + f->sizek = 0; + f->p = NULL; + f->sizep = 0; + f->code = NULL; + f->sizecode = 0; + f->sizelineinfo = 0; + f->sizeupvalues = 0; + f->nups = 0; + f->upvalues = NULL; + f->numparams = 0; + f->is_vararg = 0; + f->maxstacksize = 0; + f->lineinfo = NULL; + f->sizelocvars = 0; + f->locvars = NULL; + f->linedefined = 0; + f->lastlinedefined = 0; + f->source = NULL; + return f; +} + + +void luaF_freeproto (lua_State *L, Proto *f) { + luaM_freearray(L, f->code, f->sizecode, Instruction); + luaM_freearray(L, f->p, f->sizep, Proto *); + luaM_freearray(L, f->k, f->sizek, TValue); + luaM_freearray(L, f->lineinfo, f->sizelineinfo, int); + luaM_freearray(L, f->locvars, f->sizelocvars, struct LocVar); + luaM_freearray(L, f->upvalues, f->sizeupvalues, TString *); + luaM_free(L, f); +} + + +void luaF_freeclosure (lua_State *L, Closure *c) { + int size = (c->c.isC) ? sizeCclosure(c->c.nupvalues) : + sizeLclosure(c->l.nupvalues); + luaM_freemem(L, c, size); +} + + +/* +** Look for n-th local variable at line `line' in function `func'. +** Returns NULL if not found. +*/ +const char *luaF_getlocalname (const Proto *f, int local_number, int pc) { + int i; + for (i = 0; isizelocvars && f->locvars[i].startpc <= pc; i++) { + if (pc < f->locvars[i].endpc) { /* is variable active? */ + local_number--; + if (local_number == 0) + return getstr(f->locvars[i].varname); + } + } + return NULL; /* not found */ +} + === added file 'lua/lfunc.h' --- lua/lfunc.h 1970-01-01 00:00:00 +0000 +++ lua/lfunc.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,34 @@ +/* +** $Id: lfunc.h,v 2.4.1.1 2007/12/27 13:02:25 roberto Exp $ +** Auxiliary functions to manipulate prototypes and closures +** See Copyright Notice in lua.h +*/ + +#ifndef lfunc_h +#define lfunc_h + + +#include "lobject.h" + + +#define sizeCclosure(n) (cast(int, sizeof(CClosure)) + \ + cast(int, sizeof(TValue)*((n)-1))) + +#define sizeLclosure(n) (cast(int, sizeof(LClosure)) + \ + cast(int, sizeof(TValue *)*((n)-1))) + + +LUAI_FUNC Proto *luaF_newproto (lua_State *L); +LUAI_FUNC Closure *luaF_newCclosure (lua_State *L, int nelems, Table *e); +LUAI_FUNC Closure *luaF_newLclosure (lua_State *L, int nelems, Table *e); +LUAI_FUNC UpVal *luaF_newupval (lua_State *L); +LUAI_FUNC UpVal *luaF_findupval (lua_State *L, StkId level); +LUAI_FUNC void luaF_close (lua_State *L, StkId level); +LUAI_FUNC void luaF_freeproto (lua_State *L, Proto *f); +LUAI_FUNC void luaF_freeclosure (lua_State *L, Closure *c); +LUAI_FUNC void luaF_freeupval (lua_State *L, UpVal *uv); +LUAI_FUNC const char *luaF_getlocalname (const Proto *func, int local_number, + int pc); + + +#endif === added file 'lua/lgc.c' --- lua/lgc.c 1970-01-01 00:00:00 +0000 +++ lua/lgc.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,713 @@ +/* +** $Id: lgc.c,v 2.38.1.1 2007/12/27 13:02:25 roberto Exp $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#define lgc_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + +#define GCSTEPSIZE 1024u +#define GCSWEEPMAX 40 +#define GCSWEEPCOST 10 +#define GCFINALIZECOST 100 + + +#define maskmarks cast_byte(~(bitmask(BLACKBIT)|WHITEBITS)) + +#define makewhite(g,x) \ + ((x)->gch.marked = cast_byte(((x)->gch.marked & maskmarks) | luaC_white(g))) + +#define white2gray(x) reset2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) +#define black2gray(x) resetbit((x)->gch.marked, BLACKBIT) + +#define stringmark(s) reset2bits((s)->tsv.marked, WHITE0BIT, WHITE1BIT) + + +#define isfinalized(u) testbit((u)->marked, FINALIZEDBIT) +#define markfinalized(u) l_setbit((u)->marked, FINALIZEDBIT) + + +#define KEYWEAK bitmask(KEYWEAKBIT) +#define VALUEWEAK bitmask(VALUEWEAKBIT) + + + +#define markvalue(g,o) { checkconsistency(o); \ + if (iscollectable(o) && iswhite(gcvalue(o))) reallymarkobject(g,gcvalue(o)); } + +#define markobject(g,t) { if (iswhite(obj2gco(t))) \ + reallymarkobject(g, obj2gco(t)); } + + +#define setthreshold(g) (g->GCthreshold = (g->estimate/100) * g->gcpause) + + +static void removeentry (Node *n) { + lua_assert(ttisnil(gval(n))); + if (iscollectable(gkey(n))) + setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */ +} + + +static void reallymarkobject (global_State *g, GCObject *o) { + lua_assert(iswhite(o) && !isdead(g, o)); + white2gray(o); + switch (o->gch.tt) { + case LUA_TSTRING: { + return; + } + case LUA_TUSERDATA: { + Table *mt = gco2u(o)->metatable; + gray2black(o); /* udata are never gray */ + if (mt) markobject(g, mt); + markobject(g, gco2u(o)->env); + return; + } + case LUA_TUPVAL: { + UpVal *uv = gco2uv(o); + markvalue(g, uv->v); + if (uv->v == &uv->u.value) /* closed? */ + gray2black(o); /* open upvalues are never black */ + return; + } + case LUA_TFUNCTION: { + gco2cl(o)->c.gclist = g->gray; + g->gray = o; + break; + } + case LUA_TTABLE: { + gco2h(o)->gclist = g->gray; + g->gray = o; + break; + } + case LUA_TTHREAD: { + gco2th(o)->gclist = g->gray; + g->gray = o; + break; + } + case LUA_TPROTO: { + gco2p(o)->gclist = g->gray; + g->gray = o; + break; + } + default: lua_assert(0); + } +} + + +static void marktmu (global_State *g) { + GCObject *u = g->tmudata; + if (u) { + do { + u = u->gch.next; + makewhite(g, u); /* may be marked, if left from previous GC */ + reallymarkobject(g, u); + } while (u != g->tmudata); + } +} + + +/* move `dead' udata that need finalization to list `tmudata' */ +size_t luaC_separateudata (lua_State *L, int all) { + global_State *g = G(L); + size_t deadmem = 0; + GCObject **p = &g->mainthread->next; + GCObject *curr; + while ((curr = *p) != NULL) { + if (!(iswhite(curr) || all) || isfinalized(gco2u(curr))) + p = &curr->gch.next; /* don't bother with them */ + else if (fasttm(L, gco2u(curr)->metatable, TM_GC) == NULL) { + markfinalized(gco2u(curr)); /* don't need finalization */ + p = &curr->gch.next; + } + else { /* must call its gc method */ + deadmem += sizeudata(gco2u(curr)); + markfinalized(gco2u(curr)); + *p = curr->gch.next; + /* link `curr' at the end of `tmudata' list */ + if (g->tmudata == NULL) /* list is empty? */ + g->tmudata = curr->gch.next = curr; /* creates a circular list */ + else { + curr->gch.next = g->tmudata->gch.next; + g->tmudata->gch.next = curr; + g->tmudata = curr; + } + } + } + return deadmem; +} + + +static int traversetable (global_State *g, Table *h) { + int i; + int weakkey = 0; + int weakvalue = 0; + const TValue *mode; + if (h->metatable) + markobject(g, h->metatable); + mode = gfasttm(g, h->metatable, TM_MODE); + if (mode && ttisstring(mode)) { /* is there a weak mode? */ + weakkey = (strchr(svalue(mode), 'k') != NULL); + weakvalue = (strchr(svalue(mode), 'v') != NULL); + if (weakkey || weakvalue) { /* is really weak? */ + h->marked &= ~(KEYWEAK | VALUEWEAK); /* clear bits */ + h->marked |= cast_byte((weakkey << KEYWEAKBIT) | + (weakvalue << VALUEWEAKBIT)); + h->gclist = g->weak; /* must be cleared after GC, ... */ + g->weak = obj2gco(h); /* ... so put in the appropriate list */ + } + } + if (weakkey && weakvalue) return 1; + if (!weakvalue) { + i = h->sizearray; + while (i--) + markvalue(g, &h->array[i]); + } + i = sizenode(h); + while (i--) { + Node *n = gnode(h, i); + lua_assert(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); + if (ttisnil(gval(n))) + removeentry(n); /* remove empty entries */ + else { + lua_assert(!ttisnil(gkey(n))); + if (!weakkey) markvalue(g, gkey(n)); + if (!weakvalue) markvalue(g, gval(n)); + } + } + return weakkey || weakvalue; +} + + +/* +** All marks are conditional because a GC may happen while the +** prototype is still being created +*/ +static void traverseproto (global_State *g, Proto *f) { + int i; + if (f->source) stringmark(f->source); + for (i=0; isizek; i++) /* mark literals */ + markvalue(g, &f->k[i]); + for (i=0; isizeupvalues; i++) { /* mark upvalue names */ + if (f->upvalues[i]) + stringmark(f->upvalues[i]); + } + for (i=0; isizep; i++) { /* mark nested protos */ + if (f->p[i]) + markobject(g, f->p[i]); + } + for (i=0; isizelocvars; i++) { /* mark local-variable names */ + if (f->locvars[i].varname) + stringmark(f->locvars[i].varname); + } +} + + + +static void traverseclosure (global_State *g, Closure *cl) { + markobject(g, cl->c.env); + if (cl->c.isC) { + int i; + for (i=0; ic.nupvalues; i++) /* mark its upvalues */ + markvalue(g, &cl->c.upvalue[i]); + } + else { + int i; + lua_assert(cl->l.nupvalues == cl->l.p->nups); + markobject(g, cl->l.p); + for (i=0; il.nupvalues; i++) /* mark its upvalues */ + markobject(g, cl->l.upvals[i]); + } +} + + +static void checkstacksizes (lua_State *L, StkId max) { + int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */ + int s_used = cast_int(max - L->stack); /* part of stack in use */ + if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */ + return; /* do not touch the stacks */ + if (4*ci_used < L->size_ci && 2*BASIC_CI_SIZE < L->size_ci) + luaD_reallocCI(L, L->size_ci/2); /* still big enough... */ + condhardstacktests(luaD_reallocCI(L, ci_used + 1)); + if (4*s_used < L->stacksize && + 2*(BASIC_STACK_SIZE+EXTRA_STACK) < L->stacksize) + luaD_reallocstack(L, L->stacksize/2); /* still big enough... */ + condhardstacktests(luaD_reallocstack(L, s_used)); +} + + +static void traversestack (global_State *g, lua_State *l) { + StkId o, lim; + CallInfo *ci; + markvalue(g, gt(l)); + lim = l->top; + for (ci = l->base_ci; ci <= l->ci; ci++) { + lua_assert(ci->top <= l->stack_last); + if (lim < ci->top) lim = ci->top; + } + for (o = l->stack; o < l->top; o++) + markvalue(g, o); + for (; o <= lim; o++) + setnilvalue(o); + checkstacksizes(l, lim); +} + + +/* +** traverse one gray object, turning it to black. +** Returns `quantity' traversed. +*/ +static l_mem propagatemark (global_State *g) { + GCObject *o = g->gray; + lua_assert(isgray(o)); + gray2black(o); + switch (o->gch.tt) { + case LUA_TTABLE: { + Table *h = gco2h(o); + g->gray = h->gclist; + if (traversetable(g, h)) /* table is weak? */ + black2gray(o); /* keep it gray */ + return sizeof(Table) + sizeof(TValue) * h->sizearray + + sizeof(Node) * sizenode(h); + } + case LUA_TFUNCTION: { + Closure *cl = gco2cl(o); + g->gray = cl->c.gclist; + traverseclosure(g, cl); + return (cl->c.isC) ? sizeCclosure(cl->c.nupvalues) : + sizeLclosure(cl->l.nupvalues); + } + case LUA_TTHREAD: { + lua_State *th = gco2th(o); + g->gray = th->gclist; + th->gclist = g->grayagain; + g->grayagain = o; + black2gray(o); + traversestack(g, th); + return sizeof(lua_State) + sizeof(TValue) * th->stacksize + + sizeof(CallInfo) * th->size_ci; + } + case LUA_TPROTO: { + Proto *p = gco2p(o); + g->gray = p->gclist; + traverseproto(g, p); + return sizeof(Proto) + sizeof(Instruction) * p->sizecode + + sizeof(Proto *) * p->sizep + + sizeof(TValue) * p->sizek + + sizeof(int) * p->sizelineinfo + + sizeof(LocVar) * p->sizelocvars + + sizeof(TString *) * p->sizeupvalues; + } + default: lua_assert(0); return 0; + } +} + + +static size_t propagateall (global_State *g) { + size_t m = 0; + while (g->gray) m += propagatemark(g); + return m; +} + + +/* +** The next function tells whether a key or value can be cleared from +** a weak table. Non-collectable objects are never removed from weak +** tables. Strings behave as `values', so are never removed too. for +** other objects: if really collected, cannot keep them; for userdata +** being finalized, keep them in keys, but not in values +*/ +static int iscleared (const TValue *o, int iskey) { + if (!iscollectable(o)) return 0; + if (ttisstring(o)) { + stringmark(rawtsvalue(o)); /* strings are `values', so are never weak */ + return 0; + } + return iswhite(gcvalue(o)) || + (ttisuserdata(o) && (!iskey && isfinalized(uvalue(o)))); +} + + +/* +** clear collected entries from weaktables +*/ +static void cleartable (GCObject *l) { + while (l) { + Table *h = gco2h(l); + int i = h->sizearray; + lua_assert(testbit(h->marked, VALUEWEAKBIT) || + testbit(h->marked, KEYWEAKBIT)); + if (testbit(h->marked, VALUEWEAKBIT)) { + while (i--) { + TValue *o = &h->array[i]; + if (iscleared(o, 0)) /* value was collected? */ + setnilvalue(o); /* remove value */ + } + } + i = sizenode(h); + while (i--) { + Node *n = gnode(h, i); + if (!ttisnil(gval(n)) && /* non-empty entry? */ + (iscleared(key2tval(n), 1) || iscleared(gval(n), 0))) { + setnilvalue(gval(n)); /* remove value ... */ + removeentry(n); /* remove entry from table */ + } + } + l = h->gclist; + } +} + + +static void freeobj (lua_State *L, GCObject *o) { + switch (o->gch.tt) { + case LUA_TPROTO: luaF_freeproto(L, gco2p(o)); break; + case LUA_TFUNCTION: luaF_freeclosure(L, gco2cl(o)); break; + case LUA_TUPVAL: luaF_freeupval(L, gco2uv(o)); break; + case LUA_TTABLE: luaH_free(L, gco2h(o)); break; + case LUA_TTHREAD: { + lua_assert(gco2th(o) != L && gco2th(o) != G(L)->mainthread); + luaE_freethread(L, gco2th(o)); + break; + } + case LUA_TSTRING: { + G(L)->strt.nuse--; + luaM_freemem(L, o, sizestring(gco2ts(o))); + break; + } + case LUA_TUSERDATA: { + luaM_freemem(L, o, sizeudata(gco2u(o))); + break; + } + default: lua_assert(0); + } +} + + + +#define sweepwholelist(L,p) sweeplist(L,p,MAX_LUMEM) + + +static GCObject **sweeplist (lua_State *L, GCObject **p, lu_mem count) { + GCObject *curr; + global_State *g = G(L); + int deadmask = otherwhite(g); + while ((curr = *p) != NULL && count-- > 0) { + if (curr->gch.tt == LUA_TTHREAD) /* sweep open upvalues of each thread */ + sweepwholelist(L, &gco2th(curr)->openupval); + if ((curr->gch.marked ^ WHITEBITS) & deadmask) { /* not dead? */ + lua_assert(!isdead(g, curr) || testbit(curr->gch.marked, FIXEDBIT)); + makewhite(g, curr); /* make it white (for next cycle) */ + p = &curr->gch.next; + } + else { /* must erase `curr' */ + lua_assert(isdead(g, curr) || deadmask == bitmask(SFIXEDBIT)); + *p = curr->gch.next; + if (curr == g->rootgc) /* is the first element of the list? */ + g->rootgc = curr->gch.next; /* adjust first */ + freeobj(L, curr); + } + } + return p; +} + + +static void checkSizes (lua_State *L) { + global_State *g = G(L); + /* check size of string hash */ + if (g->strt.nuse < cast(lu_int32, g->strt.size/4) && + g->strt.size > MINSTRTABSIZE*2) + luaS_resize(L, g->strt.size/2); /* table is too big */ + /* check size of buffer */ + if (luaZ_sizebuffer(&g->buff) > LUA_MINBUFFER*2) { /* buffer too big? */ + size_t newsize = luaZ_sizebuffer(&g->buff) / 2; + luaZ_resizebuffer(L, &g->buff, newsize); + } +} + + +static void GCTM (lua_State *L) { + global_State *g = G(L); + GCObject *o = g->tmudata->gch.next; /* get first element */ + Udata *udata = rawgco2u(o); + const TValue *tm; + /* remove udata from `tmudata' */ + if (o == g->tmudata) /* last element? */ + g->tmudata = NULL; + else + g->tmudata->gch.next = udata->uv.next; + udata->uv.next = g->mainthread->next; /* return it to `root' list */ + g->mainthread->next = o; + makewhite(g, o); + tm = fasttm(L, udata->uv.metatable, TM_GC); + if (tm != NULL) { + lu_byte oldah = L->allowhook; + lu_mem oldt = g->GCthreshold; + L->allowhook = 0; /* stop debug hooks during GC tag method */ + g->GCthreshold = 2*g->totalbytes; /* avoid GC steps */ + setobj2s(L, L->top, tm); + setuvalue(L, L->top+1, udata); + L->top += 2; + luaD_call(L, L->top - 2, 0); + L->allowhook = oldah; /* restore hooks */ + g->GCthreshold = oldt; /* restore threshold */ + } +} + + +/* +** Call all GC tag methods +*/ +void luaC_callGCTM (lua_State *L) { + while (G(L)->tmudata) + GCTM(L); +} + + +void luaC_freeall (lua_State *L) { + global_State *g = G(L); + int i; + g->currentwhite = WHITEBITS | bitmask(SFIXEDBIT); /* mask to collect all elements */ + sweepwholelist(L, &g->rootgc); + for (i = 0; i < g->strt.size; i++) /* free all string lists */ + sweepwholelist(L, &g->strt.hash[i]); +} + + +static void markmt (global_State *g) { + int i; + for (i=0; imt[i]) markobject(g, g->mt[i]); +} + + +/* mark root set */ +static void markroot (lua_State *L) { + global_State *g = G(L); + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + markobject(g, g->mainthread); + /* make global table be traversed before main stack */ + markvalue(g, gt(g->mainthread)); + markvalue(g, registry(L)); + markmt(g); + g->gcstate = GCSpropagate; +} + + +static void remarkupvals (global_State *g) { + UpVal *uv; + for (uv = g->uvhead.u.l.next; uv != &g->uvhead; uv = uv->u.l.next) { + lua_assert(uv->u.l.next->u.l.prev == uv && uv->u.l.prev->u.l.next == uv); + if (isgray(obj2gco(uv))) + markvalue(g, uv->v); + } +} + + +static void atomic (lua_State *L) { + global_State *g = G(L); + size_t udsize; /* total size of userdata to be finalized */ + /* remark occasional upvalues of (maybe) dead threads */ + remarkupvals(g); + /* traverse objects cautch by write barrier and by 'remarkupvals' */ + propagateall(g); + /* remark weak tables */ + g->gray = g->weak; + g->weak = NULL; + lua_assert(!iswhite(obj2gco(g->mainthread))); + markobject(g, L); /* mark running thread */ + markmt(g); /* mark basic metatables (again) */ + propagateall(g); + /* remark gray again */ + g->gray = g->grayagain; + g->grayagain = NULL; + propagateall(g); + udsize = luaC_separateudata(L, 0); /* separate userdata to be finalized */ + marktmu(g); /* mark `preserved' userdata */ + udsize += propagateall(g); /* remark, to propagate `preserveness' */ + cleartable(g->weak); /* remove collected objects from weak tables */ + /* flip current white */ + g->currentwhite = cast_byte(otherwhite(g)); + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + g->gcstate = GCSsweepstring; + g->estimate = g->totalbytes - udsize; /* first estimate */ +} + + +static l_mem singlestep (lua_State *L) { + global_State *g = G(L); + /*lua_checkmemory(L);*/ + switch (g->gcstate) { + case GCSpause: { + markroot(L); /* start a new collection */ + return 0; + } + case GCSpropagate: { + if (g->gray) + return propagatemark(g); + else { /* no more `gray' objects */ + atomic(L); /* finish mark phase */ + return 0; + } + } + case GCSsweepstring: { + lu_mem old = g->totalbytes; + sweepwholelist(L, &g->strt.hash[g->sweepstrgc++]); + if (g->sweepstrgc >= g->strt.size) /* nothing more to sweep? */ + g->gcstate = GCSsweep; /* end sweep-string phase */ + lua_assert(old >= g->totalbytes); + g->estimate -= old - g->totalbytes; + return GCSWEEPCOST; + } + case GCSsweep: { + lu_mem old = g->totalbytes; + g->sweepgc = sweeplist(L, g->sweepgc, GCSWEEPMAX); + if (*g->sweepgc == NULL) { /* nothing more to sweep? */ + checkSizes(L); + g->gcstate = GCSfinalize; /* end sweep phase */ + } + lua_assert(old >= g->totalbytes); + g->estimate -= old - g->totalbytes; + return GCSWEEPMAX*GCSWEEPCOST; + } + case GCSfinalize: { + if (g->tmudata) { + GCTM(L); + if (g->estimate > GCFINALIZECOST) + g->estimate -= GCFINALIZECOST; + return GCFINALIZECOST; + } + else { + g->gcstate = GCSpause; /* end collection */ + g->gcdept = 0; + return 0; + } + } + default: lua_assert(0); return 0; + } +} + + +void luaC_step (lua_State *L) { + global_State *g = G(L); + l_mem lim = (GCSTEPSIZE/100) * g->gcstepmul; + if (lim == 0) + lim = (MAX_LUMEM-1)/2; /* no limit */ + g->gcdept += g->totalbytes - g->GCthreshold; + do { + lim -= singlestep(L); + if (g->gcstate == GCSpause) + break; + } while (lim > 0); + if (g->gcstate != GCSpause) { + if (g->gcdept < GCSTEPSIZE) + g->GCthreshold = g->totalbytes + GCSTEPSIZE; /* - lim/g->gcstepmul;*/ + else { + g->gcdept -= GCSTEPSIZE; + g->GCthreshold = g->totalbytes; + } + } + else { + lua_assert(g->totalbytes >= g->estimate); + setthreshold(g); + } +} + + +void luaC_fullgc (lua_State *L) { + global_State *g = G(L); + if (g->gcstate <= GCSpropagate) { + /* reset sweep marks to sweep all elements (returning them to white) */ + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + /* reset other collector lists */ + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + g->gcstate = GCSsweepstring; + } + lua_assert(g->gcstate != GCSpause && g->gcstate != GCSpropagate); + /* finish any pending sweep phase */ + while (g->gcstate != GCSfinalize) { + lua_assert(g->gcstate == GCSsweepstring || g->gcstate == GCSsweep); + singlestep(L); + } + markroot(L); + while (g->gcstate != GCSpause) { + singlestep(L); + } + setthreshold(g); +} + + +void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v) { + global_State *g = G(L); + lua_assert(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + lua_assert(ttype(&o->gch) != LUA_TTABLE); + /* must keep invariant? */ + if (g->gcstate == GCSpropagate) + reallymarkobject(g, v); /* restore invariant */ + else /* don't mind */ + makewhite(g, o); /* mark as white just to avoid other barriers */ +} + + +void luaC_barrierback (lua_State *L, Table *t) { + global_State *g = G(L); + GCObject *o = obj2gco(t); + lua_assert(isblack(o) && !isdead(g, o)); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + black2gray(o); /* make table gray (again) */ + t->gclist = g->grayagain; + g->grayagain = o; +} + + +void luaC_link (lua_State *L, GCObject *o, lu_byte tt) { + global_State *g = G(L); + o->gch.next = g->rootgc; + g->rootgc = o; + o->gch.marked = luaC_white(g); + o->gch.tt = tt; +} + + +void luaC_linkupval (lua_State *L, UpVal *uv) { + global_State *g = G(L); + GCObject *o = obj2gco(uv); + o->gch.next = g->rootgc; /* link upvalue into `rootgc' list */ + g->rootgc = o; + if (isgray(o)) { + if (g->gcstate == GCSpropagate) { + gray2black(o); /* closed upvalues need barrier */ + luaC_barrier(L, uv, uv->v); + } + else { /* sweep phase: sweep it (turning it into white) */ + makewhite(g, o); + lua_assert(g->gcstate != GCSfinalize && g->gcstate != GCSpause); + } + } +} + === added file 'lua/lgc.h' --- lua/lgc.h 1970-01-01 00:00:00 +0000 +++ lua/lgc.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,110 @@ +/* +** $Id: lgc.h,v 2.15.1.1 2007/12/27 13:02:25 roberto Exp $ +** Garbage Collector +** See Copyright Notice in lua.h +*/ + +#ifndef lgc_h +#define lgc_h + + +#include "lobject.h" + + +/* +** Possible states of the Garbage Collector +*/ +#define GCSpause 0 +#define GCSpropagate 1 +#define GCSsweepstring 2 +#define GCSsweep 3 +#define GCSfinalize 4 + + +/* +** some userful bit tricks +*/ +#define resetbits(x,m) ((x) &= cast(lu_byte, ~(m))) +#define setbits(x,m) ((x) |= (m)) +#define testbits(x,m) ((x) & (m)) +#define bitmask(b) (1<<(b)) +#define bit2mask(b1,b2) (bitmask(b1) | bitmask(b2)) +#define l_setbit(x,b) setbits(x, bitmask(b)) +#define resetbit(x,b) resetbits(x, bitmask(b)) +#define testbit(x,b) testbits(x, bitmask(b)) +#define set2bits(x,b1,b2) setbits(x, (bit2mask(b1, b2))) +#define reset2bits(x,b1,b2) resetbits(x, (bit2mask(b1, b2))) +#define test2bits(x,b1,b2) testbits(x, (bit2mask(b1, b2))) + + + +/* +** Layout for bit use in `marked' field: +** bit 0 - object is white (type 0) +** bit 1 - object is white (type 1) +** bit 2 - object is black +** bit 3 - for userdata: has been finalized +** bit 3 - for tables: has weak keys +** bit 4 - for tables: has weak values +** bit 5 - object is fixed (should not be collected) +** bit 6 - object is "super" fixed (only the main thread) +*/ + + +#define WHITE0BIT 0 +#define WHITE1BIT 1 +#define BLACKBIT 2 +#define FINALIZEDBIT 3 +#define KEYWEAKBIT 3 +#define VALUEWEAKBIT 4 +#define FIXEDBIT 5 +#define SFIXEDBIT 6 +#define WHITEBITS bit2mask(WHITE0BIT, WHITE1BIT) + + +#define iswhite(x) test2bits((x)->gch.marked, WHITE0BIT, WHITE1BIT) +#define isblack(x) testbit((x)->gch.marked, BLACKBIT) +#define isgray(x) (!isblack(x) && !iswhite(x)) + +#define otherwhite(g) (g->currentwhite ^ WHITEBITS) +#define isdead(g,v) ((v)->gch.marked & otherwhite(g) & WHITEBITS) + +#define changewhite(x) ((x)->gch.marked ^= WHITEBITS) +#define gray2black(x) l_setbit((x)->gch.marked, BLACKBIT) + +#define valiswhite(x) (iscollectable(x) && iswhite(gcvalue(x))) + +#define luaC_white(g) cast(lu_byte, (g)->currentwhite & WHITEBITS) + + +#define luaC_checkGC(L) { \ + condhardstacktests(luaD_reallocstack(L, L->stacksize - EXTRA_STACK - 1)); \ + if (G(L)->totalbytes >= G(L)->GCthreshold) \ + luaC_step(L); } + + +#define luaC_barrier(L,p,v) { if (valiswhite(v) && isblack(obj2gco(p))) \ + luaC_barrierf(L,obj2gco(p),gcvalue(v)); } + +#define luaC_barriert(L,t,v) { if (valiswhite(v) && isblack(obj2gco(t))) \ + luaC_barrierback(L,t); } + +#define luaC_objbarrier(L,p,o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(p))) \ + luaC_barrierf(L,obj2gco(p),obj2gco(o)); } + +#define luaC_objbarriert(L,t,o) \ + { if (iswhite(obj2gco(o)) && isblack(obj2gco(t))) luaC_barrierback(L,t); } + +LUAI_FUNC size_t luaC_separateudata (lua_State *L, int all); +LUAI_FUNC void luaC_callGCTM (lua_State *L); +LUAI_FUNC void luaC_freeall (lua_State *L); +LUAI_FUNC void luaC_step (lua_State *L); +LUAI_FUNC void luaC_fullgc (lua_State *L); +LUAI_FUNC void luaC_link (lua_State *L, GCObject *o, lu_byte tt); +LUAI_FUNC void luaC_linkupval (lua_State *L, UpVal *uv); +LUAI_FUNC void luaC_barrierf (lua_State *L, GCObject *o, GCObject *v); +LUAI_FUNC void luaC_barrierback (lua_State *L, Table *t); + + +#endif === added file 'lua/linit.c' --- lua/linit.c 1970-01-01 00:00:00 +0000 +++ lua/linit.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,46 @@ +/* +** $Id: linit.c,v 1.14.1.1 2007/12/27 13:02:25 roberto Exp $ +** Initialization of libraries for lua.c +** See Copyright Notice in lua.h +*/ + + +#define linit_c +#define LUA_LIB + +#include "lua.h" + +#include "lualib.h" +#include "lauxlib.h" + + +static const luaL_Reg lualibs[] = { + {"", luaopen_base}, +#ifndef USE_GRUB_LIB + {LUA_LOADLIBNAME, luaopen_package}, +#endif /* ! USE_GRUB_LIB */ + {LUA_TABLIBNAME, luaopen_table}, +#ifndef USE_GRUB_LIB + {LUA_IOLIBNAME, luaopen_io}, +#endif /* ! USE_GRUB_LIB */ +#ifndef USE_GRUB_LIB + {LUA_OSLIBNAME, luaopen_os}, +#endif /* ! USE_GRUB_LIB */ + {LUA_STRLIBNAME, luaopen_string}, +#if !defined LUA_NUMBER_INTEGRAL + {LUA_MATHLIBNAME, luaopen_math}, +#endif + {LUA_DBLIBNAME, luaopen_debug}, + {NULL, NULL} +}; + + +LUALIB_API void luaL_openlibs (lua_State *L) { + const luaL_Reg *lib = lualibs; + for (; lib->func; lib++) { + lua_pushcfunction(L, lib->func); + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + } +} + === added file 'lua/liolib.c' --- lua/liolib.c 1970-01-01 00:00:00 +0000 +++ lua/liolib.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,578 @@ +/* +** $Id: liolib.c,v 2.73.1.3 2008/01/18 17:47:43 roberto Exp $ +** Standard I/O (and system) library +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#include +#include +#include +#endif /* ! USE_GRUB_LIB */ + +#define liolib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +#define IO_INPUT 1 +#define IO_OUTPUT 2 + + +static const char *const fnames[] = {"input", "output"}; + + +static int pushresult (lua_State *L, int i, const char *filename) { + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + if (filename) + lua_pushfstring(L, "%s: %s", filename, grub_errmsg); + else + lua_pushfstring(L, "%s", grub_errmsg); + lua_pushinteger(L, grub_errno); + return 3; + } +} + + +static void fileerror (lua_State *L, int arg, const char *filename) { + lua_pushfstring(L, "%s: %s", filename, grub_errmsg); + luaL_argerror(L, arg, lua_tostring(L, -1)); +} + + +#define tofilep(L) ((grub_file_t *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) + + +static int io_type (lua_State *L) { + void *ud; + luaL_checkany(L, 1); + ud = lua_touserdata(L, 1); + lua_getfield(L, LUA_REGISTRYINDEX, LUA_FILEHANDLE); + if (ud == NULL || !lua_getmetatable(L, 1) || !lua_rawequal(L, -2, -1)) + lua_pushnil(L); /* not a file */ + else if (*((grub_file_t *)ud) == NULL) + lua_pushliteral(L, "closed file"); + else + lua_pushliteral(L, "file"); + return 1; +} + + +static grub_file_t tofile (lua_State *L) { + grub_file_t *f = tofilep(L); + if (*f == NULL) + luaL_error(L, "attempt to use a closed file"); + return *f; +} + + + +/* +** When creating file handles, always creates a `closed' file handle +** before opening the actual file; so, if there is a memory error, the +** file is not left opened. +*/ +static grub_file_t *newfile (lua_State *L) { + grub_file_t *pf = (grub_file_t *)lua_newuserdata(L, sizeof(grub_file_t)); + *pf = NULL; /* file handle is currently `closed' */ + luaL_getmetatable(L, LUA_FILEHANDLE); + lua_setmetatable(L, -2); + return pf; +} + + +/* +** function to (not) close the standard files stdin, stdout, and stderr +*/ +static int io_noclose (lua_State *L) { + lua_pushnil(L); + lua_pushliteral(L, "cannot close standard file"); + return 2; +} + + +/* +** function to close regular files +*/ +static int io_fclose (lua_State *L) { + grub_file_t *p = tofilep(L); + int ok = (grub_file_close(*p) == 0); + *p = NULL; + return pushresult(L, ok, NULL); +} + + +static int aux_close (lua_State *L) { + lua_getfenv(L, 1); + lua_getfield(L, -1, "__close"); + return (lua_tocfunction(L, -1))(L); +} + + +static int io_close (lua_State *L) { + if (lua_isnone(L, 1)) + lua_rawgeti(L, LUA_ENVIRONINDEX, IO_OUTPUT); + tofile(L); /* make sure argument is a file */ + return aux_close(L); +} + + +static int io_gc (lua_State *L) { + grub_file_t f = *tofilep(L); + /* ignore closed files */ + if (f != NULL) + aux_close(L); + return 0; +} + + +static int io_tostring (lua_State *L) { + grub_file_t f = *tofilep(L); + if (f == NULL) + lua_pushliteral(L, "file (closed)"); + else + lua_pushfstring(L, "file (%p)", f); + return 1; +} + + +static int io_open (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + const char *mode = luaL_optstring(L, 2, "r"); + grub_file_t *pf = newfile(L); + UNUSED(mode); + *pf = grub_buffile_open(filename, 0); + return (*pf == NULL) ? pushresult(L, 0, filename) : 1; +} + + +static grub_file_t getiofile (lua_State *L, int findex) { + grub_file_t f; + lua_rawgeti(L, LUA_ENVIRONINDEX, findex); + f = *(grub_file_t *)lua_touserdata(L, -1); + if (f == NULL) + luaL_error(L, "standard %s file is closed", fnames[findex - 1]); + return f; +} + + +static int g_iofile (lua_State *L, int f, const char *mode) { + if (!lua_isnoneornil(L, 1)) { + const char *filename = lua_tostring(L, 1); + if (filename) { + grub_file_t *pf = newfile(L); + *pf = grub_buffile_open(filename, 0); + if (*pf == NULL) + fileerror(L, 1, filename); + } + else { + tofile(L); /* check that it's a valid file handle */ + lua_pushvalue(L, 1); + } + lua_rawseti(L, LUA_ENVIRONINDEX, f); + } + /* return current value */ + lua_rawgeti(L, LUA_ENVIRONINDEX, f); + return 1; +} + + +static int io_input (lua_State *L) { + return g_iofile(L, IO_INPUT, "r"); +} + + +static int io_output (lua_State *L) { + return g_iofile(L, IO_OUTPUT, "w"); +} + + +static int io_readline (lua_State *L); + + +static void aux_lines (lua_State *L, int idx, int toclose) { + lua_pushvalue(L, idx); + lua_pushboolean(L, toclose); /* close/not close file when finished */ + lua_pushcclosure(L, io_readline, 2); +} + + +static int f_lines (lua_State *L) { + tofile(L); /* check that it's a valid file handle */ + aux_lines(L, 1, 0); + return 1; +} + + +static int io_lines (lua_State *L) { + if (lua_isnoneornil(L, 1)) { /* no arguments? */ + /* will iterate over default input */ + lua_rawgeti(L, LUA_ENVIRONINDEX, IO_INPUT); + return f_lines(L); + } + else { + const char *filename = luaL_checkstring(L, 1); + grub_file_t *pf = newfile(L); + *pf = grub_buffile_open(filename, 0); + if (*pf == NULL) + fileerror(L, 1, filename); + aux_lines(L, lua_gettop(L), 1); + return 1; + } +} + + +/* +** {====================================================== +** READ +** ======================================================= +*/ + + +static int read_number (lua_State *L, grub_file_t f) { + lua_Number d; + char buf[32]; + char *endptr; + int len; + int found_digit; + len = 0; + found_digit = 0; + while (len < sizeof(buf) - 1) + { + if (grub_file_read (f, &buf[len], 1) != 1) + break; + + if (found_digit) + { + if (! isdigit (buf[len])) + break; + } + else + { + if (isdigit (buf[len])) + found_digit = 1; + } + + len++; + } + buf[len] = '\0'; + d = strtol (buf, &endptr, 10); + if (buf[0] != '\0' && endptr > &buf[0]) { + lua_pushnumber(L, d); + return 1; + } + else return 0; /* read fails */ +} + + +static int test_eof (lua_State *L, grub_file_t f) { + lua_pushlstring(L, NULL, 0); + return (grub_file_tell (f) < grub_file_size (f)); +} + + +static int read_line (lua_State *L, grub_file_t f) { + luaL_Buffer b; + luaL_buffinit(L, &b); + for (;;) { + size_t l; + char *p = luaL_prepbuffer(&b); + if (fgets(p, LUAL_BUFFERSIZE, f) == NULL) { /* eof? */ + luaL_pushresult(&b); /* close buffer */ + return (lua_objlen(L, -1) > 0); /* check whether read something */ + } + l = strlen(p); + if (l == 0 || p[l-1] != '\n') + luaL_addsize(&b, l); + else { + luaL_addsize(&b, l - 1); /* do not include `eol' */ + luaL_pushresult(&b); /* close buffer */ + return 1; /* read at least an `eol' */ + } + } +} + + +static int read_chars (lua_State *L, grub_file_t f, size_t n) { + size_t rlen; /* how much to read */ + size_t nr; /* number of chars actually read */ + luaL_Buffer b; + luaL_buffinit(L, &b); + rlen = LUAL_BUFFERSIZE; /* try to read that much each time */ + do { + ssize_t ret; + char *p = luaL_prepbuffer(&b); + if (rlen > n) rlen = n; /* cannot read more than asked */ + ret = grub_file_read (f, p, rlen); + if (ret == -1) + break; + nr = (size_t) ret; + luaL_addsize(&b, nr); + n -= nr; /* still have to read `n' chars */ + } while (n > 0 && nr == rlen); /* until end of count or eof */ + luaL_pushresult(&b); /* close buffer */ + return (n == 0 || lua_objlen(L, -1) > 0); +} + + +static int g_read (lua_State *L, grub_file_t f, int first) { + int nargs = lua_gettop(L) - 1; + int success; + int n; + grub_errno = GRUB_ERR_NONE; + if (nargs == 0) { /* no arguments? */ + success = read_line(L, f); + n = first+1; /* to return 1 result */ + } + else { /* ensure stack space for all results and for auxlib's buffer */ + luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); + success = 1; + for (n = first; nargs-- && success; n++) { + if (lua_type(L, n) == LUA_TNUMBER) { + size_t l = (size_t)lua_tointeger(L, n); + success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); + } + else { + const char *p = lua_tostring(L, n); + luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); + switch (p[1]) { + case 'n': /* number */ + success = read_number(L, f); + break; + case 'l': /* line */ + success = read_line(L, f); + break; + case 'a': /* file */ + read_chars(L, f, ~((size_t)0)); /* read MAX_SIZE_T chars */ + success = 1; /* always success */ + break; + default: + return luaL_argerror(L, n, "invalid format"); + } + } + } + } + if (grub_errno != GRUB_ERR_NONE) + return pushresult(L, 0, NULL); + if (!success) { + lua_pop(L, 1); /* remove last result */ + lua_pushnil(L); /* push nil instead */ + } + return n - first; +} + + +static int io_read (lua_State *L) { + return g_read(L, getiofile(L, IO_INPUT), 1); +} + + +static int f_read (lua_State *L) { + return g_read(L, tofile(L), 2); +} + + +static int io_readline (lua_State *L) { + grub_file_t f = *(grub_file_t *)lua_touserdata(L, lua_upvalueindex(1)); + int sucess; + if (f == NULL) /* file is already closed? */ + luaL_error(L, "file is already closed"); + grub_errno = GRUB_ERR_NONE; + sucess = read_line(L, f); + if (grub_errno != GRUB_ERR_NONE) + return luaL_error(L, "%s", grub_errmsg); + if (sucess) return 1; + else { /* EOF */ + if (lua_toboolean(L, lua_upvalueindex(2))) { /* generator created file? */ + lua_settop(L, 0); + lua_pushvalue(L, lua_upvalueindex(1)); + aux_close(L); /* close it */ + } + return 0; + } +} + +/* }====================================================== */ + + +static int g_write (lua_State *L, grub_file_t f, int arg) { + int nargs = lua_gettop(L) - 1; + int status = 1; + for (; nargs--; arg++) { + if (lua_type(L, arg) == LUA_TNUMBER) { + /* optimization: could be done exactly as for strings */ + if (status) + { + char buf[32]; + size_t n; + grub_sprintf (buf, LUA_NUMBER_FMT, lua_tonumber (L, arg)); + n = strlen (buf); + status = (grub_file_write (f, buf, n) == n); + } + } + else { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + status = status && (grub_file_write(f, s, l) == l); + } + } + return pushresult(L, status, NULL); +} + + +static int io_write (lua_State *L) { + return g_write(L, getiofile(L, IO_OUTPUT), 1); +} + + +static int f_write (lua_State *L) { + return g_write(L, tofile(L), 2); +} + +enum { + SEEK_SET, SEEK_CUR, SEEK_END +}; + +static int do_grub_seek (grub_file_t f, long offset, int whence) +{ + grub_off_t pos; + switch (whence) + { + default: + case SEEK_SET: + pos = offset; + break; + case SEEK_CUR: + pos = ((long) grub_file_tell (f)) + offset; + break; + case SEEK_END: + pos = ((long) grub_file_size (f)) + offset; + break; + } + return grub_file_seek (f, pos); +} + +static int f_seek (lua_State *L) { + static const char *const modenames[] = {"set", "cur", "end", NULL}; + grub_file_t f = tofile(L); + int op = luaL_checkoption(L, 2, "cur", modenames); + long offset = luaL_optlong(L, 3, 0); + op = do_grub_seek (f, offset, mode[op]); + if (op) + return pushresult(L, 0, NULL); /* error */ + else { + lua_pushinteger(L, grub_file_tell (f)); + return 1; + } +} + + +static int f_setvbuf (lua_State *L) { + /* For GRUB we simply report success. */ + lua_pushboolean (L, 1); + return 1; +} + + + +static int io_flush (lua_State *L) { + /* For GRUB we simply report success. */ + lua_pushboolean (L, 1); + return 1; +} + + +static int f_flush (lua_State *L) { + /* For GRUB we simply report success. */ + lua_pushboolean (L, 1); + return 1; +} + + +static const luaL_Reg iolib[] = { + {"close", io_close}, + {"flush", io_flush}, + {"input", io_input}, + {"lines", io_lines}, + {"open", io_open}, + {"output", io_output}, + {"read", io_read}, + {"type", io_type}, + {"write", io_write}, + {NULL, NULL} +}; + + +static const luaL_Reg flib[] = { + {"close", io_close}, + {"flush", f_flush}, + {"lines", f_lines}, + {"read", f_read}, + {"seek", f_seek}, + {"setvbuf", f_setvbuf}, + {"write", f_write}, + {"__gc", io_gc}, + {"__tostring", io_tostring}, + {NULL, NULL} +}; + + +static void createmeta (lua_State *L) { + luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ + lua_pushvalue(L, -1); /* push metatable */ + lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ + luaL_register(L, NULL, flib); /* file methods */ +} + + +static void createstdfile (lua_State *L, grub_file_t f, int k, const char *fname) { + *newfile(L) = f; + if (k > 0) { + lua_pushvalue(L, -1); + lua_rawseti(L, LUA_ENVIRONINDEX, k); + } + lua_pushvalue(L, -2); /* copy environment */ + lua_setfenv(L, -2); /* set it */ + lua_setfield(L, -3, fname); +} + + +static void newfenv (lua_State *L, lua_CFunction cls) { + lua_createtable(L, 0, 1); + lua_pushcfunction(L, cls); + lua_setfield(L, -2, "__close"); +} + + +LUALIB_API int luaopen_io (lua_State *L) { + createmeta(L); + /* create (private) environment (with fields IO_INPUT, IO_OUTPUT, __close) */ + newfenv(L, io_fclose); + lua_replace(L, LUA_ENVIRONINDEX); + /* open library */ + luaL_register(L, LUA_IOLIBNAME, iolib); + /* create (and set) default files */ + newfenv(L, io_noclose); /* close function for default files */ + createstdfile(L, stdin, IO_INPUT, "stdin"); + createstdfile(L, stdout, IO_OUTPUT, "stdout"); + createstdfile(L, stderr, 0, "stderr"); + lua_pop(L, 1); /* pop environment for default files */ + lua_getfield(L, -1, "popen"); + newfenv(L, io_pclose); /* create environment for 'popen' */ + lua_setfenv(L, -2); /* set fenv for 'popen' */ + lua_pop(L, 1); /* pop 'popen' */ + return 1; +} + === added file 'lua/llex.c' --- lua/llex.c 1970-01-01 00:00:00 +0000 +++ lua/llex.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,469 @@ +/* +** $Id: llex.c,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#include +#include +#endif /* ! USE_GRUB_LIB */ + +#define llex_c +#define LUA_CORE + +#include "lua.h" + +#include "ldo.h" +#include "llex.h" +#include "lobject.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "lzio.h" + + + +#define next(ls) (ls->current = zgetc(ls->z)) + + + + +#define currIsNewline(ls) (ls->current == '\n' || ls->current == '\r') + + +/* ORDER RESERVED */ +const char *const luaX_tokens [] = { + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while", + "..", "...", "==", ">=", "<=", "~=", + "", "", "", "", + NULL +}; + + +#define save_and_next(ls) (save(ls, ls->current), next(ls)) + + +static void save (LexState *ls, int c) { + Mbuffer *b = ls->buff; + if (b->n + 1 > b->buffsize) { + size_t newsize; + if (b->buffsize >= MAX_SIZET/2) + luaX_lexerror(ls, "lexical element too long", 0); + newsize = b->buffsize * 2; + luaZ_resizebuffer(ls->L, b, newsize); + } + b->buffer[b->n++] = cast(char, c); +} + + +void luaX_init (lua_State *L) { + int i; + for (i=0; itsv.reserved = cast_byte(i+1); /* reserved word */ + } +} + + +#define MAXSRC 80 + + +const char *luaX_token2str (LexState *ls, int token) { + if (token < FIRST_RESERVED) { + lua_assert(token == cast(unsigned char, token)); + return (iscntrl(token)) ? luaO_pushfstring(ls->L, "char(%d)", token) : + luaO_pushfstring(ls->L, "%c", token); + } + else + return luaX_tokens[token-FIRST_RESERVED]; +} + + +static const char *txtToken (LexState *ls, int token) { + switch (token) { + case TK_NAME: + case TK_STRING: + case TK_NUMBER: + save(ls, '\0'); + return luaZ_buffer(ls->buff); + default: + return luaX_token2str(ls, token); + } +} + + +void luaX_lexerror (LexState *ls, const char *msg, int token) { + char buff[MAXSRC]; + luaO_chunkid(buff, getstr(ls->source), MAXSRC); + msg = luaO_pushfstring(ls->L, "%s:%d: %s", buff, ls->linenumber, msg); + if (token) + luaO_pushfstring(ls->L, "%s near " LUA_QS, msg, txtToken(ls, token)); + luaD_throw(ls->L, LUA_ERRSYNTAX); +} + + +void luaX_syntaxerror (LexState *ls, const char *msg) { + luaX_lexerror(ls, msg, ls->t.token); +} + + +TString *luaX_newstring (LexState *ls, const char *str, size_t l) { + lua_State *L = ls->L; + TString *ts = luaS_newlstr(L, str, l); + TValue *o = luaH_setstr(L, ls->fs->h, ts); /* entry for `str' */ + if (ttisnil(o)) + setbvalue(o, 1); /* make sure `str' will not be collected */ + return ts; +} + + +static void inclinenumber (LexState *ls) { + int old = ls->current; + lua_assert(currIsNewline(ls)); + next(ls); /* skip `\n' or `\r' */ + if (currIsNewline(ls) && ls->current != old) + next(ls); /* skip `\n\r' or `\r\n' */ + if (++ls->linenumber >= MAX_INT) + luaX_syntaxerror(ls, "chunk has too many lines"); +} + + +void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, TString *source) { + ls->decpoint = '.'; + ls->L = L; + ls->lookahead.token = TK_EOS; /* no look-ahead token */ + ls->z = z; + ls->fs = NULL; + ls->linenumber = 1; + ls->lastline = 1; + ls->source = source; + luaZ_resizebuffer(ls->L, ls->buff, LUA_MINBUFFER); /* initialize buffer */ + next(ls); /* read first char */ +} + + + +/* +** ======================================================= +** LEXICAL ANALYZER +** ======================================================= +*/ + + + +static int check_next (LexState *ls, const char *set) { + if (!strchr(set, ls->current)) + return 0; + save_and_next(ls); + return 1; +} + + +static void buffreplace (LexState *ls, char from, char to) { + size_t n = luaZ_bufflen(ls->buff); + char *p = luaZ_buffer(ls->buff); + while (n--) + if (p[n] == from) p[n] = to; +} + + +static void trydecpoint (LexState *ls, SemInfo *seminfo) { + /* format error: try to update decimal point separator */ + char old = ls->decpoint; + +#ifdef USE_GRUB_LIB + ls->decpoint = '.'; +#else /* ! USE_GRUB_LIB */ + struct lconv *cv = localeconv(); + ls->decpoint = (cv ? cv->decimal_point[0] : '.'); +#endif /* ! USE_GRUB_LIB */ + + buffreplace(ls, old, ls->decpoint); /* try updated decimal separator */ + if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) { + /* format error with correct decimal point: no more options */ + buffreplace(ls, ls->decpoint, '.'); /* undo change (for error message) */ + luaX_lexerror(ls, "malformed number", TK_NUMBER); + } +} + + +/* LUA_NUMBER */ +static void read_numeral (LexState *ls, SemInfo *seminfo) { + lua_assert(isdigit(ls->current)); + do { + save_and_next(ls); + } while (isdigit(ls->current) || ls->current == '.'); + if (check_next(ls, "Ee")) /* `E'? */ + check_next(ls, "+-"); /* optional exponent sign */ + while (isalnum(ls->current) || ls->current == '_') + save_and_next(ls); + save(ls, '\0'); + buffreplace(ls, '.', ls->decpoint); /* follow locale for decimal point */ + if (!luaO_str2d(luaZ_buffer(ls->buff), &seminfo->r)) /* format error? */ + trydecpoint(ls, seminfo); /* try to update decimal point separator */ +} + + +static int skip_sep (LexState *ls) { + int count = 0; + int s = ls->current; + lua_assert(s == '[' || s == ']'); + save_and_next(ls); + while (ls->current == '=') { + save_and_next(ls); + count++; + } + return (ls->current == s) ? count : (-count) - 1; +} + + +static void read_long_string (LexState *ls, SemInfo *seminfo, int sep) { + int cont = 0; + (void)(cont); /* avoid warnings when `cont' is not used */ + save_and_next(ls); /* skip 2nd `[' */ + if (currIsNewline(ls)) /* string starts with a newline? */ + inclinenumber(ls); /* skip it */ + for (;;) { + switch (ls->current) { + case EOZ: + luaX_lexerror(ls, (seminfo) ? "unfinished long string" : + "unfinished long comment", TK_EOS); + break; /* to avoid warnings */ +#if defined(LUA_COMPAT_LSTR) + case '[': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd `[' */ + cont++; +#if LUA_COMPAT_LSTR == 1 + if (sep == 0) + luaX_lexerror(ls, "nesting of [[...]] is deprecated", '['); +#endif + } + break; + } +#endif + case ']': { + if (skip_sep(ls) == sep) { + save_and_next(ls); /* skip 2nd `]' */ +#if defined(LUA_COMPAT_LSTR) && LUA_COMPAT_LSTR == 2 + cont--; + if (sep == 0 && cont >= 0) break; +#endif + goto endloop; + } + break; + } + case '\n': + case '\r': { + save(ls, '\n'); + inclinenumber(ls); + if (!seminfo) luaZ_resetbuffer(ls->buff); /* avoid wasting space */ + break; + } + default: { + if (seminfo) save_and_next(ls); + else next(ls); + } + } + } endloop: + if (seminfo) + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + (2 + sep), + luaZ_bufflen(ls->buff) - 2*(2 + sep)); +} + + +static void read_string (LexState *ls, int del, SemInfo *seminfo) { + save_and_next(ls); + while (ls->current != del) { + switch (ls->current) { + case EOZ: + luaX_lexerror(ls, "unfinished string", TK_EOS); + continue; /* to avoid warnings */ + case '\n': + case '\r': + luaX_lexerror(ls, "unfinished string", TK_STRING); + continue; /* to avoid warnings */ + case '\\': { + int c; + next(ls); /* do not save the `\' */ + switch (ls->current) { + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\n': /* go through */ + case '\r': save(ls, '\n'); inclinenumber(ls); continue; + case EOZ: continue; /* will raise an error next loop */ + default: { + if (!isdigit(ls->current)) + save_and_next(ls); /* handles \\, \", \', and \? */ + else { /* \xxx */ + int i = 0; + c = 0; + do { + c = 10*c + (ls->current-'0'); + next(ls); + } while (++i<3 && isdigit(ls->current)); + if (c > UCHAR_MAX) + luaX_lexerror(ls, "escape sequence too large", TK_STRING); + save(ls, c); + } + continue; + } + } + save(ls, c); + next(ls); + continue; + } + default: + save_and_next(ls); + } + } + save_and_next(ls); /* skip delimiter */ + seminfo->ts = luaX_newstring(ls, luaZ_buffer(ls->buff) + 1, + luaZ_bufflen(ls->buff) - 2); +} + + +static int llex (LexState *ls, SemInfo *seminfo) { + luaZ_resetbuffer(ls->buff); + for (;;) { + switch (ls->current) { + case '\n': + case '\r': { + inclinenumber(ls); + continue; + } + case '-': { + next(ls); + if (ls->current != '-') return '-'; + /* else is a comment */ + next(ls); + if (ls->current == '[') { + int sep = skip_sep(ls); + luaZ_resetbuffer(ls->buff); /* `skip_sep' may dirty the buffer */ + if (sep >= 0) { + read_long_string(ls, NULL, sep); /* long comment */ + luaZ_resetbuffer(ls->buff); + continue; + } + } + /* else short comment */ + while (!currIsNewline(ls) && ls->current != EOZ) + next(ls); + continue; + } + case '[': { + int sep = skip_sep(ls); + if (sep >= 0) { + read_long_string(ls, seminfo, sep); + return TK_STRING; + } + else if (sep == -1) return '['; + else luaX_lexerror(ls, "invalid long string delimiter", TK_STRING); + } + case '=': { + next(ls); + if (ls->current != '=') return '='; + else { next(ls); return TK_EQ; } + } + case '<': { + next(ls); + if (ls->current != '=') return '<'; + else { next(ls); return TK_LE; } + } + case '>': { + next(ls); + if (ls->current != '=') return '>'; + else { next(ls); return TK_GE; } + } + case '~': { + next(ls); + if (ls->current != '=') return '~'; + else { next(ls); return TK_NE; } + } + case '"': + case '\'': { + read_string(ls, ls->current, seminfo); + return TK_STRING; + } + case '.': { + save_and_next(ls); + if (check_next(ls, ".")) { + if (check_next(ls, ".")) + return TK_DOTS; /* ... */ + else return TK_CONCAT; /* .. */ + } + else if (!isdigit(ls->current)) return '.'; + else { + read_numeral(ls, seminfo); + return TK_NUMBER; + } + } + case EOZ: { + return TK_EOS; + } + default: { + if (isspace(ls->current)) { + lua_assert(!currIsNewline(ls)); + next(ls); + continue; + } + else if (isdigit(ls->current)) { + read_numeral(ls, seminfo); + return TK_NUMBER; + } + else if (isalpha(ls->current) || ls->current == '_') { + /* identifier or reserved word */ + TString *ts; + do { + save_and_next(ls); + } while (isalnum(ls->current) || ls->current == '_'); + ts = luaX_newstring(ls, luaZ_buffer(ls->buff), + luaZ_bufflen(ls->buff)); + if (ts->tsv.reserved > 0) /* reserved word? */ + return ts->tsv.reserved - 1 + FIRST_RESERVED; + else { + seminfo->ts = ts; + return TK_NAME; + } + } + else { + int c = ls->current; + next(ls); + return c; /* single-char tokens (+ - / ...) */ + } + } + } + } +} + + +void luaX_next (LexState *ls) { + ls->lastline = ls->linenumber; + if (ls->lookahead.token != TK_EOS) { /* is there a look-ahead token? */ + ls->t = ls->lookahead; /* use this one */ + ls->lookahead.token = TK_EOS; /* and discharge it */ + } + else + ls->t.token = llex(ls, &ls->t.seminfo); /* read next token */ +} + + +void luaX_lookahead (LexState *ls) { + lua_assert(ls->lookahead.token == TK_EOS); + ls->lookahead.token = llex(ls, &ls->lookahead.seminfo); +} + === added file 'lua/llex.h' --- lua/llex.h 1970-01-01 00:00:00 +0000 +++ lua/llex.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,81 @@ +/* +** $Id: llex.h,v 1.58.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lexical Analyzer +** See Copyright Notice in lua.h +*/ + +#ifndef llex_h +#define llex_h + +#include "lobject.h" +#include "lzio.h" + + +#define FIRST_RESERVED 257 + +/* maximum length of a reserved word */ +#define TOKEN_LEN (sizeof("function")/sizeof(char)) + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER RESERVED" +*/ +enum RESERVED { + /* terminal symbols denoted by reserved words */ + TK_AND = FIRST_RESERVED, TK_BREAK, + TK_DO, TK_ELSE, TK_ELSEIF, TK_END, TK_FALSE, TK_FOR, TK_FUNCTION, + TK_IF, TK_IN, TK_LOCAL, TK_NIL, TK_NOT, TK_OR, TK_REPEAT, + TK_RETURN, TK_THEN, TK_TRUE, TK_UNTIL, TK_WHILE, + /* other terminal symbols */ + TK_CONCAT, TK_DOTS, TK_EQ, TK_GE, TK_LE, TK_NE, TK_NUMBER, + TK_NAME, TK_STRING, TK_EOS +}; + +/* number of reserved words */ +#define NUM_RESERVED (cast(int, TK_WHILE-FIRST_RESERVED+1)) + + +/* array with token `names' */ +LUAI_DATA const char *const luaX_tokens []; + + +typedef union { + lua_Number r; + TString *ts; +} SemInfo; /* semantics information */ + + +typedef struct Token { + int token; + SemInfo seminfo; +} Token; + + +typedef struct LexState { + int current; /* current character (charint) */ + int linenumber; /* input line counter */ + int lastline; /* line of last token `consumed' */ + Token t; /* current token */ + Token lookahead; /* look ahead token */ + struct FuncState *fs; /* `FuncState' is private to the parser */ + struct lua_State *L; + ZIO *z; /* input stream */ + Mbuffer *buff; /* buffer for tokens */ + TString *source; /* current source name */ + char decpoint; /* locale decimal point */ +} LexState; + + +LUAI_FUNC void luaX_init (lua_State *L); +LUAI_FUNC void luaX_setinput (lua_State *L, LexState *ls, ZIO *z, + TString *source); +LUAI_FUNC TString *luaX_newstring (LexState *ls, const char *str, size_t l); +LUAI_FUNC void luaX_next (LexState *ls); +LUAI_FUNC void luaX_lookahead (LexState *ls); +LUAI_FUNC void luaX_lexerror (LexState *ls, const char *msg, int token); +LUAI_FUNC void luaX_syntaxerror (LexState *ls, const char *s); +LUAI_FUNC const char *luaX_token2str (LexState *ls, int token); + + +#endif === added file 'lua/llimits.h' --- lua/llimits.h 1970-01-01 00:00:00 +0000 +++ lua/llimits.h 2008-08-01 15:27:27 +0000 @@ -0,0 +1,136 @@ +/* +** $Id: llimits.h,v 1.69.1.1 2007/12/27 13:02:25 roberto Exp $ +** Limits, basic types, and some other `installation-dependent' definitions +** See Copyright Notice in lua.h +*/ + +#ifndef llimits_h +#define llimits_h + + +#ifndef USE_GRUB_LIB +#include +#include +#endif /* ! USE_GRUB_LIB */ + + +#include "lua.h" + + +typedef LUAI_UINT32 lu_int32; + +typedef LUAI_UMEM lu_mem; + +typedef LUAI_MEM l_mem; + + + +/* chars used as small naturals (so that `char' is reserved for characters) */ +typedef unsigned char lu_byte; + + +#define MAX_SIZET ((size_t)(~(size_t)0)-2) + +#define MAX_LUMEM ((lu_mem)(~(lu_mem)0)-2) + + +#define MAX_INT (INT_MAX-2) /* maximum value of an int (-2 for safety) */ + +/* +** conversion of pointer to integer +** this is for hashing only; there is no problem if the integer +** cannot hold the whole pointer value +*/ +#define IntPoint(p) ((unsigned int)(lu_mem)(p)) + + + +/* type to ensure maximum alignment */ +typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; + + +/* result of a `usual argument conversion' over lua_Number */ +typedef LUAI_UACNUMBER l_uacNumber; + + +/* internal assertions for in-house debugging */ +#ifdef lua_assert + +#define check_exp(c,e) (lua_assert(c), (e)) +#define api_check(l,e) lua_assert(e) + +#else + +#define lua_assert(c) ((void)0) +#define check_exp(c,e) (e) +#define api_check luai_apicheck + +#endif + + +#ifdef USE_GRUB_LIB +/* Redefine UNUSED: GRUB uses it as an attribute within the parameter + list, but LUA uses it as a statement in the function body. */ +#undef UNUSED +#endif /* USE_GRUB_LIB */ + +#ifndef UNUSED +#define UNUSED(x) ((void)(x)) /* to avoid warnings */ +#endif + + +#ifndef cast +#define cast(t, exp) ((t)(exp)) +#endif + +#define cast_byte(i) cast(lu_byte, (i)) +#define cast_num(i) cast(lua_Number, (i)) +#define cast_int(i) cast(int, (i)) + + + +/* +** type for virtual-machine instructions +** must be an unsigned with (at least) 4 bytes (see details in lopcodes.h) +*/ +typedef lu_int32 Instruction; + + + +/* maximum stack for a Lua function */ +#define MAXSTACK 250 + + + +/* minimum size for the string table (must be power of 2) */ +#ifndef MINSTRTABSIZE +#define MINSTRTABSIZE 32 +#endif + + +/* minimum size for string buffer */ +#ifndef LUA_MINBUFFER +#define LUA_MINBUFFER 32 +#endif + + +#ifndef lua_lock +#define lua_lock(L) ((void) 0) +#define lua_unlock(L) ((void) 0) +#endif + +#ifndef luai_threadyield +#define luai_threadyield(L) {lua_unlock(L); lua_lock(L);} +#endif + + +/* +** macro to control inclusion of some hard tests on stack reallocation +*/ +#ifndef HARDSTACKTESTS +#define condhardstacktests(x) ((void)0) +#else +#define condhardstacktests(x) x +#endif + +#endif === added file 'lua/lmathlib.c' --- lua/lmathlib.c 1970-01-01 00:00:00 +0000 +++ lua/lmathlib.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,267 @@ +/* +** $Id: lmathlib.c,v 1.67.1.1 2007/12/27 13:02:25 roberto Exp $ +** Standard mathematical library +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#include +#endif /* ! USE_GRUB_LIB */ + +#define lmathlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#undef PI +#define PI (3.14159265358979323846) +#define RADIANS_PER_DEGREE (PI/180.0) + + + +static int math_abs (lua_State *L) { + lua_pushnumber(L, fabs(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sin (lua_State *L) { + lua_pushnumber(L, sin(luaL_checknumber(L, 1))); + return 1; +} + +static int math_sinh (lua_State *L) { + lua_pushnumber(L, sinh(luaL_checknumber(L, 1))); + return 1; +} + +static int math_cos (lua_State *L) { + lua_pushnumber(L, cos(luaL_checknumber(L, 1))); + return 1; +} + +static int math_cosh (lua_State *L) { + lua_pushnumber(L, cosh(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tan (lua_State *L) { + lua_pushnumber(L, tan(luaL_checknumber(L, 1))); + return 1; +} + +static int math_tanh (lua_State *L) { + lua_pushnumber(L, tanh(luaL_checknumber(L, 1))); + return 1; +} + +static int math_asin (lua_State *L) { + lua_pushnumber(L, asin(luaL_checknumber(L, 1))); + return 1; +} + +static int math_acos (lua_State *L) { + lua_pushnumber(L, acos(luaL_checknumber(L, 1))); + return 1; +} + +static int math_atan (lua_State *L) { + lua_pushnumber(L, atan(luaL_checknumber(L, 1))); + return 1; +} + +static int math_atan2 (lua_State *L) { + lua_pushnumber(L, atan2(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_ceil (lua_State *L) { + lua_pushnumber(L, ceil(luaL_checknumber(L, 1))); + return 1; +} + +static int math_floor (lua_State *L) { + lua_pushnumber(L, floor(luaL_checknumber(L, 1))); + return 1; +} + +static int math_fmod (lua_State *L) { + lua_pushnumber(L, fmod(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_modf (lua_State *L) { + double ip; + double fp = modf(luaL_checknumber(L, 1), &ip); + lua_pushnumber(L, ip); + lua_pushnumber(L, fp); + return 2; +} + +static int math_sqrt (lua_State *L) { + lua_pushnumber(L, sqrt(luaL_checknumber(L, 1))); + return 1; +} + +static int math_pow (lua_State *L) { + lua_pushnumber(L, pow(luaL_checknumber(L, 1), luaL_checknumber(L, 2))); + return 1; +} + +static int math_log (lua_State *L) { + lua_pushnumber(L, log(luaL_checknumber(L, 1))); + return 1; +} + +static int math_log10 (lua_State *L) { + lua_pushnumber(L, log10(luaL_checknumber(L, 1))); + return 1; +} + +static int math_exp (lua_State *L) { + lua_pushnumber(L, exp(luaL_checknumber(L, 1))); + return 1; +} + +static int math_deg (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)/RADIANS_PER_DEGREE); + return 1; +} + +static int math_rad (lua_State *L) { + lua_pushnumber(L, luaL_checknumber(L, 1)*RADIANS_PER_DEGREE); + return 1; +} + +static int math_frexp (lua_State *L) { + int e; + lua_pushnumber(L, frexp(luaL_checknumber(L, 1), &e)); + lua_pushinteger(L, e); + return 2; +} + +static int math_ldexp (lua_State *L) { + lua_pushnumber(L, ldexp(luaL_checknumber(L, 1), luaL_checkint(L, 2))); + return 1; +} + + + +static int math_min (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmin = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d < dmin) + dmin = d; + } + lua_pushnumber(L, dmin); + return 1; +} + + +static int math_max (lua_State *L) { + int n = lua_gettop(L); /* number of arguments */ + lua_Number dmax = luaL_checknumber(L, 1); + int i; + for (i=2; i<=n; i++) { + lua_Number d = luaL_checknumber(L, i); + if (d > dmax) + dmax = d; + } + lua_pushnumber(L, dmax); + return 1; +} + + +static int math_random (lua_State *L) { + /* the `%' avoids the (rare) case of r==1, and is needed also because on + some systems (SunOS!) `rand()' may return a value larger than RAND_MAX */ + lua_Number r = (lua_Number)(rand()%RAND_MAX) / (lua_Number)RAND_MAX; + switch (lua_gettop(L)) { /* check number of arguments */ + case 0: { /* no arguments */ + lua_pushnumber(L, r); /* Number between 0 and 1 */ + break; + } + case 1: { /* only upper limit */ + int u = luaL_checkint(L, 1); + luaL_argcheck(L, 1<=u, 1, "interval is empty"); + lua_pushnumber(L, floor(r*u)+1); /* int between 1 and `u' */ + break; + } + case 2: { /* lower and upper limits */ + int l = luaL_checkint(L, 1); + int u = luaL_checkint(L, 2); + luaL_argcheck(L, l<=u, 2, "interval is empty"); + lua_pushnumber(L, floor(r*(u-l+1))+l); /* int between `l' and `u' */ + break; + } + default: return luaL_error(L, "wrong number of arguments"); + } + return 1; +} + + +static int math_randomseed (lua_State *L) { + srand(luaL_checkint(L, 1)); + return 0; +} + + +static const luaL_Reg mathlib[] = { + {"abs", math_abs}, + {"acos", math_acos}, + {"asin", math_asin}, + {"atan2", math_atan2}, + {"atan", math_atan}, + {"ceil", math_ceil}, + {"cosh", math_cosh}, + {"cos", math_cos}, + {"deg", math_deg}, + {"exp", math_exp}, + {"floor", math_floor}, + {"fmod", math_fmod}, + {"frexp", math_frexp}, + {"ldexp", math_ldexp}, + {"log10", math_log10}, + {"log", math_log}, + {"max", math_max}, + {"min", math_min}, + {"modf", math_modf}, + {"pow", math_pow}, + {"rad", math_rad}, + {"random", math_random}, + {"randomseed", math_randomseed}, + {"sinh", math_sinh}, + {"sin", math_sin}, + {"sqrt", math_sqrt}, + {"tanh", math_tanh}, + {"tan", math_tan}, + {NULL, NULL} +}; + + +/* +** Open math library +*/ +LUALIB_API int luaopen_math (lua_State *L) { + luaL_register(L, LUA_MATHLIBNAME, mathlib); + lua_pushnumber(L, PI); + lua_setfield(L, -2, "pi"); +#if !defined LUA_NUMBER_INTEGRAL + lua_pushnumber(L, HUGE_VAL); + lua_setfield(L, -2, "huge"); +#endif +#if defined(LUA_COMPAT_MOD) + lua_getfield(L, -1, "fmod"); + lua_setfield(L, -2, "mod"); +#endif + return 1; +} + === added file 'lua/lmem.c' --- lua/lmem.c 1970-01-01 00:00:00 +0000 +++ lua/lmem.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,88 @@ +/* +** $Id: lmem.c,v 1.70.1.1 2007/12/27 13:02:25 roberto Exp $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#define lmem_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" + + + +/* +** About the realloc function: +** void * frealloc (void *ud, void *ptr, size_t osize, size_t nsize); +** (`osize' is the old size, `nsize' is the new size) +** +** Lua ensures that (ptr == NULL) iff (osize == 0). +** +** * frealloc(ud, NULL, 0, x) creates a new block of size `x' +** +** * frealloc(ud, p, x, 0) frees the block `p' +** (in this specific case, frealloc must return NULL). +** particularly, frealloc(ud, NULL, 0, 0) does nothing +** (which is equivalent to free(NULL) in ANSI C) +** +** frealloc returns NULL if it cannot create or reallocate the area +** (any reallocation to an equal or smaller size cannot fail!) +*/ + + + +#define MINSIZEARRAY 4 + + +void *luaM_growaux_ (lua_State *L, void *block, int *size, size_t size_elems, + int limit, const char *errormsg) { + void *newblock; + int newsize; + if (*size >= limit/2) { /* cannot double it? */ + if (*size >= limit) /* cannot grow even a little? */ + luaG_runerror(L, errormsg); + newsize = limit; /* still have at least one free place */ + } + else { + newsize = (*size)*2; + if (newsize < MINSIZEARRAY) + newsize = MINSIZEARRAY; /* minimum size */ + } + newblock = luaM_reallocv(L, block, *size, newsize, size_elems); + *size = newsize; /* update only when everything else is OK */ + return newblock; +} + + +void *luaM_toobig (lua_State *L) { + luaG_runerror(L, "memory allocation error: block too big"); + return NULL; /* to avoid warnings */ +} + + + +/* +** generic allocation routine. +*/ +void *luaM_realloc_ (lua_State *L, void *block, size_t osize, size_t nsize) { + global_State *g = G(L); + lua_assert((osize == 0) == (block == NULL)); + block = (*g->frealloc)(g->ud, block, osize, nsize); + if (block == NULL && nsize > 0) + luaD_throw(L, LUA_ERRMEM); + lua_assert((nsize == 0) == (block == NULL)); + g->totalbytes = (g->totalbytes - osize) + nsize; + return block; +} + === added file 'lua/lmem.h' --- lua/lmem.h 1970-01-01 00:00:00 +0000 +++ lua/lmem.h 2008-08-01 15:27:27 +0000 @@ -0,0 +1,51 @@ +/* +** $Id: lmem.h,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ +** Interface to Memory Manager +** See Copyright Notice in lua.h +*/ + +#ifndef lmem_h +#define lmem_h + + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#include "llimits.h" +#include "lua.h" + +#define MEMERRMSG "not enough memory" + + +#define luaM_reallocv(L,b,on,n,e) \ + ((cast(size_t, (n)+1) <= MAX_SIZET/(e)) ? /* +1 to avoid warnings */ \ + luaM_realloc_(L, (b), (on)*(e), (n)*(e)) : \ + luaM_toobig(L)) + +#define luaM_freemem(L, b, s) luaM_realloc_(L, (b), (s), 0) +#define luaM_free(L, b) luaM_realloc_(L, (b), sizeof(*(b)), 0) +#define luaM_freearray(L, b, n, t) luaM_reallocv(L, (b), n, 0, sizeof(t)) + +#define luaM_malloc(L,t) luaM_realloc_(L, NULL, 0, (t)) +#define luaM_new(L,t) cast(t *, luaM_malloc(L, sizeof(t))) +#define luaM_newvector(L,n,t) \ + cast(t *, luaM_reallocv(L, NULL, 0, n, sizeof(t))) + +#define luaM_growvector(L,v,nelems,size,t,limit,e) \ + if ((nelems)+1 > (size)) \ + ((v)=cast(t *, luaM_growaux_(L,v,&(size),sizeof(t),limit,e))) + +#define luaM_reallocvector(L, v,oldn,n,t) \ + ((v)=cast(t *, luaM_reallocv(L, v, oldn, n, sizeof(t)))) + + +LUAI_FUNC void *luaM_realloc_ (lua_State *L, void *block, size_t oldsize, + size_t size); +LUAI_FUNC void *luaM_toobig (lua_State *L); +LUAI_FUNC void *luaM_growaux_ (lua_State *L, void *block, int *size, + size_t size_elem, int limit, + const char *errormsg); + +#endif + === added file 'lua/loadlib.c' --- lua/loadlib.c 1970-01-01 00:00:00 +0000 +++ lua/loadlib.c 2008-08-01 15:19:23 +0000 @@ -0,0 +1,664 @@ +/* +** $Id: loadlib.c,v 1.52.1.2 2007/12/28 14:58:43 roberto Exp $ +** Dynamic library loader for Lua +** See Copyright Notice in lua.h +** +** This module contains an implementation of loadlib for Unix systems +** that have dlfcn, an implementation for Darwin (Mac OS X), an +** implementation for Windows, and a stub for other systems. +*/ + + +#include +#include + + +#define loadlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* prefix for open functions in C libraries */ +#define LUA_POF "luaopen_" + +/* separator for open functions in C libraries */ +#define LUA_OFSEP "_" + + +#define LIBPREFIX "LOADLIB: " + +#define POF LUA_POF +#define LIB_FAIL "open" + + +/* error codes for ll_loadfunc */ +#define ERRLIB 1 +#define ERRFUNC 2 + +#define setprogdir(L) ((void)0) + + +static void ll_unloadlib (void *lib); +static void *ll_load (lua_State *L, const char *path); +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym); + + + +#if defined(LUA_DL_DLOPEN) +/* +** {======================================================================== +** This is an implementation of loadlib based on the dlfcn interface. +** The dlfcn interface is available in Linux, SunOS, Solaris, IRIX, FreeBSD, +** NetBSD, AIX 4.2, HPUX 11, and probably most other Unix flavors, at least +** as an emulation layer on top of native functions. +** ========================================================================= +*/ + +#include + +static void ll_unloadlib (void *lib) { + dlclose(lib); +} + + +static void *ll_load (lua_State *L, const char *path) { + void *lib = dlopen(path, RTLD_NOW); + if (lib == NULL) lua_pushstring(L, dlerror()); + return lib; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)dlsym(lib, sym); + if (f == NULL) lua_pushstring(L, dlerror()); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DLL) +/* +** {====================================================================== +** This is an implementation of loadlib for Windows using native functions. +** ======================================================================= +*/ + +#include + + +#undef setprogdir + +static void setprogdir (lua_State *L) { + char buff[MAX_PATH + 1]; + char *lb; + DWORD nsize = sizeof(buff)/sizeof(char); + DWORD n = GetModuleFileNameA(NULL, buff, nsize); + if (n == 0 || n == nsize || (lb = strrchr(buff, '\\')) == NULL) + luaL_error(L, "unable to get ModuleFileName"); + else { + *lb = '\0'; + luaL_gsub(L, lua_tostring(L, -1), LUA_EXECDIR, buff); + lua_remove(L, -2); /* remove original string */ + } +} + + +static void pusherror (lua_State *L) { + int error = GetLastError(); + char buffer[128]; + if (FormatMessageA(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, error, 0, buffer, sizeof(buffer), NULL)) + lua_pushstring(L, buffer); + else + lua_pushfstring(L, "system error %d\n", error); +} + +static void ll_unloadlib (void *lib) { + FreeLibrary((HINSTANCE)lib); +} + + +static void *ll_load (lua_State *L, const char *path) { + HINSTANCE lib = LoadLibraryA(path); + if (lib == NULL) pusherror(L); + return lib; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + lua_CFunction f = (lua_CFunction)GetProcAddress((HINSTANCE)lib, sym); + if (f == NULL) pusherror(L); + return f; +} + +/* }====================================================== */ + + + +#elif defined(LUA_DL_DYLD) +/* +** {====================================================================== +** Native Mac OS X / Darwin Implementation +** ======================================================================= +*/ + +#include + + +/* Mac appends a `_' before C function names */ +#undef POF +#define POF "_" LUA_POF + + +static void pusherror (lua_State *L) { + const char *err_str; + const char *err_file; + NSLinkEditErrors err; + int err_num; + NSLinkEditError(&err, &err_num, &err_file, &err_str); + lua_pushstring(L, err_str); +} + + +static const char *errorfromcode (NSObjectFileImageReturnCode ret) { + switch (ret) { + case NSObjectFileImageInappropriateFile: + return "file is not a bundle"; + case NSObjectFileImageArch: + return "library is for wrong CPU type"; + case NSObjectFileImageFormat: + return "bad format"; + case NSObjectFileImageAccess: + return "cannot access file"; + case NSObjectFileImageFailure: + default: + return "unable to load library"; + } +} + + +static void ll_unloadlib (void *lib) { + NSUnLinkModule((NSModule)lib, NSUNLINKMODULE_OPTION_RESET_LAZY_REFERENCES); +} + + +static void *ll_load (lua_State *L, const char *path) { + NSObjectFileImage img; + NSObjectFileImageReturnCode ret; + /* this would be a rare case, but prevents crashing if it happens */ + if(!_dyld_present()) { + lua_pushliteral(L, "dyld not present"); + return NULL; + } + ret = NSCreateObjectFileImageFromFile(path, &img); + if (ret == NSObjectFileImageSuccess) { + NSModule mod = NSLinkModule(img, path, NSLINKMODULE_OPTION_PRIVATE | + NSLINKMODULE_OPTION_RETURN_ON_ERROR); + NSDestroyObjectFileImage(img); + if (mod == NULL) pusherror(L); + return mod; + } + lua_pushstring(L, errorfromcode(ret)); + return NULL; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + NSSymbol nss = NSLookupSymbolInModule((NSModule)lib, sym); + if (nss == NULL) { + lua_pushfstring(L, "symbol " LUA_QS " not found", sym); + return NULL; + } + return (lua_CFunction)NSAddressOfSymbol(nss); +} + +/* }====================================================== */ + + + +#else +/* +** {====================================================== +** Fallback for other systems +** ======================================================= +*/ + +#undef LIB_FAIL +#define LIB_FAIL "absent" + + +#define DLMSG "dynamic libraries not enabled; check your Lua installation" + + +static void ll_unloadlib (void *lib) { + (void)lib; /* to avoid warnings */ +} + + +static void *ll_load (lua_State *L, const char *path) { + (void)path; /* to avoid warnings */ + lua_pushliteral(L, DLMSG); + return NULL; +} + + +static lua_CFunction ll_sym (lua_State *L, void *lib, const char *sym) { + (void)lib; (void)sym; /* to avoid warnings */ + lua_pushliteral(L, DLMSG); + return NULL; +} + +/* }====================================================== */ +#endif + + + +static void **ll_register (lua_State *L, const char *path) { + void **plib; + lua_pushfstring(L, "%s%s", LIBPREFIX, path); + lua_gettable(L, LUA_REGISTRYINDEX); /* check library in registry? */ + if (!lua_isnil(L, -1)) /* is there an entry? */ + plib = (void **)lua_touserdata(L, -1); + else { /* no entry yet; create one */ + lua_pop(L, 1); + plib = (void **)lua_newuserdata(L, sizeof(const void *)); + *plib = NULL; + luaL_getmetatable(L, "_LOADLIB"); + lua_setmetatable(L, -2); + lua_pushfstring(L, "%s%s", LIBPREFIX, path); + lua_pushvalue(L, -2); + lua_settable(L, LUA_REGISTRYINDEX); + } + return plib; +} + + +/* +** __gc tag method: calls library's `ll_unloadlib' function with the lib +** handle +*/ +static int gctm (lua_State *L) { + void **lib = (void **)luaL_checkudata(L, 1, "_LOADLIB"); + if (*lib) ll_unloadlib(*lib); + *lib = NULL; /* mark library as closed */ + return 0; +} + + +static int ll_loadfunc (lua_State *L, const char *path, const char *sym) { + void **reg = ll_register(L, path); + if (*reg == NULL) *reg = ll_load(L, path); + if (*reg == NULL) + return ERRLIB; /* unable to load library */ + else { + lua_CFunction f = ll_sym(L, *reg, sym); + if (f == NULL) + return ERRFUNC; /* unable to find function */ + lua_pushcfunction(L, f); + return 0; /* return function */ + } +} + + +static int ll_loadlib (lua_State *L) { + const char *path = luaL_checkstring(L, 1); + const char *init = luaL_checkstring(L, 2); + int stat = ll_loadfunc(L, path, init); + if (stat == 0) /* no errors? */ + return 1; /* return the loaded function */ + else { /* error; error message is on stack top */ + lua_pushnil(L); + lua_insert(L, -2); + lua_pushstring(L, (stat == ERRLIB) ? LIB_FAIL : "init"); + return 3; /* return nil, error message, and where */ + } +} + + + +/* +** {====================================================== +** 'require' function +** ======================================================= +*/ + + +static int readable (const char *filename) { + FILE *f = fopen(filename, "r"); /* try to open file */ + if (f == NULL) return 0; /* open failed */ + fclose(f); + return 1; +} + + +static const char *pushnexttemplate (lua_State *L, const char *path) { + const char *l; + while (*path == *LUA_PATHSEP) path++; /* skip separators */ + if (*path == '\0') return NULL; /* no more templates */ + l = strchr(path, *LUA_PATHSEP); /* find next separator */ + if (l == NULL) l = path + strlen(path); + lua_pushlstring(L, path, l - path); /* template */ + return l; +} + + +static const char *findfile (lua_State *L, const char *name, + const char *pname) { + const char *path; + name = luaL_gsub(L, name, ".", LUA_DIRSEP); + lua_getfield(L, LUA_ENVIRONINDEX, pname); + path = lua_tostring(L, -1); + if (path == NULL) + luaL_error(L, LUA_QL("package.%s") " must be a string", pname); + lua_pushliteral(L, ""); /* error accumulator */ + while ((path = pushnexttemplate(L, path)) != NULL) { + const char *filename; + filename = luaL_gsub(L, lua_tostring(L, -1), LUA_PATH_MARK, name); + lua_remove(L, -2); /* remove path template */ + if (readable(filename)) /* does file exist and is readable? */ + return filename; /* return that file name */ + lua_pushfstring(L, "\n\tno file " LUA_QS, filename); + lua_remove(L, -2); /* remove file name */ + lua_concat(L, 2); /* add entry to possible error message */ + } + return NULL; /* not found */ +} + + +static void loaderror (lua_State *L, const char *filename) { + luaL_error(L, "error loading module " LUA_QS " from file " LUA_QS ":\n\t%s", + lua_tostring(L, 1), filename, lua_tostring(L, -1)); +} + + +static int loader_Lua (lua_State *L) { + const char *filename; + const char *name = luaL_checkstring(L, 1); + filename = findfile(L, name, "path"); + if (filename == NULL) return 1; /* library not found in this path */ + if (luaL_loadfile(L, filename) != 0) + loaderror(L, filename); + return 1; /* library loaded successfully */ +} + + +static const char *mkfuncname (lua_State *L, const char *modname) { + const char *funcname; + const char *mark = strchr(modname, *LUA_IGMARK); + if (mark) modname = mark + 1; + funcname = luaL_gsub(L, modname, ".", LUA_OFSEP); + funcname = lua_pushfstring(L, POF"%s", funcname); + lua_remove(L, -2); /* remove 'gsub' result */ + return funcname; +} + + +static int loader_C (lua_State *L) { + const char *funcname; + const char *name = luaL_checkstring(L, 1); + const char *filename = findfile(L, name, "cpath"); + if (filename == NULL) return 1; /* library not found in this path */ + funcname = mkfuncname(L, name); + if (ll_loadfunc(L, filename, funcname) != 0) + loaderror(L, filename); + return 1; /* library loaded successfully */ +} + + +static int loader_Croot (lua_State *L) { + const char *funcname; + const char *filename; + const char *name = luaL_checkstring(L, 1); + const char *p = strchr(name, '.'); + int stat; + if (p == NULL) return 0; /* is root */ + lua_pushlstring(L, name, p - name); + filename = findfile(L, lua_tostring(L, -1), "cpath"); + if (filename == NULL) return 1; /* root not found */ + funcname = mkfuncname(L, name); + if ((stat = ll_loadfunc(L, filename, funcname)) != 0) { + if (stat != ERRFUNC) loaderror(L, filename); /* real error */ + lua_pushfstring(L, "\n\tno module " LUA_QS " in file " LUA_QS, + name, filename); + return 1; /* function not found */ + } + return 1; +} + + +static int loader_preload (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + lua_getfield(L, LUA_ENVIRONINDEX, "preload"); + if (!lua_istable(L, -1)) + luaL_error(L, LUA_QL("package.preload") " must be a table"); + lua_getfield(L, -1, name); + if (lua_isnil(L, -1)) /* not found? */ + lua_pushfstring(L, "\n\tno field package.preload['%s']", name); + return 1; +} + + +static const int sentinel_ = 0; +#define sentinel ((void *)&sentinel_) + + +static int ll_require (lua_State *L) { + const char *name = luaL_checkstring(L, 1); + int i; + lua_settop(L, 1); /* _LOADED table will be at index 2 */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, 2, name); + if (lua_toboolean(L, -1)) { /* is it there? */ + if (lua_touserdata(L, -1) == sentinel) /* check loops */ + luaL_error(L, "loop or previous error loading module " LUA_QS, name); + return 1; /* package is already loaded */ + } + /* else must load it; iterate over available loaders */ + lua_getfield(L, LUA_ENVIRONINDEX, "loaders"); + if (!lua_istable(L, -1)) + luaL_error(L, LUA_QL("package.loaders") " must be a table"); + lua_pushliteral(L, ""); /* error message accumulator */ + for (i=1; ; i++) { + lua_rawgeti(L, -2, i); /* get a loader */ + if (lua_isnil(L, -1)) + luaL_error(L, "module " LUA_QS " not found:%s", + name, lua_tostring(L, -2)); + lua_pushstring(L, name); + lua_call(L, 1, 1); /* call it */ + if (lua_isfunction(L, -1)) /* did it find module? */ + break; /* module loaded successfully */ + else if (lua_isstring(L, -1)) /* loader returned error message? */ + lua_concat(L, 2); /* accumulate it */ + else + lua_pop(L, 1); + } + lua_pushlightuserdata(L, sentinel); + lua_setfield(L, 2, name); /* _LOADED[name] = sentinel */ + lua_pushstring(L, name); /* pass name as argument to module */ + lua_call(L, 1, 1); /* run loaded module */ + if (!lua_isnil(L, -1)) /* non-nil return? */ + lua_setfield(L, 2, name); /* _LOADED[name] = returned value */ + lua_getfield(L, 2, name); + if (lua_touserdata(L, -1) == sentinel) { /* module did not set a value? */ + lua_pushboolean(L, 1); /* use true as result */ + lua_pushvalue(L, -1); /* extra copy to be returned */ + lua_setfield(L, 2, name); /* _LOADED[name] = true */ + } + return 1; +} + +/* }====================================================== */ + + + +/* +** {====================================================== +** 'module' function +** ======================================================= +*/ + + +static void setfenv (lua_State *L) { + lua_Debug ar; + lua_getstack(L, 1, &ar); + lua_getinfo(L, "f", &ar); + lua_pushvalue(L, -2); + lua_setfenv(L, -2); + lua_pop(L, 1); +} + + +static void dooptions (lua_State *L, int n) { + int i; + for (i = 2; i <= n; i++) { + lua_pushvalue(L, i); /* get option (a function) */ + lua_pushvalue(L, -2); /* module */ + lua_call(L, 1, 0); + } +} + + +static void modinit (lua_State *L, const char *modname) { + const char *dot; + lua_pushvalue(L, -1); + lua_setfield(L, -2, "_M"); /* module._M = module */ + lua_pushstring(L, modname); + lua_setfield(L, -2, "_NAME"); + dot = strrchr(modname, '.'); /* look for last dot in module name */ + if (dot == NULL) dot = modname; + else dot++; + /* set _PACKAGE as package name (full module name minus last part) */ + lua_pushlstring(L, modname, dot - modname); + lua_setfield(L, -2, "_PACKAGE"); +} + + +static int ll_module (lua_State *L) { + const char *modname = luaL_checkstring(L, 1); + int loaded = lua_gettop(L) + 1; /* index of _LOADED table */ + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + lua_getfield(L, loaded, modname); /* get _LOADED[modname] */ + if (!lua_istable(L, -1)) { /* not found? */ + lua_pop(L, 1); /* remove previous result */ + /* try global variable (and create one if it does not exist) */ + if (luaL_findtable(L, LUA_GLOBALSINDEX, modname, 1) != NULL) + return luaL_error(L, "name conflict for module " LUA_QS, modname); + lua_pushvalue(L, -1); + lua_setfield(L, loaded, modname); /* _LOADED[modname] = new table */ + } + /* check whether table already has a _NAME field */ + lua_getfield(L, -1, "_NAME"); + if (!lua_isnil(L, -1)) /* is table an initialized module? */ + lua_pop(L, 1); + else { /* no; initialize it */ + lua_pop(L, 1); + modinit(L, modname); + } + lua_pushvalue(L, -1); + setfenv(L); + dooptions(L, loaded - 1); + return 0; +} + + +static int ll_seeall (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + if (!lua_getmetatable(L, 1)) { + lua_createtable(L, 0, 1); /* create new metatable */ + lua_pushvalue(L, -1); + lua_setmetatable(L, 1); + } + lua_pushvalue(L, LUA_GLOBALSINDEX); + lua_setfield(L, -2, "__index"); /* mt.__index = _G */ + return 0; +} + + +/* }====================================================== */ + + + +/* auxiliary mark (for internal use) */ +#define AUXMARK "\1" + +static void setpath (lua_State *L, const char *fieldname, const char *envname, + const char *def) { + const char *path = getenv(envname); + if (path == NULL) /* no environment variable? */ + lua_pushstring(L, def); /* use default */ + else { + /* replace ";;" by ";AUXMARK;" and then AUXMARK by default path */ + path = luaL_gsub(L, path, LUA_PATHSEP LUA_PATHSEP, + LUA_PATHSEP AUXMARK LUA_PATHSEP); + luaL_gsub(L, path, AUXMARK, def); + lua_remove(L, -2); + } + setprogdir(L); + lua_setfield(L, -2, fieldname); +} + + +static const luaL_Reg pk_funcs[] = { + {"loadlib", ll_loadlib}, + {"seeall", ll_seeall}, + {NULL, NULL} +}; + + +static const luaL_Reg ll_funcs[] = { + {"module", ll_module}, + {"require", ll_require}, + {NULL, NULL} +}; + + +static const lua_CFunction loaders[] = + {loader_preload, loader_Lua, loader_C, loader_Croot, NULL}; + + +LUALIB_API int luaopen_package (lua_State *L) { + int i; + /* create new type _LOADLIB */ + luaL_newmetatable(L, "_LOADLIB"); + lua_pushcfunction(L, gctm); + lua_setfield(L, -2, "__gc"); + /* create `package' table */ + luaL_register(L, LUA_LOADLIBNAME, pk_funcs); +#if defined(LUA_COMPAT_LOADLIB) + lua_getfield(L, -1, "loadlib"); + lua_setfield(L, LUA_GLOBALSINDEX, "loadlib"); +#endif + lua_pushvalue(L, -1); + lua_replace(L, LUA_ENVIRONINDEX); + /* create `loaders' table */ + lua_createtable(L, 0, sizeof(loaders)/sizeof(loaders[0]) - 1); + /* fill it with pre-defined loaders */ + for (i=0; loaders[i] != NULL; i++) { + lua_pushcfunction(L, loaders[i]); + lua_rawseti(L, -2, i+1); + } + lua_setfield(L, -2, "loaders"); /* put it in field `loaders' */ + setpath(L, "path", LUA_PATH, LUA_PATH_DEFAULT); /* set field `path' */ + setpath(L, "cpath", LUA_CPATH, LUA_CPATH_DEFAULT); /* set field `cpath' */ + /* store config information */ + lua_pushliteral(L, LUA_DIRSEP "\n" LUA_PATHSEP "\n" LUA_PATH_MARK "\n" + LUA_EXECDIR "\n" LUA_IGMARK); + lua_setfield(L, -2, "config"); + /* set field `loaded' */ + luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 2); + lua_setfield(L, -2, "loaded"); + /* set field `preload' */ + lua_newtable(L); + lua_setfield(L, -2, "preload"); + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_register(L, NULL, ll_funcs); /* open lib into global table */ + lua_pop(L, 1); + return 1; /* return 'package' table */ +} + === added file 'lua/lobject.c' --- lua/lobject.c 1970-01-01 00:00:00 +0000 +++ lua/lobject.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,216 @@ +/* +** $Id: lobject.c,v 2.22.1.1 2007/12/27 13:02:25 roberto Exp $ +** Some generic functions over Lua objects +** See Copyright Notice in lua.h +*/ + +#ifndef USE_GRUB_LIB +#include +#include +#include +#include +#include +#endif /* ! USE_GRUB_LIB */ + +#define lobject_c +#define LUA_CORE + +#include "lua.h" + +#include "ldo.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "lvm.h" + + + +const TValue luaO_nilobject_ = {{NULL}, LUA_TNIL}; + + +/* +** converts an integer to a "floating point byte", represented as +** (eeeeexxx), where the real value is (1xxx) * 2^(eeeee - 1) if +** eeeee != 0 and (xxx) otherwise. +*/ +int luaO_int2fb (unsigned int x) { + int e = 0; /* expoent */ + while (x >= 16) { + x = (x+1) >> 1; + e++; + } + if (x < 8) return x; + else return ((e+1) << 3) | (cast_int(x) - 8); +} + + +/* converts back */ +int luaO_fb2int (int x) { + int e = (x >> 3) & 31; + if (e == 0) return x; + else return ((x & 7)+8) << (e - 1); +} + + +int luaO_log2 (unsigned int x) { + static const lu_byte log_2[256] = { + 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }; + int l = -1; + while (x >= 256) { l += 8; x >>= 8; } + return l + log_2[x]; + +} + + +int luaO_rawequalObj (const TValue *t1, const TValue *t2) { + if (ttype(t1) != ttype(t2)) return 0; + else switch (ttype(t1)) { + case LUA_TNIL: + return 1; + case LUA_TNUMBER: + return luai_numeq(nvalue(t1), nvalue(t2)); + case LUA_TBOOLEAN: + return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */ + case LUA_TLIGHTUSERDATA: + return pvalue(t1) == pvalue(t2); + default: + lua_assert(iscollectable(t1)); + return gcvalue(t1) == gcvalue(t2); + } +} + + +int luaO_str2d (const char *s, lua_Number *result) { + char *endptr; + *result = lua_str2number(s, &endptr); + if (endptr == s) return 0; /* conversion failed */ + if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */ + *result = cast_num(strtoul(s, &endptr, 16)); + if (*endptr == '\0') return 1; /* most common case */ + while (isspace(cast(unsigned char, *endptr))) endptr++; + if (*endptr != '\0') return 0; /* invalid trailing characters? */ + return 1; +} + + + +static void pushstr (lua_State *L, const char *str) { + setsvalue2s(L, L->top, luaS_new(L, str)); + incr_top(L); +} + + +/* this function handles only `%d', `%c', %f, %p, and `%s' formats */ +const char *luaO_pushvfstring (lua_State *L, const char *fmt, va_list argp) { + int n = 1; + pushstr(L, ""); + for (;;) { + const char *e = strchr(fmt, '%'); + if (e == NULL) break; + setsvalue2s(L, L->top, luaS_newlstr(L, fmt, e-fmt)); + incr_top(L); + switch (*(e+1)) { + case 's': { + const char *s = va_arg(argp, char *); + if (s == NULL) s = "(null)"; + pushstr(L, s); + break; + } + case 'c': { + char buff[2]; + buff[0] = cast(char, va_arg(argp, int)); + buff[1] = '\0'; + pushstr(L, buff); + break; + } + case 'd': { + setnvalue(L->top, cast_num(va_arg(argp, int))); + incr_top(L); + break; + } + case 'f': { + setnvalue(L->top, cast_num(va_arg(argp, l_uacNumber))); + incr_top(L); + break; + } + case 'p': { + char buff[4*sizeof(void *) + 8]; /* should be enough space for a `%p' */ + sprintf(buff, "%p", va_arg(argp, void *)); + pushstr(L, buff); + break; + } + case '%': { + pushstr(L, "%"); + break; + } + default: { + char buff[3]; + buff[0] = '%'; + buff[1] = *(e+1); + buff[2] = '\0'; + pushstr(L, buff); + break; + } + } + n += 2; + fmt = e+2; + } + pushstr(L, fmt); + luaV_concat(L, n+1, cast_int(L->top - L->base) - 1); + L->top -= n; + return svalue(L->top - 1); +} + + +const char *luaO_pushfstring (lua_State *L, const char *fmt, ...) { + const char *msg; + va_list argp; + va_start(argp, fmt); + msg = luaO_pushvfstring(L, fmt, argp); + va_end(argp); + return msg; +} + + +void luaO_chunkid (char *out, const char *source, size_t bufflen) { + if (*source == '=') { + strncpy(out, source+1, bufflen); /* remove first char */ + out[bufflen-1] = '\0'; /* ensures null termination */ + } + else { /* out = "source", or "...source" */ + if (*source == '@') { + size_t l; + source++; /* skip the `@' */ + bufflen -= sizeof(" '...' "); + l = strlen(source); + strcpy(out, ""); + if (l > bufflen) { + source += (l-bufflen); /* get last part of file name */ + strcat(out, "..."); + } + strcat(out, source); + } + else { /* out = [string "string"] */ + size_t len = strcspn(source, "\n\r"); /* stop at first newline */ + bufflen -= sizeof(" [string \"...\"] "); + if (len > bufflen) len = bufflen; + strcpy(out, "[string \""); + if (source[len] != '\0') { /* must truncate? */ + strncat(out, source, len); + strcat(out, "..."); + } + else + strcat(out, source); + strcat(out, "\"]"); + } + } +} === added file 'lua/lobject.h' --- lua/lobject.h 1970-01-01 00:00:00 +0000 +++ lua/lobject.h 2008-08-01 15:27:27 +0000 @@ -0,0 +1,383 @@ +/* +** $Id: lobject.h,v 2.20.1.1 2007/12/27 13:02:25 roberto Exp $ +** Type definitions for Lua objects +** See Copyright Notice in lua.h +*/ + + +#ifndef lobject_h +#define lobject_h + + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + + +#include "llimits.h" +#include "lua.h" + + +/* tags for values visible from Lua */ +#define LAST_TAG LUA_TTHREAD + +#define NUM_TAGS (LAST_TAG+1) + + +/* +** Extra tags for non-values +*/ +#define LUA_TPROTO (LAST_TAG+1) +#define LUA_TUPVAL (LAST_TAG+2) +#define LUA_TDEADKEY (LAST_TAG+3) + + +/* +** Union of all collectable objects +*/ +typedef union GCObject GCObject; + + +/* +** Common Header for all collectable objects (in macro form, to be +** included in other objects) +*/ +#define CommonHeader GCObject *next; lu_byte tt; lu_byte marked + + +/* +** Common header in struct form +*/ +typedef struct GCheader { + CommonHeader; +} GCheader; + + + + +/* +** Union of all Lua values +*/ +typedef union { + GCObject *gc; + void *p; + lua_Number n; + int b; +} Value; + + +/* +** Tagged Values +*/ + +#define TValuefields Value value; int tt + +typedef struct lua_TValue { + TValuefields; +} TValue; + + +/* Macros to test type */ +#define ttisnil(o) (ttype(o) == LUA_TNIL) +#define ttisnumber(o) (ttype(o) == LUA_TNUMBER) +#define ttisstring(o) (ttype(o) == LUA_TSTRING) +#define ttistable(o) (ttype(o) == LUA_TTABLE) +#define ttisfunction(o) (ttype(o) == LUA_TFUNCTION) +#define ttisboolean(o) (ttype(o) == LUA_TBOOLEAN) +#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA) +#define ttisthread(o) (ttype(o) == LUA_TTHREAD) +#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA) + +/* Macros to access values */ +#define ttype(o) ((o)->tt) +#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc) +#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p) +#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n) +#define rawtsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts) +#define tsvalue(o) (&rawtsvalue(o)->tsv) +#define rawuvalue(o) check_exp(ttisuserdata(o), &(o)->value.gc->u) +#define uvalue(o) (&rawuvalue(o)->uv) +#define clvalue(o) check_exp(ttisfunction(o), &(o)->value.gc->cl) +#define hvalue(o) check_exp(ttistable(o), &(o)->value.gc->h) +#define bvalue(o) check_exp(ttisboolean(o), (o)->value.b) +#define thvalue(o) check_exp(ttisthread(o), &(o)->value.gc->th) + +#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0)) + +/* +** for internal debug only +*/ +#define checkconsistency(obj) \ + lua_assert(!iscollectable(obj) || (ttype(obj) == (obj)->value.gc->gch.tt)) + +#define checkliveness(g,obj) \ + lua_assert(!iscollectable(obj) || \ + ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc))) + + +/* Macros to set values */ +#define setnilvalue(obj) ((obj)->tt=LUA_TNIL) + +#define setnvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.n=(x); i_o->tt=LUA_TNUMBER; } + +#define setpvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.p=(x); i_o->tt=LUA_TLIGHTUSERDATA; } + +#define setbvalue(obj,x) \ + { TValue *i_o=(obj); i_o->value.b=(x); i_o->tt=LUA_TBOOLEAN; } + +#define setsvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TSTRING; \ + checkliveness(G(L),i_o); } + +#define setuvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TUSERDATA; \ + checkliveness(G(L),i_o); } + +#define setthvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTHREAD; \ + checkliveness(G(L),i_o); } + +#define setclvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TFUNCTION; \ + checkliveness(G(L),i_o); } + +#define sethvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TTABLE; \ + checkliveness(G(L),i_o); } + +#define setptvalue(L,obj,x) \ + { TValue *i_o=(obj); \ + i_o->value.gc=cast(GCObject *, (x)); i_o->tt=LUA_TPROTO; \ + checkliveness(G(L),i_o); } + + + + +#define setobj(L,obj1,obj2) \ + { const TValue *o2=(obj2); TValue *o1=(obj1); \ + o1->value = o2->value; o1->tt=o2->tt; \ + checkliveness(G(L),o1); } + + +/* +** different types of sets, according to destination +*/ + +/* from stack to (same) stack */ +#define setobjs2s setobj +/* to stack (not from same stack) */ +#define setobj2s setobj +#define setsvalue2s setsvalue +#define sethvalue2s sethvalue +#define setptvalue2s setptvalue +/* from table to same table */ +#define setobjt2t setobj +/* to table */ +#define setobj2t setobj +/* to new object */ +#define setobj2n setobj +#define setsvalue2n setsvalue + +#define setttype(obj, tt) (ttype(obj) = (tt)) + + +#define iscollectable(o) (ttype(o) >= LUA_TSTRING) + + + +typedef TValue *StkId; /* index to stack elements */ + + +/* +** String headers for string table +*/ +typedef union TString { + L_Umaxalign dummy; /* ensures maximum alignment for strings */ + struct { + CommonHeader; + lu_byte reserved; + unsigned int hash; + size_t len; + } tsv; +} TString; + + +#define getstr(ts) cast(const char *, (ts) + 1) +#define svalue(o) getstr(tsvalue(o)) + + + +typedef union Udata { + L_Umaxalign dummy; /* ensures maximum alignment for `local' udata */ + struct { + CommonHeader; + struct Table *metatable; + struct Table *env; + size_t len; + } uv; +} Udata; + + + + +/* +** Function Prototypes +*/ +typedef struct Proto { + CommonHeader; + TValue *k; /* constants used by the function */ + Instruction *code; + struct Proto **p; /* functions defined inside the function */ + int *lineinfo; /* map from opcodes to source lines */ + struct LocVar *locvars; /* information about local variables */ + TString **upvalues; /* upvalue names */ + TString *source; + int sizeupvalues; + int sizek; /* size of `k' */ + int sizecode; + int sizelineinfo; + int sizep; /* size of `p' */ + int sizelocvars; + int linedefined; + int lastlinedefined; + GCObject *gclist; + lu_byte nups; /* number of upvalues */ + lu_byte numparams; + lu_byte is_vararg; + lu_byte maxstacksize; +} Proto; + + +/* masks for new-style vararg */ +#define VARARG_HASARG 1 +#define VARARG_ISVARARG 2 +#define VARARG_NEEDSARG 4 + + +typedef struct LocVar { + TString *varname; + int startpc; /* first point where variable is active */ + int endpc; /* first point where variable is dead */ +} LocVar; + + + +/* +** Upvalues +*/ + +typedef struct UpVal { + CommonHeader; + TValue *v; /* points to stack or to its own value */ + union { + TValue value; /* the value (when closed) */ + struct { /* double linked list (when open) */ + struct UpVal *prev; + struct UpVal *next; + } l; + } u; +} UpVal; + + +/* +** Closures +*/ + +#define ClosureHeader \ + CommonHeader; lu_byte isC; lu_byte nupvalues; GCObject *gclist; \ + struct Table *env + +typedef struct CClosure { + ClosureHeader; + lua_CFunction f; + TValue upvalue[1]; +} CClosure; + + +typedef struct LClosure { + ClosureHeader; + struct Proto *p; + UpVal *upvals[1]; +} LClosure; + + +typedef union Closure { + CClosure c; + LClosure l; +} Closure; + + +#define iscfunction(o) (ttype(o) == LUA_TFUNCTION && clvalue(o)->c.isC) +#define isLfunction(o) (ttype(o) == LUA_TFUNCTION && !clvalue(o)->c.isC) + + +/* +** Tables +*/ + +typedef union TKey { + struct { + TValuefields; + struct Node *next; /* for chaining */ + } nk; + TValue tvk; +} TKey; + + +typedef struct Node { + TValue i_val; + TKey i_key; +} Node; + + +typedef struct Table { + CommonHeader; + lu_byte flags; /* 1<

lsizenode)) + + +#define luaO_nilobject (&luaO_nilobject_) + +LUAI_DATA const TValue luaO_nilobject_; + +#define ceillog2(x) (luaO_log2((x)-1) + 1) + +LUAI_FUNC int luaO_log2 (unsigned int x); +LUAI_FUNC int luaO_int2fb (unsigned int x); +LUAI_FUNC int luaO_fb2int (int x); +LUAI_FUNC int luaO_rawequalObj (const TValue *t1, const TValue *t2); +LUAI_FUNC int luaO_str2d (const char *s, lua_Number *result); +LUAI_FUNC const char *luaO_pushvfstring (lua_State *L, const char *fmt, + va_list argp); +LUAI_FUNC const char *luaO_pushfstring (lua_State *L, const char *fmt, ...); +LUAI_FUNC void luaO_chunkid (char *out, const char *source, size_t len); + + +#endif + === added file 'lua/lopcodes.c' --- lua/lopcodes.c 1970-01-01 00:00:00 +0000 +++ lua/lopcodes.c 2008-08-01 15:19:23 +0000 @@ -0,0 +1,102 @@ +/* +** $Id: lopcodes.c,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ +** See Copyright Notice in lua.h +*/ + + +#define lopcodes_c +#define LUA_CORE + + +#include "lopcodes.h" + + +/* ORDER OP */ + +const char *const luaP_opnames[NUM_OPCODES+1] = { + "MOVE", + "LOADK", + "LOADBOOL", + "LOADNIL", + "GETUPVAL", + "GETGLOBAL", + "GETTABLE", + "SETGLOBAL", + "SETUPVAL", + "SETTABLE", + "NEWTABLE", + "SELF", + "ADD", + "SUB", + "MUL", + "DIV", + "MOD", + "POW", + "UNM", + "NOT", + "LEN", + "CONCAT", + "JMP", + "EQ", + "LT", + "LE", + "TEST", + "TESTSET", + "CALL", + "TAILCALL", + "RETURN", + "FORLOOP", + "FORPREP", + "TFORLOOP", + "SETLIST", + "CLOSE", + "CLOSURE", + "VARARG", + NULL +}; + + +#define opmode(t,a,b,c,m) (((t)<<7) | ((a)<<6) | ((b)<<4) | ((c)<<2) | (m)) + +const lu_byte luaP_opmodes[NUM_OPCODES] = { +/* T A B C mode opcode */ + opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_MOVE */ + ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_LOADK */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_LOADBOOL */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LOADNIL */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_GETUPVAL */ + ,opmode(0, 1, OpArgK, OpArgN, iABx) /* OP_GETGLOBAL */ + ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_GETTABLE */ + ,opmode(0, 0, OpArgK, OpArgN, iABx) /* OP_SETGLOBAL */ + ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_SETUPVAL */ + ,opmode(0, 0, OpArgK, OpArgK, iABC) /* OP_SETTABLE */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_NEWTABLE */ + ,opmode(0, 1, OpArgR, OpArgK, iABC) /* OP_SELF */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_ADD */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_SUB */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MUL */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_DIV */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_MOD */ + ,opmode(0, 1, OpArgK, OpArgK, iABC) /* OP_POW */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_UNM */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_NOT */ + ,opmode(0, 1, OpArgR, OpArgN, iABC) /* OP_LEN */ + ,opmode(0, 1, OpArgR, OpArgR, iABC) /* OP_CONCAT */ + ,opmode(0, 0, OpArgR, OpArgN, iAsBx) /* OP_JMP */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_EQ */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LT */ + ,opmode(1, 0, OpArgK, OpArgK, iABC) /* OP_LE */ + ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TEST */ + ,opmode(1, 1, OpArgR, OpArgU, iABC) /* OP_TESTSET */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_CALL */ + ,opmode(0, 1, OpArgU, OpArgU, iABC) /* OP_TAILCALL */ + ,opmode(0, 0, OpArgU, OpArgN, iABC) /* OP_RETURN */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORLOOP */ + ,opmode(0, 1, OpArgR, OpArgN, iAsBx) /* OP_FORPREP */ + ,opmode(1, 0, OpArgN, OpArgU, iABC) /* OP_TFORLOOP */ + ,opmode(0, 0, OpArgU, OpArgU, iABC) /* OP_SETLIST */ + ,opmode(0, 0, OpArgN, OpArgN, iABC) /* OP_CLOSE */ + ,opmode(0, 1, OpArgU, OpArgN, iABx) /* OP_CLOSURE */ + ,opmode(0, 1, OpArgU, OpArgN, iABC) /* OP_VARARG */ +}; + === added file 'lua/lopcodes.h' --- lua/lopcodes.h 1970-01-01 00:00:00 +0000 +++ lua/lopcodes.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,268 @@ +/* +** $Id: lopcodes.h,v 1.125.1.1 2007/12/27 13:02:25 roberto Exp $ +** Opcodes for Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lopcodes_h +#define lopcodes_h + +#include "llimits.h" + + +/*=========================================================================== + We assume that instructions are unsigned numbers. + All instructions have an opcode in the first 6 bits. + Instructions can have the following fields: + `A' : 8 bits + `B' : 9 bits + `C' : 9 bits + `Bx' : 18 bits (`B' and `C' together) + `sBx' : signed Bx + + A signed argument is represented in excess K; that is, the number + value is the unsigned value minus K. K is exactly the maximum value + for that argument (so that -max is represented by 0, and +max is + represented by 2*max), which is half the maximum for the corresponding + unsigned argument. +===========================================================================*/ + + +enum OpMode {iABC, iABx, iAsBx}; /* basic instruction format */ + + +/* +** size and position of opcode arguments. +*/ +#define SIZE_C 9 +#define SIZE_B 9 +#define SIZE_Bx (SIZE_C + SIZE_B) +#define SIZE_A 8 + +#define SIZE_OP 6 + +#define POS_OP 0 +#define POS_A (POS_OP + SIZE_OP) +#define POS_C (POS_A + SIZE_A) +#define POS_B (POS_C + SIZE_C) +#define POS_Bx POS_C + + +/* +** limits for opcode arguments. +** we use (signed) int to manipulate most arguments, +** so they must fit in LUAI_BITSINT-1 bits (-1 for sign) +*/ +#if SIZE_Bx < LUAI_BITSINT-1 +#define MAXARG_Bx ((1<>1) /* `sBx' is signed */ +#else +#define MAXARG_Bx MAX_INT +#define MAXARG_sBx MAX_INT +#endif + + +#define MAXARG_A ((1<>POS_OP) & MASK1(SIZE_OP,0))) +#define SET_OPCODE(i,o) ((i) = (((i)&MASK0(SIZE_OP,POS_OP)) | \ + ((cast(Instruction, o)<>POS_A) & MASK1(SIZE_A,0))) +#define SETARG_A(i,u) ((i) = (((i)&MASK0(SIZE_A,POS_A)) | \ + ((cast(Instruction, u)<>POS_B) & MASK1(SIZE_B,0))) +#define SETARG_B(i,b) ((i) = (((i)&MASK0(SIZE_B,POS_B)) | \ + ((cast(Instruction, b)<>POS_C) & MASK1(SIZE_C,0))) +#define SETARG_C(i,b) ((i) = (((i)&MASK0(SIZE_C,POS_C)) | \ + ((cast(Instruction, b)<>POS_Bx) & MASK1(SIZE_Bx,0))) +#define SETARG_Bx(i,b) ((i) = (((i)&MASK0(SIZE_Bx,POS_Bx)) | \ + ((cast(Instruction, b)< C) then pc++ */ +OP_TESTSET,/* A B C if (R(B) <=> C) then R(A) := R(B) else pc++ */ + +OP_CALL,/* A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) */ +OP_TAILCALL,/* A B C return R(A)(R(A+1), ... ,R(A+B-1)) */ +OP_RETURN,/* A B return R(A), ... ,R(A+B-2) (see note) */ + +OP_FORLOOP,/* A sBx R(A)+=R(A+2); + if R(A) =) R(A)*/ +OP_CLOSURE,/* A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) */ + +OP_VARARG/* A B R(A), R(A+1), ..., R(A+B-1) = vararg */ +} OpCode; + + +#define NUM_OPCODES (cast(int, OP_VARARG) + 1) + + + +/*=========================================================================== + Notes: + (*) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, + and can be 0: OP_CALL then sets `top' to last_result+1, so + next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use `top'. + + (*) In OP_VARARG, if (B == 0) then use actual number of varargs and + set top (like in OP_CALL with C == 0). + + (*) In OP_RETURN, if (B == 0) then return up to `top' + + (*) In OP_SETLIST, if (B == 0) then B = `top'; + if (C == 0) then next `instruction' is real C + + (*) For comparisons, A specifies what condition the test should accept + (true or false). + + (*) All `skips' (pc++) assume that next instruction is a jump +===========================================================================*/ + + +/* +** masks for instruction properties. The format is: +** bits 0-1: op mode +** bits 2-3: C arg mode +** bits 4-5: B arg mode +** bit 6: instruction set register A +** bit 7: operator is a test +*/ + +enum OpArgMask { + OpArgN, /* argument is not used */ + OpArgU, /* argument is used */ + OpArgR, /* argument is a register or a jump offset */ + OpArgK /* argument is a constant or register/constant */ +}; + +LUAI_DATA const lu_byte luaP_opmodes[NUM_OPCODES]; + +#define getOpMode(m) (cast(enum OpMode, luaP_opmodes[m] & 3)) +#define getBMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 4) & 3)) +#define getCMode(m) (cast(enum OpArgMask, (luaP_opmodes[m] >> 2) & 3)) +#define testAMode(m) (luaP_opmodes[m] & (1 << 6)) +#define testTMode(m) (luaP_opmodes[m] & (1 << 7)) + + +LUAI_DATA const char *const luaP_opnames[NUM_OPCODES+1]; /* opcode names */ + + +/* number of list items to accumulate before a SETLIST instruction */ +#define LFIELDS_PER_FLUSH 50 + + +#endif === added file 'lua/loslib.c' --- lua/loslib.c 1970-01-01 00:00:00 +0000 +++ lua/loslib.c 2008-08-01 15:21:04 +0000 @@ -0,0 +1,247 @@ +/* +** $Id: loslib.c,v 1.19.1.3 2008/01/18 16:38:18 roberto Exp $ +** Standard Operating System library +** See Copyright Notice in lua.h +*/ + + +#include +#include +#include +#include +#include + +#define loslib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +static int os_pushresult (lua_State *L, int i, const char *filename) { + int en = errno; /* calls to Lua API may change this value */ + if (i) { + lua_pushboolean(L, 1); + return 1; + } + else { + lua_pushnil(L); + lua_pushfstring(L, "%s: %s", filename, strerror(en)); + lua_pushinteger(L, en); + return 3; + } +} + + +static int os_execute (lua_State *L) { + lua_pushinteger(L, system(luaL_optstring(L, 1, NULL))); + return 1; +} + + +static int os_remove (lua_State *L) { + const char *filename = luaL_checkstring(L, 1); + return os_pushresult(L, remove(filename) == 0, filename); +} + + +static int os_rename (lua_State *L) { + const char *fromname = luaL_checkstring(L, 1); + const char *toname = luaL_checkstring(L, 2); + return os_pushresult(L, rename(fromname, toname) == 0, fromname); +} + + +static int os_tmpname (lua_State *L) { + char buff[LUA_TMPNAMBUFSIZE]; + int err; + lua_tmpnam(buff, err); + if (err) + return luaL_error(L, "unable to generate a unique filename"); + lua_pushstring(L, buff); + return 1; +} + + +static int os_getenv (lua_State *L) { + lua_pushstring(L, getenv(luaL_checkstring(L, 1))); /* if NULL push nil */ + return 1; +} + + +static int os_clock (lua_State *L) { + lua_pushnumber(L, ((lua_Number)clock())/(lua_Number)CLOCKS_PER_SEC); + return 1; +} + + +/* +** {====================================================== +** Time/Date operations +** { year=%Y, month=%m, day=%d, hour=%H, min=%M, sec=%S, +** wday=%w+1, yday=%j, isdst=? } +** ======================================================= +*/ + +static void setfield (lua_State *L, const char *key, int value) { + lua_pushinteger(L, value); + lua_setfield(L, -2, key); +} + +static void setboolfield (lua_State *L, const char *key, int value) { + if (value < 0) /* undefined? */ + return; /* does not set field */ + lua_pushboolean(L, value); + lua_setfield(L, -2, key); +} + +static int getboolfield (lua_State *L, const char *key) { + int res; + lua_getfield(L, -1, key); + res = lua_isnil(L, -1) ? -1 : lua_toboolean(L, -1); + lua_pop(L, 1); + return res; +} + + +static int getfield (lua_State *L, const char *key, int d) { + int res; + lua_getfield(L, -1, key); + if (lua_isnumber(L, -1)) + res = (int)lua_tointeger(L, -1); + else { + if (d < 0) + return luaL_error(L, "field " LUA_QS " missing in date table", key); + res = d; + } + lua_pop(L, 1); + return res; +} + + +static int os_date (lua_State *L) { + const char *s = luaL_optstring(L, 1, "%c"); + time_t t = luaL_opt(L, (time_t)luaL_checknumber, 2, time(NULL)); + struct tm *stm; + if (*s == '!') { /* UTC? */ + stm = gmtime(&t); + s++; /* skip `!' */ + } + else + stm = localtime(&t); + if (stm == NULL) /* invalid date? */ + lua_pushnil(L); + else if (strcmp(s, "*t") == 0) { + lua_createtable(L, 0, 9); /* 9 = number of fields */ + setfield(L, "sec", stm->tm_sec); + setfield(L, "min", stm->tm_min); + setfield(L, "hour", stm->tm_hour); + setfield(L, "day", stm->tm_mday); + setfield(L, "month", stm->tm_mon+1); + setfield(L, "year", stm->tm_year+1900); + setfield(L, "wday", stm->tm_wday+1); + setfield(L, "yday", stm->tm_yday+1); + setboolfield(L, "isdst", stm->tm_isdst); + } + else { + char cc[3]; + luaL_Buffer b; + cc[0] = '%'; cc[2] = '\0'; + luaL_buffinit(L, &b); + for (; *s; s++) { + if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */ + luaL_addchar(&b, *s); + else { + size_t reslen; + char buff[200]; /* should be big enough for any conversion result */ + cc[1] = *(++s); + reslen = strftime(buff, sizeof(buff), cc, stm); + luaL_addlstring(&b, buff, reslen); + } + } + luaL_pushresult(&b); + } + return 1; +} + + +static int os_time (lua_State *L) { + time_t t; + if (lua_isnoneornil(L, 1)) /* called without args? */ + t = time(NULL); /* get current time */ + else { + struct tm ts; + luaL_checktype(L, 1, LUA_TTABLE); + lua_settop(L, 1); /* make sure table is at the top */ + ts.tm_sec = getfield(L, "sec", 0); + ts.tm_min = getfield(L, "min", 0); + ts.tm_hour = getfield(L, "hour", 12); + ts.tm_mday = getfield(L, "day", -1); + ts.tm_mon = getfield(L, "month", -1) - 1; + ts.tm_year = getfield(L, "year", -1) - 1900; + ts.tm_isdst = getboolfield(L, "isdst"); + t = mktime(&ts); + } + if (t == (time_t)(-1)) + lua_pushnil(L); + else + lua_pushnumber(L, (lua_Number)t); + return 1; +} + + +#if !defined LUA_NUMBER_INTEGRAL +static int os_difftime (lua_State *L) { + lua_pushnumber(L, difftime((time_t)(luaL_checknumber(L, 1)), + (time_t)(luaL_optnumber(L, 2, 0)))); + return 1; +} +#endif + +/* }====================================================== */ + + +static int os_setlocale (lua_State *L) { + static const int cat[] = {LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, + LC_NUMERIC, LC_TIME}; + static const char *const catnames[] = {"all", "collate", "ctype", "monetary", + "numeric", "time", NULL}; + const char *l = luaL_optstring(L, 1, NULL); + int op = luaL_checkoption(L, 2, "all", catnames); + lua_pushstring(L, setlocale(cat[op], l)); + return 1; +} + + +static int os_exit (lua_State *L) { + exit(luaL_optint(L, 1, EXIT_SUCCESS)); +} + +static const luaL_Reg syslib[] = { + {"clock", os_clock}, + {"date", os_date}, +#if !defined LUA_NUMBER_INTEGRAL + {"difftime", os_difftime}, +#endif + {"execute", os_execute}, + {"exit", os_exit}, + {"getenv", os_getenv}, + {"remove", os_remove}, + {"rename", os_rename}, + {"setlocale", os_setlocale}, + {"time", os_time}, + {"tmpname", os_tmpname}, + {NULL, NULL} +}; + +/* }====================================================== */ + + + +LUALIB_API int luaopen_os (lua_State *L) { + luaL_register(L, LUA_OSLIBNAME, syslib); + return 1; +} + === added file 'lua/lparser.c' --- lua/lparser.c 1970-01-01 00:00:00 +0000 +++ lua/lparser.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,1341 @@ +/* +** $Id: lparser.c,v 2.42.1.3 2007/12/28 15:32:23 roberto Exp $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#define lparser_c +#define LUA_CORE + +#include "lua.h" + +#include "lcode.h" +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "llex.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lparser.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" + + + +#define hasmultret(k) ((k) == VCALL || (k) == VVARARG) + +#define getlocvar(fs, i) ((fs)->f->locvars[(fs)->actvar[i]]) + +#define luaY_checklimit(fs,v,l,m) if ((v)>(l)) errorlimit(fs,l,m) + + +/* +** nodes for block list (list of active blocks) +*/ +typedef struct BlockCnt { + struct BlockCnt *previous; /* chain */ + int breaklist; /* list of jumps out of this loop */ + lu_byte nactvar; /* # active locals outside the breakable structure */ + lu_byte upval; /* true if some variable in the block is an upvalue */ + lu_byte isbreakable; /* true if `block' is a loop */ +} BlockCnt; + + + +/* +** prototypes for recursive non-terminal functions +*/ +static void chunk (LexState *ls); +static void expr (LexState *ls, expdesc *v); + + +static void anchor_token (LexState *ls) { + if (ls->t.token == TK_NAME || ls->t.token == TK_STRING) { + TString *ts = ls->t.seminfo.ts; + luaX_newstring(ls, getstr(ts), ts->tsv.len); + } +} + + +static void error_expected (LexState *ls, int token) { + luaX_syntaxerror(ls, + luaO_pushfstring(ls->L, LUA_QS " expected", luaX_token2str(ls, token))); +} + + +static void errorlimit (FuncState *fs, int limit, const char *what) { + const char *msg = (fs->f->linedefined == 0) ? + luaO_pushfstring(fs->L, "main function has more than %d %s", limit, what) : + luaO_pushfstring(fs->L, "function at line %d has more than %d %s", + fs->f->linedefined, limit, what); + luaX_lexerror(fs->ls, msg, 0); +} + + +static int testnext (LexState *ls, int c) { + if (ls->t.token == c) { + luaX_next(ls); + return 1; + } + else return 0; +} + + +static void check (LexState *ls, int c) { + if (ls->t.token != c) + error_expected(ls, c); +} + +static void checknext (LexState *ls, int c) { + check(ls, c); + luaX_next(ls); +} + + +#define check_condition(ls,c,msg) { if (!(c)) luaX_syntaxerror(ls, msg); } + + + +static void check_match (LexState *ls, int what, int who, int where) { + if (!testnext(ls, what)) { + if (where == ls->linenumber) + error_expected(ls, what); + else { + luaX_syntaxerror(ls, luaO_pushfstring(ls->L, + LUA_QS " expected (to close " LUA_QS " at line %d)", + luaX_token2str(ls, what), luaX_token2str(ls, who), where)); + } + } +} + + +static TString *str_checkname (LexState *ls) { + TString *ts; + check(ls, TK_NAME); + ts = ls->t.seminfo.ts; + luaX_next(ls); + return ts; +} + + +static void init_exp (expdesc *e, expkind k, int i) { + e->f = e->t = NO_JUMP; + e->k = k; + e->u.s.info = i; +} + + +static void codestring (LexState *ls, expdesc *e, TString *s) { + init_exp(e, VK, luaK_stringK(ls->fs, s)); +} + + +static void checkname(LexState *ls, expdesc *e) { + codestring(ls, e, str_checkname(ls)); +} + + +static int registerlocalvar (LexState *ls, TString *varname) { + FuncState *fs = ls->fs; + Proto *f = fs->f; + int oldsize = f->sizelocvars; + luaM_growvector(ls->L, f->locvars, fs->nlocvars, f->sizelocvars, + LocVar, SHRT_MAX, "too many local variables"); + while (oldsize < f->sizelocvars) f->locvars[oldsize++].varname = NULL; + f->locvars[fs->nlocvars].varname = varname; + luaC_objbarrier(ls->L, f, varname); + return fs->nlocvars++; +} + + +#define new_localvarliteral(ls,v,n) \ + new_localvar(ls, luaX_newstring(ls, "" v, (sizeof(v)/sizeof(char))-1), n) + + +static void new_localvar (LexState *ls, TString *name, int n) { + FuncState *fs = ls->fs; + luaY_checklimit(fs, fs->nactvar+n+1, LUAI_MAXVARS, "local variables"); + fs->actvar[fs->nactvar+n] = cast(unsigned short, registerlocalvar(ls, name)); +} + + +static void adjustlocalvars (LexState *ls, int nvars) { + FuncState *fs = ls->fs; + fs->nactvar = cast_byte(fs->nactvar + nvars); + for (; nvars; nvars--) { + getlocvar(fs, fs->nactvar - nvars).startpc = fs->pc; + } +} + + +static void removevars (LexState *ls, int tolevel) { + FuncState *fs = ls->fs; + while (fs->nactvar > tolevel) + getlocvar(fs, --fs->nactvar).endpc = fs->pc; +} + + +static int indexupvalue (FuncState *fs, TString *name, expdesc *v) { + int i; + Proto *f = fs->f; + int oldsize = f->sizeupvalues; + for (i=0; inups; i++) { + if (fs->upvalues[i].k == v->k && fs->upvalues[i].info == v->u.s.info) { + lua_assert(f->upvalues[i] == name); + return i; + } + } + /* new one */ + luaY_checklimit(fs, f->nups + 1, LUAI_MAXUPVALUES, "upvalues"); + luaM_growvector(fs->L, f->upvalues, f->nups, f->sizeupvalues, + TString *, MAX_INT, ""); + while (oldsize < f->sizeupvalues) f->upvalues[oldsize++] = NULL; + f->upvalues[f->nups] = name; + luaC_objbarrier(fs->L, f, name); + lua_assert(v->k == VLOCAL || v->k == VUPVAL); + fs->upvalues[f->nups].k = cast_byte(v->k); + fs->upvalues[f->nups].info = cast_byte(v->u.s.info); + return f->nups++; +} + + +static int searchvar (FuncState *fs, TString *n) { + int i; + for (i=fs->nactvar-1; i >= 0; i--) { + if (n == getlocvar(fs, i).varname) + return i; + } + return -1; /* not found */ +} + + +static void markupval (FuncState *fs, int level) { + BlockCnt *bl = fs->bl; + while (bl && bl->nactvar > level) bl = bl->previous; + if (bl) bl->upval = 1; +} + + +static int singlevaraux (FuncState *fs, TString *n, expdesc *var, int base) { + if (fs == NULL) { /* no more levels? */ + init_exp(var, VGLOBAL, NO_REG); /* default is global variable */ + return VGLOBAL; + } + else { + int v = searchvar(fs, n); /* look up at current level */ + if (v >= 0) { + init_exp(var, VLOCAL, v); + if (!base) + markupval(fs, v); /* local will be used as an upval */ + return VLOCAL; + } + else { /* not found at current level; try upper one */ + if (singlevaraux(fs->prev, n, var, 0) == VGLOBAL) + return VGLOBAL; + var->u.s.info = indexupvalue(fs, n, var); /* else was LOCAL or UPVAL */ + var->k = VUPVAL; /* upvalue in this level */ + return VUPVAL; + } + } +} + + +static void singlevar (LexState *ls, expdesc *var) { + TString *varname = str_checkname(ls); + FuncState *fs = ls->fs; + if (singlevaraux(fs, varname, var, 1) == VGLOBAL) + var->u.s.info = luaK_stringK(fs, varname); /* info points to global name */ +} + + +static void adjust_assign (LexState *ls, int nvars, int nexps, expdesc *e) { + FuncState *fs = ls->fs; + int extra = nvars - nexps; + if (hasmultret(e->k)) { + extra++; /* includes call itself */ + if (extra < 0) extra = 0; + luaK_setreturns(fs, e, extra); /* last exp. provides the difference */ + if (extra > 1) luaK_reserveregs(fs, extra-1); + } + else { + if (e->k != VVOID) luaK_exp2nextreg(fs, e); /* close last expression */ + if (extra > 0) { + int reg = fs->freereg; + luaK_reserveregs(fs, extra); + luaK_nil(fs, reg, extra); + } + } +} + + +static void enterlevel (LexState *ls) { + if (++ls->L->nCcalls > LUAI_MAXCCALLS) + luaX_lexerror(ls, "chunk has too many syntax levels", 0); +} + + +#define leavelevel(ls) ((ls)->L->nCcalls--) + + +static void enterblock (FuncState *fs, BlockCnt *bl, lu_byte isbreakable) { + bl->breaklist = NO_JUMP; + bl->isbreakable = isbreakable; + bl->nactvar = fs->nactvar; + bl->upval = 0; + bl->previous = fs->bl; + fs->bl = bl; + lua_assert(fs->freereg == fs->nactvar); +} + + +static void leaveblock (FuncState *fs) { + BlockCnt *bl = fs->bl; + fs->bl = bl->previous; + removevars(fs->ls, bl->nactvar); + if (bl->upval) + luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); + /* a block either controls scope or breaks (never both) */ + lua_assert(!bl->isbreakable || !bl->upval); + lua_assert(bl->nactvar == fs->nactvar); + fs->freereg = fs->nactvar; /* free registers */ + luaK_patchtohere(fs, bl->breaklist); +} + + +static void pushclosure (LexState *ls, FuncState *func, expdesc *v) { + FuncState *fs = ls->fs; + Proto *f = fs->f; + int oldsize = f->sizep; + int i; + luaM_growvector(ls->L, f->p, fs->np, f->sizep, Proto *, + MAXARG_Bx, "constant table overflow"); + while (oldsize < f->sizep) f->p[oldsize++] = NULL; + f->p[fs->np++] = func->f; + luaC_objbarrier(ls->L, f, func->f); + init_exp(v, VRELOCABLE, luaK_codeABx(fs, OP_CLOSURE, 0, fs->np-1)); + for (i=0; if->nups; i++) { + OpCode o = (func->upvalues[i].k == VLOCAL) ? OP_MOVE : OP_GETUPVAL; + luaK_codeABC(fs, o, 0, func->upvalues[i].info, 0); + } +} + + +static void open_func (LexState *ls, FuncState *fs) { + lua_State *L = ls->L; + Proto *f = luaF_newproto(L); + fs->f = f; + fs->prev = ls->fs; /* linked list of funcstates */ + fs->ls = ls; + fs->L = L; + ls->fs = fs; + fs->pc = 0; + fs->lasttarget = -1; + fs->jpc = NO_JUMP; + fs->freereg = 0; + fs->nk = 0; + fs->np = 0; + fs->nlocvars = 0; + fs->nactvar = 0; + fs->bl = NULL; + f->source = ls->source; + f->maxstacksize = 2; /* registers 0/1 are always valid */ + fs->h = luaH_new(L, 0, 0); + /* anchor table of constants and prototype (to avoid being collected) */ + sethvalue2s(L, L->top, fs->h); + incr_top(L); + setptvalue2s(L, L->top, f); + incr_top(L); +} + + +static void close_func (LexState *ls) { + lua_State *L = ls->L; + FuncState *fs = ls->fs; + Proto *f = fs->f; + removevars(ls, 0); + luaK_ret(fs, 0, 0); /* final return */ + luaM_reallocvector(L, f->code, f->sizecode, fs->pc, Instruction); + f->sizecode = fs->pc; + luaM_reallocvector(L, f->lineinfo, f->sizelineinfo, fs->pc, int); + f->sizelineinfo = fs->pc; + luaM_reallocvector(L, f->k, f->sizek, fs->nk, TValue); + f->sizek = fs->nk; + luaM_reallocvector(L, f->p, f->sizep, fs->np, Proto *); + f->sizep = fs->np; + luaM_reallocvector(L, f->locvars, f->sizelocvars, fs->nlocvars, LocVar); + f->sizelocvars = fs->nlocvars; + luaM_reallocvector(L, f->upvalues, f->sizeupvalues, f->nups, TString *); + f->sizeupvalues = f->nups; + lua_assert(luaG_checkcode(f)); + lua_assert(fs->bl == NULL); + ls->fs = fs->prev; + L->top -= 2; /* remove table and prototype from the stack */ + /* last token read was anchored in defunct function; must reanchor it */ + if (fs) anchor_token(ls); +} + + +Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, const char *name) { + struct LexState lexstate; + struct FuncState funcstate; + lexstate.buff = buff; + luaX_setinput(L, &lexstate, z, luaS_new(L, name)); + open_func(&lexstate, &funcstate); + funcstate.f->is_vararg = VARARG_ISVARARG; /* main func. is always vararg */ + luaX_next(&lexstate); /* read first token */ + chunk(&lexstate); + check(&lexstate, TK_EOS); + close_func(&lexstate); + lua_assert(funcstate.prev == NULL); + lua_assert(funcstate.f->nups == 0); + lua_assert(lexstate.fs == NULL); + return funcstate.f; +} + + + +/*============================================================*/ +/* GRAMMAR RULES */ +/*============================================================*/ + + +static void field (LexState *ls, expdesc *v) { + /* field -> ['.' | ':'] NAME */ + FuncState *fs = ls->fs; + expdesc key; + luaK_exp2anyreg(fs, v); + luaX_next(ls); /* skip the dot or colon */ + checkname(ls, &key); + luaK_indexed(fs, v, &key); +} + + +static void yindex (LexState *ls, expdesc *v) { + /* index -> '[' expr ']' */ + luaX_next(ls); /* skip the '[' */ + expr(ls, v); + luaK_exp2val(ls->fs, v); + checknext(ls, ']'); +} + + +/* +** {====================================================================== +** Rules for Constructors +** ======================================================================= +*/ + + +struct ConsControl { + expdesc v; /* last list item read */ + expdesc *t; /* table descriptor */ + int nh; /* total number of `record' elements */ + int na; /* total number of array elements */ + int tostore; /* number of array elements pending to be stored */ +}; + + +static void recfield (LexState *ls, struct ConsControl *cc) { + /* recfield -> (NAME | `['exp1`]') = exp1 */ + FuncState *fs = ls->fs; + int reg = ls->fs->freereg; + expdesc key, val; + int rkkey; + if (ls->t.token == TK_NAME) { + luaY_checklimit(fs, cc->nh, MAX_INT, "items in a constructor"); + checkname(ls, &key); + } + else /* ls->t.token == '[' */ + yindex(ls, &key); + cc->nh++; + checknext(ls, '='); + rkkey = luaK_exp2RK(fs, &key); + expr(ls, &val); + luaK_codeABC(fs, OP_SETTABLE, cc->t->u.s.info, rkkey, luaK_exp2RK(fs, &val)); + fs->freereg = reg; /* free registers */ +} + + +static void closelistfield (FuncState *fs, struct ConsControl *cc) { + if (cc->v.k == VVOID) return; /* there is no list item */ + luaK_exp2nextreg(fs, &cc->v); + cc->v.k = VVOID; + if (cc->tostore == LFIELDS_PER_FLUSH) { + luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); /* flush */ + cc->tostore = 0; /* no more items pending */ + } +} + + +static void lastlistfield (FuncState *fs, struct ConsControl *cc) { + if (cc->tostore == 0) return; + if (hasmultret(cc->v.k)) { + luaK_setmultret(fs, &cc->v); + luaK_setlist(fs, cc->t->u.s.info, cc->na, LUA_MULTRET); + cc->na--; /* do not count last expression (unknown number of elements) */ + } + else { + if (cc->v.k != VVOID) + luaK_exp2nextreg(fs, &cc->v); + luaK_setlist(fs, cc->t->u.s.info, cc->na, cc->tostore); + } +} + + +static void listfield (LexState *ls, struct ConsControl *cc) { + expr(ls, &cc->v); + luaY_checklimit(ls->fs, cc->na, MAX_INT, "items in a constructor"); + cc->na++; + cc->tostore++; +} + + +static void constructor (LexState *ls, expdesc *t) { + /* constructor -> ?? */ + FuncState *fs = ls->fs; + int line = ls->linenumber; + int pc = luaK_codeABC(fs, OP_NEWTABLE, 0, 0, 0); + struct ConsControl cc; + cc.na = cc.nh = cc.tostore = 0; + cc.t = t; + init_exp(t, VRELOCABLE, pc); + init_exp(&cc.v, VVOID, 0); /* no value (yet) */ + luaK_exp2nextreg(ls->fs, t); /* fix it at stack top (for gc) */ + checknext(ls, '{'); + do { + lua_assert(cc.v.k == VVOID || cc.tostore > 0); + if (ls->t.token == '}') break; + closelistfield(fs, &cc); + switch(ls->t.token) { + case TK_NAME: { /* may be listfields or recfields */ + luaX_lookahead(ls); + if (ls->lookahead.token != '=') /* expression? */ + listfield(ls, &cc); + else + recfield(ls, &cc); + break; + } + case '[': { /* constructor_item -> recfield */ + recfield(ls, &cc); + break; + } + default: { /* constructor_part -> listfield */ + listfield(ls, &cc); + break; + } + } + } while (testnext(ls, ',') || testnext(ls, ';')); + check_match(ls, '}', '{', line); + lastlistfield(fs, &cc); + SETARG_B(fs->f->code[pc], luaO_int2fb(cc.na)); /* set initial array size */ + SETARG_C(fs->f->code[pc], luaO_int2fb(cc.nh)); /* set initial table size */ +} + +/* }====================================================================== */ + + + +static void parlist (LexState *ls) { + /* parlist -> [ param { `,' param } ] */ + FuncState *fs = ls->fs; + Proto *f = fs->f; + int nparams = 0; + f->is_vararg = 0; + if (ls->t.token != ')') { /* is `parlist' not empty? */ + do { + switch (ls->t.token) { + case TK_NAME: { /* param -> NAME */ + new_localvar(ls, str_checkname(ls), nparams++); + break; + } + case TK_DOTS: { /* param -> `...' */ + luaX_next(ls); +#if defined(LUA_COMPAT_VARARG) + /* use `arg' as default name */ + new_localvarliteral(ls, "arg", nparams++); + f->is_vararg = VARARG_HASARG | VARARG_NEEDSARG; +#endif + f->is_vararg |= VARARG_ISVARARG; + break; + } + default: luaX_syntaxerror(ls, " or " LUA_QL("...") " expected"); + } + } while (!f->is_vararg && testnext(ls, ',')); + } + adjustlocalvars(ls, nparams); + f->numparams = cast_byte(fs->nactvar - (f->is_vararg & VARARG_HASARG)); + luaK_reserveregs(fs, fs->nactvar); /* reserve register for parameters */ +} + + +static void body (LexState *ls, expdesc *e, int needself, int line) { + /* body -> `(' parlist `)' chunk END */ + FuncState new_fs; + open_func(ls, &new_fs); + new_fs.f->linedefined = line; + checknext(ls, '('); + if (needself) { + new_localvarliteral(ls, "self", 0); + adjustlocalvars(ls, 1); + } + parlist(ls); + checknext(ls, ')'); + chunk(ls); + new_fs.f->lastlinedefined = ls->linenumber; + check_match(ls, TK_END, TK_FUNCTION, line); + close_func(ls); + pushclosure(ls, &new_fs, e); +} + + +static int explist1 (LexState *ls, expdesc *v) { + /* explist1 -> expr { `,' expr } */ + int n = 1; /* at least one expression */ + expr(ls, v); + while (testnext(ls, ',')) { + luaK_exp2nextreg(ls->fs, v); + expr(ls, v); + n++; + } + return n; +} + + +static void funcargs (LexState *ls, expdesc *f) { + FuncState *fs = ls->fs; + expdesc args; + int base, nparams; + int line = ls->linenumber; + switch (ls->t.token) { + case '(': { /* funcargs -> `(' [ explist1 ] `)' */ + if (line != ls->lastline) + luaX_syntaxerror(ls,"ambiguous syntax (function call x new statement)"); + luaX_next(ls); + if (ls->t.token == ')') /* arg list is empty? */ + args.k = VVOID; + else { + explist1(ls, &args); + luaK_setmultret(fs, &args); + } + check_match(ls, ')', '(', line); + break; + } + case '{': { /* funcargs -> constructor */ + constructor(ls, &args); + break; + } + case TK_STRING: { /* funcargs -> STRING */ + codestring(ls, &args, ls->t.seminfo.ts); + luaX_next(ls); /* must use `seminfo' before `next' */ + break; + } + default: { + luaX_syntaxerror(ls, "function arguments expected"); + return; + } + } + lua_assert(f->k == VNONRELOC); + base = f->u.s.info; /* base register for call */ + if (hasmultret(args.k)) + nparams = LUA_MULTRET; /* open call */ + else { + if (args.k != VVOID) + luaK_exp2nextreg(fs, &args); /* close last argument */ + nparams = fs->freereg - (base+1); + } + init_exp(f, VCALL, luaK_codeABC(fs, OP_CALL, base, nparams+1, 2)); + luaK_fixline(fs, line); + fs->freereg = base+1; /* call remove function and arguments and leaves + (unless changed) one result */ +} + + + + +/* +** {====================================================================== +** Expression parsing +** ======================================================================= +*/ + + +static void prefixexp (LexState *ls, expdesc *v) { + /* prefixexp -> NAME | '(' expr ')' */ + switch (ls->t.token) { + case '(': { + int line = ls->linenumber; + luaX_next(ls); + expr(ls, v); + check_match(ls, ')', '(', line); + luaK_dischargevars(ls->fs, v); + return; + } + case TK_NAME: { + singlevar(ls, v); + return; + } + default: { + luaX_syntaxerror(ls, "unexpected symbol"); + return; + } + } +} + + +static void primaryexp (LexState *ls, expdesc *v) { + /* primaryexp -> + prefixexp { `.' NAME | `[' exp `]' | `:' NAME funcargs | funcargs } */ + FuncState *fs = ls->fs; + prefixexp(ls, v); + for (;;) { + switch (ls->t.token) { + case '.': { /* field */ + field(ls, v); + break; + } + case '[': { /* `[' exp1 `]' */ + expdesc key; + luaK_exp2anyreg(fs, v); + yindex(ls, &key); + luaK_indexed(fs, v, &key); + break; + } + case ':': { /* `:' NAME funcargs */ + expdesc key; + luaX_next(ls); + checkname(ls, &key); + luaK_self(fs, v, &key); + funcargs(ls, v); + break; + } + case '(': case TK_STRING: case '{': { /* funcargs */ + luaK_exp2nextreg(fs, v); + funcargs(ls, v); + break; + } + default: return; + } + } +} + + +static void simpleexp (LexState *ls, expdesc *v) { + /* simpleexp -> NUMBER | STRING | NIL | true | false | ... | + constructor | FUNCTION body | primaryexp */ + switch (ls->t.token) { + case TK_NUMBER: { + init_exp(v, VKNUM, 0); + v->u.nval = ls->t.seminfo.r; + break; + } + case TK_STRING: { + codestring(ls, v, ls->t.seminfo.ts); + break; + } + case TK_NIL: { + init_exp(v, VNIL, 0); + break; + } + case TK_TRUE: { + init_exp(v, VTRUE, 0); + break; + } + case TK_FALSE: { + init_exp(v, VFALSE, 0); + break; + } + case TK_DOTS: { /* vararg */ + FuncState *fs = ls->fs; + check_condition(ls, fs->f->is_vararg, + "cannot use " LUA_QL("...") " outside a vararg function"); + fs->f->is_vararg &= ~VARARG_NEEDSARG; /* don't need 'arg' */ + init_exp(v, VVARARG, luaK_codeABC(fs, OP_VARARG, 0, 1, 0)); + break; + } + case '{': { /* constructor */ + constructor(ls, v); + return; + } + case TK_FUNCTION: { + luaX_next(ls); + body(ls, v, 0, ls->linenumber); + return; + } + default: { + primaryexp(ls, v); + return; + } + } + luaX_next(ls); +} + + +static UnOpr getunopr (int op) { + switch (op) { + case TK_NOT: return OPR_NOT; + case '-': return OPR_MINUS; + case '#': return OPR_LEN; + default: return OPR_NOUNOPR; + } +} + + +static BinOpr getbinopr (int op) { + switch (op) { + case '+': return OPR_ADD; + case '-': return OPR_SUB; + case '*': return OPR_MUL; + case '/': return OPR_DIV; + case '%': return OPR_MOD; + case '^': return OPR_POW; + case TK_CONCAT: return OPR_CONCAT; + case TK_NE: return OPR_NE; + case TK_EQ: return OPR_EQ; + case '<': return OPR_LT; + case TK_LE: return OPR_LE; + case '>': return OPR_GT; + case TK_GE: return OPR_GE; + case TK_AND: return OPR_AND; + case TK_OR: return OPR_OR; + default: return OPR_NOBINOPR; + } +} + + +static const struct { + lu_byte left; /* left priority for each binary operator */ + lu_byte right; /* right priority */ +} priority[] = { /* ORDER OPR */ + {6, 6}, {6, 6}, {7, 7}, {7, 7}, {7, 7}, /* `+' `-' `/' `%' */ + {10, 9}, {5, 4}, /* power and concat (right associative) */ + {3, 3}, {3, 3}, /* equality and inequality */ + {3, 3}, {3, 3}, {3, 3}, {3, 3}, /* order */ + {2, 2}, {1, 1} /* logical (and/or) */ +}; + +#define UNARY_PRIORITY 8 /* priority for unary operators */ + + +/* +** subexpr -> (simpleexp | unop subexpr) { binop subexpr } +** where `binop' is any binary operator with a priority higher than `limit' +*/ +static BinOpr subexpr (LexState *ls, expdesc *v, unsigned int limit) { + BinOpr op; + UnOpr uop; + enterlevel(ls); + uop = getunopr(ls->t.token); + if (uop != OPR_NOUNOPR) { + luaX_next(ls); + subexpr(ls, v, UNARY_PRIORITY); + luaK_prefix(ls->fs, uop, v); + } + else simpleexp(ls, v); + /* expand while operators have priorities higher than `limit' */ + op = getbinopr(ls->t.token); + while (op != OPR_NOBINOPR && priority[op].left > limit) { + expdesc v2; + BinOpr nextop; + luaX_next(ls); + luaK_infix(ls->fs, op, v); + /* read sub-expression with higher priority */ + nextop = subexpr(ls, &v2, priority[op].right); + luaK_posfix(ls->fs, op, v, &v2); + op = nextop; + } + leavelevel(ls); + return op; /* return first untreated operator */ +} + + +static void expr (LexState *ls, expdesc *v) { + subexpr(ls, v, 0); +} + +/* }==================================================================== */ + + + +/* +** {====================================================================== +** Rules for Statements +** ======================================================================= +*/ + + +static int block_follow (int token) { + switch (token) { + case TK_ELSE: case TK_ELSEIF: case TK_END: + case TK_UNTIL: case TK_EOS: + return 1; + default: return 0; + } +} + + +static void block (LexState *ls) { + /* block -> chunk */ + FuncState *fs = ls->fs; + BlockCnt bl; + enterblock(fs, &bl, 0); + chunk(ls); + lua_assert(bl.breaklist == NO_JUMP); + leaveblock(fs); +} + + +/* +** structure to chain all variables in the left-hand side of an +** assignment +*/ +struct LHS_assign { + struct LHS_assign *prev; + expdesc v; /* variable (global, local, upvalue, or indexed) */ +}; + + +/* +** check whether, in an assignment to a local variable, the local variable +** is needed in a previous assignment (to a table). If so, save original +** local value in a safe place and use this safe copy in the previous +** assignment. +*/ +static void check_conflict (LexState *ls, struct LHS_assign *lh, expdesc *v) { + FuncState *fs = ls->fs; + int extra = fs->freereg; /* eventual position to save local variable */ + int conflict = 0; + for (; lh; lh = lh->prev) { + if (lh->v.k == VINDEXED) { + if (lh->v.u.s.info == v->u.s.info) { /* conflict? */ + conflict = 1; + lh->v.u.s.info = extra; /* previous assignment will use safe copy */ + } + if (lh->v.u.s.aux == v->u.s.info) { /* conflict? */ + conflict = 1; + lh->v.u.s.aux = extra; /* previous assignment will use safe copy */ + } + } + } + if (conflict) { + luaK_codeABC(fs, OP_MOVE, fs->freereg, v->u.s.info, 0); /* make copy */ + luaK_reserveregs(fs, 1); + } +} + + +static void assignment (LexState *ls, struct LHS_assign *lh, int nvars) { + expdesc e; + check_condition(ls, VLOCAL <= lh->v.k && lh->v.k <= VINDEXED, + "syntax error"); + if (testnext(ls, ',')) { /* assignment -> `,' primaryexp assignment */ + struct LHS_assign nv; + nv.prev = lh; + primaryexp(ls, &nv.v); + if (nv.v.k == VLOCAL) + check_conflict(ls, lh, &nv.v); + luaY_checklimit(ls->fs, nvars, LUAI_MAXCCALLS - ls->L->nCcalls, + "variables in assignment"); + assignment(ls, &nv, nvars+1); + } + else { /* assignment -> `=' explist1 */ + int nexps; + checknext(ls, '='); + nexps = explist1(ls, &e); + if (nexps != nvars) { + adjust_assign(ls, nvars, nexps, &e); + if (nexps > nvars) + ls->fs->freereg -= nexps - nvars; /* remove extra values */ + } + else { + luaK_setoneret(ls->fs, &e); /* close last expression */ + luaK_storevar(ls->fs, &lh->v, &e); + return; /* avoid default */ + } + } + init_exp(&e, VNONRELOC, ls->fs->freereg-1); /* default assignment */ + luaK_storevar(ls->fs, &lh->v, &e); +} + + +static int cond (LexState *ls) { + /* cond -> exp */ + expdesc v; + expr(ls, &v); /* read condition */ + if (v.k == VNIL) v.k = VFALSE; /* `falses' are all equal here */ + luaK_goiftrue(ls->fs, &v); + return v.f; +} + + +static void breakstat (LexState *ls) { + FuncState *fs = ls->fs; + BlockCnt *bl = fs->bl; + int upval = 0; + while (bl && !bl->isbreakable) { + upval |= bl->upval; + bl = bl->previous; + } + if (!bl) + luaX_syntaxerror(ls, "no loop to break"); + if (upval) + luaK_codeABC(fs, OP_CLOSE, bl->nactvar, 0, 0); + luaK_concat(fs, &bl->breaklist, luaK_jump(fs)); +} + + +static void whilestat (LexState *ls, int line) { + /* whilestat -> WHILE cond DO block END */ + FuncState *fs = ls->fs; + int whileinit; + int condexit; + BlockCnt bl; + luaX_next(ls); /* skip WHILE */ + whileinit = luaK_getlabel(fs); + condexit = cond(ls); + enterblock(fs, &bl, 1); + checknext(ls, TK_DO); + block(ls); + luaK_patchlist(fs, luaK_jump(fs), whileinit); + check_match(ls, TK_END, TK_WHILE, line); + leaveblock(fs); + luaK_patchtohere(fs, condexit); /* false conditions finish the loop */ +} + + +static void repeatstat (LexState *ls, int line) { + /* repeatstat -> REPEAT block UNTIL cond */ + int condexit; + FuncState *fs = ls->fs; + int repeat_init = luaK_getlabel(fs); + BlockCnt bl1, bl2; + enterblock(fs, &bl1, 1); /* loop block */ + enterblock(fs, &bl2, 0); /* scope block */ + luaX_next(ls); /* skip REPEAT */ + chunk(ls); + check_match(ls, TK_UNTIL, TK_REPEAT, line); + condexit = cond(ls); /* read condition (inside scope block) */ + if (!bl2.upval) { /* no upvalues? */ + leaveblock(fs); /* finish scope */ + luaK_patchlist(ls->fs, condexit, repeat_init); /* close the loop */ + } + else { /* complete semantics when there are upvalues */ + breakstat(ls); /* if condition then break */ + luaK_patchtohere(ls->fs, condexit); /* else... */ + leaveblock(fs); /* finish scope... */ + luaK_patchlist(ls->fs, luaK_jump(fs), repeat_init); /* and repeat */ + } + leaveblock(fs); /* finish loop */ +} + + +static int exp1 (LexState *ls) { + expdesc e; + int k; + expr(ls, &e); + k = e.k; + luaK_exp2nextreg(ls->fs, &e); + return k; +} + + +static void forbody (LexState *ls, int base, int line, int nvars, int isnum) { + /* forbody -> DO block */ + BlockCnt bl; + FuncState *fs = ls->fs; + int prep, endfor; + adjustlocalvars(ls, 3); /* control variables */ + checknext(ls, TK_DO); + prep = isnum ? luaK_codeAsBx(fs, OP_FORPREP, base, NO_JUMP) : luaK_jump(fs); + enterblock(fs, &bl, 0); /* scope for declared variables */ + adjustlocalvars(ls, nvars); + luaK_reserveregs(fs, nvars); + block(ls); + leaveblock(fs); /* end of scope for declared variables */ + luaK_patchtohere(fs, prep); + endfor = (isnum) ? luaK_codeAsBx(fs, OP_FORLOOP, base, NO_JUMP) : + luaK_codeABC(fs, OP_TFORLOOP, base, 0, nvars); + luaK_fixline(fs, line); /* pretend that `OP_FOR' starts the loop */ + luaK_patchlist(fs, (isnum ? endfor : luaK_jump(fs)), prep + 1); +} + + +static void fornum (LexState *ls, TString *varname, int line) { + /* fornum -> NAME = exp1,exp1[,exp1] forbody */ + FuncState *fs = ls->fs; + int base = fs->freereg; + new_localvarliteral(ls, "(for index)", 0); + new_localvarliteral(ls, "(for limit)", 1); + new_localvarliteral(ls, "(for step)", 2); + new_localvar(ls, varname, 3); + checknext(ls, '='); + exp1(ls); /* initial value */ + checknext(ls, ','); + exp1(ls); /* limit */ + if (testnext(ls, ',')) + exp1(ls); /* optional step */ + else { /* default step = 1 */ + luaK_codeABx(fs, OP_LOADK, fs->freereg, luaK_numberK(fs, 1)); + luaK_reserveregs(fs, 1); + } + forbody(ls, base, line, 1, 1); +} + + +static void forlist (LexState *ls, TString *indexname) { + /* forlist -> NAME {,NAME} IN explist1 forbody */ + FuncState *fs = ls->fs; + expdesc e; + int nvars = 0; + int line; + int base = fs->freereg; + /* create control variables */ + new_localvarliteral(ls, "(for generator)", nvars++); + new_localvarliteral(ls, "(for state)", nvars++); + new_localvarliteral(ls, "(for control)", nvars++); + /* create declared variables */ + new_localvar(ls, indexname, nvars++); + while (testnext(ls, ',')) + new_localvar(ls, str_checkname(ls), nvars++); + checknext(ls, TK_IN); + line = ls->linenumber; + adjust_assign(ls, 3, explist1(ls, &e), &e); + luaK_checkstack(fs, 3); /* extra space to call generator */ + forbody(ls, base, line, nvars - 3, 0); +} + + +static void forstat (LexState *ls, int line) { + /* forstat -> FOR (fornum | forlist) END */ + FuncState *fs = ls->fs; + TString *varname; + BlockCnt bl; + enterblock(fs, &bl, 1); /* scope for loop and control variables */ + luaX_next(ls); /* skip `for' */ + varname = str_checkname(ls); /* first variable name */ + switch (ls->t.token) { + case '=': fornum(ls, varname, line); break; + case ',': case TK_IN: forlist(ls, varname); break; + default: luaX_syntaxerror(ls, LUA_QL("=") " or " LUA_QL("in") " expected"); + } + check_match(ls, TK_END, TK_FOR, line); + leaveblock(fs); /* loop scope (`break' jumps to this point) */ +} + + +static int test_then_block (LexState *ls) { + /* test_then_block -> [IF | ELSEIF] cond THEN block */ + int condexit; + luaX_next(ls); /* skip IF or ELSEIF */ + condexit = cond(ls); + checknext(ls, TK_THEN); + block(ls); /* `then' part */ + return condexit; +} + + +static void ifstat (LexState *ls, int line) { + /* ifstat -> IF cond THEN block {ELSEIF cond THEN block} [ELSE block] END */ + FuncState *fs = ls->fs; + int flist; + int escapelist = NO_JUMP; + flist = test_then_block(ls); /* IF cond THEN block */ + while (ls->t.token == TK_ELSEIF) { + luaK_concat(fs, &escapelist, luaK_jump(fs)); + luaK_patchtohere(fs, flist); + flist = test_then_block(ls); /* ELSEIF cond THEN block */ + } + if (ls->t.token == TK_ELSE) { + luaK_concat(fs, &escapelist, luaK_jump(fs)); + luaK_patchtohere(fs, flist); + luaX_next(ls); /* skip ELSE (after patch, for correct line info) */ + block(ls); /* `else' part */ + } + else + luaK_concat(fs, &escapelist, flist); + luaK_patchtohere(fs, escapelist); + check_match(ls, TK_END, TK_IF, line); +} + + +static void localfunc (LexState *ls) { + expdesc v, b; + FuncState *fs = ls->fs; + new_localvar(ls, str_checkname(ls), 0); + init_exp(&v, VLOCAL, fs->freereg); + luaK_reserveregs(fs, 1); + adjustlocalvars(ls, 1); + body(ls, &b, 0, ls->linenumber); + luaK_storevar(fs, &v, &b); + /* debug information will only see the variable after this point! */ + getlocvar(fs, fs->nactvar - 1).startpc = fs->pc; +} + + +static void localstat (LexState *ls) { + /* stat -> LOCAL NAME {`,' NAME} [`=' explist1] */ + int nvars = 0; + int nexps; + expdesc e; + do { + new_localvar(ls, str_checkname(ls), nvars++); + } while (testnext(ls, ',')); + if (testnext(ls, '=')) + nexps = explist1(ls, &e); + else { + e.k = VVOID; + nexps = 0; + } + adjust_assign(ls, nvars, nexps, &e); + adjustlocalvars(ls, nvars); +} + + +static int funcname (LexState *ls, expdesc *v) { + /* funcname -> NAME {field} [`:' NAME] */ + int needself = 0; + singlevar(ls, v); + while (ls->t.token == '.') + field(ls, v); + if (ls->t.token == ':') { + needself = 1; + field(ls, v); + } + return needself; +} + + +static void funcstat (LexState *ls, int line) { + /* funcstat -> FUNCTION funcname body */ + int needself; + expdesc v, b; + luaX_next(ls); /* skip FUNCTION */ + needself = funcname(ls, &v); + body(ls, &b, needself, line); + luaK_storevar(ls->fs, &v, &b); + luaK_fixline(ls->fs, line); /* definition `happens' in the first line */ +} + + +static void exprstat (LexState *ls) { + /* stat -> func | assignment */ + FuncState *fs = ls->fs; + struct LHS_assign v; + primaryexp(ls, &v.v); + if (v.v.k == VCALL) /* stat -> func */ + SETARG_C(getcode(fs, &v.v), 1); /* call statement uses no results */ + else { /* stat -> assignment */ + v.prev = NULL; + assignment(ls, &v, 1); + } +} + + +static void retstat (LexState *ls) { + /* stat -> RETURN explist */ + FuncState *fs = ls->fs; + expdesc e; + int first, nret; /* registers with returned values */ + luaX_next(ls); /* skip RETURN */ + if (block_follow(ls->t.token) || ls->t.token == ';') + first = nret = 0; /* return no values */ + else { + nret = explist1(ls, &e); /* optional return values */ + if (hasmultret(e.k)) { + luaK_setmultret(fs, &e); + if (e.k == VCALL && nret == 1) { /* tail call? */ + SET_OPCODE(getcode(fs,&e), OP_TAILCALL); + lua_assert(GETARG_A(getcode(fs,&e)) == fs->nactvar); + } + first = fs->nactvar; + nret = LUA_MULTRET; /* return all values */ + } + else { + if (nret == 1) /* only one single value? */ + first = luaK_exp2anyreg(fs, &e); + else { + luaK_exp2nextreg(fs, &e); /* values must go to the `stack' */ + first = fs->nactvar; /* return all `active' values */ + lua_assert(nret == fs->freereg - first); + } + } + } + luaK_ret(fs, first, nret); +} + + +static int statement (LexState *ls) { + int line = ls->linenumber; /* may be needed for error messages */ + switch (ls->t.token) { + case TK_IF: { /* stat -> ifstat */ + ifstat(ls, line); + return 0; + } + case TK_WHILE: { /* stat -> whilestat */ + whilestat(ls, line); + return 0; + } + case TK_DO: { /* stat -> DO block END */ + luaX_next(ls); /* skip DO */ + block(ls); + check_match(ls, TK_END, TK_DO, line); + return 0; + } + case TK_FOR: { /* stat -> forstat */ + forstat(ls, line); + return 0; + } + case TK_REPEAT: { /* stat -> repeatstat */ + repeatstat(ls, line); + return 0; + } + case TK_FUNCTION: { + funcstat(ls, line); /* stat -> funcstat */ + return 0; + } + case TK_LOCAL: { /* stat -> localstat */ + luaX_next(ls); /* skip LOCAL */ + if (testnext(ls, TK_FUNCTION)) /* local function? */ + localfunc(ls); + else + localstat(ls); + return 0; + } + case TK_RETURN: { /* stat -> retstat */ + retstat(ls); + return 1; /* must be last statement */ + } + case TK_BREAK: { /* stat -> breakstat */ + luaX_next(ls); /* skip BREAK */ + breakstat(ls); + return 1; /* must be last statement */ + } + default: { + exprstat(ls); + return 0; /* to avoid warnings */ + } + } +} + + +static void chunk (LexState *ls) { + /* chunk -> { stat [`;'] } */ + int islast = 0; + enterlevel(ls); + while (!islast && !block_follow(ls->t.token)) { + islast = statement(ls); + testnext(ls, ';'); + lua_assert(ls->fs->f->maxstacksize >= ls->fs->freereg && + ls->fs->freereg >= ls->fs->nactvar); + ls->fs->freereg = ls->fs->nactvar; /* free registers */ + } + leavelevel(ls); +} + +/* }====================================================================== */ === added file 'lua/lparser.h' --- lua/lparser.h 1970-01-01 00:00:00 +0000 +++ lua/lparser.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,82 @@ +/* +** $Id: lparser.h,v 1.57.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua Parser +** See Copyright Notice in lua.h +*/ + +#ifndef lparser_h +#define lparser_h + +#include "llimits.h" +#include "lobject.h" +#include "lzio.h" + + +/* +** Expression descriptor +*/ + +typedef enum { + VVOID, /* no value */ + VNIL, + VTRUE, + VFALSE, + VK, /* info = index of constant in `k' */ + VKNUM, /* nval = numerical value */ + VLOCAL, /* info = local register */ + VUPVAL, /* info = index of upvalue in `upvalues' */ + VGLOBAL, /* info = index of table; aux = index of global name in `k' */ + VINDEXED, /* info = table register; aux = index register (or `k') */ + VJMP, /* info = instruction pc */ + VRELOCABLE, /* info = instruction pc */ + VNONRELOC, /* info = result register */ + VCALL, /* info = instruction pc */ + VVARARG /* info = instruction pc */ +} expkind; + +typedef struct expdesc { + expkind k; + union { + struct { int info, aux; } s; + lua_Number nval; + } u; + int t; /* patch list of `exit when true' */ + int f; /* patch list of `exit when false' */ +} expdesc; + + +typedef struct upvaldesc { + lu_byte k; + lu_byte info; +} upvaldesc; + + +struct BlockCnt; /* defined in lparser.c */ + + +/* state needed to generate code for a given function */ +typedef struct FuncState { + Proto *f; /* current function header */ + Table *h; /* table to find (and reuse) elements in `k' */ + struct FuncState *prev; /* enclosing function */ + struct LexState *ls; /* lexical state */ + struct lua_State *L; /* copy of the Lua state */ + struct BlockCnt *bl; /* chain of current blocks */ + int pc; /* next position to code (equivalent to `ncode') */ + int lasttarget; /* `pc' of last `jump target' */ + int jpc; /* list of pending jumps to `pc' */ + int freereg; /* first free register */ + int nk; /* number of elements in `k' */ + int np; /* number of elements in `p' */ + short nlocvars; /* number of elements in `locvars' */ + lu_byte nactvar; /* number of active local variables */ + upvaldesc upvalues[LUAI_MAXUPVALUES]; /* upvalues */ + unsigned short actvar[LUAI_MAXVARS]; /* declared-variable stack */ +} FuncState; + + +LUAI_FUNC Proto *luaY_parser (lua_State *L, ZIO *z, Mbuffer *buff, + const char *name); + + +#endif === added file 'lua/lstate.c' --- lua/lstate.c 1970-01-01 00:00:00 +0000 +++ lua/lstate.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,216 @@ +/* +** $Id: lstate.c,v 2.36.1.2 2008/01/03 15:20:39 roberto Exp $ +** Global State +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#define lstate_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "llex.h" +#include "lmem.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + +#define state_size(x) (sizeof(x) + LUAI_EXTRASPACE) +#define fromstate(l) (cast(lu_byte *, (l)) - LUAI_EXTRASPACE) +#define tostate(l) (cast(lua_State *, cast(lu_byte *, l) + LUAI_EXTRASPACE)) + + +/* +** Main thread combines a thread state and the global state +*/ +typedef struct LG { + lua_State l; + global_State g; +} LG; + + + +static void stack_init (lua_State *L1, lua_State *L) { + /* initialize CallInfo array */ + L1->base_ci = luaM_newvector(L, BASIC_CI_SIZE, CallInfo); + L1->ci = L1->base_ci; + L1->size_ci = BASIC_CI_SIZE; + L1->end_ci = L1->base_ci + L1->size_ci - 1; + /* initialize stack array */ + L1->stack = luaM_newvector(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue); + L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK; + L1->top = L1->stack; + L1->stack_last = L1->stack+(L1->stacksize - EXTRA_STACK)-1; + /* initialize first ci */ + L1->ci->func = L1->top; + setnilvalue(L1->top++); /* `function' entry for this `ci' */ + L1->base = L1->ci->base = L1->top; + L1->ci->top = L1->top + LUA_MINSTACK; +} + + +static void freestack (lua_State *L, lua_State *L1) { + luaM_freearray(L, L1->base_ci, L1->size_ci, CallInfo); + luaM_freearray(L, L1->stack, L1->stacksize, TValue); +} + + +/* +** open parts that may cause memory-allocation errors +*/ +static void f_luaopen (lua_State *L, void *ud) { + global_State *g = G(L); + UNUSED(ud); + stack_init(L, L); /* init stack */ + sethvalue(L, gt(L), luaH_new(L, 0, 2)); /* table of globals */ + sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */ + luaS_resize(L, MINSTRTABSIZE); /* initial size of string table */ + luaT_init(L); + luaX_init(L); + luaS_fix(luaS_newliteral(L, MEMERRMSG)); + g->GCthreshold = 4*g->totalbytes; +} + + +static void preinit_state (lua_State *L, global_State *g) { + G(L) = g; + L->stack = NULL; + L->stacksize = 0; + L->errorJmp = NULL; + L->hook = NULL; + L->hookmask = 0; + L->basehookcount = 0; + L->allowhook = 1; + resethookcount(L); + L->openupval = NULL; + L->size_ci = 0; + L->nCcalls = L->baseCcalls = 0; + L->status = 0; + L->base_ci = L->ci = NULL; + L->savedpc = NULL; + L->errfunc = 0; + setnilvalue(gt(L)); +} + + +static void close_state (lua_State *L) { + global_State *g = G(L); + luaF_close(L, L->stack); /* close all upvalues for this thread */ + luaC_freeall(L); /* collect all objects */ + lua_assert(g->rootgc == obj2gco(L)); + lua_assert(g->strt.nuse == 0); + luaM_freearray(L, G(L)->strt.hash, G(L)->strt.size, TString *); + luaZ_freebuffer(L, &g->buff); + freestack(L, L); + lua_assert(g->totalbytes == sizeof(LG)); + (*g->frealloc)(g->ud, fromstate(L), state_size(LG), 0); +} + + +lua_State *luaE_newthread (lua_State *L) { + lua_State *L1 = tostate(luaM_malloc(L, state_size(lua_State))); + luaC_link(L, obj2gco(L1), LUA_TTHREAD); + preinit_state(L1, G(L)); + stack_init(L1, L); /* init stack */ + setobj2n(L, gt(L1), gt(L)); /* share table of globals */ + L1->hookmask = L->hookmask; + L1->basehookcount = L->basehookcount; + L1->hook = L->hook; + resethookcount(L1); + lua_assert(iswhite(obj2gco(L1))); + return L1; +} + + +void luaE_freethread (lua_State *L, lua_State *L1) { + luaF_close(L1, L1->stack); /* close all upvalues for this thread */ + lua_assert(L1->openupval == NULL); + luai_userstatefree(L1); + freestack(L, L1); + luaM_freemem(L, fromstate(L1), state_size(lua_State)); +} + + +LUA_API lua_State *lua_newstate (lua_Alloc f, void *ud) { + int i; + lua_State *L; + global_State *g; + void *l = (*f)(ud, NULL, 0, state_size(LG)); + if (l == NULL) return NULL; + L = tostate(l); + g = &((LG *)L)->g; + L->next = NULL; + L->tt = LUA_TTHREAD; + g->currentwhite = bit2mask(WHITE0BIT, FIXEDBIT); + L->marked = luaC_white(g); + set2bits(L->marked, FIXEDBIT, SFIXEDBIT); + preinit_state(L, g); + g->frealloc = f; + g->ud = ud; + g->mainthread = L; + g->uvhead.u.l.prev = &g->uvhead; + g->uvhead.u.l.next = &g->uvhead; + g->GCthreshold = 0; /* mark it as unfinished state */ + g->strt.size = 0; + g->strt.nuse = 0; + g->strt.hash = NULL; + setnilvalue(registry(L)); + luaZ_initbuffer(L, &g->buff); + g->panic = NULL; + g->gcstate = GCSpause; + g->rootgc = obj2gco(L); + g->sweepstrgc = 0; + g->sweepgc = &g->rootgc; + g->gray = NULL; + g->grayagain = NULL; + g->weak = NULL; + g->tmudata = NULL; + g->totalbytes = sizeof(LG); + g->gcpause = LUAI_GCPAUSE; + g->gcstepmul = LUAI_GCMUL; + g->gcdept = 0; + for (i=0; imt[i] = NULL; + if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) { + /* memory allocation error: free partial state */ + close_state(L); + L = NULL; + } + else + luai_userstateopen(L); + return L; +} + + +static void callallgcTM (lua_State *L, void *ud) { + UNUSED(ud); + luaC_callGCTM(L); /* call GC metamethods for all udata */ +} + + +LUA_API void lua_close (lua_State *L) { + L = G(L)->mainthread; /* only the main thread can be closed */ + lua_lock(L); + luaF_close(L, L->stack); /* close all upvalues for this thread */ + luaC_separateudata(L, 1); /* separate udata that have GC metamethods */ + L->errfunc = 0; /* no error function during GC metamethods */ + do { /* repeat until no more errors */ + L->ci = L->base_ci; + L->base = L->top = L->ci->base; + L->nCcalls = L->baseCcalls = 0; + } while (luaD_rawrunprotected(L, callallgcTM, NULL) != 0); + lua_assert(G(L)->tmudata == NULL); + luai_userstateclose(L); + close_state(L); +} + === added file 'lua/lstate.h' --- lua/lstate.h 1970-01-01 00:00:00 +0000 +++ lua/lstate.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,169 @@ +/* +** $Id: lstate.h,v 2.24.1.2 2008/01/03 15:20:39 roberto Exp $ +** Global State +** See Copyright Notice in lua.h +*/ + +#ifndef lstate_h +#define lstate_h + +#include "lua.h" + +#include "lobject.h" +#include "ltm.h" +#include "lzio.h" + + + +struct lua_longjmp; /* defined in ldo.c */ + + +/* table of globals */ +#define gt(L) (&L->l_gt) + +/* registry */ +#define registry(L) (&G(L)->l_registry) + + +/* extra stack space to handle TM calls and some other extras */ +#define EXTRA_STACK 5 + + +#define BASIC_CI_SIZE 8 + +#define BASIC_STACK_SIZE (2*LUA_MINSTACK) + + + +typedef struct stringtable { + GCObject **hash; + lu_int32 nuse; /* number of elements */ + int size; +} stringtable; + + +/* +** informations about a call +*/ +typedef struct CallInfo { + StkId base; /* base for this function */ + StkId func; /* function index in the stack */ + StkId top; /* top for this function */ + const Instruction *savedpc; + int nresults; /* expected number of results from this function */ + int tailcalls; /* number of tail calls lost under this entry */ +} CallInfo; + + + +#define curr_func(L) (clvalue(L->ci->func)) +#define ci_func(ci) (clvalue((ci)->func)) +#define f_isLua(ci) (!ci_func(ci)->c.isC) +#define isLua(ci) (ttisfunction((ci)->func) && f_isLua(ci)) + + +/* +** `global state', shared by all threads of this state +*/ +typedef struct global_State { + stringtable strt; /* hash table for strings */ + lua_Alloc frealloc; /* function to reallocate memory */ + void *ud; /* auxiliary data to `frealloc' */ + lu_byte currentwhite; + lu_byte gcstate; /* state of garbage collector */ + int sweepstrgc; /* position of sweep in `strt' */ + GCObject *rootgc; /* list of all collectable objects */ + GCObject **sweepgc; /* position of sweep in `rootgc' */ + GCObject *gray; /* list of gray objects */ + GCObject *grayagain; /* list of objects to be traversed atomically */ + GCObject *weak; /* list of weak tables (to be cleared) */ + GCObject *tmudata; /* last element of list of userdata to be GC */ + Mbuffer buff; /* temporary buffer for string concatentation */ + lu_mem GCthreshold; + lu_mem totalbytes; /* number of bytes currently allocated */ + lu_mem estimate; /* an estimate of number of bytes actually in use */ + lu_mem gcdept; /* how much GC is `behind schedule' */ + int gcpause; /* size of pause between successive GCs */ + int gcstepmul; /* GC `granularity' */ + lua_CFunction panic; /* to be called in unprotected errors */ + TValue l_registry; + struct lua_State *mainthread; + UpVal uvhead; /* head of double-linked list of all open upvalues */ + struct Table *mt[NUM_TAGS]; /* metatables for basic types */ + TString *tmname[TM_N]; /* array with tag-method names */ +} global_State; + + +/* +** `per thread' state +*/ +struct lua_State { + CommonHeader; + lu_byte status; + StkId top; /* first free slot in the stack */ + StkId base; /* base of current function */ + global_State *l_G; + CallInfo *ci; /* call info for current function */ + const Instruction *savedpc; /* `savedpc' of current function */ + StkId stack_last; /* last free slot in the stack */ + StkId stack; /* stack base */ + CallInfo *end_ci; /* points after end of ci array*/ + CallInfo *base_ci; /* array of CallInfo's */ + int stacksize; + int size_ci; /* size of array `base_ci' */ + unsigned short nCcalls; /* number of nested C calls */ + unsigned short baseCcalls; /* nested C calls when resuming coroutine */ + lu_byte hookmask; + lu_byte allowhook; + int basehookcount; + int hookcount; + lua_Hook hook; + TValue l_gt; /* table of globals */ + TValue env; /* temporary place for environments */ + GCObject *openupval; /* list of open upvalues in this stack */ + GCObject *gclist; + struct lua_longjmp *errorJmp; /* current error recover point */ + ptrdiff_t errfunc; /* current error handling function (stack index) */ +}; + + +#define G(L) (L->l_G) + + +/* +** Union of all collectable objects +*/ +union GCObject { + GCheader gch; + union TString ts; + union Udata u; + union Closure cl; + struct Table h; + struct Proto p; + struct UpVal uv; + struct lua_State th; /* thread */ +}; + + +/* macros to convert a GCObject into a specific value */ +#define rawgco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts)) +#define gco2ts(o) (&rawgco2ts(o)->tsv) +#define rawgco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) +#define gco2u(o) (&rawgco2u(o)->uv) +#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl)) +#define gco2h(o) check_exp((o)->gch.tt == LUA_TTABLE, &((o)->h)) +#define gco2p(o) check_exp((o)->gch.tt == LUA_TPROTO, &((o)->p)) +#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) +#define ngcotouv(o) \ + check_exp((o) == NULL || (o)->gch.tt == LUA_TUPVAL, &((o)->uv)) +#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th)) + +/* macro to convert any Lua object into a GCObject */ +#define obj2gco(v) (cast(GCObject *, (v))) + + +LUAI_FUNC lua_State *luaE_newthread (lua_State *L); +LUAI_FUNC void luaE_freethread (lua_State *L, lua_State *L1); + +#endif + === added file 'lua/lstring.c' --- lua/lstring.c 1970-01-01 00:00:00 +0000 +++ lua/lstring.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,113 @@ +/* +** $Id: lstring.c,v 2.8.1.1 2007/12/27 13:02:25 roberto Exp $ +** String table (keeps all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#define lstring_c +#define LUA_CORE + +#include "lua.h" + +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" + + + +void luaS_resize (lua_State *L, int newsize) { + GCObject **newhash; + stringtable *tb; + int i; + if (G(L)->gcstate == GCSsweepstring) + return; /* cannot resize during GC traverse */ + newhash = luaM_newvector(L, newsize, GCObject *); + tb = &G(L)->strt; + for (i=0; isize; i++) { + GCObject *p = tb->hash[i]; + while (p) { /* for each node in the list */ + GCObject *next = p->gch.next; /* save next */ + unsigned int h = gco2ts(p)->hash; + int h1 = lmod(h, newsize); /* new position */ + lua_assert(cast_int(h%newsize) == lmod(h, newsize)); + p->gch.next = newhash[h1]; /* chain it */ + newhash[h1] = p; + p = next; + } + } + luaM_freearray(L, tb->hash, tb->size, TString *); + tb->size = newsize; + tb->hash = newhash; +} + + +static TString *newlstr (lua_State *L, const char *str, size_t l, + unsigned int h) { + TString *ts; + stringtable *tb; + if (l+1 > (MAX_SIZET - sizeof(TString))/sizeof(char)) + luaM_toobig(L); + ts = cast(TString *, luaM_malloc(L, (l+1)*sizeof(char)+sizeof(TString))); + ts->tsv.len = l; + ts->tsv.hash = h; + ts->tsv.marked = luaC_white(G(L)); + ts->tsv.tt = LUA_TSTRING; + ts->tsv.reserved = 0; + memcpy(ts+1, str, l*sizeof(char)); + ((char *)(ts+1))[l] = '\0'; /* ending 0 */ + tb = &G(L)->strt; + h = lmod(h, tb->size); + ts->tsv.next = tb->hash[h]; /* chain new entry */ + tb->hash[h] = obj2gco(ts); + tb->nuse++; + if (tb->nuse > cast(lu_int32, tb->size) && tb->size <= MAX_INT/2) + luaS_resize(L, tb->size*2); /* too crowded */ + return ts; +} + + +TString *luaS_newlstr (lua_State *L, const char *str, size_t l) { + GCObject *o; + unsigned int h = cast(unsigned int, l); /* seed */ + size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ + size_t l1; + for (l1=l; l1>=step; l1-=step) /* compute hash */ + h = h ^ ((h<<5)+(h>>2)+cast(unsigned char, str[l1-1])); + for (o = G(L)->strt.hash[lmod(h, G(L)->strt.size)]; + o != NULL; + o = o->gch.next) { + TString *ts = rawgco2ts(o); + if (ts->tsv.len == l && (memcmp(str, getstr(ts), l) == 0)) { + /* string may be dead */ + if (isdead(G(L), o)) changewhite(o); + return ts; + } + } + return newlstr(L, str, l, h); /* not found */ +} + + +Udata *luaS_newudata (lua_State *L, size_t s, Table *e) { + Udata *u; + if (s > MAX_SIZET - sizeof(Udata)) + luaM_toobig(L); + u = cast(Udata *, luaM_malloc(L, s + sizeof(Udata))); + u->uv.marked = luaC_white(G(L)); /* is not finalized */ + u->uv.tt = LUA_TUSERDATA; + u->uv.len = s; + u->uv.metatable = NULL; + u->uv.env = e; + /* chain it on udata list (after main thread) */ + u->uv.next = G(L)->mainthread->next; + G(L)->mainthread->next = obj2gco(u); + return u; +} + === added file 'lua/lstring.h' --- lua/lstring.h 1970-01-01 00:00:00 +0000 +++ lua/lstring.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,31 @@ +/* +** $Id: lstring.h,v 1.43.1.1 2007/12/27 13:02:25 roberto Exp $ +** String table (keep all strings handled by Lua) +** See Copyright Notice in lua.h +*/ + +#ifndef lstring_h +#define lstring_h + + +#include "lgc.h" +#include "lobject.h" +#include "lstate.h" + + +#define sizestring(s) (sizeof(union TString)+((s)->len+1)*sizeof(char)) + +#define sizeudata(u) (sizeof(union Udata)+(u)->len) + +#define luaS_new(L, s) (luaS_newlstr(L, s, strlen(s))) +#define luaS_newliteral(L, s) (luaS_newlstr(L, "" s, \ + (sizeof(s)/sizeof(char))-1)) + +#define luaS_fix(s) l_setbit((s)->tsv.marked, FIXEDBIT) + +LUAI_FUNC void luaS_resize (lua_State *L, int newsize); +LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, Table *e); +LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l); + + +#endif === added file 'lua/lstrlib.c' --- lua/lstrlib.c 1970-01-01 00:00:00 +0000 +++ lua/lstrlib.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,872 @@ +/* +** $Id: lstrlib.c,v 1.132.1.3 2007/12/28 15:32:23 roberto Exp $ +** Standard library for string operations and pattern-matching +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#include +#include +#include +#include +#endif /* ! USE_GRUB_LIB */ + +#define lstrlib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +/* macro to `unsign' a character */ +#define uchar(c) ((unsigned char)(c)) + + + +static int str_len (lua_State *L) { + size_t l; + luaL_checklstring(L, 1, &l); + lua_pushinteger(L, l); + return 1; +} + + +static ptrdiff_t posrelat (ptrdiff_t pos, size_t len) { + /* relative string position: negative means back from end */ + return (pos>=0) ? pos : (ptrdiff_t)len+pos+1; +} + + +static int str_sub (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + ptrdiff_t start = posrelat(luaL_checkinteger(L, 2), l); + ptrdiff_t end = posrelat(luaL_optinteger(L, 3, -1), l); + if (start < 1) start = 1; + if (end > (ptrdiff_t)l) end = (ptrdiff_t)l; + if (start <= end) + lua_pushlstring(L, s+start-1, end-start+1); + else lua_pushliteral(L, ""); + return 1; +} + + +static int str_reverse (lua_State *L) { + size_t l; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + while (l--) luaL_addchar(&b, s[l]); + luaL_pushresult(&b); + return 1; +} + + +static int str_lower (lua_State *L) { + size_t l; + size_t i; + luaL_Buffer b; + const char *s = luaL_checklstring(L, 1, &l); + luaL_buffinit(L, &b); + for (i=0; i 0) + luaL_addlstring(&b, s, l); + luaL_pushresult(&b); + return 1; +} + + +static int str_byte (lua_State *L) { + size_t l; + const char *s = luaL_checklstring(L, 1, &l); + ptrdiff_t posi = posrelat(luaL_optinteger(L, 2, 1), l); + ptrdiff_t pose = posrelat(luaL_optinteger(L, 3, posi), l); + int n, i; + if (posi <= 0) posi = 1; + if ((size_t)pose > l) pose = l; + if (posi > pose) return 0; /* empty interval; return no values */ + n = (int)(pose - posi + 1); + if (posi + n <= pose) /* overflow? */ + luaL_error(L, "string slice too long"); + luaL_checkstack(L, n, "string slice too long"); + for (i=0; i= ms->level || ms->capture[l].len == CAP_UNFINISHED) + return luaL_error(ms->L, "invalid capture index"); + return l; +} + + +static int capture_to_close (MatchState *ms) { + int level = ms->level; + for (level--; level>=0; level--) + if (ms->capture[level].len == CAP_UNFINISHED) return level; + return luaL_error(ms->L, "invalid pattern capture"); +} + + +static const char *classend (MatchState *ms, const char *p) { + switch (*p++) { + case L_ESC: { + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (ends with " LUA_QL("%%") ")"); + return p+1; + } + case '[': { + if (*p == '^') p++; + do { /* look for a `]' */ + if (*p == '\0') + luaL_error(ms->L, "malformed pattern (missing " LUA_QL("]") ")"); + if (*(p++) == L_ESC && *p != '\0') + p++; /* skip escapes (e.g. `%]') */ + } while (*p != ']'); + return p+1; + } + default: { + return p; + } + } +} + + +static int match_class (int c, int cl) { + int res; + switch (tolower(cl)) { + case 'a' : res = isalpha(c); break; + case 'c' : res = iscntrl(c); break; + case 'd' : res = isdigit(c); break; + case 'l' : res = islower(c); break; + case 'p' : res = ispunct(c); break; + case 's' : res = isspace(c); break; + case 'u' : res = isupper(c); break; + case 'w' : res = isalnum(c); break; + case 'x' : res = isxdigit(c); break; + case 'z' : res = (c == 0); break; + default: return (cl == c); + } + return (islower(cl) ? res : !res); +} + + +static int matchbracketclass (int c, const char *p, const char *ec) { + int sig = 1; + if (*(p+1) == '^') { + sig = 0; + p++; /* skip the `^' */ + } + while (++p < ec) { + if (*p == L_ESC) { + p++; + if (match_class(c, uchar(*p))) + return sig; + } + else if ((*(p+1) == '-') && (p+2 < ec)) { + p+=2; + if (uchar(*(p-2)) <= c && c <= uchar(*p)) + return sig; + } + else if (uchar(*p) == c) return sig; + } + return !sig; +} + + +static int singlematch (int c, const char *p, const char *ep) { + switch (*p) { + case '.': return 1; /* matches any char */ + case L_ESC: return match_class(c, uchar(*(p+1))); + case '[': return matchbracketclass(c, p, ep-1); + default: return (uchar(*p) == c); + } +} + + +static const char *match (MatchState *ms, const char *s, const char *p); + + +static const char *matchbalance (MatchState *ms, const char *s, + const char *p) { + if (*p == 0 || *(p+1) == 0) + luaL_error(ms->L, "unbalanced pattern"); + if (*s != *p) return NULL; + else { + int b = *p; + int e = *(p+1); + int cont = 1; + while (++s < ms->src_end) { + if (*s == e) { + if (--cont == 0) return s+1; + } + else if (*s == b) cont++; + } + } + return NULL; /* string ends out of balance */ +} + + +static const char *max_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + ptrdiff_t i = 0; /* counts maximum expand for item */ + while ((s+i)src_end && singlematch(uchar(*(s+i)), p, ep)) + i++; + /* keeps trying to match with the maximum repetitions */ + while (i>=0) { + const char *res = match(ms, (s+i), ep+1); + if (res) return res; + i--; /* else didn't match; reduce 1 repetition to try again */ + } + return NULL; +} + + +static const char *min_expand (MatchState *ms, const char *s, + const char *p, const char *ep) { + for (;;) { + const char *res = match(ms, s, ep+1); + if (res != NULL) + return res; + else if (ssrc_end && singlematch(uchar(*s), p, ep)) + s++; /* try with one more repetition */ + else return NULL; + } +} + + +static const char *start_capture (MatchState *ms, const char *s, + const char *p, int what) { + const char *res; + int level = ms->level; + if (level >= LUA_MAXCAPTURES) luaL_error(ms->L, "too many captures"); + ms->capture[level].init = s; + ms->capture[level].len = what; + ms->level = level+1; + if ((res=match(ms, s, p)) == NULL) /* match failed? */ + ms->level--; /* undo capture */ + return res; +} + + +static const char *end_capture (MatchState *ms, const char *s, + const char *p) { + int l = capture_to_close(ms); + const char *res; + ms->capture[l].len = s - ms->capture[l].init; /* close capture */ + if ((res = match(ms, s, p)) == NULL) /* match failed? */ + ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ + return res; +} + + +static const char *match_capture (MatchState *ms, const char *s, int l) { + size_t len; + l = check_capture(ms, l); + len = ms->capture[l].len; + if ((size_t)(ms->src_end-s) >= len && + memcmp(ms->capture[l].init, s, len) == 0) + return s+len; + else return NULL; +} + + +static const char *match (MatchState *ms, const char *s, const char *p) { + init: /* using goto's to optimize tail recursion */ + switch (*p) { + case '(': { /* start capture */ + if (*(p+1) == ')') /* position capture? */ + return start_capture(ms, s, p+2, CAP_POSITION); + else + return start_capture(ms, s, p+1, CAP_UNFINISHED); + } + case ')': { /* end capture */ + return end_capture(ms, s, p+1); + } + case L_ESC: { + switch (*(p+1)) { + case 'b': { /* balanced string? */ + s = matchbalance(ms, s, p+2); + if (s == NULL) return NULL; + p+=4; goto init; /* else return match(ms, s, p+4); */ + } + case 'f': { /* frontier? */ + const char *ep; char previous; + p += 2; + if (*p != '[') + luaL_error(ms->L, "missing " LUA_QL("[") " after " + LUA_QL("%%f") " in pattern"); + ep = classend(ms, p); /* points to what is next */ + previous = (s == ms->src_init) ? '\0' : *(s-1); + if (matchbracketclass(uchar(previous), p, ep-1) || + !matchbracketclass(uchar(*s), p, ep-1)) return NULL; + p=ep; goto init; /* else return match(ms, s, ep); */ + } + default: { + if (isdigit(uchar(*(p+1)))) { /* capture results (%0-%9)? */ + s = match_capture(ms, s, uchar(*(p+1))); + if (s == NULL) return NULL; + p+=2; goto init; /* else return match(ms, s, p+2) */ + } + goto dflt; /* case default */ + } + } + } + case '\0': { /* end of pattern */ + return s; /* match succeeded */ + } + case '$': { + if (*(p+1) == '\0') /* is the `$' the last char in pattern? */ + return (s == ms->src_end) ? s : NULL; /* check end of string */ + else goto dflt; + } + default: dflt: { /* it is a pattern item */ + const char *ep = classend(ms, p); /* points to what is next */ + int m = ssrc_end && singlematch(uchar(*s), p, ep); + switch (*ep) { + case '?': { /* optional */ + const char *res; + if (m && ((res=match(ms, s+1, ep+1)) != NULL)) + return res; + p=ep+1; goto init; /* else return match(ms, s, ep+1); */ + } + case '*': { /* 0 or more repetitions */ + return max_expand(ms, s, p, ep); + } + case '+': { /* 1 or more repetitions */ + return (m ? max_expand(ms, s+1, p, ep) : NULL); + } + case '-': { /* 0 or more repetitions (minimum) */ + return min_expand(ms, s, p, ep); + } + default: { + if (!m) return NULL; + s++; p=ep; goto init; /* else return match(ms, s+1, ep); */ + } + } + } + } +} + + + +static const char *lmemfind (const char *s1, size_t l1, + const char *s2, size_t l2) { + if (l2 == 0) return s1; /* empty strings are everywhere */ + else if (l2 > l1) return NULL; /* avoids a negative `l1' */ + else { + const char *init; /* to search for a `*s2' inside `s1' */ + l2--; /* 1st char will be checked by `memchr' */ + l1 = l1-l2; /* `s2' cannot be found after that */ + while (l1 > 0 && (init = (const char *)memchr(s1, *s2, l1)) != NULL) { + init++; /* 1st char is already checked */ + if (memcmp(init, s2+1, l2) == 0) + return init-1; + else { /* correct `l1' and `s1' to try again */ + l1 -= init-s1; + s1 = init; + } + } + return NULL; /* not found */ + } +} + + +static void push_onecapture (MatchState *ms, int i, const char *s, + const char *e) { + if (i >= ms->level) { + if (i == 0) /* ms->level == 0, too */ + lua_pushlstring(ms->L, s, e - s); /* add whole match */ + else + luaL_error(ms->L, "invalid capture index"); + } + else { + ptrdiff_t l = ms->capture[i].len; + if (l == CAP_UNFINISHED) luaL_error(ms->L, "unfinished capture"); + if (l == CAP_POSITION) + lua_pushinteger(ms->L, ms->capture[i].init - ms->src_init + 1); + else + lua_pushlstring(ms->L, ms->capture[i].init, l); + } +} + + +static int push_captures (MatchState *ms, const char *s, const char *e) { + int i; + int nlevels = (ms->level == 0 && s) ? 1 : ms->level; + luaL_checkstack(ms->L, nlevels, "too many captures"); + for (i = 0; i < nlevels; i++) + push_onecapture(ms, i, s, e); + return nlevels; /* number of strings pushed */ +} + + +static int str_find_aux (lua_State *L, int find) { + size_t l1, l2; + const char *s = luaL_checklstring(L, 1, &l1); + const char *p = luaL_checklstring(L, 2, &l2); + ptrdiff_t init = posrelat(luaL_optinteger(L, 3, 1), l1) - 1; + if (init < 0) init = 0; + else if ((size_t)(init) > l1) init = (ptrdiff_t)l1; + if (find && (lua_toboolean(L, 4) || /* explicit request? */ + strpbrk(p, SPECIALS) == NULL)) { /* or no special characters? */ + /* do a plain search */ + const char *s2 = lmemfind(s+init, l1-init, p, l2); + if (s2) { + lua_pushinteger(L, s2-s+1); + lua_pushinteger(L, s2-s+l2); + return 2; + } + } + else { + MatchState ms; + int anchor = (*p == '^') ? (p++, 1) : 0; + const char *s1=s+init; + ms.L = L; + ms.src_init = s; + ms.src_end = s+l1; + do { + const char *res; + ms.level = 0; + if ((res=match(&ms, s1, p)) != NULL) { + if (find) { + lua_pushinteger(L, s1-s+1); /* start */ + lua_pushinteger(L, res-s); /* end */ + return push_captures(&ms, NULL, 0) + 2; + } + else + return push_captures(&ms, s1, res); + } + } while (s1++ < ms.src_end && !anchor); + } + lua_pushnil(L); /* not found */ + return 1; +} + + +static int str_find (lua_State *L) { + return str_find_aux(L, 1); +} + + +static int str_match (lua_State *L) { + return str_find_aux(L, 0); +} + + +static int gmatch_aux (lua_State *L) { + MatchState ms; + size_t ls; + const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); + const char *p = lua_tostring(L, lua_upvalueindex(2)); + const char *src; + ms.L = L; + ms.src_init = s; + ms.src_end = s+ls; + for (src = s + (size_t)lua_tointeger(L, lua_upvalueindex(3)); + src <= ms.src_end; + src++) { + const char *e; + ms.level = 0; + if ((e = match(&ms, src, p)) != NULL) { + lua_Integer newstart = e-s; + if (e == src) newstart++; /* empty match? go at least one position */ + lua_pushinteger(L, newstart); + lua_replace(L, lua_upvalueindex(3)); + return push_captures(&ms, src, e); + } + } + return 0; /* not found */ +} + + +static int gmatch (lua_State *L) { + luaL_checkstring(L, 1); + luaL_checkstring(L, 2); + lua_settop(L, 2); + lua_pushinteger(L, 0); + lua_pushcclosure(L, gmatch_aux, 3); + return 1; +} + + +static int gfind_nodef (lua_State *L) { + return luaL_error(L, LUA_QL("string.gfind") " was renamed to " + LUA_QL("string.gmatch")); +} + + +static void add_s (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + size_t l, i; + const char *news = lua_tolstring(ms->L, 3, &l); + for (i = 0; i < l; i++) { + if (news[i] != L_ESC) + luaL_addchar(b, news[i]); + else { + i++; /* skip ESC */ + if (!isdigit(uchar(news[i]))) + luaL_addchar(b, news[i]); + else if (news[i] == '0') + luaL_addlstring(b, s, e - s); + else { + push_onecapture(ms, news[i] - '1', s, e); + luaL_addvalue(b); /* add capture to accumulated result */ + } + } + } +} + + +static void add_value (MatchState *ms, luaL_Buffer *b, const char *s, + const char *e) { + lua_State *L = ms->L; + switch (lua_type(L, 3)) { + case LUA_TNUMBER: + case LUA_TSTRING: { + add_s(ms, b, s, e); + return; + } + case LUA_TFUNCTION: { + int n; + lua_pushvalue(L, 3); + n = push_captures(ms, s, e); + lua_call(L, n, 1); + break; + } + case LUA_TTABLE: { + push_onecapture(ms, 0, s, e); + lua_gettable(L, 3); + break; + } + } + if (!lua_toboolean(L, -1)) { /* nil or false? */ + lua_pop(L, 1); + lua_pushlstring(L, s, e - s); /* keep original text */ + } + else if (!lua_isstring(L, -1)) + luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); + luaL_addvalue(b); /* add result to accumulator */ +} + + +static int str_gsub (lua_State *L) { + size_t srcl; + const char *src = luaL_checklstring(L, 1, &srcl); + const char *p = luaL_checkstring(L, 2); + int tr = lua_type(L, 3); + int max_s = luaL_optint(L, 4, srcl+1); + int anchor = (*p == '^') ? (p++, 1) : 0; + int n = 0; + MatchState ms; + luaL_Buffer b; + luaL_argcheck(L, tr == LUA_TNUMBER || tr == LUA_TSTRING || + tr == LUA_TFUNCTION || tr == LUA_TTABLE, 3, + "string/function/table expected"); + luaL_buffinit(L, &b); + ms.L = L; + ms.src_init = src; + ms.src_end = src+srcl; + while (n < max_s) { + const char *e; + ms.level = 0; + e = match(&ms, src, p); + if (e) { + n++; + add_value(&ms, &b, src, e); + } + if (e && e>src) /* non empty match? */ + src = e; /* skip it */ + else if (src < ms.src_end) + luaL_addchar(&b, *src++); + else break; + if (anchor) break; + } + luaL_addlstring(&b, src, ms.src_end-src); + luaL_pushresult(&b); + lua_pushinteger(L, n); /* number of substitutions */ + return 2; +} + +/* }====================================================== */ + + +/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ +#define MAX_ITEM 512 +/* valid flags in a format specification */ +#define FLAGS "-+ #0" +/* +** maximum size of each format specification (such as '%-099.99d') +** (+10 accounts for %99.99x plus margin of error) +*/ +#define MAX_FORMAT (sizeof(FLAGS) + sizeof(LUA_INTFRMLEN) + 10) + + +static void addquoted (lua_State *L, luaL_Buffer *b, int arg) { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + luaL_addchar(b, '"'); + while (l--) { + switch (*s) { + case '"': case '\\': case '\n': { + luaL_addchar(b, '\\'); + luaL_addchar(b, *s); + break; + } + case '\r': { + luaL_addlstring(b, "\\r", 2); + break; + } + case '\0': { + luaL_addlstring(b, "\\000", 4); + break; + } + default: { + luaL_addchar(b, *s); + break; + } + } + s++; + } + luaL_addchar(b, '"'); +} + +static const char *scanformat (lua_State *L, const char *strfrmt, char *form) { + const char *p = strfrmt; + while (*p != '\0' && strchr(FLAGS, *p) != NULL) p++; /* skip flags */ + if ((size_t)(p - strfrmt) >= sizeof(FLAGS)) + luaL_error(L, "invalid format (repeated flags)"); + if (isdigit(uchar(*p))) p++; /* skip width */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + if (*p == '.') { + p++; + if (isdigit(uchar(*p))) p++; /* skip precision */ + if (isdigit(uchar(*p))) p++; /* (2 digits at most) */ + } + if (isdigit(uchar(*p))) + luaL_error(L, "invalid format (width or precision too long)"); + *(form++) = '%'; + strncpy(form, strfrmt, p - strfrmt + 1); + form += p - strfrmt + 1; + *form = '\0'; + return p; +} + + +static void addintlen (char *form) { + size_t l = strlen(form); + char spec = form[l - 1]; + strcpy(form + l - 1, LUA_INTFRMLEN); + form[l + sizeof(LUA_INTFRMLEN) - 2] = spec; + form[l + sizeof(LUA_INTFRMLEN) - 1] = '\0'; +} + + +static int str_format (lua_State *L) { + int arg = 1; + size_t sfl; + const char *strfrmt = luaL_checklstring(L, arg, &sfl); + const char *strfrmt_end = strfrmt+sfl; + luaL_Buffer b; + luaL_buffinit(L, &b); + while (strfrmt < strfrmt_end) { + if (*strfrmt != L_ESC) + luaL_addchar(&b, *strfrmt++); + else if (*++strfrmt == L_ESC) + luaL_addchar(&b, *strfrmt++); /* %% */ + else { /* format item */ + char form[MAX_FORMAT]; /* to store the format (`%...') */ + char buff[MAX_ITEM]; /* to store the formatted item */ + arg++; + strfrmt = scanformat(L, strfrmt, form); + switch (*strfrmt++) { + case 'c': { + sprintf(buff, form, (int)luaL_checknumber(L, arg)); + break; + } + case 'd': case 'i': { + addintlen(form); + sprintf(buff, form, (LUA_INTFRM_T)luaL_checknumber(L, arg)); + break; + } + case 'o': case 'u': case 'x': case 'X': { + addintlen(form); + sprintf(buff, form, (unsigned LUA_INTFRM_T)luaL_checknumber(L, arg)); + break; + } +#if !defined LUA_NUMBER_INTEGRAL + case 'e': case 'E': case 'f': + case 'g': case 'G': { + sprintf(buff, form, (double)luaL_checknumber(L, arg)); + break; + } +#endif + case 'q': { + addquoted(L, &b, arg); + continue; /* skip the 'addsize' at the end */ + } + case 's': { + size_t l; + const char *s = luaL_checklstring(L, arg, &l); + if (!strchr(form, '.') && l >= 100) { + /* no precision and string is too long to be formatted; + keep original string */ + lua_pushvalue(L, arg); + luaL_addvalue(&b); + continue; /* skip the `addsize' at the end */ + } + else { + sprintf(buff, form, s); + break; + } + } + default: { /* also treat cases `pnLlh' */ + return luaL_error(L, "invalid option " LUA_QL("%%%c") " to " + LUA_QL("format"), *(strfrmt - 1)); + } + } + luaL_addlstring(&b, buff, strlen(buff)); + } + } + luaL_pushresult(&b); + return 1; +} + + +static const luaL_Reg strlib[] = { + {"byte", str_byte}, + {"char", str_char}, + {"dump", str_dump}, + {"find", str_find}, + {"format", str_format}, + {"gfind", gfind_nodef}, + {"gmatch", gmatch}, + {"gsub", str_gsub}, + {"len", str_len}, + {"lower", str_lower}, + {"match", str_match}, + {"rep", str_rep}, + {"reverse", str_reverse}, + {"sub", str_sub}, + {"upper", str_upper}, + {NULL, NULL} +}; + + +static void createmetatable (lua_State *L) { + lua_createtable(L, 0, 1); /* create metatable for strings */ + lua_pushliteral(L, ""); /* dummy string */ + lua_pushvalue(L, -2); + lua_setmetatable(L, -2); /* set string metatable */ + lua_pop(L, 1); /* pop dummy string */ + lua_pushvalue(L, -2); /* string library... */ + lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */ + lua_pop(L, 1); /* pop metatable */ +} + + +/* +** Open string library +*/ +LUALIB_API int luaopen_string (lua_State *L) { + luaL_register(L, LUA_STRLIBNAME, strlib); +#if defined(LUA_COMPAT_GFIND) + lua_getfield(L, -1, "gmatch"); + lua_setfield(L, -2, "gfind"); +#endif + createmetatable(L); + return 1; +} + === added file 'lua/ltable.c' --- lua/ltable.c 1970-01-01 00:00:00 +0000 +++ lua/ltable.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,590 @@ +/* +** $Id: ltable.c,v 2.32.1.2 2007/12/28 15:32:23 roberto Exp $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + + +/* +** Implementation of tables (aka arrays, objects, or hash tables). +** Tables keep its elements in two parts: an array part and a hash part. +** Non-negative integer keys are all candidates to be kept in the array +** part. The actual size of the array is the largest `n' such that at +** least half the slots between 0 and n are in use. +** Hash uses a mix of chained scatter table with Brent's variation. +** A main invariant of these tables is that, if an element is not +** in its main position (i.e. the `original' position that its hash gives +** to it), then the colliding element is in its own main position. +** Hence even when the load factor reaches 100%, performance remains good. +*/ + +#ifndef USE_GRUB_LIB +#include +#include +#endif /* ! USE_GRUB_LIB */ + +#define ltable_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lgc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstate.h" +#include "ltable.h" + + +/* +** max size of array part is 2^MAXBITS +*/ +#if LUAI_BITSINT > 26 +#define MAXBITS 26 +#else +#define MAXBITS (LUAI_BITSINT-2) +#endif + +#define MAXASIZE (1 << MAXBITS) + + +#define hashpow2(t,n) (gnode(t, lmod((n), sizenode(t)))) + +#define hashstr(t,str) hashpow2(t, (str)->tsv.hash) +#define hashboolean(t,p) hashpow2(t, p) + + +/* +** for some types, it is better to avoid modulus by power of 2, as +** they tend to have many 2 factors. +*/ +#define hashmod(t,n) (gnode(t, ((n) % ((sizenode(t)-1)|1)))) + + +#define hashpointer(t,p) hashmod(t, IntPoint(p)) + + +/* +** number of ints inside a lua_Number +*/ +#define numints cast_int(sizeof(lua_Number)/sizeof(int)) + + + +#define dummynode (&dummynode_) + +static const Node dummynode_ = { + {{NULL}, LUA_TNIL}, /* value */ + {{{NULL}, LUA_TNIL, NULL}} /* key */ +}; + + +/* +** hash for lua_Numbers +*/ +static Node *hashnum (const Table *t, lua_Number n) { + unsigned int a[numints]; + int i; + if (luai_numeq(n, 0)) /* avoid problems with -0 */ + return gnode(t, 0); + memcpy(a, &n, sizeof(a)); + for (i = 1; i < numints; i++) a[0] += a[i]; + return hashmod(t, a[0]); +} + + + +/* +** returns the `main' position of an element in a table (that is, the index +** of its hash value) +*/ +static Node *mainposition (const Table *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TNUMBER: + return hashnum(t, nvalue(key)); + case LUA_TSTRING: + return hashstr(t, rawtsvalue(key)); + case LUA_TBOOLEAN: + return hashboolean(t, bvalue(key)); + case LUA_TLIGHTUSERDATA: + return hashpointer(t, pvalue(key)); + default: + return hashpointer(t, gcvalue(key)); + } +} + + +/* +** returns the index for `key' if `key' is an appropriate key to live in +** the array part of the table, -1 otherwise. +*/ +static int arrayindex (const TValue *key) { + if (ttisnumber(key)) { + lua_Number n = nvalue(key); + int k; + lua_number2int(k, n); + if (luai_numeq(cast_num(k), n)) + return k; + } + return -1; /* `key' did not match some condition */ +} + + +/* +** returns the index of a `key' for table traversals. First goes all +** elements in the array part, then elements in the hash part. The +** beginning of a traversal is signalled by -1. +*/ +static int findindex (lua_State *L, Table *t, StkId key) { + int i; + if (ttisnil(key)) return -1; /* first iteration */ + i = arrayindex(key); + if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ + return i-1; /* yes; that's the index (corrected to C) */ + else { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + /* key may be dead already, but it is ok to use it in `next' */ + if (luaO_rawequalObj(key2tval(n), key) || + (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) && + gcvalue(gkey(n)) == gcvalue(key))) { + i = cast_int(n - gnode(t, 0)); /* key index in hash table */ + /* hash elements are numbered after array ones */ + return i + t->sizearray; + } + else n = gnext(n); + } while (n); + luaG_runerror(L, "invalid key to " LUA_QL("next")); /* key not found */ + return 0; /* to avoid warnings */ + } +} + + +int luaH_next (lua_State *L, Table *t, StkId key) { + int i = findindex(L, t, key); /* find original element */ + for (i++; i < t->sizearray; i++) { /* try first array part */ + if (!ttisnil(&t->array[i])) { /* a non-nil value? */ + setnvalue(key, cast_num(i+1)); + setobj2s(L, key+1, &t->array[i]); + return 1; + } + } + for (i -= t->sizearray; i < sizenode(t); i++) { /* then hash part */ + if (!ttisnil(gval(gnode(t, i)))) { /* a non-nil value? */ + setobj2s(L, key, key2tval(gnode(t, i))); + setobj2s(L, key+1, gval(gnode(t, i))); + return 1; + } + } + return 0; /* no more elements */ +} + + +/* +** {============================================================= +** Rehash +** ============================================================== +*/ + + +static int computesizes (int nums[], int *narray) { + int i; + int twotoi; /* 2^i */ + int a = 0; /* number of elements smaller than 2^i */ + int na = 0; /* number of elements to go to array part */ + int n = 0; /* optimal size for array part */ + for (i = 0, twotoi = 1; twotoi/2 < *narray; i++, twotoi *= 2) { + if (nums[i] > 0) { + a += nums[i]; + if (a > twotoi/2) { /* more than half elements present? */ + n = twotoi; /* optimal size (till now) */ + na = a; /* all elements smaller than n will go to array part */ + } + } + if (a == *narray) break; /* all elements already counted */ + } + *narray = n; + lua_assert(*narray/2 <= na && na <= *narray); + return na; +} + + +static int countint (const TValue *key, int *nums) { + int k = arrayindex(key); + if (0 < k && k <= MAXASIZE) { /* is `key' an appropriate array index? */ + nums[ceillog2(k)]++; /* count as such */ + return 1; + } + else + return 0; +} + + +static int numusearray (const Table *t, int *nums) { + int lg; + int ttlg; /* 2^lg */ + int ause = 0; /* summation of `nums' */ + int i = 1; /* count to traverse all array keys */ + for (lg=0, ttlg=1; lg<=MAXBITS; lg++, ttlg*=2) { /* for each slice */ + int lc = 0; /* counter */ + int lim = ttlg; + if (lim > t->sizearray) { + lim = t->sizearray; /* adjust upper limit */ + if (i > lim) + break; /* no more elements to count */ + } + /* count elements in range (2^(lg-1), 2^lg] */ + for (; i <= lim; i++) { + if (!ttisnil(&t->array[i-1])) + lc++; + } + nums[lg] += lc; + ause += lc; + } + return ause; +} + + +static int numusehash (const Table *t, int *nums, int *pnasize) { + int totaluse = 0; /* total number of elements */ + int ause = 0; /* summation of `nums' */ + int i = sizenode(t); + while (i--) { + Node *n = &t->node[i]; + if (!ttisnil(gval(n))) { + ause += countint(key2tval(n), nums); + totaluse++; + } + } + *pnasize += ause; + return totaluse; +} + + +static void setarrayvector (lua_State *L, Table *t, int size) { + int i; + luaM_reallocvector(L, t->array, t->sizearray, size, TValue); + for (i=t->sizearray; iarray[i]); + t->sizearray = size; +} + + +static void setnodevector (lua_State *L, Table *t, int size) { + int lsize; + if (size == 0) { /* no elements to hash part? */ + t->node = cast(Node *, dummynode); /* use common `dummynode' */ + lsize = 0; + } + else { + int i; + lsize = ceillog2(size); + if (lsize > MAXBITS) + luaG_runerror(L, "table overflow"); + size = twoto(lsize); + t->node = luaM_newvector(L, size, Node); + for (i=0; ilsizenode = cast_byte(lsize); + t->lastfree = gnode(t, size); /* all positions are free */ +} + + +static void resize (lua_State *L, Table *t, int nasize, int nhsize) { + int i; + int oldasize = t->sizearray; + int oldhsize = t->lsizenode; + Node *nold = t->node; /* save old hash ... */ + if (nasize > oldasize) /* array part must grow? */ + setarrayvector(L, t, nasize); + /* create new hash part with appropriate size */ + setnodevector(L, t, nhsize); + if (nasize < oldasize) { /* array part must shrink? */ + t->sizearray = nasize; + /* re-insert elements from vanishing slice */ + for (i=nasize; iarray[i])) + setobjt2t(L, luaH_setnum(L, t, i+1), &t->array[i]); + } + /* shrink array */ + luaM_reallocvector(L, t->array, oldasize, nasize, TValue); + } + /* re-insert elements from hash part */ + for (i = twoto(oldhsize) - 1; i >= 0; i--) { + Node *old = nold+i; + if (!ttisnil(gval(old))) + setobjt2t(L, luaH_set(L, t, key2tval(old)), gval(old)); + } + if (nold != dummynode) + luaM_freearray(L, nold, twoto(oldhsize), Node); /* free old array */ +} + + +void luaH_resizearray (lua_State *L, Table *t, int nasize) { + int nsize = (t->node == dummynode) ? 0 : sizenode(t); + resize(L, t, nasize, nsize); +} + + +static void rehash (lua_State *L, Table *t, const TValue *ek) { + int nasize, na; + int nums[MAXBITS+1]; /* nums[i] = number of keys between 2^(i-1) and 2^i */ + int i; + int totaluse; + for (i=0; i<=MAXBITS; i++) nums[i] = 0; /* reset counts */ + nasize = numusearray(t, nums); /* count keys in array part */ + totaluse = nasize; /* all those keys are integer keys */ + totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ + /* count extra key */ + nasize += countint(ek, nums); + totaluse++; + /* compute new size for array part */ + na = computesizes(nums, &nasize); + /* resize the table to new computed sizes */ + resize(L, t, nasize, totaluse - na); +} + + + +/* +** }============================================================= +*/ + + +Table *luaH_new (lua_State *L, int narray, int nhash) { + Table *t = luaM_new(L, Table); + luaC_link(L, obj2gco(t), LUA_TTABLE); + t->metatable = NULL; + t->flags = cast_byte(~0); + /* temporary values (kept only if some malloc fails) */ + t->array = NULL; + t->sizearray = 0; + t->lsizenode = 0; + t->node = cast(Node *, dummynode); + setarrayvector(L, t, narray); + setnodevector(L, t, nhash); + return t; +} + + +void luaH_free (lua_State *L, Table *t) { + if (t->node != dummynode) + luaM_freearray(L, t->node, sizenode(t), Node); + luaM_freearray(L, t->array, t->sizearray, TValue); + luaM_free(L, t); +} + + +static Node *getfreepos (Table *t) { + while (t->lastfree-- > t->node) { + if (ttisnil(gkey(t->lastfree))) + return t->lastfree; + } + return NULL; /* could not find a free place */ +} + + + +/* +** inserts a new key into a hash table; first, check whether key's main +** position is free. If not, check whether colliding node is in its main +** position or not: if it is not, move colliding node to an empty place and +** put new key in its main position; otherwise (colliding node is in its main +** position), new key goes to an empty position. +*/ +static TValue *newkey (lua_State *L, Table *t, const TValue *key) { + Node *mp = mainposition(t, key); + if (!ttisnil(gval(mp)) || mp == dummynode) { + Node *othern; + Node *n = getfreepos(t); /* get a free place */ + if (n == NULL) { /* cannot find a free place? */ + rehash(L, t, key); /* grow table */ + return luaH_set(L, t, key); /* re-insert key into grown table */ + } + lua_assert(n != dummynode); + othern = mainposition(t, key2tval(mp)); + if (othern != mp) { /* is colliding node out of its main position? */ + /* yes; move colliding node into free position */ + while (gnext(othern) != mp) othern = gnext(othern); /* find previous */ + gnext(othern) = n; /* redo the chain with `n' in place of `mp' */ + *n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ + gnext(mp) = NULL; /* now `mp' is free */ + setnilvalue(gval(mp)); + } + else { /* colliding node is in its own main position */ + /* new node will go into free position */ + gnext(n) = gnext(mp); /* chain new position */ + gnext(mp) = n; + mp = n; + } + } + gkey(mp)->value = key->value; gkey(mp)->tt = key->tt; + luaC_barriert(L, t, key); + lua_assert(ttisnil(gval(mp))); + return gval(mp); +} + + +/* +** search function for integers +*/ +const TValue *luaH_getnum (Table *t, int key) { + /* (1 <= key && key <= t->sizearray) */ + if (cast(unsigned int, key-1) < cast(unsigned int, t->sizearray)) + return &t->array[key-1]; + else { + lua_Number nk = cast_num(key); + Node *n = hashnum(t, nk); + do { /* check whether `key' is somewhere in the chain */ + if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; + } +} + + +/* +** search function for strings +*/ +const TValue *luaH_getstr (Table *t, TString *key) { + Node *n = hashstr(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (ttisstring(gkey(n)) && rawtsvalue(gkey(n)) == key) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; +} + + +/* +** main search function +*/ +const TValue *luaH_get (Table *t, const TValue *key) { + switch (ttype(key)) { + case LUA_TNIL: return luaO_nilobject; + case LUA_TSTRING: return luaH_getstr(t, rawtsvalue(key)); + case LUA_TNUMBER: { + int k; + lua_Number n = nvalue(key); + lua_number2int(k, n); + if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */ + return luaH_getnum(t, k); /* use specialized version */ + /* else go through */ + } + default: { + Node *n = mainposition(t, key); + do { /* check whether `key' is somewhere in the chain */ + if (luaO_rawequalObj(key2tval(n), key)) + return gval(n); /* that's it */ + else n = gnext(n); + } while (n); + return luaO_nilobject; + } + } +} + + +TValue *luaH_set (lua_State *L, Table *t, const TValue *key) { + const TValue *p = luaH_get(t, key); + t->flags = 0; + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + if (ttisnil(key)) luaG_runerror(L, "table index is nil"); + else if (ttisnumber(key) && luai_numisnan(nvalue(key))) + luaG_runerror(L, "table index is NaN"); + return newkey(L, t, key); + } +} + + +TValue *luaH_setnum (lua_State *L, Table *t, int key) { + const TValue *p = luaH_getnum(t, key); + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + TValue k; + setnvalue(&k, cast_num(key)); + return newkey(L, t, &k); + } +} + + +TValue *luaH_setstr (lua_State *L, Table *t, TString *key) { + const TValue *p = luaH_getstr(t, key); + if (p != luaO_nilobject) + return cast(TValue *, p); + else { + TValue k; + setsvalue(L, &k, key); + return newkey(L, t, &k); + } +} + + +static int unbound_search (Table *t, unsigned int j) { + unsigned int i = j; /* i is zero or a present index */ + j++; + /* find `i' and `j' such that i is present and j is not */ + while (!ttisnil(luaH_getnum(t, j))) { + i = j; + j *= 2; + if (j > cast(unsigned int, MAX_INT)) { /* overflow? */ + /* table was built with bad purposes: resort to linear search */ + i = 1; + while (!ttisnil(luaH_getnum(t, i))) i++; + return i - 1; + } + } + /* now do a binary search between them */ + while (j - i > 1) { + unsigned int m = (i+j)/2; + if (ttisnil(luaH_getnum(t, m))) j = m; + else i = m; + } + return i; +} + + +/* +** Try to find a boundary in table `t'. A `boundary' is an integer index +** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil). +*/ +int luaH_getn (Table *t) { + unsigned int j = t->sizearray; + if (j > 0 && ttisnil(&t->array[j - 1])) { + /* there is a boundary in the array part: (binary) search for it */ + unsigned int i = 0; + while (j - i > 1) { + unsigned int m = (i+j)/2; + if (ttisnil(&t->array[m - 1])) j = m; + else i = m; + } + return i; + } + /* else must find a boundary in hash part */ + else if (t->node == dummynode) /* hash part is empty? */ + return j; /* that is easy... */ + else return unbound_search(t, j); +} + + + +#if defined(LUA_DEBUG) + +Node *luaH_mainposition (const Table *t, const TValue *key) { + return mainposition(t, key); +} + +int luaH_isdummy (Node *n) { return n == dummynode; } + +#endif === added file 'lua/ltable.h' --- lua/ltable.h 1970-01-01 00:00:00 +0000 +++ lua/ltable.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,40 @@ +/* +** $Id: ltable.h,v 2.10.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua tables (hash) +** See Copyright Notice in lua.h +*/ + +#ifndef ltable_h +#define ltable_h + +#include "lobject.h" + + +#define gnode(t,i) (&(t)->node[i]) +#define gkey(n) (&(n)->i_key.nk) +#define gval(n) (&(n)->i_val) +#define gnext(n) ((n)->i_key.nk.next) + +#define key2tval(n) (&(n)->i_key.tvk) + + +LUAI_FUNC const TValue *luaH_getnum (Table *t, int key); +LUAI_FUNC TValue *luaH_setnum (lua_State *L, Table *t, int key); +LUAI_FUNC const TValue *luaH_getstr (Table *t, TString *key); +LUAI_FUNC TValue *luaH_setstr (lua_State *L, Table *t, TString *key); +LUAI_FUNC const TValue *luaH_get (Table *t, const TValue *key); +LUAI_FUNC TValue *luaH_set (lua_State *L, Table *t, const TValue *key); +LUAI_FUNC Table *luaH_new (lua_State *L, int narray, int lnhash); +LUAI_FUNC void luaH_resizearray (lua_State *L, Table *t, int nasize); +LUAI_FUNC void luaH_free (lua_State *L, Table *t); +LUAI_FUNC int luaH_next (lua_State *L, Table *t, StkId key); +LUAI_FUNC int luaH_getn (Table *t); + + +#if defined(LUA_DEBUG) +LUAI_FUNC Node *luaH_mainposition (const Table *t, const TValue *key); +LUAI_FUNC int luaH_isdummy (Node *n); +#endif + + +#endif === added file 'lua/ltablib.c' --- lua/ltablib.c 1970-01-01 00:00:00 +0000 +++ lua/ltablib.c 2008-08-01 15:19:23 +0000 @@ -0,0 +1,279 @@ +/* +** $Id: ltablib.c,v 1.38.1.2 2007/12/28 15:32:23 roberto Exp $ +** Library for Table Manipulation +** See Copyright Notice in lua.h +*/ + + +#include + +#define ltablib_c +#define LUA_LIB + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + +#define aux_getn(L,n) (luaL_checktype(L, n, LUA_TTABLE), luaL_getn(L, n)) + + +static int foreachi (lua_State *L) { + int i; + int n = aux_getn(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + for (i=1; i <= n; i++) { + lua_pushvalue(L, 2); /* function */ + lua_pushinteger(L, i); /* 1st argument */ + lua_rawgeti(L, 1, i); /* 2nd argument */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 1); /* remove nil result */ + } + return 0; +} + + +static int foreach (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); + luaL_checktype(L, 2, LUA_TFUNCTION); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1)) { + lua_pushvalue(L, 2); /* function */ + lua_pushvalue(L, -3); /* key */ + lua_pushvalue(L, -3); /* value */ + lua_call(L, 2, 1); + if (!lua_isnil(L, -1)) + return 1; + lua_pop(L, 2); /* remove value and result */ + } + return 0; +} + + +static int maxn (lua_State *L) { + lua_Number max = 0; + luaL_checktype(L, 1, LUA_TTABLE); + lua_pushnil(L); /* first key */ + while (lua_next(L, 1)) { + lua_pop(L, 1); /* remove value */ + if (lua_type(L, -1) == LUA_TNUMBER) { + lua_Number v = lua_tonumber(L, -1); + if (v > max) max = v; + } + } + lua_pushnumber(L, max); + return 1; +} + + +static int getn (lua_State *L) { + lua_pushinteger(L, aux_getn(L, 1)); + return 1; +} + + +static int setn (lua_State *L) { + luaL_checktype(L, 1, LUA_TTABLE); +#ifndef luaL_setn + luaL_setn(L, 1, luaL_checkint(L, 2)); +#else + luaL_error(L, LUA_QL("setn") " is obsolete"); +#endif + lua_pushvalue(L, 1); + return 1; +} + + +static int tinsert (lua_State *L) { + int e = aux_getn(L, 1) + 1; /* first empty element */ + int pos; /* where to insert new element */ + switch (lua_gettop(L)) { + case 2: { /* called with only 2 arguments */ + pos = e; /* insert new element at the end */ + break; + } + case 3: { + int i; + pos = luaL_checkint(L, 2); /* 2nd argument is the position */ + if (pos > e) e = pos; /* `grow' array if necessary */ + for (i = e; i > pos; i--) { /* move up elements */ + lua_rawgeti(L, 1, i-1); + lua_rawseti(L, 1, i); /* t[i] = t[i-1] */ + } + break; + } + default: { + return luaL_error(L, "wrong number of arguments to " LUA_QL("insert")); + } + } + luaL_setn(L, 1, e); /* new size */ + lua_rawseti(L, 1, pos); /* t[pos] = v */ + return 0; +} + + +static int tremove (lua_State *L) { + int e = aux_getn(L, 1); + int pos = luaL_optint(L, 2, e); + if (!(1 <= pos && pos <= e)) /* position is outside bounds? */ + return 0; /* nothing to remove */ + luaL_setn(L, 1, e - 1); /* t.n = n-1 */ + lua_rawgeti(L, 1, pos); /* result = t[pos] */ + for ( ;pos= P */ + while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) { + if (i>u) luaL_error(L, "invalid order function for sorting"); + lua_pop(L, 1); /* remove a[i] */ + } + /* repeat --j until a[j] <= P */ + while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) { + if (j +#endif /* ! USE_GRUB_LIB */ + +#define ltm_c +#define LUA_CORE + +#include "lua.h" + +#include "lobject.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" + + + +const char *const luaT_typenames[] = { + "nil", "boolean", "userdata", "number", + "string", "table", "function", "userdata", "thread", + "proto", "upval" +}; + + +void luaT_init (lua_State *L) { + static const char *const luaT_eventname[] = { /* ORDER TM */ + "__index", "__newindex", + "__gc", "__mode", "__eq", + "__add", "__sub", "__mul", "__div", "__mod", + "__pow", "__unm", "__len", "__lt", "__le", + "__concat", "__call" + }; + int i; + for (i=0; itmname[i] = luaS_new(L, luaT_eventname[i]); + luaS_fix(G(L)->tmname[i]); /* never collect these names */ + } +} + + +/* +** function to be used with macro "fasttm": optimized for absence of +** tag methods +*/ +const TValue *luaT_gettm (Table *events, TMS event, TString *ename) { + const TValue *tm = luaH_getstr(events, ename); + lua_assert(event <= TM_EQ); + if (ttisnil(tm)) { /* no tag method? */ + events->flags |= cast_byte(1u<metatable; + break; + case LUA_TUSERDATA: + mt = uvalue(o)->metatable; + break; + default: + mt = G(L)->mt[ttype(o)]; + } + return (mt ? luaH_getstr(mt, G(L)->tmname[event]) : luaO_nilobject); +} + === added file 'lua/ltm.h' --- lua/ltm.h 1970-01-01 00:00:00 +0000 +++ lua/ltm.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,54 @@ +/* +** $Id: ltm.h,v 2.6.1.1 2007/12/27 13:02:25 roberto Exp $ +** Tag methods +** See Copyright Notice in lua.h +*/ + +#ifndef ltm_h +#define ltm_h + + +#include "lobject.h" + + +/* +* WARNING: if you change the order of this enumeration, +* grep "ORDER TM" +*/ +typedef enum { + TM_INDEX, + TM_NEWINDEX, + TM_GC, + TM_MODE, + TM_EQ, /* last tag method with `fast' access */ + TM_ADD, + TM_SUB, + TM_MUL, + TM_DIV, + TM_MOD, + TM_POW, + TM_UNM, + TM_LEN, + TM_LT, + TM_LE, + TM_CONCAT, + TM_CALL, + TM_N /* number of elements in the enum */ +} TMS; + + + +#define gfasttm(g,et,e) ((et) == NULL ? NULL : \ + ((et)->flags & (1u<<(e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e])) + +#define fasttm(l,et,e) gfasttm(G(l), et, e) + +LUAI_DATA const char *const luaT_typenames[]; + + +LUAI_FUNC const TValue *luaT_gettm (Table *events, TMS event, TString *ename); +LUAI_FUNC const TValue *luaT_gettmbyobj (lua_State *L, const TValue *o, + TMS event); +LUAI_FUNC void luaT_init (lua_State *L); + +#endif === added file 'lua/lua.c' --- lua/lua.c 1970-01-01 00:00:00 +0000 +++ lua/lua.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,418 @@ +/* +** $Id: lua.c,v 1.160.1.2 2007/12/28 15:32:23 roberto Exp $ +** Lua stand-alone interpreter +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#include +#include +#include + +#define STDERR_PRINTF(format, args...) fprintf(stderr, format, ##args) +#define STDERR_FLUSH() fflush(stderr) +#define PRINT(s) fputs(s, stdout) +#define FLUSH() fflush(stdout) +#define GETENV(name) getenv(name) + +#else /* USE_GRUB_LIB */ +#include "luacmd.h" +#define STDERR_PRINTF(format, args...) grub_printf(format, ##args) +#define STDERR_FLUSH() +#define PRINT(s) grub_printf("%s", s) +#define FLUSH() +#define GETENV(name) grub_env_get(name) + +#endif /* USE_GRUB_LIB */ + +#define lua_c + +#include "lua.h" + +#include "lauxlib.h" +#include "lualib.h" + + + +static lua_State *globalL = NULL; + +static const char *progname = LUA_PROGNAME; + +#ifndef USE_GRUB_LIB +static void lstop (lua_State *L, lua_Debug *ar) { + (void)ar; /* unused arg. */ + lua_sethook(L, NULL, 0, 0); + luaL_error(L, "interrupted!"); +} + + +static void laction (int i) { + signal(i, SIG_DFL); /* if another SIGINT happens before lstop, + terminate process (default action) */ + lua_sethook(globalL, lstop, LUA_MASKCALL | LUA_MASKRET | LUA_MASKCOUNT, 1); +} +#endif /* ! USE_GRUB_LIB */ + + +static void print_usage (void) { + STDERR_PRINTF( + "usage: %s [options] [script [args]].\n" + "Available options are:\n" + " -e stat execute string " LUA_QL("stat") "\n" + " -l name require library " LUA_QL("name") "\n" + " -i enter interactive mode after executing " LUA_QL("script") "\n" + " -v show version information\n" + " -- stop handling options\n" + " - execute stdin and stop handling options\n" + , + progname); + STDERR_FLUSH(); +} + + +static void l_message (const char *pname, const char *msg) { + if (pname) + STDERR_PRINTF("%s: ", pname); + STDERR_PRINTF("%s\n", msg); + STDERR_FLUSH(); +} + + +static int report (lua_State *L, int status) { + if (status && !lua_isnil(L, -1)) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) msg = "(error object is not a string)"; + l_message(progname, msg); + lua_pop(L, 1); + } + return status; +} + + +static int traceback (lua_State *L) { + if (!lua_isstring(L, 1)) /* 'message' not a string? */ + return 1; /* keep it intact */ + lua_getfield(L, LUA_GLOBALSINDEX, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} + + +static int docall (lua_State *L, int narg, int clear) { + int status; + int base = lua_gettop(L) - narg; /* function index */ + lua_pushcfunction(L, traceback); /* push traceback function */ + lua_insert(L, base); /* put it under chunk and args */ +#ifndef USE_GRUB_LIB + signal(SIGINT, laction); +#endif /* ! USE_GRUB_LIB */ + status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base); +#ifndef USE_GRUB_LIB + signal(SIGINT, SIG_DFL); +#endif /* ! USE_GRUB_LIB */ + lua_remove(L, base); /* remove traceback function */ + /* force a complete garbage collection in case of errors */ + if (status != 0) lua_gc(L, LUA_GCCOLLECT, 0); + return status; +} + + +static void print_version (void) { + l_message(NULL, LUA_RELEASE " " LUA_COPYRIGHT); +} + + +static int getargs (lua_State *L, char **argv, int n) { + int narg; + int i; + int argc = 0; + while (argv[argc]) argc++; /* count total number of arguments */ + narg = argc - (n + 1); /* number of arguments to the script */ + luaL_checkstack(L, narg + 3, "too many arguments to script"); + for (i=n+1; i < argc; i++) + lua_pushstring(L, argv[i]); + lua_createtable(L, narg, n + 1); + for (i=0; i < argc; i++) { + lua_pushstring(L, argv[i]); + lua_rawseti(L, -2, i - n); + } + return narg; +} + + +static int dofile (lua_State *L, const char *name) { + int status = luaL_loadfile(L, name) || docall(L, 0, 1); + return report(L, status); +} + + +static int dostring (lua_State *L, const char *s, const char *name) { + int status = luaL_loadbuffer(L, s, strlen(s), name) || docall(L, 0, 1); + return report(L, status); +} + + +static int dolibrary (lua_State *L, const char *name) { + lua_getglobal(L, "require"); + lua_pushstring(L, name); + return report(L, docall(L, 1, 1)); +} + + +static const char *get_prompt (lua_State *L, int firstline) { + const char *p; + lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2"); + p = lua_tostring(L, -1); + if (p == NULL) p = (firstline ? LUA_PROMPT : LUA_PROMPT2); + lua_pop(L, 1); /* remove global */ + return p; +} + + +static int incomplete (lua_State *L, int status) { + if (status == LUA_ERRSYNTAX) { + size_t lmsg; + const char *msg = lua_tolstring(L, -1, &lmsg); + const char *tp = msg + lmsg - (sizeof(LUA_QL("")) - 1); + if (strstr(msg, LUA_QL("")) == tp) { + lua_pop(L, 1); + return 1; + } + } + return 0; /* else... */ +} + + +static int pushline (lua_State *L, int firstline) { + char buffer[LUA_MAXINPUT]; + char *b = buffer; + size_t l; + const char *prmt = get_prompt(L, firstline); + if (lua_readline(L, b, prmt) == 0) + return 0; /* no input */ + l = strlen(b); + if (l > 0 && b[l-1] == '\n') /* line ends with newline? */ + b[l-1] = '\0'; /* remove it */ + if (firstline && b[0] == '=') /* first line starts with `=' ? */ + lua_pushfstring(L, "return %s", b+1); /* change it to `return' */ + else + lua_pushstring(L, b); + lua_freeline(L, b); + return 1; +} + + +static int loadline (lua_State *L) { + int status; + lua_settop(L, 0); + if (!pushline(L, 1)) + return -1; /* no input */ + for (;;) { /* repeat until gets a complete line */ + status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin"); + if (!incomplete(L, status)) break; /* cannot try to add lines? */ + if (!pushline(L, 0)) /* no more input? */ + return -1; + lua_pushliteral(L, "\n"); /* add a new line... */ + lua_insert(L, -2); /* ...between the two lines */ + lua_concat(L, 3); /* join them */ + } + lua_saveline(L, 1); + lua_remove(L, 1); /* remove line */ + return status; +} + + +static void dotty (lua_State *L) { + int status; + const char *oldprogname = progname; + progname = NULL; + while ((status = loadline(L)) != -1) { + if (status == 0) status = docall(L, 0, 0); + report(L, status); + if (status == 0 && lua_gettop(L) > 0) { /* any result to print? */ + lua_getglobal(L, "print"); + lua_insert(L, 1); + if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0) + l_message(progname, lua_pushfstring(L, + "error calling " LUA_QL("print") " (%s)", + lua_tostring(L, -1))); + } + } + lua_settop(L, 0); /* clear stack */ + PRINT("\n"); + FLUSH(); + progname = oldprogname; +} + + +static int handle_script (lua_State *L, char **argv, int n) { + int status; + const char *fname; + int narg = getargs(L, argv, n); /* collect arguments */ + lua_setglobal(L, "arg"); + fname = argv[n]; + if (strcmp(fname, "-") == 0 && strcmp(argv[n-1], "--") != 0) + fname = NULL; /* stdin */ + status = luaL_loadfile(L, fname); + lua_insert(L, -(narg+1)); + if (status == 0) + status = docall(L, narg, 0); + else + lua_pop(L, narg); + return report(L, status); +} + + +/* check that argument has no extra characters at the end */ +#define notail(x) {if ((x)[2] != '\0') return -1;} + + +static int collectargs (char **argv, int *pi, int *pv, int *pe) { + int i; + for (i = 1; argv[i] != NULL; i++) { + if (argv[i][0] != '-') /* not an option? */ + return i; + switch (argv[i][1]) { /* option */ + case '-': + notail(argv[i]); + return (argv[i+1] != NULL ? i+1 : 0); + case '\0': + return i; + case 'i': + notail(argv[i]); + *pi = 1; /* go through */ + case 'v': + notail(argv[i]); + *pv = 1; + break; + case 'e': + *pe = 1; /* go through */ + case 'l': + if (argv[i][2] == '\0') { + i++; + if (argv[i] == NULL) return -1; + } + break; + default: return -1; /* invalid option */ + } + } + return 0; +} + + +static int runargs (lua_State *L, char **argv, int n) { + int i; + for (i = 1; i < n; i++) { + if (argv[i] == NULL) continue; + lua_assert(argv[i][0] == '-'); + switch (argv[i][1]) { /* option */ + case 'e': { + const char *chunk = argv[i] + 2; + if (*chunk == '\0') chunk = argv[++i]; + lua_assert(chunk != NULL); + if (dostring(L, chunk, "=(command line)") != 0) + return 1; + break; + } + case 'l': { + const char *filename = argv[i] + 2; + if (*filename == '\0') filename = argv[++i]; + lua_assert(filename != NULL); + if (dolibrary(L, filename)) + return 1; /* stop if file fails */ + break; + } + default: break; + } + } + return 0; +} + + +static int handle_luainit (lua_State *L) { + const char *init = GETENV(LUA_INIT); + if (init == NULL) return 0; /* status OK */ + else if (init[0] == '@') + return dofile(L, init+1); + else + return dostring(L, init, "=" LUA_INIT); +} + + +struct Smain { + int argc; + char **argv; + int status; +}; + + +static int pmain (lua_State *L) { + struct Smain *s = (struct Smain *)lua_touserdata(L, 1); + char **argv = s->argv; + int script; + int has_i = 0, has_v = 0, has_e = 0; + globalL = L; + if (argv[0] && argv[0][0]) progname = argv[0]; + lua_gc(L, LUA_GCSTOP, 0); /* stop collector during initialization */ + luaL_openlibs(L); /* open libraries */ + lua_gc(L, LUA_GCRESTART, 0); + s->status = handle_luainit(L); + if (s->status != 0) return 0; + script = collectargs(argv, &has_i, &has_v, &has_e); + if (script < 0) { /* invalid args? */ + print_usage(); + s->status = 1; + return 0; + } + if (has_v) print_version(); + s->status = runargs(L, argv, (script > 0) ? script : s->argc); + if (s->status != 0) return 0; + if (script) + s->status = handle_script(L, argv, script); + if (s->status != 0) return 0; + if (has_i) + dotty(L); + else if (script == 0 && !has_e && !has_v) { + if (lua_stdin_is_tty()) { + print_version(); + dotty(L); + } + else dofile(L, NULL); /* executes stdin as a file */ + } + return 0; +} + + +#ifdef GRUB_COMMAND +int grub_lua_main (int argc, char **argv) { +#else +int main (int argc, char **argv) { +#endif /* ! USE_GRUB_LIB */ + int status; + struct Smain s; + lua_State *L = lua_open(); /* create state */ + if (L == NULL) { + l_message(argv[0], "cannot create state: not enough memory"); + return EXIT_FAILURE; + } + s.argc = argc; + s.argv = argv; + status = lua_cpcall(L, &pmain, &s); + report(L, status); + lua_close(L); + return (status || s.status) ? EXIT_FAILURE : EXIT_SUCCESS; +} + === added file 'lua/lua.h' --- lua/lua.h 1970-01-01 00:00:00 +0000 +++ lua/lua.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,388 @@ +/* +** $Id: lua.h,v 1.218.1.4 2008/01/03 15:41:15 roberto Exp $ +** Lua - An Extensible Extension Language +** Lua.org, PUC-Rio, Brazil (http://www.lua.org) +** See Copyright Notice at the end of this file +*/ + + +#ifndef lua_h +#define lua_h + +#include +#include + + +#include "luaconf.h" + + +#define LUA_VERSION "Lua 5.1" +#define LUA_RELEASE "Lua 5.1.3" +#define LUA_VERSION_NUM 501 +#define LUA_COPYRIGHT "Copyright (C) 1994-2008 Lua.org, PUC-Rio" +#define LUA_AUTHORS "R. Ierusalimschy, L. H. de Figueiredo & W. Celes" + + +/* mark for precompiled code (`Lua') */ +#define LUA_SIGNATURE "\033Lua" + +/* option for multiple returns in `lua_pcall' and `lua_call' */ +#define LUA_MULTRET (-1) + + +/* +** pseudo-indices +*/ +#define LUA_REGISTRYINDEX (-10000) +#define LUA_ENVIRONINDEX (-10001) +#define LUA_GLOBALSINDEX (-10002) +#define lua_upvalueindex(i) (LUA_GLOBALSINDEX-(i)) + + +/* thread status; 0 is OK */ +#define LUA_YIELD 1 +#define LUA_ERRRUN 2 +#define LUA_ERRSYNTAX 3 +#define LUA_ERRMEM 4 +#define LUA_ERRERR 5 + + +typedef struct lua_State lua_State; + +typedef int (*lua_CFunction) (lua_State *L); + + +/* +** functions that read/write blocks when loading/dumping Lua chunks +*/ +typedef const char * (*lua_Reader) (lua_State *L, void *ud, size_t *sz); + +typedef int (*lua_Writer) (lua_State *L, const void* p, size_t sz, void* ud); + + +/* +** prototype for memory-allocation functions +*/ +typedef void * (*lua_Alloc) (void *ud, void *ptr, size_t osize, size_t nsize); + + +/* +** basic types +*/ +#define LUA_TNONE (-1) + +#define LUA_TNIL 0 +#define LUA_TBOOLEAN 1 +#define LUA_TLIGHTUSERDATA 2 +#define LUA_TNUMBER 3 +#define LUA_TSTRING 4 +#define LUA_TTABLE 5 +#define LUA_TFUNCTION 6 +#define LUA_TUSERDATA 7 +#define LUA_TTHREAD 8 + + + +/* minimum Lua stack available to a C function */ +#define LUA_MINSTACK 20 + + +/* +** generic extra include file +*/ +#if defined(LUA_USER_H) +#include LUA_USER_H +#endif + + +/* type of numbers in Lua */ +typedef LUA_NUMBER lua_Number; + + +/* type for integer functions */ +typedef LUA_INTEGER lua_Integer; + + + +/* +** state manipulation +*/ +LUA_API lua_State *(lua_newstate) (lua_Alloc f, void *ud); +LUA_API void (lua_close) (lua_State *L); +LUA_API lua_State *(lua_newthread) (lua_State *L); + +LUA_API lua_CFunction (lua_atpanic) (lua_State *L, lua_CFunction panicf); + + +/* +** basic stack manipulation +*/ +LUA_API int (lua_gettop) (lua_State *L); +LUA_API void (lua_settop) (lua_State *L, int idx); +LUA_API void (lua_pushvalue) (lua_State *L, int idx); +LUA_API void (lua_remove) (lua_State *L, int idx); +LUA_API void (lua_insert) (lua_State *L, int idx); +LUA_API void (lua_replace) (lua_State *L, int idx); +LUA_API int (lua_checkstack) (lua_State *L, int sz); + +LUA_API void (lua_xmove) (lua_State *from, lua_State *to, int n); + + +/* +** access functions (stack -> C) +*/ + +LUA_API int (lua_isnumber) (lua_State *L, int idx); +LUA_API int (lua_isstring) (lua_State *L, int idx); +LUA_API int (lua_iscfunction) (lua_State *L, int idx); +LUA_API int (lua_isuserdata) (lua_State *L, int idx); +LUA_API int (lua_type) (lua_State *L, int idx); +LUA_API const char *(lua_typename) (lua_State *L, int tp); + +LUA_API int (lua_equal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_rawequal) (lua_State *L, int idx1, int idx2); +LUA_API int (lua_lessthan) (lua_State *L, int idx1, int idx2); + +LUA_API lua_Number (lua_tonumber) (lua_State *L, int idx); +LUA_API lua_Integer (lua_tointeger) (lua_State *L, int idx); +LUA_API int (lua_toboolean) (lua_State *L, int idx); +LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len); +LUA_API size_t (lua_objlen) (lua_State *L, int idx); +LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx); +LUA_API void *(lua_touserdata) (lua_State *L, int idx); +LUA_API lua_State *(lua_tothread) (lua_State *L, int idx); +LUA_API const void *(lua_topointer) (lua_State *L, int idx); + + +/* +** push functions (C -> stack) +*/ +LUA_API void (lua_pushnil) (lua_State *L); +LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n); +LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n); +LUA_API void (lua_pushlstring) (lua_State *L, const char *s, size_t l); +LUA_API void (lua_pushstring) (lua_State *L, const char *s); +LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, + va_list argp); +LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...); +LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n); +LUA_API void (lua_pushboolean) (lua_State *L, int b); +LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p); +LUA_API int (lua_pushthread) (lua_State *L); + + +/* +** get functions (Lua -> stack) +*/ +LUA_API void (lua_gettable) (lua_State *L, int idx); +LUA_API void (lua_getfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawget) (lua_State *L, int idx); +LUA_API void (lua_rawgeti) (lua_State *L, int idx, int n); +LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec); +LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz); +LUA_API int (lua_getmetatable) (lua_State *L, int objindex); +LUA_API void (lua_getfenv) (lua_State *L, int idx); + + +/* +** set functions (stack -> Lua) +*/ +LUA_API void (lua_settable) (lua_State *L, int idx); +LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k); +LUA_API void (lua_rawset) (lua_State *L, int idx); +LUA_API void (lua_rawseti) (lua_State *L, int idx, int n); +LUA_API int (lua_setmetatable) (lua_State *L, int objindex); +LUA_API int (lua_setfenv) (lua_State *L, int idx); + + +/* +** `load' and `call' functions (load and run Lua code) +*/ +LUA_API void (lua_call) (lua_State *L, int nargs, int nresults); +LUA_API int (lua_pcall) (lua_State *L, int nargs, int nresults, int errfunc); +LUA_API int (lua_cpcall) (lua_State *L, lua_CFunction func, void *ud); +LUA_API int (lua_load) (lua_State *L, lua_Reader reader, void *dt, + const char *chunkname); + +LUA_API int (lua_dump) (lua_State *L, lua_Writer writer, void *data); + + +/* +** coroutine functions +*/ +LUA_API int (lua_yield) (lua_State *L, int nresults); +LUA_API int (lua_resume) (lua_State *L, int narg); +LUA_API int (lua_status) (lua_State *L); + +/* +** garbage-collection function and options +*/ + +#define LUA_GCSTOP 0 +#define LUA_GCRESTART 1 +#define LUA_GCCOLLECT 2 +#define LUA_GCCOUNT 3 +#define LUA_GCCOUNTB 4 +#define LUA_GCSTEP 5 +#define LUA_GCSETPAUSE 6 +#define LUA_GCSETSTEPMUL 7 + +LUA_API int (lua_gc) (lua_State *L, int what, int data); + + +/* +** miscellaneous functions +*/ + +LUA_API int (lua_error) (lua_State *L); + +LUA_API int (lua_next) (lua_State *L, int idx); + +LUA_API void (lua_concat) (lua_State *L, int n); + +LUA_API lua_Alloc (lua_getallocf) (lua_State *L, void **ud); +LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud); + + + +/* +** =============================================================== +** some useful macros +** =============================================================== +*/ + +#define lua_pop(L,n) lua_settop(L, -(n)-1) + +#define lua_newtable(L) lua_createtable(L, 0, 0) + +#define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) + +#define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) + +#define lua_strlen(L,i) lua_objlen(L, (i)) + +#define lua_isfunction(L,n) (lua_type(L, (n)) == LUA_TFUNCTION) +#define lua_istable(L,n) (lua_type(L, (n)) == LUA_TTABLE) +#define lua_islightuserdata(L,n) (lua_type(L, (n)) == LUA_TLIGHTUSERDATA) +#define lua_isnil(L,n) (lua_type(L, (n)) == LUA_TNIL) +#define lua_isboolean(L,n) (lua_type(L, (n)) == LUA_TBOOLEAN) +#define lua_isthread(L,n) (lua_type(L, (n)) == LUA_TTHREAD) +#define lua_isnone(L,n) (lua_type(L, (n)) == LUA_TNONE) +#define lua_isnoneornil(L, n) (lua_type(L, (n)) <= 0) + +#define lua_pushliteral(L, s) \ + lua_pushlstring(L, "" s, (sizeof(s)/sizeof(char))-1) + +#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, (s)) +#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s)) + +#define lua_tostring(L,i) lua_tolstring(L, (i), NULL) + + + +/* +** compatibility macros and functions +*/ + +#define lua_open() luaL_newstate() + +#define lua_getregistry(L) lua_pushvalue(L, LUA_REGISTRYINDEX) + +#define lua_getgccount(L) lua_gc(L, LUA_GCCOUNT, 0) + +#define lua_Chunkreader lua_Reader +#define lua_Chunkwriter lua_Writer + + +/* hack */ +LUA_API void lua_setlevel (lua_State *from, lua_State *to); + + +/* +** {====================================================================== +** Debug API +** ======================================================================= +*/ + + +/* +** Event codes +*/ +#define LUA_HOOKCALL 0 +#define LUA_HOOKRET 1 +#define LUA_HOOKLINE 2 +#define LUA_HOOKCOUNT 3 +#define LUA_HOOKTAILRET 4 + + +/* +** Event masks +*/ +#define LUA_MASKCALL (1 << LUA_HOOKCALL) +#define LUA_MASKRET (1 << LUA_HOOKRET) +#define LUA_MASKLINE (1 << LUA_HOOKLINE) +#define LUA_MASKCOUNT (1 << LUA_HOOKCOUNT) + +typedef struct lua_Debug lua_Debug; /* activation record */ + + +/* Functions to be called by the debuger in specific events */ +typedef void (*lua_Hook) (lua_State *L, lua_Debug *ar); + + +LUA_API int lua_getstack (lua_State *L, int level, lua_Debug *ar); +LUA_API int lua_getinfo (lua_State *L, const char *what, lua_Debug *ar); +LUA_API const char *lua_getlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_setlocal (lua_State *L, const lua_Debug *ar, int n); +LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n); +LUA_API const char *lua_setupvalue (lua_State *L, int funcindex, int n); + +LUA_API int lua_sethook (lua_State *L, lua_Hook func, int mask, int count); +LUA_API lua_Hook lua_gethook (lua_State *L); +LUA_API int lua_gethookmask (lua_State *L); +LUA_API int lua_gethookcount (lua_State *L); + + +struct lua_Debug { + int event; + const char *name; /* (n) */ + const char *namewhat; /* (n) `global', `local', `field', `method' */ + const char *what; /* (S) `Lua', `C', `main', `tail' */ + const char *source; /* (S) */ + int currentline; /* (l) */ + int nups; /* (u) number of upvalues */ + int linedefined; /* (S) */ + int lastlinedefined; /* (S) */ + char short_src[LUA_IDSIZE]; /* (S) */ + /* private part */ + int i_ci; /* active function */ +}; + +/* }====================================================================== */ + + +/****************************************************************************** +* Copyright (C) 1994-2008 Lua.org, PUC-Rio. All rights reserved. +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + + +#endif === added file 'lua/luac.c' --- lua/luac.c 1970-01-01 00:00:00 +0000 +++ lua/luac.c 2008-08-01 15:19:23 +0000 @@ -0,0 +1,200 @@ +/* +** $Id: luac.c,v 1.54 2006/06/02 17:37:11 lhf Exp $ +** Lua compiler (saves bytecodes to files; also list bytecodes) +** See Copyright Notice in lua.h +*/ + +#include +#include +#include +#include + +#define luac_c +#define LUA_CORE + +#include "lua.h" +#include "lauxlib.h" + +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstring.h" +#include "lundump.h" + +#define PROGNAME "luac" /* default program name */ +#define OUTPUT PROGNAME ".out" /* default output file */ + +static int listing=0; /* list bytecodes? */ +static int dumping=1; /* dump bytecodes? */ +static int stripping=0; /* strip debug information? */ +static char Output[]={ OUTPUT }; /* default output file name */ +static const char* output=Output; /* actual output file name */ +static const char* progname=PROGNAME; /* actual program name */ + +static void fatal(const char* message) +{ + fprintf(stderr,"%s: %s\n",progname,message); + exit(EXIT_FAILURE); +} + +static void cannot(const char* what) +{ + fprintf(stderr,"%s: cannot %s %s: %s\n",progname,what,output,strerror(errno)); + exit(EXIT_FAILURE); +} + +static void usage(const char* message) +{ + if (*message=='-') + fprintf(stderr,"%s: unrecognized option " LUA_QS "\n",progname,message); + else + fprintf(stderr,"%s: %s\n",progname,message); + fprintf(stderr, + "usage: %s [options] [filenames].\n" + "Available options are:\n" + " - process stdin\n" + " -l list\n" + " -o name output to file " LUA_QL("name") " (default is \"%s\")\n" + " -p parse only\n" + " -s strip debug information\n" + " -v show version information\n" + " -- stop handling options\n", + progname,Output); + exit(EXIT_FAILURE); +} + +#define IS(s) (strcmp(argv[i],s)==0) + +static int doargs(int argc, char* argv[]) +{ + int i; + int version=0; + if (argv[0]!=NULL && *argv[0]!=0) progname=argv[0]; + for (i=1; itop+(i))->l.p) + +static const Proto* combine(lua_State* L, int n) +{ + if (n==1) + return toproto(L,-1); + else + { + int i,pc; + Proto* f=luaF_newproto(L); + setptvalue2s(L,L->top,f); incr_top(L); + f->source=luaS_newliteral(L,"=(" PROGNAME ")"); + f->maxstacksize=1; + pc=2*n+1; + f->code=luaM_newvector(L,pc,Instruction); + f->sizecode=pc; + f->p=luaM_newvector(L,n,Proto*); + f->sizep=n; + pc=0; + for (i=0; ip[i]=toproto(L,i-n-1); + f->code[pc++]=CREATE_ABx(OP_CLOSURE,0,i); + f->code[pc++]=CREATE_ABC(OP_CALL,0,1,1); + } + f->code[pc++]=CREATE_ABC(OP_RETURN,0,1,0); + return f; + } +} + +static int writer(lua_State* L, const void* p, size_t size, void* u) +{ + UNUSED(L); + return (fwrite(p,size,1,(FILE*)u)!=1) && (size!=0); +} + +struct Smain { + int argc; + char** argv; +}; + +static int pmain(lua_State* L) +{ + struct Smain* s = (struct Smain*)lua_touserdata(L, 1); + int argc=s->argc; + char** argv=s->argv; + const Proto* f; + int i; + if (!lua_checkstack(L,argc)) fatal("too many input files"); + for (i=0; i1); + if (dumping) + { + FILE* D= (output==NULL) ? stdout : fopen(output,"wb"); + if (D==NULL) cannot("open"); + lua_lock(L); + luaU_dump(L,f,writer,D,stripping); + lua_unlock(L); + if (ferror(D)) cannot("write"); + if (fclose(D)) cannot("close"); + } + return 0; +} + +int main(int argc, char* argv[]) +{ + lua_State* L; + struct Smain s; + int i=doargs(argc,argv); + argc-=i; argv+=i; + if (argc<=0) usage("no input files given"); + L=lua_open(); + if (L==NULL) fatal("not enough memory for state"); + s.argc=argc; + s.argv=argv; + if (lua_cpcall(L,pmain,&s)!=0) fatal(lua_tostring(L,-1)); + lua_close(L); + return EXIT_SUCCESS; +} === added file 'lua/luacmd.c' --- lua/luacmd.c 1970-01-01 00:00:00 +0000 +++ lua/luacmd.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,69 @@ +/* luacmd.c - Lua interpreter command */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 +#include +#include "luacmd.h" +#include "lua.h" +#include "lauxlib.h" + +static grub_err_t +grub_cmd_lua (struct grub_arg_list *state __attribute__ ((unused)), + int argc, + char **argv) +{ + /* Make a copy of the GRUB command's argv so we can put the command + name at index 0, like Lua's main function expects, as well as + terminating the argv array with a NULL pointer. */ + + char **main_argv = grub_malloc (sizeof (char *) * (argc + 2)); + if (! main_argv) + return grub_errno; + + main_argv[0] = "lua"; + grub_memcpy (&main_argv[1], argv, sizeof (char *) * (argc + 1)); + main_argv[argc + 1] = 0; + + int ret; + ret = grub_lua_main (argc + 1, main_argv); + grub_free (main_argv); + + return ret; +} + +GRUB_MOD_INIT(luacmd) +{ + (void)mod; /* To stop warning. */ + /* We use the NO_ARG_PARSE flag so command line options are passed + to the Lua interpreter for handling. */ + grub_register_command ("lua", grub_cmd_lua, + (GRUB_COMMAND_FLAG_BOTH + | GRUB_COMMAND_FLAG_NO_ARG_PARSE), + "lua", "Run the Lua interpreter", 0); +} + +GRUB_MOD_FINI(luacmd) +{ + grub_unregister_command ("lua"); +} + === added file 'lua/luacmd.h' --- lua/luacmd.h 1970-01-01 00:00:00 +0000 +++ lua/luacmd.h 2008-08-01 15:27:27 +0000 @@ -0,0 +1,25 @@ +/* luacmd.h - Lua command include file. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2008 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 . + */ + +#ifndef GRUB_LUA_LUACMD_HEADER +#define GRUB_LUA_LUACMD_HEADER 1 + +int grub_lua_main (int argc, char **argv); + +#endif /* ! GRUB_LUA_LUACMD_HEADER */ === added file 'lua/luaconf.h' --- lua/luaconf.h 1970-01-01 00:00:00 +0000 +++ lua/luaconf.h 2008-08-01 15:27:27 +0000 @@ -0,0 +1,849 @@ +/* +** $Id: luaconf.h,v 1.82.1.6 2008/01/18 17:07:48 roberto Exp $ +** Configuration file for Lua +** See Copyright Notice in lua.h +*/ + + +#ifndef lconfig_h +#define lconfig_h + +#include +#ifndef USE_GRUB_LIB +#include +#else /* USE_GRUB_LIB */ +# include "grublib.h" +# include +# include +# define assert(boolean) real_assert (boolean, __FILE__, __LINE__) +static inline void +real_assert (int boolean, const char *file, const int line) +{ + if (! boolean) + grub_printf ("Assertion failed at %s:%d\n", file, line); +} +#endif /* USE_GRUB_LIB */ + + +/* +** ================================================================== +** Search for "@@" to find all configurable definitions. +** =================================================================== +*/ + + +/* +@@ LUA_ANSI controls the use of non-ansi features. +** CHANGE it (define it) if you want Lua to avoid the use of any +** non-ansi feature or library. +*/ +#if defined(__STRICT_ANSI__) +#define LUA_ANSI +#endif + + +#if !defined(LUA_ANSI) && defined(_WIN32) +#define LUA_WIN +#endif + +#if defined(LUA_USE_LINUX) +#define LUA_USE_POSIX +#define LUA_USE_DLOPEN /* needs an extra library: -ldl */ +#define LUA_USE_READLINE /* needs some extra libraries */ +#endif + +#if defined(LUA_USE_MACOSX) +#define LUA_USE_POSIX +#define LUA_DL_DYLD /* does not need extra library */ +#endif + + + +/* +@@ LUA_USE_POSIX includes all functionallity listed as X/Open System +@* Interfaces Extension (XSI). +** CHANGE it (define it) if your system is XSI compatible. +*/ +#if defined(LUA_USE_POSIX) +#define LUA_USE_MKSTEMP +#define LUA_USE_ISATTY +#define LUA_USE_POPEN +#define LUA_USE_ULONGJMP +#endif + + +/* +@@ LUA_PATH and LUA_CPATH are the names of the environment variables that +@* Lua check to set its paths. +@@ LUA_INIT is the name of the environment variable that Lua +@* checks for initialization code. +** CHANGE them if you want different names. +*/ +#define LUA_PATH "LUA_PATH" +#define LUA_CPATH "LUA_CPATH" +#define LUA_INIT "LUA_INIT" + + +/* +@@ LUA_PATH_DEFAULT is the default path that Lua uses to look for +@* Lua libraries. +@@ LUA_CPATH_DEFAULT is the default path that Lua uses to look for +@* C libraries. +** CHANGE them if your machine has a non-conventional directory +** hierarchy or if you want to install your libraries in +** non-conventional directories. +*/ +#if defined(_WIN32) +/* +** In Windows, any exclamation mark ('!') in the path is replaced by the +** path of the directory of the executable file of the current process. +*/ +#define LUA_LDIR "!\\lua\\" +#define LUA_CDIR "!\\" +#define LUA_PATH_DEFAULT \ + ".\\?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?\\init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?\\init.lua" +#define LUA_CPATH_DEFAULT \ + ".\\?.dll;" LUA_CDIR"?.dll;" LUA_CDIR"loadall.dll" + +#else +#define LUA_ROOT "/usr/local/" +#define LUA_LDIR LUA_ROOT "share/lua/5.1/" +#define LUA_CDIR LUA_ROOT "lib/lua/5.1/" +#define LUA_PATH_DEFAULT \ + "./?.lua;" LUA_LDIR"?.lua;" LUA_LDIR"?/init.lua;" \ + LUA_CDIR"?.lua;" LUA_CDIR"?/init.lua" +#define LUA_CPATH_DEFAULT \ + "./?.so;" LUA_CDIR"?.so;" LUA_CDIR"loadall.so" +#endif + + +/* +@@ LUA_DIRSEP is the directory separator (for submodules). +** CHANGE it if your machine does not use "/" as the directory separator +** and is not Windows. (On Windows Lua automatically uses "\".) +*/ +#if defined(_WIN32) +#define LUA_DIRSEP "\\" +#else +#define LUA_DIRSEP "/" +#endif + + +/* +@@ LUA_PATHSEP is the character that separates templates in a path. +@@ LUA_PATH_MARK is the string that marks the substitution points in a +@* template. +@@ LUA_EXECDIR in a Windows path is replaced by the executable's +@* directory. +@@ LUA_IGMARK is a mark to ignore all before it when bulding the +@* luaopen_ function name. +** CHANGE them if for some reason your system cannot use those +** characters. (E.g., if one of those characters is a common character +** in file/directory names.) Probably you do not need to change them. +*/ +#define LUA_PATHSEP ";" +#define LUA_PATH_MARK "?" +#define LUA_EXECDIR "!" +#define LUA_IGMARK "-" + + +/* +@@ LUA_INTEGER is the integral type used by lua_pushinteger/lua_tointeger. +** CHANGE that if ptrdiff_t is not adequate on your machine. (On most +** machines, ptrdiff_t gives a good choice between int or long.) +*/ + +/* Changed to long for use with integral Lua numbers. */ +#define LUA_INTEGER long + +/* +@@ LUA_API is a mark for all core API functions. +@@ LUALIB_API is a mark for all standard library functions. +** CHANGE them if you need to define those functions in some special way. +** For instance, if you want to create one Windows DLL with the core and +** the libraries, you may want to use the following definition (define +** LUA_BUILD_AS_DLL to get it). +*/ +#if defined(LUA_BUILD_AS_DLL) + +#if defined(LUA_CORE) || defined(LUA_LIB) +#define LUA_API __declspec(dllexport) +#else +#define LUA_API __declspec(dllimport) +#endif + +#else + +#define LUA_API extern + +#endif + +/* more often than not the libs go together with the core */ +#define LUALIB_API LUA_API + + +/* +@@ LUAI_FUNC is a mark for all extern functions that are not to be +@* exported to outside modules. +@@ LUAI_DATA is a mark for all extern (const) variables that are not to +@* be exported to outside modules. +** CHANGE them if you need to mark them in some special way. Elf/gcc +** (versions 3.2 and later) mark them as "hidden" to optimize access +** when Lua is compiled as a shared library. +*/ +#if defined(luaall_c) +#define LUAI_FUNC static +#define LUAI_DATA /* empty */ + +#elif defined(__GNUC__) && ((__GNUC__*100 + __GNUC_MINOR__) >= 302) && \ + defined(__ELF__) +#define LUAI_FUNC __attribute__((visibility("hidden"))) extern +#define LUAI_DATA LUAI_FUNC + +#else +#define LUAI_FUNC extern +#define LUAI_DATA extern +#endif + + + +/* +@@ LUA_QL describes how error messages quote program elements. +** CHANGE it if you want a different appearance. +*/ +#define LUA_QL(x) "'" x "'" +#define LUA_QS LUA_QL("%s") + + +/* +@@ LUA_IDSIZE gives the maximum size for the description of the source +@* of a function in debug information. +** CHANGE it if you want a different size. +*/ +#define LUA_IDSIZE 60 + + +/* +** {================================================================== +** Stand-alone configuration +** =================================================================== +*/ + +#if defined(lua_c) || defined(luaall_c) + +/* +@@ lua_stdin_is_tty detects whether the standard input is a 'tty' (that +@* is, whether we're running lua interactively). +** CHANGE it if you have a better definition for non-POSIX/non-Windows +** systems. +*/ +#if defined(LUA_USE_ISATTY) +#include +#define lua_stdin_is_tty() isatty(0) +#elif defined(LUA_WIN) +#include +#include +#define lua_stdin_is_tty() _isatty(_fileno(stdin)) +#else +#define lua_stdin_is_tty() 1 /* assume stdin is a tty */ +#endif + + +/* +@@ LUA_PROMPT is the default prompt used by stand-alone Lua. +@@ LUA_PROMPT2 is the default continuation prompt used by stand-alone Lua. +** CHANGE them if you want different prompts. (You can also change the +** prompts dynamically, assigning to globals _PROMPT/_PROMPT2.) +*/ +#define LUA_PROMPT "> " +#define LUA_PROMPT2 ">> " + + +/* +@@ LUA_PROGNAME is the default name for the stand-alone Lua program. +** CHANGE it if your stand-alone interpreter has a different name and +** your system is not able to detect that name automatically. +*/ +#define LUA_PROGNAME "lua" + + +/* +@@ LUA_MAXINPUT is the maximum length for an input line in the +@* stand-alone interpreter. +** CHANGE it if you need longer lines. +*/ +#define LUA_MAXINPUT 512 + + +/* +@@ lua_readline defines how to show a prompt and then read a line from +@* the standard input. +@@ lua_saveline defines how to "save" a read line in a "history". +@@ lua_freeline defines how to free a line read by lua_readline. +** CHANGE them if you want to improve this functionality (e.g., by using +** GNU readline and history facilities). +*/ +#if defined(LUA_USE_READLINE) +#include +#include +#include +#define lua_readline(L,b,p) ((void)L, ((b)=readline(p)) != NULL) +#define lua_saveline(L,idx) \ + if (lua_strlen(L,idx) > 0) /* non-empty line? */ \ + add_history(lua_tostring(L, idx)); /* add it to history */ +#define lua_freeline(L,b) ((void)L, free(b)) + +#elif defined(USE_GRUB_LIB) +#include /* For grub_cmdline_get(), for user input. */ +#define lua_readline(L,buf,prompt) \ + ((void)L, buf[0]=0, grub_cmdline_get(prompt, buf, LUA_MAXINPUT, 0, 1)) +#define lua_saveline(L,idx) { (void)L; (void)idx; } +#define lua_freeline(L,b) { (void)L; (void)b; } + +#else +#define lua_readline(L,b,p) \ + ((void)L, fputs(p, stdout), fflush(stdout), /* show prompt */ \ + fgets(b, LUA_MAXINPUT, stdin) != NULL) /* get line */ +#define lua_saveline(L,idx) { (void)L; (void)idx; } +#define lua_freeline(L,b) { (void)L; (void)b; } +#endif + +#endif /* lua_c || luaall_c */ + +/* }================================================================== */ + + +/* +@@ LUAI_GCPAUSE defines the default pause between garbage-collector cycles +@* as a percentage. +** CHANGE it if you want the GC to run faster or slower (higher values +** mean larger pauses which mean slower collection.) You can also change +** this value dynamically. +*/ +#define LUAI_GCPAUSE 200 /* 200% (wait memory to double before next GC) */ + + +/* +@@ LUAI_GCMUL defines the default speed of garbage collection relative to +@* memory allocation as a percentage. +** CHANGE it if you want to change the granularity of the garbage +** collection. (Higher values mean coarser collections. 0 represents +** infinity, where each step performs a full collection.) You can also +** change this value dynamically. +*/ +#define LUAI_GCMUL 200 /* GC runs 'twice the speed' of memory allocation */ + + + +/* +@@ LUA_COMPAT_GETN controls compatibility with old getn behavior. +** CHANGE it (define it) if you want exact compatibility with the +** behavior of setn/getn in Lua 5.0. +*/ +#undef LUA_COMPAT_GETN + +/* +@@ LUA_COMPAT_LOADLIB controls compatibility about global loadlib. +** CHANGE it to undefined as soon as you do not need a global 'loadlib' +** function (the function is still available as 'package.loadlib'). +*/ +#undef LUA_COMPAT_LOADLIB + +/* +@@ LUA_COMPAT_VARARG controls compatibility with old vararg feature. +** CHANGE it to undefined as soon as your programs use only '...' to +** access vararg parameters (instead of the old 'arg' table). +*/ +#define LUA_COMPAT_VARARG + +/* +@@ LUA_COMPAT_MOD controls compatibility with old math.mod function. +** CHANGE it to undefined as soon as your programs use 'math.fmod' or +** the new '%' operator instead of 'math.mod'. +*/ +#define LUA_COMPAT_MOD + +/* +@@ LUA_COMPAT_LSTR controls compatibility with old long string nesting +@* facility. +** CHANGE it to 2 if you want the old behaviour, or undefine it to turn +** off the advisory error when nesting [[...]]. +*/ +#define LUA_COMPAT_LSTR 1 + +/* +@@ LUA_COMPAT_GFIND controls compatibility with old 'string.gfind' name. +** CHANGE it to undefined as soon as you rename 'string.gfind' to +** 'string.gmatch'. +*/ +#define LUA_COMPAT_GFIND + +/* +@@ LUA_COMPAT_OPENLIB controls compatibility with old 'luaL_openlib' +@* behavior. +** CHANGE it to undefined as soon as you replace to 'luaL_register' +** your uses of 'luaL_openlib' +*/ +#define LUA_COMPAT_OPENLIB + + + +/* +@@ luai_apicheck is the assert macro used by the Lua-C API. +** CHANGE luai_apicheck if you want Lua to perform some checks in the +** parameters it gets from API calls. This may slow down the interpreter +** a bit, but may be quite useful when debugging C code that interfaces +** with Lua. A useful redefinition is to use assert.h. +*/ +#if defined(LUA_USE_APICHECK) +#include +#define luai_apicheck(L,o) { (void)L; assert(o); } +#else +#define luai_apicheck(L,o) { (void)L; } +#endif + + +/* +@@ LUAI_BITSINT defines the number of bits in an int. +** CHANGE here if Lua cannot automatically detect the number of bits of +** your machine. Probably you do not need to change this. +*/ +/* avoid overflows in comparison */ +#if INT_MAX-20 < 32760 +#define LUAI_BITSINT 16 +#elif INT_MAX > 2147483640L +/* int has at least 32 bits */ +#define LUAI_BITSINT 32 +#else +#error "you must define LUA_BITSINT with number of bits in an integer" +#endif + + +/* +@@ LUAI_UINT32 is an unsigned integer with at least 32 bits. +@@ LUAI_INT32 is an signed integer with at least 32 bits. +@@ LUAI_UMEM is an unsigned integer big enough to count the total +@* memory used by Lua. +@@ LUAI_MEM is a signed integer big enough to count the total memory +@* used by Lua. +** CHANGE here if for some weird reason the default definitions are not +** good enough for your machine. (The definitions in the 'else' +** part always works, but may waste space on machines with 64-bit +** longs.) Probably you do not need to change this. +*/ +#if LUAI_BITSINT >= 32 +#define LUAI_UINT32 unsigned int +#define LUAI_INT32 int +#define LUAI_MAXINT32 INT_MAX +#define LUAI_UMEM size_t +#define LUAI_MEM ptrdiff_t +#else +/* 16-bit ints */ +#define LUAI_UINT32 unsigned long +#define LUAI_INT32 long +#define LUAI_MAXINT32 LONG_MAX +#define LUAI_UMEM unsigned long +#define LUAI_MEM long +#endif + + +/* +@@ LUAI_MAXCALLS limits the number of nested calls. +** CHANGE it if you need really deep recursive calls. This limit is +** arbitrary; its only purpose is to stop infinite recursion before +** exhausting memory. +*/ +#define LUAI_MAXCALLS 20000 + + +/* +@@ LUAI_MAXCSTACK limits the number of Lua stack slots that a C function +@* can use. +** CHANGE it if you need lots of (Lua) stack space for your C +** functions. This limit is arbitrary; its only purpose is to stop C +** functions to consume unlimited stack space. +*/ +#define LUAI_MCS_AUX ((int)(INT_MAX / (4*sizeof(LUA_NUMBER)))) +#define LUAI_MAXCSTACK (LUAI_MCS_AUX > SHRT_MAX ? SHRT_MAX : LUAI_MCS_AUX) + + + +/* +** {================================================================== +** CHANGE (to smaller values) the following definitions if your system +** has a small C stack. (Or you may want to change them to larger +** values if your system has a large C stack and these limits are +** too rigid for you.) Some of these constants control the size of +** stack-allocated arrays used by the compiler or the interpreter, while +** others limit the maximum number of recursive calls that the compiler +** or the interpreter can perform. Values too large may cause a C stack +** overflow for some forms of deep constructs. +** =================================================================== +*/ + + +/* +@@ LUAI_MAXCCALLS is the maximum depth for nested C calls (short) and +@* syntactical nested non-terminals in a program. +*/ +#define LUAI_MAXCCALLS 200 + + +/* +@@ LUAI_MAXVARS is the maximum number of local variables per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXVARS 200 + + +/* +@@ LUAI_MAXUPVALUES is the maximum number of upvalues per function +@* (must be smaller than 250). +*/ +#define LUAI_MAXUPVALUES 60 + + +/* +@@ LUAL_BUFFERSIZE is the buffer size used by the lauxlib buffer system. +*/ +#ifdef USE_GRUB_LIB +#define LUAL_BUFFERSIZE 32768 +#else +#define LUAL_BUFFERSIZE BUFSIZ +#endif + +/* }================================================================== */ + + + + +/* +** {================================================================== +@@ LUA_NUMBER is the type of numbers in Lua. +** CHANGE the following definitions only if you want to build Lua +** with a number type different from double. You may also need to +** change lua_number2int & lua_number2integer. +** =================================================================== +*/ + +/* Define LUA_NUMBER_INTEGRAL to produce a system that uses no + floating point operations by changing the type of Lua numbers from + double to long. It implements division and modulus so that + + x == (x / y) * y + x % y. + + The exponentiation function returns zero for negative exponents. + Defining LUA_NUMBER_INTEGRAL also removes the difftime function, + and the math module should not be used. The string.format function + no longer handles the floating point directives %e, %E, %f, %g, and + %G. */ + +#define LUA_NUMBER_INTEGRAL +#if defined LUA_NUMBER_INTEGRAL +#define LUA_NUMBER long +#else +#define LUA_NUMBER_DOUBLE +#define LUA_NUMBER double +#endif + +/* +@@ LUAI_UACNUMBER is the result of an 'usual argument conversion' +@* over a number. +*/ +#define LUAI_UACNUMBER LUA_NUMBER + + +/* +@@ LUA_NUMBER_SCAN is the format for reading numbers. +@@ LUA_NUMBER_FMT is the format for writing numbers. +@@ lua_number2str converts a number to a string. +@@ LUAI_MAXNUMBER2STR is maximum size of previous conversion. +@@ lua_str2number converts a string to a number. +*/ +#if defined LUA_NUMBER_INTEGRAL +#define LUA_NUMBER_SCAN "%ld" +#define LUA_NUMBER_FMT "%ld" +#else +#define LUA_NUMBER_SCAN "%lf" +#define LUA_NUMBER_FMT "%.14g" +#endif +#define lua_number2str(s,n) sprintf((s), LUA_NUMBER_FMT, (n)) +#define LUAI_MAXNUMBER2STR 32 /* 16 digits, sign, point, and \0 */ +#if defined LUA_NUMBER_INTEGRAL +#define lua_str2number(s,p) strtol((s), (p), 10) +#else +#define lua_str2number(s,p) strtod((s), (p)) +#endif + + +/* +@@ The luai_num* macros define the primitive operations over numbers. +*/ +#if defined(LUA_CORE) +#include +#define luai_numadd(a,b) ((a)+(b)) +#define luai_numsub(a,b) ((a)-(b)) +#define luai_nummul(a,b) ((a)*(b)) +#if defined LUA_NUMBER_INTEGRAL +#define luai_numdiv(a,b) \ + (-1/2? \ + (a)/(b): \ + (((a)<0)==((b)<0)||(a)%(b)==0? \ + (a)/(b): \ + (a)/(b)-1)) +#define luai_nummod(a,b) \ + (-1/2? \ + (a)%(b): \ + (((a)<0)==((b)<0)||(a)%(b)==0? \ + (a)%(b): \ + (a)%(b)+(b))) +#define luai_lnumdiv(a,b) \ + ((b)==0? \ + (luaG_runerror(L,"divide by zero"),0): \ + luai_numdiv(a,b)) +#define luai_lnummod(a,b) \ + ((b)==0? \ + (luaG_runerror(L,"modulo by zero"),0): \ + luai_nummod(a,b)) +LUA_NUMBER luai_ipow(LUA_NUMBER, LUA_NUMBER); +#define luai_numpow(a,b) (luai_ipow(a,b)) +#else +#define luai_numdiv(a,b) ((a)/(b)) +#define luai_nummod(a,b) ((a) - floor((a)/(b))*(b)) +#define luai_lnumdiv(a,b) (luai_numdiv(a,b)) +#define luai_lnummod(a,b) (luai_nummod(a,b)) +#define luai_numpow(a,b) (pow(a,b)) +#endif +#define luai_numunm(a) (-(a)) +#define luai_numeq(a,b) ((a)==(b)) +#define luai_numlt(a,b) ((a)<(b)) +#define luai_numle(a,b) ((a)<=(b)) +#define luai_numisnan(a) (!luai_numeq((a), (a))) +#endif + + +/* +@@ lua_number2int is a macro to convert lua_Number to int. +@@ lua_number2integer is a macro to convert lua_Number to lua_Integer. +** CHANGE them if you know a faster way to convert a lua_Number to +** int (with any rounding method and without throwing errors) in your +** system. In Pentium machines, a naive typecast from double to int +** in C is extremely slow, so any alternative is worth trying. +*/ + +/* On a Pentium, resort to a trick */ +#if defined(LUA_NUMBER_DOUBLE) && !defined(LUA_ANSI) && !defined(__SSE2__) && \ + (defined(__i386) || defined (_M_IX86) || defined(__i386__)) + +/* On a Microsoft compiler, use assembler */ +#if defined(_MSC_VER) + +#define lua_number2int(i,d) __asm fld d __asm fistp i +#define lua_number2integer(i,n) lua_number2int(i, n) + +/* the next trick should work on any Pentium, but sometimes clashes + with a DirectX idiosyncrasy */ +#else + +union luai_Cast { double l_d; long l_l; }; +#define lua_number2int(i,d) \ + { volatile union luai_Cast u; u.l_d = (d) + 6755399441055744.0; (i) = u.l_l; } +#define lua_number2integer(i,n) lua_number2int(i, n) + +#endif + + +/* this option always works, but may be slow */ +#else +#define lua_number2int(i,d) ((i)=(int)(d)) +#define lua_number2integer(i,d) ((i)=(lua_Integer)(d)) + +#endif + +/* }================================================================== */ + + +/* +@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment. +** CHANGE it if your system requires alignments larger than double. (For +** instance, if your system supports long doubles and they must be +** aligned in 16-byte boundaries, then you should add long double in the +** union.) Probably you do not need to change this. +*/ +#define LUAI_USER_ALIGNMENT_T union { double u; void *s; long l; } + + +/* +@@ LUAI_THROW/LUAI_TRY define how Lua does exception handling. +** CHANGE them if you prefer to use longjmp/setjmp even with C++ +** or if want/don't to use _longjmp/_setjmp instead of regular +** longjmp/setjmp. By default, Lua handles errors with exceptions when +** compiling as C++ code, with _longjmp/_setjmp when asked to use them, +** and with longjmp/setjmp otherwise. +*/ +#if defined(USE_GRUB_LIB) +#include +/* handling with long jumps, using the GRUB functions */ +#define LUAI_THROW(L,c) grub_longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (grub_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf grub_jmp_buf + +#elif defined(__cplusplus) +/* C++ exceptions */ +#define LUAI_THROW(L,c) throw(c) +#define LUAI_TRY(L,c,a) try { a } catch(...) \ + { if ((c)->status == 0) (c)->status = -1; } +#define luai_jmpbuf int /* dummy variable */ + +#elif defined(LUA_USE_ULONGJMP) +/* in Unix, try _longjmp/_setjmp (more efficient) */ +#define LUAI_THROW(L,c) _longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (_setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#else +/* default handling with long jumps */ +#define LUAI_THROW(L,c) longjmp((c)->b, 1) +#define LUAI_TRY(L,c,a) if (setjmp((c)->b) == 0) { a } +#define luai_jmpbuf jmp_buf + +#endif + + +/* +@@ LUA_MAXCAPTURES is the maximum number of captures that a pattern +@* can do during pattern-matching. +** CHANGE it if you need more captures. This limit is arbitrary. +*/ +#define LUA_MAXCAPTURES 32 + + +/* +@@ lua_tmpnam is the function that the OS library uses to create a +@* temporary name. +@@ LUA_TMPNAMBUFSIZE is the maximum size of a name created by lua_tmpnam. +** CHANGE them if you have an alternative to tmpnam (which is considered +** insecure) or if you want the original tmpnam anyway. By default, Lua +** uses tmpnam except when POSIX is available, where it uses mkstemp. +*/ +#if defined(loslib_c) || defined(luaall_c) + +#if defined(LUA_USE_MKSTEMP) +#include +#define LUA_TMPNAMBUFSIZE 32 +#define lua_tmpnam(b,e) { \ + strcpy(b, "/tmp/lua_XXXXXX"); \ + e = mkstemp(b); \ + if (e != -1) close(e); \ + e = (e == -1); } + +#else +#define LUA_TMPNAMBUFSIZE L_tmpnam +#define lua_tmpnam(b,e) { e = (tmpnam(b) == NULL); } +#endif + +#endif + + +/* +@@ lua_popen spawns a new process connected to the current one through +@* the file streams. +** CHANGE it if you have a way to implement it in your system. +*/ +#if defined(LUA_USE_POPEN) + +#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) +#define lua_pclose(L,file) ((void)L, (pclose(file) != -1)) + +#elif defined(LUA_WIN) + +#define lua_popen(L,c,m) ((void)L, _popen(c,m)) +#define lua_pclose(L,file) ((void)L, (_pclose(file) != -1)) + +#else + +#define lua_popen(L,c,m) ((void)((void)c, m), \ + luaL_error(L, LUA_QL("popen") " not supported"), (FILE*)0) +#define lua_pclose(L,file) ((void)((void)L, file), 0) + +#endif + +/* +@@ LUA_DL_* define which dynamic-library system Lua should use. +** CHANGE here if Lua has problems choosing the appropriate +** dynamic-library system for your platform (either Windows' DLL, Mac's +** dyld, or Unix's dlopen). If your system is some kind of Unix, there +** is a good chance that it has dlopen, so LUA_DL_DLOPEN will work for +** it. To use dlopen you also need to adapt the src/Makefile (probably +** adding -ldl to the linker options), so Lua does not select it +** automatically. (When you change the makefile to add -ldl, you must +** also add -DLUA_USE_DLOPEN.) +** If you do not want any kind of dynamic library, undefine all these +** options. +** By default, _WIN32 gets LUA_DL_DLL and MAC OS X gets LUA_DL_DYLD. +*/ +#if defined(LUA_USE_DLOPEN) +#define LUA_DL_DLOPEN +#endif + +#if defined(LUA_WIN) +#define LUA_DL_DLL +#endif + + +/* +@@ LUAI_EXTRASPACE allows you to add user-specific data in a lua_State +@* (the data goes just *before* the lua_State pointer). +** CHANGE (define) this if you really need that. This value must be +** a multiple of the maximum alignment required for your machine. +*/ +#define LUAI_EXTRASPACE 0 + + +/* +@@ luai_userstate* allow user-specific actions on threads. +** CHANGE them if you defined LUAI_EXTRASPACE and need to do something +** extra when a thread is created/deleted/resumed/yielded. +*/ +#define luai_userstateopen(L) ((void)L) +#define luai_userstateclose(L) ((void)L) +#define luai_userstatethread(L,L1) ((void)L) +#define luai_userstatefree(L) ((void)L) +#define luai_userstateresume(L,n) ((void)L) +#define luai_userstateyield(L,n) ((void)L) + + +/* +@@ LUA_INTFRMLEN is the length modifier for integer conversions +@* in 'string.format'. +@@ LUA_INTFRM_T is the integer type correspoding to the previous length +@* modifier. +** CHANGE them if your system supports long long or does not support long. +*/ + +#if defined(LUA_USELONGLONG) + +#define LUA_INTFRMLEN "ll" +#define LUA_INTFRM_T long long + +#else + +#define LUA_INTFRMLEN "l" +#define LUA_INTFRM_T long + +#endif + + + +/* =================================================================== */ + +/* +** Local configuration. You can use this space to add your redefinitions +** without modifying the main part of the file. +*/ + + + +#endif + === added file 'lua/lualib.h' --- lua/lualib.h 1970-01-01 00:00:00 +0000 +++ lua/lualib.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,53 @@ +/* +** $Id: lualib.h,v 1.36.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua standard libraries +** See Copyright Notice in lua.h +*/ + + +#ifndef lualib_h +#define lualib_h + +#include "lua.h" + + +/* Key to file-handle type */ +#define LUA_FILEHANDLE "FILE*" + + +#define LUA_COLIBNAME "coroutine" +LUALIB_API int (luaopen_base) (lua_State *L); + +#define LUA_TABLIBNAME "table" +LUALIB_API int (luaopen_table) (lua_State *L); + +#define LUA_IOLIBNAME "io" +LUALIB_API int (luaopen_io) (lua_State *L); + +#define LUA_OSLIBNAME "os" +LUALIB_API int (luaopen_os) (lua_State *L); + +#define LUA_STRLIBNAME "string" +LUALIB_API int (luaopen_string) (lua_State *L); + +#define LUA_MATHLIBNAME "math" +LUALIB_API int (luaopen_math) (lua_State *L); + +#define LUA_DBLIBNAME "debug" +LUALIB_API int (luaopen_debug) (lua_State *L); + +#define LUA_LOADLIBNAME "package" +LUALIB_API int (luaopen_package) (lua_State *L); + + +/* open all previous libraries */ +LUALIB_API void (luaL_openlibs) (lua_State *L); + + + +#ifndef lua_assert +#define lua_assert(x) ((void)0) +#endif + + +#endif === added file 'lua/lundump.c' --- lua/lundump.c 1970-01-01 00:00:00 +0000 +++ lua/lundump.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,227 @@ +/* +** $Id: lundump.c,v 2.7.1.2 2008/01/18 16:39:11 roberto Exp $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#define lundump_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lmem.h" +#include "lobject.h" +#include "lstring.h" +#include "lundump.h" +#include "lzio.h" + +typedef struct { + lua_State* L; + ZIO* Z; + Mbuffer* b; + const char* name; +} LoadState; + +#ifdef LUAC_TRUST_BINARIES +#define IF(c,s) +#define error(S,s) +#else +#define IF(c,s) if (c) error(S,s) + +static void error(LoadState* S, const char* why) +{ + luaO_pushfstring(S->L,"%s: %s in precompiled chunk",S->name,why); + luaD_throw(S->L,LUA_ERRSYNTAX); +} +#endif + +#define LoadMem(S,b,n,size) LoadBlock(S,b,(n)*(size)) +#define LoadByte(S) (lu_byte)LoadChar(S) +#define LoadVar(S,x) LoadMem(S,&x,1,sizeof(x)) +#define LoadVector(S,b,n,size) LoadMem(S,b,n,size) + +static void LoadBlock(LoadState* S, void* b, size_t size) +{ + size_t r=luaZ_read(S->Z,b,size); + UNUSED(r); + IF (r!=0, "unexpected end"); +} + +static int LoadChar(LoadState* S) +{ + char x; + LoadVar(S,x); + return x; +} + +static int LoadInt(LoadState* S) +{ + int x; + LoadVar(S,x); + IF (x<0, "bad integer"); + return x; +} + +static lua_Number LoadNumber(LoadState* S) +{ + lua_Number x; + LoadVar(S,x); + return x; +} + +static TString* LoadString(LoadState* S) +{ + size_t size; + LoadVar(S,size); + if (size==0) + return NULL; + else + { + char* s=luaZ_openspace(S->L,S->b,size); + LoadBlock(S,s,size); + return luaS_newlstr(S->L,s,size-1); /* remove trailing '\0' */ + } +} + +static void LoadCode(LoadState* S, Proto* f) +{ + int n=LoadInt(S); + f->code=luaM_newvector(S->L,n,Instruction); + f->sizecode=n; + LoadVector(S,f->code,n,sizeof(Instruction)); +} + +static Proto* LoadFunction(LoadState* S, TString* p); + +static void LoadConstants(LoadState* S, Proto* f) +{ + int i,n; + n=LoadInt(S); + f->k=luaM_newvector(S->L,n,TValue); + f->sizek=n; + for (i=0; ik[i]); + for (i=0; ik[i]; + int t=LoadChar(S); + switch (t) + { + case LUA_TNIL: + setnilvalue(o); + break; + case LUA_TBOOLEAN: + setbvalue(o,LoadChar(S)); + break; + case LUA_TNUMBER: + setnvalue(o,LoadNumber(S)); + break; + case LUA_TSTRING: + setsvalue2n(S->L,o,LoadString(S)); + break; + default: + error(S,"bad constant"); + break; + } + } + n=LoadInt(S); + f->p=luaM_newvector(S->L,n,Proto*); + f->sizep=n; + for (i=0; ip[i]=NULL; + for (i=0; ip[i]=LoadFunction(S,f->source); +} + +static void LoadDebug(LoadState* S, Proto* f) +{ + int i,n; + n=LoadInt(S); + f->lineinfo=luaM_newvector(S->L,n,int); + f->sizelineinfo=n; + LoadVector(S,f->lineinfo,n,sizeof(int)); + n=LoadInt(S); + f->locvars=luaM_newvector(S->L,n,LocVar); + f->sizelocvars=n; + for (i=0; ilocvars[i].varname=NULL; + for (i=0; ilocvars[i].varname=LoadString(S); + f->locvars[i].startpc=LoadInt(S); + f->locvars[i].endpc=LoadInt(S); + } + n=LoadInt(S); + f->upvalues=luaM_newvector(S->L,n,TString*); + f->sizeupvalues=n; + for (i=0; iupvalues[i]=NULL; + for (i=0; iupvalues[i]=LoadString(S); +} + +static Proto* LoadFunction(LoadState* S, TString* p) +{ + Proto* f=luaF_newproto(S->L); + setptvalue2s(S->L,S->L->top,f); incr_top(S->L); + f->source=LoadString(S); if (f->source==NULL) f->source=p; + f->linedefined=LoadInt(S); + f->lastlinedefined=LoadInt(S); + f->nups=LoadByte(S); + f->numparams=LoadByte(S); + f->is_vararg=LoadByte(S); + f->maxstacksize=LoadByte(S); + LoadCode(S,f); + LoadConstants(S,f); + LoadDebug(S,f); + IF (!luaG_checkcode(f), "bad code"); + S->L->top--; + return f; +} + +static void LoadHeader(LoadState* S) +{ + char h[LUAC_HEADERSIZE]; + char s[LUAC_HEADERSIZE]; + luaU_header(h); + LoadBlock(S,s,LUAC_HEADERSIZE); + IF (memcmp(h,s,LUAC_HEADERSIZE)!=0, "bad header"); +} + +/* +** load precompiled chunk +*/ +Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name) +{ + LoadState S; + if (*name=='@' || *name=='=') + S.name=name+1; + else if (*name==LUA_SIGNATURE[0]) + S.name="binary string"; + else + S.name=name; + S.L=L; + S.Z=Z; + S.b=buff; + LoadHeader(&S); + return LoadFunction(&S,luaS_newliteral(L,"=?")); +} + +/* +* make header +*/ +void luaU_header (char* h) +{ + int x=1; + memcpy(h,LUA_SIGNATURE,sizeof(LUA_SIGNATURE)-1); + h+=sizeof(LUA_SIGNATURE)-1; + *h++=(char)LUAC_VERSION; + *h++=(char)LUAC_FORMAT; + *h++=(char)*(char*)&x; /* endianness */ + *h++=(char)sizeof(int); + *h++=(char)sizeof(size_t); + *h++=(char)sizeof(Instruction); + *h++=(char)sizeof(lua_Number); + *h++=(char)(((lua_Number)0.5)==0); /* is lua_Number integral? */ +} === added file 'lua/lundump.h' --- lua/lundump.h 1970-01-01 00:00:00 +0000 +++ lua/lundump.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,36 @@ +/* +** $Id: lundump.h,v 1.37.1.1 2007/12/27 13:02:25 roberto Exp $ +** load precompiled Lua chunks +** See Copyright Notice in lua.h +*/ + +#ifndef lundump_h +#define lundump_h + +#include "lobject.h" +#include "lzio.h" + +/* load one chunk; from lundump.c */ +LUAI_FUNC Proto* luaU_undump (lua_State* L, ZIO* Z, Mbuffer* buff, const char* name); + +/* make header; from lundump.c */ +LUAI_FUNC void luaU_header (char* h); + +/* dump one chunk; from ldump.c */ +LUAI_FUNC int luaU_dump (lua_State* L, const Proto* f, lua_Writer w, void* data, int strip); + +#ifdef luac_c +/* print one chunk; from print.c */ +LUAI_FUNC void luaU_print (const Proto* f, int full); +#endif + +/* for header of binary files -- this is Lua 5.1 */ +#define LUAC_VERSION 0x51 + +/* for header of binary files -- this is the official format */ +#define LUAC_FORMAT 0 + +/* size of header of binary files */ +#define LUAC_HEADERSIZE 12 + +#endif === added file 'lua/lvm.c' --- lua/lvm.c 1970-01-01 00:00:00 +0000 +++ lua/lvm.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,784 @@ +/* +** $Id: lvm.c,v 2.63.1.3 2007/12/28 15:32:23 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#include +#include +#endif /* ! USE_GRUB_LIB */ + +#define lvm_c +#define LUA_CORE + +#include "lua.h" + +#include "ldebug.h" +#include "ldo.h" +#include "lfunc.h" +#include "lgc.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lstate.h" +#include "lstring.h" +#include "ltable.h" +#include "ltm.h" +#include "lvm.h" + + +#if defined LUA_NUMBER_INTEGRAL +LUA_NUMBER luai_ipow(LUA_NUMBER a, LUA_NUMBER b) { + if (b < 0) + return 0; + else if (b == 0) + return 1; + else { + LUA_NUMBER c = 1; + for (;;) { + if (b & 1) + c *= a; + b = b >> 1; + if (b == 0) + return c; + a *= a; + } + } +} +#endif + +/* limit for table tag-method chains (to avoid loops) */ +#define MAXTAGLOOP 100 + + +const TValue *luaV_tonumber (const TValue *obj, TValue *n) { + lua_Number num; + if (ttisnumber(obj)) return obj; + if (ttisstring(obj) && luaO_str2d(svalue(obj), &num)) { + setnvalue(n, num); + return n; + } + else + return NULL; +} + + +int luaV_tostring (lua_State *L, StkId obj) { + if (!ttisnumber(obj)) + return 0; + else { + char s[LUAI_MAXNUMBER2STR]; + lua_Number n = nvalue(obj); + lua_number2str(s, n); + setsvalue2s(L, obj, luaS_new(L, s)); + return 1; + } +} + + +static void traceexec (lua_State *L, const Instruction *pc) { + lu_byte mask = L->hookmask; + const Instruction *oldpc = L->savedpc; + L->savedpc = pc; + if ((mask & LUA_MASKCOUNT) && L->hookcount == 0) { + resethookcount(L); + luaD_callhook(L, LUA_HOOKCOUNT, -1); + } + if (mask & LUA_MASKLINE) { + Proto *p = ci_func(L->ci)->l.p; + int npc = pcRel(pc, p); + int newline = getline(p, npc); + /* call linehook when enter a new function, when jump back (loop), + or when enter a new line */ + if (npc == 0 || pc <= oldpc || newline != getline(p, pcRel(oldpc, p))) + luaD_callhook(L, LUA_HOOKLINE, newline); + } +} + + +static void callTMres (lua_State *L, StkId res, const TValue *f, + const TValue *p1, const TValue *p2) { + ptrdiff_t result = savestack(L, res); + setobj2s(L, L->top, f); /* push function */ + setobj2s(L, L->top+1, p1); /* 1st argument */ + setobj2s(L, L->top+2, p2); /* 2nd argument */ + luaD_checkstack(L, 3); + L->top += 3; + luaD_call(L, L->top - 3, 1); + res = restorestack(L, result); + L->top--; + setobjs2s(L, res, L->top); +} + + + +static void callTM (lua_State *L, const TValue *f, const TValue *p1, + const TValue *p2, const TValue *p3) { + setobj2s(L, L->top, f); /* push function */ + setobj2s(L, L->top+1, p1); /* 1st argument */ + setobj2s(L, L->top+2, p2); /* 2nd argument */ + setobj2s(L, L->top+3, p3); /* 3th argument */ + luaD_checkstack(L, 4); + L->top += 4; + luaD_call(L, L->top - 4, 0); +} + + +void luaV_gettable (lua_State *L, const TValue *t, TValue *key, StkId val) { + int loop; + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; + if (ttistable(t)) { /* `t' is a table? */ + Table *h = hvalue(t); + const TValue *res = luaH_get(h, key); /* do a primitive get */ + if (!ttisnil(res) || /* result is no nil? */ + (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) { /* or no TM? */ + setobj2s(L, val, res); + return; + } + /* else will try the tag method */ + } + else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) + luaG_typeerror(L, t, "index"); + if (ttisfunction(tm)) { + callTMres(L, val, tm, t, key); + return; + } + t = tm; /* else repeat with `tm' */ + } + luaG_runerror(L, "loop in gettable"); +} + + +void luaV_settable (lua_State *L, const TValue *t, TValue *key, StkId val) { + int loop; + for (loop = 0; loop < MAXTAGLOOP; loop++) { + const TValue *tm; + if (ttistable(t)) { /* `t' is a table? */ + Table *h = hvalue(t); + TValue *oldval = luaH_set(L, h, key); /* do a primitive set */ + if (!ttisnil(oldval) || /* result is no nil? */ + (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) { /* or no TM? */ + setobj2t(L, oldval, val); + luaC_barriert(L, h, val); + return; + } + /* else will try the tag method */ + } + else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) + luaG_typeerror(L, t, "index"); + if (ttisfunction(tm)) { + callTM(L, tm, t, key, val); + return; + } + t = tm; /* else repeat with `tm' */ + } + luaG_runerror(L, "loop in settable"); +} + + +static int call_binTM (lua_State *L, const TValue *p1, const TValue *p2, + StkId res, TMS event) { + const TValue *tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ + if (ttisnil(tm)) + tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ + if (ttisnil(tm)) return 0; + callTMres(L, res, tm, p1, p2); + return 1; +} + + +static const TValue *get_compTM (lua_State *L, Table *mt1, Table *mt2, + TMS event) { + const TValue *tm1 = fasttm(L, mt1, event); + const TValue *tm2; + if (tm1 == NULL) return NULL; /* no metamethod */ + if (mt1 == mt2) return tm1; /* same metatables => same metamethods */ + tm2 = fasttm(L, mt2, event); + if (tm2 == NULL) return NULL; /* no metamethod */ + if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */ + return tm1; + return NULL; +} + + +static int call_orderTM (lua_State *L, const TValue *p1, const TValue *p2, + TMS event) { + const TValue *tm1 = luaT_gettmbyobj(L, p1, event); + const TValue *tm2; + if (ttisnil(tm1)) return -1; /* no metamethod? */ + tm2 = luaT_gettmbyobj(L, p2, event); + if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */ + return -1; + callTMres(L, L->top, tm1, p1, p2); + return !l_isfalse(L->top); +} + + +static int l_strcmp (const TString *ls, const TString *rs) { + const char *l = getstr(ls); + size_t ll = ls->tsv.len; + const char *r = getstr(rs); + size_t lr = rs->tsv.len; + for (;;) { + int temp = strcoll(l, r); + if (temp != 0) return temp; + else { /* strings are equal up to a `\0' */ + size_t len = strlen(l); /* index of first `\0' in both strings */ + if (len == lr) /* r is finished? */ + return (len == ll) ? 0 : 1; + else if (len == ll) /* l is finished? */ + return -1; /* l is smaller than r (because r is not finished) */ + /* both strings longer than `len'; go on comparing (after the `\0') */ + len++; + l += len; ll -= len; r += len; lr -= len; + } + } +} + + +int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r) { + int res; + if (ttype(l) != ttype(r)) + return luaG_ordererror(L, l, r); + else if (ttisnumber(l)) + return luai_numlt(nvalue(l), nvalue(r)); + else if (ttisstring(l)) + return l_strcmp(rawtsvalue(l), rawtsvalue(r)) < 0; + else if ((res = call_orderTM(L, l, r, TM_LT)) != -1) + return res; + return luaG_ordererror(L, l, r); +} + + +static int lessequal (lua_State *L, const TValue *l, const TValue *r) { + int res; + if (ttype(l) != ttype(r)) + return luaG_ordererror(L, l, r); + else if (ttisnumber(l)) + return luai_numle(nvalue(l), nvalue(r)); + else if (ttisstring(l)) + return l_strcmp(rawtsvalue(l), rawtsvalue(r)) <= 0; + else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */ + return res; + else if ((res = call_orderTM(L, r, l, TM_LT)) != -1) /* else try `lt' */ + return !res; + return luaG_ordererror(L, l, r); +} + + +int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2) { + const TValue *tm; + lua_assert(ttype(t1) == ttype(t2)); + switch (ttype(t1)) { + case LUA_TNIL: return 1; + case LUA_TNUMBER: return luai_numeq(nvalue(t1), nvalue(t2)); + case LUA_TBOOLEAN: return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ + case LUA_TLIGHTUSERDATA: return pvalue(t1) == pvalue(t2); + case LUA_TUSERDATA: { + if (uvalue(t1) == uvalue(t2)) return 1; + tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, + TM_EQ); + break; /* will try TM */ + } + case LUA_TTABLE: { + if (hvalue(t1) == hvalue(t2)) return 1; + tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); + break; /* will try TM */ + } + default: return gcvalue(t1) == gcvalue(t2); + } + if (tm == NULL) return 0; /* no TM? */ + callTMres(L, L->top, tm, t1, t2); /* call TM */ + return !l_isfalse(L->top); +} + + +void luaV_concat (lua_State *L, int total, int last) { + do { + StkId top = L->base + last + 1; + int n = 2; /* number of elements handled in this pass (at least 2) */ + if (!(ttisstring(top-2) || ttisnumber(top-2)) || !tostring(L, top-1)) { + if (!call_binTM(L, top-2, top-1, top-2, TM_CONCAT)) + luaG_concaterror(L, top-2, top-1); + } else if (tsvalue(top-1)->len == 0) /* second op is empty? */ + (void)tostring(L, top - 2); /* result is first op (as string) */ + else { + /* at least two string values; get as many as possible */ + size_t tl = tsvalue(top-1)->len; + char *buffer; + int i; + /* collect total length */ + for (n = 1; n < total && tostring(L, top-n-1); n++) { + size_t l = tsvalue(top-n-1)->len; + if (l >= MAX_SIZET - tl) luaG_runerror(L, "string length overflow"); + tl += l; + } + buffer = luaZ_openspace(L, &G(L)->buff, tl); + tl = 0; + for (i=n; i>0; i--) { /* concat all strings */ + size_t l = tsvalue(top-i)->len; + memcpy(buffer+tl, svalue(top-i), l); + tl += l; + } + setsvalue2s(L, top-n, luaS_newlstr(L, buffer, tl)); + } + total -= n-1; /* got `n' strings to create 1 new */ + last -= n-1; + } while (total > 1); /* repeat until only 1 result left */ +} + + +static void Arith (lua_State *L, StkId ra, const TValue *rb, + const TValue *rc, TMS op) { + TValue tempb, tempc; + const TValue *b, *c; + if ((b = luaV_tonumber(rb, &tempb)) != NULL && + (c = luaV_tonumber(rc, &tempc)) != NULL) { + lua_Number nb = nvalue(b), nc = nvalue(c); + switch (op) { + case TM_ADD: setnvalue(ra, luai_numadd(nb, nc)); break; + case TM_SUB: setnvalue(ra, luai_numsub(nb, nc)); break; + case TM_MUL: setnvalue(ra, luai_nummul(nb, nc)); break; + case TM_DIV: setnvalue(ra, luai_lnumdiv(nb, nc)); break; + case TM_MOD: setnvalue(ra, luai_lnummod(nb, nc)); break; + case TM_POW: setnvalue(ra, luai_numpow(nb, nc)); break; + case TM_UNM: setnvalue(ra, luai_numunm(nb)); break; + default: lua_assert(0); break; + } + } + else if (!call_binTM(L, rb, rc, ra, op)) + luaG_aritherror(L, rb, rc); +} + + + +/* +** some macros for common tasks in `luaV_execute' +*/ + +#define runtime_check(L, c) { if (!(c)) break; } + +#define RA(i) (base+GETARG_A(i)) +/* to be used after possible stack reallocation */ +#define RB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgR, base+GETARG_B(i)) +#define RC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgR, base+GETARG_C(i)) +#define RKB(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, \ + ISK(GETARG_B(i)) ? k+INDEXK(GETARG_B(i)) : base+GETARG_B(i)) +#define RKC(i) check_exp(getCMode(GET_OPCODE(i)) == OpArgK, \ + ISK(GETARG_C(i)) ? k+INDEXK(GETARG_C(i)) : base+GETARG_C(i)) +#define KBx(i) check_exp(getBMode(GET_OPCODE(i)) == OpArgK, k+GETARG_Bx(i)) + + +#define dojump(L,pc,i) {(pc) += (i); luai_threadyield(L);} + + +#define Protect(x) { L->savedpc = pc; {x;}; base = L->base; } + + +#define arith_op(op,tm) { \ + TValue *rb = RKB(i); \ + TValue *rc = RKC(i); \ + if (ttisnumber(rb) && ttisnumber(rc)) { \ + lua_Number nb = nvalue(rb), nc = nvalue(rc); \ + setnvalue(ra, op(nb, nc)); \ + } \ + else \ + Protect(Arith(L, ra, rb, rc, tm)); \ + } + + + +void luaV_execute (lua_State *L, int nexeccalls) { + LClosure *cl; + StkId base; + TValue *k; + const Instruction *pc; + reentry: /* entry point */ + lua_assert(isLua(L->ci)); + pc = L->savedpc; + cl = &clvalue(L->ci->func)->l; + base = L->base; + k = cl->p->k; + /* main loop of interpreter */ + for (;;) { + const Instruction i = *pc++; + StkId ra; + if ((L->hookmask & (LUA_MASKLINE | LUA_MASKCOUNT)) && + (--L->hookcount == 0 || L->hookmask & LUA_MASKLINE)) { + traceexec(L, pc); + if (L->status == LUA_YIELD) { /* did hook yield? */ + L->savedpc = pc - 1; + return; + } + base = L->base; + } + /* warning!! several calls may realloc the stack and invalidate `ra' */ + ra = RA(i); + lua_assert(base == L->base && L->base == L->ci->base); + lua_assert(base <= L->top && L->top <= L->stack + L->stacksize); + lua_assert(L->top == L->ci->top || luaG_checkopenop(i)); + switch (GET_OPCODE(i)) { + case OP_MOVE: { + setobjs2s(L, ra, RB(i)); + continue; + } + case OP_LOADK: { + setobj2s(L, ra, KBx(i)); + continue; + } + case OP_LOADBOOL: { + setbvalue(ra, GETARG_B(i)); + if (GETARG_C(i)) pc++; /* skip next instruction (if C) */ + continue; + } + case OP_LOADNIL: { + TValue *rb = RB(i); + do { + setnilvalue(rb--); + } while (rb >= ra); + continue; + } + case OP_GETUPVAL: { + int b = GETARG_B(i); + setobj2s(L, ra, cl->upvals[b]->v); + continue; + } + case OP_GETGLOBAL: { + TValue g; + TValue *rb = KBx(i); + sethvalue(L, &g, cl->env); + lua_assert(ttisstring(rb)); + Protect(luaV_gettable(L, &g, rb, ra)); + continue; + } + case OP_GETTABLE: { + Protect(luaV_gettable(L, RB(i), RKC(i), ra)); + continue; + } + case OP_SETGLOBAL: { + TValue g; + sethvalue(L, &g, cl->env); + lua_assert(ttisstring(KBx(i))); + Protect(luaV_settable(L, &g, KBx(i), ra)); + continue; + } + case OP_SETUPVAL: { + UpVal *uv = cl->upvals[GETARG_B(i)]; + setobj(L, uv->v, ra); + luaC_barrier(L, uv, ra); + continue; + } + case OP_SETTABLE: { + Protect(luaV_settable(L, ra, RKB(i), RKC(i))); + continue; + } + case OP_NEWTABLE: { + int b = GETARG_B(i); + int c = GETARG_C(i); + sethvalue(L, ra, luaH_new(L, luaO_fb2int(b), luaO_fb2int(c))); + Protect(luaC_checkGC(L)); + continue; + } + case OP_SELF: { + StkId rb = RB(i); + setobjs2s(L, ra+1, rb); + Protect(luaV_gettable(L, rb, RKC(i), ra)); + continue; + } + case OP_ADD: { + arith_op(luai_numadd, TM_ADD); + continue; + } + case OP_SUB: { + arith_op(luai_numsub, TM_SUB); + continue; + } + case OP_MUL: { + arith_op(luai_nummul, TM_MUL); + continue; + } + case OP_DIV: { + arith_op(luai_lnumdiv, TM_DIV); + continue; + } + case OP_MOD: { + arith_op(luai_lnummod, TM_MOD); + continue; + } + case OP_POW: { + arith_op(luai_numpow, TM_POW); + continue; + } + case OP_UNM: { + TValue *rb = RB(i); + if (ttisnumber(rb)) { + lua_Number nb = nvalue(rb); + setnvalue(ra, luai_numunm(nb)); + } + else { + Protect(Arith(L, ra, rb, rb, TM_UNM)); + } + continue; + } + case OP_NOT: { + int res = l_isfalse(RB(i)); /* next assignment may change this value */ + setbvalue(ra, res); + continue; + } + case OP_LEN: { + const TValue *rb = RB(i); + switch (ttype(rb)) { + case LUA_TTABLE: { + setnvalue(ra, cast_num(luaH_getn(hvalue(rb)))); + break; + } + case LUA_TSTRING: { + setnvalue(ra, cast_num(tsvalue(rb)->len)); + break; + } + default: { /* try metamethod */ + Protect( + if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN)) + luaG_typeerror(L, rb, "get length of"); + ) + } + } + continue; + } + case OP_CONCAT: { + int b = GETARG_B(i); + int c = GETARG_C(i); + Protect(luaV_concat(L, c-b+1, c); luaC_checkGC(L)); + setobjs2s(L, RA(i), base+b); + continue; + } + case OP_JMP: { + dojump(L, pc, GETARG_sBx(i)); + continue; + } + case OP_EQ: { + TValue *rb = RKB(i); + TValue *rc = RKC(i); + Protect( + if (equalobj(L, rb, rc) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_LT: { + Protect( + if (luaV_lessthan(L, RKB(i), RKC(i)) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_LE: { + Protect( + if (lessequal(L, RKB(i), RKC(i)) == GETARG_A(i)) + dojump(L, pc, GETARG_sBx(*pc)); + ) + pc++; + continue; + } + case OP_TEST: { + if (l_isfalse(ra) != GETARG_C(i)) + dojump(L, pc, GETARG_sBx(*pc)); + pc++; + continue; + } + case OP_TESTSET: { + TValue *rb = RB(i); + if (l_isfalse(rb) != GETARG_C(i)) { + setobjs2s(L, ra, rb); + dojump(L, pc, GETARG_sBx(*pc)); + } + pc++; + continue; + } + case OP_CALL: { + int b = GETARG_B(i); + int nresults = GETARG_C(i) - 1; + if (b != 0) L->top = ra+b; /* else previous instruction set top */ + L->savedpc = pc; + switch (luaD_precall(L, ra, nresults)) { + case PCRLUA: { + nexeccalls++; + goto reentry; /* restart luaV_execute over new Lua function */ + } + case PCRC: { + /* it was a C function (`precall' called it); adjust results */ + if (nresults >= 0) L->top = L->ci->top; + base = L->base; + continue; + } + default: { + return; /* yield */ + } + } + } + case OP_TAILCALL: { + int b = GETARG_B(i); + if (b != 0) L->top = ra+b; /* else previous instruction set top */ + L->savedpc = pc; + lua_assert(GETARG_C(i) - 1 == LUA_MULTRET); + switch (luaD_precall(L, ra, LUA_MULTRET)) { + case PCRLUA: { + /* tail call: put new frame in place of previous one */ + CallInfo *ci = L->ci - 1; /* previous frame */ + int aux; + StkId func = ci->func; + StkId pfunc = (ci+1)->func; /* previous function index */ + if (L->openupval) luaF_close(L, ci->base); + L->base = ci->base = ci->func + ((ci+1)->base - pfunc); + for (aux = 0; pfunc+aux < L->top; aux++) /* move frame down */ + setobjs2s(L, func+aux, pfunc+aux); + ci->top = L->top = func+aux; /* correct top */ + lua_assert(L->top == L->base + clvalue(func)->l.p->maxstacksize); + ci->savedpc = L->savedpc; + ci->tailcalls++; /* one more call lost */ + L->ci--; /* remove new frame */ + goto reentry; + } + case PCRC: { /* it was a C function (`precall' called it) */ + base = L->base; + continue; + } + default: { + return; /* yield */ + } + } + } + case OP_RETURN: { + int b = GETARG_B(i); + if (b != 0) L->top = ra+b-1; + if (L->openupval) luaF_close(L, base); + L->savedpc = pc; + b = luaD_poscall(L, ra); + if (--nexeccalls == 0) /* was previous function running `here'? */ + return; /* no: return */ + else { /* yes: continue its execution */ + if (b) L->top = L->ci->top; + lua_assert(isLua(L->ci)); + lua_assert(GET_OPCODE(*((L->ci)->savedpc - 1)) == OP_CALL); + goto reentry; + } + } + case OP_FORLOOP: { + lua_Number step = nvalue(ra+2); + lua_Number idx = luai_numadd(nvalue(ra), step); /* increment index */ + lua_Number limit = nvalue(ra+1); + if (luai_numlt(0, step) ? luai_numle(idx, limit) + : luai_numle(limit, idx)) { + dojump(L, pc, GETARG_sBx(i)); /* jump back */ + setnvalue(ra, idx); /* update internal index... */ + setnvalue(ra+3, idx); /* ...and external index */ + } + continue; + } + case OP_FORPREP: { + const TValue *init = ra; + const TValue *plimit = ra+1; + const TValue *pstep = ra+2; + L->savedpc = pc; /* next steps may throw errors */ + if (!tonumber(init, ra)) + luaG_runerror(L, LUA_QL("for") " initial value must be a number"); + else if (!tonumber(plimit, ra+1)) + luaG_runerror(L, LUA_QL("for") " limit must be a number"); + else if (!tonumber(pstep, ra+2)) + luaG_runerror(L, LUA_QL("for") " step must be a number"); + setnvalue(ra, luai_numsub(nvalue(ra), nvalue(pstep))); + dojump(L, pc, GETARG_sBx(i)); + continue; + } + case OP_TFORLOOP: { + StkId cb = ra + 3; /* call base */ + setobjs2s(L, cb+2, ra+2); + setobjs2s(L, cb+1, ra+1); + setobjs2s(L, cb, ra); + L->top = cb+3; /* func. + 2 args (state and index) */ + Protect(luaD_call(L, cb, GETARG_C(i))); + L->top = L->ci->top; + cb = RA(i) + 3; /* previous call may change the stack */ + if (!ttisnil(cb)) { /* continue loop? */ + setobjs2s(L, cb-1, cb); /* save control variable */ + dojump(L, pc, GETARG_sBx(*pc)); /* jump back */ + } + pc++; + continue; + } + case OP_SETLIST: { + int n = GETARG_B(i); + int c = GETARG_C(i); + int last; + Table *h; + if (n == 0) { + n = cast_int(L->top - ra) - 1; + L->top = L->ci->top; + } + if (c == 0) c = cast_int(*pc++); + runtime_check(L, ttistable(ra)); + h = hvalue(ra); + last = ((c-1)*LFIELDS_PER_FLUSH) + n; + if (last > h->sizearray) /* needs more space? */ + luaH_resizearray(L, h, last); /* pre-alloc it at once */ + for (; n > 0; n--) { + TValue *val = ra+n; + setobj2t(L, luaH_setnum(L, h, last--), val); + luaC_barriert(L, h, val); + } + continue; + } + case OP_CLOSE: { + luaF_close(L, ra); + continue; + } + case OP_CLOSURE: { + Proto *p; + Closure *ncl; + int nup, j; + p = cl->p->p[GETARG_Bx(i)]; + nup = p->nups; + ncl = luaF_newLclosure(L, nup, cl->env); + ncl->l.p = p; + for (j=0; jl.upvals[j] = cl->upvals[GETARG_B(*pc)]; + else { + lua_assert(GET_OPCODE(*pc) == OP_MOVE); + ncl->l.upvals[j] = luaF_findupval(L, base + GETARG_B(*pc)); + } + } + setclvalue(L, ra, ncl); + Protect(luaC_checkGC(L)); + continue; + } + case OP_VARARG: { + int b = GETARG_B(i) - 1; + int j; + CallInfo *ci = L->ci; + int n = cast_int(ci->base - ci->func) - cl->p->numparams - 1; + if (b == LUA_MULTRET) { + Protect(luaD_checkstack(L, n)); + ra = RA(i); /* previous call may change the stack */ + b = n; + L->top = ra + n; + } + for (j = 0; j < b; j++) { + if (j < n) { + setobjs2s(L, ra + j, ci->base - n + j); + } + else { + setnilvalue(ra + j); + } + } + continue; + } + } + } +} + === added file 'lua/lvm.h' --- lua/lvm.h 1970-01-01 00:00:00 +0000 +++ lua/lvm.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,36 @@ +/* +** $Id: lvm.h,v 2.5.1.1 2007/12/27 13:02:25 roberto Exp $ +** Lua virtual machine +** See Copyright Notice in lua.h +*/ + +#ifndef lvm_h +#define lvm_h + + +#include "ldo.h" +#include "lobject.h" +#include "ltm.h" + + +#define tostring(L,o) ((ttype(o) == LUA_TSTRING) || (luaV_tostring(L, o))) + +#define tonumber(o,n) (ttype(o) == LUA_TNUMBER || \ + (((o) = luaV_tonumber(o,n)) != NULL)) + +#define equalobj(L,o1,o2) \ + (ttype(o1) == ttype(o2) && luaV_equalval(L, o1, o2)) + + +LUAI_FUNC int luaV_lessthan (lua_State *L, const TValue *l, const TValue *r); +LUAI_FUNC int luaV_equalval (lua_State *L, const TValue *t1, const TValue *t2); +LUAI_FUNC const TValue *luaV_tonumber (const TValue *obj, TValue *n); +LUAI_FUNC int luaV_tostring (lua_State *L, StkId obj); +LUAI_FUNC void luaV_gettable (lua_State *L, const TValue *t, TValue *key, + StkId val); +LUAI_FUNC void luaV_settable (lua_State *L, const TValue *t, TValue *key, + StkId val); +LUAI_FUNC void luaV_execute (lua_State *L, int nexeccalls); +LUAI_FUNC void luaV_concat (lua_State *L, int total, int last); + +#endif === added file 'lua/lzio.c' --- lua/lzio.c 1970-01-01 00:00:00 +0000 +++ lua/lzio.c 2008-08-01 15:27:27 +0000 @@ -0,0 +1,84 @@ +/* +** $Id: lzio.c,v 1.31.1.1 2007/12/27 13:02:25 roberto Exp $ +** a generic input stream interface +** See Copyright Notice in lua.h +*/ + + +#ifndef USE_GRUB_LIB +#include +#endif /* ! USE_GRUB_LIB */ + +#define lzio_c +#define LUA_CORE + +#include "lua.h" + +#include "llimits.h" +#include "lmem.h" +#include "lstate.h" +#include "lzio.h" + + +int luaZ_fill (ZIO *z) { + size_t size; + lua_State *L = z->L; + const char *buff; + lua_unlock(L); + buff = z->reader(L, z->data, &size); + lua_lock(L); + if (buff == NULL || size == 0) return EOZ; + z->n = size - 1; + z->p = buff; + return char2int(*(z->p++)); +} + + +int luaZ_lookahead (ZIO *z) { + if (z->n == 0) { + if (luaZ_fill(z) == EOZ) + return EOZ; + else { + z->n++; /* luaZ_fill removed first byte; put back it */ + z->p--; + } + } + return char2int(*z->p); +} + + +void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, void *data) { + z->L = L; + z->reader = reader; + z->data = data; + z->n = 0; + z->p = NULL; +} + + +/* --------------------------------------------------------------- read --- */ +size_t luaZ_read (ZIO *z, void *b, size_t n) { + while (n) { + size_t m; + if (luaZ_lookahead(z) == EOZ) + return n; /* return number of missing bytes */ + m = (n <= z->n) ? n : z->n; /* min. between n and z->n */ + memcpy(b, z->p, m); + z->n -= m; + z->p += m; + b = (char *)b + m; + n -= m; + } + return 0; +} + +/* ------------------------------------------------------------------------ */ +char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n) { + if (n > buff->buffsize) { + if (n < LUA_MINBUFFER) n = LUA_MINBUFFER; + luaZ_resizebuffer(L, buff, n); + } + return buff->buffer; +} + + === added file 'lua/lzio.h' --- lua/lzio.h 1970-01-01 00:00:00 +0000 +++ lua/lzio.h 2008-08-01 15:19:23 +0000 @@ -0,0 +1,67 @@ +/* +** $Id: lzio.h,v 1.21.1.1 2007/12/27 13:02:25 roberto Exp $ +** Buffered streams +** See Copyright Notice in lua.h +*/ + + +#ifndef lzio_h +#define lzio_h + +#include "lua.h" + +#include "lmem.h" + + +#define EOZ (-1) /* end of stream */ + +typedef struct Zio ZIO; + +#define char2int(c) cast(int, cast(unsigned char, (c))) + +#define zgetc(z) (((z)->n--)>0 ? char2int(*(z)->p++) : luaZ_fill(z)) + +typedef struct Mbuffer { + char *buffer; + size_t n; + size_t buffsize; +} Mbuffer; + +#define luaZ_initbuffer(L, buff) ((buff)->buffer = NULL, (buff)->buffsize = 0) + +#define luaZ_buffer(buff) ((buff)->buffer) +#define luaZ_sizebuffer(buff) ((buff)->buffsize) +#define luaZ_bufflen(buff) ((buff)->n) + +#define luaZ_resetbuffer(buff) ((buff)->n = 0) + + +#define luaZ_resizebuffer(L, buff, size) \ + (luaM_reallocvector(L, (buff)->buffer, (buff)->buffsize, size, char), \ + (buff)->buffsize = size) + +#define luaZ_freebuffer(L, buff) luaZ_resizebuffer(L, buff, 0) + + +LUAI_FUNC char *luaZ_openspace (lua_State *L, Mbuffer *buff, size_t n); +LUAI_FUNC void luaZ_init (lua_State *L, ZIO *z, lua_Reader reader, + void *data); +LUAI_FUNC size_t luaZ_read (ZIO* z, void* b, size_t n); /* read next n bytes */ +LUAI_FUNC int luaZ_lookahead (ZIO *z); + + + +/* --------- Private Part ------------------ */ + +struct Zio { + size_t n; /* bytes still unread */ + const char *p; /* current position in buffer */ + lua_Reader reader; + void* data; /* additional data */ + lua_State *L; /* Lua state (for reader) */ +}; + + +LUAI_FUNC int luaZ_fill (ZIO *z); + +#endif === added file 'lua/print.c' --- lua/print.c 1970-01-01 00:00:00 +0000 +++ lua/print.c 2008-08-01 15:19:23 +0000 @@ -0,0 +1,227 @@ +/* +** $Id: print.c,v 1.55a 2006/05/31 13:30:05 lhf Exp $ +** print bytecodes +** See Copyright Notice in lua.h +*/ + +#include +#include + +#define luac_c +#define LUA_CORE + +#include "ldebug.h" +#include "lobject.h" +#include "lopcodes.h" +#include "lundump.h" + +#define PrintFunction luaU_print + +#define Sizeof(x) ((int)sizeof(x)) +#define VOID(p) ((const void*)(p)) + +static void PrintString(const TString* ts) +{ + const char* s=getstr(ts); + size_t i,n=ts->tsv.len; + putchar('"'); + for (i=0; ik[i]; + switch (ttype(o)) + { + case LUA_TNIL: + printf("nil"); + break; + case LUA_TBOOLEAN: + printf(bvalue(o) ? "true" : "false"); + break; + case LUA_TNUMBER: + printf(LUA_NUMBER_FMT,nvalue(o)); + break; + case LUA_TSTRING: + PrintString(rawtsvalue(o)); + break; + default: /* cannot happen */ + printf("? type=%d",ttype(o)); + break; + } +} + +static void PrintCode(const Proto* f) +{ + const Instruction* code=f->code; + int pc,n=f->sizecode; + for (pc=0; pc0) printf("[%d]\t",line); else printf("[-]\t"); + printf("%-9s\t",luaP_opnames[o]); + switch (getOpMode(o)) + { + case iABC: + printf("%d",a); + if (getBMode(o)!=OpArgN) printf(" %d",ISK(b) ? (-1-INDEXK(b)) : b); + if (getCMode(o)!=OpArgN) printf(" %d",ISK(c) ? (-1-INDEXK(c)) : c); + break; + case iABx: + if (getBMode(o)==OpArgK) printf("%d %d",a,-1-bx); else printf("%d %d",a,bx); + break; + case iAsBx: + if (o==OP_JMP) printf("%d",sbx); else printf("%d %d",a,sbx); + break; + } + switch (o) + { + case OP_LOADK: + printf("\t; "); PrintConstant(f,bx); + break; + case OP_GETUPVAL: + case OP_SETUPVAL: + printf("\t; %s", (f->sizeupvalues>0) ? getstr(f->upvalues[b]) : "-"); + break; + case OP_GETGLOBAL: + case OP_SETGLOBAL: + printf("\t; %s",svalue(&f->k[bx])); + break; + case OP_GETTABLE: + case OP_SELF: + if (ISK(c)) { printf("\t; "); PrintConstant(f,INDEXK(c)); } + break; + case OP_SETTABLE: + case OP_ADD: + case OP_SUB: + case OP_MUL: + case OP_DIV: + case OP_POW: + case OP_EQ: + case OP_LT: + case OP_LE: + if (ISK(b) || ISK(c)) + { + printf("\t; "); + if (ISK(b)) PrintConstant(f,INDEXK(b)); else printf("-"); + printf(" "); + if (ISK(c)) PrintConstant(f,INDEXK(c)); else printf("-"); + } + break; + case OP_JMP: + case OP_FORLOOP: + case OP_FORPREP: + printf("\t; to %d",sbx+pc+2); + break; + case OP_CLOSURE: + printf("\t; %p",VOID(f->p[bx])); + break; + case OP_SETLIST: + if (c==0) printf("\t; %d",(int)code[++pc]); + else printf("\t; %d",c); + break; + default: + break; + } + printf("\n"); + } +} + +#define SS(x) (x==1)?"":"s" +#define S(x) x,SS(x) + +static void PrintHeader(const Proto* f) +{ + const char* s=getstr(f->source); + if (*s=='@' || *s=='=') + s++; + else if (*s==LUA_SIGNATURE[0]) + s="(bstring)"; + else + s="(string)"; + printf("\n%s <%s:%d,%d> (%d instruction%s, %d bytes at %p)\n", + (f->linedefined==0)?"main":"function",s, + f->linedefined,f->lastlinedefined, + S(f->sizecode),f->sizecode*Sizeof(Instruction),VOID(f)); + printf("%d%s param%s, %d slot%s, %d upvalue%s, ", + f->numparams,f->is_vararg?"+":"",SS(f->numparams), + S(f->maxstacksize),S(f->nups)); + printf("%d local%s, %d constant%s, %d function%s\n", + S(f->sizelocvars),S(f->sizek),S(f->sizep)); +} + +static void PrintConstants(const Proto* f) +{ + int i,n=f->sizek; + printf("constants (%d) for %p:\n",n,VOID(f)); + for (i=0; isizelocvars; + printf("locals (%d) for %p:\n",n,VOID(f)); + for (i=0; ilocvars[i].varname),f->locvars[i].startpc+1,f->locvars[i].endpc+1); + } +} + +static void PrintUpvalues(const Proto* f) +{ + int i,n=f->sizeupvalues; + printf("upvalues (%d) for %p:\n",n,VOID(f)); + if (f->upvalues==NULL) return; + for (i=0; iupvalues[i])); + } +} + +void PrintFunction(const Proto* f, int full) +{ + int i,n=f->sizep; + PrintHeader(f); + PrintCode(f); + if (full) + { + PrintConstants(f); + PrintLocals(f); + PrintUpvalues(f); + } + for (i=0; ip[i],full); +} === modified file 'normal/main.c' --- normal/main.c 2008-07-29 14:07:47 +0000 +++ normal/main.c 2008-08-03 04:00:22 +0000 @@ -28,6 +28,7 @@ #include #include #include +#include grub_jmp_buf grub_exit_env; @@ -147,14 +148,132 @@ grub_env_unset_data_slot ("menu"); } +static void +free_menu_entry_classes (struct grub_menu_entry_class *head) +{ + /* Free all the classes. */ + while (head) + { + struct grub_menu_entry_class *next; + + grub_free (head->name); + next = head->next; + grub_free (head); + head = next; + } +} + +/* The tag that can be added to a menu entry's title to specify a class + for the UI to use in selecting an icon or other visual attributes. */ +static const char entry_class_attr_tag[] = "|class="; + +#define ENTRY_ATTR_SEPARATOR_CHAR ',' + +/* Parse and strip a possible "class" attribute in the title. + This code is not designed to support other attributes than "class" + in the title since, we are planning to use a better method of + specifying this information in the future. The parameter TITLE is + modified by storing a '\0' at the appropriate location to strip the + class information, if it exists. The class list is stored into *HEAD. */ +static struct grub_menu_entry_class * +get_classes_from_entry_title (char *title) +{ + struct grub_menu_entry_class *head; + char *attr_start; + + head = grub_malloc (sizeof (struct grub_menu_entry_class)); + if (! head) + return 0; + head->name = 0; + head->next = 0; + + attr_start = grub_strstr (title, entry_class_attr_tag); + if (attr_start) + { + struct grub_menu_entry_class *tail; + const char *p; + + /* Trim the properties off of the title. */ + *attr_start = '\0'; + + /* Move the pointer to the beginning of the first class name. */ + attr_start += grub_strlen (entry_class_attr_tag); + + tail = head; + p = attr_start; + while (p && *p) + { + const char *q; + const char *end; + const char *next_start; + + /* Skip any leading whitespace. */ + while (*p && grub_isspace (*p)) + p++; + + /* Find the comma terminating this one ... */ + q = grub_strchr (p, ENTRY_ATTR_SEPARATOR_CHAR); + /* ... or if it's the last one, find the '\0' terminator. */ + if (q) + { + end = q - 1; + next_start = q + 1; + } + else + { + /* For the last class, extend it to the end. */ + end = p + grub_strlen (p); + next_start = 0; + } + + /* Trim any trailing whitespace. */ + while (end > p && grub_isspace (*end)) + end--; + + grub_size_t len = end - p + 1; + /* Copy the class name into a new string. */ + char *class_name = grub_malloc (len + 1); + if (! class_name) + { + free_menu_entry_classes (head); + return 0; + } + grub_memcpy (class_name, p, len); + class_name[len] = '\0'; + + /* Create a new class and add it at the tail of the list. */ + struct grub_menu_entry_class *new_class; + new_class = grub_malloc (sizeof (struct grub_menu_entry_class)); + if (! new_class) + { + grub_free (class_name); + free_menu_entry_classes (head); + return 0; + } + /* Fill in the new class node. */ + new_class->name = class_name; + new_class->next = 0; + /* Link the tail to it, and make it the new tail. */ + tail->next = new_class; + tail = new_class; + + /* Advance the character pointer. */ + p = next_start; + } + } + + return head; +} + grub_err_t grub_normal_menu_addentry (const char *title, struct grub_script *script, const char *sourcecode) { - const char *menutitle; + char *menutitle; const char *menusourcecode; grub_menu_t menu; grub_menu_entry_t *last; + struct grub_menu_entry_class *classes; menu = grub_env_get_data_slot("menu"); if (! menu) @@ -173,6 +292,14 @@ return grub_errno; } + classes = get_classes_from_entry_title (menutitle); + if (! classes) + { + grub_free ((void *) menutitle); + grub_free ((void *) menusourcecode); + return grub_errno; + } + /* Add the menu entry at the end of the list. */ while (*last) last = &(*last)->next; @@ -180,6 +307,7 @@ *last = grub_malloc (sizeof (**last)); if (! *last) { + free_menu_entry_classes (classes); grub_free ((void *) menutitle); grub_free ((void *) menusourcecode); return grub_errno; @@ -187,6 +315,7 @@ (*last)->commands = script; (*last)->title = menutitle; + (*last)->classes = classes; (*last)->next = 0; (*last)->sourcecode = menusourcecode; @@ -476,7 +605,7 @@ if (menu && menu->size) { - grub_menu_run (menu, nested); + grub_menu_viewer_show_menu (menu, nested); if (nested) free_menu (menu); } @@ -519,6 +648,8 @@ if (mod) grub_dl_ref (mod); + grub_menu_viewer_register (&grub_normal_terminal_menu_viewer); + grub_set_history (GRUB_DEFAULT_HISTORY_SIZE); /* Register a command "normal" for the rescue mode. */ @@ -535,6 +666,8 @@ /* This registers some built-in commands. */ grub_command_init (); + + grub_menu_viewer_init (); } GRUB_MOD_FINI(normal) === modified file 'normal/menu.c' --- normal/menu.c 2008-02-09 11:00:19 +0000 +++ normal/menu.c 2008-07-26 23:37:04 +0000 @@ -21,9 +21,11 @@ #include #include #include +#include #include #include #include +#include static grub_uint8_t grub_color_menu_normal; static grub_uint8_t grub_color_menu_highlight; @@ -241,8 +243,8 @@ /* Return the current timeout. If the variable "timeout" is not set or invalid, return -1. */ -static int -get_timeout (void) +int +grub_menu_get_timeout (void) { char *val; int timeout; @@ -269,8 +271,8 @@ } /* Set current timeout in the variable "timeout". */ -static void -set_timeout (int timeout) +void +grub_menu_set_timeout (int timeout) { /* Ignore TIMEOUT if it is zero, because it will be unset really soon. */ if (timeout > 0) @@ -308,6 +310,57 @@ return entry; } +/* Get the default menu entry index. */ +int +grub_menu_get_default_entry_index (grub_menu_t menu) +{ + int i = get_entry_number ("default"); + + /* If DEFAULT_ENTRY is not within the menu entries, fall back to + the first entry. */ + if (i < 0 || i >= menu->size) + i = 0; + + return i; +} + +/* Get the first entry number from the variable NAME, which is a + space-separated list of nonnegative integers. The entry number which + is returned is stripped from the value of NAME. */ +static int +get_and_remove_first_entry_number (const char *name) +{ + char *val; + char *tail; + int entry; + + val = grub_env_get (name); + if (! val) + return -1; + + grub_error_push (); + + entry = (int) grub_strtoul (val, &tail, 0); + + if (grub_errno == GRUB_ERR_NONE) + { + /* Skip whitespace to find the next digit. */ + while (*tail && grub_isspace (*tail)) + tail++; + grub_env_set (name, tail); + } + else + { + grub_env_unset (name); + grub_errno = GRUB_ERR_NONE; + entry = -1; + } + + grub_error_pop (); + + return entry; +} + static void print_timeout (int timeout, int offset, int second_stage) { @@ -332,15 +385,10 @@ first = 0; - default_entry = get_entry_number ("default"); - - /* If DEFAULT_ENTRY is not within the menu entries, fall back to - the first entry. */ - if (default_entry < 0 || default_entry >= menu->size) - default_entry = 0; + default_entry = grub_menu_get_default_entry_index (menu); /* If timeout is 0, drawing is pointless (and ugly). */ - if (get_timeout () == 0) + if (grub_menu_get_timeout () == 0) return default_entry; offset = default_entry; @@ -359,15 +407,15 @@ print_entries (menu, first, offset); grub_refresh (); - timeout = get_timeout (); + timeout = grub_menu_get_timeout (); if (timeout > 0) print_timeout (timeout, offset, 0); - while (1) + while (!grub_menu_viewer_should_return ()) { int c; - timeout = get_timeout (); + timeout = grub_menu_get_timeout (); if (timeout > 0) { @@ -377,7 +425,7 @@ if (current_time - saved_time >= GRUB_TICKS_PER_SECOND) { timeout--; - set_timeout (timeout); + grub_menu_set_timeout (timeout); saved_time = current_time; print_timeout (timeout, offset, 1); } @@ -468,6 +516,10 @@ } goto refresh; + case 't': + grub_env_set ("menuviewer", "protomenu"); + goto refresh; + default: break; } @@ -476,13 +528,14 @@ } } - /* Never reach here. */ + /* Exit menu without activating an item. This occurs if the user presses + * 't', switching to the graphical menu viewer. */ return -1; } /* Run a menu entry. */ -static void -run_menu_entry (grub_menu_entry_t entry) +void +grub_menu_execute_entry(grub_menu_entry_t entry) { grub_script_execute (entry->commands); @@ -491,15 +544,80 @@ grub_command_execute ("boot", 0); } +/* Execute ENTRY from the menu MENU, falling back to entries specified + in the environment variable "fallback" if it fails. CALLBACK is a + pointer to a struct of function pointers which are used to allow the + caller provide feedback to the user. */ void -grub_menu_run (grub_menu_t menu, int nested) +grub_menu_execute_with_fallback (grub_menu_t menu, + grub_menu_entry_t entry, + grub_menu_execute_callback_t callback, + void *callback_data) +{ + int fallback_entry; + + callback->notify_booting (callback_data, entry); + + grub_menu_execute_entry (entry); + + /* Deal with fallback entries. */ + while ((fallback_entry = get_and_remove_first_entry_number ("fallback")) + >= 0) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + + entry = get_entry (menu, fallback_entry); + callback->notify_fallback (callback_data, entry); + grub_menu_execute_entry (entry); + } + + if (grub_errno != GRUB_ERR_NONE) + callback->notify_failure (callback_data); +} + +static void +notify_booting (void *userdata __attribute__((unused)), + grub_menu_entry_t entry) +{ + grub_printf (" Booting \'%s\'\n\n", entry->title); +} + +static void +notify_fallback (void *userdata __attribute__((unused)), + grub_menu_entry_t entry) +{ + grub_printf ("\n Falling back to \'%s\'\n\n", entry->title); + grub_millisleep (2000); +} + +static void +notify_execution_failure (void *userdata __attribute__((unused))) +{ + if (grub_errno != GRUB_ERR_NONE) + { + grub_print_error (); + grub_errno = GRUB_ERR_NONE; + + grub_wait_after_message (); + } +} + +static struct grub_menu_execute_callback execution_callback = +{ + .notify_booting = notify_booting, + .notify_fallback = notify_fallback, + .notify_failure = notify_execution_failure +}; + +static grub_err_t +show_menu (grub_menu_t menu, int nested) { while (1) { int boot_entry; grub_menu_entry_t e; - int fallback_entry; - + boot_entry = run_menu (menu, nested); if (boot_entry < 0) break; @@ -507,34 +625,18 @@ e = get_entry (menu, boot_entry); if (! e) continue; /* Menu is empty. */ - + grub_cls (); grub_setcursor (1); - grub_printf (" Booting \'%s\'\n\n", e->title); - - run_menu_entry (e); - - /* Deal with a fallback entry. */ - /* FIXME: Multiple fallback entries like GRUB Legacy. */ - fallback_entry = get_entry_number ("fallback"); - if (fallback_entry >= 0) - { - grub_print_error (); - grub_errno = GRUB_ERR_NONE; - - e = get_entry (menu, fallback_entry); - grub_env_unset ("fallback"); - grub_printf ("\n Falling back to \'%s\'\n\n", e->title); - run_menu_entry (e); - } - - if (grub_errno != GRUB_ERR_NONE) - { - grub_print_error (); - grub_errno = GRUB_ERR_NONE; - - grub_wait_after_message (); - } + grub_menu_execute_with_fallback (menu, e, &execution_callback, 0); } + + return GRUB_ERR_NONE; } + +struct grub_menu_viewer grub_normal_terminal_menu_viewer = +{ + .name = "terminal", + .show_menu = show_menu +}; === modified file 'term/gfxterm.c' --- term/gfxterm.c 2008-01-01 12:02:07 +0000 +++ term/gfxterm.c 2008-07-19 19:56:06 +0000 @@ -28,14 +28,12 @@ #include #include #include +#include #include +#include #define DEFAULT_VIDEO_WIDTH 640 #define DEFAULT_VIDEO_HEIGHT 480 -#define DEFAULT_VIDEO_FLAGS 0 - -#define DEFAULT_CHAR_WIDTH 8 -#define DEFAULT_CHAR_HEIGHT 16 #define DEFAULT_BORDER_WIDTH 10 @@ -91,6 +89,9 @@ unsigned int cursor_y; int cursor_state; + /* Font settings. */ + grub_font_t font; + /* Terminal color settings. */ grub_uint8_t standard_color_setting; grub_uint8_t normal_color_setting; @@ -107,10 +108,20 @@ struct grub_colored_char *text_buffer; }; +static int refcount; +static struct grub_video_render_target *render_target; +static grub_video_rect_t window; static struct grub_virtual_screen virtual_screen; +static grub_gfxterm_repaint_callback_t repaint_callback; + +static grub_err_t init_window (struct grub_video_render_target *target, + int x, int y, int width, int height, + const char *font_name, int border_width); + +static void destroy_window (void); + static grub_dl_t my_mod; -static struct grub_video_mode_info mode_info; static struct grub_video_render_target *text_layer; @@ -170,18 +181,25 @@ static grub_err_t grub_virtual_screen_setup (unsigned int x, unsigned int y, - unsigned int width, unsigned int height) + unsigned int width, unsigned int height, + const char *font_name) { /* Free old virtual screen. */ grub_virtual_screen_free (); /* Initialize with default data. */ + virtual_screen.font = grub_font_get (font_name); + if (!virtual_screen.font) + return grub_error (GRUB_ERR_BAD_FONT, + "No font loaded."); virtual_screen.width = width; virtual_screen.height = height; virtual_screen.offset_x = x; virtual_screen.offset_y = y; - virtual_screen.char_width = DEFAULT_CHAR_WIDTH; - virtual_screen.char_height = DEFAULT_CHAR_HEIGHT; + virtual_screen.char_width = + grub_font_get_max_char_width (virtual_screen.font); + virtual_screen.char_height = + grub_font_get_max_char_height (virtual_screen.font); virtual_screen.cursor_x = 0; virtual_screen.cursor_y = 0; virtual_screen.cursor_state = 1; @@ -227,281 +245,129 @@ } static grub_err_t +init_window (struct grub_video_render_target *target, + int x, int y, int width, int height, + const char *font_name, int border_width) +{ + /* Clean up any prior instance. */ + destroy_window (); + + /* Create virtual screen. */ + if (grub_virtual_screen_setup (border_width, border_width, + width - 2 * border_width, + height - 2 * border_width, + font_name) + != GRUB_ERR_NONE) + { + return grub_errno; + } + + /* Set the render target. */ + render_target = target; + + /* Set window bounds. */ + window.x = x; + window.y = y; + window.width = width; + window.height = height; + + /* Mark whole window as dirty. */ + dirty_region_reset (); + dirty_region_add (0, 0, width, height); + + return (grub_errno = GRUB_ERR_NONE); +} + +grub_err_t +grub_gfxterm_init_window (struct grub_video_render_target *target, + int x, int y, int width, int height, + const char *font_name, int border_width) +{ + grub_errno = GRUB_ERR_NONE; + if (refcount++ == 0) + init_window (target, x, y, width, height, font_name, border_width); + return grub_errno; +} + +static grub_err_t grub_gfxterm_init (void) { - char *modevar; - int width = DEFAULT_VIDEO_WIDTH; - int height = DEFAULT_VIDEO_HEIGHT; - int depth = -1; - int flags = DEFAULT_VIDEO_FLAGS; - grub_video_color_t color; + /* If gfxterm has already been initialized by calling the init_window + function, then leave it alone when it is set as the current terminal. */ + if (refcount++ != 0) + return GRUB_ERR_NONE; /* Parse gfxmode environment variable if set. */ - modevar = grub_env_get ("gfxmode"); - if (modevar) - { - char *tmp; - char *next_mode; - char *current_mode; - char *param; - char *value; - int mode_found = 0; - - /* Take copy of env.var. as we don't want to modify that. */ - tmp = grub_strdup (modevar); - modevar = tmp; - - if (grub_errno != GRUB_ERR_NONE) - return grub_errno; - - /* Initialize next mode. */ - next_mode = modevar; - - /* Loop until all modes has been tested out. */ - while (next_mode != NULL) - { - /* Use last next_mode as current mode. */ - tmp = next_mode; - - /* Reset video mode settings. */ - width = DEFAULT_VIDEO_WIDTH; - height = DEFAULT_VIDEO_HEIGHT; - depth = -1; - flags = DEFAULT_VIDEO_FLAGS; - - /* Save position of next mode and separate modes. */ - next_mode = grub_strchr(next_mode, ';'); - if (next_mode) - { - *next_mode = 0; - next_mode++; - } - - /* Skip whitespace. */ - while (grub_isspace (*tmp)) - tmp++; - - /* Initialize token holders. */ - current_mode = tmp; - param = tmp; - value = NULL; - - /* Parse x[x]*/ - - /* Find width value. */ - value = param; - param = grub_strchr(param, 'x'); - if (param == NULL) - { - grub_err_t rc; - - /* First setup error message. */ - rc = grub_error (GRUB_ERR_BAD_ARGUMENT, - "Invalid mode: %s\n", - current_mode); - - /* Free memory before returning. */ - grub_free (modevar); - - return rc; - } - - *param = 0; - param++; - - width = grub_strtoul (value, 0, 0); - if (grub_errno != GRUB_ERR_NONE) - { - grub_err_t rc; - - /* First setup error message. */ - rc = grub_error (GRUB_ERR_BAD_ARGUMENT, - "Invalid mode: %s\n", - current_mode); - - /* Free memory before returning. */ - grub_free (modevar); - - return rc; - } - - /* Find height value. */ - value = param; - param = grub_strchr(param, 'x'); - if (param == NULL) - { - height = grub_strtoul (value, 0, 0); - if (grub_errno != GRUB_ERR_NONE) - { - grub_err_t rc; - - /* First setup error message. */ - rc = grub_error (GRUB_ERR_BAD_ARGUMENT, - "Invalid mode: %s\n", - current_mode); - - /* Free memory before returning. */ - grub_free (modevar); - - return rc; - } - } - else - { - /* We have optional color depth value. */ - *param = 0; - param++; - - height = grub_strtoul (value, 0, 0); - if (grub_errno != GRUB_ERR_NONE) - { - grub_err_t rc; - - /* First setup error message. */ - rc = grub_error (GRUB_ERR_BAD_ARGUMENT, - "Invalid mode: %s\n", - current_mode); - - /* Free memory before returning. */ - grub_free (modevar); - - return rc; - } - - /* Convert color depth value. */ - value = param; - depth = grub_strtoul (value, 0, 0); - if (grub_errno != GRUB_ERR_NONE) - { - grub_err_t rc; - - /* First setup error message. */ - rc = grub_error (GRUB_ERR_BAD_ARGUMENT, - "Invalid mode: %s\n", - current_mode); - - /* Free memory before returning. */ - grub_free (modevar); - - return rc; - } - } - - /* Try out video mode. */ - - /* If we have 8 or less bits, then assume that it is indexed color mode. */ - if ((depth <= 8) && (depth != -1)) - flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; - - /* We have more than 8 bits, then assume that it is RGB color mode. */ - if (depth > 8) - flags |= GRUB_VIDEO_MODE_TYPE_RGB; - - /* If user requested specific depth, forward that information to driver. */ - if (depth != -1) - flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) - & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK; - - /* Try to initialize requested mode. Ignore any errors. */ - grub_error_push (); - if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE) - { - grub_error_pop (); - continue; - } - - /* Figure out what mode we ended up. */ - if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE) - { - /* Couldn't get video mode info, restore old mode and continue to next one. */ - grub_error_pop (); - - grub_video_restore (); - continue; - } - - /* Restore state of error stack. */ - grub_error_pop (); - - /* Mode found! Exit loop. */ - mode_found = 1; - break; - } - - /* Free memory. */ - grub_free (modevar); - - if (!mode_found) - return grub_error (GRUB_ERR_BAD_ARGUMENT, - "No suitable mode found."); - } - else - { - /* No gfxmode variable set, use defaults. */ - - /* If we have 8 or less bits, then assume that it is indexed color mode. */ - if ((depth <= 8) && (depth != -1)) - flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; - - /* We have more than 8 bits, then assume that it is RGB color mode. */ - if (depth > 8) - flags |= GRUB_VIDEO_MODE_TYPE_RGB; - - /* If user requested specific depth, forward that information to driver. */ - if (depth != -1) - flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) - & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK; - - /* Initialize user requested mode. */ - if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE) - return grub_errno; - - /* Figure out what mode we ended up. */ - if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE) - { - grub_video_restore (); - return grub_errno; - } + const char *modevar = grub_env_get ("gfxmode"); + if (grub_video_setup_preferred_mode (modevar, 0, + DEFAULT_VIDEO_WIDTH, + DEFAULT_VIDEO_HEIGHT) + != GRUB_ERR_NONE) + return grub_errno; + + /* Figure out what mode we ended up. */ + struct grub_video_mode_info mode_info; + if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE) + { + grub_video_restore (); + return grub_errno; } /* Make sure screen is black. */ - color = grub_video_map_rgb (0, 0, 0); - grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + grub_video_fill_rect (grub_video_map_rgb (0, 0, 0), + 0, 0, mode_info.width, mode_info.height); bitmap = 0; + /* Select the font to use. */ + char *font_name = grub_env_get ("gfxterm_font"); + if (!font_name) + font_name = ""; /* Allow fallback to any font. */ + /* Leave borders for virtual screen. */ - width = mode_info.width - (2 * DEFAULT_BORDER_WIDTH); - height = mode_info.height - (2 * DEFAULT_BORDER_WIDTH); - - /* Create virtual screen. */ - if (grub_virtual_screen_setup (DEFAULT_BORDER_WIDTH, DEFAULT_BORDER_WIDTH, - width, height) != GRUB_ERR_NONE) + if (init_window (GRUB_VIDEO_RENDER_TARGET_DISPLAY, + 0, 0, mode_info.width, mode_info.height, + font_name, + DEFAULT_BORDER_WIDTH) != GRUB_ERR_NONE) { grub_video_restore (); return grub_errno; } - /* Mark whole screen as dirty. */ - dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); - return (grub_errno = GRUB_ERR_NONE); } +static void +destroy_window (void) +{ + if (bitmap) + { + grub_video_bitmap_destroy (bitmap); + bitmap = 0; + } + + repaint_callback = 0; + grub_virtual_screen_free (); +} + +void +grub_gfxterm_destroy_window (void) +{ + if (--refcount == 0) + destroy_window (); +} + static grub_err_t grub_gfxterm_fini (void) { - if (bitmap) + /* Don't destroy an explicitly initialized terminal instance when it is + unset as the current terminal. */ + if (--refcount == 0) { - grub_video_bitmap_destroy (bitmap); - bitmap = 0; + destroy_window (); + grub_video_restore (); } - grub_virtual_screen_free (); - - grub_video_restore (); - - return GRUB_ERR_NONE; + return (grub_errno = GRUB_ERR_NONE); } static void @@ -509,9 +375,15 @@ unsigned int width, unsigned int height) { grub_video_color_t color; - - grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); - + grub_video_rect_t saved_view; + + grub_video_set_active_render_target (render_target); + /* Save viewport and set it to our window. */ + grub_video_get_viewport ((unsigned *) &saved_view.x, + (unsigned *) &saved_view.y, + (unsigned *) &saved_view.width, + (unsigned *) &saved_view.height); + grub_video_set_viewport (window.x, window.y, window.width, window.height); if (bitmap) { @@ -578,6 +450,14 @@ y - virtual_screen.offset_y, width, height); } + + /* Restore saved viewport. */ + grub_video_set_viewport (saved_view.x, saved_view.y, + saved_view.width, saved_view.height); + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + + if (repaint_callback) + repaint_callback (x, y, width, height); } static void @@ -661,11 +541,12 @@ write_char (void) { struct grub_colored_char *p; - struct grub_font_glyph glyph; + struct grub_font_glyph *glyph; grub_video_color_t color; grub_video_color_t bgcolor; unsigned int x; unsigned int y; + int ascent; /* Find out active character. */ p = (virtual_screen.text_buffer @@ -675,7 +556,8 @@ p -= p->index; /* Get glyph for character. */ - grub_font_get_glyph (p->code, &glyph); + glyph = grub_font_get_glyph (virtual_screen.font, p->code); + ascent = grub_font_get_ascent (virtual_screen.font); color = p->fg_color; bgcolor = p->bg_color; @@ -685,13 +567,13 @@ /* Render glyph to text layer. */ grub_video_set_active_render_target (text_layer); - grub_video_fill_rect (bgcolor, x, y, glyph.width, glyph.height); - grub_video_blit_glyph (&glyph, color, x, y); + grub_video_fill_rect (bgcolor, x, y, glyph->width, glyph->height); + grub_video_blit_glyph (glyph, color, x, y + ascent); grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); /* Mark character to be drawn. */ dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y, - glyph.width, glyph.height); + glyph->width, glyph->height); } static void @@ -705,7 +587,8 @@ /* Determine cursor properties and position on text layer. */ x = virtual_screen.cursor_x * virtual_screen.char_width; - y = ((virtual_screen.cursor_y + 1) * virtual_screen.char_height) - 3; + y = (virtual_screen.cursor_y * virtual_screen.char_height + + grub_font_get_ascent (virtual_screen.font)); width = virtual_screen.char_width; height = 2; @@ -769,7 +652,16 @@ dirty_region_add_virtualscreen (); } else - { + { + grub_video_rect_t saved_view; + grub_video_set_active_render_target (render_target); + /* Save viewport and set it to our window. */ + grub_video_get_viewport ((unsigned *) &saved_view.x, + (unsigned *) &saved_view.y, + (unsigned *) &saved_view.width, + (unsigned *) &saved_view.height); + grub_video_set_viewport (window.x, window.y, window.width, window.height); + /* Clear new border area. */ grub_video_fill_rect (color, virtual_screen.offset_x, virtual_screen.offset_y, @@ -778,10 +670,18 @@ /* Scroll physical screen. */ grub_video_scroll (color, 0, -virtual_screen.char_height); + /* Restore saved viewport. */ + grub_video_set_viewport (saved_view.x, saved_view.y, + saved_view.width, saved_view.height); + grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); + /* Draw cursor if visible. */ if (virtual_screen.cursor_state) write_cursor (); } + + if (repaint_callback) + repaint_callback (window.x, window.y, window.width, window.height); } static void @@ -822,14 +722,18 @@ } else { - struct grub_font_glyph glyph; + struct grub_font_glyph *glyph; struct grub_colored_char *p; + unsigned char_width; /* Get properties of the character. */ - grub_font_get_glyph (c, &glyph); + glyph = grub_font_get_glyph (virtual_screen.font, c); + + /* TODO [CDB] Fix wide characters. Bi-width font? */ + char_width = 1; /* If we are about to exceed line length, wrap to next line. */ - if (virtual_screen.cursor_x + glyph.char_width > virtual_screen.columns) + if (virtual_screen.cursor_x + char_width > virtual_screen.columns) grub_putchar ('\n'); /* Find position on virtual screen, and fill information. */ @@ -839,18 +743,18 @@ p->code = c; p->fg_color = virtual_screen.fg_color; p->bg_color = virtual_screen.bg_color; - p->width = glyph.char_width - 1; + p->width = char_width - 1; p->index = 0; /* If we have large glyph, add fixup info. */ - if (glyph.char_width > 1) + if (char_width > 1) { unsigned i; - for (i = 1; i < glyph.char_width; i++) + for (i = 1; i < char_width; i++) { p[i].code = ' '; - p[i].width = glyph.char_width - 1; + p[i].width = char_width - 1; p[i].index = i; } } @@ -859,7 +763,7 @@ write_char (); /* Make sure we scroll screen when needed and wrap line correctly. */ - virtual_screen.cursor_x += glyph.char_width; + virtual_screen.cursor_x += char_width; if (virtual_screen.cursor_x >= virtual_screen.columns) { virtual_screen.cursor_x = 0; @@ -877,13 +781,17 @@ } static grub_ssize_t -grub_gfxterm_getcharwidth (grub_uint32_t c) +grub_gfxterm_getcharwidth (grub_uint32_t c __attribute__((unused))) { - struct grub_font_glyph glyph; - - grub_font_get_glyph (c, &glyph); - - return glyph.char_width; +#if 0 + struct grub_font_glyph *glyph; + + glyph = grub_font_get_glyph (c); + + return glyph->char_width; +#else + return 1; /* TODO [CDB] Fix wide characters. */ +#endif } static grub_uint16_t @@ -945,7 +853,8 @@ /* Clear text layer. */ grub_video_set_active_render_target (text_layer); color = virtual_screen.bg_color; - grub_video_fill_rect (color, 0, 0, mode_info.width, mode_info.height); + grub_video_fill_rect (color, 0, 0, + virtual_screen.width, virtual_screen.height); grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY); /* Mark virtual screen to be redrawn. */ @@ -1014,8 +923,23 @@ dirty_region_redraw (); } +void +grub_gfxterm_set_repaint_callback (grub_gfxterm_repaint_callback_t func) +{ + repaint_callback = func; +} + +/* Option array indices. */ +#define BACKGROUND_CMD_ARGINDEX_MODE 0 + +static const struct grub_arg_option background_image_cmd_options[] = { + {"mode", 'm', 0, "Background image mode (`stretch', `normal').", 0, + ARG_TYPE_STRING}, + {0, 0, 0, 0, 0, 0} +}; + static grub_err_t -grub_gfxterm_background_image_cmd (struct grub_arg_list *state __attribute__ ((unused)), +grub_gfxterm_background_image_cmd (struct grub_arg_list *state, int argc, char **args) { @@ -1031,7 +955,7 @@ /* Mark whole screen as dirty. */ dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); + dirty_region_add (0, 0, window.width, window.height); } /* If filename was provided, try to load that. */ @@ -1042,16 +966,40 @@ if (grub_errno != GRUB_ERR_NONE) return grub_errno; + /* Determine if the bitmap should be scaled to fit the screen. */ + if (!state[BACKGROUND_CMD_ARGINDEX_MODE].set + || grub_strcmp (state[BACKGROUND_CMD_ARGINDEX_MODE].arg, + "stretch") == 0) + { + if (window.width != (int) grub_video_bitmap_get_width (bitmap) + || window.height != (int) grub_video_bitmap_get_height (bitmap)) + { + struct grub_video_bitmap *scaled_bitmap; + grub_video_bitmap_create_scaled (&scaled_bitmap, + window.width, + window.height, + bitmap, + GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST); + if (grub_errno == GRUB_ERR_NONE) + { + /* Replace the original bitmap with the scaled one. */ + grub_video_bitmap_destroy (bitmap); + bitmap = scaled_bitmap; + } + } + } + + /* If bitmap was loaded correctly, display it. */ if (bitmap) { /* Determine bitmap dimensions. */ bitmap_width = grub_video_bitmap_get_width (bitmap); - bitmap_height = grub_video_bitmap_get_width (bitmap); + bitmap_height = grub_video_bitmap_get_height (bitmap); /* Mark whole screen as dirty. */ dirty_region_reset (); - dirty_region_add (0, 0, mode_info.width, mode_info.height); + dirty_region_add (0, 0, window.width, window.height); } } @@ -1082,9 +1030,16 @@ .next = 0 }; +grub_term_t +grub_gfxterm_get_term (void) +{ + return &grub_video_term; +} + GRUB_MOD_INIT(term_gfxterm) { my_mod = mod; + refcount = 0; grub_term_register (&grub_video_term); grub_register_command ("background_image", @@ -1092,7 +1047,7 @@ GRUB_COMMAND_FLAG_BOTH, "background_image", "Load background image for active terminal", - 0); + background_image_cmd_options); } GRUB_MOD_FINI(term_gfxterm) === modified file 'term/i386/pc/vesafb.c' --- term/i386/pc/vesafb.c 2007-12-30 08:52:06 +0000 +++ term/i386/pc/vesafb.c 2008-07-03 14:12:08 +0000 @@ -250,10 +250,11 @@ break; default: - return grub_font_get_glyph (code, bitmap, width); + return grub_font_get_glyph_any (code, bitmap, width); } } + /* TODO [CDB] This is wrong for the new font module. Should it be fixed? */ if (bitmap) grub_memcpy (bitmap, vga_font + code * virtual_screen.char_height, === modified file 'term/i386/pc/vga.c' --- term/i386/pc/vga.c 2008-01-21 15:48:27 +0000 +++ term/i386/pc/vga.c 2008-07-03 14:12:08 +0000 @@ -65,6 +65,7 @@ static struct colored_char text_buf[TEXT_WIDTH * TEXT_HEIGHT]; static unsigned char saved_map_mask; static int page = 0; +static grub_font_t font = 0; #define SEQUENCER_ADDR_PORT 0x3C4 #define SEQUENCER_DATA_PORT 0x3C5 @@ -161,6 +162,9 @@ saved_map_mask = get_map_mask (); set_map_mask (0x0f); set_start_address (PAGE_OFFSET (page)); + font = grub_font_get (""); /* Choose any font, for now. */ + if (!font) + return grub_error (GRUB_ERR_BAD_FONT, "No font loaded."); return GRUB_ERR_NONE; } @@ -185,7 +189,7 @@ write_char (void) { struct colored_char *p = text_buf + xpos + ypos * TEXT_WIDTH; - struct grub_font_glyph glyph; + struct grub_font_glyph *glyph; unsigned char *mem_base; unsigned plane; @@ -194,7 +198,7 @@ p -= p->index; /* Get glyph for character. */ - grub_font_get_glyph (p->code, &glyph); + glyph = grub_font_get_glyph (font, p->code); for (plane = 0x01; plane <= 0x08; plane <<= 1) { @@ -210,17 +214,21 @@ { unsigned i; - for (i = 0; i < glyph.char_width && offset < 32; i++) + unsigned char_width = 1; /* TODO [CDB] Figure out wide characters. */ + /* TODO [CDB] Re-implement glyph drawing for vga module. */ +#if 0 + for (i = 0; i < char_width && offset < 32; i++) { unsigned char fg_mask, bg_mask; - fg_mask = (p->fg_color & plane) ? glyph.bitmap[offset] : 0; - bg_mask = (p->bg_color & plane) ? ~(glyph.bitmap[offset]) : 0; + fg_mask = (p->fg_color & plane) ? glyph->bitmap[offset] : 0; + bg_mask = (p->bg_color & plane) ? ~(glyph->bitmap[offset]) : 0; offset++; if (check_vga_mem (mem + i)) mem[i] = (fg_mask | bg_mask); } +#endif /* 0 */ } } @@ -320,36 +328,37 @@ } else { - struct grub_font_glyph glyph; + struct grub_font_glyph *glyph; struct colored_char *p; + unsigned char_width = 1; - grub_font_get_glyph(c, &glyph); + glyph = grub_font_get_glyph(font, c); - if (xpos + glyph.char_width > TEXT_WIDTH) + if (xpos + char_width > TEXT_WIDTH) grub_putchar ('\n'); p = text_buf + xpos + ypos * TEXT_WIDTH; p->code = c; p->fg_color = fg_color; p->bg_color = bg_color; - p->width = glyph.char_width - 1; + p->width = char_width - 1; p->index = 0; - if (glyph.char_width > 1) + if (char_width > 1) { unsigned i; - for (i = 1; i < glyph.char_width; i++) + for (i = 1; i < char_width; i++) { p[i].code = ' '; - p[i].width = glyph.char_width - 1; + p[i].width = char_width - 1; p[i].index = i; } } write_char (); - xpos += glyph.char_width; + xpos += char_width; if (xpos >= TEXT_WIDTH) { xpos = 0; @@ -381,11 +390,16 @@ static grub_ssize_t grub_vga_getcharwidth (grub_uint32_t c) { +#if 0 struct grub_font_glyph glyph; - grub_font_get_glyph (c, &glyph); + glyph = grub_font_get_glyph (c); return glyph.char_width; +#else + /* TODO [CDB] Glyph wide characters? vga? */ + return 1; +#endif } static grub_uint16_t === added directory 'util/fonttool' === added file 'util/fonttool/build.xml' --- util/fonttool/build.xml 1970-01-01 00:00:00 +0000 +++ util/fonttool/build.xml 2008-07-03 14:08:39 +0000 @@ -0,0 +1,23 @@ + + GRUB 2 font tool. + + + + + + + + + + + + + + + + + + + + === added file 'util/fonttool/fonttool.iml' --- util/fonttool/fonttool.iml 1970-01-01 00:00:00 +0000 +++ util/fonttool/fonttool.iml 2008-07-03 14:08:39 +0000 @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + === added file 'util/fonttool/fonttool.ipr' --- util/fonttool/fonttool.ipr 1970-01-01 00:00:00 +0000 +++ util/fonttool/fonttool.ipr 2008-07-03 14:08:39 +0000 @@ -0,0 +1,306 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + === added directory 'util/fonttool/src' === added directory 'util/fonttool/src/org' === added directory 'util/fonttool/src/org/gnu' === added directory 'util/fonttool/src/org/gnu/grub' === added directory 'util/fonttool/src/org/gnu/grub/fonttool' === added file 'util/fonttool/src/org/gnu/grub/fonttool/BDFFont.java' --- util/fonttool/src/org/gnu/grub/fonttool/BDFFont.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/BDFFont.java 2008-07-03 14:08:39 +0000 @@ -0,0 +1,24 @@ +package org.gnu.grub.fonttool; + +import java.awt.image.BufferedImage; +import java.util.HashMap; +import java.util.Map; + +public class BDFFont { + private Map glyphs = new HashMap(); + private int height = 0; + + void putGlyph(char ch, BufferedImage glyph) { + glyphs.put(ch, glyph); + if (glyph.getHeight() > height) + height = glyph.getHeight(); + } + + public BufferedImage getGlyph(char c) { + return glyphs.get(c); + } + + public int getHeight() { + return height; + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java' --- util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,254 @@ +package org.gnu.grub.fonttool; + +import java.io.*; +import java.util.StringTokenizer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class BDFLoader { + private final BufferedReader in; + private final Font font; + private int maxCharWidth; + private int maxCharHeight; + + BDFLoader(BufferedReader in) { + this.in = in; + this.font = new Font(); + this.maxCharWidth = 0; + this.maxCharHeight = 0; + } + + public static boolean isBDFFile(String filename) { + DataInputStream in = null; + try { + in = new DataInputStream(new FileInputStream(filename)); + final String signature = "STARTFONT "; + byte[] b = new byte[signature.length()]; + in.readFully(b); + in.close(); + + String s = new String(b, "US-ASCII"); + return signature.equals(s); + } catch (IOException e) { + if (in != null) { + try { + in.close(); + } catch (IOException e1) { + // Ignore. + } + } + return false; + } + } + + public static Font loadFontResource(String resourceName) throws IOException { + InputStream in = BDFLoader.class.getClassLoader().getResourceAsStream(resourceName); + if (in == null) + throw new FileNotFoundException("Font resource " + resourceName + " not found"); + return loadFontFromStream(in); + } + + public static Font loadFontFile(String filename) throws IOException { + InputStream in = new FileInputStream(filename); + return loadFontFromStream(in); + } + + private static Font loadFontFromStream(InputStream inStream) throws IOException { + BufferedReader in; + try { + in = new BufferedReader(new InputStreamReader(inStream, "ISO-8859-1")); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException("Encoding not supported: " + e.getMessage(), e); + } + return new BDFLoader(in).loadFont(); + } + + private Font loadFont() throws IOException { + loadFontInfo(); + while (loadChar()) { + /* Loop. */ + } + + font.setMaxCharWidth(maxCharWidth); + font.setMaxCharHeight(maxCharHeight); + return font; + } + + private void loadFontInfo() throws IOException { + final Pattern stringSettingPattern = Pattern.compile("^(\\w+)\\s+\"([^\"]+)\"$"); + String line; + // Load the global font information that appears before CHARS. + final int UNSET = Integer.MIN_VALUE; + font.setAscent(UNSET); + font.setDescent(UNSET); + font.setFamily("Unknown"); + font.setBold(false); + font.setItalic(false); + font.setPointSize(Font.UNKNOWN_POINT_SIZE); + do { + line = in.readLine(); + if (line == null) + throw new IOException("BDF format error: end of file while " + + "reading global font information"); + + StringTokenizer st = new StringTokenizer(line); + if (st.hasMoreTokens()) { + String name = st.nextToken(); + if (name.equals("FONT_ASCENT")) { + if (!st.hasMoreTokens()) + throw new IOException("BDF format error: " + + "no tokens after " + name); + font.setAscent(Integer.parseInt(st.nextToken())); + } else if (name.equals("FONT_DESCENT")) { + if (!st.hasMoreTokens()) + throw new IOException("BDF format error: " + + "no tokens after " + name); + font.setDescent(Integer.parseInt(st.nextToken())); + } else if (name.equals("POINT_SIZE")) { + if (!st.hasMoreTokens()) + throw new IOException("BDF format error: " + + "no tokens after " + name); + // Divide by 10, since it is stored X10. + font.setPointSize(Integer.parseInt(st.nextToken()) / 10); + } else if (name.equals("FAMILY_NAME")) { + Matcher matcher = stringSettingPattern.matcher(line); + if (!matcher.matches()) + throw new IOException("BDF format error: " + + "line doesn't match string " + + "setting pattern: " + line); + font.setFamily(matcher.group(2)); + } else if (name.equals("WEIGHT_NAME")) { + Matcher matcher = stringSettingPattern.matcher(line); + if (!matcher.matches()) + throw new IOException("BDF format error: " + + "line doesn't match string " + + "setting pattern: " + line); + String weightName = matcher.group(2); + font.setBold("bold".equalsIgnoreCase(weightName)); + } else if (name.equals("SLANT")) { + Matcher matcher = stringSettingPattern.matcher(line); + if (!matcher.matches()) + throw new IOException("BDF format error: " + + "line doesn't match string " + + "setting pattern: " + line); + String slantType = matcher.group(2); + font.setItalic(!"R".equalsIgnoreCase(slantType)); + } else if (name.equals("CHARS")) { + // This is the end of the global font information and + // the beginning of the character definitions. + break; + } else { + // Skip other fields. + } + } + } while (true); + + if (font.getAscent() == UNSET) + throw new IOException("BDF format error: no FONT_ASCENT property"); + if (font.getDescent() == UNSET) + throw new IOException("BDF format error: no FONT_DESCENT property"); + } + + private boolean loadChar() throws IOException { + String line; + // Find start of character + do { + line = in.readLine(); + if (line == null) + return false; + StringTokenizer st = new StringTokenizer(line); + if (st.hasMoreTokens() && st.nextToken().equals("STARTCHAR")) { + if (!st.hasMoreTokens()) + throw new IOException("BDF format error: no character name after STARTCHAR"); + break; + } + } while (true); + + // Find properties + final int UNSET = Integer.MIN_VALUE; + int codePoint = UNSET; + int bbx = UNSET; + int bby = UNSET; + int bbox = UNSET; + int bboy = UNSET; + int dwidth = UNSET; + do { + line = in.readLine(); + if (line == null) + return false; + StringTokenizer st = new StringTokenizer(line); + if (st.hasMoreTokens()) { + String field = st.nextToken(); + if (field.equals("ENCODING")) { + if (!st.hasMoreTokens()) + throw new IOException("BDF format error: no encoding # after ENCODING"); + String codePointStr = st.nextToken(); + codePoint = Integer.parseInt(codePointStr); + } else if (field.equals("BBX")) { + if (!st.hasMoreTokens()) + throw new IOException("BDF format error: no tokens after BBX"); + bbx = Integer.parseInt(st.nextToken()); + bby = Integer.parseInt(st.nextToken()); + bbox = Integer.parseInt(st.nextToken()); + bboy = Integer.parseInt(st.nextToken()); + } else if (field.equals("DWIDTH")) { + if (!st.hasMoreTokens()) + throw new IOException("BDF format error: no tokens after DWIDTH"); + dwidth = Integer.parseInt(st.nextToken()); + int dwidthY = Integer.parseInt(st.nextToken()); + // The DWIDTH Y value should be zero for any normal font. + if (dwidthY != 0) { + throw new IOException("BDF format error: dwidth Y value" + + "is nonzero (" + dwidthY + ") " + + "for char " + codePoint + "."); + } + } else if (field.equals("BITMAP")) { + break; // now read the bitmap + } + } + } while (true); + + if (codePoint == UNSET) + throw new IOException("BDF format error: " + + "no code point set"); + if (bbx == UNSET || bby == UNSET) + throw new IOException("BDF format error: " + + "bbx/bby missing: " + bbx + ", " + bby + + " for char " + codePoint); + + if (bbox == UNSET || bboy == UNSET) + throw new IOException("BDF format error: " + + "bbox/bboy missing: " + bbox + ", " + bboy + + " for char " + codePoint); + + if (dwidth == UNSET) + throw new IOException("BDF format error: " + + "dwidth missing for char " + codePoint); + + final int glyphWidth = bbx; + final int glyphHeight = bby; + if (glyphWidth > maxCharWidth) + maxCharWidth = glyphWidth; + if (glyphHeight > maxCharHeight) + maxCharHeight = glyphHeight; + + // Read the bitmap + Glyph glyph = new Glyph(codePoint, glyphWidth, glyphHeight, bbox, bboy, dwidth); + for (int y = 0; y < glyphHeight; y++) { + line = in.readLine(); + if (line == null) + return false; + for (int b = 0; b < line.length(); b++) { + int v = Integer.parseInt(Character.toString(line.charAt(b)), 16); + for (int x = b * 4, i = 0; i < 4 && x < glyphWidth; x++, i++) { + boolean set = (v & 0x8) != 0; + v <<= 1; + glyph.setPixel(x, y, set); + } + } + } + + font.putGlyph(codePoint, glyph); + return true; + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java' --- util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,135 @@ +package org.gnu.grub.fonttool; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashMap; +import java.util.TreeMap; + +class CharDefs { + private boolean debug = "1".equals(System.getProperty("fonttool.debug")); + private TreeMap glyphs; + private ByteArrayOutputStream charDefsData; + private int maxCharWidth; + private int maxCharHeight; + private HashMap charIndex; + + public CharDefs(Font font) { + this.glyphs = font.getGlyphs(); + this.charIndex = null; + this.charDefsData = null; + + calculateMaxSizes(); + } + + private void calculateMaxSizes() { + maxCharWidth = 0; + maxCharHeight = 0; + for (Glyph glyph : glyphs.values()) { + final int w = glyph.getWidth(); + final int h = glyph.getHeight(); + if (w > maxCharWidth) + maxCharWidth = w; + if (h > maxCharHeight) + maxCharHeight = h; + } + } + + void buildDefinitions() { + charIndex = new HashMap(); + HashMap charDefIndex = new HashMap(); + charDefsData = new ByteArrayOutputStream(); + DataOutputStream charDefs = new DataOutputStream(charDefsData); + try { + // Loop through all the glyphs, writing the glyph data to the + // in-memory byte stream, collapsing duplicate glyphs, and + // constructing index information. + for (Glyph glyph : glyphs.values()) { + CharDef charDef = new CharDef(glyph.getWidth(), + glyph.getHeight(), + glyph.getBitmap()); + + if (charDefIndex.containsKey(charDef)) { + // Use already-written glyph. + if (debug) + System.out.printf("Duplicate glyph for character U+%04X%n", + glyph.getCodePoint()); + final int charOffset = charDefIndex.get(charDef).intValue(); + final CharStorageInfo info = + new CharStorageInfo(glyph.getCodePoint(), charOffset); + charIndex.put(glyph.getCodePoint(), info); + } else { + // Write glyph data. + final int charOffset = charDefs.size(); + final CharStorageInfo info = + new CharStorageInfo(glyph.getCodePoint(), charOffset); + charIndex.put(glyph.getCodePoint(), info); + + charDefIndex.put(charDef, (long) charOffset); + + charDefs.writeShort(glyph.getWidth()); + charDefs.writeShort(glyph.getHeight()); + charDefs.writeShort(glyph.getBbox()); + charDefs.writeShort(glyph.getBboy()); + charDefs.writeShort(glyph.getDeviceWidth()); + charDefs.write(glyph.getBitmap()); + } + } + } catch (IOException e) { + throw new RuntimeException("Error writing to in-memory byte stream", e); + } + } + + public int getMaxCharWidth() { + return maxCharWidth; + } + + public int getMaxCharHeight() { + return maxCharHeight; + } + + public HashMap getCharIndex() { + if (charIndex == null) throw new IllegalStateException(); + return charIndex; + } + + public byte[] getDefinitionData() { + if (charDefsData == null) throw new IllegalStateException(); + return charDefsData.toByteArray(); + } + + + private static class CharDef { + private final int width; + private final int height; + private final byte[] data; + + public CharDef(int width, int height, byte[] data) { + this.width = width; + this.height = height; + this.data = data; + } + + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + CharDef charDef = (CharDef) o; + + if (height != charDef.height) return false; + if (width != charDef.width) return false; + if (!Arrays.equals(data, charDef.data)) return false; + + return true; + } + + public int hashCode() { + int result; + result = width; + result = 31 * result + height; + result = 31 * result + Arrays.hashCode(data); + return result; + } + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/CharMatrix.java' --- util/fonttool/src/org/gnu/grub/fonttool/CharMatrix.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/CharMatrix.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,139 @@ +package org.gnu.grub.fonttool; + +import java.awt.*; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionAdapter; +import java.util.HashMap; +import java.util.Map; +import javax.swing.*; + +public class CharMatrix extends JComponent { + private Font charFont; + private int zoom = 1; + private int startingChar; + private Map glyphLocations = new HashMap(); + private GlyphSelectionListener glyphSelectionListener; + private Point mouseLocation; + + public interface GlyphSelectionListener { + void glyphSelected(int codePoint); + } + + public CharMatrix() { + addMouseListener(new MouseAdapter() { + public void mousePressed(MouseEvent e) { + int codePoint = getCodePointForGlyphAt(e.getPoint()); + if (codePoint != -1) { + if (glyphSelectionListener != null) + glyphSelectionListener.glyphSelected(codePoint); + } + } + + public void mouseExited(MouseEvent e) { + mouseLocation = null; + repaint(); + } + }); + + addMouseMotionListener(new MouseMotionAdapter() { + public void mouseMoved(MouseEvent e) { + mouseLocation = e.getPoint(); + repaint(); + } + }); + } + + public void setGlyphSelectionListener(GlyphSelectionListener listener) { + this.glyphSelectionListener = listener; + } + + public Font getCharFont() { + return charFont; + } + + public void setCharFont(Font font) { + this.charFont = font; + repaint(); + } + + public int getZoom() { + return zoom; + } + + public void setZoom(int zoom) { + this.zoom = zoom; + repaint(); + } + + public int getStartingChar() { + return startingChar; + } + + public void setStartingChar(int startingChar) { + this.startingChar = startingChar; + repaint(); + } + + public int getCodePointForGlyphAt(Point p) { + for (Rectangle rectangle : glyphLocations.keySet()) { + if (rectangle.contains(p)) + return glyphLocations.get(rectangle); + } + return -1; + } + + protected void paintComponent(Graphics g) { + glyphLocations.clear(); + + if (charFont == null) + return; + + int w = getWidth(); + int h = getHeight(); + int maxCharWidth = charFont.getMaxCharWidth(); + int maxCharHeight = charFont.getMaxCharHeight(); + int space = 2; + int charsPerRow = w / (maxCharWidth + space); + int rows = h / (maxCharHeight + space); + + Rectangle highlightBounds = null; + int codePoint = startingChar; + int y = 3 + charFont.getAscent(); // Point y to the baseline. + for (int row = 0; row < rows; row++) { + int x = 3; + for (int col = 0; col < charsPerRow; col++) { + Glyph glyph = charFont.getGlyph(codePoint); + + if (glyph != null) { + Rectangle charBounds = + CharView.drawGlyph(g, charFont, glyph, x, y, zoom, getForeground()); + // Expand the bounding box for a bigger clickable area. + final int boundsPad = 1; + charBounds.x -= boundsPad; + charBounds.y -= boundsPad; + charBounds.width += 2 * boundsPad; + charBounds.height += 2 * boundsPad; + glyphLocations.put(charBounds, codePoint); + if (mouseLocation != null && charBounds.contains(mouseLocation)) { + highlightBounds = charBounds; + } + } + x += maxCharWidth + space; + codePoint++; + } + + y += maxCharHeight + space; + } + + if (highlightBounds != null) { + g.setColor(Color.blue); + Graphics2D g2 = (Graphics2D) g; + g2.setStroke(new BasicStroke(3f, BasicStroke.CAP_SQUARE, BasicStroke.JOIN_MITER, 2f)); + g2.draw(new Rectangle(highlightBounds.x - 2, highlightBounds.y - 2, + highlightBounds.width + 3, highlightBounds.height + 3)); + g2.setStroke(new BasicStroke(1f)); + } + + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/CharStorageInfo.java' --- util/fonttool/src/org/gnu/grub/fonttool/CharStorageInfo.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/CharStorageInfo.java 2008-07-03 14:08:39 +0000 @@ -0,0 +1,19 @@ +package org.gnu.grub.fonttool; + +class CharStorageInfo { + private final int codePoint; + private final int fileOffset; + + public CharStorageInfo(int codePoint, int fileOffset) { + this.codePoint = codePoint; + this.fileOffset = fileOffset; + } + + public int getCodePoint() { + return codePoint; + } + + public int getFileOffset() { + return fileOffset; + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/CharView.java' --- util/fonttool/src/org/gnu/grub/fonttool/CharView.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/CharView.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,101 @@ +package org.gnu.grub.fonttool; + +import java.awt.*; +import java.util.Formatter; +import javax.swing.*; + +public class CharView extends JComponent { + private Font charFont = null; + private Glyph glyph = null; + private int zoom = 1; + + public Font getCharFont() { + return charFont; + } + + public void setCharFont(Font charFont) { + this.charFont = charFont; + } + + public Glyph getGlyph() { + return glyph; + } + + public void setGlyph(Glyph glyph) { + this.glyph = glyph; + repaint(); + } + + public void setZoom(int zoom) { + this.zoom = zoom; + } + + public Dimension getPreferredSize() { + if (glyph == null) + return new Dimension(1, 1); + return new Dimension(glyph.getWidth() * zoom, glyph.getHeight() * zoom); + } + + protected void paintComponent(Graphics g) { + if (glyph == null) { + // Draw a gray 'X' indicating no character selected. + g.setColor(Color.gray); + g.drawLine(0, 0, getWidth() - 1, getHeight() - 1); + g.drawLine(getWidth(), 0, 0, getHeight() - 1); + return; + } + + final int x0 = (getWidth() - glyph.getWidth() * zoom) / 2; + final int y0 = getHeight() / 2; + drawGlyph(g, charFont, glyph, x0, y0, zoom, getForeground()); + + g.setFont(new java.awt.Font("SansSerif", java.awt.Font.PLAIN, 12)); + FontMetrics fm = g.getFontMetrics(); + Formatter f = new Formatter(); + f.format("U+%04X", glyph.getCodePoint()); + String str = f.toString(); + g.drawString(str, + (getWidth() - fm.stringWidth(str)) / 2, + getHeight() - fm.getHeight()); + } + + /** + * Draw the specified glyph with its baseline at y0 and the horizontal + * start at x0. + */ + static Rectangle drawGlyph(Graphics g, Font font, Glyph glyph, int startX, int baselineY, int zoom, Color fg) { + // Adjust (x0, y0) so that it puts the bitmap at the right location. + final int offsetX = zoom * glyph.getBbox(); + final int offsetY = zoom * glyph.getBboy(); + final int bitmapLeft = startX + offsetX; + final int bitmapBottom = baselineY - offsetY; + final int bitmapTop = bitmapBottom - zoom * glyph.getHeight(); + + // Draw a gray box around the glyph bounds. + g.setColor(Color.lightGray); + g.drawRect(bitmapLeft - 1, bitmapTop - 1, + glyph.getWidth() * zoom + 1, zoom * glyph.getHeight() + 1); + + // Draw the baseline + g.setColor(new Color(110, 110, 255)); + g.drawLine(startX, baselineY, startX + zoom * glyph.getDeviceWidth(), baselineY); + + // Draw the glyph. + g.setColor(fg); + byte[] bitmap = glyph.getBitmap(); + int bitIndex = 0; + for (int y = 0; y < glyph.getHeight(); y++) { + for (int x = 0; x < glyph.getWidth(); x++, bitIndex++) { + int byteIndex = bitIndex / 8; + int bitShift = bitIndex % 8; + byte b = bitmap[byteIndex]; + boolean bitValue = (b & (0x80 >>> bitShift)) != 0; + if (bitValue) { + g.fillRect(bitmapLeft + x * zoom, bitmapTop + y * zoom, zoom, zoom); + } + } + } + + return new Rectangle(bitmapLeft, bitmapTop, zoom * glyph.getWidth(), zoom * glyph.getHeight()); + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/Converter.java' --- util/fonttool/src/org/gnu/grub/fonttool/Converter.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/Converter.java 2008-07-03 14:08:39 +0000 @@ -0,0 +1,78 @@ +package org.gnu.grub.fonttool; + +import java.io.IOException; + +/** + * Program to convert BDF fonts into PFF2 fonts for use with GRUB. + */ +public class Converter { + public static void main(String[] args) { + if (args.length < 1) { + printUsageAndExit(); + } + + String in = null; + String out = null; + + try { + for (String arg : args) { + if (arg.startsWith("--")) { + String option; + String value; + int equalsPos = arg.indexOf('='); + if (equalsPos < 0) { + option = arg.substring(2); + value = null; + } else { + option = arg.substring(2, equalsPos); + value = arg.substring(equalsPos + 1); + } + + if ("in".equals(option)) { + if (value == null) + throw new CommandLineException(option + " option requires a value."); + in = value; + } else if ("out".equals(option)) { + if (value == null) + throw new CommandLineException(option + " option requires a value."); + out = value; + } + } else { + throw new CommandLineException("Non-option argument `" + arg + "'."); + } + } + if (in == null || out == null) { + throw new CommandLineException("Both --in=X and --out=Y must be specified."); + } + } catch (CommandLineException e) { + System.err.println("Error: " + e.getMessage()); + System.exit(1); + } + + try { + // Read BDF. + Font font = BDFLoader.loadFontFile(in); + + // Write PFF2. + new PFF2Writer(out).writeFont(font); + } catch (IOException e) { + System.err.println("I/O error converting font: " + e); + e.printStackTrace(); + System.exit(1); + } + } + + private static class CommandLineException extends Exception { + public CommandLineException(String message) { + super(message); + } + } + + private static void printUsageAndExit() { + System.err.println("GNU GRUB Font Conversion Tool"); + System.err.println(); + System.err.println("Usage: Converter --in=IN.bdf --out=OUT.pf2"); + System.err.println(); + System.exit(1); + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/FileFormatException.java' --- util/fonttool/src/org/gnu/grub/fonttool/FileFormatException.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/FileFormatException.java 2008-07-03 14:08:39 +0000 @@ -0,0 +1,9 @@ +package org.gnu.grub.fonttool; + +import java.io.IOException; + +public class FileFormatException extends IOException { + public FileFormatException(String msg) { + super(msg); + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/Font.java' --- util/fonttool/src/org/gnu/grub/fonttool/Font.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/Font.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,106 @@ +package org.gnu.grub.fonttool; + +import java.util.TreeMap; + +public class Font { + public static final int UNKNOWN_POINT_SIZE = -1; + + private TreeMap glyphs; + private String family; + private boolean bold; + private boolean italic; + private int pointSize; + private int maxCharWidth; + private int maxCharHeight; + private int ascent; + private int descent; + + public Font() { + glyphs = new TreeMap(); + } + + public String getFamily() { + return family; + } + + public void setFamily(String family) { + this.family = family; + } + + public boolean isBold() { + return bold; + } + + public void setBold(boolean bold) { + this.bold = bold; + } + + public boolean isItalic() { + return italic; + } + + public void setItalic(boolean italic) { + this.italic = italic; + } + + public int getPointSize() { + return pointSize; + } + + public void setPointSize(int pointSize) { + this.pointSize = pointSize; + } + + public int getMaxCharWidth() { + return maxCharWidth; + } + + public void setMaxCharWidth(int maxCharWidth) { + this.maxCharWidth = maxCharWidth; + } + + public int getMaxCharHeight() { + return maxCharHeight; + } + + public void setMaxCharHeight(int maxCharHeight) { + this.maxCharHeight = maxCharHeight; + } + + public int getAscent() { + return ascent; + } + + public void setAscent(int ascent) { + this.ascent = ascent; + } + + public int getDescent() { + return descent; + } + + public void setDescent(int descent) { + this.descent = descent; + } + + public void putGlyph(int codePoint, Glyph glyph) { + glyphs.put(codePoint, glyph); + } + + public TreeMap getGlyphs() { + return glyphs; + } + + public Glyph getGlyph(int codePoint) { + return glyphs.get(codePoint); + } + + public String getStandardName() { + StringBuilder name = new StringBuilder(getFamily()); + if (isBold()) name.append(" Bold"); + if (isItalic()) name.append(" Italic"); + name.append(' '); + name.append(getPointSize()); + return name.toString(); + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/Glyph.java' --- util/fonttool/src/org/gnu/grub/fonttool/Glyph.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/Glyph.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,83 @@ +package org.gnu.grub.fonttool; + +public class Glyph { + private final int codePoint; + private final int width; + private final int height; + + // These define the amounts to shift the character bitmap by + // before drawing it. See + // http://www.adobe.com/devnet/font/pdfs/5005.BDF_Spec.pdf + // and + // http://www.linuxbabble.com/documentation/x/bdf/ + // for explanatory figures. + private final int bbox; + private final int bboy; + + // Number of pixels to advance horizontally from this character's origin + // to the origin of the next character. + private final int deviceWidth; + + // Row-major order, no padding. Rows can break within a byte. + // MSb is first (leftmost/uppermost) pixel. + private final byte[] bitmap; + + public Glyph(int codePoint, int width, int height, int bbox, int bboy, int deviceWidth) { + this(codePoint, width, height, bbox, bboy, deviceWidth, + new byte[(width * height + 7) / 8]); + } + + public Glyph(int codePoint, int width, int height, + int bbox, int bboy, int deviceWidth, + byte[] bitmap) { + this.codePoint = codePoint; + this.width = width; + this.height = height; + this.bboy = bboy; + this.bbox = bbox; + this.deviceWidth = deviceWidth; + this.bitmap = bitmap; + } + + public void setPixel(int x, int y, boolean value) { + if (x < 0 || y < 0 || x >= width || y >= height) + throw new IllegalArgumentException( + "Invalid pixel location (" + x + ", " + y + ") for " + + width + "x" + height + " glyph"); + + int bitIndex = y * width + x; + int byteIndex = bitIndex / 8; + int bitPos = bitIndex % 8; + int v = value ? 0x80 >>> bitPos : 0; + int mask = ~(0x80 >>> bitPos); + bitmap[byteIndex] = (byte) ((bitmap[byteIndex] & mask) | v); + } + + public int getCodePoint() { + return codePoint; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public int getBbox() { + return bbox; + } + + public int getBboy() { + return bboy; + } + + public int getDeviceWidth() { + return deviceWidth; + } + + public byte[] getBitmap() { + return bitmap; + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/PFF2Loader.java' --- util/fonttool/src/org/gnu/grub/fonttool/PFF2Loader.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/PFF2Loader.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,203 @@ +package org.gnu.grub.fonttool; + +import java.io.EOFException; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; +import java.nio.charset.Charset; + +/** + * Load a PFF2 font completely into memory as an instance of Font. + */ +public class PFF2Loader { + private static final int debug + = Integer.parseInt(System.getProperty("fonttool.debug", "0")); + + private RandomAccessFile f; + private Font font; + + private PFF2Loader(String filename) throws FileNotFoundException { + f = new RandomAccessFile(filename, "r"); + font = new Font(); + } + + public static boolean isPFF2File(String filename) { + RandomAccessFile f = null; + boolean isFont = false; + try { + f = new RandomAccessFile(filename, "r"); + byte[] sectionNameBytes = new byte[4]; + f.readFully(sectionNameBytes); + String sectionName = new String(sectionNameBytes, "US-ASCII"); + if (sectionName.equals(PFF2Sections.FILE)) { + final String expectedType = "PFF2"; + int len = f.readInt(); + if (len == expectedType.length()) { + byte[] typeBytes = new byte[len]; + f.readFully(typeBytes); + String type = new String(typeBytes, "US-ASCII"); + if (type.equals(expectedType)) { + isFont = true; + } + } + } + f.close(); + } catch (IOException e) { + if (f != null) { + try { + f.close(); + } catch (IOException e1) { + // Ignore. + } + } + } + + return isFont; + } + + public static Font loadFontFile(String filename) throws IOException { + PFF2Loader loader = new PFF2Loader(filename); + loader.loadFont(); + return loader.getFont(); + } + + private void loadFont() throws IOException { + String s; + s = openSection(); + if (!PFF2Sections.FILE.equals(s)) + throw new FileFormatException( + "First PFF2 section must be FILE but is `" + s + "'."); + + s = readCompleteSectionAsString(); + if (!"PFF2".equals(s)) + throw new FileFormatException("Unsupported file type `" + s + "'."); + + font.setFamily("Unnamed"); + font.setPointSize(Font.UNKNOWN_POINT_SIZE); + try { + if (debug >= 1) System.out.println("Loading font information..."); + boolean doneLoadingInfo = false; + while (!doneLoadingInfo) { + s = openSection(); + int sectionLength = f.readInt(); + if (debug >= 2) + System.out.println("Section: " + s + " length: " + sectionLength); + if (s.equals(PFF2Sections.MAX_CHAR_WIDTH)) { + int maxCharWidth = readSectionContentsAsUShort(sectionLength); + font.setMaxCharWidth(maxCharWidth); + } else if (s.equals(PFF2Sections.MAX_CHAR_HEIGHT)) { + int maxCharHeight = readSectionContentsAsUShort(sectionLength); + font.setMaxCharHeight(maxCharHeight); + } else if (s.equals(PFF2Sections.FONT_POINT_SIZE)) { + int pointSize = readSectionContentsAsUShort(sectionLength); + font.setPointSize(pointSize); + } else if (s.equals(PFF2Sections.FONT_ASCENT)) { + int ascent = readSectionContentsAsUShort(sectionLength); + font.setAscent(ascent); + } else if (s.equals(PFF2Sections.FONT_DESCENT)) { + int descent = readSectionContentsAsUShort(sectionLength); + font.setDescent(descent); + } else if (s.equals(PFF2Sections.FONT_NAME)) { + String fontName = readSectionContentsAsString(sectionLength); + if (debug >= 1) + System.out.println("Loaded font: " + fontName); + } else if (s.equals(PFF2Sections.FONT_FAMILY)) { + font.setFamily(readSectionContentsAsString(sectionLength)); + } else if (s.equals(PFF2Sections.FONT_WEIGHT)) { + String weightString = readSectionContentsAsString(sectionLength); + font.setBold(weightString.equalsIgnoreCase("bold")); + } else if (s.equals(PFF2Sections.FONT_SLANT)) { + String slantString = readSectionContentsAsString(sectionLength); + font.setItalic(slantString.equalsIgnoreCase("italic")); + } else if (s.equals(PFF2Sections.CHAR_INDEX)) { + loadCharsFromIndex(sectionLength); + } else if (s.equals(PFF2Sections.REMAINDER_IS_DATA)) { + doneLoadingInfo = true; + } else { + // If the section is not handled, then we just skip it. + if (f.skipBytes(sectionLength) != sectionLength) { + System.err.println("Warning: Could not skip " + + sectionLength + + " bytes for section `" + s + "'."); + } + } + } + } catch (EOFException eofException) { + // End of file; done reading font. + } + f.close(); + } + + private void loadCharsFromIndex(int sectionLength) throws IOException { + long indexSectionEnd = f.getFilePointer() + sectionLength; + try { + while (f.getFilePointer() < indexSectionEnd) { + int codePoint = f.readInt(); + int storageFlags = f.readUnsignedByte(); + int offset = f.readInt(); + + if ((storageFlags & 0x07) == 0) { + Glyph g = loadUncompressedGlyph(codePoint, offset); + font.putGlyph(codePoint, g); + } + } + } catch (EOFException eofException) { + // End of file; simple return. + } + } + + private Glyph loadUncompressedGlyph(int codePoint, int fileOffset) throws IOException { + if (debug >= 3) + System.out.printf("Loading uncompressed glyph U+%04X at %d%n", codePoint, fileOffset); + + long savedPos = f.getFilePointer(); + + f.seek(fileOffset); + int width = f.readUnsignedShort(); + int height = f.readUnsignedShort(); + int offsetX = f.readShort(); + int offsetY = f.readShort(); + int deviceWidth = f.readShort(); + if (debug >= 3) System.out.println(" Size=" + width + "x" + height + + " offset=(" + offsetX + ", " + offsetY + + ") dWidth=" + deviceWidth); + byte[] bitmap = new byte[(width * height + 7) / 8]; + f.readFully(bitmap); + + f.seek(savedPos); + return new Glyph(codePoint, width, height, + offsetX, offsetY, deviceWidth, bitmap); + } + + private int readSectionContentsAsUShort(int sectionLength) throws IOException { + if (sectionLength != 2) + throw new FileFormatException( + "For uint16 section, expected 2 bytes but length is " + + sectionLength + "."); + return f.readUnsignedShort(); + } + + private String openSection() throws IOException { + byte[] b = new byte[4]; + f.readFully(b); + return new String(b, Charset.forName("US-ASCII")); + } + + private String readCompleteSectionAsString() throws IOException { + int n = f.readInt(); + return readSectionContentsAsString(n); + } + + private String readSectionContentsAsString(int n) throws IOException { + StringBuilder buf = new StringBuilder(); + while (n-- != 0) { + char c = (char) (f.readByte() & 0xFF); + buf.append(c); + } + return buf.toString(); + } + + public Font getFont() { + return font; + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java' --- util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,19 @@ +package org.gnu.grub.fonttool; + +/** + * Section name constants for the PFF2 file format. + */ +public class PFF2Sections { + static final String FILE = "FILE"; + static final String FONT_NAME = "NAME"; + static final String FONT_FAMILY = "FAMI"; + static final String FONT_WEIGHT = "WEIG"; + static final String FONT_SLANT = "SLAN"; + static final String FONT_POINT_SIZE = "PTSZ"; + static final String MAX_CHAR_WIDTH = "MAXW"; + static final String MAX_CHAR_HEIGHT = "MAXH"; + static final String FONT_ASCENT = "ASCE"; + static final String FONT_DESCENT = "DESC"; + static final String CHAR_INDEX = "CHIX"; + static final String REMAINDER_IS_DATA = "DATA"; +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java' --- util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,133 @@ +package org.gnu.grub.fonttool; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.RandomAccessFile; + +// TODO Add DEFLATE compressed blocks of characters. +public class PFF2Writer { + private RandomAccessFile f; + private String currentSection; + private long currentSectionStart; + + public PFF2Writer(String filename) throws FileNotFoundException { + this.f = new RandomAccessFile(filename, "rw"); + this.currentSection = null; + this.currentSectionStart = -1; + } + + public void writeFont(Font font) throws IOException { + // Write file type ID header. + writeSection(PFF2Sections.FILE, "PFF2"); + + writeSection(PFF2Sections.FONT_NAME, font.getStandardName()); + writeSection(PFF2Sections.FONT_FAMILY, font.getFamily()); + writeSection(PFF2Sections.FONT_WEIGHT, font.isBold() ? "bold" : "normal"); + writeSection(PFF2Sections.FONT_SLANT, font.isItalic() ? "italic" : "normal"); + if (font.getPointSize() != Font.UNKNOWN_POINT_SIZE) + writeShortSection(PFF2Sections.FONT_POINT_SIZE, font.getPointSize()); + + // Construct character definitions. + CharDefs charDefs = new CharDefs(font); + charDefs.buildDefinitions(); + + // Write max character width and height metrics. + writeShortSection(PFF2Sections.MAX_CHAR_WIDTH, charDefs.getMaxCharWidth()); + writeShortSection(PFF2Sections.MAX_CHAR_HEIGHT, charDefs.getMaxCharHeight()); + writeShortSection(PFF2Sections.FONT_ASCENT, font.getAscent()); + writeShortSection(PFF2Sections.FONT_DESCENT, font.getDescent()); + + // Write character index with pointers to the character definitions. + beginSection(PFF2Sections.CHAR_INDEX); + + // Determine the size of the index, so we can properly refer to the + // character definition offset in the index. The actual number of + // bytes written is compared to the calculated value to ensure we + // are correct. + final int indexStart = (int) f.getFilePointer(); + final int calculatedIndexLength = + font.getGlyphs().size() * (4 + 1 + 4); + final int charDefStart = indexStart + calculatedIndexLength + 8; + + for (CharStorageInfo storageInfo : charDefs.getCharIndex().values()) { + f.writeInt(storageInfo.getCodePoint()); + f.writeByte(0); // Storage flags: bits 1..0 = 00b : uncompressed. + f.writeInt(charDefStart + storageInfo.getFileOffset()); + } + + final int indexEnd = (int) f.getFilePointer(); + if (indexEnd - indexStart != calculatedIndexLength) { + throw new RuntimeException("Incorrect index length calculated, calc=" + + calculatedIndexLength + + " actual=" + (indexEnd - indexStart)); + } + endSection(PFF2Sections.CHAR_INDEX); + + f.writeBytes(PFF2Sections.REMAINDER_IS_DATA); + f.writeInt(-1); // Data takes up the rest of the file. + f.write(charDefs.getDefinitionData()); + + f.close(); + } + + private void beginSection(String sectionName) throws IOException { + verifyOkToBeginSection(sectionName); + + f.writeBytes(sectionName); + f.writeInt(-1); // Placeholder for the section length. + currentSection = sectionName; + currentSectionStart = f.getFilePointer(); + } + + private void endSection(String sectionName) throws IOException { + verifyOkToEndSection(sectionName); + + long sectionEnd = f.getFilePointer(); + long sectionLength = sectionEnd - currentSectionStart; + f.seek(currentSectionStart - 4); + f.writeInt((int) sectionLength); + f.seek(sectionEnd); + currentSection = null; + currentSectionStart = -1; + } + + private void verifyOkToBeginSection(String sectionName) { + if (sectionName.length() != 4) + throw new IllegalArgumentException( + "Section names must be 4 characters: `" + sectionName + "'."); + if (currentSection != null) + throw new IllegalStateException( + "Attempt to start `" + sectionName + + "' section before ending the previous section `" + + currentSection + "'."); + } + + private void verifyOkToEndSection(String sectionName) { + if (sectionName.length() != 4) + throw new IllegalStateException("Invalid section name '" + sectionName + + "'; must be 4 characters."); + if (currentSection == null) + throw new IllegalStateException( + "Attempt to end section `" + sectionName + + "' when no section active."); + if (!sectionName.equals(currentSection)) + throw new IllegalStateException( + "Attempt to end `" + sectionName + + "' section during active section `" + + currentSection + "'."); + } + + private void writeSection(String sectionName, String contents) throws IOException { + verifyOkToBeginSection(sectionName); + f.writeBytes(sectionName); + f.writeInt(contents.length()); + f.writeBytes(contents); + } + + private void writeShortSection(String sectionName, int value) throws IOException { + verifyOkToBeginSection(sectionName); + f.writeBytes(sectionName); + f.writeInt(2); + f.writeShort(value); + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/Viewer.java' --- util/fonttool/src/org/gnu/grub/fonttool/Viewer.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/Viewer.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,135 @@ +package org.gnu.grub.fonttool; + +import java.io.File; +import java.io.IOException; +import javax.swing.*; + +/** + * Program to view fonts. + *

+ * Supports PFF2 and BDF format font files. + */ +public class Viewer { + public static void main(String[] args) { + if (args.length < 1) { + printUsageAndExit(); + } + + boolean verbose = false; + String format = null; + String filename = null; + + try { + for (String arg : args) { + if (arg.startsWith("--")) { + String option; + String value; + int equalsPos = arg.indexOf('='); + if (equalsPos < 0) { + option = arg.substring(2); + value = null; + } else { + option = arg.substring(2, equalsPos); + value = arg.substring(equalsPos + 1); + } + + if ("format".equals(option)) { + if (value == null) + throw new CommandLineException(option + " option requires a value."); + format = value; + } else if ("verbose".equals(option)) { + verbose = true; + } + } else { + if (filename == null) { + filename = arg; + } else { + throw new CommandLineException( + "Extra argument after filename: `" + arg + "'."); + } + } + } + if (filename == null) { + throw new CommandLineException("A filename must be specified."); + } + if (!(format == null || "bdf".equals(format) || "pff2".equals(format))) { + throw new CommandLineException("Invalid format: must be `bdf' or `pff2'."); + } + } catch (CommandLineException e) { + System.err.println("Error: " + e.getMessage()); + System.exit(1); + } + + if (!new File(filename).exists()) { + System.err.println("Error: File `" + filename + "' not found."); + System.exit(1); + } + + if (format == null) { + // Autodetect the format. + if (PFF2Loader.isPFF2File(filename)) + format = "pff2"; + else if (BDFLoader.isBDFFile(filename)) + format = "bdf"; + else { + System.err.println("Error: Unable to detect font file format " + + "for `" + filename + "'."); + System.exit(1); + } + if (verbose) + System.out.println("Detected file format `" + format + "'."); + } + + Font font = null; + try { + if ("bdf".equals(format)) { + if (verbose) + System.out.println("Loading BDF font."); + font = BDFLoader.loadFontFile(filename); + } else if ("pff2".equals(format)) { + if (verbose) + System.out.println("Loading PFF2 font."); + font = PFF2Loader.loadFontFile(filename); + } else { + System.err.println("Error: Bad format `" + format + "'"); + System.exit(1); + } + } catch (IOException e) { + System.err.println("I/O error loading font: " + e); + e.printStackTrace(); + System.exit(1); + } + + if (font == null) { + System.err.println("Error: Font not loaded."); + System.exit(1); + } + + if (verbose) { + System.out.println("Loaded font: '" + font.getStandardName() + "'"); + } + + ViewerFrame f = new ViewerFrame(font); + f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + f.setLocationRelativeTo(null); // Center the frame on screen. + f.setVisible(true); + } + + private static class CommandLineException extends Exception { + public CommandLineException(String message) { + super(message); + } + } + + private static void printUsageAndExit() { + System.err.println("GNU GRUB Font Viewer"); + System.err.println(); + System.err.println("Usage: Viewer [--format=FORMAT] FILE"); + System.err.println(" --format=bdf The font is in BDF format."); + System.err.println(" --format=pff2 The font is in PFF2 format."); + System.err.println(" If no --format option is given, the type will be"); + System.err.println(" automatically detected."); + System.err.println(); + System.exit(1); + } +} === added file 'util/fonttool/src/org/gnu/grub/fonttool/ViewerFrame.java' --- util/fonttool/src/org/gnu/grub/fonttool/ViewerFrame.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/src/org/gnu/grub/fonttool/ViewerFrame.java 2008-07-03 14:08:39 +0000 @@ -0,0 +1,61 @@ +package org.gnu.grub.fonttool; + +import java.awt.*; +import javax.swing.*; +import javax.swing.border.BevelBorder; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +public class ViewerFrame extends JFrame { + private final Font font; + + private JLabel startingCodePointLabel; + private JSlider startingCodePointSlider; + private CharMatrix charMatrix; + private CharView charView; + private JLabel statusBar; + + public ViewerFrame(Font font) { + this.font = font; + + createComponents(); + buildUI(); + setSize(800, 600); + } + + private void createComponents() { + startingCodePointLabel = new JLabel("Starting Code Point"); + startingCodePointSlider = new JSlider(JSlider.HORIZONTAL, 0, 65535, 0); + startingCodePointSlider.addChangeListener(new ChangeListener() { + public void stateChanged(ChangeEvent e) { + charMatrix.setStartingChar(startingCodePointSlider.getValue()); + } + }); + charMatrix = new CharMatrix(); + charMatrix.setCharFont(font); + charMatrix.setGlyphSelectionListener(new CharMatrix.GlyphSelectionListener() { + public void glyphSelected(int codePoint) { + charView.setGlyph(font.getGlyph(codePoint)); + } + }); + charView = new CharView(); + charView.setZoom(3); + charView.setBorder(new BevelBorder(BevelBorder.RAISED)); + statusBar = new JLabel(); + } + + private void buildUI() { + JComponent topPanel = Box.createVerticalBox(); + topPanel.add(startingCodePointLabel); + topPanel.add(startingCodePointSlider); + + JComponent charPanel = new JPanel(new GridLayout(2, 1)); + charPanel.add(charMatrix); + charPanel.add(charView); + + setLayout(new BorderLayout()); + add(topPanel, BorderLayout.NORTH); + add(charPanel, BorderLayout.CENTER); + add(statusBar, BorderLayout.SOUTH); + } +} === added directory 'util/fonttool/test' === added directory 'util/fonttool/test/org' === added directory 'util/fonttool/test/org/gnu' === added directory 'util/fonttool/test/org/gnu/grub' === added directory 'util/fonttool/test/org/gnu/grub/fonttool' === added file 'util/fonttool/test/org/gnu/grub/fonttool/TestGlyph.java' --- util/fonttool/test/org/gnu/grub/fonttool/TestGlyph.java 1970-01-01 00:00:00 +0000 +++ util/fonttool/test/org/gnu/grub/fonttool/TestGlyph.java 2008-07-03 14:10:25 +0000 @@ -0,0 +1,110 @@ +package org.gnu.grub.fonttool; + +import org.junit.Assert; +import org.junit.Test; + +public class TestGlyph { + @Test + public void testConstructor3x5() { + Glyph g = new Glyph(65, 3, 5, 0, 0, 0); + + // Should be empty upon construction. + assertBitmapEquals("..." + + "..." + + "..." + + "..." + + "...", + g.getBitmap()); + + // Should have ceil(3 * 5 / 8) = ceil(15 / 8) = ceil(1 + 7/8) = 2 bytes. + Assert.assertEquals("Wrong bitmap array size.", + 2, g.getBitmap().length); + } + + @Test + public void testConstructor5x5() { + Glyph g = new Glyph(65, 5, 5, 0, 0, 0); + + // Should be empty upon construction. + assertBitmapEquals("....." + + "....." + + "....." + + "....." + + ".....", + g.getBitmap()); + + // Should have ceil(5 * 5 / 8) = ceil(25 / 8) = ceil(3 + 1/8) = 4 bytes. + Assert.assertEquals("Wrong bitmap array size.", + 4, g.getBitmap().length); + } + + @Test + public void testSetPixel() { + Glyph g = new Glyph(65, 3, 5, 0, 0, 0); + + g.setPixel(0, 0, true); + Assert.assertEquals(0x80, g.getBitmap()[0] & 0xFF); + Assert.assertEquals(0x00, g.getBitmap()[1] & 0xFF); + assertBitmapEquals("X.." + + "..." + + "..." + + "..." + + "...", + g.getBitmap()); + + g.setPixel(2, 1, true); + Assert.assertEquals(0x84, g.getBitmap()[0] & 0xFF); + Assert.assertEquals(0x00, g.getBitmap()[1] & 0xFF); + assertBitmapEquals("X.." + + "..X" + + "..." + + "..." + + "...", + g.getBitmap()); + } + + static void assertBitmapEquals(String expectedBitmap, byte[] actualBitmap) { + Assert.assertNotNull("expected bitmap null", expectedBitmap); + Assert.assertNotNull("actual bitmap null", actualBitmap); + StringBuilder errors = new StringBuilder(); + + int bit = 7; + int byteIndex = -1; + for (int bitIndex = 0; bitIndex < expectedBitmap.length(); bitIndex++) { + char c = expectedBitmap.charAt(bitIndex); + boolean expectedBit; + if (c == '.') { + expectedBit = false; + } else if (c == 'X') { + expectedBit = true; + } else { + throw new IllegalArgumentException("Bad character '" + c + "' in expected bitmap string"); + } + + byteIndex = bitIndex / 8; + byte b = actualBitmap[byteIndex]; + boolean actualBit = (b & (1 << bit)) != 0; + if (actualBit != expectedBit) { + errors.append("[").append(bitIndex) + .append("] wrong (it is ") + .append(actualBit ? "1" : "0").append("). "); + } + + if (bit == 0) { + bit = 7; + } else { + bit--; + } + } + + if (byteIndex != expectedBitmap.length() / 8) { + errors.append("Last byte index wrong, was ") + .append(byteIndex).append(" but expected ") + .append(expectedBitmap.length() - 1); + } + + if (errors.length() != 0) { + Assert.fail("Bitmap wrong: " + errors); + } + } +} === modified file 'util/i386/pc/grub-mkrescue.in' --- util/i386/pc/grub-mkrescue.in 2008-07-12 14:40:50 +0000 +++ util/i386/pc/grub-mkrescue.in 2008-07-19 21:39:24 +0000 @@ -71,7 +71,7 @@ --modules=*) modules=`echo "$option" | sed 's/--modules=//'` ;; --overlay=*) - overlay=`echo "$option" | sed 's/--overlay=//'` ;; + overlay=${overlay}${overlay:+ }`echo "$option" | sed 's/--overlay=//'` ;; --pkglibdir=*) input_dir=`echo "$option" | sed 's/--pkglibdir=//'` ;; --grub-mkimage=*) @@ -124,9 +124,10 @@ echo "insmod $i" done > ${aux_dir}/boot/grub/grub.cfg -if test "x$overlay" = x ; then : ; else - cp -dpR ${overlay}/* ${aux_dir}/ -fi +for d in ${overlay}; do + echo "Overlaying $d" + cp -vdpR ${d}/* ${aux_dir}/ +done if [ "x${image_type}" = xfloppy -o "x${emulation}" = xfloppy ] ; then # build memdisk === modified file 'video/bitmap.c' --- video/bitmap.c 2007-07-21 22:32:33 +0000 +++ video/bitmap.c 2008-07-03 13:49:18 +0000 @@ -78,7 +78,7 @@ switch (blit_format) { - case GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8: + case GRUB_VIDEO_BLIT_FORMAT_RGBA_8888: mode_info->mode_type = GRUB_VIDEO_MODE_TYPE_RGB | GRUB_VIDEO_MODE_TYPE_ALPHA; mode_info->bpp = 32; @@ -94,7 +94,7 @@ mode_info->reserved_field_pos = 24; break; - case GRUB_VIDEO_BLIT_FORMAT_R8G8B8: + case GRUB_VIDEO_BLIT_FORMAT_RGB_888: mode_info->mode_type = GRUB_VIDEO_MODE_TYPE_RGB; mode_info->bpp = 24; mode_info->bytes_per_pixel = 3; === added file 'video/bitmap_scale.c' --- video/bitmap_scale.c 1970-01-01 00:00:00 +0000 +++ video/bitmap_scale.c 2008-08-03 01:30:02 +0000 @@ -0,0 +1,101 @@ +/* bitmap_scale.c - Bitmap scaling interface. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007 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 +#include + +/* + * This function creates a new scaled version of the bitmap SRC. The new + * bitmap has dimensions DST_WIDTH by DST_HEIGHT. The scaling algorithm + * is given by SCALE_METHOD. If an error is encountered, the return code is + * not equal to GRUB_ERR_NONE, and the bitmap DST is either not created, or + * it is destroyed before this function returns. + * + * Supports only direct color modes which have components separated + * into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + * But because of this simplifying assumption, the implementation is + * greatly simplified. + */ +grub_err_t +grub_video_bitmap_create_scaled (struct grub_video_bitmap **dst, + int dst_width, int dst_height, + struct grub_video_bitmap *src, + enum grub_video_bitmap_scale_method + scale_method) +{ + *dst = 0; + + /* Verify the simplifying assumptions. */ + if (src == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "null src bitmap in grub_video_bitmap_create_scaled"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "src format not supported for scale"); + if (src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension"); + if (dst_width <= 0 || dst_height <= 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "requested to scale to a size w/ a zero dimension"); + if (src->mode_info.bytes_per_pixel * 8 != src->mode_info.bpp) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "bitmap to scale has inconsistent Bpp and bpp"); + + /* Create the new bitmap. */ + grub_err_t ret; + ret = grub_video_bitmap_create (dst, dst_width, dst_height, + src->mode_info.blit_format); + if (ret != GRUB_ERR_NONE) + return ret; /* Error. */ + + switch (scale_method) + { + case GRUB_VIDEO_BITMAP_SCALE_METHOD_FASTEST: + case GRUB_VIDEO_BITMAP_SCALE_METHOD_NEAREST: + ret = grub_video_bitmap_scale_nn (*dst, src); + break; + case GRUB_VIDEO_BITMAP_SCALE_METHOD_BEST: + case GRUB_VIDEO_BITMAP_SCALE_METHOD_BILINEAR: + ret = grub_video_bitmap_scale_bilinear (*dst, src); + break; + default: + ret = grub_error (GRUB_ERR_BAD_ARGUMENT, "Invalid scale_method value"); + break; + } + + if (ret == GRUB_ERR_NONE) + { + /* Success: *dst is now a pointer to the scaled bitmap. */ + return GRUB_ERR_NONE; + } + else + { + /* Destroy the bitmap and return the error code. */ + grub_video_bitmap_destroy (*dst); + *dst = 0; + return ret; + } +} === added file 'video/bitmap_scale_bilinear.c' --- video/bitmap_scale_bilinear.c 1970-01-01 00:00:00 +0000 +++ video/bitmap_scale_bilinear.c 2008-07-03 14:28:15 +0000 @@ -0,0 +1,145 @@ +/* bitmap_scale_bilinear.c - Bilinear image scaling algorithm. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007 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 +#include + +/* + * Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the + * dimensions of DST. This function uses the bilinear interpolation algorithm + * to interpolate the pixels. + * + * Supports only direct color modes which have components separated + * into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + * But because of this simplifying assumption, the implementation is + * greatly simplified. + */ +grub_err_t +grub_video_bitmap_scale_bilinear (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src) +{ + /* Verify the simplifying assumptions. */ + if (dst == 0 || src == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale func"); + if (dst->mode_info.red_field_pos % 8 != 0 + || dst->mode_info.green_field_pos % 8 != 0 + || dst->mode_info.blue_field_pos % 8 != 0 + || dst->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported"); + if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos + || dst->mode_info.red_mask_size != src->mode_info.red_mask_size + || dst->mode_info.green_field_pos != src->mode_info.green_field_pos + || dst->mode_info.green_mask_size != src->mode_info.green_mask_size + || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos + || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size + || dst->mode_info.reserved_field_pos != + src->mode_info.reserved_field_pos + || dst->mode_info.reserved_mask_size != + src->mode_info.reserved_mask_size) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.width == 0 || dst->mode_info.height == 0 + || src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension"); + + grub_uint8_t *ddata = dst->data; + grub_uint8_t *sdata = src->data; + int dw = dst->mode_info.width; + int dh = dst->mode_info.height; + int sw = src->mode_info.width; + int sh = src->mode_info.height; + int dstride = dst->mode_info.pitch; + int sstride = src->mode_info.pitch; + /* bytes_per_pixel is the same for both src and dst. */ + int bytes_per_pixel = dst->mode_info.bytes_per_pixel; + + int dy; + for (dy = 0; dy < dh; dy++) + { + int dx; + for (dx = 0; dx < dw; dx++) + { + grub_uint8_t *dptr; + grub_uint8_t *sptr; + int sx; + int sy; + int comp; + + /* Compute the source coordinate that the destination coordinate + * maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */ + sx = sw * dx / dw; + sy = sh * dy / dh; + + /* Get the address of the pixels in src and dst. */ + dptr = ddata + dy * dstride + dx * bytes_per_pixel; + sptr = sdata + sy * sstride + sx * bytes_per_pixel; + + /* If we have enough space to do so, use bilinear interpolation. + * Otherwise, fall back to nearest neighbor for this pixel. */ + if (sx < sw - 1 && sy < sh - 1) + { + /* Do bilinear interpolation. */ + + /* Fixed-point .8 numbers representing the fraction of the + * distance in the x (u) and y (v) direction within the + * box of 4 pixels in the source. */ + int u = (256 * sw * dx / dw) - (sx * 256); + int v = (256 * sh * dy / dh) - (sy * 256); + + for (comp = 0; comp < bytes_per_pixel; comp++) + { + /* Get the component's values for the + * 4 source corner pixels. */ + grub_uint8_t f00 = sptr[comp]; + grub_uint8_t f10 = sptr[comp + bytes_per_pixel]; + grub_uint8_t f01 = sptr[comp + sstride]; + grub_uint8_t f11 = sptr[comp + sstride + bytes_per_pixel]; + + /* Do linear interpolations along the top and bottom + * rows of the box. */ + grub_uint8_t f0y = (256 - v) * f00 / 256 + v * f01 / 256; + grub_uint8_t f1y = (256 - v) * f10 / 256 + v * f11 / 256; + + /* Interpolate vertically. */ + grub_uint8_t fxy = (256 - u) * f0y / 256 + u * f1y / 256; + + dptr[comp] = fxy; + } + } + else + { + /* Fall back to nearest neighbor interpolation. */ + /* Copy the pixel color value. */ + for (comp = 0; comp < bytes_per_pixel; comp++) + dptr[comp] = sptr[comp]; + } + } + } + return GRUB_ERR_NONE; +} === added file 'video/bitmap_scale_nn.c' --- video/bitmap_scale_nn.c 1970-01-01 00:00:00 +0000 +++ video/bitmap_scale_nn.c 2008-07-03 14:27:43 +0000 @@ -0,0 +1,109 @@ +/* bitmap_scale_nn.c - Nearest neighbor image scaling algorithm. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007 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 +#include + +/* + * Copy the bitmap SRC to the bitmap DST, scaling the bitmap to fit the + * dimensions of DST. This function uses the nearest neighbor algorithm to + * interpolate the pixels. + * + * Supports only direct color modes which have components separated + * into bytes (e.g., RGBA 8:8:8:8 or BGR 8:8:8 true color). + * But because of this simplifying assumption, the implementation is + * greatly simplified. + */ +grub_err_t +grub_video_bitmap_scale_nn (struct grub_video_bitmap *dst, + struct grub_video_bitmap *src) +{ + /* Verify the simplifying assumptions. */ + if (dst == 0 || src == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "null bitmap in scale_nn"); + if (dst->mode_info.red_field_pos % 8 != 0 + || dst->mode_info.green_field_pos % 8 != 0 + || dst->mode_info.blue_field_pos % 8 != 0 + || dst->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst format not supported"); + if (src->mode_info.red_field_pos % 8 != 0 + || src->mode_info.green_field_pos % 8 != 0 + || src->mode_info.blue_field_pos % 8 != 0 + || src->mode_info.reserved_field_pos % 8 != 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "src format not supported"); + if (dst->mode_info.red_field_pos != src->mode_info.red_field_pos + || dst->mode_info.red_mask_size != src->mode_info.red_mask_size + || dst->mode_info.green_field_pos != src->mode_info.green_field_pos + || dst->mode_info.green_mask_size != src->mode_info.green_mask_size + || dst->mode_info.blue_field_pos != src->mode_info.blue_field_pos + || dst->mode_info.blue_mask_size != src->mode_info.blue_mask_size + || dst->mode_info.reserved_field_pos != + src->mode_info.reserved_field_pos + || dst->mode_info.reserved_mask_size != + src->mode_info.reserved_mask_size) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.bytes_per_pixel != src->mode_info.bytes_per_pixel) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "dst and src not compatible"); + if (dst->mode_info.width == 0 || dst->mode_info.height == 0 + || src->mode_info.width == 0 || src->mode_info.height == 0) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "bitmap has a zero dimension"); + + grub_uint8_t *ddata = dst->data; + grub_uint8_t *sdata = src->data; + int dw = dst->mode_info.width; + int dh = dst->mode_info.height; + int sw = src->mode_info.width; + int sh = src->mode_info.height; + int dstride = dst->mode_info.pitch; + int sstride = src->mode_info.pitch; + /* bytes_per_pixel is the same for both src and dst. */ + int bytes_per_pixel = dst->mode_info.bytes_per_pixel; + + int dy; + for (dy = 0; dy < dh; dy++) + { + int dx; + for (dx = 0; dx < dw; dx++) + { + grub_uint8_t *dptr; + grub_uint8_t *sptr; + int sx; + int sy; + int comp; + + /* Compute the source coordinate that the destination coordinate + * maps to. Note: sx/sw = dx/dw => sx = sw*dx/dw. */ + sx = sw * dx / dw; + sy = sh * dy / dh; + + /* Get the address of the pixels in src and dst. */ + dptr = ddata + dy * dstride + dx * bytes_per_pixel; + sptr = sdata + sy * sstride + sx * bytes_per_pixel; + + /* Copy the pixel color value. */ + for (comp = 0; comp < bytes_per_pixel; comp++) + dptr[comp] = sptr[comp]; + } + } + return GRUB_ERR_NONE; +} === modified file 'video/i386/pc/vbe.c' --- video/i386/pc/vbe.c 2008-01-21 15:48:27 +0000 +++ video/i386/pc/vbe.c 2008-07-19 19:42:37 +0000 @@ -63,6 +63,7 @@ static struct { struct grub_video_render_target render_target; + int is_double_buffered; /* Is the video mode double buffered? */ unsigned int bytes_per_scan_line; unsigned int bytes_per_pixel; @@ -77,6 +78,24 @@ static grub_uint32_t mode_in_use = 0x55aa; static grub_uint16_t *mode_list; +static struct +{ + grub_size_t page_size; /* The size of a page in bytes. */ + + /* For page flipping strategy. */ + int displayed_page; /* The page # that is the front buffer. */ + int render_page; /* The page # that is the back buffer. */ + + /* For blit strategy. */ + grub_uint8_t *offscreen_buffer; + + /* Virtual functions. */ + int (*update_screen) (void); + int (*destroy) (void); +} doublebuf_state; + +static void double_buffering_init (void); + static void * real2pm (grub_vbe_farptr_t ptr) { @@ -376,6 +395,7 @@ /* Reset frame buffer and render target variables. */ grub_memset (&framebuffer, 0, sizeof(framebuffer)); render_target = &framebuffer.render_target; + grub_memset (&doublebuf_state, 0, sizeof(doublebuf_state)); return GRUB_ERR_NONE; } @@ -391,6 +411,9 @@ /* TODO: Decide, is this something we want to do. */ return grub_errno; + if (doublebuf_state.destroy) + doublebuf_state.destroy(); + /* TODO: Free any resources allocated by driver. */ grub_free (mode_list); mode_list = 0; @@ -533,9 +556,13 @@ render_target->viewport.width = active_mode_info.x_resolution; render_target->viewport.height = active_mode_info.y_resolution; - /* Set framebuffer pointer and mark it as non allocated. */ + /* Mark framebuffer memory as non allocated. */ render_target->is_allocated = 0; - render_target->data = framebuffer.ptr; + + /* Set up double buffering information. */ + framebuffer.is_double_buffered = + ((mode_type & GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED) != 0); + double_buffering_init (); /* Copy default palette to initialize emulated palette. */ for (i = 0; @@ -556,6 +583,166 @@ return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "no matching mode found."); } +/* + Set framebuffer render target page and display the proper page, based on + `doublebuf_state.render_page' and `doublebuf_state.displayed_page', + respectively. + + Returns 0 upon success, nonzero upon failure. + */ +static int +doublebuf_pageflipping_commit (void) +{ + /* Set the render target's data pointer to the start of the render_page. */ + framebuffer.render_target.data = + ((char *) framebuffer.ptr) + + doublebuf_state.page_size * doublebuf_state.render_page; + + /* Tell the video adapter to display the new front page. */ + int display_start_line = + framebuffer.render_target.mode_info.height + * doublebuf_state.displayed_page; + + grub_vbe_status_t vbe_err = + grub_vbe_bios_set_display_start (0, display_start_line); + if (vbe_err != GRUB_VBE_STATUS_OK) + return 1; + + return 0; +} + +static int +doublebuf_pageflipping_update_screen (void) +{ + /* Swap the page numbers in the framebuffer struct. */ + int new_displayed_page = doublebuf_state.render_page; + doublebuf_state.render_page = doublebuf_state.displayed_page; + doublebuf_state.displayed_page = new_displayed_page; + + return doublebuf_pageflipping_commit (); +} + +static int +doublebuf_pageflipping_destroy (void) +{ + doublebuf_state.update_screen = 0; + doublebuf_state.destroy = 0; + return 0; +} + +static int +doublebuf_pageflipping_init (void) +{ + doublebuf_state.page_size = + framebuffer.bytes_per_scan_line * render_target->mode_info.height; + + /* Get video RAM size in bytes. */ + grub_size_t vram_size = controller_info.total_memory << 16; + + if (2 * doublebuf_state.page_size > vram_size) + return 1; /* Not enough video memory for 2 pages. */ + + doublebuf_state.displayed_page = 0; + doublebuf_state.render_page = 1; + + doublebuf_state.update_screen = doublebuf_pageflipping_update_screen; + doublebuf_state.destroy = doublebuf_pageflipping_destroy; + + /* Set the framebuffer memory data pointer and display the right page. */ + if (doublebuf_pageflipping_commit () != GRUB_ERR_NONE) + return 1; /* Unable to set the display start. */ + + framebuffer.render_target.mode_info.mode_type + |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + return 0; +} + +static int +doublebuf_blit_update_screen (void) +{ + grub_memcpy (framebuffer.ptr, + doublebuf_state.offscreen_buffer, + doublebuf_state.page_size); + return 0; +} + +static int +doublebuf_blit_destroy (void) +{ + grub_free (doublebuf_state.offscreen_buffer); + doublebuf_state.offscreen_buffer = 0; + + doublebuf_state.update_screen = 0; + doublebuf_state.destroy = 0; + return 0; +} + +static int +doublebuf_blit_init (void) +{ + doublebuf_state.page_size = + framebuffer.bytes_per_scan_line * render_target->mode_info.height; + + doublebuf_state.offscreen_buffer = (grub_uint8_t *) + grub_malloc (doublebuf_state.page_size); + if (doublebuf_state.offscreen_buffer == 0) + return 1; /* Error. */ + + framebuffer.render_target.data = doublebuf_state.offscreen_buffer; + doublebuf_state.update_screen = doublebuf_blit_update_screen; + doublebuf_state.destroy = doublebuf_blit_destroy; + + framebuffer.render_target.mode_info.mode_type + |= GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + return 0; +} + +static int +doublebuf_null_update_screen (void) +{ + return 0; +} + +static int +doublebuf_null_destroy (void) +{ + doublebuf_state.update_screen = 0; + doublebuf_state.destroy = 0; + return 0; +} + +static int +doublebuf_null_init (void) +{ + framebuffer.render_target.data = framebuffer.ptr; + doublebuf_state.update_screen = doublebuf_null_update_screen; + doublebuf_state.destroy = doublebuf_null_destroy; + + framebuffer.render_target.mode_info.mode_type + &= ~GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED; + return 0; +} + +/* Select the best double buffering mode available. */ +static void +double_buffering_init (void) +{ + if (doublebuf_state.destroy) + doublebuf_state.destroy(); + + if (framebuffer.is_double_buffered) + { + if (doublebuf_pageflipping_init () == 0) + return; + + if (doublebuf_blit_init () == 0) + return; + } + + /* Fall back to no double buffering. */ + doublebuf_null_init (); +} + static grub_err_t grub_video_vbe_get_info (struct grub_video_mode_info *mode_info) { @@ -708,6 +895,16 @@ return minindex; } + else if ((render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) + { + if (red == render_target->mode_info.fg_red + && green == render_target->mode_info.fg_green + && blue == render_target->mode_info.fg_blue) + return 1; + else + return 0; + } else { grub_uint32_t value; @@ -737,6 +934,17 @@ /* No alpha available in index color modes, just use same value as in only RGB modes. */ return grub_video_vbe_map_rgb (red, green, blue); + else if ((render_target->mode_info.mode_type + & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) + { + if (red == render_target->mode_info.fg_red + && green == render_target->mode_info.fg_green + && blue == render_target->mode_info.fg_blue + && alpha == render_target->mode_info.fg_alpha) + return 1; + else + return 0; + } else { grub_uint32_t value; @@ -797,6 +1005,24 @@ *alpha = framebuffer.palette[color].a; return; } + else if ((mode_info->mode_type + & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0) + { + if (color & 1) + { + *red = mode_info->fg_red; + *green = mode_info->fg_green; + *blue = mode_info->fg_blue; + *alpha = mode_info->fg_alpha; + } + else + { + *red = mode_info->bg_red; + *green = mode_info->bg_green; + *blue = mode_info->bg_blue; + *alpha = mode_info->bg_alpha; + } + } else { grub_uint32_t tmp; @@ -876,17 +1102,24 @@ target.data = render_target->data; /* Try to figure out more optimized version. */ - if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8) - { - grub_video_i386_vbefill_R8G8B8A8 (&target, color, x, y, - width, height); - return GRUB_ERR_NONE; - } - - if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8) - { - grub_video_i386_vbefill_R8G8B8 (&target, color, x, y, - width, height); + if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_i386_vbefill_direct32 (&target, color, x, y, + width, height); + return GRUB_ERR_NONE; + } + + if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + grub_video_i386_vbefill_direct32 (&target, color, x, y, + width, height); + return GRUB_ERR_NONE; + } + + if (target.mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + grub_video_i386_vbefill_direct24 (&target, color, x, y, + width, height); return GRUB_ERR_NONE; } @@ -903,76 +1136,6 @@ return GRUB_ERR_NONE; } -// TODO: Remove this method and replace with bitmap based glyphs -static grub_err_t -grub_video_vbe_blit_glyph (struct grub_font_glyph * glyph, - grub_video_color_t color, int x, int y) -{ - struct grub_video_i386_vbeblit_info target; - unsigned int width; - unsigned int charwidth; - unsigned int height; - unsigned int i; - unsigned int j; - unsigned int x_offset = 0; - unsigned int y_offset = 0; - - /* Make sure there is something to do. */ - if (x >= (int)render_target->viewport.width) - return GRUB_ERR_NONE; - - if (y >= (int)render_target->viewport.height) - return GRUB_ERR_NONE; - - /* Calculate glyph dimensions. */ - width = ((glyph->width + 7) / 8) * 8; - charwidth = width; - height = glyph->height; - - if (x + (int)width < 0) - return GRUB_ERR_NONE; - - if (y + (int)height < 0) - return GRUB_ERR_NONE; - - /* Do not allow drawing out of viewport. */ - if (x < 0) - { - width += x; - x_offset = (unsigned int)-x; - x = 0; - } - if (y < 0) - { - height += y; - y_offset = (unsigned int)-y; - y = 0; - } - - if ((x + width) > render_target->viewport.width) - width = render_target->viewport.width - x; - if ((y + height) > render_target->viewport.height) - height = render_target->viewport.height - y; - - /* Add viewport offset. */ - x += render_target->viewport.x; - y += render_target->viewport.y; - - /* Use vbeblit_info to encapsulate rendering. */ - target.mode_info = &render_target->mode_info; - target.data = render_target->data; - - /* Draw glyph. */ - for (j = 0; j < height; j++) - for (i = 0; i < width; i++) - if ((glyph->bitmap[((i + x_offset) / 8) - + (j + y_offset) * (charwidth / 8)] - & (1 << ((charwidth - (i + x_offset) - 1) % 8)))) - set_pixel (&target, x+i, y+j, color); - - return GRUB_ERR_NONE; -} - /* NOTE: This function assumes that given coordinates are within bounds of handled data. */ static void @@ -985,19 +1148,35 @@ if (oper == GRUB_VIDEO_BLIT_REPLACE) { /* Try to figure out more optimized version for replace operator. */ - if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8) + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) { - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8) - { - grub_video_i386_vbeblit_R8G8B8X8_R8G8B8X8 (target, source, + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + grub_video_i386_vbeblit_direct32_copy (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_i386_vbeblit_BGRX8888_RGBX8888 (target, source, x, y, width, height, offset_x, offset_y); return; } - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8) - { - grub_video_i386_vbeblit_R8G8B8_R8G8B8X8 (target, source, + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) + { + grub_video_i386_vbeblit_BGR888_RGBX8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + grub_video_i386_vbeblit_RGB888_RGBX8888 (target, source, x, y, width, height, offset_x, offset_y); return; @@ -1005,26 +1184,42 @@ if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) { - grub_video_i386_vbeblit_index_R8G8B8X8 (target, source, + grub_video_i386_vbeblit_index_RGBX8888 (target, source, x, y, width, height, offset_x, offset_y); return; } } - if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8) + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) { - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8) - { - grub_video_i386_vbeblit_R8G8B8A8_R8G8B8 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8) - { - grub_video_i386_vbeblit_R8G8B8_R8G8B8 (target, source, + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_i386_vbeblit_BGRA8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + grub_video_i386_vbeblit_RGBA8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) + { + grub_video_i386_vbeblit_BGR888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + grub_video_i386_vbeblit_RGB888_RGB888 (target, source, x, y, width, height, offset_x, offset_y); return; @@ -1032,13 +1227,24 @@ if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) { - grub_video_i386_vbeblit_index_R8G8B8 (target, source, + grub_video_i386_vbeblit_index_RGB888 (target, source, x, y, width, height, offset_x, offset_y); return; } } + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_i386_vbeblit_direct32_copy (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + } + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) { if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) @@ -1057,19 +1263,35 @@ else { /* Try to figure out more optimized blend operator. */ - if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8) + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) { - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8) - { - grub_video_i386_vbeblit_R8G8B8A8_R8G8B8A8 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8) - { - grub_video_i386_vbeblit_R8G8B8_R8G8B8A8 (target, source, + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_i386_vbeblit_BGRA8888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + grub_video_i386_vbeblit_RGBA8888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) + { + grub_video_i386_vbeblit_BGR888_RGBA8888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + grub_video_i386_vbeblit_RGB888_RGBA8888 (target, source, x, y, width, height, offset_x, offset_y); return; @@ -1077,26 +1299,42 @@ if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) { - grub_video_i386_vbeblit_index_R8G8B8A8 (target, source, + grub_video_i386_vbeblit_index_RGBA8888 (target, source, x, y, width, height, offset_x, offset_y); return; } } - if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8) + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) { - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8) - { - grub_video_i386_vbeblit_R8G8B8A8_R8G8B8 (target, source, - x, y, width, height, - offset_x, offset_y); - return; - } - - if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_R8G8B8) - { - grub_video_i386_vbeblit_R8G8B8_R8G8B8 (target, source, + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGRA_8888) + { + grub_video_i386_vbeblit_BGRA8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGBA_8888) + { + grub_video_i386_vbeblit_RGBA8888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_BGR_888) + { + grub_video_i386_vbeblit_BGR888_RGB888 (target, source, + x, y, width, height, + offset_x, offset_y); + return; + } + + if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_RGB_888) + { + grub_video_i386_vbeblit_RGB888_RGB888 (target, source, x, y, width, height, offset_x, offset_y); return; @@ -1104,7 +1342,7 @@ if (target->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR) { - grub_video_i386_vbeblit_index_R8G8B8 (target, source, + grub_video_i386_vbeblit_index_RGB888 (target, source, x, y, width, height, offset_x, offset_y); return; @@ -1219,6 +1457,77 @@ return GRUB_ERR_NONE; } +/* + * Draw the specified glyph at (x, y). The y coordinate designates the + * baseline of the character, while the x coordinate designates the left + * side location of the character. + */ +static grub_err_t +grub_video_vbe_blit_glyph (struct grub_font_glyph *glyph, + grub_video_color_t color, + int left_x, int baseline_y) +{ + struct grub_video_bitmap glyph_bitmap; + + /* Don't try to draw empty glyphs (U+0020, etc.). */ + if (glyph->width == 0 || glyph->height == 0) + return GRUB_ERR_NONE; + + glyph_bitmap.mode_info.width = glyph->width; + glyph_bitmap.mode_info.height = glyph->height; + glyph_bitmap.mode_info.mode_type = + (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) + | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP; + glyph_bitmap.mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED; + glyph_bitmap.mode_info.bpp = 1; + glyph_bitmap.mode_info.bytes_per_pixel = 0; /* Really 1 bit per pixel. */ + glyph_bitmap.mode_info.pitch = glyph->width; /* Packed densely as bits. */ + glyph_bitmap.mode_info.number_of_colors = 2; + glyph_bitmap.mode_info.bg_red = 0; + glyph_bitmap.mode_info.bg_green = 0; + glyph_bitmap.mode_info.bg_blue = 0; + glyph_bitmap.mode_info.bg_alpha = 0; + grub_video_vbe_unmap_color(color, + &glyph_bitmap.mode_info.fg_red, + &glyph_bitmap.mode_info.fg_green, + &glyph_bitmap.mode_info.fg_blue, + &glyph_bitmap.mode_info.fg_alpha); + glyph_bitmap.data = glyph->bitmap; + + int bitmap_left = left_x + glyph->offset_x; + int bitmap_bottom = baseline_y - glyph->offset_y; + int bitmap_top = bitmap_bottom - glyph->height; + + return grub_video_vbe_blit_bitmap (&glyph_bitmap, GRUB_VIDEO_BLIT_BLEND, + bitmap_left, bitmap_top, + 0, 0, + glyph->width, glyph->height); +} + +static grub_err_t +grub_video_vbe_draw_string (const char *str, grub_font_t font, + grub_video_color_t color, + int left_x, int baseline_y) +{ + grub_size_t len; + grub_size_t i; + int x; + struct grub_font_glyph *glyph; + + len = grub_strlen (str); + x = left_x; + for (i = 0; i < len; i++) + { + glyph = grub_font_get_glyph (font, str[i]); + if (grub_video_vbe_blit_glyph (glyph, color, x, baseline_y) + != GRUB_ERR_NONE) + return grub_errno; + x += glyph->device_width; + } + + return GRUB_ERR_NONE; +} + static grub_err_t grub_video_vbe_blit_render_target (struct grub_video_render_target *source, enum grub_video_blit_operators oper, @@ -1403,10 +1712,13 @@ return GRUB_ERR_NONE; } -static grub_err_t +static grub_err_t grub_video_vbe_swap_buffers (void) { - /* TODO: Implement buffer swapping. */ + if (doublebuf_state.update_screen () != 0) + return grub_error (GRUB_ERR_INVALID_COMMAND, + "Double buffer update failed"); + return GRUB_ERR_NONE; } @@ -1505,17 +1817,13 @@ static grub_err_t grub_video_vbe_set_active_render_target (struct grub_video_render_target *target) { - if (target == GRUB_VIDEO_RENDER_TARGET_FRONT_BUFFER) + if (target == GRUB_VIDEO_RENDER_TARGET_DISPLAY) { render_target = &framebuffer.render_target; - + return GRUB_ERR_NONE; } - if (target == GRUB_VIDEO_RENDER_TARGET_BACK_BUFFER) - return grub_error (GRUB_ERR_NOT_IMPLEMENTED_YET, - "double buffering not implemented yet."); - if (! target->data) return grub_error (GRUB_ERR_BAD_ARGUMENT, "invalid render target given."); @@ -1551,6 +1859,7 @@ .unmap_color = grub_video_vbe_unmap_color, .fill_rect = grub_video_vbe_fill_rect, .blit_glyph = grub_video_vbe_blit_glyph, + .draw_string = grub_video_vbe_draw_string, .blit_bitmap = grub_video_vbe_blit_bitmap, .blit_render_target = grub_video_vbe_blit_render_target, .scroll = grub_video_vbe_scroll, === modified file 'video/i386/pc/vbeblit.c' --- video/i386/pc/vbeblit.c 2008-01-01 12:02:07 +0000 +++ video/i386/pc/vbeblit.c 2008-07-09 17:27:53 +0000 @@ -35,7 +35,343 @@ #include void -grub_video_i386_vbeblit_R8G8B8A8_R8G8B8A8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbeblit_BGRX8888_RGBX8888 (struct grub_video_i386_vbeblit_info + *dst, + struct grub_video_i386_vbeblit_info + *src, int x, int y, int width, + int height, int offset_x, + int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned srcrowskip; + unsigned dstrowskip; + + /* Calculate the number of bytes to advance from the end of one line + * to the beginning of the next line. */ + srcrowskip = + src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = + dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = (grub_uint8_t *) get_data_ptr (src, offset_x, offset_y); + dstptr = (grub_uint8_t *) get_data_ptr (dst, x, y); + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint8_t r = *srcptr++; + grub_uint8_t g = *srcptr++; + grub_uint8_t b = *srcptr++; + grub_uint8_t a = *srcptr++; + + *dstptr++ = b; + *dstptr++ = g; + *dstptr++ = r; + *dstptr++ = a; + } + srcptr += srcrowskip; + dstptr += dstrowskip; + } +} + +void +grub_video_i386_vbeblit_BGRA8888_RGB888 (struct grub_video_i386_vbeblit_info + *dst, + struct grub_video_i386_vbeblit_info + *src, int x, int y, int width, + int height, int offset_x, + int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned srcrowskip; + unsigned dstrowskip; + + /* Calculate the number of bytes to advance from the end of one line + * to the beginning of the next line. */ + srcrowskip = + src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = + dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = (grub_uint8_t *) get_data_ptr (src, offset_x, offset_y); + dstptr = (grub_uint8_t *) get_data_ptr (dst, x, y); + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint8_t r = *srcptr++; + grub_uint8_t g = *srcptr++; + grub_uint8_t b = *srcptr++; + + *dstptr++ = b; + *dstptr++ = g; + *dstptr++ = r; + *dstptr++ = 255; /* Alpha component: Set opaque. */ + } + srcptr += srcrowskip; + dstptr += dstrowskip; + } +} + +void +grub_video_i386_vbeblit_BGR888_RGB888 (struct grub_video_i386_vbeblit_info + *dst, + struct grub_video_i386_vbeblit_info + *src, int x, int y, int width, + int height, int offset_x, + int offset_y) +{ + int i; + int j; + grub_uint8_t *srcptr; + grub_uint8_t *dstptr; + unsigned srcrowskip; + unsigned dstrowskip; + + /* Calculate the number of bytes to advance from the end of one line + * to the beginning of the next line. */ + srcrowskip = + src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = + dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = (grub_uint8_t *) get_data_ptr (src, offset_x, offset_y); + dstptr = (grub_uint8_t *) get_data_ptr (dst, x, y); + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint8_t r = *srcptr++; + grub_uint8_t g = *srcptr++; + grub_uint8_t b = *srcptr++; + + *dstptr++ = b; + *dstptr++ = g; + *dstptr++ = r; + } + srcptr += srcrowskip; + dstptr += dstrowskip; + } +} + +void +grub_video_i386_vbeblit_BGRA8888_RGBA8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t *srcptr; + grub_uint32_t *dstptr; + unsigned srcrowskip; + unsigned dstrowskip; + int i; + int j; + + /* Calculate the number of bytes to advance from the end of one line + * to the beginning of the next line. */ + srcrowskip = + src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = + dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = (grub_uint32_t *) get_data_ptr (src, offset_x, offset_y); + dstptr = (grub_uint32_t *) get_data_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint32_t color; + unsigned int sr; + unsigned int sg; + unsigned int sb; + unsigned int a; + unsigned int dr; + unsigned int dg; + unsigned int db; + + color = *srcptr++; + + a = color >> 24; + + if (a == 0) + { + /* Skip transparent source pixels. */ + dstptr++; + continue; + } + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + + if (a == 255) + { + /* Opaque pixel shortcut. */ + dr = sr; + dg = sg; + db = sb; + } + else + { + /* General pixel color blending. */ + color = *dstptr; + + dr = (color >> 16) & 0xFF; + dr = (dr * (255 - a) + sr * a) / 255; + dg = (color >> 8) & 0xFF; + dg = (dg * (255 - a) + sg * a) / 255; + db = (color >> 0) & 0xFF; + db = (db * (255 - a) + sb * a) / 255; + } + + color = (a << 24) | (dr << 16) | (dg << 8) | db; + + *dstptr++ = color; + } + + srcptr = (grub_uint32_t *) (((grub_uint8_t *) srcptr) + srcrowskip); + dstptr = (grub_uint32_t *) (((grub_uint8_t *) dstptr) + dstrowskip); + } +} + +void +grub_video_i386_vbeblit_BGR888_RGBA8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t *srcptr; + grub_uint8_t *dstptr; + unsigned srcrowskip; + unsigned dstrowskip; + int i; + int j; + + /* Calculate the number of bytes to advance from the end of one line + * to the beginning of the next line. */ + srcrowskip = + src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = + dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = (grub_uint32_t *) get_data_ptr (src, offset_x, offset_y); + dstptr = (grub_uint8_t *) get_data_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint32_t color; + unsigned int sr; + unsigned int sg; + unsigned int sb; + unsigned int a; + unsigned int dr; + unsigned int dg; + unsigned int db; + + color = *srcptr++; + + a = color >> 24; + + if (a == 0) + { + /* Skip transparent source pixels. */ + dstptr += 3; + continue; + } + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + + if (a == 255) + { + /* Opaque pixel shortcut. */ + dr = sr; + dg = sg; + db = sb; + } + else + { + /* General pixel color blending. */ + color = *dstptr; + + db = dstptr[0]; + db = (db * (255 - a) + sb * a) / 255; + dg = dstptr[1]; + dg = (dg * (255 - a) + sg * a) / 255; + dr = dstptr[2]; + dr = (dr * (255 - a) + sr * a) / 255; + } + + *dstptr++ = db; + *dstptr++ = dg; + *dstptr++ = dr; + } + + srcptr = (grub_uint32_t *) (((grub_uint8_t *) srcptr) + srcrowskip); + dstptr += dstrowskip; + } +} + +void +grub_video_i386_vbeblit_BGR888_RGBX8888 (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y) +{ + grub_uint32_t *srcptr; + grub_uint8_t *dstptr; + unsigned srcrowskip; + unsigned dstrowskip; + int i; + int j; + + /* Calculate the number of bytes to advance from the end of one line + * to the beginning of the next line. */ + srcrowskip = + src->mode_info->pitch - src->mode_info->bytes_per_pixel * width; + dstrowskip = + dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + srcptr = (grub_uint32_t *) get_data_ptr (src, offset_x, offset_y); + dstptr = (grub_uint8_t *) get_data_ptr (dst, x, y); + + for (j = 0; j < height; j++) + { + for (i = 0; i < width; i++) + { + grub_uint32_t color; + grub_uint8_t sr; + grub_uint8_t sg; + grub_uint8_t sb; + + color = *srcptr++; + + sr = (color >> 0) & 0xFF; + sg = (color >> 8) & 0xFF; + sb = (color >> 16) & 0xFF; + + *dstptr++ = sb; + *dstptr++ = sg; + *dstptr++ = sr; + } + + srcptr = (grub_uint32_t *) (((grub_uint8_t *) srcptr) + srcrowskip); + dstptr += dstrowskip; + } +} + +void +grub_video_i386_vbeblit_RGBA8888_RGBA8888 (struct grub_video_i386_vbeblit_info *dst, struct grub_video_i386_vbeblit_info *src, int x, int y, int width, int height, int offset_x, int offset_y) @@ -101,10 +437,10 @@ } void -grub_video_i386_vbeblit_R8G8B8X8_R8G8B8X8 (struct grub_video_i386_vbeblit_info *dst, - struct grub_video_i386_vbeblit_info *src, - int x, int y, int width, int height, - int offset_x, int offset_y) +grub_video_i386_vbeblit_direct32_copy (struct grub_video_i386_vbeblit_info *dst, + struct grub_video_i386_vbeblit_info *src, + int x, int y, int width, int height, + int offset_x, int offset_y) { int j; grub_uint32_t *srcptr; @@ -126,7 +462,7 @@ } void -grub_video_i386_vbeblit_R8G8B8_R8G8B8A8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbeblit_RGB888_RGBA8888 (struct grub_video_i386_vbeblit_info *dst, struct grub_video_i386_vbeblit_info *src, int x, int y, int width, int height, int offset_x, int offset_y) @@ -193,7 +529,7 @@ } void -grub_video_i386_vbeblit_R8G8B8_R8G8B8X8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbeblit_RGB888_RGBX8888 (struct grub_video_i386_vbeblit_info *dst, struct grub_video_i386_vbeblit_info *src, int x, int y, int width, int height, int offset_x, int offset_y) @@ -231,7 +567,7 @@ } void -grub_video_i386_vbeblit_index_R8G8B8A8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbeblit_index_RGBA8888 (struct grub_video_i386_vbeblit_info *dst, struct grub_video_i386_vbeblit_info *src, int x, int y, int width, int height, int offset_x, int offset_y) @@ -295,7 +631,7 @@ } void -grub_video_i386_vbeblit_index_R8G8B8X8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbeblit_index_RGBX8888 (struct grub_video_i386_vbeblit_info *dst, struct grub_video_i386_vbeblit_info *src, int x, int y, int width, int height, int offset_x, int offset_y) @@ -332,7 +668,7 @@ } void -grub_video_i386_vbeblit_R8G8B8A8_R8G8B8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbeblit_RGBA8888_RGB888 (struct grub_video_i386_vbeblit_info *dst, struct grub_video_i386_vbeblit_info *src, int x, int y, int width, int height, int offset_x, int offset_y) @@ -368,7 +704,7 @@ } void -grub_video_i386_vbeblit_R8G8B8_R8G8B8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbeblit_RGB888_RGB888 (struct grub_video_i386_vbeblit_info *dst, struct grub_video_i386_vbeblit_info *src, int x, int y, int width, int height, int offset_x, int offset_y) @@ -393,7 +729,7 @@ } void -grub_video_i386_vbeblit_index_R8G8B8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbeblit_index_RGB888 (struct grub_video_i386_vbeblit_info *dst, struct grub_video_i386_vbeblit_info *src, int x, int y, int width, int height, int offset_x, int offset_y) === modified file 'video/i386/pc/vbefill.c' --- video/i386/pc/vbefill.c 2007-07-21 22:32:33 +0000 +++ video/i386/pc/vbefill.c 2008-07-03 13:49:18 +0000 @@ -34,51 +34,63 @@ #include void -grub_video_i386_vbefill_R8G8B8A8 (struct grub_video_i386_vbeblit_info *dst, +grub_video_i386_vbefill_direct32 (struct grub_video_i386_vbeblit_info *dst, grub_video_color_t color, int x, int y, int width, int height) { int i; int j; grub_uint32_t *dstptr; - - /* We do not need to worry about data being out of bounds - as we assume that everything has been checked before. */ + grub_size_t rowskip; + + /* Calculate the number of bytes to advance from the end of one line + * to the beginning of the next line. */ + rowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + /* Get the start address. */ + dstptr = (grub_uint32_t *) grub_video_vbe_get_video_ptr (dst, x, y); for (j = 0; j < height; j++) { - dstptr = (grub_uint32_t *)grub_video_vbe_get_video_ptr (dst, x, y + j); - for (i = 0; i < width; i++) *dstptr++ = color; + + /* Advance the dest pointer to the right location on the next line. */ + dstptr = (grub_uint32_t *) (((char *) dstptr) + rowskip); } } void -grub_video_i386_vbefill_R8G8B8 (struct grub_video_i386_vbeblit_info *dst, - grub_video_color_t color, int x, int y, - int width, int height) +grub_video_i386_vbefill_direct24 (struct grub_video_i386_vbeblit_info *dst, + grub_video_color_t color, int x, int y, + int width, int height) { int i; int j; + grub_size_t rowskip; grub_uint8_t *dstptr; - grub_uint8_t fillr = (grub_uint8_t)((color >> 0) & 0xFF); - grub_uint8_t fillg = (grub_uint8_t)((color >> 8) & 0xFF); - grub_uint8_t fillb = (grub_uint8_t)((color >> 16) & 0xFF); + grub_uint8_t fill0 = (grub_uint8_t)((color >> 0) & 0xFF); + grub_uint8_t fill1 = (grub_uint8_t)((color >> 8) & 0xFF); + grub_uint8_t fill2 = (grub_uint8_t)((color >> 16) & 0xFF); - /* We do not need to worry about data being out of bounds - as we assume that everything has been checked before. */ + /* Calculate the number of bytes to advance from the end of one line + * to the beginning of the next line. */ + rowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + /* Get the start address. */ + dstptr = (grub_uint8_t *) grub_video_vbe_get_video_ptr (dst, x, y); for (j = 0; j < height; j++) { - dstptr = (grub_uint8_t *)grub_video_vbe_get_video_ptr (dst, x, y + j); - for (i = 0; i < width; i++) { - *dstptr++ = fillr; - *dstptr++ = fillg; - *dstptr++ = fillb; + *dstptr++ = fill0; + *dstptr++ = fill1; + *dstptr++ = fill2; } + + /* Advance the dest pointer to the right location on the next line. */ + dstptr += rowskip; } } @@ -89,18 +101,24 @@ { int i; int j; + grub_size_t rowskip; grub_uint8_t *dstptr; grub_uint8_t fill = (grub_uint8_t)color & 0xFF; - /* We do not need to worry about data being out of bounds - as we assume that everything has been checked before. */ + /* Calculate the number of bytes to advance from the end of one line + * to the beginning of the next line. */ + rowskip = dst->mode_info->pitch - dst->mode_info->bytes_per_pixel * width; + + /* Get the start address. */ + dstptr = (grub_uint8_t *) grub_video_vbe_get_video_ptr (dst, x, y); for (j = 0; j < height; j++) { - dstptr = (grub_uint8_t *)grub_video_vbe_get_video_ptr (dst, x, y + j); - for (i = 0; i < width; i++) *dstptr++ = fill; + + /* Advance the dest pointer to the right location on the next line. */ + dstptr += rowskip; } } @@ -112,9 +130,6 @@ int i; int j; - /* We do not need to worry about data being out of bounds - as we assume that everything has been checked before. */ - for (j = 0; j < height; j++) for (i = 0; i < width; i++) set_pixel (dst, x+i, y+j, color); === modified file 'video/i386/pc/vbeutil.c' --- video/i386/pc/vbeutil.c 2007-07-21 22:32:33 +0000 +++ video/i386/pc/vbeutil.c 2008-07-03 14:10:25 +0000 @@ -52,6 +52,11 @@ + y * source->mode_info->pitch + x; break; + + /* case 1: */ + /* For 1-bit bitmaps, addressing needs to be done at the bit level + * and it doesn't make sense, in general, to ask for a pointer + * to a particular pixel's data. */ } return ptr; @@ -86,6 +91,17 @@ color = *(grub_uint8_t *)get_data_ptr (source, x, y); break; + case 1: + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED) + { + int bit_index = y * source->mode_info->width + x; + grub_uint8_t *ptr = (grub_uint8_t *)source->data + + bit_index / 8; + int bit_pos = 7 - bit_index % 8; + color = (*ptr >> bit_pos) & 0x01; + } + break; + default: break; } @@ -143,6 +159,17 @@ } break; + case 1: + if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED) + { + int bit_index = y * source->mode_info->width + x; + grub_uint8_t *ptr = (grub_uint8_t *)source->data + + bit_index / 8; + int bit_pos = 7 - bit_index % 8; + *ptr = (*ptr & ~(1 << bit_pos)) | ((color & 0x01) << bit_pos); + } + break; + default: break; } === modified file 'video/readers/jpeg.c' --- video/readers/jpeg.c 2008-08-01 03:06:55 +0000 +++ video/readers/jpeg.c 2008-08-03 03:57:46 +0000 @@ -1,20 +1,20 @@ /* - * GRUB -- GRand Unified Bootloader - * Copyright (C) 2008 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 . - */ +* GRUB -- GRand Unified Bootloader +* Copyright (C) 2008 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 @@ -542,7 +542,7 @@ if (grub_video_bitmap_create (data->bitmap, data->image_width, data->image_height, - GRUB_VIDEO_BLIT_FORMAT_R8G8B8)) + GRUB_VIDEO_BLIT_FORMAT_RGB_888)) return grub_errno; data->bit_mask = 0x0; === modified file 'video/readers/png.c' --- video/readers/png.c 2008-08-01 03:06:55 +0000 +++ video/readers/png.c 2008-08-03 04:00:22 +0000 @@ -231,7 +231,7 @@ { if (grub_video_bitmap_create (data->bitmap, data->image_width, data->image_height, - GRUB_VIDEO_BLIT_FORMAT_R8G8B8)) + GRUB_VIDEO_BLIT_FORMAT_RGB_888)) return grub_errno; data->bpp = 3; } @@ -239,7 +239,7 @@ { if (grub_video_bitmap_create (data->bitmap, data->image_width, data->image_height, - GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8)) + GRUB_VIDEO_BLIT_FORMAT_RGBA_8888)) return grub_errno; data->bpp = 4; } === modified file 'video/readers/tga.c' --- video/readers/tga.c 2008-08-01 03:06:55 +0000 +++ video/readers/tga.c 2008-08-03 03:57:46 +0000 @@ -397,7 +397,7 @@ { grub_video_bitmap_create (bitmap, header.image_width, header.image_height, - GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8); + GRUB_VIDEO_BLIT_FORMAT_RGBA_8888); if (grub_errno != GRUB_ERR_NONE) { grub_file_close (file); @@ -420,7 +420,7 @@ { grub_video_bitmap_create (bitmap, header.image_width, header.image_height, - GRUB_VIDEO_BLIT_FORMAT_R8G8B8); + GRUB_VIDEO_BLIT_FORMAT_RGB_888); if (grub_errno != GRUB_ERR_NONE) { grub_file_close (file); === added file 'video/setmode.c' --- video/setmode.c 1970-01-01 00:00:00 +0000 +++ video/setmode.c 2008-07-19 19:31:46 +0000 @@ -0,0 +1,249 @@ +/* video/setmode.c - Smart video mode selection based on preferences. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2006,2007,2008 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 + +/* Set the video mode based on the preferred modes specified in MODE_LIST in + the form: x[x][;...] + + For example: 640x480;800x600x8;400x300x32 + + If MODE_LIST is null, or no modes in it are usable, then DEFAULT_WIDTH and + DEFAULT_HEIGHT are used to set the mode. The MODE_FLAGS argument determines + the video mode flags such as double buffering that are used. */ + +grub_err_t +grub_video_setup_preferred_mode (const char *mode_list, int mode_flags, + int default_width, int default_height) +{ + int mode_found = 0; + + if (mode_list != NULL) + { + /* Take copy of mode_list as we don't want tat. */ + char *const modes_copy = grub_strdup (mode_list); + if (modes_copy == NULL) + return grub_errno; + + /* Initialize next mode. */ + char *next_mode = modes_copy; + + /* Loop until all modes has been tested out. */ + while ((next_mode != NULL) && !mode_found) + { + /* Use last next_mode as current mode. */ + char *tmp = next_mode; + + int width = -1; + int height = -1; + int depth = -1; + + /* Save position of next mode and separate modes. */ + next_mode = grub_strchr(next_mode, ';'); + if (next_mode) + { + *next_mode = 0; + next_mode++; + } + + /* Skip whitespace. */ + while (grub_isspace (*tmp)) + tmp++; + + /* Initialize token holders. */ + char *current_mode = tmp; + char *param = tmp; + char *value = NULL; + + /* Parse x[x]*/ + + /* Find width value. */ + value = param; + param = grub_strchr(param, 'x'); + if (param == NULL) + { + grub_err_t rc; + + /* First setup error message. */ + rc = grub_error (GRUB_ERR_BAD_ARGUMENT, + "Invalid mode: %s\n", + current_mode); + + /* Free memory before returning. */ + grub_free (modes_copy); + + return rc; + } + + *param = 0; + param++; + + width = grub_strtoul (value, 0, 0); + if (grub_errno != GRUB_ERR_NONE) + { + grub_err_t rc; + + /* First setup error message. */ + rc = grub_error (GRUB_ERR_BAD_ARGUMENT, + "Invalid mode: %s\n", + current_mode); + + /* Free memory before returning. */ + grub_free (modes_copy); + + return rc; + } + + /* Find height value. */ + value = param; + param = grub_strchr(param, 'x'); + if (param == NULL) + { + height = grub_strtoul (value, 0, 0); + if (grub_errno != GRUB_ERR_NONE) + { + grub_err_t rc; + + /* First setup error message. */ + rc = grub_error (GRUB_ERR_BAD_ARGUMENT, + "Invalid mode: %s\n", + current_mode); + + /* Free memory before returning. */ + grub_free (modes_copy); + + return rc; + } + } + else + { + /* We have optional color depth value. */ + *param = 0; + param++; + + height = grub_strtoul (value, 0, 0); + if (grub_errno != GRUB_ERR_NONE) + { + grub_err_t rc; + + /* First setup error message. */ + rc = grub_error (GRUB_ERR_BAD_ARGUMENT, + "Invalid mode: %s\n", + current_mode); + + /* Free memory before returning. */ + grub_free (modes_copy); + + return rc; + } + + /* Convert color depth value. */ + value = param; + depth = grub_strtoul (value, 0, 0); + if (grub_errno != GRUB_ERR_NONE) + { + grub_err_t rc; + + /* First setup error message. */ + rc = grub_error (GRUB_ERR_BAD_ARGUMENT, + "Invalid mode: %s\n", + current_mode); + + /* Free memory before returning. */ + grub_free (modes_copy); + + return rc; + } + } + + /* Try out video mode. */ + + int flags = mode_flags; + /* If we have <= 8 bits, assume it is an indexed color mode. */ + if ((depth <= 8) && (depth != -1)) + flags |= GRUB_VIDEO_MODE_TYPE_INDEX_COLOR; + + /* We have > 8 bits; assume that it is RGB color mode. */ + if (depth > 8) + flags |= GRUB_VIDEO_MODE_TYPE_RGB; + + /* If user requested specific depth, pass the request to driver. */ + if (depth != -1) + flags |= (depth << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) + & GRUB_VIDEO_MODE_TYPE_DEPTH_MASK; + + /* Try to initialize requested mode. Ignore any errors. */ + grub_error_push (); + if (grub_video_setup (width, height, flags) != GRUB_ERR_NONE) + { + grub_error_pop (); + continue; + } + + /* Figure out what mode we ended up. */ + struct grub_video_mode_info mode_info; + if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE) + { + /* Couldn't get video mode info, restore old mode + and continue to next one. */ + grub_error_pop (); + + grub_video_restore (); + continue; + } + + /* Restore state of error stack. */ + grub_error_pop (); + + /* Mode found! Exit loop. */ + mode_found = 1; + } + + /* Free memory. */ + grub_free (modes_copy); + } + + if (!mode_found) + { + /* No gfxmode variable set, or no listed mode was supported. + Use the caller-specified defaults. */ + int flags = mode_flags | GRUB_VIDEO_MODE_TYPE_RGB; + + /* Initialize user requested mode. */ + if (grub_video_setup (default_width, default_height, flags) + != GRUB_ERR_NONE) + return grub_errno; + + /* Figure out what mode we ended up. */ + struct grub_video_mode_info mode_info; + if (grub_video_get_info (&mode_info) != GRUB_ERR_NONE) + grub_video_restore (); + else + mode_found = 1; + } + + if (!mode_found) + return grub_error (GRUB_ERR_BAD_ARGUMENT, + "No suitable mode found."); + + return (grub_errno = GRUB_ERR_NONE); +} === modified file 'video/video.c' --- video/video.c 2008-01-01 12:02:07 +0000 +++ video/video.c 2008-07-09 16:50:11 +0000 @@ -152,15 +152,22 @@ if (mode_info->bpp == 32) { if ((mode_info->red_mask_size == 8) + && (mode_info->red_field_pos == 16) + && (mode_info->green_mask_size == 8) + && (mode_info->green_field_pos == 8) + && (mode_info->blue_mask_size == 8) + && (mode_info->blue_field_pos == 0)) + { + return GRUB_VIDEO_BLIT_FORMAT_BGRA_8888; + } + if ((mode_info->red_mask_size == 8) && (mode_info->red_field_pos == 0) && (mode_info->green_mask_size == 8) && (mode_info->green_field_pos == 8) && (mode_info->blue_mask_size == 8) - && (mode_info->blue_field_pos == 16) - && (mode_info->reserved_mask_size == 8) - && (mode_info->reserved_field_pos == 24)) + && (mode_info->blue_field_pos == 16)) { - return GRUB_VIDEO_BLIT_FORMAT_R8G8B8A8; + return GRUB_VIDEO_BLIT_FORMAT_RGBA_8888; } } @@ -168,13 +175,22 @@ if (mode_info->bpp == 24) { if ((mode_info->red_mask_size == 8) + && (mode_info->red_field_pos == 16) + && (mode_info->green_mask_size == 8) + && (mode_info->green_field_pos == 8) + && (mode_info->blue_mask_size == 8) + && (mode_info->blue_field_pos == 0)) + { + return GRUB_VIDEO_BLIT_FORMAT_BGR_888; + } + if ((mode_info->red_mask_size == 8) && (mode_info->red_field_pos == 0) && (mode_info->green_mask_size == 8) && (mode_info->green_field_pos == 8) && (mode_info->blue_mask_size == 8) && (mode_info->blue_field_pos == 16)) { - return GRUB_VIDEO_BLIT_FORMAT_R8G8B8; + return GRUB_VIDEO_BLIT_FORMAT_RGB_888; } } @@ -305,6 +321,19 @@ return grub_video_adapter_active->blit_glyph (glyph, color, x, y); } +/* Draw string to screen using specified color and font. */ +grub_err_t +grub_video_draw_string (const char *str, grub_font_t font, + grub_video_color_t color, + int left_x, int baseline_y) +{ + if (! grub_video_adapter_active) + return grub_error (GRUB_ERR_BAD_DEVICE, "No video mode activated"); + + return grub_video_adapter_active->draw_string (str, font, color, + left_x, baseline_y); +} + /* Blit bitmap to screen. */ grub_err_t grub_video_blit_bitmap (struct grub_video_bitmap *bitmap, # Begin bundle IyBCYXphYXIgcmV2aXNpb24gYnVuZGxlIHY0CiMKQlpoOTFBWSZTWYxiW6UCvHP/gH////////// //////////9jq798BG+3fd14uW52buU+b6S6wiMzoe+zxN4dU0Z13Apc+r3vr749AiKpJRJ0fVHk 16qpQFD1uQZAus6+jqR6x7brL2b3Nfet3PtgAAPo11tY6NF2aAAClFJSFA6B53ven3qnU7Yvm5Ud 5fQs2Nfd9cH3VPYbb667vUFADz7fdJR9sUCqvd7jVJASF4PpQAZ12aAdDPdffdvTm+F699mF73wn 2553vNX3rW7DHMyvdnJd57caRSa5cvevTd6F25r2rOHqqJ1g6aFKMtdrntdfa2XK+HVAGvLUYUAC +GVRnnvPL3PvgAXh7vb64A+269DtZ66AC7MGgVIyKAkAabWtYBVA2B9zoBCBe9uebTu6cvY27u5O 2JUd7x6fCwesyfLFuuzM7ixA+kDu40Og+ew6Lfcd8mA8O27e9h9vvOO++fV9nUj7pZ6eXWgCvvWb 5h0B75WM2ZPns3Yb27t5zUvO7qonu9T6vs9vh0409oOPKSg76Ke1g3PdYF1qUX3dxzDbHdg9u92u 02pYzdznbXbu89e9uvbPTo99Zsb775726DmyhIevaapWe3Toega71tGV7Pffa172g++fc733DPd7 xdtV9nzkTXfboVACgu2pSQJJGjL3d1Oy589p7s+h515859g1e2djPvn3S9PdXY3bW7AAB5VvNN9U +m2ZUMOm1OuF2HgFB0Ru8+sfe73qvQ749rdN5w9VVdaBoU97J7Ze7zb3Pu7i96ca67u918t33zfP vV9es8uXfV67vD33s3ue2i9dX3q0noABtgACgGjQAAB9H0DkClAPQAAAAANAAAAAAoEgAAAAACgA A9HfH0IFIFKkT20AU6ANUGt8+1rykBrSu76O+2fRI218+3vt18L2N6wUo983VKopS+e93nod2U7Y AJBQe2kdfHNXz17nvvPTju3MyunOkfe87xNbR9lKBH0IgUPu+nvs+cqmLvmrW1j0Hu9l7z6863zz o+OKWoAWPh4l7zOr47y9n0ap689e+77nZUOtVxVr4QHPvere4nve+pFsz68Ojnzbd2dUIu53Kfdx di23e+n14BnfE7LRvX2+e5meqtbbYqxfdzrUwa3vt3n0HveOmTSpUA0MmwCvbOqVUoDEBZ99g54M lX2ykBR2ehn3uiqBVB0BZ9DOBkJUoUGt0OU5oERXrXQ01gRsxVCgBoW+57YH1x8bo+utX3e903Y6 bb619s+7tzfNrfc59D1STT60euh7rUC2Ps4c18Wh3Yc+zqj0HTrp0G7DXzPXp8eKkiRK9aDWEc9v cNfDHvh3idY+nvjLMakhDO4520q2m77JvcC9mgePY5C4V1eu7e6oVXe4c6dPo87aPR33jHM0MFDZ u7uqgo22d7eQhQAIpR57NqtxqqAdtdmarC7Y++e+99te7veuHOjR26LcOt3cPWK7NnudOhu1sPn1 d72+zl3LeugPV940fedAMI1kC+zbGlV7maH2UDPe3fPm7Boz3zGjX03zOXp10e2672daK52O7eQ6 063THdb3d73o9C9S2kJW9gdsO9rdjzyiewvR13N299ID7xa3325727WjTrusuW8tPa59Za570TYa Cdc6olV12XLbN2Odw3GFtN11s6eLsyxnetIT6559974OgucfavenqLtPJ9HB1ts2bCGJPrzTNkql e3d8AHvd732985a0h49ffffe99zm+uPq67vr3E9TpJN3JccgM43bHSmW7uiDpKzdu1UooF73CjlR p1R298NAe0+vXt977eG970x93dsbu5XfWvLQK99dylPeO7AA53u9A9Z497rwNABoAoBVsAB5A95h trt0HXzYBIPvrDvm+9AANCeg+2fZq4Pp8+HcXW3u70D7wAKvm+eusvuzhRXz3dHc7tt1juZp27tO t0durrjkXr4Kle+3D7a3TyttHZ4933Gjjyl9jFIFAAzYZ2DUlm69a3tbvM65vgHke+y9glNF9KVv du+9uvcW00u1uXFXHveHWIesN7Z7ye3c93Q6Yu7d1wLWNHG7TIiGUUcwocpHZdbWDbAOlphyTMxY 7rSbrZiBu5wKAOcp612yFHukLYNVLTnZ06NHbuteiu9tve3vZ67cKk13dyc1dADEDnc3Z3b1u3lm 2x3anRhQbknLxpPSvbRrvdZjRrUwcydzveeiiyzZcbS1buNte1489Vevvlg99HuXY4X1EGwpt7e8 BvbVgHLoae2O7AB0B2tPTtnup6tt15NqgzdXZO7DnDu3TagnMys2u5bZmNV08gHSh21Ejq7ubnOV ve9beV2uu++bZVe7w+7duM3A0Aap2h24ASpQ1pF1kzmMgmpDSAk7vXvemsbnNZZbu17L3vaysFrG PRgTtStu24AAKoFBraDGlxMu1lq0W2ZgJXO5Zd44XnsAO09x73LWvvu77Tt9T2MlXY073vO92Od1 q7aDO4bzt0ChLrFxVdDcvXvbzWnka3YxOdGq7J2OTmkyrdrs0SK93oq7BoBTQJGzW0mN96vpQo7s ctDoeTTRyO85wNZ5ny9OcwS+L0r65EH19op9TNayu1LFZyL3eete7o3e+2K++j7w16aew28ATO62 73tC690Vrtkxu4amVae6eSqegfdZe994W30gu7veM1O7bWwbvbgO9jcNsjBq2LYDx2zWloG9x13R e2zrnVQAwVObuXPW4ebWN2MtjpNuuc3BrEpdrSUNWNiaFAU7i8r3j2Tbe3KqQHbBSQACdsNho5sa dzdu3stz2d273l17y5ttLVS3TdGTbubujpLjbdtdEu++M73c++77pr6kVRni+lQUtbugeL60gAAG qBtd9dw00QAQEAQAEACGQExAgAGqbQRT9Mgmp+qfpqng1RtlECU0CCEEEEaAQAEDSMJlT9NVPyU/ TRptRQ9T1DQwQBp6gGI0CQSIggTQmQBNCeiYmBMmRMU9Cn5U9TxTGRqPCh6h5I009RtTR6jICTSS EEBGgmjTIAJpkmjJpoZJpmoyaUe1PTSjynieqeTUb1DU9PSmJtNMoIUkQICNABNAExGjTIBU9o0n iTTBNMU9CGMRlTyYmjU1PanqaagiSEICZABAACZBoAE0yAEyaAmCNJpkGSn6Tak8k9GSev44PkhI bTd9Sp75c/0yRwoQAujnArMAhcCGIF2ESGtRkGRhRKUVEj/bLtBHFwVHYIChDeQTEhU2AJFcIBV/ FAphCFIgIMEjIUMAUKMEAxCAA73/sf3n401uSHKnFsUCkchNofsk/85AMhUOuIlFUX8hAuQUCIqf v6xERSCVBHkgJyOr/ZF/2zID9/9GMXd1ha/6/cZDZJAmYfsDI2RNgENVJ/p/skuV0f0/3YK/q/nv Nf/vQa0iSAZaf7c/6/6+jNOnhxknRf/pbplgTEQKC35f22S37yUdBCo7ePV/p/lL8+nN/u59L/4U g5tyLFWiaRJlTE0i/lPSm++QA5P4/8z/sYPgHIx/5ik/u2NYWFrQEJ/vsoQYZ8J6ZgZ6u/Nx9WyU Yve3qrr58bKy01Tn7MHRMmSVag8rqUf5RXW59zVMS0szMRqcQo7YMn/ZPdHy4QY02xi2yND/36n0 8r2Xx14t8nq16/7d/7+d75oU7Uhy7LMTXhTABMSisO/LpQQpCom7SlDKMBceC1E7/w2H0E9fmWq2 DSoJhYfTb+2n0ofen1i/GzMD4MqnDp6WnLu92ecjI8SNm5ewpw7f94BRoGSxZSK4MJ5gYQdOSsfV g0di9BDZZOm/rC//VWcpJktlObut/cwjBQ7Y9SyLK13JNwRR1gJ8UCAKo3HpKej1rxzULt9UdKBV Eeu9mgpqdPu+c7RoDUtbaGTdNfdk3/BUqDDdNiZfbUBRU27RtCPDPAw03x5VBt8NIetKNy/xu3KL 0/4Q04MSi/Spvrt/G71sZXZ73H81qv/ptmFSjGpU3Lnah50MQ6dMIoHX1XiXCehazfUSVLUhNlBJ ooWSqnxXq0EnoLgZwohSUor536fgEqHwd64BUTP/dLONdHbbT/+aLg919ksVRJcNTR/qOsduwTxT IHMPkQ5aY4HQ6NKsxgXxnVnP50mM5gc5S+Sg9tyV2NiAn+0u4iiPoF/35TpIf/Dm5+naHPiwmPsH /wP+cupvzpOSgkfmF3NVkGOijJ4LzbbBHZSg4SHKD9m3X/7GVaHYorw6zsT/zgBLqK27uWjdLf/d qq4S/Za2k22qlQrG6wltfuPq2LoNZu+z5OW4vCb8HFCptlk02TVNahmqyWPCt5VWNs/33WgxzUV3 cpND1Iswz+/ZSLw1uOXTbOeG+m2L/Lk3Q5m9u1poFPhObD8rTCVFoY0G71k6rk92/v5viY4Xdn6J rWqQbkYNtjjCd3mQf8fhvXO9mSOCedLGrkDmzg/XY68WVMwvrjO07/Y44oR8zGf+XO4yvJqpeFVI Tzp/xxKVPdw6iVckTU09TGVFyDrKHTWgU8ySpjremhDkYir3crmlDhUilolaEMkzMRpAoaBpCn1m sJ81NJipWmnNYt57V2PNTjLBtKx4sKmWihsqdNtYdmimL3LvcicJjJOrHUL3QbktCVbY76xDJNRk mpXeEp1CmRS0gu0ag2ijCASiGmYjlhBpSYkplAKkNOkBZpF/Sw0zGA8gvnYGWTbU7wzfy1ZpCjSA Uh47G2jvmxURJ7UgVWIqP9trCFCNR8RZJSdIHMwXJGZ7yyApCmabxymRFSwQVRFVJBFLEkW0hkzU tAUhBjgZBElFVEZJ2yJo/b5PHb3qv8Llx8Uc3q2oZY0iMGMUBXne50JNM2DVlEwZpMEJn3lN3c/c s2SIJGcUlUEDhACpFIobMlB3YHewub4ClE2VNM6hgITgUQGRNQRU6hcuyXfRj0IPPDw2yZaSYOWt aEkkJigKViDIcgcg933vxe953+v1nPs+ToZ1XrluoTo/ma+pG6FXGpvVyqjUw9W5Bvrk/qv/yZe3 d/b150dBrra8zVytjJIMXdhCaarFFBVAeFNVNnZDEFWGNezek2MpDIP38bhi724D5WY8N0q87cpZ uvT+2D7e2Kmj/hBHY0n+GKimhUhJCXVQQwk0pIm2YYidRKwQS5ttcwxuokDQqsikTGj0hD7GVfWV vjhmBRaUy3R+KflSWE0xt1uzhRE+eIMEgggLd8wyY1hiSsVHsew/8cCiGCkmmQitWod0AtBS0SQQ 54mf6uz8JvxxiukOO3btAZysw06Xsfz1/c6LFHyQmah5YIC6ij7hTpuv/KdInK59qdDrFKlq+Vzf w5BRi6Z1357aPW13Z3JwmJtvd0z57MNUnQHji5PazE/n2KYsY/zvca/Jf33z4685XvuQwwmPvm6y lNPHkMyFmGt6zUpp2zPvyXC6yYOwbG05K5JvCMYaeSkemFdwto6MdjmvomANnTnjtYNLNJg4lQYq AKC21STmwLwNfCwxIGIKoKCJ/ChJy4NvxEBP73ogdqT9np5cODa4ov1Ob3q5VC/VB6SPVKaM5hOD PqidXrIpnBl3Vku4YloQtVKyXjKesTZqcjxOtPWM5vGlpZUJ9Vl6jMF6mqpwmHySzuTq6pQ9alZM 1iLZybxm8VVowydofFatKWlETLpBgzqbz13/ETOvZ0R/ceaX11VO1O9qyL+ql4cNfG188hrncU1I n8znF44Mb3uMr4ZCMNL/XT3L2lO4j9QLKHKmUff/C/8lkp7c07pv/I4a/gXzw/ZeLosT5pTeTvr7 acenOsycSkdRcu0S/j/ft4DvX83txzyKuMji3F2XVpW/OEudfjj3xlXuKntp5QtRFHiK4zV6VXeZ YwDcUdp7btOXQCIf9Z5FjqSszNzYK7WCh9nsf4ZK0P1ViKURbnej/3uSZnBH34dmOn/WvuknL+yM y6TbxuEq+b20Oo+eIktc/Nnny9L7cP77HMcP6GXt5IZ+85mUC//O4rv/hA3xR30OxpZ4ciXYkkmf Pi8fHtBWHtFkrLx0ppkbs0Ms/nKRr0kQabfxC7wwvS0zId3Wm8L+uiNWGWNg36jrvKSocahr6C4O Na82fC7+N5s3fc8sp33gDDqhzYF37qEHxk4H1+Mupjr24yD7To8s+78HzP7vRfQfQOpINbpKU0uC NHMHezs2eg/cgqrj9rlczMmph/P97BaEhllekL3db1H6/b3foqb3MvGI31A3SjosguYxJDmWeMu7 1esUpU4bCpqqIZ4QrOelHM+/aqxHwdTjPKqLvGNVby+25ZCEJMJMkDIXZN0whLuQd6d5aht4Ou8d 57Ez1UzT98dsrvb5dnIhExOlTuRiDEOpMXmnzdRZRMZa8zlNhVcxNqNTVuYVy7trNTa1ZrOsaxYo UkYmUI5i2vq2bw25Cnv8pFX8XiBRQCAyCKlGUlssMr9u/r6vAiPSsYxhOnrGRJ5xNwpyZxGLURnD hPrROVendJ7qFWNY0lWoiIrJGsPmrJzgWjSVowVYtBzQc6NUCWCIShQC1LhMy8oAD8/FtPo/8XH1 9YG3EDcYKP8SBdx2TzMArpKyFTywojhDo9zMZDSpQnCTEPSz+FkzWIm8NmtKm8oG0jK0xJKGoAok FBYCwWWqCCLpArypY7DtrUZJrM1JlWkaWmlaXJdb4JGWJR8MBE+Or07RI00POfQ6B7fcyF7LMyks Ewf/7JKMiZyaiV+2X5Fa9OaZALLjpJIEuVUQpRZSeWspydnLt8xfTGg6T1wOvSt/oYkb+O8GcvgI d26b79g0gmGqkql+ZUECJVUVmO7OXrkOXp+EvOt0TpA3oAiCaHIDr6eYlAaio1pgVnGTufamjZSF VKkbYsqdlK1qoI5bvaYehscLIOscMRBZEQrF9DpUKrPY4anojM2d5bWKKUcaCD14FxptfLzmb1Mi jE3vAoyp5kboiDost1ZiuKWIiwrVLpuBaIlkurMiYMMFSoVxTLQa2tbjmGMVEhiKhVsUM0mm2W+y Sy6aInK7YUcuSGINpYpN2bLFF0yqoIsk9NmbU39kLjpK75WMqqJVWlWqjWlFStltgpfXuXLtsurG PIReGWxibbORn+XpzvJIjfDI2M8Zmm8gBv5fF8mXIKtC1w+B9rj8Raj1I44yNtjSyyki8RqPbWVS GVUxqY5E5JYNLxvd3nm+G96ZKJMG2krWijKWNNJcUattxN0rmqChVynIQxXTMtylLbKiitay1Utl tG20rijTpS2LtVa3zLvGrDTmMT3BTq963bHZMEqKaG6utDbrQmWmNZRMFklYt5KNuKNDYDispZCH P9tox4JuOS0FYKIscoW0qiLvmONqi0RimUwvGaYoKui2WxtY2tjilhWOuETInZJSPpkde6OzduTe rkx5caMI45bjyvLihHGDIyJkJBLYiWLbs5q1kxrK3VMazBimOGVbs0uF2qxhaMK7MK7FZQppY68p 0auTTVkmpOgoudz2xxNkccH6XGZpW0vgNLZ2shrahQ8mB9KTOFsH6KTZU4ZPTbJB2LAPVHv7l5li Gze3WeLobSZjZCI2r4amjHVoJQFhQbaQkI1GoMbLXhlL4xaHZjUssDE0vViDt4ZTRy4HMICbObSV NNqT6shky4M/jtzegHDDDTqmdipTTUKzBcqOGVczBcxxKjlFK1bAtRMX0wvbx2svlz0TGas5ncJQ +SJZ3TjicTCDhyuYNfxpsuPJXZNaIqu9bm/wWE5if3+Vm/IXsSVOdpqdqHYkPQWw7EkD6/KgLAmk gLIeTUNrVVUEFDMsDE+Dl7B6wyME6u2FoaiRCloUrssqVKCJYwEtkqCMBQRvylkxKFptYlGQFCGS tKBEuTkIwiqiKjIUW22tbBGpa2oAsCgyDb86USGMClGxRQWVkqFZRrI1sKiVbCoHmtDBJFIJBhs+ f8elwPifiRnlKPVs/N3m2uIuQL9WMOlLQlvFPGh+H9k6eNP/zRcsYOAyGBDf/2Bs2cRQSVQJAZch +BRxHNTtu74PLfVyo5SVkPm+XMGeNq77vMSuDls+rw/FCQNBp3b+P5jM8bt6KFHQlRXNe7c33eq1 UYQhvOmGkLWA5JENMUWFaKwMtHsk1TCdoqLB3A0pjI0PcXnuaNTBv7Ya1ltgaccHwMFpzEmkrIpD EFUCnjvJcG7V8zK73XTcM0tWdd2jxwaewg20e/NGEbaWk4IKqyobIyLbVkD6LjhVtW0NtoUashS4 eE542LaOI4hyPGitd2Y2Voqb2osh5nWB9bvONhQ2FBFIaVC8WqUgsyKtA1e2sNjuloHg1vSRC0qW 8UB7tKG2Npjo7GG0Uo7HBdEZUyjSjRl2uYNosrrCVUx2Mwo2DYxky1uSJOCpa/xsynI93NwycZic y1MY5GONDemtUJicUVkrtviaRPu68bwbI9jN4P6JXpDY+YKUlZbI4QstkKQidYF/upjxxeMI4245 WiMtGNUcKNETVodnXg8a84rnt1KsVqKytMYR9qcYQG9QWuHhbLnfOO+bb3xdZt60OSYQpIWxNg2S attr85iqozYnZLGOV331J9iYZdKPMTHWiqOCJXqc3HB6DQbkemRoIoMG8Xat6oBGm0aNGFWMQN4T Lgxsr7OLGjQXhm9IhE9ENO42SGZMyJvG3GozGkVrVuoGNnjzwbzYSVyR2Eu2zXERLUvgFNRU1wik 2CXVOhcO9jthEUk7ihQG20zDJekhXCd7qSt49pqsT1l5X0+B958ZSlClKMIeru6HIVBpQW2kqVkl UrEpSpSFCuGWBX3fl5/guW+BHljw8s4828I7y67aNeMpwly7/ZeL3gIH2nEOBrXhLPQL9DmGnpiI +m4omJUZq5hTLRYoIgKh6a0o5ZazW5ScjY1ko4XHbiXt00ixYFRUERFijcyTdmbMyM7kLrcbEUBi jG23WWZRhqgqigYhWSbavCaB8kxhtmdXBg64OMOlKaObpY6Ncn0ysDOFxv9WhbKSP5eCVhWRlkW2 HRnq1fZ141qyz3J0N+fgnAcpOTIjJxiaTQxyxU8LLlLdY9bmtOfM35LNGJRZRKWwRWBsx3pXDZqe /oHm15tixAYc1SV9KVFgiCqvg1VV08PTuqJct79zfW6MxlptY0uNEp3zWY3Vi0T5U4zTXbKaxx1g aMhqFu8TZ270WsPVTjmiJQfJA1IiDhA3I7BOcSPTyI1YNlZEJ3SSZH1pyjUGZtqcHwvdc293juhu JmkYETZxxM6dZxrVuqqmN0Y2Th1puhOfC5q0LHzNUREVFWKIoxRgvJRB3ODUN12+z0X2pO7LxIch gRRSAqNI9ekpQuWZDEBvDqENQIVCooiUVU+FxBRcW2NtYVNIWjY6IFSEhF+t15aubXyx68jY6zgk OMA4lqD4ZTEr3Jg4lStRpV6MLw2IqzXSnokJh8eHi8PBbFVu1GEwmSyY4xYc6iYYpVXlTaOnVr6a UJk05m8u3cGGIdIM9WbDRrt9Oc7ROeauTkjkckDXPuNjom2Dvy1MEFBkEMdiOY1ik5J2ebDVbsCf zwiXUMh0ga0PEQoRmwh0bw+jNMwetPBjaclarQRd7FQesc1KjemimVdo4Yo1DZMW2A2xjk8riajr gpMXQbYG7iu4na/FhpTObcoojNx+HQWOoWedkmCJ1ugXIb9T2CCmhMOmEL49DnH1PpmNflfxGb9l bveYSOHNMdiUmV1gVKyiuk0MPY+lmxEN7sZnD9mWnLKyTayGJ2EsVDsMRzZzNmbvD9+t0y2ht1od igyw+hMLyZWRXmY6yLhrTxYk+Jwym927K6eyCqZg8AJswzeegQUqbLylKaiCKokWKRUVh8Hj3T2Y mvwON+nXkdbksUQUMOOdES0xDagdXuzYbCbzTExVjGLu2RI/1UxdjjiZW8FDhYhtzZkd8fs+IY6/ Uzpx084oxb89+f5/kOnmLgkImnIEPtdL6ecMr7WswWIOx2IUzBMDu0FZ5WGg9LiX6KYONQDozozD x7+nWeQzX98OyuQnmOjO+LyeAdZe7XvCHE+NR5XkBrhQOww+md/8WB8b7u1bvK8A0NxwCR8GVSBa BEl2julumFB3sFm+VZpUCoV8caGiHbAs0Tcu4jhRjHdXdfW8Oc95JnVPiYHu5tmJ6mTXpDbQrydX 1ZhOtyg65vUDIpoTaoSgebq49WzovskOiyblQUhSm8KZ9oBQN4t+yvVHzcaXk202IvIxN04a9qbt WD4zQoXKLglq03l0euGI6JAcQTtj17KcUPAqF1V7VBRjf65BYw/f4ZUC20iH8gQQEMeL+WfoyQPD F5IckDapANjEHbfgJfkPnmrdROEW2kD1n+gevZg4VAFaea9nmOz345Dx9BD/NdneQ43B77BHs8Dp 2H03upBEPWoINYUS0x0PzYDnLtYR3S2XW0Yo3V2Z36Qs8Y3YccXPd8enZlHSIjc36TLLOuOfZyXT ivomcihwiBcqU4eaBmdMV64cKpibTR88iU5hphsfSa2dC7sR8zNPDNMPwGSuuKnnPw1r4mG6KVgc 24VyjHMLEfWmMMTt9nZhpKXk4Yju0gsPtTzjD4p/n3xGKbuuVKZCTS/7nuZCEKDwpEFdsZYgqZRq xtKMZXWeclTO2lhq6XEriFhlsR3LgB95ITPdttOCyruy1KqhbKogMjbQRgxFKJbWNtEqttKtBEtr ay2NjbWKrJpycIFQ05aFYVFgKBmVYsMEWKswRFBS1SqbWmX/Dmoaqa7fG6RZEZ9h4efDh4VKm9tq oSoCNGS05BbYYyFDMFVIjLSy0CiWylGqlssS4xYKZzusp2efR2I6oQFCsKgpsw2wrqkroZDSKBpl I9gygxInNs1mEruqeZ4SG5d8DbMcqlLFQRG/dTIgJqpzuZaxrQ5IYixVgjFA9DWaSo+poiGCwGIx DTXLYjFDk0VZzlJYI6srKWS20EVGAox1cMG2LVpaNfh9eejWzA4ZXptXKW0iysKgjC2UuWRTLe5s UYuJGltqMtKqSKVL1ayCMM6XGKKzBUCwqFalFKyrS1q2UUWpS2ojES0UopRqLK1tsqtVK7+f3f0X t68l5Tg8cdWEU1mZE4XOHnKbQLtBsG0IxGUO99iGAIQ2RPZmBglQ9jMFOd0RlTmMdWsqpMbqVLaW r8NqIiYKgWIGhksVAwYVL4PX4ZXvcBfG2h0czymtxj0+qiMYjhkaxhlpIxkUUBGCqCklXMyuQFAT Qq0G4GoECn3uypZ6mAxSqsL+iAdU9QKk1bveGXZsO5L0ao87tqhpIcWskfhyQe5iGoyJlk1l6S3U iDQdGWzF4uETtl4sNIFRoZwQGLEaguEyVpnSwPDCqjU269EeQbcyNpx3eLCwNDkx8OtwQ4EJRjxT d42S1GNQdoURhp0jlzKaElRQUKJpyHOzBwVBHVnTNYS73YzqmoyaYobCafYyBuybsZIVFtviemTI 3Lcg0ZiUmNFoXamjRSKoLD09DWQ3QsazbKYjjQxy2mUWs2SmNYy2xd7RYYw25pYba2xS2mW4IxhL FUSFthGisciTpgwvg5kIyDelrectInScG+Hg9Qw2HY4tMUrKM2u5gNZaogp01rDQwrKZKFSLbW2Y iqLMaysVYNbKlHGKb7lXTrYzFA8MsQyEYxi9WRnjvhVDwrLtrGjGLC9S9Lo9LrsO8oBJsoQrQkwk 0p4fD+lEotw3TZhkZWKLBtqoF1o1moY61cbaYa+HJRm0UtuqvGiqsDSDrDEwKmWlapXKRtkLlAhi WBSNtOQTqWQrGotfNaEQXTRWIGmNpbLbM7M30TSVmM2rRSipRVRSq2VpvtTM3LcrtVoMEaWhRbRl FZTa4rjltZWAriUqSjUrGC5fG3STUpRRQUKCwEREYWyly3KYXbRV1JUFI2wlEDQIxqY0a3KHNRom eBQZrfvNzY4+6wrDknKQaijh4uZEVWNYSsBYIc6GJS3LXGi0SmK0uW5QS1VRUmNSY1KODbrRqQNE ZFKiNZFYoIx9ObJrQjKmDFscog1z4NXU04qTEwYLJUolSZlMSiiZbJVqWttqqKipRrUUlFkSlbC3 gsmOMNJG2GFpVYpFi1E81mtGsUWiBXqc820FEXalr1LYGJRkUIpESoFkZKgjpoqltmFhUVRt77cE o0trUQZrGyWIjbXR2DAgwiaIxQjjKxst9bmMjXMMwpbrBZWRRHGY5lBYtGCJKkKwVisqpjCLIZlb aogRpNoTcckkiLvqIsa08eqQMfHXdMb32YtsJuqKII8rKhypU3bRE3pDBKmA8nSIxdtqG2mydXW1 qiejco5YsFmnEDnTMD3dtk0w5u1LDHExpcE62mKD1ButFbLNWKwkZBxksETspTBmlyp5mugpNICw e6lZiFZLbCpRrI2hUrFJpMSE8Msmk0xqLCQOQgvF4waHp6YWzVmSlQ3GY8ZbAKBiHFR0TdgxTMhc cKpXSZdZbYoCIYsTGSq5oyMwxjaTMdqK6xmEUKQ/cxQZoMhGtSAVp5AwYbdHWQa1p0KDTNMxzihu 7bMqyblLDVuCBQZFisHEbQTVAxrA9LpHVgiltIIINtVlsu0MhhQSYIgu1JpVYaBmnLC0a2tCjRhU XsfVmspbQ8Ly3NZXYlvGO0UcqigyEeZcMIom05G2DFvJoxFdWiiBjWKBpFmIY4rNZVuU4QObttdj C0SGJKIpuyvo2oGx9dJgIrHNpZHe2MWBoYfNm2RSLJpPDo3WrNrKxWsy3q1ESY8sNs0zTpMCsbEN SxZVGiBkdK4mn5/LbWoc96T5g14kfUQXb1CiijFRey0BKQpFKQezPDQpy1xOroZVhUkQFalEBZbR YVCKRpVBbaRYqog2hbRRRVqxIV9VuKYZDDBqxESNbFqCKtpUVVCJVrEoxGlbEZWUUtqqwUqRLaCS iVUKMqVGMoxiKVUWKRRYVhaWUZKysVLbaAtQ5w8XegGrvACeWIj0CpyBVCUQF6xE9QqvcIkEShEz ET0giZl+QYIYwaf0soVxTrEVKtTPgfH7dwwPtp8o+5hkWXqMS36sGMIsRDkEHfBartoYGyJmDyJ5 okQkDzyRRKUhQUxRCqjD1IEWQWBRFFJUo21AaoIqIJMmfQf4jvAw85EREREYJ1HAeq4xQUvIqNx3 hobB2ohJkCo/l1onj/IKP6+r//ajtf3+/j7h+v9hnu9InrtOFlDkPiqjUibabo1XjuasyeN2M3xp cN/+V1ed7RwP+co4H/IBUYBISistMVoXkLGknKQf6P6V9Tkqf5/ILTwcPd/JD/BbmKWKryno/7Rr PB9PeriOO6EYs1tRO6Wk6TczOx6fhK1JynKbqAiIUFVuEDNJFUbSnLc6IKuCkMhQkwVcnCj8be6E JYHDH50z0/7G/IiXbXN2sKUt7+6n+XWdabxWeVlFZ/jvt9HOY+LzwLwSRYqQm0qfvCv/cXC8WeTs X+LQ3LpvdDGkOwueCAgvttGC4wp8njhg/HZCwRdkafY4p9Vru38tvMsr+pURFWKsRHlSqjVRRS6k j48eNp3zKJiSigr7aJd7leRJVIT+tZH3uh/04vchCeUVFHKzg7zdjvXQyI45dZvtNPJk7Gfup1Z3 9FVSoOC2Ws8rRyiG9LOuqwSp/zEmk/nf4P5p+Hq1fVtrW/El+PKYodWDW2e1Kw/P+T4NG/1eM/D0 7c3inHTRxlOPF+cYxXRVB9jp+t750FsbiHvWM3f3TX5ECyACsEI/r0+SS+XNnQp8nz9ga8Uk1QtF kA4+vO2gO/QHZECpZfL7vHdL7a+Pv3O/FwR+GB8kWzuhwyyml9zco1LRUGMUi7UfdhhA+to9YFkS DFDhCephKCqiYaCoilaahhpZJGgiIolaQFFUAHO/MgKEPGgYnWyQ4hGIZUCWg7oyldQVHizIyMJU 1IEgxA/kzMKhqmZFi21UUUEBVgPFKIoLWowSIDCpWCCQFiIoqlKvMUj9300O0/hP5Oz8//r/Dt+j +P4nTPJUk36h0fvz+RlxkiIE3Cb3U+nu/Krvrwqn4/H+JkPNfs+xw9Zz29PmwnvWv1Z4O4tD/4gM +8wEVUY/h9+EKKuZOdEwhh8HioeP3e7MDiEQ2KxdPm51htsma+l4iLP5bOFM3FNDlnEPbJ6rpFXw fSL1hBi54JhKiB0OoWm4Qd0eH7wO4vh4elSYpDZjf9kjpxcOiO3BtFWSQTPu0H3eUg0P84KLYBaI TYopEl/L714l6dzlQf0AoN+ij7ty7rvxNcIbC/JXhoNvBwc4MgkQQaUX7VXOn9Mwu86z5N3JitCQ G5KA4oOZ8wYDiqZXzweucNsHMFkxYGzuWMPEvNJ6NRECR6T4+KMCqHSZglB9qr/kWZ8SH6MeLHlP bYT69oXUzXDMH4nzkU1e/Tq5nrYx5fHYl2e3pxijikj+Lr3n7bjOGKMDZ03kYe+Va/U1nWZv5qum dmrpiptUdRg4UgOvte8klIGd2nVBMO77Nw0HxBKAfIoH+1M6Gb+hg/1IF/Xv+u9Tmtf8Iw2P85qB 3xmys3dRT0UYjVXVYKy6rRsZuUMw3xQDe6ZoSEzA39qIR29KjO3dHFM5xxmGJbHMfmytXaahMjzD pRIIM6c2/yMOH5tLCw6M4sONdKGWX0xeHgjIWwd0FTcNPD+lX2rF+P7XyvL60s0wZ75WxEPmjeOy AZ7PxbOvVyFlXODz8afmObLKzXBbl4/DnyG3o6uler5dsR7HS+SyaPf6GmfCYdby9uJAJQkGaoSi oijx5gUVRURVPt9Eevq3jvf3cufcy9kHr1LNpwUvD5BTScQyibpMYqqhmnMRjOMjl3ItOU+MarFY uMUZpZtaQhXmyCqaasmY1uL1tCu9wlCqqzGWlOsoYhBtKtQbRlF7wRpMsvtErGn1NzMRENrEoSJi rrU3bzBrcTCtkTxspG1mHDMxDDWhIDaZiEGkxhDatzCYvhOGXeU2UGM8QZWFtnJ4Zw8nd2T5fPZy VBQ+j4NsD5H7jxpmQK1SFAUBRllZGFUjEhQDNEKyKigimNFfqApJpUxEQmqyyalYKqaoomhrLIcp CqrJKyVBEa2KltlSKQWqQi1D6dumH+AQ2VFRYCxPooWteqNfq+eyFzQa8kfDxhy24OTm2QRbwTsg dBKvIg9Pmw9Hp30emRKV5S85dScZA1ImoXhHCRT1QDkDvIUbyRB0IQ4QBqBNSpvU9IdpW1gHKTlI hxgeULykd44wakTnyxHhI5A9OWEOjUESAccWQrIGkDdgQUOGcK0QBvBHNjrIBnBqAultCtZnNemn Ns7vI663PGYHj2LYNJVUaCVRSGLUrrgmgZX7j2GUz4jNs7yRiw9Ekd2kMJaUalKZJmaKpE7sIxch FAZEZKUKKIW0QiMBBbaDgZi4GBiCwxUgxJQREddwOJDl2NE0TBvOffz5jd1EAhBM3eSc0oNAMD4i YeZtHYGDZ3M2sxKjwdeDZRJDiZYiLn2IkjxTkiWfAtfmvrAb3xw71cdjo0ck599LxnT1zNSRuSRn k4WSRuepSdEYm+B0aUmFKNbHnvPwtpfWXd8U0KHxhSazCjUYnBN06zRiitYu4e8qaowVNxq1mjFC ReZbQrzCmqBEjxY8mpRalItYnV29zeNYMZe4urk1rSuobTUO7kGTWkD1iVlSqzp4WImHKnONXFaf OYzmcZfOlq4vEu1MoJ1hKGkyaCGFhjGxtIlwfObOYcdMvGw6JmqtA2FNvhVOHdZrNp56vQqdsl4E 5u9qLmU11iMVVVGIxJnaLrWNUG9ZN4N3pCzEyNgRe33A+kO7sYeoKjMVVGA1rVbeMRrFJnpTtzAn edFlzWp0LLolFYh8Wam2gRFYjBTixVSStDrKjUqyJWbxrWL2JG9XBKtAOI0mZzAYkh0jCw7xrN4r QrzmyMDzL7HLvCJzbvQ6w84WZVSXZmY3rbmlgWZfBrEBKGyXDzaW3jLzGtzmrqFFREOjMD6YtM4q 20yae871VK5xUY1aNJjK26u3ta1elGJ3eZ0BhGB4UMsuzXrCV5NaW84TQjJItVpQ6k95Z5gYosSA 9aBXss7WTwQUOrOpnNwnS3916bUDSRYjFks8AiJewICcymrkUXqqMUSMSrFGhi75ZOfT2TRkMQbz 2ZdefXxcq084zqW3Xhw8pkfB3WDDHgQ54ZMMjDOecDgVQxIU0usyIpShCfEgI2SFQingnZoa7ZKz NkLSWWHbXQBRtmBTNIJQlJvWYAU0RARKhSib5m0KbVVwk3slDexNMDGGIiUE8GBUwaCjAU8r45Ui MNMAlQRA5MOHSEWaYFRTJSkCkyQcl6dmaKJrrhySeVrE9FKLNdx29eeF33RgrBFisiwegWlPMzIG KKdGXnHyZjJwhe6kKwXE4YF7u/W2tpWHJgQqQvqPYIIkO6L6+/8vmwY9fNVrUp6Ih5oEfYUKZxT9 0BH4LjfDIh9BIH34FPEPl9N5ZKrIqjJJRg20ZLbKgVg21gpWVjaLWLWqyFSVBtqFQlalRGSFYFQW VAlYy0KyoLKqFa+i1Qp1OERIZOWVZKUJEDEuUKkKyQrCoVFUFK1EQq21KMgslq2BWCqRGFaktsbV K2LaVBErUKlRFo2CMkVLQtaooqlZatKgiqyNpUUlYVltUUURWLI1oIwaLKREkKgKFZWIKoiIj99M yqjGQUZIagyrUo5DkpSrTllCv4rQRFWRRRGAiGMsVhW2z9GsoppppooJijjn3TfCmdiVEp9tc/Pr SP5dGexdfyOujD7sN0FgdQPjRe5IFE/xyKoalVRADxR1zvC/yI/EoOqahVoMnAOmH0C7CnXxRYe7 2zmA9OtDEioe6X+a9i8Ryi/q++jEHSD92Wvfg822m+49N7cY82rmHgwH8zMkceG3vmzTb05q21f1 0MzWIAD2N8EcJV1AgKIHiIR9FrO3ltWjRYUCoigUqRHDMFBAIQfcQL4EpvCFIdcoL6IffIDCE/x1 f9iEX74gL34CoEAiCL9MPtgm/qt8HTb2cE8hIIA+JkBROkoI0KJQqo9e3OxOqVzETvRR0++u5tSB qjlBPSTpgbXOPbKHCFAeyEECgPRCiGSAsRQAbSoCZIqBzZTIBKABDOSAivi1m0/LkwGc02DInIbb 9X2fZf3H+WT+/jq66und/cUe0y8nT5OT5urTYTvxvAejooFA8dykU/ISgAdsCAcoFRoVDIREDkTx drDwhNXKPLCLuQD3nXgIe2BVA/iJ36Y8mVD1Sip4QKFCo6HcDbSOQRC0ooRIIEr34KmZii4nz4I5 CAksCRCKD64VH2T80qkhSrz/oMENQiz4oz+ODDVgx1g/LodANIyl/SWBMB1APlg/WQPhgXE/jEMR Mj0EQ6yA/VJAOBx5LKDnBCH2/q8/Kezp2X58fDbX34Bth2gEfd3qsQE5IPs+DFOOxg+GYokQUQAc R8p7TREIeUJQ80qdJA58sIcwyICi9sfhYo6xEgwt4eVVRVTAUvjzKMzM/Ho5IEiNwOBOoRS72hMA MlQsRA/ZBHF9kqEQPuiUkgcTEPWIl9zgbMGk/R7zrlnVmwk+SIV3SGxMUQKyQWSDq4mIMZCiVkKW UAzds06daNZ6RNnI7FKERhMlooVAKzZFIOE2u2UQ0xdkmCe26TaamIYwigtYVc23mjNVH7ErMiFQ d945LCCUyA5BkgFsCNKMspQgODRDEyrlWVxxKosxrMSIoiMFUhY1CscLINWnDLl8ybRkMZDepXGJ tHnIisjYw0/80JTCKV6uPGNLKhKi6cZTccDSW6kqhhSVmNVgaTGTZyNcEDTsIoRcpZHMwlqKlajV oVEUKsDH6UNIs66s2GTfagsBGGIPSmzprIUXRRaI2hWByTGa1QuXEKY3BMQMUHKrW0RKkbthjjcc xjFf6sNtXVgK1GkzigBjpmkWFjHllNA6TLJiAIkqQf5GmtNJKzGW0gpiFghILJ0wuM0lSHU1ZjCG krJUKqjIcNYSHJmyIuDNrmQSSoY4hbYFYVmRBajaSTLZioChipUQxWy97EqGI2jsv9eHJIActCHp muJcUfzmfg6r4rS1fIUF2NohCCwiH4kTCA20W2Lz//etAG1eH66ikClglLNdkEXQBE0pPeIlaFpk QO6AjZCMBCOHQGYNHAhDFqn+GgvswjkFFkRZhSBf/9ocaaqnQMClHHAUyAQtQR24uRVLRTEFOhIl iK0Sl0ESngdfKeOGZg+OHpgltBEhSi9ULkqbGOIhsImYAXeQE2RRRQpk4CB35gKUqjSg0jUEC0IU gq0vuhAypEoRSkpBDhKFCgZUCodJUwhNSLhIrDKGShEKOSrSSTEKg9V1SgDqARNpRUyQEKVqJEAD mIkCGQNCohQgIHGVXCFbgEjRhuQ5A0jzoAVk5EElQC8FmMd09jgRfHz0DETWYIm0ovKUXUKi85Cg RHIaWkQKVShBpKaQUO0lQ5iJocEoZh6oBDCUbmYAOEESCkQrSCBQkzQpSoI3FTw+I214TkD0G8+k K9vr3yqD6rQqvgj8Re7lqH7Ya5eFsqPErgoD6vs+jVdckh9JE3w1RD/pZWHTkL5f86/rWOcY7IG7 oEwkIabcL8DltD8tnvYKA6SwRUYiseNynG3nyhxH7fyf/ZfaMLKbnyO39iY2gnXbZTFAk2KBaO6/ KbsHb/5nznoqMaoZpxyzbBn26lYTHHYGDAghwTpHSz6e6I3fshGJvwVtBv7+Fv25wfIebO/p8cB4 OPn5X3dvzKisW/V/KeZe4PV5ZhPij4r3BSCivYKD6LZ7YxkFxb71ch2OTZwV5kSUkq2/fjmqqQSp wbNpaJVFVUaSyv2Zw4BvLiuSeKIwg4J965ZGCUG76be/+HvcLJIDToILKdhRwIuQNtlR5CYkR97b uiHKKAPLKqCKja1OHD+MG6oBmeFChQHBt1BCFEmp5PEVL5CHyfn77Nrk03abR9YahkyENWZEYUlX 5+ShRtaGpCfM1+Gx/Z+Jc6ealDq4UIloHo2UmI0G2nEAnTQqcS77aaP1QvHWcTu30vGUeccSeZSi QSh6eeg0gz4oxVjU4LqBHeVA1wh3gekJSa5GDqA6+OLqFOuHpOraFz4uzSOxI8yDpBkmXn44UFL7 CPbnXTr5HzFz0Ho1muDyxHEA7lR7ANoSIbNhUKi1Oef1D+Ds64obkT+IiHziIkC47T0HmHM2Tzj2 lUZgfAdCo+U6egcNRQ4dw5T8P1HUAg9YBTQPaW7lUCacAoB6XNZC+oqFaTEjW32IRr5ehln9/W9D UdO29t0wEazNt2xujMrXrt5S974mvfmrbkq+ne1l1N8YufvOUeb5faTTe+/HNLnFn020CNayjliG vFOyaGsSk7QOe9heOFwcEf/EpE3MCKeaznyr+546KqAKMFA3e5XB2ylPLKII/v/g7d00yP01p5cd f3OFYKm58zlwR/8wHpAhtgofxXa2Y5gfL/tvXlHllut9/TvoBiU2DcMldOwH4/r9rJRVCkC5SSeb THSuzXDs5N7Ku71kgdvRuDv2cHGPS9w4eaL35v6t/9Xv+FHThutcnZVILIvyRRo9sSkXMYNRiYL8 cBlMQVbYOUOEqGQ5JCiGpGpQQrDE3xLLFGAaAGB7Sr7PzetNETimx/4blIvl6r910tB0qqvVkLFs TPlxImCaO8jrH3kbVWUnn+r1xb1PmdTL4SqnNAoUrrI4FD+0Yv3WBSMIBlgUvhQUoQYNEYdGaGFg /bFaTk4eg9Hza1JNyyhClCrFIUpKJGKvnTMoKR7meAzt2ps44k9esMJ12ukow5MIQPn7P69f01Oj F/1nzsN00prrmOD8zusLPr0q8DznazPJH8vPTNHPhMZ0kPKxcYQkIpJe/koDpqVp1B+7rA+uEUwg 2hPHmeOXx/FRGTKVrKiwXzOGIxaCPAwNFolltRD2Xf7Lg1X/0cvbHd4Mp5Nn9hrK2ZpUD5AIEPV0 XQ79c7Z5TlD0/rK6A927Z6+uPeSePp/ZVc0D7IP0wnPuxCLDNy9x+maIbeLgPOf/82xIaFLLinTb QL0Nmw6oVlQ/93E67Pmg9J8kN0I07ukH4y/FHGl9R7WDSxEM4tPm18/uyGo8fP2vpRarn0ZvFBmn zuXISSPQ/nv9rvL45nXUyzpxvvgxvqC4zrNCnmIt3oOx3y4Jxn7JkljS+Sl79/f14bXR8tD6TwDr EV/hzJKKp8JhYmwQndNoHkgB5di34xGKMIwksXH/aSOpdnkEE1UN93nb1iQ+9io90g7+cDvf+oHt IeO0DDEc+HuSGOPkzL7QoaA6QNXswB+BlwP56G1neEB2/0e2IHyKdoGOYbXYiAIvsQS4RsOoQZDZ g3czof0eGnd35Cvl/nCXNHUVoxBX83h94JJx6Uokmuj89lDioiJSUBUMhEoopVxM4VCyFBXU1J0U Jgh6UxAXw/BxEs58/5al1I6chlYOJ27tYHLyyiFDuAgQp5rqqaMGKjCBgz1OD2Adzv+RiHEJqUcj KF7eL5CVQFMvw5ZYYwxL5hZtNuM82kOmCgM5PVtdhaNLo8CLp3NCgphGiq29weNQ4bF+uVYoUhIK ingILdI8hc17oXbuxKD5PsHC+0WUsJhwxsd0ouzcSU44TtZ64TH1tHYQgc82oaPvLUpkHP01xicc NDhP99uTBwgdQwtxlZwZankJjzPDbjiImV86Dl2FCUkOXPXTYy17fPeTXkJ67a2coetmxaKK4FzO byV6bm+GanLlXQBWZPETa5hds0HSzM4Hvy5wIYdB+JGTPuq8elfntvpjedsYj5TKCWGQg+TV112O 8nYyMkDjs7t8EFkFeudzOT2DRQDAUTufiRvMR2EPMX9q5TH/YMkvt255MOCXFalSZpmUAhcM6+UF kjcda/AVnCey91tAasADiqCe7i6wsXGHT489u18eff0rdKO9kWdty9jfkBSsCHlxSZV2FkrZKbw5 RhyHWRzljx9tPeJu/wn0Nef0AWrkgUSxCg7SmBATpDmKrxZOoE8iYdpEIGPDQdEU6jW4A0N4axfl JqY+AxqM+jS2x53PfqQsh8SDT2O7MOQwO7yWaNCJ2nYYEHt0QzZmac81/cmtMcU7CQ/jt9vsX8cY UJJjuqiWIksTj7j5D58+FY38+K9VZbE2EC2KSidUV4RQxupQmeQdIdgcxoLkEnNpsDXqoCt0g4Jv Yi67ai46eSSPHxWvOG0SxG5quezyC55dUFctWs+cZxEIKsDtqObrO12uVNoQCkDCyZiZiiDHMTAt z9xcCuTPnOXAui90TkYA3IPAoleffQc8ajARg7vh8Pn+zBhN2cNuwccwYBGokGONVeUGi8ZNqgjB HZjxojOHM1KDkRaU1S418d4h3u3q4fZrU7j4adMMw8oeh5pX8O/zhsSxSiVE5EGO6twYfR/P1Ne1 Gg218h7ecM7pMG/lp50j6MD4XwrypvjFWHHci+jcPJkTdiPXl2SG6RSA5fPyk+yqhuuNG5Yr5J6+ QesDSmSmSIJb6z4e7n03ek8ld5u8rfuNzM3KV2iKUKTZ4LFCQ5LgjZsVK1dieXevtmIiowCjUcQf ioUcbdUAjRXrvgw1Egu0pf6io24rhTZC/7H9P6PzydRiOUp+l1GnATGjQo2sHP/e/Jgphs03VE0r YyYFnMOlZH7D68Hwzdvn3vHJJfG4LntzjOsN5xJ+/EdXzG9xf9GXyrPFVxzDsRa9nCoYcFmhs7ZF bYh0JvFaRavJePUiyVEL1TbsxEceTsJ4CYD6pApXBcjMun8oCgYPLMnYy6XSCkKOxwekbwrS381g x7Qeg5dHAIKNi8cV6kl+RMUZCyPTmYjukzjp3XH1ege9Nzn10/ju+WUueOdMltPQbxK23YKmX9nN rs+3kIYS4DYN4pvKYlARylZyileLjFSCS5HvXTB+CAyWhHwEeLcPTa4z3KWJeG4PghzT6hxa08FR MpHoQx9a0k7nVALvBB8bbr1qhde8Dp1SGRkWM3jWTrmJ1gthkpMyWNtKShdDM/iBzQOaHOdsA9fw /rbR8muBLwTAgxU6KGBIiCp6gVBRBukhXRcyzkkqLoZWLDZofJ9V41T50gCAw+PIlkFgAY+U54B4 sUT9mo8w2wyNfeAqcxP6qT5/Knr0FPHYe+qILD+aT0Zyw1+Q/X+QxYzfqcVGMT4+Lm6g4Y2o0UCa 57wXIaIIwFBxbHxGLwALoh7p+lrXVBHFmEgYlNOTwFSTy8lx+gjo94qsAIFr8O/NNaDFnooaOD06 P5zGP0rjfQuB03Am18JmRSgkxIlz/G5iWtkyYTFCA4osx6FA+fSPvuPurNOwyMca+0fRBhGKAR3q MgQNmO/Mrg7O2gLTuLUmvhWtNb6g5C6uyM8Hqy7ff7us5HVmR94gfCAId7NHzdrgU83XMeNasqqa qpVWNUA00z7Ln74vg8N7b/LORSctKeT2G8zPHzfjQXtbQ0Nqh8OhTTONfAoij2+U7dmOGENxBvKI TOrlUcvd469BjD347oUQw7TkJ2/xP6w8cqpUPsPEUWYRfD2bzxR3GztqV2Wo8Xb2aPgfrQ7TuGMT vqmSB5GeHLuKuwYoqfUu+ijAgYArDVsMXbrS5uAg6NVOP4ugJIRFlIFFSPzOwB1Y4gPjLtCOMkL6 eIYyhTsP5fUlXlLvVMCN0VA8SEgLlCKdoBOL0Aw+nw+EI7j3tw43KAbYYXzChVoOh++i7ENJREpP mBp5CM3I5GttR6cEHZzJTGZqVUanRzhDJQL5PD43bWMD3pCBWzjH5K8hy6sr1AmzPiB+8A/gCgJG xEH1WUYzkrX6FrtEOkj9UDWrPdjZHyGWP4X8f8xtaYvV+WPiv+sZgC479/IXTVtoNNBEhERFha3c d8o7MobvjP+4/4muuH5CI63YjuTQAvsqMjsv6f8+2b9mgAU+tMboyIMiMDISpBfLt9Ftw0c2nX7S WZ4hZKeJnzziZ5bnZ/Cj9OTR3+b4jg7Plk4kuNW9SUmD8z/U+LoQyGOv8ih8F+rKfQcoYy6MyjNk QmsL5WoldqTb0hxoElWmSHzgp7PJO9EtPw0/vfVqE2BHiEP5fozPV6GIYwqYQga61fgw+uZmuRzO fwuBZ7JUfX16jZsIR8/xugBhhfTIsFrbiQfhlFrNxkIeHHD40llCT91Kmjtp+B8of88p/d9vNqnd Y35Xk5S2f63BT+dlLrt3BEhH0ypI+Htp7yEP4u/N/p7D+tgj2su2Qiu1Rl4TzJhr7ExeaXLNAiBv f6Gbfudx/39v8ucv6R7XD7M6E4Oep4FKnJwH+gdPuekuIrFuUP1iIMcc+TCl61++lWoQ3TARaFnX f3TjYH+pajL78WqkTKL30MHS9LOGBR8d71JLot8XK5Yj7F4kTerybrJ0OG741tJJl5x3K6DUuDcv xW6iUHLIhSpWqs7Nh6ixquyVGF54SWVIXVXrw9bguFJhGcVHXxc8ZT7Re3dXLspyPf4UzTJzJUtV yv3vUiRXBXlJiK5mAILUyCdZdj1Ljdsp6tka33yFxsZyhRchzzK/j4zSPV9Lh5g+zL/9fGuMlnqC oo2U/7Xs/MHRLjLHSsR8Gg/gMnJyyGYLDP59qlLt3O3Q10GZj/bGTzi1M4Gh+plV6cLN85PHpUYW l/gzCJoYWc9y5XwYjSuM3BRNmqY/yKs8xbUj4kB82nlHjG7xWbZO6RtjFioyMmo6+kMR9UYuIKy3 dTg8k5as2JzzwruxdeLekOQQFCcFuitXtRZ7RR7DsvhV+B3plvjBnDjqO048PWacOC/NjlU74iEu knB1vdakNotCQ6E+nh7mqUf5ZRnT1xtrH1y9nvcrl07+R0d5CjjbUOhId6FYuWdVyp1xvSTfpZ/L LlKVdr16tteHwQIYk5V72RGs16w8MeloB641sQASk1UWICoUoxO+ajESu/396QnRB/hjbGGnttnr lYdZQJF7l59ttN5bb4L/VBc86VYHKmwCD988fTOX90V6PGQ3CeAnZTNALK6LKMDEbw4tiJR0nJwd XXc+QsJMsm8Wr64h4kMIVi7kViKEVVQnMuwqYeghZwUVhHt4b8duFu2bOzdR7G6J0zwRk0IIWpHd aB6W9nKdcd3ly67Uz8NcdUVFVRIKpEYKM1hXfIdmK1tNcS4bnSeHVEF42EMoBtuEUepsuMH7H1m8 dG3Bw1y2yc4ElZQmLPeV6X8HvR5SUZ4Vfoyi9tpHDnP3YrzstPd4r4em8GtVeuOMzgWe9tCKR3tK OMVmNFhRz81IjIQ0mNcZM+AJlgGe6w+NsPdDQpFXgthjQUcqgj+Wf3ZyOULWpUhzypf8Wg+3S5S0 t2qMPjqw++IulcMHv6TeOQePhhTrkJU5stGax3m/uUc9RsnTesz2kVM9nGgxZILRjEfKMdjl/OOn cKWGXyrsoZjjBS7Frh4mlf6c4LbA7AlQmveceMz8Thn6bBUUQQg0WgYamIslhF2yyym9ttmgOm1Y 6GMMLTeKnFoh2NWGPc4QN55OfSYn7S+JuT5OFzSGNJJpB0IMz33mFnP0Tdp1r7aJF0qT71H5cUKa f4bzG9LK3DyOGfqCj5etf+no94aJMV7f9XfQugIdqs93/RXNu2Z9ajrI27Pht3xTfI9vU9oeZqUt c0izk57GDoiEGgRGwxIxLQ7gvhDBnParsSHdUI/CLeyC/BcgMcGzvZ2S163AGKzAA+0GUTARZe+n 5f1dnwHCPb15z5TV/fj0ltXfA5f5NbaPuuHqLpydKBSJgDTb0N1NMrFULw+aoGSy7Ht1b4CuD5Bj JrmfW03bSWjNwq93GIbHj90PmURjl1HVP07tL0TmKWdaZo8v3SUJ3fF6zp63ZbrtiDD3stlsMar1 rUUyb6/5bcXJsCIenABAgRPf+Uo5G5k+MX8q0/lnYJHn7KNMrMS4X7bFAmsH4COcV+gPqHDhmYsB z3E7HfjjmmA4JOrfPbmN6U3TESjPapchGAHKXTLLacxINEirYgUSBsUMewZx5+3Dn4HBgHVhTpT7 ZjzgJxtrNKtllSvhm/ckkOIb0KzKEaSSUyqgBERRsZvfhvNH7DkYmhhLmPonP7N/43ck9AEeom+5 SHEkJmc920PDcPYMwNNu2BW3Wio9zJhsbihD2ZNJ/vKDJ/JHtjSGMBY7Dn1aBgpMA3oSOHtKQAu2 RkEw0fEwUKbGKmok/ms1hARJS3bK4DAHCSwFiBPVc1yv+3HterxWqaYtdIf1qCnNtAEMhCGTJKmJ iaTs799NtGR2dvWa4pu4vZHE4c/XGLTLILgfCAYap4XTP7zEHdbfU7A05MDSJhVGbtfDFE7Yg20u kiRKhMx78zCG4RaF53DMDpJrBOh0DMkGuu9nFGSGH5Qw6hgdHV1Gsko+EaZsAdXiC0iDT56BzmZE L7iuqlGERqBIFcpzYuTZWSEU0q8Gwd/YFjAExxOk5qQa75jHRhim9BX21FWilY/YsgxmB1R4pe81 /kUJE9j1TkdV6ar86vD7gDm5+Td1VYwgevVljseWteMGC9jo4/507h+w8JEUrNes1wxQQYeWUr2A Kf4JAZ4WAWO7nZ/XBYrEFc1UOYYYq90IyP9bv3OpVXU/zffT+ZoHL0LJziM5UlX3tPT9wm3N3g78 ELwiuZvIOG7F+JCaKozQ/6/4j9jD65TEk6xf8h17m07IAPUlCHJxplB3TaUHgPQ4FhsqGIVIao7G iJOL+5xkc2/wK1Kc4pfP7vF33EuLpx9f9jhtktoRRrZKG8DFNmrPIBQElBuCjWCo6Owjv49koCj/ eTikeDzz164/6hoaWhA4Zyw+J+tCEfDg+KeQgMHvYa/Dl7fdV3uG0/2LneGNGHIjj5R6JJyQhBzU 6PEwH1J9iVSotaTSfvpSwBBCdXTPHsgjKooFKTodekj0BCn2IEAKTyrKqX2hbPZ4LyPj13YftO/p 4/6/3V8LpNhieZRleMp+1Ii/OalCASRMqq4kDTZ0MFp3tyL4TpV7GKy2AObEhxzI+DleRcsIKNpj 0fffGwEKnJQHtRsS3y67TPSuSBlKAxQfWRM5oFFmrtlIkoxUg/3BMD18klEU6paWXQPRUsBzZMwh cVmMQ4AOdy6IIw9vqOOtBYVEhOoFOIZuchJk8vLQaguFzstjwxZrMsehRvb1+zn2/L7Lzv+C7Wri MMEdmj1Hfe1q7SXIa+a7q/OYSi5A+cpN2YdXXmaX3gjfkRdJ+P9uXvAHxA80CIMkoA/XV/WgsQvn T4jhsi76+fhv3fp4dfPZE0JDKUqtKxIFIPnsO5oYhgqj9kmQU5YtJWuECIgYb6IM8J8ZgQ67Kwgq hBJPSsxwUdb610qOkOshRqK16FHXDTK1so2tffdcQ9v0yVn6nP0d8H+gZ0BtuHVQJdF/Ijl4cbu2 W22jXliJTIUjCYHPzt+s1d1sE+nO7+H+zCFnMy379ydfI7fd+u3Amn98yamUl0vB6Mjw1LLI9z5C 58vD4wycSBHyvJuLZaO1M27aoR4POepRziSA2H1wGoG+IA2gBwuNCqahHHfEXnIjtAiH9UCOQLEA oZKgZLWpURyAXIReSMI7ECI7QJS0BQIu0CuSL78i5+4HZKp3yP2Qr80gGpaQuH9Q7v2Ty/CHkUYQ S0D+N9lg+T9lVJGRZW86z5PZ578fsAUfU0AfBFAhSIUgUgFDQqU0BNElUZMWYOQ0tUBQ0LSlDUTQ kSBE0DErQUkQFJQNBENJVLSEStFIU00ksBSzCRAwQRIRDQSM0UFDJKFBQQkoTCUTKwTBARDUQRKx KUJJATUURLEMy0jUSUkUSlKwSDMEREkQLEJVEyNCwSTKlI0FBEAQhIkwhMNBFSUNATA0qQQyQFNU UxCUnIhc0fX4d3o6XbXZH4LXYer5bAd/aaoPOxIQYJ7/daps02SCIbDCFYWjQQZWChbZFAlo0gsu Tb5NV0qI7LtgZYilGRLimGMhFFqNsVYsKNRSpbQLZkwYsEFGKAYw3RKNTmYtEQlBqUwIGkayRpTB ZFSlGYHzwmEpEkR8SAVVYHPpPNrDrlzJzsNC2hYggwooimCKhyQpMskMSZChOdJWBjJRKhRICohB VA6NGhFmaKap1LjCRK05IZE0kSpQNAxDRExDVOZhRExUVFBMlIrtAHuL7y1hmeWyNKmwQ+UAP3gf unkJZRTSIoeSLQQVQ+49ng9f+dvhMg+8tMZW8uED4cfXpNWvOY1S5L4xt4Uth8RuIM4xbxqctoBz NazFzq86lzJnD3N4vOW1MRZGnLeiI1cvG2b9aYPmfz9v1L832f5hT/D0oAMyE2FB0AR5CSj7uv1d 4gnNM8CyYLHxz7LC4Mz5Ty0/OlXsN+ua0AukFApm+7iUujSosYwAV2tYltLYVRkWAg0qWYZhQZRI kSFkRJiuLMZjZYAUrQFIlNJQTDlSagMGChjAmzDGIZYELaWIBERkJVI1KgG+Ja32OmfT9wLsy6x7 /Ydk8ZDwePwQ60hcPiiEoR2kCw29PDHa4T/x4N9Hhz2HP59+7JBp18Z1RPW4Q9KhnPBIY8JRY8im 4EV4KjRV7XycjECZNN3i58IfKpF+FrOsFtm9E57RLMOqWI102+dKGDfpggO6auHeX+vZvqbwztu+ sbp9vKbd1BpHO3unaFwpUIwjSKWDjUPsqCxsYHxGLvQo1qJxGc0NG3b5/UdNEjm8c0G0JAhEEP24 cM9h4Q3YvZVUHa8efxsOr6vPTy5KlCSEmOd5IxxcyYRhQjLkZLpnQVlJmZpaL1jxNvUybzuZZzxc G12He7jUtygPmZc6EJcJ36t4zB3xji+MPhJaxd4wIgWFdDoQdkOilvUQyvUQGQFDCMc51ONozft9 OvrDu6Xhj2Yv11vvY/ZMoXUITHoefENGXAhYujwTiPOMnYOb2lxXp9MVxpMkLPM4NcQk2CwTroyP hNx04aTQYWTL7lPWkneHzmMU37PfceBNkh3E2dvzU8S3CB44ozCzjCeKMFGEl+GnIzEBazl9yF1E uWdH49vlX1h6L1G/KMikGoer+Sq0ygssQtI558DWbWTBV2lF0lqtLhlzIy2hhgllQxmNARMSLILs wMSsBQxAqSGITTprMSEEwtKUI241mJRGTIz5Y+npo3QX2w6GyebWXcx6BfkiOsgEijAgDydXPOy3 Nz46iFGBqqxyb+U1mY+ONIiWynyQVGMDs6m7iZ4RnF7GMAsQZPAjJz6IiVGzYhKj2dY6tya/ul39 U1Du47i4vdovf2W2m1ycwzQ2wZAMEEDfk0plfVx+5sZ24cU4YDRS1Q9Oeal+T5lHwOrvSB2RK7+e L8LC+QQ8uleFV7jvkkIZXReqSbT/Y5COuOtvNn3LL32sMLEp2yva9QYVxrUaoDcDn3aPr/D8/PG9 7c2HGziIt1LyrWRAniEgnJ2epUO2bojoROcqG0pQYRlITTacnUw2FOiMY1mMFxcGT7z88NFulzh0 uNvyr7irnNcoG6Nbg+n5zmMLOXMZcPGqi1fVHuDCD1GRSHbW1LGq+JbehVpadscOem5m6QhgQdus QzT4Q3porUwh5zDhpxh9VDBpY05lIfMExMQV6XIbxOxpxt6t3LdyFjRyU2acHTGBWmxTsbSRNubX GZhWDkof7zt4BePV2+hjdpGEX4Gigx/2n08Zj+m/Gz4Hy/wVX0t3PakOoisFip0dFdtc/S3bl+uV fqve/Vna1ssZZOTFCcqoFMQnHET9UKH+dbzL7Dj7Dnhlhfh5PUYTj7bT7fhfD2yOzLk+qKD3RqfX 3nd3XnjyxDuNbe/ZCgxj09z4vIbabNjmTLy6Y+GXMKUm98HByUI09Fql1DSVHOqb2VIxjwTs3dPE RiCBJBRNyh+eJ18LjuT3b19bIfjcgJcnLnKbzWLlrWTGec0kZy86mIubnJDmMuSry7MsCfamKaHN PghPvniC06EIfBxOqMZ1uRIbIuKV3L6etO04djWGcOynD6UVdTCYmcQE8O1LSPHX3xkJtXqJ3g7Y 9Y6z8IAHDZx4svat4ca4Sq5pHWIiFaM3uOsHkM9XShB9Bs0G/O9DZB3bdw8I7w5KhB6EPBFcyPgy pDFs7Hg7uPRv0OHNbpqUHLrvYL+WdIckhGEfFVLt8kwQvlnioPqnF4RatcImeIG5XSpcrRl4TV07 NOXEjeLgjNqLW8PqJyNsr4k0ZlTfVJM28fzvl9zmdHvvFLPFqFFt9R9OfGw+xFiGbx5Nb8defF4q Cu5m3si3msullKb2D1nQdG0OnFcgidgERR9K2nOYS+JYYyx5i9HWHbnqY/+WB7Jh4L08w5FO2J1A LjOM3YdozAFoyWnGpMUuyYzD6Q6VThThdUXC1xMrgvic1FyMlFviVNSZWFN6gzVVEs0KzLzE7nai tTa178mxmqdY3LwUxqObdVCiHCzk5t4FjXO/TWkD98wkcG1wQeFM9cPJOOOZasPjWYSdLHUvLPh2 bCO0Yhv5cBXLB73fjO4jbsPwP8td6756beSp1HdSgwHFTOFSSaEUIv63qXKt2JRnpzUPTQ+ml327 ZU06d/Tx4buWGtY1rno571m8WO7M1Q8HZ5Tqq30cNm2dDB3G9Pw7kT2Rp37JjrT1LnXYiBpxHbvQ V2ziWSMYdtO8mtQKLICVSbTaeltBKuq3JR8TpmbdXvDaqpmFS0JMISUBVXTboLJTtKAXlbGmGT8H YSGL5Plnr9oJxbgYQjnAp9He4+OBFnMHVJzh4sHjx/xN914Rk957Omf4V4+DjQ0iqT2ihtJsr4ZL LU/BYZNenLyO1ZThSpkEK6dsoyjAmZJDqpESCqrL5J6E55DihAJ3INu9QyPwRLZi+mOWjaMS/MSm CsFLlPHiOnckqwmFKyBAdPc1+rFkgQpgUgCGot5ue4iqyerhVZnSnl3oUT7z5FSQP7yP4d/rh9UQ 86dDMAz1CvuwBVK4ejc/yjY+us2Ay+8FUufgGoxRc/P1p9f9UUhuxYVH712hAQlVs+S4/1cNo/QY AiqMQ7CEnzxSolyJ+b/6PR/3X5p7PpPsPz9Ja35uWrYD7LMenZ9/rp1DuC0imDDsCxAHNywAIqyp 1kCzaDHoNGEnnqMXC8wunJk0/oOwTBBBIqkklO00y/mhFwD03UGVRHfBoVAnMEzeTDBBsU98J70Q asmarUO+qFHjQhMONIZ0zGLx/HSgxMdFDXDMgyd0BEHConjvYOHl0NByDUhv2bAFGycmzxwaNBB6 5LNSkXNGPTwLw7oQ1wf4PJz4bpc99MenLZ04ps2GXiuzbAMMul18Vg2WWWNOMoy0e7rOBSrSmqKq mUUB98BQ+aX3DzIyyZAzcBtVG57WIeoB9YCEdQnDNZHnJlIEnfp6fvv4P3ejfqx/K9plezJri5U1 X3+3aDl+X9tx9Bw9h18+8pJ4iujx0YePrKwFzth293hzOjxzWx3wPLBNqz+MIIPqi/kmIrp68QH4 frsCVGPqnHbsheNcYnN7eGzT3OWKlVNZiCHgkBvcNsniAX18d+A2Wk+r9/rEtlQH4+3BEAjf2bhC xzrQ8H0g/hpCIhKE4iuIpWHHEUsrZPEVG3mz7FLdj6hsU/gSI4PQ8bzLGplz6pH+LO2g4bluzji/ N+vYeggmHZLunXpkrKESHXfOA5CA7jgUpwfetdcDkG4gWwqPdyrpSASbCoZpg32O6La8WZt+6Ijt oMsZpiUCqrihwrLLJnPcYvUiKZtMPgrQ5yRjbKz37VW2j4RdLCku2QUJKK5KMHrmbNte7GsANBgo ckQQveH7TPN8c6soVHpoxIvFYvmDEsrOuVYo0ZT3QAY5zYiYUgwdpCmIN4hf9hjbJo41nN+LmbcI 7bunATyV+/O+OqTcJCaIg2FETqCQGUOBCRRHYcRD/UGplKWxhmFXfBMZDhVXwXes5kRAdN+fOAi5 V5zYwkzbYqOGAM4lw3BXTiZwNlZBugrh1CXHkUPZu3XFDT8XDAWPNpWuWD1MDMKoZMQoYRIhsA8w AQQPET+I6htydnYUD2e/+XOMyMu48QEEUTpdwB7UFiB5GO7sA6hZ518ZpwTAJYATUDsfwHbQcPav ocHZeOu7dMQ3zA2JpkS7uxAPXkFMMXMy0tj4ZJEv+eX0TED4+RhDfXPr3XOpQP3zMho+p1RVJdDb Dqtt02XvCPdfDOemi50nzbNjVYbaTfXEQpiH1q+5e+1RuWhFdHjLLOEMKvxBRHBhWu2JXkMURBR7 /X328HgcMJcQdwxAVL5Hji4EjetdrhIETsM7uyTVbzK9eMtkbuhZoJMVhxA0wlvjGGSBLIU2gQil nwqLjKHXyDxeTYwxKz2MFBXms3boOzYqGaONs78gKTgHEkgZ4CwgkyLyu7ccDucMWGfCjCdNqZAk aDhdWTZNZGeaAgutFwqsAoDyI8NHaxF6ok0TTY7hewu7voHpUSFg8T43eJPEMMKapcM7b2ena/em st5rRxEOXpg8O4UYy3Nkg5LFTUbFPb8IOdv24ZGsMDz6Ru3LbHMChzTRnedRZ6YZm6mjM9xo3JMJ uoOS2Xk2qQyJybpjI9K0o9Yx45zfJHh7j1uiYmrpSBAecygXs5ipAd1QXduTjryGZEJUX1O7Maja OOMjus21BZa560W70EfVjK8Y+g0z9b54PAmdd/XoDLU1Ozjjo9YQTiGmA2rc6a4Jfm0y6gl+IQr/ 73bbiHTDJMqbim+ohk5MI7J10B94doz8IHk3jvUJ4xTGvGQckRHGgo4XNeeLbZB6r4tRLbgUz401 6VpC1JOe2yMMVWK9iIiLOfdN4Rb1DfWudOxrfbrsdF8G0tiJy9LTkG/p2hprLdMJKhmx6CgoXub7 m/MYqqQaA+xkHgSbSDvwI+Grac/LZPatOwIGmmhgm3Ubct2IPKw3Y2fDDPDPsTw25xImFARI6BxA oRq8Eq5AqSKX1eIAZs/QYGmVJ76ZpkU4ccRslKqiaiYm8SHIWHCXUOAbShABzVLPrtF4JhkblAGk vIo7lUAvcaXxdBi/qNGfAoFYcH8RK1pLygDyoNnLM8CkWZ/Xz4MmfBt2L4HENw3uDSCKB0ASAVI7 tYuy3zbnaLAcGZst60DDkkFqMDkqJn0cicRE6VSyAil6Bycbh7rxAaAoujmb3bX3IfDmQcXl3DTc 9oRcXdfDDHqexyD+ATaIwIOWpIgjVBVZIzZZMDiJXiM5A3Xtfojk8jldW7h8+KJAp+zR6Q5wvPSh ZXEQGXJ/520hCER1GeTw/DodLqlqBGZY9ErvUTyfBPv1qDvUl54vGft65nNN8WOZN0hJMmSRXEMh dUsjt71eFaLstyvIcO9K7GcQGWtY57CH04jBWRXAEZHPaIxvnopAMzCM3P0uON0bfWomkoajFRnj AVuGtdOWNIZig6hJ6Bskg2QMu/gCQoXEX2jERGIze0NzmQtmt06Hgc8eJ1nVmZJq8EpuaAdCVgN+ xzodi8Z7cSFPJZdC24qHKqTnOZQQTz7ObxaYnlu02I+8NX6dHZtSXPSvU4gpluxc4WMjTDv6HXB4 RDAsOlbEvteWucuynEN29DJNDq7PKaef0dZqQfhgr4q8fR1YO6b4/R/WH07mJ+q+01KKFMp3d8XW awZN6z7b+9GVwJ0b224lqE4LPFBHvv8YmD5OxG+sHJJgQfNI+mKiM8oq8HJfYkltfjmpc9kEqbZN 1HbsQ2lZw1sdLwmGO3gz9PprUmz0PTueo8mIcv8T9yOxO3MOn1iqf4UzoRmoa/xj3ufVPYdR/NzE vmzm79D9llqbS/8po0qZk3Shw4yQA3pfvPsxEXzHyGtvWPJ5ddvOdfpTUPJOSIgtYUbaKCiqFAJk ircwO89xJwhYuoVsoXpK1WXKLw89/UFPGQnJ4FOmq58X6x1yc6cn3fRyYimmsA4jasMpcVvz09Bs 8PRWWE2xkV4Q1D1HUdW7BtkYRQkRu9zVcoUzsoVs8wNrnGlSGeMGtPgrLyck+SoMCxGxQppqm9JX DQvG0FwZRNE27XPLiabpCgllE5BwEZW4OUjJJIudcD0e37AgA+YvMJ/QMtvWczzG/15ikP5hn1uc d7nc4zNpY9oMq7uHJ+VJ4nkyX6BKOYPpbjOBKEJ1VkJOUBqDIuY0l1b3dbssRYo/huq7ZWk7zAgu 9QOOcOs4p5h0L51xeG48LcK2OYFd2m0B2D46iPfrMOnkBT+sA8QXTcEds2ucAA4S5MHYRSvducKz 47W16MBUJsA3taunEdWG4WSWgdhufq/qgN7agYEBz9WD1XEFL81duXwuoJ0b7A+GH5omkG/RReDa Hng5aU7eujjDKOyXhUCQqeWKeCP4fv/SKcMvH8nqPaon41g/TbRo3hD+XbUrj7rebZ/LeImPP4JH p7vQWPR8l3kd38z2mzHyYy6j+7rr9DKoJ/GyeGWkvQ+0nyQ4ED1fbqsPV+9AMAUQCoruhfHrE4kM RMsPm5SNYMrPVV7+cMpnzHwinj9Zr/bsJJsl3f4/3qya5gtojWeikAkAcvZB4w7sxwqBoKD5N+5H X6nPgA5fH/J8AQHpDkFQF5sg98Py/DoCBA16JpNfPEQLJ4Rw3wTvArR/weOEsQiSIBNKdOG+4Hds QVsoPdgQrOrP/u+Q6vJRs2mHVWnYeMVVFKgWkBwPR6Ps6qhpqAEExtBRFzHBKAD6vh8Y8ASSaUc/ 69swe7LntJvFO+d86BwsHyTLhy4emwavB7KdPK5897RztbT5lYr2RrMuxHxaXJQYN/oPeePSS/dH 6IJbJuuB2ORNBVsfjDHkZHj6RDe+3ZsIDoqNF4fXHnnF89KZ+3+8xHVji0eDOu7Pu8pcERA+vL7n uHp/T0CbJ2j6TlwxB4jmNR7uS8weohgChkrFgBgnsZp166h/WQ8gdUUqyBwKAg1q7oQKtWxVH9xq aLYaa+oh8tzbdN9TX+u89ej1ejXvv6+vzU+Tv6Py4Gc4X5ZliFG229fp1/Xq8vTnxHNh5xPRbPqT 1tkqHbE1R7S3iLAdtFB4LV8aL5IqExYYxs0RIgJkiEKMH0QjIEb4dyaBaPoD579juYiYoMinO+w1 O7l+gzNRzFPAFyyief6VE+vxcho6w+nwY0yQyh091entj2wolniRe9EzZ7TUeAg7/wlTw3JLhZC5 5lHvr6Wzftb8/2PDGEcDDMSARIQvVkxOVKyEvwqV+zU7vPy347KigJUlFVAgU4uj8PW3Rq/rZnK7 Dsdx8LXwQkb7qE/uImiJEd9PTn8/P582eVugVQFFBVVRTw58vRt9CG4s5MedMcSDaX97MmJEja0U WJ4rH5WBE1iJzEESv+85/db1duQIlufstm9oKeP/HMN73MGmMC4XCn4HA4ccwctvgSQnfaMFAZtz 53zW220uLS23HMtkskmEXLRDwxttjwoHD+vuRLWRKTNBY2ToO432tkGi5pnQYmsRNhaW6BVaULxL EHvmM2xUMHn6fIJmGaZnkb+i5zavl5rm3M1GunFyZ5eOxm/V8gZ9HswfG3+Hz7Md0QeJOcU4CF2D LZPv5fBrj6fT5doxYosqQhafbYFzDug/Pr6PfN1n5Q5zyAEwM5yu/Fn42xjW4WBj+DsW60CEe2ZI 6yxAglF6d6TF0JewaIouzwUeGPT+zo3Pk12tXIdltG8Ghlz0XTXHluF6TyRykwmyaZKldQ1T8aa2 KdPbcHknAyY4+xNdII02ikgSQZxvjDDIEY2v6YjjrhOVl7FHZoIx41rFClmYYXhp6lGYJMZERbD5 L0zT1LbHnrXG8h+AwgbgMNGhDMaWBa20WHRxcWltToMB7onlsgBBOnwdoZGb3kJ4+L5gjlRZefl7 B6MOuIXNd64sLFM/2e7WKt/R7/i8WN+ckhG+3LXsx5dLLYo3ktv1JnWy+ewuUtpbW1XY4zGL8760 4DC78b41t+HMVV2EkO7emrVGrfM/c16m0hRieLUWuZTe1goixREWKtNl8qkUkJJJJJLJkUnSSQna EyE6ys7g0iN91zZ9RbVJarcfFks563z3qnorkwQfFr5kKanmzp0uplndAx+mzWqq23zl/y/16Lk1 ba71n2WPweg2MaaGDGNmElQC+Txo8GQMHa6BxWiAlthYDQEZN2hlkR5ZKyYNkLaCStkFSigCFCMr SLaUVGQWTTMZYJJEjzne1cHI63w2fYKroIm8sdula4yIfu9vxniISSgjjGCrrtx+nROsLL2mSJOy qoFwQN0xvIL7SHkMJHqVsgOTB01klZDGEYeqar8E7mAAUEE1wOMqCthGHf8ZD6xLMQijWUT0gUo6 ZdGg6g9qOsymD/l/KZ2w8UB/MoOgi5UjyZWB2lQXDQKLgODeoubh9AhHbzzpxPTjpd5gBb80PVB9 v9RALPrEon1EfXTeIo4Yjh2aBHB1ACj+1hDsHU8dP6zRGd2hu+tyN+Z6v2DQN+4hvAjdL26bOz5/ uj6su0+xUkCAYX3pNp0NdZhaSy1ea0PAY+g9fb7tk5QgRBQi1FR0g9GgxJyHCF4v3PZs8CSjhYMU pEoEpQZGTlhARBBwEPN2/IVPP2e0jmseWfpPH+rMO2IsnbGPugoKQRECTZiPBPQob5fTq6OrRJiX 2AT2DgqhVhPZCSSSSSSSSyftkyEkJkk+MzJLJSSEkJxap55pamlqkkimWlkUtKWRSRSQJIpS0NZm YrCEtpYQkUJbSwhJCfThhhkikna5mWRS04xZYEkWWrMC0hJFJFMLVbbKy220ltJC9jgfo+muFZh1 nZM+Dr4ZJwCE3XRhAZl7wpviQZbUKDtggjCNfLo2LGerF9PKPb/GkEHYnqtKjvVSvwim82BCk8F8 X+L+x8D5SYb2u/aUVZMEsPooHA/DpijkQIMwMxBfji6uJ1bhGgBHn45WUuR+8j2fJOY+s0tM6vMS 8J/xu/BoZ7TtmaEaonb5S9hvCrlNEZBLbMeTn6hVdZlDfOoxw7qscK3MQrns5rqiYyKvIWJYMgX9 5R/X8vf09xZuqiSr02RK8+j9fx/1wG+MxQVR4VUChUZCO3YOnYNj5bFHgn00vgUIW1SdDaUMSCwV BEFiKpy2uDDU+74uegnvDeZ3/ov7T1FXaqoq+v7/DDbsv5Ohze5QvIk9hUmeRZ/ADrRv55qMQCK/ RQPpUJOCoiPrpRK2JXqs+2Ce0h3gxTITXRaLqA89xVNXkMev2FAQ47dnjgSGgEYBBCARIRSqNuSN a6lzSgtRsJ4xOGqyaRd3m3nzfdD4XA+/rhsHH/J/Th393R5oP+7o245lTtYMWJDetmVVIQmsblMz LUWIt1mIrlui6wNZbaqMhIx15QgYRRjaeX6uv3Hz7v61yjb51IqNLzyiiqoo4WrWitttt9HY+44o F86zOS5LfH3+FYDE78Ow5jfrCRbe8oPHagysUGdVaYHpPu3iP2oJu6pEEBiOU0oZEYwP1ONMyAhO CIMRAJHOe2Umje4caLglaEosZHql48CbMfnv7fm1nla5axYlKUBn60lSIiIiCya88sliMIsLfj78 wuYYVRSR27OfhxPVmtH1cvvt6I8+x7hf19pyfNJDM1+j7chA3nRIgEyndo8zz3GzIP+b/fAT5BAg UDFL1hMWGqkiwBRSVFdemqc30TORzw8/r8/0Puf3p3/qCOEi15YjvxuSIoW09D8etPBhIpNUKraF jVhTRhmYFC2lQ2WlKBbKhlhlrWZBWxUPTw+rzBl3WOfYthLKQ5+/9gQIMvn25shIB0mD94+GYQII WGARkTuo1dP2eFrTGffH7w6OiX98+b19X0nfNgifV3aPOBdh6/l4n5+8dHBDyW+j1+roMapPi7S5 Xi+r09SUcx60sV4MW9427jMv1ROeisjKV63qNZafLi1vaOS+rCLyRdmidhHjBzLEZfgPvdiTVLsv si1KJ4AAMCEBATlwop8g3dn/CUkTSP4pwFWXrHZ04BpG/YSQGb1OUDIiUVThFd4EcvhljUJIcgQQ Rk/4Cd/5xnGQQS/tFnXl8vIV6oTYYs9slhsf6T7a33zonp3eln7OwQsq3Z3bXTZHLTHKYbq55QFo GWT13C463yc9sbeUJL9esg8uwgsJP4mH7aSMxXAmUZuyiI0+2Suf1hWmFwR+7bp32fDFZGjl3fOY rOfTRUPTdKt9Qd8+bjDhXGu3pLTjR9/ROvOJzLRDtvW6W0zaayw20zBOQWWmm+dnrSpGL8GhkQ6u hteqRe+cZOXdSHfUROkX1SzHKDnA5KMIFZvQhcmzHnHbfjP5uxxSOy1jBwpBW8/FHT6aCzGeeyCV RsRD9neeLjOKzedVC076NJyF2SiKHrZgxvAoPsnzjTVI6Ld8Hu9DDE6uOB6QqVw5m2rRq5L9NTvm 4MNg4J473fJjKjZEZrJGMJ4d7p8PNyMZxdk8uYOPFzaVOlveqjii5dIrvDbFSimxb6eH1jDPSZXc pEt18ombNPZnLH7pMj65wXHzPJzIaLzXuPLVkng80zVV8nMb6YsWiS9OdnfajSn2+J9vzDmPXzsk g/mjsCWMAsLKruZbPBd7loXM53S7g2WFZ3zgxdDZsVcthaxIkg4O5CCh6qJ+Ki6/ausFxl0OQTyF MHNP+pmIlgofSfbBY76asfs2dOM9vuUCQNdeuPpzn7IjEHUCv1EJsJRjCN/GnjwpdR8dwuzMDs7f JmuB93EtTqIgeI4VQaRgLK5oMp0TRzoOTyvB29VAIhbmgrA9XZ2cVQCk0YRJxcvZlYRcK831NM5U cPITFGpVq8FnSTJQGfqLxThIRkHgdTp/ZKQQILWq6BT+taspGi3qyho9GH9/7ZWdKjOTqIo59G2x LxcPeyKFXwInxm0h0vEfTepV+rP1gFI9EtieqCcvO3Ep6MPckoliHEcnCnWiBNnRDifTgFNyPuil hIugJ7PJ37MVRhLo/T+jgMOfXj+xRVMNCBmU+o73+p7JaL6clHJ3gHcBMO5Bz+Ym0YwAc5k6gw7I i1lubQZ+G2kBURE/sz80He+PRuWFHkjRMsW2DYuKYy0AWrgqK/pbpDYlOl2PXRsLRatmH0dljhGg QIH9q90JK0O+wtahc8WEI2CBBrOPQayb0HV4AUJSUJQh0PN2bRHDYfn7ivneWqjRQB7hPEdfcY+e 9/38Xzyd0/ZeQjuIfFTXmotLfdao2NvKchjk+36OfmwxD5D20C2k8GEKRpwLMg6SAPyLH60KSQEJ 6J6eke4QHW+M1CkiPQzHBQuHPzH7Hv3TIUHvlzDg0+rQsBMvnKLmuJspi5JAv3ZgZXBHcf2MHsHj YssB4ePk7zwK+cdi6OIKlxTfj37KMymAk4RAjYf2OH9r+eItmLY8Yi0HbNaQ8Hhcxbs+qchPDt49 dRN/VjSd8YeEPfSiT9y77lmGNDQkPW5j0Lnwkg83AdJCoRucwYQ5OEx0VW+cdWw2fl9l4A2gC0WD jR0ytvJ7dXIJOf1wUw2rjAuFnbg+79VA0yGr5CxQ8mVQxxRxF7pCBewsGTfvpXpleIg+CMNGPnjv 8GrlZ1Ca+dQWsnNFFUREZPTsLXZNGqyhzXbmdw2c0YwiBCQ5ubfw2K5k85n15cvordT9L83t3mMO rCnOyIPo5e3YNAg+bUtkE9EdYQvFivgOhN2vIrHiORcmCtbDCQv6c1yKkkE2Az9z8ccZTlUhdkrS y6FdhguttpFgLjGqsk0SR3AokHIqEubUhzuTTa8PxG76O7KKdbtUXrAkuGGpTc6G7EDfUfGIE3m5 j6UO1ViJGfBiad8Q9nu59xpzsJjGq+9Im1j1dBjwY8dsIz7S6DcJk1A0b0tQsw1cGXTK7okYR+/j M83t8ixAwxkEIdo9Ar73WPpN5C8+mOOGHxVqpYaB8qeGWrJvF/kfjaRIfW7h546s04ikrP6lnm5A KuiLhgwcizYLfsS08o/jAYEAUFK24SyeUHXFDv7WJOPMcw4B1BMeAKJtBlfHX25iB4hzApMYo53d /HthsveLZigJKg4AMmQ4OTkmivR4j2Tci/0/4A/M3+GGZDD6Q7AOlpdf4UG13f5ZEepUXVC5driA tMW3x2s4etgW6R+Tfg8jdJ4+5/madvKckf4Zjb8ckGUBLFA4RHn6FCEHtc5yORHSlGVhVNxrAXQE Rp91Nwj1xGopNXiPsENqelUjqNPUPU+kBoNR6PR+D8eZbT85IBWoq/f83CoOIefbfN6/VtIIHhnm jvqFWaoBuWlXG0vn26un6eBrx3uYuDvuY9D7VrCHMRctfQ6I+nkduRR2cDt2e85cj5rm+xAuGM93 VvEsPf084Q0YQh68ytJiuU02W05i+wIDQ9IYac2hzGCEQjRfOqqqI+tNRdUDQQJiq8wOy7Kh21AL ON4iKbtvdKlggQO7vr9D5FB1A8TYIXEbemg0F98lE2wzJJ90Dl0v0XtE2/0UOstWUbQz5dCzggd6 cO/1VKbLpwsfCPY2OH6GNHul+sgmkenCf6+zTtGfku70K8fpowA9UaQFxAJ2p2jpA2jpZgkPQidq /vIfJl92LYdfrO07Ow2kTlSZEeiGhO1NDZH1UrIusCDZzr6uk/oMRDvDR8g1nNv2ehq3ICv5jt6u 0XecAxEZhcCAsVuzIGTuEJCgM64qGJIUZpQYI12S6C4KoQPwBwBCY4MJTvYn/Lv3sKSEJN+13s7k fjrFjmBGBZs0V2ahQgQP4ibNXADvRAmK7qweKzapRNpRFPwumO3PeZulvCiZDx/CEvAcjn7HqPCp Kabn1aB0EiQlZjp7Kjh93br2PNhvq1FX9X5YumSvGljBY0zUFWSloWjI6ciNVyHGjt5cOcDhAkWN SePs+Tqu7o8Wz3JpD09qBfLXYbHTu4RneaxPbMlKWaya8PV1zdOgeqjNWHtTwR86RRsXhKKq06DM ZhUiQAswAMlNofdCGpQdiGjJpp44YBQBRRQuocgCIBaHJDJSJpShdRogJUBL/zH9dPR6Ez3agBR7 3MNhjGURSJJNCJ+Ryfo+PwNufWuPX7fssrc2SfmpoVF5H8PTdJF5kInVMDMVCqWGFHQ7uOmwel7J 2ghBoDu6R0sea7eE8Dg8LguRFPvCsD6QqdvsDm+lvNp13thR43jRVOECu4pPH0HfIPVx8pybp3ss XhYqw3/DGULZHBhGJKIFaYmzgamDFbjVgOyEtnHfrTZOwGxwE1GQcecy8hy5LciS9PM/LVNRynXC E2fhlY10TZqhchTVlbF5oXjy4+OAfNz57lx+eQn5uk33xbnPV3IcUoGSbzwd/dwrMTRA8oFVeAM5 gmiZ2V93VVo1I4h+rifIzbp4rAvKARaWUtKvYfVBXl2CsIvM+n68kxyeR2Og8XOxzh1farhmTLCS AwZOGUUyCEhKKqoHVdp0ONMcBE9oEKuoSLbNOODxSkiPZ6jKi8x/ACeTwdGu3JkVz/Y7Qe5TR5SQ mayMyHsLtVHM5i0cIqX49MRRq5rEP53m6z6jQJDO2EwsbPIIQxbn2MB6qCN74BYUcSVhR7+3SGvp gkRBPf9KjlOmt37qw/e6f6/0AUSHAkc0KKnFqEEXBdwUXaAlZA+bDYxxiwgI79p3YVKfDyDBSjFG KIO4dDi/TgYoyH7bh7Twh3eRG5lPCiDRkGLUKWkpEiUoSjGFkpYgWTgZy3CYHbSbRhkERLCQEjQy wwkYWSBCNkGMOYuVCxFI2kpYQacYJTAwDe2EwEB4VKenG2IzuwY5bRy2NeMxLYlPjEU9sBYEdkZD 0emqUQoOe2Ys8fKbj6VfPyc8W3WlJfHzH9X5/cv97+r8Z0T3XwGEmcTxMOIXbnZ0GrlKPBUjg2Vl IoloTXYxA6+MIYYWo5PFmni+LHkqRT6yKYCfnbkQJOrZfqOwuQHueGHvcKk9htvhVIOBbMTuwUoT Y4bqBkkYFEgvqTMJ49+feeK5SfDfd8qKZ9+GyjlDiPDBSUOiZCGbkdw49eWXSfJOINuzqqcjZ8n/ Ca/nPJ+huGC3GS5OE8ov+OTtSBnqqiFGojhs5DfICf0GHo6Ry19cud/3+JTYQ1nw8dTfjxC+Ouf6 OZu4qqqqpJJJJJI4sP441NNkkn1rbBVl0h0nMNo+qodDgyT8noh84svb/P39oBBfvvJkRZ6Q68TM GoZ0LtEOEynAJkcdoVVRVVDgYkDvCFP6bYWvfd69UDhBQgDiEA3kBSCC2Y8KJt+r6/OI6bcRLZ9u xoEvAXTe9Z9MWDHkr8pjrmHBPJCslHlndxcfmnUjCLcQwehdhzcGiNRk5ECB44JtWaY9Sk0wX0wD mA+KZCacLJ5FlgCrVOP7B/CwGWchHhUPTwXxDwIXhKw2kk8p2cJtiaCQQIIApJ4gBq4YfU9dV/HO aEe1q+66cSExQ+aBVzLSEUDRNEAbKSHzF1ptkBr2jxExzHDe0YSVd09vlFkJ2lSI4lFYjYwfjrBo zc6Qhqo8cXiHiV+2yZOb0YOgg3jFPEDhXDirRGeYaAwA2KXOHmAClgiEAgQCMA+TlJNoD2Lb0JJY /PlqBExzZNg4T3F4ymEkCrhibCuaoo8EJE7S9jcqPiIp2E/D4+JT2Nk8g+cxlIYsWFZIP5vhP7e/ odOqbe/qTrjz1fy5c4GQVdE5CCNRXN5O/YL3DutLrWBjDAkQuYMk4TKVCMbr3UhMisfBkKCapLPf qARlVnNntFuh33PK7ri/W6HweF0CBA5h597RGXWYAwi2USoV1F9P+te7YPI4SfNaBYGA78K5a38d djVWu3kno6XqRH7BibptkYr+DFuRDImQhghIaDzbYybke8kaGSKCSQA3UlcDj29/Zt30XjtG5vBc YKN4KrDIe1I/iBJA+YJzPMTTK1YiA6tzeV1eACE6FQCLDLS9UuIxAD8euEozZ5kPsZ7w4baR7dOE AbvRgwGUZjMEKU9WIngEJE+hvaLdwtKn2J2XFbPFCwFXuqADqAQKXbi+XY41KYBw5HY027aqBc7c cB4nnweSS4R4XV17pmGzwnmK2rHW2vldDtMbLh0KErWnQXMODL/EjN/Gy3EAwWDYSB81V3cdc5JB J02oFhYwlFUjfg7fB/OnGEbiVEgAZBBrrxYOIAJAFgYSNVfXF9sdFFdcc720W+5yXcECDCH7QHhC QQ4cD6JiEYkakOMHIhXLMh1lGPhKeT99Bz01HDqE8ay0qN7xB+gTAv5DxTll6sKbhMcHQC0XfGIA CMQiAhEj3HydrDTIv7NEgQxLruluYeKq5177bj8cdG//bpUR78skVhqNi3CPqCTAWMWp24p0dr00 w4EUo1hQ/ZR9LJ0/NDkP0HoW7d/jk8lMmeck/m/yfYGmdkgUUtSxrxhmJ2Efx9h1yPxpFsuTKrkt LLfhfrTzjNI7XrTjWsjW7FUsyzENUyQ3UQVLVVUVTVEomWmqod3uXppeWmIrA4Z2diiiiih3GtGP a7ak1yw8eiXGV2i55JYkkkjy65aw9mzfPcECCqeAOEs6TR6ImHLH8nYz4NIjYCEIo3MM4TuyA7hM jef5pf0LslmIRBVTYPAaaU2Py+cVuoxGxQ6PZuvUdbWrPltjTywVhDr4UoXoDphimz1deiYaj60e ZN9+9RtD52h2C0xi3qGj7wLD0QaQ3ZGWns32YiD4r8f1z+NV/WgoCEST46DmYB6esPfHtIiJ6qeb qFJFEILgARqO3585PEjKTsh14Dd+2+FQHBNygBONeXqboEn5YkZp7Ve5UU9e9jWT9hJNkQIugPZg Jp2oEQkCQ287deDPRzgj0xiHQioITYmMiIdnTfvob8MU7adL3PXmUmlYSE7sepDxi4lFrK47JDpv 7WDwZve6fBSoUlIAlt6gUzRQs9+O97hMGjNuHhnq8RETEKo9WDt+FmSUW0cj8GDBEsNBBg4RL6VI skz0G1hyIwjZ2nmeXz4wBghUCQ3HjaKiUuRWCJ5IGmHtyuaoa9vIbQNxETT+VrR9p5wIbIQ2sqpV aNkkClMPATzwxDZkWRR8scapUHwKdgYEWEhmjjmiqUXtfKcEAOkAIhaB4CCIrbADbcTQevu8iPLP mqW/qwtkiIntgkQIhICyCcxpza68mcKCjq/cZW5fw5jOH8Lb7xOJz41DUseD7diyWyrOoIkQJC1h PwFIp6JYcr1TK77B05fdJHC9sJVKrJhNan8brGkpZ0hSDmUuWgIQIIvs+DjQ00jqzvqdVq9N295T p8cziNs7rFxcKjd5BZ8ox2IZOPmaI4d7Djdagjx2Sb0Qmhc5gJevJmdRtrHHDqbd+mzUGg6BTKT/ D1RlIJ1ymC4eTGs7Ov1HZ0YyQ5PUJRvCFASxVNdzc6fXgzONFBOXvz4+XLea6ei/hnw8E3RQy34x CppCpPMdpbBvR6FBVSd0bCAEg8RYhll30VSiLuughIAkU6j5UtREPXNyVtJHwna+qc+IqcL0+OUW 2M2ENBEsWL6X2vA1MpYab6sbs8751f3UdzwrEnV+9gxORwYYDXsZ4OQf3llHg93U5QxOzaSoAsJI 89SLNQrPX2nq+Cz3Ncr4ds0wYPT+agR1xEI00SJpyFdQyigwnvpbaWxSP8sSmIB0SkfJyd90Zm+w iOa/PT5Ywg+BIOx9ZyOqdLtEBWMO8kqNr+GAX5aMhSroP6d223QYAxpJ8MPQYt1A924fe2yJDDnX gJYMIvjsbjeSq+T44xc5yX9Mc7E+kI0Otlaqij6+0fdh7vmxRA68GwGBhL+vCCd8GK0clw5RWYZM 6M3Nyozlgmt6aYywlGnYkdhrKjF+CsMJ2AAebmxuyjIizmyjR5N659CzkscvznUHTsc7J7J6KIsQ P/m/fdx359utbns9di+MnB7BNG3x0Kh1vuDJ8bmqFggx4b6swMjGnvwrMgIychsRBMI/n7SLhzDw 9wxjGU+kZ2vj58/km+mEkkk87a5CqqtGIZixkGDLoJw51gKRCorBRwI5Loxssbc5Y/JMCdxuQvDn 7YiD8fi7atyvqccPnlZgxQSA+S2fJlMCUB3BAqeYtHjDqgp2FSiWoig0czN65P7nebFQ28uU7k3x jB+z/D5Ym8b1PG4URHnmh7gmBDqP3hqwgqbteYbly759ILhyw9HTshn41aKTUYEMdT36lxH5jGuA Y4QZ/t0Gu4UzJvlBwHYgcyj4FEsimkj9n+ojJyyEH2n17+UtcaHEepVOvqTiUN+eZmSUYEm7lET+ Nfjo+zJkznJwxg0OCJo4LISEhAolXqGt182wALbKcTt5O6WEoYLOiDomw6CJezZzZ2/H1ud0wW4A gMFAUnXTmwA2oEIQoBFIHMBQ0JqB9Dqd9viwk66Q7809ChkmcgsqXiNl4ELBMSMJsjYuJMxLGBbM H49GcmTiRAKg7ZhriMhzRGGKDozoxaSRloRc8apW22gXzUD3bEc+vR8nNHedSyb/FeR6Jy82jqh2 wEpZ3C5MWcDKrKdNjAwti3bC5SrqWTonznYUQuHKPDr9Rsr4TBNJSHBXggR0LI6jbMxKRh2HYzR8 8dzT1NycDJA5jCC7I7mz1dNgJYQ6BBu0nwQ5gBcdBEe7IgSQfMTM5a5Xs/wfjVBV1c8CATIGZDOP KyG4MhEQ73odvXuhAzVyyZDvcQrUMB8B2wkxDlEIKKqgBkgTSpigLBeNJEtBTJ3bNpiOIVaqZo9E 0ZrQ6xQ8PC/hY40iCfwgtGl2K2/xu2pD/W5WJLw8tAQdxkziR2or4OedgZKDgv6yq6Ox2+lfbdKj wIqyylKjk9mV+0VGyBp4VdiMLpk3heesHuylMPi4Ku1UXiWZUD/yKaVYT34jwRoyMVZ4QIHTjWmH UwuIUI68RX23hsOoBTuFwmSKeVKfYXdWKjihJIJQhYAwRBEQdenJ4m1EelLPph9gTy577FGZFRmZ FVWDaZHhwv60dfwps6HdnJtZdBwwNAt6YkB4n5uXl2h4jo9ywdvEKuU459EmegRBcxIxiCm9EG6a wmWVFrVRGEk73s7fPkYuiOUDm7jBwDKWIkNZ2GLXtlqNEvv1NOlATuoWRTCCMi6avVcNsE0TSVJI 4oTw6JfFuzl6cyQht2ZCis6LyouwhFmkMSEqJ3Us2dagy1OFW/AlJRGqKv24Bvk86jjTOnWykhx9 tnkxgRswIqxEjtpQzuauXiodbSFSp+6Q5CFL88gZUFIEICCYwLEBQRRIuFsjWhOgWd22h1QwlDkZ GVGQhRMK5KYQprWakmAcgQwCJCBCnJyGmik3YHIiA1AlOFTQBEgFGQ4MlDqQchCCQlNZkDQzlgAU FIlMSVFEsURgWKADCzwp9BEm6jx83t9FEOrh6fiO0b4d45ypf7Icqxvh+nUM/lphEVilVAbLO46x WIIIBxIrzi8ODLeeTWg5lMhFXu4O7Mc/Tq4jQWQ9XrsgaSRuwhJQJOc38Tmslv3DDjDGp+f8UXUw Y4y1EFryfJcThFYxHSlQpp4l/nFQYE0Q8hEZ1Un+5w/atL4Zxhw/SAUMou/rUKBkFTvkFhNZwMpt 0Z1UPplXsVcPc4htS56t69oXIzDdN04dp+Curpvfw+bFx3PG2mK524sOrexfu0PS/g1Tpwn1Z+bb DYLVvoYYhIqxnlFyPTVQo6YpJGB67SfVZYCqrI6/fUb/EB5OtmEuNOTTDcUTAL7tfW/jvljJcLzR CrSrydIIdzgs6D4Osuf0FfXolMRnHvGVRcR7OIdHqfObr+gRhrt7I4EX1RQhSYJQIBZq/D3Pcu3E SBqr4cwo12D+i+ezZymihBPSLa3R69ZCinBtpBEX0erivWwg5LNqDLs7BBkukNm43QYMiEERiCWE 3QfTddBIDootT4Scmt3wMG9QjdRTEXEkqRxGyhVY0hjrgonu2MRv1h8qeV9nj5v4AW3bA6WwVS2w ShmwjswAAHtQBOCeIowkPT8E2SByTg6TWTRW6TIgyL95lh1Sg8ExJiYzu+GOcclV3uTeYCiTUqyB QzQ4YUTCyqrZaK2blJM1oocWZsaDaCsKalMYCgUKlESjK0lNMuUFGNKUFCko1gTBMejIaMIauHOW 8aArQjemwK02VDY+gCQJjY82LokA4YG5fwZA0mryuWYE5aaBdWigjCpYLKygy5hkYMkulrJMMcED uUoDrUlIRMzRqFIJLYFjB9DKLTAICBtnWBjIkwNvZEpQwTMZhkQMQoJbaUsQYgIdCyBYR37NiTRo N6GHKwqx04ZJTBElLFkFBTkJV41dGioiZJEhZcMKu1mmGAkDd1ok0auoYDGQRUvVMEVUinwakqCi b3U9ejJlLBhwy9SlYkUhOdKg0MKGJY6GhhEhUcDgh1+bioaFs3sdhi5w1ghLgS8OAYu7XqoPRs9G XkJknDJei8qWIODN1l5QPHE4AkjG8ckdmSRNyuYCiMKn8CzWPBk5WmbS4V7iPveZ5PcID+oNAH3B +9kzF9YqqqqqqrEVVU7nn0NYn3+H0+P6IMNmTD9yoEeZ9frHq61PFWj8LbiOIMGlPEPMCO5uPd+y q+isB/T/SsxOvpiHhyjogo/SYZkxEWMx9VT73vTqlqD8TpSAeHbHd7pPH4R6vvCBBANt9AXaRunD f7+zOE/OY4dtrbsQgQbG2rLIrJwYdUEW8Bo7tikOhcJfZwdGI4KZowKr/KAsyq9c+tn9xWlpSd74 TIy+XBoAiW1OSft30hcC7YOEhqrvtCdfmk0lAe0JzTEZ0wXawGD9JlCwpyPg7Z/l5LwRovEf0ezr 73dWcf2uKVWGfhRa01Hk5tcUBHsvmL5aevI8Hd+3X0oxD7LTIX2EKD96X+IORmtfwuai/Rb+fWq9 rTkhOLbCXFmZO/F8F+L3W46iZbynngr7IxSvEXMxDswxi2fH8vzcn4/i5hzLKZI6rqMl0CPAq4SM d/1uduima1Dkcyr96Pvg+UMNEwKO0Vzm2QTTEfyGGI45HJGGOOfSZOh6WjIzm1bLEYViJSOI2Fg5 VWhZMcGBIgCilEP7FQh0v4hysi/fmod6C2ebBn6bssQGoFEROOrOkFnB1Zhm5/aRHDng8CJAuIkX l3EHMK69Kb44cQIOnarH6tBkWFYDezjjER7HRMziJ7Tqvda8U/H6UuHkH8u2KbxBAP0DDWSkHIq0 xm7gowMy4GJHdJToepPYHvQbCALIUkO02oMHu4XDacgqFnIwOMOcoCTyMDjuDd1Ii1DOc07H3fXr kU+eDzBdccn98y/Nfwzs1hI+Kig5/R0GT95hKmykkUpn3TvHdeoY0tDMIvpvA9Gpq2wY3Z5NRMXr Ig/q4Oym/vlOAUByuy5jDkzxewmnsIQQPeO0GI59fX0fe1a4el7dvrBbltZ+beJHpoJiSNOx2m17 WeBJP2jiQ29R8F9owxXHocgyc58wcznZJhlVGErkmXKZj3eZL61RgZldl/VJM87G7oBdd2KhX5zG uYMpyks+R5Q5YOvnwFMOhe9lMva2qzZbYDoCEkDKMjYLMGJk09XVd6faeudfY8WH5Bg1HqHuHUUT 1MiC1dzAdnfnwenPdiJMCsGBUgMJGzlDdC89cxXivoqGI/d2DREcCvZEUvYvEAADCAg+eA1Lfb/2 x+G7YITJUFB4bi31BeOEuusIK3T39m3rhe8/a37CvSLER+b835NWkefDZpp0P7X+ZiLDpzwZck78 Aspb4ymkkeLz/L1gCg/melAd0HQKfSIPWYmWYp2r6mLYgyEU1erfqJeGdenmNZY72ur78uOnmHvh i9iokrw/YICBV4XjIRYR7BfnL5dHbJjiuDlQdZFaiwhHYuToTweOwHAEXuQqF8G/pjJNUIKEjkVQ ihCpMgh5dttjvd0wk/NtuU2F6HP69lXjKqtZQuNyRvhF8UfZqnwPYHeePie7SQaesyGrDEBSHmSr 4TR9mm/njAQj4h6ud+5QOO+unlo+HM+scbCMngskREYhwcHpG2rSOH/katUkk4bDV+6AIo4h2CdW 0awrq4aAIC4R3DGCorhDQSaOkI5ZcZiYh2WjU823ZySB5qFpYVdyXnYKXGmzTXeHvkutWZJIFBF2 XeVjWlkeChw32pKQkYXFaTNIPE3kNXad467IkWnEZsGbZhsyrjSYqgNKCuOO3TwVNp9Q6kDIybSy ljVZJKK2MlHttUwTXc5mpyvzTCXKOOx3ZmykJ+9nfw+qLYZXJcDoxTMkTWGqWwxI0MbamYawXL4a 6Nlgm1SV1Yr7ZiPv07c9h6eYUd/aoyN+BybZJtqpJJJFVVVVFRVFVVVVVVVVbb7YfWzSsJPOIKNg xMzKns9bohntiw1BGapsaiKfQlRMJsnF/kcNJZocg4QkJwHLDThRB2CCGwOOIxjlYMQqfyOYQwHB 7GXGid85Yzexm9lyDVkkiMCxgthCGsm0WmfUkVlx8CpM4hCaiWchkhIg1hhGLKKKyoMCEMhk2UUR Dmks3ONyYEz9xkB440cMtqwqG3bqFa1AWSJLog7M6jQwfMPY5UaaDnWxgxHlQ5bksgoKcZgQpwBs kxXoPGqwpMZk4U4RXXu2qaQcOrRvvQJ0hHGjlhmQ7c8pGdNyCqR79RZyD1TB1IoCmrYWF5AoOZjm QHRZsaERE0zEHK4CTtFbd2wYR9WCoNRBYh0wkD6WyrrNM1FUTLOKJNi7fUcwVg5geHEOagg00kEI XKc4pnIBkJCs3XKyJ6Mq3lZzXs7b4NdCKaNjJekmT9vUwikTBntRxhTFoYGBUmfJShpDahPl/Z9e l7tnXggz6tpcjEd+hfLnz3DW/AUhjIgnDFCnMfhwGLBit0NbVXC3SYQ5QIuUQSc/LXNdNSdZd2j1 iBobeMn/bWi6zIu2oGEmZi9OD/mLyUh0fqsiLERhPJYhHD/KAchAhfPPj23tGdekOWqVQh0eE53p gJfQ9o3lI7fNptd5L8260XosKGLgAe7FUdmJtBO2UGk0wndbDZBYDaDbJUAF8RJOmUYhMwRiYASY SEmM2YRMsJNebiiitYqwA51oqweWKMo0B6vjafMGblZnTNmOO/iWhNuX7QOhmQSnZBtEISygk3SQ WUjNtnGWs8ORx7x4dUVcrd0ND4+XUd4juhvY2PyIW2+fJI/Z3ytnHHFkhJISQnlff2m6qqveQ53f j4+/z9V52qqqvvOZ0Nlb0N4IJYX3UVBNpkyBCSEnqivngNNyP+l2MvNOImB/moDv68s/bm9k8Beo 6nW2r0SEQ/pB0RMd+wUmkp0b+ZaM4EMwJm6oi/7bWG1Il4HCSrJUC/1/Gz46Uvshh0DceYufHd4R Dc1yc+7hh6AgHTDp1VeaBUEGOSCIZmzXL3q4wUEwf2KqnypURA737QR5YUfnJCw8sCOHwZ7FwNtm epZZJ8ipxefTfo21O21RFVrWqjaqqqlSiojNzjCeKt8IxrRHIWIFXEqjkLiA5xUDxVfqnxBMkNcW FSprTGq/3b+FjTRzPyovbtSe4+d+LTFqOawjNx6vuqbdZ3u4fA4N66DnFyA9drjCMA4RCx2qRSoR mS+OPipOUQYYctkPeBj62M1XVVsQnD+RCFV8hvyved6s6m3pO50XSMJxIIcMuTuo9fNXcNTisvHK b45Xlui54440imVikMtlYPsjigmUR12F4bFEWX2ruO10trgKAEcL+HuG4Z7k4GduKbtvAz275aKS Rrin3e1YXH8CbwwqNw2vCN6PWIxPM6e3t9XqEQgR4XMKSJDvZgULEHj00aUDYEW6jloi6F8hblWI QIKQnuYYEJOu20wtJuhvcMccMH1UcLSqHhE9eOqrnWhhigQm/Hb4luOCXFq+5rZqfg8lkhw9OChB 6dB+g7PAcRQPkDv2zZUooHWZQV4YgxZhgk4O4vGkUQaPiwFSxKN37VXfvRlPThvI3Ssmjo9namOh cCOnkP7JbX4lscJudj4XyhAKNgG072Hon6x05UAfw+izHvBgeuXp7efTPTxe+FGfiKfGhDoIukkg e+PFDS5pTY79g6EJtMSZwyNtWJBX40Csm4yU7peut+NadxvNKxGig+lHkIUgQ7BcpPk6o6u7pe73 kCBRSHTQYZHhHmYTqwYOVyTHkFAjcBASBlqi5EBBQ+MeqtrYF54z1GdhB227zBxHnUTAyyFJFKsR kBBT5KFJobIaC+PQvaHfaHEOPY3ntz38slEGRYKIgDBBBFiu/KhlQoJ8Bax2LVRO075PR9fZ5d84 CefXAheg2QVO0DKIYBtCoKuVumFCTLb2EZR0OJwO7Pv2XBty67Vw5vXcNju28NUCmPwV0j6AIUU1 IpBoiQmHh5wMMUjEbw6VEr+TsTZhX7RSv4uXRlQ1Lmk5l3lDufuhtkmKb5rr91lL4lW4w+55UxTt ACR9st9WTGT93V643EZy0uDsmQao9mdcNCIjhZ8fs7HLz7ynNnu2shnw3NRrFyOVQpU1OA/MDBKF CRJKK2Hkwk4TJbFx1ipjnxFWRwY/Xzs/rke+Yz5+8ydMP9jlfk5KCLdyHWovHrUEYfW1wzQP04JW /Dio2NFH8RGjgPh7dmb7eevDZPcEIO6Nhw+JjuvK7I3Twd4mF3x771jbaQ6DhT+E7WZdjKblYdwt H3KV3rLdHshv2hDd6w2l7w7NhHaNrwgJJPyPAaqMOFnAmh9d5mM1KCnlEJknSdysbcPdPgu0hZdD DHU7U0QYj0gjKPT9UtaP8fb25y0MJYbyaic7CyIImRPiu2Go/dmsmop9fPXYz3jAnXw1TIA8FLyk 7svst9oPLb4euZX6kL745M+l/I60GfjhSsD3Pu2zVmrPlsD7XnHdFotaH/0nz0Xwu/WfC9Ak797h Je70kIgVn9u8c2YEWqUMmEfPLkOfwdyZ2mYgR9JWw+J9iTBogqdr7Ghgcm61tc9dRCORnIcw5VpY mDBi2JHvirKev1NWBsIXhDYYRtuzwcNPDRgFwDliBJkyTVj83r38oSPwo/HZZlqU/Nzn576WhxFf Zx9lhCybGGLJIRdN+v68UU2G0sf0ax9z63rOdDtAouqye/GGve9527OWnbMEsDYzIl9dIJds61GP rmsvPpdZwxlHwkTbWqPS4NFuoZAlMKc76bU6b/CcZeSbqk2h+tOV3MheQOqaIaUnsmAhmhS7JdPj 7cX81Rky1HdPDzyMMbGGEv8hhh5IPJepk6jdEmsENGZazFUqqWj3txTp6ARBgfi3bQ5imll77qis GXlqw/FtUPMayR68YPP5fWGgrjnWadRiG/u8RqO09FrEIZd6lCkgJFJqY8bGgg+sPUscTnB4/MX7 lhaQhXp/BM3Sd3hl6+9+DlL0KBgjUcLoyB5k3HF3MLyMCDDp2N3kOKD3Mrdept0rv3qGzswWPe6n K0JWKedm3DmfYTe1o9Ibk7uHVUPmhmsPAMGd2wgjI/cANelaCEzsZQxBE2bSgbjOiSBXNkaSpHiX MIqpaZ6txHaJuZHbr9l8f30OHpkTCHZfpjvHq18VFdvoh1w5RiQo5mO2MM+sZ2G0PwALjQfsxDDj CjLKT9jB1DWNRQ7k5nbUiTp7mU32NJ6jDHYxfCanSavcm/Rk0TBRAgyPfVR6k/X5hAg+x39RkBfs 2c88MTq3On8dnbzdGMnqPHyMhEGKghR39jR6DTQYOIfJ3nTpxxEIuFR2OEyAY5OXa+nEpbCSbIm4 RH6ohilhPjvnjhlhbNHx7veBSGyI3eOlNp9CiAc7aQFGLDR5hts5e6XMEX1mh36vbsMEudUCpOrj iTTk+oFOvYp5u3u7SidvPzpXDBuy5bDaGkEyNBo4EDBFYS8DNn3KU1BXNdOMneIbWiwryKuHcnlq fFzfZPChM/bfS37XaB0lwBCu6mE3IsguKZVH9u+zkkx4UjQVez4YxW+Ps+tJJKnNJZeFKKm/35mr d3LcfGQd8Z6ZUeDslqHCds45ssVmM3uSHUsTCa+JmPtuAOniqB6YZqMRyxno7LGeQ9gwVrLdxXiA 532ZywtAA9PUePTo9JHeSD0ajoIai9zWar/VhDSTCSMkIuoJPm/TXwGXQwAcN81ny8IaSxlEbA3D fo/haxEw5RpmCK0UKpG4YuH+H57AwFREhdxGlxjB2hDyYouGziGjJyYliUjEnzrUECTqxWiODFg2 7eokNBODJwtcJn2zZHQRQwsPZg+3c7GcFxKBMW55Zfft3YAQAkD53fTXN2hkCJMaZBv7vhLdZnHk fb0Gzx9fKxGQIVQyp4sGe7pcj4QaY5R3tAzScZZJD0KiruwZyCo40hiFyq+GLcTTrpQYyIrk78Vf TaK4eTCjlUhxN+ZtkvUwwjNH2p7lm9EnJRN6bnqgjBZeMuNqwNhrAP7K/UHioHQkDQexSzBjBAGD iB5ve5CMbC1/w79ztq2x4urkeq0YhbtiRj1XdPhLMTBt6hdsYUOCrgl9sFvIsMhZpBzD8hSI5EOD ExHggWSZ08fi6OsQuGB+4yMTqqjwTT03wc4yHLA8ELcgiec5t4nqibN6VeJyUc0g890QUIR8RsmB y7VQlIMoMB1CeO2KWCNrD252MfqHyXz8ctHZ5Y7avgEmakWfUOzlLO0cGiUbNrtM6ejM08WSpFGC TuYyf6tu2TPvPKgNov6/6SgSOLZnpL8JIQlygAegqgwqltA3W27ZBYrUhimNFBAmruwzlWO7IlUu rMYfGTBMPUmp40tRvHcQ7TxQ3yp9Y/wXiILHNQSmOGVG9wPDH1UXqzvmrrogQHtxviX/kNwj0h2O 2LpoKhWoECB3Lr2pjuf66DQAzxG48zmN2jDPUPjD7B259mhwBVUGIBIQZskPd2Qwu2jm3kOzvi98 GOnbrcxDKTcsS9el7rhXP6x3gK+HVsw0uhg0g+SGDhDHOa7HYjK2JH2YuWs5HFvfSglQ4gMFiHdD 3/YU72vaw5zrxS+JSs8JvM0PLlulXs1YWnJywnflvM8/n/LKkd2BxnFPrlwPQRP02DZ3sVYoaYWx iXHHefLzI/b5ze51ZA7qnVzCzT1FSHYP9+x06nahAYi1s4EZlEXOq5KCIw9zuHdzhIWECBAtV9b3 CWneXA7HZYI5L7lFhAXMRddpJp6DeurD58rWKDV38gyHSU5kHdDqnmitRbWG8Rqy6ePENvdW6hyh eUAbpRvLQppp3s1hKASJUsUJ2N3WhB54pgzwSJaRi2BLaBg4YTaQDKLnGs6hpDSjJyzAwCoRSbUs CVizTMU2kNiXeySlKcTDAfq2MV3K53DbRDGTcQUMQBGCmDCDXr18cN9uOFlgYzGebK/JrR3XKzje kW+QvWKDPd81SqD2CHLf65pkIlLAZEEeQGYG8ax+Q01ZBQ1BxO2kOl5Z5Or+z2p/C/ma/ppT/sHZ DEy1VUNm7NADz93LPu8h26cOzJDqF5T+pyeb9hJP1j+gdPITKXylLOGMIfWu/cBgBPkfX0Vv6/VE D6AjvmpKfIoCCeXBGTV5K/G1grBABzfFPWTd6TfiyH6Nk9UKAyFk/UeYYTGB3jl2mDBPfML8errc ikTIpXZLRkGLZlZAkoF5iQYHSqnG4deqAT+wTUD5CwChjIO+MnH3nY53t2Yhu3dxc9CSTeCW+/zm q8cZN7/Vn3nwUaxuc9Yoosl6dp/S8t9l92ZT8m9yKu5luHIUZk+gyO8fFRjDD1EDefZrOZ5Wg5Ci txdsn4ms75YCQLnR4Lm+o610h2XX8nM4cnzERERERERER8R+wMECDlaeHO1PbkoNlzkFCqFrSA2F yIIPMD6iz5WVkDuhhFHSRQdQyUkOaeMMxmqP744WYvlRmyktUb3iEw3dFIcpvy8yUVfW96IzDgHn icJS9O5WJasqvZy27PWbLN04YX3wOKYoRRek5qG5axn8TJ46pWxq6LU89A0uuNGpf1u2ghgNCPgw g7DLXCoMFCggEkX2if3kAUG3L4UlEIEDxNHB1P7WfK/OFnHC4zDldb+Vf4O9jLFDjrOyYY9cl5+d RSKiDCokDL4ZWl2I9FSYKYJMzL3AqkAwi/tT2bK5vnMct4XZahpJPDtTsxhstTfVHZGAiJFXm/b3 6MG9DbS2nHq7+8Pf3WebGyUsWUE8LKd6WeYHbiVfVbD7js4XBb+zCHpPnYUBUoSZPpNsdLMh+jfW ilsnImOKc/B9NHwv4Kv4G2yWBhU/KQi8v15R228cTbSja0xQt+npuedO9nypgHQH3u5gQM+DB97f JTQUb4kLVyghVJoOWE+2kHkMB4w5puwCXLfjHiGq9Dov+OzuuBP+vEIEHfkvfvtHWisu8O1Exi0E gt45TQA+ASL2kIg3T95R70oHoEEQ/l9q4RhjzkYYcnuh0CTOY16eOu9HFOznPpPrPJZe3dDQi/1a n6Kjhg79jY0b+U5niPm7w4cykj4EjiyzEwEkdR6X1qg6QUFxBKIF3CAXo3IX+iKeV9T+J4kDeU3V cxHQ5C8IrTnz8cfPMR9K9yJyc6yeetT081V5x/rBIpwo8S+07gd4pInwF6VDgG57pCgPxj0AaPFt X3sjt2cCcn1mGPPXUUSIeWOGdKBJnT144gxgxe7O29VkER36F0eOxRnJmaQcaOKhnVuA1ElJ/tfx JGPFSqYQz0MngezGDOdNDUkXw77ckDWogwmZtMzJu/bUCtw+FQF40vKln7L98MHGzn834xUr6Rnh IOHoYfXam7J5EEGHPqceiRJvx8tHTkqTf4vP8KdpPISnZVJ5FqaLVVVVVV5ny7Qta7t+Tcw73iG3 xxo/a+mbfF3dL6NdJeGx4Kc9MaNLNzpQkc5QnNXs0jUWlsnf64VBbaqCWT2oQuOadL8YnMocxZ7D OB6601TX76vI2OAfWI3D4/iwHIV9WxvoqiujDtREoIUKULFrLFD7xlFOfyYjDLTYQvosg62QzTIk LC1CjrKsUDC2miNirEJKBVGyeoxO4/UHyGejR5ZQXEPHK1713vzAAyQnpXFCLuelZDgMnww6sYwC wDdk2AFCD5BYDgUlfYWXMyfdgcg2c+x2uvPLvZmvHSGZJmZvmgHQJ7r8g8VDgw2KHoMqQFenkHjD Y6PZNM011guhUonGG767m2e3Srca3aWkYQ4znEgWPO4qwRWMRGDEGNNHsZjPuy0ZavfQ+OvTvtGv M6yIoGGjhAPVXKFQw1Qa7ej9tfEN09fNpmLeyJ6uUKB/KzzJ4krmnUilO3k84r2q/Ex3fyVrtEeF 7dqohFM985DLseJ4CFe/VoxftWu7y3ZTxBFaKDnR5G50s0E7jvfIKVYlfQ6b68ZtySZ5A95YBxeo MlgFdrMNgoHOad4MyTJFVQq9QoG4nOddH8mCBBAUfvyfvPOKqUYwFGfoR0vgOSbVePIieYXteLtF 0npnfgGiNlePeT1Z198uqMfNHxYSZJC8KPtl+lLwe5J4lyoqMW3RZ+M4pzIdLdef0kG+0ephgOwq ECDEVxvll14ypeN/5+eL3gdkkfZPlvD1e9x+Li9t0hhhHF/HXeyVHlGPJI6NHKo5yc5MtRWA+Ywx VsMMSmYYVPxus6oU8Y8KiqDhGDcnjqqc9a/p3npdOeeuJuoiJbl2s07T3jy578T/U4Njxz5L7O1m jhV2ImNXOcV9l4CqZR67cKdZ01iIZ9TnXZ/byZOODfD/IRSyuCmPQjB4nFtqs9oQ2NU2iTAhWe/n GnWGMeQNcy0uPy78GDhzDWo4bNXVLx7lm9eNNlIXJcKFUEcze/w+p4w2mXcRJo4G3HZpTkzyWdse uOxh9+kZ4sVxFPmTGyOHHbkdoMNW4Phk91ZjjhiUMMYeezXQww9hzCceTcGRQjO7lXWeMao+m3St LSEMwVecJ2pS0c1D7mkiWtZRdE1i0PGnwrfT763RnS606wyATI1reS4GHNK5f3o1J+onsuKrutZq NkkmVumcsz1F5rV5vJqeamuI9K9sNjjgdabJi/Ji9Xx5MOzPlnLzJls674lQY7Ht0U01DxAVDEfo mGXHDr1y2O0ihuo7wisLon2tmGJrPHePEPssRqSMkZkoeFhI6mV1i6mFBg+Gl7gTfIz0ezlGO4BN imOZOFtpyd+uViH6KOTsKhzU749nQVhMpT+mVaisyvX4Ad22+oDyP9q9JgqYCQxwbmRCiepjzxn4 8kLy0xE7QUKeFXCLxtmpByIGlvN5zxvxkkYq9L3HmdIl3DQKx7aGMQnYdP63vpuK+Wz5JRxuRWqz C4TLlmMSiIHw6HDoA7UXQ2sryDJFVDJgXBRF4u+2/unKdcuIePApQRBXaaOG4gRZwJBuo4BKB5Tg ZIHH6Y9+26C/Qjnm/nBFcfWP2ifPs9NH0V4P6Omgnv2dDgHAgExnmhDh1LmOkCUnEU3azp8i90Lb tecJxi+9ke7so2z3rAIOwqdlRo3PUF1r5Iifm+Z0Gbd4PCe+0xeschqDy/gO9tcZfqyQ1xcYbvZj /G7Xj7H1DOfQTc6T+e+vNFecE1TJ9mKpCZ6ujEh7dC/lIR6S6Z6J1LHYC8H4RbZdmls4vpA5CpPP izxTvG/qc4cqqBUZhVUTLcdsnBxJGcXADQK7JYUdvLDKmyEaDbwzy4wiNJZ4eJGb49piC2OYnKWr gw5Ksn6NR3SwhAVVnjN7U4abS7Dfq7BJgDQXk9JBWhj13eNBAfqIynz2+nY94FIqFcTtdtVeooI6 hVMU5fhnV/AZxbqUEovjCe3yWAcQ+DiQqQGYVBEPfy0YVEfbl7TCZIRPBhj+ibs9nfp/Ga+lUk2J iULc/gZdYf4u7Pzxiay+6SA8axR/FUjyY0ZJP2L+aR8GWykAjduqyg0KNRXNYNee1+Lq9AgGHcG6 FUPV/dscIuHJRZ6rWSjH5EZzH6pXkDF66LQSVCkIdoe+ujQzookdL5I/eu5vfv8CuXEjpHxfH4OH NJKxf19/6ryFXRCHR6fROJ0h4ZIZCmn+E+lMbMUg8ZlBKpiHuI17Xvc9s1TyLDu3mwcScS5vruAo wI7+tNv6O8R0AYK3yo4cyDh4ud47QgiQN3EzCP4t07qLS8KCJYpM3ISAL07t740LoCUSMZ8JgrPl 0fWBry5U000iidtmY2PuZJOSijHMaOC2eR9ZsoTKIqJcIduGQjsWmJASJJJQgvEwpIyKIrMP7f7/ TpiAeudJcRT1SSx2s7JtmwLqMBgigfJAKNAzauK9DOouyowQ0z5SiMppMOl4QaCetaMscc+3LaCJ U+A1YOTvXDa70uIJ8upHYWXtcTEOnya/4ruX8dcY02uB/uzjFxybxds6vhGx89NEl1wpD/NASf4O zDEgjXbbDvcHt3LsxUY+233ZpOHXGzaijrDhNjyHrWGvlOXSfD8k8rZ7IYVFFPxytMhh8XI+TzbZ sVdvsM7zphjeVQ+mCuRA9sfKR0xGtfHfsl1zymVqLOSCQH2jjIQevf5Huo71X8K5qfwX4LS2t+1p yYjpCuDspf1Z0xzyZKY0BdcWxLrZ5wE5xno9x/phMLqxU/jD2jw/qi9Swuwhz7fs9uMvEEyYPXy0 e2BMOYHggRAHrqDUB4IBHnPGPYuk3duxxnqdwzKNtsSpxhpCeQA0nOT2u53hfFIZqkIKClyVQmZJ JQvpptsevtJ28VR+H2z342NfOentD4sIQS0RDDG+jn56dCr+Lt8RbPZ2sVvR5965+n7g7ISD2+Pu bJ7cjGEbO39zn8JZgo4X5cfGHSRKEITpiMu7gmCL7e7fZnF3BxiacxD8P2swLVLrDfbcljZVp4Hp 2YDVOm19emr5+hAKpR6tRCJSvNbVYxplnCgZUpKS/KzEwYJH1REx4IJzgiQaUpKVSYOILwhyVwGA JVxH0EohsRFw5REcf3dBgW/Su07njE/QJoiBJxQTDM/dFUDKUXURdq8Yy0D0wMjw98wdgbYyJ5oz Zvl9OPlDpUXk5MmFiMQwUCHb5NaPHZ06jKNz2DVRoNvtSAvlbzM7ibvQ6J1rGcpP8/WOQxoMhATx UgdxCpygFeQu7bMY5DlFH6+3s85xfjtzr5XCRk67YlHKimfkKQC/RMzlnOrcA9HLWttvuylUFIsQ FJQUQQEJJAJBRCL+lzXInjoGneHldrgncI12CoNI4VhKj/vBPXlBg+naFjUsaxMDXhGCEBKgooRE XGhWJ+zcGNlRAjv3I7AvTotSeCtsqtUTRacLLwFeMggQQcoWyPcrmRwUfivD0OnuOhJh+NFeECCj 8DGiRmM+iEslG721gkoAwWahqtfddIYQC/jQJ+ZdhVoEd4vpfDCgedZeaZCMvRHvCBBtCuJKdqhQ LLy7C7RtE3iAUgk5wyKREA4AmEH/erjt7PDZ2T8QU8pRDmH0ci+H0Xyb8+3lsOFquEUaVIQlcOT4 Ow5GGSZFMrZJOenC6l4O/avJoBaceSj6eSmRxXDAoikSR/qjGKIOZ/U9Rf4cYew56O30+HovwnPz 65MbTqESvam3cFJ1ZFCXKTszTC5+ckSudjDHUZOsGE2EDvtfLsotyOzkqP7Efz/gHXPBl/JDYpHv Z9F2byIQ7px3BCJtw/ZYyF5Ve4GVM+W5yLJVGKCxPVQpxrMEUUgTTRkUy2KuWgolFTAlFFExTEmR hUJElWYZFC0lFMMjQUZYWnyF4zNcTfGiGnjG0jbJqCORezKom000NsAV3ZKZSsqFbaI1KDBLSoKC gpaUKkK0QWjWFW2xRYyVqCyZlxzMtuNTZWCLkKlKFkWEtCxZDFXDMykqIyK5bFklayjgwwrYeWjW TUJGKCVCYiwcrDUbMJRWS08LTFUHeSkxEtoImC61kiZhTKk0IUXVhDIoooXUBlkOSEx1gczBKBfz RbxCGXTRr9+BJFLQ2R5It4nP8FKZ9mMOXSkk+xm6aYG1sDSGaqhWSKEnCJ+24RtDS9fz5qVCgQOy cneA4/yYm0KJvJw7cAA1C/1SgG8EEgPZEGg6bgba/A/a0hHqks0W+cb9y5hgyUiP9LWGNRZidQ0U HUpfE81yc9jMFlSMGVJKJvbFMGVlEUirKi0aIZlxayJQrW05EDKnrOPVAyXr3Ydsb9XbmnWZzDbD 7NvXJ1wg0CUg0rSh+y9sDFEFUzsI+nfahCN8uectvmZSfhB58VAETy3a7I/H48/8fxWiC0R1VcPt GHO7hy+I7gmP2Vayw58g/HH7Z/yxpOgf0dtZclGUHvibl/+0cn2CV08yfBz/VoT/qQFM988/yi7E JcIPly5KapnauvzOMY0hwhXLiw8tGo4ceNvzQx1Gh7fIi+RERpUKEfoBnH6SDP0/DgAu5Esd/5b8 nixqhqiO/nOXnLjigD0qPxnr9f1TltsC9/L+Y7/hNwNoIH+apIClRKMeXl9Ms/f2Zn+nMU6pIFf6 cx+P7vwfs97gAg/+MhQRKUP/CBQPg9Y+67uJ1DzibsRUBEEBFgColUNQS0VMNJSFLTEJQdkOSEQU AUoUB8/o/FxA+QA1B/p+BUGZAT7+XhZw+h5EJAsa8FgQpQACVXu922kf6yOyrsP7YngIWnLBPLNV xPHuoD5RN32f+H6P4dB8HG2sx+G1Ze7H0bv9Gm/10n5aWggCBB93+X2bqRhVzjvGvj5INSCEQ7AI VL930bcGGBzd2z+0/OH9kH/eEzXEQ2eQoO9AzrSaNWn6ik0zIs07a1v3/T8OSfenkhK/VUVSg0I/ Ep5uX8+lf5eHgpUkBwvTAdSeLdlPCQmSUJT08TRzkgAMiqE+H836/m8cCg/kN5Xg/Jbx+X+lUfHb ln9Th0bl6ep1Fb0giXYR8mCADLy/ckv+JmLXMh/q6D3FdP9zOKeanEsWj69flvq69ij0b88XUZ/x xlzV1W3mQY8DYmZkeH/2W5p8ch30R4mNRoZPH6PkO7y9OzxLNB6gFPc5WBP+3vxQMRrS8XDVdzAJ /SUHL1P1/7R3qPoN2n2z5+jia8/r/T7U+TtjH0dRp1yVi1QuZ/XBxbINFy2V5b3+xQqOIl6p+9Yw EVRQr8tM+3t+vEfonvRIob9EHwSZH8/6v3dJtHCDgHGcJbG4TqL+XGjBT+dgghxkvVJwl+RCkGJo 255APn5D8nJ+vo/b3dz//4jLb+XJTb3nT9X5Sj65fy/jjkM6LH+GH7cn1lqNhRR69P6ttcfiNgXd /gKO8vP0+4PHzVh8Pb8fn0A9HHH/Uubfdz93jPcGX5/mO732+I8wFSpwHYPXX1bP8/CMqv7McVDe GKoGgf1ynZXJBA4dkv7cKQdgSwZfF8qeX4wDv4l7fbab1hgKjD3esdDY290UEh6Ao+2ZK+7C42Di Kh+tzYf/i6iD97c/iyYTfTBcJv8h6HtOM94l9cXv3dURL5RRfUL73JU/Ep7vb7pOCY7M+eyd9mI2 bXUcXb+NuPQengHUZ8uxwozFEHWGwjWDR8O+f+7BIBtWC4L3JgAqND5QxrEM9Ii+uWID3rWMTgHC QEPxD2yc3+1RSZDvRm1TDbFY7tIORdx2sMhxhsf+FbUhFnWouihgiQIGDMd1sIPepEZ8Pk4VfaUl +GDFBEO/pPon7OqvV21Y+Y/vv/nt3+qMt/V+H4C246YzYjdD/wXdwtgD8wPx34c0sPMgZLUVHe9F eCof/P+OvdNUfdf7iBSYeUU/rMd/b/f+ilUcn+l7f+X/n73en4QYTUMWm7/wbd4fXv9o+vUc9vn3 j/X9i+Bf9Z+v88l7rejF8nEzE1GdXesCid3+SX9nkjl9++NK1XZfb8/hLXZRTOgK76e5/OKsyuEC smgcZiI4uiFLtilZxwV9VG+evFXKvywaUcOXvjwEcq4eulTGpB8Hr+0szUgLxBwmMdcLAu+z6oL9 HSBlV2Xa9+kO8h9HW59R4vpwL6mD7stHx9ufOfhqciYw5Ym/15cpT/bP2UMepon2icus36I5XkME mCQg/BJzxMqGqPuj6vf3ilfOZ0pKQhKK7ISlsq2KJ1eNXjqhCL1HcFx2qoIJOH6xyKR2pz6ynk2W Mv/PisTCNP8I4+UFYXjvn46rV4qejJHaRMYWlOqcBGfiQ7hvi5BV5BQlAoif59P4ISFNZ9IRYw8v ToDX0JT9/f/2ATCxtDbLyDf+bxnhOTknRb8uGwNc3S329fGa8Jkm9Ro0pUKLFLUJBAoiIyA+n/TP f1bdLZ/7r7PH/08tuZ8Pon6vzfMfm9/8Po7240qtf27OrrMd/wHvqs5h+/i/y7Ij1S+Mx/vH+IBH hongI+yFf00lVR9EeHye/t66+IMSQokJP0c/R/R9ZqUdI/GH9wHj2e7FufgTmlB7kPkA+QhYxLoz EK/3n+j+6aN5IbIJSVgUm9oRMsb/RS5G2kTExE/8bbNRUpv/l/yP9YxoNZoNiaFRpzuKZjDExGVl djDP+Z/0nGtblYIdGtf8L/xqzSjETCB9rO2jSaNMJ/1RR/8Wm9qhKDGtf60xyItsM31MUf/jz3GG 4gnaykythVVlkQpBEstOloDOJclolgppihgD/F8OGG2RsAdk1H/XZWQUrHG6ltPs2w0KMhiGn/aN V6M78ojJkPQcZoagf8qVENtFypSNVGShWHWnWh//LAbG8cND7WWMIQbqzeeN5434ZjmPNBZgSXCx NOE1cPV/bZtsVWkqfjTFki4X0UKYi1BF5tnJ0jjSQ6PeewOlMm5E4JIhugBw0k2gV2oqPGgU0fSV V0ZBiAkICbFGCFk6upIGWKFgitJH6WvhvEzciVBXn45LIIGhI69ZcDzJXsjt3RZ0i91J/pZHkm4E sttUVcabjbNlJXohKRyWr+Wyryaqeay+dN2IZEthRkWnWUoIqcrxhghtpJYVnRMPVrIjUDTMHYEo 7BZYVi1cIBRhBSFGgehDFTTJ32nnQ8WbaShxmY6cw/xfufTr4//UgInGUiX/hoNGiD4QhNEaBR8k Ki0qhSr58ykSgapURpoQMkQyVYkQoQKFENMKuVCI0qgFCL/slUaKqFlYLGh/If9gOhyDhUezwbcX HpJKn82HhpHBYDrhoLAcUqkDmYDgp1DH/LoOAf9SV+Y+gwCP/2YeLvxUbAX965AEZDu/ubP7/+6w AdqlpfP5CB/HDQZf+F8spz/nH4v1/1P9/7wP+j5H/BP7j8wD+D/gqi4X+TJzJxF/jLymdJJt/axf 7eFf9XXMuVcy2Tidobmwkh8LTTdLqmANYbRSsFilw41IGRZuGOx4wYHQ1/59dPfO9Dt2vf0KHJvD t8VJhgfm5UaBgaGfRrC6Ybaxg3jnG1XiP3UKcgotCGXvrV1ZmQUnircwEvoz+n3/7cYm/fSfZbJB 64Mi44GdnZ2+jcIn65OERLf2QY7Y9Yefsd4fj7Pd1/FcOaFFP881w0mQzbTQHkgP+gCSFSFIkgFD N9tgEIwLSi0wSlBEKEgf9pB+cjIH/sMEE99UB+UUaiC8GJ4ONrMtanyxK0hSkAQSY1YDBARBVoIp qKSWhGlJQjIoXIApaLrbBKWJApaAkmCCkWSBqBlCKv/fAwfI44sEbxkBDPCDEiKmZiGloSIIFAhI ZWYJ/vdkGGfZ5Q9F/qKyO77+z6x/uH+ofMYjTr7f9Fe9vfn/LT0XGqtPo9kPfhv/1fQ19fWv9sf/ TPh0in+4nC/45dXyPctNcvvy4c8RmrncOWFGi2a48oGgvYaboPuNmPS2P/maxXYtbcdmf+L/3R33 xeMn7X57PqTMU8/H/u/4f8+FuN888u+w8Ol32hT7fT+H7f2bu7lPbmOXXrj4rtpN8ab+jw6Xjt8v f+eAcn+3Z5ZYMuzNg3pHSOa9ERAbp7m0HrrHJI7PlGU+U/zdrOnD1xGLx4BWtPf3xAcP75wj5GZH x2C4vzydrhkMabOx9xIiHW37M6ZL189Ol1Dwf0OyHP+e2Ex/zsJbSpd1f7el0u7Dp6a8/H29G6iV 8ty9XVu2Z+/rv3/YIezz+B/uOJ85/qPuEf2kfL+fyzTlThAfqkNdWP5pD5bcgNv/zrP5R2R/n+f/ D1eD9I+z64J8tn2XPjP/f83CQt59TTh/wu3AP8Xej3Cod+I/VhEfsLqYgMB+vPng+J6umXCGy1Nk B90O4Afqwt8Pzd1Jrsinl8JecXw/8V9BH/p9v3fXpDPH07Oy+m2X1jojQGvD/Q7TJ/uIdh6h1qoc MsQIkBOU7DcP+I//kfAf+oHMD5jsDBwiP/UeIgGH2j/UPvH2ggQDBQPd4/T5/M/L1i7cvLmO7p5/ V+R/MbzxI+aSKfD+Hlf3cM8GyvOP5oGo9UFNpB+KJB+xPDLlwv+/8K+3opKSz9+v8Psl9k5YZ4fv 07PP5d3W9973ve9+3+f5/l9383P7+/8m77/xXd158/P0dr/L5z++3Q1fF2OvXu//7d/VHn5dfo+v /32f+vHLq7f3bvH5L+fDPJ/af/JfiB9UUHfjxtXo6OnpmB9fTvl9rwnhZXH+3/kqe34u3s8+74tl vj7F6CCdpBPfjm9t8iKDlFBfNAA8f6bWF95AXvxENIKEhqr5/48CxPbz13fTReP7oZ9dHVC2unay 8pHnBYCH57BYCwiHbP8MG0leqcl/igyVLMMYClEyCQrIKTcYM0F9b3Fy4fwTLECp0y2GfyrCz8Y9 sPhB4kRPXSLIIG57ZR5JiaTUUUOzSiOkRBtagoyajNGU3T37JPQtnbDoNasGr/oScemd4iGGms7k MBFQU6xQDUOpwGVirK3xEYMbW+YBpZrimt6F2ixpUKUEiIauZp/eIyedAw8QxHtea6JKH8JkQqM3 IfjSBxc2n+Km0hno9PNvLaG3f5IlnVV0bAyYeEUoU7FQcdf8/uLsRi4MQmoZqlAPq/71sR/wALw8 j6cdXsU/08jHhT+eO20xARUnjCf92gPn/tw27C9nTOseBqIbXHwiq1FgOiiPwQoL1gf9boN/zCHD OID2TfPZcNq/HqmZBSaqU/YOMZ+nz/vx8rDk9GP6LJfr7fRo5DC+GZJ/kGRQnH/KmJj/qXAtMx8k LMkMf8NxG27ETPX3N9esC9hR/yN3O2KfRMwnKrtoVWute/bQqupPKZmxwGwbrFEpzf6MEjmNm42G rfglpaoIorf05m/hMayD/t5H+60B2wX4M5zmyx0cg0coWCixDCWS+mDnASGjhh/RahLW8QEJt4Pu 7cdHCFlnXKM8WKCFSkST6MIKntiImlk5FNMm07tqeYJ6uGeR4DOclceWxtrZZUIZm9kwYd17e0f5 GPEZI3rcSZWUD4DpM0eubigh46/798qL2ZzXd/KSaJH98P26bp4+KZehAwMOkbshCMuv4RJRngPG YhIAZVm1tCubH1sPLHvR2BeaFl+aLlPXFSQWZf4/0z8sBly0/VFNcAOJtKJIMDWmmaapM7GF5ssX lwc2eaAn3xdIJ7Z5/TTUh/pV4u4hkR/2fPgetPpH4kOEYedJZKB8zAULwvqPuyG+1Gh4/9CZ8iDk ROegfWyge3vOz1PjDfF8mdfe9lPmPTnTDeFSQ4acH0ZA5pOTEpOcZeIh/mCv1rsLBYOXH/qloSE5 i13ryHa6r4kNmPCbT72e239G9uIDSWGcNs3Dgw0n4ci+SF0gnCi3LTo6mjhWw7rpaOEz0tyFV/bc y8Wbr70MxVIbbLZ7MUdlLRyHdbhynPajKnWlt3SwQ3GfvIEncyT2XgpnmHOoTyxAgkbkaAdJigSx HO3OE4SAfBk5y6Qf7PSrp8DABPXKTD+63pbGcQwq9PQGt7i91Q9lIyaQxi+DiiPIeWZm5gUv3dqx jj82MKZiTpGftmsU2uY3GTDkuO5n/wpuv58XDoOm5uJAhMR36OVmjw75jxBKBKU4jaghtDwzQ7+P a4UnnBsT5PRDNgLQdlf/g7pLnhzSEmGN4cNdJ4XXDOEciI6tL03A8fGqzmUSGfHPCnX1uzxu1GuJ 19sDNkXuNhepg6t/XxF9sIHdJhDiSSTMOhz1A9JDE87kIgrkk+fw6FMfBN2PD8pUY17sNF4KbaoS KCoO5A5fBnitTajezRRKaeBOitSnG96IuD0gvME+39Wg2tBE/+NPXw3dP9x80/Rvfq/jix5oDiQk l3s39TY+2MfFzwsawZIOvXDXcwNDNMWh1ulhSEqwTCtG8jUMXDvJJMt2UJhKtM0G21okMZuTehIS 2aLLYURECibkDjlT0IjODY4HbRJDak32n88T0l2o/q6S2EDsmgQ4cjOVx3288pdbsUmuLTx1juQe 5kANts0a0S9SQVOCl3avmp6tJCEOQz9WNMPYd4ySZ42fB7zVrp+KK4MjabHMit3zBjCfatafxG2U MkyY0hh0zU5xuMijnvQx5OvBwhlk2wFhoxoTDPJcAaMwlbcjvzhEDvooieyHqECpRMcjhUiYx9u8 HKSHU44dM4lli37MXPIjKc3yLCneFKH1gygp3CjZp8sV2xE9no/1dxhtoO86EoXSKCpEiwVYjzx7 +ijk1o1JZwMvViD1YQQwjgbDIQM/4dxxKgQsy4Uaam8/DXvbTz5x41LPFiTHq+Im+xHtY7JQW6Xp UyE7IggLdWvgsq1zxljKZIc6znMd9cFIzqfQSE1GbjPr313vKswyo+KOEidGXVqvn36OPJGvjVio r0O5vQ32ydxmzPGbY4YWmHZHUUu9vB9mYO+yO0lhsyj7K3nrMt0sHoSIelrxpxGViHQdvmZolZS/ 1h2bauyhcRkCRGW6WrZ9Rytk2mvLXeVDZr7+nEdFIiqMUDA4CVtSHjWAnRrDUVoZWuHM2ZrVmEVh RY84fn0Oes662ZVCjbuYxKlawOJtuPhyjD9EFe7wAqE0siktmEak9p70bxjLeQxtyFsmlAGtZBLj IcmEHo7e6QcAvTKgJEqNqJ4nNAHUO9ZZKCVJCr1B4c+rKWxCnNcZIq4WrqcAu4NNo377Su7GnbM7 dkezxI6unBj5M34aIxCHeKPcg9gRo98Vn0gnJgRllhKHHc9NvnLC5lHRtXzIQHgsabtjYz2wLhrd X8VdgK3dxxWgIys3KwjOmixS5HSnUmAFsHgNVVqSqm27Bo2nO71m6PV5vxjA54JI84GXR1WTLc9W I+f1YMH25+yAR8/jOLx3Gxur9e1jfHWpPUhRB7eCHGGhFbHQD3VuHQl2XMagsv8M31Dz6PB/hQZo nesvUCfZf/tyaOmUx0Xl60ddNFlckEvAE5zfOzmmO0vza9HWD8lFLBDY6Vyko8kPdvLuqenzp4ZN k7zEVVVVVVFEvYHmm3LS+mPR2ngqS+kQz2cuZWwYwpft9XHdd8PBmwoOrlHdGUAkjtmOY/v/sOJU gnYVIJzDkzIt51FwNzIBjGX1GgvTaHRKSJkDJTRRc5ID9JRZD+dv8MCc2KBpNUTkuo9e/H2l//g/ RW/A4LUPLu8f7ev0X4Pj9drMwqMM6KUrv7cDRPvQPpih9PcGFjMZ3YcqcXTUaI0CXxDygp7v2sf8 vv/RT/bwd7JpeQaD7O/wqjGJg4k8B0PGVxkkQYap3JOeircOYNxQxd8kPPB75Feq9ZwEo5Nv2aYc SQc/irV+WnmtdAPv09+2i/E6ZM9Rc3VULGONJvNWlnh08+sUL6qNIGsPypeeDaBUN5DxZ12qVHsK pqCrQe4L2iYEC9R8DpC7Db3OiYLtadRxGs8j0n/1N+3qW+tliIWBWCXL0fpM7sDX0iYrwIIKqXxl 796lyCXD4eD7I1dM8I80Z8J30Kb+rCsUvoZRJe4YesuhNqeXXzZqZfWcWZp1hOo+n1PbuLqeRy77 WHC3XaMWSJRzUloDnsyfFORxpLkVqApyZ2s9Gm7Xc7dmdv+6AYJq/yTkvlhkM9VIfJ8WwsuvKl4w 4xD4I7IbCB1EG4QELxM8dNrc1d3puey19P0Ub7XPVsv/CXrho+IkMrrmz8Ean1MP/eE0/fzwEU3T ozD2W5Dy0apjs3W8fRSBgiX2fkEt5dlOYYv5qrnDvTcqdhD0fpimhRwfMlk6FUMolH/JiSccai4U ZYrfK0qr6WTjyUuIatrQwgI+11tPNnRk2YzybA5Zhk5FEWUGJLrPC6k8OPC0h8UL/eJK5J+v+nPT EVEX0db7/Xr6t9+wHH3ByYkD+J1gXN3HUJ4bh1sQTELxIVA2/mwCkcyVNsRerXr3plc8EtaPh+Nf wh3IqOFpU6ZRRTGFwtdHIEDVeinJtR7EMhnctbWydISdjDhzkGbUM2TNatpHhyabHJMuYMWJsp8u LL45zeNcNiZBGzbQuPVTzbcjisg4CJ/o49Jo6WCT7EZTHwJZ8CZzlM55+R13rHjNn5dZiy+wsVJA dGE7PAoOfbnLLNXZzndZ0PmZqKyLOVqrFZ85tfNwfgmD0+e9IuAUKF1KgiP/VYnb+W8lfioZdlNk UH4TdkoOmMxlED6eQwc7QXnZO9YOrU+uwqWkIoT3Z8hnv6QDjPlSVOEZGQIGVCnyEhgQahOEpq0h AGgkIlQ7Ho0gjgrJZwJ0GMGDlTOvFySp9yhB5lAJlKHXsMmQAH4n1/WvpHte9VSoxkzBMHTCemiB MK/te8MsJQQ8Nhh3HZf8Fhd/EFpJZQZLJ2tSSUJrBlarsizLcUJWTe0tZe3eL3eJrfLvuZDZs0H4 4oBtgmNhwhnswlokgmK3FWC0QfevlUvt9fGmnISNH2fEZ72Fxg59dXZ4evNe9Evu6bOOQrKPMB89 VYPT22vCD4q2C2BpjS0lP6aHoz35UiyKQ/FQsKgf9Ih1QG+lbj99yzzcOJRUAykhzE6v2GmiNwh0 mhX8n+6CDiecE4dGPlk8IIkIiU1GTKqjzRMTASHc8I01WomQ8aQ78dZOlkJCOzdo7jtJQGVLu30B 2MWTlxVnG90UN85JxSw8W7BeE/jF5WAcIoPdwTJjbAUNtfWbhrUX18lE6vrl7iHHPcQEoZAh49g7 PzRTCOyeujJXWKWcjXCgdk2monsXdsUmOMZTjwyW1eH9bdk5c94frdAajBQ80D2sOXP72nczXK8G mqSRoPg9Tld8gTIoANDgGGreFtZDIoPDf7WQSRMECqNMUoGQJEhBtgtPDdpVVXD91tNlYSkc58z3 bsskn6PG+LFiysWV7YOZ0zyygeWxvkonrpgWhHfrKE8HNXDpn3WsQ0IqKJqhjYa7BpbGukfyw/zw bXOUMgSkoKtZ2snzyZJ74FS8QcQ/bfE7PJ3i2RMwKQlUbX6vrsGO81iZgYJ3pPvy+WdfJh1+PAi9 xI6l8p07H35P74G2hJgLE3ps9A2J5IzwrTcECh00UWjOSHVXXdvM963JKDqmgOwhevsNdDWyYIbb JRSJ+FBuYJVRPdcNCFF6py98P65C5u1FSTMzf07Lp8HpmaHnvR+4iDIoBgUGQ02XmP4MPREDAakK CK3KVWjJFoIGKQIQRBYoAHMoh9/rZORxB1kPI1rjjAYwkiU/KYFAPfjbg+z1fsn0jbWCAWLgR0md 1YHaQiEhJVgyLBU2BbsiYDkoGGCoLuUUONn2s+xtabnZMqIIkYI6djwjg6emanbvDihyz62ESxSz nK7z4W0/pF4uUcc9lXkvSfv/LCTDySz9jntgdGaGg9dK/fASIk7BV315+0889I6BSbvAgMOvlqQ8 toOhoY6Rw2p5gSZLFM34Ib8VMHrDw7713CUKFlL9LiGEEdW0fzPtUroNrod78Elp4EGvJhWfv7y6 tEc94H+p/sZ9me4q8ekOrEjlj686DEDB98tfkxwQwdRz2on3P3/+Wn3mvnhs2GwsnmTT+94puHpT 4fE3whN6E8Tx5uG26uMqr8WgO1CKde+7pKCHpzjNT6bJphNPq2nLABT9vbd3yAPWDqkwpUVVPtEI OtGHZq/N6AKMpIqQ0wAZXp+SipDdEd5BWppw4mJUkZwC7h8tX42YkO+AhUIKB0QPCJB7q/Zn6djM Fx+XosW3ygHYRtP/73y5aQrp1vNdeUiJ/ACYjqUiFTqM62QSQS968MPc7xPVTbM1FrB0w6YLwj0T 5M68FNAOwiH7SIJsinA5KhuDPB24uFO+NCoLXXqppPY6fgTn4Po69nawXqqJTvUFSfVb6w1NmIws dwtgEhmTft3YBFFgegQCjO3/rZ6kP4D3l7KI1+8sA/TpG3Y70G5SRTIY6KmyD0/AgOepHA9/o3vD uKpy7KrgsqsxhMZnmaGJfVmD8duj+HA4gfCgoAoqsUDyjFK6Lp9hVPbIaauHfVeBCoFHFTgHD7p8 vnj4zbxOB/86/7HdNLOBP09YaDWDAuH0keL4YTDY5eegxZrErPZHsHS5g7+ev69h9mRkReUrzJRD pdWGBQoeSAwhgqkgpBYJIvjiO7Pus4npxttoQT52GUPT2mtnEIB8vD/t5ZdM5xw4H2H6q0EmlyX/ OMcQVW8Q+wcOyEB/HHrvyjjND4mW3jv6Q3NhhfSeqAHmz7Jq9GJpY04E1Ugft+5kfuIfDYr/PB+M TwvpfO/297d5OiO74r+GaxXBIFChBKEoASEB1PDCNJC5/R3gJPN4NEeAGLtjlqg6keKiULQSfQOm i3VcVffb6/VJIcC/EPFiIFTD9+4j9G3wyDGj74x+udd3PEaJJlDchzPph9+USJCb34ZcR18WF8oW k4Cst92grSUFbojICgFKZ8RSeV6AyV78uOxlVbgyGuZOeKM1cIBjkDg/Orta/9OsfkAnRz2jq/fs pE7T/4GUM2+30qkt61LBVETIju//E25Pp1OYfliw8i73KBbzUdscXu/16KO7/S/8T074YO5MNrtl XY4LMiEX/c7/z7O9ggwdhRl6X4m31f8A0HElQCdxHWSUoTR7M+2fd/yEEcyih69y7DatE4LhMwfu 8HS/+/0HWoU2FBxH87vP/u/XFzsg6KtOFYdVHJ6fiaD/vdhKLQWwetJwgRVecnkR6HzpKYdnludy 3eju4cujryyp8hrwbM+Bqef/H1AfJwtzFU5J73np8GnWa2n73dP1r3MHsQR5kYB3bvQfaoQdD3Yu HsBJknr2WaHjTlB2Qz4tusL1xPCCAlVChRgFHo5jo4YV+VBqZo4iivQMA8vcHs3ruw+oDiFx59U0 gBiow6k/TV86LxGbYawx2BZBbtHQ/ZpJOzjbD6L8hl5XCYDaFRFqPp3XyQVQAjf5KJ6qLS9koPtd wAcQ7UFWHVjhv5xn1S6w4IqUQqJyVBkQigoHYn/eduT2ArB/TWr67tOl6U9JoHfi9x25hs5G//ls 5g6TteJHUcOLpq4hhfx/mP0+vWO5KMKm4gTOG58lfOe0tQewYbw3OnS0BkTNMsYd8l+vXPlH1aPw vNmTrBJHaNPSmt9IJ0+hWqfb6+5/1FHkf+mTczN6uDyw+o9HAxY1Ue3r6Kj7t3Jrwn6ubb6G4xBC 9MhRfVNshTkCvkRhSpNAMv96QAezx8JJnmPECuCejJrM7liA4NBNrdQ1HGCxUP036uFiYJqm3SoH ZEV+3Ja+yaQAkiO9WTnEI3gM2fNgyAL6U/7vuc71/UqRHieuDj9iud/R2/lsh8h0bgybT3nU+0Bh iE6RuyZad6yTEIXfUpgooW3BI+LnBV6bG+goO3/KGMbgHXGOnUCUjR/ww++dtzk7tgkNiOL6Yp66 sRQZy8nefr32ggBS4XC3Orl7/lr5Bc7Qn3Tr1rt5j7QPSAlA7U6mJBKXkqc3RyzuM3P+/JWCt+e3 7B1w6fDCAd1ovZZyfVv4e/P2xTwIHeV03DJ3gUWfcw6D98V2soTkR29e386NsF3mOJCXBTHFeOO7 qXoD5EQ5KERUFV+PXt7S4fdtVOooPjhyxXLD7vRrD8NgrTpDMCdpZneqNx3/d+zn1ez/FvZ9ngTf Dj9P3mXpt0ilmoo8N/a3nSYmoxn6xsp2dnGHDo8v09Pr75QiNwEEw+S+nl4DzVPvKbj2i3q1d1OW nHz63Aj6drcgY3AmD9aFBXPtfF5FQhfyJ7S/2PwD4ovM256+fBIYNGqHO53vWyiHr2jRUOVLm5FK BDY43Aigui6BVeihwmCDiUtV+JEBN18+v3FKLhvcTL3GcaF8yHgkYn4GeOxXSXY/dn0Q9P6bOA0U n1UF+HxngNlB0a8/6+bX5T4beTfrLs9w9o1GiEBuKgdFLOdP4/ayTfkVpnj5xdFZfOGFHNTtj9cF JpvUKcvJpuC0Rh72ar6u4Re9CSXGuKjBsnuoOyTGa+J9jlIco6RD6D3D2oHgXCjhzPiVK/g3gID1 OTOeMoj3TQMFnlG2jaOGO4DiiYoJB4B2ctLAex49GQUBQ7MkyefUaMR3nCC7HKSich2DqEx8fRJH HG3o9HgMZdAAT6CP/oO39Hx+s+Z9eKSrQbHb9fLXb8c+zo9PmbEKZXl835tMKLl7eOft8Er3ej3L z/0eEfD6E49vsLvy4eh1usdcOUvRjjhMl+BtbrH2VaOp4dnr9uFr/RAAOoED90fqB/HKXw89/u7v cg2U+/5B/yjjy73y4A+2yzLhlJjGDUye90VJHBUJZqdSmzOpCEr+moiHg3gdIwehm+Sr0ekVD0LM GBPuT3MbiWdyq1GjMTvKZg+TsqPJdOzM6xECNOfEc/Xs4HklammUw3A7rInQuL84ddFrvHZJ6vD1 0avT93VWcPXai14EV4yzxiJO6SeSzsbCSqv3veJHf3q6Hfhw0bY8w3XqPSirO/W/19+Vo9GyX4bM JVHXXHrXaEjw7tw2Z8Mxg37HQEPAMj1GwDocGDx0dfiGXuvpLib3Tg3ANmKSEcHQHTWfKwO6e2Qs 6twHBlCD7AncE2lO0fhX6znmRYIcq+IeA5v+oTPuSY0/h29wmM0QBCPrlwIx6+7pHT0v8HNxLT6S O0y15vUDPGdfxxw4BPmgy2KkiiDgRMegN68N37JVAsfL7O1+7guhqHLdvWuY2QQfGu09VbMBs4h3 qes8UTpFRz2IPwfHVMWHYXbodaQ+0IjBMfCTD7vrD/DHXU5C4d+I9LvQZ5qpIJBB71UYsdSwcSR0 bVq43UePco9eq6EY+XkuxqwgaqllUS7+gfHTrXsYSGEwyUT3Nz1z3xAxEFTDLjTB4hOm+ZcH/TC0 IF+kRq4N975CO/WbvAQgNj12pl98MRZNm2Xs7q1I3QmRpdXhuz5fjx6IdX4X6qYAbutu0OXln6x3 jeqCvVEDw6MxL6J/OfQ9R0j3DxwmMRt6l9mvo32lHukzU3+XtF6oBexTRMfVD9d3mEGGG71D8ZBA g7pOHaPXs39X4cLbFmHyHz3B9Dy2L2fZ7qCkduHfXo90Ic/x05i0/MGQz6uvkzu1czPoqR51UNkj QgJ4DZpxRg7vvUaTgEv+bggkQRgByBQMZCihkM+1cbKmVyUGVHycmOcxKH068b/CXR66YHghG23b 4v6n3j6M9gIbwePHy4u6ISI7/DpHNPbfp9sh29+5fd3RSmOajqyreShSnX7evY6gt0O9HwHqm4cj ZKUupTtLmRSjwbIfehPWa0o8Na9nzaNcT6ucK/WSzi9acZlGfzW7ZB11HM+V/o2has9/m1e2Hd9y Yp2d8vPYe1Hu9/LHPAc/Vzc3GBYd/ep+KKIx8AR1T47ex6OAeS5hYuHIrQKNIZAHtfv8WIu/00r0 kTJZejjoWPG8M+aK9/3DwLYWrZZcfA7Y9xo83PAFWLOl3DNt5j8cn9vCj43j0vZs701e/17LuxoV 8+72WFHrLYw9bwXvqq+lX9ro3et19Hohr/1jI2e1tf93VG/6LHb74OyBfm1nbM7tqUHEov3CQbfX tLJZ6xZmIBErPj4HpZfjslGYUPbj3djPfyjHYRXGX7Nm+rta1nrBDhMMQe4sqp0e6rTTtXLk7C7z y97Oz88MHo+/Bh1cF3n0Ccff6wFKcS6Gm9zpL7BCXIOC4rB4cHcdsAAMuSgJHbhg6t4jkUxO8u9C ysrsn+0dW/DTbsp69iP6FlJ17xB60P6iHOT0H8Ey7Rr+ameqI+z40fWOGdYPrR8NZRc+h3JPrEX4 t03YlHQ5pNCYpBSGeQlHqggh+e3h38ZUTngpGXsEHbSGINIKOja97ngK15uyiOnxijOkAG+OUmr5 srSlRqqxzLOwgtpwIYBnxytmIQ3ypiiX70UMCNTjoFQRw2NZypg5qdM5G8xhpi1HLBcS2jlwTNFc 5LssEAUjC6ud9fbk8WSGyO56uhJ0cLYuGes27+C2zTSAdhthBybTDhBkEGc9yY3tupHCkmX7b6RG WUOMwpo4Y6vkRpPa2BGhiMKTMLDDRYeBbsJJ86PXj+/nPA076qnKaha+pO2vHe+iu78mTs4BuSzu MIqSIh+bw8Is6rg0M8KbpPEuzgMMn40BnNwiJYXLEWOmijWVcFjeHUYVy8pJiW2XkKkIBSGvdPr5 9fQqusmoJjPv8/To1xvLN5Oi95egsw79JLycodm3xkqUVUQIpXoWt9imWUeDooEhoF4kDT7Txlt3 13STeUGSz29LxUoN0k4UDm3bRi9TVXbV23GosvRaZbfHqDuradmLhy3CVD1adjz194VB3AVXcp7T dw9GwfNh6iHmahRylphB4PrX34ybH3xYjs619KX0pFHhd+Tg7Hj1OD/SFHZHv2KOOOhaf1kdlFcJ 88uke+PZL3/k/9kPWzctj0Gvwd7uEIff65bsKz6N0BPYyS2+v4Hh9h2eR8B4g3wuBz90lz4aQt4Z 1h90U6J429Mx38fZ+TD7PqUerb02Xb2B7LtyYQVO2PtD3Jyhx7RxAh3XiCPgg+z70+CDq258PvgP +G0zVEzxeNqU2qTxfV7L1fe2MpY85F0PilF6zyxjUgdy+f0ct8HZ037/ti+N+rJ/lCgrct7l6y2M e3m+2Hv+pw8AQxcb52akfHNw0KSHCGrvzkv2x19uPy/fEd3y4PkOvXHf0dW/y8BAS6O3dntd6FHP 0L1kkZWHk3qzX5c1HrFUXvYcFrl4+eAbRGoo2vCogwsK9rtlBQpAc+LDMuE+TcjyGmfvwfmj+DSY fsn48LdGPC/OkE8oNbFbdOO4bI7bjRu8IHHgnGGUvNUqgV3h1PHUOHV9mfyz+oc9uKGuxhLlgGxT oIHMgYOUHU39LpEbX5YOYAYVdyXMbx1Mpbh2hpl0VHQaeFJOBE5buMKjuHo5ZTuPEDYus9lj1ZqI rdXdYURfUVG3Pr7NtZyxzDn2O3uTZnEQADtB+I0EjBBaIz5PhshBzZGQdlk8RGrfeJdcNpeR4FfH uXDYnAaxAdmvObOt2bMS7koXQHnLZh39mLCIpsy29OIgNO9VYVJIwcPK67ujC+fRthNr76XIA1IT fIO7sXB+Q6w9l4XzcOdu7tdbRV7HdOnU7eM6oBV7hNcSjiKAw+H8GCOT9kPDkBzPHAYA9vPf2ax9 HDgPUu+rsCB1EU15jnTn8Pr55Y9b0tkgA8n24P8731fs47WEsfCrjEZdTl8vHqGXRC8+qfHxQOgM 3dW7qPCIAnE7vfng5+OsaueBut55Vzi/gvV1dXT0vh8+rNuFsmGJVFenUdATTbHyHp758ekdQ6eA hMBSEJJCW06tc7QYzREkFzcN3TDZ6Jxy2JVtctmfvSqZYQwjuBCfXysM5h+DBWiFosuHHiPGnWSM iN3GunlK/AZevjQXdRtY+OENuMtoy5cqvzuN0stbxIju+5251zACOrWdUo+tvo6J9MlYtonHe8dZ sM3tymsuzgSG7WDK3rp/NCDPEetpe9UHM91vulm8L0dQHOeDNv6oyDHlLjo7cCA9geN/i8InRz37 OvwyyuRxjBJe9whhlYc79Tu/urxrcDaBxvGXeP37bBwjIfXjpjbB2GuWrhnNNrTpNnjzFeh4Xp7x k9pI+XdsdrLsZKDr6hMOoctHMUQAmsGZdkucMyaJKkABNx5w348tOqPHnLemGO5mzzW4foZN+L7H R0N+xmdDZo6Dc9vpm7fbI793LDHhm7GTNPGzx2z2QV3Ds+F3GIBRPAnMJH1PD7xEPDbXXZFB7nyS vzKjpnj3L19NZvy+++dN2eGa26+LIE6EAn1N0D1qKDf6R7BUB/DseHjIbfDLuHX2bezKJVAQMiD8 nBmG7r6k6dXzA58wZbttR0IbzlDuu2z0w28OCbSvHoCsFLUr2+4d+YwypWuFBGPQpZbtvwkz5aK/ iNkHYxpp1Ti8bpRAjp9fXtfvfgoGbvVet8wKydQasBkNawNlyVcNgUDt6cunB47AfXkPCWOPaGGd qw2h1O7eLiAzkoCrZHh2w1FLNDKdBKs85bf2aasiuV4/2B2OsQJrLV+Th15BIlYuw+uW3q+rANue JMON7/o3gKuwHWBxDdOJJQbQxA2G2w9PNyZ2D5CQqF2d5RJIgY12AR2EOORBEGn6913BwiuYwkgk F6h2DSYk4kxSQ/CWPdrU2GeELsQze2PeBnbXc9Y7LFjhJ6CcpIEJTrAjw+NHDy+xwV9/4uiMHie3 a/5eXfBKp/ydzyEk8jFsBUKA3V49P5fTuqMZzOhFDfrXjhIJgg6ejQyUh4mLwUd93tzH1lLfI5Q8 QI+XtGqRcYg7CB3oPEo5z3uf6FVz7hlV3aHgjZP0S214AeXlwJBeFBM9uHon+Xpjho8r4Yzf2P6I 5+gGMBo7wd00vqy/N4xbFk8odDB8155duA5SfEQBqXFMFoF2rxEfeGD4/ZjD+b5Vu6M6FXlwWin0 lqDmrcIBtoM5uRyPb7u2LS6V6u7D7LRlPaFE+mCcsAPyH/sPvG2QnsUbBdS7IzG4XDB7ZBp6qjjm aJRkZGPWsHCSSPQNQzSZem4UY8QOtoYYdeNRlEKMzvROjTTw9z+Vb6gHw6dV11nDBdrOL5qm3ll/ Dr6ziZXuswvHHZ5Z68eHHYMcMr4Ca7KZfUzcoUvEuUXa5G4RVi6pgGoW6XSRXM4j19QdSJk6XEPd +vS/0d16uw26Md9dn2+zLYH7VtxVanYQejh1DSHgspfZxZoBXkfv5q/VtkFZo+/3w4m+sDSoVuOs Xh1ruvLbshLtoNC+Af0IPGw5cV78AEh9RTFi/amnPGPnl6JOg0ndG52zIaw0241/Y+OX6a8I4HKE qZ4SPpV2pipKyKeJoUUR83S+NvD6ecqeU72uw05SFpv3DEadsN6ttceibfZGDSpwdlk9Xjo43isF mR04vUesX8qY4XPUwbMz0niMdMg1tk/Z0LjGpa61YRwdPu29Eb034hZYL0Z6ideezGt21j+y4o/W 2eI2ehd1hhocLDX9ZN6ukZ3rmy7BHj0PZwpfgRihgNh38fufrtzzaUrCEIvhdl6os78fQuUXycoz etOcWXcFHH2mZMBntgCW4qG6Fz2ZUhy4buMLVwM99/bgctl69cY7L1t7YAOMok4KXDcNQ2xe+ODe ndSZdGOTK1qHPVeRdTbTB6Q2eqOstApvuy2g6W03+NRCkvTnwNK1j1Tw3Z2Vzwoi/IyclSkRKbbz srwbMpU/Q4CjhwPQMcBHfdvZhTYNm2mBzd3M+xaPR4zEcd5DIwf3NcuO/noodXZ+1XRLuzofwHpo HkQj3TG4NmtLufjw6YOB4g2ZxUyInts0TWfAwFgYHyJxrMmU5uj+pm7Y+X8RZIwWY69QJiGHuVay 3R737O2MRrRXjpwobS2azlNLAeTDYhGHEKN4rMNzPNoY/Fad3RNqctnlyGcB98V6+Ctw9KgX3LsT FF2jhDi/v2b5Rf1T3T1zWL4gGnFMxXvnN0Z9EXF3ZtSIYSeDk6LnQD5V5dHdKN6tpsXQgVAxHWHF wEiDPFV4VVuImB1MNdRxvK3WXO66GEeuueApqjHThsa0CfDjtGeMSJRGHobbhHfKVNmssTu9ytW2 rSqgoOPP1jAaNAhl6etbKvtPK6ymwCggEQ+Pp9sbg69h8l5dZbi8P6SVjkfissv20xjDP1HTqX3l Ydzub4PxJB5ZrkW4vYfX+K/EiBU/8DDw19l/XAeW/iJ6erfKXgOf7FTrkHGg94fRl+D6t193Powa KkTyPF3sR9Ke8jio7c1m6wzcpH+Lx/Gx8JkyQlOX79Qy+nEQn7o+zdnqMxmZNx9egt5+wm3TqK7M laeFOHBdv0jwEzPbRne9+I6HPkpBop5LFxAvuZs+xoXL1QW0Up7NB2/mBGZjx6M3MQImnM44Wf+7 L3sBx06DH3dGsLb9YNzhjtrcjblpIhIx606ZkVHuFUrpAXOqGIq4dY4x7sH23dw9/x0fh9Caz2BV diqqsJh7+x+coxuwjy+urEdemjg4A2kjCfPs74SIBgJCjnTHdALcDIS6UaHRxgOoZoPZvwQzx0zf NaPr9qxR9cN4QY8pJzyfSYMv0oAs1SIicfcnSdwkuWjoua9hxtGT88zR6v2+fv4v8O6ObiYIgPF8 z6YeE3whg5cOmU9wgwq0fDCYbP3JJHuQLJf7z6aryX5e3NNm5d+9ROcw3iPZbwy3RjPcRVnR1t5o PHKbFLEAPYIJt84DMiRUmPO0+797Rv/V92HY+GPgGSAbP0xlxG/AeduvG44BR1Vr0uUbpaS5cbWT 6l2VWIp0WUpoWgpmzfjuGJ2IOWct+MJe1HIKcMm9OjPXCfiaj4bIRjsZAIxEhiIu7BBWb48WuRwf wG0O03U6zIGPIXrj9SZ6Pez2v+tPYa7lbj10dt6l4Vtyagf3FS/eivHGXQCkogMH4P3SwWOscfOG sF/BdGjv+JbBynzLRQbqLu6VPLvCHvXYgNlCZzllLu+ocBDeCY/GPX+c1NgUcGVBjbn5ZxEEB1TE b0x2fmufBCCB3IvaygEYEp5KjMu3jk9nBSNp+edXY0CgfUVMRri4X7ctMHX1mHUfXIFZhzPcFqvb 2T7oq89u7DtpMVQAzoJpN69ca+wfW0HRVDyfJ9M/tl8/kqbU494ZO3HqtafGP3r6/vvSXFZUi3r6 o9CxTo2s0YMRP8398H7RAeeZ6OyHV5j7sRUP61obabhr9AG8s6ahNH/TwdO797nyx8YvPj9J09A9 X59Ge2vZ35T2cMuBbq4eleGq73TcGHQc2GQHHmN8hAwEV+3MYwLlCHI33Sy7IVFQdkxATajlHryW 9O90fswmNjkHuIknprV0IwYL9bQxjjfHoi3o8VGvCsHT8HXthjwyoHjmWg8lXMSqxUM6iDw6oy8q Cvvz+zGvBmd4aNrngaiNiSpx3iqv2pGdP40xdoYQlBVRfRHuwdGb/XryqI+m/t6NvHpUAYEHqK8d 3ouN3X+ODukToIgxY7WUcPY7Y+HYIy4nTsleSx5hsfVp3eb68Qu/MLMsRoXSBrc36hDyh6Itm5q6 s6aqL+3ZWxpJf4sJ+YoF3wWe1enWoZzNAgq2hMKgQGW0Z8MN0XFBcokY0YXLgI85zdDh4cXWpCIw /Cxg+KxKEbc2RhF6iF26h7vy/CHV6s+eZTSHstxd6dJ9+i14o7XYaRbLCMnhQy3ANa0jtA23UOzg slkjT4ghgejk6J1g334461XCfouOyuvVTcZRgbFRtlq6JERExOzRnF6IOwgEgQl9eghFJN2ywHq3 Sd0tkfj+DzYem/pz3M76hn+I2fDXHnisSvb96sNX5s7BXT7u6HU/t9DuQ3d1enwgvi4ChxnUfX4Q cIAEEdTlRNNnQaO9/rTt16/xp0dYpHEBnOG/O44xUbSN8VVHKBMFGirwfU71bfiZ17vuzEu26Dvg OP098XOAXJOsk32Z19lvptNcZUsAGv4SfSFTm4Lhw2y6hB0O9Gx5c55pSfXOY+M+5/XIYbXWcydL pwy4AeN9tV2Qds4Xzh6jjtpR1jXBdgpFsbKJvVwMmbblKEYBteh9YSo71r0tsMaZFvZkN9tmB1+E g6c2MejFfb4O93Djs7L7LY/ChjhjmttHcHSTxc4XcOdHy5nr6IMe3xRB17YdfVDKCNnqcKr0RCC7 Or4jqhTHce+oUUsfBXY83u6XxKWU21Fw258K4NKJVYmkew6i8xCoIG0OYMOg7RqoCdr1PHjLEpsA KlQejl12mN0BJ0ZBd7w4x0cjubds42GjJQ0eGLOseVX/uVdtp3cPd154ugFxkXUVRWDX+hUHKQ8O +dCEzuoe8zPvBfuRfnTT0Ib1/j+3GaxHPY3XKIgslVDkeZfk/YSQGwl6UzTwYcK/LPEPG6qdluiP WfOG9mh3ZGC2i0YfPvm78rx2ZeTYk3IbDvy6Xtz8ZYRSyVx7FaXk0CC5QgBvKHPwy7ZZSDpl9L0w tOFIxWlFBgzuC1mFji4l0PaXiUCtJ1g+DxKKla1T3vmb3r7ev1mAmbHGS3UT9VsjPkflHGjrY8nD dtZ/jzwp9R6T9vnh7Dv3T8D8HcKR198VblpVZdvrMuMEEMGfAcOqwdbyZX+p+XVg/1PxfN4joiUq /t0zavbR9ldmW3erp361TPKW4dde8ECnk9HDyTmfinjpOvplOR9nc2nnWALkt/CAwHKEdUDr7grt VWs+hW3s+ZEtN85dDVqsVeguZUHey8zEUh9x9KYpONwZYqMD63dG7OMMmgxHY7Do+kHexVnLfvfN w4uyjtElg8T9rLlLbX1+GysyCDHEn1jaLCzg/oWXdDWr4Fy2toe3vjUcX7OoVjspmRnznJ+IgPG8 Nu4Hq8tuUI47nMeGXkS6Pj9GEelJFRoRpJx9PIPfvniR6OKipAjpVs8NuOEPGbbgoHE5O3DDpw55 +IvR/taeGYhlO+g7N9Nhe8yevOgcAa7hwZaPAbNUUgoQQZb1Favg7I3fNYMNB5w7+OIbUrLp2jlV 0vorNdEAbjTDYocKWKz6uiHhJqjbKXx6+SQfY6BmC8e2HZa97z6ctsH9Lt6r1jez+JyaFR9eyrdH uCQnmD0bFtu/jL22ggd0VpdGj7Ppyw0B4oPl+JTdO2YxoMU+HPmMN+uWOKXAzHfrLCRyIcgZmZwH mm4XKwI7WRxMEtKGCG/0dIBo9WXiHn9KIPhnfy6On0BPtIAIQgBh1OX8+QHWvaNv6fUO/kPDkBgB 99Sy08evrHV6cX7OQhrHjfwW8fDsOuoT3DV+s7ZiwfJvksW889ZetbeqYhAN+ca6DrJu7c32sSM7 ufybn9godmZkCkqrEZgYYYk4z/qdCLhINA0C0KrpQlyM/sx1oFKURHCERJIackhGY1SxsoDCyjL/ 5iZlxw0RjgQtFQwEhQQENf4ThNGhHJYiyXISiHqjG1gGY2O9gicMBMIUmiaHYg1ah71PFo0o/3Zg 8OGIGiJIggaVaF1GSLJQ5syYREARARNYUhkURSM5UC8iCVRB1Klk8jz6MBDdLIFpWHFlRQUgqwWS E3EqlLD/jlhp0hiKUQsraMgMZWNopEQZRlpa8gmsDWmSEpArIUBYkoJcsAWwZg5lFmRgVhYyCLWM yWikJmnJtGJonUfvsuoDSViIoKEXVKOEsLEAVYKFNsyEe61iwTzFJtz0YLIshj3w2uIbOb2OWoTW BjIY/8INzSmBMEqb1LvKlWixQotYZCnnnLt/z5qKHEhOBJqpKpVOYSn+WdoNsOXTs3AdiYjeMAwM AHISJQyXeAzmgbDO2y7XVCBvMS6JT/q6P5o2jZQyGqJknCyIMkwkKXFgyxoU5Fwg7lumKspLYVAv DUewN8wCgaFpmTcIchKcYMgDe1OoTdCkIAyRoEmaSIDmQ75V/SyocIHmEAxhytWA71ZzGGlU2baR MkNmFAyiXaV75DJE1QJ4zfWh1BZCpFkRlLaWrDfWZPOHibkyaRUQU6JArBEmDIVnFohLErShqVAp TJXCRcpjyR4cuDpd5IuASmEpjIZAYH/0RgCaoLCiyjpj5pA1AsztBtoxWlhjisLMsGmdMOx20myG KorDtSBkyyUBKyVqAdEKztGSsgb7ZkkCQUUZCPtCV1xJMKdHUFSi99SIo6UKXz6U/Ih7E+I175qw 0amwMge8/1+F2hpBRXYe3DqYEhcYUAKh3snWPPDeLEOJREXksneyI7hPElKC4SgFILkKLzJRR2E2 Ngy1jpZBen+3FXf6//gfN+DPfLLgdJiGqgQr9eBlVKUSy5GERP8OOP78nZMQOkpYJhCRIBlgioAn 3z4PPmU1bAIbUFEKv9cAi7ygA/6IFQP1QCCHCQV3lETJ4SUFLATVQxFJNRQw0RAUNRBVNAM1FS0M JEonSyWKAfZz/yu49Cl/nccZu8UEFVhEUSMJKVCpaKgCCZID+QzBhoImYmohBNpQ/22QawjHw9X+ Tzdnw9AM18HaFC3ney+O3URABTBFC8qH5iZX12VNN4iOI6hMy+OEcImFIeOIGQj/BIn+iFNQJS1S PAlP+ZD9yDWx5hDQH8xaIVOhKGG5jlEJQgZmLEh2fsi/RodggaAUZ6+/SmqZUK9Eocp1d9gRQD4Y mFCDrMGsuqU3naCkDIBQxN8UNS8Z2kIlE3hdSf6zjiYTvwyhoAMJKTIoQ5R2mjFNmRGKMweRAGVp DAgokgJpDJFrbfHYkdswIKkFHdxOOK2mKsTYjsw1qw1bWCEpccMg3lNoG0YhvB4oNEoSVQYkgNaA nWm3GoZjIFNyhYxZt3fLZp2IEPGePBXRFKVSciE+WBXVQDQL9yE2hKAKRDoRkBsQtAdpB+0jCTC0 SrR+tIRKj/y6aaT/5YHe1IcJR1KgeEI8QS/HID/yJXnJtBzIQyU/DIPGEXpvgDxJEaij/+Th/9PV mdH6I/t/ZW9VU+Tx/pT9sP0adwhc/YAaqT88Un7U/GmLEv7f8aYRBwY6TJid+vicDZgfOlTFLQ+9 mmf0JOE5sv6sXOVxz5W43sbkztcMzohO+EYsqGjfNcZweGyI7EPxPEFqe0WfxeIcx8BQSuOb3pmr bNfaG4xS4Qd/T/hlGMhdQsWxZjYvZcQcN0P74LemEiZh5wjVvc78NNwPefS7TkQ5HTjIGdFm4cf7 paFZ9uHtCJ3V69S9f0+h/tvzjkgXa/s/Jn9QElArswert+6G6CRshSa4wzv+BmZ3w2IODfEPtzFz +E+JfdGG53Vc1vbPEeSxBfebO66249doCM2ywdsXO8o1Ef8VhdhBRuWw5nntMWLl7tWLPLqvu0zb ZlSs85r7QVexVSAWbfTtfB39gdc+3kfJCGwOo6+jcT7TCAbg05Q9IIXPBpxngV/gF3qw/yKOG14i yOQujTH0JVdJzHSeilPfxzbDdjX0FH939393ZD2+c/y0P8CEPYf6nJwO6/CZ99TmxD4YfsJI6TPw V06XfFAPj1hQXD9APxf2/7v9/8Gf7NOAADz93sYDllv29Ih28pDeIKFodjQy4M56mKj+ejAOzQJf 2+LmeJkD6GgHSUI+Gob3CWc3zmgEUL4xjUkiCrp/r8P9nt0CZ1v33uGr9fQgfLOPt3zP0Vw9Cm+r okTicQLLE7PpxROAFwMT5ayYC6Y2QWxzG9E4ldHKqqipIUIBzUZPymsX3yGYWgzUQlS7shbE7vZ/ 2xGUZWqJNrAjXu4Z4bBIIbZB9AQwGAgwtpgmyw7htWIGd9XPeHTFWTeaYSyTXTSL4CFqdlo/HXBL 2R4GgBCexoNAlGnPbesksOTbD/tlmz2eofuGRwxQOx8O3BrCEMTr92qy1GU5DXYHUR+SZtaCo4Ve u6do9EobERAOiHfkJ2f6lVX+t/h9/yVYy8M/lb3Zf7/m/3aZ+M3ay+/6fegoZ9QQINXzy8Nw9bvt 4140BHMfVb2HxQDvzifd2UhKGiCXjrCSQpYWAH3b+eYcEA/qC9vZ+7wd6Sd8gnq9/DJwGATloqde dB8UPgh7tGkOzXQk1obO8UUVtMw7vanzWcyH+/XFyiwUpAgCHbonpB4EejMzEti7a4mYawcIjzsn YDze8remZRBlGiFfEua62k0J4cEyd2WMnwyHsm2W+CzBn8T0zJXbrUGXZ+y44FlBnRqNG+lHmOU2 eW82TV8zFlgfhc59SCacP703Agw2NEcY5Vv+m7Ywm5TYUpmFzUELaiCBQyF3Vm8biS7CDgzBTUkk 6HNOPQqEHtqCp7JxBwmiGH/l/HcddOZw4cW6EwxIuyWZQSgOIlsymgy/b0jtb/T6/19GA5ICPbst tCvH7tv4cY8cR69DzT25+qSh1R8nmKMXyI9DBj537/E7UxtM6dDQJsr8uZhs6+4ImWY+daO9He0u QMxEKELBhIZ9ZUom534yCun1OG76J8XuAJGDkVJakDw2V0AmMauYjw8Z1HmQm0ioPwErBIiY/XfH sI6iKduji3wZ5ljEChAxVBIL00d7XTyXWGauJcrVCyN4gUVe2MXLd7AdiEbyDGSADd8JpQ1MheIC 1NGWOsvydpmFA9JAHqKXsoRQMSjM4KQ0+BBej81romCshCSxGQPTu2h3VIOsRu27GSrQA41t6S6n iiyCHjijJyZVlBMovVFviFWIIez55KGVQdPdRkEVE+jePKBKN764ggdNqQGpCzI3HlAP64eEHV1x xf+jtoZVXEKpBi7c3CzmIO0NdTIi3F/3Xkieo93l0s+KhNTmQ85AbYCGzTXR+chITDVOkJ2faD8s r/29/uqB+MknhhB17L+UEw7B06sN+B8AjzfsCiSRAlEOQvf9c/FkH10x/LLGQ74eDFkz2xfkuXLN LDpLUHLHg4ZEddjlc/NfQ5F2RDHz59WD989950SGpZEBKJmCSChIWTm6z3fDYvgPec1aoG2OuKhn HvwcoBXjpC+FJ4PJhjOHi07vX2ZOHfmX40nNMZJ1Qnq4vDPo+Hlncw5M3T0MnISY9EV9LTwE12WH TJxdelzXviK0CvlAu4jxez9H3rvcQp6xL1aQ2ENZFp7d/dZU6OFBrrhoH0Q8pD5Tr+yx9ndWm7/y +frf718/HvolKLbIOwUKRUhtIhBpMxCCh47mC+Ug5XngXr+XomQH6rrO/NyqCRNf29Ood0T4pwIH JE0CW9lG98g+fUQhg1HuKVfc0+V7nDCBjPMA7w6x/YUtGZjUZuYPSXPcr0tBchLzHo1bLeDBzb/j zOQh93jfIeSH1GZBB+shboPSOfpwUfV1d0RiKEDZXfNB7nhfWvzGI+uYDgeXcPQ2GAexxiibx+pH uO0cwMvD7bilIiOl3TKDiPPfh5nt4vQDMeve3tF88MvYlSoboQajq12eK7zchZrrveAAhyyECPVu x286pscRxFXBPYRyKJ7Sz9i9ZkcqnDRJHhinb461Z4xcD3wVsmKxAN+yt88p9PjLcWe+pr/Jx0wH Nnzg+aD+RzEDBfoYg2vy/O5VfWO5FK/GcUwke3J/TrABAGqR0KST9EgHHsc8ogJcSIJGo6Q4/dbG KZeJKAkkklf1BH/iDsDzNt/c1N99PXRk0WSovWSdTZSPEgj0KNu57AJjyAXS4Xp1JPy93w/H8DtQ DSNoOqBtnRznMxkZ+d8kSQo0/HZiq2cd0N5ywkrZNG/Feh8DB8bmBvfg3n4X8sphVnwzxfh1G8nf nt38j8M5NwHUxe8Q6CD3BvjLnMG369bv1IH69wpjOJhqv5lpv6WXIUW83ORz4Aq6Wn1vT0+g+f6D MCPLrO6Qepf+nYPmLB4eFIBQKfeRwHuV4H7hr75etV7FUGIT0w7igQbHBQmg97tk1gWLG5T74p1g dUqAkJ6cgy/839wrKXpCXLpnaCiP6wfYwPog6qicFvPeG3Nu93sSWPrkGECp4J5Mt0XB+PiGJQ+C uSLdfHVCuSx54efVXNR7mx1QJ6OkNfJabPzZ+4pQ8tqHtH7ejPnpwEpTgYdsmHsC/cnYMA/PZrH7 7x9YSIYdwyy0G/h+7ZPD8ps5KNSqXHcHabrymiEI/GcxcJ6Sk1vs66TpIeOSVso2ZvF1Ido0bptb nn56v470a3TBhIKaGz3wGg8fOjhtiqz++bn07t+m/nbqVRwlRA/wwIIHOQfLQBU2DeBn1I2DhTis hTs/HzGSWF/h71QdZAHvNSJaIUJFmDEINaAZbs26gQSBulKKPVFuNywBr5WLGL4I+32LxwWBs5p5 E9Dv72u9NGzgawnqOdPhBA591C9fQzDWFPHUPQb6Ipj+hLbYfJ2txEdFL3ylVNB5Px+3x295b0mo GI9nYyJ0Jio2pFB5DxFQ8nA9PcNQF6wiWj7yI7eT47bYmOG1z5Dzmrv5HI4g9pHvxeHlpPLDfBdR Pt+ujInMiAIGZCdhXVUhi5w+ZyfAJ4ein5xxB+/xb6B3l2dG3KASMB8QiebaX0zO/nw4dhzuLDjS TbLPYMLJD1UZIy/Tr9UhQRVdJAfYRWGbJgUAGJCDVkojnPGYUINMGaJIJiqWGb/zdzLFVF/HGk3D Ao11+VpVyYghsY7Nmk9b4S2ToxzTWQgTOHVOidd7UA2P4ofbtr65s54eIKiiyyZKrLTXHvkB63qs HxDlKoOUkIpy9dO0hCTZ2HPolN3yHr7ijG7KVkDmglTUPCiHo6WGfrXWT2fHc3rydt6XxihAYEOE /OPUeoTmUT4dSgfZtowkNigbj2g9JP7okj53eggREj45LxAxmxHXgyYYjo3Nd9+khgVHS+18sICI nHoChUwki0huhlc5Pe6PALlMNmZyE31b3lenDaFpDiLCJQlESUDZdGXl38aa9qjETVEnDkziKZgZ ucMTioeuTnJMKFHpZmF48n8sAgiKkSyvr3fl5/NkUlpTVVJUqUFL9sfm58w59ryxAOud71Vq8+Wn D6e/zg2iCDsVFjQJ5uEOtM3juBcPd40HmHfX6hwF8nn7h9Qsweo9I8F0lyYGe/cXp775kTcLEVPh 6nvzTZMb5ScGTILkcEeJYotRYsnrwi20uYlooB6H0NLt718fGDgJMYMkzLfte/DsaVihM3bveXLf 3Pszv2OEX9S8P0lG5elEmNlNvXjJyJtoYbbcuCP60QDv5ZJ35uastZwRHmX11MA0vEAl5ZqbF5XO sotN7Ml7edD2CbD6kUjwdb0CS8I7YaszcaW6sGd9syYQQhznIcjyb1HzfHLB9B1i5AJBJKD4Lh7c V6uGerKNTzm2AtXj3QDxsv119V5B7q9aoxwKEp7AUoLIo0Ivg/7bU8ecNbjoi8jAjrkn3VU9AOd7 z2d68jShj9fg6+u77Lo8BHd3WxgLIQ2PRhR4+r1yblh29fbTo/AW/YU9vBXd/w2fW9OAY9BYJuIT XAB6p5/DJn6qKGMhhFZP2CPrfz6LRtvDnOKIhgoAHF3QMlgifDd401SouPOQYGtk8AyBkP1djMMg Tx8wAzu8MPOr4ZDX7PUoiR6yIytZmQoByPHaoKtDR6C44dhTySbfeHIw5p6mLJ4uI/NW9q5aZZMj UR7lMdq7/Hx06h5Ptxs1Dq2b8r+Hrq2x3Ef0iINH1KO8H1H3zUYmsx2rlySuzeHSBIX2Da3AuJHv UTcjcAHApzo0DKYMvJ7wPQRCoVJHf2dspvz2HGl+cWc2lHotixx+I5baTS4brJ29YZhZ8cAkkEUB ROIooTYQkSciICiAdp+COdh3F4+BwXjWpgJTBLOwD2KBgUI6Vhi4Y27JDLhMMkxZLUxVeUYdUTWi YTD4v6I70iX+ktfn0qCGSXo7iS6a4Dfxo7AIJkQxDdpxPFwT41MjOHRwq0SeDpKeeBwL9gQIMmfi yubycN8N8oxxM1ooVNW19CSEAAXb0YERlOA24UZ4LjMFD3EdYZPTgOmbjx3MFIeRs43FXdU8H/N6 AOH9+onphTXgmXVuxyA2Y5v37ux2oddo/+PQneQ0nsGXknZuTnvC4RjjQgt2Eu0bOKgOIHEpdhMN HsUUN2VCwHr2bHzAr3uej/LoZdolaCYN0MBjjFkHLt8+ThxA8G4XeBuuFTM0jwjGCQUhfFrbnJg6 GhcEB+ZRcHqGeQyrbhA+weQ6MDz6+vr/ZvD0kCPFGCKFAKADEIEBnRswMsjnHBqbYvDpz3CLn7ah jXv2TfeSoZa9ar0AO1GRBB6+bB1pJgCwHTIkWDy/wYc4ve2x21OTaxKTlRL0gHY0EXF42iwg6vz4 C/eLbcIjjBAg+3EKNYEj47tMRX3b9uFicCiC6hm7FW4q9SGrKG3CGDnnQbOrBbWFKUQmPLz5ZJ0e EpczsnP5uzwbux10b93jtVjojsimk6ZN9K8YkivL4jWX5zdk/KK9m1TLNU2cSUfjvxHkdUOoPZ3K Bo94fYK9MkZk6JPwkM85pff0cDPnq4Zi/qTKHGWfW53VUDKOJU8acYJqMVQOeojB+3djqflIIPbL ftZudT6V4Vm/dyjEPhsK6FLe5SDA9kV+3r2aeiGPLnmK1QR05FthZu91VPYUECiIQQ4o8opAYNmi dd+nKV8sx0VRxtDpoXAiYGAGgz1mJ5ca05E397XVQ0bNBvTcF1z37z3QMJF7XoCgXBUqCVKgYw+P gGbjj/2YitseU3COSG+u6kc94lJXURUWyv27NftqPtNSgKQAEaHiMqM9bJFCUBQDgBrnvVVVERSF KInl0zAO7026cse90d2dRUahR+8IOqtdQCK8FG3PtYPG2Gv9IBATSHSRsHgzJ4b07/i5T1+1VKt+ bH6v0/V74L+Sr+9XJ9rv3OKftHdpbx/SHkN+l52eHu7dLod9fs/26tD+dH8GTbYP/zKNWwK1/ag4 CmaDJbKzJVhkKv5Aq/Gc232b+Az5ZXp+Wey+Qq+lBBYncfsjQMfavS5PMSpyFx9Vmg2aQ+X/Klu0 bBkO0eM1H7x6HkOQzwl7dPLzNjj8ko1F/aB9ragqBsbUPyQjT++XfOGOifHg55DZQ238NpxCG8+P LLjKqBDuvD+taw0TmGC02rLAcm7IgGj8g7AjIj0bdQwiEKcUzn9edYynNcLJwTaFS2aLFLg7ygsQ ByODYucEuhTCqjggtAM6qWIRp8V39r2HHBd+QXaeLlRFZRXx8WGDMkdxRol5dvSBesHScET5c+Lr 5198FfdteKpa5MdXZlzwu/tUP2DIfGuAgNyiZFn666EIUNVExVNFVFBVVScd+nHtLy8uh5fHz/dQ x/oX6Z5EIlBKFCIEEMSUgFAUqpBAjSLQgRCUXk/XaiQhGfj5Pq+oDjB0P49lfoiYhqBYGugKBT+o 7Om9dGfR/E01VSSNBNOz06pNRct2/uu3g4h5FexrgCn8iAP5jk4D0U6cB8da29mAyigyI+kNXGvp tuzaxbScgdnLJ8PWU/3WoLw/2E0n1P0Pl+kpVF9gQeAeB9upcuXmHv1sDxj/DdT5Rp9caWp97/5z h1dHdjnu+farfZk+sP4Mn4lXVSA2UWfs/esnN+dnXW+Q4YFYGOmApDDdsDE/tGfX/Dzw8GcPB38z +XVB/ytwdDW7kIP2Gd6j7cmDAyZA380fbB134jxm1mzDd2YBbTp6qdyHU5nTKbWxvY42JfxZlUVV F8e6pynUnOXpHdJ16wclM3e4sOvsYUiwizvAvA2249SIK+YTr4UPAYRtsOQ4dlMJN5UnOXlXxffw x29GvbyDDw+XfAYkEglCURB1FAgFQSG7py05Dvn7u+B9rSqHbx3FHOBJ/KK149N3ynPQQLhiWkdS JKBCAUYAGYoDyBShSQrYwAoULIUthLLKAW2EspQltCFthDrkxEQ2tBRQYpZgiYqCiigpoqYooXQA S1pCFMKFyh38n3WrfgwxRioiqqP6ft17D3+23x1rTde3bl+v2EOvQYetn7kELBErZsk0zZgA2oCt 21rxC6MDs0H4ByuD7j4Qzvf15Fv2Ov+eaqLgQegNYZ/J9CoaKJ6O7VZUfo8QOIergdv5/zEsoXIA WC0AhuGUNScZNf1JEDxEfYMF9r0G9n5BibajJO3DGKlopiCdWCagpSjIeKFQPZqXWB6TW/CqUU0i kK0fz4DlLQUKMreWdmu6VIi84pL4TZ/FqQ0RkIkl2F2t1Cego9FLG2NNlstEtvIHrAOztE7oQoaX nDkrwuXM5/WZHaEGhydZ00RNNF9Ah1qh4g4eIO8fL5qgNDaebUBZC8+NDV8g3A5g503g6JhccvPO cT3Gtpse8GCHKvNzmoNk16r2tOWW9dVMZA07w1rzeHWeMMCCEyMVjDEEOo06Qz5d14SyLqCIBBCI vatoSHZNvG9rSZOEdon2gNntlH412h65B5mgxh0qcW7fHKakW8HnB7AKRdDk1ZUG4qNZ6BywmWk5 +hWQN46w6QObgauFSFLUCzdTcPOdW1460gq9EDZm3qsxJpC6nRTiyiTtnUXsRUEy2rShcw7R3DsJ 03RnJybSHnfBSIJmSY0hw3iKeE8JMnNklkggHBj7CHjiKIj5uvdcMs3gd6QU5hsnTFtTvPMSa2OB jGTgxQUUVlO9ICTCcCSKJiIJIJoiIiCSIiiIiJiHqOATu+ny2TRmZ1vjhfq5D4h7GAoIh6j73ga0 HkYaOXavTENYr+x5JGkPbGufQrFG7opmSJdE4nc2JdUHm4L5zHUB1A59w3WQa7DJSI/K1AcDcovc RGBIER6gOD2x5ITp7KIarFpCUy03hY2bqE68w5QHrRm2GgWO7Na2EO0Oo3OYO3N7DwYSqTOfMEpP UoHWJJhPI9Cdsnl1oW9sqRntoJmdnkigKeJ5gWCMnn7yigxRGW0FAqUQqKEUKwFlYpOoryfC6yaE xSA4vKSqKKTvkycucmqkp2blGTyx1pPePEj5jA8m8U0xMSl4njHOSPfhzBcQK6E4nG6GNOCt+veB 30MBuSEB3vbtiqdcUNFJkmRBVSw6aQUnpssikFmOMADIZ0cwZjUZsHNDSC5trO2QCEHHdY5eObaD s0tkjwRJrxOLrDxZglZWpU2mUFxxmMupY5Zs+h43nky93RL4AcbsneMHcaYu81uHqD41HUDbmbIp abmT0Up6htFRDOw2yxGQDZlACEJJeA2mlwBFM4jjYYgjTESYNwkmR4A8fRD/Q8gQ+uEKX6X9HmsD 1nyeE1FduNUzn0Mos/d8CqL7/rPmE8XPaPhPgXWZpFHxd/fv7DYoSP1RuBtQoo8fQTr5whAWmKfW qJiyUN+7Vys7y8iiIHdobju9Cj8wcN6cUROgDaarvcp1YSbIPMDSYmRxT5D3ZtA7Q2c3PUquVNez ZQTcA7wprJ9MUkjXqOO4aABg7kMTJVVVhWA2iTIuCAIg7jQenMpPfxOAGsCt/j4Fd+sPrUbtyG4n E4apytwy8HwnQXh3jr4veDtBoNZ2HNOrxzWdfM0m5OBz8x1naezPpKNZXio16+C925RTvaCj1cHw 94NB0VDsHcbfTu2+LXrnPv2qJy6+CImoHaZBKOrLl+rv4wYOoO8jR8VytOHRyd06nIz1cToyMIiZ M8KG/rEDNL+AOod68fBix58HWJkm4OXgLr+pP158uZ9OJ14Hw9DRXwV8exlfJQzwrlZ737KeDcjE QGEm5uGBsaWn0yEUBssI5PNVur7Yf4OxKXBGSz91We+is7JftUKW5zgT7xhSAxvCZ92UM8PC9XyR sQgQaoAggYYgEzo2bV38K0ZJpz/xGGOmf+uTF9jyWy7xBlh+/174Vj+U5CBFd6ulFzoPCq6bCLB/ wlr73ziv/m1WpNyirnlnTaQpx678IQafByutmXVTCQa8nW+hhB8TExZz3CZU8vmsYPaLdq8Hvmog ua+lztesYQ0dMabVR910Oj3+2TzNbi4C+G4M5IYCN6CD2YPM1VR9IrwrMqwykmM/fcG9X8FRQvSo 93qPRWofEVF+Lv2wzUjRRk69aoTJk1b+yHXfo+2N6WvgbhqNZfX9V2xJm7zRR5xa9MPYuyfUe8ei vT9lxxsfUuvJwP7gwmD0OXdneYcJt5tsJayQ3jOItLiO1O4IEEtCmWQtgCB/e+l00SwdMUk4IEDq Sd0u3uiOSLr4w6G0pV6dgZHDtbgtniwiws4erTV0LKtCo6J0WFIIarCAn/jiHwIq3bT3fX23F8YU SjGj0YdYGQyAhNA6WBOCgQFXtBnIGGZcJmuaiVgWgrS5CIeHn6fDkGwtOOoxgLS0XrOOOgG2aVEl 7tZpZPwjsEg8PyWbBVW7Ky3KHJiR/f6v1TsOntvXQ51Z1EiRiTmZHHP9VSI3vxeJjcJoOU2DIHDB FcR3qpLgtpTi4G6xO5SqpUqH5Lxrfs9v9UxEZDZoAMls+7Zo0nDYpuvhjD+0boOfD4DX20AFCPbu 6B/fiMzH3eu+7I86Btn1UiIUWAWRAVz37cm9zacir7yCymoCe08yio7aOS4KGG4QEg6GuHTUg9Dx mloxffiozZyuBuCMwOPyuKMhugv+v95/V9ePp2es1beff1ThRhTwmt5TqlbCGI8zPHSB+iV9VkPz wFxMEpPKQJ7+/xnRzbNsPhwZWmRQOpHbn57Bs7f/GV+rT8egpDMf9/6Cx+eXh8neq3QvYfuoyta5 c/kTDn+X0Syr3V9vl8uWV8S2d1H+3/P+6cEZRrBd5qbFL1qs6rSiIzcTGrggeEzf05h0SGuWpgzs aE2tFUVR00MtJEhCEnGNGt/JETl9E7+/7PF5pfuSNi1kCmzl5siguPoXOCGb+f30OZmaag4dtFuf LM2mNCUKfsfCh6B3D9oxyE0sD8Vo8e0CiRifYPjiBL74wEBQf9WLf7NC7EQQWABCWCBw0YfKq7MC gHzIBbRhnqipZr9/re1fQfzU4YL7LiUI/rnUZTZQ/9SWR84SR0bxwij/cieZ1OQfW+S62/Njyyzt s41XabmmCAFYUEmRzR4M2HCuMKjnOK1Ard905KzrlRpy2rZ2JZX4/f2ygPPG9ejnZ8cd3njF3/pD dCMHcXEDcikVEHrNqutYh0r4SvSTNUFJCmM0qMBomEk+h6iPDQmPDeAgR4JOx2Ds1HAjwKDw50bN kDis8mtkHBvzyZttLljyxnLdf1V/xC/oC7i5zltFAQNhCvHi+8o2Dhytq8nSWNoZjb0fWY9W86x5 Xu7Q8Hk6A/R3t8HnO46oRM5ySCeKqlAdsrkKO+idFjsfBtk15Cdr03KdsaiuyxC3DUmz3b+yPKTN 9wBdm97ZAeUAOZ+xGG2GWSiKGdXKyXRRw5geO+obE70DjF6QTDzhxtI2Vb4XwzbKIPiIReyD6mDi +sAnPw67HGyrpKUyrz94kLy9Tbh04zBuPPSYYd0lZVyVl7fn5dR0u16Gz2fjvnl3CyFLEC8Q5Yis bS8FiRlOUOzlvnhhCsOTWdBjEOzYnAxSCEWk0ep9ZdElNay1MoDCzqTWFi9eeXjZF4db0UV4ycgF XxADsgcTrN+UneANpsRyoSCCOHeE76fi6Tqjis13m9d9kmbww7jQiMRY76XDxOV3SOBHjBQe6rWK Xq9swdspiTbbsAYqHamnKOBeRGWc6kAarB4d+6DV1wVvwjsMOkoo11eaznfOu2K67dRqeDp28FG/ J0YajyLLZQccm+Jo7cmzofnOVXjuceJ5jyuNBg4U1Pm8BmS0YE3q78p0yRaB32Y7rzpdVQua3nN3 CBMZukwiGMEJID1xXdqsoTOf3Muml5RgMAUF8kaBwlr7iYV0NSUDthwrlWYsrxWUjDfLGEKRA/pA OoZ8hoTSBsDVcCKRSaSmLcMKLZB6FW3LdngXbfqoddVEO8agOkNU6LQQjKCOMlG2Y7C4Tb9PVK6H IBMQnS7yKaYc6qVaCR0ogcKYf4fRrPoDb5bQ3jnuRyisipIgRL9nO86umzZstyiVYNd/dL3Pieno XDzIcjDxpfQ/X7C+4Po4t9tijTspPiKT5A67m6sYovqJvOD2DWuHrGBqcTw1QwcDlDB4hscrsIMk hrdHWNzteTnHMjWg7TqO/r4hyh5Sr82EtyAHoYBZ7NA4chI2lSk+ilIkylB7pJ2nt7S83YQRXYPC R3fH4x1jtOzofso8E8jM8MJzcsqzq8PMKxgKPqZlqrWlGdXzrccbR6hbHYYY9vywMeQ5TMy7AHP+ /gPvm0K3Fr0UELs5Mfk0HbJ6waPiIdAfgObNMqhIZeNxOlOx1+kPAPad0BBAbIeWBdGwOwSw309f mfuZ9ihKDqRzvNSDzSHquCusADcHRoOtg+Ll7kyutF72bwLnfoL+QxlwHccoeI03zyVQ6dHkPNps M1OGojwO9eek8UuPB0AAGmkqjpG7zMuzuR8URtvadd/IEoUAJQAXFMiHhO5XzbtE5cB2OrqL9w2D gLkFQnMvb1c72lkzeD1D5H3t8TBLTCCQQrJJotiD42YBCqiIGBhpi5aCIa+XRzyTlou8Gy1rBqWh QPb7Zg8Ya3g2WvE1IRS2o+nRxAf/9CoP9ECP8MIh1wioAZAiB/mPhEP/shFwmKEiBpE09MrVhGlI USNN7Fiy+Psnj/CORzjB5h/D3pYun038q/xwr1/iQF+9/dmSERlRkrkYQVVJSNUlAUlUUtUM2TgT VZUZFIFKEQ0hayEHwI/2oMiprRYf5khUA/2jrD147MpFD/l/1w20RtOWZuaJMBjlVVIsuFoxuNxW 0oFy4D/0pmFBTgM3DDSxT/wLswZ/+iBj3zXGmMtOU1rnNtG3poZtZrWtDMkEz/f3pTNBTjCLGBGj KNXkzLkuuhzVs/7jipjOP+ENB4jBjCLvETjZUuG66llDHTuSBlU7uuBnNW7tFgsorg0DMaoRkyUb GrbRjCM/5FjMhE41DLZSk7vCseRxyDJYDcsF0HQUY/8G96VWI/x+M5ck7NpKMTewttPGovfC7knU SlZCBwgOO+2g2lIJ7BmzMA3sJsgFt8WBWGkKiwKyZIORvGEiRIagXCDclMsYKdSBkJaw2269C06J 3lJijaU5zwgGqEN5XaXTJEGS7y6IBJZ4Sci0TqXpAa3wCN5cRpdVCmShzhNScoU5Tk7kJtUHVCYB DQlLQUEb4thg1lmWBRQptUbyqVwhNSDzhHIN6hyQMkpTeMkdS0q91ogaFelygN7I6JDvCDtAcSTe GJHYlHaAyApEzWLq0QJqBKVNSuQbTrYwUArjedAqRTZljEQNmSqii0ldQFVYl6sDliMaBmQJIGmi pKsQ2kRMIPrfwGF6ZOX5ZEyQ1FLTNXMYazgmM1pR1tlj1l0f/MX+s7FqEj/EC/x/aAR/T7v4/x+X 9R/ifX+s/3/1f2fwff8vl9knlDVlCqoVf0+9sbrD7/vVxwxxVAEH7pbos3+55q2sK1U1rP9ZPgYQ hQ0nunOpuE/s1x/7PmMnDmIpDiFU7kAQfMBQfR/TH+AbZt261OzevV0P6cntBzQjHj/RWsWhCD3P XR7QeFylDmHkOPVuzVUWGePVsym63Pj1P5iKAyKwgyCPLOkRP2n2/h/7c3+zHf/x9NvKn9mP6jzc j/B6vZOPX5BJ0+aSwnttcl+sSz5/Zt73sv5fJWWWLYxtBMt//Xkr1cSiS3g74qKcFG3gFD+zxZrA KAki8JhN4QLp5cANtn/4bReaAC2SgIzVCs5WQ/3lyFNQRszIs8DG/ljZEQdQ7kHpc6n93sq0P52K qp05WMoGfB54dQcOeuXi5g6Fg7FH3FydQnVv4dj2VZ/Obh5lGBer0xy7/e8RlVUVQoqins4whD0O uVvFoumq9x3+zw1sr5k4cRRStYsU8u73762N7833++1ORrdN0qW6gq3PcCBB4HSL3c3fs6kyRTGv oPTcTl+QbvQCk6Dcbjq23kk8ff5tDSE2fuf9b7eXJbdrz8fKIeLYoo6aJl2aCYy7gspnBGRKTeXo ux47iSidT0cj28arz2tmZgvco8hu1+0p4ZA4y3bzfnxnL1opkvyRPzTu9EnH6tWBgvhubqCIr/Mj z9UnytGNatByrKIsIvaysaqn2Rmlb6rMTqUAGUzEZzIRID65eeIYicm+FJF078LccXW6RM2lMSph ydTkLch8EDlivWFRRDj4ouWdK9ZzT8SQOzVkllif2YFNqf6HbD3fjHRubLYAEm2c+57Nu06+PK7I ByTnCcJHUhqWg2g3nnbkJu1JkbyaiJIh7ZXhLyktsQ0czMWQ6CBskC61hwwlQ0wnDOgriC64C6Vr xnh14vbSK6ouCBCDlfOy6S8YM4Ym8olIcZQOMqHOQ4Sm8OTvtx22dQZK0vSKMkQpaV1IsaAI5xOb ELoxcvXXVQzmIGwRwMLcIeEnCV6bYjvAhwzKFel0jnhz6bC10g6RvFKFL0kTnti8tsEOcq7QJqTU rkg5BohpNS7XOReRHSEzjpAS3+Qbt3w9/aXhybH/XXeBiFMB1TIifeXXrEQ/5/B0u93I7rToJ7CE NOiHEddWfzErEYdAn84gSAP3JebdI29m2wHJFU5NdAmzLhIjsb4Xfxw0mEmQO7AeO4QMQGYYAeOH CShDaVOEB3QkiYMSZByDwUTjdGVlA2EDOhKEzChwcn/6gPHbcuKcU2eJIBzk3IoiBOe2GLBYCHTW 5tsAdqa99rF2SKLADgQRP+n/hTERIyMMUb/1//3/LM2myqbkQDEC4F2vm+/1edRtFzV1Fu79M2AQ MxLY7sqDirSQWByQmhmtFnnTZ+LLDYg5QcTwMOMP9YJoGh5u/1H7d1bGf7+Br3cAtnxpcEffJR0Z hUGnfOB2Pbmvsu4ohgDaIwZxwQ4MYo36s3mI2c63o1TTmoY6QY2n+DpiKNvBNCOztrZUbdmsb8Uz SKhIR/uRlZjF8hY9swpttkbJnkB9Hx40D5MA8kv3Vz5Xsu/I42F5AhjbSGcUHFFKhIAgDN1Gv9ez Phso0hs0GD9maZMkpxzxsLWhTKSjOk4wYCCFXCWhqiIgRCFs38QasbZhmOq27o4OdFjMAetk9z1i fSVHjnruKt4btxOcujnDhzzqgZmEea079tjNF+fCiOEb5F8P5lg0755vkkIXaCDCpBLjcjTB7ON1 IBFjSejee1844fRHWNQBeFp5ZgxvrUjNOp7Tej0RECJSFssAEk7F9H/PRERMQJLXMulgxHMDCFmF Vw0z0ll7Rk6q2VFTEoLbAK1vkNsgBIpAAOQBENHMgCIgrKrsNskgHScOCysXjVQMU0hY3k2C3RmV 8bOlFcIaWesMZKg1Fr2IXE1USczOJN8QqI9b4uu4PW/LVsZQGyiTBiCJDBpCCRgqJjGElpM7Basc NyFcn0ws9FZQBcZJAZoyZsvPfuo1/Bty0Y9IlIFC5mIN1r0ZstID+/HNMCkUioFRpPKKAXGcrgOG qLZcMQKfKF7WacJuOz7AnLrnOpufDMzDMo17TvPMOzw5GGG9pbvAeyL00qw0hljaelnmgdSmCiz3 u2K0orlsfSc3mNLKGfWION+nJtdLH4IKb9xpohtqwWrTrsJxOY1qougcmEE1cBVAvEHZC+La7oAc bZ77q3YqmyIN4Kus48IByXhjUMym2c5e+V9klgLq6mFmEBiBS1wDW2E9cJUypa+TOCoiARN1JRix I7y6NA3MxRWlRVNtkdWmyw7UCIKEKiCgoHNgRjdmmjxQyre97WyplGdIzwnbEht+Ml5rjfYk6eLf kcZt4dzh7QxBvDB27bzg9r0eX5WvedHYtOhXOWTCmCjK2BdG1bvnKKjKv7B0PBNoUTZkWhRXs983 XsgG0YFIpkgtInskTaQtuFoA1CpSAZBuWWEo05KuiQF+CBckHsO0w+/spkqwzFFK7fUaDxcg+Ac/ 4et7v7VAliMCCa6vdyaseiHNA0t7M/htmZb+L7skOh+CHCYfV1paRHRNshXKlXhEvCapWNMIs6d8 qWvezmxgIQxrOOF4jFJ2TK9Xqo9SOzfXC+WMc6Vi5sWhGpOeNgglFc4rd4IjWUsI2W1ukYNpQC+S lpwrxfEZTfOOLs87MEQwfR07a4rBbudmsUpa971yREQImkgMcsccAAERIG6aPfjh4zg0qQzmFICr G2QMbjF8pFzV0nXOqyhjhXSgzzaGIFjFBHKs0gyF7NSC5ZZTAIZ+Cmt8Y8eUTMOk/Q9Ot/OvlLHQ P4APWHOH3htODrPh7j3H1hQHKTvWtZLsZVbYAH2hzhzG0PKYXbFHof7MADHFBVRV9nw/D7t4eRr4 c32J5su2cuRm18c08pDy4kM2UFZidyvTMcXRRG2JEwDQAcITJDncpFTaReuKquOBShgYaeN554Hq 2OHtXdEYchNERCFF8VHNDzt9seccCA5Qm8Fc5euQ5TtG/LCmQXTp4aNdIQ06SYNGQxDk1WctoMET +sWUezFR+vesgqhjbP0snAp6o46OCAbKjJ76gzbjv7XXssThTEWeuJYusNlZz7O496bY+rTvWZyo 7MzNW5xbYnFRvjjFu8aoxxvGdLA9aVzJiJgQpUYfWR7t94mdRGtTSIya3qDOdLeDVEJK0IrRwlJm jH/iozzVMwgcoUDp08hHu9n/mEgnCI63ODn8Glv3kxvSUBFJYB8HYqHwX3uXir2AFiiMU48L/2n9 f0789Cvm2ecbsZJE5+JPzn5xOV4pRn0bTbYqyyZKlKL1GOBMfZ+Y1p9PDTp/Fhg9Lrys0idzZxXk lyxUUonElPgpTNN7OlrZJzqZZbDTD6C0zPyZqne6arQxsGSpVQOMuAuviutV6FLlLFLSrRpIKMX/ i623QDq4y+bijUVam7lWO05qMGG9SptuaVtZImpFCwJRyGSzKA7mXWks0Jgo70pbDZyJsyq1JemG KTGOkuWDZUjZaXWcGikxqOmm5rDSiauxUMqJRKqKZ+S8MwYdmulN1sOie5EjphmsCjHIGNFLF2rL E1Blq0RSHIhwmk2lwYdWrfG5lFrFE7rnXWBGMTmWiBpKiW20AqsgVigu/8vybeL5+V9PM5hqCnnI eU8XwNRmMt3KEIR+EwaC8zW81rRQGjUNhU9Rs5dP6N3ZJvlqN1obAFB5+3n3aqS4WG07GQ1Y3QzV ZFWU3eTCLwOA2eouhKMy6WYRMJ5gXzu17uxOAZokNpWDKkvsWqnMHTyBrxt58jlE8xwh6CHzKovq PKjluVRc+dTpIRiMIm2lnKcWWRE2gZawNRWoOjSluaWOHgfCq79Z68gLgQgMGEMESqpPP8tzYG6J iO2bSsr9p7csssJr+vMO5AL7T+fDR/zdH7fftOEWIk2fR97fJnI0OiybOYlI6zy0/wxWfAp/74PV Ob4ik9JJB6IBeQj0qKYD5Lfuhzzpg29FZkPKXxLHNEK1VtDmEnDYD4sfV9uqzrkiVit16TavRXbi vHteQDSRvAdn5fMBSCZdoQJdn9G71TFrk2zHoH904JAd/LhypA4uMJuFlMeyICft1jtTZOPtr9Hu 7OH2J6R155VCx2UOmDogbYqidRUkohTXcyuGgbgEF3jaIbcIhORQKQx2WcyAMt6K5HkAXYLvrdrX ts2Tp4b+Y6jcmwZMM2muqulLyQqcIBwwiAO0451ldqbtOA+TBJlblj28uwL08v5kwcLuZwdfZcLp njqkoEm9DjAuXEO/Actmiw0AEMVRN4ggjr0+JowZJyZVFGKLHvuIepNOobHw+uAl02YXk3Uj1bMk 592m3rXSjJl6D35cNtx58qBjCU5NcwfaeDggO3e8Igjmi1dEIOtNq7TPigGP44vBKIwGqPxDY7wi Rm9r84hncxLjMLtGuomGapeML33vG6mbt9cbRjie9TjW4x1pYcjMPrZz1Ec7zLSfNUfxe9+IatcI tjAnDCOAW0J3s8ztSkmvBFV9Ge+p6vT1WC41nW76Wn7wccdzOJfnNFUo3Nc7W8YK9y2qGc5YXq6G Eo3xhS9nMMYGmNhjasHUvi0mEmqILW0LOrORfS88Z3UztW0wuNjjW2F6WdWNFo6GFnmq0F5NhCE4 VdjZrPnKuNllHGN6YUcY1lNzsWwhfCJxdhezms81WcjCqXzEp5CGQiMbF06WdZBsW4DRRR0BaY7K H4iBeYjANywXnDAui4E09Q2Q+OG9SA+LP1pfKsjShC5wxmLTWHtHMANju7TbO3oGCST3PV4GYAQb 7McMILt6bjrMDkLFBDRyIAGxhfCe1F0EdcBBDV+PoA/ECyDcBjUIY9zKyssRa1a+Wd0Opknp1Jk4 DhnWhFNwqKasuFweaHSQE01GuxScA0CgrojdU6LHJ05rma1wGNnOgbYiFGaWasNTywbceQwBcFVC vu9NLVzBKy/bNd7FvNI79QAU3ZCuFK8BnfgrOib0Dm2rWWktl9dwXJAMSBymwbFQN7Se7Z7srRCd CAcIUSY4ga1kmux4cgCII0Oza7AOyNjjAa6IXfDfD2YqGa2zOJDYK2/igLrPF1Ecrb4AM6VIvEhI PxZya52ZxQJv4CmsFmdmWTriNsKbKcAQdJIg9QDkxAIyTrI3ghIoVj6TiMmcDnb6O619OHFSE4hH swMQ9SidNJ1zr40nPVmw10qKcFOSYrC9PV1GNU4oZM0T0mb1qpN8YAM/D4v75Y6+fLdRLPsQa7SB Glm4zQUHZGcknqCY92Ybqe3PXk8tz4fMS3yB+GZL4pxbel59KG8lz1xzU21h2+b/VYj5GMDe9D02 A32Ev1c+Bc9/JQIFnBtRwEgWHPLezGycAzZaBxe7SLPreL60gmWMMI4zweMMWreDjjM/GXu0ePHh eEdndkJhuy6j1CB6hUENPen+eQ9AGzIU/rti+PZYY59d4GkyQQwgWpGcIG96loBu1essc4Jtvb52 2ovStUSU49bbyu/fDM2D0CJgB+k4tfJcKNtwxSBhvnuAnXBAhIJGQOwKq23Yue3lrm6d97UOiYTT VzDOlRMAjt5BzLI6bGBmE25PMGr7XYPTG2EXVBuaVzj08TmhoVUG6cXWeALg0tDWtTiKrtgFqYhc sWsmw5xvu+rdA4zCUP4cxloYg+OcjIyAi2miSsIjCpayiIfktD4G7UoqRVGcRbsHkjKzozGg/TqG 2H0NdcgkM51VuyQ0hCaYTEkhmVemFd7WOqUVgIiirA4ZjicOCFZlmZmU/Khh8o0W16p2hyHQlJDe tH50/7Bf5tf/JETj5+Q4+0rMpm9KVf2ta3uyYWOwi0FijG1ps4Uh0V6efZ2eKQ7Ao7HCPVEja0Ix eu90AvbOk2Eu0hrq9ZTMJS7sHgFLYT+YSzsBzawBuLstgVxKijTdkrsMK4ujkxaNEpGr5tPigQIK IldAC7SSkkRWFBjSr3YyhTxGbQolLMsNEaHNRncHTCOELtZ3tSCl0eaIMmlAbYzlpppOczjIVwi9 awexR2lqGmsbO1iuGBworaC1zllGtHSywwztVLrfKfRhBydRcwfksMSsnXzbVRiTnY1ji9ddRegw 1fCkP642xquWKdz343VcwbRSGSvmDnZHyOOMJrlnNo1GGUxsxBnWAxvlIVvuAHggS4Q95Hx+bzJ7 R4JdzJ0h6jxhYTspUO06PAPENw60adH+QZ9rdnfOiwcDvGt1D1fz+Ix+PUOSlqu59WLLMi9r+n5y /D6dNpyKI7maxL3mNA7z2wpQqQxpXcxaDyYzhZfTY4C3cAEm0gYdM2VHI5bXDChMmP2szLm+s5VG lxzW1VMudExKrENSMvq8qk+oMZejBERDg6BLaAd8ZEV6YiEllKYwU68auG+ZW22lzj5hVMIJtOwJ kQqe3gX6vtgfUbEeQg9vstI4A/8gUjkdLnE55vdoI6ldTIOdGMnmaLMMUEwZfD4mKfNvnAa09Vp6 zqXKKzb/zAbmTTibSfWoNPtZmixLahhGSIudO6p1KlF6JfEyziS5XwjBzptJ5mrhFgzELBz0JnN/ x9iXXXv9G3u7Ysc+H47mjZvFl6eX/b76UGfAZg2fK8oz6mePjxEI/Wg5BupZ0HU4PJ9WQ7fmH4YB DI/xQ+OJQ8SPxXAAkIMtX817meyqAG4q8po/6HlD74ARzUO2/FOt5rMKzBS/LLzP8c8RcC88NXjs gVe2CkQcHGCX63PKCO3Jl7KtFJ0f42cgLM1Ynz6xJPWPWKIZ0H4qpy54JHDfM7OJShy8jYTqcpvJ H3WVVQsWrvl5a0QnelLu5zZjDNhqHayHBaQPFBM3gZeXfzcFOg3ac+Z+mDujz+Q4GngRJ0Z2kGLq ltmkye4afPPrhkPP1oC/q5/Ti6Q27Gsytswdf8srLnRUdRykq+VYPzeICb+5MdD2QnwamGlJrKHM 9Pf5vXf2aap17lHz7yEOTGiCAj3T/ZqKCg6jfNUSna03BHBVl7FcLJ0e7UYiBBihDykPD0b34rYC ioux9/QH3ramKZkKQhEiSmkiGIS5H+fVA4qKpg+89nHDFv1cuy3NL5F/k4ZBArLAb+aW67hs+v6e 9ucDydocFUW9ybuFmsGvkuUj0GNerBlt+wGwGcrLnWzQrVM4ZabEncAlEVVIPMKoTA6GBvbJyBOT 3IX4PRDszqr2xarsxrnrW5m2rxedLGblnniO2FsYrvRlm5pC4UthbZhBM+eaNXg46x4KbzAXTa5M 8sNH1uz3MiZTS7bRGD9TTOI2gYCBRBqCx6dtYqLB8y5pDyNcW09HpPcPA+/b2mmY9irjL7WAyyES y7dm2bngXtqmuMBAoACkSBrKYq57UbXuwvHbHbzj27e7d5YaRJUhMjzwUV4j3AiA5EU5neopYNU4 Vx5uTfd47dLG8DLZsdNHJJx15Fsxjg1ljxrVuMkNwqOPDjv48T1vjeqa2rfWcjzEkQIK431o9NlM XtkLwhgktYVfOloFKZoNgCtbRT1MK0TmtzctcDSXLGUdXgCAGpNScodey4h48ednb1vjtRw2PhBV 0r9SmbVvMeIKO++0lXHbq84pg95F7OlUIcH4Z/ADJwdeJA2JNsCUQDVRoxY7N+zrVb7AcZc/gwb9 T3rPRY2dPZ0aLcM3srnqzfdx13TqecrnGD4IsIBIBQHtLHiOnXrzB5bdIul3UTwwhTQMerbcuyHT jqscA6h38/RrrvdEhENumDnpTkw4ltqTWL9sgZghePKeIHMCksdSgOWAvJtpZHKmnf3G8LhwN27N z5+fXyhljSsyrJjg5AXBLI1c3PEMCNlw+q+YnQB88YuGZyubZgZK8y9+1XCnutndN7Brlu6ZMBbO MbbmDr5MzN3i8CYOoHa3mRgPuZrtqPICvU9vieD35ag5AO/rLjALn0sPWuZmgK94Y7jMViBvWe1d pDktIxD752xeMAAERMQN03Cssri86Vo1hFpGt9TY2++u/R3/6nr7y+aG0wb957/OAZU6Yc0JOmNL oX1g3dw36LHGOM+XMNcV5tmZy312cW5+cnsucRGJ+RE8dMHeQzU75fnHi3mH8diG4O25P8BvUOzb TLhDo5d0MhJb+L+nY6ZwsPpRn1zDXF4RERyuDpkHFqUR8446RRtK0cgza4Yb8LTneQD++Mtjp59u t7e3ffPBO++9EEcHLDDHoBDMzdoQF0VRSIJ68jP0afBc+PtpmfX7b8x38vJjAGfGFCx+c7/gUSg7 C9VtAXnmBuVhzRJ9GPYTI35k/giInuIgoQd6P+DQgfn9k9TpDZEN8kseq/VYD8cg8oQFfrOFvFU8 Ph579vZY+wnly8/4BIeM8FYN5GA8vLqCOFMpWvm/7UAQQjBzePWhIJRNcZ09uWMsqpO+cZ3kzZXv pKBiu27pyAo08VbPqRP3IFcSmdtBZpTwQpJytm+CSvoIgZJlN9BnomGGjYYXcYs17Qpaa5ABO8D6 9APADigk0DpK79j9cI4ZnV1A6IiNGtmtbWXZvRBK4CW24JKd63hJzsTjFY3LiEiJ47Izo8UW+Nl5 d8+NdCsgXWd5frTJ74bf2cTPfibUwyjiFCujXLdYYXyesGWt8o24ATlYK7RrPfEyqL/WIGR8shJ2 Jw6syBJIj+Ht8XE7OYfjDdujLofvAewXwB7OwO4J8DXR+p49Ii7XOv4cLKFtFGDLIhaiSqy1Hzb+ B8BPmO1BOrPmkhJ5A4owCA4YnSGOjgnKG0g9L4DyD9cZAJj0jp2duztVA3aehVkSekiWLiEeDMtR QFfOHkvCK1vMvr+m+RmZIYbl7IlIZB/AARc8PynU4TEvtaEuE8RTzDu/A5WWuhEKxFNHRkM70jWS J66umZFJJYIZbmHDQZW8DsDqUzYQ9vCwJp/w/aQ2WtLl+DjnVq1xy5XMJ0PF5qUs0GcReWiLK3nW 3l3/qZiZ3rjGLfhHFvUvPFW6SqVw25V4TGkTVRcW5uoc0TgxRvGzc3GFiVnaqrzctSTOPjRoWyqi LbWiK1e2GYxsSYS4/rBh/P9NnZA/zd0H+Hks3NUcslExmyFRmWVrD+EjJjltTMD6w2dTuNoc5zA0 IpRBVPpREgXE2RXZrnegA6yv22aTZs8EoSATkoF2EQUES4blRYJZJu3Yr7fh6Ly5GCUHYZti2pA8 hLJkARD3StNwwUaaCc4ySDgQGYQrLv2BZe6l0nOeKSo84PGirE3G6bv3BI4w4EIh36/pqDSYs34j 1WigPmRnzNp476ZFXgrlttphZxnV2Or7DEDQCJARHoUxeAoRAgFRlhd+U64OdLL4bVegyKYDAaQd iiQctjEpImvGrij9w+fs29ZlRcJny01PUd4YYsY66KgZQLHRRtzoP79zam3gAsU5hn3Vd8r3ulVF 7erV0uOsHAJRC/c+fN6Ev5hE5yE3iSMiZPVyDhcMWhunyeHyAGip8woAADpyit7GuKyXo0M4SdiN ONH4pJXBJCSTuxhMQO/Lw1HtR6WauWyBXWqpvEpsnTmdtrPM0FMnAXgrBz84gGcGTCcI44WqMtjr 7cdJPyZ4EX0lgM5yW+IkuOr9ASwKHRFDypOb2Dw59b7MKXw7g0eYwTw2anT+bnpaOO/Ou61OMMxM gyYa3M3Qdi0MQAmpLkGaAtnRs2tHW95phCLVFRyzDo5XN0RAgpD4ohvBceWZgmPTVS8+M1q/N+Cx 0Ma9G0axbNCG6Z9HXIer8LXifTGgTfbHR+pzxOebYdoEMzsGKALaLEhjEjVMQOAYDybzmzPdMOnw e8ARmDvyc3iL4vNE+OPWJbZ/rS7E1NCttu2r9HvxGgQbs602hNmCXREAAKIQTYlRTYo2hduKRzsF QWpOuxZWig1ntLAT0NcIAPfnRboDbEHCNZVfrKuxJbBFoXLsM34UfhsGWAQXJJIFjVMpsQBsSezI q8R0elxRM9I54qBgNihKia/USEAA1QgBAkMS6Oe3a/DZSSZ46sMEaz2zuGnfTST0RECJdqCTTfeW NAPaALYjdeOQeTmoUk4BqMmwqz8E0CTCAJhojT10oEGujrJZ90DUi8DOApOQni59xlejkQbRegE8 gmN4vnDGTgrLlXJhLHAWzzuETk3S1OW/O9UbYcu2yAbIUgemGAXcEFg65FCAP/ZAgQWh0ADII/GI G9ihixNt2gs+UZX26is3tB2t3xnd09GV80i1aVs4SwcH7kF3Pstn3i5aYrgbufg99r22s1Wnha76 4YYRu+N+cAHxvCStjfN0TJ1kikXC6tB7Xi+CwzXKsqLnmM4gcLWmmz5hK3dRYO0eEekc+ZwdF+nr U9vV8VxArV8FeWJ3d9tdorR2A6LWvRTq+9LW553nPJ6zeU/cp9VVT3pPrxcW8LC0IY5BEaVOvOuD lKZ55JhHKzooavVjNs7uwuivyDWfJoxi6bQvSUYnMoJyha1/DBnQSMwMgg+z6u0fIAW9wZHzljj2 mhj5i3NuGvnh17eQMtYg8+SHs5YdXP3n04g1fU/E+pZzf/Ug/ML6caW5is29pVxtGL4birNR0zNa GlBpNHQOE3mk72c5oW954p8PtGYhnWoiN4xCq0GiHmFlMxEuzsmN6lbat2PjOZyIpMwkGXqomocf TMhfa0BfHDvZnU1W9bIMRVmGxmXmHt4mcRVY4Fd4iCR1rDl3Cw2Xj9Jt1xebeBVPEcTOI1G8qMmN W851PDjxubnZiEr0Ter1Tp8MkYfdTS3OogpVTvrepWGoIfW6e32N/EmFhpYG/ghm/pl2SPI4ODK9 XG8PTWFfz7rPH5QNpR33mMceCZQyXWkbQmQ0MewglwUj+9VWPFe/1/uDYZs+WSQ+T8f7YiHi5MiP FzJ7ONXD3YksHViPjA5C//R79vGWoHhjK75Phv09Je9O3mAMQz9fTymfr4ZIKeO+nDW0vPmBmYZh 3xxm55rYcXwa/Zsft5+R6ymas+NEngXkwA5DDanHMelYRvyOR1tMQDHne+G8XvxjUaVg4ZKpWlYa axbOSBLSoHpMTIAUWSzWhcjOQCfOIDlMN49OrkA2g54cGYQcenFyC9FGL7kWyGD8auy0FH6yyGJB qVK3c4kaENF7CcKj6q7bDZ6Nk9c2zQ2mx44NVMNanctmfEmXsbIuD0NDYPVwLQEvpoDw3pz5rPFq O0Z789tIPG84MaYwns1ra9SCEXCrkGQTRIB4TWkLbMNXXfNEvqoM+GfPe+po71DEJhq59R+vFA3H f230YrvXiTIxIhEaGIQXtfBLvfC2onSc8TVwOoqrB2DCZcjXf5ikJQ6PIZkwd+zlXNe2tcZG8eKO pzmdPufXfFoPOF6ui/VnOwqXaxgLTy1D61tUB5poLWMyExLDALoypUqQ5VLhlBsxa2DgmmVVyree UgA5XZ5CunONeMvDdXM48VW8Fdo64nO869PQM7x5h5yjlWeQZEEJhBKy4iQzlKodXQ4xuM8JyGcK 0hOOjSizLhd6LZRAGLpoiIESUJ1rjdo2MH5Pxw+9BbPHQQlmBAl6hXNiEiiBtFhV9cHSwqyrVGHz SImurhCG3ZtdCWoxxvURoXyzobVtsyjTKeL1eM2nddcRmMccFdiSXFxxfYIm5yABq+GMp7XwnXES aGLsNbuYCrAHVCouDdav6bJDhsydCaQuNxUZ1tRotjuhntVcxpINeOcBTWu4YuKB6aqloRnQXzne aM0gAhzzTGB1ldBKOabHWaneQrXfbfXnyZgT8AUNs+IgJJ0xN8U+CIbAie4IKHWBy/Gmvm5+Wsql WnNt48S4qKdRXO7Y/XzmmNYUGSUEA6dr6FTaK8ltLWwxbfjwzc0tsjdazweRNmfKCSgauZ+VX2Sb QOSDiiJSLxheMXoeD8EwqJYDDJb4ZUvW78p3nk+SETcTlKwmKvvyREQImQbAWSl8Vg965TWilb44 oxrhzW5vak1L8lzoHB+DRfk5fihCQxtjG2eLnEWizSjXPOWdrY4DLF4wqcUteIzxWN9BogkyYUOc aOSE3APwRsnhdVxFdXaPZjXSb6rTXBLYXXCSV0njE4WetZxu+bpAWjNJxpVn6ulKV2xtT+uuSRvG lC4zL6OFyNEsB7ghBr+sNXMH1h4Q+1vxEsINOXg8JE5Mdz7/QB2B4z9qY+fz7efsf4fGV2kUteH+ kjppzQ+JJftgtRWcy+vjwxhAcsiZzDcGpea5zCp9u+8q8RM4znWYx4GtdRty6C9ElCRaiAkgmSSA YiCIIwZCRGKG5dHPLU3tOFsEMaBrSmHKdIUVDTI5u3hQlh6WETBUH7LZnhGK444niRcRTIzgh9cT dtWLTzjT6HylL6xmU+s6ULUNNadZjRpQsKaa0ZJ1BrTORg3EYdApxMu9wavMLb71WHxG0ZfG6vWE 475VTWXus0XS0mzGYqhmhMB+kqmb9JVSOh8X22bJFhNJ8M3S7EdwfXYosnHetGavs4thJuBwpNao b762tuDO/vNOIki80y5/Z07ZixSqglQSTfd7wjkAoHhNBcBzCmN9Rh1wzEl8rctfLwinkNs2FXbH US1K7gWAFaARlRGi7BQopjhruC5+ir1xvi7BV6tdwbLDJFkIZI5iAxtZXkEaw2hmnMA5bcRmVark jfBtajSK4azyTZjVMcgz5pYgTSgOD3AYR2xQBEGGGeT3Avi2eVr1k19gdgyOqzE2JrvUHz9XPHqE RthrZpH6482x64cQHFdiW1mu04Y1sLILj3TLfrQwHoaysBNIfft2JvxvWu96BZOdpaLZ21lON2MS rKtkVBZ1C5sHnK9tja5Kwre1EMb64+MIvQRiAC7TN7JlQracJXzybWZ9IAzhNTqXv5Da9+sdxOPz DkHxUCJTClz2TOmc6JchzejLuzkcJTSYgI7DKiMFMmTKLbKBAueyr9VwwNDN3nLTmuUx6Aduuu2u pbfc56wcNN+gughmVyORJxdLECO4dBezN+hDjw+QsQvXYZydtAxwlHPV4aMcYo7XISkETB6ApGYS 07BIDV1EZ4jrO+3+QwDzbTvWQb174gAv5HGwI8ueMehn39Lx3pJhu6ZnPKHQkmokZwuqnwM0jK+d +0xvE0m+E0WTlTG7DuADPbGdZbrvvDFV0YIMQMJ0hO7X6X78MvVhvWcSxz3PXB57bmCEk1qHiCSB j2fPabvtfbV1We+l1/sBrHD2bjYXDVzx7Nz4plJnHo+IDmM8HPVtmWBWDtH4Xo8VYlA0V0eFzieI EcztOBzk8jHTbpwzBEX46BABMbM5YQeJYAazrQEXyTbSWJzrm9qWjZWZM4O2qMYKsQQToyiF64rY 1gIzZcNgyE5SgbgUvego0K65Z1rBbZVdamornLJWdPB+lgEiEFSQVR3tbIbMK55SwhSWyEIbFx1I paD2fS8Lwc/SOyr6XdQJg2CRm9oXm5YNGzyb3ctozwcrtVhebsGm10RJmFWMFDq5FYySlLCS5SvV 2TxbLUJSwaQjjXfh2ZVjug7uBuIrNwXqo8dozoOK3i+GOOO8VnRr4pUUNWfJ+Azi/GaOni8VsZ3o 6CIiBEztKIEmYPxtI5TJCIVq+dy6cKquUMUhfDBauHvHEX0pYyzs6la54rmKGkIwPz01nO0dIOex yde6xuEAEwg9IOwHR8AHqB+lThwVGt6nYNzo9j1IA4Dj4uw9m7+/4+3yodHviOD1Hqa+VRdcmImo hJRuPG7JvCvK1KouPsKQG065NQ6eB65sje9bxGc11ypyFeOeZN5ysNOkiKicOZsh0TlopStkUJK6 uZ25hKp0oQmGcYdM+ajBnWYqcaKzuEZzvRhENqM2txNicWzWI+52xFcRrVwDszZQ0RvFVd4VOTmc 2tzapD3cxnVaq8LGdZzmFBk4mmETmQ1a3rLQlabO3fEBt1cdVVDzqm2yrZTSDp7KuTOaoGCHqibf WVvRuK/Fh2BwIpDFUFNCZAbC0G2A4Ok6EsPAFJ4Bb9tp+75qIiOJlRSBqD4iR2U0oVKWLGrRZZQg 55CkMihW5xjr6ISyvIzdqcb78/h27Xqb7WurI4tTzQ4cV64fouwfXSWEpWWStl3PsFccbrdPHqwy hiK2ljDEPVHI5XIKQDZ6vrI3V8mxuikzN8pYxXQiRjR185uL74NLRyDSYdpWDpLdMnRhg4q+jo1v aire+L7RW1hCF/uYL0iVidp8Dkp5mCL7O3nxPjz4zivD7iWYZh2TMeeeZq8PSEZMHT1xit3M0b7Y 7dmAlS2LtHKIZ5vGWITHU5w2naEkkkoTkuQJk/z7N1zffJ1b671HlYWqqTOFoSzVlm+yQqElKCAL KsjBLZ4uqc6kbYNO9dA18sYqGADUKZ5LbJYyIdzuD1O/RyEFPJgiezsQjTPKuyIUAboLhrk0Jv0T lDeqoFWDtsr3iXO34W0xladxuQ4+0IiY2daFaXOcrnBNhjLOHDHTAagczAvKEjaEeNjR1dy2K/Ma GpZwemxKReHhEtDpAGkX5ijtWmJBcKy2hcWyqcsrgQOTscY070rXXYGzO3UJkyKy64ucA+Xbrtzd cVD5PNRx7c31z1sms3JcP03nnrFAUcLS13PUDCLoK2GdgNkLPMJtZJDDTNsAKLdmE1LOYhx++wcQ L7OMe+e/rvJ5iihA/pFNxyvXUmpf0jSiOjQY3jCT7Qnld0NI6543EWhDIIAH6jKRjngULGDYsFIY MzFBQAB0ErgmGIilcMM8I4HB12mK2ujhClo551camr2V+WT4Xrhg+EZec9CMBcoi4Ki1ObINhnYP DaavmmbaI7KQdJ6SjdHXIOuGdFbCBnSGrxHWTw92KYyMb4YvKzGC5WQKaaDF9MZiCIiBEHFVFoNR ISF6aOR2NG8LqMVBtlnq5homTBS/Y45a4x0Gtlym/GuERfQ0zAwzxdjSMMYXjGeAWq3ccKALCFqX QWwZEQpk6eEDeDzl8AMg7F8KZgVFs5QfPJ+cmlBgMIYi74yrwAzdYTvDG+FpjG18HXjWdXwveZzq b0czQc1RLKlscDGNEnC99H3m+EqvFBa7jhzCJuH1jJ8cw2Nlm5EhksTYHV2sUylSDKwplV86MkFy Wj9ZarKFcTr6wHaZvrpS10CcPkkI2R89RShdTPPSUFWNYyk2PrekKrqms0DB7xMj3hLAOAAT7xBy QU6zs5hxbca06DnMz0wGzz35PV0X0lyDnr6w/eKtz4W69Hesw7zJO+75rNY3mOXS5p64uN41iXrV p4sesHKi7u+C90zMxIJmBMISAGhiSGML0u2M1vrdWbYs6Q1zN5ElllVRMviYKpydVFpYN3EtUXEq qjMywObUG7m90z4iCCNrb3FQU16qGZm0YlRovdqiYoSrVVO97mafCt80NhPDzuHsfFbRnD5rEXrG 9Ot4fL4xEhVbhamMj40zNIatWYRC3VuvVMvQXHX8FB6i3pYhQK/AUbBhrCKotKRKHUIE5PNuqrmE E8NEJsh4eOfnOjlvlmBSJqGBBLINlera8Vw7nRmseyVBUIAiJamDhZM9IgIga2CQUAT7oWYzEnrB 9h21aYRMSiCHX1xyhO1VhR2lFEihtaWGAMGkuKMCTMpJ1mCdgSgiAyrpG+rtKOykpwhjpi+lGDhp FA2ra1GmOKyxQXQAAY2o2L9USWGT8MnYwxWyRoFm/DBqQjRDbOWF10xVzKZKDc5s6Ji9RCDrWfO2 FKwnBER2OdmxwShQJBviGGJy3HRwxDMHY9a2Ypm59OtTCDWc7QwEsB2JZ2BsTdyzMzefF1j132rb 8ab8AGXnOGgF2va7NZQEtBboxTBApZgobEqmG6dM8A0cJ60vKzOE8YLlHZB18gH5TqiC5nD13yAF zhg4WxN7aZ2wwAslrQxkuaSAjgApFXsApAYFVQejDAbXa0vjkbDG9ZuCX3BBqJsIqBSD8glhMNqm EHagV50pW2OzLNzXWGz6/WB2aFwDALv5gcIbzGdzKZ6YCBMI8rEvidyoXb08tapgLWjHjN+LDjcH XnMzPOdVhKVUcgfVzTlhMTwk+mzW4noumhGighQoUYwVTKF2jgfNnSbpGTGb8vP9q75J8r0nqPSX wzNqbo9Mr5BsCclrOmuAtSt5lGFmc5MJ8tyJGlsMQYZZ1hC+Es1WTSo7BYvlPWctBHCxUQS2EIDA QByIbNpLQAzN38UdYoF48Ci9caYbl3YVxxyo111Rntqhde5vTBcas7StX5gx9qIDSMBhm64wrZXE BSn1hM6HVy6uy1eDpIClcnb4vfgtr653hsxxmLplWTs8xB0YYxxMjGoRECIMECBBBJDyR8dZbNV2 NXDWd2MdDxyxlaDUjZ0XWvLXRrt0gJtlewwEZVZ2Ayg2j7JUF+mabYYXYIkzUP1q+rwPnhb6zjjv t+3UZLFzbyWWlnFki/N74TiWk1JYmLSvXOUqOZKlbZIrhbBoUFcoJZjV+DeSIgEMZBxm0hauCpFz Szi+ryt4NOb/UiBEHbJrKaX1zq+lrao6cYIlYzytZotSZtZV0plrjAykK0tYquOCxjhfGUsMbTRX 4RMjKlIwcs7NWDr6Vi18qJC0mvi8UjUSrYs+cJ5YHCOY+AxDt4G0OCJn2PI9RdNYgfIcp7QO1fkO zo5jAeUe+9IgE+D/vK3dt15U49PRjIGM1KhXTkOdJykYSo5niFaAokTD0qONnJMBHD7lIvS1F4hX wsWzrCZVmMcYuYkVlr3btrOHGNvfQcuuLLsvhwkgiEdhgRu8kAPDgVvT3jG74iMNKjD6QUgNaiys xBetY4iiSTYoIVspzqcah8KofbRWDezNXo2jLp1W5MGDcyb0b2G97gp8xRG4jLvWbsSMPlbvGYzj O7grK/58Elo/V97cyO3LfvYLcXWGkfNQUZbctp4sl+70lLoclBWqvrOlouXNitwFfcGb0KosMGfO BW8FgN0eZWtvfv7s5mzAN9/5B/zvJ87zzplEPHBbns3ykITGRXzWDEL27NHzv24Niim6IgHN05vj 03ZAZ6Ob7PXgXfZ02dR4bai2YqgewWKAu7fz4KuKBESFeyVYB07Ro6bP3LWxydMTQNTK1hm8oA81 RVBLMFDYZedesCqUnHDLCsJuDhrOeMpSGkISnpIDLVjZlsSr3hqGbjzPn8RtG2Y7b8dtaDcd+2N7 y060uRg+rxcfW7DPNJVVmxYEcU6t8BqN+tUnrdGiNMoMgaDbHARUZaUhamTDcj72Acuk9izAhtlR 18Ma5gJGJs2CZ5YSmmeYyYPNzaEBk1hZ7xGIFECMcVxFkRAKJpAJPLKMpBAEoE0wvmKY4xpGS3lb STsqTrd9slhjiheRotGcznQ2QeAma6Zhlwm+mGqZKMrwCTMnRxtm0BWgpUlwwvJah9lADKJWvTAS FtFhquTdIb9vHfuQDAC4byPnxLe0oq+fTdVfZD8Peq4v01hvTxHAcPnXnrvbbL6LuPS+vPgm+ZGA 8nljyuik9HZ+3dzw+YY7+1plaAD/qQwG78ZzGt+vKnFX3xqM0pZ0LJnotopilA/NGACBGxQSa9ly WGNnO0hG4EiliFwfGj0TNwtXbcaXE5yWZUzsWcsLykMI7IT1w9yAIn3RI+3g5kOU1CMDIEbxk23d F4wmkbZ+y+D624wB5c84kYPUqoqiq31YzfPFS4/vw7XSq5bd9s44N1GLSehnaUK4hAALBAJ/cETn 6CSSWgmawEgmRxx/ZvTVUHk4ebOvw6CAW3IN/RsGS5wTV8jLVAFN7TxwmiJDESZRi57lczv35fA/ WBmVWu9401fFqdcuc8hfPROu1XzcKOcd+eYDbscKrHJs/vREAxjeCrgJIBkEuc4BQAMgQ8Bd/0Tf 0ph6Jor8qiG7uVRYcnLjm6aDHTy3fAycd+mszRhv3cL74wsaRemJrBcFrF5hdr9SJsjhnK1266Ii IESz1VXTriyuWUdIxrpSWemLmtBpY6K6gjiTo6TXWeELPjO0wKOW5a7Lgz1e7CTThWd5LbkgfE1R 7DDF2blu44xMY52moyjKEKSZ1a5TzsFyyg6iaq+wp2zHBnvnu3Oq7ZxHnmecxXgnin8FrumShEMq uyetcVs4oI7AgCXjdcjSq55Mt83PEoRzyo4VBhjjk6EkExcSylXOpreWL5Yywqn2o4J2rL6k2Ad7 gPjwPtCwc54kuO8dA1BkmQHyh1BsE3h0nIKe0D64AD5gfcO5ue4dK71UNyeOe55WJCu2xaEcdZTL cDo/aJ/6R+dLHRp4mHxKrnBJUvKIpQ+LH4ZmtEGTERwjU4Uc7m4lWnFitTItiJbMwO5qS5w8uaiN ypk3mU+cVMY0RRiFCuoe5zuEsZmMQsNmVh3l0a2ReK3ms5KzvJWaMiwoubwqyro1e95Vvq3xOTeH nEk4hsN/+jh/jrihtiZgZMxzl3mm2Gz73S/X1jkSymfULckmxffZIdNcnTpVX4URzWbvleEXDvON oSRMBKhpaLSyyuERXc7sM5TERdBXKyI7HCUZHHBVUvZjM41CDLvs+QqABXRXDSl7ufFUN6RrmJyw ImXZ0QjyQCxESL6kE0imcErbu3WHaomVEdu+rgY2gO2wDJq35lmymGnfr3p/SuZMOcbeqnccerCy MvXVuRMRc+Hcq9/ix15J9PbnvtbYbzB2NzjbsDMeO6YJIByTrF+OD40yUa62achRADca5urF88hK aY/bjTLv7dGOpAfTZUvGGIxcDhHKCppslOhEoztKOsCMKOlba7aMNUz2zIsBQCtrNYya1XWDqhav vUZNJLyc5d5f09HoUCd3iR+MsuVrvTHl8uo5EGpvxJ1CINIV02o0coAWpHQUBhZSrLcHHITydSTb QgzlhAzrKQT345EQOU/MsddsXrgPT21zzzPFYoaTy9swiSbB7k2YvyxNsIOXB+D53pd4WDYSn3po mkFnfRBF5WAyVIMtIYJtF9VlAI+eovEV1R7QlWEzDNKaaZYGZW7lc7GVbYSg8oJWlKeNH5OJgVJO Nns7EyUUpU4ZWxhc1diKOhVGfR8L1fTCuV9baaB1g0sMxCd34u06nJru87Vdlx6HfWn0rSVkdnp9 A2+TCTow6D8q1q94obzjLAY2jWLtMLidcYioEdozAFcCcjQ4myq6rJCHEHDRQ6az0wypWHwAvteF 2yow2zxTXZMYWBncYl8jjaervfSAG50Ee6XgB5IgRB6n1qgQIgwIA6Z77Ox1k7LBTku0cIBEWJ1e qO2wlNzOSEw1Nl7VE91aYIHMhSBErwvWqgF+GFa1s164YstaLScoYQhjgHekIbi+M60dcidnFXzs IYUhTDGdH3caWNX0kmV2cTg6ThKt5tOl4ia5LPDBnuVWg2LHBpxrjaLSWlfT1RGURKOagYAM4zNM WLYwyjgIwcMZIGAdy0k+q0vjZ14kNQQikBBsJCtaoiASvoMH1hPF1kwWFZYzU5VYWvB6RrWlI4Tt HzRAiBfNIAfgiEBEQdwDuHrO9zHwY9W8NXh5zJRO2IKbfW+Dxf2Bw9Pvv/R7ufh3DXNycHU6r/x+ HxFP7sQXPL8v5/w7oRk/5/Sn/xCJjX+n/1VU/981/rvuHR9v8oWH/v3U60DOSGXvGmegg/XVwuNg i/R4VV44/U1f8ZWE30sqsS5XuK5jM/3EkB+1CUA/nzv/ukNP/j8dsYjDb+fam8r4KHKfH9F8iOra g207LFyDD7ob9P2hB9YcFGTNkDxcnuyo1CT2Odzfx1cT4ffqJCxMyJQxiane2bfAk355kmTh4gGJ EH3Y2ySP7q/lBx6Ox17jk/rZ1eXA3D8ApZ1pYIH5rz479uR7Smtwo3LZH/5Z4R5WX/1BB0Ij6vAg 4D193T1jZ73QBHuHwH/rcWHu97V9Jn7cMPWR7E093HbuyHSMUTPa6IcB6KCR4B4H409lgYqiqKKo oqqaoqiiqKFFUVRTs+Xy8e2H/kn+TuKICc+Z/Y1FEjsDn4aAKQ71/u/snPcgEPKvTn09Pa8YlAOS /ZdQOE7sDXqXr4qSgL3HtcJBvtgOtqR+2VxK0mliE2mPHvkCbbdu2XM1w4AVDuwEKRmOEggh7L+p ggQR4cL3O7gvcvCr5HF7iRE7afSSwo9ft38an01zEX4r0PW9WHvfTDFPt3Tphz1k8UvcYPgPe9Ug ou2dgzBQT0mjDdSd4LrdsClfbV0u1k2Jl2Ft+d/sGUtDpeYxOKxgilwfElkylGEMhriXnUess5Zt pflZpnv3Fsa7+tbM76RX/WQpQMQsHP3+mDdC9NeOeGek8KfX6f3+fo83IUC/+yZgfQervT5EU8/y qsB96Eb97XrFNpCn1zkxIUAUCT+5Cfmd99OZuBqVYQ1ZGwEJxvtgoTU8TbGqEOEh+slOUIGBisQg Ac/N8IibBN5znxcHLQEDgiKZih+xESwCFLxgI6qFRSh4CopfkFRSyqKb7oiUmFUWAh6Ov6fLHHWw zZnDliepQR1/+z0YTVEA/YDu+zubppJWHdl9fhns3Nfonl1WucuEQfy9rmNqDTkCP/VeM8ddcnD4 f/Kq5QC4o/Zq3nB1tr95ESEkRuiqfyNZL4QUWfOyj18IOQkEED0H+DYZ6+lXnZEOqB1ka9S0pjsf cWOm9nr9l65yGQwEmSZrvehR1nzJwA4MiobbmlbKFIKLGtTu6G2hjbux+ZDGF9iKF3b/p48Fea31 0el1Tu1l0fD4REc0yHtqBP+j1DsHpA0xCVsWI0PQ3YGHp6wsYngw06yJ2eko6pb+BHp8fixCHkn7 YGJ48+Z687Y0M1AKiFlx7NjXVIV7X95FblFSKVTZ6U3UhEAaKBxaD7IYbuyMHDnd3PYFGNU6zGgv aDYmvDiKQI4spKCA5PDAEtpkkHZFIefjdk0jA7sGbIw3u8fW5q8HnNHl6hth6xMteJ+buvd5RR3Q Vp0j+ne976AZyBOQgdhlsL6+4NKdyqfzU5fTxk4/HRh9bw+G62P5/Rq7PCZInJL+Eh1JDZjBrJCS nDLChPT/Vxr5diiEePkRY6vv8nvz4Pzf2M+jf0UbmoTnq7B38/XWjgOwhMBH+lk3NkzzvIuUROJy IidxNuLzVC7353uHy1XneXF73/Ghw+50nVkOn7dOuhoWQCxGl1AsQJZqE+hkwVIEeFOhkyIbFQMu M2A2lB0EbQQKkf4goxDET6l5EYOuGyyurIXKDf4feVXrD5WRhMBz+PEK1Br0+w9t3464/DYhw3xp NR6emi5yVTj6Obmx5sUZ1QkiHh++9vRHdvFSZ2H2kZm5AuXkTVdx4TUEoATuOJY+ySokEn0szaXd jddd6oB8/2/Tr6u2n7vGboIBNlKEocgoU4ruIYgkBV0AYAJL9xslIfTJeg8jMMvHB3m94DICCs1T 9H66Yza3+JDwfWnxdlk72fx/VTn3+nPkdHOorPtYYEDmFK4Pq9wSEvkziiQgupFygJ+8gJ3nAjoI 39qpXaoAgURAVChLFxsfWe/Zmyvgdnjt9sUlPnsG+PHXTriGzppwR+rhWVqW2dGnflaYKPunXBvd k9f7eZAO9J7/QUgoHNBYdUAMT4uo/J3AonUaf1qBtIHrlv93yfcfDOAe/X3c5zqRBQFq7v7dvZw5 88nyxz9XHTQPxSnjqA8LaQfJeqwhMn9iANSu1kHMoBUpXZzYNkV3GBzPIjSK2Ze52bJKkOjFnHde 92ezuz1jodPPEfU4j32nDN93Qc3XjWFqKJ8PzZ0KUYYJdAoGcRxZM2GCcPUIOBhthw7o5RuSWMTy iW38HOeWP2WnYI+qux86YiClZOETFtGKQKzxZ1e3nQ2ZUreD6QbTUTHaooQFIwKZcODXB4chDaLb TAZKmCPdXn+MOKPkKOBGxzizbnKh2HYWzKVkMHJvZ8AqmnVNnFDnn8VOfOYfJxfrZyvhBcWO+lIM mBMZt8/XEQgOV6W4ff4H5x29JA7CMicQG0f7svaSSbyRZ72fAqjDytNndMTDoWHv8qHWM8+CVXy9 jSzMD6jfsNrIw9yYHg16/fmh/U8eNRkCSgdQ1wc4PKbmJUJqQ2CjrAIzz+fh27lMECTKBWJvZzyI N42qjwOUYsEcQgMVT0cVzKrdjiockjz9fs1PenoT4GczmFhe8oXv3dzpsNod4yCeaGvOjvROWKZR OWGyBUktuu27DrK3Ovy8t3hNa9XHXckADoCCTcUt4tmInGB5IBmXETh379BhWIYyBUEChbVzKNil isX6aueRePdhr9u6cYRWIxVEfnSVKhVtrEJJIZVYp8CjhnTe3nzV8Kspw10biQnQfecgUHtlSeCC bMVUV8E6oPqhfmoWdtVxxZTgTfE29eCdsc4+Xpj0lpoaYl6u3No7iTqpLsO6hP2P9Ce94fpYHu6f ruHnQ9/WwOQnHwUvythPnQ7/VVAxIfGD9X4/HJ18fTnHUovin0uyHRzMAQO4ohI6cgqCeSjoIe9e k7deTHwv6oQgA82OnuVETI4KvaUwKIMSM7axcF3u1xwEA4iBTrWBg4IQnzi2l39CIZMWgn8E+Sn0 v8kFb34aqjJ+jdTt5WuuavrXBFWAVyxRzrAkEkpzgLudD8fD68sY4fLa2Hf4MmaAgnuMdfWyUioQ VHp6NLsjJ3weX8h6S7N1fGKC+CK5FQpmgC4j1YwGH9ruLhLFen4KLGfw7etXAZkKxmisYw2dw4De iRRH8mi4AdhoUHreVGA5DkxIqbSit5ssPXNnZWZqKzx35PUcuHRmuIem7Tl8pQ9RwYcgfiKqT1Ds Gg7goUDDeIxYjq8mZtjBWGyCvR6qyIPVxpPTDt9lF5nDMjcvQFvhoOPC/Zv6DIBxRCZEcX9hzcit D1YKTWsnx5gQExFo9dlem0gL18wvQA4Ch9yq4jZbDqttHY5zl6hNnOwoER5VVRB6IZTt3U4iW7aD hOoieCq6oCdE8uxPzFh7CBvOSUhiAFoDyHJQVO2g3nRddW1Rp1eijVVFSpAG3pCsO2F3Jgc92DAd QMDyIGZGJAYiwXNgMCORg9UsRkYYKavZ0Xy9L/NyjrO0jUj8BZanf1n4v9VaUS7d81yRJBje6rFW RAh17KJTxiIpAfUd/iqI87i3ljZ7/ff4tb/DhjkRugmpIscntV6DN+oYaSZ3FpsWg5WGg0HB+giK UI3VdUkhAE7j0LtYPxZOj/uzOMS46aejPxSB+XSii3Lkj9cz0WH1PTlWe5pKMvqd296Lu4Isnja5 wG8h51KKRiQgwO0oMJrEeSvNNLbW8Obh/H4TB9Q7fViMUw/W4f8AR4kQkrybWX0mxG3EDNgmxlG8 wIG8oBuQEdW49DCirpsyZAntPM+RuSQSiYjcZVFmkYeCEJ0iLmeqiyqAt3sm4wdBWA3kF2r1clwL Hb44l43YZUu9QumtpfG44ejDBcaqVbbbC7u+4UCB1gKA8ZncWCB5HKrgwxmybCmJ16fQDLqfzmom 89zUe9Xxwx98FfHKmmjfw8CjsOPH30XvfVsoA8rIKNCku9XiBQ4MgeQNTf38W4km/Eqw2CTO2XAY QJtDNhoXzjugr9d0agSAq3g7fxKOCChCDW46GcdwmyeoNFn+BCJUDAi3YnVcQQ4QGwjdYOF56iOD 5SoydQqBMdCR28FVZBgQ3LajIiSpCznI5SMQ87AauUAO6IYgEMqV67UHfmOfnrDXhTuyvqpPp62H q52tQX4XtfWh4ET40PjYmp20SKZQqaovLC0tfpsrxhqYGmnDO/ZHWTRRrxxy73p49DmCkcGwaTKm 3uWBQDcQFIS5lUKcsB5aobzOOqZ7aU/PAynNpQ4il/j32NUnbQHiUFSu7nPsuOmKAYfL8kYAaFE2 3Uc/C2JHbvhbBM9HBjcp4AgKMxsyZ0BCe9uy1c5zBmBGE3c+31SA68fGCbQ+3u/fR1VDMfZyQYYa tHPaesjlVToUu6TVIgvH0wgPG45xxpYpHxkAyYlaC7C6vPk4ZdMW9UN4xGrLMuBgOm+vt5rV8Kjz 2Ye0beo6aPJEwFcy7gy93brECN+wMOgAv9pWJRIFiI+a+p+9ue0hZNhx6el6u20lDo7GDih74OrX K8nlptFPD277Cbond3E4Va05Zxj54SCoi7+NKzdFNPrZ60km3MvafJ27bff7+IGjTdZtr1SJ1GRV IkmzoP0GBS8dOpRoR7yLFMSiDaUsXGxHs8FQDYUAgCBmRgRmQninTWxGonIaqNZqU2zzn44QdcS5 fXDZMZYjdUAXIQcVVE8ygAkU9kU84dRqQ8UonbHSU8u2P3737zztrA605JM1SpFPiTSHV8k9XZTt e5PVkvJdduZeDIfIzSfAw3EnRIaYp5eFMZ8LO56OvV7Mzw0Gk8fUc9oQy6a6YcNNVq5wq5EeEA0i mIohrh6PmoVLwTjOPn2W3wS8cdVHVNIeKCabintnl6aXXD44nugnJureQWxeXsJRzFGcE0IYjtgy 5i3JA7o5Q7p3abdV84h2QatQdYKBtjpWW47+LDb2tJnYaKKAHtAnBhxXVgnFspuycbvxcdnFgNcZ FYlnObOb7d2EL1OEFCS+fz2OF81GJA6sL5vTgHCcLaeCGIByfM/uIb/zXo9U+jjXy0HyHyCjig+H t3dd6b0GGRcUpooDemyM+PoMFVyuDBxEjX+RX44KI2+d/bxJgnmITCQQj5piBN96Y3/R/squdY/B fJCUq1CYtE3G5Jlyoc9ViHMH2xE/sc/Wnd2uXrxxR8L5wH225CMIHRq36SSTD0GCswf28Wt5KhQh SgIKQvhB025tDX3TYOFehQ/vvRh+dnDr4sMnYmdLEcdrZw/nTLdj8Le2/giRcQpFaKHlTqUwIjXr aZAB3jrBg7J6hj7vSzJl4cu270AmR0VgreztVAO3n2pmO58y+zmU9W4bvDg8wXb1KiVIQBxFihUk ilnDp+9Qu1QLkZ7I13ShQWXC6kFMd1Q7R4HvICkSn5MB1EJkaFPedT68V95RAVA+HOgbddDaf0hB VT8jdu9XMzsoktV50dhmibd4FZnyAZ/jBJn7n/Q+Wi6+Htnr6s5ljlgR8yIcF2liOLBSAXOWR5+p RAkEkcDrEKN0exy/KvsjkQlTFVwKDqLEjuUXI6eL2FF3/GjbYJYg+xQPcThAX3vYpmcy8FJkE5BV dgqauUeua1cpZmsRWqwKUICOKMSfLHdJw6M68HXKcSE2F5DkNsZtq9XvuT2vPTt6ENdG0bETXFPx fVfx78ZQX3U5JNPpTr6ODNvD59tG0fWl3vVCq3i6Pl+K4qPxtDHxZuRWO3swxsWe++Tx8an8Txg3 x8NX0PmEGIDs1GjoPe9HfTrfh+UBYjmUx+Z0wtB26UjNQN+Co4kyIooVQVI9vaqPqodTKLn6M2EF TNVwmqXH2qHxQHK839sMfVx96XP1kEiD0SvxB4Ii3kivMje/tZH717iLlfNQKMMG4+bKg/BzCTlY kZmpmRJV/DlRHug9zAdQktcBglktrflhvVMyRCU7BRApI1IA2FEDvR8WRpXDEpv88qtyOYJ4F/V1 rmuPfLbKG/KfoIeTlVRlAUHEzQRTwGgXMh21cSB9+K/MRRMLD3QgHEDgVC7gvR9KdIGOgUVIEyKg FXkDoPn8fr8uZXZ08xzYiUa7TZcHqmhPFAO9H5vR5LB7SIrYWwUOSr3hglDUq5R5nj1KHFLXUDga kZ9Mh0+1/V6lBXt5ORrTs7AjPzUacJjx+L+sPVOZuUFS6chw4vQ2B2bsctSY8zvsqIkSEST1nFYX 8V4uGSxDABSOmtqbNzrxxKQcq0TAMMz5rD63d5gUgPeIIGAkMjn6Z49UVw9bJ1BVTjMKnbBeh9mT iXkARpum9zweCgZnib1T6WXeFI+GLhoMTJ6es+H5dG3qE2/PiL7CL/eoyI0mSVYr9FA5l5RD4LTl FS1oALM6OULxjpud2wfJdCBr1KAORTDkqYYrtIUgdeX5NiXkAKofCG8HXDUv0fPolsVpSkRwv1qB SPcVLk+j90wt2i1aP6JhYHaZkZwxkI7H+qk0HmqpAp48Nog/lBB0caaOPlNDpwd8h5Ia8V0RA5IF 4VAzmzfQuUC3UJ3kSKYFASkSiChSh6+Hpa0LMKctteVoCLs3Z3gKKVKEmsnMg6yiBxHCDZcr8JyR OZCEgH8wbfWhLBwg+PKGNsn0d6eXthrJmoYNBWztLHo4+DJTbZpSeKdTvJ8XhT57mxx3iw3V6uyW IWAT0nc+jfL8u37HDka6rzzUDQ7ynIvjvEOIxTxABQPzsONAN/+QtAYmwgEoQ43g8sg9psEKR/hr 9vH8vbCKImJrw8MNjsOPXsn8VViAAnrRCgDK3RVA75FO1I0GQdFan++0EgJgjmPOO73ftugt//gr 6esOO3N+3aN/v+kNRzAIAIIxGEUkTlr4bHsg4L6bKD07r9RWZtPxPrIfN6vCb/jo9biHn5vDvCg4 O/9HZibDKSDVy3xf5U0pjbivmzvQ4eLY1cjlUfLksJdJXA6c1Laox5chyDIFKIAJRUYurQBPeaFN 8p4KKBJ0cgv7S+khgHRl6I+ne7lLaU8BrLmMO1g8PXo0+B3Jz86eE8sHRrN2zcAwEIl+R3dD49Fm Rnps+njhF2LCKRD3ceHQwTSWYmTNPyaNBrLH8SurPHdivqETcP2+44vGXWGTac1HLy07+yg+SP2e pUiR7/4ETx+yu6O4+b3/M+n7Qiqo6f5vED+xQEhT/Xhy94t6ANh8nHx5nfsf6SRSiYIkIr/9GKYy MFQ2DgOMgAYEdvc7cI/u3FP2YD/P/H/0vVw/rUZP+65aFpYl43uWLbtYa/pgbFTh7Jg9ULwGwXqp ZXERQ5uN/Td1ftiadt+Jx8Yk9ruCowt1/AhBjQB6MWJCj9CX83tBtxbhz8uzGtegmVoAi6yCJ+v/ A1bgvtUaKySSiUDJ/GvfudZZg9/A4lwjDGwEwMkoyJOQkGkzNtncDuxuLBgtUlGhgoEDXhvZ1YMi xifx/Ye799Sfm//JMgGhgB/eQSwk4EITBIbccYTQJAOEgs3pSgWyyoqgUQ/eyioMP7BPMimKIqxG LsSCLRRlVEtgaM04LBTLEXhfmYdi3l3IAP9snGVIhT5Y3kch2jOHPfY4l+uQMlCkKW3xOUK6MSKW iaAsRe3qsDNh2EkISGBQqKcAmOMWboTC5kYxFRzMYmISCLbArKJChoIhhSMRCWxtHLKVmCYkG0rK W2jSg20WQWKCrFNBMsoiLJEdQxCm0iIbEIMSDZsYmtgzUTacsgMywMgT+0qV3kXnKb25UOhk4hM1 /2XQuCBXbTXeqpoAUVFOGaV4gJC6LDi2G2A2GtXmIQ/tm1kOGcAtpbBEbd7WQmcjAxwYUze1rKIJ jLIXCF3YTENSpBFI4Bgj2DlOcsT0wmb7aKoSqBLQKKk4vGAY4wlRCjVkUMSYMAbRrbF4DMwBmrsI LITTNJiBUUbZWq3QaVtrcNIiFJLCcxIpaEiaqmqSZWkSImIqSiJghwTJUYpwKaAQVBEM1Uy6Nw/8 UCgMmbYFNmi6IDB2uNpInbMYil3sSJYqkicDwa5r8CmzwenQPH98WNJGmkasCs1Imp6R2km0nMZ5 vF3YdM7OIYwOJwb9aTask/5sxnMYQNCQ3Ek4QNkTsnGENiUTeeUo6DRs1pbbE1CGQ5WsTUInOV0E pQMSjrMViHTji0pqQ1KajM20m9ubmI/tgODKhwzEeEoKZArlQCzGpHeTITbgY472IjyslXRJtrAm ypqYapTdWBtownOjjS4YykSowGwKQyoV46wFEnAgxRHjcYoAxUIbRJsMomxvSsplMt3hvRsZUrLZ Y1LTabWSQNMhw+LIeWXSG3NkUFAUY0Ic2c1OdCbbWoSsDcyhpgbbWGJAWEWKZGFQlEo41KQPCE/h lTbbA3JHP5dGg0TxhGgwiCDmQc4SOe2jhK5PBSCCdMmxIDojkTw4YAcIDIGIW3bB3hwYBJAiAXJe JlAHEEAzm88fL6/d9/kgFectY5tvJu/O6a1P3aPgUYbX9TcQiNLCBLYobDBJRVNVFNTAgIoVDSwN bBHSBH3QO8gv4HMd6Y3ZWkA2Q5CH7MiMNuMqBNJbGAEWQ2IN/5ta4Qb+d8Y8Ow17eetb/ix/+G5r 1HA1U/nKtKeb3v1AHf6qYG3t153fAB8uD3ybEPo/ff8vq6vF5fGvOd85HHOVONWlpCd/BDrh2dG3 5lE+jXRjUmuGhq6cktcQPbnbw83YZmYBUB5gQfoIIAJ07TjJ2/eS1t3ev54nRy9/Ygr19d/er/Qo YAeQ1rSZRjiWDODUscDJBOyKPDhyTfXqv+rHjy6eXLUG4jsJUTZwolAPsHwChPxQ4gwIH8Oe/ic/ SMstgmKBA2zpLYVkOZBIWLjsH1DrqAJvgGYuCs7+7j2fUuHl7RjT/V0DAZ/5vwPFwSw6/8J9DbvW lMERy5j0/J23b+1/v5+GQvYWU/2dzm9fdNkxlCLmMtAIQB4cmC91uXHPlzlvRjzHD2+euXd06hsn f27v8B+ITv82dMMQGkB/t3tz7LHpHiGSmphpCKkCpOWGcsOGsecw1UE00VNLRnIRtFqJA35cuPy7 8AWCt8wHQaUL0TGvnIM4YuNW6eFcMZRSYwmEg8bTh67MKqCIW0FReHtACT2RNY/rNaO2no5cj74G 34rVd4wJuCEiAoiIgZMEMIZbBYCIICowGUEqBt7jDBFRFvLwfBNjZk/mZKJDmjyAyMQFRCO9K0nG 30QM1bQPZmECBy6w5ZkSVMVA4wKiASALutnl6vHjIB/56NfHu7dLpvToAOyKgeTUlHaaihVFA/0g II80AVA4QFALkT6z6g/huO+HRX8Ip/CAX8G8yO/psL8h1O7i58pFfjIO8et7OC7Hxkrg8BicPZdw fEzwkOjFwi4/3YOdKcYp7YYRMs2/J4njhaptKrOEjSTTdGDmF+jmoLcf9CNbq5x+wc9qNY61H66H xEVPt28ViPDNBxdmz33/eFpcUwk/K5vh/wXwy9eGFco8oq5Rlhy/pAIEhFTjKIiZRH159+1uS1uX lwGOakPj56fdVFR+KGqFHlWxHZ/iCBQsgf+GX6NgfnVcEBAvyv+0T/U9nyh/wA/afClj/bmmQXLW uxYUEQf61VFiwImhgQYqNmKBgOpQClo5A5RCLDuRBA7//D/X9n4WgTLONEPzN7iB/1uClk55Rcop 07bdZ2RHWm0j/GSBIKL9n6yzh5tLQySCdJazVKRW9+ATu1/n9P6zbkP/65t+z0cngr2lVNVV0AOw qORzL853mMAx+lQN1E89+KchyUKWIKWhIa/1YvA34YsYv2fxD3icEHDEjSbVu7t1gT/aH+BACLr7 8BiULZAcmgKCKQi/8eVQbGWtPznDb+wu7XkqAhCAIkqP9oQMuO72dzwRBeoA2F7N5Dz07lsxcril bWW2dMrjaJRRWVttiKtVqtgBwEkO6eAHPuk8t4fFrpBziAd74ISxRVGRxU3AG5E17kII/1QEDkjk lfabTk5a5rFRDmvxa5LRLHKNwdTQKQOoiYMpI8XWGSiZmQKZIpR5eLoYoFmEAJhVK0J9UPWECxgD tCqGDBrQ6daiXsdJMdOW/oFCO/h/lPNbcnv4UFDRDAReMROzs8X+qjxgv9xwEKQCJCCAbiAaN9yN jdWDz2zUdqI7HTkOSTeV/02IWUWawwCHHZQ1olg/eFa3uZAN5v7zFE4jyufcUlNzTrvVZUZNDWrX sATWeFsbgYJyXxonBkTcNFH+6Nw2eZ5zwE8hzV3WkIhkQg7BYeD45g/t8xy4nKQaiCgUDq6ihKK8 EygikovSAKvihBHBIIAH5Ow8n9QH5/jsS6P8+eh+KJzIRHOGYTz/p8Nm9x2OyT8QQ/axAD9Tn6TQ P77f02D3LNQb1kCE2gbn/gB/v0/d/AmH4tq0nP8fiq4llE9fpo+wZ7v0Z+JRMgDIyCRALQvRWh1B aggg7troYEhjDByPtNyaSfj+WkVsW1EYIys5E4qbRzGBbTl/nJrWoqrSvSBEwm93xmaBwybrKH9w WDE2vCQwLNwU/x+sAT8QoBChAgSErIBIEKj+CADIX8smIcpSgaFAoHJGHsBZ2CPrhA5d0HdcCnX7 5uJYPripwUH61QwHLE44V7LfcAB9EKLCEfKQ5oZw8U3fE0bgw0vvRQshc+Xo+XebQ/y2wh3BIJ5p +z+/7vnRNrcKVo1BULbINvCUOXdf+d+g81UUE7odA2Dh1+uUkFXdfJGF0duIQp6S+BEPaJ0qma99 TZPXw6iH9JEwUSQUQRBxTNwzpI+7p1hvw9A4CZzIwUf1+vGhoSnwFCPRj7DwofJwOZCyvAFTiAgw ICQlAIzCiH5xQOTzvgi+1jD/CTyDf5gyoObzUHbANR9hIfEWDPUrjEiUlXwQuH1BQht2RnTISHGW iHLc1+4iKqqfvHbxxrWPnFYme2Ol+Ser+YceOv04dpzNwxsyvcV/NhjJgisGX7FqjS1oOHVoqIYL Fd9GGmOlL8N2DznysOmHfoPJk/h5/n+e5q0QZVV6CjsHuyMpn5C2dvEWOU9PzBoRIcpAhnf9vivc wXXOwwrYjuRA26iiSxzEPAfz7NW0wUWfUKaRlxIfgUwD6wDAQZ9h+xZwzAOLUAY5ozt2UNECBFku b/1RaFeXmxuDQOIFsHBsGcvg24LoalB+u+h1NkHCS4h87a2ReeOP2aMCa7P+o/X7mt2mz3/W1/2r wyOy0Rs26UUQkHnz5LDP5WAaTDSCQheh7T4fs9islc+MwxjMKR0ajrbJCXt+mbWOBRcgwOT8EPcA egez7eYdkcAjxhCdTBgfZfYh1J83cDcco+lDdqIQCeLYMOTjTRBOBwuJmcY5gZ8o7g5cB/yIHIU8 0eEG6rQKalEo7CZ3dhYKpoDm3EuSiiyY5M7uwvHeLr/t/QSJzGxDYHMDaKm07YJSbceD+f325IMg 71M3ofetBqA/WIESDgyesO9QBzgQR1hCQXgJE0ONFQvFTc9PTiRHUdPaNTTzU7UR5gTDgy8B9pYp oMA85Sd0ZA/ROlCB1KngFtBbDRrzpCKJ0BZ4eDs8WQQ1nPYklL3rlBcIFS8Ln0ACcRHpj+Xy0NiB 9JFKgeeqqPwB3pP+JGIKCvgDA2Hh8Wfbn+xdGPZiRPmSpJE3/7MnV5zhZ8yfkgEKAwRH/oXQDIvs Mb/C0p3/rp5zY0E0KD/lDvQkf/Wdhcleuce2s5VEqVQfcLHqB7j9vMb6lwiPYBymaFk4PLaiK8hE /KZd2xzDQsYfnhb8++w7bjoTdJdomAcxoHXBBfL1HWgWOby+polHq9dowt7rFsUmPx/Ni+jCSiY0 /s+DvzEPaXM3KXExjbn+LCJYSQf4MJxFanB1yMPe8uKnF9o+ctrV1HFNtAf+hAJMx3s4Wxmzl/XT 2iLe7iBohLPLLlwX+7rjEjZdTJnVOJVMQphS6WZcBjLbrtt7kit4pHM6ReftbQQ91VGCc2YNnRbI RQ2CDZ+sNKQDRNeQtgLXkaikBmTtMgEDGCBEyETWBohtsuaugZ5pRCyCNCjp+YU/lAwaK4Bs8n7O J2HCpIiqOK0ocjvTOqF5HhCdYyP4hNnvmCQ8osJsuusKUmNgktFTlHk5SDgXJLQ1cgYEuGFMbY6O i6E1AupzUtaUhcbCmxsuLhiJZtgv8yImh3md8p9dpPdu+zeLdgPlPKVY84pDzR9UQtCxkWAbCaF1 oP1os670XYHnmrjLkJqU5TQLTSe/KZIbXm8vnz2/2Ty6+q2C5SBS4xLDFn4IxLn3daqkJskH2jTJ 9dfpJZgsR/kuadmDDBksqZITRfUVTiUYzxefyp5akT1Wu8XOe6f8TDo5tpG1dpqtRYzGVJQWQM0N lcpu2sH97s+4/v5wXbObx4kPHbhp+hD9fRTRKnaPUyLNMtLS8dszMx0Q9NAw8LjdWjFtaqf04nBL Ogh+RQmNqMR7R88ZXOjLepAg/Vooh+T6d++s0E/7bm/tfBLcIImnnj29ZrDnDx229EymWReFAgKe XcC7yB1OMl2vGwgzBCIZ/9n83H+PxSOO3ScHRC6Hs7dC1DfVjRVt2s37aXWZoU6DYuAvMVdN8wyG boR6nOyJGZYyvjXhL6YcqxAcDA/zFESGbqeLcOKcnYPLGQPXuBoZg5kb/ILNyMaDQbwf0sF+YXw5 O1+HhVTraGkueDmHHhq2n5ZlNCrU1fCjkg7B1LfdjZWjF1IUhxGD/LQWpFCKRMEMJwCQZHHtHgvD rqii6kNyOgZ5AcuELqNvAbx7vyzaP+NNcHW5HWcIB+DxYanl2PYenZ3P990OR6aLyJYFMOS0ZA1c RdQnm4boOU+WkbAZg7LFs1DUwBud438aKgRRhCDprNALiOTStsxDQ7iwvhPdkJYTiAgacw7IEg1Q QlPW0rem7QxImXGAHLYO6nfJLC272ZYW8E8rB4R1xs8poeGeTc4bnODhJhZUrk+vsHnOhRNEHIGr uYPQPKPADmGOTy0qveECJJXZ2gZ1BVVMdOtHxYYHa8S3LYLLc7ziyOjWeRcIpomAcb68PNzQOsB/ e667EZwiqPB6y4CYsG7492YYhw9h27DeSLbUG1BICQ4Hr6O3J2q2NEYiwx2k5DEhBogMbURQlJBU CVS0TCESN1d+nq2wqKmLs0gaR0EMERFEVLNFRETwAIBMkaVoNwO7qOpe96ATpEDvCDlkdYTrhULg wXBTEKT98DjdXiLvHkzOSbrBxU6IOoCjLBdZvRpG41lDA2ZivPCkBgyWyAhFm1Q1iLYBEUxi21Jk 2IZK5IvMYwa9Sg5Gv3jvA7D7TyVZ+uLXt+b87C7D+JR7wynynvIQJcuDYoKQrkt0BRT6g/fZ8xhv gTtYblsNTQJSBbMgVAkf9S5lgoMloMUAuqJRVHzdB0zN9U/lGoHmm6nZau/bz3C4QOIpe0lsX+Vf G4IT+zSpA/sIEyAydzmvEdwdZjRLYVE0CBKwiUODg+L/Lzqe4kZA6qZDUL7c75oF4LZ8QvKiMQ3K Bvd4dSen2D3J+P6aRdhHf7qrItYJnxWxoSsniFmwh63lpdo8go4PrhIomFC+moifEXNMCuUGJHgP dp1hhklDbDxUjhEQaWXR0X07uyJNHZ3mIQtKmS0UUUYSFDTqyrEQ/SyN9dKhiJYMwQ/PYSyfQF9A bX1vOcRHITYf3tny6QEI5PJnUhVJRXkV5h7dfaGoLjy9nxKJ4No+mPkA/JERJeSFk/FlGzElrF2f 259tzCYUhcjcspSmSB10JJQmG9CNPw5wkw9sTNT9/6LhcLhBZNqOOh8nMKHUcIUjtFIyQNwicDG8 PeBg3cdQ3+XPLSH+GaUIpvUDYZwlClkEdW0ybgF9qeelHaEgCkH8pl5CoAyZB8peEsF3RR9vhfH2 +gt5J5zBg71eUx4lWzV6qCar8n39s85B/lS1sPQjbKmD9ugskF58nn1ZJ3KisflPByRuqUmHieQX bwg/7J/g/sRoNSkRzjOrMbx4ir2K/nALdoGCYDuWEvH+PT45ClN0CN4wL70gn858fDNB/QrdgeEA lywJL5TBgJiGCNAh0MKREgeQbnebbih+tnpZ+RDWhLA0mk1q6dMxNFlJpJFnLmkPxxrIoHICG5/c nt4sER8qBedqb9h8XMgMPVAcsD8aWeCDxPkD52SDCBDUAW1uQe9YAcTYqrk2A8gBrRvQFNKJjplM gilJQKcGHuYHzMIGSSjQpTAvjhKRDEhDqZ3AQlf8gsmyB/d/Mh/1yB/cB/k/9zD3ICif+dEpQKVa OThlgbhGxKnDngMJ40wROw8JTylIZYA1hIFdz4/SC7sCbQgSQCW5g6SCQh1LH5wBxEgOKfHAgH9c cpQ/rh/o+UcO1/q0aNQn4ahv6yGj54VYIWsh9hMT7sDTYPzS1tvOHzRPQ9xQWsof2fAFlteigfng afoYfkY9Y/YCpeChoMhmvzUgO6N8xocI/zqDe2fAq6hdDnREs5BYWkDK2wgJbmgqXIqahiH0jf4x f6IpmGerNA8Kq2zDtoD+P41+HJJ2mB3I9h/VMS2wOE0lMBkfkMxoL19QmvzdfKhe/N1PVJF9J0sb fTcykh6wvQjIgjaIBeAqWuH9H7Qd4ZGodRm6H/NESa9sJ9ngO/qPubCg8kAEPdtPRbQ2ahcoom4K YG3fD7UD8NEEC3melYJs/xQ/cH9m3dqHUH2Kr+pKdRU1uUWIh0gW982BCalQOiaPKB4b5/Dr8TF2 YLEtCYh4By8hAHfKFI4SZBEUuT+M4gQKY8GCL3wu6FqE3PHt63PpiXZDwWbcdRPLcKvtQXgQXNX/ Tyvj5e5vpWOOu9W2qXGxg8yiulspSsqjZRuesn98+T/Kp8R2nzeecjziAvlF8Uh5dh4hxIIgiCII gjs9vpi8fLZvEsRAoiPZrsFEI2dqbUdgvUZvW5ruJIsB/g7HA7+03Z6XUBpdGnORHsKICYSAzz8t +jmtIeQDPa8AptY/tRMlyAT748E7PJTQFFESlK0OQ4SFF8+86FQzGKxaF6kWu4ApLj0XPFtA5tp4 PD3Dtwd/Nmqn/Snu3g9QK9yDBoiG5bmzq74GCyVXda8bqEkkgHKoWzJxBTHU606tEToUSCvcc+4R JC0R7yxUZTQlJmAXy1ah6SwUbxkaBxC+MG+6HkRSNAC9FqUHjxA9UCA/GEg7fWHpBU/0VGdPWKh/ IIh8h8hGIfFYXwT996AHOvkGQj/dm2mCH9RbEcN11BcNAXJKP7y1No0ZlBV40y9Jov0nJtuVc2xf 4RfzfmqcFqLYP1ttLCJy7oIMgh+kgwmHti/6RLE4MBkp1JEBeMZP4rw8rP6T/YiJ5URNWzX8j0Gs +VCxW/FFoT4dL++0sQjGDo2SK70hqAHn4BfGB8x72fLPlPdc92FLubd59btwFodzibbFJD7OixVi iCc8Hmog6mjQVzmpnUyV6JMkhOlPOTSE2Zt2GqIazi8jBOMA+ZolNecwOwK2QkmHXgzQHG6KIiIp uSbzDad8+joGc1Z/S3PjU19AHWhw1tFFdD75Kab6l7Q/Q4rSxAAYJGOgwU+VRF6nK91h+Qjwn3ed /KWGIf16b/mcDTDdmHna8J376UmfMcfH/T7Uo37dodWbvqDmcxF6dn+fJpDhrNkhWlu1M7dYJ/eh wEDkSZ/P/9+WSThFAVQrqFvUgdeAwDSSyCw3sKkFCTEWt0UGQ0UQQVTRS0LQvCRc0mQMAZAlAUnV YUwHdIZC8oHlDqaVppoAtYlfi2xQ2JUppFMgd4jirhw3MBZqKHSSUY4WLAN0xm9bu8a57akmyKAK glAFJEtA1E0KlJEphKBklLzqTeRclFMJQAo3gyQqmgDeAiDKIUTlAjSCXbiqjxkTtww6kIloZETh AmpBKRMnROYZxl0Dy6Zo3WSUN93BTUEQ8zpoUeUvGEpQ3YEyQzclFKxHicsTeR4VU0lFES8YpHUq ZVyuECHGCWEQ3gMFB5SCmIuoeN3wGtnEYFgQwg2IQSjeAR6SpqB2gNwgADjCYAztBtLy3OHTc0W7 wZaRSKYKGlSgO95kX54TUavw2H4bEHthoU2g4i0T9dhRdklEGa3cUJsh8ptYLIukl/TbFGDq10tn RN6a3cdO1m+GL9wVeQwNgTxTqgoRcGSYlN6RjgGRRgUUyxIyVUEYYOOkxN/+FLMpiF2lkURFGCEW IJAVZPzdmsJbQDx8IXoRd6U4g0oolExJkNYjSFBRkeDueMLDjorGtH5shgjrVyV8nNSzHjCbM66v K1VIVxAVNhsk2YaTVvcJji5ZRJsM3cSVJf1DQRFkU5s4SThm9lB6/EZ6/tE+5O5RfPY9SYUT4Lue oMgz2QSgDJ48iH+ej/8RNU/R8DIyOegWgQYNg0D1/DxLL9qiAi3EBXWaaVaKv0yeyEDg4tNsWmRQ cC5C6clGI0ElstQVQFhbYVl6SHMPENbEfvNx6MsUJgBo6Bnuf8vIZls0cnaCR0NAMfp7aty1AiHf 9WwT2MSAbJG1JHXJD2eD/d8296023rWtay3M5eQ6dkkP9kWwKlww4Mdp1oZKZP69kqFRPUXmw138 3gbIdDoUpTBUZRlGsYNjcuXLoUWLHFDc6gUgAHAIJrEy5zYSYogW45BqsqihbLWAVINkuSA0hSxK FFRIkUQFZA4QVQTJLALQgHjTAXETT2nX6IEpGgpPzKh1KO34og2grHhy0M/1fBqXHWLDYhuDUroc d5yPgqIyNxkcKaGWAO8fe7jIO/DCQ4hgqdnbJdryz0PzPee9Ca8GWbS4jhIJ0UAtRUCENQ0vGaay T7kHvwJuBqRCnIJURavPHHIst9fTguTVDCgGkEqKVCgYG++P3uGIaIooIiiiI7IUwjlL8mjAKKoE pHaP/qF6SJvK73ayJ1kfHPPcwCZKOEAZZcGB1Kag4TS8I7d8Beqy5x2msqhpAnMeU9J/pjnUpygf +2ctFLqE/+4+qf/yHhKp4rHia19RPUXXbnZtraU3lGgKe+chQoaRKRCuUDkKXHIvI5ZmNWVZhNlV BMvu9iJ5A9a/eUnVx9xpD+CAGL61iDogqV25G5tVI2hFKjSsigBtiB+X9BxjRi1V+f8bU1/YH6w/ YGf9f9TCHMgyAPKgGiVSH9ZceeKIkHNha95nKPt1m/A6SSQkMIHCE7Ar1+C1h3xakSDsrrVghEup QOZP9Z7PBoxVwOs2OUcv++H+zzHBL+lMXlu+OV96ToRsQNIzJSAUtIlfx5+9+nbvFg4XGR/n94H4 JoHyw8ZUSff96M+fVbB9EalsftG6N8s5i1pVasfXf++ZZNEqpWeTu1mPXGjkBm5GxkD/EAgwkZAZ Jmr/BSlqWv8RJT9kOgqqq/92AO1tTSow/CAMIh57h/j5OSwffPxqg/94fR9p0AnyBqWAmklAy/4Y sdfgUOohj9vZbaK1aJ2oodoXfpRpXwsUTrIlTHRkUVRlXF9pij7Yxg3HkMzuomt3Y4yBcg91hSaA OFMhBPebOPRfe83JHlAbvhAnNlSAJoDsFzA+F/a4r74EcJe36+8qrLAiIhkJCgOir3doShKQy83B hN3wrr5/DnW6hLQtBWtjmY6Sv3sKwdVtKWUXPddvEIhTxiiqi7NJEBmNzHfdNg0/lFE4nSfqhP1l /uY+v8VR7vfaqC8PBNAqFoNkqof1BrV/TYPW5/cpsFu6iCH3fYwpLjP7IGshs+ArLmP3eCEJb0Xt cu1VUy173zMZfX/f0Si248H5rdQnK1z8Wc1qqm/FP3z0HWnZ2MUbrEe5iSHAg9napc6JDFxBs9As XvaFWUP8Sna3YMqKx/YXOriHpKIlBkh68p0kyUJjNcUEqHzZzWLJeBQwCupgVf9Agw2bifGTeREf O6x6uN87WJNgtrEQX4A/m+eLDtp4HNFKyd2Q8UNxSGmUNhz9p5nXcZCZbcDcEQoA3O9W8BuAFXb3 GEaqF1k60BOM6dAyCODjK2tKiBIqsdgbSgsKyFoz77xz0TnOEN6iaXDdA0QsOwaBTISKFoK7ACNj CDAZKc+X2fx5hWekqXphYYI4wVJkA5YNMDcMa9lzyhBLMY7BIzPR02yQoVIlTOxHocm2eXp1o4Hd iP1JrJRImwTAmwX8ceq+wQ3rMyzbH6YGXUUMi0IGaXRLkxtR8mODsIQkrIBjZaZoksEGFpVhRIjD QWmqF1KQwlmJCL/iRVpAaBpjQD0qjVEVIRiOQibCQwE4s53IAGmeJzn5oQqkWlCqYukg5ABhINCX 7AbP5JPVKL/+ShwkJlU4MPi8aHm+g0tU7aXIij8ji5URQLp/LEGDX90hbi84kDlEDJ5wU5yUl2w/ 4RMCHfaaIEJfpAovgKW5sPcHpnwSfsS3yfA1gMSS2yVm4T3/cSlEGHtLPzs909w1upkwSoOIaJqP fcuqtjnAqIsGA7T3lf7YJIX7vp+DnVny3r3QkoguAjj3LoDbEuFUZ/cVZl6wn1gKanvZWBSB9qn6 YvoAkECJJhhRjkoYg6RhZ7eiI/YOfCMCBUC0dZtN7ApfkQMgPusho+TIjTF2kANeylN1pCIZ3oEa ExLiLYR4CCb5ScEcK2lqRwByov+V6UA1Ggd9obUCRBQR+wQdUB1XbZ38GSRRKkDGMkwBOrh3SU6f UR3HKpkQMHQDE3Jh70D/A+5MtJWNpKgsimJWFJQ0tD9+MhpKShpKSgoCgpKSmgLbEyGkKWgp74yW k1ZDQUlDRz8aKYvd4u/bXb26pja8fUcu9VWnfJDQztOvMAgzodd/Akodr8O6PwbrGEYarQxCGxB4 rulQGUEOZ0F501PDaB2qGHQA9/AO4XiImqdNv08MRdppQTsE3cOJKCb4FRaSngKl+UTnwUIp0InK weUEIIXQCK3EC5WINxLg3Gk+xUD+0Cyg9sOb4IHpgf0Hwe12Wpnz8BjlJSemUCJFfpkrUfyZR8Aw PVF08YBSYn06jjsJJfweQ6JA+OvAdZcouxksmGDB4feZre4X+gpeAcIMjTMsstFirG1qKNs2yuUS jS0qQI4+cfhToXBnZRPWJg+qbThWVYkpTCQKdBHvXITUAmbgGvJZNZYQ0A1xTCI+DNbs7x+qnphF /tgpT5h8h95zN4MiIASYnKm5FOAmRtyPXpRXEHZq1ialoNehCENUX6kb0+lRIJiT4qCtv9H1viL2 ggiIII/bVUUVMy0l8IG+Y3b5gdxv20AI3rwdy+rCa42jDkryz9bDBAfAv257kQN47094TzCcjSbq Hb04h4k5tKJ1e/RNVEVEfFsH5wQ5qPFIBiWYBYZFIIKGEgCVIEIZYZRhiMHzicdfkSe34fb9MKq1 vpr5yVCHEfKEL5fCfjsT82jQ9ZD+q6kg7N1M+SMDaWZBJBkqB+FnBpB6LTDbV4yyQ97JqJ2bT8m9 OGvM9z/Z8pTIOvfET3QnoD7rXxSFQBxPYQ5YoAeUQnmUXrryX97A8Ut7KuRPHED4pbwmtbcbBdSn 7jrkbopt8eZSTbKJFZJrk2Q41alV2Pg3kDXU9pFFkPNfmQxVg4K4Pmt0mArPyOzD5U69r2Wnaz5t Xs81siGx0mMaF5lIeTmc+fnV6I9qIjp2i6dIw2VTGbSVg3JqA215fn4YRymP4/o5A48x21wBTfrF DeNVF+QouG0DAm01QfZ3KVAKegQ6KJz28DSDCa4BvpEnRYS5NB8BeI8hDc6gOges1qb34kNNsTJQ iKe5H7xdvUasA8ma4LI8w8GizFu4d4WExzfIJ+a0a1RQG0KnLZ9NCbk7w6w6EAuIZQBIxX5+qhUu qI79ca+lNlwE5o8ACC42luirXh8s/pLUb4RdzPxgkMKVhyMksgQXjxcOoXnHfsO9cRcnkWNcUsmo qQgMXWhGuCheCTgAcroF0O5O4uKidF5j0AGQDiEgIZoyyzE5pAExYi3INo8g8wCKtIRGAZRLke5F QTIHKbdGSVmjhrMgacSlGggGh6PAKmHKQLXBQypqAxphgNBBk0ASBiFtpdmDZAxFCHaDAgRKQMiJ YIDa9QMcffoInr2p3EEDdPW0n+NV8/65P25GnZT9pHNEn6A+qgPnNhRQFmkPqIifE8tWMfVYkT/l 9FZSYp7u3113f4dl5bx2rE8Mqp39DRHXq8LzdxrhWyzJ7vg8+innwvHdfNRJ3+MHvVsgIXH5ImmE ZmLYwsshIQ92FRUgkVIqWYWm4foEZAPpj9FJLNie+uImbqhDI2L1D0EUD0UBEg5Dx1Hzw9tmyYw2 o9tJ296HWkDf6LGHFo8fr2Nyv4ZZphRRQkIX1AS5REGrlUSAtR4TEJV/B8/nBN+S4lZ+tIRJ7uCd OCZJkzfZ6nOj6H+cTMek9vB6IkMzB5Ue6BOJsRVWgiC4vTfgf1T04bPD0LbPWkzMk6vwmqYtsQnJ Zb2qphSkMpuX9swuOMHENfxTvygkKrDTt+wkHt8FOWh7XwNwiGi4lWD8XZ+nM/iafjvzJgzl1ZLb A+kjWGILWHbxrO0gjXhk7RspbpGVA3vzQJ2AWzNBMctzqANXMh0hgjkTsEOujZ4Njfvd+mzLUBTl GrwWW1DTTWoG8ItMHB7KVvUNphGh3leuAaMQzg8T83mo3IoSKNyKDW+e3kIhGSKJ0XkN7g1iRcjR wOqL78IahwZAwb9R4Q6Icy7UU+HVg78Qw5dZnNZpITmhVQmNhGYNolUXA9e3TlDMcwclYjxQocdE /O9+OxYexn7v2HUPXGHec/VoPV+SaOridCMSKmKaKCiYnrIWCB1EeROZB02IXsOzjSnWO9MgL5bI gcSMb9gJB9A53GUm2H7YdvzuS70BbIEzdrC2jn2B5N9FuQmoDXucg5lPB3hLr5R85IHnPId384/b I6/LPsV/a2EUQuDmXEum95AD9RYe5VzRByJ/QjF1fq+b9RcQ/Vq/IJ9eRMvNyeFcjgtl7yEE2DyR wQmILX5T+jmJQmWOLJGyFoCpItHTR1fjuB9GZoWQMjQiR1n+fZUavaxmYbmgIKnMorAQyUEi9atq T9fG1jEw2plWtaOFIJgHFXxjF/uHND5vd7bJX0UrUkBJBKhV/42LRCQMPVwt3leCH5RhO/gp9oAw yR0PxoqSf7vCiyYmqsuGRZ41wVwyHFiYT2dV/sT9UbL7IXxLWoL/pmamCamzsY/VtK6jMh4zh2OL RNtH9zPm/n+j0xeIeJ7mOEGf97FBU6mXWW+8x+se35/V3+/1eA+r0/P6xArMiLbQ9nBSSFMmDkDA j+HvE62atRhiGnbp6xLsV1ANgylmAayW8cjDG8LmDtV8SuTPGbbgdaVnPet7/6TR8BN3CaWX21Aq XPHeP95baExAM42FjmpSKTNTEuhNKMZxuGyHJvpmr5MboYZ0LL0FFYPbuWwsFj+mWZjLaa5kGbSE 3fUJJp8c4mn3s1ir/txmDNcTAZ0dVY6hRyTJWKjWn4zpm1Jq5XcZ2CWhZ5XHCuuH6ovL2JYe5jN1 bUwrdQOdrnjMMATZRPiPzxW0KZLStOPWHIVpPvYJ4IP1umAsd8CGwIUQYMB2qA3QkMAWU1AHLDdY MIZWegEhmgTE+60qCw8np8G14qq8Q2fwfgtPynVpu+ULxG21B0Te78ZRLgwB2K8ub9vCFEiEDVqw Fy8kDWDoX0jq+7GThA1BrUTgr0msckSkvYHUbVEicANG4FpoPTnANQ1QSIkTUDaLDYgrrvmhsRzf T5Pl9Xx31jk1ldwZS18nUawCKHe2Bc/+v6v9lgCoo/lH8Yh8Qb04h39qibFE7c2DES6c9c5RfGbF 6wcwOnr8IB2ilxF2pF+0NISBVDEHISdxhqbeqgKIbghXI98IianMDCHc6FO3Hf4JP6cwD+OpT/TP 6Cv8uCbkB+26cwhSmCIgJjtZXBwOzWhmWJLQdYQ6ANe5kmYltHI4ySKddVKfd68VP4ZB0FFEo9k4 RsQBNWaWXdFvPW8Xo6aESo0IndqRHlHk7pyjHGSUJjMXABdDtoaBDJzLA4W7FBwJ2I6LWlYbXy0L 4Q059RdM2QhRqMi5FNM2G+RAaUWLkqOUGCGKABTEl0jlhKDUUBkFm1u78lT8uAHMTl6WuiPVHUm0 7lQVSVQVS0/s460J/zZ/q+9n21+5n7p9/n+qrYHpPOFQo5wn8UFOQ/u/wD8BZgBP2rcStR8oZFb2 /REN2qqqiu3/r9lAAHrkcb5vuxQv3xPOEQBT+mAgK+hRPFAEYMBSL8KtPqiii5t01utzwkAixH+K AesIILIn3ZcTVcNwX18BNkQ+P2KJN0QuACmZ6gMhWZiRKuqJvwEdwLpqDHWgEDNghcdh7QRTUuel 69Hml8juJ7iHWgDMeWCeWh+upSGCv2hv3xIGuniNKFlZaQ/oQD0yELnDeEKOc2zfG4lWzQacohYB CCVBIQNSltm/I8yGwNwidUKVmetraHS0Dhh1l2NSSGMP0i8Dw+nmOEHe+LB3L1CUYQiFg7Ybqw9/ 4rNIT6EPlO3lKGFif/MPo71HxHF1lC8utDjFTL+DCFiwWco8vgw7qotKISSFF1f8/CGzZpiZnjLA dYnfU96EwhcQsXsKgZ3HgAQBdx8qVSEF8ciHeD6wU+kU+sJvgABPTAPqsJkVzmCNH3HhS40Pk/ou 3cPT8VfIpA8kNkxDpZO78ih6/BROu0sZQD+T0nM68GYwFO78SSERXQd9207uguNQv/bsdvvxEDwf dwBTcmDBThKdc32cj+3bQFVQbYcx/GMJ0EGuddaMsxpyZ+2dVMfyFuRoKcPTxOkdIPcT6pdg8g/1 MyOj0/fyvpC3RKIQLTmYVZrWW7QkmQTREw+IHPT/1znGWb6fo1s/oNWvvGKrk27da06iVh1ayCJg S0IsKzqgsaWGF6vcO6vWB7ZvwdzzyEJWJDeBagxBpFNaQ+hC7cA78dfeuhZwxApCnUmHm0wBkDFD QZFQsUKSz6kBJHflfb9dL7P5PZVoYYx5+UjDHY3J/rQWVjcBwpiZ5WNeWX900vpkInwi8dUSSIPl bCHYm/MzFH40vr3jv59hlcNMXD/KAc6H/BETadkFTX1bu/Zuf4f/CIlht2ePRQX2QEdYYAkGSuG4 EbHWEPGZTd/pIw9UcRbf59/1mug4s59La/6R/9f50D+dMmf9fiuC/VRf+cv7PiBQWEykqNZ9hoA4 CQuaEl2M4V842aDWCzUv21P8eMTr1qTTaLy7kksnhcn9R2HLEcjmBCRfDsZBhH2+7B9q0DxndHfz DYLiCPsTezM4/QhUFdUrEhwcbBfsdvCPZJAhIP7MOxe4bAmCUHxKIMv9rtIuq88viIYUEgyyUnEm v9gz1b4/+bAvShAm+PHB37blUOPapCZYw8LSIlyzDimNzhGHpEMbUq1KSOIaG2nVZuFVZ3iy904Z MWQMyEJMNB8hyDKfKFrC1I+o1KSemN5oz2sII8NnyUXl29MvoZCb1BBH7zvzDShSP0oXIniW2m6p Shk25Jmck1G8pOMW+4jEDkUzvLmVy/ExfwSkyYbUkPQd7//clftghIqSK9EP+doJUIqiiCIIihE2 a2z+7nnV+2/7ePp+qm6HIuGkFkUUANbmrO7/ZrHaGK1F6T/3MqdsPHjQkJAjmfljf/h/xs/xMsXw dOzvWvLUv4MvwzUoaSZ40Pu//egw33/ebHaRHIipXfm9eB/1W8GS6Q36RYVjSGcqqndWL0h9fw5E Fn1Tho/z+XkbnAEt0/zW973hSM1TgJUApBgeWeKZH3+0J/wse+r3f/KkX9IiHBdkXCH/5Hu9K/uV ResK/8hphL7H07lkL3WzfVDbsgcsWlQfNEoSNBtDmaoj/QRWUftb4IZyxIZwSF7PrHNT/O9Y8iuT x1v3Lyb8+e5w2nyZ6ZcnZpfghzf/qDtgB9sP2CEQyTnBIsCmLCJAYBwFi/RRccPP6KE7SKDqvSao BqUwI71DmHZRsgwb+XiFI/gVA9dKfneqDGwW4bmzvv8OwMKA90FY3WhHW7nzdeRcCwYDvHjg4OT/ dX/n4f9+UMyBbxiwgyhLHzKZEeP8fVa/qhHPe09KUcBApgNyiEF/LurKRE0pNOnN9TUWPEccmflw 03gbBQQHSHIkugh4cRGPb9NMfTst/ccfAsZuCuKlvCQ7nMMOqsuSsDwYDomqAZgpTgpVaJhorceG LDVVAV2Xp6no8aF9ZRc+joQsH9g40GHolOoK224PlKbP1/1vh55rYhZSUhtNO/5mQhuKSyC9b/bA KIbP53R2Ydh6EITo5hUoU8f7Pl8XHu+FfoGD7Ph+FK8g++YCckT96p4DyHL5/Dg8+yTPB3h/+R38 kdA2Bns2asD++sW5O9tf4/Gcz86+2BJ+1UZ2/Lz/zlSBsd7pmiWscaB/j+g52I1v9DPv7zgf3bU8 k8V2zEQRBKlKwMShCBAVIQRBFFIBAFRIyrj3Av9BWE21d2HJmcj+xCZXkQCUkDYoOgaIcVNYEYUT GK2u0A/im2N0bCIbE3nxGoHmQ1CjkiW1N0ArfoVEKS0Y2t/jzMgS2DjPKTwEDp/oO0n+ARBA2CYT 2BkHOq8vLO0NAO6g7Atq0imsvg3868w4EdMzhsnRYliiUdMclAyHN2XfYfg8ToOV6YMNb1uxVo/m BvC4x7OjnTNONrl7sY5JA+PweWFFhmeB1txiCuInjToEGR0YUmTxUdRq6dBqCZAa1aEwbHXKLQsk mJHa8VB7wEB2RywoeW/DuewXddA8ktxHQ1Dps3CF1tB2KwU0dWpg9PJJsUHuuNCauQd4IBZHRO8e g8hweIOYwQ4ZyrzD42H2ntDAHuXsCAJ8FeYB5WfEAwcNgBIF8hoO3uZj1b/Gvldny/03OTMXF1Cx rOsxk1ecarTNptdky9OAXyDuAQH1MDs3uyCCnOstLSE0uWQ4JntSEI6+I5dqnU4DRHU0DjYcvaXf +mDcGjrN/BA5OoDkQDodvI+IOAeQuOozv6wkkmqZP6AmsTb5OBHUBA55h4HsOCEIUtGMoHgvoBpR LcdT4WC8Cw1sYbISTDsQ4h4T49e8z4bDxBaGsRs9Fyni45WlZSuZMtD1AbAhEGE9EkY5O8cGtAtW lVCjMeXspC7tzM+MOVxZhRVc9jud4gavwd4u3Uh4Zoa6YSpW4DQUxjAue3xf6e5EISY1g3CSXs3q NtqYkoAY9fJ2oL4qqqg00zTVmDnJIFqEeTrHUuzuFXl75vk5jHlduQ8g94+8OG2JqiA6Ca9h58ow 1vI6niBQc3NqczlNQ6csuTdBCLUU3DdULue8kJkdbzNAbQrTdKalVU/vAInE1ajW6jBTya6Kn/Lc Uouad8NCk13KDlEpFgfB0J3SV2aE7mls5AO35v7C+kjCzJHD8QjSNZrTigOTSWJKZg45Kkuy7Hu7 V6kTcWEih0BuopSL3FrHI3VMjWUtsY9W9Wv7vYHNU05MgoZJlKVj3bwU1Thn0Lrk/bWvok8q7fRo J4A6y3KnBTyPpPIGjqNhwpiyzvTzhp2nx+cEFT7gATQNJSRdG53t83i9a4Ny3VjDQIcwPcdx3Adw OavlNmaPl6dZVEsOQDzHWmfaahNQHJmAuIuZbNOhuIDy5CmwiLgCEDYO6HETJPtrzbJgD0bPY60m Q3Y7GPTf4RKoBgPA5nTyQNbyBamOYbOZ6zQOYDC97sPcCfsIHd6vN+53ffnf3sQT0MJP6bUXKUWU +0zASDJGiCUez8we09Yn6zq8SKW8x5z3lHQAaj3j3JtA1Kv0r5XJQpDYp9gWNwaHmiIBMd8YnJCS TIMObmI2wszP+YdaJrcAbkUDzHPqgqPhJ4ghhIahAduodMccGkouAJuSKOv6DIKeY0dRgP6fxs3C EJXjoiKoqmhgohmIiqpqiqKorUYUAH7T/xA3EMFfEi+dXZh2Bg2HzwzAaZgTD9/Xq/nXJ5rt2l/6 eavVYnchlXQ23WU85X2dsoWwEK7HfqZCYA53gvVjr/cWsWAunMKdAQU5TIgwiN+gdkAkHaAbBDvh AU5RNpCHzDYE46gNADAIcAk+42p4ixC0VXDKkJNgn4yXWpF21hhknlYMgpTUEVZB7PCQ8FEoiReQ 6zoNvdopAgVXT7DtcYvIXAtrNWi5D3pH1r1SEM+gobY0fR3y/NJJWXLlsMMWTHLlFbb0SA+KDrpt oOp3/ceESqEJZSdr31ckPYGiHAOlhORAo0DZuR48R8Q2pTCOwegicpv3BZSGryGZVbDebNhougb9 10Nx1lCWvR0GR2BdHNdF6zoBpNZu5BObvybAw8gvXzfEySv6uBmbeeVKZOL1L4Avq1LMm3QLs46A 2KVX0BPL1OpuJSYjAogtAGwIrjBJzIx59h3NxEzIgI0wEvaB7tzt5axGBjQ2IDAaCuhiDuDL4zl2 DvHqISWzht5Y9ykIMgEA7AU6OdnU3Mu58FE8Qd42mScxqOoaKKIQoppxkqzKzp2lHAQDghAK/R8q cwA4B1D42vSGgcqWX0hthGMeJqtJRzu7kc8wY2xso/p8ztKOvZrDDAZJISEJqtW3UCQ2IySgkA38 uAIjeCb58ttGjVySYINgB+8YgP1kZDD8Sol7V6r6wqIv1ph6HkeodQPoCAPqvLXn8aiu3zS+4C3N Itq1dHNe973rh5gi+Qt0gbDTYn9yHUUu0otLKdeaXsuTtShS6GpoSxI3VI05FhCxvKT0EFlHkLhc djompDeXTM31ZJVIpkhEJIHURHsXNPnDPeThm2oaKueBr5UQRpUDbZWEpPmKog/OEQA2m9zKiHUm Mi+UgiSUIgJ6xiPoPC+xi4O0c3mO9yATpOzX2VO9nfq35cOy+uunsrU69uKmSuTO+OX5zlVxvmOP 1hk2JMyJAd29IUo1zBTAuRyJEcDMbTGePQQdjYTRER1vtOaoO51+zreREReJU5jiRtdh/xdaSXDC Ko7CwuA2NjY9EFREF61oUdyKNiCsYCSc1Vt+jtJ5jsvgtCSWMpnZjGc5MXYGB/gBy2G7DHr8gF7M BkJ1GpU1gUp6g4JT5nA11mbPEdb6EsmB+PaBQHE8hwW4A3WAO7QTn7kA3kBu6gdodiMDlVRTYR6l OU6MxNa+AkJ2SBpCU2LRqH0jhRC0pfmPBV7z4oOxo6M6Z2c26QqvQpw7FyN20C9gkZBtCeCiipKD nA8QFHd0poeSBUDnbAYcUhMLVRhdAwgB4+l8DOZuHg+FrrPESHE7szwjDnOos+4NTSeMiiedYdB3 KPad82GwhCim2il+PuYDqeflD0AwQ3KZ7z5YfCxGC9gCZB6XgQmQSvEJRNMbTEidLDym0DXzIQqQ 6dB28J1GdC2e3O1fBKWXC4GJOGFRKbvA9zDQK7GxyOw6HWoePcsE7iBIkKXiR1bhyhgqevepUrUF Ue0PEANHidh5E8g+0qCMv1gc4G66B4guniOyxCd9aT2SSTJyaHUjuevl1G+052mmggoJqYSjcNtz 5Q+FU6RO8dIJqOLcsL5ewppJgnPUdghrTCt2AgeYC4Gp0HUaiintRxSjsEJImMP2eagIg+IN/xCa Bfe6CRiGCMj3h9Q+++88iO5vdyKiKt8yqpivA8TFVVFxDzH4D5StuAedQ8FDyBx+8YhMGc8D7+fG onNQOIn0p2fJRFIUBVNJITlEef05hsVHXE9lZghwIEeRODYO96ZdegMPF2HYmnwnROKolbTW8hvY BqYKHwVCFIyEoCfierrhYtAI7BRVyPOx2XTAFFCJNYygGeE6gHanZ4CzDVNBh+IpwC73xOcn1EQG AYewkiMFI6KHQ8jowPYsgJ206Cw2HqezgAaoK2p0p8BktszqR4J609TQ7Q1qekNmt0DtXcbTuUaT k6/j1Km0x2nmAdeM/HaWbSXRNwbAQ0U28yBWyazVjnolXPMMWGEMzO4ztone1JOV2D6Qvy9RQRI9 ImoeQ6AsFizuKBsHtsTlKDvpv4XAlUClRJ7H7X03eVMU7ijOylVcPAvBaAgfGZSEYI71c6Z0GpPQ Pqwn2i+AeUbGzBxJTJVpziCPvLEdPeQMXBAIkbX3BetcHFLwGYBkHRC5VQh4TEItntDvLyjqDkAw GsnmTQKtcMAh2wIRR1vE8myxuInhmCWEjjEpxuDD37l9ENyQE2+nCNbHpRNpDcPfFgcmQcKooIEA ib/i/YPm6eXVv0NUVhmVq+gNYfwCxGKFNwo9SwNPXXsQmAxNMbv+c3u6TLcGKT0CBJymfY7HZHgG R9kWzWxCBukxRrvW2mGThsbM0MsYR2KRgRFk1zlgZyk0fr3LWxM4mHBudefPnz2Mndt77CLaAXR3 nGppgBsMhUlEUh0kOw46glo6mwE2iLqcqtgOphPFdiYaRyKjWmdAUUTphqKYCE2SmRIWTkQ2OOcN gwWaUgsljMU1k7YdHbBO0Fw7ZZN3QHlcAmI7c9/HfyHPS8cAfI5JEIEA8tZ9gOAcgmR3HgehOhy1 QYXY8zt6qstg5MDxDzOB+B+VDRqudZlZ5Uw8H04CYzS7xSCgcCORAxz8GSSbR5BPL3kyOCtCOs7l NthPIeahoJXsTqU6NBQnPacV7CQuSHdz6HuI2UZnW2Am4camLrB7k7kzzzsJBSJIesCuLJJPmGgy y5wek0A1iCnubLqEA5NDUWmQZhzHFwLyHMcJCIwIcU3uhg+DJEo1aw4PkVBu2VVOBCJUiSJFAxZU MAhAg9bj1e+ecYKEB4J4Gh991TDXDXgnzL2HAvlxiU9ohiSnmCiXMAwcDKIy2RzJJUbRPBTLmjOP k0C2Za9GYIcwzqE7C9lzijJqb4Z+B8QJ7YD+ch+ECmD+RC8Kn1SWhSBUA/CBs4cbImNKFPyIAraI EIIaRQcydvZxob3ph99Ys5nVT25pijKykrRRH/WyeneliZqa+OMP8SYG/THon389TZ1/2d6aNGoD /5xwWZqpRrlDTgzfQW+yGiVIIyTSb7zpdtQBdUlBBFPBqc+KEJsgaFrYBvGikdWAVIgYlT6qWTNp hMlYUqlDGYhJ5JJCizRGLSmS0uQDSbQZWQYpSiXPi1g4MQ0MtLTkhWYMB1bEKxtGw/4c/p1P5EXH 9c0IyeA7dkLlpV7SGCriA13GkQjA8j/Op46CLoxUU1BV5EVDyT3SVErK6PGipcPjjaCi4c2GDNms xFixVYxduN6GTbZssSmUTKlNsBLXHgKQcK+z8lceGnuQe85vRELMwYnIQdEeSCAICADSzCiVPp/x /lh9PF/5Sf7FH/7fm/0Y0S8/3ZmvyJAGZv55dU2jML2Qa28Ynn/QXOfCSo4zhS9WtLCrYTo5zRnA L30hT2Qc++FCMl5hmYZi+p85vY5lt5579cBXgnz57HnunyP3S32xuaxMYE1nZ57Tzeea2l51T1xe zUYoMHjPLveLirOZ9UlcNScBFXVtNXLcGeTgiZIKuxreQjhWcjMWCIr6YCkTjhNbF7p1F8Q+FMZO xwu+IdPLC9J1wjljbgBKsL6lYXIljInCbOgcss5TyyQ3ExZZZ4VrD7EQIgmEjB0tYmU3VdIXnKEs cdbUL45ZRnJ1c8aHK8LuxyyVn0OS4UxxnYZMpOUJ0En4zjlC9tUCYoL1EKpaVErHPF8c6Y5Rvcf/ YrpSWcYzriofjMzdLCZolbpPB0MhTCZk/KlKxm6NDGumIfBYYzs4VpcYARxvd1bpELwzvt21q1OY /x6OsOqJOmu4hKqkg2j3oy1XLYA22PXoRQcJEQX+QlQMYT2QoZKqO7hAf2QlGKF78mwIou6QiB5E kAMCFACAgFHcSURAlBQor0853c/VU7ujs6ZWTe+fZO2Ljutsz7dlUz3dBi63QuAGKf9NQNUSVIXF 7dMrSfG15ZiqwrqtmFycu3e6c2/7tMWd+/fu5vsX61iySKO7l12D4NoMOqwU1ZLS1Sp2ggQ/ejJ0 Sa2w0nvQ94jNShXfNjIqhWiQsQqu7VdRWL53Ghxpbe3BghHLEBxcKKMB1+POWiMmEDaA3I9qjbwc ZDObykzuBdkSEZa1ksrsHWJ9knt6fFdbZJQP8rtf8IFEhCpcc55PxDhUql6vF2+TyW717eS/jrv5 zyQ2bQ0hF9hgUi+UeXY3bbZt3KiIMe5KevswCqQCQSViSxorFLLK0oNG/IOQQYqS0CJ0lBacmELG 6in/HA5yW/p6dV66Ve5R+X8D2LsTPj2VMOJ2da9fPxkE1WswlKTTVSCrJ91pDEkoIOrYxTYQKiSS awsBXCR2FlyFosxKcwKKiMiBAson8iYCwtwRt3Ip4vJu3GpfgmgqddnL8HUdIBDXZnDJTL7ehhCd W3I5u5tL/XPsqA+4AF/WfqQ8qJQL3GGPVBiTRomAhYK8kGRJFDB/eiGgR9BSTErFhjEzLglLKoyM GYFDkpJDDITcJyIYKEJihXcnKnGKYggrIwTCB+w4eIHkPevSPLrEHREPAO9MYqfUjn0anlkRB+li hyBSCQ7wpWIBkhkgYISBJEiRGCkUiLHOkaOc7TP1twOXZkKb+/9X/5aJSnfxrR+78ZyP1rzIHrzx YsR/UJNQZUA+7y3F3vr5uvzc4oaukoRIkkjCAQgJgiaKKBlCVkCmFJeG/k9u+4DTRRMzB/Se/yXx yf1QEbgPCQZFer3+FBLAXXIGkD1JbVZ8c8Hg8mrxfHzc7dpWffJ67bdaP48ALvjZ/xxilb4IIqwn L5ogQXcKLCt64VF8f9KIiBEleM0g2NrqM89MklcDOrrYtKd9AiW0Hflv3bccbdJ7gNgUAe0iyOZ4 uxchPpIVFipN39o8SD5UD6/Rh2n5tYuj5JLTrWtN9KDUWE0aIF65QC6CsupmYSCKxsrUTG03Ziq0 MRcWowqKFbTLaMoI4MwJMoKmy34FTd33zN8M0JjnNBd1xTVBsBuVsRQO8eDMKErklgeCqnAXzQUU MK0KfAI4Bh+IIO8CNRCJCGqOpD7Q/SVkOYMFAgdrPzMxgGhICCBeGnQQLlQ80ApHWqFAz/YftHo4 /JyiaHaCAmvspeSwX9LMQWCioisQuFWTUJPU50H0OsKoeM/DgEh7JGKMgQoV6/xJBnGgI0jTG0Vh /RwZ0aNnSMSSXGuhiS5hsdhzHTJCxEXt2HvUXiWGvGGukYtxmDJ9b7xWGpQKWOoaOp1F14WG4pu6 WszimpuMbVDKdI78kp1ON4728RBSUTXUQvQ4bOTxhK89sNdDlsEyMIqKcSkc2EwRMKbN4GhWJEow SjNvHBg2JRnLB06YoxJBeHxoCK0CGTAk9msMxWHdM+jXvVFeigFFsVBB1i0HTaAa6raM2gZ9gBAN bpSABR9FdaanUNjoU2CNK6iNGtXUiO+IHyOa8eA6AYd+phsWQgQM7OYEssdORt6MwPHURInbrLeE r3Dfi+IvjqsBQE2GSoKR4YVFCINGGNMEtx9eH4q6YjSqQTuVB7j2YJyBSU+F5mDMdlR2WFQnfPCR 7CgIPcSO09exQifDjl7oMWIkxknd3lm2KDGVWUb/Z2hYEz9JBeC84EnpTruUw4ccqTGwRQ+ADU6S cSELPv+W1mayfEMF07u8eJN977UQqMgVF1QHBPKgL2vMHgzbKnuAhWAUIKACkmUlmJmJhiIKlSJA FJkoIgIkIogmmoRCJEWIRmAKEWhBO8Di7CO4TmFxpwdYFpbYNJVKCBRRahgeXybfvhAu5AZUihQU lCPYREGmwnJMZcocbSaCggzBaVXUaiICIQaQiVCGShRaBaQXRI4YMZgMOSZCKlYEgGIEDKMgEQoy 9AcAez0AIdnv68QBQmHxxyHIwoqHvNFRWd1I6ZZMSwMib2wVXWiFSRctMMjTEtJoxJqkMKiOqtIR a4rKJswpuJYJA0MUOmhsUM1ITQZfnPhiDEmfBtyoZkrSkvd2UZGqef8KUDVBCIeQhWZVGmVIH0CJ AG5Be6D/8wwXpD6Z5yagOcBiEgYTkLvFPbK5G8gXg1Kj11kpdp2LMKJliwvfEMK8upRNzsAgOAPz J4iV6sOLxeFUSU0gxYic0TUieg/Gg0F3nPtHSPR/cbNvXvpVNRqTIrynq0Dcao+1NUaW0GlIhKqa w2XcoY2njpW4Kbg29B7s7EkCQgkiKQJPPZVpA+xXOR7n7wXUK8QDqIEeuGsjrhDUxIagUoDQRMIP kCSJCAEAbisFQOIsHArwF2ftPg9n8cG1INCblkgTeAx7B98XMesxfjh4PvTeJlFR1nhQ4CD7zmEH A8vaonhXlzHgbDmEIgH0wqoal9OBNMG2H9eYshLahJs3XEVtD/s1lS/S1DiejOEQhffjHeTpzDo7 m2QJkg7JAHRYoRhREOeZn9xmfy1m7ck1AZ1cYOtVPlmcQyMeileIS4hlIjpd0xnHM0HJpF/s1t/6 p85VokY3f795c4yyE0M2lI/PwJoxT8Cy5T/i9rjxGadR7Q8e/w0aHso3xSWHi93LIAQ/8dtKLhoQ pFcEAF6BUq4gUDzjACn0H9bBkcGbscgLI0Q67nefFEQOpSz2mAcctiEhYIk4SFyUgVDH3yAYacem IRBjXStvZNxUHEAlKXfmLkeKHPYlUWtCmzD5D9n7AnnTGQQztY8WVYPyMs95RRjRH40KhDaye2CL DAT6Bk4ywiqG2XPIibjJm1hSKHUD/un+XZ/0OnWQP8v9LiCm4GSH+UebnHQQed9ESEOI3wKI6B7+ DyKJJCD6vZw8PG7aFmT8j6Q322Nb6/oro/3HBwHHrURywKhh1+1l6xOr4oAHm83TC2K+nOToGem2 +m8MrtxvTNOkcLE1bFFI/PJIdJD4Ge89VzrSweITiBpsY4OwA98AFkVPKGiakNWTrLKnlT9MUNZ1 UIlZsAhz7sWLqB96AGNY24QywNRlMZmC9Wo9aSYMImDcfa9oAYdZ1pJBEknGyJBoIIRgkiIiCUP0 PcUCRJV70Yo7McQzkmmlIDbMQopEioiiiiWZ8WYMQBSQQhEiGhKHJKRKiyBIfl9iaGA/vOpDET7I cQSqR+BRC5YxRhnOlZjXZlQOyTn4B6GaawRwYsRFWCstBVbWDVSiRYiINAtC2kIVUnsahpgiX+IH ADyoQQmt3ynttDGrbQaw06CgsPifEQKtxIpswQkJQIEROnkzUNBUz+0MATJpd5VfwSga8M2H21FF yzIA/0ymBLiMpg4gGg4SKgzp93b2GdfQHkqLIoLzDnVc/FTIqwiiUROqGgYTUfvTbyHdFWvZmNMS PgkCmJ1Y83O/EEcgWPYeUK7AMVjJAglK/OQU16uffmsCCyfjFLWq5FpIDDsP1YEEveC5SwCwfBiW FSSWQ29j08uFtlPTX04TQmQMKs1ytY7gG82MnkPEg1mRQamK4WGBSKQqOzDMKYjvQMd3/tTDYslC A1oQYSGQg1nvSlJgRJpMoxrC0EQYGpGxNkKs0ZKJhsQhxsa42WMx1DKCbBUR0RBdDWixEbTG9yYT EpHCHO7Aw4G2GkOBwfWoPwElA0gRKAUotIUIQwAcFAd6ac+6qqjRTUyLsCKFQUqPcCQAmQocUY+z 2nQ9cAYdqeB2lhsas8WZsbazMTaaOUCirO+AZLcPkdQvXPJEIBDW29OzGro3DGAmrEYy9MzOZ/IT mZYDG4xW8CMORIbuQgE3En+yQ36vJrHGijhFM2b4wYyxDHGZBoFjNhDg2Rs1OJLURkal7J45lO7z 1oFOCjw35TRsiPCE20Ds56TkYuvQpUMTTBNHCGa5WSW9ziagoFmL1TPFm5nDicNFMG2cCQpnKZhh DsngbL6nVm8DBJIU5M2AdhqSrENCFIjLI+qTAlCFkhJhQjDMBOCqAGSiAQ7Bx67plzbfoaXF/3ay W7kopjRaLuILoiXphJqLQ69bNYntMzxyD3rhp7tJqlI1RrS8+0zert4Zw4FaPEQkXNhqXo+LG1ov Ftu2YHELVJN3TxqRTcDyP7n3SbG2cBCHS0B2vDjR5ffuhwkqkDnJeZ8y9pBHRfuKOJ6A5sZ1x5V8 00IQQUEfb9HH25nWsIKR7jebiEOVRB27eZe8iKTebvJsLHAjAQlEo/HcADy6d3cn9Xj8/AmobhwY ZZ5ZXvaTyM2zd+JQFueh7JFQiFo6jj9QgH56dlO+U0gHb41CIIor9t59KJhGANARQIwMByoFrA3C 11X9EN3YJcAO946FExEQOyIB1CQA7jsgkhYZ8VkvboAMB9ywjLCwGsBHcXuDBTYh/kGAH+hRLkg/ JA99oGlYPbmc42lTRljYFswadwnaVpUySJDabWGQ4QhGmMBZYTYwSmewCsASBFsfFsbWXqf0B8Xq wEe4DvqnsLeiipRSch17T8/89TePMcnl00IZamL8EWG+wyPpZEf6D5vkspXhD96p3pWYwiHkRBFm ZBqR5cfYGP7cZ2oRj9RKqvyxJSF17CP3OyIbYjR6L0/r6nLP0nKb69DKszmogLFfspyPK3iy+goF RQ6nJNtfta2w2vQpKmnGI1gslqnSq80MBosPyYt6BjXy8Qa3wKeBnDzjxe1wcmkTtYGGsp3QZyWN 66NFuSB9fNCDTdna+6QhMhN0jX1nhuVvuIyuD7mI/wiGCkzyZggSBA0AlEQgd/M0op+2UvCQBDvQ Fe//T+b+3T+y/4WjbtxjHxW/fp/b7Nb/RLGyI0u1RaH13jK4RJTcHASJ2AbkHNIuApOWT0S9VDhm sXe7PCkyJPAzyR+c1DN/IfBEFMqPmRiUwvgX3M5YQjaQnC+DU59hJP/FQv2AbQNzkw2ypjc6MLqr mIz2bGGiTg6hnw2cURECJhaO20Ua1+AlLewg6zQndBOOm4kneoXZdK21uK0REQIkYYwALmAijDDZ LXTLZ2Xa45XVW1SliderHL63dZeqz684xru5a5XHEw9CApelAiYwxsmGAKQhaF6UnLLJbTFpxq/G 8Wc6SAyi7HCjBy5GoL32xwyBu/6fEAVyEzBrSdTRa3GNwscZl82hW4ljpPOarB7aWztez888H2dR 0YSoKZSlTHOkkjIwyhjPOWWMnNMzrXCK1W64JVbOZKQGBcirSa0nZ0KiOTzRnwhi6TLhNnNOUX2G DSvisqh9a4PV2Fa9prOprHTbQddV1xrspwUXrOG9vwd2f1D7IY+wQHvTURuYZ7/z4U6B/DnxBp94 FWEhXqIQRiVEH0kohgwhASohoICINCRVUH5Q5erq45c/Nk9myTq7O7tc85s2bYYNyXDtu/t7/4jr NncnTp6i1ixAolEogUWLVCRPzBdPM+X2+9bnTyeXr55d5uTtOCvKIp9/V/C5BPN9CUP+SsWCnnFS eQfcIrEDJyKz8k35d9Bw+vOEmMSRDbY7/1mxpJMSWVNGFnqT+t422MYosUYiVUh6SA0RNBMhSH5Z A1Og/HbwGkJwRFQJGaoAf8gv2b+PWBxX49vYnS/0efbOT3y7vTL0+SqMOojhsdbwp3Z+tRF923dd g8/FMFr/xX8ZcPgglSv9pADyiBxV6hiHH5lMZ12E2Pq3bdrl3asN0t0tgF7tFxlvirTdvjjSxZWD sKPlixmbLNpTmAshi1azjyTTeGMteev5kb8lr7JbV/Lr6nx9XJHpEDugKH6YCIi/RPWdeKdUyu7N Wy3bOUOL2nvG4yHPgnKT3fVmidqBE335big7bcehfF4Nmo/LTXq5jUGwNkQoR9H2lInfuUB0qnxk BaTwnKUhykPeQPRDQOKIKeAUZ02G4B5JV31/Ovg7u3snXbvX7/P3pXba/g0rokva+LqCRtMxHVaq ntHaoRK1pPH0j6tx6No8vkA8ezsRM+q5QckyKPMHaDKe8iCB7jgBMvf954TYl2GQsCgPLr4KOjfz +0+YDQMsA/9tNA6IjgjG3REEDP5+DgkQFI8VF+WwXrYGnIg2wzyTBBgf6POpm6vzz7iwpmJuohoV hS/dnI1APxZYQf+RZJM56RSTJbQLY/qQzsrZZFJz/30/4IavakKfUHlGZxfYn9pAUCSQK/+EgJQY AUIFS19eYADQSkgByem3/S/an+/XvDNkeDxiHJLHYGkx6/0Ffle0IKOWvl9OHafgH3pEaEBpYkDA McaQMzEyChqZXIDLCRMikrCEgkEaQclGgwzKA+WEEpSBakWkOCbkyfNdQxET4ySQlEokJSKAXD1I h2ikDNUrx0l7N0VlV2VZYMh+zg0J6clyHjczOug6SQ1VIaurYIlgOWLuxWpLIYIknr4DgR+YePxI ia0HnMcWEjDjVA2A1+NuFg97X/j9I9n5vu8tzFT2pidP1U1nI5QWe6wqLKDL9Ab+uaod89YlxzHj jlQiPoInigB2r/yjoL5b0mM5GYv3yI/idjXwYPxSJwCU2gKRYM+t9uQsfPthliVH3lDjydGBIkKT A62L6DN+9HzmZz6y1rZAdAeRFfmiuA73TxPabOQlRREEKRQwVIpQFKFBNYQEqn74e/DrIoIPg+DQ H2E+2BaVCzkiOUcK5uJnKxpvnzLDx8YWDGr9p+nweE5SSSEYOfmPlstz8PSWRDvAPdKtR+BtfGa/ r2nYHFY15JnMvDmWNw5MRplBS4EQenNa+PThlkyEnFU9vn9G+4RaWayqQaDBws40awymAMxUwMzF FMf47T4gVIdnb8twh/jJyJ44YJ/fe4VizO6czRzK36kVZ0h5XEpKKdSb7d4dNdI5llagJUDlCHdt tXVpZBICyEYrQ1zMGqodwxMZFoYmIFOwxxJ9l+2fdcQLcIIIQ+MIMeY9S6EP/thXzKQ+l5uO7Iyh p62dDjODgOtGUbM6dCmacDTD9TBAQSjBNQJ40IInbYcR7uICOdt9ECu6duoKD5hPFT/3niKBHbEd RHygi7O8WkMFJQfAhyJGd5Gg+P9h6vBC5nXgPSFhUvBjBDzQ5CBfahj5Q9H3emxsFIkExBeGKHin VDWRws6gwfRtH9mh193F+qGUkSBA/0/5WQ8D6EcBRCET+MJW761rvHg8E+Ki+UVEqlX4z9V7V5bB fFVsPd0/VLGRD5ZjHnA8amZ8mlUM/3piRgTa0JXVkTTAGc3lsZBUFfNkIhrx8WwohkG2qTRUvnXs FEB+CAhJsQhGg/ifVHS0p16q1Lu52JnNMpH8Zft2fW1caGDF7IdjmLsG4VhsbNTPYd7QJp6ETNse Cigq94JTLFuUaZVaAdi3j2ljmYymZuthDRMeN8SvGePKECPVMSPQFoIhRnUIs7XAADKKLud22oAX TDa5eoqqkuYCxX/OkpVgF46Ssw2FgyI5TEo1Uq535wsuEjqWICBscWs/kq9eW5huckPHeoeYNrbM xDAqw/X+jpRkPAebpqrO/bBrxaZ2hBuonSEOsKjgIR98FNbIlhLuIMTw0Gmr57/F9ztjZ6v5R4Sk hceTm2aZseVHEHCihzA5ZpRnlaS+rEGYdCIK+Dx1Nz0b6F/2IooTt2d8dPKaqEBqyoIRrqdcoNM8 Dz87xqLhtdVmF85ck3F+E5MBibeHH5qHe5mOIjp78TvO9YdFQy++tINPykNpGNs97A6sbQHIZRpB bP0eCqmDhWvTEIFQ07JJ9FxTUyhmVMpXeZTJiEkp2RHBgfxONLIgoPLcSRAOJ3PgAE/LhM9l8Npd WAC336p8uR3RFhU0Y8SQv/Bo6AkQUhkPZUgIjTGjRbAMl5Xud2J6uusMuYUpnzSIh0ibaU2giqxg MTTYNtji6S1VgLBHcNxLe+JJ07C8Tl00Cj919iZEEPy/P/FVF/h59vqTLv/0fp3/x5cfy3fRnK3b m+LkxW+3bG81kjrtUTfLqWZmGbrtfXX/ZfbO407RxE7xtgpoZD6Y0war6PfJpF14UyxhWcJWGIgJ NaKXNsXlmvkH2vd4XLC2Qy+iC1Q1hJoXq+uE4uVbY5PjNKVQPpissae8Z6Oqk1hJaJHKklsZ44wr jerQjFZvbKQjg0nuW15q0MpfaEAStAamQlfLQOvlgkcbzrR04xljdLXtlKtJI+CMmeOGLZ0lbHA1 jfE0dGbowUXxhRsaYSwSWLShZWdSuOKOk6yBcaUvZ+IQVa01xyxk+k7ZZbgBlbKJiBN5fbJKOqyR S+cca5YwwcFhhW1ciJySIxvFasGqjRdhMYNW0yqG2cMpiEltKj2Nb3kkawGGFMJUhGYf9owpMWuY 4zWsmFxnmVylhCscoTtWL4kROeONznV7qUZmsoBCZQhG16yhK+VEm6kZ4urGzBmyva2WAG7pGxB0 9PJfQYJ1OQljBA5gziukWDHz5OZn2PA7tLRn2dDDOFeuYLm5ktGwV7wqGUiztgV4Qj31/KVfg5hs Cd+qD2GgXHDAAIFhKgO0iQkCTdYakBQ0mQ0xpRZSnMXytYkvccm7ZKU7/d3cO/3+9XVjjnjeeD6c y0qinTCdg3TvCDaFhivTlnvRChJQhAQSKVaDk/NDnkIj+icJAiEShRn2pcIMbAZNdweCBcwcxnVz /SsxxA8kVNMDPrKTbmz/mIHLZhEyMS6Mq1XaN5rRnTanPllgMf1IL/giTAZ89CkIgPYyxJvyQPJK wyA4plFyIFO8J0O0BN1PxwdoSKlQdMppqlYigPtjs7M59i8VnutlMMoMPugxkEF8B8QlnmH88ow9 StRtoqqtwo4WRpWK/qhHIoOlWSyODePHR3JjGPLJbbcdaMwxFy0FCmNgIwDIgDEJhJl1CUIGQlDF WqcgzMoVYosUUWsrFrLbLapWKNpVRtUixDAZNJqMihrLAxhkCcJGIGYIyqlPLdI+f9gy5JFCeGLQ Uf7C1onMjHbRYa+zPttQJvK5B+9a1jEh2/dL8gygYka2kfUHmIobbBOyMHRhM1S0CbHm2zdoKnxm JRP4D0D1eHj7S3oPtb2Dj7nyj7MWl0+2aCc5H3Xm99Ce0KssJLj8crv98M46CS4aZV+A8wPtCbgm 4hDvCowCqQSSUVZ7cd5ydrpsxlGALt+mc4aDbZ0Hh+6IlDPTgoz3aB3dnFJx5dvJNLJq425fm/dr CokGAQBqBABCN/OgDuYLAomCCduHCsTFRvDnb8eBHCSM+dYOz9tRo1Occhfcbc/ewfboHSQzoPvd 3ToQlHszAwUIDCK4WFbuRuSGKqCaCUkJaZYVLaWK0cpZCswBKUpliEpcbCjMxKEIgLDGkcMkgKVQ Qgt67kx0Thr9QXYH/ClNEDoTq6z664QwnJpXp46BeQTMOIGdhl5HGrarrXMMGu3bnvHTdYOWjk57 zkn1Fjq41YD3fJ4F9ULdQiavQaG/rUEbKpqUA7zknDqYkQd14HfZlJZmbe2U5xw1gU6kDJKDJToQ /xB4O4czz+flKG3qHEbzEcW5UfISAfB5nED7QgA6Huyfa6Ov0Y90cIr07sc9Kz0jMAIz5DOeGNnk WpaATXl2lSigAgL0dn1CPegrtiKVO2qRq0Ssr36cYpsRtYmLBrFmiAtVTUbbB1SOOGStbZjSHKPk 79ejALpkZCpMBUFdWap4Ii48JUlfJIZH20QPUA0sfr/6l9mjed4Q1KFD9sAZGSJkAhqU2olQoQHU miPpIB3kdo/5QqG0KO9vA0TAXoCaTdhWH7De5aaSBiEPuyh+JP0BnxAeB6EDMkMck5nAhKSJJiCK CgofqGv7AZYgttna1mEzWjRRxpFpfDRoNYz+yoLU3Xd+0G02kv4AkT9wpTUkoIape9uJyMLIxmzR GQA/T4qgJixcLsGEVhlZtvHwNd7Lvk8kPqg2JB8d37em7za5hzYmYDliGGHE3kWKZz5xOJ2ghB3O 2QCJTWhcAXA4zI/9x7DNyoerg8hTSQhZYlIkQPpI8awNJZBIbS6y0AgRgQEjZIJlxVNxS3BYREDi pzcwBUT8APOFhGz4yqU8yDzghqSgQwH5RZBCQCsAFzfE7SBxIDuGW/PKkTR37dRn3Qyg62FgjKOo 9nQp9W7rcVuiw2ZDHIMeig/rj/vxegbW6E6cZVnQHLIRrLR1KNu0HGZhZkao5qFMyq6KK88wFYMz MSytwo/py5JmWMxprCN4xYxsG2aNiZbBpaWlQouHenDDZ9vRYH1BfPgQPAYFk9wub5omxvtIwhgt lERKYRBiFKxKUlFFLVGpIgpWyxKjLSgzEggUtQWDBltStFKkximRkSWhUxhSSlFhYFGGGBCqwZUQ NDRMhJEQlDFIwRCYGBQ4ZhI4xWRKESFEoFYsiuUg1ssURZUnzaLMRYmqWlDCUkSBS2SsBxhxAlcR TAjWKJoRtRZghKANBFMrp1Gs0WmZdChaASiTSEUmzlnD6foDvNY7S690TYUP/d/OVcly+V0ty1Yi 9I1rNUb17ORGxzZxSnxyQE9hrQjMT3s/uyQlnS9o+/KaDzU+h35Xcdz9Us69lU4Zw7jnuoxDhPGH +w+IA6HLan8V6ukiJ9X4U9+G8MPsiFohNUhcazOkpNIsZlexavTTqlqL2DCdsTVE85OaN8HE9O1/ V/pJ+bRx0wuhVzbMXWxNjcISQPjQB6fGR+V4Bkfdj4p/wD2hzAflO+mSU0fH92oBiIAkipvQHzrx H4ZTpBxHT74gd2+sMaIzDC0ok9H1XTXYT4Hx0e8a4knzzkPUA+gca8eT39k0LtvaDt7CxFrHVaWq jUPGhXjIog+oSmrPN6KyE6JIOIfnfmTqKai/BGJDJLcT44X9s9bu4IR+C9cFK9d5LMpBic1XgO36 E2f0/SoQDEREDAxQISsqQQwSUJJIQRVNMRQRBVNNDDJEUVTM1QRBBUkFQUjFTTQEwEMMEkksEBEg sAENIBQkQfUeQ29EkQez2CUh6dNNDIs+lDyQjok/GfiE0YB7uiXvaOhh8HIDBBknQEYM1uhZvC1l 4woM6C8bZhTaJFY8v6LKB1BmCCBvYcVGHXoWhkpANUWiBzLO9F2HGk9n+QHHTkcOtslxs3YWaGt5 BTrgFHJk2gQfC3gp86rHeKXSXtl3CT4RBOs+CdL7tAnIrNNfDmC7rCBfwQMGIrSOE0qvOK+dELJc Jr6K/+IaOfWYzHBywqeEhWdH74EeIFj5TvzySeOOK0DNMSc4yGn1VVRELlEh3SdnxYfJcSa//3ox OVSnQinB5YdCXRf9rIimeV/3/z4n9X/95aOlWGJ9+9ht/fv7PPTQnf9lKyod7DvHvd2LgQ5In9P9 Gf8d/39KeKTiQhklESDtAaJAx/2YmBf94wYHrj0wdceS4SeUnvk5JBP8r/K5Oj/8/de45bYEtsOz iyRSIohvkeS0trFJhD/JdmjANo/rkMgTRLXtzMlIvC14H9tpHdZJNsQqJiD0wD2ESRC3qVCgNsOB A98LysVxM6OV+OtfsQoHOMgGvnFDVb9qcQtVy79c2w0jZn8DUYh9X154iAe2R/MJPcExIjDVufxW bmik6ioH3pF6oTR+27UzMtE/PdR/eWIP0jKhyRg4voZzQXJewxdsFwjiZSVJImxIGU2mCjR7NiXD Of5uan2ovELi8uGxfu+WRmHUWswzA/lcHEFJUOsnNKn66UDSuKjbr8GQhYEiOK6gFNGCnShP8SPL YH65lyK/0f1KYX/rDWkSqSkesyD9YO4DAbFGDJYGh6v5kdIgO4igY6BMIKUGIlgKVg1GLQrotcMQ VNiYGpUlqgiCoWklNSGpAtsAaQ2gBMJTaRQpQoUWFAmtbGUQEVTBkWIWtI6IU1DI4uQUFZi4RRiK wYNZWAVhRCkK2xlKUaFkJKQoqTUugitTGOJTSBgQEQ5FOJKkAUMIQSkGVcygjKmRHApClXWBmSEE KKhRG1pQYSCLALZYRIQCcmqViKChoI2wTUpUK0UIsARNizSIrMsoEERBbSSKFRpBpKClA5/ONnXw Cak8QDtNy2OKPhnnNh+Jdun2kSj1n/CyQm/Xh8hLkGEL2cjG1hyI56Kw1Bxf4APYwmlWEKT0zn6H SuV04zan5z9i1ycp+VJVAPuqJ/fDb3uK5uMFgB2buhMd4WgAPO9nghsewUZmH1E5a/9eyboww6RL AwZgz4srG0oypRyYgGxJGS5MWc/aFEL+C1xAhOJ2n+hcTcmmupsLFdxGwMz5SII5yxgXDYRMJWVU LCJLCzZkElSa0ksR1auawTAoCFsEYODKNoJsYxCDIHM3ubZt2SYV5gY5FmniHR6oaHvMrIpgamSV 0yN41jRZvLlkaTZhNpKA5hs3I3CwUlaP9RqgORSzxwcp80xVNxoe5SSO1yKfftvWkaWiCwUbVVTF 4jZvRp6ILHpjwmN49LA1ybhXnGXU2XreKkkQ6vwWkLDCHfM00jEcRXjaBhvrcBkw2CyEKqMZOE4Y iOmsyKgzV0xCLiZHLFoYPQvJm1huVqnGdcM1hK022cRWxBhI7a2yw4C00gWBY4rI4Qc0Ie0zoLSQ jTQGmILTK1xLrIzpmuCCW+m0swg+kDkr2qEdIWxRdLmHElY0bqj6aLhmqm01IREAfK5uVk4fTiDe bBkGjjZaLDJsdIzlvccZubBoyIb6MArERcUMVwdtC/JBAWMO1w1LabEuGLNqKEMhyjWRIzVtxkU3 3d7cxCk7IDRAKxljGMGdSl4ggW2D1lRbscsfSTt0heHgOm2oh0zstQ0m23Q64x7ugrRviBoOEh9Y RaZNRasaSQibIQY2ZzzgsG96aiG2WObqq6PXOGsZvQjG3FxUOZGb8ritBrDBapCjE2ZQ1a4cjYoS EbkwDiFbyloN3Lb02sKxZGmWpawaij05vcCpRSymlIsnSkBnOXcI1yaZAdbohVKBDnWI6aN2Aw5o 8OZrTgjDLbI2yi6kdBxdti2AjBSIME52iIG15ZNhK3LRTkoCMbEcMXDVYtJ8l5AlCMvEl5N2ccBm 7vR52XeyiAwBIQ4u0eLo3akY0tmgr1MeQf9bP7rPg41030uAOwxoHkCySdgyPphobKOQ3Sm0inXJ xvQzKyD/fDwpFzNWuv/PvjninG2ljw5KUkiRzvqCQIcxUMCWkDHExosjLAENpGurRrqNG4QxamJk ItiIq9g1RurArTme0oUVNm1lhkxaimlJvf+kWbWSqfCoh2dJnj+FsQihFIJQwYgoDAOIb93u2Pl/ hwl45h5LnlMVNM4McZ7rG6gUqt4dWNqlhUYdwMJmaLuYmGKcphFbraCXvY1hhN61azpSvTcckNti bDQZlokdAYJYdHtBuIGPa8OhUZkGQkZyymyD4hRxmnhgUKPPdFQ2w0fAnECyZFCJxYBycShcwtKQ rAFC81USAkCRB/mNkJTT5GyCBe4mwDKh0rKXzBS1m1f1dOv/A+L9b/Lz38meeqxnp/h/HH+LK1TT /IRSwaH96BAg/6hAiAT7c6lMJSfAT/WD7yxh0rhOSxlnVSRFTnOGEeZvqKPlPKWF3RpOEoYve98H krphaOUMMjhhSCY5Qg05LSt63hWj75XneQe0oUnkbm7rSGOVo3mFwnV2S1tCeOBDXwwqZ4hEpcOh KNq0ala1Dmc5f7QT99MXjHPM+NTz21gL7UUcZuFmO8zXHPjfL64cQct4umHWac5sCs+YEn1xvLPz 0/kkI6pfNHHPSloHPRYOhCAE4wjOBlkMpSDjpraeykoxxgdYYwSApXLCM2xka4c0AyyyzjJ8bTtf LN2csmqkuaIiBEF7ZwEIhn4jG9S3+oDagVEQDB1cVQaNhpW+kHwjKR0ao2aZQOkMeARECIH4NbCK TNFeESFbwwwywsyxxcWq+gaEFhPKdKNMuUvxLTbELeLuQTJ9lD8KUfBJQpWGM23HKe1Gtk56SDZ1 CwqucqWhlhRxGQeNKpOHAbx0S6UBvtf2JIVywG+03RtkArVi+wJAy2xw/JweZQpbKIqaVBFrKJVT +T/sl/zJuBGlgZ1HJN2klDb00jENo9zERi2au0GIlIAofShCIRAwDU6lRsSa4OFDSOuDRpN0kDTF UoScTFey0vl66Rd0ls4DbGkGwIQGVtohQo0g/od2SEHAHkY02gZBZMAjYDAUJlFzNCDQqhBsELgw xBiTMGqiDC4MjxVuXLkAefLi8IhaUou484UmgcXHB0VSnU6sJdNbMOBrB6CH+iQ5VfQMAwnvOzvp 4CiMP9ddrS5qHie8/b2OQJbo5H7abbjKoKCIc48immQpglpIljcsKNsjRtyj6Q1t0udzFZUzZdEB /tP4AyHEOMjLNA0rSyGnlJsl/I5146n8sn8qsCyH5xT60OT+i3NeRCIF8ID2+9islXYPeE9/Z29i r2L393mvcB7+2Yn4+fp9o9VHLh6o1lgNoAAWIZaZZNG88brjNcb+gIT8B8ABy2ACiBKY3uVREQIl sXuwZ+1+Mc3I54FJ12muy6wnSSvs/YDsKs+uoxDUhYJlFTiLttjhGSzIQCoyHsQgEoCECEATzytz Eek8+W67eNauvB6d4PMVhmNc9ZdJ6TG41Y2X1CGyX38OLcZu9qPgOnXwzAzipIlIGh+8GS/MgAFx gQJMQETGFGQqBRhSh5kJK4gIhWoJKdiwKpJNEOo1CRoxcsJXIYMyhSAYAUpUyETIQIPKqi5cN3Xf k5txRw5bcOa8yzDk2a5vEN8GQVQ5CKKOOTgnZ958Klk/COYnL1ByaufLzk8aSQDb7JJa/hADBRTT R19XTy4X2xTI4L57siXx05/Bf4mHxzpcYq8nxRw7iWhqGmqalzGiZT4HBD0eXBnxYdh183WGmSPO FFWLACxEocQhCsmhwBd9j38Ag5o+fQ3gEgD+dR/QDSfGp3vbR7LNP4RKiSIQQBCmo27cIByT+kEL ZJQYEIiANKovZKYT2yCGQhmzgigaZUqszYG/Ukm6T902djSoc7I4Q7AUhwxXIKE+u8Pb79iDqUKR F7s7JfjQUcxkGshejsUEQ9Klue/G2STtHgFbkDufKhI5YvN9mofNMlKNkhK0q1Rk1oAmjJlaFNUx JiGMxLFEGC5WIUJTSGTIyGwEShmoc+hQ2UDeDXU+3jGDlMNRINCn4ESG7Ij4k6nvPnbBO51gZkwy VjS/6H0eFP03XAeg5OQ+/00NJ+9B3CdZwkPLA5ERGGeEogqYg7R4WehzB4ZIUEmXMgAEiAREDTvU kgEjz4b+kejx9nlB4j2e/1RorfAvLveai3m3ZZ6HybNNXJPNOaqGpCmmx1NJd56Tn8D3xyecD30b t0vNfQw7MppKkRT2AB1CopZDvzxStqqfENFqD3kRsWB4imi6twJ+8f4evjcfsqk3E4lXKoRLUuDH AiOGsseQ2VYqkLJUzFyhVcf0n3g7/4aQ+sCCc3JqheBdSB5INM/6A0U+6HtjYHbW+JpI7DIoz5Qb rESUT/zPBDjG2rj+RSyOyNIc2YfTSpq/6QP0vU+VRTMDsHoE2WOWANEWPxbATrFgpwcjJTnDyFJo HyPIj/ZSmp7MNNU73XD4AIcQJBkDjThDqibWCtQDtD6A+j03ubSu7T8DzN9gofMZ6rXIffXKRe6C HUQHwzdKk+94PElzLggUuk2zEfqj/hVOc1xqP0zhz/WWEMRyDwehLcSIYDUFB2WwXwK964yno971 CeidoDLKszAKJR4Gucgmwh/Hr/z+o8p3uV5RoBPhHi26ToCRtPT3WX2qk9nGmta14IcJ6zeU323U /QE7SDnv+Qezsk/Zd06kaUHw+JSinu5sRKB3uCDFYLrawlVXM3MJmwHy7gPMlhTc/B5DxD5NFQCa iA7AfeSXCFggIg3iVA0Q2LuDhxAwJqELRaW+6EJI0rtHTHd3rcRyOj/dzgAL9guQnJ0bU0DlaAdS rhpD9UJ/i9J2EWg8+g10ipgCpCQ8/V5s3LcqZQtTEwwX/v+9TU9Y9aHyJD6T8Svw5DWwgfBNs48p SWh3g2j+SnbcJGQgbmYB4fKmAA6IQj3GD7DRgfF7DTJJoTiDYaKK42bPJXHstZogXT4Ort1XfBL/ SWfD0HyytX/TWFu9T8hnPr2X15hz3u2TSYgI5wDUP027YB+FncyJN+HgE2BhO27Tu0QQhiGJEKSD EsB0mLET/hM5/ujMPp4VuowabWYv7Hw+LUjXLxtGGVtEYra0Jie84G5O583OcMxqF2LG61p0mDDw otgADjsZyLW/9jHFfHjjdmznJ6rAephOU5DQFLQLSbQ1B4wXEG1UtQDchkkBIHIVnuDfgLwSkiIc XdMkCENVeTK7CwWSg4jL81MoLzbv4fkNHKBwNA7p8GGRD89rMUUiTApigklOwhF0VhMbQWEONhM4 jAmamoHYhMzGsaCKEdOZ6kdawOVXGJDnb40+Yd4mWjFVeB20PiujJydSxlFoWE4PwxvXNErisxAa NECmUYkbdu4x0zsM4hhDJmFyZjsnTXTVA4iExogNTuhsCzbPdvDvRo7OznMi6yYTLthqMCccXJDG TDkYKijORZkdRUdR1i9vKdHCSjNQVxSh3zGIuOUOqSNihZdpQ+UQ+aObKt0+k+djxMYmbOdx2zib iFxbWrVpJpdxcOzth6gfeSbg3ZODEJZ4gRlyZ0sZjquKfBNmoEuIvrHEX1hhhCRpdgURgqwpG3mC SSgNAFzKMiTdQlWnWhCMCiphBYxNV0tqXS4hqbawWVKaRNoA2AIIYICWyCDDN+Qdsr6k22qOzNkL gw4klkvGIBoTs7Ds7b02ODfJzOm67+kCMa7X5sIZDRqlBqtpvDESq466ucQRXcIkBWIXbVI6E3Xd wLFQY4d8qf6ugu/LYfJwHbVV5t+dPFj1y/rOY5xiZXO9NjkxjHKO1EoRx4ZnGkNDNMdnbrHBBnD2 JjoRlTWWl4dMTpbiyKCDTQxlzRfLYwQt5+m+b87ZzhhtAhsiNy0DJocojwPoNBgMMTgzJbMrUnjs zszK3bYlTwg9v3v5TeuH6FhUvTTWEHYhOJn4xhWPbS8ODBfV3mjOJfLZwXxek2E8ouBr1ScgqhMa DonYO0lVmkr9tSNSMadksIHfVNIGcWVus6Bak6WBxqjVmVmznVG2CwQjiOsImfTghZSia8w1iKXE S+N1rJyqqrXENAbAicowR3GTOT3R4OzxAYdMqDwqKqmoqqqiqlVpJYlpJliEppSlKGiZDKEqzchC zvpX+FpCLyOnTmdu+5zghxECSrDWQbHaTxZ2YikhphpZFsoWLSF2AmAmQimAwNwwqGkQywg9GiVo bRiEmxFCBM6d8ot641Urnh4xnVzGsaqlDXgxJVy784I1Q+HIOddYTnWXWWVAYZ8wd1NdD7RMqMxn Y7dZFzvHUtyuU2NP38Z61jXTdyIDvNam53vvBXSM8VNalztnnnWEd1m+3OMNRe60x03ygkIrntM4 4E6Trrl+UEptZmYRtmGOszGEf+PE577jnunp+NhydDOws7qqMLOFqGG0IdcuuIZ1KGNpswP29HFv q3DqKcbprCQnJVBvXY4d8sTRdw5TepQ2Nm3hmjnx1s6DQtsCbl+yBki47cMWx2d2OrxiQ3DGBnib K1nuYljPC8NECPDJuUMk0NccnEQogqX5IGvnoz6f+RlVlJq7PMcKNwOzTp+wueZZyAFbaIpJDeux hiLmkYTNqfGgdeT15MUX1wc6n19Ccsl3SdI64JAbkU69aukmOjXe2lmzm6nFdQ3ZYnysmO0VY+Wh Lib5u7ncCdO0K3fR3RIFy/PeolkEw4GUYfiuxh2ZuTBvpwIjD4NLFjB0JhhxAabnl+x1rUOumkq1 5KcfuiRhibo7TF5zBNZzw2khmTISZdspLRM91yMh+e6wyvVNwJnA4rJfeIdFun6dzlP09vQJpOnr gy65Mszreymxtxbs2hYnYhZ7Ijo2zbAj2aUDpSjr6NZWLohQ9zDN59EtkhysOcmfFAzHi9t0B3TA AWOygeB33jvntDHKGGOdnGd444in8hznkZhssPnGVAeDtjc2duxKMiMWOXmZhuOG2Tmr47O0AgaX c3dORdQJLjCNPwaHeJMUndU3HUtHOHaXnLcOFYl3jzba2XSOtciTITNKbUcYdXKgYYjvBho6OIdq xqSBO2dx22/J3L5xifLVbKJbMaoljK6re7wk1usITIzocphuR9IbQ8hAouNmaAuuo42skK1aPRBe eTDNLSZ0Ilpy2mMw1ZtikcIDCGDCBijRFWI4sBHdgGDbEljBJh35I2qW8riWdbsjDgzuaxc8sWbd 4TBse0PrRxEUrvmvJwTSbIVwzO0HOi1pMOKNCE9mKbrPI0McogEKHflyZcA8IacKnSjHU4rbtjs+ RgXU8MumOmT5Y1jrsOeYITGM3A+351UHDsoCK4qtm7E8wQjDdn7qOXlrbUdi7O2CelB3T9G87eO3 ftHHYy+y6yhIRxMR0Nhuaq55eXg76mS4wOHQA5kRwXvg0GW4Mncfts7IC13zEDZHbnZz2sQg2KTT eq32GrriiNvCq75DwsH1JBs8pROzgqam9x+zXkLSOqNpphnfAoVZGGKqB97zcyGZvtpiDph/CoW+ zCNdHN2EfXxOiVaDXjUbRXBcsy87MWi0o3MdIpqjgg2xNLbq34y46bbuyCnJXG5z462ItN6N5Mb7 PCpjRQz+PA/lIcYYzDZZD8QI4lgsWUDZEbXKfD5TGUM2Pz7tRh2Kly0liK6OIyNZS7ng21JBSE5r 0Yk4OKowXu1W1qXZ10uPJiBgbli241QOnI1PnT+gwwlZSx6Mwx6aTzKp3DK78P35xuTG4yobygva pvGSTVHc29aeF0Yp0MSS5qzrCVD0cLDbK4m1tXSzkgg6xzgwLk4WTmsNHQtbDhNFcXiCGbhShkb8 atkSYT800RLg/qP4rmAq3B1JrzDTRzRweUXdo6aWRfSvwc92xwyGOuIN6VaYdkWT1FRcclZbN35N VK9LPbsefLtaQvPJzAwxm4no7HdrOmHc7EsPfBxGKNQA6DMMJM2/GS9uGkI35K0tum0DR2xsgRnj eTldiDu8E8wssMzZQwNhtaS32dqACFrrTfs3bm9u3dxORFFNTJFFQQQFBFWp9nbsi6CpiqEuIb3j 24R2rv/jjt3dbxjkdzxp+XDXdnnDj6eNuFXCl6qVQjeJSROYwX4/CpOc+Jfl3jNJawRqH2C5jepC 4zqb2t8qTOCBIZAgEIRA5wA0eYxNUp3NnNbzJc45gmuH1M/8W37m3NPsxybZM3SLElvA8dpbkw6n 1h4JQ+jCqg3CbJ3CoM43BOWa0zM3xhY4FfDlFsz9XejSbCZJmhDootaKQ69aizjtA2TUnXYAowUf CjAkjI+jiDh7eEiNmg1qZy5nSVnLOUAwZYQUk3UdOjNCWzRooMYw7ZC4TLjgjN7k20omGvjQ0mNz UPobeIYWpbiiqoRA7qHrjCiBKHZzLhowsXNZ4Y005hk5oBIQmCRDGMLgmdnbPWtQTjTF9YBKjGmi cw/QpzPNHJje8Pw7jp0YMtJjTBGWttKyZVsQ4HHi6ichjAZq0OLUVjGt+BhjJhBbRlKp1ZPCbKC0 QgZToqyNsY2ueWZNqazAtEcsRJuxsIIRJ2FxJ34pRHJq00hYDkHI6xVJuDGNKMU2jfWO+GMU7kYB M7gbLc+sizc7Y57GtFFkMbw7Mw8OBWZjcTYk0g4hDTeyKmUKgsJR1FLvTm46tH/EHsaRRDK8vfJj 7H1dkIZPRDj6TDweY89OGCabuuZYoRcZKaR1DcyXRwzhcpluaQYehFvow6tXczM2S3YSaqOezxGs ZEY2/WHPPFwzxS1Zyn2sF7WH7LmgvUk7GPvcnB2gWr+HnW5Y7XLx4240JrXGIcnHBF4hyX6jHlzh GrgMM8xiww10bFPoKOORt2V3rmUI5M7d9Sm2iM056TgEKBdoCNO7YzBAtPhUmt+1Y7TkkgiMUVVu bBvSaQUXpnsde9JVmcgKCzXImRnhNYVkLU1rkJyZFFZKJJOcsxFjd5GLSMxhNlBZDxcg4LDClBnU 6SrIQ1XxerzVnanHdZWJdwLQ6zzU3RWcKKqNJsLS4rM5JIIxZRKemJYUzM1o7ZN4YRl6WVVkaV7G rLybbLQhxCC44V4Hvi61ismNRpg0nOHeWtYjIUtfhuKdBhBxEOQaROHxPOeYZ31Gze9ccO0IdFC0 rKkmWyLSqHvEMSYRq2d+z8FLqLF6kO8Y3DTKEnJTQkJQGH3W44TotSneLkjVQwqNGR8vKjF8Yca6 xF46geKxfeZt8Z7M4wHLcRO+Eh/vrqA5ao4flDZkYScFPUfAdTC0YUilRMKefsi00H93B1H4sFi7 EQFyEWRpV9A7eCueWgd2ckDt4YDPkXrZJfY3yVhFdYGk9UHn1fv464ZcaGBRUBwYXSpk3WC+SVLk lVHiHh6KJMoob0ZMxooxB3valO5lzeHKBsqzL0mdBe8tfRfiCS8QMMdO3atPTZYoYPIrORMcyUZt nOzu+xw3OZmLJNTWjJTYVoSHlMh3PfiCTsa1FXQgNQDfsQ4ywpynzfT01hgVKuEiyE5HNDqHkiDu BzqThz56OYRtBErmOzgo6uLCxin/2s0YMuQUcucMPGTVVtHS94F6wOewrobBRcLdV5xD8KJad4R6 GdViydsLiKik2A2CnA6azXBhj1455Oa4HB6duFJyLjm6fF4Iw08jzYnVy26MQTBf15eyTZACcysM IK1r3nMP/JHa27mabffWYrt0kSINHF+cY2vLRJkjjJWUp01DSDr57D1QdEl7JXjhFUBem5aUgUEU bKEsipbUtuQYJytFRSFTpQp00S84y+jobgxnjo0c88O7wWz+jllf2m2Gf0b95GWZmGY2CVdFU/b5 /mOH1n+Nv6Bor7IiIEQ/KbfAQrApOkouk1fq51+zVgy16WlOrRt5juO9BqCxqjv+W1ktVdSIkp6Z 3tmmGWgRBpN7K09n81bQEdTL9wu6bB8H1e9rxid25YQE7IcaLCywllKyY4MHuV3RrvAI2lPIEBAV KhM56446hy6PJEBti2IGxFaWY01oza0zGPWkqYzRt2T6G0N1wxi+MRtHYLIQAJaZZ9QTbtwfwuMA NX1MtW1ekHboOrADZMYNWMhPZF6RRT9PY8ZHdPCNkxFkNgwiL0yREQImbssmlSFcMD8+hD/FU+rL dXNHvKChGpXWT3Dl/xZvOr7vepmWB97l4qINBrcXHfiMOQwg1JYSw333T2Rtar0hPGrn6Z5a4w1M 81FuldBnhJzsbGer9GXQyw2kbHLxmJOGVV0xVz1F8lvRahypQxNIqTkVqrzfJeLwtMZV3v2GBm/Q Oi95u9d5it2asGSR3VgyE7FXHEXlZ6QIO3XTli+C9Kz4vFXYYqjtQo1lKk3gSfErq5M+LScBoCuV 2REXmrzN+1hyL8VF96ussuOd6PDnqTYnOjNLdDojrteRU7+cWni/Ztr87OzpGqveNBSL3ziOoi8o dFcSkxDKuNa2o2ARMItOz5GRGRdjhP7eMBm/GNdsxhUudeKy933xPD7QzeO1z2I6nupHy1gjY1F1 XFqq2OFsVVclc08MMcoVrWlwIuW76yta2WNkY1vKFkELXfa86wgMS1QqB+SoJZZTBdCU2wtWFGs5 cGmK7QtL3nKOGdcoZ5TVyxabVXKE3zV4c9zNi6ODquxEr5ALaUh4+R35ngOgahgURWQ3i8OcfMjy MMP+qaYkXO8xg1f3xLuRKmzfD4vZV8SmW7mHcHNjy1FS+C0VGbf8wspi5MclBsqargzoxIOhqvla 43lUwT1FEYJsYN0mbP5rzgzRL5dnkLHFcsTD4MyCC8CClLIdijOiKTQ7flvf8VYpq+KueOnUkKlP c121VcLPY7O/GDBdd3iSzulmaRzlzbvt6vLFIIdMoE+ZE97wdKTrO+eOcFEvdEbds8p9faNMNlx8 2f7kxGzYPj/o787JvOGbIyftEAJgZRQhQtFJ46zAoRiCqWuM4ksYVIVxg2kWQMqEprGdKR6HHG2O yIUDvOciQYsWOlju+BcOZ5cjJrMtf8U9J1yna33qt/VnG2YB+BMYCAx8GABj1TDQdceFhavUOcng G1R6IPYGRnEaow11NZ4EVVJUPlRIlEMEQgaG20hnIddGPlRES2a5mwODYZjGKtIvkYeAkBLBKyIs BJgJIvYxOGG2FkMuAO6F0GYMaKcU0YJMgxt49Gk8eUV41uWsssAmv4pv+CZS1CVljMbAHZvIbJGg Q4Zk8ju7VgiSpKoKIqSnYQhl0gRI6Y5s1BuuOwaB4Ha107pIIqN1Nh5p3HW626ZYRgqhnSmhNyaT cDmqim67nQcOYaE4KAQkaMFeclJ1dREZvRTUAmL1Qk5DJNtlMKGQJA2FkmTc2ECDm5tDz5ASwwww pCLMASghuWQQTkROTkHQHSHE60dCkBzDDsA8AioUUEQQU6Ojl1p0Cz90OsM3YkZL14z7u08O+MLu 78Wi7ZOd7Ni2VM+oICQQB4AFAiJiUA3KqIwPxVY4mEEwQQYxMSZoWFDTERAWCjrU1kMMLVSQSCVB BUgqMMsooIR7emYyKbFCBN0n911MXFy4WMYJkxlMmSUMAo1aSGyPXBDIYo7tOebufpL5HtkRiSAo BICVSESywkDPKDt8iQYAhkC5HUD7JGmlQzBwO0DyCD6g+nZwYmcIsLD4o98P6YTANz/o3YJ6N/gH uEoKpA1aO8IrvzuN4pEQZFQgeIMIT5ji2zeOOuPHI8ew063MnVOJaHbWKAUAt2JejqvUOGzX2g7B QfMBzms5kHpDqLSEGkSwbH/2FhqBgKpIJQKU4f+B6j0yZhHq9j1LyXP3WPEcvGuxNm3iGdv5NvHE QK85iNsc8u8aoB1BT7A58eI8ewV24fIwxs9fPj03YH+n+8QDfFDL9vgwTmFTlQsPmBBAoe9JA851 8zLHieeNA0G6cvfN1xeAPv4GlPtwUodYYlCESnpGerCcmbQNUKLhiNwtkaJaHXzmjFdKaNaQoChV lIBp2Z5A6U0rbJE0S4dxwLCyKzErMzNXHTiMmzhNBliMguMHBPmgcJMGEyoZhHJCzB4EaRYSkR1E SiZVKFVCOCSjvocRS1CuHPbfd8RBIP/iwzJpDqR53CKU2QfMS+dJImHlf4Z7DX7CEW+Jjz04OjvJ RtF+NNcNNJM8UDGYmMypyZG25hpf3l5xUampo1jOe1XPD0RrIv3pzNRL8Zs5xRqTQimN5NyXnHQw xMghmpsNHF6IhZGGNUbxpvaOYMtxNqWIfJUkfqgqO4/cYYUiJOyPhPVNMCPFOSn2zkKjMjNIX10a bEqwEwkdI0JOd4wMuqCZkzQKWlxqplhs8FXnhoLZnE0HLznL4FqjqZaWYYdtEvTMDU0IJqvFkktN WFNwhLosKvmZPFvCoORCYkRDLJmMw/dWaO3E9hcHPWp1hOmw1A7NHPXciWHfqnHFyuWXXVGckTVx lniA0LKmU/KOA7vpW2ajjdaNcUEGXKIe2z3SO1iGGKuMW7LLNp4KRhBxDhKZsIhZIfjOLqpetc0x lhg1lqQ/I7rwo8gwlVnfnmYMnjrJzzxhFuMYazBDSydWRPAzSDp9TyShVg2c2SqNVChx1gMZ4rwz jWnPEo4pYoYcM6xEND0VwIeZBUj19Pl4+I7e4hoQpAQzd4w85SWnCE5OfKaN5RrP0vxtx1HLjuYc Irx4hTB0nI98YBxXiIvcoQPoq2UxDHgjZIUCoa0QTgNBIWSCZkwihBzKLEYRLPchkC2XlemcePWc +PD2enjfl65psQ8IiHk6AeSKEO3d3a9gecPL4Xs1CJf9NHpfUq94uvZgZzkQOQKWALIqWYDIKtCJ AqCni34FN+85Ofn414u7q17O7LVpjJ45BEnPklZ/QI2N8gMSEQZepzaEkkIls5sgfNme9HPugWlI VACqrjx3bLvPy0MILpsfKKadvr/Mdqvj8vbroISEEn30ig7cIssnAEfWw58DfH6tZoqCInWOv95I mxAMEAgmEu0gUIGYdJhg2U5zmuwWW5s+1FLJCFGSEKhLYIhZW+NKYCoJLKFBJDIQSgQjgmzf026d d8d3PnNeiHgdC9vpBzUTXl3cbcgufNzYnMMM+cuoFyAqyKHRnwNee3ZtrPjtXzkEC+jvP+TwXzp8 z3kOxDlRUDwlFClEoUoFUJImFWhEKBoKYliTqFe82UfJ8pscFOEjsn95JQFKPwCDsb+KQMwcEYIh iIve1oATF1gm6HxiceYKBc/6bGBFaVEskFO8jq169mGZj1bLLQHNHND9wQPxkg0TKTBQRJMrQkKx USNLSpE0CsTQJNSRRCBQvxBKPBH6PILIcF64hNZ7qspYDv8zvcxdmr1dyqbREuORtNSH1BAX2yco ddn0Rd33zO9jBZ2tpiNgauGUbMyIMxg5HHEwZhmMykmVtk91StRmK3UGkZURojgJvTrqbRLKU0Yo YYwpIFyWgxBhqphiJVF0sQpllGBaNguWBXftRzgdtz2qKPkZHzfUVJMABCX6OY+KA8UgO0EvCRDn /8tm1hBnB1goaMU/uW5TfmqqYUhzg13u1IFzvD2V/2tvIK99+UrrA1imxXuTsLPcsYhB5f38wlAj +QH6kD9J/av272MbhFBIaNxTV2qRhCQFHWWEVSOUX9jJiadCYywC0wcVmIFEkrjKxiCypUwzMrIV IvCSoqiEIbIbOwbbylbxvBhGWVEViQP3fu/Phtzgcwerkp90aIrywYXqElUC0xVvxMJQwK0UKWn/ lYMKWZbqgaitBIBAJCfaekNO2yj8yyAahCEBT9pETufIeEtZhA3BsTVjQUaVTX3kcLaR9UyiJXGw ArSjARGQNl4BdRZ3kFC4iHsIqOQCcye+SIHrLQ9JIk66cQMAiomJCBbk7djqk7SYlz5NGnAYgQqk kg4ppNImJZBoITCA3IdiNEJDMAwhtIbOOrZj5IXVOTU1oswYFVraNluM080PT1gRD1hXVpOsWZEq Y7j1paEIeliigMjhW3DTni2zOMoQhzM3VjGSGmYYoSHCTJpDJRzMISaYpmejYWjMl5kpioigctNb xwa22kgFLIkqQKkKIFLGbhpZNgnZSAhlSH514m/Yb4o5i2Jkr9EoG0GQIbB1G8SattI9UOX4K924 lKegPigps3Kazqt5/Hho8lrHKHdQKnpE/lHJ5Pzc7NWhuZPR25kgFV+Q1A0DR1Mv/P1pQjSjG4fg hTfX8+YAzb7lDV/Q56h7dvFGVdCJPMIGPuS+cQOwkCBtnmsqvMp/hBeodMv+KpTgnGtmgZAP94qv iiiIikIkR+FD8Z1FAFNIUpQUKRKksTJSSEIFFKyPKsQMD4HHxq3XvuxHMtFhEYeBVX1wInY/IpDs HAHXmAijcyMjLGAkk+t4wlO6kWQqbQEMvLVdrR/M/RvEwpENDDuw2wXJhxvKqkk9VEE2baoYoh3V ZVx54WMUw3qzlmuA2ZggbAhz1tMHmRDbKJllGOJkBkRiXFGTk3IcbFZMthRKJUm2iQ2mUcK4DTMs pmhcwVEHJhImwv/un0PxFMwR27cguxn/eLuCKe/3VH2Bv61FGQJD5XVaj6ZciYTYjEf5AYDDYsqJ 9kHwkPpKUwTMIUwGGqNQgLIQDmPignl7vFhQD3WElmanzMu0uJGMrFYW6C2ZRK2UoTuJISGiCTIE +9ACkpFVP8Ovrfiwqy1YFzUMD2qcEOX7dcDbHyhxlNoF2I0EL3IEmn3Z2vfijFd+MzSHh2naCvzE QVXpPKA7CK7NwNAFoguqAu4wdTH4Q8RrDccpyE5xLgLPaCDyMDR8aRVnhJH6/y3N6GlsSjTYhltE dUYMTVB+D3zg2y4O49gsrLw2jED+nBiYxhyQdqkvhqaaFZwuJorkofOQJCtcUdR9xGhyWJ9BEadS 7UwlwblUDpZmKJEfPhPWUlCxEGyZdJ1coHqH8/Z53CBXWBmHzv4gm0koEDS134OqIgxKJGL8SfmR IjQ8XgIg0bafG7KrKYqIxk9zr54lDDPZAl2aqd0axsv0zoGtlb22225xOpzU0sfsTXOFRtFJJNVQ YQ8ymZjJUEVI5Ca1humkthfmiGRinhMJIl3L3cbOGyTJjMGWGhawXZiiHJckxxknscxjbiHQkLNj QLSRTu7uyS5XELDdEA61rk6rOeKic4EzwzNxbxiTklnkzxCMKY41FJbd87MNDnTOhzva4abut1c6 m2VweJpLaaSFuED92edHOOlPa4t27LqR11a4BcsWdijVVTdtaH4J0PJPjtOnvJDs+NhOQwg4YIuC EQknU53xMUa3ZZIwT29ZRmGsGcMQiFEIdMuHIINQ1pYdLjqNnWmLnlfJYekLJpnp7w5HONJZyzon wQIdGOxtDdCKu1GcvBIggTaFwpLBIyGqbYc7okMiVktEoisEctYIrqmso6iomx0ZvVJg7pdOjMW3 tnCkHo1A8l4KBnIgZ9YNLXAkUMUhikzst3rLSmsq4EEykkkswllcVRYS5jCHkQCGId5t4ILGKCWZ Q+adUqIY73CQic6rQsNZhZSONGJ0u6ytMm0xLkpQ29juoRDLKQmyOVcYY2OMKQkG5hhnCFigw4MG Yu8xpiDkiQtZ49xBrMp14W+Jojc06QGoOMw208mHolxcGpV9HXWWNBeGID9ZY4ZGYYupzUuQqFTS NYMehIGLXkigYBFAhcKYLSheUawsbB4bxVajZEMBARbLxJt57oTRZW9MNzVBRjGJkGFzQsVsUHkE U2bTfBxCMgRTsEDRGkHA3NYawoEwKax37NvJRoloSUpMKFnrP43kPWPXCnTuOvdCTTKi9WBiI7QF Uh5QWMIaBxQZMjRBSAx51XelCmrh69/OMv2di1lHGKLEnzw/KAe8icE2+Cys9wic9UnkRJPkDSXV taI5fQEhZCTUV+gEIaPyNgAfcFB0ljmOBy3Jx72UmPF2W1IfRBCRkE//ERO6B18J4HA7KJeqgqUo IqaapGLrNhZsBZK2t9wXcO6qu5w340Nv3w1LsODICa0KvRnssinXzUdAHTX2d2pG92gorkSA/V/j 1dV88nd6OChaHgOu0loeiNy+LQkh2XtDY/gzaowE1CQuBmX66BjNGSMDcPeBNiBV85rkjwJNuMuE zEwSA9AJVAwPjI4gWid0RzA0Jh/q/aHAQwDdA1h1nNbp+sfQpkdv8DH7Jh1+FRuZZJ3fWVjeusHp WWvVtOiij9GcvtJ0+HzeDPH7NuogfOcw1powWuFKFbjusQ6yh028xwpWn3g0OudwznpQ7eDrx3b8 mcJpMakrEZ2tS2CQzCDZjnEx1Zo65uzNN+LMZ4Hbadn2s3puKXqLVjUi1MGne4W0FBUBVwaFgGKK VxhDc+eWxGTGHfCrQP0cM6XHLXXTjltwgYqJ2LT2YQat/Bic7zD45mUyoWgrpQj88GO2S3uuyH1Y E+HnORxwUnJ0bPMd8Njnb7VL9x1N5tKr3161+fNOVndVEXLeDGdsbWV0OxxIq57uYSFt0nL10PWq 0YYRVSh5dBEjj4x6TIDvE4hRmMrLRupV4w8LVjrGeV6Mu+MYjKh1xum5QMIGZGeBhjbNSawkrvox CoEMDrvKy2HRLbG2ERAzKFMN0ODlKbRhoQR7Xp0OfZ462TYYFWO1CslMhcXwEKwxBgIB2lKB2hA8 cACfF7PYfO2wJ9SwJMh4gvKrigHjIaVDW1IsBj9RUr1Gkcf6vQGC7kkbpZDyD0MhRE52FGszECks EIoEEUkQF3i/jYVp0aQ3LEkbFz9949TLJGXonCadhwtnt22fLiu0RMMQxCqISHo+E9PJlaqIKscs Ogd4cldXIOtktVEXAWOf1pITJw5GEdFeWKENx05npmkA80NLsL3W4DwigUqqmVSikKAH6Q9fQ7g2 Z8BawIHG2JymQqSSUTRZBQihK1oxSVhQFFC2sbQQX9A1GRPyjVIJWWCgoIoYylzHLSlPddOWGlyl iDmVIIUqxUGpiTMFRO+USEROJW/zY7W1YQ5hDBkRMttLICVlEYzx1cgwBP8pSSqqHShP1GU4FGKA oDFqFY8tZFJBCf0yJiSEwhDJBJEzIxDTUSjB1RgTBM5D1aDBktSZBBUgQlKykMMhRIskoyMikRMg xQMkDAgQSiQoHsS7wawDxqJo2NAxj1aUB0aBqqRhe0+INDoQ9YBwHoSgd2pE5KMmthN4aDaxVFYo YpiQiKqqmiUlGJgqihiKBgmr2IGFpVNaYooiCp4EGew66xQ1VFNBVMQFUVFFu+wCxXTJ8Y4hiiTJ DVERQSUjEUVUkk1BTIRRRVLMUlQARLDAtApBCpJB7jbTqCIaRopDk5hBKkodzZVVVUVRUD2e8ml0 L4wmWsRyBZUmeBhiJVRVWICCeoJEIhQBpVUiREiRCFqZmqRscEIN0fn312B7XHUHcGgDpy5dWKHi WHvY3omeZqQi9eG4In5mK9PeICk+eUHxEX1QbpD8vN8Jbfmae+4nBTnybG9A77BbCpMcIBAigxBI KHvYWy1JVGSoAIhtSwhIAlIifMSGAbS4RDJMgRMSMIzowQTET2K++PlDWEvAYRN0kVeQPkgR6pRc lBiRO6A1Ew0lKUnI2DCFkRP3hRIEqAsLvjnbB70TrnWHd3iFtwQ6w/Gvdb9MLQGDIhvHejXIUUBs mJIYfvNMCd2Ewto6/Z+p5+iq6LuIURrF4AJGZDYZ7xd2wEfbDzvoO7dUNfjrE8I0TC8YGlP2QieB POHeO68lQui/GScpo0QPGQ8kPQqCufVhpkPvRroDy7+/mhsD6IVoQKiAIKhVYiZLjiCYfVRRYrUk EkEMGgfzUR+8+LDdN1Dv0xInJH09jK6fFlYqGYshoM3nlRTxT+cEohFlQrSoPihXITgQp8r/kVD5 jkGHz6Xt6yz7Za/yy98S0vPeS+efbnm+wBYQAdcfmiQikESZSmHhmMY9HSYHIzKQBcADQxT1yi6o YYIgmIGkSlTCQHJVdSIYQIIzAI8ePK+sljgcZmCheIA/HEDVAAGysEENvjQBuhhm/samM5x50nPi 3yQIwUII+r8Ppedx9mFeAm1hx3ZtPDY7iojiPzf3YxNgKLiMB19DzZrDeqCeJHE7fEz7ScBbShjM UiylVl81yy4YVsIFGKQtFBQYhC1gZXdgvZIO9EhMbZiGACaSxqoYFYDGkozsapUZqKAmG7aSTWYS ThjkQhDkmgrCYlIUN8oCyRYoCzZDTshXQkWYCk2YogbyLhAW2O9tCOo1Dm2EHGwTDGgjzQEDTCBh rDKKNIcRJCRviv7c8v+1zo8hjO9S74xW/ut8BoTyRqE4+p6iUWkD/3LEe0vPnvEaXaEYZoGbt9b8 YkU3QbiQE5Q8/Zs1GCtTANR4dBh9XSnLWG/gy0i5+atsbOXD5YQ4KDO5ZOS/0P9OVBURgInPa89Z ORroYW+96Z2HFaYQ8MpTThU9PWnHIcvKLm4qempr6RWDBggl0YFWsIeDB9ZqmYSiVk0+GD/u1JOM nIwixael2eSIrKExtrlyNkq1dY2pgWwIbT1I3wanDS2OwGBpI7MfKNFrv5oFY/XY1kCJmuP6LXP2 75bF9JUDVeLX0xz3ul2X+R4jUmMTZbCSNDRVCAzItWz123bjP34k2VRDsgAl52PbYYhTegLMYZlD EMMpC24jNVpQFVgzD2E93H1nr/x6cgG+mqD4UQwLhvgzzzUtIt5NF0AUAJIBPXL3j9w+iMLIJjm7 o63I750/x/xEPD/L+X8tUjMP82Z/jF4lNcKj3TlEkl8KfMDJMihKF9omWc7Kjx/dG18aLnlWa2s9 8XSgYPk0Q+iWdGInBdI6f04NiU1vjeEarZcsWSKjA20TR/zKDWOOQ0wwddxYSMs9cHlU0l7QzwJx rnUFVwOqRNLYuCYYV12QZZOGBaNjBxzng6c4GjimGD5IiIESF+KSaUFEqrO2P+gIFD6JFSgfaU8c Zs+pXS64Npdq0pnYPi0UzCx0uk5JeNxF1L4YOoZs2WLxkuLsH1xljk1JDEZQKIgQ2V8sp1Fwdcc9 RjTc4U9t767Bzw4wGu3aDpjsc6wHNXV2EVrXGI58ckvNcad+k1Tzo62jtheMLtjx4pauoDUc696O LqYYHJr5b0ySV4Nng54jiTgJXYRbBwsTGOXIR0vOsUzYTxqjmxdJ44hPvAwtfG5drg9QiMmjJGGG JcsPvATF1RgoUyyNHX1WGEEbR3R0AYAD0r0+l+Ys1NI+zeV5qeNy906a/H9epg+YH4Tg4s5+yrQc I5D+Hr5FJEhKmjh+OK0hHyxfg4CMEeUR4CMiBAQoPDUdnDorx4xV74ceLoN036Oovai9Ll6GHUA+ znJop+oBBZQk6NkqwWZTBz4jp2fiBsJ2HUoGoA+2FirnJ+UAS+86gz1kL266Co5ebFkog7ERTl4B 3dHV2Hbe1+n1atOUOjA5yVqUoruQNqnhu6EXnkF9IGdZ3Z7XfbiNWg6XQ07Q/XYGdnnDXSwhR8Xw 1ZdcI57IB0csBOTo1XDbwG8L3AaABO/ZexFuEL3yVUCsj8HCPnksQiblqgV1bOdo6t2k5Ztd3YEr K7oW+gAxeK1CTdWW4Rdg+o4XqLi3q2LpS+HC7wbRi2pDoccgiIEQLTgBlSMEfV065IiO2jpXjtoA pg9nUbQUOiRiKjACAhETsu51cUuQHdlqD0BUlBQRF2QQYWfFstnD0oOUbMByITZtEpnbuwwR15UH HhOqHQAaIgDmKgJIEg0B2cxq387kVxtt18cZCopznLtQRCyEFXVDcXpUDpQ4dBYMKD6YIRDQJMEz SRRABUQCNIaJkdcPV2JX4T58HaeMb727UpStQu5tQq+fwCY71V4qkecs1eiRTHouGREByIAyORPj +8ovtDVROIKT3bqdUzhY/x3p8SgXNQQ9xHn+X5c9drDLy2PVwB9LIkFklKPJFgIhK+ImyGnnnGZz 0CAw5gk2UKA+15/gOQUHUQdRwnxhrY0sEx4RgQwMwYY4wOgyXDMkwxOM+udl9D6XBPw/1WFblgTl JLLKQP0pOONTEDZsGCQxzGxxyGCHqwR/rf8swPW9QqKbPlSTthHw8MY+zHrndV9yJEFBB7NKUkkD Xm7+3bRv/wtVQ8UrollqYGMFGDea0lISQUwajFZIhph92vcpYBh9prtbq7ci5JDnKQDiUvSlwSXt NKFXg15P9/8CHq0L/BkVGNYywqKYUoUpYgeJE/xyIfo/dFb0f4OygH78701i+Hv0YNdPXEkMCcwq KlQFFgHmdI5R7Vc2Ab7ygA7B8F7vGebSC0POp2TDeci020JSuKqIZUCGSQkmkKAwigwJxy8RgsOU yoYz4SBCCnuVhUxNt8wMdR2kpQQYdYl1SeT1lhyzAz4BT/HDYvg1IWLPrBq53721ffZOl54BQyDe SjJtSRGMj9CInaKoPkkUup2ueQyMCIP/naw499cnZ5awMZfXlPWPb5rCQPNcUi/esMPxrl8GLu0m SCx6tragKiGrVXawgFlkYqfMz+v/d9ZUuhwENDNsjRRAysrFKXRvS8DZwcDMhrZKxiqYMIfVNVR8 aVn4jVPZT6fOqKKjFWojYdSPfHcEQyafAxDSqlpMzmEPOhDbjbAw7ny5rycNbKMoIYgGHwEsezox AeQA2ROchS9sDhYHwU4+TsM8pNCtMk6gyU4MizA5mbbq+VmQRhttQBSwYeIwhnWgSKJ3R0kMhIkN pyQpKFGgaQIkKFXYPr4dewHsUbeoD2Vxzx69+aKZqDBTNUqFE8/x2TwMv3WSHBhWBDZF78DZISxj TIoJLwimTtwK2Toy+R3bsUkc4PCV4MDMr5ds5md+jOY+bQnFoGRgHNqp56pr72lcJuiNKYSD1VUz oqa+WYJqzHTD3iaZY8KQATvZIBfaeawdIc6LSHhV8Rvs1pAWQUCtM7yDbKgc2VdcYlhzBKoxJ9Q6 l9+TxSCI/jb7yw/7UCfmiQJiooeQUs4ODWaZNB1jsC9Df05uBOjYYA1hijuSMTVK4kUqkrGQVQjq RHQrCwrBqRrCTIIksJwKKAOEBQWHusTvy5/xzw3oCwUF1bDGosihvJsExBT69sT6P146t6g2kqgr Cgc8siOOiAy9nYIe0ED34BvCboSrvmBz6MoqgHsD1h+g/cyABEiEjmJaB74lEVM7fSAGgdUwv45h kigYundEU0NvelH0cX1ppR54c/izrnKm2Z96N/BL3NG06glxVzdEvX1OSdr/8As8fnaWDsdmcDui +cwRC3DrJPBkyYXyxWuOkRX5DQRi8Wvqf7Wc7w6wXqzkXSWzAyxv2G7u6A+QlrvPyYOYYHNgNxJ8 nZnXUeeQP0EKnmgraT7kqUBoh+aEKaTUaib0Hp41ERFAQ/y5mZlMbCnxwqn6hAoT+YOo/R9osQU+ ClBHJ8qA/l6gu6gtdC4QTl+L6Pd9e0RfMttIHC2SU3oDCMXEJRGeyade9TWoAHBSJHDSxn1OtfjG EfQS0RLkTADFxU3GUhzc34wxOLnng4sfeTJ9Ag23A2PmE4hjOUQXUuyenGG2p2RgRuIRKHVAmCSu urAcnUhkppITeeyA2gTU0JvCW85FsQUIbWQ6zCl4DQGxiKbSOSbSibQORBIZCjvAGiRG330/t1ny 28smIbu6Yw9hq7c9sgpN0jpsQ/xWQm/VLBVThBW6SZBRwVZBnjiOKNJe/bs3gvPiVbb3TGhTCBGp +Q9Dk9i3JDRbK354VZJ5chNyI66p6YjMnOtVaJmljC7ig2TXXS61bpqZnGjT4e3Wo+d0ViJMtKNs kamCPXGxC35Q4oRDQ0BxGC8JqdnR3vu444Jufz3QbCWRjOvMLkIhgQoHMIJVOzEmvGZuk+UNGAmE hofHNIqYfg7/if2lZadkNa/VotbfiO3ilZUmDlVeMCo6T3apiMmiwxmIeLoTm4OJwyugYlq7Ibpe VxNfkzODVW5ZiyMYJglGjSaBinTFGOJE5iuKLvntONDgEi5FVBB56oj8R3BgfmI/9g61xOYBxxCU kgZGLrjUoSUGspoaLcC/UeYn6cygcCJBC3fA1xNT24PKD3Dyh+tRT0bQ6Jzw0NXKX3HmjDBUOF/j vusaknrSlMfR8ve+LetZ9tNcZGZpdxlUqB5HB7jBkNIHjgyCAAglCUgS5xeD2mtjx+ReA5twUioi 2BtC5muY2QNbrXLK5ZuFF70Ngy+f41JBhqV3IcoZYiSEugXE+Nc9u0APgnx8SUnpYmsKqVSO2BTU F9dmazILbEVLaAMrT9zUNQDcbcp+TjwPQmSqW4hz+e25S10a/gEDg+hBYv1J9LifsxLQS2lGX6/h APZOgbfgxRyCo/DPE/HiWd3uD2HOapJgpDwnIoMxzJyVssLMTJQoJUqYPTrSoVQe2Voqhwfg+5oO EARARKMJIqpH1y5ue/2AnbIdYHriVPlrsv+IYPTrNRriNMNsA28KCoOGEqUhuNBqMRGJEmELSOYA 51CykPoMcY6o8ZlhTg/ojwgfejZqK0XGHBZD45Yt1qcmCemgQ4QnGyalYJRRSSlYhVEYDEVTzUzK kFEiIyJ/85cRkWIiMGillbbNWtSWiwyMm5qcfJrhG0yQkqnIZ2r+ItXACSAku4+aOIXtrIwhp9XM A00G17qmoYUOviVaQkIdRqGvBSVpHG6gwMIXKGBdYEWAdxO0uvbZ4kWf6XZhWcbHJJVcIGx4yyGH 8CWaSc8U34mAaGARv2WguXjAe/+EKaZ6VP0hH+NUwmdXaSL7r2tZjO1ozX/bkxA7KShIJHZDh8O0 Hod3cdxYPFrObYQowLsKLhyJbnvJHOiwAdrCrhSK1CU0IlZPZX0w/SZJkZQS9k9rsn/ieM9ZahYE DykaUFA8AIf8EQQMxTxXuLzc1uqdIEB1nSWFBsIHIDZH0pTmhI2EV+JhY9pFT2QDoqwmV/BVAkEi n8x+g+a/YiGH1mZVHL8VmQVi/p3Mn18pQAwl4xcgk/EaMwjGVpviujtKWwkhJ+/EGm31hDiJRDY7 If8RyMPeeBxm6w6FlLadTDJp4YfpTO6l6/XYvLwMkZiZRGEHoSW6CU+D7Gu0A+cIB4kCACnQXIQN tVb8poOXwWVMGcIyQr+2BB6DaIueEfhWBxGJ9SkOXuru4APP6A0gxK0Fn5g0ZtEizRs2a3+ESP3A w9XjfkfU8EMz0QNI8EcFJ4kYGPUe0IQudGOHNKunliEoTtmwzQxEksG4PWm5ISL4MPbkck2TWyA/ CA+EyL2bGYBA0VAxAWihBKQHZVF/DwRPSzMqwQHzhBjMyEEAdA71xNH47LLWjrH15qTJHwhdxJag /mcAN1fesGCGRMQLSE9BCAzDcmEytaY0xVP3kDMUqBYiREBGMUK7sNwNQoa2xNQ0kASOwODgQ0Mk X8cun+4ZUB00G0lEP32Y4ivDmXHEwhO4e4Q1qsZzMJKqNSWyookzqYFZ2BqIw7q002ukCg261LEG uZWuQaWMUTAIhh+1qpMttQ2sYuIEDWilQsgu9mMFUa44ooqqIo0gfnSU1qgqixRFHIQcSuDcsxDf lRq2bOrRbaQUqHJxicpSCHBBEbDaIkLbrz/b/2T/Jh+Z8dpZzTGJj2geNK2Em5HqPAhrOwYm3giC Y7/6FJqLza7+1QUNEDxENdWHppWkkQ1GQBRgiNSFaIYJDBkm3Kw4HowOBiwN5LLIftdfLN3behzQ 0IdRhFJKAlTkRYjAZcsCrrMAKiCHa+TNSIaQsYiccoUt1oDQatFXQyBhI7JZZERtKaQINiCIiYJP IpLGDgaZVJI4bpwNGb4YQTt27YawSdf/5ZLaTSbpyHga+Tgk2FOS9mKP2f9DFThwMCqHhhjyMMeu MjTJuJtDRSU0tFOKDthEYOxUKERvrFdKZJgbDCcr2/T9r778ZodJEaoVIMoVKoiTHIYKILFqWoWA fXibKTtBOsTJdocGQxlwKhzQZZnnPX7cA59ZUuHBPsBhBT/MFz6jooBgBpcVPrWVTnzmJKZrc++G R/eJ8AP2n6tiMHRytxBor82dgqfYacQfdFIA2SlSvfERtexZV/XdTbFuQCim5IpFkiiOTMlyRBia wtjmIA64EzK2QNDRGCxkBKNKFBciRwbdwrXMUuiSkxA7ElQOAEm4FLGCWCGas8hDeJDTYxQmhCbr 0ttWLD9hE9RMEhdoIjEgQO0z7+RXVx+2nBpORWkpbOxkPVqyaQRAwZgncoyEKzT3kJxZA0eFJUU3 WM3LWVQSsGWA0uXFKrYxxus31tsG2TARNoCyGsBoyi0sxyIYWUSykTDRjARLAFINxIRiGCKsggAk zSlQyxCI44gY29LAtMKUgOOBSoxWC1EpVEUwbijLqC3YXSAAHhYiZ5FUo4TntS/LCJguBtoRIggb ICjGgYRpT9eCOTWFEaoKjhHKMsNhGMVRO3U6anYICO7ugnjSFFSa3vezOTToyVCdqcWzvEMExklS G6Ly1KVu94eQGWIyHIkOEYXbIdUct8eUCdGC4YnAkHgSgXDrnTTIQI0TmCx4MDQaXcQI/jKJZ9W4 6KmwoUDpqg4gouqErDr0swmRsREkeIMxRIN8DcHRKGSv+fX5UF+kB+zZrf3H9Y98P0vXq+0DWH9Y H6A/RbhyHHfRR7Dv0mraAyaYiLyyag1UuT7LU7tHA0FGIhqVqZHkVyCYH17p876DS6aUHfO2kDsQ ETWktYO3Kpg2kRtSNI0EVQBoPAwUaZ8Bhg0C0QBmIpjCOmRNBIGoB0joVFJ32EJNbKOu7ZCgOwJU 0B/fKMDMwGAfu/PoURDaQBmRIJaCGSKhFggYZiUghSSUkAgiAgqVlaaIJAIVAiaQbk4LqSlRuRhA ugG8T668U4VVWtvT0uGzZ9X3qIMM5PJTWn7+C4QwcjoftZpi/A0cuuTPInOZSRKAIkFiEYg/uwQy oCg92wPZCLB3n+Q8BT0joMEPaEdXt+Q61R/9JQpAopEUoUKYkKBBqlChApWgRpCIiFSJBoWhiUZJ KEGhBoEKWIGiJUpqhqhYhSIRiClWgoSKCQKVZgKQGgKBHiyGQLQFAdYAbh2fVSFGf/QZefnZVMk1 C6pUwb+IfhQWkfy6wTrAwCUIhKUEiVUHtRSIiCJki16OHT060jNthDhkyCiwRpmCP0Id5r9mH5jf hVwH46WG/Jn7GvvjWn9hDTNrE1ByQCcsizmU0sRrDNTKVaJocuxQwNkJWREijWWGzMepqikG4VEc oybG9CfymiwJzicQUPaQVETMBUXvTklRU0UQVWQRYE5CYXewjMHsKkiCIVMYlvvG2cMicBzJgxGI hgPljHZSTHb4zF8pEwtxGz2tSGrMQzsnrMkU8BoD7whl2ZC7beQecDhtFKFPoMav8d0shshPso+y 1agUTgnp1KlSaol4Y5U6NGfhdhE0GhmmQpCmYglmmomqKlCmCApCZAfo05Zv7PDtRHYRJoh1AbAb KRFLCdBZBps9bXzWUdvc0KmSgaj+wKs5opW+yRQYzzWbqB569/AdBLqIbQvvjROE2NyIa+vi7LxR LscYiUlmLMJgxSqZO05mYnR+RtCJAhBrqFPSgxAQmJrEdEMQiUjQPPyFt3cFBzeMoVGAHIbyFD3z WAnKjoCpcQLUiJ3KSASCQ+IxJqUerNROoVADn+npMnY5B5v+dkdDPzCJNLI84h0ZIJ0wJw7lQ/M5 2TApASkQERHaNM9MpwfxpgwWa0ciwPKQkL7BffNYI+g34+F1zVL8HG5r/4nddb2FtigLx9pAkhGZ IB/0SBs7DDuvowNzySmlWlZAPHjONQ6O/wKQaZ+qYPJOuj7pbIEYPpIWD0qJlCDRV4FWs2P/fXx0 cj4IfcR0DmUaiyLDV/ZTnYCkgFV9Ge4esnlsl4KVpvuwUQzj28tAjqRV5WkMbaEvbQoLgWXvvLei xM1dbwU570K+EGMnCiY4ODkWNRJHbjSonhjNWCMCODCtBahCG0CkDyT+LzX0b/pmVOJzua/qR7cN b+azEDArCzvfQmd7rIbGGk1qaAehSwFVWJqlFG2fmb6WMVNm3yzjo5qod41bP+OexLhAN6/L5qva dNGql2qyiEwzBPV856QPH/Kq+aX1QtKqkpWwCPFNPhp+ED84awf8jdl4hf94Px91QPMIey0VSL45 ZgIiSCIpIU+GY0vsnzRq82NG0PDZQfOiMVBEEAH+uQP7ftRfWBw92KNCo4yPZAfmA3ID85kQNW0C 6Dhbt6AA8Q0J5Bh5ULuaRHK/5v0CJjywiEIp0jESgkbHzjA74kBvdu2G0tcAV/XbyIggbnrJC/Yi CBGE0MlVwR+FGUf/SQKAoV4gi0bb8T4/xRwtzgayAmCCpYhVZpyTCGszBM9+w47ZFG2LrRkRCSim 2zrDQBSgTAkVtgIOlCRpBUo4ajbQms4hpNG4GImxwTZsM0gobSrMgUoIAhFYwFamIwR44ciOF4hd DELFhdBpIhpVbAz+kjfkA5qcLggNjq1HItFPAC544qP0XLqm8VWoIkETMNTtVNnYOxyLawJegNd6 LwHwaeo6e8eTDByD5/yze03UfZVF/k/4OJqqko4KP+Hb4B3FhIHeJmk+k0KPoUR/ASoZAo+eQyBI hGmgGIk9mGBKupwgrMwKRoUUgJcLEIYgSloYgQiWYR8u4YirrkW9zPbsgnrgcXA9CWAac8/fip/v kFQORQGVEH8N2of0Qcgcohp39djdHeGYoaEUamFpiCiqSYYkClZVgYqJogGKgSJRChKGSmShQiBT j6yPnz7fXtpPnyIRwiqUWmkKPokP6LR5/RBSUHeAvaIkP2r1iexPs2VoaoYgaNnMIkBIqUpMZyoF JIW+Lh9PrNyRj3vpD3tkDUjS1R8pY5vmgoCYOE57Dh7H3SRJSFKKESpMqNKjQFUqQQCewFXyT8Q4 B+OUvXYLktZZGNtYH7/4Fn7LX/Fmadmfqagm9kOBom7oVhgwG2UQBTX/ZLDhNbNLh/s0LDbWM21s ldsGQxwfGyBp9E1NuBupwbIwjBcmv206aIcOMsgSRVnDl05i2ixjG0VQuFAqLDHHWtYoD/LHIhsY 21rrajQ7yS61wFbzeE1CdBkpCbdrDckkDMuPDe6jGkb1Fp7KeRJcZqGSzTWZeWImIkTS1OSOOLjW KLwobAHeeMZy42BI1IzeiAbwJy1GCiMdWpEZyeOGhvvvgvCpsCJZQcie4TI2skHZI5FCj4ymM26j TS6bvHo7O0GzsOGUOBiVvA6JCZIRyhLaTmIGrxk2NCLKFUxsNGUeR6i1hAbMrytlGN9dQNGiauVV satgNOD6uGapGNUijZOAqwu0qEeyDWy2rLl06GMdJKxGCwFBZVS8Uoq5S2m22GQalB1Qr0wuRpbN nBwEhyQuWKKraIjXfMeWqYOUKGFslaaGgu7DQoM0lWwy6ZpvTUx1zrFGIbe97xC4ehgaEzNksyPC uOFbzdIGjfFAyTeJiRYuJJVTdDBNsBERKGlFZrawLmsplsVkqTYywRnNpqkZaUZ/hlOOZngbwYQu TIq9rzeKO8PHYoF7ZUY7tljQECbVAxU0mIm0G8A8YFPf+b4+wvoBylO0APYQFVO5TwxIIlUpvYV3 pRoiJm7u51BkImTsag6OobXN/f+dh9RYlUEBJYGY/xf2Out/9byDDkhgHYoMrEKpKjTEEQdeIoZK AxCJSpgEq4YjK9YgdE0SFKrQABRFQ1d7sdS/L0wTCrDmoPqMxW5H/r9RcyH9k3RPtOhopSipRKCi pS0WYmYxqwwjIEsZArIVY0Ayg4EC/qSA+f7WApCJDw7SggKECiIQYAkQoEgE7pJlTIblg5DQWsQy mXUBkH0sCmwsoAmiQQHIRmEIQiWmWAjYkQXibgQGBuyCBoSAtBA9pu4ReKflJA0NIOrulxa3ay8p Pxkimgljs20amwwMnAAf3AV8bcqprVTjBSlGyN+RTpIv1HWPnPlstvvpz7RPGB3lzXF65iUA/ilA pAX/f74QgUonY/b8AH8tqRKKtxMmIDsYnjiAWB4OT+p/N5x57oHRJARsAX9yqarzm4JGYlipIkLo v0bvpl9T48PbrYm6zPLrCVF2g5YUKcn9o1NH5OZkO2dKED6UiIvCekFCGRwlLMZgKNZkSkSi6hx1 dZpyEkgiIlKA3kRGwD8rDbxDSIpqkX5+FO/iZB0FR3IPT4o8Y4rjlonAXh+t4isZp69xPFCBAeoT auev0hqzXUUmRSk86pRCqpBKKJqsMzFxgS3E2MKgn1UXyCc3uAL8J9mfYoIhP8BB/MAwh/Pd/0Zx y5GjTMIsxqwO8Oz+Ow/GxpQRL8LWDmsyTUSAqQHxcDiSfpLALwzEN7COsxT6pdFUrqDJD2yxBj7r dG4pTEManIUDSLz+0kv5nWPRdawjGPFgajAPpuqFDTQWF8ZDoP1EKfm1uaP0baNg4m5kWT1BAfEW HuUZ71+wSwQ+NC9DiJ8BKBpREaWkiqIGlaKYyimDFq2whRMQPgXCiGMLATaUVMiikoBKCgHYlXCC NE19SSnKUyXJy1tgq8AqtYYSNDShlsLDplDZQPjgUMUXCxQ16XLDNKpDOgIwMG59sQ59U66cS8JD 0+nUaqCJYsEBLVlMia37LDhq3ezfGNmMGUYEUhGmMUaKFZLatGwo6ImQMliAaHJRglMpircJmQoR gyP6NAQSP1feguen5zr2/K/L6RN9ww8Myu3x4ZCGMCcMgcMCMb91kx0/FEkFGnIctIzP1tU6yQ05 Gb3O29U1sIZoH4G9Wk2jFxY8CKXzWoeGVlajCqyGmrzEsxFsrpxmnzwYxuk24cttS8U1CB1OdVNq 2YmsXBDgeYgce0lzvGw3yox7TsCl3xFSESI/7xxCkhwcZN4Z+eIGgQ85U2UURa5UEpR1mjmXirZE ObJurrSj1SDS8TZFL108060WkSrE2HL/LKIBwlTjI8KnJpHly56OBzTbXEtgu75bWizUVQwii4m4 xTnC4rWSXL2ZnpEqgKECAwFg1tCBMXYqiYyKQh2YdazSxEjzJ1O74/kz/EcJIe+d6FilDsW0Hbag e7yGs8hfsq0tiGLu2ympOQROdq7sDnxT5qc/5fn4x0fZAPeQ+CD/3rSc+H507HM7xLNyyR+KsDLS DjdnA5D8QcD2RS6DqTqnogtUFIGmIh3zxq+gOoC/0GxHxCQCgMHiUt+CoaKkvc0i6YvbPsP3s3I/ jhN4EyA5yd/3CM8MwNIEowipUbQ9X9lJtPUIXyLYMA2xSmLQKe6Kf5zbLixCAa1ocfF7tLg3iP+Z /keLS+tIB+i0P0MORnUDjQAoT5mY9GoIxNk/VuC6u+5ffd13ddnO9aLBiTEHuINQCHI8EVNmxgtt AH+h1h46m8kaXZpBs1T/QzbjjqOpzZkZNCAGMJhqwDZIA1J3zAwZDtDta8WCv+4/owHjPKoOqHIW TmGKAH8u1gnbvih55X/CR64CJ25YtAal1+92nj4bjvAUf7rNYiZIr2VVVVRFVNRVVVQNVVKqqqrJ IZSzl20AL4mYGYZhIdqnHfCJDaRDXHEIlSlWzBMqcUSCjqgTSFHGRVCc3FQ/X4fyp5hcf+FLdpTV kh62dDsskepRYbpOidu9A1AnZK5HCX1wb20B26cdeLOjA8YF2gN4SkHhKnAIR3IXqDfE41bTWLQ0 l589SivTjWRhvkYQRjQqNW++r/yzM8/+vXvSh2SBd1hEEQRPkDyHgnl8odueYEN5Qb+FJeKsIV0M h5bFBYHvjO8fO/JJ8ogMiCjkDIl3oZJm2QROZtkEM/mh0aZZW4BS++kEnXAjXQtpHA4WBmBJwX9Z yJiCGComICRhYlaRANhNbR1EBokmJEYxgfilRWNGLqLOna4B3YLDDS0V1DowTY2NiNJuXMeZdC5X 6UhYAigGxiQYqfBVgtYmtVQ2I/S0i2QDf9dZsg5KNoDVY3ir9hYzz9/ykErYSMixkqaA+Qn8HG+0 O6BNgh/7YF/x5bBsEEcUjsWzTihLNkFKCIi00+RgX51d1/eICO6MCBZaDyEjTflkNOiJxJQyZQxB IhEohYCJAQEKCCAIDw6vBoteMa0h3wrQ0ixJFUS0BQFC0gUAkIkoXQLMDJwRyoQpoEoVqhWgWIYm hAwlBcgMYAwhoClpWhaAKVIhICChyoUmHCIhKKUSgiUApRoAoEBiBGIYRISkXJQchWqQgJEoRQoC hSgApoEIiKCBBiEOH6qJD8gPVrUI0Cc/WalT1QGR0xwHUOQBDA2eXWXureLTFz5/DgtDJacsiHLW ssicgn2If8gIn8A+tBS5xRD8ceXdAu/0MUkkE/MWE6gMqA/QOHqV8wPaJ+UCRWQWJAJpE2V9XnPC cgImgPSMZJQlIJ+mQTJqgiZlUmqgJkmQqlElSGqP3ZHKEmTWGQQQ0htAxIGRUTVMBDQsMAVEpBEx SRRIUemFHGAohhiqJjhjjYYIlndbF8OKf9dP53Xjf0M/ulpCuFxjTKjYgjPC3cmyEY53sMxDp8i4 WL+JIDETTnKEQ4LFtaOlGKrFuJhQtwayXO4dJ5eUQ7PAt1w7aWCam3cuJBEiPQn918aXiZAc5vN+ cn2YRHbtdeJDg2S6hEWA3vJAKAPhV9Oj7fQVCN7OWdEixhXIToDAkboIB/JIfdgwhcgCqFMJEYQR NBIEOTZkJmjfCcCHWagajK/SoBSGZm0naPe1IiZAl9sIbN5q1BN9mTzIXiOMOT9V/x+BOvm0G81p XTAxgJJRAbTYpHtYMCbyLZhNLUDTbRMki2CMHfQOOs12QbkZAwVVQxspKId0QlAlvKzF7cHZN8tZ I5srDcCWbYYwmYYUTURVM6uWW43S5BiBemRA0S9kaiUMiVTphpjU7s1TAWyQyR5VQeqUMp6nbkyW BzYKKQNoQIBZSb4NBpoVqopcTMvymaOhFxSrugpUCKEIEGhxFZLF4hPv+32HQN932KCfCjMEejxU ENQ1tzx0TUIRCRAxBIZclulTUs9ijT5jalfGo4QDx2kUfWqNqVGFop+z/NmbQFh/sP7v33R//Y7T nMPzG2+8DcNNewoX5OQfzxE06h+U+7VVP5efEa8j5oaF9qqcH0Jo9Y1Qqv8pimJEPjAGNrBQtFID 3xEzvPRPIWYexw/P9lIUcZjWCgLBTBMssJgwSmOGgxzUpYj2FbKL8iAHzEiURJVErFIRDQNCTAJJ I0AobsB3Qby3zGGgNT9zTi0tcGMLAaAmoiJJNlF2Xc7C6qVvwCGFUGRCXq5evZbDET0ZDuB84z0m SClwOEEiyDnAahPFAZFKHkESyO9DcDKrSxND3RH1/+k+DpE1TbXLJY591vkb1gpSRDCxftHjw2Np ThDQ8xXokiUriMjkCPj0P2g9XeUhNzQJkEf6CESsxqoiBgyiQiuNVYExQVmWmhEyyRGgVDJMgiQH ITLUIGmRACXEg7ITSqO0gJQJodWakioR11J7fUD9n8/vntGuzFw2Sib/p/LAj+EAwRZHEA95NLtB CKRDdRUSe71WU/27j+/MIf5GPvGrKMsdhAQP7j83wA/VY/wVDlT1DxXdqKCVT1EnxH/yiJxS/p+7 1fTIfdirINAJe1BEGPwvpU3Acxw/1E/2voHlsZ6p4qj+0hKaEfLAVhKAQikGCnuE1dJncfmP+Rt1 6g0+UzA+Fuf5Bf5j7qKhIQqil/I+8MA+PUHP0BzHP+TepRJ3fQv/Kp1fRy856Q3CmfU09YfVu5jy dR9lwD5YG4TJTIyHJrPIuEQ+kPPuOvPAyJJJJFCg8K95M39/OD5APhPuPIKwPBgp5q7/IS2tSVyX t9ZwHgw5Kmcmh7pKWsxDcRuN2NSjAY02RnWkjQVoVaS09tBlnS9Ls5DcDVibWxkNc2dany02JNcH HFtN88wOOIZV5UvN5O0OZwKRFSCuZfx0Nl3rxmCguMWAUphjvgZg1i2MujJhC2Q5cGPBmsytptJs 1AglowKVLIpHqxGmhvC6ePELDQ1o1mNh2mshp1h0zYAVn0Jb/kNa2gOUNAKHEYoANDKBmsyQxIIK GQ4nNsV5gyjNMwe2MYtsS0wGYWpdfXdA4aCRYx0zVP5zVO5PbWHH7mzOsFRTYnNyf0j7ZDeJ3GDc tDPjLEMUZrZVrAm3SmnyU11oumOa7cEuS4IP/W5IKDhbf0BxvFBbHsB/9oHA+0Ng+PmvHntwTRk+ AUoMSmduRIiQgZ0MA3pc1imn1fCFY6L8py69s0Ri0zOhsCIMUqUHeg0gyjNOxDRkmdzNTSQJhAH0 +1FTRo8BjkB1BDyE1vJ4CSr4iFDA4C4MzEDBIuV0chlEyAIIfAEGlDj24UkVsuQZ5UBOaxa1qAI8 /jH/RBkckOcHNRXW3Dmci/1Au596qKIglY9hgRAZ8Bi59LrqTbrCho35+VToEIe2w1IxWBY2CWqw Qo2fiANu83z1NcowgjYWipr4EiqMrFxYiWkKUTEM20WflT6ozcGYjaHhiB5Npg6k8HW9r7F770i+ 0D1j3x5sG943iVRTXp7egA+VAP0kDTQFEkiHINjEBjUKWUUel+lKG6QD1QE9mZqALaOiilb+QpTe UmRZFtZRKATIA5wpQ6G9A9AR4ZBRd2VUdW+URxGsMq1jFStmDhkJD+0PcWSTcOZSE5fhxsi3XGhq RsI/BE5TPGoJCPV8wZA624MT1eGMfw/1jA3wA+mjk/e/q4cN8B/5X2mhKp/Cqu7LbDV/T8rzZsZo Y+hKyGBlIWYUUG0Xi4qIuFqgMkSbciVhgYSYjpcx0hSu93wGtJB0++P7T90Lk/QPfOcdM+/gRhtR 31R4K++DrkcbhU2Np1HePLGGiSZbe4CV/h5iPFg5qcO6QXLw4mwuunJo7su44czxFHbVyheHLkO0 YMYkEgQFgqdgt2tMW2qEkVEO5K6s1IOd7UmipSiCNoUNWuJYySrbFcYFiurLXAQYzckhSsBjckWy ARbaUM7XQmnim+SXIjU3lJmUhRSjs4hjLKNGaGFLYW0LFDagUVYVNRmtBs4GvChtqiMUSGi0N2LG NGfGJqwnJZU4zohXh2+pUstf4XmZLY3ohUSZWdHMW1+D59tGwxU2BA3MxP237kM2mhFHSvDCo3mJ lG0BjrQQQEcQDIgsZLSwokYVomWSpipWJKWkYcWr4vWkZETbZMbuBCJoHYWIoKg0C3dZ6sa0srHo YuMVucHsmj2wJgmWVAyQRTu5rWbKar9ZROZ1/6bPh/ZnJjSaIGHtR9Tk7plNKnhQoJK6RgJw4itI mcJjnESkwS9ISf5TAs1+7VTI9PSuYr/Y+bwi8YuCRwd4ipHHdLQ5RHOEOVNJ8/4RrOOkCzCWJ+L3 TOYB6TeowwjZ6SmgKRFLlvGIKScjBbS2mDK+Bw8IYtR2fpcj85lTA9x42HK98o17nipWYlNSzBbF DZ4WAuFwpuRWERV08BGl5HDtJ6Hdd9gtB03a2Mwv7sOSyDepBkkkI/NBvKgrobZ+gAgC2QHIzuOX i9QaGRYy6Sf1zz9Zx8Zzh3pymCuaPw+kPBjZjx7KzvZsGWujXtmVit/wignUdo/MpYEDPyk9RWlm 5YKxA9OOJJrp8mtt5NL8VoZDaHRDDPQ4Q3P1XpnfDGn/GzSKwRuPaJqS1MGKUcFEEuve5iUms1UR JtOk35E/+RLtVKf1GHS5UhaEH4RNBj+GBofiQJ7CC0nVYENsrSmgfowcXftND7B1BVQRHk5bghNe s0XI8e2iysRhA0t+shCH7SE0M7ZowNYQ7vHLmswFBIqGvKiyBC4fY6DCdSmpJNiJTUmiiIiIiJ9p 6OoUlH1+FR4CPnFf1R6FTUXTwA/aRZFQxuPDXBIWJ4w0NrDtJXNy2ebTvrsRuD3ksmsN8DEgif+R FEkRay5w6+y9i6f5S3dSge12BBT42b7X2hukrDy9tMVT7GWPnJ6nlFOYk57FhMKJR3orgLjExUKD IGzk9ZAdiMo8h2dl3u02DCeMYAXp1rx5f01yCHJJmbybZ1HfEa+rtot4QPsA+G5qI9LKmuPYDOQE 8Dz9L3YxIAmI0sCNsbJi5INeT9Rp2thUmU/Xak0QUeU/afNtuxXPDAlRIiZYgjUnGTQZOw2pK2Fh SIUJDOIGoHU7h7ng0U0ITkBlTEdAyMpGM3h49MquGGOqZ+kftL3C6ZuwgupWVNkcJhSCBTcl5Zue oc+etjmYaZnMQgJHu2xXTEQashIkLEAogTSWTutBiwSJNWhDvEArIsmRKpswN0mVuWwqCwoxkUOF +rSa0YxInCDE4VmLEtcZTf0ZeXyyaJjXVkBoxqGoEBDI1AJrjBb0RMb5kGBw7ceZFJCdLWIfROEi iNRBy7gPZuIwY2h9BqaatiAxnLA4MpxceZrWFHmgVDkyio+Nldra25afh3d9YWS0BMTRBAhJAUyC DGCkQ/N04w0kZmttawSFZDVJWHVJUKlWT62xjBJlA0wswu5ey22BDIg3IcFNQGKRvKYTEqhGYEzE QrK7XArUaVGVm5iG+cC69GE8bZltYARWJQTEs4EqhIqEI+hJAeLQkrKQDDXQoOisYZghDElFYyE5 WNg5ceejCAoSatdiWMDDNaUpZBDNblBSlmzuMIahObwETBJF2JVXgsd/NeklK00AEB85RAHx7Inz knUi9PD2kUTUwJUzBSCzREsJCWtCaOglxXAy5ewRTQMKwQoXQlumQl5RZtYAOIi2eIcCtTzr6aSm BEe2vjJOtDg+NOzt8gZgHWTERKSrIFINMADv6BNTS0dENvepMaXfUH2rHxnry9GKtoTceu3gZGkE vuKkf3WI4RwkGmrOXGlp+D83v9b3coiZyDvLnaCEzrh6QFKcLcXQ6fhrcZzDnZWjKpdsvRjwav+a aRbNksuJddaQxmLX6ngNmmpxBflS+AJJe8C72KI5GnRjTKM4oiDDAFgiGYUYwrCRDE1CGI8BxZGa NVGGHHzl+YoL5k9p1pgsBEGMPZo1qPvSopFUcpVjV+UuCTGiUpthdXWe10Oo1Fn7/8+/r3LgNA8G yBA8RTQE4YwPe1HPVB3TZ5Dpem4apSqz3qZeO05j3cbG6Yw/0s+VA6/dvwmpE7zDohHnKO3oipZH he30DWqeLp7ZO2dktOcPjQ0KP4kJOnPpHz0rqEDdzRNEKxTHyn/ai8PCF+wNFMHtIJyiSp7Y/+fg rb7LWktV/V53c5EAc0LLnsY9BP+v611/mvMXlI7fdVge8X1+baB+w7584MQ5PKap2yJv8peU7i1/ nhRAQBDFQA8Jz/s7vDJ/wr8LQHWKCEUEVEEtfutK763eFiMTV4soogkqzBWPAkzOnGECQJISYEmT qlA7DQ+YjN5zjF3WLxX2fb6HJ0YPBwcUdm6vtTFmxMpYzEcifppimoBHaCEQXIA1KDkCklonR9WN YJ1GIUUb64gaQQEkVGhep4BbWIlj5OkTMUcxR/SBpKB79otJmImfUtIdQHxwDMJkJmK7+jh1ey16 h8ksx/bH2ne+KwYhyQD5y5+xyjcDLKj9RHdzFJyIveI8eoBEDGAPx10eXnO+1wOmYDOBi2DWEvt/ mDMFnToGQzkmk7mme6YajP5/kuQTdqmzZx774nBMPFUoyryPETyry7AQaGNJ90sYyiDvSryoed4Z zu5kwYHGqnELOSFTW5dw3ifUQwE/PwqiKPd8dXFHa0tLdzPosoMYlapj70QSFe46OHNvskpQI+Jy dgTPSFAmP0kNLSKQs/My+SaEBgwOqD1WYVfl/OGTnghMT0cpuX0qJEVucsNvQdh4y54oJIhhbFeP q2LY83nzN4sZuGBHCBuUAfkj6r5DpLtllIjQPgDhDvFr7NYnkgQsCoaD2dEio8ga4hGQyqhy/6cP r577kgb91Er48r8p91eSPgev+qMg8xEFDar5YkiEZDpQz8TitC0e6hbdIELih9RAD5rBJkWvqDOz 7IfbAsdA0iWi6DAP0M07MX9TYQizcpbQU0mIDtSjEFawokrJpwVBFYszCyDlnDsyYCsRimoH0099 piqyCMgTZDfc1JOeo0JRUURETKqRLAJkWUaTEVCFFCkRwUetKeqDYLohtoA+m2OZxMRLgEKOKGT/ xQgCyYTykTakqeCO3c5iZgaxXCJok0IZ8E6HGWAkRz/GTyYA7oQJKihClCgghR2d9ts2jWC4soGS ptdKIGiSApP4En7nQ6i7JwgTsVsJaPCdDPnidU8iL6dgbL0B6AN+rqxR3VgTli4sjZ4XqdbBxbDU /PD2kIwPRHEDmmRRqNBhALiFaqB4CJB5yEZxPmNlh0Z/znuI79hsNxdzLxCO61kg/kSvjSnD47ap d5JpM4Qzicj8if9iAKdb4ADM1EBNFWUfZrxJqfAQCek+ZDADk+SXBvkbGjDonkn4apH4TMvRgc9O rw+iKF3Q7A9cgbwUsxFMGCm34T6QKUKGK9GiIgkaEpGlgWUhGCvqHZGJc19/XdLwngLIDhvw57Jt zR6J0eP254RqjRlOR9VuKQLuE6dPBtti5LL9nskz2CqewpPLswNo8ZJszwoRIO747N2qJOjMnGca Ql7VyhNiOwqcg9JD7yx6oDdCFTfHAcgIjzgC/qgLcyJlNUC6GIAIEQEU/TAx6ef0XMW5VoRmYYTZ B2t+kSlOGGLWJMNyJjLCdcpq9GP3JhEfUEG762hdZCQnjzLbIXJftxdDngmJbICXeKp3K2VMdSQv UrDTOEFaR7Kh1Ih9EGpHWzgiYVK84oeUoBsRBAwURqDEKAJlCVyOELoOOABwf1KKdekftwMViSqp gkKGKFkS9fx/5dIh1NwlOricNGTRWaxNGpwkpGboInWdUvbhuRcowOyN4k3FO4fU6UQNMJQKBSID ArSJVKTC7Z1cTbzHl12G3YxrtCwHlKLyzDpICaKEqaQfeDu+bk8HhuBcve9CQrcoJUbQ1MpW61uc hhOcI15sMPdrX1nT1ioeePe+DBf4IWIED34VCqJBTQ2EU2qSpsiVKn0I+PnDPlIuDMgJyQsuU7AI eUxB9BDgTEfRMazQTm4UavcRktWuHpw2J++CiZChLe103aFzOgNzsSQDsqfPcnJiZ+jAO4d4km6Q XZBZHewNKzZmxjVKYn5clZNaoIMEQyuXDCi2UY42F+coGcXBIapREREehSzZN/56c2ckJy3JY2q9 I8GGk/26LOYk2paI0pEEJvSwRDKNBZN2IzZvFCjh/3XEjk54BjhZSDZQiypdqLKNRLTGVJFFmMxk mDAoyWyqKKKMawMTJXMjGZYq0tibE1WhR4wRrSmgyLFjjmDqpzvDWhuuf1buMzTikjyKDK0o8dQl UayS/+gJQ/sERKhD+UpTstAKkChh0EZmDkPCwZQ//JNStDhttLqQ0xYoqMeRzyuBWqM7Ghjrs7M2 eOaZlV77mSXWZFz30qCsYixjrpX941AxJ3jUh2llRN7VRWQrKyHLtLjFk4ZCiRQF7bTW2YSVUKbJ WkYxHsbSw6hUYwNMwylBtIxhD8TsaqP+m05aKTePdaUZRRP3d6KEx6a3NaBSiGwlFECdGTTpJ32z IgkyEUJ0Ej5UDX7vvPns0stjyhk1NMeiKshGkSQkbjeaIfFrqzbB4RFGv9Pf/dgaYR2L4IvY9jz7 mqcP9rUWcmGvgoQzUsWRQ0d9IGkP1+iw1td0hRJ4qhdNB6iVQ0zTssgnVhzYZvSFkBSAIkpSME+K HhIaEk4ScPbjxueYYSQWgcTGio7XSQnTpk2pi0xJiWmB7QrNMbYcMowxUZrCzFRRUyNVJur5JkVQ dtdM8wZQU07VKmURznrIqmvcWisMtJpCq4lSBWTEMdkqFZJocbvwYUNatMCtKqjdmv60hVGEV3LJ vDZEs0IzdFqMSkYoNqyq1FhRtsrRpZGoovMZfllm4khHewMnF2TYcct+GZUE6DNuKFRTHW0aehJz Z2oaVDlE5ErQa2CaMpkQoo4Na4AJExNPJmkwaozm7zepq0W1rCp/7Mu6Q0mQViLXvKZiXVMYpd8y CpbSMciU5ksUUNWtoGKxR2GrEDYsGgUYw0twMRIiVoyYtbYZQoooWyqVSyVS2Xa3waxNMrtztXut dMWbJjlGVnJKmkKfstdqVZbRNmqC5VJVAQqVNsooGMo1AWAZavO11STkJgwXHbMzbVNIKQV1sYUQ UBSPKylZUTuI3zbctE0yOWpPjfJ/uYaZNn4mYiOIWpzDFRqgxVTWuWtR1HW4q5IPwwhkQbYQa+Sn g/dnhVsenskQBHD31YTMTZSRtRwjlBtLTSUU3fmMlBQU00CgsJiTcSHBB4+coaHTuinNqKUUUV9b /4fiUAE+kBPnSBVKpAFaUCmkBIkUZgIQlUT4Mg2kHWMrk2WfjFNPb8LJrA+IA2ttoJdzDZCCmaZJ akMEf1DsH5QI/KB1RPaSdyWXbP8zzB5n5/3vFGVRB2X795Y7ne1uL0GRNNNmPixDN+7azhQ40XBu yb1arXcZhWg4YQlBReDK/Wa/5KJk5aH40JuCh+wJGFJZkmYmEpSgYAQlhCGBCCB7Q8weI8CBPIgn Xsm7/3HWeEIn4/MX+I9R60nU++J+ioYtXYgh5Yf3IznSW4wjkga3Vo76/tzvzy9cLNEhwfhZcWFa b7U1kOzauJVicoiBUcu6DKYTxMTfVwVagjXGtY6NIUdjIaMcJlPnJhURSNCxahDCKYFKQJRhQpGU rRoGjJY4IiEN2HfvpTnvA2DCBjd+E1yiQ/OX975429z0xXoxjIQkXTSg6dJaMpuUM3obO5vVgVP3 id0E7XDTLD/ny5qlwhLjBtxaE9pn8OqvKLKqcwh3pXs9mdTa5mZBhix48v/TRRpb1sidEIgFgJqT eIafCiS03Ev97i20Slkj4RiWhohoGUwozGFkzLNnRmerSwSCMB5zW2zbSG/+kPd7VacHLushL4km ZqX/tDyo/3ERpLhDc/q9HOfqU3b/9pvAeIPOXdp1O4hykJ0ttfTUmC5KyhurIzKUYBwDLJxY8tMT 3k9xvpFFESKCqKCiiix9d6bwhZNFCcjJiAzbbBzBDCV04bg6EwdORhJjVSBAxYNYY83u3J++Ief4 Wk9EV2JHgH8mLIpUpKYO5e+f3mDojRDlHq/pNGr6ICxBhT6X1F8EcqMij+oDeGPomuH4zpNWJZLf KpteBsIcr7pH5C+rMPD1yEA0+D7QezeESkJjvdz1I9xIpuhhK+sH/NKAWYJkiyECxAqEHFRxRMiH RjhARSIQ+p18NXC8JDJa1VEmfWxHmzQbCF/EeaCIvVjhEE6fAg+dPRhRtwGZ9lB5Wfq6Tbh0FaPt 9cnI7VjHgp5zleBX1U+1Fk8jTP5Bgg8jfffnkbYyds0CWTWqUvYogV0mrpthWnSfZYugyMGzSaw3 F1kX67F71IVOr6jfT2IXebO88noC9RFP0wH6BkxyHrTUp7fIUAGZlxK6SQf+T0nl+3dPc5xPafTP 7P6jKloaTeqTUhoYabl6w+IaYsLBhmDCw56D/qyL1/OA1kLIZJQoZ/3eoUFPS9HS9ULA+QWyjYij 86Cnzg/6BANpqUp1ZwfES9NVBORGaMDKLWkxCJSpK1YrTQtKSSgU0BMNAW2OTqXUUiroQrolVqCx RDWYJgVxmIJkFMLCFahMtrlTkaJQK02RtOFgSmEFFY5iqUg5KAUKiZIoZAUJMhAEiX5B/R+huVhZ nuDgHCKFkplCkQSZAIkDmjDydKKaUdDg+ITFHb3NlU3BwShcu8bgaSB4gUhan6Pi/RjZBDqPilCH w3nkos6iiod+AeYinuin6dWTzqeLlOWqhAhZ5S3C2qUtjY0csyGVUGIgjQo2lblmExzKIPsTWQVj FCCkNMqojlojEEFVSRtCoogoqw3DmkPyiVfkpZAxFA5d2B+NNI++y0siIpEaGR/qURP1iIuadKe6 XkjKqYnWsqqh2AYIb0dEUuGnNlgGhSRAIkxh+bmYgZyzhyJEgaOLN/Ttb35QQ5AIwSoveW0KYZRL 3oReZkh99Cw5n3SknCHn+BT6cQSRDP6aYAef7kp0YroN3dAFmv33fWihww1zsGk2DsXzHR2DVM+2 rReB8H4dg4blBNP5HlrSFDDn6KqewKeU8H+qClhT/aH3jRwD8A8rt6mj++Mg7V7ykT3OSBgGaRsY +jLysqeC51z4FTeQa5qSAtuhhfxwc7tGrzu5JAGxd25WXWakCQF4BBUtyUD9jgV6g4FJ9fHQ6Aj6 TV4KqTkW1uCJsdyi4PtDNssYFlIAwG8VpzSug8Yfn2c1ltz9NFvP0jAtRXiabh30NQfFor+0+64W 2xWoydRCmgPNM4j+6qFvvvjpMyM+/Yh4Ehm+CfX/F9bu/WfdPFCUtdTIxIxIhkgLk/xgZH1wJUfr Q/9zMHyzoDrST4gi3gqjR+t6w70IxsRjkg+5I19hyiAxJKC8VmSzRpundeoAO7scrQi5EfeeIp0j pB80GW+b33tE3Q+uGVs8XPHLeJZEC7ZAeuUaPhUT7HF+c9p9WuH6Sx4qvwRkFERQTDEhQNIUNI41 EEUh1nxUgfW+shUFp9kaQlRKPfBChos1Of9MY3vXy7djq5Dygqihi9Bgc00j0eDIkUTBQGwfgZx2 ydNCx4+FjXN1xdG0hjEowgRNrZlHrs9VbAS2mgYqQKgMACxYIWD/MVFLmYQUsP8D9mS/gfCTURUD QkScBJReKPF0qhklwBwXS42oSm4MaGFAsWufe30dDh6yBWSd9DbLSss3T3pZCDGMMJMOBgogOBmu LmLXm4LbxGpj3qVtkB/hpmx3AnAh6hSwaEYjZhRLgG/vMKrKz44mJttOm+jpAp6crBTNruCWFRDY MEhLoXN+9u6i/xzfCO25r+3GAteWgtyG4sonHpxZSGat/UC6KHdH3kIR4HL+BDwPDp8rrNFEe03E o79AYYCExhVHM4ooJq37SYfJ6zYGQPrOx+cEO8PQzeP0BmE6hEoW4fgMDCjvYodAfmH/Q5GxfcWK oaj6lP6KR9vafWQy5EK4n56LFpxsXIFNGvWYl9OPWPBEawKJzUS9NYaiIDFtgWqlmkvFURKTWrU2 Gdoj2ZNI173xruNbdkwibIoC7WssDIAUpAUlYVLG4RAZm34m8l1aV3LS6HHeShtCAwrnVWp2EGN4 UqUYuzD4anLWmbsKk0rTIU7nEDEWSpFrNM/cTWoeDVaFXE9NQwgm4wXMzXSeXxVL9OtGOx25QAYH LhglGpuUCCZGSoD1jBWGxCGOoMeccxpzVInr5ud6GXpEuWHOo8sMfL53PpTjoapsMJ+lNbTTUvBw 92zo3Tk0t2GnapYhzkUlv/IE2CEzPJb91QZErZzmSenOl0uMOzdGGfEGoHQDp0E0ayjwTBtoKs7V OAxUilpEv9DgwQy+RomAo1kjPeZMY8SAJ9wRTLhMHvVIXB0FNNLVUf/tzDEbm2y2wYrVAjKe2Bgw NjOWjYny7VVUmO2nBtEHHAiT1klwHE0YwjOzhispXhJZCMVYtZXmEwI22yIIe/jWb3XvCWzMIYXK TONBgjCGgqZBQFKWR3WSFZAhYrKIwbwjcIV5LmPMimVqrfQJDInGpvli6wpkTIO7DsGGJrWN2sGw 0NgZEdUxFrQcbVyYIlNuykEmhYOAxyRsZO11TIRSMEyRTGYhiZsXEmww0yBNJswxWBaGsWCx4MmW KFCibabSdTaO2VMYjDISK2WxL8xJts0htVoVWmNZXjbuDGoLKZg4YDFcrErgxqyoyI5d0ajKxITA SjAikQxoco+S5LAbS6NgwS2znYM78cE0DDAiK5ZQNpaTGKVc1c3dutlNSTcSQVUbbYSbDNzSG9aH YBQumoEC41kmqJYsautZgSsUurDQhmiELpbcwKmkk2o0ONqLBgyNVNQsyrLEYLIQI5TNSsbaMBGM GMrqRwmHOsV0Qu5ThaC5opWs0iYblbAjSu2pltCsJQSEMIQd0l1dTJSsVTZCozVNGBa2lkLiRjCC gzfVEZgRtt4oWBGXAxMpFS4HPG95IbowNxkHU6d8VwiMQ6rB0UQlLOLKP/MJDLFCtXvcf06rTA0d SFaJObjQz/OZTYrDjswQiBAx/yRRyldXmlNTVXhI2NHWES+ZhWtZAXFIRc9EJWrX6MyqnSxakUfy mbd6ogmgQCRKBxJFVTNn/F/0hmOaDEfpSRKIaCZKpjhzccCOAyiqLFh8tuwyhRtlXtso2ZaFTkTU YxqvJCVYzB4OKOjdoIxoNl0Kg2hpJrRlO+DJGF03JCqmg0mSYF8OtJTw9R90aCiIPNLWHX0cc9Ex S3kdhkH4c3BIIwacMS1ZngUp1wU5K+VvgU6lrqZOf2cYyMMVaZM0IMSTjBRYxFPmYWlrEZbx/Prj R+DnGuTzZJsb7QyExQQ3raZfwawNIVEZNcZIZByBExvIQluWkUHmA2HceVz+mwa0RNpjPuW8VGZC j447a2aGZS+DbNMFB6t5LablY3cORTPrfqPAu/XfOv+K7B4DWgYdYdzvqm9m2AK1w/AxVq8OJCNK RQsO43oZ/d9nFN8seiN8motpZM4uGnLHuAVfXduJAy481cu+qrEqxs+wNaiVE7FGIbe0jAPTJxj9 SIkrxuEKnzQmSmo4l1ilElQxFgkPqiJqFHZmhA9SIYgaYB/gjdIDZV/Km9KnVmKkzeqlftD+Yb3M CansD9zp1gc+9fr+pRTVu3sU70h5J2d1QyLJsxzKSoVULTMmDiq9MwSrWNRTKrjQYx1MjwpGUCNr 31Gg+jAmgkyN3Y4KtwnzjhfqhbA2uTRUPbM4kdbyMWXf25NDNfKiQOWmMdODIsVQBx9+fS7gn1CN UFtro4w1YjEQQI3Jej5YGg1uezdEf4pT5Z/DKTRz2VFBUcOJ0mSZIn2IUWfdA18z1ry6dNUcF4IY JGGC7nzquGwM7J6aQsAEBNpQUMB2Py89c9Vypzyn+C8rrHohuo7xXUqZ1h2sh2QDZYEZGVgMEHyt FGVokhQaIKRIlICLLX/Wm4IPzkKE9XYeAoH9MoxHcVhBE2EIw63yakEQevRvRPuNTghRsnES2CtN ZB8VSQv9SPuUe67a2P2h5McwKZ60uSexKrtjtA1thl/vW1/WIGpMJyEPZYvZpkg15esgisvU76fY t8I6GZ4pzvZzqZwb3FJScG1utLBUE1/Ihkb5rhgpku0edJQeCFLuh36EhmQeE9LK1OBJoxUjHuvF Z4d5dKqjTna3ug8O3Uv8FFHgEXLH8Yczfjo7RVnVtaLnplHJmWMKP4ObAZQckWZWNFkzei1ZAYqw rOhNs0cSQAt+DBknsskeh/8V6DOrZsCx1Pf1z/5Uy7cqmYAFoUKO1qJoIOCJ4DREWOAqVWTJtRw0 ZPNYSjhHvDRl+VSxqSBv3JhmG8IBratmij9ploWJCECbixw54yW5eEnqeXNTd8keIlhPp6GHbD5v lHc1OKpLq4igB5eQfXD24a4/SkftX3+x75UpQ/bIn45+mdswcITqCSPpXaem29WXyVL7tw7IKfaQ 7IYWfTnvOhMeanTvJFNEAYQqYhKj3ADAqm7K8j5VArJdc+mozGxhMNMa1kYqGSNpaMrcEwcGQBCB yuwG3y85/I4akzICAJ5gHRE71/hjMNz4TY25Jknn7w4rsKHdJgqqfRwDxSxEJSWsSY1BF6Fo8MdI OSjY6ZanYBJ2UQVeKWxfDi5YAqGGokSCpUPrYGUYm6UWFtmEZjIaY0pAqFHSopQQmMwUZCLBqwXc SE3NGmpRhHKErAojaMGbJKyYjaLNkjRkbyshWWgMoNKINDahBrCYNNgxk1qPWIJkjoyMyaB3EmrZ alqBS6qKXbkqA0lonxBQtnIjhh3CHY06TADDDSGlcEBwifgUMYioAIKKqmUZDA2sIJRJFsOwihWi JKpoqZIJooRiRYkAoaGkaWlIkZZEaYkIIGKJFOQkBs4sOKcMRFMNY7zmZtGmgjZRxXmBiDTQmwRi 0UKgKIKkFrUSj0GGEugNMhhah2GRZTZgAiIqoiKpKKQBSZAGhCgWYKVopKVKQIqFmESAgRSICmIp ZihiChGlQpBqkNkj3SmoAoQo0QK5CzFJEjiTkoEwkweWFnAj4jXoBO+wG5mZCacMSPrZQ0svFtmB Gh0GO5oHJNVYqMEBrS6NKVBoxV4ovpg5QHttSfljy3Zm2uMMC6ybimFNUQgQJRfzgETA++vTyCCZ KEJxPyhru7rvjrtQGmzFHERDiEgvKA4DlMwMRRzyQ3IHC58eWT3suSKegYwIpvv/8TWoH/HORSNA BSRHxQZKH9MK5KBxRKA/eyL5J+4S69pi5lDY6RKm6WsGMBfamZZCwKyVhaTXnbU1fA8u8rLLEIzX qhb8Ir9cfmmTHRAaHBh1EwHBwT3p2+w3zrLScp7/2qGT8n7SCKiw3zOxNUp4ZJoxIKQNMh5jzHKG KgL3/jokpf8LAxNIR7qFBDuO6RYyKPyQnbs9pWG+DSw7qo6MEtxUSEJZQY3RxgHlO71f3uQsjXZn /UaWkvUL95s8Qr6YIn6RigdS+ZNj3XEsgfBCAMiwEgRI0KUtTJJMwkEFDSJMIrMohQBTJCHEkA0z JLVCjQAFALVCARAxDQRAJEI0UJFEskUjSMQJEpSLMo1EIFK0K4S4IkP20oy4SU0hhACGQCFUKpBT ARAVQ0DTQlIBgEqBkilUqOBRIQhEY48HYUDTcSf8oRpGA3hqREkgoSIRcl+4IKaxDjAVz1RK6DV8 oQ9+DxvzI3LaQ2AUpGOeztoRJlynuK54myPjIVIVUfH9KYVIgZBIE8SUdcB0qe5R+3sO5H7xVcyN SPcazfCoQHBECqOfqR4sHQSF1coFrFCI1EoL6JoKJhHkOxnAdc16vWDyTgx5yO6NFQVIQgrDPXra tRa+Nb3l3CWilAmsIiGuw1/XMg2G2K2MdkJiKHNOF50Q0vtAejqapqqmJmiivJzDxv48PzeYTv5H Tu7S4uk0LazHm8nYBPDu8qtAyHpigfMWVLWhhyQ1CuMtN02ENElCl2HbHjgD3zdH9gbKwYxG/m0v aNJiPz4NredC8MYZI8yI3rWg2wjc11vTjpP44eyV9p8sJ4lsrtOjolJKJwBvo/wH9f8w2icHwhm9 hNsAchoKSTexYko5kBkUAHisiYoftsISKhomV1JlFFTtgZXuinuY24FA8zZHvWdp8VB8MAiidg10 BPcRMwm2dIGBII+UCI5F8H11zgQzjEJe9BqPReytoMibdL2kTtHzO4Q18KPgITtnCNVGEJULJJhf ljWktoJ5ZQuqpDTCsLaEwZSW1IiCpbFIRhsfu6U1DSolJEOtzN2SAolqgocsLCJcJQl9BRfQl1Cz tNxDzQTS7gKdV8jAiCmpGIJBvCwVMDEaoSUpqRpTDHIacQORxB0hSqagAxKSpOMCZTMklEBzCjCc xkLNIStKUmMFhpmIKpKNtv+qG0LIVlGGAOxLVRtWgxRtWElkLFSwHCIGgdYEVkSKySWksoq0iNJg zQ1Kx5Q2ITUmzmrQLtAZIAU6kd0tSG59GZCDM6JChCnIpHCADIXIDckUwLhKGEmoNSGwDMxJgkaQ 1DCglpSFC2ERIoBWSBSpZIQKkgCYzLmFpMEggSJorCYnFKRssEdiQoQwqEoQNEEkBJEGWKAkTMOQ A0kQmMqBgARSAlBtrFRiDSawQdEBqA2tpXaRKdmCJVwIMkcgDCFyHK0YCZJGJloSQyIKSpJUtNSw wiYClqmrrWYaYDUpNISsBVIgyDFAUaRGAJjSQJkN2sq1SjKaJMSazMUpZRR5OGRSlqVYGYWLYQid YQKqGhYgQzAVVUY9O2odDREGFiiaiKQUQnqoMYJsdYxcYFcsrRRuUK0SUjAUylKUrWDKyooVJaKN KIFtDMMuS5lLJLMStzEpSFBKyjRQGy2IjEohWWjQopLQcyASXa1JtjEkYBkSSYTFSDEI6lwSQiGZ m+guojYrS1FRhS7OCUpZcSmFpYqy0rrXGQk1vZpahpA07JhMkSQcS0rpB3iYDE1BiYNAoGMQwgSE IGAfJtsbCSjpAuWzixBbnCGJZgICUAlJkCmBDggk7m5lkZMfuELrRghUxLYm+BUEBGnEDIV3lVCZ HhtGs3XoQ4DOhVsdod2VkWWQCXUI/qHW6gaVXkwi0IySGM3SCIBu4xRdnTG0NZmBNJG00kmIiFtE 0yEs0JhGQoRQGFlTpGFjDBUUhXYMANhE2wBT5CBIAkYRJABMiRWFjf4meQYK8JIiYPiOb1FdUovf HWWQ2DAzSN/XGK0gVHJFeD9/QwY2ir6EKDDoUshoS5EwQw7/cvTpFJCz+HXVYMOv2wMIO76bXSNv ogI0LpitDCpHRpUYFMkClQ5glggxFecuOcb77HGNt5CQNAfZiaU453E0UJzCLgb/gjrn9zgpxJkJ E4CXlJmeE9Bh5ZUPLC0rJCk9J2VujwJ03fXUnbB8sm00vYpoTUeliti17FHdDdGU8IWlX0x3Ydfq TlugczJLkHvBjEjBIKIk+AmHErQtEvw5TVThGAE4QV3bhiL1quQTBzQKYnJ/YehwP8FPwE5wsyER PRBUK8n9B8AXV9swr9Lvt9PJzXoqr5Fjpfx+hPPChvshYgl440CjoD9kZFl7WUzV+2weuMQDv15C PXtuu2UrXspYgn7TUHvd9g1MJCMAaECMSaEN/R3oKng21+FzYTb1sDe8S2AykEQKlU022vvVHDxi z8y88WJchsI15ToqOxHk7MDJNPX4GldSHb2Yf3kOqIek8QgOnPESmOXjjhs+uFTeDrDpxchyEPtS HPqU5VsmCQXfChyeNrFANWVvgHkCV47OI//JjlUolUjYXLXdo1jDrN0GwgQm2C00trUE0xd4lqsY o11GeQF8L3yDe9EqYDe0nWMoGRLoGXsOpw3A0p0bItSnmjEOQyU9XfIGM8eiKHd3gdc5cdzU2ZTL Nkx7ms6oSpWFQ5sJdrJ0gmMUn9waLOUdu53aHDWVzWDvg5ytGjMVKTCBN+In4QaGcIpjBYFgdmJW gdLoYGY7IpVcthl5xdCgvpnmbVAl/qq5DqqSkUZSlaURbEb2ZKJOEkvoD6UkiYHUOGDi0xaxPQ9D ES0sWpt4wQz225mslfVqlXS043k3YKLJuyqyG3EqbJJAncD4phvimZhr0YDeKDOnLYQQrB1sC8Om 3NLc1tXRi0sqNfdWVadTG/yCdT4ZtIVH0KuE+Bb3yspNkMmGtsKmsEceuArRq8DcuXY9MacWXw1k ZU6OjgoNH8/j8ZaVOgiTKpI88BB6upOTuXRMUgUjRTyCEKUdHgbuZqs7YTstlCTgCdi2YgooCMpO ugCzNMEUCAJhJWpaAyBqWiLnlRIaCJDQ2AGoG9APgPd/uGinmdOKwvkGb13bQ6Q2rxTnpKhU8E6v N7R4NxM86X9s/Ejp3KJoB/Lleg5APEv8Efz8vV5gA7a7qOSNoCjYIX6b7AMNGPigLd7fi2JYiD8Z dPCh11gDyByGx12YF1+d8BzkAEJliJIdKBiMAYQAQvoIwDZysVBglGQmAYIJkZESAOBjAYOMdoh/ cEFCSkCSEQRKSsqkQEi0MFSoFEstRJMgMRQ1MIG5wx+EZVTX+fSI+FNaavvLnI3L3rSgmGixacB8 WdMisCKegpLhEEaLFVltlGWCw0AGi8wGX19hcSzCrK2BswUCClwMkXWkDp2RE1L294GI0iUq4MOE otMSlTBBsOyaDd+pPOGlBQ2n0b14OgOZIgel7GJSKbwMaxYIZuwlYIJiHoywOlsESBz50KKhYf3W shh+IuGYiQGCwVlwwbVBqWFioNKfr2M1GKIg7NNtZsa1iVplE2KQUHWFiHrWLE2lhIaYhGSUMGMA 1DAJaAaaIJUl2tHK1qiWMEMcykWpmGZMtybUDEjNnWEoRpLYUYooRga2uaBY1KkUkOQg7KQIQkRO EGixkcLEIhVCYIAhSBMMB3gFDSFasYmiQaJaQiGDpp0qOjtgxU5KR9o9yYAcpCSEkkPaLfA/RBT7 YffOr4fKlCn1WEHnEH3KFLQBJDCokMIIkgj9JAYnv/aVVVfbMP5r+mYkrACpClCggysLflyTIMrM BMR+iQLChjLKyU/ng/+sHBcKUPXz+8hYbhEA8HCTyPdVkLJjp0pGfDv/CIplQQKfKvygewlyOIaI VSB5PRSPiBv9IZI/H3jYqFh8/1qig+4f57/D4hoJCUqkKhaKUiqkCIYkiQkluCkoZQEA3YL7Q8h9 3UJygHUQPNBKghIiU0UQOhOAfubAbFCSgc160/DKNSBUiEjIwEgzD90fFCBh69xGiHQTWqBVTYYu 3qr4g3gjIc/czldQRC58O56SH/gkFH0YRH6Zr7jBFejbYqnZcWsbwMTIAfr4ufzRvB8hg5+ZN3a2 gh9YOJFhHLkHI7hVpKOYsHm6CDCHachyij8z0iggeemKRTGHEgKA8SkAhohCGVSZADoH5jjwWgNI dRv1iHWQec34J18V0TaMLMMQXtgXbQ4STFRJASclAQMYAMY0BoCd009Vscl9EFK/ZwMQg3QeEsLK lNCSHBxUMgRmWIoAJogAiBwCMJ4um73xTQC7QBQhQr8soHKVA2tiS54LoiIszGiAoLMF9MDqR60n JTYnHwMAyCIXeBMDRUVirSbZiqRCNIxIg0cYAeJIamkaGIKkp3Amx3h07ymQUFSLElI0sVmAB1hI 6kduj9YQVFoVA5MqB5ZcmhN5TCQRggfRKJ1dJ/HGo3rWDSYUaQEOo4gYgGppSUZFcQhEN4A0DABA ELsYCHfD1h0JM7GFSQhTmdewkdzmTmYOMLW8pvAhaiEap+NdjoIJcTYGW7WIp1wEEUp1AeKmiShY hQKC+goEsBo5qu+Jkrclw2CC8IoSC3hBlGHmMOLkCopDIQyiTIxFPpFCqfZNFAFWWZmBScSwKpGR hYgiJUllKDJMIYgdRhEIQDSCYwuELkiLkpJDQUxCjFMrSQMWYAoyfQki7g3Y8MQA0kBQhhFN4DQ2 gsYhCDoDrV6DCLtqntZPmDp6QA8SeP/4A0alcgwkIAhUoDUdfH4vhwVw6GErqEHDxi/8wlTyp2nc 9Yh11DQHpIetbsXq7m7CwoZX8zBgBaET0fB1Gd/qTykm2ge6IoJHwhWhUw4YBhtihtGFcZ/c0qb1 HEVhS3VcxyjeR+KUMNQ2TZikgGARUsSDAQRTBOe0Dax6bEQQF/NlezdM7K3iFEULkFl4BQw/nStS PVvYmzAD7+KjXbTrIoXe4MmJCwPSMVeKBdTnBegDoEScAB3/T91k+sNEPZOEHP3UGyrSYoof16N5 NZcfO2uFOektsqwUBGC8wfSWLDsCkyQPesD50OyVt5gyFIicodPQ+5ExACZrqjdDuDQSnMywMQ/8 pQpA2kHg/3BV+qk94gkf/UhmsInEI/iZePSyW7fhx1tmRwGTJ94wQyQI1Yj+UPIeQFfvjCQiNuIn jfr9hrRhlN5an2MmQIQCzAYEG2pWTGoJlCAFDtTBCaGGoQsEhmiloid7g7fAcDIOEInB0kokbsOV BVBMdk5CRFb/NhvDog4QmzURlQ1GGmRETKY6HTyVJhtmBGtONMwi0GluFDbUQ9sTDD8+AYsa1uJb Jo5zow4Hp6Vi4dQleiPUQjASKhiyIg3zCi64vIyDdhBt6fSkNyRDjAoYJKIshaJ0wKhBHoxEkrWi A9C0T9m5ycUljNgplM+PO7UNa1BoJVd4IJAlkCgohiiCJiWQjJwQpKjWIawwenTfdzVgYMKwFIDO wTCYUisoUhChnFCsKMDYRC+CUGISSxS0oGD3OwqA+NOC/veOGl7vWEn0klB5MPY99gdQXg8q6tSm vajb8aPmgWuWh47S9ypUkETSCKJQWsbEGhbDLDHJRJbEbEK4Dg0NiFmibFh4pM8gaE0I84etOBxE KUSzewb4Pros/Mp1obfmjxQ+mOh2qJ4gWEAO+CGBCpgdLwDw/AJcLpdLxV3WkZGRPqqg2Q74Zh3/ buEmFmDmLhmMKksuOAWUmICIoDSUCiIB2zCkHmbdk5+sRgG9hQQ1VUlUXaGJ6evmBEK+TocfwGwG 9jKM1C4h7tzyhGyHGDgUQptTIgej+C/lkr5YKtwx1G6eHkVfcY0KV78BiGmRMLWGDJpCAzVGqBCo R2vBlAWMUgJ5jv6h/yyA5chkHvjptBMPg9SQD+KFaKSkO3dVDY8E8/o8EPc7p8OhyudEyy0ZshCT E0MHVCKYgFST9LKwUhqliM1d8CQoP1pBIFXtkkYQLBtQH5m9jKh4qhe8t4gHNX0daGXSkildddtP ts1AMCbh0roqMGfMwidiqeMACgIvhwSCFYYA8r5A7XuPB/N2I/p/5MClAFIEIkgbB+5IrsdE7pgc V6zr9YOWQREyqYQ5V80OppDNuCfMCRDTBMSTIfMkGJ3CaEUwy7gyhhIc5oBxkR92k5Hb0kR7oAx9 18yIn6SUpDR3wJ7SQ96AOHpHrpJjqF24Achz9z801F4Cm+mpLCQqUPty9kyYnh1UlJDxFBm4cxPi K8mij0o+fGa/FtNf0Y22MnOJyvgAnigpBoE8l1yo4VO6HqlT+w/rNpRYFL4zIBF/uDtPP1SfBgps 3LJCvwtb5Wh/jg9UwhHaT9WFEI4m64tDTBcmCY2DIRZVa9ck08UtDTGbQmERpn8VjSWDIGM+8OJy 7iiCeO91qyAML3ltSpF1HLNGSYIPPwFD5WQoESYEDQiQUUySUQH6w+c+L3H6TzL6PaF6+36PwhZz 5h+eSE6628ujeUJ0BVFVVVtvkoUCo5ZUsqor3QapJlzNDG5DLjhmGQaWmxiDSCDSKUfVhBmt0wSo bSGILIayDxQ1ShR/U/5mFigN4MNlBqgGp6Szb1atLP7o2dVt1xzE4eU/1ss+XHmzXiP9H+ltZHQ7 McpjymqeBPzPqpp7Tm1ovogrs28Tih17LPflF5wFsoTSmnvniNaf231xOp7+nWIlsJgR1lQLCakz UJSPZcYVDOx5OSJo01Ew9rU0a7PrmmG7l4W5bHl3DiVhY20THprI9Y4qjxhmDJibZLzbdtjG5q38 Wic60x4bNRluoUNarK6OkhoKBE6a0rkq1XpfP2mtnC4DGHFCAfR/10X8uqmcf98mZgr+6Ydj/nM0 5CtpBAOtkOIFL1uGyJBNFERMqRA1ukoBTIUVSbmEYiVIwEjW4DCswCv9QMqzeA6gH4zz2ngkfT37 61n3r1zqurbWjmipgoVDmQqFb1m4NzEJM3vOLdyT+178J1M5iRS2pn1hulFKy5+vy/LGGq4Usgkx XfpxHMusW6dIK6FLMZm7794fiQ8Pahti1jEIqFLiC9SWJpXFQ+BFfU7pr5i0TtaiuHdd3fGTLtHl XRbPzqiE0Cb/sr+fH0R9EQrPtyxyg6cenVMh4eUSyzvW6R6eygR2EPtew7wtkOzp110R4m0Hom7o 2CxpgcG87WAiocsSkwbJzOh84pgyj1rMdlGZ9USarP+jlYEWzD8LNT8UK3eHyhj833cEeEOmtCVU +UwuVSRt/h5kg/7+cBhqSPETDSnTOgeZdhyOeQdUvCHxV57xt4pqRmRfLDqa0wyMqFGCMQZaAJBZ yoD85v9x7dpgww304ZQ7hgGE+SiZbYNVEDKSL+QPp0Y9BCNs3C1kh2O6RheUhLU+Af3hB7R+l+7c H+4PqSPRx3+JFPE7zhlXOePsCWvi4Jq+vdufSK9klhmTpcYqEZEaUKWw4RXMij77PeDCtiGmWiz9 T8MYbgvtwHOnk3zESjTtrDy47mF0mtYcA7flpWZOtuaf/6WGBthst2aAYiDSQBBOARuH3zBDhO4f 51GwOue3rREGHy/0WbG2xm86EYYSRKFATGcp3ARUwYWkYjrClV3DlnML3Kj94RyXmxD7afM8gB93 w/DlYqZFmPwNVpwsD9IfTaWh7fbS2gmYIuAhwHjAEyRhEVL7DMC8wES+XV2XdOo5Ift49PQxfTYH Ye0ZT3HuH2mTPVhhDyPQKisQU8wR7QKqBT/iKilDh5oft6Rddk6DxkqVCviJBAL5FAFIdwyGB6Kd WGEfxivyb6QIwIESigerTZo0mnCbAhURPn0EE62Dp0rMWD+sorLIQspdWlbuUzE4pmZiC6Ml3aW/ ZfpvWk0zqWrd1PJ8mFxoBDiCRNskCaoUqbZcixVPoPEp4XAFHNcH6BDvhz2/m5+qA/6a8EtP3S6T X2qqdqRlvI8DkYlYkpEge5oaYL/rVPT1ktRrTZ9DqttNZDWzThSWkjetswksptW4FocX9o16jw15 UycU8/3P/gtpg0ZRxGojNK7/vxXBL5PdixvuJoEWvH87yLLJbTOVde8CyFa7uZA8tnlEvpR+Jr5t /ZmBjENuVy0myHpT1RF4hvD7n5APfNaKnr6sxUU/GnOSk80DhDFQF9kImKK6kcgH2faTVZT3noFP S+qXgiUQwqEJYRDAgPah1J0AXkweBIFMEGMgpurz0LcWD0jrN0Ax7TEDNiCLMYvz3zYDH0yEMIdF ZDc9iSxNjqFMIGWNstAU2DDlQefPw6ndYOgeQOy3ugcxtXyf1jvFFTIAyTgCFkXCinIbpryyRkAE nB2qnEqigjOrp6jxo9AaHUIQYQIQyMrRIUhEoHWf+Ua6HpDD8Dzfg3x28zDQQNBGTpYT5YOxU+0N ioxSicWCfGbTPacuZkoOOfbeEU5tv0I62uTUB23Pn9nAVNi6B4dAdxv1BnUb0dQr8IHWI/8u5606 /HrqT247uogDKYnnh2TzjskCeZlD/GN4B7AdYo9avF3oRAN8RZAaThxQkIQeZHvv4ExNqN09X2J4 8tIkRjIg6WyELSJGmg2CCZnmJ2JE96HytZFqYElBfK4U2O1lg/lPlTT8TUXx8vy1ZQP/18O3CAbE rB9XYUS7VyEmPpaFCJLhBDZEkOmeyC3tPWdJqrFnwNB5A35+U9fqM/xYmTe4PqEfh/FBQGHalgLI /WHvpwJ8wsFomVjkdz8BA8lueHxEB+8HA0kl6JIclg8s/aMce3tNlrXSkjslQpS5g+ZO54TdGhEP eQ5h+4kRDtqXU931ZtohNvBlyiFMe4wg+iym8I/czjto7h4j8Iu5D7lS4EfqNTs+z5MSQmfmqWms TPjyVUrANPTGGAZBbYDnDUN/i2hOKLmtxOCIemWqEMloHu8WsgNofV3V+cOGz+i4l9oWpTfgpTrQ U/OiRkDteSbgn5IR+0RjO46bJqfN4vkIYoruauWv8upHxwDKcSUFZp4yM+Cj6def4t6WxptWRWsY 95jhqMGR6/DgU41Ci8FtpQU/rtyjyG5jYMklLKSk4L1j6/I6wm3VWiMZ1cyb0kphpywub47zV1kh Rk4EUn7bRZD8GVd9+p0AxHOYnzhCm8KZbK1IianBGCVCGGUSIOpxwl1jSmQtI6YIaNzk28yPeX52 1FEhEjCjjg8RHkhe/zbODWbHMEGAnKx4bWGHA9odAooo+I7doMIf2/167Jeg5OEVJATnEHYNgXrQ NguHwzEIUHyiyhkh8oSBQa1ExlzhIPIXedgp6M9VxQuBBVJ+BSKeTWAIUrCRFGwVgAvwd0lsoiFK oIgjKJQo+qSyyMBzBsWCEYSQCi/RxW4X76lZcxrO5ESxHJyDBT78gESg/1nAQfYkPwvwo+cR6gQI rF/rn4kUR/Tx8br1w3ng+K33QC13wJqnSSQ54BH3gfs4P4/SBc/c/CHTyidHV3cD1mzpUTnDin1D +zUHfAz/2H3nIgpgdA/3v6aQU+AOXCoZD+wek5EHlj/tIDAIjIq/dNZzLQmAipIR5zr7U5/0sTzv U130FMHMIGsDsYfQQghIe710mMVRO+3m0R/W0R23zcf7HHZ0DKVkMJnvMhRTVCAxiAIMGlCIqmFT NkPZsIJjrMAadMILwIQwemZHGMY3IwIaSwIyDx4YzJI08WYMzZLczF0QmlNzrMQA2oNsjFKmpqIK amTMcFxYIGCaQQKRQKApQBGQgHzvTKcQxhYGbe7rYcHHPc9+xzTZTQzBF/sc8N4HJbuGye11NqYM vJTFyQxp9XbLiWbG9kNRpmaKEgnf84g02LKww/f8/wNHiPjtwucDpfOo3vypTXAlbSrhhGNpEFDL QxqGUqxViphcygoohFA1MUrQSUBBE1AGWUMVqVgrRGEKQQZSlkogiJUKqMWI6pYqCsyhRkTupYOW FWIxAUbQqJFi2MUqCop4jREcLRQRhUrCpWCy2FRQBAWIyii217nKyWGWNKLxbFMSiwrYNphLZMRU kjGARlUqAoMJwqjfMWkgiIJCAshgwyTJohDIUlunFtRUMLhlSoqLbcGhlqwYYlkVSYUYKAWqFpQQ QLY0zCxSCapRMIVQsFgwDVosVIsRCUQsEClRTTGSuQBlZjLthibY5BEVEKJbWDMY5hQzOH49Jpm2 rKYmddehJkYm1LbBkX9JYvpswRO0wpdPunVuRLkpQmFJDAl8MPfbE2FFLa/ZrRmMFuCCBvSlpRBp SoFLALAwIwLjmHdjQ8Qk1KpLQ0qEykUQykLMRDjBQPi1jLTBonLROoMUCNBOoHJJC0UoIhijJWjQ EVYoxUR3bSOCaGK0qTW4iBXgEZ0sx4RNS6WJSieNCowrArIkvFDMbY0YUtjadLiYwVVwzA0RBDCx VrVghidDLHHQk1Fia1FGKXBRVhlNaKHjh0NzIcpVWQ4Zj7k9r540bbaBsbVS6h5ttq2620s8z6Yz hO3rOMOBEZzHdP4mpVat5qhR6MlZiado5tZ4AUmoeBvdhKIf3Lxym6t9U0Gc1DJ8DuiKeEQondca 7ah75y+TObC9y19IMYxg2mmkukinAC2MRcYyR8IqMIDUGzHSFyFSZDKFiTYnsZAoxTFBhlC3TJUW aHhp6KiHbthRGKPbjeUKhtoXAQ13f2BzyFNpCkDmhvYFBGHS2RGYciGkqhmJiigihgaoDLIDKhIY ackTdZkOK7GhbaMWIJAtpbV1CDAsFD/B9f/DiB/WyH6/9ZyBfDOVBSHtO0edLG4IjQfC0+V7AOXz 7PcWw8wWScMhOhT1oN6mvSbHwWzYRY391uC+QjxivdtMSdihEZpNOHcmMGXmWZBcHrh1vdjNOY6q tvSIiY0oDMymZXwbxlHwVut7j2ZIRJrX7OY747iCqUgpIMAyQJBhiAZI8h0hpDSmSxJhRKwZgKcw 0UVTk4DlWKM+yAQYJiGgZi0RESoQ0IMShTSKym2sFChgh2IA1KBBEwYDASZEQwSQWCAZGVK0S1Mm HCtMobGjqFDY22QaQI2eXrjc/R9uAP077jOjEywsgooKSIqUhIErMPMjcsb/9ChfmCBtFA8uh9nr /VXBuMUQHNVNlvBBACWoBaodttaF0liej/CQE1CAYjoheC8c4J3d+38r+vD13Dt74KM0L0Dq84iR +NL/AqYJiRzhw7O0QPIo+HQxDIoKSsgiC4el1OEyboWCUpMAsq5YJzPKoCuaZrCdTEkGrMciiCmJ hqnigY8Cnt2V/mkgxo10gV/7ftMrFGDAfG4i0C6DQaCSep7WhSsbhWIRoYEYCq0U745mf0vl6yCJ CaaiSqJSIBoAlpqCk8uYISRBNFIBCQP8vyh2Bxnn1QGi1mSF4w8YJ/YTi+ceANIb+w1E/cf+LSjs myAVVCkxSBYiK6wKEpU6liHWptiAOwyR3VRkDxjICU5hSIWAhYsi0MVgaerf8BszgmlLawgook5q DYin1PIAMA/eGU2Xw2Qee3HfKUw7ROcZIWsyqVG2BFSVDRCQtMzMYNClo0MzNZkMG2qjqhILCsVR bErQtJayIjSiY67IY6O4MoqNKECDLClBtJf0XBwZUpWkroTLcyymUuQMApaWV2UyWIwWAkEUIo5d Y0thZiU++dsyYIaGR2pQkGaDWYEwkjoxwzvi1vTB6z2KwL86iiDE+/aiir7AbFESI+AgURsYQ3qL 5rHLiw6iCTcjhhCy+hgUD2FARMGfz7nAYTPuoJsG+CP8zkE/DTS22qLQfaKbaxEufbVUR9sPh/iZ uTsmYungec+lABN11eDRWZi4jgyDJ/k/e+zQe+o33yMxX/UwLSrQpkAH3XHvG3vPiE0RTu7asZEH EW3yzz8D1wVh5klE80Gi0aKBpTD1kKZe4MLFl3ebBndNt9Udsb0oxRwoFCcTHFUo2cjKCrwLkZkJ fEh78lYHFf9fQFMj0qgfAIpFNEMAUKkx+KEPLBo0Bio5IeZfOMSCETIEPANAj3dvl29Madael/pM uthhun5xnrQnfSxCVCiAVhKcfReA8r+zsR+4dSaYv4FpTcqZeRPi3nKljkG7mjWOEHNTu5nNeMxA U9MDIiS85dQSsgT35RVcPnNtSPSKtqbQZLdw6FopA8fB2afEOAqGoMLVEH3TX1NIt7ShR+AoWpr9 hvJLyDby0uM6rOkvdAKTxBRwAaPuJDSCkVGCi+Mi1A98ULRUYKikEvn3/AGriXhg2h/MwnJzZDk8 JnB0xTI3i69KIlMpPPA8UgGMnFtPRQDO/xJ55Poh2J/rTj6k7nsbbbU7+Jo1qlJPY/E6w2yaafST nF9qaYdQd95bD/lzWgo2fNrSvOaSkrhBwgdn3w86hr2BKZHt0jryo7B9BVFiXfB4vUfdBM2EREh5 U4SYDOn307X3EOw9x+YLBD681qx1JqB39o3pUvdPWpIeOVy72VwT9o9ZSsmJqEmq4+RiJvSwxgeq DA6jwElgS0WfSTbq/hYN3wWPIYMJ18rpYOrIyKOxj8vOhBmYMfilvhwmZqRQXmxsJ1SPymHP0lnV cRPhVFfG5hqE7WSuJpBSMtib+1hjMYnqI1V7xaFblYxqSDbmCGaEdHA9933qSaiIIKIkZkAVZBRZ F2tlNjMmKEwKO2gyICyJCE0QpW0qjJSxpUy4mJMGhw4KZdWSQb0S1QayANMMY0olVmMCwahWRQVS EtfabGuVsruYu+5qUZMQrYquM3Q22oxU2BEZslQyiZgtVMZKzf6ZTSA7VtETgaibSIwhvJYMzVWZ CJYahgmjGhESaYGXJWDpklsjcdDeKAutUUohgxaMrjDA1ZQjhs44NsLaWtJVW1aJDYNZltkFiw06 QxLskuRkVSaDLDBquxrRgFt6H7VumMUDUa2uOdhpCs0VRthJDY8eo5E0kBTEhUmarqwDTpgPUvq7 LxbAXhgJ/7oG3aOWKwIWHyRCanxWBMDeqTykKRGBAQTy8alDknmG122Cp4w9HWA5FD408N0XYoYJ VwS94AfRFKKFMkDIFiACKgXGUMhMKAICICESUpwBGU5gBDwZoCkCkA4SgIbMoDkIkQPxTWubG3tz BFyAA0pfsIhrBSMADg4kkNRvg48rANKfgUHuJUY4QKj6oOYv7T5zeJqvFHXBRdcE9rBvShiEFgsa 2SFQko3WqYSaYCEAiEki00lAgUqyyg/cOCHwREDxgMQIglQHeU0bqHiRMQ2/Sn/SJIEcspaIHXlZ E6hJjQyYTyEOcKB9J0HnLunA/PIYStI0mQZBQI/MPicBdUQJuZByDmhYspg56FKf8yZhk/qwhmXh AhENioq6aMqOHIwdRDIERK/Vi4osAg8CFxEPMoEYwiQnoKCl9qd80psJ2xKDVIFIETEgiMECqUqp QKtDQgoTCLEwLTStKLIFRILRIle15KmB2LsA4DyVx2kgG5CoSobQkbB+xPPpShIYKHoeQO5EeCaT UmoOkaIRiGohhwF4JJRDuHcAL9oxcgI9QdhE4K9tw+L7+je/Lv+DY3/cTRL9fx9yD4QsQnoFVGZA OYuaQJRlElIVloDtA9P2jQva8rD1BiRdycM4NH4k/mzjqUhCiBqUnXLrFSfSmyYGYZjBBjEmwnIT Qg+pNoH2MpEJUSEhNJQnbBikiEwVLEsqmIA/HALMqzO5oDtAIA3ZIBAwTEQNgAgQH+QhiFmESCYm iilQSJiViSSQIkRoHDkmOG2FlkBBnwFKZK1EqoMUCoVVKx3oBjjJoSQxohQhRwy1PMmK6g9+F0Rq Xcg12W0lIz0jA2oWmYsnEzIrMJAjcjKImgSWATlhjPckYTqaBShEIPjcMdxBKuGzNksjcbx0Txlw wYiECqn8ZTaEIkpbStpLSrRbWUYUssJBjbKFGjQBBVilCjluOzCWRGhGqSGB0EimAQBtI4soImUt rAFJWpQmoBQBRtrKjZCkINCUUBFGHSAbQiH0WL4pKUmeo1oJICArRIGGZj8ciPJUst7JBqRZsRMr jUHXfHOqoon9JogIhs47kKIWoBiqLnZPcV5DvwLnxeV607UP3mQHDnzDQaGA6GJqLDJCgmMLJTCS JZMhpmCDCszJMsCiDGCqhMos+5xcBhVFZXBkLMGyRlLaFUltKyUYFKWElLZbEoy0bTuChRykYrFF EaCKncpZaDJm3+vmBxmZNjL3ebn3pdxlKzXfZZf487iNwA6g1CuaUj+p5EOBgQiggdNCPY9eDvzl 1SPRW/NS5vjc/429Z8b6rsqZISSl24TZV19cFgA4q+LRL1g1nZ0V15zZgaiLxp+mokP4bB3D3YOP HFDdY9Xt3kGKZwJpuzPgyHr663peZP8NcHiB0cGmlqRxwTG93mGDN9j0kwwv1c86k9llOuE3hY6Q 4iU4YOMEEcSRRy5BxmHJiGnj2gc3MatXJuK47KA1I6yuWxDs6xvqDOH15HJZF2hPBqDb7yefSlPB Q8HbN6mDlXq53BUVcIiyjmsW7YtKpLUW5aLTx8cyKOYrMp/2olUvioa1hChnIhnhITaV5nd4seXx t7WKzE5qMvHHrWereG405pJZC3L1D1Lp2ct6MKowBhmn4BS/DycT5IZqh/QyjrVFvzVtnZVTb50J X1OHNOZLo5aF93yTOodTtIu/8A12cMnnQSvMR88RU9n3M5YhuahiaiTy9uzv1Bcy2FhRIs5g4RCl JowT7Za0sK9Z60Uv0S25zA/I53RCRhdNEVLjfrOozzyZaZ8EP292wdx36MhZzagXibbjjftUsFar dOZiMy4EKEL0rD7nJ9vsUZzGA1llq0Rgm3PTz/G0GG4CWyoUKj9mq+HfBYco4XSPjxDQUKE2WOfQ 4KSk+jsYaM6jtTibQj7HA+Xu57FPIuyHaUMCCIvJzaywrQptsy7XiMH0HvefG01NlH3LGsVqhwTU 53irZprG2/hMdTEQb09cTQXUPDR9WGDe3tqtqjI+pKuYtPj2998uxYjeLzdxY0aMuJU47LT2iFaZ 0ySjWzPGjevKMrxXC4UOrWPFBLqeJvrFsJv1gzyHEoLQwxdqkX2xY3nQCvNM0a4HLCprObiH7Rhc vVY5yTKhjBR2FXHwqPXO5EjyzLCzjfs/codd71G+49z7XLb5cpMMxeIhI89qgynQCQUuddlzOxx8 CXZzXZ6l807H58WSkvT1caDz75jyUyxkhjv4eBA5cVm8IKmeeMqlWjqg5kcZKZwJAIbitwYqEvC/ Hj2A9p4MvlyzqfFPTsd+rrFOCt4JPaCqbzbtRuuJPaPf0iNhd+63BXETykpxfENMji7L1Xfd9Euc GSOEw6DXq2ZJp2c0/w7m6kzpTwUuztunN3mHOz4TXIk2tYaK6w/NZI3Dnw22Gp4XY0yWliMuszmu qQEWRHBq41ZgwsRWdKujO/zaBIQTT5ty+HMwOJNYEjtUSTwR3STkqmqRyajEdn7onWR8sJsGo7y3 UdlZ3Hag7KU2WUp0vg44wx5cpEjDZReBxg3mfHErluO0inJijDByilRvBDwKVKYlhNlCOnH27NON vzw9bFyaNo4NuS+SR45oMJqVLUloeJUDJJofsqkdlJ5sN8mNYaRS1oXpZw8PBsU57f92QNvV23jn jjY+PerYKKc108VNK5J73mX8u2FMg8DtOOMW0Klyd5nFOJau9Uy3qzJjKO5g83OruHxzNC2WVmsH rFBKb09j04e4qcmGYbok5YlzY4nPbrm1JAUFM1EcZQdEbRdZCRfkCkSDnVNCJv3KmYSBzkNfTuYb HKPHYMt7U1gHkXIYhliE41OY+PTnKbSYBfE2RvxBA3uW7dgdAvgdBbMzpmQUG6PPBfSk+XxiM+kH eSSxGBgliW07axGYGFXyuIZDU8IIKsuJ83JkPhpmsPZ01gWh6FSiHKROctuOq6b75puC6aRS8G6H dCstMKdV1+BdcRw9QddnjQ69EvBLucEKD12uYlB8o9x5Tn7eCozoGqomsDflQTDwifviOsdQLlVw X+WGKQvaM5iF0ei9hYWOx6ajF0JXFblnttpGLgTzuqMmd8UMtWnlYtPJlem2AvOsn2+JRfbOTc9g d79H/zXMfFcY9OQ7AdKafIpuiCNVCVWyeu7OZUqUW8euev88/QbXixG1HHNFe82jpuL76VP7YHvF EtK0nbucK5+Nx5nEM7chTUPqSHY/m9X3vF43wur5gOWy4/nw0Hfl5seOsZkoRKe84M7trTMcJY/Z muz9sBOneTjmes8w0qnd5rHPcjrT5vWYd4LnN/bUwO+F7U665UNwYnbpug5HQ4vnjfUdVITpJnbp SHLueuvL4RStOd+pjhM4Un3uOlQi9uzAjCfIg+r0+AcvMJsMbFBkrxb80UJaQ8jLV4e+HpkH63Rp q16pCCqH/1ZAfzvkU0wrktmytmLkJ4YyRMDqQKgoAFQc19Cs7aBQQYnYUopOBJYoiaELFUVFAyIT YYIJhq9+ZjsGX1ztMakJHKdKGz3lm1R+UfM7XSjiKGYZAFdzs8SyQhUaSTkNE3e4/N+fvFwjnjZH D+D7z9EBI18WOdPKYxzgzEJ0He7qRRAUiagA6acXvtoQpoIiUjtngdjvhoPFwdih/vuTeQqCEOhB wuCAnKqCmEw0r2CkBo0PO2LvphpFTYJre6aMRwUi84znarJ4ArJuYEhd37zLHRx0cCAmrdiQYwGO m+K1FM7UGmEYEdP3he4u7abo97bU5IQRYIrFjBC2xyzk3l6OXPbg7N/Wb7bGz2ozbHNeZNd1y85x qEokkDdI7xSI4GURWBJmXV6qwDZA2BlU7U0qUi4KsnkhwZ2QxEwAOJ14gBDNIGm+3aUswGmG2/EA ZDgJPYbj09npBDP9yfM6fvoYYpEFEOgfvM4qiK9sSTmVUwpLf2sdL6s1dFbFEPmC21iiaIgvZj1k 1WojV0fVHa6+otsTIQT8d5wctkUCHMEQQkl4gNTdCTvddY+vGmyMx2ZAYXGj3Dx+8ZB75Y2ii0nK OyTp6FPXrkQeY468c7m7z5KUQQRJEyxMF5ZMqdwnCAI36HB5GGZaMeGdHfPZrG2LrC9z2N4GL86A w3RJY0FHSbLxRNgwNlFECQjMoD6eb4O/7yzvLTxSv4promVZYqHnooxU7x9g/KKdXQP4P0eH+0Tl +QHsOnx7+q5Y2+Qx2W811ogXrOcnixhtKWVvpM+++Y+gW7Ftr7iCg++72laH38nf7z83LyxhY4di 7T6qER2OsVPDiK+PskpMfedTZ5WhW+9jTdcJv4xJBJHF8KaoUUnb9fNRx66hpIw8Y3gos5vtzosS SynkohKVLZ9M0rvQazzvF4VMjiak/B21Wm9fwiO2HSwGQkNDIh30k7nn36xZfW729zmG1iczesTB a5ctPgWaJcxU1mPhi/MO83PeYd2CSTK+5kASSKtUGQqCZwvjawk9Sirnp+mSEgSbxBA6B02Ku5tf Hzdq4Lm6dyeOsge04cYU+o3n7Ow85F+0IBH20HTZzmiO3nHtHcBd4HKFOIXgvDm8QnzjjB0JqNQG o45ZixMGODFI4+YOjSHXzBiD3LnDjWj7vkx1uQt8QafMaED/Ylw1TfeKqoTkexHELL0TncRwrDQD gh2YiYof2sMHNwNHEvBqcwGyB46NlDU2EF/pnfdt6FwRO9FNLmzVruHM5rSZlAYLI2IEOgOpoLBg GDsXxhczvJk9I6WA6UfAbBiWBDWb3WuuDrS5Au2DUnh6DAWRMDvryKJryDxQ3GUBkYQhBGHztMCP NbQWsA9V8HQZGRkp9Hy371/Z0z8uq/vXEdvzYobv2wD7ED0q/WG20h/lDlaNClKDluHEE9jfvB1C IRgJBuyEzBQVRCmGFIn3jZOpAwZTOv/FuAqzwsCkO+WWhr9P7m2vGKBKB7oIcfq30di3ae/qiYkq JIohgqIr71RNVUZRBBBVQVRUEljOEaFdvhwPoGANIfqg5y9EC66K72p3NT/faaVNOP9VoY4wrMgw 7+3lpeKP5vg1tthuN+ksD6SjDkdyod7F7YQG8MX5BEwwB83PEN9RsUIDQ5AmsHbEFa/Aw1rAIsiI 1WlFkI1U0EZk4EjRSVtMkZhY2Yoq7QijpGlXkD8O4V8GciEbK1yGkc8ZhqY5Yzik2UY2ZwIQ93Y3 VtMZgmgAIGmlKsQ321pgslanF3BUEtSmGCA6dtb7EDQxtqOzp22sR3SuhJwSJRB0xndo3tlTcxRw qCggw2HI21LY1GrMtkxwSo5YxLECA1y0XpCtGmQQirGNNMeGtZSIthRg0RYiIto2WT7Cw4JEhs6V dDpwNJj39gFBoxOhGuBPN5wZ3kH8hGE8CYZkgXiQBEGEER3B5ELU+sEDciHDDyGFEWGAMggGGEQ5 U0FRSEpLF7IwiOSEED6DlPNY4nEhAsQeJVI2Yc5zXIJz6myhoShJd7KYJvhvwEh1Hr5m+1n1UxC/ 3+CWITINQI6l673z7miid5l06rW3FRiiqsz6i6VrTX2VKU0Psyhwp9P5plOn27am21m6MBxFlnVK MgW0+OmzsXvCHUXerXod6bgw85Kmw6q44SvHra8YWgFDnS8eex0TSYPmIoDAlPNes6GGA+hkiBPX OAj/q/Tv1FsHCR4nuoaalWVhQapRlIq7BA6fLiPDfhDHnskDI223xtNQidVJg5jwcYzwv00TheQB P8hg1ANoCokHEO5qBxigyaQ96dXu4yIbxELK6TyYSsknqSx7C8j8Pir1NLub91IdUJtk2ViJBRQV RTk1Fg2DckDBDhDQB/H9ou+sE+7hqeJysQisyYYsFyJAdiGjvDu2/zxswyIh43Wdwgu8i9AtgLsA kkMZxB344N7xS1yw7wDcPYqd19mQ2SdUbYUQmhhjgwv7Sf/VOj1rW1OvZr6dvltzehzkynA3ScXf A4BuRvEa29PButJtEHFVaaddZwifVUGKKuSpyENwg0gIBegvJUhRndMnCuTs6ltBSVJuwGkHWYpJ FtC1irVXH0v9YX7jo8P4e5foI4fRn+jzB5EyEPIYI9RBR8yAYKLhC25Mg3eIyMNttFWaoVkRorta KkbUQsBKUGynq2sWhUGpBvEs3oVuh6RUUyOjwUXUNyROVCjJhIprHWgE6EgumUYwVNATpTJfZEPJ Ne48xCyNiKDeItnzSM2w/2DPsMjw5PwXwF4ABsYETAFPMn20n1yhS+k86WlRi7hDIfcs5WoUfgg+ 7wyHacX55/BsCYVRHmhAMppIoKB6qFPSkMkkzggIaYB18PVTJJh6/NwIbV+a+Y0VdrRMpweyEIJP EfYnYdezL5Hbew7+tdrUVQkMcFymJQe11kJGgeBQYwSUm6TBgeQH80XkkkHGToSxqF1n19Vk1sbw 9Upcembz472Tvrk8rnkGLu/Zrz439Mk/Zr8qrpbG2PXQWYDVxL1U9Y65SWv+cL4Jm/GyudNGs5Hp eskpaIvkHgEoBMogRRDsB2XOJW/uLO0michzR+s27UqzaJaO+g5L6jzfPr7Vtb0u/pYhVGHOUjhv JeLjSM22WHRKl/VdDLZy9mB/0xONcypMPCMuEHDxPfE+gch2h2i4ErviM9Oztrw8+NSFVZ2PGU4J dm/sYZIBGCxCTCY3Fcjv1bcsYVtvqzbILNV1VwX4VoO9PnXgv7+X3FGDPhOx/4//05hgEH8PsDs0 fzSGCRFM/gvijCR97T93Qm+iDGdFC1n2KypDCG1wSGAiHLhhmRhyhP+caannxw13cSB9x2BSO87m gowQ1AFCUDSegwd5xzGWCipmUgr7S/ctR7x+PB9/bb9//3d77McuMnCoefKBkIzU5N4F3bCMgZCA 9u4+wLTXf8/bf0li17rgSRO8HrYKh7SZBAQowniRiCQR8da9xQVkBrR2CIt8SdAUyanWYBALWZES T+qcYo6B7n0jGGQYmA1UDUI+n3bguD0G8JSfQ3TaGiIH3DA5GFeHsimar0dNaasJDI8kMe0Pq8ew KcT9aoV6OZyR5FLiH2wXb7+TvcxVTCU9tftQGjEEZEkGFX6vuG+ZIN7aLqk00ZglfokYL2KuUdRd bt7ypKTN9xqpMsd7PreKQec+PKM6zUUJjBApuQu4KfkqvGHiDR7AS53yKfZUVMtAG3PymjueXnVL PzhwE2Dt4R8XZ8QfdDoMkMzPqtX9wgUZG3qsnX9nf2WLrrYE4myFYO9inBR0ar2ihcKKZokNIvxp /+KA0kBidRiJU/OPDy9XZEGJq2N3A2/RH180xd0CV+cfF6Yo9tiwE+zMDMEwiqQgpitoMKKYiJdk xyZjbAyAJYWmYIFhK/64hgozBRRBECgfsQmgDJAk/ynFCwL3JeBAIROK88C4eRwphdqckllJtANI y32PPWhZlLJ9glI7WyY7MhppaEz+sga54LrI37XSjS+Rku1QuzEqtDt9ANNJFGhCTMuWx6CwDnNQ 6zDqAGrR1HIggxCSFm4CGwVV443OJlHptsrgRTbw5ItFNFejNzULUi028GtwNwjC3WTYkGEMFrUw TYJsgqtcWtmg3W+YDMRl1xDZw2gZhhpaa3oshXq4tQ2g2jeR8x2Ff/gqgbQqk0A0MSYNCUpQu4cl kOM1rRWKB2cG1zkUavbMARm8mNYzHRpMGUYgZslAkwCsaFlRhv/FIwnAa+cJ0LIbn8YSypOdC1A6 JVY2h01UwEvKAi+gL33hFC6mSUqXILCwtDQlKwuXSkHXu9xlb5A+g3mC2MKPgOAa60jGwu0/HNK1 +P9dzDq6g6nAgwjUBGdGilkMhGNvviVVfboEH3S5eAUBgVBCXpeLgaA+uiqImioqyeJkupeSBzZD yRbCPhitcZ+VVUDx7wp/GnFhwnnk6vIwjaD0aCORGqP8W6jAPp2QpxTZNZTU5M5pg/PqwJ9LD8eW 1bkFQRP9Pjr3Xy6nqYq6LvOIxFKkzGUhe3z1rGzLxreglLx9xYOle8ezS7e3g1e8ao47+rI6Wi5p 3BtLZCF0406fMu0yHAMiG1off+YmBIcCGkcJZn8UZSQmooGhTImpExHDAEoVchChBoRCgCkAoAKF aMgFyAShKGhBKwgMgwhyQCgMfdzma1O87lSn37T1YcjKUtRLfeegpz2JsxYr8qwI2zQ8o1Pj2O1f XrsXLRpo4E0GZCHJBZDcQtfZxr26qZSqKjVR/vUw2eNK1CUfOoW26UxZEwlDb7yUXgb0yR4Qut/d nCoKfl9APxCcg6ppX9AIHwklgTa3hoH5R23vCb7mJK1nlgXNBDbCRVJEPmNxZNZ9S4TJCwnmInsm WEo9b5LkJGKGF8Bz6hPP+1NSOXnwP0SQDpsdK2NRNXsMP1qop2+3qU2wcwppmiCxZKEimYyjiuHg GA8gA2KDh952B9vI5a8w6ylOXL3HHJQV5lsLA0DzSWusVYfGZ6fhRy7IF3q9NrYN7ZrJIl1IUUEA qAB86SCz8JwO1oGurJwJktSpWgUJlMHCv4ncy/a2BGTpGHY/DOynMvL8NZFYw8w9aImrx9ZCES+A 9e5Mdw+kLfCaq8qkkDLvUGZLUfEPdWN0c81doO0gPkg3h8cLEId9pzFal4Oy4apzNCHSPMEvYFZC dlUcDcBYGDV77EUJ7dEEKFoLZvWZgbRti2iloxhJJIia1DE3zFoqY1rU3pckPLovOeDkacwWbTAu Efj31dlO+ba8Tr7oBQf6PNjdBL8gkMvdXS7gw+HJs9d54qUOj/OgPtMow49FT4cPsh7Amq9bvFRp VAy+EJ3DWs+LMO77/ar6gB+Lw4HGumfP3sC/RtRqVkNrWHGUqVKwUh1akKwxMtlG7aMhyGbfUOYW tRCipuapVy3QlFKILIURkaCFC22Vz9pwVSaBA0qiiosrrVcLpFKQixbSkqpCH/X7idn6DJ/oJ4Yi 3D/gXT8MBz7OvYJzGQrBQe6nc1zRFoGDoCEiewE5wgSRZ4KD3PyK4TwvjrZsqiXMjV1fxJPPF+cJ Mx5IBhGeQZ38r2PFl+Mh77ovJCQnBn67wUBPpgmg82K4ZiZGjB7uZ1d5uPGUoPxecEMIrQ5VYoHK TQefIcdGv40ToREh6sFBIPEhUAlUlQX5zsRE0HqI5R+A/0hh5UTY9TgaP7pCoj1j1leuyVcZ4+9K 5xDUAHsHGDDVSoEEiz2WFzDADBqQFkqew8qPylPhRT+c2HuMrnemZVHKOOUbJ+uGfPT1RNNv12MT WLRRQQGoB80QNeql3kfInePCZ7C56K+7cb7c3J5dlRMjt+Gz4Nf8o4P661xsNzMcbp6Ku1cTKgex EKQkJFMRCQBFRSwXn5mcq1uQeM5lhjSlAkzL7sEEP5wxrV5WEIDlstGLQp4phnRkEmMpYVo8IjkT S8wUSyBb4fU6kOkII4BEwJ/SOxVHbONmTfO0lybT4dIF8cULjSJPBlW2B3B7xd9Zzq4DTS7aEwZN 4FxuFNTFnNxWyHRuCiyTvofUcG8YHpbcwOMJznTjsTCauBnwIWEtUbDbxDJA7b50Tc2vFsDlpFaW NJjN7hjHO8IOBSp1RBHZ3jm3W7jSwnTPWEk48fI73+3XjnNI0yyd+hNEury3rJbelWO7euZIIodx 1vb0VaLF3d+unxhxbHaVFkzNFc0dSO1EZOonm4bimxHCY7laHYaGKhmhJggxpYklqDEeZmQwZN5H g1C6B0dsD1rx0aeqMAbghrdnjmikIc1FMNzTgQ2tOYqWgKDazAWz9qpmpjFay7CTLgdmlBYyi2TH fRqkEZE7+jjsmSLneiX2QP07YRI/XOpnZxSY4XjBjE8iUk7zMxeHC0XUPDSDqcKCamOcYrBWAxnE BQUd9TIhMW70J8WVjNLoyNRk30YmaRlp6B/QS8zOYiMejcjV1wEwzkppimjrgpAGN8+xzmKvMnOQ MnHoX64KofTTnDxvlaOD3NKCC50RjpEylBJMkR+y4ls5WuJmbQtAg6PS19lsZiiPBBCgK3u9Lmme mbwEu/uQbKHbnvMVWM8euo80dm1n0tuTnv3djXGWkQyPj6kFh7vghnLRzZCTMwotqJlVIUFBbGFF Vs6Uo9Aqk4HFNQmLVxOqhpg6dfHWw9XPzhoJICUog881tOg6461QO1mlQ27sKYYc27vbJCWosEQD Mywa4BczIqjSQY6MCqwImVzGzU6pxRpzRCXS4eL29k4jmQzNnKJEcJEQ8SjTp7IdpxbNDeuY/cYd BXULcJty3V88lmLKTUkHbsMMaqh8jIunlmGgDmHYWnJuwTvDgYOuOdZyYWOWgSY0jsyZhIA5cZwb EQLoGCBDKBQyJMgxjaBDNDNoZeeTCZFiLNd1wYcNJBOg79LzLQRYYEltcwGIbjBioUyYCdSAMzNQ HAzJ9DVRnSETLwTEmhDOnCjYqbxwzQedYMIQm7XsgxnmKFoQQ4PtjRMBwmhAFcva0nZHQas5lg4I sgBIbgSRsE2wYcrcQG00xghmjA7D5WpuJ7SkVqClZbBD2Kd30dEk9YIREbF0y99SpkZyA2kDS3zQ aO+i6EulKAUQwTXdWDdQ+OWdaEs6HMAzIay0TfUXBGOZmAYGRKNDTiEoXmSmrtskKwwzAzNGBsqg hLi+sZighGBJMI07aXW87oMZpEMG6lSxGQ1ysO3S65mVZbL4AsMUSOgSExAqYxKC2MoykBQKMSBj EJVooKZB+DW8xRh0Z3uiQoocqqXbpnHMdpeEoZKsQJEosOrYa3/0hh02HcnB3nnxWCbWFIOmdtC6 TAbLdg4cXWDub0mY7tw0Rwa3TLYAmYHCcPvopoVO3KEnW8bM0rw2MEkRWojCB2TTc7TDZFEidR9j KZI13c7pwEjXQjOocIUjlyAtoSGYHEOqYFthmC4bY01S0ESsYcMU58herns8aUioI8e3OCztzG0j s0t0pt4i4dcxC7pDRE21eUNWYp0l4R0Dp18s2OTckhAwlQ56ELBIZTEg4QYQbGJsbEuwowdMkFco d6HVk65OjhJDYE6m9vTkoGgNJ0+c8GRUhDFgzcNqG0rFQajijB1HE4ylDzzClKAqICkJSKkMQ7Xl 25bgSK9GhIdnUdqHQ1BIyGTnCFEo2hkgbChAXwPjLurYc6RmWSaAggiHNgHZ2Uk6zGO/XNNaOJUl W7Nsq2lLbRicpUpQoZjaDDc6dTYJHVd4ljmq8UdyyzVlTSbgQNwExjo0RbYrvGV201xoEw213RQm TLDJAkkcjnSaEZNI31McD3ycldUEc20Mm0ga+4dUMWqTMb284HhAocNLnLtkEJAYd5xgmXzZGaZ1 i2chIEyEKk2oHEI6bBZATF8ToWQQ68U33l2E4haF5gUrB62Q0Im00MadhGNLRZyTx4xLBndnDOhv VMGRIObFN0BLLnfZe5BdRsNVgyQ0aL1CdMN2oclFRSRSs2UeiVhoRTcAhi9QQ2Ikc794ObKhA2J4 yUTQ07xNCrnFQqSdsu7I2mdoTSQOlgupofo5jbI4UIVO4IWGnZkDVoXHW8VTYEiUb7TSDKHZwzc4 HoIZDd4gS5RXSHZjlI6pQ6GdMdCYvXbJWzIYJEqHGdEK2YYiCWfQKtLtoJynaUcE5nJDs3C5EaEH aB2a9woA4Tz2xQx0DNTBYx12fklwwIF23VCKCOM0MEHLbbQUQjxZDinbvzs2JI1DxIBMkxwRMHrC CKd4M539PI3O4JxxPznm3a0+XXL4XStHG3rHJDvedvy++/GmscMkXy+CfEJD1xzoiAfWriMNTMtu HPOTXM8b04uP3j8Dk+Q6xMHRozty2RA7pw2HDMQL22yngJeGqbOVGvDxXRiweIgxpV0Gtj3W4JVS aJabtYVQq0wpZFMQQnPRDN+pz5zSbQs4yRhSkifjRWDpGdioTQcr4cB2IIMTqTl2qM0milFnVBFG CnGpYcbbdl5Gt+joObeV9sLDRRtzRSa2FFNCHq8GixwQyYQulUTF/hgiNiY+mB0eacJ3IcJ4PVx2 vw5HXfSqGZm47d144rOOTZm8tIoImdiJDV3svGVy03D1HOSWHYnNFA6xleOI5FNzz0ToJQFurm+a VYdwYBtpEYJUaSOai5nQXLo+CAcBOHkCUWE46HKtiTRz1hjPFxaXQVKG2g5olIBYuw0jhmkYcDi4 lob9v0VRvOB+qdHHAsNB5+lsQIyHS8Et2rgpNHbwfTTrjRhMLlw663RKQSdm357QcVnGHf56c7kp 85iGpiyM+MGXXQV6G/XHDbTIZCWW0+SeEQXujrnpq3dc3kNpsJnRj5p9938P5gNaVtnqqV5qZi1D xm5tJedWH0XHgKvlSoIvaZTsSpt8OseVE2kUs8Hl56HZFdIcXuhlmNHD0RjEOq9DOyAMngzaZMYF sQZOfF1q2Ixup1b5dmcM3b55+TnwzfzPj2JGunnlKbU7siYyMbHIROL3e7e/O7OvVR20bUCZU3f0 34q0B1vqFo13dpnmXeD86vwbyTn13M4O9RtQefHr7xui9smf2geXXs/YXtgTO/fGsyF18BQdwQkA mRXj1MqRtL4o364phi2cc8tI8FeYNi0zDiL5CkGNjziIqPshRgyKgkQxHe8RfRM9VPE7Yhxvgh4R 4Q/woBzqUI4QkjIVMmro7k9X5j4MrEOBjHDA95vg78dumEaxbeezek65s4MrfOIZtNx2YN9FPbeR GD22ux22a4rsa2U0LSyhtMmdjzzvDGuB4MCgO8FYJZy2qTqimfGGPG2MIEkkEO+4WInENhszJ45D AcorLJtmOo4k6O+Hpas95NtfFNXIwxv0y4Y9G7MAnIZQaj0WvHigkMcjsOBreyh/dC9HPLK5zlJY /btI1eiQQqJxDsQyE4gQA2DmUi2cN0kmwgXBLNbkchAkBLgmL+Mop0ugZhbER+qyuo5kv0hrTdhU OroOG3lMqbz1d5jZZFtrdmMr9Mp8nGo4xviQeiPgOXEWaJJKEWahoyUEEfE/ubH3rmzVM7QhTTm8 OvYBhbyEGQcQSJvoqpTRWbZEEDVqc6wgYcVJMhRxaGYkTOyYDMOJtJbDWuG5nIiHc9BQBcAQEroB ttzP66tk64UJHcXJ936fBd7/Gl7kDja4Sjyn9YbAZi8SdZ5cbUFjGkOJ1B6tFGUy0Hgb/i31ZhcO VlBAVrM5Q5biu8Xj5DaDzKhcwoh+zaD2wCzJ1beFi47oJlalEo68y/Db5eZc+wc5dpFD4UTrEFJE j4xTzhGKHqkmZO87789NIuRYcwIwb7YSucOXRRr3XMnMk25bWQ1ad6xSRM6dpIwipxVqrdSYMe4s mE5iHBYchEQNXKzZjH2qKlzF1PpEBzgtHjG7ajQ5yRFZmFNNMdlfim7Iws4iFfPWuurvWTeJRIqp P33OlMMIz3jX2q8az7Ixfz/PF/cB2vxLP9qLpitSxh/embKYIPPb2Q1O2caGO8sRC9rFh0Mwtjty j1ZZ3OB/emc56HDYDGmImtYgYyKEIpgpHA6rTY4YYE0CydFNKUmEHlTB46euIih6tjFTmihAaYx5 tRAdMJzxW+ps0Lnq3EtDQVM4GV9QKQqXADAzNFFppLBkEzqJdIm6YHHKAwDjUkSDBnMIkZnjAyO3 07Trrg5MNaEQTA0NF/5ccjvA89N0zM1DDaRQ6i8gwSODa2ltIJmI5XAbKKipBLhAN4ayk0csK5mp LIWCkircDQgGwa3GIdKEEUQhzJDe4TTJVahMhyCBCAFmIptclS43UzByLCthLkKTchIsSJqNil7i BgWkgPYI9SNBsS5mVTaKmaXaSAZobJyDmBtlMcXgrw6ECwQTRxHRsaRwMCIcQZPGwtbC1slwhIVE uWCOAuBqSIXhuqRoLsXYRVb7gMgoRDeTJWlWkBMlAOC959+9gHXc40cBs/F6AqezfDeCCKCigxRV RV+Ap7BmAs4fwTsdeVquhlTx8oVGMOjRH/K1bujPAsKzn7ew60zZ5pRE8SFIVO8ojmUiaBzx7TTG MSOhenjczTSxgwFFUFLBJU5gqAPnhy8MLhkJ3j0HAzZfASEIkDVcdql4o71F+YiKhEiB6TtA3V4k V2lbOP2/Zh2ynmr7/fDF+7WeULTaonv2p3YWtQSAScyQF778gEH+fwXoHE4AYDx6+/DpBxQ8kEQ1 B7ETLLKsI98iLnagYjeSHP1EPybp4L+bK5t5tOva78hXYm2uOe50DidveU9/YtUUMzS0A0BQjRSK RBMI5tio1igOjGIAghgUVZWICSmolqVoiCQCRWWCGAaERKQVQ8anjIDyQGlBGQLgvVL3/AHeSPQO ESRE1S8mR8BGFonEPAJlIWkYgUoXz4rkiULBCuBKmQUK/xjuA9CDgeXkKjcH6WOcIc4cCXCTMNWi 0cdGiA2qchVVZkWlaFNmNSplULKUrNMxAMSZcw1ilCCCUhDV1qi4mjKxE00giGJYxEgglUFe9lNa M2dcWBrEbN0XZAmCFluox3pJjDWWpjg6Mq0RRmFDBrq01CiohBKCoUgosRCN9ZGiQMlAiDfMpRmo iTJkJ5aOA3SfSmxkWJwybIVEKSkEokBjDyJAzSSQByFMWEjRbQBo4CYnSOCb8ZgGlKbib+HBIN49 lkGx8qtlkpVMuUg2vxvdW5rC3qIRnc0QGPYOSMydKGHD4NTLCG3zcsjiKMlTmhyYcK8G+Q31yW2d MZtmjDUte9614wpbAaYLujAVuZICHkR6R9NhXxbvVD1nUDSxKMhSRMsUCkqgYr5t/it/XYmVdsUv nwHY5UIxCNOzUXOfZpS2QIhNoOIi4WiwIaZ6Tfk9cFewlGzWoNmiQrz0m0by3ZJB3GPiIpHrG9aS TazUujLZG8mWIxsbJR6qkM2hhjRF4hsLWdRjRhFCMIJSBtKKKSdLXsa4drrjXQZOmicZeh0y0iFn MFxBgFMTUQBo4Gz60gHcQifXyHMN4QbvGTNrDRMa8I8Nw/oij9+NpOCkeyPf1k6/63+TZ9KQif4X 7Mw7Q9NIREQr5UxhPYGnBTzvJMbzlPrtZqsM0QuSwe4dyFhmkiK96zIwClcmj3JJCnxQ7NeaFogc 7YHw0lKhQ/FdgfSgqzPthns3IfbN4Mn8KX3cr/v5F0PhB7B0RPFR+XveQMfGsdASbo+IOnK+orHh NrZAspoeXz557XRRd4EGPLer1IvpICgpFCCE+QGBoARLAcgn817z3K3FwbTQ0qV9Ygfaw0INg6bY xqUiMkca2jEUKwrCIIcaswom2NtTZDEVEUBjBloFTtymDKVurKpioSksWRrHE3VThYQpEfzcNttn fbAmbdtUas/jNYmxlDJPc1xmtYQINEQWUNGktiSb7cDrcZ4plpuamgWKCIRFww/SwNSkffoNgxSh SwtbzpSZESk01EZgrYlaLPO4ZYVgUJomJxEomKMMjDHHMhaMJckSkAueDonTIe/u7bG1gwGZLVLB RaJJRi1lEUtLCosikYyK0oJsFTrspzrqqoDHsaEEQ06JEwcQwDGElOL3MgQT6ZRKAA+WBQyEUHIy EEIhSh9+QUDUqgHxpIPH1uJ5COJ9yDRazI8gfqfPCYEdkRPH89GsOvmOUncMZVWjRYLDJqowRMwo 3ETF0w/3w6U9cGya3I+El2e+UE/YTC/tlKUo8iHIXRRDzZJZQfrPVPRszMl8JF5oZmZmPe0dCSgy yyxbRSBzM/zB1MCgLE/w/zfi8DzAXW8MqhWGhrYvldAXqHr6I7jEN57hkNNCnd5O+9MAmbgnxQic CRBWgUAgNr9dsUeIEnZJ7GdFRA62HQ3gp1EJjOLCdijzCPOvKOwhOafih6iHpNBkCVaJ5Yqdtoff 8nxXwSJ8R8LPqimzZEOKRBTcZnIFgK9yShhay1CSPVNmKeILhexC6JcJK3r62/D4ZwnseOfIapw1 ZpaxX1wvG+arcN8HyTBqoDADqu53OWkMaBiV0nWdIpiJKK2wcsKhyDLmERQWUTFExvWho1lLMTVr pzWsUmJWpUsRQctYLlbIGRMaFJRWMmSlLYY4ZqhoVDSRQNauMEZpqUgiWUsYc8KIi5tZiRRg4wmC TBiCUywtLcoQWM6FKgGCBDI0YG6OIg2DY07TKZkTIwNOIuolSLAXSWmoZjIZaW2qmMctxHWsLLKn GTEQhiyUwZFmQNmXGDEgDIIFLWBmp05iM2EEAMpCJBQaZFMKhGMRHGJWwqIrGVraVMtCQWKTGQoZ VRZHLMEyY4hUxx6kobEKGRpgWhmRTbatzHTpgV1TGCwcsKNHe5gkNTFlwylqDhZyyhjFMa4gOq2m GSFKCpYqdJigREa0hA9aHHrAR6uUnYJuZ69tSHHqDAYvjlbwjMpIVVU1E4uxeCd3gFQFLypkXClt CoiDGRJO47YG+QgH1oqRR8xb4dof04X3/+W0QNcQd7h0HaHds1TE7JjjU/fycF0MaRfQIlhCyxes Y3OPK1UObWW5LE4Go6X4sFpjP3EU/ZcbmNVJSHciGowXhAkKS8pu8/pqqqmvIAqnRDinegn3TnwD R7TR8vtA93YZ7swm7pNXwnQfBkOfUH1iwgYPNOo604I4UeP5NCHawVzEDuGETRgIYDtrPfRGEwqS koaaaKK8UYSgYShQlImSTKG5rAQwkCQghE1iswxFCGJCDlkLMtmK/ctBBMqbSuSPzSBvCgjJDZWE qGiBw07eFxZCawzQe+QZN71J5FesBCV0dfDlrlJ7oNFVJqPmtPhhOpOch1B1DbIA0MtyVULBrQQj PyuNghEjsGipiUhyptTQ53nuO7PkDROyJ9OwwWLECuaK4GEY1ZNAtBHuKOyFjZQmE7MKURFtAG8s REtETCHazXxb7Damgqhy9Yb8UaGWJhFYSKGg4RXKKBuzGAb6sxM1SBgyatCGdg0Gls+LBtfpTXRg KX4bAu3wHEhzhOkmbGRA/Z8x6PbSoIW0ipJFRCpAfDGGEcjtkhJkgQFybZjHMHCL0cnr9na5mjTH WJ5qqAD9vXezIQ9GlHlgGrUA0njjmOxGjwgELC6fOvvd25o9SKT8aeDu+4Pd1OsCyioNBADD74AT /B/sdj1khAQyUgEp12HqzH/42cDSo0kk1RE7YYSpEJERQkSzIQ1MpDKyIbxhGG9AMHPDtI88f2GC gMlQ32RsHtTMXhBVengop1ABKPWMSlMbeO9R8fh0OEsSYFuaE+dDYMEfEkIlQpCJOzp0jyEbnAiQ EpCjMRUTAFEBERRNTE0jNRDJRTIBUsgAyVFREQUUiUsRDNKEpAxIrQwwEtEqUqnmR0OIDaDIxQKU A6oqIBDJA3TlBAliyUp+hVF4A8IKoA0hARNC0EBQTN8In4T5N9oYKUSIghoKQHfxQW+0H9o5Cejw wuYYMG207DBmWOEIwKzwdtr4JuJhG8e1T3n6dTMvM93rpZyjqzn2UwwNJ7zV2bF4tRHiqvFLUF21 U4bBjAVowrxYbdu02gaY/bbFOXDsfPJo0unSCsguWVNtIWaIjgWKdHwKwyIJYr/YNLhBQN8OeuHY 8brh0xaghqS+4lnTT/mnGw9qBO3JrZFHaZqgfTDpgyZX6kHus1v3To1TtFPLrmFY2O9tJCbGDMvV L/AN1qtHHde61MS7yCdt2HKZhtE5RMtRX6Gb9qDyAENYQGJgehODoKimx177tESoviaKqFdkfKCc 9L+hMvJOM4Y6Bp8e5UJWptsz+k2ypTcQOSj0DMT4yH+MqO52MqJVPzYLqVOXUH3nDMjCyWlFFW2o jUohK1tpYkS2K0MkIFVUfWYsGDWBIxD2+JOcecVugviRTcAlGNOmx/Ggxgm59kDOCnzyvlKqQppM MgnUPYIM9stjFVKgMAYeHiR7iAdfhA2SpS0okL4o0AwTGNc9wEECZREOmYqOwiD4kbm6e4gB5gIa jUMUDbYOzvbPYigXR4D03Id6Q9Hs5fJrw9PfSvagncU0JqygqiYHYIRQwxGMoEg7SDgHhNpo0hph 7l9pEOwD38m04F0oG0hAKTm9xbdcHNAiSaKcAWHNIceA7GvHJsX6l+aBSkpaXdR10oCX8/yAux3o nQ8PXq9vkunpymdH+6rvCH3QXfCc5HstTH38tP86RGosQmWCkFigJWD1QfsvzHuD4E65ezzwy9kH dGWtCn67eDhhy+8ma2jJe0nyEQsnrLEphi3cDxSgfL0KQqJTc0a0NCJVSASAyKh0EW3s9IfvH2F/ TdfKD1jkfp/kHiyH5g/YH3qKWDrdvVzSqJDqn8idHXJIN0APyAJIjzjEghYAB/9RMilAKClQqWAa ghJFAmaVEAA8CAHslRfGHoH62chpYpKISAkDQLjiSoGIBcfGbu84AykRFKqiJAeoQDDoCf2CqyqG 0olwPjcQa8COBAakSYaPzD1YHP4sCJMz3eBBVLMFyFBZ6b7j0bHyG0hsqMMWzaxwbgYzUunFLew8 PiMGsMoUkcGHFNi0vEjDcIIhzsIFJEJu7B93QHYQjVAsSihSr4WVBMHmJcDMAydEJQYSNaZ8YW0a JNqBCkSKCEqAUKVShBSlmSiIRPogTUqYwp8Um25YGChktBYRI4UxUABOzIoVAgkPoVAMHiHkLCCb 4i/jEIX/cLTIyMsQNKLQg0CpQSElCDSg+kD1WACAfDinuRIc/ce9R//vh+nlM1N0GDBfIHbBRS6p vj2O5HuPqAgUShEkIHWau43TrIBvqmxHMwcHA+1eSKPC5qp/xIeWo6P5MUyP+ZI4QsQpLpLfzB3H SWmZEhKmYgImJYBIJGFFiRI6gCUHJTY4iG0BLFflxxgyCkTEQocokAOZJEpMxSLAFKEwpEDDRQSA MSkSLBEtSDErQpuIGwvcKHcSLsibzusjErxVPe+oDBhiCJKGJCJBokX/egBBsDoMGcSE3HWkHksB 8D4ggmC94w9PoisMyPGAo+CABCG0i6JF8xtglIUqFCFASywSkBIUwQrQBQpNNMjTMqpVAHlUFP9A 7zUQQo4ecx/JoF9T4IOdAHYmUIP2x+CElQPyj/A6XyqGiqLBS6ZxYwT4CNBoUXGxR935t6CG2eAN WXx7EK9ZucTCpoZAJ7OyEYSAdv7HgL0dGMTvl7dUlrNh/t4iaDiXCHUsx5EBJev1vWXevFB1hWaD OYf0AeAvd9KeavGKdIkCePKF2QLJsoY2uRCz++ntgYv8OYdoWTzB8QejbdXEDpy1Se4xZQ9MYw2g vUzce82Fe0m35pMnvb9NNDQM+fQ+OFiGuRw893A/Lw6BrNSjdKUyOhoIJaqR2OOpHKkZ9/FptjWk 5B0xzRHAwQ6H/oB8/jTt83sUoIiBAqoIhG9fG8AJJpjQKPm2S+lliMGQaCJJ2CwZkiUICTXDDAdR UVjTK2sMYigl2jghrrDdLqqImcADxxAfc6lQT+oijc6tpwFoaKoGhKUS8eZTyh52SajaNVBQaHLJ aiEpCliQooKUKpQNvNU+Y8X1/qfEpm4UnTDoYYVraZDBgidGBRAWIMRJBB8LDY20sMMYuObED0hf nGdkHdgHyssBtVR8XxUIORB06fDy3DlKcJeNMZTVGGVZrXseC9SN2kIUeUQPQ+J/K4rFikKCMbKT 07+t1393sIAKgmqaiiCapeqX0w/v+7xdS9gUEB9yIRKjPzRMZhCFslGtEqNo2NlilkEoTGzCIJyD EqYCRpGIrkTgwktUNKywRBKGH/t02Ds4BoViQiksSahLUNsSGCYhF5oqrFfs/N/B9APg8y0cx0nI jqXdJDdSTpbbg5FHfEzB/shCLFm7ANJyhxnZ9WD6eQO/7IVU1HnoPjN2NzeYtGOQky/5BzD8dk0b obVRZ5R+Y23yYW4z2nAFfTDtPKEowBT2hVAMQh9U6hiC/u3GMFtHZ1WcCbGY/apC/6NlNP7vG59f TomSLjPoejb3JSaja7EJvfD+Qp20/yZ+tD6phexDkwEYluxGJSPh2bk/YP2lsDj+Oy+bBlJYT27R okwKn7n55gxg2ZqehLVexBSEC2Jup/M3YbybZjNx7ZQu7w2ai0STlM1hs7XAbWeJNlYdKb9cBC7T HHFHMdgqWbw/CVMaBMN9UtKgYNtvidZj2ru01uyKIL+3xkM0HfoeL+71J/gjsxwg1yj7g8ioWua8 rDUOlETxptTZtT5Q+rJ8PsNznpxhsszleslFUXoo55WMcKytcyRElQ0p4R1KAb+OpPrv4fDAPmUe AB8ZCwPj2Eh5h+cDwvYy4GfmTNGg9UsN04HwNspWLAQQXA+Tjlhe0vR17NYwstPH84O1zjL7KnTS dIPUqQQIASYGS/ODYI/CbSZo9NTtoyvtJbdS4sFRSERkM9Tu/rO4s/XcGn96m84gfChzUUSCMIin U7vUePd+DNjZ73GReMlJEPvRs1OSpk3j3pmEFYlxvgHZ0HFX0Jxxi9gL8N4zTbUW0VWlEy7EEu4l uZu7vBh1Y+CJKl1LpFTOGLE7op3UQlioUVFkvE2IU0rVCLRtrTSTkx42qLDgmzjZY3BymGjhHFOw POqfSPbz7DFi7JseF/qoVgmu+1MBM8IbfsBoBoR4HX0jkQQw40ES32vUuKuRFD5X62thAidJQwgw /lvNWBcE89FeesIIqiJioCaCCokqvwHWmGxBtD2hBhAUPACVPBXV/AonhxOkmnZzpIm7VjqTTRbE jkZTODnGQlchdv37bX7qGpN2XqQYggkUV5RAO9UP43SB7WTgGdkDzduWVKsqWlp39DZu4iTimhta mgaqGCpapE98FT5UA3fM9u5aGAA7D+ZYssLFjlOYsX6TQN5PzECeRAPv+mfIcGuPD7vFCrURKLZG JRZSGGMFNThJkOTMwSGGRatS22lotKCEhxcMaWJLUp5WgI5aLJCyUoyFALAoM6hSGhP5N/J2EFBG jRp0ryDgInLJmYCQzILFWnIRSlvyD4ibTlpLEsZA4aYggUjlAebT/nGUYjCEOjBUGDesGsPhMan2 d7qDNHQUx1slD9kFTWaoKUt/Z6+3QKaxwqDR0NCpq/mJdapEogGnDQ1cGQvId6ynWBYDrO85v4B5 ENuontB35KuraEMAPQfegGhY7oeaz5AbDqf+reYERYLln+vP0Gu05hgnWHEygN4D6dyz9ss2cYgw EEetvbZTEFFGn+e1lFstRUwe8p/iZhmZ9n6XmS85xphx/O4yYc/p0LdWsaf2GYjhmHkcgs0w5vKM +gQ0hkZONrRcmXJ1blKalrJwwSBIio9LYTKcyyVdGtaT3j3B6xU+BXAm3mlfJS98sT2+u2r6j96/ D8k3muDzTSEJ6E5ikT9we1D60RPGeH74nP84SJAFCylCL1gRMp8k/65NJA7UQgYzMDh4YhkiZB70 AGQhEA/XsAYg6qqqqqqqqqqqqqqoiqiKiqqqoqqqqqqqqqjA1tZUVkZRVVERQoahiUpNSI5CQhBW 4qMrgdMPUYa7HqDA9fy4EsoGY97mfh9C9eW5Ym+qT9Q+sPiZ/IHKfr2rb/EgZlbflBh6Icjn8J5D gnniWDIo20/nViftVpv+kiN8GjSw449HcM42c3fxbxhoBSkWdUVYDZg2JiTUlCUOZw+nY1BERECk FPZYTvEUrBsGWIiCM+1A+M6U7v4PwzjGFgoER0lbItrcLbpyTEG0Co6z9Gb/dnhWZXOIsDqtKTCQ q/vFBCWDfYcf0xe39EKIQ1SMdLm+yDa3uqykg2qlIpNn3lUHsec2PrOH9TzmqBxEH8tT/4n6w8gn lTXQeDg7+o5TK7y9Z/fZzzMGdkLhmMCMgMFIMBKKIsQYYpJAMqEoefluA5B/L4yIAPE9U+QRT/pF ByT4P10oJudtB8E9oPo4PvDoE9g35s9fAoT9ZAP5T9+w6rH7kiw+GUdh6Kq/PBms1UedH1KJm9X2 DecA7+uiYYimiCGGUWSvjTFdKDuBiHUzb+xrRJz0a8FJfFb7YLBiR+SXtUTmimIAKRfR7LibggaC 0WAGDJ+6/6GfjeorG2T+JkxBQrp0xQMTSOUFKwWgmyRSiBUMQy2WaVpJsyTuQNeXAguh5fhyqbrH QUNBTsG8J9KXnFnLL5UJ5P4Dx6dvTACJYcmixVy2+4UAJrVKw9yYfSeF3XRuh58qvhtVcLDizflL Q+LJ2hf3Gab2zhXdMMR2OsMT0oxmW5hdbhIZH2sB2f7awkRdEys2llo1kbSFssRKyGCGUtFFjDnz aE+o3mxijq7+OW4oG45s0t5cuofCQ9faKGpNRGgFOQAnXIUinQkR+UHzHMOQfYpInAIAWUXbPwO8 n/nEzUTWux8tbyBvCHlgeaIaKRvH370X1I7vzoKcR4eXTz1bgl+3DY8Eh7SDiIcUqkKPTAtg9dHk ger7itMig9kPUR7B5/p7jywCQ7KMC4gk0gYJKQPrORoHQcBBw3koZO/2IL4KJzApsnOpmA5/0g6z vXL3DFs4E+0e908+ito9gdD+uDqwa7sg0UshfPtnVfU0zwWC6PRMipkOl9/JDrko4VUfFCgf+0D9 5mBH/ZKNzs0UL+xZyJi2+jYgF/dER8fleGO7lwfGZD34q3f+q25auWTYEL+PePJI/B27sc+0vxj4 y75qHbeoCD5ED4ey5JVJtfJzu3mvQPbX4UeTLco7HQu8d/HrZK9DdXSuMGeXWLwK4hxSxxVg7FGd X5b3TbSuyfgamGqRQcPf94xaYYjuz6GH0k0en9Fxmb/v1J+O38oQN9TTQlUWWyNtCiEAkQsJiTWS hLoMLGzxy/NgtZBzjOJfBMyYo7QyxAVdw1kBKb8pZ2LNMHrq6u2AbQ080ROzCcRjFSR81sonbnHG 9NBm10kYeanetmNdCMJcRGmxRzMdUnFqTkbVVjgnWbdZtA0goCqczoGwegcKQDAs8wlTanSzxxCl 6gVF+PUedkqmGOIUaRzrOOujbX2Yz26+3OSzsCIAF2Zhk7Ma1bcGDPBfv0UFsKaeMikgSboH6eD+ kscPqMgo9NgdO8707YQO41K1L5UqUawmGBTBCz0ABVur1aB8XlNhyEI+FX7zW5sesb9tm8YRBLPP J99cBnkj7giGwP2QpzCDqM+jkrqEW58ujv7jpW7i8GxH63+Hakcn6ztitlj6cH1FNOumBnuxX/p0 /6HxGhbkNNXM9wgFNKlNARCKTB2mYbm+c56jsrFOAKaHAWmhosFMSxAWn+KilwuzMNAwhZ+QCf5V aQQpCX9x8ofsNKGzUgFPGJKJkBOeE0xQRM0mByPSIM8aC+LKgA+woPUeIyA/W6xXAhsIwIqSbAsO r35iJdUCwAp/9nwH+tg8gmhqIA8yM8p3w2O3cqOaInRFA+JSKnaJBtB1cLEVOEin0tShvHu/Zijq KIGgx1Ccmx+71jTwQMhBe2CIHkZ3xf7jFB2laFdoDrYI6AOCJkLSkIq9sXtmtno2BmNylE+YP6Of n/TzmhAOYrcDq9O8fy9Hl+O1q0EShE8RdLzt1WslyY52+LYuibpIEecz1mM8LSOq2DqmGEIXpdAm faLIvA6bCGX3IaD1b7E3LUQfa7Nz31xzOyCSp0PJXUvCwO45Tm1ipFCioGI79cfj/Jn/h+5wVgij RCAv6W+DhsI22XQY9nTUOSaRTUkXVcBD4QVBEEba7QRuvdVXB5jVKiqZ/Qylv4i2I2ILCrLqUag4 bKAFQp2ahgOJwmLsK8VQapsvuDt5Z2otjln8O/pPo/iAg4HgUHVvcnTwQfUiVOUFSpIHC869dVaI OaSIiatmQpgxG5o9ob8c65gyXlz6NAad2ohMjVGjPvAEAiacZIgwPh46JBueGvZi83UlYlSmNVSF TU2sEFTxmUZqNCRW96V6BlZuceeqXCIWgdNkwZKGGFpxMMxD1HWOwsXoy38dvJTM22eB74JuGzcR ohR7MY0wrGOEeEe/A/u3Xww52HJ3PDjFcijSIurpC91nJnM3SFccYeDOrW30JOOIqPo++LFZFsVd b1mMIQF0fjDKtg/J8PQRRYZmNGMLczHoqjVA8MrJp4ZeuxUfZJYm0ozSa41uCEpRQtMPOaMJhpHG /eLDWW0XMpoa/ZHus3Ji8mvZho6eG/DS1zO1SI0WQAcJQ0XsiGuuUICm0ZqIjMjRBoZpcRbYtCiC YNpgtFKROmLhjoQ5Bz8kzeaNqjNmA3CPPmuRDxqJqRkRhYVjOksZW0EnuDCiKGQMbcPf1o5LdrLJ Yk8IYgkGZCOEE3sgbW5Knp+NyqOe2lo5x8aHRd/v7+j2e3Xm15CGuTnMGibA2AbzmDo8QP3uwMwm XxRRPyQUuBQU4Zdl+jfKHSs6kuMQwBBoHP14DsDbXDcJSKhahj/dyW25LkdJM4mKJjuFG63eEgRy Xf0DhuaTAZ54QYaZ5KEIDdYroESFCdj/xo6kdAuGoPcHxdXJsRvtC+CY2A7dqFvP6wtm3C81ryQ3 HPsMBnYtykun1yprf0/lXBDYcQ6wdgc4B0w9HcIeEnMkH9ckhH3hp/nEPSXTWjBDyIfOc4eY9IUA B2wEHoA3pxF/gkoSkQCCAKEBMUEH1p9YHkDmr0UR9w8+DSREJ2mYYRByl8kpSkokm3H9Id6o/gaS FYJAYQRgwIEBHtO3W7jdpjV4jgib3S4Yrcfl/D+7+Q19Cn5TeymTErbPZlE+CmNz8ssPnTifXLQy vV4ZHlED2j0+0fH0vymzUSc3ygHRBBUPrhUAIgPVuM0zNBJYiH3PUHicGejxdGtM1A4MSy/d9n9P 5twHkFyeIQPKvvDMHINqcAfGRU3cvkfanrNjzhdUNZyeH/EdQCmSq0Co/owDJZUhlYkiSpSFqEAi kpKoimYpiKSiAiClpAZKJGYIWYYCEihiNEIC/hAKICWSiSlGpiqQmJqmiJISFA/QoEgH+NIADZko IopUkApQaQBoihgiEJgWlKFaEIZhRgIgCIGGJCYohaAKEoiSJSkiaEghooGJGqpaVYikUiRAiIgE gkKKGKSGAhmKKpQpBkZUiZIpiBoQhShYgqIhEH9YQ1/NIZCyRxhGiIWJIhIkiBGkgkCIQ+hNYaGV Q/WwfyH+YGMwG3jr0bBD9GxwnmX6xKiFg/jjAOaY4FAxTfw/7daYGJqYhKAiQaBiUpaFKRihkGgi A/fxAvz+w+U/T7fXxF+skIg/yko//T/3nFNgYgJ9JgmJqUDfaqrF2GZJQ+4aBD/rJpNWnsBNZRVF kiwgpCQPjNNoKRRRQIm0qomKCaBD3lch97/0MIENjhQ4QKqmHAwsgEzbNMNNyzAXIFAwdoEE8JPV ug9FIJGYggiABhipQhggiJpNwgR6AdYkLDJMD1CHQfe4PNkSlmBhZpGCRWWAkikKRpFCJSJSkWec ihiJ13XF1Y4QbSlAFKCTVKFJJJF/yE3wg3+X/+Ze8ddNDgD+UBLQ+PqvzYIhxJXj5l1IHfZYZNc5 slVYtfDPPgaNXllFrXzes9J3gcBlzG/M/Md41BpRcq31XPr+kvn9vk6s8HXEp+YUNKXEM0Pn/VRc W+d6y8rDHD6mfAY9sI5ZL/MV0WYtf/jx/hUrGPMdu6Oy8bqFftG4cjN6O1Tl6R4RS3z3il/46DiS QBeApuo2Od8ovcVxXK34fs/ifT+JToYD+v+uAPadh1L9UOaRQPyoTEFBWISsDJg55f6v4XwgZIeT 4/PdRsMMeor7YSSSQgbJU//8xQVkmU1lY2/0UAIgU/4B///////////////7/////YVl+9bDwfbV VF998+g+d9eyVfT756Oh7777fA3wXd1URDtYNAPs9PKsIVbrr3vvvSbbnzHucACt9g3fePIN7NaA PoAHXoGqa6dNKPvuJZfN3UnoAxVV9fXUPu4uUXZ1UEkXAGGpffGHTyN8+A4k59X2+174D15cAHwB GXvmXwURZnpxcC3u0e7HvKs6SSYNODYAaAXHoie+vU2vt9xXeB8bAN91vjwAFu7z3D460qqlSbGi 7DuZaztl7AGpDw+74A+WfXyUAxSEAIA0oFaoEiooWmWwaABjO9Ab730uSoki2GdMipIV2ZAQzMAA Yd76B93ncPiJ3uHVSSqUk7MLbCNsAAGN4A53bZqolIqqXtiKSSASXL5nB0FCy+ALusjoGSgCKhSV X0wAAoLxvAN3Z3cKFSoJFJJFHtnoAZKZu99A+ffNRQXrrhJKQSUJAoF9g43wC93Tw6KKKBQPbJVI oAHDwD3ehU8wNCqAAoqbABoYA59768vUm9URQAKoAAFUUqNto0GmQADNrabNChn3ZzasRb7s6wBm VWH3d8AAAAREprN2cr3m2tjStaUN7vAOqAUAKns0FFLcgUFetd6gAAANJ7AO7Fms9EAAAAgpA92v egAAaAtZvbkHJm9OQ0HPvNto1b77ife+fEAICAAc8Svdb3OnPtXeex1rMfRKqPm87X3veHtNUnt2 +0AqgATsyABTB4gObKpIAQKEhBBAoKoiKSr42USKVRQAAISnXcw5GjRiEShTbASBAIKVIoEBCaYS CICQKXXcGgAVQIEUKKKCgkU3qXT3gcqAAoUXd7rbJXS28UiFXs0JB7MoAUAApoKGEEQSC1gBmduA AHQ1MIAQAQaEyEGgmmjIKeRPSelPU9T1PKeoM1PUANNigNBpsUASgQAESIpkTU8mIT1KfpJgaj1A AAaA0AAAAAAANMQIkSFNJ5NE2iTyEYSfqnoDQEaADR6h6jCGAIwmJgmnogQlIkCCCZMiYNJplMNI YSntNT2hRibRNpNNNQflT0n6SZ6U9M1PSjahjUCJQggACAATTQAQaAgCZMmmk9Am01DIYlPNEhmK b1R5QSJAIAIghMk9E0xNEySeKbJPU0NBo0ANAAGgAAA0fWGV7mjA+IhD51/3/I+mwl+xKXxAUwgH wJIkQF9EO0/wWi5gFjZf7X8RjqF/xtdVpG0VaET6X/dDRav1glyaIi0IR/+j+p37P3fmiwR/8j+t zh/1h/X/bP/vcBCjGfjJKBS/sp/CgywtGTBkGAf5jJ/y/la83s5+v1moqmIiIKab4z/JZuRXI11u Xd1SVSf7vr+7x8Sf9XjH/v0Z5+TBoPSHQFdfVzhTEehAaCogo5PpD31k2w+ITf5fX4fqfZ5Cef8v 5fT1Orcjl8fV8L6j8zwsfvfK5n4s+Y/nifLcrTPG+Tw6+brvd08rgo6zVTuVXJel1lYC1yW9u3XD mCsE5WHlzm4rtGlOVnK2Rziq8mRlVXwPn6j2nAz2eaD326mRXq/KVYiGxZ9nWvdjmHea3KxcM7NV zBi3OVXMLwYkGMy+bU2dbpk7zTWCtZYVXetKBqeUJaMW7SEH9+JnJ3HvJd90kjbd9ZNV5keRvHA1 CXWDRazt5cA+1i3ACSC+o2ZK1UCCA0pLMDC5nVAQ+nYOh5NF7uc72PPvHWP+FzvefZ09dVcOBy5m QfRUZItOXIYz/7l0YJoxfEAKBqkyB6EnO7dQdwd861w5B52m+faDxsHb45jcoeq/M8JJV/vZQI4f 17EsfvpeJeyrXtqIXWx+eD/5+OuN5nv4fJD+p2amvhaO7QXWQxAo2RP633fNzgAFpRG+5mJptnk3 Ik0ihJER9RCmHP9Q5cRyxk0HQiLSGlDigDmoSDGKSpkXdOUKBQMffxfwH9U4BA+9FdPaHWuGTJ/e nsu49tkPq6N8J78/Pnl7O4Z5w3Mfe/DX4Ps+9+FO/x/tZ3nSB3P4uNRJPMyiAZUAMlT+p/+Sow57 D5zzlj1HYeOk/8n/kr4ntp7iWlaQoENJlP/JbeeBrQv3tLVU4A/4jiD4iT31BNUzX+IMaj52kg0C HeD6BAefzvIvYGhmxF5A+Q7GVDBWAxfuGAQYC0KhZDeF+Zt7t85v0JQadDmP9zfRtKbdtZv3oyTa +Z5Jy5B7xfzBSu3nxCmF9ggFCjcIhBiNRuCVFw2EZcHF1GuJXPV5cAakVoNCL/2DhIKhKZPBv7Ax uIlBvheMTImUoMh1GmbSdQY8uJGdcoAnnsW4EnQ3EaLPCqcYE4QoFsFBMOY7ShGZIQ4Dm4E2yQLg HoOAaIuwHQcxDoHg+pF5y6a7WkpUJSorDdeTUarVVXKiyMaGT9eAcVGUOHgb2SJOnjlDYKqyHzAj KkmyTJCfGpeDk2gjmXZy0EuGzXJTmncX308ynlminlGsc0CkhJjjVqGeHD3ewNGND9UP9XemDkHz Qp4kDqRPKUpDv2yBthYwyiZ4yNCUjIkYyA9/V4+kzGi/6N0m3bMm2Mb0rxOFM1Xh+rrQiVyVcncx ENDbmOIjyxrkGNk+2Tzm6ud7T13114I6764Xjr5+/F4PW0e70wd9avTHn67ouRqOop0nb0DcnJE4 Kyp2T7naQt6QONFWSP1xAgYwXe+EhjGxDGvupS6YcSn+FQ1YahEYGt2pwR8MbManojHD5jDw5GIq vj8m4Q2xptu8Kl5/yIM898EypubUIw/ZomNE8tyofbn7PZvt3w8NBJRHtvDwxnwXdWen58/KFZUb jsKOkcLuqgXNu/+t/hv+n+M5oqoHLujgTDT6tiHqcwREH4/l+g50URFBBFMV3j+1HCWCJJfO0FF8 3fCFkrY6sg3MLa60J6frhR5thgWWdpiABixjOZGp+HQ2PIJH2/c3+e/ybI6Se7j7ZUBGLPD6+98f wfHr82bIPakNJnrbiTC+2Z9pCDiIIj6jhwEJBBGi2WakL7VHD6cOig6r91ksrWeTQVLQEnnGbqSL X1NyQhAsNXSZZC3iZMXjLsim3vxLBGLCq44wyhVyG0Qy0Ci4sTDJMETRFaYQIqmWLBviiDSBpcOS iRCvGA0mORusc48jeLrnRJH4Mcu4L49+vT3ekm892YHJewrVIuTKcIFTdSEfKkNmmhPhGuyF8sYY OWUj7qWQTXXBMDpFEB5ZLQrjREicmzQDMMiJUqH2roE2KV+OFKRMejpci2GUkscnWhP4rJmhE0HK AIuW2mkPKC/je/toYq2lm4yOptYVlZVCzN+cs+uOI5WGQgXXuEv8M2v74Qhdp5+eh3F2EYaHUeo+ fZO/jW4ERdu/VEOQ4cHgKSb5oxjTMAngwepLnjgBsD57j+Vyr+s4V/VQ2oYfTn7dSZw9X+D5vC7a qO1ozJ9RqYc+gRN8UMIOAqg54Zs1AxiZQGsADRpww0MhIFe7sDnCmGpTbAugOqqUaTkBiMQGjJbm XUScOcuPKJWiSjXujk9TZDqdRwwRtijuA5ByNBousPVyGY6k1RRRbJoo6jFwjEURFGzqMSG0aUiE ghECVBC0EIBpISGYlRmtcsspm0pVtKudrNVpXcFyRVkXITZp3QO0bQNZXw0ecyp5uTiSOqUeeOQ3 u21sZoLZ70OO9kBxXCXE4UaaqRVUFVTEg9WVRwshSiGbm1nIfG3Zs76gRAXd3The8E0syjg6ddEL cN9fMoaZqhU0HKir3nKmb2VopVuCszKu0pueWhsnOedbRliUXrObzAESaF1tsCw2mm2w3+sTb4UO m667swRFzZ5REXTrpuihWBWKbYB9q6+fC6ftQekcgDWjlXJKT7UesB68URw4QNMmNMawtklCyjA4 lFHBB0juo9b0jhL6xzyPTh3d2hPWOEhyPEecemw6UkGm8xAWQASBKUI1KGGLMnoIvetec2JKSr13 cnnD1O8eVuaTzjlzYcuWJaAOqiGCIJiiBKQMlDCGZpUY7kjkwyIea2QhS14xARgmIFEUYiiM3HEY YL1wNMQOlkXxWYgkQFijSAN1CJIMF4yniQ9bSvcj6yeJPI8/TrpNPrPlHrHkPlg0+nM0j1BEqHV5 3IXqR0Gg7xlPOEDxCujxeICt16caCyAsxAgBETvUR3gGMVWs1Em86HeyzcPgfZ0nuCdMYDzMyNwh 6uyKiuAYoZvAh0c6KWphJOkNdnm6OSlkhd2uDMDIaQnOc7VVunO4uhBalZYIYRJHTZAoETAsI3xS ai88qZZFXOWf9GD0YgSFL8PaPtEMI3zEG5JENKMk0CRDMJJBDRIQ021P9Vfcr0/kTLSz7+fXxVq6 RDJNygcS8uUfn+uohyrHE/nf1ivl58D61XsU0vUjKnFQzNRu9dsRN67NimqpNXQwycnGSKW5gszp BZK5b4ObdthXgvm0ZN5q3lozJrJZvCwsu64LGCgcFzlVmRy6vZDYT58hdPHhgxxDIAJHHgkiBcxR ieoxMqh03BJw2otAbhPh16Xn4x5V15hpvCcqooKC9Hhw8zw3aI4JrOVQMTbKwGYG85ry6vOMprLp FhrJvcmhw1NjdGVSGmKu8ilFXAHAJQ01HExxqRuqmkZIyXd0JU2cxUlMC7XF0QIAFVRXU0+9DNHi 7TZubkTxTtg72zetbTq3VHNkbsvKupE6rB5bE7e5mI6MGht3zK5YFjjuQbmY3AGRgMbKyqS5ZI5m alk425c7V1xXvJjQoHKlCto1L1WOZslU5HJejbzRVAzLWmc1MzNXc6HZpclTfLXZW72s4060rE5D XvOlatN3yaFRdLrlWQZUMlNVVVIUFBUVAVRVUAQ0UVRKEFUVVMhUx+p5fd7/F933e37vw6wOq6tf RTkx8+0zwO2z065doh4YN/HTJJJJEJAkFRUBVFVQBDRRVEoQVRVUyFTHDne3zHPvfV9v6vufpe3R 193b7nfpzeCXtUVGmTnSpREEymxOvEUYpu1VAfLqSrfiU+eo8M6f2V03ylrpDg1eMtVwP8oSmwRJ oDy0jHsj+QvarSUjTSN8TdHqSKoP3GfcXM8vr+6kzKpA1CcntE5QgF38zJaM9GT1hFFs8gD8Ep6f DCAI/HzacKCjoRBoP3R/+f8z/Er/4YIKVrRTdEkVAvigu6CZMtFUGkQuiLCLSAp88CRBQzERCKsY QiFU9kX/5qaC4oR97FCjuiBtGAfglYTfRKKpf/dRFuLODB/XwGgJn2g2w/C5AUxVccgYie/Af7Xh 6cPy/2Pcd9P4o3R0GP/G6XkPrLyoQguceRtqLY0C0AjQqgUAhbDopaBSIChHbUIFItH/H/027eT6 KfZB83Gj8oEJAkkJPmww15DMX07/R55fL9vdXeqDhxPl/L/b8dPvvs8wu8bjyA8cOm/RjAXwOvCj JCb3PeQgWqGyOWtnPK73f6NOLhQWf8FkGczM2iSCwhAZgUrAFZeP7H9H6a3MyCtQgUDiSckVtMl1 pU04kj+apiZ/pdv9dc2IfDZ/DM0/x6WbPXeL6CEUTEtauJpexpHhE4hiiY/gdPAn/3zdYLm9T1Ln D5+PPb8Aex9Pn6J4D15S+3mJs4BTFJTMyXyOfgclj+2PLKpGU7NvcpaOshchR+nbHqY71RJjwR57 foyOEcHOuqOksJCE1POH4eRwztL+/MdpnNpf5zPa3tuX/7lTV15yu9bVGWjw0YOeEAkNnaScFxvG 8VYRbgHGECIGqFCqFVqFr1EC8a/Npf5EuJ/Z1WrdbQrvWojfho2SHJ1JuVgx+AZl/2ssHKTVoIIb viaIQqDLZLVFSdWZ1VxG0Cm+W+TK+5pFHyTL2ox+12Rf4EfvIA0tyYM1iO8cmiYFEoqH0ik9MbPc DPV3z65qhihI7z+OVvv7Muu9PCLeuxPjDZDhuUCkxks1WiZL5kTXbKVcB4lQOnQZoyBipi3Prlrr +UYMmJJ6gS0YHgW0VhBRAEQQkTQGJMo5rvHlvToSWkWqzO2KLJJCeewOeipcUkbs/3+ZD1wjHyIM TmjlmXMLWQ7SwvMtmYHpG0+2+Cp908ZWK1b+qT8R2m1sauelK8pv5GewvppM5+IaD+f8NzfJJPuM VIwl9GxlqVuutNpsw4AxOqNOYrh4whGu/sPE5zUPLQTDExXyfVwOJBQUUFUxEUEFF1bq176201Z1 Cf4+f/hQZ4ztyM8hJQZ1kEv0N5CO0OP6rkpMw7DDKcIpEKciRc94KCYNLJJ5ljv5n3Ay1ntQmfqn SpQv/IfC5QpBjgUTBhbSUiS7CAUa0i4BryVXMMbkCED6taAtAx4UIPqUuAKaT8sLq7kPEeRpHKfD IvqzDQ9Rs1VrX8Nq0sFuFCQ+k/Fbh5O3ML9BAAhA9MFLWo0+KgCnq3nSiL8E/RNBVUFtqiCdDpCK qUpI1ilKUpopiTRitl0NEVBkUaSISgaKKpiKapj4ZxIRIFI0gUA1RQRBEhSDQTUFBE0iRAlESVSU DSTLSUJTSFLTQU0FBRSxKUtKRABSUqRBQNUtRBSUATJEjMlITIxBTEtAUhEhSJEARCxCxIESpQFC zIxCNAtNIJQDS1UQQQFKTIBSUlU1ExJQUtBQSBBiJy57/84yI/fGQ56UKAJpiG81nR9yIiCBggSK AqX9p7D84nzwADGAeHt/EeGIYh8alwRoarj+4tHOa0nvDVETZJjEOJPHuHGAo7IvhvUB2yOHaJWc vk2N5zZtZvHW5148vUX6QQwvoZVhUyQFNFFNNSEJRMgUJkyFJMMU0ERRQFTA01SDSDQIQUxREhTJ RUSUIRDQhTDJJAyRIRBQDMVEny/2NfR5vp35mMyJhEXSuwJmF+OJZaGQaq5OcDTPRGmI/WFeQi+B 5j1lwbNql+eh8mBiNmDfQ2TThrrCYfyY7OjN0bySJLpN2e9IPVnNmg9pAqguWazTmlqEnpF99x9x g+Q5G2vT6bpMcGej56xVIUyOndQZOrCiQn2b4ppxcxhAdIMhAqsHJ5SgO6NzHIxDZQ6LqgbIXF2n YwD7w7Tg9e7rkUXXoY8ujJIe2NSVHr48dHfantjuSuHd7SHjHUeRG8u/Hgece53pvEnZVL2eLlYa oc7neZOcymAK6ogaUYkT3sgEshYcYF9UJ8IODKoESEqQrBIx3t0FQ6HubzuhXE+D8furs8Q0+5CD pr3SvuSRWYx6ehk3R9nOjdz3S42OaeXMpFmS8QRAJJMpEdhJA01GK2gSfNAUTiqXPL5Ywgl7otXV bTtFYs8qNSnRsZP4wh7tJuKZdGNXBwxGqNDkrVrYKqsapirVVyksXMiDD2ka77BbONKc63zAR3UE Rh0yDBpVvnBikelZXbY5Uy7kZfXRFTe5VatpcMBAqtbPn5QQCDH6SgfLYh9CiIBkK6bS3Zx3I0rr luTaTSN2lYeozavgfHbxCcrhkE5FTgsHFO3fIgfWPgfKgxoZstgxOlszpGzBaqL7xFHqP4+YfPmT JCcIlTBAtlIFXAYbcV+k9+FQlwrNzu7gsHNNFVwXwxw6YchoBmKe8qtjW8Qql+lxliyHRUmECt64 oiXYfSH0KBJNqsWdWygiFigw5apdkyh+oP3K9EVovZipbU1yV6USYRDqPQeZL9VgQYJw4Zag7XJ0 vFZzin6QIzmU+qPs+vZnvnPJieK77Yyou0BJAoyzLDfy2ZIgVasjEiEiYXISJN+zrDMibqZINuEG 7WTyYnpgDDF2UOtDtHoeEmKJpYQ0gyGc4ck6Oky7JA4DzHgygSM7bJRVlSrdkCTbY6PJXbgJuIrY vWqoTnmtPJC6/duIRpfRevhn1CDXpiakhEhW1M18pt18icW2vlFrmTmBLkbyaZWDo+zJgj39COcl UHiF9CQ+iih79hR7kIielQvd0JHFbhEgiXkWZA8NWBjiJ4fjJFkRwEkIbqYRXAEAgDpRi9E1MkCi bvdEuggchy5LzL6hQFq2KpOVYtbvJJG+fgGzG+EYnDAJiSvHsiTKFCVJ4hZB81kh+h652K2UMpAr K27HInBswrXSLIW2mAjqQnnGkgnr22400OHg5lXxYOIUQCDxojU54s3m51IAilxm0DkqiiYnbYYk UoY5eMYRCPCBYKSmUJlDCl0KvYmhzFwkuVHQY0ukxxDLzlnud2SD23iGTPaphtCzffj80geVA6+t d22jBVMhoeiLbDOGGaapYx/Q0IhiWatV3fNr5SM2ewxS7tFGXQhBuDHZ3hh3TnnPoPxTY6vPPvAd E2jVC5e7qD3z4Tq5x1t8np17/Td7O8bSEwc88+8/ADu1I8dt3OAyAV44hklpuAFOuZQ4QlhYCEoZ V0JgctXKiwRhHK4k6HAoWPgmPimNFiuqu8qSPe4LndR1eHhG2vGsF1ycSwzEhCRXOXVvWptgfQ1M JSu3L0hWkmA4CtCSH3E5umHiAPFhDJHeXAXKdB7dUyKyRQIMr00GUp7h5dEeoOGz904wOGqjqlbs g2VRluyYd3VgwrhQFRqsui4DarOlPnFdzjdnpODBVQAEF5G6Pyx6Gd537BNJsYw6DZJo6OGhiSlV Nza82HfVQhCozGn4FUyRqzt6LCSDrOUKjne7vcgAYSEafG+cYrUG4diJiJM6SU0iSCDCQDLgT2vi B6eiqhIvER670nwdY8vLyubd5TkAwJVYL0VDsqAS38aO1zeZPJMMOIJRIMlSxILYL5E2kYISaabC AnCitpYKlxYLcgVxfZHgEeVi7kpeVXT5sVV+c3Tfkibnu1fq9HkTEdgpJnTkgEgaXEEgi5YkriEu Yo8ilQHhVBSqhJSS1KsC6/h9vE1rgtn3FeoNtezzUOTe8j2ejCCSilmzHMzaPbQ4hVrmyxZJwmF1 qu8mNMXfHWIZnGEY7BoleTtPEKdypek1WTueTCEUDDQ2NoIaf3DnOXzB6IZBF74WBVbSBQacEwK0 oSfU2rypMbaiEYo2aeGOb8gJrhBzD0d1dXsQLSdqLBbNu+TRdjHGWoXXAfS1NcoWsQPVwSIS32hP iQvfbE+7dllm5upTfJwiOAsxRRRXYhxYxXo1rt2Z7VdVfI8NUJ6jIQFERh3KrooR4V4KiigyZDLO 5CiT4tGyzs7hW+5vJfK13ZvZy07B/RUK8xD1zyyHnPSKerCnX8aMw8Eb3kaqf5fadnvDGe9sfsyk PqCgHcLESdiK/DGAfcciErH5BDbyt1oFOrVvM/TRengrvVuLKC+JVThxMg/QF++JHp6XKTm6beFB HTbc+XbTon4sdXzKZXMjMcd/nf/D99z9T+TVwWVhZC24iPIcejTePXj3VSek75UaKh1UN8kaxkz0 yOJvJjok3JG3IiIs1dIGMwgHPWsLYiw3RIgxqhQT0oJuQzRLCmi1L71ayjJDMnM7pSn2aiG0iX0F pe0o3ygkl5uN6kP5EgLg39fLS8zm0sVofnrm1aH877Px01afA98C8oSB79L8nUQrjcXniP6AUDc5 D7L8uB+C63m2aj2XE6MTYEOz5/TVafxqlBwyBX+SEgIN5+PNN+LXY0Mbsw8jx48aPbll7LmPLy58 p9nOyk99qv9u2CPDwtGL4/Zb7PTs5+U8HX7YAfEgIgflGTeRw+1/OfXeFoXf6OnhANl+XScF9dcJ gZIRhDHEpWZsPlEZT5fkNkeHkxJBkqEwHYJdvWIjnGwjQzBPaOL/aH9zulIi3jLkMt2z7d8MvauU 6cJDYmwuEDguPoFYY+yYSnPy8njMXvRnZkaFcL+GIagyBIzJNVC8c17+mzFIIGu8oD1nA6QhXTVn +xd2kqbREZRpvG7anzZbj2QyIgbTGxe+EfIm2Yz3suKMaymxv33arCLPtuRiB4L9+XWgFCDCJniX C2IiJut5TlIwCHAo57OSiY1bxyNHtgUgtboKZBjS4LFACK9q2hj38KkaXr1sv6WPWWvXvXOUv+ao zMqUD/pXyYO+9zW4SbVrUt89B0bS62NiOFHZ/lDtPvYOrkp3/mDsG3E9LoSbzc4ZWNIzJ6N/g5kH hScPRBXaZ/M57o1itgiTJVM9PmP2wMaUomtY+4gbLDaPx+8yW6TmnEtUqVyDINR8THU/HU+9jXnR hEIYNqQqz0XLATPdT/CV3n5JkMKiRCU9ECJipiYQ2NJbuJoJs6iOURFg+AdnarqE/4fKbYUnnuoE TBmMAZFWRPMFJvv2VoekjTYjb/Z5x8kg707hUlYI8QXUe5Qrh9n811IsyL6Plgxwl0XVv2tMdhpx EVGZABxNBIRCAL3EApTZ1g712hydjBl11sq3Vx9cfTvsHhkWoA5ggE3lgbrCIbhmTRY8KZm328z5 js/TiWaBC3jQQA2lTLbY2Gk2k2QwhjTE0Nr8I9ucvudtq7R7kw8fDjK1/xy1ZkTQyNhSNJwGgga1 r939rqdFxyR/m/XHfQhAbfUefWSR465G2Nt9qHmvYpHFyaDyLTB/yb8k4RjwGI7rBYcAwqW2wbA3 oM7ggxVSY7ObVDtr+z6/cvEY640MsUP6RaCknRuZL6OHq+ebFg6Zjk+DzUxoDc+yb99wayJE0ND7 2ojT3JGV47WHc/sZAHyiBshjiBf3c6ZmD321uB/BmVftkKMtSWJ0mejwIMAZ388niYcZ2+mvQwSi QJQSE5H+SsFQfMBbeZH2wpZe3On91k6jtJdMvYcDy+F5W6I668fl4KkOmvMsKH49cYcccZRQon9m ULWjK5GnM9qV9MefWmXm9c2/m6t7FlKQ49pVuQNDEkkPJigpRggBKASICmgX7bXnZwXkKD1/h3JI q+LbbG7t+eW0ZT0Wbb4j3Z7ZJjMAQwUW/zh7Qx0nR4C8Mlz0j4ku+DfZgMPp4XnKyO5on4573xLQ dMqS+/ET24yvgti6/xv8WPqIQExCA012NROwWxcWXXcGOILkEzsxyDFd8zwYmxSbaKDVzrT7evX7 IuR0y7jAxSK4zrxOHVwoUSLcbDOAQiBS+QNDsd8stHWEBgC5MDsKgmeZolSF5GMmxsUKSzLunEvh lNzZZrMquA0ACUde2xWrUeWOCD4mYmwMv7qD02TuvzcW75yptTdzb8DWcDFc2/b5XpTlSSDmEjUp NZXAx6nwuxo+lNvpI0JEctQHkezCHPT6myAW+jyn4nSyhwxoBgYTW0lAqMo0b57mn5XFUyqf2/9G ZMiCKB8iSzDgxIRRMCbDYF8omZj6B/Y5q3n0NZ6SIwyrG0UQnwzGE/CcPfAHiFIOw6Cc8Dyw7ykP OHHZsIDRsvlG/JNMTPz28y77JsfwdxUdfLNgQEQBYH8r7A3dStEbQDf2ZMM4yDEZluwEIrACrgpj fsHpghzjED5f0+/bwbuj19udgewMKTJCUkCRAwgQYBuN+7X0VvdZM/wYVtW3xIer6mvofzTHeJE7 vObcQ52PwU/xQwpEoR7KYQmd6Dg42nFEiTyCewpC7jF7Xgyu+LNgIreR+opBISu8E4PojcGaaoLn w4NEGjJk2RYwKXwPgEfIEwRQkMnxinSbYijqgRihQYBBhdjv682uPhqn3mfRkpTtEQM3fBo+r+3d QsYTmR/JvLdOLQZEQ3H8vU6P0untupARLclyWEIAgiCKF6SvXsjSZ7NkAhV7V6IQPve8WKAhDyBC xONPxI3dHbxHeTej0JIN2dMhqeDQsr/Sn6vT9VxSltk+DbX9LAC9QGqGl3Kr7w/IgZTJMQ32GcXh wtGdDgtFAzIyf2MdwpkTMi2XlI0nCbESGJ3DR5+pijkoWweuLJkRE0fhS+4cbw4iV4a4lMi3b0xv DmesjJydnFfj8PbCO62dyHFddqbORZlNnNRQMOUEMcWlYDh8ptVmQdeIUhua8bZGrpwvZ/EnOVa9 715p8MYnvSP2TnvE7yBV/BAl6IGIYhANAgSnXWJ6Eptk/TaSwZUP0HnWvmacZW8eJOVVpilPbSxO gy7JskefJK5oLTM0vWZ8z5DICwueaolugM2iJjsFG1uwcHVJ1lA1ikoQGhyd97moIAG5TySCAxkQ JgSuoeDQfbJUfx8nqzYz29YrAng+G0tKplKOrHaO3fU/rPynM7dZI7WdJarTtSKDd7SNdp4y/X8C zcrK/9MyXQJAB8wz9/27u28ZIEIAvAXMi8nzuGgbaLsbAruoaB9/na1i8SE6wd3/RVOuZ26YIffQ 0OjkCMIicLVHyErh6qPgGsiegU5Fgw9bJxA94IdYgxGz7C0hRr3g/kfqLjre8eoFCXRjN4orKUIw SzosoMmjO+se1pfKiQZtIwMeUSrJt5Z+f1/gBQA+wIiKIiiIiIiiiIooooooooiKKKKKKKKKIIiK KKIiiiiiiiIooiKKKKKKKJiiiiiiiiiiiiiiiiiCKKIIoogiiiiCKKKKIiiiiiiiiiiiiiiiiiii iIogIooiKKIiiiiiiiiYooooooiKKIiiiigRAiAf6rrZ/mrxAp++26YaBBmV0DRo4o1MB0MwZs5k uZuHyggQIcZkyraV9K4/WGij3hAqgccA1rps+iQgTxmxhushgg7sTmVJz3fTi92Drdi1It9DDr0g nfHz+LcAWHzuEFjjpfdbp7su0gHvkmE0fUwRBk19eVfNPEulkKTPt7zFcWQsy3Jz2OrwV6z05jrx X3lkQ1NDAiBV72QEEOAMNbJB6q0QEIszjCTw3HzGo/QeWHNDgVVRnn/E8lnIHDbbKhEEEQfr9y6M HivHEozNIomxZ1sb6+amti5LRI/EEkkk4LFACDifCK/QqENKu+kNaSDQOdBQ0cHXQf4q7DFYyLzw t2C2UTsWufZV0vZHr23Kcd4vxoGn4LJFS8wcPx+CzQ/pjJe62xaeYVXus+Q9HbmypTdStq701po1 qTXVZzo5eJWq97axdq2KyWa7zKKKXHv2W6TNV9Vx83dnR57LUoZWo8mTh5I8rY33PN+QaywYWF1t r95X9WOQAVuokLrDqRA30ty6OnFh7iydMIX8rmAy2AYGAAjXLTvOqLmHNJF+F2WL/zBTR0BRfMd8 +LmlHxkec0aIApzqiiI+LJIwSg4tQ00iRib2IrHO8VfbMc5OQ2QnWm3ZK17heFpHpSEOnoqrI4Ov IwpsDGjoDZXxmpbVhz8GCMyFDv1+yUHuYKyR+4e/UffEclviwZQhvB3MPv8fl/BE7X4nvjLtp50g LIQHBg/xBrS9DjkIEzoviReGwcTvaLw+7a80BC6pNMWx7GwT46Ns8PKtC5CgHwfte8OfJB9Uv1Hh PR34jnKq8GxTH6xAf6eVmDrxt6BwPPu1tiC+Y+ft+XG7+VA0n6CYPRWP6TDDjjKPn5o7Jte8hy3c eIhwM4xHL0Wu8W8itV9gwTijCIOI2MCm3q2cKhzGEGaguP2YI7W9AY2Nja0QYWs+2+1gImAgTG3b A/TKRStNvoXL+WTeHie58xx6NF5CfMCYd3+/2KHwuYCXxa8Zn8dAmJi/9YxnGQZBGe7ruH4ZjrB9 x08DzZPL/vlYiSCHG7eVLMgB+iCyf7WH9JALMYnNuEwoWPbc80uy/jfvfy/0Upv/pn90V/PY/lHH r/f5J25C2rCiOW1uuWc3mKiJFSMx1fJDaB5O3rXG6WbIuVepGtdPjrc4RLxXJeZxgkSs4wrS17NL QiMZwTWzh5ti6kLM3mEbhm0bqVWMcVo4r4ttqucem8SGg3JBB2ucUNjlsbLK22bnjk2tuMtaRoNI LlUka5MLiZZ3LoJ2TRrFOVPNzDQZ5Q1mUxO3TGNbo5Id5tyK5G/tL+N+t39zvtf7jvgPIJdIbq+P 54U7PQ/fnZj7tc822ZFufGsy5dfx87Nk+u77sD488N0S5Ou5ld776afq5lZlkePcZtw34eqXL+fj jaVYk3lnK290X1as9j17unRt6ZRVG4ub3a9wAS6ZBT7FbNissDg+fyOMFCttGXlc/FNJqjyqzhuZ +T8/Lyr4R6n1p7fLXhzgzc5ug5w3lHeUjWbeTtnGdT3jyoqRPicHkrje/OLIyi1xFGFSksHKIwLJ +fDSL6dY58HbZ89YaP60qLbVXjytvuenWbRMc/UGBgA59tAwMADg/cskiWh8PifCf2HoPdHgzMy5 kZa5LIZkMBgaoFnRfNUYU0JWhuPsRMhoCZMqBNTz2h7ItwRJiIEXRKpvZs2ZM4LGrFrFi1i1ksYM YMLBgoGMGDE8GDGDGDGFgxjBieDGJWYVFFVqlapWpXAgVLFhHTDiiUpABlDNTy66Pbh17doc9GTo NxQgqQhhcDyv7piPZgC5557kKUm49HDJxCTnAfWIwhJaIjkgOREO7FogGMRTlt1ywjZBJD1OHlyJ pifa0kUzVEUzUTUiSLIEhISDJJImcznrxzbOynwz+GW7h5+BRHROMRCAInF3oDIyCJtvjLjxqzPj sxkimTT8K2rwcB9xdIMoxGU/oWxrYL59UkybmUjCdueV82Dc+fOfqu5+/V+WcTskolICRDgEDpiG O9HVabQ0/MSQkzCaU76kieJspVxKmiWQikgdYKJhB3DnamiiI4YUlQTF2zQCvnsKTFpIEIhH1aB9 r31FdvfVgcuaecKsV4RTUJtwk048mZmElJEg184gQBc6wAPCI3nvMpBbx+tcker6Y5OvoIbLX4fX 3z9RR+o/fdn5fo5PZ+ju8nh6vt6NZOHJ3dX29XN4eThyfoxJ6cNHp08JNH9M+Gz6PDp8Pss6cPs/ BoqfhV1e67vW91y5fC9bXrfC+N8L3Xvq6vO+NyfF7qta+F4vda+Fr2vdcvha87yu73Xc17Xi910s s4aPhR6cNkn4LMmOkmSz8Gzho/B9n2UyeTJl22268k314ILNxuc3DHar37j2s81VEytqdnmvpNtK qt5t43EU4tblwM63K0N8fHtHc25pWWWL0m/K5LzzGBaZbdKNrxv4ynv4dzspb9jmN+eiaWRSVqvf Z0rdng5NcqWvvSqrfLJBblY3C3DGwPi+luZohKjj3y8zbG7+p25N1plg/VeVFs6Q8G7YV6z13aJT lKpSsxkbn0wpRwbJUPdKTzJ976S22R1ScLDPaOKJrjR+Rwu7Fj2Cv4LnlzSmFtdX14pHfTfVLd1x RzvQNhqrcaomm0OcL3W55i7s06bbjREs3Y81sXBtHOzhSg26TrPnh3TjWt+Zznde7Rd8gWDtN/CJ uN/FDSzhhvvtBX5XxwITknJya0thlKhIIzhcmXTrtSLyfPjfx3PshBCzTSWLxmd8MG828dOPDlw3 0u58Zc8G77uOrc88IEr+i8S35m3hrKOM41ujGqd3G9qacncV6bx3YinRr7avINN0tYeuJadp4Zpu hJN3lWW7yVMmkljNGrF5KTt/Diu2GnZk0pdGuWu7uPzRx1FOZtZs1Ie93h2tsYkzxfcXmItDOksp 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