From 93bfb80ea8766d03728b3a1ade02e59f24aea7b4 Mon Sep 17 00:00:00 2001 From: gong-guilin Date: Thu, 21 May 2026 18:43:05 +0800 Subject: [PATCH 1/2] =?UTF-8?q?toybox=E5=8D=87=E7=BA=A70.8.12?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: gong-guilin --- .gitignore | 25 +- kconfig/android_miniconfig | 198 +++++++++ kconfig/kconfig-language.txt | 284 +++++++++++++ lib/lib.c | 1 + lib/lib.h | 4 + lib/xwrap.c | 15 + tests/sha224sum.test | 0 tests/sha256sum.test | 0 tests/sha384sum.test | 0 tests/sha3sum.test | 0 tests/sha512sum.test | 0 toys.h | 2 + toys/android/README | 2 +- toys/example/README | 2 +- toys/example/logwrapper.c | 75 ---- toys/net/README | 2 +- toys/net/netcat.c | 2 +- toys/net/ping.c | 10 +- toys/other/bzcat.c | 2 +- toys/other/free.c | 8 +- toys/other/lsattr.c | 321 ++++++++------- toys/other/lspci.c | 128 ------ toys/other/lsusb.c | 256 +++++++++++- toys/other/nsenter.c | 93 ++--- toys/other/pmap.c | 118 +++--- toys/other/pwgen.c | 2 +- toys/other/setsid.c | 40 +- toys/other/stat.c | 51 +-- toys/other/sysctl.c | 32 +- toys/other/timeout.c | 99 +++-- toys/other/watch.c | 6 +- toys/pending/README | 2 +- toys/pending/bootchartd.c | 18 +- toys/pending/ipcs.c | 2 +- toys/pending/mke2fs.c | 768 ----------------------------------- toys/pending/more.c | 2 +- toys/pending/route.c | 601 ++++++++++++--------------- toys/pending/traceroute.c | 64 +-- toys/posix/cpio.c | 212 ++++++---- toys/posix/patch.c | 368 +++++++++-------- toys/posix/ps.c | 12 +- toys/posix/sort.c | 158 ++++--- update/toys/net/netcat.c | 258 ------------ update/toys/other/free.c | 58 --- update/toys/other/lsattr.c | 337 --------------- update/toys/other/lsusb.c | 261 ------------ update/toys/other/nsenter.c | 166 -------- update/toys/other/pmap.c | 103 ----- update/toys/other/setsid.c | 58 --- update/toys/other/stat.c | 204 ---------- update/toys/other/sysctl.c | 149 ------- update/toys/other/timeout.c | 107 ----- update/toys/pending/route.c | 383 ----------------- update/toys/posix/cpio.c | 324 --------------- update/toys/posix/kill.c | 161 -------- update/toys/posix/patch.c | 485 ---------------------- update/toys/posix/sort.c | 409 ------------------- www/towel.jpg | Bin 0 -> 387102 bytes 58 files changed, 1870 insertions(+), 5578 deletions(-) create mode 100644 kconfig/android_miniconfig create mode 100644 kconfig/kconfig-language.txt mode change 100644 => 120000 tests/sha224sum.test mode change 100644 => 120000 tests/sha256sum.test mode change 100644 => 120000 tests/sha384sum.test mode change 100644 => 120000 tests/sha3sum.test mode change 100644 => 120000 tests/sha512sum.test delete mode 100644 toys/example/logwrapper.c delete mode 100644 toys/other/lspci.c delete mode 100644 toys/pending/mke2fs.c delete mode 100644 update/toys/net/netcat.c delete mode 100644 update/toys/other/free.c delete mode 100644 update/toys/other/lsattr.c delete mode 100644 update/toys/other/lsusb.c delete mode 100644 update/toys/other/nsenter.c delete mode 100644 update/toys/other/pmap.c delete mode 100644 update/toys/other/setsid.c delete mode 100644 update/toys/other/stat.c delete mode 100644 update/toys/other/sysctl.c delete mode 100644 update/toys/other/timeout.c delete mode 100644 update/toys/pending/route.c delete mode 100644 update/toys/posix/cpio.c delete mode 100644 update/toys/posix/kill.c delete mode 100644 update/toys/posix/patch.c delete mode 100644 update/toys/posix/sort.c create mode 100644 www/towel.jpg diff --git a/.gitignore b/.gitignore index 625c162..2b74ad1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,17 +1,10 @@ syntax: glob -/.config.old -.single* -change/ -/generated/build.sh -/generated/cflags -/generated/config2help -/generated/Config.in -/generated/Config.probed -/generated/flags.raw -/generated/mkflags -/generated/mktags -/generated/obj -/generated/optlibs.dat -/kconfig -toybox -toybox_unstripped +/.config* +/.single* +/change/ +/generated/ +/kconfig/mconf +/kconfig/conf +/kconfig/*.c +/toybox +/toybox_unstripped diff --git a/kconfig/android_miniconfig b/kconfig/android_miniconfig new file mode 100644 index 0000000..4428a2b --- /dev/null +++ b/kconfig/android_miniconfig @@ -0,0 +1,198 @@ +CONFIG_BASENAME=y +CONFIG_CAL=y +CONFIG_CAT=y +CONFIG_CHGRP=y +CONFIG_CHOWN=y +CONFIG_CHMOD=y +CONFIG_CKSUM=y +CONFIG_CMP=y +CONFIG_COMM=y +CONFIG_CP=y +CONFIG_MV=y +CONFIG_INSTALL=y +CONFIG_CPIO=y +CONFIG_CUT=y +CONFIG_DATE=y +CONFIG_DF=y +CONFIG_DIRNAME=y +CONFIG_DU=y +CONFIG_ECHO=y +CONFIG_ENV=y +CONFIG_EXPAND=y +CONFIG_FALSE=y +CONFIG_FILE=y +CONFIG_FIND=y +CONFIG_GETCONF=y +CONFIG_GREP=y +CONFIG_HEAD=y +CONFIG_ICONV=y +CONFIG_ID=y +CONFIG_GROUPS=y +CONFIG_LOGNAME=y +CONFIG_WHOAMI=y +CONFIG_KILL=y +CONFIG_LN=y +CONFIG_LS=y +CONFIG_MKDIR=y +CONFIG_MKFIFO=y +CONFIG_NICE=y +CONFIG_NL=y +CONFIG_NOHUP=y +CONFIG_OD=y +CONFIG_PASTE=y +CONFIG_PATCH=y +CONFIG_PRINTF=y +CONFIG_PS=y +CONFIG_TOP=y +CONFIG_IOTOP=y +CONFIG_PGREP=y +CONFIG_PKILL=y +CONFIG_PWD=y +CONFIG_RENICE=y +CONFIG_RM=y +CONFIG_RMDIR=y +CONFIG_SED=y +CONFIG_SLEEP=y +CONFIG_SORT=y +CONFIG_SPLIT=y +CONFIG_STRINGS=y +CONFIG_TAIL=y +CONFIG_TAR=y +CONFIG_TEE=y +CONFIG_TEST=y +CONFIG_TIME=y +CONFIG_TOUCH=y +CONFIG_TRUE=y +CONFIG_TTY=y +CONFIG_ULIMIT=y +CONFIG_UNAME=y +CONFIG_UNIQ=y +CONFIG_UNLINK=y +CONFIG_UUDECODE=y +CONFIG_UUENCODE=y +CONFIG_WC=y +CONFIG_XARGS=y +CONFIG_DD=y +CONFIG_DIFF=y +CONFIG_EXPR=y +CONFIG_GETFATTR=y +CONFIG_GETOPT=y +CONFIG_LSOF=y +CONFIG_MODPROBE=y +CONFIG_MORE=y +CONFIG_STTY=y +CONFIG_TR=y +CONFIG_TRACEROUTE=y +CONFIG_VI=y +CONFIG_ACPI=y +CONFIG_BASE64=y +CONFIG_BLKDISCARD=y +CONFIG_BLKID=y +CONFIG_BLOCKDEV=y +CONFIG_CHCON=y +CONFIG_CHROOT=y +CONFIG_CHRT=y +CONFIG_CLEAR=y +CONFIG_DEVMEM=y +CONFIG_DOS2UNIX=y +CONFIG_UNIX2DOS=y +CONFIG_FALLOCATE=y +CONFIG_FLOCK=y +CONFIG_FMT=y +CONFIG_FREE=y +CONFIG_FREERAMDISK=y +CONFIG_FSFREEZE=y +CONFIG_FSYNC=y +CONFIG_HELP=y +CONFIG_HWCLOCK=y +CONFIG_I2CDETECT=y +CONFIG_I2CDUMP=y +CONFIG_I2CGET=y +CONFIG_I2CSET=y +CONFIG_INOTIFYD=y +CONFIG_INSMOD=y +CONFIG_IONICE=y +CONFIG_IORENICE=y +CONFIG_LOSETUP=y +CONFIG_LSATTR=y +CONFIG_CHATTR=y +CONFIG_LSMOD=y +CONFIG_LSPCI=y +CONFIG_LSUSB=y +CONFIG_MAKEDEVS=y +CONFIG_MKSWAP=y +CONFIG_MODINFO=y +CONFIG_MOUNTPOINT=y +CONFIG_NBD_CLIENT=y +CONFIG_UNSHARE=y +CONFIG_NSENTER=y +CONFIG_PARTPROBE=y +CONFIG_PIVOT_ROOT=y +CONFIG_PMAP=y +CONFIG_PRINTENV=y +CONFIG_PWDX=y +CONFIG_READELF=y +CONFIG_READLINK=y +CONFIG_REALPATH=y +CONFIG_REV=y +CONFIG_RMMOD=y +CONFIG_RTCWAKE=y +CONFIG_SETFATTR=y +CONFIG_SETSID=y +CONFIG_STAT=y +CONFIG_SWAPOFF=y +CONFIG_SWAPON=y +CONFIG_SYSCTL=y +CONFIG_TAC=y +CONFIG_NPROC=y +CONFIG_TASKSET=y +CONFIG_TIMEOUT=y +CONFIG_TRUNCATE=y +CONFIG_UCLAMPSET=y +CONFIG_UPTIME=y +CONFIG_USLEEP=y +CONFIG_UUIDGEN=y +CONFIG_VCONFIG=y +CONFIG_VMSTAT=y +CONFIG_WATCH=y +CONFIG_WHICH=y +CONFIG_XXD=y +CONFIG_YES=y +CONFIG_IFCONFIG=y +CONFIG_MICROCOM=y +CONFIG_NETCAT=y +CONFIG_NETSTAT=y +CONFIG_PING=y +CONFIG_RFKILL=y +CONFIG_TUNCTL=y +CONFIG_DMESG=y +CONFIG_GZIP=y +CONFIG_GUNZIP=y +CONFIG_ZCAT=y +CONFIG_HOSTNAME=y +CONFIG_KILLALL=y +CONFIG_MD5SUM=y +CONFIG_SHA1SUM=y +CONFIG_SHA224SUM=y +CONFIG_SHA256SUM=y +CONFIG_SHA384SUM=y +CONFIG_SHA512SUM=y +CONFIG_MKNOD=y +CONFIG_MKTEMP=y +CONFIG_MOUNT=y +CONFIG_PIDOF=y +CONFIG_SEQ=y +CONFIG_SYNC=y +CONFIG_UMOUNT=y +CONFIG_GETENFORCE=y +CONFIG_LOAD_POLICY=y +CONFIG_RESTORECON=y +CONFIG_RUNCON=y +CONFIG_SETENFORCE=y +CONFIG_TOYBOX_SELINUX=y +CONFIG_TOYBOX_LIBCRYPTO=y +CONFIG_TOYBOX_LIBZ=y +CONFIG_TOYBOX_FLOAT=y +CONFIG_TOYBOX_HELP=y +CONFIG_TOYBOX_HELP_DASHDASH=y +CONFIG_TOYBOX_NORECURSE=y diff --git a/kconfig/kconfig-language.txt b/kconfig/kconfig-language.txt new file mode 100644 index 0000000..d821369 --- /dev/null +++ b/kconfig/kconfig-language.txt @@ -0,0 +1,284 @@ +Linux 2.6.12 Documentation/kbuild/kconfig-language.txt + +Introduction +------------ + +The configuration database is collection of configuration options +organized in a tree structure: + + +- Code maturity level options + | +- Prompt for development and/or incomplete code/drivers + +- General setup + | +- Networking support + | +- System V IPC + | +- BSD Process Accounting + | +- Sysctl support + +- Loadable module support + | +- Enable loadable module support + | +- Set version information on all module symbols + | +- Kernel module loader + +- ... + +Every entry has its own dependencies. These dependencies are used +to determine the visibility of an entry. Any child entry is only +visible if its parent entry is also visible. + +Menu entries +------------ + +Most entries define a config option, all other entries help to organize +them. A single configuration option is defined like this: + +config MODVERSIONS + bool "Set version information on all module symbols" + depends MODULES + help + Usually, modules have to be recompiled whenever you switch to a new + kernel. ... + +Every line starts with a key word and can be followed by multiple +arguments. "config" starts a new config entry. The following lines +define attributes for this config option. Attributes can be the type of +the config option, input prompt, dependencies, help text and default +values. A config option can be defined multiple times with the same +name, but every definition can have only a single input prompt and the +type must not conflict. + +Menu attributes +--------------- + +A menu entry can have a number of attributes. Not all of them are +applicable everywhere (see syntax). + +- type definition: "bool"/"tristate"/"string"/"hex"/"int" + Every config option must have a type. There are only two basic types: + tristate and string, the other types are based on these two. The type + definition optionally accepts an input prompt, so these two examples + are equivalent: + + bool "Networking support" + and + bool + prompt "Networking support" + +- input prompt: "prompt" ["if" ] + Every menu entry can have at most one prompt, which is used to display + to the user. Optionally dependencies only for this prompt can be added + with "if". + +- default value: "default" ["if" ] + A config option can have any number of default values. If multiple + default values are visible, only the first defined one is active. + Default values are not limited to the menu entry, where they are + defined, this means the default can be defined somewhere else or be + overridden by an earlier definition. + The default value is only assigned to the config symbol if no other + value was set by the user (via the input prompt above). If an input + prompt is visible the default value is presented to the user and can + be overridden by him. + Optionally dependencies only for this default value can be added with + "if". + +- dependencies: "depends on"/"requires" + This defines a dependency for this menu entry. If multiple + dependencies are defined they are connected with '&&'. Dependencies + are applied to all other options within this menu entry (which also + accept an "if" expression), so these two examples are equivalent: + + bool "foo" if BAR + default y if BAR + and + depends on BAR + bool "foo" + default y + +- reverse dependencies: "select" ["if" ] + While normal dependencies reduce the upper limit of a symbol (see + below), reverse dependencies can be used to force a lower limit of + another symbol. The value of the current menu symbol is used as the + minimal value can be set to. If is selected multiple + times, the limit is set to the largest selection. + Reverse dependencies can only be used with boolean or tristate + symbols. + +- numerical ranges: "range" ["if" ] + This allows to limit the range of possible input values for int + and hex symbols. The user can only input a value which is larger than + or equal to the first symbol and smaller than or equal to the second + symbol. + +- help text: "help" or "---help---" + This defines a help text. The end of the help text is determined by + the indentation level, this means it ends at the first line which has + a smaller indentation than the first line of the help text. + "---help---" and "help" do not differ in behaviour, "---help---" is + used to help visually seperate configuration logic from help within + the file as an aid to developers. + + +Menu dependencies +----------------- + +Dependencies define the visibility of a menu entry and can also reduce +the input range of tristate symbols. The tristate logic used in the +expressions uses one more state than normal boolean logic to express the +module state. Dependency expressions have the following syntax: + + ::= (1) + '=' (2) + '!=' (3) + '(' ')' (4) + '!' (5) + '&&' (6) + '||' (7) + +Expressions are listed in decreasing order of precedence. + +(1) Convert the symbol into an expression. Boolean and tristate symbols + are simply converted into the respective expression values. All + other symbol types result in 'n'. +(2) If the values of both symbols are equal, it returns 'y', + otherwise 'n'. +(3) If the values of both symbols are equal, it returns 'n', + otherwise 'y'. +(4) Returns the value of the expression. Used to override precedence. +(5) Returns the result of (2-/expr/). +(6) Returns the result of min(/expr/, /expr/). +(7) Returns the result of max(/expr/, /expr/). + +An expression can have a value of 'n', 'm' or 'y' (or 0, 1, 2 +respectively for calculations). A menu entry becomes visible when it's +expression evaluates to 'm' or 'y'. + +There are two types of symbols: constant and nonconstant symbols. +Nonconstant symbols are the most common ones and are defined with the +'config' statement. Nonconstant symbols consist entirely of alphanumeric +characters or underscores. +Constant symbols are only part of expressions. Constant symbols are +always surrounded by single or double quotes. Within the quote any +other character is allowed and the quotes can be escaped using '\'. + +Menu structure +-------------- + +The position of a menu entry in the tree is determined in two ways. First +it can be specified explicitly: + +menu "Network device support" + depends NET + +config NETDEVICES + ... + +endmenu + +All entries within the "menu" ... "endmenu" block become a submenu of +"Network device support". All subentries inherit the dependencies from +the menu entry, e.g. this means the dependency "NET" is added to the +dependency list of the config option NETDEVICES. + +The other way to generate the menu structure is done by analyzing the +dependencies. If a menu entry somehow depends on the previous entry, it +can be made a submenu of it. First, the previous (parent) symbol must +be part of the dependency list and then one of these two conditions +must be true: +- the child entry must become invisible, if the parent is set to 'n' +- the child entry must only be visible, if the parent is visible + +config MODULES + bool "Enable loadable module support" + +config MODVERSIONS + bool "Set version information on all module symbols" + depends MODULES + +comment "module support disabled" + depends !MODULES + +MODVERSIONS directly depends on MODULES, this means it's only visible if +MODULES is different from 'n'. The comment on the other hand is always +visible when MODULES is visible (the (empty) dependency of MODULES is +also part of the comment dependencies). + + +Kconfig syntax +-------------- + +The configuration file describes a series of menu entries, where every +line starts with a keyword (except help texts). The following keywords +end a menu entry: +- config +- menuconfig +- choice/endchoice +- comment +- menu/endmenu +- if/endif +- source +The first five also start the definition of a menu entry. + +config: + + "config" + + +This defines a config symbol and accepts any of above +attributes as options. + +menuconfig: + "menuconfig" + + +This is similiar to the simple config entry above, but it also gives a +hint to front ends, that all suboptions should be displayed as a +separate list of options. + +choices: + + "choice" + + + "endchoice" + +This defines a choice group and accepts any of above attributes as +options. A choice can only be of type bool or tristate, while a boolean +choice only allows a single config entry to be selected, a tristate +choice also allows any number of config entries to be set to 'm'. This +can be used if multiple drivers for a single hardware exists and only a +single driver can be compiled/loaded into the kernel, but all drivers +can be compiled as modules. +A choice accepts another option "optional", which allows to set the +choice to 'n' and no entry needs to be selected. + +comment: + + "comment" + + +This defines a comment which is displayed to the user during the +configuration process and is also echoed to the output files. The only +possible options are dependencies. + +menu: + + "menu" + + + "endmenu" + +This defines a menu block, see "Menu structure" above for more +information. The only possible options are dependencies. + +if: + + "if" + + "endif" + +This defines an if block. The dependency expression is appended +to all enclosed menu entries. + +source: + + "source" + +This reads the specified configuration file. This file is always parsed. diff --git a/lib/lib.c b/lib/lib.c index 6c7456a..142c7e3 100644 --- a/lib/lib.c +++ b/lib/lib.c @@ -1565,6 +1565,7 @@ int smemcmp(char *one, char *two, unsigned long len) return ii; } +// patch.c used // Slow, but small. char *get_line(int fd) { diff --git a/lib/lib.h b/lib/lib.h index b4a39b2..fe0cac3 100644 --- a/lib/lib.h +++ b/lib/lib.h @@ -121,7 +121,10 @@ void xputsl(char *s, int len); void xputsn(char *s); void xputs(char *s); void xputc(char c); +#ifdef TOYBOX_OH_ADAPT +// watch.c used. fix exit with abnormal char '' void xflush(int flush); +#endif void xvdaemon(void); void xexec(char **argv); pid_t xpopen_setup(char **argv, int *pipes, void (*callback)(char **argv)); @@ -442,4 +445,5 @@ pid_t __attribute__((returns_twice)) xvforkwrap(pid_t pid); #define minof(a, b) ({typeof(a) aa = (a); typeof(b) bb = (b); aabb ? aa : bb;}) +// patch.c used char *get_line(int fd); diff --git a/lib/xwrap.c b/lib/xwrap.c index a5552fc..f79ab0b 100644 --- a/lib/xwrap.c +++ b/lib/xwrap.c @@ -147,12 +147,15 @@ void xferror(FILE *fp) if (ferror(fp)) perror_exit(fp==stdout ? "stdout" : "write"); } +#ifdef TOYBOX_OH_ADAPT +// watch.c used. fix exit with abnormal char '' // if !flush just check for error on stdout without flushing void xflush(int flush) { if ((flush && fflush(0)) || ferror(stdout)) if (!toys.exitval) perror_msg("write"); } +#endif void xprintf(char *format, ...) { @@ -729,7 +732,11 @@ struct group *xgetgrgid(gid_t gid) { struct group *group = getgrgid(gid); +#ifdef TOYBOX_OH_ADAPT if (!group) perror_exit("bad gid %ld", (long)gid); +#else + if (!group) perror_exit("gid %ld", (long)gid); +#endif return group; } @@ -765,7 +772,11 @@ struct passwd *xgetpwnam(char *name) { struct passwd *up = getpwnam(name); +#ifdef TOYBOX_OH_ADAPT if (!up) perror_exit("bad user '%s'", name); +#else + if (!up) perror_exit("user '%s'", name); +#endif return up; } @@ -773,7 +784,11 @@ struct group *xgetgrnam(char *name) { struct group *gr = getgrnam(name); +#ifdef TOYBOX_OH_ADAPT if (!gr) perror_exit("bad group '%s'", name); +#else + if (!gr) perror_exit("group '%s'", name); +#endif return gr; } diff --git a/tests/sha224sum.test b/tests/sha224sum.test deleted file mode 100644 index bc02ebe..0000000 --- a/tests/sha224sum.test +++ /dev/null @@ -1 +0,0 @@ -sha1sum.test \ No newline at end of file diff --git a/tests/sha224sum.test b/tests/sha224sum.test new file mode 120000 index 0000000..bc02ebe --- /dev/null +++ b/tests/sha224sum.test @@ -0,0 +1 @@ +sha1sum.test \ No newline at end of file diff --git a/tests/sha256sum.test b/tests/sha256sum.test deleted file mode 100644 index bc02ebe..0000000 --- a/tests/sha256sum.test +++ /dev/null @@ -1 +0,0 @@ -sha1sum.test \ No newline at end of file diff --git a/tests/sha256sum.test b/tests/sha256sum.test new file mode 120000 index 0000000..bc02ebe --- /dev/null +++ b/tests/sha256sum.test @@ -0,0 +1 @@ +sha1sum.test \ No newline at end of file diff --git a/tests/sha384sum.test b/tests/sha384sum.test deleted file mode 100644 index bc02ebe..0000000 --- a/tests/sha384sum.test +++ /dev/null @@ -1 +0,0 @@ -sha1sum.test \ No newline at end of file diff --git a/tests/sha384sum.test b/tests/sha384sum.test new file mode 120000 index 0000000..bc02ebe --- /dev/null +++ b/tests/sha384sum.test @@ -0,0 +1 @@ +sha1sum.test \ No newline at end of file diff --git a/tests/sha3sum.test b/tests/sha3sum.test deleted file mode 100644 index bc02ebe..0000000 --- a/tests/sha3sum.test +++ /dev/null @@ -1 +0,0 @@ -sha1sum.test \ No newline at end of file diff --git a/tests/sha3sum.test b/tests/sha3sum.test new file mode 120000 index 0000000..bc02ebe --- /dev/null +++ b/tests/sha3sum.test @@ -0,0 +1 @@ +sha1sum.test \ No newline at end of file diff --git a/tests/sha512sum.test b/tests/sha512sum.test deleted file mode 100644 index bc02ebe..0000000 --- a/tests/sha512sum.test +++ /dev/null @@ -1 +0,0 @@ -sha1sum.test \ No newline at end of file diff --git a/tests/sha512sum.test b/tests/sha512sum.test new file mode 120000 index 0000000..bc02ebe --- /dev/null +++ b/tests/sha512sum.test @@ -0,0 +1 @@ +sha1sum.test \ No newline at end of file diff --git a/toys.h b/toys.h index e781669..ae9d34a 100644 --- a/toys.h +++ b/toys.h @@ -11,7 +11,9 @@ // General posix-2008 headers #include #include +#ifdef TOYBOX_OH_ADAPT #include +#endif #include #include #include diff --git a/toys/android/README b/toys/android/README index 5c643c5..b1b627c 100644 --- a/toys/android/README +++ b/toys/android/README @@ -3,4 +3,4 @@ Android commands Commands primarily used by Android, not present in vanilla Linux. (Mostly SELinux stuff.) -Bug Elliott Hughes about this. \ No newline at end of file +Bug Elliott Hughes about this. diff --git a/toys/example/README b/toys/example/README index 3b0deac..6e73fa1 100644 --- a/toys/example/README +++ b/toys/example/README @@ -20,4 +20,4 @@ Some of the commands in here are test infrastructure: regression test library functions directly. hostid.c is an obsolete command still in posix and present on some recent -Linux systems, but not really useful on modern systems. \ No newline at end of file +Linux systems, but not really useful on modern systems. diff --git a/toys/example/logwrapper.c b/toys/example/logwrapper.c deleted file mode 100644 index 036fcd3..0000000 --- a/toys/example/logwrapper.c +++ /dev/null @@ -1,75 +0,0 @@ -/* logwrapper.c - Record commands called out of $PATH to a log - * - * Copyright 2019 Rob Landley - * - * I made it up. Must be built standalone to work. (Is its own multiplexer.) - -USE_LOGWRAPPER(NEWTOY(logwrapper, 0, TOYFLAG_NOHELP|TOYFLAG_USR|TOYFLAG_BIN)) - -config LOGWRAPPER - bool "logwrapper" - default n - help - usage: logwrapper ... - - Append command line to $WRAPLOG, then call second instance - of command in $PATH. -*/ - -#define FOR_logwrapper -#include "toys.h" - -void logwrapper_main(void) -{ - char *log = getenv("WRAPLOG"), *omnom = basename(*toys.argv), - *s, *ss, *sss; - struct string_list *list; - int i, len; - - // Log the command line - if (!log) error_exit("no $WRAPLOG"); - len = strlen(omnom)+2; - for (i = 0; istr, *toys.argv)) break; - free(llist_pop(&list)); - } - } - - // Skip first instance and try to run next one, until out of instances. - for (;;) { - if (list) free(llist_pop(&list)); - if (!list) - error_exit("no %s after %s in $PATH=%s", omnom, - **toys.argv == '/' ? *toys.argv : "logwrapper", getenv("PATH")); - *toys.argv = list->str; - execve(list->str, toys.argv, environ); - } -} diff --git a/toys/net/README b/toys/net/README index 7a16262..49259ca 100644 --- a/toys/net/README +++ b/toys/net/README @@ -3,4 +3,4 @@ Networking commands The Internet Engineering Task Force publishes standards drafts at https://www.ietf.org/rfc/rfc-index.txt (ala https://www.ietf.org/rfc/rfc3.txt) and the commands tend to be documented in manual page section 8 -https://man7.org/linux/man-pages/dir_section_8.html \ No newline at end of file +https://man7.org/linux/man-pages/dir_section_8.html diff --git a/toys/net/netcat.c b/toys/net/netcat.c index ef94cf9..557380a 100644 --- a/toys/net/netcat.c +++ b/toys/net/netcat.c @@ -255,4 +255,4 @@ cleanup: close(in1); close(sockfd); } -} \ No newline at end of file +} diff --git a/toys/net/ping.c b/toys/net/ping.c index 3b73023..f71ab82 100644 --- a/toys/net/ping.c +++ b/toys/net/ping.c @@ -13,7 +13,7 @@ // -s > 4064 = sizeof(toybuf)-sizeof(struct icmphdr)-CMSG_SPACE(sizeof(uint8_t)), then kernel adds 20 bytes USE_PING(NEWTOY(ping, "<1>1m#t#<0>255=64c#<0=3s#<0>4064=56i%W#<0=3w#<0qf46I:[-46]", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LINEBUF)) USE_PING(OLDTOY(ping6, ping, TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_LINEBUF)) - + config PING bool "ping" default y @@ -40,7 +40,7 @@ config PING -w SEC Exit after this many seconds */ -#define FOR_ping +#define FOR_ping #include "toys.h" #include @@ -215,7 +215,7 @@ void ping_main(void) memset(&msg, 0, sizeof(msg)); // left enought space to store ttl value - + #ifdef TOYBOX_OH_ADAPT /* fix "ping -s 65500" fail problem*/ char *mybuff = malloc(65536); @@ -338,9 +338,7 @@ void ping_main(void) toys.exitval = 0; } - sigatexit(0); - summary(0); - + // summary(0) gets called for us atexit. if (CFG_TOYBOX_FREE) { freeaddrinfo(ai2); if (ifa2) freeifaddrs(ifa2); diff --git a/toys/other/bzcat.c b/toys/other/bzcat.c index df8bd87..c27324d 100644 --- a/toys/other/bzcat.c +++ b/toys/other/bzcat.c @@ -723,7 +723,7 @@ static void do_bunzip2(int fd, char *name) free(tmp); tmp = 0; #endif - } else { + } else if (!err) { if (dotbz) *dotbz = '.'; // unlink success return 0, fail return -1 #ifdef TOYBOX_OH_ADAPT diff --git a/toys/other/free.c b/toys/other/free.c index ce0df02..6be1cdc 100644 --- a/toys/other/free.c +++ b/toys/other/free.c @@ -2,8 +2,8 @@ * * Copyright 2012 Elie De Brauwer -// Flag order is signifcant: b-t are units in order, FLAG_h-1 is unit mask -USE_FREE(NEWTOY(free, "htgmkb[!htgmkb]", TOYFLAG_USR|TOYFLAG_BIN)) +// Flag order is signifcant: b-g are units in order, FLAG_h-1 is unit mask +USE_FREE(NEWTOY(free, "hgmkb[!hgmkb]", TOYFLAG_USR|TOYFLAG_BIN)) config FREE bool "free" @@ -13,7 +13,7 @@ config FREE Display the total, free and used amount of physical memory and swap space. - -bkmgt Output units (default is bytes) + -bkmg Output units (default is bytes) -h Human readable (K=1024) */ @@ -31,7 +31,7 @@ static char *convert(unsigned long d) long long ll = d*TT.units; char *s = TT.buf; - if (toys.optflags & FLAG_h) human_readable(s, ll, 0); + if (FLAG(h)) human_readable(s, ll, 0); else sprintf(s, "%llu",ll>>TT.bits); TT.buf += strlen(TT.buf)+1; diff --git a/toys/other/lsattr.c b/toys/other/lsattr.c index 5a10201..93af8cd 100644 --- a/toys/other/lsattr.c +++ b/toys/other/lsattr.c @@ -7,33 +7,35 @@ * * TODO cleanup -USE_LSATTR(NEWTOY(lsattr, "vldaR", TOYFLAG_BIN)) -USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN)) +USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN)) +USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN)) config LSATTR bool "lsattr" default y help - usage: lsattr [-Radlv] [Files...] + usage: lsattr [-Radlpv] [FILE...] - List file attributes on a Linux second extended file system. - (AacDdijsStu defined in chattr --help) + List file attributes on a Linux file system. + Flag letters are defined in chattr help. -R Recursively list attributes of directories and their contents -a List all files in directories, including files that start with '.' -d List directories like other files, rather than listing their contents -l List long flag names + -p List the file's project number -v List the file's version/generation number config CHATTR bool "chattr" default y help - usage: chattr [-R] [-+=AacDdijsStTu] [-v version] [File...] + usage: chattr [-R] [-+=AacDdijsStTu] [-p PROJID] [-v VERSION] [FILE...] - Change file attributes on a Linux second extended file system. + Change file attributes on a Linux file system. -R Recurse + -p Set the file's project number -v Set the file's version/generation number Operators: @@ -42,77 +44,121 @@ config CHATTR '=' Set attributes Attributes: - A Don't track atime - a Append mode only - c Enable compress - D Write dir contents synchronously - d Don't backup with dump - i Cannot be modified (immutable) - j Write all data to journal first - s Zero disk storage when deleted - S Write file contents synchronously - t Disable tail-merging of partial blocks with other files - u Allow file to be undeleted + A No atime a Append only + C No COW c Compression + D Synchronous dir updates d No dump + E Encrypted e Extents + F Case-insensitive (casefold) + I Indexed directory i Immutable + j Journal data + N Inline data in inode + P Project hierarchy + S Synchronous file updates s Secure delete + T Top of dir hierarchy t No tail-merging + u Allow undelete + V Verity */ #define FOR_lsattr #include "toys.h" #include +GLOBALS( + long v, p; + + unsigned add, rm, set; + // !add and !rm tell us whether they were used, but `chattr =` is meaningful. + int have_set; +) + +// Added more recently than the 7 year support horizon. TODO: remove +#ifndef FS_CASEFOLD_FL +#define FS_CASEFOLD_FL 0x40000000 // commit 71e90b4654a92 2019-07-23 +#endif +#ifndef FS_VERITY_FL +#define FS_VERITY_FL 0x00100000 // commit fe9918d3b228b 2019-07-22 +#endif + static struct ext2_attr { char *name; - unsigned long flag; + unsigned flag; char opt; } e2attrs[] = { - {"Secure_Deletion", FS_SECRM_FL, 's'}, // Secure deletion - {"Undelete", FS_UNRM_FL, 'u'}, // Undelete - {"Compression_Requested", FS_COMPR_FL, 'c'}, // Compress file - {"Synchronous_Updates", FS_SYNC_FL, 'S'}, // Synchronous updates - {"Immutable", FS_IMMUTABLE_FL, 'i'}, // Immutable file - {"Append_Only", FS_APPEND_FL, 'a'}, // writes to file may only append - {"No_Dump", FS_NODUMP_FL, 'd'}, // do not dump file - {"No_Atime", FS_NOATIME_FL, 'A'}, // do not update atime - {"Indexed_directory", FS_INDEX_FL, 'I'}, // hash-indexed directory - {"Journaled_Data", FS_JOURNAL_DATA_FL, 'j'}, // file data should be journaled - {"No_Tailmerging", FS_NOTAIL_FL, 't'}, // file tail should not be merged - {"Synchronous_Directory_Updates", FS_DIRSYNC_FL, 'D'}, // dirsync behaviour (directories only) - {"Top_of_Directory_Hierarchies", FS_TOPDIR_FL, 'T'}, // Top of directory hierarchies - {NULL, -1, 0}, + // Do not sort! These are in the order that lsattr outputs them. + {"Secure_Deletion", FS_SECRM_FL, 's'}, + {"Undelete", FS_UNRM_FL, 'u'}, + {"Synchronous_Updates", FS_SYNC_FL, 'S'}, + {"Synchronous_Directory_Updates", FS_DIRSYNC_FL, 'D'}, + {"Immutable", FS_IMMUTABLE_FL, 'i'}, + {"Append_Only", FS_APPEND_FL, 'a'}, + {"No_Dump", FS_NODUMP_FL, 'd'}, + {"No_Atime", FS_NOATIME_FL, 'A'}, + {"Compression_Requested", FS_COMPR_FL, 'c'}, + {"Encrypted", FS_ENCRYPT_FL, 'E'}, + {"Journaled_Data", FS_JOURNAL_DATA_FL, 'j'}, + {"Indexed_directory", FS_INDEX_FL, 'I'}, + {"No_Tailmerging", FS_NOTAIL_FL, 't'}, + {"Top_of_Directory_Hierarchies", FS_TOPDIR_FL, 'T'}, + {"Extents", FS_EXTENT_FL, 'e'}, + {"No_COW", FS_NOCOW_FL, 'C'}, + {"Casefold", FS_CASEFOLD_FL, 'F'}, + {"Inline_Data", FS_INLINE_DATA_FL, 'N'}, + {"Project_Hierarchy", FS_PROJINHERIT_FL, 'P'}, + {"Verity", FS_VERITY_FL, 'V'}, + {NULL, 0, 0}, }; // Get file flags on a Linux second extended file system. -static int ext2_getflag(int fd, struct stat *sb, unsigned long *flag) +static int ext2_getflag(int fd, struct stat *sb, unsigned *flag) { if(!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) { errno = EOPNOTSUPP; return -1; } - return (ioctl(fd, FS_IOC_GETFLAGS, (void*)flag)); + return ioctl(fd, FS_IOC_GETFLAGS, flag); +} + +static char *attrstr(unsigned attrs, int full) +{ + struct ext2_attr *a = e2attrs; + char *s = toybuf; + + for (; a->name; a++) + if (attrs & a->flag) *s++ = a->opt; + else if (full) *s++ = '-'; + *s = 0; + + return toybuf; } static void print_file_attr(char *path) { - unsigned long flag = 0, version = 0; - int fd; + unsigned flag = 0, version = 0; + int fd = -1; struct stat sb; if (!stat(path, &sb) && !S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) { errno = EOPNOTSUPP; - goto LABEL1; + goto error; } - if (-1 == (fd=open(path, O_RDONLY | O_NONBLOCK))) goto LABEL1; + if (-1 == (fd=open(path, O_RDONLY | O_NONBLOCK))) goto error; - if (toys.optflags & FLAG_v) { - if (ioctl(fd, FS_IOC_GETVERSION, (void*)&version) < 0) goto LABEL2; - xprintf("%5lu ", version); + if (FLAG(p)) { + struct fsxattr fsx; + + if (ioctl(fd, FS_IOC_FSGETXATTR, &fsx)) goto error; + xprintf("%5u ", fsx.fsx_projid); + } + if (FLAG(v)) { + if (ioctl(fd, FS_IOC_GETVERSION, (void*)&version) < 0) goto error; + xprintf("%-10u ", version); } if (ext2_getflag(fd, &sb, &flag) < 0) perror_msg("reading flags '%s'", path); else { struct ext2_attr *ptr = e2attrs; + int name_found = 0; - if (toys.optflags & FLAG_l) { - int name_found = 0; - + if (FLAG(l)) { xprintf("%-50s ", path); for (; ptr->name; ptr++) { if (flag & ptr->flag) { @@ -123,127 +169,89 @@ static void print_file_attr(char *path) } if (!name_found) xprintf("---"); xputc('\n'); - } else { - int index = 0; - - for (; ptr->name; ptr++) - toybuf[index++] = (flag & ptr->flag) ? ptr->opt : '-'; - toybuf[index] = '\0'; - xprintf("%s %s\n", toybuf, path); - } + } else xprintf("%s %s\n", attrstr(flag, 1), path); } + path = 0; +error: xclose(fd); - return; -LABEL2: xclose(fd); -LABEL1: perror_msg("reading '%s'", path); + if (path) perror_msg("reading '%s'", path); } // Get directory information. static int retell_dir(struct dirtree *root) { - char *fpath = NULL; - + char *fpath = 0; + if (root->again) { xputc('\n'); + return 0; } - if (S_ISDIR(root->st.st_mode) && !root->parent) - return (DIRTREE_RECURSE | DIRTREE_COMEAGAIN); + if (S_ISDIR(root->st.st_mode) && !root->parent) + return DIRTREE_RECURSE|DIRTREE_COMEAGAIN; fpath = dirtree_path(root, NULL); - //Special case: with '-a' option and '.'/'..' also included in printing list. - if ((root->name[0] != '.') || (toys.optflags & FLAG_a)) { + if (*root->name != '.' || FLAG(a)) { print_file_attr(fpath); - if (S_ISDIR(root->st.st_mode) && (toys.optflags & FLAG_R) - && dirtree_notdotdot(root)) { + if (S_ISDIR(root->st.st_mode) && FLAG(R) && dirtree_notdotdot(root)) { xprintf("\n%s:\n", fpath); free(fpath); - return (DIRTREE_RECURSE | DIRTREE_COMEAGAIN); + return DIRTREE_RECURSE|DIRTREE_COMEAGAIN; } } free(fpath); + return 0; } void lsattr_main(void) { if (!*toys.optargs) dirtree_read(".", retell_dir); - else - for (; *toys.optargs; toys.optargs++) { - struct stat sb; + else for (; *toys.optargs; toys.optargs++) { + struct stat sb; - if (lstat(*toys.optargs, &sb)) perror_msg("stat '%s'", *toys.optargs); - else if (S_ISDIR(sb.st_mode) && !(toys.optflags & FLAG_d)) - dirtree_read(*toys.optargs, retell_dir); - else print_file_attr(*toys.optargs);// to handle "./Filename" or "./Dir" - } + if (lstat(*toys.optargs, &sb)) perror_msg("stat '%s'", *toys.optargs); + else if (S_ISDIR(sb.st_mode) && !FLAG(d)) + dirtree_read(*toys.optargs, retell_dir); + else print_file_attr(*toys.optargs);// to handle "./Filename" or "./Dir" + } } // Switch gears from lsattr to chattr. -#define CLEANUP_lsattr #define FOR_chattr #include "generated/flags.h" -static struct _chattr { - unsigned long add, rm, set, version; - unsigned char vflag, recursive; -} chattr; - // Set file flags on a Linux second extended file system. -static inline int ext2_setflag(int fd, struct stat *sb, unsigned long flag) +static inline int ext2_setflag(int fd, struct stat *sb, unsigned flag) { if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) { errno = EOPNOTSUPP; return -1; } - return (ioctl(fd, FS_IOC_SETFLAGS, (void*)&flag)); + return ioctl(fd, FS_IOC_SETFLAGS, &flag); } -static unsigned long get_flag_val(char ch) +static unsigned get_flag_val(char ch) { struct ext2_attr *ptr = e2attrs; - for (; ptr->name; ptr++) - if (ptr->opt == ch) return ptr->flag; + for (; ptr->name; ptr++) if (ptr->opt == ch) return ptr->flag; help_exit("bad '%c'", ch); } // Parse command line argument and fill the chattr structure. static void parse_cmdline_arg(char ***argv) { - char *arg = **argv, *ptr = NULL; + char *arg = **argv, *ptr; while (arg) { - switch (arg[0]) { - case '-': - for (ptr = ++arg; *ptr; ptr++) { - if (*ptr == 'R') { - chattr.recursive = 1; - continue; - } else if (*ptr == 'v') {// get version from next argv. - char *endptr; - - errno = 0; - arg = *(*argv += 1); - if (!arg) help_exit("bad -v"); - if (*arg == '-') perror_exit("Invalid Number '%s'", arg); - chattr.version = strtoul(arg, &endptr, 0); - if (errno || *endptr) perror_exit("bad version '%s'", arg); - chattr.vflag = 1; - continue; - } else chattr.rm |= get_flag_val(*ptr); - } - break; - case '+': - for (ptr = ++arg; *ptr; ptr++) - chattr.add |= get_flag_val(*ptr); - break; - case '=': - for (ptr = ++arg; *ptr; ptr++) - chattr.set |= get_flag_val(*ptr); - break; - default: return; - } + if (*arg=='-') for (ptr = ++arg; *ptr; ptr++) TT.rm |= get_flag_val(*ptr); + else if (*arg=='+') + for (ptr = ++arg; *ptr; ptr++) TT.add |= get_flag_val(*ptr); + else if (*arg=='=') { + TT.have_set = 1; + for (ptr = ++arg; *ptr; ptr++) TT.set |= get_flag_val(*ptr); + } else return; arg = *(*argv += 1); } } @@ -251,17 +259,14 @@ static void parse_cmdline_arg(char ***argv) // Update attribute of given file. static int update_attr(struct dirtree *root) { - unsigned long fval = 0; - char *fpath = NULL; - int fd; + char *fpath = 0; + int vv = TT.v, fd; if (!dirtree_notdotdot(root)) return 0; - /* - * if file is a link and recursive is set or file is not regular+link+dir - * (like fifo or dev file) then escape the file. - */ - if ((S_ISLNK(root->st.st_mode) && chattr.recursive) + // if file is a link and recursive is set or file is not regular+link+dir + // (like fifo or dev file) then escape the file. + if ((S_ISLNK(root->st.st_mode) && FLAG(R)) || (!S_ISREG(root->st.st_mode) && !S_ISLNK(root->st.st_mode) && !S_ISDIR(root->st.st_mode))) return 0; @@ -271,44 +276,62 @@ static int update_attr(struct dirtree *root) free(fpath); return DIRTREE_ABORT; } - // Get current attr of file. - if (ext2_getflag(fd, &(root->st), &fval) < 0) { - perror_msg("read flags of '%s'", fpath); - free(fpath); - xclose(fd); - return DIRTREE_ABORT; + + // Any potential flag changes? + if (TT.have_set | TT.add | TT.rm) { + unsigned orig, new; + + // Read current flags. + if (ext2_getflag(fd, &(root->st), &orig) < 0) { + perror_msg("read flags of '%s'", fpath); + free(fpath); + xclose(fd); + return DIRTREE_ABORT; + } + // Apply the requested changes. + if (TT.have_set) new = TT.set; // '='. + else { // '-' and/or '+'. + new = orig; + new &= ~(TT.rm); + new |= TT.add; + if (!S_ISDIR(root->st.st_mode)) new &= ~FS_DIRSYNC_FL; + } + // Write them back if there was any change. + if (orig != new && ext2_setflag(fd, &(root->st), new)<0) + perror_msg("%s: setting flags to =%s failed", fpath, attrstr(new, 0)); } - if (chattr.set) { // for '=' operator. - if (ext2_setflag(fd, &(root->st), chattr.set) < 0) - perror_msg("setting flags '%s'", fpath); - } else { // for '-' / '+' operator. - fval &= ~(chattr.rm); - fval |= chattr.add; - if (!S_ISDIR(root->st.st_mode)) fval &= ~FS_DIRSYNC_FL; - if (ext2_setflag(fd, &(root->st), fval) < 0) - perror_msg("setting flags '%s'", fpath); + + // (FS_IOC_SETVERSION works all the way back to 2.6, but FS_IOC_FSSETXATTR + // isn't available until 4.5.) + if (FLAG(v) && (ioctl(fd, FS_IOC_SETVERSION, &vv)<0)) + perror_msg("%s: setting version to %d failed", fpath, vv); + + if (FLAG(p)) { + struct fsxattr fsx; + int fail = ioctl(fd, FS_IOC_FSGETXATTR, &fsx); + + fsx.fsx_projid = TT.p; + if (fail || ioctl(fd, FS_IOC_FSSETXATTR, &fsx)) + perror_msg("%s: setting projid to %u failed", fpath, fsx.fsx_projid); } - // set file version - if (chattr.vflag && (ioctl(fd, FS_IOC_SETVERSION, &chattr.version)<0)) - perror_msg("while setting version on '%s'", fpath); + free(fpath); xclose(fd); - - return (S_ISDIR(root->st.st_mode) && chattr.recursive) ? DIRTREE_RECURSE : 0; + return (FLAG(R) && S_ISDIR(root->st.st_mode)) ? DIRTREE_RECURSE : 0; } void chattr_main(void) { char **argv = toys.optargs; - memset(&chattr, 0, sizeof(struct _chattr)); parse_cmdline_arg(&argv); + if (TT.p < 0 || TT.p > UINT_MAX) error_exit("bad projid %lu", TT.p); + if (TT.v < 0 || TT.v > UINT_MAX) error_exit("bad version %ld", TT.v); if (!*argv) help_exit("no file"); - if (chattr.set && (chattr.add || chattr.rm)) + if (TT.have_set && (TT.add || TT.rm)) error_exit("no '=' with '-' or '+'"); - if (chattr.rm & chattr.add) error_exit("set/unset same flag"); - if (!(chattr.add || chattr.rm || chattr.set || chattr.vflag)) - error_exit("need '-v', '=', '-' or '+'"); + if (TT.rm & TT.add) error_exit("set/unset same flag"); + if (!(TT.add || TT.rm || TT.have_set || FLAG(p) || FLAG(v))) + error_exit("need '-p', '-v', '=', '-', or '+'"); for (; *argv; argv++) dirtree_read(*argv, update_attr); - toys.exitval = 0; //always set success at this point. } diff --git a/toys/other/lspci.c b/toys/other/lspci.c deleted file mode 100644 index c208484..0000000 --- a/toys/other/lspci.c +++ /dev/null @@ -1,128 +0,0 @@ -/* - * lspci - written by Isaac Dunham - -USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN)) - -config LSPCI - bool "lspci" - default y - help - usage: lspci [-ekm] - - List PCI devices. - - -e Print all 6 digits in class - -k Print kernel driver - -m Machine parseable format - -config LSPCI_TEXT - bool "lspci readable output" - depends on LSPCI - default y - help - usage: lspci [-n] [-i FILE ] - - -n Numeric output (repeat for readable and numeric) - -i PCI ID database (default /usr/share/misc/pci.ids) - -*/ - -#define FOR_lspci -#include "toys.h" - -GLOBALS( - char *i; - long n; - - FILE *db; -) - -static int do_lspci(struct dirtree *new) -{ - char *p = toybuf, *vendor = toybuf+9, *device = toybuf+18, - driver[256], *vbig = 0, *dbig = 0, **fields; - int dirfd; - - if (!new->parent) return DIRTREE_RECURSE; - - // Parse data out of /proc - - if (-1 == (dirfd = openat(dirtree_parentfd(new), new->name, O_RDONLY))) - return 0; - - *driver = 0; - if (toys.optflags & FLAG_k) - readlinkat0(dirfd, "driver", driver, sizeof(driver)); - - for (fields = (char*[]){"class", "vendor", "device", 0}; *fields; fields++) { - int fd, size = 6 + 2*((toys.optflags & FLAG_e) && p == toybuf); - *p = 0; - - if (-1 == (fd = openat(dirfd, *fields, O_RDONLY))) { - close(dirfd); - return 0; - } - xreadall(fd, p, size); - memmove(p, p+2, size -= 2); - p[size] = 0; - close(fd); - p += 9; - } - - close(dirfd); - - // Lookup/display data from pci.ids? - - if (CFG_LSPCI_TEXT && TT.db) { - if (TT.n != 1) { - char *s; - - fseek(TT.db, 0, SEEK_SET); - while (!vbig || !dbig) { - s = p; - if (!fgets(s, sizeof(toybuf)-(p-toybuf)-1, TT.db)) break; - while (isspace(*s)) s++; - if (*s == '#') continue; - if (vbig && s == p) break; - if (strstart(&s, vbig ? device : vendor)) { - if (vbig) dbig = s+2; - else vbig = s+2; - s += strlen(s); - s[-1] = 0; // trim ending newline - p = s + 1; - } - } - } - - if (TT.n > 1) { - printf((toys.optflags & FLAG_m) - ? "%s, \"%s\" \"%s [%s]\" \"%s [%s]\"" - : "%s Class %s: %s [%s] %s [%s]", - new->name+5, toybuf, vbig ? vbig : "", vendor, - dbig ? dbig : "", device); - - goto driver; - } - } - - printf((toys.optflags & FLAG_m) ? "%s \"%s\" \"%s\" \"%s\"" - : "%s Class %s: %s:%s", new->name+5, toybuf, - vbig ? vbig : vendor, dbig ? dbig : device); - -driver: - if (*driver) - printf((toys.optflags & FLAG_m) ? " \"%s\"" : " %s", basename(driver)); - xputc('\n'); - - return 0; -} - -void lspci_main(void) -{ - if (CFG_LSPCI_TEXT && TT.n != 1) { - if (!TT.i) TT.i = "/usr/share/misc/pci.ids"; - if (!(TT.db = fopen(TT.i, "r"))) perror_msg("%s", TT.i); - } - - dirtree_read("/sys/bus/pci/devices", do_lspci); -} diff --git a/toys/other/lsusb.c b/toys/other/lsusb.c index 031dbd9..3abbe47 100644 --- a/toys/other/lsusb.c +++ b/toys/other/lsusb.c @@ -1,49 +1,261 @@ /* lsusb.c - list available USB devices * * Copyright 2013 Andre Renaud + * Copyright 2013 Isaac Dunham -USE_LSUSB(NEWTOY(lsusb, NULL, TOYFLAG_USR|TOYFLAG_BIN)) +USE_LSUSB(NEWTOY(lsusb, "i:", TOYFLAG_USR|TOYFLAG_BIN)) +USE_LSPCI(NEWTOY(lspci, "eDmkn@x@i:", TOYFLAG_USR|TOYFLAG_BIN)) + +config LSPCI + bool "lspci" + default y + help + usage: lspci [-ekmn] [-i FILE] + + List PCI devices. + + -e Extended (6 digit) class + -i ID database (default /etc/pci.ids[.gz]) + -k Show kernel driver + -m Machine readable + -n Numeric output (-nn for both) + -D Print domain numbers + -x Hex dump of config space (64 bytes; -xxx for 256, -xxxx for 4096) config LSUSB bool "lsusb" default y help - usage: lsusb + usage: lsusb [-i] List USB hosts/devices. + + -i ID database (default /etc/usb.ids[.gz]) */ +#define FOR_lsusb #include "toys.h" -static int list_device(struct dirtree *new) +GLOBALS( + char *i; + long x, n; + + void *ids, *class; + int count; +) + +struct dev_ids { + struct dev_ids *next, *child; + int id; + char name[]; +}; + +struct scanloop { + char *pattern; + void *d1, *d2; +}; + +// Common function to read uevent file under /proc for both pci and usb +// note that %s is omitted (because pointer is into toybuf, avoiding copy). +static int scan_uevent(struct dirtree *new, int len, struct scanloop *sl) +{ + int ii, saw = 0; + off_t flen = sizeof(toybuf); + char *ss, *yy; + + // Read data + if (*new->name == '.') return 0; + sprintf(toybuf, "%s/uevent", new->name); + if (!readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &flen)) return 0; + + // Loop over lines + while ((flen = strcspn(ss, "\n"))) { + if (ss[flen]) ss[flen++] = 0; + yy = ss+flen; + + // Try each pattern + for (ii = 0; iinext) { + if (id1 != ids->id) continue; + *name1 = ids->name; + for (ids = ids->child; ids; ids = ids->next) { + if (id2 != ids->id) continue; + *name2 = ids->name; + return; + } + return; + } +} + +// Search for pci.ids or usb.ids and return parsed structure or NULL +struct dev_ids *parse_dev_ids(char *name, struct dev_ids **and) +{ + char *path = "/etc:/vendor:/usr/share/hwdata:/usr/share/misc"; + struct string_list *sl = 0; + FILE *fp; + char *s, *ss, *sss; + struct dev_ids *ids = 0, *new; + int fd = -1; + + // Open compressed or uncompressed file + signal(SIGCHLD, SIG_IGN); + s = TT.i; + if (!s) { + sprintf(toybuf, "%s.gz", name); + if ((sl = find_in_path(path, toybuf)) || (sl = find_in_path(path, name))) + s = sl->str; + } + if (s && strend(s, ".gz")) xpopen((char *[]){"zcat", sl->str, 0}, &fd, 1); + else if (s) fd = xopen(s, O_RDONLY); + llist_traverse(sl, free); + if (fd == -1) return 0; + + for (fp = fdopen(fd, "r"); (s = ss = xgetline(fp)); free(s)) { + // TODO parse and use third level instead of skipping it here + if (s[strspn(s, " \t")]=='#' || strstart(&ss, "\t\t")) continue; + + // Switch to device class list? + if (strstart(&ss, "C ") && and) { + *and = ids; + and = 0; + } + fd = estrtol(sss = ss, &ss, 16); + if (ss>sss && *ss++==' ') { + while (isspace(*ss)) ss++; + new = xmalloc(sizeof(*new)+strlen(ss)+1); + new->child = 0; + new->id = fd; + strcpy(new->name, ss); + if (!ids || *s!='\t') { + new->next = ids; + ids = new; + } else { + new->next = ids->child; + ids->child = new; + } + } + } + fclose(fp); + + return ids; +} + +static int list_usb(struct dirtree *new) { - FILE *file; - char *name; int busnum = 0, devnum = 0, pid = 0, vid = 0; + char *n1, *n2; if (!new->parent) return DIRTREE_RECURSE; - if (new->name[0] == '.') return 0; - name = dirtree_path(new, 0); - sprintf(toybuf, "%s/uevent", name); - file = fopen(toybuf, "r"); - if (file) { - int count = 0; - - while (fgets(toybuf, sizeof(toybuf), file)) - if (sscanf(toybuf, "BUSNUM=%u\n", &busnum) - || sscanf(toybuf, "DEVNUM=%u\n", &devnum) - || sscanf(toybuf, "PRODUCT=%x/%x/", &pid, &vid)) count++; - - if (count == 3) - printf("Bus %03d Device %03d: ID %04x:%04x\n", busnum, devnum, pid, vid); - fclose(file); + if (7 == scan_uevent(new, 3, (struct scanloop[]){{"BUSNUM=%u", &busnum, 0}, + {"DEVNUM=%u", &devnum, 0}, {"PRODUCT=%x/%x", &pid, &vid}})) + { + get_names(TT.ids, pid, vid, &n1, &n2); + printf("Bus %03d Device %03d: ID %04x:%04x %s %s\n", + busnum, devnum, pid, vid, n1, n2); } - free(name); return 0; } void lsusb_main(void) { - dirtree_read("/sys/bus/usb/devices/", list_device); + // Parse http://www.linux-usb.org/usb.ids file (if available) + TT.ids = parse_dev_ids("usb.ids", 0); + dirtree_read("/sys/bus/usb/devices/", list_usb); +} + +#define FOR_lspci +#include "generated/flags.h" + +// TODO: -v +static int list_pci(struct dirtree *new) +{ + char *driver = 0, buf[16], *ss, *names[3]; + int cvd[3] = {0}, ii, revision = 0; + off_t len = sizeof(toybuf); + /* skip 0000: part by default */ + char *bus = strchr(new->name, ':') + 1; + +// Output formats: -n, -nn, -m, -nm, -nnm, -k + + if (!new->parent) return DIRTREE_RECURSE; + if (!bus || strlen(new->name)<6) return 0; + TT.count = 0; + + // Load revision + sprintf(toybuf, "%s/revision", new->name); + if (readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &len)) { + strstart(&ss, "0x"); + sscanf(ss, "%x", &revision); + } + + // Load uevent data, look up names in database + if (6>scan_uevent(new, 3, (struct scanloop[]){{"DRIVER=", &driver, 0}, + {"PCI_CLASS=%x", cvd, 0}, {"PCI_ID=%x:%x", cvd+1, cvd+2}})) return 0; + get_names(TT.class, 255&(cvd[0]>>16), 255&(cvd[0]>>8), names, names); + get_names(TT.ids, cvd[1], cvd[2], names+1, names+2); + if (!FLAG(e)) cvd[0] >>= 8; + + // Output line according to flags + if (FLAG(D) || strncmp(new->name, "0000:", bus-new->name)) bus = new->name; + printf("%s", bus); + for (ii = 0; ii<3; ii++) { + sprintf(buf, "%0*x", 6-2*(ii||!FLAG(e)), cvd[ii]); + if (!TT.n) printf(FLAG(m) ? " \"%s\"" : ": %s"+(ii!=1), names[ii] ? : buf); + else if (TT.n==1) printf(FLAG(m) ? " \"%s\"" : (ii==2)?"%s ":" %s:", buf); + else if (!FLAG(m)) { + // This one permutes the order, so do it all first time and abort loop + printf(" %s [%s]: %s %s [%04x:%04x]", names[0], buf, names[1], names[2], + cvd[1], cvd[2]); + break; + } else printf(" \"%s [%s]\"", names[ii], buf); + } + if (revision) printf(FLAG(m) ? " -r%02x" : " (rev %02x)", revision); + if (FLAG(k) && driver) printf(FLAG(m) ? " \"%s\"" : " %s", driver); + xputc('\n'); + + if (TT.x) { + FILE *fp; + int b, col = 0, max = (TT.x >= 4) ? 4096 : ((TT.x >= 3) ? 256 : 64); + + snprintf(toybuf, sizeof(toybuf), "/sys/bus/pci/devices/%s/config", new->name); + fp = xfopen(toybuf, "r"); + while ((b = fgetc(fp)) != EOF) { + if ((col % 16) == 0) printf("%02x: ", col & 0xf0); + printf("%02x ", (b & 0xff)); + if ((++col % 16) == 0) xputc('\n'); + if (col == max) break; + } + xputc('\n'); + fclose(fp); + } + + return 0; +} + +void lspci_main(void) +{ + // Parse https://pci-ids.ucw.cz/v2.2/pci.ids (if available) + if (TT.n != 1) TT.class = parse_dev_ids("pci.ids", (void *)&TT.ids); + dirtree_read("/sys/bus/pci/devices/", list_pci); } diff --git a/toys/other/nsenter.c b/toys/other/nsenter.c index dfcfbe8..bafed47 100644 --- a/toys/other/nsenter.c +++ b/toys/other/nsenter.c @@ -12,45 +12,52 @@ * // Note: flags go in same order (right to left) for shared subset -USE_NSENTER(NEWTOY(nsenter, "<1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN)) -USE_UNSHARE(NEWTOY(unshare, "<1^f(fork);r(map-root-user);i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN)) +USE_NSENTER(NEWTOY(nsenter, "<1a(all)F(no-fork)t#<1(target)C(cgroup):; i(ipc):; m(mount):; n(net):; p(pid):; u(uts):; U(user):; ", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT)) +USE_UNSHARE(NEWTOY(unshare, "<1^a(all)f(fork)r(map-root-user)C(cgroup):; i(ipc):; m(mount):; n(net):; p(pid):; u(uts):; U(user):; ", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT)) config UNSHARE bool "unshare" default y - depends on TOYBOX_CONTAINER help usage: unshare [-imnpuUr] COMMAND... - Create new container namespace(s) for this process and its children, so - some attribute is not shared with the parent process. + Create new container namespace(s) for this process and its children, allowing + the new set of processes to have a different view of the system than the + parent process. + -a Unshare all supported namespaces -f Fork command in the background (--fork) + -r Become root (map current euid/egid to 0/0, implies -U) (--map-root-user) + + Available namespaces: + -C Control groups (--cgroup) -i SysV IPC (message queues, semaphores, shared memory) (--ipc) -m Mount/unmount tree (--mount) -n Network address, sockets, routing, iptables (--net) -p Process IDs and init (--pid) - -r Become root (map current euid/egid to 0/0, implies -U) (--map-root-user) -u Host and domain names (--uts) -U UIDs, GIDs, capabilities (--user) - A namespace allows a set of processes to have a different view of the - system than other sets of processes. + Each namespace can take an optional argument, a persistent mountpoint usable + by the nsenter command to add new processes to that the namespace. (Specify + multiple namespaces to unshare separately, ala -c -i -m because -cim is -c + with persistent mount "im".) config NSENTER bool "nsenter" - depends on TOYBOX_CONTAINER default y help usage: nsenter [-t pid] [-F] [-i] [-m] [-n] [-p] [-u] [-U] COMMAND... Run COMMAND in an existing (set of) namespace(s). - -t PID to take namespaces from (--target) + -a Enter all supported namespaces (--all) -F don't fork, even if -p is used (--no-fork) + -t PID to take namespaces from (--target) The namespaces to switch are: + -C Control groups (--cgroup) -i SysV IPC: message queues, semaphores, shared memory (--ipc) -m Mount/unmount tree (--mount) -n Network address, sockets, routing, iptables (--net) @@ -64,62 +71,41 @@ config NSENTER #define FOR_nsenter #include "toys.h" -#include #include #define unshare(flags) syscall(SYS_unshare, flags) #define setns(fd, nstype) syscall(SYS_setns, fd, nstype) GLOBALS( - char *Uupnmi[6]; + char *UupnmiC[7]; long t; ) // Code that must run in unshare's flag context -#define CLEANUP_nsenter #define FOR_unshare #include static void write_ugid_map(char *map, unsigned eugid) { - int bytes = sprintf(toybuf, "0 %u 1", eugid), fd = xopen(map, O_WRONLY); + int fd = xopen(map, O_WRONLY); - xwrite(fd, toybuf, bytes); + dprintf(fd, "0 %u 1", eugid); xclose(fd); } -static void handle_r(int euid, int egid) -{ - int fd; - - if ((fd = open("/proc/self/setgroups", O_WRONLY)) >= 0) { - xwrite(fd, "deny", 4); - close(fd); - } - - write_ugid_map("/proc/self/uid_map", euid); - write_ugid_map("/proc/self/gid_map", egid); -} - -static int test_r() -{ - return toys.optflags & FLAG_r; -} - -static int test_f() -{ - return toys.optflags & FLAG_f; -} +static int test_a() { return FLAG(a); } +static int test_r() { return FLAG(r); } +static int test_f() { return FLAG(f); } // Shift back to the context GLOBALS lives in (I.E. matching the filename). -#define CLEANUP_unshare #define FOR_nsenter #include void unshare_main(void) { + char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc\0cgroup"; unsigned flags[]={CLONE_NEWUSER, CLONE_NEWUTS, CLONE_NEWPID, CLONE_NEWNET, - CLONE_NEWNS, CLONE_NEWIPC}, f = 0; + CLONE_NEWNS, CLONE_NEWIPC, CLONE_NEWCGROUP}, f = 0; int i, fd; // Create new namespace(s)? @@ -131,10 +117,17 @@ void unshare_main(void) if (test_r()) toys.optflags |= FLAG_U; for (i = 0; i= 0) { + xwrite(fd, "deny", 4); + close(fd); + } + + write_ugid_map("/proc/self/uid_map", euid); + write_ugid_map("/proc/self/gid_map", egid); + } if (test_f()) { toys.exitval = xrun(toys.optargs); @@ -143,25 +136,21 @@ void unshare_main(void) } // Bind to existing namespace(s)? } else if (CFG_NSENTER) { - char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc"; + for (i = 0; i * * No Standard. + * + * TODO: two passes so we can auto-size the columns? -USE_PMAP(NEWTOY(pmap, "<1xq", TOYFLAG_USR|TOYFLAG_BIN)) +USE_PMAP(NEWTOY(pmap, "<1pqx", TOYFLAG_USR|TOYFLAG_BIN)) config PMAP bool "pmap" default y help - usage: pmap [-xq] [pids...] + usage: pmap [-pqx] PID... Report the memory map of a process or processes. + -p Show full paths + -q Do not show header or footer -x Show the extended format - -q Do not display some header/footer lines */ #define FOR_pmap @@ -24,90 +27,77 @@ config PMAP void pmap_main(void) { - char **optargs; + char **optargs, *line = 0; + size_t len = 0; for (optargs = toys.optargs; *optargs; optargs++) { + long long start, end, pss, tpss=0, dirty, tdirty=0, swap, tswap=0, total=0; + char *name = 0, *k = "K"+FLAG(x), mode[5]; pid_t pid = atolx(*optargs); + int extras = 0, off, count; FILE *fp; - char *line, *oldline = 0, *name = 0, - *k = (toys.optflags & FLAG_x) ? "" : "K"; - size_t len; - long long start, end, pss, tpss = 0, dirty, tdirty = 0, swap, tswap = 0, - total = 0; - int xx = 0; - snprintf(toybuf, sizeof(toybuf), "/proc/%u/cmdline", pid); - line = readfile(toybuf, 0, 0); - if (!line) error_msg("No %lu", (long)pid); - xprintf("%u: %s\n", (int)pid, line); - free(line); + sprintf(toybuf, "/proc/%u/cmdline", pid); + if (!(name = readfile(toybuf, 0, 0))) { + error_msg("no %s", toybuf); + continue; + } + xprintf("%d: %s\n", pid, name); + free(name); - // Header - // Only use the more verbose file in -x mode - sprintf(toybuf, "/proc/%u/%smaps", pid, - (toys.optflags & FLAG_x) ? "s" : ""); + // Only bother scanning the more verbose smaps file in -x mode. + sprintf(toybuf, "/proc/%u/%smaps", pid, "s"+!FLAG(x)); if (!(fp = fopen(toybuf, "r"))) { - error_msg("No %ld\n", (long)pid); - return; + error_msg("no %s", toybuf); + continue; } - if ((toys.optflags & (FLAG_q|FLAG_x)) == FLAG_x) - xprintf("Address%*cKbytes PSS Dirty Swap Mode Mapping\n", - (int)(sizeof(long)*2)-4, ' '); - - // Loop through mappings - for (;;) { - int off, count; - - line = 0; - if (0 >= getline(&line, &len, fp)) break; - count = sscanf(line, "%llx-%llx %s %*s %*s %*s %n", - &start, &end, toybuf, &off); + if (FLAG(x) && !FLAG(q)) + xprintf("Address%*cKbytes PSS Dirty Swap Mode Mapping\n", + (int)(sizeof(long)*2)-5, ' '); + while (getline(&line, &len, fp) > 0) { + count = sscanf(line, "%llx-%llx %4s %*s %*s %*s %n", &start, &end, mode, + &off); if (count == 3) { name = line[off] ? line+off : " [anon]\n"; - if (toybuf[3] == 'p') toybuf[3] = '-'; + if (mode[3] == 'p') mode[3] = '-'; total += end = (end-start)/1024; - printf("%0*llx % *lld%s ", (int)(2*sizeof(long)), start, - 6+!!(toys.optflags & FLAG_x), end, k); - if (toys.optflags & FLAG_x) { - oldline = line; + printf("%0*llx % *lld%s ", (int)(2*sizeof(long)), start, 6+FLAG(x), + end, k); + if (FLAG(x)) { + strcpy(toybuf, name); + name = toybuf; continue; } } else { - if (0 + * Copyright 2020 Moritz Röhrich USE_PWGEN(NEWTOY(pwgen, ">2r(remove):c(capitalize)n(numerals)y(symbols)s(secure)B(ambiguous)h(help)C1vA(no-capitalize)0(no-numerals)[-cA][-n0][-C1]", TOYFLAG_USR|TOYFLAG_BIN)) diff --git a/toys/other/setsid.c b/toys/other/setsid.c index 9569826..c6e0c53 100644 --- a/toys/other/setsid.c +++ b/toys/other/setsid.c @@ -2,27 +2,57 @@ * * Copyright 2006 Rob Landley -USE_SETSID(NEWTOY(setsid, "^<1t", TOYFLAG_USR|TOYFLAG_BIN)) +USE_SETSID(NEWTOY(setsid, "^<1wc@d[!dc]", TOYFLAG_USR|TOYFLAG_BIN)) config SETSID bool "setsid" default y help - usage: setsid [-t] command [args...] + usage: setsid [-cdw] command [args...] Run process in a new session. - -t Grab tty (become foreground process, receiving keyboard signals) + -d Detach from tty + -c Control tty (repeat to steal) + -w Wait for child (and exit with its status) */ +#define FOR_setsid #include "toys.h" +GLOBALS( + long c; +) + void setsid_main(void) { - while (setsid()<0) if (XVFORK()) _exit(0); - if (toys.optflags) { + int i; + + // setsid() fails if we're already session leader, ala "exec setsid" from sh. + // Second call can't fail, so loop won't continue endlessly. + while (setsid()<0) { + pid_t pid; + + // This must be before vfork() or tcsetpgrp() will hang waiting for parent. setpgid(0, 0); + + pid = XVFORK(); + if (pid) { + i = 0; + if (FLAG(w)) { + i = 127; + if (pid>0) i = xwaitpid(pid); + } + _exit(i); + } + } + + if (FLAG(c)) { + ioctl(0, TIOCSCTTY, TT.c>1); tcsetpgrp(0, getpid()); + } if (FLAG(d) && (i = open("/dev/tty", O_RDONLY)) != -1) { + ioctl(i, TIOCNOTTY); + close(i); } xexec(toys.optargs); } diff --git a/toys/other/stat.c b/toys/other/stat.c index 840b29a..463aa46 100644 --- a/toys/other/stat.c +++ b/toys/other/stat.c @@ -33,8 +33,8 @@ config STAT The valid format escape sequences for filesystems: %a Available blocks |%b Total blocks |%c Total inodes %d Free inodes |%f Free blocks |%i File system ID - %l Max filename length |%n File name |%s Fragment size - %S Best transfer size |%t FS type (hex) |%T FS type (driver name) + %l Max filename length |%n File name |%s Best transfer size + %S Actual block size |%t FS type (hex) |%T FS type (driver name) */ #define FOR_stat @@ -141,52 +141,29 @@ static void print_statfs(char type) { else if (type == 'c') out('u', statfs->f_files); else if (type == 'd') out('u', statfs->f_ffree); else if (type == 'f') out('u', statfs->f_bfree); - else if (type == 'l') { -#ifdef __APPLE__ - // TODO: move this into portability.c somehow, or just use this everywhere? - // (glibc and bionic will just re-do the statfs and return f_namelen.) - out('d', pathconf(TT.file, _PC_NAME_MAX)); -#else - out('d', statfs->f_namelen); -#endif - } else if (type == 't') out('x', statfs->f_type); - else if (type == 'T') { - char *s = "unknown"; - struct {unsigned num; char *name;} nn[] = { - {0xADFF, "affs"}, {0x5346544e, "ntfs"}, {0x1Cd1, "devpts"}, - {0x137D, "ext"}, {0xEF51, "ext2"}, {0xEF53, "ext3"}, - {0x1BADFACE, "bfs"}, {0x9123683E, "btrfs"}, {0x28cd3d45, "cramfs"}, - {0x3153464a, "jfs"}, {0x7275, "romfs"}, {0x01021994, "tmpfs"}, - {0x3434, "nilfs"}, {0x6969, "nfs"}, {0x9fa0, "proc"}, - {0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"}, - {0x4d44, "msdos"}, {0x4006, "fat"}, {0x43415d53, "smackfs"}, - {0x73717368, "squashfs"} - }; - int i; - - for (i=0; if_type) s = nn[i].name; - strout(s); - } else if (type == 'i') { + else if (type == 'l') out('d', pathconf(TT.file, _PC_NAME_MAX)); + else if (type == 't') out('x', statfs->f_type); + else if (type == 'T') strout(fs_type_name(statfs)); + else if (type == 'i') { int *val = (int *) &statfs->f_fsid; char buf[32]; sprintf(buf, "%08x%08x", val[0], val[1]); strout(buf); - } else if (type == 's') out('d', statfs->f_frsize); - else if (type == 'S') out('d', statfs->f_bsize); + } else if (type == 's') out('d', statfs_bsize(statfs)); + else if (type == 'S') out('d', statfs_frsize(statfs)); else strout("?"); } void stat_main(void) { - int flagf = FLAG(f), i; + int i; char *format, *f; - if (FLAG(t)) format = flagf + if (FLAG(t)) format = FLAG(f) ? "%n %i %l %t %s %S %b %f %a %c %d" : "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; - else format = flagf + else format = FLAG(f) ? " File: \"%n\"\n ID: %i Namelen: %l Type: %T\n" "Block Size: %s Fundamental block size: %S\n" "Blocks: Total: %b\tFree: %f\tAvailable: %a\n" @@ -203,8 +180,8 @@ void stat_main(void) // stat the file or filesystem TT.file = toys.optargs[i]; - if (flagf && !statfs(TT.file, (void *)&TT.stat)); - else if (flagf || (FLAG(L) ? stat : lstat)(TT.file, (void *)&TT.stat)) { + if (FLAG(f) && !statfs(TT.file, (void *)&TT.stat)); + else if (FLAG(f) || (FLAG(L) ? stat : lstat)(TT.file, (void *)&TT.stat)) { perror_msg("'%s'", TT.file); continue; } @@ -218,7 +195,7 @@ void stat_main(void) TT.patlen = f-TT.pattern; if (!*f || TT.patlen>99) error_exit("bad %s", TT.pattern); if (*f == 'n') strout(TT.file); - else if (flagf) print_statfs(*f); + else if (FLAG(f)) print_statfs(*f); else print_stat(*f); } } diff --git a/toys/other/sysctl.c b/toys/other/sysctl.c index 3773594..7a2cba6 100644 --- a/toys/other/sysctl.c +++ b/toys/other/sysctl.c @@ -4,18 +4,18 @@ * Copyright 2014 Kyungwan Han * * No Standard - + USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN)) config SYSCTL bool "sysctl" default y help - usage: sysctl [-aAeNnqw] [-p [FILE] | KEY[=VALUE]...] + usage: sysctl [-aeNnqw] [-p [FILE] | KEY[=VALUE]...] Read/write system control data (under /proc/sys). - -a,A Show all values + -a Show all values -e Don't warn about unknown keys -N Don't print key values -n Don't print key names @@ -43,9 +43,8 @@ static void replace_char(char *str, char old, char new) static void key_error(char *key) { - if (errno == ENOENT) { - if (!(toys.optflags & FLAG_e)) error_msg("unknown key '%s'", key); - } else perror_msg("key '%s'", key); + if (errno != ENOENT) perror_msg("key '%s'", key); + else if (!FLAG(e)) error_msg("unknown key '%s'", key); } static int write_key(char *path, char *key, char *value) @@ -77,11 +76,11 @@ static int do_show_keys(struct dirtree *dt) if (!data) key_error(key); else { // Print the parts that aren't switched off by flags. - if (!(toys.optflags & FLAG_n)) xprintf("%s", key); - if (!(toys.optflags & (FLAG_N|FLAG_n))) xprintf(" = "); + if (!FLAG(n)) xprintf("%s", key); + if (!FLAG(N) && !FLAG(n)) xprintf(" = "); for (key = data+strlen(data); key > data && isspace(*--key); *key = 0); - if (!(toys.optflags & FLAG_N)) xprintf("%s", data); - if ((toys.optflags & (FLAG_N|FLAG_n)) != (FLAG_N|FLAG_n)) xputc('\n'); + if (!FLAG(N)) xprintf("%s", data); + if (!FLAG(N) || !FLAG(n)) xputc('\n'); } free(data); @@ -96,16 +95,11 @@ static void process_key(char *key, char *value) char *path; if (!value) value = split_key(key); - if ((toys.optflags & FLAG_w) && !value) { - error_msg("'%s' not key=value", key); - - return; - } - + if (FLAG(w) && !value) return error_msg("'%s' not key=value", key); path = xmprintf("/proc/sys/%s", key); replace_char(path, '.', '/'); // Note: failure to assign to a non-leaf node suppresses the display. - if (!(value && (!write_key(path, key, value) || (toys.optflags & FLAG_q)))) { + if (!(value && (!write_key(path, key, value) || FLAG(q)))) { if (!access(path, R_OK)) dirtree_read(path, do_show_keys); else key_error(key); } @@ -117,10 +111,10 @@ void sysctl_main() char **args = 0; // Display all keys - if (toys.optflags & FLAG_a) dirtree_read("/proc/sys", do_show_keys); + if (FLAG(a)) dirtree_read("/proc/sys", do_show_keys); // read file - else if (toys.optflags & FLAG_p) { + else if (FLAG(p)) { FILE *fp = xfopen(*toys.optargs ? *toys.optargs : "/etc/sysctl.conf", "r"); size_t len; diff --git a/toys/other/timeout.c b/toys/other/timeout.c index 55b3dca..4c4288e 100644 --- a/toys/other/timeout.c +++ b/toys/other/timeout.c @@ -4,13 +4,13 @@ * * No standard -USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125))) +USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):i", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125))) config TIMEOUT bool "timeout" default y help - usage: timeout [-k DURATION] [-s SIGNAL] DURATION COMMAND... + usage: timeout [-iv] [-k DURATION] [-s SIGNAL] DURATION COMMAND... Run command line as a child process, sending child a signal if the command doesn't exit soon enough. @@ -18,8 +18,9 @@ config TIMEOUT DURATION can be a decimal fraction. An optional suffix can be "m" (minutes), "h" (hours), "d" (days), or "s" (seconds, the default). - -s Send specified signal (default TERM) + -i Only kill for inactivity (restart timeout when command produces output) -k Send KILL signal if child still running this long after first signal + -s Send specified signal (default TERM) -v Verbose --foreground Don't create new process group --preserve-status Exit with the child's exit status @@ -31,60 +32,76 @@ config TIMEOUT GLOBALS( char *s, *k; - int nextsig; - pid_t pid; - struct timeval ktv; - struct itimerval itv; + struct pollfd pfd; + sigjmp_buf sj; + int fds[2], pid, rc; ) -static void handler(int i) +static void handler(int sig, siginfo_t *si) { - if (FLAG(v)) - fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig); - - toys.exitval = (TT.nextsig==9) ? 137 : 124; - kill(TT.pid, TT.nextsig); - if (TT.k) { - TT.k = 0; - TT.nextsig = SIGKILL; - xsignal(SIGALRM, handler); - TT.itv.it_value = TT.ktv; - setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf); - } + TT.rc = si->si_status + ((si->si_code!=CLD_EXITED)<<7); + siglongjmp(TT.sj, 1); } -// timeval inexplicably makes up a new type for microseconds, despite timespec's -// nanoseconds field (needing to store 1000* the range) using "long". Bravo. -void xparsetimeval(char *s, struct timeval *tv) +static long nantomil(struct timespec *ts) { - long ll; + return ts->tv_sec*1000+ts->tv_nsec/1000000; +} - tv->tv_sec = xparsetime(s, 6, &ll); - tv->tv_usec = ll; +static void callback(char *argv[]) +{ + if (!FLAG(foreground)) setpgid(0, 0); } void timeout_main(void) { + int ii, ms, nextsig = SIGTERM; + struct timespec tts, kts; + // Use same ARGFAIL value for any remaining parsing errors toys.exitval = 125; - xparsetimeval(*toys.optargs, &TT.itv.it_value); - if (TT.k) xparsetimeval(TT.k, &TT.ktv); - - TT.nextsig = SIGTERM; - if (TT.s && -1 == (TT.nextsig = sig_to_num(TT.s))) - error_exit("bad -s: '%s'", TT.s); - - if (!FLAG(foreground)) setpgid(0, 0); + xparsetimespec(*toys.optargs, &tts); + if (TT.k) xparsetimespec(TT.k, &kts); + if (TT.s && -1==(nextsig = sig_to_num(TT.s))) error_exit("bad -s: '%s'",TT.s); toys.exitval = 0; - if (!(TT.pid = XVFORK())) xexec(toys.optargs+1); - else { - int status; + TT.pfd.events = POLLIN; + TT.fds[1] = -1; + if (sigsetjmp(TT.sj, 1)) goto done; + xsignal_flags(SIGCHLD, handler, SA_NOCLDSTOP|SA_SIGINFO); - xsignal(SIGALRM, handler); - setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf); + TT.pid = xpopen_setup(toys.optargs+1, FLAG(i) ? TT.fds : 0, callback); + xsignal(SIGTTIN, SIG_IGN); + xsignal(SIGTTOU, SIG_IGN); + xsignal(SIGTSTP, SIG_IGN); + if (!FLAG(i)) xpipe(TT.fds); + TT.pfd.fd = TT.fds[1]; + ms = nantomil(&tts); + for (;;) { + if (1 != xpoll(&TT.pfd, 1, ms)) { + if (FLAG(v)) + perror_msg("sending signal %s to command %s", num_to_sig(nextsig), + toys.optargs[1]); + toys.exitval = (nextsig==9) ? 137 : 124; + kill(FLAG(foreground) ? TT.pid : -TT.pid, nextsig); + if (!TT.k || nextsig==SIGKILL) break; + nextsig = SIGKILL; + ms = nantomil(&kts); - status = xwaitpid(TT.pid); - if (FLAG(preserve_status) || !toys.exitval) toys.exitval = status; + continue; + } + if (TT.pfd.revents&POLLIN) { + errno = 0; + if (1>(ii = read(TT.fds[1], toybuf, sizeof(toybuf)))) { + if (errno==EINTR) continue; + break; + } + writeall(1, toybuf, ii); + } + if (TT.pfd.revents&POLLHUP) break; } +done: + xpclose_both(TT.pid, TT.fds); + + if (FLAG(preserve_status) || !toys.exitval) toys.exitval = TT.rc; } diff --git a/toys/other/watch.c b/toys/other/watch.c index 99e817b..2399826 100644 --- a/toys/other/watch.c +++ b/toys/other/watch.c @@ -58,7 +58,7 @@ static void watch_child(int sig) static int watch_escape(FILE *out, int cols, int wc) { if (wc==27 || (wc>=7 && wc<=13)) return -1; - if (wc < 32) return 0; + if (wc<32) return 0; return crunch_escape(out, cols, wc); } @@ -106,8 +106,8 @@ void watch_main(void) // Get and measure time string, trimming gratuitous \n ctimelen = strlen(ss = ctime(&t)); if (ss[ctimelen-1]=='\n') ss[--ctimelen] = 0; - - // print cmdline, then * or ' ' (showing truncation), then ctime + + // print cmdline, then * or ' ' (showing truncation), then ctime pad = width-++ctimelen; if (pad>0) draw_trim(cmd, -pad, pad); printf("%c", pad - * Copyright 2014 Kyungwan Han + * Copyright 2014 Kyungwan Han * * No Standard - + USE_BOOTCHARTD(NEWTOY(bootchartd, 0, TOYFLAG_STAYROOT|TOYFLAG_USR|TOYFLAG_BIN)) config BOOTCHARTD @@ -157,7 +157,7 @@ static void start_logging() (int) (ts.tv_nsec/10000000)); dump_data_in_file("/proc/stat", sfd); dump_data_in_file("/proc/diskstats", dfd); - // stop proc dumping in 2 secs if getty or gdm, kdm, xdm found + // stop proc dumping in 2 secs if getty or gdm, kdm, xdm found if (dump_proc_data(proc_ps_fp)) if (tcnt > 2 * 1000 / TT.msec) tcnt = 2 * 1000 / TT.msec; fflush(0); @@ -203,7 +203,7 @@ static void stop_logging(char *tmp_dir, char *prog) close(kcmd_line_fd); fclose(hdr_fp); memset(toybuf, 0, sizeof(toybuf)); - snprintf(toybuf, sizeof(toybuf), "tar -zcf /var/log/bootlog.tgz header %s *.log", + snprintf(toybuf, sizeof(toybuf), "tar -zcf /var/log/bootlog.tgz header %s *.log", TT.proc_accounting ? "kernel_procs_acct" : ""); system(toybuf); @@ -246,7 +246,7 @@ void bootchartd_main() } } else if (TT.pid != 1) error_exit("not PID 1"); - // Execute the code below for start or init or PID1 + // Execute the code below for start or init or PID1 if (!parse_config_file("bootchartd.conf")) parse_config_file("/etc/bootchartd.conf"); @@ -256,23 +256,23 @@ void bootchartd_main() sigatexit(generic_signal); raise(SIGSTOP); - if (!bchartd_opt && !getenv("PATH")) + if (!bchartd_opt && !getenv("PATH")) putenv("PATH=/sbin:/usr/sbin:/bin:/usr/bin"); start_logging(); stop_logging(tmp_dir, bchartd_opt == 1 ? toys.optargs[1] : NULL); return; - } + } waitpid(lgr_pid, NULL, WUNTRACED); kill(lgr_pid, SIGCONT); - if (!bchartd_opt) { + if (!bchartd_opt) { char *pbchart_init = getenv("bootchart_init"); if (pbchart_init) execl(pbchart_init, pbchart_init, NULL); execl("/init", "init", (void *)0); execl("/sbin/init", "init", (void *)0); } - if (bchartd_opt == 1 && toys.optargs[1]) { + if (bchartd_opt == 1 && toys.optargs[1]) { pid_t prog_pid; if (!(prog_pid = xfork())) xexec(toys.optargs+1); diff --git a/toys/pending/ipcs.c b/toys/pending/ipcs.c index 8ad7c73..3e7129b 100644 --- a/toys/pending/ipcs.c +++ b/toys/pending/ipcs.c @@ -276,7 +276,7 @@ static void sem_array(void) "allocated semaphores = %d\n", info_buf.semusz, info_buf.semaem); return; - } + } if (flag(l)) { printf("------ Semaphore Limits --------\n"); u.array = (unsigned short *)&info_buf; diff --git a/toys/pending/mke2fs.c b/toys/pending/mke2fs.c deleted file mode 100644 index ee0a5b1..0000000 --- a/toys/pending/mke2fs.c +++ /dev/null @@ -1,768 +0,0 @@ -/* mke2fs.c - Create an ext2 filesystem image. - * - * Copyright 2006, 2007 Rob Landley - -// Still to go: "E:jJ:L:m:O:" -USE_MKE2FS(NEWTOY(mke2fs, "<1>2g:Fnqm#N#i#b#", TOYFLAG_SBIN)) - -config MKE2FS - bool "mke2fs" - default n - help - usage: mke2fs [-Fnq] [-b ###] [-N|i ###] [-m ###] device - - Create an ext2 filesystem on a block device or filesystem image. - - -F Force to run on a mounted device - -n Don't write to device - -q Quiet (no output) - -b size Block size (1024, 2048, or 4096) - -N inodes Allocate this many inodes - -i bytes Allocate one inode for every XXX bytes of device - -m percent Reserve this percent of filesystem space for root user - -config MKE2FS_JOURNAL - bool "Journaling support (ext3)" - default n - depends on MKE2FS - help - usage: mke2fs [-j] [-J size=###,device=XXX] - - -j Create journal (ext3) - -J Journal options - size: Number of blocks (1024-102400) - device: Specify an external journal - -config MKE2FS_GEN - bool "Generate (gene2fs)" - default n - depends on MKE2FS - help - usage: gene2fs [options] device filename - - The [options] are the same as mke2fs. - -config MKE2FS_LABEL - bool "Label support" - default n - depends on MKE2FS - help - usage: mke2fs [-L label] [-M path] [-o string] - - -L Volume label - -M Path to mount point - -o Created by - -config MKE2FS_EXTENDED - bool "Extended options" - default n - depends on MKE2FS - help - usage: mke2fs [-E stride=###] [-O option[,option]] - - -E stride= Set RAID stripe size (in blocks) - -O [opts] Specify fewer ext2 option flags (for old kernels) - All of these are on by default (as appropriate) - none Clear default options (all but journaling) - dir_index Use htree indexes for large directories - filetype Store file type info in directory entry - has_journal Set by -j - journal_dev Set by -J device=XXX - sparse_super Don't allocate huge numbers of redundant superblocks -*/ - -#define FOR_mke2fs -#include "toys.h" - -GLOBALS( - // Command line arguments. - long blocksize; - long bytes_per_inode; - long inodes; // Total inodes in filesystem. - long reserved_percent; // Integer precent of space to reserve for root. - char *gendir; // Where to read dirtree from. - - // Internal data. - struct dirtree *dt; // Tree of files to copy into the new filesystem. - unsigned treeblocks; // Blocks used by dt - unsigned treeinodes; // Inodes used by dt - - unsigned blocks; // Total blocks in the filesystem. - unsigned freeblocks; // Free blocks in the filesystem. - unsigned inodespg; // Inodes per group - unsigned groups; // Total number of block groups. - unsigned blockbits; // Bits per block. (Also blocks per group.) - - // For gene2fs - unsigned nextblock; // Next data block to allocate - unsigned nextgroup; // Next group we'll be allocating from - int fsfd; // File descriptor of filesystem (to output to). -) - -// Stuff defined in linux/ext2_fs.h - -#define EXT2_SUPER_MAGIC 0xEF53 - -struct ext2_superblock { - uint32_t inodes_count; // Inodes count - uint32_t blocks_count; // Blocks count - uint32_t r_blocks_count; // Reserved blocks count - uint32_t free_blocks_count; // Free blocks count - uint32_t free_inodes_count; // Free inodes count - uint32_t first_data_block; // First Data Block - uint32_t log_block_size; // Block size - uint32_t log_frag_size; // Fragment size - uint32_t blocks_per_group; // Blocks per group - uint32_t frags_per_group; // Fragments per group - uint32_t inodes_per_group; // Inodes per group - uint32_t mtime; // Mount time - uint32_t wtime; // Write time - uint16_t mnt_count; // Mount count - uint16_t max_mnt_count; // Maximal mount count - uint16_t magic; // Magic signature - uint16_t state; // File system state - uint16_t errors; // Behaviour when detecting errors - uint16_t minor_rev_level; // minor revision level - uint32_t lastcheck; // time of last check - uint32_t checkinterval; // max. time between checks - uint32_t creator_os; // OS - uint32_t rev_level; // Revision level - uint16_t def_resuid; // Default uid for reserved blocks - uint16_t def_resgid; // Default gid for reserved blocks - uint32_t first_ino; // First non-reserved inode - uint16_t inode_size; // size of inode structure - uint16_t block_group_nr; // block group # of this superblock - uint32_t feature_compat; // compatible feature set - uint32_t feature_incompat; // incompatible feature set - uint32_t feature_ro_compat; // readonly-compatible feature set - char uuid[16]; // 128-bit uuid for volume - char volume_name[16]; // volume name - char last_mounted[64]; // directory where last mounted - uint32_t alg_usage_bitmap; // For compression - // For EXT2_COMPAT_PREALLOC - uint8_t prealloc_blocks; // Nr of blocks to try to preallocate - uint8_t prealloc_dir_blocks; //Nr to preallocate for dirs - uint16_t padding1; - // For EXT3_FEATURE_COMPAT_HAS_JOURNAL - uint8_t journal_uuid[16]; // uuid of journal superblock - uint32_t journal_inum; // inode number of journal file - uint32_t journal_dev; // device number of journal file - uint32_t last_orphan; // start of list of inodes to delete - uint32_t hash_seed[4]; // HTREE hash seed - uint8_t def_hash_version; // Default hash version to use - uint8_t padding2[3]; - uint32_t default_mount_opts; - uint32_t first_meta_bg; // First metablock block group - uint32_t mkfs_time; // Creation timestamp - uint32_t jnl_blocks[17]; // Backup of journal inode - // uint32_t reserved[172]; // Padding to the end of the block -}; - -struct ext2_group -{ - uint32_t block_bitmap; // Block number of block bitmap - uint32_t inode_bitmap; // Block number of inode bitmap - uint32_t inode_table; // Block number of inode table - uint16_t free_blocks_count; // How many free blocks in this group? - uint16_t free_inodes_count; // How many free inodes in this group? - uint16_t used_dirs_count; // How many directories? - uint16_t reserved[7]; // pad to 32 bytes -}; - -struct ext2_dentry { - uint32_t inode; // Inode number - uint16_t rec_len; // Directory entry length - uint8_t name_len; // Name length - uint8_t file_type; - char name[0]; // File name -}; - -struct ext2_inode { - uint16_t mode; // File mode - uint16_t uid; // Low 16 bits of Owner Uid - uint32_t size; // Size in bytes - uint32_t atime; // Access time - uint32_t ctime; // Creation time - uint32_t mtime; // Modification time - uint32_t dtime; // Deletion Time - uint16_t gid; // Low 16 bits of Group Id - uint16_t links_count; // Links count - uint32_t blocks; // Blocks count - uint32_t flags; // File flags - uint32_t reserved1; - uint32_t block[15]; // Pointers to blocks - uint32_t generation; // File version (for NFS) - uint32_t file_acl; // File ACL - uint32_t dir_acl; // Directory ACL (or top bits of file length) - uint32_t faddr; // Last block in file - uint8_t frag; // Fragment number - uint8_t fsize; // Fragment size - uint16_t pad1; - uint16_t uid_high; // High bits of uid - uint16_t gid_high; // High bits of gid - uint32_t reserved2; -}; - -#define EXT2_FEATURE_COMPAT_DIR_PREALLOC 0x0001 -#define EXT2_FEATURE_COMPAT_IMAGIC_INODES 0x0002 -#define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004 -#define EXT2_FEATURE_COMPAT_EXT_ATTR 0x0008 -#define EXT2_FEATURE_COMPAT_RESIZE_INO 0x0010 -#define EXT2_FEATURE_COMPAT_DIR_INDEX 0x0020 - -#define EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER 0x0001 -#define EXT2_FEATURE_RO_COMPAT_LARGE_FILE 0x0002 -#define EXT2_FEATURE_RO_COMPAT_BTREE_DIR 0x0004 - -#define EXT2_FEATURE_INCOMPAT_COMPRESSION 0x0001 -#define EXT2_FEATURE_INCOMPAT_FILETYPE 0x0002 -#define EXT3_FEATURE_INCOMPAT_RECOVER 0x0004 -#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV 0x0008 -#define EXT2_FEATURE_INCOMPAT_META_BG 0x0010 - -#define EXT2_NAME_LEN 255 - -// Ext2 directory file types. Only the low 3 bits are used. The -// other bits are reserved for now. - -enum { - EXT2_FT_UNKNOWN, - EXT2_FT_REG_FILE, - EXT2_FT_DIR, - EXT2_FT_CHRDEV, - EXT2_FT_BLKDEV, - EXT2_FT_FIFO, - EXT2_FT_SOCK, - EXT2_FT_SYMLINK, - EXT2_FT_MAX -}; - -#define INODES_RESERVED 10 - -static uint32_t div_round_up(uint32_t a, uint32_t b) -{ - uint32_t c = a/b; - - if (a%b) c++; - return c; -} - -// Calculate data blocks plus index blocks needed to hold a file. - -static uint32_t file_blocks_used(uint64_t size, uint32_t *blocklist) -{ - uint32_t dblocks = (uint32_t)((size+(TT.blocksize-1))/TT.blocksize); - uint32_t idx=TT.blocksize/4, iblocks=0, diblocks=0, tiblocks=0; - - // Fill out index blocks in inode. - - if (blocklist) { - int i; - - // Direct index blocks - for (i=0; i<13 && i 13+idx) blocklist[13] = 13+idx; - // Doubly indirect index blocks - idx = 13 + idx + (idx*idx); - if (dblocks > idx) blocklist[14] = idx; - - return 0; - } - - // Account for direct, singly, doubly, and triply indirect index blocks - - if (dblocks > 12) { - iblocks = ((dblocks-13)/idx)+1; - if (iblocks > 1) { - diblocks = ((iblocks-2)/idx)+1; - if (diblocks > 1) - tiblocks = ((diblocks-2)/idx)+1; - } - } - - return dblocks + iblocks + diblocks + tiblocks; -} - -// Use the parent pointer to iterate through the tree non-recursively. -static struct dirtree *treenext(struct dirtree *this) -{ - while (this && !this->next) this = this->parent; - if (this) this = this->next; - - return this; -} - -// Recursively calculate the number of blocks used by each inode in the tree. -// Returns blocks used by this directory, assigns bytes used to *size. -// Writes total block count to TT.treeblocks and inode count to TT.treeinodes. - -static long check_treesize(struct dirtree *that, off_t *size) -{ - long blocks; - - while (that) { - *size += sizeof(struct ext2_dentry) + strlen(that->name); - - if (that->child) - that->st.st_blocks = check_treesize(that->child, &that->st.st_size); - else if (S_ISREG(that->st.st_mode)) { - that->st.st_blocks = file_blocks_used(that->st.st_size, 0); - TT.treeblocks += that->st.st_blocks; - } - that = that->next; - } - TT.treeblocks += blocks = file_blocks_used(*size, 0); - TT.treeinodes++; - - return blocks; -} - -// Calculate inode numbers and link counts. -// -// To do this right I need to copy the tree and sort it, but here's a really -// ugly n^2 way of dealing with the problem that doesn't scale well to large -// numbers of files (> 100,000) but can be done in very little code. -// This rewrites inode numbers to their final values, allocating depth first. - -static void check_treelinks(struct dirtree *tree) -{ - struct dirtree *current=tree, *that; - long inode = INODES_RESERVED; - - while (current) { - ++inode; - // Since we can't hardlink to directories, we know their link count. - if (S_ISDIR(current->st.st_mode)) current->st.st_nlink = 2; - else { - dev_t new = current->st.st_dev; - - if (!new) continue; - - // Look for other copies of current node - current->st.st_nlink = 0; - for (that = tree; that; that = treenext(that)) { - if (current->st.st_ino == that->st.st_ino && - current->st.st_dev == that->st.st_dev) - { - current->st.st_nlink++; - current->st.st_ino = inode; - } - } - } - current->st.st_ino = inode; - current = treenext(current); - } -} - -// Calculate inodes per group from total inodes. -static uint32_t get_inodespg(uint32_t inodes) -{ - uint32_t temp; - - // Round up to fill complete inode blocks. - temp = (inodes + TT.groups - 1) / TT.groups; - inodes = TT.blocksize/sizeof(struct ext2_inode); - return ((temp + inodes - 1)/inodes)*inodes; -} - -// Fill out superblock and TT structures. - -static void init_superblock(struct ext2_superblock *sb) -{ - uint32_t temp; - - // Set log_block_size and log_frag_size. - - for (temp = 0; temp < 4; temp++) if (TT.blocksize == 1024<log_block_size = sb->log_frag_size = SWAP_LE32(temp); - - // Fill out blocks_count, r_blocks_count, first_data_block - - sb->blocks_count = SWAP_LE32(TT.blocks); - sb->free_blocks_count = SWAP_LE32(TT.freeblocks); - temp = (TT.blocks * (uint64_t)TT.reserved_percent) / 100; - sb->r_blocks_count = SWAP_LE32(temp); - - sb->first_data_block = SWAP_LE32(TT.blocksize == 1024 ? 1 : 0); - - // Set blocks_per_group and frags_per_group, which is the size of an - // allocation bitmap that fits in one block (I.E. how many bits per block)? - - sb->blocks_per_group = sb->frags_per_group = SWAP_LE32(TT.blockbits); - - // Set inodes_per_group and total inodes_count - sb->inodes_per_group = SWAP_LE32(TT.inodespg); - sb->inodes_count = SWAP_LE32(TT.inodespg * TT.groups); - - // Determine free inodes. - temp = TT.inodespg*TT.groups - INODES_RESERVED; - if (temp < TT.treeinodes) error_exit("Not enough inodes.\n"); - sb->free_inodes_count = SWAP_LE32(temp - TT.treeinodes); - - // Fill out the rest of the superblock. - sb->max_mnt_count=0xFFFF; - sb->wtime = sb->lastcheck = sb->mkfs_time = SWAP_LE32(time(NULL)); - sb->magic = SWAP_LE32(0xEF53); - sb->state = sb->errors = SWAP_LE16(1); - - sb->rev_level = SWAP_LE32(1); - sb->first_ino = SWAP_LE32(INODES_RESERVED+1); - sb->inode_size = SWAP_LE16(sizeof(struct ext2_inode)); - sb->feature_incompat = SWAP_LE32(EXT2_FEATURE_INCOMPAT_FILETYPE); - sb->feature_ro_compat = SWAP_LE32(EXT2_FEATURE_RO_COMPAT_SPARSE_SUPER); - - create_uuid(sb->uuid); - - // TODO If we're called as mke3fs or mkfs.ext3, do a journal. - - //if (strchr(toys.which->name,'3')) - // sb->feature_compat |= SWAP_LE32(EXT3_FEATURE_COMPAT_HAS_JOURNAL); -} - -// Does this group contain a superblock backup (and group descriptor table)? -static int is_sb_group(uint32_t group) -{ - int i; - - // Superblock backups are on groups 0, 1, and powers of 3, 5, and 7. - if(!group || group==1) return 1; - for (i=3; i<9; i+=2) { - int j = i; - while (j sizeof(toybuf) ? sizeof(toybuf) : len; - xwrite(TT.fsfd, toybuf, out); - len -= out; - } - } -} - -// Fill out an inode structure from struct stat info in dirtree. -static void fill_inode(struct ext2_inode *in, struct dirtree *that) -{ - uint32_t fbu[15]; - int temp; - - file_blocks_used(that->st.st_size, fbu); - - // If that inode needs data blocks allocated to it. - if (that->st.st_size) { - int i, group = TT.nextblock/TT.blockbits; - - // TODO: teach this about indirect blocks. - for (i=0; i<15; i++) { - // If we just jumped into a new group, skip group overhead blocks. - while (group >= TT.nextgroup) - TT.nextblock += group_overhead(TT.nextgroup++); - } - } - // TODO : S_ISREG/DIR/CHR/BLK/FIFO/LNK/SOCK(m) - in->mode = SWAP_LE32(that->st.st_mode); - - in->uid = SWAP_LE16(that->st.st_uid & 0xFFFF); - in->uid_high = SWAP_LE16(that->st.st_uid >> 16); - in->gid = SWAP_LE16(that->st.st_gid & 0xFFFF); - in->gid_high = SWAP_LE16(that->st.st_gid >> 16); - in->size = SWAP_LE32(that->st.st_size & 0xFFFFFFFF); - - // Contortions to make the compiler not generate a warning for x>>32 - // when x is 32 bits. The optimizer should clean this up. - if (sizeof(that->st.st_size) > 4) temp = 32; - else temp = 0; - if (temp) in->dir_acl = SWAP_LE32(that->st.st_size >> temp); - - in->atime = SWAP_LE32(that->st.st_atime); - in->ctime = SWAP_LE32(that->st.st_ctime); - in->mtime = SWAP_LE32(that->st.st_mtime); - - in->links_count = SWAP_LE16(that->st.st_nlink); - in->blocks = SWAP_LE32(that->st.st_blocks); - // in->faddr -} - -// Works like an archiver. -// The first argument is the name of the file to create. If it already -// exists, that size will be used. - -void mke2fs_main(void) -{ - int i, temp; - off_t length; - uint32_t usedblocks, usedinodes, dtbblk; - struct dirtree *dti, *dtb; - struct ext2_superblock sb; - - // Handle command line arguments. - - if (toys.optargs[1]) { - sscanf(toys.optargs[1], "%u", &TT.blocks); - temp = O_RDWR|O_CREAT; - } else temp = O_RDWR; - if (!TT.reserved_percent) TT.reserved_percent = 5; - - // TODO: Check if filesystem is mounted here - - // For mke?fs, open file. For gene?fs, create file. - TT.fsfd = xcreate(*toys.optargs, temp, 0777); - - // Determine appropriate block size and block count from file length. - // (If no length, default to 4k. They can override it on the cmdline.) - - length = fdlength(TT.fsfd); - if (!TT.blocksize) TT.blocksize = (length && length < 1<<29) ? 1024 : 4096; - TT.blockbits = 8*TT.blocksize; - if (!TT.blocks) TT.blocks = length/TT.blocksize; - - // Collect gene2fs list or lost+found, calculate requirements. - - if (TT.gendir) { - strncpy(toybuf, TT.gendir, sizeof(toybuf)); - dti = dirtree_read(toybuf, dirtree_notdotdot); - } else { - dti = xzalloc(sizeof(struct dirtree)+11); - strcpy(dti->name, "lost+found"); - dti->st.st_mode = S_IFDIR|0755; - dti->st.st_ctime = dti->st.st_mtime = time(NULL); - } - - // Add root directory inode. This is iterated through for when finding - // blocks, but not when finding inodes. The tree's parent pointers don't - // point back into this. - - dtb = xzalloc(sizeof(struct dirtree)+1); - dtb->st.st_mode = S_IFDIR|0755; - dtb->st.st_ctime = dtb->st.st_mtime = time(NULL); - dtb->child = dti; - - // Figure out how much space is used by preset files - length = check_treesize(dtb, &(dtb->st.st_size)); - check_treelinks(dtb); - - // Figure out how many total inodes we need. - - if (!TT.inodes) { - if (!TT.bytes_per_inode) TT.bytes_per_inode = 8192; - TT.inodes = (TT.blocks * (uint64_t)TT.blocksize) / TT.bytes_per_inode; - } - - // If we're generating a filesystem and have no idea how many blocks it - // needs, start with a minimal guess, find the overhead of that many - // groups, and loop until this is enough groups to store this many blocks. - if (!TT.blocks) TT.groups = (TT.treeblocks/TT.blockbits)+1; - else TT.groups = div_round_up(TT.blocks, TT.blockbits); - - for (;;) { - temp = TT.treeblocks; - - for (i = 0; i TT.blocks) end = TT.blocks & (TT.blockbits-1); - - // Blocks used by inode table - itable = (TT.inodespg*sizeof(struct ext2_inode))/TT.blocksize; - - // If a superblock goes here, write it out. - start = group_superblock_overhead(i); - if (start) { - struct ext2_group *bg = (struct ext2_group *)toybuf; - int treeblocks = TT.treeblocks, treeinodes = TT.treeinodes; - - sb.block_group_nr = SWAP_LE16(i); - - // Write superblock and pad it up to block size - xwrite(TT.fsfd, &sb, sizeof(struct ext2_superblock)); - temp = TT.blocksize - sizeof(struct ext2_superblock); - if (!i && TT.blocksize > 1024) temp -= 1024; - memset(toybuf, 0, TT.blocksize); - xwrite(TT.fsfd, toybuf, temp); - - // Loop through groups to write group descriptor table. - for(j=0; j treeinodes) { - treeinodes -= temp; - temp = 0; - } else { - temp -= treeinodes; - treeinodes = 0; - } - bg[slot].free_inodes_count = SWAP_LE16(temp); - - // How many free blocks in this group? - temp = TT.inodespg/(TT.blocksize/sizeof(struct ext2_inode)) + 2; - temp = end-used-temp; - if (temp > treeblocks) { - treeblocks -= temp; - temp = 0; - } else { - temp -= treeblocks; - treeblocks = 0; - } - bg[slot].free_blocks_count = SWAP_LE32(temp); - - // Fill out rest of group structure - used += j*TT.blockbits; - bg[slot].block_bitmap = SWAP_LE32(used++); - bg[slot].inode_bitmap = SWAP_LE32(used++); - bg[slot].inode_table = SWAP_LE32(used); - bg[slot].used_dirs_count = 0; // (TODO) - } - xwrite(TT.fsfd, bg, TT.blocksize); - } - - // Now write out stuff that every block group has. - - // Write block usage bitmap - - start += 2 + itable; - memset(toybuf, 0, TT.blocksize); - bits_set(toybuf, 0, start); - bits_set(toybuf, end, TT.blockbits-end); - temp = TT.treeblocks - usedblocks; - if (temp) { - if (end-start > temp) temp = end-start; - bits_set(toybuf, start, temp); - } - xwrite(TT.fsfd, toybuf, TT.blocksize); - - // Write inode bitmap - memset(toybuf, 0, TT.blocksize); - j = 0; - if (!i) bits_set(toybuf, 0, j = INODES_RESERVED); - bits_set(toybuf, TT.inodespg, slot = TT.blockbits-TT.inodespg); - temp = TT.treeinodes - usedinodes; - if (temp) { - if (slot-j > temp) temp = slot-j; - bits_set(toybuf, j, temp); - } - xwrite(TT.fsfd, toybuf, TT.blocksize); - - // Write inode table for this group (TODO) - for (j = 0; j +#define _LINUX_SYSINFO_H // workaround for musl bug +#include GLOBALS( - char *family; + char *A; ) -#define DEFAULT_PREFIXLEN 128 -#define INVALID_ADDR 0xffffffffUL -#define IPV6_ADDR_LEN 40 //32 + 7 (':') + 1 ('\0') - struct _arglist { char *arg; int action; @@ -73,103 +65,176 @@ static struct _arglist arglist2[] = { { NULL, 0 } }; -// to get the host name from the given ip. -static int get_hostname(char *ipstr, struct sockaddr_in *sockin) +void xsend(int sockfd, void *buf, size_t len) { - struct hostent *host; - - sockin->sin_family = AF_INET; - sockin->sin_port = 0; - - if (!strcmp(ipstr, "default")) { - sockin->sin_addr.s_addr = INADDR_ANY; - return 1; - } - - if (inet_aton(ipstr, &sockin->sin_addr)) return 0; - if (!(host = gethostbyname(ipstr))) perror_exit("resolving '%s'", ipstr); - memcpy(&sockin->sin_addr, host->h_addr_list[0], sizeof(struct in_addr)); - - return 0; + if (send(sockfd, buf, len, 0) != len) perror_exit("xsend"); } -// used to extract the address info from the given ip. -static int get_addrinfo(char *ip, struct sockaddr_in6 *sock_in6) +int xrecv(int sockfd, void *buf, size_t len) { - struct addrinfo hints, *result; - int status = 0; + int msg_len = recv(sockfd, buf, len, 0); + if (msg_len < 0) perror_exit("xrecv"); - memset(&hints, 0, sizeof(struct addrinfo)); - hints.ai_family = AF_INET6; - if ((status = getaddrinfo(ip, NULL, &hints, &result))) { - perror_msg("getaddrinfo: %s", gai_strerror(status)); - return -1; - } - if (result) { - memcpy(sock_in6, result->ai_addr, sizeof(*sock_in6)); - freeaddrinfo(result); - } - return 0; + return msg_len; } -static void get_flag_value(char *str, int flags) +void addAttr(struct nlmsghdr *nl, int maxlen, void *attr, int type, int len) { - // RTF_* bits in order: - // UP, GATEWAY, HOST, REINSTATE, DYNAMIC, MODIFIED, DEFAULT, ADDRCONF, CACHE - int i = 0, mask = 0x105003f; - - for (; mask; mask>>=1) if (mask&1) { - if (flags&(1<nlmsg_len) + rtlen > maxlen) perror_exit("addAttr"); + rt = (struct rtattr*)((char *)nl + NLMSG_ALIGN(nl->nlmsg_len)); + rt->rta_type = type; + rt->rta_len = rtlen; + memcpy(RTA_DATA(rt), attr, len); + nl->nlmsg_len = NLMSG_ALIGN(nl->nlmsg_len) + rtlen; } -// extract inet4 route info from /proc/net/route file and display it. -static void display_routes(void) -{ - unsigned long dest, gate, mask; - int flags, ref, use, metric, mss, win, irtt, items; - char iface[64] = {0,}, flag_val[10]; //there are 9 flags "UGHRDMDAC" for route. +static void get_hostname(sa_family_t f, void *a, char *dst, size_t len) { + size_t a_len = (AF_INET6 == f) ? sizeof(struct in6_addr) : sizeof(struct in_addr); - FILE *fp = xfopen("/proc/net/route", "r"); - - xprintf("Kernel IP routing table\n" - "Destination Gateway Genmask Flags %s Iface\n", - (toys.optflags & FLAG_e)? " MSS Window irtt" : "Metric Ref Use"); - - if (fscanf(fp, "%*[^\n]\n") < 0) perror_exit("fscanf"); //skip 1st line - while ((items = fscanf(fp, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", iface, &dest, - &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt)) == 11) - { - char *destip = toybuf, *gateip = toybuf+32, *maskip = toybuf+64; //ip string 16 - - if (!(flags & RTF_UP)) continue; //skip down interfaces. - - if (!dest && !(toys.optflags & FLAG_n)) strcpy( destip, "default"); - else if (!inet_ntop(AF_INET, &dest, destip, 32)) perror_exit("inet"); - - if (!gate && !(toys.optflags & FLAG_n)) strcpy( gateip, "*"); - else if (!inet_ntop(AF_INET, &gate, gateip, 32)) perror_exit("inet"); - - if (!inet_ntop(AF_INET, &mask, maskip, 32)) perror_exit("inet"); - - //Get flag Values - get_flag_value(flag_val, flags); - if (flags & RTF_REJECT) flag_val[0] = '!'; - xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val); - if (toys.optflags & FLAG_e) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface); - else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface); - } - - if (items > 0 && feof(fp)) perror_exit("fscanf %d", items); - fclose(fp); + struct hostent *host = gethostbyaddr(a, a_len, f); + if (host) xstrncpy(dst, host->h_name, len); } -/* - * find the given parameter in list like add/del/net/host. - * and if match found return the appropriate action. - */ +static void display_routes(sa_family_t f) +{ + int fd, msg_hdr_len, route_protocol; + struct { + struct nlmsghdr nl; + struct rtmsg rt; + } req; + struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)]; + struct nlmsghdr *msg_hdr_ptr; + struct rtmsg *route_entry; + struct rtattr *rteattr; + + fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + + memset(&req, 0, sizeof(req)); + req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); + req.nl.nlmsg_type = RTM_GETROUTE; + req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; + req.nl.nlmsg_pid = getpid(); + req.nl.nlmsg_seq = 1; + req.rt.rtm_family = f; + req.rt.rtm_table = RT_TABLE_MAIN; + xsend(fd, &req, sizeof(req)); + + if (f == AF_INET) { + xprintf("Kernel IP routing table\n" + "Destination Gateway Genmask Flags %s Iface\n", + FLAG(e) ? " MSS Window irtt" : "Metric Ref Use"); + } else { + xprintf("Kernel IPv6 routing table\n" + "%-31s%-26s Flag Metric Ref Use If\n", "Destination", "Next Hop"); + } + + msg_hdr_len = xrecv(fd, buf, sizeof(buf)); + msg_hdr_ptr = buf; + while (msg_hdr_ptr->nlmsg_type != NLMSG_DONE) { + while (NLMSG_OK(msg_hdr_ptr, msg_hdr_len)) { + route_entry = NLMSG_DATA(msg_hdr_ptr); + route_protocol = route_entry->rtm_protocol; + + // Annoyingly NLM_F_MATCH is not yet implemented so even if we pass in + // RT_TABLE_MAIN with RTM_GETROUTE it still returns everything so we + // have to filter here. + if (route_entry->rtm_table == RT_TABLE_MAIN) { + int route_attribute_len; + char dest[INET6_ADDRSTRLEN], gate[INET6_ADDRSTRLEN], netmask[32], + flags[10] = "U", if_name[IF_NAMESIZE] = "-"; + unsigned priority = 0, mss = 0, win = 0, irtt = 0, ref = 0, use = 0, + route_netmask, metric_len; + struct in_addr netmask_addr; + struct rtattr *metric; + struct rta_cacheinfo *cache_info; + + if (f == AF_INET) { + strcpy(dest, FLAG(n) ? "0.0.0.0" : "default"); + strcpy(gate, FLAG(n) ? "*" : "0.0.0.0"); + strcpy(netmask, "0.0.0.0"); + } else { + strcpy(dest, "::"); + strcpy(gate, "::"); + } + + route_netmask = route_entry->rtm_dst_len; + if (route_netmask == 0) netmask_addr.s_addr = ~((in_addr_t) -1); + else netmask_addr.s_addr = htonl(~((1 << (32 - route_netmask)) - 1)); + inet_ntop(AF_INET, &netmask_addr, netmask, sizeof(netmask)); + + rteattr = RTM_RTA(route_entry); + route_attribute_len = RTM_PAYLOAD(msg_hdr_ptr); + while (RTA_OK(rteattr, route_attribute_len)) { + switch (rteattr->rta_type) { + case RTA_DST: + if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), dest, sizeof(dest)); + else get_hostname(f, RTA_DATA(rteattr), dest, sizeof(dest)); + break; + + case RTA_GATEWAY: + if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), gate, sizeof(dest)); + else get_hostname(f, RTA_DATA(rteattr), gate, sizeof(dest)); + strcat(flags, "G"); + break; + + case RTA_PRIORITY: + priority = *(unsigned *)RTA_DATA(rteattr); + break; + + case RTA_OIF: + if_indextoname(*(int *)RTA_DATA(rteattr), if_name); + break; + + case RTA_METRICS: + metric_len = RTA_PAYLOAD(rteattr); + for (metric = RTA_DATA(rteattr); RTA_OK(metric, metric_len); + metric = RTA_NEXT(metric, metric_len)) + if (metric->rta_type == RTAX_ADVMSS) + mss = *(unsigned *)RTA_DATA(metric); + else if (metric->rta_type == RTAX_WINDOW) + win = *(unsigned *)RTA_DATA(metric); + else if (metric->rta_type == RTAX_RTT) + irtt = (*(unsigned *)RTA_DATA(metric))/8; + break; + + case RTA_CACHEINFO: + cache_info = RTA_DATA(rteattr); + ref = cache_info->rta_clntref; + use = cache_info->rta_used; + break; + } + + rteattr = RTA_NEXT(rteattr, route_attribute_len); + } + + if (route_entry->rtm_type == RTN_UNREACHABLE) flags[0] = '!'; + if (route_netmask == 32) strcat(flags, "H"); + if (route_protocol == RTPROT_REDIRECT) strcat(flags, "D"); + + if (f == AF_INET) { + xprintf("%-15.15s %-15.15s %-16s%-6s", dest, gate, netmask, flags); + if (FLAG(e)) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, if_name); + else xprintf("%-6d %-2d %7d %s\n", priority, ref, use, if_name); + } else { + char *dest_with_mask = xmprintf("%s/%u", dest, route_netmask); + xprintf("%-30s %-26s %-4s %-6d %-4d %2d %-8s\n", + dest_with_mask, gate, flags, priority, ref, use, if_name); + free(dest_with_mask); + } + } + msg_hdr_ptr = NLMSG_NEXT(msg_hdr_ptr, msg_hdr_len); + } + + msg_hdr_len = xrecv(fd, buf, sizeof(buf)); + msg_hdr_ptr = buf; + } + + xclose(fd); +} + +// find parameter (add/del/net/host) in list, return appropriate action or 0. static int get_action(char ***argv, struct _arglist *list) { struct _arglist *alist; @@ -184,271 +249,135 @@ static int get_action(char ***argv, struct _arglist *list) return 0; } -/* - * used to get the params like: metric, netmask, gw, mss, window, irtt, dev and their values. - * additionally set the flag values for reject, mod, dyn and reinstate. - */ -static void get_next_params(char **argv, struct rtentry *rt, char **netmask) -{ - for (;*argv;argv++) { - if (!strcmp(*argv, "reject")) rt->rt_flags |= RTF_REJECT; - else if (!strcmp(*argv, "mod")) rt->rt_flags |= RTF_MODIFIED; - else if (!strcmp(*argv, "dyn")) rt->rt_flags |= RTF_DYNAMIC; - else if (!strcmp(*argv, "reinstate")) rt->rt_flags |= RTF_REINSTATE; - else { - if (!argv[1]) help_exit(0); - - //set the metric field in the routing table. - if (!strcmp(*argv, "metric")) - rt->rt_metric = atolx_range(argv[1], 0, ULONG_MAX) + 1; - else if (!strcmp(*argv, "netmask")) { - //when adding a network route, the netmask to be used. - struct sockaddr sock; - unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr); - - if (addr_mask) help_exit("dup netmask"); - *netmask = argv[1]; - get_hostname(*netmask, (struct sockaddr_in *) &sock); - rt->rt_genmask = sock; - } else if (!strcmp(*argv, "gw")) { - //route packets via a gateway. - if (!(rt->rt_flags & RTF_GATEWAY)) { - if (!get_hostname(argv[1], (struct sockaddr_in *) &rt->rt_gateway)) - rt->rt_flags |= RTF_GATEWAY; - else perror_exit("gateway '%s' is a NETWORK", argv[1]); - } else help_exit("dup gw"); - } else if (!strcmp(*argv, "mss")) { - //set the TCP Maximum Segment Size for connections over this route. - rt->rt_mtu = atolx_range(argv[1], 64, 65536); - rt->rt_flags |= RTF_MSS; - } else if (!strcmp(*argv, "window")) { - //set the TCP window size for connections over this route to W bytes. - rt->rt_window = atolx_range(argv[1], 128, INT_MAX); //win low - rt->rt_flags |= RTF_WINDOW; - } else if (!strcmp(*argv, "irtt")) { - rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX); - rt->rt_flags |= RTF_IRTT; - } else if (!strcmp(*argv, "dev") && !rt->rt_dev) rt->rt_dev = argv[1]; - else help_exit("no '%s'", *argv); - argv++; - } - } - - if (!rt->rt_dev && (rt->rt_flags & RTF_REJECT)) rt->rt_dev = (char *)"lo"; -} - -// verify the netmask and conflict in netmask and route address. -static void verify_netmask(struct rtentry *rt, char *netmask) -{ - unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr); - unsigned int router_addr = ~(unsigned int)(((struct sockaddr_in *)&((rt)->rt_dst))->sin_addr.s_addr); - - if (addr_mask) { - addr_mask = ~ntohl(addr_mask); - if ((rt->rt_flags & RTF_HOST) && addr_mask != INVALID_ADDR) - perror_exit("conflicting netmask and host route"); - if (addr_mask & (addr_mask + 1)) perror_exit("wrong netmask '%s'", netmask); - addr_mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr; - if (addr_mask & router_addr) perror_exit("conflicting netmask and route address"); - } -} - // add/del a route. -static void setroute(char **argv) +static void setroute(sa_family_t f, char **argv) { - struct rtentry rt; - char *netmask, *targetip; - int is_net_or_host = 0, sokfd, arg2_action; + char *tgtip; + int sockfd, arg2_action; int action = get_action(&argv, arglist1); //verify the arg for add/del. + struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)]; + struct nlmsghdr *nlMsg; + struct rtmsg *rtMsg; if (!action || !*argv) help_exit("setroute"); - arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host if (!*argv) help_exit("setroute"); + tgtip = *argv++; + sockfd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + memset(buf, 0, sizeof(buf)); + nlMsg = (struct nlmsghdr *) buf; + rtMsg = (struct rtmsg *) NLMSG_DATA(nlMsg); - memset(&rt, 0, sizeof(struct rtentry)); - targetip = *argv++; + nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - netmask = strchr(targetip, '/'); - if (netmask) { - *netmask++ = 0; - //used to verify the netmask and route conflict. - (((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr) - = htonl((1<<(32-atolx_range(netmask, 0, 32)))-1); - rt.rt_genmask.sa_family = AF_INET; - netmask = 0; - } else netmask = "default"; + //TODO(emolitor): Improve action and arg2_action handling + if (action == 1) { // Add + nlMsg->nlmsg_type = RTM_NEWROUTE; + nlMsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; + } else { // Delete + nlMsg->nlmsg_type = RTM_DELROUTE; + nlMsg->nlmsg_flags = NLM_F_REQUEST; + } - is_net_or_host = get_hostname(targetip, (void *)&rt.rt_dst); + nlMsg->nlmsg_pid = getpid(); + nlMsg->nlmsg_seq = 1; + rtMsg->rtm_family = f; + rtMsg->rtm_table = RT_TABLE_UNSPEC; + rtMsg->rtm_type = RTN_UNICAST; + rtMsg->rtm_protocol = RTPROT_UNSPEC; + rtMsg->rtm_flags = RTM_F_NOTIFY; + rtMsg->rtm_dst_len = rtMsg->rtm_src_len = (f == AF_INET) ? 32 : 128; - if (arg2_action) is_net_or_host = arg2_action & 1; - rt.rt_flags = ((is_net_or_host) ? RTF_UP : (RTF_UP | RTF_HOST)); + if (arg2_action == 2) rtMsg->rtm_scope = RT_SCOPE_HOST; - get_next_params(argv, &rt, (char **)&netmask); - verify_netmask(&rt, (char *)netmask); + size_t addr_len = sizeof(struct in_addr); + if (f == AF_INET6) addr_len = sizeof(struct in6_addr); + unsigned char addr[sizeof(struct in6_addr)] = {0,}; - if ((action == 1) && (rt.rt_flags & RTF_HOST)) - (((struct sockaddr_in *)&((rt).rt_genmask))->sin_addr.s_addr) = INVALID_ADDR; - - sokfd = xsocket(AF_INET, SOCK_DGRAM, 0); - if (action == 1) xioctl(sokfd, SIOCADDRT, &rt); - else xioctl(sokfd, SIOCDELRT, &rt); - xclose(sokfd); -} - -/* - * get prefix len (if any) and remove the prefix from target ip. - * if no prefix then set default prefix len. - */ -static void is_prefix_inet6(char **tip, struct in6_rtmsg *rt) -{ - unsigned long plen; - char *prefix = strchr(*tip, '/'); - - if (prefix) { - *prefix = '\0'; - plen = atolx_range(prefix + 1, 0, 128); //DEFAULT_PREFIXLEN); - } else plen = DEFAULT_PREFIXLEN; - - rt->rtmsg_flags = (plen == DEFAULT_PREFIXLEN) ? (RTF_UP | RTF_HOST) : RTF_UP; - rt->rtmsg_dst_len = plen; -} - -/* - * used to get the params like: metric, gw, dev and their values. - * additionally set the flag values for mod and dyn. - */ -static void get_next_params_inet6(char **argv, struct sockaddr_in6 *sock_in6, struct in6_rtmsg *rt, char **dev_name) -{ - for (;*argv;argv++) { - if (!strcmp(*argv, "mod")) rt->rtmsg_flags |= RTF_MODIFIED; - else if (!strcmp(*argv, "dyn")) rt->rtmsg_flags |= RTF_DYNAMIC; + for (; *argv; argv++) { + if (!strcmp(*argv, "mod")) continue; + else if (!strcmp(*argv, "dyn")) continue; + else if (!strcmp(*argv, "reinstate")) continue; + else if (!strcmp(*argv, "reject")) rtMsg->rtm_type = RTN_UNREACHABLE; else { - if (!argv[1]) help_exit(0); + if (!argv[1]) show_help(stdout, 1); - if (!strcmp(*argv, "metric")) - rt->rtmsg_metric = atolx_range(argv[1], 0, ULONG_MAX); - else if (!strcmp(*argv, "gw")) { - //route packets via a gateway. - if (!(rt->rtmsg_flags & RTF_GATEWAY)) { - if (!get_addrinfo(argv[1], (struct sockaddr_in6 *) &sock_in6)) { - memcpy(&rt->rtmsg_gateway, sock_in6->sin6_addr.s6_addr, sizeof(struct in6_addr)); - rt->rtmsg_flags |= RTF_GATEWAY; - } else perror_exit("resolving '%s'", argv[1]); - } else help_exit(0); + if (!strcmp(*argv, "metric")) { + unsigned int priority = atolx_range(argv[1], 0, UINT_MAX); + addAttr(nlMsg, sizeof(toybuf), &priority, RTA_PRIORITY, sizeof(unsigned int)); + } else if (!strcmp(*argv, "netmask")) { + uint32_t netmask; + char *ptr; + uint32_t naddr[4] = {0,}; + uint64_t plen; + + netmask = (f == AF_INET6) ? 128 : 32; // set default netmask + plen = strtoul(argv[1], &ptr, 0); + + if (!ptr || ptr == argv[1] || *ptr || !plen || plen > netmask) { + if (!inet_pton(f, argv[1], &naddr)) error_exit("invalid netmask"); + if (f == AF_INET) { + uint32_t mask = htonl(*naddr), host = ~mask; + if (host & (host + 1)) error_exit("invalid netmask"); + for (plen = 0; mask; mask <<= 1) ++plen; + if (plen > 32) error_exit("invalid netmask"); + } + } + netmask = plen; + rtMsg->rtm_dst_len = netmask; + } else if (!strcmp(*argv, "gw")) { + if (!inet_pton(f, argv[1], &addr)) error_exit("invalid gw"); + addAttr(nlMsg, sizeof(toybuf), &addr, RTA_GATEWAY, addr_len); + } else if (!strcmp(*argv, "mss")) { + // TODO(emolitor): Add RTA_METRICS support + //set the TCP Maximum Segment Size for connections over this route. + //rt->rt_mtu = atolx_range(argv[1], 64, 65536); + //rt->rt_flags |= RTF_MSS; + } else if (!strcmp(*argv, "window")) { + // TODO(emolitor): Add RTA_METRICS support + //set the TCP window size for connections over this route to W bytes. + //rt->rt_window = atolx_range(argv[1], 128, INT_MAX); //win low + //rt->rt_flags |= RTF_WINDOW; + } else if (!strcmp(*argv, "irtt")) { + // TODO(emolitor): Add RTA_METRICS support + //rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX); + //rt->rt_flags |= RTF_IRTT; } else if (!strcmp(*argv, "dev")) { - if (!*dev_name) *dev_name = argv[1]; - } else help_exit(0); + unsigned int if_idx = if_nametoindex(argv[1]); + if (!if_idx) perror_exit("dev"); + addAttr(nlMsg, sizeof(toybuf), &if_idx, RTA_OIF, sizeof(unsigned int)); + } else help_exit("no '%s'", *argv); argv++; } } -} -// add/del a route. -static void setroute_inet6(char **argv) -{ - struct sockaddr_in6 sock_in6; - struct in6_rtmsg rt; - char *targetip, *dev_name = 0; - int sockfd, action = get_action(&argv, arglist1); + if (strcmp(tgtip, "default") != 0) { + char *prefix = strtok(0, "/"); - if (!action || !*argv) help_exit(0); - memset(&sock_in6, 0, sizeof(struct sockaddr_in6)); - memset(&rt, 0, sizeof(struct in6_rtmsg)); - targetip = *argv++; - if (!*argv) help_exit(0); + if (prefix) rtMsg->rtm_dst_len = strtoul(prefix, &prefix, 0); + if (!inet_pton(f, strtok(tgtip, "/"), &addr)) error_exit("invalid target"); + addAttr(nlMsg, sizeof(toybuf), &addr, RTA_DST, addr_len); + } else rtMsg->rtm_dst_len = 0; - if (!strcmp(targetip, "default")) { - rt.rtmsg_flags = RTF_UP; - rt.rtmsg_dst_len = 0; - } else { - is_prefix_inet6((char **)&targetip, &rt); - if (get_addrinfo(targetip, (struct sockaddr_in6 *) &sock_in6)) - perror_exit("resolving '%s'", targetip); - } - rt.rtmsg_metric = 1; //default metric. - memcpy(&rt.rtmsg_dst, sock_in6.sin6_addr.s6_addr, sizeof(struct in6_addr)); - get_next_params_inet6(argv, &sock_in6, &rt, (char **)&dev_name); - - sockfd = xsocket(AF_INET6, SOCK_DGRAM, 0); - if (dev_name) { - char ifre_buf[sizeof(struct ifreq)] = {0,}; - struct ifreq *ifre = (struct ifreq*)ifre_buf; - xstrncpy(ifre->ifr_name, dev_name, IFNAMSIZ); - xioctl(sockfd, SIOGIFINDEX, ifre); - rt.rtmsg_ifindex = ifre->ifr_ifindex; - } - if (action == 1) xioctl(sockfd, SIOCADDRT, &rt); - else xioctl(sockfd, SIOCDELRT, &rt); + xsend(sockfd, nlMsg, nlMsg->nlmsg_len); xclose(sockfd); } -/* - * format the dest and src address in ipv6 format. - * e.g. 2002:6b6d:26c8:d:ea03:9aff:fe65:9d62 - */ -static void ipv6_addr_formating(char *ptr, char *addr) -{ - int i = 0; - while (i <= IPV6_ADDR_LEN) { - if (!*ptr) { - if (i == IPV6_ADDR_LEN) { - addr[IPV6_ADDR_LEN - 1] = 0; //NULL terminating the ':' separated address. - break; - } - error_exit("IPv6 ip format error"); - } - addr[i++] = *ptr++; - if (!((i+1) % 5)) addr[i++] = ':'; //put ':' after 4th bit - } -} - -static void display_routes6(void) -{ - char iface[16] = {0,}, ipv6_dest_addr[41]; - char ipv6_src_addr[41], flag_val[10], buf2[INET6_ADDRSTRLEN]; - int prefixlen, metric, use, refcount, flag, items = 0; - unsigned char buf[sizeof(struct in6_addr)]; - - FILE *fp = xfopen("/proc/net/ipv6_route", "r"); - - xprintf("Kernel IPv6 routing table\n" - "%-43s%-40s Flags Metric Ref Use Iface\n", "Destination", "Next Hop"); - - while ((items = fscanf(fp, "%32s%x%*s%*x%32s%x%x%x%x%15s\n", ipv6_dest_addr+8, - &prefixlen, ipv6_src_addr+8, &metric, &use, &refcount, &flag, - iface)) == 8) - { - if (!(flag & RTF_UP)) continue; //skip down interfaces. - - //ipv6_dest_addr+8: as the values are filled from the 8th location of the array. - ipv6_addr_formating(ipv6_dest_addr+8, ipv6_dest_addr); - ipv6_addr_formating(ipv6_src_addr+8, ipv6_src_addr); - - get_flag_value(flag_val, flag); - if (inet_pton(AF_INET6, ipv6_dest_addr, buf) <= 0) perror_exit("inet"); - if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN)) - sprintf(toybuf, "%s/%d", buf2, prefixlen); - - if (inet_pton(AF_INET6, ipv6_src_addr, buf) <= 0) perror_exit("inet"); - if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN)) - xprintf("%-43s %-39s %-5s %-6d %-4d %5d %-8s\n", - toybuf, buf2, flag_val, metric, refcount, use, iface); - } - if ((items > 0) && feof(fp)) perror_exit("fscanf"); - - fclose(fp); -} - void route_main(void) { - if (!TT.family) TT.family = "inet"; if (!*toys.optargs) { - if (!strcmp(TT.family, "inet")) display_routes(); - else if (!strcmp(TT.family, "inet6")) display_routes6(); - else help_exit(0); + if (!TT.A || !strcmp(TT.A, "inet")) display_routes(AF_INET); + else if (!strcmp(TT.A, "inet6")) display_routes(AF_INET6); + else show_help(stdout, 1); } else { - if (!strcmp(TT.family, "inet6")) setroute_inet6(toys.optargs); - else setroute(toys.optargs); + if (!TT.A) { + if (toys.optc>1 && strchr(toys.optargs[1], ':')) { + xprintf("WARNING: Implicit IPV6 address using -Ainet6\n"); + TT.A = "inet6"; + } else TT.A = "inet"; + } + + if (!strcmp(TT.A, "inet")) setroute(AF_INET, toys.optargs); + else setroute(AF_INET6, toys.optargs); } } diff --git a/toys/pending/traceroute.c b/toys/pending/traceroute.c index 559c077..d3a9788 100644 --- a/toys/pending/traceroute.c +++ b/toys/pending/traceroute.c @@ -100,7 +100,7 @@ static u_int16_t in_cksum(u_int16_t *p, u_int len) u.c[1] = 0; sum += u.w; } - // end-around-carry + // end-around-carry sum = (sum >> 16) + (sum & 0xffff); sum += (sum >> 16); return (~sum); @@ -133,7 +133,7 @@ static void send_probe4(int seq, int ttl) if (res < 0) perror_exit("setsockopt ttl %d", ttl); len = TT.msg_len; - res = sendto(TT.snd_sock, out, len, 0, (struct sockaddr *) &dest, + res = sendto(TT.snd_sock, out, len, 0, (struct sockaddr *) &dest, sizeof(struct sockaddr_in)); if (res != len) perror_exit(" sendto"); } @@ -148,7 +148,7 @@ static void send_probe6(int seq, int ttl) send_data6->ident = TT.ident; ((struct sockaddr_in6 *)&dest)->sin6_port = TT.port; - if (setsockopt(TT.snd_sock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, + if (setsockopt(TT.snd_sock, SOL_IPV6, IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)) < 0) error_exit("setsockopt ttl %d", ttl); out = send_data6; @@ -225,12 +225,12 @@ static void do_trace() gettimeofday(&t1, NULL); t2 = t1; - while ((tleft = (int)(tv - ((unsigned long long)(t2.tv_sec * 1000ULL + while ((tleft = (int)(tv - ((unsigned long long)(t2.tv_sec * 1000ULL + t2.tv_usec/1000) - (unsigned long long)(t1.tv_sec * 1000ULL + t1.tv_usec/1000)))) >= 0) { unsigned delta = 0; - if (!(res = poll(pfd, 1, tleft))) { - xprintf(" *"); + if (!(res = poll(pfd, 1, tleft))) { + xprintf(" *"); break; } gettimeofday(&t2, NULL); @@ -276,28 +276,28 @@ static void do_trace() ricmp->icmp_code); } else { hicmp = (struct icmp *) ((char*)hip + (hip->ip_hl << 2)); - if (ricmp->icmp_type == ICMP_ECHOREPLY + if (ricmp->icmp_type == ICMP_ECHOREPLY && ricmp->icmp_id == ntohs(TT.ident) && ricmp->icmp_seq == ntohs(seq)) icmp_res = ICMP_UNREACH_PORT; - else if ((hip->ip_hl << 2) + ICMP_HD_SIZE4 + else if ((hip->ip_hl << 2) + ICMP_HD_SIZE4 <= (rcv_len - (rcv_pkt->ip_hl << 2)) && hip->ip_p == IPPROTO_ICMP && hicmp->icmp_id == htons(TT.ident) && hicmp->icmp_seq == htons(seq)) - icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 : + icmp_res = (ricmp->icmp_type == ICMP_TIMXCEED ? -1 : ricmp->icmp_code); } } if (!icmp_res) continue; if (addrlen > 0) { - if (memcmp(&((struct sockaddr_in *)&last_addr)->sin_addr, - &((struct sockaddr_in *)&from)->sin_addr, + if (memcmp(&((struct sockaddr_in *)&last_addr)->sin_addr, + &((struct sockaddr_in *)&from)->sin_addr, sizeof(struct in_addr))) { if (!(toys.optflags & FLAG_n)) { char host[NI_MAXHOST]; - if (!getnameinfo((struct sockaddr *) &from, + if (!getnameinfo((struct sockaddr *) &from, sizeof(struct sockaddr_in), host, NI_MAXHOST, NULL, 0, 0)) xprintf(" %s (", host); else xprintf(" %s (", inet_ntoa( @@ -359,7 +359,7 @@ static void do_trace() xprintf(" !C"); ++fexit; break; - case ICMP_UNREACH_NET_UNKNOWN: + case ICMP_UNREACH_NET_UNKNOWN: case ICMP_UNREACH_HOST_UNKNOWN: xprintf(" !U"); ++fexit; @@ -407,8 +407,8 @@ static void do_trace() if (!icmp_res) continue; if (addrlen > 0) { - if (memcmp(&((struct sockaddr_in6 *)&last_addr)->sin6_addr, - &((struct sockaddr_in6 *)&from)->sin6_addr, + if (memcmp(&((struct sockaddr_in6 *)&last_addr)->sin6_addr, + &((struct sockaddr_in6 *)&from)->sin6_addr, sizeof(struct in6_addr))) { if (!(toys.optflags & FLAG_n)) { char host[NI_MAXHOST]; @@ -467,7 +467,7 @@ static void do_trace() } xputc('\n'); if(!TT.istraceroute6) { - if (!memcmp(&((struct sockaddr_in *)&from)->sin_addr, + if (!memcmp(&((struct sockaddr_in *)&from)->sin_addr, &((struct sockaddr_in *)&dest)->sin_addr, sizeof(struct in_addr)) || dest_reach || (fexit && fexit >= TT.ttl_probes -1)) break; @@ -479,8 +479,8 @@ void traceroute_main(void) { unsigned pack_size = 0, tyser = 0; int lsrr = 0, set = 1; - - if(!(toys.optflags & FLAG_4) && + + if(!(toys.optflags & FLAG_4) && (inet_pton(AF_INET6, toys.optargs[0], &dest))) toys.optflags |= FLAG_6; @@ -511,29 +511,29 @@ void traceroute_main(void) if (TT.istraceroute6) { int two = 2; #ifdef IPV6_RECVPKTINFO - setsockopt(TT.recv_sock, SOL_IPV6, IPV6_RECVPKTINFO, &set, + setsockopt(TT.recv_sock, SOL_IPV6, IPV6_RECVPKTINFO, &set, sizeof(set)); - setsockopt(TT.recv_sock, SOL_IPV6, IPV6_2292PKTINFO, &set, + setsockopt(TT.recv_sock, SOL_IPV6, IPV6_2292PKTINFO, &set, sizeof(set)); #else setsockopt(TT.recv_sock, SOL_IPV6, IPV6_PKTINFO, &set, sizeof(set)); #endif - if (setsockopt(TT.recv_sock, SOL_RAW, IPV6_CHECKSUM, &two, + if (setsockopt(TT.recv_sock, SOL_RAW, IPV6_CHECKSUM, &two, sizeof(two)) < 0) perror_exit("setsockopt RAW_CHECKSUM"); } set_flag_dr(TT.recv_sock); if (!TT.istraceroute6) { - if (toys.optflags & FLAG_U) + if (toys.optflags & FLAG_U) TT.snd_sock = xsocket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); else TT.snd_sock = xsocket(AF_INET, SOCK_RAW, IPPROTO_ICMP); if (toys.optflags & FLAG_i) bind_to_interface(TT.snd_sock); - resolve_addr(toys.optargs[0], AF_INET, ((toys.optflags & FLAG_U) ? - SOCK_DGRAM : SOCK_RAW), ((toys.optflags & FLAG_U) ? IPPROTO_UDP : + resolve_addr(toys.optargs[0], AF_INET, ((toys.optflags & FLAG_U) ? + SOCK_DGRAM : SOCK_RAW), ((toys.optflags & FLAG_U) ? IPPROTO_UDP : IPPROTO_ICMP), &dest); if (lsrr > 0) { unsigned char optlist[MAX_IPOPTLEN]; @@ -543,7 +543,7 @@ void traceroute_main(void) ++lsrr; optlist[0] = IPOPT_NOP; - optlist[1] = IPOPT_LSRR;// loose source route option + optlist[1] = IPOPT_LSRR;// loose source route option size = lsrr * sizeof(TT.gw_list[0]); optlist[2] = size + 3; optlist[3] = IPOPT_MINOFF; @@ -555,17 +555,17 @@ void traceroute_main(void) } } else TT.snd_sock = xsocket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP); - if (setsockopt(TT.snd_sock, SOL_SOCKET, SO_SNDBUF, &TT.msg_len, + if (setsockopt(TT.snd_sock, SOL_SOCKET, SO_SNDBUF, &TT.msg_len, sizeof(TT.msg_len)) < 0) perror_exit("SO_SNDBUF failed "); if (!TT.istraceroute6) { - if ((toys.optflags & FLAG_t) && + if ((toys.optflags & FLAG_t) && setsockopt(TT.snd_sock, IPPROTO_IP, IP_TOS, &tyser, sizeof(tyser)) < 0) perror_exit("IP_TOS %ld failed ", TT.tos); #ifdef IP_DONTFRAG if ((toys.optflags & FLAG_F) && - (setsockopt(TT.snd_sock, IPPROTO_IP, IP_DONTFRAG, &set, + (setsockopt(TT.snd_sock, IPPROTO_IP, IP_DONTFRAG, &set, sizeof(set)) < 0)) perror_exit("IP_DONTFRAG failed "); #endif } else if (setsockopt(TT.snd_sock, IPPROTO_IPV6, IPV6_TCLASS, &TT.tos, @@ -593,10 +593,10 @@ void traceroute_main(void) xbind(TT.snd_sock,(struct sockaddr*)&source, sizeof(struct sockaddr_in)); } - if(TT.first_ttl > TT.max_ttl) + if(TT.first_ttl > TT.max_ttl) error_exit("ERROR :Range for -f is 1 to %ld (max ttl)", TT.max_ttl); - xprintf("traceroute to %s(%s)", toys.optargs[0], + xprintf("traceroute to %s(%s)", toys.optargs[0], inet_ntoa(((struct sockaddr_in *)&dest)->sin_addr)); } else { if (toys.optflags & FLAG_i) bind_to_interface(TT.snd_sock); @@ -622,7 +622,7 @@ void traceroute_main(void) ((struct sockaddr_in6 *)&dest)->sin6_port = htons(1025); xconnect(p_fd, (struct sockaddr *)&dest, sizeof(struct sockaddr_in6)); - if(getsockname(p_fd, (struct sockaddr *)&prb, &len)) + if(getsockname(p_fd, (struct sockaddr *)&prb, &len)) error_exit("probe addr failed"); close(p_fd); prb.sin6_port = 0; @@ -634,7 +634,7 @@ void traceroute_main(void) xbind(TT.recv_sock, (struct sockaddr*)&prb, sizeof(struct sockaddr_in6)); } - inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&dest)->sin6_addr, + inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&dest)->sin6_addr, addr_str, INET6_ADDRSTRLEN); xprintf("traceroute6 to %s(%s)", toys.optargs[0], addr_str); } diff --git a/toys/posix/cpio.c b/toys/posix/cpio.c index 90a2e2b..321bbfd 100644 --- a/toys/posix/cpio.c +++ b/toys/posix/cpio.c @@ -13,35 +13,40 @@ * In order: magic ino mode uid gid nlink mtime filesize devmajor devminor * rdevmajor rdevminor namesize check * This is the equivalent of mode -H newc in other implementations. + * We always do --quiet, but accept it as a compatibility NOP. * - * todo: export/import linux file list text format ala gen_initramfs_list.sh + * TODO: export/import linux file list text format ala gen_initramfs_list.sh + * TODO: hardlink support, -A, -0, -a, -L, --sparse + * TODO: --renumber-archives (probably always?) --ignore-devno --reproducible -USE_CPIO(NEWTOY(cpio, "(no-preserve-owner)(trailer)mduH:p:|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN)) +USE_CPIO(NEWTOY(cpio, "(ignore-devno)(renumber-inodes)(quiet)(no-preserve-owner)R(owner):md(make-directories)uLH:p|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN)) config CPIO bool "cpio" default y help - usage: cpio -{o|t|i|p DEST} [-v] [--verbose] [-F FILE] [--no-preserve-owner] - [ignored: -mdu -H newc] + usage: cpio -{o|t|i|p DEST} [-dLtuv] [--verbose] [-F FILE] [-R [USER][:GROUP] [--no-preserve-owner] Copy files into and out of a "newc" format cpio archive. + -d Create directories if needed -F FILE Use archive FILE instead of stdin/stdout - -p DEST Copy-pass mode, copy stdin file list to directory DEST -i Extract from archive into file system (stdin=archive) + -L Follow symlinks -o Create archive (stdin=list of files, stdout=archive) + -p DEST Copy-pass mode, copy stdin file list to directory DEST + -R USER Replace owner with USER[:GROUP] -t Test files (list only, stdin=archive, stdout=list of files) + -u Unlink existing files when extracting -v Verbose - --no-preserve-owner (don't set ownership during extract) - --trailer Add legacy trailer (prevents concatenation) + --no-preserve-owner Don't set ownership during extract */ #define FOR_cpio #include "toys.h" GLOBALS( - char *F, *p, *H; + char *F, *H, *R; ) // Read strings, tail padded to 4 byte alignment. Argument "align" is amount @@ -72,20 +77,37 @@ static unsigned x8u(char *hex) // Because scanf gratuitously treats %*X differently than printf does. sprintf(pattern, "%%%dX%%n", inpos); sscanf(hex, pattern, &val, &outpos); - if (inpos != outpos) error_exit("bad header"); + if (inpos != outpos) error_exit("bad hex"); return val; } void cpio_main(void) { - // Subtle bit: FLAG_o is 1 so we can just use it to select stdin/stdout. - int pipe, afd = toys.optflags & FLAG_o; + int pipe, afd = FLAG(o), reown = !geteuid() && !FLAG(no_preserve_owner), + empty = 1; pid_t pid = 0; + long Ruid = -1, Rgid = -1; + char *tofree = 0; + + if (TT.R) { + char *group = TT.R+strcspn(TT.R, ":."); + + if (*group) { + Rgid = xgetgid(group+1); + *group = 0; + } + if (group != TT.R) Ruid = xgetuid(TT.R); + } // In passthrough mode, parent stays in original dir and generates archive // to pipe, child does chdir to new dir and reads archive from stdin (pipe). - if (TT.p) { + if (FLAG(p)) { + if (FLAG(d)) { + if (!*toys.optargs) error_exit("need directory for -p"); + if (mkdir(*toys.optargs, 0700) == -1 && errno != EEXIST) + perror_msg("mkdir %s", *toys.optargs); + } if (toys.stacktop) { // xpopen() doesn't return from child due to vfork(), instead restarts // with !toys.stacktop @@ -94,31 +116,44 @@ void cpio_main(void) } else { // child toys.optflags |= FLAG_i; - xchdir(TT.p); + xchdir(*toys.optargs); } } if (TT.F) { - int perm = (toys.optflags & FLAG_o) ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY; + int perm = FLAG(o) ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY; afd = xcreate(TT.F, perm, 0644); } // read cpio archive - if (toys.optflags & (FLAG_i|FLAG_t)) for (;;) { - char *name, *tofree, *data; - unsigned size, mode, uid, gid, timestamp; - int test = toys.optflags & FLAG_t, err = 0; + if (FLAG(i) || FLAG(t)) for (;; empty = 0) { + char *name, *data; + unsigned mode, uid, gid, timestamp; + int test = FLAG(t), err = 0, size = 0, len; - // Read header and name. - if (!(size =readall(afd, toybuf, 110))) break; - if (size != 110 || memcmp(toybuf, "070701", 6)) error_exit("bad header"); - tofree = name = strpad(afd, x8u(toybuf+94), 110); - if (!strcmp("TRAILER!!!", name)) { - if (CFG_TOYBOX_FREE) free(tofree); - break; + free(tofree); + tofree = 0; + // read header, skipping arbitrary leading NUL bytes (concatenated archives) + for (;;) { + if (1>(len = readall(afd, toybuf+size, 110-size))) break; + if (size || *toybuf) { + size += len; + break; + } + for (size = 0; size=0) ? Ruid : x8u(toybuf+22); + gid = (Rgid>=0) ? Rgid : x8u(toybuf+30); timestamp = x8u(toybuf+46); // unsigned 32 bit, so year 2100 problem - if (toys.optflags & (FLAG_t|FLAG_v)) puts(name); + // (This output is unaffected by --quiet.) + if (FLAG(t) || FLAG(v)) puts(name); - if (!test && strrchr(name, '/') && mkpath(name)) { + if (FLAG(u) && !test) if (unlink(name) && errno == EISDIR) rmdir(name); + + if (!test && FLAG(d) && strrchr(name, '/') && mkpath(name)) { perror_msg("mkpath '%s'", name); test++; } @@ -141,16 +179,25 @@ void cpio_main(void) // properly aligned with next file. if (S_ISDIR(mode)) { - if (!test) err = mkdir(name, mode); - } else if (S_ISLNK(mode)) { - data = strpad(afd, size, 0); - if (!test) err = symlink(data, name); - free(data); - // Can't get a filehandle to a symlink, so do special chown - if (!err && !geteuid() && !(toys.optflags & FLAG_no_preserve_owner)) - err = lchown(name, uid, gid); + if (test) continue; + err = mkdir(name, mode) && (errno != EEXIST && !FLAG(u)); + + // Creading dir/dev doesn't give us a filehandle, we have to refer to it + // by name to chown/utime, but how do we know it's the same item? + // Check that we at least have the right type of entity open, and do + // NOT restore dropped suid bit in this case. + if (S_ISDIR(mode) && reown) { + int fd = open(name, O_RDONLY|O_NOFOLLOW); + struct stat st; + + if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT)) + err = fchown(fd, uid, gid); + else err = 1; + + close(fd); + } } else if (S_ISREG(mode)) { - int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, mode); + int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, mode); // If write fails, we still need to read/discard data to continue with // archive. Since doing so overwrites errno, report error now @@ -173,73 +220,71 @@ void cpio_main(void) if (!test) { // set owner, restore dropped suid bit - if (!geteuid() && !(toys.optflags & FLAG_no_preserve_owner)) { - err = fchown(fd, uid, gid); - if (!err) err = fchmod(fd, mode); - } + if (reown) err = fchown(fd, uid, gid) && fchmod(fd, mode); close(fd); } - } else if (!test) - err = mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86))); + } else { + data = S_ISLNK(mode) ? strpad(afd, size, 0) : 0; + if (!test) { + err = data ? symlink(data, name) + : mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86))); - // Set ownership and timestamp. + // Can't get a filehandle to a symlink or a node on nodev mount, + // so do special chown that at least doesn't follow symlinks. + // We also don't chmod after, so dropped suid bit isn't restored + if (!err && reown) err = lchown(name, uid, gid); + } + free(data); + } + + // Set timestamp. if (!test && !err) { - // Creading dir/dev doesn't give us a filehandle, we have to refer to it - // by name to chown/utime, but how do we know it's the same item? - // Check that we at least have the right type of entity open, and do - // NOT restore dropped suid bit in this case. - if (!S_ISREG(mode) && !S_ISLNK(mode) && !geteuid() - && !(toys.optflags & FLAG_no_preserve_owner)) - { - int fd = open(name, O_RDONLY|O_NOFOLLOW); - struct stat st; + struct timespec times[2]; - if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT)) - err = fchown(fd, uid, gid); - else err = 1; - - close(fd); - } - - // set timestamp - if (!err) { - struct timespec times[2]; - - memset(times, 0, sizeof(struct timespec)*2); - times[0].tv_sec = times[1].tv_sec = timestamp; - err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW); - } + memset(times, 0, sizeof(struct timespec)*2); + times[0].tv_sec = times[1].tv_sec = timestamp; + err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW); } if (err) perror_msg_raw(name); - free(tofree); // Output cpio archive } else { char *name = 0; size_t size = 0; + unsigned inode = 0; for (;;) { struct stat st; unsigned nlen, error = 0, zero = 0; int len, fd = -1; + char *link = 0; ssize_t llen; len = getline(&name, &size, stdin); if (len<1) break; if (name[len-1] == '\n') name[--len] = 0; + if (!len) continue; nlen = len+1; - if (lstat(name, &st) || (S_ISREG(st.st_mode) - && st.st_size && (fd = open(name, O_RDONLY))<0)) + if ((FLAG(L)?stat:lstat)(name, &st) || (S_ISREG(st.st_mode) + && st.st_size && (fd = open(name, O_RDONLY))<0) + || (S_ISLNK(st.st_mode) && !(link = xreadlink(name)))) { perror_msg_raw(name); continue; } + // encrypted filesystems can stat the wrong link size + if (link) st.st_size = strlen(link); + if (Ruid>=0) st.st_uid = Ruid; + if (Rgid>=0) st.st_gid = Rgid; + if (FLAG(no_preserve_owner)) st.st_uid = st.st_gid = 0; if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) st.st_size = 0; if (st.st_size >> 32) perror_msg("skipping >2G file '%s'", name); else { + if (FLAG(renumber_inodes)) st.st_ino = ++inode; + if (FLAG(ignore_devno)) st.st_rdev = 0; llen = sprintf(toybuf, "070701%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X", (int)st.st_ino, st.st_mode, st.st_uid, st.st_gid, (int)st.st_nlink, @@ -254,14 +299,9 @@ void cpio_main(void) if (llen) xwrite(afd, &zero, 4-llen); // Write out body for symlink or regular file - llen = st.st_size; - if (S_ISLNK(st.st_mode)) { - if (readlink(name, toybuf, sizeof(toybuf)-1) == llen) - xwrite(afd, toybuf, llen); - else perror_msg("readlink '%s'", name); - } else while (llen) { + if (link) xwrite(afd, link, st.st_size); + else for (llen = st.st_size; llen; llen -= nlen) { nlen = llen > sizeof(toybuf) ? sizeof(toybuf) : llen; - llen -= nlen; // If read fails, write anyway (already wrote size in header) if (nlen != readall(fd, toybuf, nlen)) if (!error++) perror_msg("bad read from file '%s'", name); @@ -270,17 +310,15 @@ void cpio_main(void) llen = st.st_size & 3; if (llen) xwrite(afd, &zero, 4-llen); } - close(fd); + free(link); + xclose(fd); } - free(name); + if (CFG_TOYBOX_FREE) free(name); - if (FLAG(trailer)) { - memset(toybuf, 0, sizeof(toybuf)); - xwrite(afd, toybuf, - sprintf(toybuf, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0)+4); - } + // nlink=1, namesize=11, with padding + dprintf(afd, "070701%040X%056X%08XTRAILER!!!%c%c%c%c", 1, 11, 0, 0, 0, 0,0); } if (TT.F) xclose(afd); - if (TT.p) toys.exitval |= xpclose(pid, pipe); + if (FLAG(p) && pid) toys.exitval |= xpclose(pid, pipe); } diff --git a/toys/posix/patch.c b/toys/posix/patch.c index 3234a1f..b66d590 100644 --- a/toys/posix/patch.c +++ b/toys/posix/patch.c @@ -8,40 +8,39 @@ * TODO: * -b backup * -N ignore already applied - * -d chdir first * -D define wrap #ifdef and #ifndef around changes * -o outfile output here instead of in place * -r rejectfile write rejected hunks to this file - * * -E remove empty files --remove-empty-files - * -F fuzz (number, default 2) - * [file] which file to patch + * git syntax (rename, etc) -USE_PATCH(NEWTOY(patch, "(no-backup-if-mismatch)(dry-run)"USE_TOYBOX_DEBUG("x")"g#fulp#d:i:Rs(quiet)", TOYFLAG_USR|TOYFLAG_BIN)) +USE_PATCH(NEWTOY(patch, ">2(no-backup-if-mismatch)(dry-run)F#g#fulp#v(verbose)@d:i:Rs(quiet)[!sv]", TOYFLAG_USR|TOYFLAG_BIN)) config PATCH bool "patch" default y help - usage: patch [-d DIR] [-i file] [-p depth] [-Rlsu] [--dry-run] + usage: patch [-Rlsuv] [-d DIR] [-i FILE] [-p DEPTH] [-F FUZZ] [--dry-run] [FILE [PATCH]] Apply a unified diff to one or more files. -d Modify files in DIR - -i Input file (default=stdin) + -F Fuzz factor (number of non-matching context lines allowed per hunk) + -i Input patch from FILE (default=stdin) -l Loose match (ignore whitespace) -p Number of '/' to strip from start of file paths (default=all) -R Reverse patch -s Silent except for errors - -u Ignored (only handles "unified" diffs) + -v Verbose (-vv to see decisions) --dry-run Don't change files, just confirm patch applies - This version of patch only handles unified diffs, and only modifies - a file when all hunks to that file apply. Patch prints failed hunks + Only handles "unified" diff format (-u is assumed and ignored). Only + modifies files when all hunks to that file apply. Prints failed hunks to stderr, and exits with nonzero status if any hunks fail. - A file compared against /dev/null (or with a date <= the epoch) is - created/deleted as appropriate. + Files compared against /dev/null (or with a date <= the unix epoch) are + created/deleted as appropriate. Default -F value is the number of + leading/trailing context lines minus one (usually 2). */ #define FOR_patch @@ -49,15 +48,33 @@ config PATCH GLOBALS( char *i, *d; - long p, g; + long v, p, g, F; - struct double_list *current_hunk; - long oldline, oldlen, newline, newlen; - long linenum; + void *current_hunk; + long oldline, oldlen, newline, newlen, linenum, outnum; int context, state, filein, fileout, filepatch, hunknum; char *tempname; ) +// TODO xgetline() instead, but replace_tempfile() wants fd... +char *get_line(int fd) +{ + char c, *buf = 0; + long len = 0; + + for (;;) { + if (1>read(fd, &c, 1)) break; + if (!(len & 63)) buf=xrealloc(buf, len+65); + if ((buf[len++]=c) == '\n') break; + } + if (buf) { + buf[len]=0; + if (buf[--len]=='\n') buf[len]=0; + } + + return buf; +} + // Dispose of a line of input, either by writing it out or discarding it. // state < 2: just free @@ -67,20 +84,14 @@ GLOBALS( static void do_line(void *data) { - struct double_list *dlist = (struct double_list *)data; + struct double_list *dlist = data; - if (TT.state>1 && *dlist->data != TT.state) { - char *s = dlist->data+(TT.state>3); - int i = TT.state == 2 ? 2 : TT.fileout; + TT.outnum++; + if (TT.state>1) + if (0>dprintf(TT.state==2 ? 2 : TT.fileout,"%s\n",dlist->data+(TT.state>3))) + perror_exit("write"); - xwrite(i, s, strlen(s)); - xwrite(i, "\n", 1); - } - - if (FLAG(x)) fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data); - - free(dlist->data); - free(data); + llist_free_double(data); } static void finish_oldfile(void) @@ -102,7 +113,7 @@ static void fail_hunk(void) TT.state = 2; llist_traverse(TT.current_hunk, do_line); - TT.current_hunk = NULL; + TT.current_hunk = 0; if (!FLAG(dry_run)) delete_tempfile(TT.filein, TT.fileout, &TT.tempname); TT.state = 0; } @@ -123,156 +134,168 @@ static int loosecmp(char *aa, char *bb) // Given a hunk of a unified diff, make the appropriate change to the file. // This does not use the location information, but instead treats a hunk -// as a sort of regex. Copies data from input to output until it finds +// as a sort of regex. Copies data from input to output until it finds // the change to be made, then outputs the changed data and returns. -// (Finding EOF first is an error.) This is a single pass operation, so +// (Finding EOF first is an error.) This is a single pass operation, so // multiple hunks must occur in order in the file. static int apply_one_hunk(void) { - struct double_list *plist, *buf = NULL, *check; - int matcheof, trailing = 0, reverse = FLAG(R), backwarn = 0; - int (*lcmp)(char *aa, char *bb); + struct double_list *plist, *buf = 0, *check = 0; + int matcheof, trail = 0, allfuzz = 0, fuzz, ii; + int (*lcmp)(char *aa, char *bb) = FLAG(l) ? (void *)loosecmp : (void *)strcmp; + long backwarn = 0; + char *data = toybuf; - lcmp = FLAG(l) ? (void *)loosecmp : (void *)strcmp; - dlist_terminate(TT.current_hunk); + if (TT.v>1) printf("START %d\n", TT.hunknum); // Match EOF if there aren't as many ending context lines as beginning - for (plist = TT.current_hunk; plist; plist = plist->next) { - if (plist->data[0]==' ') trailing++; - else trailing = 0; - if (FLAG(x)) fprintf(stderr, "HUNK:%s\n", plist->data); - } - matcheof = !trailing || trailing < TT.context; + dlist_terminate(TT.current_hunk); + for (fuzz = 0, plist = TT.current_hunk; plist; plist = plist->next) { + char *s = plist->data, c = *s; - if (FLAG(x)) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N'); + if (c==' ') trail++; + else trail = 0; + + // Only allow fuzz if 2 context lines have multiple nonwhitespace chars. + // avoids the "all context was blank or } lines" issue. Removed lines + // count as context since they're matched. + if (c==' ' || c=="-+"[FLAG(R)]) { + while (isspace(*++s)); + if (*s && s[1] && !isspace(s[1])) fuzz++; + } + } + matcheof = !trail || trail < TT.context; + if (FLAG(F) && !TT.F) fuzz = 0; + if (fuzz>1) allfuzz = TT.F ? : TT.context ? TT.context-1 : 0; // Loop through input data searching for this hunk. Match all context - // lines and all lines to be removed until we've found the end of a - // complete hunk. + // lines and lines to be removed until we've found end of complete hunk. plist = TT.current_hunk; - buf = NULL; - + fuzz = 0; for (;;) { - char *data = get_line(TT.filein); - - TT.linenum++; - // Figure out which line of hunk to compare with next. (Skip lines - // of the hunk we'd be adding.) - while (plist && *plist->data == "+-"[reverse]) { - if (data && !lcmp(data, plist->data+1)) - if (!backwarn) backwarn = TT.linenum; - plist = plist->next; + if (data) { + data = get_line(TT.filein); + check = data ? dlist_add(&buf, data) : 0; + TT.linenum++; } + if (TT.v>1) printf("READ[%ld] %s\n", TT.linenum, data ? : "(NULL)"); - // Is this EOF? - if (!data) { - if (FLAG(x)) fprintf(stderr, "INEOF\n"); - - // Does this hunk need to match EOF? - if (!plist && matcheof) break; - - if (backwarn && !FLAG(s)) - fprintf(stderr, "Possibly reversed hunk %d at %ld\n", - TT.hunknum, TT.linenum); - - // File ended before we found a place for this hunk. - fail_hunk(); - goto done; - } else if (FLAG(x)) fprintf(stderr, "IN: %s\n", data); - check = dlist_add(&buf, data); - - // Compare this line with next expected line of hunk. - - // A match can fail because the next line doesn't match, or because - // we hit the end of a hunk that needed EOF, and this isn't EOF. - - // If match failed, flush first line of buffered data and - // recheck buffered data for a new match until we find one or run - // out of buffer. - + // Compare buffered line(s) with expected lines of hunk. Match can fail + // because next line doesn't match, or because we hit end of a hunk that + // needed EOF and this isn't EOF. for (;;) { - if (!plist || lcmp(check->data, plist->data+1)) { - // Match failed. Write out first line of buffered data and - // recheck remaining buffered data for a new match. - - if (FLAG(x)) { - int bug = 0; - - if (!plist) fprintf(stderr, "NULL plist\n"); - else { - while (plist->data[bug] == check->data[bug]) bug++; - fprintf(stderr, "NOT(%d:%d!=%d): %s\n", bug, plist->data[bug], - check->data[bug], plist->data); - } + // Find hunk line to match (skip added lines) and detect reverse matches + while (plist && *plist->data == "+-"[FLAG(R)]) { + // TODO: proper backwarn = full hunk applies in reverse, not just 1 line + if (data) { + ii = strcspn(data, " \t"); + if (data[ii+!!data[ii]] && !lcmp(data, plist->data+1)) + backwarn = TT.linenum; } - - // If this hunk must match start of file, fail if it didn't. - if (!TT.context || trailing>TT.context) { - fail_hunk(); - goto done; - } - - TT.state = 3; - do_line(check = dlist_pop(&buf)); - plist = TT.current_hunk; - - // If we've reached the end of the buffer without confirming a - // match, read more lines. - if (!buf) break; - check = buf; - } else { - if (FLAG(x)) fprintf(stderr, "MAYBE: %s\n", plist->data); - // This line matches. Advance plist, detect successful match. plist = plist->next; - if (!plist && !matcheof) goto out; - check = check->next; - if (check == buf) break; } + if (TT.v>1 && plist) + printf("HUNK %s\nLINE %s\n", plist->data+1, check ? check->data : ""); + + // End of hunk? + if (!plist) { + if (TT.v>1) printf("END OF HUNK\n"); + if (matcheof == !data) goto out; + + // Compare line and handle match + } else if (check && !lcmp(check->data, plist->data+1)) { + if (TT.v>1) printf("MATCH\n"); +handle_match: + plist = plist->next; + if ((check = check->next) == buf) { + if (plist || matcheof) break; + goto out; + } else continue; + } + + // Did we hit EOF? + if (!data) { + if (TT.v>1) printf("EOF\n"); + if (backwarn && !FLAG(s)) + fprintf(stderr, "Possibly reversed hunk %d at %ld\n", + TT.hunknum, backwarn); + + // File ended before we found a place for this hunk. + fail_hunk(); + goto done; + } + if (TT.v>1) printf("NOT MATCH\n"); + + // Match failed: can we fuzz it? + if (plist && *plist->data == ' ' && fuzz1) printf("FUZZ %d %s\n", fuzz, check->data); + goto handle_match; + } + + // If this hunk must match start of file, fail if it didn't. + if (!TT.context || trail>TT.context) { + fail_hunk(); + goto done; + } + + // Write out first line of buffer and recheck rest for new match. + TT.state = 3; + if (TT.v>1) printf("WRITE %s\n", buf->data); + do_line(check = dlist_pop(&buf)); + plist = TT.current_hunk; + fuzz = 0; + + // If end of the buffer without finishing a match, read more lines. + if (!buf) break; + check = buf; } } out: + if (TT.v) xprintf("Hunk #%d succeeded at %ld.\n", TT.hunknum, TT.linenum); // We have a match. Emit changed data. - TT.state = "-+"[reverse]; - llist_traverse(TT.current_hunk, do_line); - TT.current_hunk = NULL; + TT.state = "-+"[FLAG(R)]; + while ((plist = dlist_pop(&TT.current_hunk))) { + if (TT.state == *plist->data || *plist->data == ' ') { + if (*plist->data == ' ') dprintf(TT.fileout, "%s\n", buf->data); + llist_free_double(dlist_pop(&buf)); + } else dprintf(TT.fileout, "%s\n", plist->data+1); + llist_free_double(plist); + } + TT.current_hunk = 0; TT.state = 1; done: - if (buf) { - dlist_terminate(buf); - llist_traverse(buf, do_line); - } + llist_traverse(buf, do_line); return TT.state; } // read a filename that has been quoted or escaped -char *unquote_file(char *filename) { - char *s = filename, *result, *t, *u; - int quote = 0, ch; +static char *unquote_file(char *filename) +{ + char *s = filename, *t, *newfile; + + // Return copy of file that wasn't quoted + if (*s++ != '"' || !*s) return xstrdup(filename); // quoted and escaped filenames are larger than the original - result = xmalloc(strlen(filename) + 1); - t = result; - if (*s == '"') { - s++; - quote = 1; - } - for (; *s && !(quote && *s == '"' && !s[1]); s++) { + for (t = newfile = xmalloc(strlen(s) + 1); *s != '"'; s++) { + if (!s[1]) error_exit("bad %s", filename); + // don't accept escape sequences unless the filename is quoted - if (quote && *s == '\\' && s[1]) { - if (s[1] >= '0' && s[1] < '8') { - *t++ = strtoul(s + 1, &u, 8); - s = u - 1; - } else { - ch = unescape(s[1]); - *t++ = ch ? ch : s[1]; - s++; - } - } else *t++ = *s; + if (*s != '\\') *t++ = *s; + else if (*++s >= '0' && *s < '8') { + *t++ = strtoul(s, &s, 8); + s--; + } else { + if (!(*t = unescape(*s))) *t = *s;; + t++; + } } *t = 0; - return result; + + return newfile; } // Read a patch file and find hunks, opening/creating/deleting files. @@ -285,28 +308,28 @@ char *unquote_file(char *filename) { void patch_main(void) { - int reverse = FLAG(R), state = 0, patchlinenum = 0, strip = 0; - char *oldname = NULL, *newname = NULL; + int state = 0, patchlinenum = 0, strip = 0; + char *oldname = 0, *newname = 0; + if (toys.optc == 2) TT.i = toys.optargs[1]; if (TT.i) TT.filepatch = xopenro(TT.i); TT.filein = TT.fileout = -1; if (TT.d) xchdir(TT.d); - // Loop through the lines in the patch + // Loop through the lines in the patch file (-i or stdin) collecting hunks for (;;) { char *patchline; - patchline = get_line(TT.filepatch); - if (!patchline) break; + if (!(patchline = get_line(TT.filepatch))) break; // Other versions of patch accept damaged patches, so we need to also. if (strip || !patchlinenum++) { int len = strlen(patchline); - if (patchline[len-1] == '\r') { + if (len && patchline[len-1] == '\r') { if (!strip && !FLAG(s)) fprintf(stderr, "Removing DOS newlines\n"); strip = 1; - patchline[len-1]=0; + patchline[len-1] = 0; } } if (!*patchline) { @@ -317,7 +340,7 @@ void patch_main(void) // Are we assembling a hunk? if (state >= 2) { if (*patchline==' ' || *patchline=='+' || *patchline=='-') { - dlist_add(&TT.current_hunk, patchline); + dlist_add((void *)&TT.current_hunk, patchline); if (*patchline != '+') TT.oldlen--; if (*patchline != '-') TT.newlen--; @@ -328,11 +351,11 @@ void patch_main(void) // If we've consumed all expected hunk lines, apply the hunk. if (!TT.oldlen && !TT.newlen) state = apply_one_hunk(); - continue; + } else { + dlist_terminate(TT.current_hunk); + fail_hunk(); + state = 0; } - dlist_terminate(TT.current_hunk); - fail_hunk(); - state = 0; continue; } @@ -349,7 +372,7 @@ void patch_main(void) free(*name); finish_oldfile(); - // Trim date from end of filename (if any). We don't care. + // Trim date from end of filename (if any). Date<=epoch means delete. for (s = patchline+4; *s && *s!='\t'; s++); i = atoi(s); if (i>1900 && i<=1970) *name = xstrdup("/dev/null"); @@ -388,13 +411,24 @@ void patch_main(void) oldsum = TT.oldline + TT.oldlen; newsum = TT.newline + TT.newlen; - name = reverse ? oldname : newname; + // If an original file was provided on the command line, it overrides + // *all* files mentioned in the patch, not just the first. + if (toys.optc) { + char **which = FLAG(R) ? &oldname : &newname; + + free(*which); + *which = xstrdup(toys.optargs[0]); + // The supplied path should be taken literally with or without -p. + toys.optflags |= FLAG_p; + TT.p = 0; + } + + name = FLAG(R) ? oldname : newname; // We're deleting oldname if new file is /dev/null (before -p) // or if new hunk is empty (zero context) after patching - if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) - { - name = reverse ? newname : oldname; + if (!strcmp(name, "/dev/null") || !(FLAG(R) ? oldsum : newsum)) { + name = FLAG(R) ? newname : oldname; del++; } @@ -409,7 +443,7 @@ void patch_main(void) if (del) { if (!FLAG(s)) printf("removing %s\n", name); - xunlink(name); + if (!FLAG(dry_run)) xunlink(name); state = 0; // If we've got a file to open, do so. } else if (!FLAG(p) || i <= TT.p) { @@ -417,16 +451,18 @@ void patch_main(void) if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK)) { if (!FLAG(s)) printf("creating %s\n", name); - if (mkpath(name)) perror_exit("mkpath %s", name); - TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666); + if (FLAG(dry_run)) TT.filein = xopen("/dev/null", O_RDWR); + else { + if (mkpath(name)) perror_exit("mkpath %s", name); + TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666); + } } else { if (!FLAG(s)) printf("patching %s\n", name); TT.filein = xopenro(name); } if (FLAG(dry_run)) TT.fileout = xopen("/dev/null", O_RDWR); else TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname); - TT.linenum = 0; - TT.hunknum = 0; + TT.linenum = TT.outnum = TT.hunknum = 0; } } diff --git a/toys/posix/ps.c b/toys/posix/ps.c index 78925f8..efe8160 100644 --- a/toys/posix/ps.c +++ b/toys/posix/ps.c @@ -557,7 +557,7 @@ static char *string_field(struct procpid *tb, struct ofields *field) // Clock displays } else if (which <= PS_TIME_) { - int unit = 60, pad = 2, j = TT.ticks; + int unit = 60, pad = 2, j = TT.ticks; time_t seconds; if (which!=PS_TIME_) unit *= 60*24; @@ -620,7 +620,7 @@ static char *string_field(struct procpid *tb, struct ofields *field) if (slot[SLOT_sid]==*slot) *s++ = 's'; if (slot[SLOT_vmlck]) *s++ = 'L'; if (slot[SLOT_ttypgrp]==*slot) *s++ = '+'; - } + } *s = 0; } else if (which==PS_STIME) { time_t t = time(0)-slot[SLOT_uptime]+slot[SLOT_starttime]/TT.ticks; @@ -1284,7 +1284,7 @@ static int ksort(void *aa, void *bb) // Collect ->extra field from leaf nodes DIRTREE_SAVEd by get_ps() into array // (recursion because tree from get_thread() isn't flat list of siblings) -static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt) +static struct procpid **collate_leaves(struct procpid **tb, struct dirtree *dt) { while (dt) { struct dirtree *next = dt->next; @@ -1307,7 +1307,7 @@ static struct procpid **collate(int count, struct dirtree *dt) collate_leaves(tbsort, dt); return tbsort; -} +} // parse command line arguments (ala -k -o) with a comma separated FIELD list static void default_ko(char *s, void *fields, char *err, struct arg_list *arg) @@ -1660,10 +1660,10 @@ static void top_common( /* fix "iotop -m 10" show 13 lines problem*/ if (TT.top.m) { if (toys.which->name[0] == 'i') { - // iotop 鍛戒护 + // iotop 命令 TT.height = TT.top.m + 2; } else { - // top 鍛戒护 + // top 命令 TT.height = TT.top.m + 5; } } diff --git a/toys/posix/sort.c b/toys/posix/sort.c index 4b3fe24..d69b622 100644 --- a/toys/posix/sort.c +++ b/toys/posix/sort.c @@ -7,40 +7,42 @@ * Deviations from POSIX: Lots. * We invented -x -USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) +USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMCcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) config SORT bool "sort" default y help - usage: sort [-runbcdfiMsz] [FILE...] [-k#[,#[x]] [-t X]] [-o FILE] + usage: sort [-runbCcdfiMsxVz] [FILE...] [-k#[,#[x]] [-t X]] [-o FILE] Sort all lines of text from input files (or stdin) to stdout. -r Reverse -u Unique lines only -n Numeric order (instead of alphabetical) + -b Ignore leading blanks (or trailing blanks in second part of key) - -c Check whether input is sorted + -C Check whether input is sorted + -c Warn if input is unsorted -d Dictionary order (use alphanumeric and whitespace chars only) -f Force uppercase (case insensitive sort) -i Ignore nonprinting characters - -M Month sort (jan, feb, etc) - -x Hexadecimal numerical sort - -s Skip fallback sort (only sort with keys) - -z Zero (null) terminated lines -k Sort by "key" (see below) - -t Use a key separator other than whitespace + -M Month sort (jan, feb, etc) -o Output to FILE instead of stdout + -s Skip fallback sort (only sort with keys) + -t Use a key separator other than whitespace + -x Hexadecimal numerical sort -V Version numbers (name-1.234-rc6.5b.tgz) + -z Zero (null) terminated lines Sorting by key looks at a subset of the words on each line. -k2 uses the second word to the end of the line, -k2,2 looks at only the second word, -k2,4 looks from the start of the second to the end of the fourth word. -k2.4,5 starts from the fourth character of the second word, to the end - of the fifth word. Specifying multiple keys uses the later keys as tie - breakers, in order. A type specifier appended to a sort key (such as -2,2n) - applies only to sorting that key. + of the fifth word. Negative values count from the end. Specifying multiple + keys uses the later keys as tie breakers, in order. A type specifier + appended to a sort key (such as -2,2n) applies only to sorting that key. config SORT_FLOAT bool @@ -61,9 +63,8 @@ GLOBALS( char *o, *T, S; void *key_list; - int linecount; - char **lines; - char *name; + unsigned linecount; + char **lines, *name; ) // The sort types are n, g, and M. @@ -73,50 +74,57 @@ GLOBALS( #define FLAG_bb (1<<31) // Ignore trailing blanks -struct sort_key -{ +struct sort_key { struct sort_key *next_key; // linked list - unsigned range[4]; // start word, start char, end word, end char + long range[4]; // start word, start char, end word, end char int flags; }; +static int skip_key(char *str) +{ + int end = 0; + + // Skip leading blanks + if (str[end] && !TT.t) while (isspace(str[end])) end++; + + // Skip body of key + for (; str[end]; end++) { + if (TT.t) { + if (str[end]==*TT.t) { + end++; + break; + } + } else if (isspace(str[end])) break; + } + + return end; +} + // Copy of the part of this string corresponding to a key/flags. static char *get_key_data(char *str, struct sort_key *key, int flags) { - int start=0, end, len, i, j; + long start = 0, end, len, h, i, j, k; // Special case whole string, so we don't have to make a copy - if(key->range[0]==1 && !key->range[1] && !key->range[2] && !key->range[3] && !(flags&(FLAG_b|FLAG_d|FLAG_i|FLAG_bb))) return str; // Find start of key on first pass, end on second pass - len = strlen(str); for (j=0; j<2; j++) { - if (!key->range[2*j]) end=len; + if (!(k = key->range[2*j])) end=len; // Loop through fields else { - end=0; - for (i=1; i < key->range[2*j]+j; i++) { - - // Skip leading blanks - if (str[end] && !TT.t) while (isspace(str[end])) end++; - - // Skip body of key - for (; str[end]; end++) { - if (TT.t) { - if (str[end]==*TT.t) { - end++; - break; - } - } else if (isspace(str[end])) break; - } + if (k<1) for (end = h = 0;; end += h) { + ++k; + if (!(h = skip_key(str+end))) break; } + if (k<1) end = len*!j; + else for (end = 0, i = 1; istart && isspace(str[end-1])) end--; // Handle offsets on start and end - if (key->range[3]) { + if (key->range[3]>0) { end += key->range[3]-1; if (end>len) end=len; } - if (key->range[1]) { + if (key->range[1]>0) { start += key->range[1]-1; if (start>len) start=len; } // Make the copy - if (endnext_key); return *pkey = xzalloc(sizeof(struct sort_key)); @@ -195,7 +202,7 @@ static int compare_values(int flags, char *x, char *y) } if (yinf) return dy<0 ? 1 : -1; - return dx>dy ? 1 : (dxdy; } else if (flags & FLAG_M) { struct tm thyme; int dx; @@ -228,14 +235,11 @@ static int compare_values(int flags, char *x, char *y) } } return *x ? !!*y : -1; + // This is actually an integer sort with decimals sorted by string fallback. } else if (flags & FLAG_n) { - // Full floating point version of -n - if (CFG_SORT_FLOAT) { - double dx = atof(x), dy = atof(y); + long long dx = atoll(x), dy = atoll(y); - return dx>dy ? 1 : (dxdy; // Ascii sort } else return ((flags&FLAG_f) ? strcasecmp : strcmp)(x, y); @@ -248,8 +252,8 @@ static int compare_keys(const void *xarg, const void *yarg) char *x, *y, *xx = *(char **)xarg, *yy = *(char **)yarg; struct sort_key *key; - for (key=(struct sort_key *)TT.key_list; !retval && key; key = key->next_key){ - flags = key->flags ? key->flags : toys.optflags; + for (key=(void *)TT.key_list; !retval && key; key = key->next_key) { + flags = key->flags ? : toys.optflags; // Chop out and modify key chunks, handling -dfib @@ -279,21 +283,22 @@ static int compare_keys(const void *xarg, const void *yarg) // Read each line from file, appending to a big array. static void sort_lines(char **pline, long len) { - char * line; + char *line; if (!pline) return; line = *pline; if (!FLAG(z) && len && line[len-1]=='\n') line[--len] = 0; - *pline = NULL; + *pline = 0; // handle -c here so we don't allocate more memory than necessary. - if (FLAG(c)) { - int j = FLAG(u) ? -1 : 0; - - if (TT.lines && compare_keys((void *)&TT.lines, &line)>j) - error_exit("%s: Check line %d\n", TT.name, TT.linecount); + if (FLAG(C)||FLAG(c)) { + if (TT.lines && compare_keys((void *)&TT.lines, &line)>-FLAG(u)) { + toys.exitval = 1; + if (FLAG(C)) xexit(); + error_exit("%s: Check line %u", TT.name, TT.linecount+1); + } free(TT.lines); - TT.lines = (char **)line; + TT.lines = (void *)line; } else { if (!(TT.linecount&63)) TT.lines = xrealloc(TT.lines, sizeof(char *)*(TT.linecount+64)); @@ -306,12 +311,14 @@ static void sort_lines(char **pline, long len) static void sort_read(int fd, char *name) { TT.name = name; - do_lines(fd, FLAG(z) ? '\0' : '\n', sort_lines); + do_lines(fd, '\n'*!FLAG(z), sort_lines); } void sort_main(void) { - int idx, fd = 1; + int idx, jdx, fd = 1; + + if (FLAG(u)) toys.optflags |= FLAG_s; // Parse -k sort keys. if (TT.k) { @@ -319,42 +326,34 @@ void sort_main(void) for (arg = TT.k; arg; arg = arg->next) { struct sort_key *key = add_key(); - char *temp; + char *temp, *temp2, *optlist; int flag; idx = 0; temp = arg->arg; while (*temp) { // Start of range - key->range[2*idx] = (unsigned)strtol(temp, &temp, 10); - if (*temp=='.') - key->range[(2*idx)+1] = (unsigned)strtol(temp+1, &temp, 10); + key->range[2*idx] = strtol(temp, &temp, 10); + if (*temp=='.') key->range[(2*idx)+1] = strtol(temp+1, &temp, 10); // Handle flags appended to a key type. for (;*temp;temp++) { - char *temp2, *optlist; - - // Note that a second comma becomes an "Unknown key" error. + // Second comma becomes an "Unknown key" error. if (*temp==',' && !idx++) { temp++; break; } // Which flag is this? - optlist = toys.which->options; temp2 = strchr(optlist, *temp); - flag = (1<<(optlist-temp2+strlen(optlist)-1)); + flag = 1<<(optlist-temp2+strlen(optlist)-1); // Was it a flag that can apply to a key? - - if (!temp2 || flag>FLAG_x - || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z))) - { - toys.exitval = 2; + if (!temp2 || flag>FLAG_x || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z))) error_exit("Unknown key option."); - } + // b after , means strip _trailing_ space, not leading. if (idx && flag==FLAG_b) flag = FLAG_bb; key->flags |= flag; @@ -374,18 +373,15 @@ void sort_main(void) // The compare (-c) logic was handled in sort_read(), // so if we got here, we're done. - if (FLAG(c)) goto exit_now; + if (FLAG(C)||FLAG(c)) goto exit_now; // Perform the actual sort qsort(TT.lines, TT.linecount, sizeof(char *), compare_keys); // handle unique (-u) if (FLAG(u)) { - int jdx; - for (jdx=0, idx=1; idx - * - * TODO: genericize for telnet/microcom/tail-f, fix -t with login_tty() - -USE_NETCAT(NEWTOY(netcat, "^tElLw#<1W#<1p#<1>65535q#<1O:o:s:f:46uUnz[!tlL][!Lw][!Lu][!46U][!oO]", TOYFLAG_BIN)) -USE_NETCAT(OLDTOY(nc, netcat, TOYFLAG_USR|TOYFLAG_BIN)) - -config NETCAT - bool "netcat" - default y - help - usage: netcat [-46ELlntUu] [-pqWw #] [-s addr] [-o FILE] {IPADDR PORTNUM|-f FILENAME|COMMAND...} - - Forward stdin/stdout to a file or network connection. - - -4 Force IPv4 - -6 Force IPv6 - -E Forward stderr - -f Use FILENAME (ala /dev/ttyS0) instead of network - -L Listen and background each incoming connection (server mode) - -l Listen for one incoming connection, then exit - -n No DNS lookup - -o Hex dump to FILE (show packets, -o- writes hex only to stdout) - -O Hex dump to FILE (streaming mode) - -p Local port number - -q Quit SECONDS after EOF on stdin, even if stdout hasn't closed yet - -s Local source address - -t Allocate tty - -u Use UDP - -U Use a UNIX domain socket - -W SECONDS timeout for more data on an idle connection - -w SECONDS timeout to establish connection - -z zero-I/O mode [used for scanning] - - When listening the COMMAND line is executed as a child process to handle - an incoming connection. With no COMMAND -l forwards the connection - to stdin/stdout. If no -p specified, -l prints the port it bound to and - backgrounds itself (returning immediately). - - For a quick-and-dirty server, try something like: - netcat -s 127.0.0.1 -p 1234 -tL sh -l - - Or use "stty 115200 -F /dev/ttyS0 && stty raw -echo -ctlecho" with - netcat -f to connect to a serial port. -*/ - -#define FOR_netcat -#include "toys.h" - -GLOBALS( - char *f, *s, *o, *O; - long q, p, W, w; - - unsigned ofd, olast, opos, ocount[2]; - char obuf[16]; -) - -static void timeout(int signum) -{ - if (TT.w) error_exit("Timeout"); - xexit(); -} - -// open AF_UNIX socket -static int usock(char *name, int type, int out) -{ - int sockfd; - struct sockaddr_un sockaddr; - - memset(&sockaddr, 0, sizeof(struct sockaddr_un)); - - if (strlen(name) + 1 > sizeof(sockaddr.sun_path)) - error_exit("socket path too long %s", name); - strcpy(sockaddr.sun_path, name); - sockaddr.sun_family = AF_UNIX; - - sockfd = xsocket(AF_UNIX, type, 0); - (out?xconnect:xbind)(sockfd, (struct sockaddr*)&sockaddr, sizeof(sockaddr)); - - return sockfd; -} - -// Hex dump accumulated buffer data -void oflush(void) -{ - char *s = toybuf; - unsigned *oc = TT.ocount+(TT.olast==1), uu; - - if (!TT.opos) return; - s += sprintf(toybuf, "%c %08x", 60+2*(TT.olast==1), *oc); - for (uu = 0; uu<16; uu++) { - s += sprintf(s, uu95) TT.obuf[uu] = '.'; - } - dprintf(TT.ofd, "%s # %.*s\n", toybuf, TT.opos, TT.obuf); - *oc += TT.opos; - TT.opos = 0; -} - -// Write data to output, and hex dump to -o if enabled. -void ohexwrite(int fd, void *buf, size_t len) -{ - // Hex dump if -o specified. Output is always to fd 1, input != 1. - if (TT.ofd) { - int i = 0, j; - - if (TT.olast != fd) oflush(); - TT.olast = fd; - - while (isa_family == AF_INET) - port_be = ((struct sockaddr_in*)address)->sin_port; - else if (address->sa_family == AF_INET6) - port_be = ((struct sockaddr_in6*)address)->sin6_port; - else perror_exit("getsockname: bad family"); - - dprintf(1, "%d\n", SWAP_BE16(port_be)); - // Return immediately if no -p and -Ll has arguments, so wrapper - // script can use port number. - if (CFG_TOYBOX_FORK && toys.optc && xfork()) goto cleanup; - } - - do { - len = sizeof(struct sockaddr_storage); - if (FLAG(u)) { - if (-1 == recvfrom(in1 = dup(sockfd), &child, 1, MSG_PEEK, - (void *)toybuf, &len)) perror_exit("recvfrom"); - } else if ((in1 = accept(sockfd, 0, 0))<0) perror_exit("accept"); - out2 = in1; - child = 0; - - // We have a connection. Disarm timeout. - alarm(0); - - // Fork a child as necessary. Parent cleans up and continues here. - if (toys.optc && FLAG(L)) NOEXIT(child = XVFORK()); - if (child) { - close(in1); - continue; - } - - if (FLAG(u)) - xconnect(in1, (void *)toybuf, sizeof(struct sockaddr_storage)); - - // Cleanup and redirect for exec - if (toys.optc) { - // Do we need a tty? -// TODO nommu and -t only affects server mode... -// if (FLAG(t)) child = forkpty(&fdout, NULL, NULL, NULL); - - close(sockfd); - dup2(in1, 0); - dup2(in1, 1); - if (FLAG(E)) dup2(in1, 2); - if (in1>2) close(in1); - xexec(toys.optargs); - - // Copy stdin/out - } else { - pollinate(in1, in2, out1, out2, ohexwrite, TT.W, TT.q); - close(in1); - } - } while (FLAG(L)); - } - } - -cleanup: - if (CFG_TOYBOX_FREE) { - close(in1); - close(sockfd); - } -} diff --git a/update/toys/other/free.c b/update/toys/other/free.c deleted file mode 100644 index 6be1cdc..0000000 --- a/update/toys/other/free.c +++ /dev/null @@ -1,58 +0,0 @@ -/* free.c - Display amount of free and used memory in the system. - * - * Copyright 2012 Elie De Brauwer - -// Flag order is signifcant: b-g are units in order, FLAG_h-1 is unit mask -USE_FREE(NEWTOY(free, "hgmkb[!hgmkb]", TOYFLAG_USR|TOYFLAG_BIN)) - -config FREE - bool "free" - default y - help - usage: free [-bkmgt] - - Display the total, free and used amount of physical memory and swap space. - - -bkmg Output units (default is bytes) - -h Human readable (K=1024) -*/ - -#define FOR_free -#include "toys.h" - -GLOBALS( - unsigned bits; - unsigned long long units; - char *buf; -) - -static char *convert(unsigned long d) -{ - long long ll = d*TT.units; - char *s = TT.buf; - - if (FLAG(h)) human_readable(s, ll, 0); - else sprintf(s, "%llu",ll>>TT.bits); - TT.buf += strlen(TT.buf)+1; - - return s; -} - -void free_main(void) -{ - struct sysinfo in; - - sysinfo(&in); - TT.units = in.mem_unit ? in.mem_unit : 1; - while ((toys.optflags&(FLAG_h-1)) && !(toys.optflags&(1< - * Copyright 2013 Kyungwan Han - * - * No Standard. - * - * TODO cleanup - -USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN)) -USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN)) - -config LSATTR - bool "lsattr" - default y - help - usage: lsattr [-Radlpv] [FILE...] - - List file attributes on a Linux file system. - Flag letters are defined in chattr help. - - -R Recursively list attributes of directories and their contents - -a List all files in directories, including files that start with '.' - -d List directories like other files, rather than listing their contents - -l List long flag names - -p List the file's project number - -v List the file's version/generation number - -config CHATTR - bool "chattr" - default y - help - usage: chattr [-R] [-+=AacDdijsStTu] [-p PROJID] [-v VERSION] [FILE...] - - Change file attributes on a Linux file system. - - -R Recurse - -p Set the file's project number - -v Set the file's version/generation number - - Operators: - '-' Remove attributes - '+' Add attributes - '=' Set attributes - - Attributes: - A No atime a Append only - C No COW c Compression - D Synchronous dir updates d No dump - E Encrypted e Extents - F Case-insensitive (casefold) - I Indexed directory i Immutable - j Journal data - N Inline data in inode - P Project hierarchy - S Synchronous file updates s Secure delete - T Top of dir hierarchy t No tail-merging - u Allow undelete - V Verity -*/ -#define FOR_lsattr -#include "toys.h" -#include - -GLOBALS( - long v, p; - - unsigned add, rm, set; - // !add and !rm tell us whether they were used, but `chattr =` is meaningful. - int have_set; -) - -// Added more recently than the 7 year support horizon. TODO: remove -#ifndef FS_CASEFOLD_FL -#define FS_CASEFOLD_FL 0x40000000 // commit 71e90b4654a92 2019-07-23 -#endif -#ifndef FS_VERITY_FL -#define FS_VERITY_FL 0x00100000 // commit fe9918d3b228b 2019-07-22 -#endif - -static struct ext2_attr { - char *name; - unsigned flag; - char opt; -} e2attrs[] = { - // Do not sort! These are in the order that lsattr outputs them. - {"Secure_Deletion", FS_SECRM_FL, 's'}, - {"Undelete", FS_UNRM_FL, 'u'}, - {"Synchronous_Updates", FS_SYNC_FL, 'S'}, - {"Synchronous_Directory_Updates", FS_DIRSYNC_FL, 'D'}, - {"Immutable", FS_IMMUTABLE_FL, 'i'}, - {"Append_Only", FS_APPEND_FL, 'a'}, - {"No_Dump", FS_NODUMP_FL, 'd'}, - {"No_Atime", FS_NOATIME_FL, 'A'}, - {"Compression_Requested", FS_COMPR_FL, 'c'}, - {"Encrypted", FS_ENCRYPT_FL, 'E'}, - {"Journaled_Data", FS_JOURNAL_DATA_FL, 'j'}, - {"Indexed_directory", FS_INDEX_FL, 'I'}, - {"No_Tailmerging", FS_NOTAIL_FL, 't'}, - {"Top_of_Directory_Hierarchies", FS_TOPDIR_FL, 'T'}, - {"Extents", FS_EXTENT_FL, 'e'}, - {"No_COW", FS_NOCOW_FL, 'C'}, - {"Casefold", FS_CASEFOLD_FL, 'F'}, - {"Inline_Data", FS_INLINE_DATA_FL, 'N'}, - {"Project_Hierarchy", FS_PROJINHERIT_FL, 'P'}, - {"Verity", FS_VERITY_FL, 'V'}, - {NULL, 0, 0}, -}; - -// Get file flags on a Linux second extended file system. -static int ext2_getflag(int fd, struct stat *sb, unsigned *flag) -{ - if(!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) { - errno = EOPNOTSUPP; - return -1; - } - return ioctl(fd, FS_IOC_GETFLAGS, flag); -} - -static char *attrstr(unsigned attrs, int full) -{ - struct ext2_attr *a = e2attrs; - char *s = toybuf; - - for (; a->name; a++) - if (attrs & a->flag) *s++ = a->opt; - else if (full) *s++ = '-'; - *s = 0; - - return toybuf; -} - -static void print_file_attr(char *path) -{ - unsigned flag = 0, version = 0; - int fd = -1; - struct stat sb; - - if (!stat(path, &sb) && !S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) { - errno = EOPNOTSUPP; - goto error; - } - if (-1 == (fd=open(path, O_RDONLY | O_NONBLOCK))) goto error; - - if (FLAG(p)) { - struct fsxattr fsx; - - if (ioctl(fd, FS_IOC_FSGETXATTR, &fsx)) goto error; - xprintf("%5u ", fsx.fsx_projid); - } - if (FLAG(v)) { - if (ioctl(fd, FS_IOC_GETVERSION, (void*)&version) < 0) goto error; - xprintf("%-10u ", version); - } - - if (ext2_getflag(fd, &sb, &flag) < 0) perror_msg("reading flags '%s'", path); - else { - struct ext2_attr *ptr = e2attrs; - int name_found = 0; - - if (FLAG(l)) { - xprintf("%-50s ", path); - for (; ptr->name; ptr++) { - if (flag & ptr->flag) { - if (name_found) xprintf(", "); //for formatting. - xprintf("%s", ptr->name); - name_found = 1; - } - } - if (!name_found) xprintf("---"); - xputc('\n'); - } else xprintf("%s %s\n", attrstr(flag, 1), path); - } - path = 0; -error: - xclose(fd); - if (path) perror_msg("reading '%s'", path); -} - -// Get directory information. -static int retell_dir(struct dirtree *root) -{ - char *fpath = 0; - - if (root->again) { - xputc('\n'); - - return 0; - } - if (S_ISDIR(root->st.st_mode) && !root->parent) - return DIRTREE_RECURSE|DIRTREE_COMEAGAIN; - - fpath = dirtree_path(root, NULL); - if (*root->name != '.' || FLAG(a)) { - print_file_attr(fpath); - if (S_ISDIR(root->st.st_mode) && FLAG(R) && dirtree_notdotdot(root)) { - xprintf("\n%s:\n", fpath); - free(fpath); - return DIRTREE_RECURSE|DIRTREE_COMEAGAIN; - } - } - free(fpath); - - return 0; -} - -void lsattr_main(void) -{ - if (!*toys.optargs) dirtree_read(".", retell_dir); - else for (; *toys.optargs; toys.optargs++) { - struct stat sb; - - if (lstat(*toys.optargs, &sb)) perror_msg("stat '%s'", *toys.optargs); - else if (S_ISDIR(sb.st_mode) && !FLAG(d)) - dirtree_read(*toys.optargs, retell_dir); - else print_file_attr(*toys.optargs);// to handle "./Filename" or "./Dir" - } -} - -// Switch gears from lsattr to chattr. -#define FOR_chattr -#include "generated/flags.h" - -// Set file flags on a Linux second extended file system. -static inline int ext2_setflag(int fd, struct stat *sb, unsigned flag) -{ - if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) { - errno = EOPNOTSUPP; - return -1; - } - return ioctl(fd, FS_IOC_SETFLAGS, &flag); -} - -static unsigned get_flag_val(char ch) -{ - struct ext2_attr *ptr = e2attrs; - - for (; ptr->name; ptr++) if (ptr->opt == ch) return ptr->flag; - help_exit("bad '%c'", ch); -} - -// Parse command line argument and fill the chattr structure. -static void parse_cmdline_arg(char ***argv) -{ - char *arg = **argv, *ptr; - - while (arg) { - if (*arg=='-') for (ptr = ++arg; *ptr; ptr++) TT.rm |= get_flag_val(*ptr); - else if (*arg=='+') - for (ptr = ++arg; *ptr; ptr++) TT.add |= get_flag_val(*ptr); - else if (*arg=='=') { - TT.have_set = 1; - for (ptr = ++arg; *ptr; ptr++) TT.set |= get_flag_val(*ptr); - } else return; - arg = *(*argv += 1); - } -} - -// Update attribute of given file. -static int update_attr(struct dirtree *root) -{ - char *fpath = 0; - int vv = TT.v, fd; - - if (!dirtree_notdotdot(root)) return 0; - - // if file is a link and recursive is set or file is not regular+link+dir - // (like fifo or dev file) then escape the file. - if ((S_ISLNK(root->st.st_mode) && FLAG(R)) - || (!S_ISREG(root->st.st_mode) && !S_ISLNK(root->st.st_mode) - && !S_ISDIR(root->st.st_mode))) - return 0; - - fpath = dirtree_path(root, NULL); - if (-1 == (fd=open(fpath, O_RDONLY | O_NONBLOCK))) { - free(fpath); - return DIRTREE_ABORT; - } - - // Any potential flag changes? - if (TT.have_set | TT.add | TT.rm) { - unsigned orig, new; - - // Read current flags. - if (ext2_getflag(fd, &(root->st), &orig) < 0) { - perror_msg("read flags of '%s'", fpath); - free(fpath); - xclose(fd); - return DIRTREE_ABORT; - } - // Apply the requested changes. - if (TT.have_set) new = TT.set; // '='. - else { // '-' and/or '+'. - new = orig; - new &= ~(TT.rm); - new |= TT.add; - if (!S_ISDIR(root->st.st_mode)) new &= ~FS_DIRSYNC_FL; - } - // Write them back if there was any change. - if (orig != new && ext2_setflag(fd, &(root->st), new)<0) - perror_msg("%s: setting flags to =%s failed", fpath, attrstr(new, 0)); - } - - // (FS_IOC_SETVERSION works all the way back to 2.6, but FS_IOC_FSSETXATTR - // isn't available until 4.5.) - if (FLAG(v) && (ioctl(fd, FS_IOC_SETVERSION, &vv)<0)) - perror_msg("%s: setting version to %d failed", fpath, vv); - - if (FLAG(p)) { - struct fsxattr fsx; - int fail = ioctl(fd, FS_IOC_FSGETXATTR, &fsx); - - fsx.fsx_projid = TT.p; - if (fail || ioctl(fd, FS_IOC_FSSETXATTR, &fsx)) - perror_msg("%s: setting projid to %u failed", fpath, fsx.fsx_projid); - } - - free(fpath); - xclose(fd); - return (FLAG(R) && S_ISDIR(root->st.st_mode)) ? DIRTREE_RECURSE : 0; -} - -void chattr_main(void) -{ - char **argv = toys.optargs; - - parse_cmdline_arg(&argv); - if (TT.p < 0 || TT.p > UINT_MAX) error_exit("bad projid %lu", TT.p); - if (TT.v < 0 || TT.v > UINT_MAX) error_exit("bad version %ld", TT.v); - if (!*argv) help_exit("no file"); - if (TT.have_set && (TT.add || TT.rm)) - error_exit("no '=' with '-' or '+'"); - if (TT.rm & TT.add) error_exit("set/unset same flag"); - if (!(TT.add || TT.rm || TT.have_set || FLAG(p) || FLAG(v))) - error_exit("need '-p', '-v', '=', '-', or '+'"); - for (; *argv; argv++) dirtree_read(*argv, update_attr); -} diff --git a/update/toys/other/lsusb.c b/update/toys/other/lsusb.c deleted file mode 100644 index 3abbe47..0000000 --- a/update/toys/other/lsusb.c +++ /dev/null @@ -1,261 +0,0 @@ -/* lsusb.c - list available USB devices - * - * Copyright 2013 Andre Renaud - * Copyright 2013 Isaac Dunham - -USE_LSUSB(NEWTOY(lsusb, "i:", TOYFLAG_USR|TOYFLAG_BIN)) -USE_LSPCI(NEWTOY(lspci, "eDmkn@x@i:", TOYFLAG_USR|TOYFLAG_BIN)) - -config LSPCI - bool "lspci" - default y - help - usage: lspci [-ekmn] [-i FILE] - - List PCI devices. - - -e Extended (6 digit) class - -i ID database (default /etc/pci.ids[.gz]) - -k Show kernel driver - -m Machine readable - -n Numeric output (-nn for both) - -D Print domain numbers - -x Hex dump of config space (64 bytes; -xxx for 256, -xxxx for 4096) - -config LSUSB - bool "lsusb" - default y - help - usage: lsusb [-i] - - List USB hosts/devices. - - -i ID database (default /etc/usb.ids[.gz]) -*/ - -#define FOR_lsusb -#include "toys.h" - -GLOBALS( - char *i; - long x, n; - - void *ids, *class; - int count; -) - -struct dev_ids { - struct dev_ids *next, *child; - int id; - char name[]; -}; - -struct scanloop { - char *pattern; - void *d1, *d2; -}; - -// Common function to read uevent file under /proc for both pci and usb -// note that %s is omitted (because pointer is into toybuf, avoiding copy). -static int scan_uevent(struct dirtree *new, int len, struct scanloop *sl) -{ - int ii, saw = 0; - off_t flen = sizeof(toybuf); - char *ss, *yy; - - // Read data - if (*new->name == '.') return 0; - sprintf(toybuf, "%s/uevent", new->name); - if (!readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &flen)) return 0; - - // Loop over lines - while ((flen = strcspn(ss, "\n"))) { - if (ss[flen]) ss[flen++] = 0; - yy = ss+flen; - - // Try each pattern - for (ii = 0; iinext) { - if (id1 != ids->id) continue; - *name1 = ids->name; - for (ids = ids->child; ids; ids = ids->next) { - if (id2 != ids->id) continue; - *name2 = ids->name; - return; - } - return; - } -} - -// Search for pci.ids or usb.ids and return parsed structure or NULL -struct dev_ids *parse_dev_ids(char *name, struct dev_ids **and) -{ - char *path = "/etc:/vendor:/usr/share/hwdata:/usr/share/misc"; - struct string_list *sl = 0; - FILE *fp; - char *s, *ss, *sss; - struct dev_ids *ids = 0, *new; - int fd = -1; - - // Open compressed or uncompressed file - signal(SIGCHLD, SIG_IGN); - s = TT.i; - if (!s) { - sprintf(toybuf, "%s.gz", name); - if ((sl = find_in_path(path, toybuf)) || (sl = find_in_path(path, name))) - s = sl->str; - } - if (s && strend(s, ".gz")) xpopen((char *[]){"zcat", sl->str, 0}, &fd, 1); - else if (s) fd = xopen(s, O_RDONLY); - llist_traverse(sl, free); - if (fd == -1) return 0; - - for (fp = fdopen(fd, "r"); (s = ss = xgetline(fp)); free(s)) { - // TODO parse and use third level instead of skipping it here - if (s[strspn(s, " \t")]=='#' || strstart(&ss, "\t\t")) continue; - - // Switch to device class list? - if (strstart(&ss, "C ") && and) { - *and = ids; - and = 0; - } - fd = estrtol(sss = ss, &ss, 16); - if (ss>sss && *ss++==' ') { - while (isspace(*ss)) ss++; - new = xmalloc(sizeof(*new)+strlen(ss)+1); - new->child = 0; - new->id = fd; - strcpy(new->name, ss); - if (!ids || *s!='\t') { - new->next = ids; - ids = new; - } else { - new->next = ids->child; - ids->child = new; - } - } - } - fclose(fp); - - return ids; -} - -static int list_usb(struct dirtree *new) -{ - int busnum = 0, devnum = 0, pid = 0, vid = 0; - char *n1, *n2; - - if (!new->parent) return DIRTREE_RECURSE; - if (7 == scan_uevent(new, 3, (struct scanloop[]){{"BUSNUM=%u", &busnum, 0}, - {"DEVNUM=%u", &devnum, 0}, {"PRODUCT=%x/%x", &pid, &vid}})) - { - get_names(TT.ids, pid, vid, &n1, &n2); - printf("Bus %03d Device %03d: ID %04x:%04x %s %s\n", - busnum, devnum, pid, vid, n1, n2); - } - - return 0; -} - -void lsusb_main(void) -{ - // Parse http://www.linux-usb.org/usb.ids file (if available) - TT.ids = parse_dev_ids("usb.ids", 0); - dirtree_read("/sys/bus/usb/devices/", list_usb); -} - -#define FOR_lspci -#include "generated/flags.h" - -// TODO: -v -static int list_pci(struct dirtree *new) -{ - char *driver = 0, buf[16], *ss, *names[3]; - int cvd[3] = {0}, ii, revision = 0; - off_t len = sizeof(toybuf); - /* skip 0000: part by default */ - char *bus = strchr(new->name, ':') + 1; - -// Output formats: -n, -nn, -m, -nm, -nnm, -k - - if (!new->parent) return DIRTREE_RECURSE; - if (!bus || strlen(new->name)<6) return 0; - TT.count = 0; - - // Load revision - sprintf(toybuf, "%s/revision", new->name); - if (readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &len)) { - strstart(&ss, "0x"); - sscanf(ss, "%x", &revision); - } - - // Load uevent data, look up names in database - if (6>scan_uevent(new, 3, (struct scanloop[]){{"DRIVER=", &driver, 0}, - {"PCI_CLASS=%x", cvd, 0}, {"PCI_ID=%x:%x", cvd+1, cvd+2}})) return 0; - get_names(TT.class, 255&(cvd[0]>>16), 255&(cvd[0]>>8), names, names); - get_names(TT.ids, cvd[1], cvd[2], names+1, names+2); - if (!FLAG(e)) cvd[0] >>= 8; - - // Output line according to flags - if (FLAG(D) || strncmp(new->name, "0000:", bus-new->name)) bus = new->name; - printf("%s", bus); - for (ii = 0; ii<3; ii++) { - sprintf(buf, "%0*x", 6-2*(ii||!FLAG(e)), cvd[ii]); - if (!TT.n) printf(FLAG(m) ? " \"%s\"" : ": %s"+(ii!=1), names[ii] ? : buf); - else if (TT.n==1) printf(FLAG(m) ? " \"%s\"" : (ii==2)?"%s ":" %s:", buf); - else if (!FLAG(m)) { - // This one permutes the order, so do it all first time and abort loop - printf(" %s [%s]: %s %s [%04x:%04x]", names[0], buf, names[1], names[2], - cvd[1], cvd[2]); - break; - } else printf(" \"%s [%s]\"", names[ii], buf); - } - if (revision) printf(FLAG(m) ? " -r%02x" : " (rev %02x)", revision); - if (FLAG(k) && driver) printf(FLAG(m) ? " \"%s\"" : " %s", driver); - xputc('\n'); - - if (TT.x) { - FILE *fp; - int b, col = 0, max = (TT.x >= 4) ? 4096 : ((TT.x >= 3) ? 256 : 64); - - snprintf(toybuf, sizeof(toybuf), "/sys/bus/pci/devices/%s/config", new->name); - fp = xfopen(toybuf, "r"); - while ((b = fgetc(fp)) != EOF) { - if ((col % 16) == 0) printf("%02x: ", col & 0xf0); - printf("%02x ", (b & 0xff)); - if ((++col % 16) == 0) xputc('\n'); - if (col == max) break; - } - xputc('\n'); - fclose(fp); - } - - return 0; -} - -void lspci_main(void) -{ - // Parse https://pci-ids.ucw.cz/v2.2/pci.ids (if available) - if (TT.n != 1) TT.class = parse_dev_ids("pci.ids", (void *)&TT.ids); - dirtree_read("/sys/bus/pci/devices/", list_pci); -} diff --git a/update/toys/other/nsenter.c b/update/toys/other/nsenter.c deleted file mode 100644 index bafed47..0000000 --- a/update/toys/other/nsenter.c +++ /dev/null @@ -1,166 +0,0 @@ -/* nsenter.c - Enter existing namespaces - * - * Copyright 2014 Andy Lutomirski - * - * See http://man7.org/linux/man-pages/man1/nsenter.1.html - * - * unshare.c - run command in new context - * - * Copyright 2011 Rob Landley - * - * See http://man7.org/linux/man-pages/man1/unshare.1.html - * - -// Note: flags go in same order (right to left) for shared subset -USE_NSENTER(NEWTOY(nsenter, "<1a(all)F(no-fork)t#<1(target)C(cgroup):; i(ipc):; m(mount):; n(net):; p(pid):; u(uts):; U(user):; ", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT)) -USE_UNSHARE(NEWTOY(unshare, "<1^a(all)f(fork)r(map-root-user)C(cgroup):; i(ipc):; m(mount):; n(net):; p(pid):; u(uts):; U(user):; ", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT)) - -config UNSHARE - bool "unshare" - default y - help - usage: unshare [-imnpuUr] COMMAND... - - Create new container namespace(s) for this process and its children, allowing - the new set of processes to have a different view of the system than the - parent process. - - -a Unshare all supported namespaces - -f Fork command in the background (--fork) - -r Become root (map current euid/egid to 0/0, implies -U) (--map-root-user) - - Available namespaces: - -C Control groups (--cgroup) - -i SysV IPC (message queues, semaphores, shared memory) (--ipc) - -m Mount/unmount tree (--mount) - -n Network address, sockets, routing, iptables (--net) - -p Process IDs and init (--pid) - -u Host and domain names (--uts) - -U UIDs, GIDs, capabilities (--user) - - Each namespace can take an optional argument, a persistent mountpoint usable - by the nsenter command to add new processes to that the namespace. (Specify - multiple namespaces to unshare separately, ala -c -i -m because -cim is -c - with persistent mount "im".) - -config NSENTER - bool "nsenter" - default y - help - usage: nsenter [-t pid] [-F] [-i] [-m] [-n] [-p] [-u] [-U] COMMAND... - - Run COMMAND in an existing (set of) namespace(s). - - -a Enter all supported namespaces (--all) - -F don't fork, even if -p is used (--no-fork) - -t PID to take namespaces from (--target) - - The namespaces to switch are: - - -C Control groups (--cgroup) - -i SysV IPC: message queues, semaphores, shared memory (--ipc) - -m Mount/unmount tree (--mount) - -n Network address, sockets, routing, iptables (--net) - -p Process IDs and init, will fork unless -F is used (--pid) - -u Host and domain names (--uts) - -U UIDs, GIDs, capabilities (--user) - - If -t isn't specified, each namespace argument must provide a path - to a namespace file, ala "-i=/proc/$PID/ns/ipc" -*/ - -#define FOR_nsenter -#include "toys.h" -#include - -#define unshare(flags) syscall(SYS_unshare, flags) -#define setns(fd, nstype) syscall(SYS_setns, fd, nstype) - -GLOBALS( - char *UupnmiC[7]; - long t; -) - -// Code that must run in unshare's flag context -#define FOR_unshare -#include - -static void write_ugid_map(char *map, unsigned eugid) -{ - int fd = xopen(map, O_WRONLY); - - dprintf(fd, "0 %u 1", eugid); - xclose(fd); -} - -static int test_a() { return FLAG(a); } -static int test_r() { return FLAG(r); } -static int test_f() { return FLAG(f); } - -// Shift back to the context GLOBALS lives in (I.E. matching the filename). -#define FOR_nsenter -#include - -void unshare_main(void) -{ - char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc\0cgroup"; - unsigned flags[]={CLONE_NEWUSER, CLONE_NEWUTS, CLONE_NEWPID, CLONE_NEWNET, - CLONE_NEWNS, CLONE_NEWIPC, CLONE_NEWCGROUP}, f = 0; - int i, fd; - - // Create new namespace(s)? - if (CFG_UNSHARE && *toys.which->name=='u') { - // For -r, we have to save our original [ug]id before calling unshare() - int euid = geteuid(), egid = getegid(); - - // unshare -U does not imply -r, so we cannot use [+rU] - if (test_r()) toys.optflags |= FLAG_U; - - for (i = 0; i= 0) { - xwrite(fd, "deny", 4); - close(fd); - } - - write_ugid_map("/proc/self/uid_map", euid); - write_ugid_map("/proc/self/gid_map", egid); - } - - if (test_f()) { - toys.exitval = xrun(toys.optargs); - - return; - } - // Bind to existing namespace(s)? - } else if (CFG_NSENTER) { - for (i = 0; i - * Copyright 2013 Kyungwan Han - * - * No Standard. - * - * TODO: two passes so we can auto-size the columns? - -USE_PMAP(NEWTOY(pmap, "<1pqx", TOYFLAG_USR|TOYFLAG_BIN)) - -config PMAP - bool "pmap" - default y - help - usage: pmap [-pqx] PID... - - Report the memory map of a process or processes. - - -p Show full paths - -q Do not show header or footer - -x Show the extended format -*/ - -#define FOR_pmap -#include "toys.h" - -void pmap_main(void) -{ - char **optargs, *line = 0; - size_t len = 0; - - for (optargs = toys.optargs; *optargs; optargs++) { - long long start, end, pss, tpss=0, dirty, tdirty=0, swap, tswap=0, total=0; - char *name = 0, *k = "K"+FLAG(x), mode[5]; - pid_t pid = atolx(*optargs); - int extras = 0, off, count; - FILE *fp; - - sprintf(toybuf, "/proc/%u/cmdline", pid); - if (!(name = readfile(toybuf, 0, 0))) { - error_msg("no %s", toybuf); - continue; - } - xprintf("%d: %s\n", pid, name); - free(name); - - // Only bother scanning the more verbose smaps file in -x mode. - sprintf(toybuf, "/proc/%u/%smaps", pid, "s"+!FLAG(x)); - if (!(fp = fopen(toybuf, "r"))) { - error_msg("no %s", toybuf); - continue; - } - - if (FLAG(x) && !FLAG(q)) - xprintf("Address%*cKbytes PSS Dirty Swap Mode Mapping\n", - (int)(sizeof(long)*2)-5, ' '); - - while (getline(&line, &len, fp) > 0) { - count = sscanf(line, "%llx-%llx %4s %*s %*s %*s %n", &start, &end, mode, - &off); - if (count == 3) { - name = line[off] ? line+off : " [anon]\n"; - if (mode[3] == 'p') mode[3] = '-'; - total += end = (end-start)/1024; - printf("%0*llx % *lld%s ", (int)(2*sizeof(long)), start, 6+FLAG(x), - end, k); - if (FLAG(x)) { - strcpy(toybuf, name); - name = toybuf; - continue; - } - } else { - if (sscanf(line, "Pss: %lld", &pss) || - sscanf(line, "Private_Dirty: %lld", &dirty) || - sscanf(line, "Swap: %lld", &swap)) extras++; - if (extras==3) { - printf("% 7lld %7lld %7lld ", pss, dirty, swap); - tpss += pss; - tdirty += dirty; - tswap += swap; - extras = 0; - } else continue; - } - - xprintf("%s- %s%s", mode, *name == '[' ? " " : "", - FLAG(p) ? name : basename(name)); - } - - if (!FLAG(q)) { - if (FLAG(x)) { - xprintf("---------------- ------ ------ ------ ------\n" + - ((sizeof(long)==4)?8:0)); - } - printf("total% *lld%s", 2*(int)(sizeof(long)+1)+FLAG(x), total, k); - if (FLAG(x)) printf("% 8lld% 8lld% 8lld", tpss, tdirty, tswap); - xputc('\n'); - } - - fclose(fp); - } - free(line); -} diff --git a/update/toys/other/setsid.c b/update/toys/other/setsid.c deleted file mode 100644 index c6e0c53..0000000 --- a/update/toys/other/setsid.c +++ /dev/null @@ -1,58 +0,0 @@ -/* setsid.c - Run program in a new session ID. - * - * Copyright 2006 Rob Landley - -USE_SETSID(NEWTOY(setsid, "^<1wc@d[!dc]", TOYFLAG_USR|TOYFLAG_BIN)) - -config SETSID - bool "setsid" - default y - help - usage: setsid [-cdw] command [args...] - - Run process in a new session. - - -d Detach from tty - -c Control tty (repeat to steal) - -w Wait for child (and exit with its status) -*/ - -#define FOR_setsid -#include "toys.h" - -GLOBALS( - long c; -) - -void setsid_main(void) -{ - int i; - - // setsid() fails if we're already session leader, ala "exec setsid" from sh. - // Second call can't fail, so loop won't continue endlessly. - while (setsid()<0) { - pid_t pid; - - // This must be before vfork() or tcsetpgrp() will hang waiting for parent. - setpgid(0, 0); - - pid = XVFORK(); - if (pid) { - i = 0; - if (FLAG(w)) { - i = 127; - if (pid>0) i = xwaitpid(pid); - } - _exit(i); - } - } - - if (FLAG(c)) { - ioctl(0, TIOCSCTTY, TT.c>1); - tcsetpgrp(0, getpid()); - } if (FLAG(d) && (i = open("/dev/tty", O_RDONLY)) != -1) { - ioctl(i, TIOCNOTTY); - close(i); - } - xexec(toys.optargs); -} diff --git a/update/toys/other/stat.c b/update/toys/other/stat.c deleted file mode 100644 index 463aa46..0000000 --- a/update/toys/other/stat.c +++ /dev/null @@ -1,204 +0,0 @@ -/* stat.c : display file or file system status - * Copyright 2012 - * Copyright 2013 - -USE_STAT(NEWTOY(stat, "<1c:(format)fLt", TOYFLAG_BIN)) - -config STAT - bool stat - default y - help - usage: stat [-tfL] [-c FORMAT] FILE... - - Display status of files or filesystems. - - -c Output specified FORMAT string instead of default - -f Display filesystem status instead of file status - -L Follow symlinks - -t terse (-c "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o") - (with -f = -c "%n %i %l %t %s %S %b %f %a %c %d") - - The valid format escape sequences for files: - %a Access bits (octal) |%A Access bits (flags)|%b Size/512 - %B Bytes per %b (512) |%C Security context |%d Device ID (dec) - %D Device ID (hex) |%f All mode bits (hex)|%F File type - %g Group ID |%G Group name |%h Hard links - %i Inode |%m Mount point |%n Filename - %N Long filename |%o I/O block size |%s Size (bytes) - %t Devtype major (hex) |%T Devtype minor (hex)|%u User ID - %U User name |%x Access time |%X Access unix time - %y Modification time |%Y Mod unix time |%z Creation time - %Z Creation unix time - - The valid format escape sequences for filesystems: - %a Available blocks |%b Total blocks |%c Total inodes - %d Free inodes |%f Free blocks |%i File system ID - %l Max filename length |%n File name |%s Best transfer size - %S Actual block size |%t FS type (hex) |%T FS type (driver name) -*/ - -#define FOR_stat -#include "toys.h" - -GLOBALS( - char *c; - - union { - struct stat st; - struct statfs sf; - } stat; - char *file, *pattern; - int patlen; -) - -// Force numeric output to long long instead of manually typecasting everything -// and safely parse length prefix -static void out(char c, long long val) -{ - sprintf(toybuf, "%.*sll%c", TT.patlen, TT.pattern, c); - printf(toybuf, val); -} - -// Output string with parsed length prefix -static void strout(char *val) -{ - sprintf(toybuf, "%.*ss", TT.patlen, TT.pattern); - printf(toybuf, val); -} - -static void date_stat_format(struct timespec *ts) -{ - strout(format_iso_time(toybuf+128, sizeof(toybuf)-128, ts)); -} - -static void print_stat(char type) -{ - struct stat *stat = (struct stat *)&TT.stat; - - if (type == 'a') out('o', stat->st_mode&~S_IFMT); - else if (type == 'A') { - char str[11]; - - mode_to_string(stat->st_mode, str); - strout(str); - } else if (type == 'b') out('u', stat->st_blocks); - else if (type == 'B') out('d', 512); - else if (type == 'C') { - char *context = NULL; - - strout(lsm_get_context(TT.file, &context) != -1 ? context : "?"); - free(context); - } else if (type == 'd') out('d', stat->st_dev); - else if (type == 'D') out('x', stat->st_dev); - else if (type == 'f') out('x', stat->st_mode); - else if (type == 'F') { - char *t = "character device\0directory\0block device\0" \ - "regular file\0symbolic link\0socket\0FIFO (named pipe)"; - int i, filetype = stat->st_mode & S_IFMT; - - for (i = 1; filetype != (i*8192) && i < 7; i++) t += strlen(t)+1; - if (!stat->st_size && filetype == S_IFREG) t = "regular empty file"; - strout(t); - } else if (type == 'g') out('u', stat->st_gid); - else if (type == 'G') strout(getgroupname(stat->st_gid)); - else if (type == 'h') out('u', stat->st_nlink); - else if (type == 'i') out('u', stat->st_ino); - else if (type == 'm') { - struct mtab_list *mt = xgetmountlist(0); - dev_t dev = stat->st_rdev ? stat->st_rdev : stat->st_dev; - - // This mount point could exist multiple times, so show oldest. - for (dlist_terminate(mt); mt; mt = mt->next) if (mt->stat.st_dev == dev) { - strout(mt->dir); - break; - } - llist_traverse(mt, free); - } else if (type == 'N') { - printf("%s", TT.file); - if (S_ISLNK(stat->st_mode)) - if (readlink0(TT.file, toybuf, sizeof(toybuf))) - printf(" -> '%s'", toybuf); - } else if (type == 'o') out('u', stat->st_blksize); - else if (type == 's') out('u', stat->st_size); - else if (type == 't') out('x', dev_major(stat->st_rdev)); - else if (type == 'T') out('x', dev_minor(stat->st_rdev)); - else if (type == 'u') out('u', stat->st_uid); - else if (type == 'U') strout(getusername(stat->st_uid)); - else if (type == 'x') date_stat_format(&stat->st_atim); - else if (type == 'X') out('u', stat->st_atime); - else if (type == 'y') date_stat_format(&stat->st_mtim); - else if (type == 'Y') out('u', stat->st_mtime); - else if (type == 'z') date_stat_format(&stat->st_ctim); - else if (type == 'Z') out('u', stat->st_ctime); - else putchar('?'); -} - -static void print_statfs(char type) { - struct statfs *statfs = (struct statfs *)&TT.stat; - - if (type == 'a') out('u', statfs->f_bavail); - else if (type == 'b') out('u', statfs->f_blocks); - else if (type == 'c') out('u', statfs->f_files); - else if (type == 'd') out('u', statfs->f_ffree); - else if (type == 'f') out('u', statfs->f_bfree); - else if (type == 'l') out('d', pathconf(TT.file, _PC_NAME_MAX)); - else if (type == 't') out('x', statfs->f_type); - else if (type == 'T') strout(fs_type_name(statfs)); - else if (type == 'i') { - int *val = (int *) &statfs->f_fsid; - char buf[32]; - - sprintf(buf, "%08x%08x", val[0], val[1]); - strout(buf); - } else if (type == 's') out('d', statfs_bsize(statfs)); - else if (type == 'S') out('d', statfs_frsize(statfs)); - else strout("?"); -} - -void stat_main(void) -{ - int i; - char *format, *f; - - if (FLAG(t)) format = FLAG(f) - ? "%n %i %l %t %s %S %b %f %a %c %d" - : "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; - else format = FLAG(f) - ? " File: \"%n\"\n ID: %i Namelen: %l Type: %T\n" - "Block Size: %s Fundamental block size: %S\n" - "Blocks: Total: %b\tFree: %f\tAvailable: %a\n" - "Inodes: Total: %c\tFree: %d" - : " File: %N\n Size: %s\t Blocks: %b\t IO Blocks: %B\t %F\n" - "Device: %Dh/%dd\t Inode: %i\t Links: %h\t Device type: %t,%T\n" - "Access: (%04a/%A)\tUid: (%5u/%8U)\tGid: (%5g/%8G)\n" - "Access: %x\nModify: %y\nChange: %z"; - - if (FLAG(c)) format = TT.c; - - // loop through files listed on command line - for (i = 0; toys.optargs[i]; i++) { - - // stat the file or filesystem - TT.file = toys.optargs[i]; - if (FLAG(f) && !statfs(TT.file, (void *)&TT.stat)); - else if (FLAG(f) || (FLAG(L) ? stat : lstat)(TT.file, (void *)&TT.stat)) { - perror_msg("'%s'", TT.file); - continue; - } - - // parse format and print what it says - for (f = format; *f; f++) { - if (*f != '%' || !f[1]) putchar(*f); - else if (f[1]=='%') putchar(*f++); - else { - f = next_printf(f, &TT.pattern); - TT.patlen = f-TT.pattern; - if (!*f || TT.patlen>99) error_exit("bad %s", TT.pattern); - if (*f == 'n') strout(TT.file); - else if (FLAG(f)) print_statfs(*f); - else print_stat(*f); - } - } - xputc('\n'); - } -} diff --git a/update/toys/other/sysctl.c b/update/toys/other/sysctl.c deleted file mode 100644 index 7a2cba6..0000000 --- a/update/toys/other/sysctl.c +++ /dev/null @@ -1,149 +0,0 @@ -/* sysctl.c - A utility to read and manipulate the sysctl parameters. - * - * Copyright 2014 Bilal Qureshi - * Copyright 2014 Kyungwan Han - * - * No Standard - -USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN)) - -config SYSCTL - bool "sysctl" - default y - help - usage: sysctl [-aeNnqw] [-p [FILE] | KEY[=VALUE]...] - - Read/write system control data (under /proc/sys). - - -a Show all values - -e Don't warn about unknown keys - -N Don't print key values - -n Don't print key names - -p Read values from FILE (default /etc/sysctl.conf) - -q Don't show value after write - -w Only write values (object to reading) -*/ -#define FOR_sysctl -#include "toys.h" - -// Null terminate at =, return value -static char *split_key(char *key) -{ - char *value = strchr(key, '='); - - if (value) *(value++)=0; - - return value; -} - -static void replace_char(char *str, char old, char new) -{ - for (; *str; str++) if (*str == old) *str = new; -} - -static void key_error(char *key) -{ - if (errno != ENOENT) perror_msg("key '%s'", key); - else if (!FLAG(e)) error_msg("unknown key '%s'", key); -} - -static int write_key(char *path, char *key, char *value) -{ - int fd = open(path, O_WRONLY); - - if (fd < 0) { - key_error(key); - - return 0; - } - xwrite(fd, value, strlen(value)); - xclose(fd); - - return 1; -} - -// Display all keys under a path -static int do_show_keys(struct dirtree *dt) -{ - char *path, *data, *key; - - if (!dirtree_notdotdot(dt)) return 0; // Skip . and .. - if (S_ISDIR(dt->st.st_mode)) return DIRTREE_RECURSE; - - path = dirtree_path(dt, 0); - data = readfile(path, 0, 0); - replace_char(key = path + 10, '/', '.'); // skip "/proc/sys/" - if (!data) key_error(key); - else { - // Print the parts that aren't switched off by flags. - if (!FLAG(n)) xprintf("%s", key); - if (!FLAG(N) && !FLAG(n)) xprintf(" = "); - for (key = data+strlen(data); key > data && isspace(*--key); *key = 0); - if (!FLAG(N)) xprintf("%s", data); - if (!FLAG(N) || !FLAG(n)) xputc('\n'); - } - - free(data); - free(path); - - return 0; -} - -// Read/write entries under a key. Accepts "key=value" in key if !value -static void process_key(char *key, char *value) -{ - char *path; - - if (!value) value = split_key(key); - if (FLAG(w) && !value) return error_msg("'%s' not key=value", key); - path = xmprintf("/proc/sys/%s", key); - replace_char(path, '.', '/'); - // Note: failure to assign to a non-leaf node suppresses the display. - if (!(value && (!write_key(path, key, value) || FLAG(q)))) { - if (!access(path, R_OK)) dirtree_read(path, do_show_keys); - else key_error(key); - } - free(path); -} - -void sysctl_main() -{ - char **args = 0; - - // Display all keys - if (FLAG(a)) dirtree_read("/proc/sys", do_show_keys); - - // read file - else if (FLAG(p)) { - FILE *fp = xfopen(*toys.optargs ? *toys.optargs : "/etc/sysctl.conf", "r"); - size_t len; - - for (;;) { - char *line = 0, *key, *val; - - if (-1 == (len = getline(&line, &len, fp))) break; - key = line; - while (isspace(*key)) key++; - if (*key == '#' || *key == ';' || !*key) continue; - while (len && isspace(line[len-1])) line[--len] = 0; - if (!(val = split_key(line))) { - error_msg("'%s' not key=value", line); - continue; - } - - // Trim whitespace around = - len = (val-line)-1; - while (len && isspace(line[len-1])) line[--len] = 0; - while (isspace(*val)) val++;; - - process_key(key, val); - free(line); - } - fclose(fp); - - // Loop through arguments, displaying or assigning as appropriate - } else { - if (!*toys.optargs) help_exit("Needs 1 arg"); - for (args = toys.optargs; *args; args++) process_key(*args, 0); - } -} diff --git a/update/toys/other/timeout.c b/update/toys/other/timeout.c deleted file mode 100644 index 4c4288e..0000000 --- a/update/toys/other/timeout.c +++ /dev/null @@ -1,107 +0,0 @@ -/* timeout.c - Run command line with a timeout - * - * Copyright 2013 Rob Landley - * - * No standard - -USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):i", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125))) - -config TIMEOUT - bool "timeout" - default y - help - usage: timeout [-iv] [-k DURATION] [-s SIGNAL] DURATION COMMAND... - - Run command line as a child process, sending child a signal if the - command doesn't exit soon enough. - - DURATION can be a decimal fraction. An optional suffix can be "m" - (minutes), "h" (hours), "d" (days), or "s" (seconds, the default). - - -i Only kill for inactivity (restart timeout when command produces output) - -k Send KILL signal if child still running this long after first signal - -s Send specified signal (default TERM) - -v Verbose - --foreground Don't create new process group - --preserve-status Exit with the child's exit status -*/ - -#define FOR_timeout -#include "toys.h" - -GLOBALS( - char *s, *k; - - struct pollfd pfd; - sigjmp_buf sj; - int fds[2], pid, rc; -) - -static void handler(int sig, siginfo_t *si) -{ - TT.rc = si->si_status + ((si->si_code!=CLD_EXITED)<<7); - siglongjmp(TT.sj, 1); -} - -static long nantomil(struct timespec *ts) -{ - return ts->tv_sec*1000+ts->tv_nsec/1000000; -} - -static void callback(char *argv[]) -{ - if (!FLAG(foreground)) setpgid(0, 0); -} - -void timeout_main(void) -{ - int ii, ms, nextsig = SIGTERM; - struct timespec tts, kts; - - // Use same ARGFAIL value for any remaining parsing errors - toys.exitval = 125; - xparsetimespec(*toys.optargs, &tts); - if (TT.k) xparsetimespec(TT.k, &kts); - if (TT.s && -1==(nextsig = sig_to_num(TT.s))) error_exit("bad -s: '%s'",TT.s); - - toys.exitval = 0; - TT.pfd.events = POLLIN; - TT.fds[1] = -1; - if (sigsetjmp(TT.sj, 1)) goto done; - xsignal_flags(SIGCHLD, handler, SA_NOCLDSTOP|SA_SIGINFO); - - TT.pid = xpopen_setup(toys.optargs+1, FLAG(i) ? TT.fds : 0, callback); - xsignal(SIGTTIN, SIG_IGN); - xsignal(SIGTTOU, SIG_IGN); - xsignal(SIGTSTP, SIG_IGN); - if (!FLAG(i)) xpipe(TT.fds); - TT.pfd.fd = TT.fds[1]; - ms = nantomil(&tts); - for (;;) { - if (1 != xpoll(&TT.pfd, 1, ms)) { - if (FLAG(v)) - perror_msg("sending signal %s to command %s", num_to_sig(nextsig), - toys.optargs[1]); - toys.exitval = (nextsig==9) ? 137 : 124; - kill(FLAG(foreground) ? TT.pid : -TT.pid, nextsig); - if (!TT.k || nextsig==SIGKILL) break; - nextsig = SIGKILL; - ms = nantomil(&kts); - - continue; - } - if (TT.pfd.revents&POLLIN) { - errno = 0; - if (1>(ii = read(TT.fds[1], toybuf, sizeof(toybuf)))) { - if (errno==EINTR) continue; - break; - } - writeall(1, toybuf, ii); - } - if (TT.pfd.revents&POLLHUP) break; - } -done: - xpclose_both(TT.pid, TT.fds); - - if (FLAG(preserve_status) || !toys.exitval) toys.exitval = TT.rc; -} diff --git a/update/toys/pending/route.c b/update/toys/pending/route.c deleted file mode 100644 index 3b0e5be..0000000 --- a/update/toys/pending/route.c +++ /dev/null @@ -1,383 +0,0 @@ -/* route.c - Display/edit network routing table. - * - * Copyright 2012 Ranjan Kumar - * Copyright 2013 Kyungwan Han - * Copyright 2020 Eric Molitor - * - * No Standard - * - * route add -net target 10.0.0.0 netmask 255.0.0.0 dev eth0 - * route del delete - * delete net route, must match netmask, informative error message - * - * mod dyn reinstate metric netmask gw mss window irtt dev - -USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_SBIN)) -config ROUTE - bool "route" - default n - help - usage: route [-ne] [-A [inet|inet6]] [add|del TARGET [OPTIONS]] - - Display, add or delete network routes in the "Forwarding Information Base", - which send packets out a network interface to an address. - - -n Show numerical addresses (no DNS lookups) - -e display netstat fields - - Assigning an address to an interface automatically creates an appropriate - network route ("ifconfig eth0 10.0.2.15/8" does "route add 10.0.0.0/8 eth0" - for you), although some devices (such as loopback) won't show it in the - table. For machines more than one hop away, you need to specify a gateway - (ala "route add default gw 10.0.2.2"). - - The address "default" is a wildcard address (0.0.0.0/0) matching all - packets without a more specific route. - - Available OPTIONS include: - reject - blocking route (force match failure) - dev NAME - force matching packets out this interface (ala "eth0") - netmask - old way of saying things like ADDR/24 - gw ADDR - forward packets to gateway ADDR -*/ - -#define FOR_route -#include "toys.h" -#define _LINUX_SYSINFO_H // workaround for musl bug -#include - -GLOBALS( - char *A; -) - -struct _arglist { - char *arg; - int action; -}; - -static struct _arglist arglist1[] = { - { "add", 1 }, { "del", 2 }, - { "delete", 2 }, { NULL, 0 } -}; - -static struct _arglist arglist2[] = { - { "-net", 1 }, { "-host", 2 }, - { NULL, 0 } -}; - -void xsend(int sockfd, void *buf, size_t len) -{ - if (send(sockfd, buf, len, 0) != len) perror_exit("xsend"); -} - -int xrecv(int sockfd, void *buf, size_t len) -{ - int msg_len = recv(sockfd, buf, len, 0); - if (msg_len < 0) perror_exit("xrecv"); - - return msg_len; -} - -void addAttr(struct nlmsghdr *nl, int maxlen, void *attr, int type, int len) -{ - struct rtattr *rt; - int rtlen = RTA_LENGTH(len); - if (NLMSG_ALIGN(nl->nlmsg_len) + rtlen > maxlen) perror_exit("addAttr"); - rt = (struct rtattr*)((char *)nl + NLMSG_ALIGN(nl->nlmsg_len)); - rt->rta_type = type; - rt->rta_len = rtlen; - memcpy(RTA_DATA(rt), attr, len); - nl->nlmsg_len = NLMSG_ALIGN(nl->nlmsg_len) + rtlen; -} - -static void get_hostname(sa_family_t f, void *a, char *dst, size_t len) { - size_t a_len = (AF_INET6 == f) ? sizeof(struct in6_addr) : sizeof(struct in_addr); - - struct hostent *host = gethostbyaddr(a, a_len, f); - if (host) xstrncpy(dst, host->h_name, len); -} - -static void display_routes(sa_family_t f) -{ - int fd, msg_hdr_len, route_protocol; - struct { - struct nlmsghdr nl; - struct rtmsg rt; - } req; - struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)]; - struct nlmsghdr *msg_hdr_ptr; - struct rtmsg *route_entry; - struct rtattr *rteattr; - - fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - - memset(&req, 0, sizeof(req)); - req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.nl.nlmsg_type = RTM_GETROUTE; - req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.nl.nlmsg_pid = getpid(); - req.nl.nlmsg_seq = 1; - req.rt.rtm_family = f; - req.rt.rtm_table = RT_TABLE_MAIN; - xsend(fd, &req, sizeof(req)); - - if (f == AF_INET) { - xprintf("Kernel IP routing table\n" - "Destination Gateway Genmask Flags %s Iface\n", - FLAG(e) ? " MSS Window irtt" : "Metric Ref Use"); - } else { - xprintf("Kernel IPv6 routing table\n" - "%-31s%-26s Flag Metric Ref Use If\n", "Destination", "Next Hop"); - } - - msg_hdr_len = xrecv(fd, buf, sizeof(buf)); - msg_hdr_ptr = buf; - while (msg_hdr_ptr->nlmsg_type != NLMSG_DONE) { - while (NLMSG_OK(msg_hdr_ptr, msg_hdr_len)) { - route_entry = NLMSG_DATA(msg_hdr_ptr); - route_protocol = route_entry->rtm_protocol; - - // Annoyingly NLM_F_MATCH is not yet implemented so even if we pass in - // RT_TABLE_MAIN with RTM_GETROUTE it still returns everything so we - // have to filter here. - if (route_entry->rtm_table == RT_TABLE_MAIN) { - int route_attribute_len; - char dest[INET6_ADDRSTRLEN], gate[INET6_ADDRSTRLEN], netmask[32], - flags[10] = "U", if_name[IF_NAMESIZE] = "-"; - unsigned priority = 0, mss = 0, win = 0, irtt = 0, ref = 0, use = 0, - route_netmask, metric_len; - struct in_addr netmask_addr; - struct rtattr *metric; - struct rta_cacheinfo *cache_info; - - if (f == AF_INET) { - strcpy(dest, FLAG(n) ? "0.0.0.0" : "default"); - strcpy(gate, FLAG(n) ? "*" : "0.0.0.0"); - strcpy(netmask, "0.0.0.0"); - } else { - strcpy(dest, "::"); - strcpy(gate, "::"); - } - - route_netmask = route_entry->rtm_dst_len; - if (route_netmask == 0) netmask_addr.s_addr = ~((in_addr_t) -1); - else netmask_addr.s_addr = htonl(~((1 << (32 - route_netmask)) - 1)); - inet_ntop(AF_INET, &netmask_addr, netmask, sizeof(netmask)); - - rteattr = RTM_RTA(route_entry); - route_attribute_len = RTM_PAYLOAD(msg_hdr_ptr); - while (RTA_OK(rteattr, route_attribute_len)) { - switch (rteattr->rta_type) { - case RTA_DST: - if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), dest, sizeof(dest)); - else get_hostname(f, RTA_DATA(rteattr), dest, sizeof(dest)); - break; - - case RTA_GATEWAY: - if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), gate, sizeof(dest)); - else get_hostname(f, RTA_DATA(rteattr), gate, sizeof(dest)); - strcat(flags, "G"); - break; - - case RTA_PRIORITY: - priority = *(unsigned *)RTA_DATA(rteattr); - break; - - case RTA_OIF: - if_indextoname(*(int *)RTA_DATA(rteattr), if_name); - break; - - case RTA_METRICS: - metric_len = RTA_PAYLOAD(rteattr); - for (metric = RTA_DATA(rteattr); RTA_OK(metric, metric_len); - metric = RTA_NEXT(metric, metric_len)) - if (metric->rta_type == RTAX_ADVMSS) - mss = *(unsigned *)RTA_DATA(metric); - else if (metric->rta_type == RTAX_WINDOW) - win = *(unsigned *)RTA_DATA(metric); - else if (metric->rta_type == RTAX_RTT) - irtt = (*(unsigned *)RTA_DATA(metric))/8; - break; - - case RTA_CACHEINFO: - cache_info = RTA_DATA(rteattr); - ref = cache_info->rta_clntref; - use = cache_info->rta_used; - break; - } - - rteattr = RTA_NEXT(rteattr, route_attribute_len); - } - - if (route_entry->rtm_type == RTN_UNREACHABLE) flags[0] = '!'; - if (route_netmask == 32) strcat(flags, "H"); - if (route_protocol == RTPROT_REDIRECT) strcat(flags, "D"); - - if (f == AF_INET) { - xprintf("%-15.15s %-15.15s %-16s%-6s", dest, gate, netmask, flags); - if (FLAG(e)) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, if_name); - else xprintf("%-6d %-2d %7d %s\n", priority, ref, use, if_name); - } else { - char *dest_with_mask = xmprintf("%s/%u", dest, route_netmask); - xprintf("%-30s %-26s %-4s %-6d %-4d %2d %-8s\n", - dest_with_mask, gate, flags, priority, ref, use, if_name); - free(dest_with_mask); - } - } - msg_hdr_ptr = NLMSG_NEXT(msg_hdr_ptr, msg_hdr_len); - } - - msg_hdr_len = xrecv(fd, buf, sizeof(buf)); - msg_hdr_ptr = buf; - } - - xclose(fd); -} - -// find parameter (add/del/net/host) in list, return appropriate action or 0. -static int get_action(char ***argv, struct _arglist *list) -{ - struct _arglist *alist; - - if (!**argv) return 0; - for (alist = list; alist->arg; alist++) { //find the given parameter in list - if (!strcmp(**argv, alist->arg)) { - *argv += 1; - return alist->action; - } - } - return 0; -} - -// add/del a route. -static void setroute(sa_family_t f, char **argv) -{ - char *tgtip; - int sockfd, arg2_action; - int action = get_action(&argv, arglist1); //verify the arg for add/del. - struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)]; - struct nlmsghdr *nlMsg; - struct rtmsg *rtMsg; - - if (!action || !*argv) help_exit("setroute"); - arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host - if (!*argv) help_exit("setroute"); - tgtip = *argv++; - sockfd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - memset(buf, 0, sizeof(buf)); - nlMsg = (struct nlmsghdr *) buf; - rtMsg = (struct rtmsg *) NLMSG_DATA(nlMsg); - - nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - - //TODO(emolitor): Improve action and arg2_action handling - if (action == 1) { // Add - nlMsg->nlmsg_type = RTM_NEWROUTE; - nlMsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - } else { // Delete - nlMsg->nlmsg_type = RTM_DELROUTE; - nlMsg->nlmsg_flags = NLM_F_REQUEST; - } - - nlMsg->nlmsg_pid = getpid(); - nlMsg->nlmsg_seq = 1; - rtMsg->rtm_family = f; - rtMsg->rtm_table = RT_TABLE_UNSPEC; - rtMsg->rtm_type = RTN_UNICAST; - rtMsg->rtm_protocol = RTPROT_UNSPEC; - rtMsg->rtm_flags = RTM_F_NOTIFY; - rtMsg->rtm_dst_len = rtMsg->rtm_src_len = (f == AF_INET) ? 32 : 128; - - if (arg2_action == 2) rtMsg->rtm_scope = RT_SCOPE_HOST; - - size_t addr_len = sizeof(struct in_addr); - if (f == AF_INET6) addr_len = sizeof(struct in6_addr); - unsigned char addr[sizeof(struct in6_addr)] = {0,}; - - for (; *argv; argv++) { - if (!strcmp(*argv, "mod")) continue; - else if (!strcmp(*argv, "dyn")) continue; - else if (!strcmp(*argv, "reinstate")) continue; - else if (!strcmp(*argv, "reject")) rtMsg->rtm_type = RTN_UNREACHABLE; - else { - if (!argv[1]) show_help(stdout, 1); - - if (!strcmp(*argv, "metric")) { - unsigned int priority = atolx_range(argv[1], 0, UINT_MAX); - addAttr(nlMsg, sizeof(toybuf), &priority, RTA_PRIORITY, sizeof(unsigned int)); - } else if (!strcmp(*argv, "netmask")) { - uint32_t netmask; - char *ptr; - uint32_t naddr[4] = {0,}; - uint64_t plen; - - netmask = (f == AF_INET6) ? 128 : 32; // set default netmask - plen = strtoul(argv[1], &ptr, 0); - - if (!ptr || ptr == argv[1] || *ptr || !plen || plen > netmask) { - if (!inet_pton(f, argv[1], &naddr)) error_exit("invalid netmask"); - if (f == AF_INET) { - uint32_t mask = htonl(*naddr), host = ~mask; - if (host & (host + 1)) error_exit("invalid netmask"); - for (plen = 0; mask; mask <<= 1) ++plen; - if (plen > 32) error_exit("invalid netmask"); - } - } - netmask = plen; - rtMsg->rtm_dst_len = netmask; - } else if (!strcmp(*argv, "gw")) { - if (!inet_pton(f, argv[1], &addr)) error_exit("invalid gw"); - addAttr(nlMsg, sizeof(toybuf), &addr, RTA_GATEWAY, addr_len); - } else if (!strcmp(*argv, "mss")) { - // TODO(emolitor): Add RTA_METRICS support - //set the TCP Maximum Segment Size for connections over this route. - //rt->rt_mtu = atolx_range(argv[1], 64, 65536); - //rt->rt_flags |= RTF_MSS; - } else if (!strcmp(*argv, "window")) { - // TODO(emolitor): Add RTA_METRICS support - //set the TCP window size for connections over this route to W bytes. - //rt->rt_window = atolx_range(argv[1], 128, INT_MAX); //win low - //rt->rt_flags |= RTF_WINDOW; - } else if (!strcmp(*argv, "irtt")) { - // TODO(emolitor): Add RTA_METRICS support - //rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX); - //rt->rt_flags |= RTF_IRTT; - } else if (!strcmp(*argv, "dev")) { - unsigned int if_idx = if_nametoindex(argv[1]); - if (!if_idx) perror_exit("dev"); - addAttr(nlMsg, sizeof(toybuf), &if_idx, RTA_OIF, sizeof(unsigned int)); - } else help_exit("no '%s'", *argv); - argv++; - } - } - - if (strcmp(tgtip, "default") != 0) { - char *prefix = strtok(0, "/"); - - if (prefix) rtMsg->rtm_dst_len = strtoul(prefix, &prefix, 0); - if (!inet_pton(f, strtok(tgtip, "/"), &addr)) error_exit("invalid target"); - addAttr(nlMsg, sizeof(toybuf), &addr, RTA_DST, addr_len); - } else rtMsg->rtm_dst_len = 0; - - xsend(sockfd, nlMsg, nlMsg->nlmsg_len); - xclose(sockfd); -} - -void route_main(void) -{ - if (!*toys.optargs) { - if (!TT.A || !strcmp(TT.A, "inet")) display_routes(AF_INET); - else if (!strcmp(TT.A, "inet6")) display_routes(AF_INET6); - else show_help(stdout, 1); - } else { - if (!TT.A) { - if (toys.optc>1 && strchr(toys.optargs[1], ':')) { - xprintf("WARNING: Implicit IPV6 address using -Ainet6\n"); - TT.A = "inet6"; - } else TT.A = "inet"; - } - - if (!strcmp(TT.A, "inet")) setroute(AF_INET, toys.optargs); - else setroute(AF_INET6, toys.optargs); - } -} diff --git a/update/toys/posix/cpio.c b/update/toys/posix/cpio.c deleted file mode 100644 index 321bbfd..0000000 --- a/update/toys/posix/cpio.c +++ /dev/null @@ -1,324 +0,0 @@ -/* cpio.c - a basic cpio - * - * Copyright 2013 Isaac Dunham - * Copyright 2015 Frontier Silicon Ltd. - * - * see https://www.kernel.org/doc/Documentation/early-userspace/buffer-format.txt - * and http://refspecs.linuxfoundation.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/cpio.html - * and http://pubs.opengroup.org/onlinepubs/7908799/xcu/cpio.html - * - * Yes, that's SUSv2, newer versions removed it, but RPM and initramfs use - * this archive format. We implement (only) the modern "-H newc" variant which - * expanded headers to 110 bytes (first field 6 bytes, rest are 8). - * In order: magic ino mode uid gid nlink mtime filesize devmajor devminor - * rdevmajor rdevminor namesize check - * This is the equivalent of mode -H newc in other implementations. - * We always do --quiet, but accept it as a compatibility NOP. - * - * TODO: export/import linux file list text format ala gen_initramfs_list.sh - * TODO: hardlink support, -A, -0, -a, -L, --sparse - * TODO: --renumber-archives (probably always?) --ignore-devno --reproducible - -USE_CPIO(NEWTOY(cpio, "(ignore-devno)(renumber-inodes)(quiet)(no-preserve-owner)R(owner):md(make-directories)uLH:p|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN)) - -config CPIO - bool "cpio" - default y - help - usage: cpio -{o|t|i|p DEST} [-dLtuv] [--verbose] [-F FILE] [-R [USER][:GROUP] [--no-preserve-owner] - - Copy files into and out of a "newc" format cpio archive. - - -d Create directories if needed - -F FILE Use archive FILE instead of stdin/stdout - -i Extract from archive into file system (stdin=archive) - -L Follow symlinks - -o Create archive (stdin=list of files, stdout=archive) - -p DEST Copy-pass mode, copy stdin file list to directory DEST - -R USER Replace owner with USER[:GROUP] - -t Test files (list only, stdin=archive, stdout=list of files) - -u Unlink existing files when extracting - -v Verbose - --no-preserve-owner Don't set ownership during extract -*/ - -#define FOR_cpio -#include "toys.h" - -GLOBALS( - char *F, *H, *R; -) - -// Read strings, tail padded to 4 byte alignment. Argument "align" is amount -// by which start of string isn't aligned (usually 0, but header is 110 bytes -// which is 2 bytes off because the first field wasn't expanded from 6 to 8). -static char *strpad(int fd, unsigned len, unsigned align) -{ - char *str; - - align = (align + len) & 3; - if (align) len += (4-align); - xreadall(fd, str = xmalloc(len+1), len); - str[len]=0; // redundant, in case archive is bad - - return str; -} - -//convert hex to uint; mostly to allow using bits of non-terminated strings -static unsigned x8u(char *hex) -{ - unsigned val, inpos = 8, outpos; - char pattern[6]; - - while (*hex == '0') { - hex++; - if (!--inpos) return 0; - } - // Because scanf gratuitously treats %*X differently than printf does. - sprintf(pattern, "%%%dX%%n", inpos); - sscanf(hex, pattern, &val, &outpos); - if (inpos != outpos) error_exit("bad hex"); - - return val; -} - -void cpio_main(void) -{ - int pipe, afd = FLAG(o), reown = !geteuid() && !FLAG(no_preserve_owner), - empty = 1; - pid_t pid = 0; - long Ruid = -1, Rgid = -1; - char *tofree = 0; - - if (TT.R) { - char *group = TT.R+strcspn(TT.R, ":."); - - if (*group) { - Rgid = xgetgid(group+1); - *group = 0; - } - if (group != TT.R) Ruid = xgetuid(TT.R); - } - - // In passthrough mode, parent stays in original dir and generates archive - // to pipe, child does chdir to new dir and reads archive from stdin (pipe). - if (FLAG(p)) { - if (FLAG(d)) { - if (!*toys.optargs) error_exit("need directory for -p"); - if (mkdir(*toys.optargs, 0700) == -1 && errno != EEXIST) - perror_msg("mkdir %s", *toys.optargs); - } - if (toys.stacktop) { - // xpopen() doesn't return from child due to vfork(), instead restarts - // with !toys.stacktop - pid = xpopen(0, &pipe, 0); - afd = pipe; - } else { - // child - toys.optflags |= FLAG_i; - xchdir(*toys.optargs); - } - } - - if (TT.F) { - int perm = FLAG(o) ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY; - - afd = xcreate(TT.F, perm, 0644); - } - - // read cpio archive - - if (FLAG(i) || FLAG(t)) for (;; empty = 0) { - char *name, *data; - unsigned mode, uid, gid, timestamp; - int test = FLAG(t), err = 0, size = 0, len; - - free(tofree); - tofree = 0; - // read header, skipping arbitrary leading NUL bytes (concatenated archives) - for (;;) { - if (1>(len = readall(afd, toybuf+size, 110-size))) break; - if (size || *toybuf) { - size += len; - break; - } - for (size = 0; size=0) ? Ruid : x8u(toybuf+22); - gid = (Rgid>=0) ? Rgid : x8u(toybuf+30); - timestamp = x8u(toybuf+46); // unsigned 32 bit, so year 2100 problem - - // (This output is unaffected by --quiet.) - if (FLAG(t) || FLAG(v)) puts(name); - - if (FLAG(u) && !test) if (unlink(name) && errno == EISDIR) rmdir(name); - - if (!test && FLAG(d) && strrchr(name, '/') && mkpath(name)) { - perror_msg("mkpath '%s'", name); - test++; - } - - // Consume entire record even if it couldn't create file, so we're - // properly aligned with next file. - - if (S_ISDIR(mode)) { - if (test) continue; - err = mkdir(name, mode) && (errno != EEXIST && !FLAG(u)); - - // Creading dir/dev doesn't give us a filehandle, we have to refer to it - // by name to chown/utime, but how do we know it's the same item? - // Check that we at least have the right type of entity open, and do - // NOT restore dropped suid bit in this case. - if (S_ISDIR(mode) && reown) { - int fd = open(name, O_RDONLY|O_NOFOLLOW); - struct stat st; - - if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT)) - err = fchown(fd, uid, gid); - else err = 1; - - close(fd); - } - } else if (S_ISREG(mode)) { - int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, mode); - - // If write fails, we still need to read/discard data to continue with - // archive. Since doing so overwrites errno, report error now - if (fd < 0) { - perror_msg("create %s", name); - test++; - } - - data = toybuf; - while (size) { - if (size < sizeof(toybuf)) data = strpad(afd, size, 0); - else xreadall(afd, toybuf, sizeof(toybuf)); - if (!test) xwrite(fd, data, data == toybuf ? sizeof(toybuf) : size); - if (data != toybuf) { - free(data); - break; - } - size -= sizeof(toybuf); - } - - if (!test) { - // set owner, restore dropped suid bit - if (reown) err = fchown(fd, uid, gid) && fchmod(fd, mode); - close(fd); - } - } else { - data = S_ISLNK(mode) ? strpad(afd, size, 0) : 0; - if (!test) { - err = data ? symlink(data, name) - : mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86))); - - // Can't get a filehandle to a symlink or a node on nodev mount, - // so do special chown that at least doesn't follow symlinks. - // We also don't chmod after, so dropped suid bit isn't restored - if (!err && reown) err = lchown(name, uid, gid); - } - free(data); - } - - // Set timestamp. - if (!test && !err) { - struct timespec times[2]; - - memset(times, 0, sizeof(struct timespec)*2); - times[0].tv_sec = times[1].tv_sec = timestamp; - err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW); - } - - if (err) perror_msg_raw(name); - - // Output cpio archive - - } else { - char *name = 0; - size_t size = 0; - unsigned inode = 0; - - for (;;) { - struct stat st; - unsigned nlen, error = 0, zero = 0; - int len, fd = -1; - char *link = 0; - ssize_t llen; - - len = getline(&name, &size, stdin); - if (len<1) break; - if (name[len-1] == '\n') name[--len] = 0; - if (!len) continue; - nlen = len+1; - if ((FLAG(L)?stat:lstat)(name, &st) || (S_ISREG(st.st_mode) - && st.st_size && (fd = open(name, O_RDONLY))<0) - || (S_ISLNK(st.st_mode) && !(link = xreadlink(name)))) - { - perror_msg_raw(name); - continue; - } - // encrypted filesystems can stat the wrong link size - if (link) st.st_size = strlen(link); - - if (Ruid>=0) st.st_uid = Ruid; - if (Rgid>=0) st.st_gid = Rgid; - if (FLAG(no_preserve_owner)) st.st_uid = st.st_gid = 0; - if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) st.st_size = 0; - if (st.st_size >> 32) perror_msg("skipping >2G file '%s'", name); - else { - if (FLAG(renumber_inodes)) st.st_ino = ++inode; - if (FLAG(ignore_devno)) st.st_rdev = 0; - llen = sprintf(toybuf, - "070701%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X", - (int)st.st_ino, st.st_mode, st.st_uid, st.st_gid, (int)st.st_nlink, - (int)st.st_mtime, (int)st.st_size, dev_major(st.st_dev), - dev_minor(st.st_dev), dev_major(st.st_rdev), dev_minor(st.st_rdev), - nlen, 0); - xwrite(afd, toybuf, llen); - xwrite(afd, name, nlen); - - // NUL Pad header up to 4 multiple bytes. - llen = (llen + nlen) & 3; - if (llen) xwrite(afd, &zero, 4-llen); - - // Write out body for symlink or regular file - if (link) xwrite(afd, link, st.st_size); - else for (llen = st.st_size; llen; llen -= nlen) { - nlen = llen > sizeof(toybuf) ? sizeof(toybuf) : llen; - // If read fails, write anyway (already wrote size in header) - if (nlen != readall(fd, toybuf, nlen)) - if (!error++) perror_msg("bad read from file '%s'", name); - xwrite(afd, toybuf, nlen); - } - llen = st.st_size & 3; - if (llen) xwrite(afd, &zero, 4-llen); - } - free(link); - xclose(fd); - } - if (CFG_TOYBOX_FREE) free(name); - - // nlink=1, namesize=11, with padding - dprintf(afd, "070701%040X%056X%08XTRAILER!!!%c%c%c%c", 1, 11, 0, 0, 0, 0,0); - } - if (TT.F) xclose(afd); - - if (FLAG(p) && pid) toys.exitval |= xpclose(pid, pipe); -} diff --git a/update/toys/posix/kill.c b/update/toys/posix/kill.c deleted file mode 100644 index dc4c370..0000000 --- a/update/toys/posix/kill.c +++ /dev/null @@ -1,161 +0,0 @@ -/* kill.c - a program to send signals to processes - * - * Copyright 2012 Daniel Walter - * - * See http://opengroup.org/onlinepubs/9699919799/utilities/kill.html - * - * killall5.c - Send signal to all processes outside current session. - * - * Copyright 2014 Ranjan Kumar - * Copyright 2014 Kyungwan Han - * - * No Standard - * - * TODO: toysh jobspec support, -n -L - -USE_KILL(NEWTOY(kill, "?ls: ", TOYFLAG_BIN|TOYFLAG_MAYFORK)) -USE_KILLALL5(NEWTOY(killall5, "?o*ls: [!lo][!ls]", TOYFLAG_SBIN)) - -config KILL - bool "kill" - default y - help - usage: kill [-l [SIGNAL] | -s SIGNAL | -SIGNAL] PID... - - Send signal to process(es). - - -l List signal name(s) and number(s) - -s Send SIGNAL (default SIGTERM) - -config KILLALL5 - bool "killall5" - default y - depends on KILL - help - usage: killall5 [-l [SIGNAL]] [-SIGNAL|-s SIGNAL] [-o PID]... - - Send a signal to all processes outside current session. - - -l List signal name(s) and number(s) - -o PID Omit PID - -s Send SIGNAL (default SIGTERM) -*/ - -// This has to match the filename: -#define FOR_kill -#define FORCE_FLAGS -#include "toys.h" - -GLOBALS( - char *s; - struct arg_list *o; -) - -// But kill's flags are a subset of killall5's - -#define FOR_killall5 -#include "generated/flags.h" - -void kill_main(void) -{ - int signum; - char *tmp, **args = toys.optargs; - pid_t pid; - - // list signal(s) - if (FLAG(l)) { - if (*args) { - int signum = sig_to_num(*args); - char *s = 0; - - if (signum>=0) s = num_to_sig(signum&127); - if (isdigit(**args)) puts(s ? s : "UNKNOWN"); - else printf("%d\n", signum); - } else list_signals(); - - return; - } - - // signal must come before pids, so "kill -9 -1" isn't confusing. - - if (!TT.s && *args && **args=='-') TT.s = *(args++)+1; - if (TT.s) { - char *arg; - int i = strtol(TT.s, &arg, 10); - - if (!*arg) arg = num_to_sig(i); - else arg = TT.s; - - if (!arg || -1 == (signum = sig_to_num(arg))) - error_exit("Unknown signal '%s'", arg); - } else signum = SIGTERM; - - // is it killall5? - if (CFG_KILLALL5 && toys.which->name[4]=='a') { - DIR *dp; - struct dirent *entry; - int pid, sid; - long *olist = 0, ocount = 0; - - // parse omit list - if (FLAG(o)) { - struct arg_list *ptr; - - for (ptr = TT.o; ptr; ptr = ptr->next) ocount++; - olist = xmalloc(ocount*sizeof(long)); - ocount = 0; - for (ptr = TT.o; ptr; ptr=ptr->next) olist[ocount++] = atolx(ptr->arg); - } - - sid = getsid(pid = getpid()); - - if (!(dp = opendir("/proc"))) { - free(olist); - perror_exit("/proc"); - } - while ((entry = readdir(dp))) { - int count, procpid, procsid; - - if (!(procpid = atoi(entry->d_name))) continue; - - snprintf(toybuf, sizeof(toybuf), "/proc/%d/stat", procpid); - if (!readfile(toybuf, toybuf, sizeof(toybuf))) continue; - // command name can have embedded space and/or ) - if (!(tmp = strrchr(toybuf, ')')) - || sscanf(tmp, " %*c %*d %*d %d", &procsid) != 1) continue; - if (pid == procpid || sid == procsid || procpid == 1) continue; - - // Check for kernel threads. - snprintf(toybuf, sizeof(toybuf), "/proc/%d/cmdline", procpid); - if (!readfile(toybuf, toybuf, sizeof(toybuf)) || !*toybuf) continue; - - // Check with omit list. - for (count = 0; count < ocount; count++) - if (procpid == olist[count]) break; - if (count != ocount) continue; - - kill(procpid, signum); - } - closedir(dp); - free(olist); - - // is it kill? - } else { - - // "<1" in optstr wouldn't cover this because "-SIGNAL" - if (!*args) help_exit("missing argument"); - - while (*args) { - char *arg = *(args++); - - pid = estrtol(arg, &tmp, 10); - if (!errno && *tmp) errno = ESRCH; - if (errno || kill(pid, signum)<0) perror_msg("bad pid '%s'", arg); - } - } -} - -void killall5_main(void) -{ - kill_main(); -} diff --git a/update/toys/posix/patch.c b/update/toys/posix/patch.c deleted file mode 100644 index b66d590..0000000 --- a/update/toys/posix/patch.c +++ /dev/null @@ -1,485 +0,0 @@ -/* patch.c - Apply a "universal" diff. - * - * Copyright 2007 Rob Landley - * - * see http://opengroup.org/onlinepubs/9699919799/utilities/patch.html - * (But only does -u, because who still cares about "ed"?) - * - * TODO: - * -b backup - * -N ignore already applied - * -D define wrap #ifdef and #ifndef around changes - * -o outfile output here instead of in place - * -r rejectfile write rejected hunks to this file - * -E remove empty files --remove-empty-files - * git syntax (rename, etc) - -USE_PATCH(NEWTOY(patch, ">2(no-backup-if-mismatch)(dry-run)F#g#fulp#v(verbose)@d:i:Rs(quiet)[!sv]", TOYFLAG_USR|TOYFLAG_BIN)) - -config PATCH - bool "patch" - default y - help - usage: patch [-Rlsuv] [-d DIR] [-i FILE] [-p DEPTH] [-F FUZZ] [--dry-run] [FILE [PATCH]] - - Apply a unified diff to one or more files. - - -d Modify files in DIR - -F Fuzz factor (number of non-matching context lines allowed per hunk) - -i Input patch from FILE (default=stdin) - -l Loose match (ignore whitespace) - -p Number of '/' to strip from start of file paths (default=all) - -R Reverse patch - -s Silent except for errors - -v Verbose (-vv to see decisions) - --dry-run Don't change files, just confirm patch applies - - Only handles "unified" diff format (-u is assumed and ignored). Only - modifies files when all hunks to that file apply. Prints failed hunks - to stderr, and exits with nonzero status if any hunks fail. - - Files compared against /dev/null (or with a date <= the unix epoch) are - created/deleted as appropriate. Default -F value is the number of - leading/trailing context lines minus one (usually 2). -*/ - -#define FOR_patch -#include "toys.h" - -GLOBALS( - char *i, *d; - long v, p, g, F; - - void *current_hunk; - long oldline, oldlen, newline, newlen, linenum, outnum; - int context, state, filein, fileout, filepatch, hunknum; - char *tempname; -) - -// TODO xgetline() instead, but replace_tempfile() wants fd... -char *get_line(int fd) -{ - char c, *buf = 0; - long len = 0; - - for (;;) { - if (1>read(fd, &c, 1)) break; - if (!(len & 63)) buf=xrealloc(buf, len+65); - if ((buf[len++]=c) == '\n') break; - } - if (buf) { - buf[len]=0; - if (buf[--len]=='\n') buf[len]=0; - } - - return buf; -} - -// Dispose of a line of input, either by writing it out or discarding it. - -// state < 2: just free -// state = 2: write whole line to stderr -// state = 3: write whole line to fileout -// state > 3: write line+1 to fileout when *line != state - -static void do_line(void *data) -{ - struct double_list *dlist = data; - - TT.outnum++; - if (TT.state>1) - if (0>dprintf(TT.state==2 ? 2 : TT.fileout,"%s\n",dlist->data+(TT.state>3))) - perror_exit("write"); - - llist_free_double(data); -} - -static void finish_oldfile(void) -{ - if (TT.tempname) replace_tempfile(TT.filein, TT.fileout, &TT.tempname); - TT.fileout = TT.filein = -1; -} - -static void fail_hunk(void) -{ - if (!TT.current_hunk) return; - - fprintf(stderr, "Hunk %d FAILED %ld/%ld.\n", - TT.hunknum, TT.oldline, TT.newline); - toys.exitval = 1; - - // If we got to this point, we've seeked to the end. Discard changes to - // this file and advance to next file. - - TT.state = 2; - llist_traverse(TT.current_hunk, do_line); - TT.current_hunk = 0; - if (!FLAG(dry_run)) delete_tempfile(TT.filein, TT.fileout, &TT.tempname); - TT.state = 0; -} - -// Compare ignoring whitespace. Just returns 0/1, no > or < -static int loosecmp(char *aa, char *bb) -{ - int a = 0, b = 0; - - for (;;) { - while (isspace(aa[a])) a++; - while (isspace(bb[b])) b++; - if (aa[a] != bb[b]) return 1; - if (!aa[a]) return 0; - a++, b++; - } -} - -// Given a hunk of a unified diff, make the appropriate change to the file. -// This does not use the location information, but instead treats a hunk -// as a sort of regex. Copies data from input to output until it finds -// the change to be made, then outputs the changed data and returns. -// (Finding EOF first is an error.) This is a single pass operation, so -// multiple hunks must occur in order in the file. - -static int apply_one_hunk(void) -{ - struct double_list *plist, *buf = 0, *check = 0; - int matcheof, trail = 0, allfuzz = 0, fuzz, ii; - int (*lcmp)(char *aa, char *bb) = FLAG(l) ? (void *)loosecmp : (void *)strcmp; - long backwarn = 0; - char *data = toybuf; - - if (TT.v>1) printf("START %d\n", TT.hunknum); - - // Match EOF if there aren't as many ending context lines as beginning - dlist_terminate(TT.current_hunk); - for (fuzz = 0, plist = TT.current_hunk; plist; plist = plist->next) { - char *s = plist->data, c = *s; - - if (c==' ') trail++; - else trail = 0; - - // Only allow fuzz if 2 context lines have multiple nonwhitespace chars. - // avoids the "all context was blank or } lines" issue. Removed lines - // count as context since they're matched. - if (c==' ' || c=="-+"[FLAG(R)]) { - while (isspace(*++s)); - if (*s && s[1] && !isspace(s[1])) fuzz++; - } - } - matcheof = !trail || trail < TT.context; - if (FLAG(F) && !TT.F) fuzz = 0; - if (fuzz>1) allfuzz = TT.F ? : TT.context ? TT.context-1 : 0; - - // Loop through input data searching for this hunk. Match all context - // lines and lines to be removed until we've found end of complete hunk. - plist = TT.current_hunk; - fuzz = 0; - for (;;) { - if (data) { - data = get_line(TT.filein); - check = data ? dlist_add(&buf, data) : 0; - TT.linenum++; - } - if (TT.v>1) printf("READ[%ld] %s\n", TT.linenum, data ? : "(NULL)"); - - // Compare buffered line(s) with expected lines of hunk. Match can fail - // because next line doesn't match, or because we hit end of a hunk that - // needed EOF and this isn't EOF. - for (;;) { - // Find hunk line to match (skip added lines) and detect reverse matches - while (plist && *plist->data == "+-"[FLAG(R)]) { - // TODO: proper backwarn = full hunk applies in reverse, not just 1 line - if (data) { - ii = strcspn(data, " \t"); - if (data[ii+!!data[ii]] && !lcmp(data, plist->data+1)) - backwarn = TT.linenum; - } - plist = plist->next; - } - if (TT.v>1 && plist) - printf("HUNK %s\nLINE %s\n", plist->data+1, check ? check->data : ""); - - // End of hunk? - if (!plist) { - if (TT.v>1) printf("END OF HUNK\n"); - if (matcheof == !data) goto out; - - // Compare line and handle match - } else if (check && !lcmp(check->data, plist->data+1)) { - if (TT.v>1) printf("MATCH\n"); -handle_match: - plist = plist->next; - if ((check = check->next) == buf) { - if (plist || matcheof) break; - goto out; - } else continue; - } - - // Did we hit EOF? - if (!data) { - if (TT.v>1) printf("EOF\n"); - if (backwarn && !FLAG(s)) - fprintf(stderr, "Possibly reversed hunk %d at %ld\n", - TT.hunknum, backwarn); - - // File ended before we found a place for this hunk. - fail_hunk(); - goto done; - } - if (TT.v>1) printf("NOT MATCH\n"); - - // Match failed: can we fuzz it? - if (plist && *plist->data == ' ' && fuzz1) printf("FUZZ %d %s\n", fuzz, check->data); - goto handle_match; - } - - // If this hunk must match start of file, fail if it didn't. - if (!TT.context || trail>TT.context) { - fail_hunk(); - goto done; - } - - // Write out first line of buffer and recheck rest for new match. - TT.state = 3; - if (TT.v>1) printf("WRITE %s\n", buf->data); - do_line(check = dlist_pop(&buf)); - plist = TT.current_hunk; - fuzz = 0; - - // If end of the buffer without finishing a match, read more lines. - if (!buf) break; - check = buf; - } - } -out: - if (TT.v) xprintf("Hunk #%d succeeded at %ld.\n", TT.hunknum, TT.linenum); - // We have a match. Emit changed data. - TT.state = "-+"[FLAG(R)]; - while ((plist = dlist_pop(&TT.current_hunk))) { - if (TT.state == *plist->data || *plist->data == ' ') { - if (*plist->data == ' ') dprintf(TT.fileout, "%s\n", buf->data); - llist_free_double(dlist_pop(&buf)); - } else dprintf(TT.fileout, "%s\n", plist->data+1); - llist_free_double(plist); - } - TT.current_hunk = 0; - TT.state = 1; -done: - llist_traverse(buf, do_line); - - return TT.state; -} - -// read a filename that has been quoted or escaped -static char *unquote_file(char *filename) -{ - char *s = filename, *t, *newfile; - - // Return copy of file that wasn't quoted - if (*s++ != '"' || !*s) return xstrdup(filename); - - // quoted and escaped filenames are larger than the original - for (t = newfile = xmalloc(strlen(s) + 1); *s != '"'; s++) { - if (!s[1]) error_exit("bad %s", filename); - - // don't accept escape sequences unless the filename is quoted - if (*s != '\\') *t++ = *s; - else if (*++s >= '0' && *s < '8') { - *t++ = strtoul(s, &s, 8); - s--; - } else { - if (!(*t = unescape(*s))) *t = *s;; - t++; - } - } - *t = 0; - - return newfile; -} - -// Read a patch file and find hunks, opening/creating/deleting files. -// Call apply_one_hunk() on each hunk. - -// state 0: Not in a hunk, look for +++. -// state 1: Found +++ file indicator, look for @@ -// state 2: In hunk: counting initial context lines -// state 3: In hunk: getting body - -void patch_main(void) -{ - int state = 0, patchlinenum = 0, strip = 0; - char *oldname = 0, *newname = 0; - - if (toys.optc == 2) TT.i = toys.optargs[1]; - if (TT.i) TT.filepatch = xopenro(TT.i); - TT.filein = TT.fileout = -1; - - if (TT.d) xchdir(TT.d); - - // Loop through the lines in the patch file (-i or stdin) collecting hunks - for (;;) { - char *patchline; - - if (!(patchline = get_line(TT.filepatch))) break; - - // Other versions of patch accept damaged patches, so we need to also. - if (strip || !patchlinenum++) { - int len = strlen(patchline); - if (len && patchline[len-1] == '\r') { - if (!strip && !FLAG(s)) fprintf(stderr, "Removing DOS newlines\n"); - strip = 1; - patchline[len-1] = 0; - } - } - if (!*patchline) { - free(patchline); - patchline = xstrdup(" "); - } - - // Are we assembling a hunk? - if (state >= 2) { - if (*patchline==' ' || *patchline=='+' || *patchline=='-') { - dlist_add((void *)&TT.current_hunk, patchline); - - if (*patchline != '+') TT.oldlen--; - if (*patchline != '-') TT.newlen--; - - // Context line? - if (*patchline==' ' && state==2) TT.context++; - else state=3; - - // If we've consumed all expected hunk lines, apply the hunk. - if (!TT.oldlen && !TT.newlen) state = apply_one_hunk(); - } else { - dlist_terminate(TT.current_hunk); - fail_hunk(); - state = 0; - } - continue; - } - - // Open a new file? - if (!strncmp("--- ", patchline, 4) || !strncmp("+++ ", patchline, 4)) { - char *s, **name = &oldname; - int i; - - if (*patchline == '+') { - name = &newname; - state = 1; - } - - free(*name); - finish_oldfile(); - - // Trim date from end of filename (if any). Date<=epoch means delete. - for (s = patchline+4; *s && *s!='\t'; s++); - i = atoi(s); - if (i>1900 && i<=1970) *name = xstrdup("/dev/null"); - else { - *s = 0; - *name = unquote_file(patchline+4); - } - - // We defer actually opening the file because svn produces broken - // patches that don't signal they want to create a new file the - // way the patch man page says, so you have to read the first hunk - // and _guess_. - - // Start a new hunk? Usually @@ -oldline,oldlen +newline,newlen @@ - // but a missing ,value means the value is 1. - } else if (state == 1 && !strncmp("@@ -", patchline, 4)) { - int i; - char *s = patchline+4; - - // Read oldline[,oldlen] +newline[,newlen] - - TT.oldlen = TT.newlen = 1; - TT.oldline = strtol(s, &s, 10); - if (*s == ',') TT.oldlen=strtol(s+1, &s, 10); - TT.newline = strtol(s+2, &s, 10); - if (*s == ',') TT.newlen = strtol(s+1, &s, 10); - - TT.context = 0; - state = 2; - - // If this is the first hunk, open the file. - if (TT.filein == -1) { - int oldsum, newsum, del = 0; - char *name; - - oldsum = TT.oldline + TT.oldlen; - newsum = TT.newline + TT.newlen; - - // If an original file was provided on the command line, it overrides - // *all* files mentioned in the patch, not just the first. - if (toys.optc) { - char **which = FLAG(R) ? &oldname : &newname; - - free(*which); - *which = xstrdup(toys.optargs[0]); - // The supplied path should be taken literally with or without -p. - toys.optflags |= FLAG_p; - TT.p = 0; - } - - name = FLAG(R) ? oldname : newname; - - // We're deleting oldname if new file is /dev/null (before -p) - // or if new hunk is empty (zero context) after patching - if (!strcmp(name, "/dev/null") || !(FLAG(R) ? oldsum : newsum)) { - name = FLAG(R) ? newname : oldname; - del++; - } - - // handle -p path truncation. - for (i = 0, s = name; *s;) { - if (FLAG(p) && TT.p == i) break; - if (*s++ != '/') continue; - while (*s == '/') s++; - name = s; - i++; - } - - if (del) { - if (!FLAG(s)) printf("removing %s\n", name); - if (!FLAG(dry_run)) xunlink(name); - state = 0; - // If we've got a file to open, do so. - } else if (!FLAG(p) || i <= TT.p) { - // If the old file was null, we're creating a new one. - if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK)) - { - if (!FLAG(s)) printf("creating %s\n", name); - if (FLAG(dry_run)) TT.filein = xopen("/dev/null", O_RDWR); - else { - if (mkpath(name)) perror_exit("mkpath %s", name); - TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666); - } - } else { - if (!FLAG(s)) printf("patching %s\n", name); - TT.filein = xopenro(name); - } - if (FLAG(dry_run)) TT.fileout = xopen("/dev/null", O_RDWR); - else TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname); - TT.linenum = TT.outnum = TT.hunknum = 0; - } - } - - TT.hunknum++; - - continue; - } - - // If we didn't continue above, discard this line. - free(patchline); - } - - finish_oldfile(); - - if (CFG_TOYBOX_FREE) { - close(TT.filepatch); - free(oldname); - free(newname); - } -} diff --git a/update/toys/posix/sort.c b/update/toys/posix/sort.c deleted file mode 100644 index d69b622..0000000 --- a/update/toys/posix/sort.c +++ /dev/null @@ -1,409 +0,0 @@ -/* sort.c - put input lines into order - * - * Copyright 2004, 2008 Rob Landley - * - * See http://opengroup.org/onlinepubs/007904975/utilities/sort.html - * - * Deviations from POSIX: Lots. - * We invented -x - -USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMCcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) - -config SORT - bool "sort" - default y - help - usage: sort [-runbCcdfiMsxVz] [FILE...] [-k#[,#[x]] [-t X]] [-o FILE] - - Sort all lines of text from input files (or stdin) to stdout. - - -r Reverse - -u Unique lines only - -n Numeric order (instead of alphabetical) - - -b Ignore leading blanks (or trailing blanks in second part of key) - -C Check whether input is sorted - -c Warn if input is unsorted - -d Dictionary order (use alphanumeric and whitespace chars only) - -f Force uppercase (case insensitive sort) - -i Ignore nonprinting characters - -k Sort by "key" (see below) - -M Month sort (jan, feb, etc) - -o Output to FILE instead of stdout - -s Skip fallback sort (only sort with keys) - -t Use a key separator other than whitespace - -x Hexadecimal numerical sort - -V Version numbers (name-1.234-rc6.5b.tgz) - -z Zero (null) terminated lines - - Sorting by key looks at a subset of the words on each line. -k2 uses the - second word to the end of the line, -k2,2 looks at only the second word, - -k2,4 looks from the start of the second to the end of the fourth word. - -k2.4,5 starts from the fourth character of the second word, to the end - of the fifth word. Negative values count from the end. Specifying multiple - keys uses the later keys as tie breakers, in order. A type specifier - appended to a sort key (such as -2,2n) applies only to sorting that key. - -config SORT_FLOAT - bool - default y - depends on TOYBOX_FLOAT - help - usage: sort [-g] - - -g General numeric sort (double precision with nan and inf) -*/ - -#define FOR_sort -#include "toys.h" - -GLOBALS( - char *t; - struct arg_list *k; - char *o, *T, S; - - void *key_list; - unsigned linecount; - char **lines, *name; -) - -// The sort types are n, g, and M. -// u, c, s, and z apply to top level only, not to keys. -// b at top level implies bb. -// The remaining options can be applied to search keys. - -#define FLAG_bb (1<<31) // Ignore trailing blanks - -struct sort_key { - struct sort_key *next_key; // linked list - long range[4]; // start word, start char, end word, end char - int flags; -}; - -static int skip_key(char *str) -{ - int end = 0; - - // Skip leading blanks - if (str[end] && !TT.t) while (isspace(str[end])) end++; - - // Skip body of key - for (; str[end]; end++) { - if (TT.t) { - if (str[end]==*TT.t) { - end++; - break; - } - } else if (isspace(str[end])) break; - } - - return end; -} - -// Copy of the part of this string corresponding to a key/flags. - -static char *get_key_data(char *str, struct sort_key *key, int flags) -{ - long start = 0, end, len, h, i, j, k; - - // Special case whole string, so we don't have to make a copy - if(key->range[0]==1 && !key->range[1] && !key->range[2] && !key->range[3] - && !(flags&(FLAG_b|FLAG_d|FLAG_i|FLAG_bb))) return str; - - // Find start of key on first pass, end on second pass - len = strlen(str); - for (j=0; j<2; j++) { - if (!(k = key->range[2*j])) end=len; - - // Loop through fields - else { - if (k<1) for (end = h = 0;; end += h) { - ++k; - if (!(h = skip_key(str+end))) break; - } - if (k<1) end = len*!j; - else for (end = 0, i = 1; irange[3])) - while (isspace(str[start])) start++; - if (flags&FLAG_bb) while (end>start && isspace(str[end-1])) end--; - - // Handle offsets on start and end - if (key->range[3]>0) { - end += key->range[3]-1; - if (end>len) end=len; - } - if (key->range[1]>0) { - start += key->range[1]-1; - if (start>len) start=len; - } - - // Make the copy - if (endnext_key); - return *pkey = xzalloc(sizeof(struct sort_key)); -} - -// Perform actual comparison -static int compare_values(int flags, char *x, char *y) -{ - if (CFG_SORT_FLOAT && (flags & FLAG_g)) { - char *xx,*yy; - double dx = strtod(x,&xx), dy = strtod(y,&yy); - int xinf, yinf; - - // not numbers < NaN < -infinity < numbers < +infinity - - if (x==xx) return y==yy ? 0 : -1; - if (y==yy) return 1; - - // Check for isnan - if (dx!=dx) return (dy!=dy) ? 0 : -1; - if (dy!=dy) return 1; - - // Check for infinity. (Could underflow, but avoids needing libm.) - xinf = (1.0/dx == 0.0); - yinf = (1.0/dy == 0.0); - if (xinf) { - if(dx<0) return (yinf && dy<0) ? 0 : -1; - return (yinf && dy>0) ? 0 : 1; - } - if (yinf) return dy<0 ? 1 : -1; - - return dxdy; - } else if (flags & FLAG_M) { - struct tm thyme; - int dx; - char *xx,*yy; - - xx = strptime(x,"%b",&thyme); - dx = thyme.tm_mon; - yy = strptime(y,"%b",&thyme); - if (!xx) return !yy ? 0 : -1; - else if (!yy) return 1; - else return dx==thyme.tm_mon ? 0 : dx-thyme.tm_mon; - - } else if (flags & FLAG_x) return strtol(x, NULL, 16)-strtol(y, NULL, 16); - else if (flags & FLAG_V) { - while (*x && *y) { - while (*x && *x == *y) x++, y++; - if (isdigit(*x) && isdigit(*y)) { - long long xx = strtoll(x, &x, 10), yy = strtoll(y, &y, 10); - - if (xxyy) return 1; - } else { - char xx = *x ? *x : x[-1], yy = *y ? *y : y[-1]; - - // -rc/-pre hack so abc-123 > abc-123-rc1 (other way already - < 0-9) - if (xx != yy) { - if (xxdy; - - // Ascii sort - } else return ((flags&FLAG_f) ? strcasecmp : strcmp)(x, y); -} - -// Callback from qsort(): Iterate through key_list and perform comparisons. -static int compare_keys(const void *xarg, const void *yarg) -{ - int flags = toys.optflags, retval = 0; - char *x, *y, *xx = *(char **)xarg, *yy = *(char **)yarg; - struct sort_key *key; - - for (key=(void *)TT.key_list; !retval && key; key = key->next_key) { - flags = key->flags ? : toys.optflags; - - // Chop out and modify key chunks, handling -dfib - - x = get_key_data(xx, key, flags); - y = get_key_data(yy, key, flags); - - retval = compare_values(flags, x, y); - - // Free the copies get_key_data() made. - - if (x != xx) free(x); - if (y != yy) free(y); - - if (retval) break; - } - - // Perform fallback sort if necessary (always case insensitive, no -f, - // the point is to get a stable order even for -f sorts) - if (!retval && !FLAG(s)) { - flags = toys.optflags; - retval = strcmp(xx, yy); - } - - return retval * ((flags&FLAG_r) ? -1 : 1); -} - -// Read each line from file, appending to a big array. -static void sort_lines(char **pline, long len) -{ - char *line; - - if (!pline) return; - line = *pline; - if (!FLAG(z) && len && line[len-1]=='\n') line[--len] = 0; - *pline = 0; - - // handle -c here so we don't allocate more memory than necessary. - if (FLAG(C)||FLAG(c)) { - if (TT.lines && compare_keys((void *)&TT.lines, &line)>-FLAG(u)) { - toys.exitval = 1; - if (FLAG(C)) xexit(); - error_exit("%s: Check line %u", TT.name, TT.linecount+1); - } - free(TT.lines); - TT.lines = (void *)line; - } else { - if (!(TT.linecount&63)) - TT.lines = xrealloc(TT.lines, sizeof(char *)*(TT.linecount+64)); - TT.lines[TT.linecount] = line; - } - TT.linecount++; -} - -// Callback from loopfiles to handle input files. -static void sort_read(int fd, char *name) -{ - TT.name = name; - do_lines(fd, '\n'*!FLAG(z), sort_lines); -} - -void sort_main(void) -{ - int idx, jdx, fd = 1; - - if (FLAG(u)) toys.optflags |= FLAG_s; - - // Parse -k sort keys. - if (TT.k) { - struct arg_list *arg; - - for (arg = TT.k; arg; arg = arg->next) { - struct sort_key *key = add_key(); - char *temp, *temp2, *optlist; - int flag; - - idx = 0; - temp = arg->arg; - while (*temp) { - // Start of range - key->range[2*idx] = strtol(temp, &temp, 10); - if (*temp=='.') key->range[(2*idx)+1] = strtol(temp+1, &temp, 10); - - // Handle flags appended to a key type. - for (;*temp;temp++) { - - // Second comma becomes an "Unknown key" error. - if (*temp==',' && !idx++) { - temp++; - break; - } - - // Which flag is this? - optlist = toys.which->options; - temp2 = strchr(optlist, *temp); - flag = 1<<(optlist-temp2+strlen(optlist)-1); - - // Was it a flag that can apply to a key? - if (!temp2 || flag>FLAG_x || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z))) - error_exit("Unknown key option."); - - // b after , means strip _trailing_ space, not leading. - if (idx && flag==FLAG_b) flag = FLAG_bb; - key->flags |= flag; - } - } - } - } - - // global b flag strips both leading and trailing spaces - if (FLAG(b)) toys.optflags |= FLAG_bb; - - // If no keys, perform alphabetic sort over the whole line. - if (!TT.key_list) add_key()->range[0] = 1; - - // Open input files and read data, populating TT.lines[TT.linecount] - loopfiles(toys.optargs, sort_read); - - // The compare (-c) logic was handled in sort_read(), - // so if we got here, we're done. - if (FLAG(C)||FLAG(c)) goto exit_now; - - // Perform the actual sort - qsort(TT.lines, TT.linecount, sizeof(char *), compare_keys); - - // handle unique (-u) - if (FLAG(u)) { - for (jdx=0, idx=1; idxMGCAVK0KL^Oy=j0vVYiD+$5YecOHZRy@-*I2jBwz_NDL;r7lTwqCkF@^|C$s)s{NM8bA-1-=@#2rx0bqvof5`uTOR~+*`E!=F z(3IFkOce*2(!xnzlQ&LpG67)_~_pN z01^LBT4pN%3{?Zbo`wHO!^;6+=S2X}Jug04a`Jx-25S8U=jH;y`b_{p9tQv?BLE=S z|2@$Esrx^3@&E9wIBT8vtj}3wy-opqAP2w!RG<*Z0(`BKA8-IT2n7DV2pj`;+1WeT zBX&6;9UOP>a@ymC+2icI=imn(ZeGFuhYx-1|4|SnDlLu@o)QuCQ9?mt%8ATV+*3zr zpPVbsKAVha_RlPVS5AGq_Ak6dag03h*jj(j|GniudH`7)x905K{ombMWVr za33Tz(dqXEz#d{9#%{=N;3%-!bH%`4V8Lgvoe96y$Dh98d&~Aw1gBo`Fzn(hdrao5 zS{qK)`KCj`F?iM5oQoAU@1p5i2{x zi8?dtxFlj4O7s=f$oac)SzsH-jpX`Q)G_{j3l-J5DhKv%oc^Xcrg z&x8TrH&6ra9HFH;8mIlgxlExF49d2BoSXrQvb`tpHDKrGErN6)I4R+;z;uwy${c}( zf(^u*=EQ}J8g=fFAK=OyNeX8w2A z9UpxsTx#{XQ!2qt#5Q(lTMQ^$pMh|8C@b=G=B?f_NW5O%gUWp^`lz#y(3o(~`>2>l z0yRBo?nu#Vr#1egfh3!P1K79p?aLAYjEDFg&~#9>&}k2%;s#m2)VHs5EIAW?Wzi>x zCrsag3?=vuqu(*=&1AqJE6+Sd5^P8v>zI%`4Ux^k1_| z9iJ}##F{58CzpfjO~aWp@tJV1uLYHFjQ>)?__8U_h;KlQN08FGqP28+$~W!4kj(Fw zzZ!GlmT6(}l{MsN+VRWz7YNcV2r0xS*Rp~R(I5i^wyDb74|cg=?3V1pZj`M9FYXbe z!QZ{OG6JI>j~MKO_l7Qjcx$ZlnTjq9sHgOCfIEO^FiV5sO~jT*{08jUDQMWEtXMwpMs$($t?&P48#BD@-VW}CLx1Em{& zvI?+fCcLeDEvdPHJ>&Bhyznh7VfwUYJvO3{uR~vIL0cpcp#_aL4k>;BPBdlM>9%0Kl!M_f^vR&Y*Ly1JZT53exb}MV zaZ6HoY*l2sAFcBSar6rk`ObPsgArH`VAlnm-X|7u1)V`#CXY-j=CrwBtxj&ZXI@U zCU${7Ao(j!a!%#dJOWS2ufUFvfb$_E=mXQ( zSXyQuc-9hB% zRW$(Xfev#3_;SQ!4-TEa7!biL0qEm1IQ+Y2+;@fr%Kmv4r@aT?f?*;NFF&hzjoDg z65w5t^!WL3cC=`-?R1k2$g$@ZyS2D&zOEf`F!8ymCYO=w{_}PSEb3Ml<@tL~nd8E*CcnT4M&)O|6{)&>41Jrao^lw)+gX3t z^fdhiQ|70ID<9vI@pUj7gkR8@>Kc_^JR!)VmcWxR`{uZjtTlS@$!~z~R)+Trcp+2d zDL6MNCOgNVs9$%OBAC|Ng&}rP8Jk4lA7OHG(<@-WQwdr??mP`Sqp+oZkZxGZdfn&2 z&||KKk^c0{?xezb#!c+DYefTL({lyUX2X?c&SX<$D4|-|DwtPJk>LevhCcU=ScgK= z9YtJMQmBTgZe3h-8MtqBRCr{BvCXilq#q=GxHnhRnt2~2#+zW=Nit7GK^{h;E5VjQ zeSoHi>l5opURYyx;dBq|dRX(2SR&4pdTfk^{2at8&1Td^eSoN*c;YgOhawi0^*#Ql z%{}9SqM^t{xt}&~s;MQ?ulAeazIV{P4d$1(BgQ}V3~x{iF2f+k1-WEILWxyeZzy$H zwv``K(YJSbpMypZ4{>HRuK0~pT*eKCMLwN|zE5|q)xS`qx&oNtj(rxd z!CWmo{l=l!*wcdCA5S{3cD1}|6ebFkVcnvp2a>o+00QdEiIoeG+I(T+dT6f}Rioy5 zU-fOcw3rod00o&b%#=(a5AWQ-B2rw6V#{6M#8jp2ayGuc>_HRi92-3NUn*>Rq71iV zq97F?V=oxmupo`P8IRJ^d1Mz3^fyF{9I3EmPhExgnoDDKr!<{omCNd(Noza!N~1UtQ;yReOabV!wg%%q;osn!=22E2bZ1%gUl1W{#7PX z=oK+&{z9U0rgqRL22){;pXXT`&ryO&HZabi%6a9i>pIk!Fw#LmD>^O!!dtfuVi%7E z@BMhOYr4@~*1m$>OSzRYng~kH_Uf`9x{uCUklLugl=%`EH?6DFh6NkfkcXHgAP{Cq zc3c2zD*OlMNzpSIV^!HU!8eKsLP_xhgo zBr16hKm-^9mitbYft1k=umt9Tc4{ zI#)en+w8}2r5ia%@hh2Owxe62f)$|qqXu(8_$IN&s;RoBAe|eU1$jOTfKEik@`_Ily4x}koi`B1 zd$>T!d{ghQ&~bf(=gFZ`;)fu$^ewq1h~iy_QeXb$V_=;86?B7Tvjd*qxaydiWcB5QF)MzO7>7*)cq zJ#{@0=ZUcQHWAaMI^=zi<&lf_Uatzakiv7ssI>AX;jM|kPx3LLarf4-@#Z$d3TPW+ znV?5M-sv%n{n-%ll23Uaj>#x0F&wt$x;{k`~Ykf8STjSx$U20mk_lT=MR@ak7``(=Q><&$L~gGkeyhJ#Ff^=tQI2Fglb}`%`WsT@#MtSuw zKK@=G2i)(7846{^@`hgl^~(;_J3J-LotlCLDK~`E=HdL4kU^|NdtAXL zB8qRnj_*br;V&OJ)dply)u4-$EO^ydCPY}o1z;zojh%bXlj7Gy=kW7%zz_7+P8uYd z?V(4Xz=>xv-w6*lZSEQo$?{JSUQ2w|K}^Q6=imj1T`apO1Kuo;ZgJX1Fari?ILyJa z?{mMF+Hmb>-p1rg`7($DY2fA)EM@xdkI_`eC~u^wb%@38r;n2V5u1J%;QE}3dU6!^ zEyeAdMcK5P=UDH!qd`*=9NzOnhHSE3XqBy-5n_P*H7i94Z5V+a-Hk{|3S4z&Tj zKCFHY(nr&%D9W6>1m}N4zVaY{-jh-)i<4L^)8%#2HQOPTu)=DT^h^dOUi;XuGgnE) zQ*P$6jBD=+pAfi@&BTUQn- z+41-9ErEqBs1Q{oIgj=zD@}?K+YJ~~Q1o{~Y@J{FL&}UQ_wj<|V@p-~gh8<75j9uQ zC6}*b(aR3C?b@Axa=rKD>j>K5L&{QhAX0fLhyV&@=Jo1Hd{Fq+{2UkljKcn7Ul;ez z6HN|RUcQAdP6>w0ioAEA+S{S)evgiXmW%Lth919b`WG+7h)#Wx9xYwi_492&NBbO( ze|1EE&oUh~o^O!v{m70a@$$&&;OMXa$3J#1nEa6P%quB;x$)dOOcgf=osMLKhu;rx zY!WBNtw?x}8e$Ak+CxYd8@dHyE{~t-NZIdxaazOf%a4suqa3eA`d=A9MR2W1j zM$@Ykf$BxFY(1xwL#{dRU`aQi=ySNm0lqZcrTCXX2dh&C|7st5(|M`K4RvbBaCmu! z`_(<%^P&!Fhv4qsA1F|o80P*-TGJB}-+tOsk)VAaaDLQak(?Mi(<`qbGc4VKM*%De zS;d6k_AYPf6-4_;>F~6n2vriL5f~0vcC|L6^fU8>E7RmgQL}r-c(ochgHYwR7NB_3 zep_+>Y`JOhSh3gMdw)lJmBvR9D3m;mB1`xWsx?cZzR_#0%4XqtNPoyD3YI4&`o*O- zn1B}(Cj^y{<`WnZnYSia;FF10C;2x>2<=6ZsO?VlsHbIS6L;6gu1Er*5w7s|>9FAb zfnIc!?F2>UOXb=KoL6fX+zxnTMY8iFrH~L`=Si;jIq>TFz0qR4zD$5`2&SY#%pWoi z;61{Glg35Q=lTVRySnZsrNv=%T+2q_rq2(Q`IpvvRs;gg{mk$co3M6Q^Q{imC2ced z3u(WSGQi8I{W{&xRu#k@mU?C&Q17Y=6dkd)shfVIf$*AW9kfw6vf|s0=^;qJ7yFkQ zOpQG_sb_h$-FdKXzR$+*3@3k+RGe6V*@BRtejUAK1avLzImMf-D8z&2lG7PBLb}nh z!q%qXa}e#N4E{hiL*GKYFdy&%o@0k}Rr43)*;@V_AC-9kiHKvpG z@laSx*c>Fvg`7`8VbffPO7pJc((07#=3u{fAmvEaXDdlr@*96qF=VFXx6s*==%?2u zlwhBM7mI#dM&!*4(7}u-t+0Z+^3`H5N_Z|-tz`8{7Qu6SLtC$=ohq0gj0JC7?PqTj z+UWy&GwvidZ_dN(b^<{!IT?c8KnWFXn47WJuH57!)8$)4*vJ8_g*pCcs+q(4v66bn zdu3L*(iZ;U>>Xbt@}a#U#D!OWkzFooBwYKfhbI4o9xVa$1Bq>$E@(4k5>0?T)zgn- z>W`974XQw21W+&4KK6e2bywLV$=g$Fke3@ixn&VSZo>N%SBboU{ZRgYEzbE7iaV$e4-zb1XxFm+*y9gf8PD{KSn)s@swh4$%T z$@AnkEjwGr;&@HWAMQ5`^D8PZH{1q7e6i;}1}x~C8n_ zwjKA`c|ikyTi9!nt`6k=`{dm<@DFK@V!099ST9`QCK81CXUzQ?@ zc?aM>3>u(mg-uVp$oE`U6!Yy{d;eizmjRD}NAT|Ty5ICppPY;z?TNZAoFuQXOLyj; z;pDZac3K`d8X-})YqT+({KvUxEUB6Eu_o4D{dG+Yqh`$yZmXqng5`>$v z>Hn3m0IimW&xye${c%+(dWw3->*oC5fDl~-uNjAAlm3>}xlSXRU}(R>OCN%3yV^Pc z>H({VPeYXV?xk`&5<~MvmPz2D{p@FkRLG~&lNtgW*-xR)xi8!y<4!ZVk8@|YaE*26 zl~}mRF|zT4086EIHL^@Vq;cDYidAqgSob>sDc$S!Ky;!ZwjBoO>iAs=;QY#O>nIDi z0d@5V!Z%Pj&Cp2RhQm%6|26u_fEKz?=BdZmC%7&Wo=l%0FNR~g+Ik>||F&2z z#X}oE^$FiFIu&5qe&0tFc7yT^_$V%rSR4guctb6}Y@a>=u94N=`4G0hVcpKOkmFP% z%PP{FTvKu%?g$(USWI*Mgl1ArTbICj6iNBE<^X=%RZuuS1H3tHASwzPiyFQ!`y@tDd^~q(>*{Nv~ zK5LN8^;QgMkDO-|WwfYq}o-*IVC6`acg=lx=7 zyc%vxEvC~~pDshHg5n-*22R6v^|VDwV3FzsS8;IX`ca%|=f0?V2B;}u<+ec$M0*cM z6?$A@fPmKjwsbE^dMO3hy#(l7mJ z4*G@^SU33da-+zDy5q+x(s}TGce8QN`2Yi>iVlknobP&sw?3MLDLT00_GoiMVywOL z$^J;0zaCM*e;>D0OCj5a>^4En<8@)CMNYPKK($2#DNnV_h7nQrz3YYAhtZX3i^NE4F>^y@qlp`a99# z0wJbH)EYDYObbt{tkrN ze$ITkP^5VIZ|?tW^L+uo+FnJ&X9-G&*pR>{`)E!%5Y@yz`I*U`xE#wFt@oCjho;Po zy!%vLa&ir^`fPASn4qPSsk=^P@xPX&orema&S)>aYa3*Fpgjk!1g6mQ@kwS!oSRy{ z%3tcqEqyDzDtfXWP0r^;dno1!3HRTgd?S;WKEgZKtVIXu;#OHhOcv*oX)n(^8P1G8kEA1OQ zu*CJDExQc-P%d_y^6bAsejGs3O*NSo&67nOUNW1gp`OsIn$5NgEWdiH2}o647a0TM zDfxGvL*KcURK(s3c&cA4jk^wa;*&(WZRgP>@-KSVK}*1S^IV|_4|@6lerr0u%Gcoy zNY0$7h&U%F5<$aJ?z2+hrUhPCP9XQ@hR7h)Ih^H+ZV3sni$rJ@B{w07%|>0f7kn{n zg7I5}vY5ijIgy*7k_jrEBxX;C<~TH)yJ0#ksI+P{V5JKWk@GIY6LeUHmPrC7zx7UexjY?q}0n>*bbd-2ebozO2w zjJ6EPJYEd({T96oHa3()S+OPSGvh)aR3d)aS2Q9Vk+^{eIjpQ)ewi>w&rhbg_)xHT z?`f{1X?K@0r&a*d_Y&vHMpu4P$%zj#j$MLf%#&7F{dso_j%M4+a=h*Lnsr++XS|hU+4LV$;|4~ZX+%F7JY2k&k zU2av>b?^FLf!dLCggQ~UlRQ3qk>9-zHVoTYvB^#U0*os67R=!;tb>hs?@xp3+jjZ< zSy7{V;RX$nC;XVudkLwXB#0d|vkUcHFX?Yj^^Lfa{bOFjb)-BwzGMC>f zM>>p7F{2E=TjQgBEVk9K^l?kU+4>FK_tb|+@;<9<<7^x%wmlGlnvT5M+?bogOC;f( zo4qDi!VZbZ-qM}GA<0xki9w{^^VbJ><`%w<6Jp_ZuQ^X!V2eJnq=(cJ=y#*EDdY3J zxD{I>9o?Tcce?L==v#QVYhO;OFH;X`u;U`Sy6XeSY^~ZcE-P2E{)E3*I7cWule1KC z-nj)mytz(aMtbC}fGbD(*U6xq(Q~_e|L%ESV4&wfGr6OQk+-*zH- zo&mxeQ%y$Kfh>gzR3uDV+G*)dMRv?pDtAU9$yvJg+9KMP3nfRFR_|$`o(DyXJ->B~ zZ6pEmW~)N>QrL+9N$RcR5q<}NCOwL^_l@027BIYxF$SyAe$|Od4&>1hQ7}s45$oZ+ zElyty&y^_NoDsm6E)WW?#EhYT!GBRj-M1q5Q>AdeTS?-aAPjm`#LnGOD20}ln1(3( z&n?OZ&lPYhQn9)QTUvZZNNaE0StuOW9pWa*&kKOXV=tFml0b1!bcoxvy2Ru%WL=To zI|RJ+*^}0n!r7w))_V0sta#7%=P<7khX-PE)4c1Vbv5^{XS=tRo5pSg{uK7xoBpV4 zpSnkn9;Aa7)EZ}JVRG1&fw~-9GObC!=S4=ab;WiqC;#dPKUokp zofPRn5+@L~h&P@yB91X0!3E5Dm=Bax$)OYHz%sT^glmEHA??ro%570&(WdY8f@W2Y zoL9RRiOjZWcYc>Gj^(4Eq~w^nzWOB3SQmo&EF{?Z-{i&^h9O%s(jUMh%b=` z$bCwgz~R2DkC)ZA11ZV}jrhM!id3&%t(Ha$T0?wk@Zl6?0Q+++Fcot$;)#rSG90Nl(od&ow3@pGUCa0oC zUN0j2?Ndl+EdWu8T(Ds<1Tv=X2#FhYeYIfxqJv964?Vi zJ{kwaPm;z;4Y~Pqn7c2<>GUy|Oob?2c!y*sNoy_CmsJP7YYl&E6E$J=HjPe(SUQNS z@tdHuI;P9-z_M++b%T9*U3xHnZ2SS}+N`IJTth^S`xoNXBQoWdO~wuvRJ1O!+(h(p zB|1YHClwU@5~E7%C65_D!B^CWAv2&U0(~Aw z%rPmA_n2bu11=cA`#E8cG4=RC@kcmud@8r+P;IeOSP$VxK61>AUkP zYq56PLl8+1?kLQK>5XOq<*zDN7xoK#K#A6+>DbSQm@#3qQVKTB`3bpc%QqUom*$}W zN|B_*m>fN#^0yfMnShQs{Y2cNkLS|sL=wF0~%mhGa3 zr5cUg?I0C5J#}~%N@|@-_S6tq2nmR!_3>p)A9PK>j ztY;ed2}u;@cwoE)%&Rq$?$Cv|-{#y$)yO>}?;Z#Q&sP5MAN&&^aGrsC+Bw@+8%;W3WuBRlhl{XkMRMz zzT5M*tsLhYvH#>~pL1xif7z}mpJ;JFIiQ5Tb^Z`YS`T8cMPHiU0;CT=vcexILEjIe zUcbfCdInk!bHp5DXs-M;MZ&~YvQ6k4hhIzzZd$x?H_ILQ)qP$dt1WlnO&e9ML} zmQr%2RPW(>!Evp9@X~8f)F5ZWzI=+8Pq{>gP2K=+**VhzsY`jCk<};0?w88=U$eo= z7xf+$q0`|qK&c}i`W*PP=TL4Z+U&Rv9yTuSy23Sy!vo0_OH#|=ws6yfjkgVi z2xvd0*??v1ApWgcTxny1D@_$87Jc*xDqggZK*4?DH~gLr;%T|>tMCUMtC9H}B;qq& zu;0vgxDayV`l-RrZ?=Lytfk_6gv6>#>FQa(Pk=JwjEel=O2;D2>XdoM9gexLO*bEb zJRFP=u+l__SG?=1N)u8xGE+)R@8sEvCJFa^zs=<>g7yp+-F<$VE2Eh(r**j^*)|Z7 zLb&DK`Uy|;@E_)np<^&P{ht#>Ysg)^(Gv^EltWk9VQ;SHHrzz3w)}?!1h5Rz&1WLk z-hZ3z=YZEvV&yAb=JhK2rS2*vg=tu*zj{JY6De$DZV?{cpan4dW94MhD9*L^?4{nf?c&5iRMUgst5a6fvAH?v>5@5|< z?XU!UNhCX#{o+fZYW($bpI$JGn_bpA>^EF}IUY~}dM%4H^x<+1pS6&Ml`V=_V(4`# zotkQ;5@vNIYW!(yr^@|l+P5@~Un6FEXBHFsy7F^rwCEAVnbe||sX_VLp=}qT3OfA* zm@i-~yBmWd2Mxhik6KzWg=(tYMqB++`vWJ*={n&qjKd-%h@U4`iZut<4nK@Y2>Mn? zT#wH@0!(5CBl$Y;MowKWPqRnbe*(WT?t84sB|_sYE3?bp*m78a+!#oSl*N)G;wiGQ zrR5Mx5ewO$aq0CMEb7*WTFC7w>XekK0#=`;IjK%jjzk-K{3jsvUW*v11nK4fI6XiU zD*b$3h}pwd44tobs*Bb)lz-r@4Jo3KzsT(~9q{qI?+acM70t8Fvs?*M6u)aT-ZkP)IOp_*)`^Omeon%viZKq6y75$9Ka=wc{ zA#}xsy{c5g^r=&1sIu8|gP5D*4&mJ=JU~N0*te(tq8J==O8?^V&QZ3N0^k_Mg*5yz zlXuNnlsNsB9w{lP0PX9M_ehA;1s|7(0C;H|Ll8;|V2(2;lcO1PCsYZHU~2O}ALH%3 zkSDi!jRqHPlA%ilwXzfVrJjTI$C!$Uvz~lOxo}Rr?Zjz^QIER6tXbjOtDc1pcJ$#8 z4ZRv($vq&zQahMEQ(R<(6d-bwiyAoj;y3>B(tM%+LQ7YVCw|aE5+z68^K0YhTR{D4 zCwbfwaMR2?{Bra@!HBJ$9ca|$Oe&{OzT6hwUawvY9R%hm?+}?o~rD$E{;2_Vvv>=mOE<>I-lQXI9vFhl)FG23gna4PWsuNk;fe z8ncz$87r&_`M`O`=mzqWj&xqAjIv$a3-aRxwxnEwJi?L3nz8-@J)1zT(`}0&LmkS> zxphdq(*V=qr?mRPF}qyBp;|h;@E)|E_dxE*8| zl5ChTCMiFZJ6uVGKf4Dz3LKQANe#{qV+Ah_nd%{Vz!t`D_Fy~(=@_7*3&&J*;1ks< zIBVXYg?I=!%{n}&o(kfS_QRCHD_bz4at_BhxlkYX?~E(k!i*oy+9Q9`+T%^yZM*pE zFdmQ`-j4mjm1}oz_+ji!VSU#YjG%?N98=l8X~vPZ-by^luaAN#--(^LU*6E>)w3W6 zOOqD+M_j>+du_}Z2vQrJ)EgA_cQw~GI^(l8Sdr{W63pOaX9jjp? zkEkS1$`1>nx@}kG%g1kQ&fi~8)QR@Rj!QYw6KjDh-7odeP7kOGl&9`%DG4vDj}y!9 zgJaMB0I5wopxg4A?V~JMwL$$~+d|9sE_kG9NIOG3DN3>3h57n<6?K;j_g+-DW3QKM zG4EN0aAy8no~j)gnU9&zX%*^(%H(d@b<|=%Ymp6|E5fOP3~*=Z0~O z=suVpf6vQpU>eKbUhXlWl{6_G1zO60uk=(8E(2=h@un`DyVBTkX8s^aM0MqTw!+kZ zdHg2b!NW@1D^T=_s3QR3p54oO!Tx<2!oMbYL}>W7+UjJ7=w%$Y?06viJg~5h42H|X zJI%HV!bWf!sJ_0qQ0nicPH=*baUq7l2Tp^QMi4i_Kj=PqA>R7!1!!w}Y&4{jLxm#7&PtIEsjDxCftIwJfJb(c)>~*b3#=TlEt^SD4?RZ` zn+ge2Ozm5R>~Dyw1`gXc&1CAJKAiEOZBS?2iuZBHp$V{p%Wv20FiuP{TyhCW`eoVmqJu>gnT-#8v1;P|sZDeR`1PXbCtfl!%pM=olY*1~5@uIEk+u4bo$^?U2UH zmg!GnSCZsCK49*9Y?t$BoZn?F0}X0<5}~`7+fs~!G5XV-7UaZ}TA%9Q3@U*40hy?8 zQ#3D)Ci^acYpsO(m%`gM$?z{Yh11CNsz)Cz$_c`d>kY%89{)QuQ!%UpE-J~J0iqxO zm6qFL-s1T3HEumZzhx>s>cJGyZhK(9E}eCZOjJ&dz(Re1P&E%c(K_asA_rdloJ2AMvYlrQ#+u}sG_nkQ8H z_ZlykL@XQ@Qg4?dAMPq9ej@Ev)K%Jyr30&@->)vB-t-18oDhCIlZHN5E_3*S4YpA$ zl4EnwF_x->(lX!I=TMS}GOw(4^awJQtPgw}_hHdcenUwKZt?)XKthR*a#e{14*|yA z`6hLp)&oMXtX_n-SkF832J~gmlbV7`MbUBeOSd1H*zjLe(3yg2#3OUJ5P2K2PJAFd0&dlQcb70Ls;{3q zQsAuLYd{BBfv6uND#ndxJUhn$X1mPtF=ibS%1qI$3ZT;U_=|_sjYJjH62wlsTyQy` z!dyfXM1;!}hgZ-WDcMgBTb(j93z57Wn?oL+HRLWQASY7FL<715Xiu z(1k`JS)uu(%>$Z%4*xq0Ika0kzfTLg6Wz-F2o&=lxGryz0=%XAW?YU?L0?toD9#&Q z^V<6e5=}o1#O>0yyo>4TTe0noJgyo|0M7?XQE@~zXl9;g1Dyt`(bI~!x~5P<%CePD z3K^RYRqz!>VRuczF(->Ey)QuNOVtd~&1K)(>ijVzfgV_Q4VWMf(AXK(`#CY;uy!h8<9bD5Ys9gLp~6wo87djABCLb5DR*CoaLWc=*W>#d@4x(y`+& zUub;n2ozFP?(hRhiXL2a6}5Tx{* z87WrMnU#$4(B{s6AR+v@{NDkJ_Sx{SfVu)H(Dvy`BQEyfC#_Poq*+{Fa3gRt=!~Xm zNf=43Qgm*kiGXST?A}F+1N};Gz(SrF*71h_yuIFV&S=#E6mQ}MbfNb;O&b^2eW12T zOxloMm@l{CBDG%LP5Qp<#kbK9>dfGfspNqvrE!nouGbq(f#+K89FVUKjA|D?r3O`m zl3J-tcbHzko0gD)58%hlcralsV~(ys*m4v4gs6qIR3u}ZZ$y=<#PKKwXx`1Jaz z&nVJ92sdxq;cMabUtylwCp~`6BvDDusP}xYDyCeBpTxKF4r*)tzfka|qFal!fI8n$ zMx>DOwKod?(87oY(-ltj}EN5=BGU~;{R<-LN=?ku_QJOx8tYq=j2nLqh?zE{A+ z{@%TZ8X=X#xUbu*kHT;4H8(!3^?~7ah7f`TRNx=t>G9Zu4{o#km6jZuueVSB>~Z0% z5`tEjI~HKRXIFqIO-!~C5UKP09e7Dw{o1Wr?aB4f9#8qIs#-hlx0oN^c-C>%i@fK*D@7!f6>$1^_`aG7hUjfm&j8MWQy_?@7=&}`dZQotAPxeKo zzh3sTt=jfdqTeJ%@y>K=#V5L<+8+w zOzp>^!$n*jyd9QAB$;6SaH22;|Mo32S3i5am}%Y8OX%Z6<+u{CWv!3hY3oCLnM-c_SeImy(EN)HjKs`3xm&36OVA_<+J6BHLgT|0y538-~ z$PHwgFgNxyCljoD7z4R4%BzFw^p?G&$4XE!R)MFujdm0|Z9jByKs$!OSCc-|kKJFR z@wy&Cd{^BioTosiK?7+y*;PjIM- zfoT%P82UKLktc&-v*$!=F*x2(5@c>Ci5M`+U&fNa+u(wc&y1>D@PtpIzf6}|58c7b zMa-w8CNG_E=8WbMEL{nmA(-YasgEV^iE0D5E;MaOnGl-%3TC(>(zou6W-rJv zpYf#AY`-xykXRslbSM1o7Qi~cpF_L_lE>ta-Vc`osw=++d`!ZXgCeGHM7P~Q9Elz} ziJcobs3U!}`W8Bacl&C&h{HtDjvNy}M!naCWnQrT9^8;dV*>IjZ={kYxxa8VQpRrH+x zqLea7LhY+hni`j%pME5z778Ulhd=$1vJ66Qf}#+2IrD7Wpjc7TETrht$`3At=@2Ue ztTV+kSO2k-GzPte*2NffZQ8FYysP!U&Sn;t#l~OI%M2GvdasWWa(T>lLAzA zZFWFuicINy37aF}f5qE3V(Z;S?`#%zCu%fgJw-|4#>G4xow z#|{>mQG03(t2Q!RSGlt$_|ug=-oPE^jxJ(}j&|RN2xd-_XP`6_<*@#rT{{8{f~&my zSsbJv6Mql9-Pog|o%5dn4StIVUDyoJqPUD=k@8FJ_ZH-VJKlkB+UU^WNZ#)N6n-5E z2GcHvVb?-J8MTeAxeD!Wzszie!SM#~>EM_vS%Rpb9_7%C8Do@Q0Zp~#8P-Kq=Ds$r zI|OLRzd^K38~Jx3y73d4^Qp8m>#(KVtNh1|KKL~7iODmv&Hus%_}PHi<d_P(*>pX!goF+_NeKViF4I&OikN1uhLrNq01)^SUHlsN12!#=> z;hnWnfatF=pF}3!3~U~AlwW`+b1D0C)g;LOMbX*#vw83T|GILgLbOAus4mqAV!lkO&^Sgfk0eR%Q^7*`9ujlif64`mEpZ?(E-C$zCheK~qSEJYB~7BJ-49 zt3Z>X&*3R}SQ=&k%vy~#7!fp?SH$4xDq@y2U2w3`tp)7ccc`J71m-YRc?z%%n7nIf zy@*t1tqMsmhpu-6Ov0DhQ;Xlrql+lU?NV9$Z{;eisY?{#c^vqz(bx|-!?ow%a>HIX zE2FH}7v{~64P54&{T9?~dSWMD((Z5%^(uFm_+=BQr)74rtwHi*hbQ@R(OI1nquu^i zV3Oqako#)Z5ardNmuaU}j>GIP`E&L^#UQ#~-$>DSM)aY*)Gofu;V}%AJJsm{`?{P~ zlMOogA2a+FHdEy_>-&0{rkmu#7sUdvn@m z5TB;l1Uj_-q2V6vXBZZ*{o*YHTEOv^>{ z50m(QdZXcrg+7q(>~%M^jmVF9RTkK^8eth9@*4BEe)vm15gMs;Xs+$6M2qPYm($&`{@P4{DwyD-{ z5>J8%=jW@Ux2kiVGAU(gY{HVXeE!n0|w`;A^VZ5`}5@Nr|2E`b21hWAgqjM z^pfNC{Q84XpCaavZ7!V^(OOl%G)nio{{mF`5R;n*<7pqP=Mc3VFTNPl;&fAC7dm?z zdd8fjwRY))YEAdr#ftwryIxPMWqeM$M`t*q-sM+=QSl)SHAIUf6?@aA6+Dg)+F!?C z2?A4Mm8ogft0?)7-W+F;oiVQDP0~X&H2+JhD1trgVbJ|ej$8WaIGz?_{)k6>Y)j|x zYc(F0K)nt}yc=RfbG`q$O9`|c9j2o9YcK^z28;>spalQblLY}Uhk~}CKe%p>f}+R3 z%<=m2J8n71V7K(4T~{hnkFvPhs@#qYK%iyiA?byY-}AvepAJe4<*6 z>Gsie{nRyl4yx{mSZ5uJq~maEakta?1htiMzA%v_rwcstp%b=kq-oWjtj2x40W!e* zpEZ?(9Fvt6nkG&yS+_5I6H2B5@5)_U44MfbUCw*BikqAUGMc^QjH*jO=5ULl-r+Am zGuwue{bI;cWNF=#s`K=dar1VK`(~oQ!?ek= zQ`!Ad36i>{ZdLfA>eXBNDK4e!QC2MEF8HXnjsC@OFV!v!%p9}}S=^@ZT@_f?714mn z71YFv<;KToHUis~+B%`~n>1GkiW@H<_&^`gj@7gD6yryB8w9werH#!h38%IKQ|}r+ zyM$Vgd)W`%o-a1KYBZRKSBd|sty#rxE&U_j3}&6^RpnHa&hY}C$r)C`=-A5K^frBl z2F&{RaGSI(RExY)EDsa3-quZ{l)UwizWu~<>7g=1O4fb0g?DckR$T=$8tSHL>2v;0 zG%ZHNem=QjvlDL@GPmN4h`tT?r-drit?BkYTQNxb9h&rXGg%vgXjR8|GW-7~Q}uvL zk?Ea3qZKNvm1o19KMgDfeY}d1M~!VzUqn%$mij*Xnm$64!9N=6#+9|X)=2Uc-)7%) zm;=8oUOAQ;(O47=hi%1?`HzB%@swZfx%i}+lQ*9$0E<1OcjNI$&a&TQ!$3#{boQom zzf}Pnv%qtvr3FUgF4@c*g`)gf@r^U1f*52}+{?$*ZwyWgvOYOnvBB0X zWwin~o9LOn+)sHeTH1)e$rF{nG{Ibk#D!-~$%-B6CsExFrIX;9?w#MCmWtiZ>O*t& z0kR2nn(Ea8wC|@O5i352CRbtkQT$W=28-GU!!zfat=q{<2S81Jv(TGC({=Al8N*)& z7=FSHsyFs2W&MkSq#K`rew5l(V1_C>p74R56?ux|AASem2gF?|B1|GJ0|(0MhuY{h zzzX52R6OHPXH3%GZhV@;%z^|h1<^0Uk;5|r2d%~Nr70)or{R>yOYW|1(Gnlt{@RfL z)Fuclj~@9VLS&y{A)!t;ruZ+pLB@;Z;?-`vj15aawHe1T1iTMTp<8q( z_vN>uX;QT9_#T!nM?a04jVN?u0~&9J-KjYqzsdB^GmG7@Y{8deZj{P$}rivLv> zLA~l>UGGU|6{75&SN7Ip;?jR%6r41kA9fZ!B72~J2-R`SiS&L(*uhPZWmBxP?Ab!lD$i*fWk!p9)l3R>VU^ulL|SLY zgKi;85`4DTUx^7A@_t7jHJ@3NPDA0_o|K6ip{lwGv!15ohh<@-2u3VBqhUxhbs><$ z!yzd3$C5zCcy{T#At0VW!};1^m##Nh_lnEhj;`XCu=z?yUTd>Onq#WVBAUE~kDuXk zyy=!5RA>_QHNd|8Tu8|rK0b%6&Y9MCn=@~Nb*xv-xX1OHR9Q?WPyQ>^#j!6UO^QC_ zMUD0Dzk8m}-_!Lgwk>D$v47+Xso&%FfwW&16`UyS>&I43LtbI2bV2vev+1H3Y`uTD z?fs(+h+`9}5t7h+b5LF-AQ&Adk6#3$$2_jfJecov4G+IGXY!WMp%#1N*)Fv;fVeXE z+oM0(CaTE>&+S>20$W9~Jb2as*SUo|XPI@GodjkJ`1L|L^LXzEYuXm59apX~0u+^i zmt@PSOJApR&O^L3F8=r%6nMZLd+*T0maF8%|L&jv3%rdYtg$ERQPb<(W$L^FuCtbi z`E%)wy5y86Rq#WdSf00N9b`(7vptY$t(yid7H;+qC&_Khk3kDoq=1tID&7ymr}+&( zT`Ga=KhNFAY;x<=qJ^?G!jW4@%@<-}{a7PXc}(BpTL1k!ol{*mxmk<)A3&-OS%)dT zT=jkP>8hFq9cCopvHt^JOqZ!xTkQhgX5jN2fn#BpTgE&8@ClyPY1z!KyUS}BPJ85? z!EClpiR4)NTG{PC(wtulzjK3{khmbcU;<7BW3nj?KboYLf$sfSC@4`9U6qWN1l-mS zYg|;8kCMBFPY$}9EHgt{$Cl0g4|2cRMqZF!_Vk+Z;@gTwnTvDdGBESVI<4k1KHPf6 zEAOUK9d(y=qgQvq?_R%&77%`b$8>G-4kfzPZBLu**{4C&{tOVdv5)B~rsmNoTxZUgnVs#-`8~@#hYiP7Y*9T*b~&VC6#7!w z7;N?D@vHBA-Wh=dCvc8B+u*^*+DXICDdWQn?q&I9zO+S;npP_wQx&e{b*lUsNaOEX zcK$U(4`wNo{?R5`%RkYCXv1XQB)!WI9CE#6<$dIKc;nZWGJJ^c3BHmfTSa|eQcpjY zW?7#|5AeXnI8^O#tj&<-`l6Z{#X`tyHS88hk{ISxs-10a1Pc8zGUxX90|kjc+r8H5 zH5&dUlCFwG;}9+PFgn{!*l{^Eh}Hs%Vt^H4E@^`Pvakf=daMm$j{c^c^-ERkmxr=YX1iiAZ1&+LKx@E`Xs;tr5xVOf}BXv*kC@~})qtfl?) zB=J-mS-x)FjAQF3l3)EDY1(zW869Z;M2H7{^4;#jH-33U!;d6{*Y)%zpB#+#F+XuQ zz>~HP{K5(SxP0z3y7o->KCh#A0aibJyL-C{eNw_6JU&NfeLs6PeG*G&GrXvX%g8LM z0huQ|3;AE#CqCG;PfZ{n=Bxj!qr^%%~zY z)Rb5uV`WU6kXO@k+tSAIP>oE4^_q2DNi<6xW)X>3kt%%B4HWm%3W`m2N|MBT^)u^k zSAFy9<(6v4YRWYnNh$uQ$yg@?C`tHOPseN?oBaeeJ<}Q$7Cpa#Ja(jL(k%({6TjurXq0m7g-Z#*8BaU5XGL+B5yDHTYPX{%kOD)&rxx;a zxn}g}B_Lv^Pb{g&!el`EAE!(aUA2BA*zYR5?FGO=SlYa=R zM2)xX6TCx*ZYll;43(z6hol58knm& z^qu4DV&|MMK8z)z=(L5svBlHGH!b-lORYY6QR{i!&Tsup<;R#Lx@%3fJp2Wc4Gxb# zu$SEnqB{m>Wwj8NH8xOnPpv%@rvAFE93LHOliJ^LYcf8_ELzCUZ`%@03=pk?#bbx6mO)L? zpCJ!z1$z&@g|xy+@pkF%^-Zh)GY_CCD~Rn$_CC2o)-n}>H$yFSupfKbA0Y$2enjmY z%v!P}C&RA^LksA(Sd|RHcW3y|oc0)QsuiQt`spIe1&`*hOdgp0hH@1SQ9rff(Zpk3 zA{!Dm?WMMP@}}|o;bXu!#p2Bh#b1Y;@i2NFC3JtzH_W=lQj~+B2(xo0{F*`op zDhS68*)njAokLRQ{aWV#g}hlt)1crUg7Ab=y>o!vA2mft@B(O3A>KZrwnmaL22oFoP$+IP?0l{zWTpXU2OC3M5!e1IQ*1x0MrC$>$y3-F?84&ju@n9U z(QwKRVV{JUMyIVuKi$GcH(Po+hEzQ3fpToxLKD?$d}P|2CB>)bfv`q>X!eNFR^V*W zZ+RTAO%Y7=TXj+)Q7G~&oke+SJ>TfKOPS=voi1l=0=%&p4aU~L=g~ncUVyhmFbT6I zS=X;m1V;z(LW+?Cm{Ptzsx=fyNYe&F)Q=HZt2iPbvU!!)CCwu&T zdRM`8&FOA?BcTz~JoWCW5pi0%&e`YjZ+ZrMuGRY73kk#5svjLhSc@P=xokKjsz?cW zw!AsIOUWHKT~q27Ab5Cz)ZBZ8J`^xU8WT_WHCqxwO6WM8{ONLy`XywutLrN7D>w z;`z1EUB$CJ>bc?;S$%ZmCiZf?=|`a_Z7j4&{pnoZ2GuJtIwQ%+5GG*pYFZg}E~*Sx z`rA;3;Bubikm96ldexbaN3a4ITT<>yz$*znM$IqDl%)=zSK~?|b;+`DY)Pu_`F-6^ zVS8lKHH{QhdCJ&h3@I<+?s-8R zJvH~}Mj-8*EU)p9>a^ES6#nL2U(;k5et<^!=L&cwl!2)KfRWmzz)*5@^8t?>&VwYR z#=(p}qhXJU3m8N%p@o~6A+R*KN0gBy)7y|osz|sPq~b$Zc?~%;8=(>^8_hcrtdRZ- zK17G5tO7S4#e1D!pDFrb1B^WKd;TI`yJPX;ag7YE#^&D5voe6nkLsQc$D2pF{&TAU z6O53=Sk;9n+8g1bRd`FSb{#X(Qt7G(8zv|Bx%a>wti&Gw%)vYePfqHX;JNe~YWpf) zEn3n|1&Dn8dAJ}Skqwrqxc{U%e;UgSCR1vaLW z&G$<=%46P*(EFTTL%oIC7MsISLd}%Znf&*8;DfJ1U8u6t>oGweECp@mn@Y1q$Y1$K z>FlapA_9kKvfskH-iY!*2cWqwcr8oUI)~eEyUQfHodz|bOjtd)4b$9hAT$|+??y-k zXX(4J5$oxZkIzcD)6$l}bOt9K8V%J=J@VFxV&&mh(sd#0`C2{$Kc3P*>`I86tE?U`gmjjgaMOaWHVGWzgfNxFQWR~a_3$7sbdk1#LV-lPm5z>*b|Xx<~RmlLPXqvm=|dcmYNN06o<{HAg&7c z-{m#w@ExoaD;@55d{o!0SDxDbR=_y=GNPBdhmp;E@PhcU3-J>m%Fxm{(x z-jSx>ShHr@$^e@8wf3)~k~1J68EE%8+%io_0s}XPh=Rf=iEZi!Mp;M&6nyw3@;#!ykLo z(xEBhyK@t(?1OdCkJ()QPxp1B&P(c20W$4P_9wk8h3~_JTp!|W?Jr?BSqkuG+ZF5( z<^NPdIFxnenYWMwpmU3@y5!|pT`Tmg#4OKqmbL7wJ}Y&&qk6X@41%9TwS~im8{Sbg z{Uygy?fZ7nJETz|$@Q;`pwEyRuq~y3_e%&C>sT_TzrHH8@!WXbI;AR%RsEZkHt&Rh zacC}~BjW4brWw4TmYf(Wz?O9Sbu@PAES$ZrfO90o$KhJXCnby(+{t2b3!BZ$al?h;Dp<#(FM3H>+=fEahu)qN zLjjqC!ZsO)2#bX_VEh5hDMUW_v4fuU>92O;@LG<)m|w94^-x0AStyOZ!<$|n)ZSyr zMhfkdS5fa@5dF#!Yuwd?9hMG5!m>6ZA+4*{P+OvvXsC}4197Z_nBNy4gB?H5ZX5hR zlQJ#A{mjq_Wq<>SQ$%Rd#`0@mN;L@C`@*^qGjTe7^aynb>D z{FW7?TOCARfGtxtkVk za72`LoxZhQDs+wRCNI@SJe9R`I2fJ2^`hStmRO?6x@eWyigmG5ZH9LBs@xsfme#E| z>1h3c$tkge(YuCf*dlX^{bn^$L4p90p0z17THNjTLp3Bz0&bBLjxvmRcYXCeR>Ro-2PT}`QiB+0K)xL8HC@~}Nc8I*{(xbi7q`_A zJIvdJ?wc4IupWBS4X!~v>&oSGLlz*4s*bYYMz?vEEBdKMLB{M{VNReXLA9~FeGU3P zS=16lpx448m(OaiqPn#f5$=oC?sTpxpuA2Zp}6ez6lLnHT*OsTQ^(QcKBx3`K()i4 z68DaIXx(r|-*F61C8rctBCIqvKo2V&3LVir&mBR(V*Y|;d>2vLhj}7)!uU!;W=e8D zWu?U-xgRVghib8FjN_mE4+t9&*iGes%%>kSz_#Rt{A-y;FBeIm7kxQD5BmKU71v?T zzZ?9o>l1YNmpjnxNMt7-c~h0EqRlTkxgC+^6>?HfJ+m>{9=#Gw9=Y-OPz*V|L1Jnh zO`F7et_H0#8mSVhrsT$kO+WxThkfhIN3r#~Zkw>Hm=tLKaGq2BhW%P%4l0EDKY+)- z7fDz2@^yA?d{cZ%GgX3RBm3$7J4Es1TcLUQy_7Mc6(Q<=ZE1d)tfhL=v`b{Y82^Y$ z^SprM_$^JC4uEATgYRLrHvIeA(Z0+CE6EW|$1E(}D~*9`r-b}Y|4s2Ndm+v#^S9I0 z9Nw;N`n^;Qvg5TerC4`ngYwJF9-zJ(dv$gHS86e=4;DTkxf$OZ z?CP3>po!vDonqDVK$cTVoau-6%5dC7h#VuYA1F82m(b@IJ9Qtqi5mWqOoq5Z1IK=9 z`FLQj%0*nzL9}Nl0hX}|wy@F0^_H6X4pI`*?v!gVnKKPTiF#U<683+kU!Gf#vu}Mi zjl#p_*NkVK{pbKqtf)_xxZ-^fa@c@}S&mw{|+gPPh;#q29WhgA(-I!_qXE*fE_`T6nlf?mk!b)cl~Gg zHM-G=(z0`O+=DuyzYAtSTEB@rd3GiJEwq49s&GB&yD5MDp)xY;EKe|cx<=~Mizm2; zx0!0w%7fgzextKljn8Fa@p*@VoMn4&O7UYsrP^L`Q_1n070D2(Y`@~kRR>JQb=ro@ zMbntazXvh5vMp!o5FPA>@B5c$7-(ATefoskRlTwnM^SZ|Zbati-;vR#R`7I}AN3rE zW)I}>`U3Epp~1|B2emkfCsW3&@25al7r7w?!?;$k;7A{EqG5?vc*uZ%c?Zl`o^8%*26x_(m)l%AfJd z3}%9)x!Q^LUKk3gc3p1Wh7}d zsYKgy6_ZeO1vc}(F}y3M@WPv9{Els&%JkzlPvl{7V%ie?i7-0uaXm&w3T4|KI=f(a zKA-rqC>s~Jvj-vmqBnih<|V58ZuIxdaD$ncWBDyHaj{})EapLQWVj69XEmw96me`$ zG;P!0cV`h?E&z&7x}4+ezC;;MZX_l_zct&-63Tap23=Owo^sk7#L zO=g^Zkn7P<<#=|aE$Fr;gD_7Igo{cwf$UhT2i-ZW9cQZxR!YLRLDmsp{$f^ z;A+x0kFAVoLR906PJqjG=2a&O9wQhyzXcf33A%*2V_hTwpH90fTswL_G&sj{A+QHq zYji00jws569Md_ux}~W`qZ7M?>uf_;_}z!W9Ojp5%$;_)_Q=#k0lVk1TVJDNzEScF z_y8Ig@F0NGjwN2g3x^TDCwil7Z5fm+MbBdT?X-9s)qr!hQe&7!TFZrL0 zdh0|W9`49i+#%{FW-n=-9rITsBg2t=(OuZ$@7l!^#soaM;MSqqm-epkZUU?_NbR3w z`T#XRu74Y1s0q6QrZ8W0>7^pv=LG>zu)?ZyY-Fto9b|SDjhbH;u6M3t646IrV9r24 z@zIxz1)}VSA_wE)1EQ7J|CXLe~$JWD4R2}pQD-R z^oPoh&frsocg#4hC0R50`O?MTq<#Z?HOY+!ky5Ky&jb%w>6BM8OwpbdjNpPt{i_Z4 zMR5Nmf9k%E)e~YU-fG6Z-?9mj{VoYz3Adl*g0a1cB01gVe(k$|J!2z7UO#`jR9h>w zOFw*PfhVYm^+O(-3!gLHwA;6Wo@HE3%{s?FfmrG`XRX-J%^b1HD=wMe@CItU!H!&h zgcdw=BMTArvX_3apki^NK3cl--J@6|C#}w4^Rj3$U}Rx8KwTM&yXT5n-s{$$0@0!i zA1satFr6@M{U8Z;8rZ}f+K!rEv~F*-ml*A?SZl^Qo3OAaA*{i>wqMdYo? z9Q^yhFc4E9!p*gZtVLPQeAxlGpZ+saqql@bi zF|$Vi1o)e3Y7t6%y%&){IksWt+-9b5PEVv?q+!AqA12G`NwR?@IBIbPkG7!yB>cR= za~QjTt-pp?kTtQrrP-(un4S-rBrR5-T>+-mrtu1lc5q?MbHkHpl~zr_=Mk~(q+8d3 z*(hxZ^CmmMki}U^ip6IsG=XGYbI$jMDWao2%RxwbYQeiR*ecjP(!^ZCAD3>2NvyEy zp0M!Uv*v%~Ren_{x#%3iHxedMO9$}l{{x62e{6<~6P?FnvKOa(_qDS}p4^4u+WL$| z7=P=%wQafQXDFU5_1wRS=V8aK&~y)K$6W-8K1UZ0(=3lmn&>mt^`4NGbTzrXB^ylD zO03eHBD!fxE@_fF0C3znwSorAPqFj1)5DIrA0FIQDQL?l+!nFb3;B%VOHEd5sDzBC zeK>=Crrx77nADqjLwWIG4;PT z^d$eEw#~oQ{Ll>c*#FtMwo4n1>$_)P6|_R}?uGW+SNEf*zp<|0QX-~nXF(OY;D`=K zE}i^f6>?_5#9lUKdbX=cLT_+C1G;4tvx-dWb*Jamz< zd0IN*P>$JEBNOYtQ>r{a%^7ca(YFul&$mq&)Gmug)e9@Y>$g$6x)UBT4)yxC8ouFD zt8=#SZo`O6-qA&&RMC(xsst8oZh&55XCD(<7F%)atRCz_b^A_S!Y#NBm?+D!zZ3{L z<7M6v6DEs%^FI_8)|ldALnus-Q2fk)5oWiLS3xJ{ruX0A;1auqcf6(_wyamQG8lca zq93I$MJd;7jypA3Wrj&-qpY)(jLTkOXKuk|VZo#fg;r;I8M@S)9oOYHFxkIU5O}rR zo@sAQg=-0FpngR9z{FAeD$Y?aQgD61?A`FEhKDg$OwlJ(^`(<)FV@pf=ek}zig5FQ zeXx=ylydw~7FfrhIo|rYlmZ9Z^K!Z+nk;yBW(G55K~65tiJ^@a1oL3nZ3GdAWC%uz z6IX@)Th})b%cKdfJPZWu75Sk*6gOBt;1~#Bg$bco@FtbHK0@ZVx)^*%+RKu!=&Z+& zd_KJBl+#(yu162eAq;h;%ZMoS`?Sa2bac z;fQ3*_83H)x-R|uf^J%eE+;ovBk)mUw6iHYx0^Xup(5C&IG}A9`xd>Mu25!cc44FrFf*HcSLK7cJq!yIjbt~5WREz3zJz@yo8f?s zCS_h(L6#&-b|inXfW97?8jjSxNGj}@LxzTmu0&uY0S5~|r<07^NrqE6aJ<*RmsJ7RjzQF#55@4GmXGk4ev;K!I3zXAJ7eNAcz?{Bf|!kfqolAdy+FCgl|% z{)}|duF+o~f>fQULTz|?za@dIlq$a_ljUNozULUnUU!w$;KpkKDrR=zy+ebL0h4s$ zYFNW4A}=qWneCMtzU|qW&4BMUK*e~ITdf$=`n24pmyVa)OV6yM87r&?m)c`Ip_kW& zosZP}_(VtM2nqNAGG-nJ-%r1sxEg#S2*!l#Wvdw1hUu?-8+VHlH=;(pe>dU6N7kd6 zh8C60E7R=swaoQ5z5@`xKipPOMQ_y?yJCy4Q=7>0Es0<>OoOW}C}6Z;iijBPF6YSf z`}PX9?zgtVy3*;6ckl^l`gz8MRRDa@RaoVIb5; zcn#-LMf88fO^ESF-XH{s7oo=AM?0a5AN7#efU(dw_b7+P_OAlop18cK4l$HidkLHB z(2RYiZ$U;hWA6~h?}q>_igiV&E-{yQ=w;xkwRt-Y;^}*7_E(~QdggB-#n1ntl{)?W zAY>lC?Ez9)siceB9M4T@wzXLMycjH&bKn3XB@ot!E|7qyp*g|34L*XjijP(H!Z3m8O7mq z0k_kGLTHX&SQ_Us_O+&T4#{Gy!@dqlV0w~3bXh^eTo%OfZo!2Ap|NxPE-{zaj9Gd} zv|7cyp;^*mFYPv?iXPcSzu9Knv$OSI?BYes@6x9BQq|&Nni9i^z3B1DbvXry6~zl= z?ha^uMVTwpcUz+aD~^R8K}nKrc{HThQgS@mPmJefY1i3d^c+qSJ^%;wUQTDiE@=Ek z7qkq_vC5N&AYPYF9C-?9^_)~)0Q$Lp0jl>vUSs^}YH+A{F0%+eX0&_o%%d`+_o!nO zt}ZY{iIe!7eg5nJT0(SJADL1M|3tFK13t=Xk>Zt1r~9==t6-OXmzMXjQ12y?#+W69 z@?$+Lya2cVB}w+h$;O-d5l~`B#>kc}(tGf8i@yyjm{6nnaaeASr@xlIl8~AhO1BF6 zu2O{}eXtDln1)LDpY_|#C5CFCh8o#SJR&ss8BN&;9My?#FANhjgKp`fDKKRglX4|O z8|<2{!5vNnU^cEiCN>M#Xfy&FK7Ue9^+}q>Gw2T+KPFOToK3$)xr(;_u&O1NvnrgN z1tKJKGzeQ1_C61%ioOH*hx|U>&V>0@_*utr;M#MwXvjdgQsl}FY0@Fn73z%V)b9`l zdhOB}7*mN!R$#fbg-0I8tuna#%sCZLBrsl!Fi)EHQvXZKFJ7cfo{HWauX|DeJ4qZ0 zdWv5}eJ!VmK5EcH1h34~>uY=?yFy>puJ(W3m+x1XF@Fxt=gG}Al2*jvNmrD{|B59s z_Yzo}QVeft;eZqN_H`~TpBE!eCrKS1JF3ZV0Qfbo;yS9lY~H3BE`S1uTkrE#_SREQ z0_?(y$FoWAW$Brj!-s=uYk>$h_lo60exlhD&MoF0iGtAu#2gXJYx%v*%Y_Ww3#$pu ztGE@I2Ub5yL9+M%1tzKtbA~wj!AC)117%J?^k_kZ>=)i*EomaHDo=6%9+uCoQAu~@022quV% z{_uE0)V+Y;pCB0%v=4LYO+-eCOawhV7v<8?4_wz+a=lHC`;$BGxn^oNkRH{wr`U+O zJWSymv@exrNk~Gb&|NnC*NhPMJ=tP+hVqo!c{Uic z_4RZ+aB6mwTA{0vz0`AvUWupIOV2*y>?yCG737tb?pU$1jov1Rt5 z1^z|K3M-i1{4m=Q)@@qK3>wJdEWZtet(J6c4A~Z*%;#lY=IO~ecs9l%+TjH^nTyuh{GsZMZpAgcfvCKDW z29G;8p7xokKLRrasyoT0!Q8Hi)L6-yi#Q2eAY<@r1qPqg}!4<5e zK3KDbMYFK4?H4QSla^W}ekmg(x@xy5%JYd|hPP*g>QV~HV6@&5@dP@v#`)t23o&&L zp8xkjvr(gu;#dH~nKeKbcYE=?t14~bk|c1tJbM&!yL;KHUI<=)^36=W%P8b+-B)~i zhh6HhuYEHo?1GEBaZxPsA$_1nu440zF3t(``;C765hL!QT3R)X&0pDey$!k|<#O&e zfs8g?NlD~ts(>Q2eQ+bAjEn;|`P0gS#8HDYW5{gZ6j>{)&e((I;AlOv*Pwxb%5 zf(dz}U^JD-?(UOC{4q$XCij7XAJ8vvc^&z%1L?M!aEgWG|0++zbiTmE8v^F;w(;1- zOAm2Gglvswm@pUoBmP*r_6VB4f!$&8u`DV=imS)-qbeYo;Na5y@1-=vyZ`9M?e32v zQWiRLKF;3)GxG(ukB&k6&d6#P5G7J1n_tiMn9iH8a3!(jJzE{X|kB zBrm~l7go9w$qk-$c)kYAhKl*c_5JjmJAN|gP zHBdWYipxT~shq{1AbFnnJ*6_tvV=C}H%@iB*??=KfB(m({eqNw!#t%@d!?->_xA3l z`(MShv=&>DCw=NGtFiUSpDp$b&jTKhFSt=yLgwFw zZv8dah>Ya&-GAL1L|8|zI?yrIk31$>+AnHQnNe6J$7k5~$YO=o#%P~8>Xjf|94&?o z+wNiFO@c_Fw(|aUZ}P*wN1pKpyoUNsaV0EEtSu+U`p%l;PLbvq8;FFf<(J$Eo37TA zlVpZJpT|w?Xjer*3MFgT1usOM{dIVa4}xWkz?#Z{L?-1x`VD+AuAD0 zep{&an?)=oUSZTjET!|=^t|L5See+~F@TIhEc#^7=9TB&4!;6!I_H(#hcTc>NlVyD zxi$Z+!0szG$ur0~rJx(BZz0C76sb^u5eCvT$IlMil*2|O4(2BT?i`;g6JJA2I7jNl z#{2Zhyh6j96?;H=df7o5fGXh&;2TGtXskYE8|-wB_`22L^uRo!qFx4NdEShx>fcW5 zI95qcS(`g3l-&rOM`4wbK)3wUMeRSo!d#(k*&Q23FP5JycC14kbP9u7hloTUlEG>N zpcckD;MkCrr1y$9a&D0s~)Q>J*l~aLARB_u10?lyP0zTI@g|$$v zYbTDueXie2mNPk54@d359)0Rpxt19@Bnfd*mW12KO_-23DI>9hn|G{n3CLn+FV;tQ zTwm{wOKR_-Tr2*77I^~o*C#dbYOm9c%NJbz=NNEbGeT6ciFhxvdp9FK(P^(h@3K?a zrIb!q5;KO!ChG&1u8>^p?E3#I4czA#S=47&0ngZE`K4NV zg1H4Bn~kissMQ#7aC8dZepyC(QV@dj%zp&`jx0Kd{#g}cb;&ELO(#4g^}aEvI5I$t z9OrmD#pTiigQFhyA8;cv(idZ-2^;ASSg{; znTnlD8h`!B1MV9l7(tYfRd|V5KenNEK^`!v+MP1mej-5>&mrzEjNfo1EnqTvp~F2t zJEHBgF1m%9=`BuKcKlYu&ZU|g*<638KZw}Y$k0<uoSY@0eDq(XvfmGIkZh}m?{dM@p2-^t#0qKWDw-*+`4*-x$)X!U_ct7AH^_}DMP zCfWQs4%Z_^gFofW@ zUBW0WPB&Aci?sg-<_{!?-R9isa7`l^xi>&;j-oU2kRu zvsU}PM%Oy>cGaUdOOnf2b>ukS^Lp1&*oVmaRxq_Az3P75_8Cqnvi+|4M{8`Ml~8pV zaX#|O(Tb1_AZPz&)SbhaX~@g#ie(#bawS;A9?DbH#U#L+4IMbZSyOKr#WE4{UVFH| zYfx+?l_i4}5kvJj$;1T5_f3}Q2fh0iuu3cV7B*@Z|B5%_4Oaa}&!%d%yFl)dH|4j1 z9A-Vv{U~K6*wqXlH(;Ontv8Fb+fYzm3CGJyjb%6tQ43>3>9hX#{Cg2Lp>~lsmn`dw z45Er9#896i5A*&MK`0ny`?nxOXkx0)dq+Y_h=&$ftoZuQ9+%4*4;57peyyGiK5=%G z0$-Qj2yh;CHcu?Rl7ns1ceOKsmZ7!G`gRu(Z<71Z=iLxE2HfZ*vVE!%%dHI8-W)cn zhO@%?+0KW~;wyPbcA)8#03<7beMpriU-2Y`*ye-|Pu)x^KZk>Zn9-2Z`Ladq=wKWH zj)V$=zZ&F%orAs(bg%sh`kNzv@G|Mo(v~5GyIE#PM*DNT4>(6P>JHdzBBt{ZjV@yO zb{>-^;SvmW4}p088y~cpu1TRGXCJ=jMHL&?Mca{ZE*-K+xNa z<22d)8hT3QQ|<4-rvUrsPyc!kJJUFc_|@qy$1s5m7#VV38iW0R$`W*HHoGb-H#G59*01z9R-fYqnx24k(fSX` z)|j)18BwMBif+R65OsXi1X}r3JFE8W6AQ@;x{vHw;g)c;b;#+#B@-_3{JA&E5=(#j z_RFCiu&oNZshp|!YtuC4+qQ<1_U=n>A!5lwhQE#m)>7PSk!3f&B3$-`H;T?saXttI zblm^)YG!5%V7;yt#u(zw=#F2Hb1%6IK=QSRB`nOzxsd@~vAWTN{k^vGlZBCV4wI&n zM?JKUI|@tFm7FV#fzT^JRC~|=qv%{5l05YP{{VstVNFFRt|CKJNG(O%vJ0AcM8PXy zffw>NP^{*huJqUg&PvTfNK2iCgl1lm5Hqu|W{J)-)MKK~#Wr^4zG?G#T@C_N0Xd}Stzr|A#jxI+bSVp(-1N}p=C`s~ zFp@Twb8#w6-m%6J1JQkVduKs^U?eE>|I&28ca1v_ux}Z@sh!@9w|+1G_b}+(`8Cf| zl@G7_EOSc!gjoAuz%wJ)NLE@g@1^fqo<9ybF-rdlFh5kA9eqzsHc1#Qo4bkq#yE$e zGQXFiAj5~8;xKh*13uDTXSjDjh>)|&-(32F7TgdR8aq50+M)>zS5=jwqd?yy{s#4% zD{ntZv0+=@TMowPWB>LE>&mA&Z5RkhnErD|zzn3JTuZ)^{zC^Xqp!*152jDgs0MYh zHdmIfwe0n%>8ZQY>YP@_iS%?G!-(SW+DfbAnHmd0Jbv3c&82<%GCinA4m) z!DB9eoT0IvK#WOLMgMRq?d4$DjLXw|O5aPU+xPc*Kx-7?m*@u+W}`-l4wHM)TmJPl zxIVWLO~f-F;R~M1LeGmKj&ucJI-#f8C5^^`C%boGmBeAao{CQf-;tP9(OJWIRw{#H zr6q>anKZffyx6b2Sa5vNGNCc!AJfICSvzT#U;(;?uI>4r9I5P5QE|(5imZYo69j*J zcoyS67q;f}Cxx!EmqyET!@!K#@b#djPk>nSL-bUp2~82o{laQJd<)CLW`W*!3vdiy z_i2ZzZJ=BA6nc#{=?k87=}ht4riWJqM0#UU6BO0YAX?CKF=ru=c36l~pyAw{nnqJA ztRRFR&b7H@`NtMc6Qm6-^j!k@c2_}1qW);%9#_!9c^?`ADJ#o-66ZPq(KUGeew%qh zFBiEF7LqU4xY!(wYDMpb&bBWYc%fe zTfofz0PD}}gBk6uS!9@K11X$6YKIQ7-8rYe;VZ3z-dNGGvX#<_gNy_~_yA*Gx$gWz zSkH%Nd{~4UVRQpu^9ta<6r*g&Y(y*Yi}e_)-ypVk=!pTo;B`Xshr) z{)~iTh5h(v>YP_vFh;i9_X%N=G065g;V2lH{CpLbu~33UB(9>SRtgjD7M$W)O>Uyq zYHyu>7mM+youNOfjk|%>+-twPy3^>efx5AVEDTxkWc%2-Oiej_psP(${C#{Ccgu<= zHDF+&8L1KAM76fX%0mPdhv^tPhUDN<@$EWCQ6lp6RvaEG?5P*-+XodaKBlM1{62R1 zo!RAYtXna=<_yy3>qZWi2yYvZdeNJEZoLl?vF_Eu_x9J(owFM+4Bz^06rYf=Xv!bg zO)0TXnNzky5YxL$rm0-QhyS+5(NHM(aKl7pmP+X|d*^T!Nm?aT9M3M1h!xP-{%owQ zOrF1g_au`^FJ(o%g>oRc+?e%VftMAoc_Jp@u6zUZ`-2A z)yR>ZaGw!A->T-0%L2;j-8NH6yJ!=bx;C{Ar}UUzi%zcgqdgNkQ7yN|^`QHYwGM;P z=K+S}DGxSkA);W*@zp*)8RVCc^(%3O~h+d#a2>r$;dB8O0 zU~48?I^i!92?-r@`N?OK_;6jNaoTGYb-9J@yh50Kh|>ytOvhE00SEtI1>tR#oyfGK zun-bdW=e*g!#l82!On&Q{ZCMh>qx_mjM8ZL6>81j z#h)iU+1sL2eHQ&y+~UGNlC_Q|$;di}Q>|-0!Z*Z; zQ<8sbUXWaAYA3_kHlmGItp|2)HZa*lAN6S zcZo<9SyyqAK9B3whC4;-S#tzzb?xJ@r@`u%P^5mzs9vG)5eH)4id4rpfygvuL3V=Z z1a?oL8xQw{04+k39Tju|_wZWgE3kQ9Fc-|keyM4Fmfi`*FN955y7g&<=!mXB|5It6 z7~un7C)&<`zQ-OaFcm_;0$6%OL~?y&E_g_Ir8T#29Q40HytC|k_jn$x5V}_-39{#f zInO&E1GOjjuZcJzh@hX=gC!m?k7&9MNngk+8f`Tp>oe}h*F7LS)%##J-iI!sCO$c6 ztS+oGOqRfPM`;bt>+YF`eL5=Ubmi{h$SyD1f>^?3^bMc*DXK6&qA_VWMm5i;&0a?z zowM;yhzzMYo?D5bLn{FKbD36&btO*y(!il*-&D)pr)>>8l#>*=%6x;6^`AS*caC&^ z39vexjmuzz?&&?JzGv1#m|z4!OSaz*V1;s3p0>k`(>}>~v4@Me`vUeX_)gT*X#fi4 zZ{)+DoEn`kaa6;?`~`q#DI83Z#wt!nwd+5HiW#p~O>tfp*mx3mhoY!`ZfGw>76j3Y z5>8XmEN}&%5U6)}1Db&%>RTOZy@HmO{fTj)gaKh)_ zj1f5;vfG_ZcbwloO!=|w<|lzJ zE6T3@?f6WX`1;V*o18;%#r=?=@DEC(`m>V6_pW1gXI7#YvOn2Q#J}Mv2hGGzFu6m{ zq8_r~yMR{d%*Dy0z(0R_bT;D)y1wGPfc+x7?PCB{eKN(2YMcw2erujX2zl78 z=Ysg;FA8-sSCO8ACSgdUCMgp;NO@lgy|#JB6}kVVA8HS@Vj{lC@L+EOg_T|LcU^xjJJOXR zQO}gZq(!PJfSYrrTIL`5-REHos7BZoMH=aP!Q9o+o&g+`VRRQ@&trmwFpkqYXUygv z%-1PQHB5Ss#-dg2SV`(gKkXicvF9tJJvQZhgD_7>nM<%)o@RgnVfL+1IaK^oBJmKT zJF#CRjz5e^E9c>&TG7d(lsVra zV8$P0D(a?j$>n_a-KY~zB28`vqm*HGG0l|qvR;~;u9lyT1uMI0yFD)PZHB-eFhhX) znU$mn9zN^vG^o%x7?@RzNOmb7yt5B&UhcCN%_D`-$!W;Zg89m2y1DSOy-Y~M4LY}V z7*9tcC#jMiJFemDzr(fqD8oF>IL@<9NW5aRqQa(ZaGvuF@#*zs81d7byf1_6kDanb z7W*CV|4vSc6yj>N60huv-0ZYq+H@%+SFXyL0hR_sN4BfuQlGO@AR!tA68ccxCX(F1 zBb~BRfzemWLLE(HJns${)7tf{;oz4xljHcwc?uQQ)Tv^u64 z=+pXxi=rK@Rvx-p2R5Q!br26FU(aqVc3E)s+H!=nV;k z+Q9A0G*7*CkOqoHwR;&Ne_HxY-Twdu(;eaWIuk;jlco1xpc?X0R|F-)o zGl=OSnjIxQum959#UyIja!fVD!-G>gelRH3vAS{nrNL)&$a@~j3!)>EBf+<=gNJYA z{XUw=*H)U)B!Tqw!tPoPTw$$2@lU#=l8Q*X{VRRRkcr0B>2z&W}E zVRCN#;vwdX;}A^D=HjDk!~6H*sd}7-IN7?6<$M5(bZbW~!4*~xwv`O-M4F6ujsl6k z8v*rdq#*4hm%e2LM8h~6)4zPjU%{46+iF}U5B}!Fwu9wAzZmtX++-`)Z%}ZdZaL-! zo^LQ#v(r(uc11Ty2%h=i%;*%B7;BOI2KHHMd*HP)T2hg!T>HlOuP6aLq%QyD!p`%j<?gS z6z8bfnPjZ%oH_61dI8uSqxU)HQE5gVc=JUnqL7VL5!)IeXv|NNiPSP#v_RD2dH250 zh3227UCZ#Lsk)|f0j4hCOXpf~lgp!J)E%C6oPF>wbdq@6|8+^TzYfzQGZxR2m7<4) z26FStdBI9P>bmYDxVyKnU4ru{_Mob(DiS&>;yPNFME>$q1_UMY97^U;@0IM>oTPDJ zG}q~~f@r%gNiRra&5m+a%(yN`Z}ZbD7=1CUK@*tV+$(4OW-6RXBgCHK?-20Zz16oi zFbR-$WCPVNY-!2BeGfh`A1g(UJRvIOR%pdn9~V9PjzGF0W4MlfLU4U{$DMzO^k@}( zmt(*urqo)W@x(KGy1MD@+QKu@wB3yT3txl^%5fd{WK8xtXFfnYsVot@y6Pr6*~rmiX0ludK_)# z)7^^qGL+Y8PT7j&51s3g;c{V5u`i&mdvqfwaA=Yb7Y2?4nX z?|L${GmzaE_K^9-@K^Ite}(w-Qb^lr!{9v*$+Z}dU5zh_o4J2PrO?>sG83AMDKu_P zOTy2Jk8ak!_^iP2+LN6jzw&+E=1$4=eGn+B&mfkImb3nCaBS|O$srYeGGQ+S+{|Id z#5wAK7e`6Da#{UI((Vt-#51N-S^JNVhV>W{ZPRULrI82I!HP<2(}g`JTkGTAyp6Tk zdlaS1g0vXP6M~tD>g*Zy3P)rNZVy}DQ*fp0+F^*+b%0(fIe|7W?nul;bL;culgF`=^)kGkp3+VI;pOch;c!Fan3js?D(0gBAccEZao_@MJWxc7|gj7%&M$$q$^ zq1K-<-n>u4E!{Jlx>jNIJai!&*Em#t168f+#n7akEE>9Jn3Zd%a%GIvt(OQV};Yf-<@>NS>&$cujrx^b4 z%rtmY zMSC3>vVza^li^Vh=C1opdrw2QVIH7ea858GUtA-qZYKEsAA-khp=2k%aRECn2E4@% zxqjXb?+9H8>%58G2&kmzpjPAD@A-1v|0bRhIpe!^rht=o1RLq z%!-vVMGQDq+j{e?kG5kGJX!j$n90060z|j18&B5QybB-G({@W{MTH6)^6}*HLR8X% zid7Mm8s_&CvmwxcVVx!X@i^=i=(hC2@uLtcJ+ zl(nbNa>wszg^(Xe3u^#`NUbijL6ArMR=4=y4^5wh=#lJ%oX7NOFXR{ynfE1XDk2ea zo|M`3%HT@67hiv<-P!OA`Zp}^*o_BPd6s)xIKySZR(wq6Wh7FvQVs4sz`)$y_m!R7 z9w!;zBXu+9Y}!Y02I`fTHQ)Wg@~FS+Pd{zolf^Ldp5{DG0z7h;bV;mwL;uH9X&aXG zahKwvHlOZT zH!gFwX-Sv-CW7v!{RN}N{JDgjN8nxd^4H%F*e(@-NDu36aC^C-q=Brlk%driNIOKd`6K9lr3NTO; z^%93|zvluFol3=g*IU)6z565VP+)3x+Qn^{EN+FJTXSh;1%XUHE|`zRq|()uy4%L; zcdx-9mh3K@tJnZ2-Pl9L5y0#hYc5?R`!glT)Oz^lEW#zqr}b5tH8O8M)I!7 z6_Z;>n6h0#n#YLd;h!~E_1L`X<&3)N#;>!F?m+xqJ$r}!5pc%%asOimBhRTZ8P;^| za~$;EgR%10T*xLj{!Rp?5b6DJvZ)!;{Scf=J!y`jn#pFJPq$uek3sNdEAlbx`rDM$VE~0$+s7`6M|R7Hb(SDeoJu^*Ku8 z<5~HbQvwR}L}E!kE94dE$!xFKawWjqCXRW}D;<6>^WBVSa@&#}Xo$^T;0%w=mND8} zk)-ZN@Wz;q@E`v*!YR3)B$yMq+_c?0zln2M9&9R<)RlW$?W5dU7X^N`qa&2Juu!q#4X=mP%#)XZkNagj$E6 z`&w;Q0#@0xe#RZqUtO%2R#{ZS2EpyU|3ACL9Sr~N=oEAvd1B}rPLtYw?K+5|f;;p} z?Uz-@xyVc>rrhg^rEZL(Vw<3Y1dR9>Zb^|jtHNY9xlDQ)wuewiv*4l`uYQY}YG2 zfH;x(s8{J_h&X6$mDx~1cE!2aHLy$A7!iq0sf-Brk?3n~xRxx1=wtrSP&ZL8pas}2 z3oD*T_R+LBp;rj*VUO-pzwtON^S!1u+Xaw4+WNIywQ(~Nbe7AkEJ={7dlsRNdVLB; zj2TIj-Mc$XOzyoPbTfBgd%f+1a;0b3m_&mQS3ht#ew8*m=Dr=K5A9yLSXq^#Oi@BF zc7xucntog~)QlC6ht2!;Y3;>A*ZC(kd8NQb>5{^Xt=F?SU!BO6g`ECrN!6+& z_KfOox(x<#Petg1A(fM4tO?pJGP=yf38?bpNM^Q|ZF=jCWa7Nnvh7tA>wfS^)Bv#R z;`{H2+&WtM1}-*YFtJCFwSD-|PZghQ%cS|ur$!y+SuMi}_Za{@h^ShckR13mD~2Eb zJB=${4uPUzTCptniZMTa9(MYQS;s~$oeNE6gcN|C+~IE^TgR#IZ!R^RMdeI%C?h%G z$zaFHQd&dA-~#*GIMW9wsL4BW9$m^Gf+nPN^DV;<9TCn+usmudBA0`yB6X<=zd_flD#iz0N+&tT}Mdt$5@b_3B;)8&ui%|0_CqeUO=Xul+pl-c= z|Hv(O?}tI(|K*%ThonIbY-z_Sj!-^SJ2&`6sGHKlUw0LV+$=T0$lk@Xu93-KgZNbV zQbJMYZ%f`M{61ufatgSY&XRYm9vPXQ)4c&j_G4D#ZxlIdk3(z-6-gp9WvB zFMPuxT978y_9nyXSiW{=!T680G5RA$NrUQiUHLK|o`BUxUM8<$2dT^CWfoq zRgsC$N_xE>)4D1LndJc(=```T@(#GVG2EQ@AD+9$T{*=aKUJi3*Cv%XUTfY09%90& zaoF-bU9zOimz}z*CK#DA$8i|&_UY1Ui#=7yx-p{!PB-Qu**WtwIEQ;Zdu)ksLpKnS zgM7>d(>o6G@ZQ6ll)OF*gn?Vc^{#Z=bHuO!VcSbNf_h1EooAW&6LxKM$aIowNK6~P zwZ49_%|kB6-c|PU>GLR&c^aoGeF|mI^nw@$xkHSRouBqJytfdX?^VMZw0B|_N!x6P zy#()uAGCO<$pWA04pZK8Vl8cCODN?%MnGI=j>nE3&u)ruSPP9*U)iAE-HhdZ3Rr7DkFx7E$1>gn_({;O2!CwAW_tr_H~r8gI%o9% z9@FlLgGFTf5|}U5_fD|$o6+EV4;_A>CC(D!2GZ21!M=ItF@HF;=s|w-$gP*vu}L`g zdC?m^QKo(#Mef5#)bceiFlt`P%DXcYQ2#{0fwqC2-eB5GZHF?{WQ3BdAi^& z;L1!aT;~X)GhenPKuW)S=ADq}2;F6%fakq3i=0QL{7{bO7(XAjd8g4o28C~tA{A{av6W-=^WsuFw}{&|6`sG!)xT$=ubt)12R`1HVbdkqg9!sG zbhIfUX6R+*LXy=n=tDhnjEv8m1p^E1rlr5Q0+E9Xk|_>@?$zqz8_1R!wL{~&&9`^Y zl!;25(g6F(qSKY*O`wSy6)V*NHxxXveUKF!@c)UaPTZwIiumge!N1Iy9y=#EE%-{v z{V!=0jwixAos8p_(1Wt?kcprR88=LnM0tLeZ8}zi|A9s3;i0I7`)i zDxNaK`_i||I$(L|CODnOw!Vi|pZl@$UOq~EF)rses0?y$$ysvx%rXHr#`J87f{Ci2 z^Ta;W)W$VL@p(^ei5m+XkExgrSZTbHm~3+6p_Z~-&u147Ac{Hy76yZIt_-arRV+Q9 zcA5IyoqgT1);Or8R6ZhVbq@6q%7v#5&v3n&m1r;gVwHFzWCIcH(cY2GgJPc&bdk^_ zs*++}t)MR1w4fQ6IxXJrmYsY+;%R4*UUov z%j~FZNc|gBG5s|6tGN*B&0w4IHO9-~1wr`ICwPFKt*g92MTs2mM?LU#d5aQZa(X7C zeBBCW0xnLIk!Z_2;^6$wmkV!Mq@}@JT5|2lBRgC_x|G)-N^n?F%Mi?rZpNSJGlIMq zTtVY`vk5K#&yp_>B^h}_$btCFVYS`cCa=#Ua#&}N{PFYVe!Xacwk`eT3PRhrCE?FQ z%0-iD|IU1TS3NhN&_>q3(2(bXA5 zlGc6@dkrDT!c{Tg(VO0!6h-S}y9ARPyF%NMxxR$xAKjn~Ik92g^tzkDh6Y?J16p;H zyFVsIjqtn+_~A?XM>QvVvl!I`KW~YOcDOQvzJKRg(z#u5=d7-4`Z1H^|A6Lb({_Lp z^CDYZd~*kJA;_X2G!d+MNSLRE*QI<&(N>5P_Rvy4Br@ZWm9)2H8okG=KXL^&Pn^py z%XFE5`zl=`KY?ZpSb3(MO;zdNWbiq>$*ozX_c1vNE_b3P4?+ARJ^bF>fp+te^5`%% z<4|@EI18#6)v!2zM3cS^V$@yfWgQz9+V9!$Qo`&@*F$_^{t$G3fKc^t^tPsB9#&%O zJ=OloqYm8pV3PPKK=U6ZmCN0#yJdii&dBDCfN5AWkACX3;mk8qIRfoh@t)BtH6&W5 znfP{Bf+V*_^Q$bc8(0)Rfh4)Fv2&>XZHeMq!@PP?M}{2{8v6B)ETnY|iCd_GHXC-C7h z)!zLEcu|LK1{-Rw-AQ^9&S*t7ut@1`IYyAP>NEK_W`Ut0RDWoV2mdqYHsPKV17goheybl&&z%UXSGnG55yVIzPF>!A$ATSbT6)1-1{|@-G+DqrKq=H z!;22_D*pbr6kP`t-FdMNAr8xy27sN2f~ZZnC_gH?E_xEW!QS4A^6l?cBVCV4b=C9~ zMbr}@Tc)nLxRj^04LAMgzF*42EXUrw4L{b$Wb3y6t@@nBDjdYbc2ASs=Qa>teCBhv z9m&uIE!i^D?)wcxM@Y^cdwx*Xq6Kpm{g*V>@Z0++zhL@8N5JAkL_ynx$3_s%+5XxW zs3Dl$NzStUq3rH&p-7H|5KL=ajAyg!y=XaA`mvFydy>NPxT5i)ho*i33wnc!HYC35 zp=+qqC1+2~Q__JNzh8gZ^Y2rFl*OLxfz?m-uME@CG|q9Mb0Z&}C%HRxN#w;%`3@v& z)%-4x{{h!V(Ggio;*c}~d+uy-8I4sZ88;@tf2r!*2-Hpd4_Z*TwLG;aTc*|74C5i# zFXD;#(EhjM%#^R9C3(k2#nRT_Ifgq&Cgg8X4f@xhy2?=G2fVEL(&jQOA6ija!F`N@grH21Iu3hVwpRLrx1p#$y7^7c&N-N&oR;@z5{ z(-FxhBl2UTBlK@!A62J9grF+*E^y)?WTS=th>@h1l_c!Zh@~#zB#(s6F-%2XS@dm~ zDWyS>nl;J_X&O+w?^K;OwD|-OLCR!HX{Zuf^Go})jw`VjM}oOFsstWp?uhchLaKPK zA-iAc5%At#Lwy(8H(Df$FFN}ecscx9;JX3OUw39Ztb^yg@Rmg7ub_QmVcK4LlG5QE zs{Q7S5HH|j_3R^Uh{8idYlkMOsHibLi)*+(FqtO<)3koCh&C0F&-t}nHvn7weS;MH zhCTdwXp~1dRHfoA+7Vo4gf92~bY04YN?BzO@Mom$Fl(D!pY#wGsle@YV;t-b3!?qy zVw#5kh&yrSV$PJMjOtwvizhM*;z_~TH};M?&7GxUVf^#W7WpaSI%)<{m~B?NG>}m* z1W^^s6~s_~ebo(`57*FsbMhqcxoR!AMu?V)>9DwSu7(rn{PW zHuu`-nNd#q{5nU?MkS_C?}1F$dydnn$;YI6EcowXmz(~wYNFwstw@6JOCy~lf!$;C z&T=v^%?=7r1GcXnTzDMS@C$-=aPcwfN>9W+Q0z*oinLRp=URI9Bnt82R2cK%7Df|o z#_A6R7R;)03=F>K8fy?9HUp87^BLv6(7T4L%Tk%aQ@ z_=2^Tun0cPV))BkV8ztm2!7`P)?@6!MLpIZmZU1D^W4K5*pQJE*5SU2X@}^F)kDbyo1yA?m9pKt6n^o=YN-4#Rjd^@*9C74M)4Vw z>U9?BfnP9uijo|-kCKJ|!t!k%J}Vqodooui=f;b5d5P9w#mSVLmNGEQ~d1G<~>T^KdWr}tG&-}1NZ;0ZC3(FGi(A5gn%;44E7 z*S_ypt{{Sa7AU0G1*nFU>iTGF*W)n820}#Y9Jgx~K{dskCIk?Ayf{c1(E0Tvmv{8o zI-A-(eLu^sR77jmYl-Rc{3h^K!v*<`;42FjliQjwXxJAUFqU^3v%4=HYsIVwr=FjuS7sfio;wk3eo*V3!UrM5LHfY)Uq#=j;>nlsh;L1xh!#(I2edqD- z;WJ<&jOh7Ixuad*GT%;|Iq;;_E8+ zJSd1^Y@XsVHpo4rl{r#uC2H&#;aCmp?3JNP8dI8N86ZSdTu?G?EdwMzOr>N_lL%t)YU zYsXnsr~L=?-q7v?;sEinKDu!46FH8jjOdiDQYIT^9P40b}J-4aD`x zk*ma?AX+#p=Cy|@gvFnup6Xt)4ZF5n= z|0PZzN~B7+aA4dtP6M+TsXt8n!rC$BU>`r4`&{?QhNNuJ=S2O`**JOshBto@S+LcM zDaynY)SMU^v`sV8S*t$B2rb7K1?-n?ITiX@Lf3nX^Q2odA71ROEqdyuMm#}sB_ZyF zHeGPrGuCzQL>ZawYj7@))CsckMJ=rV z2KEA6(+uK9E|2<73zk5dCMb{|d)t(VgP`pCE#Xk7fhEI#7 zLF_0Xk6*={nqOeYEnkur13lrjry=CXAi&gZSPK|LXcM#=VEGP&=tRzNXJ4|aeaehe zyKwq5ZGtNmf%Bs~_kJcn!1Or>`$D6y)Qi;dSAT`nckzW!>;`G|AJ4ZE|DZ)zdWAQ* zfD=#r!-ilG=w2evHTp_;C1uT!{wGbQCqr&@q5Xl@w`Jh(UBJTS_Wy)_N1U?Salh5H zf|I=SYxaNQqXkUr5o3>x1sth8+3}bx0S|Pb-=>UTBLvqR>Ik43AXs`SP?*Wj;%T-U z+Se+=^!W4ZaCFpsMt15JtcCNBvE^?$2ObKto-fw4PS%dX-#n(~tsZ3c0H%oaoXLdM z#A@}l(iV3y3;*#=HJjoCgnOqJB@$1;dNmYX)1h$ zI39{jfNi+10t=Wtr@@CNnLV~_j@zbc9gPbibh?SjhQwa-I@W}#vUy#bwU}wM_;2OI zAnpT{4z_su(gf19xM6ncvHAvogiac!^U!w~+S3L|E&hq6&8~yBRTV*-`eL|F;k=#3 zN-ZmlG}%_KefoFk-fA-f5O4Z`I7yC}LLA0v|C9bowbyR~8Nh?%Tf{noy;*7nyYSWt;y1vT zI~{*6_I4t#fm3i@0uNP{xh=t!rf{)oTB_eu44)5gX&3f9L(^i64jop|NA@lWmaHLq z?~t4n-}B6DQxy{%cEGgv{f;LwQLlxAJtcODnK&)PzFfBV=3cD@K+1OH7X}h=KEu$nf&Zn9q6I zJ@rsGqR{M@4OVZuC)eGi7s^PA9fF+BanoH(gNJ%q{EtcXj997F9#`3y3^`gaqgQH= z=%D7oND_#hQ%n2P>qFT|>GRMLVw-N{K;DHtyB{Ev8#VogS?Ls<{(A0Y9K6`Ye%g9+0WnGVMQkcgd9-4KF0Xc&&nMr0nR#$rY%ka_>qY<8Z7&Y4wzmg`)oV6 z1MbY4^6tWs;LtmFkab2i4a7`3&Zr*3*jIjdKHPzb8bH-}hQr&u86gn(xsO5J?V-MP z({}VRFaVjOiks%BJVEfio0S>#VFdTn^o3PQu2snvsZB|H1jP3?GPqXNLl-2ZhWVQp z73{`T;DpdA@ANl_Uaze2`%(H&rPwK<;~o1ayO$xs(Y{Wnx4!;>^X6)-hfY2XeO0nJ z(t@)7uC5Ml=<)n;!8&t?x@CvSPQe97`4`%}>~b&-C1j1d4U#HaD}MkT(}&FiM*#cW zQAv|xBj__xi|FfD)X+h%f7IuGb<7x%qQd(EPQ4k<>ZXSX`*Cv>?|>DnZ{Qdk#NLpJ zu^v8TMm*U|zkvK6AWxDu&%4v??E-)|E*qurHonQ`wsJ+BaGy`hu8*L+kGTYi2MqBk zU!Bl_b_^;?m=Jm5$dY}cWiah1ykQD2MtWMu)kmNR)%6T$WAv@OCW3XHO{iKwwZKE> zHc6;4zU?LW(r15Hx-+*KMU?^EvNSDsIklbQUVv)R1l@ zW7u?NO_AFmj{rpv>ru}^{Qp0C_XzgO&4-xe`nW{l*9yx7ZpwQ~bO7q9l6)V>Czk9F z@{A~~&&y9SiuW?SHqnc6NJk^IA3_KvDl8Gau3)F)oU;}yOP@Dd|M4Iks}ei&sB&Nk zm!jCPnTMrP87H_;SsMuMOdO*+*|$E&AQI|8s08gRWo-wwm%zLoK}x@$Se22`cQ_{Q z@-`3kl;EeW_ALcPjOaI>L(%r_271akJYd&0{zDg(Zrd?L0WwxL*RhTpT*V!5un1g^1kQ8kh91?mljN7ItMdIP6wN5LmUl0g$%5t_j$caEtlE@Y2g z#)xOjC<^G--=PA-twD8f11vh^UeD5gvc#8pv z3+5*AVHZs5N16oY-@!vaV#4KGU;{TCB#<_)+KVA9*hUvRcT`-kqugz-o2KW^B~ngB zO%a}CLC#~g8(_(DQg%pBZl#HYCvw!88W~?POR=_K^8}m_F&J#{N zn*~J7a7jbuo59H&pOu=Nn)||$$dH1NXltTR0-V`^ao0GYMt8W9+Prh4S8ZZsDMv96 zw_x2XZd&0z71}r+P7!1U4Ksowrz_t_EGPq?wsL&~eg!GHo6v?mO|H6(&r2@B@NB>- zcjhjgJN#kRXkG)4kY@4bJD^GSlPOxEglu|Fsbj+S1opx&!0jvOw3Y?cg~QORei8(B zp;D41b@g5>QuTfna=8Yfy_6bt+1tKyl2{=*8kwlcG2?8>i*KrnUfB(@ zj`eY1O_y@dl;Dd=OR(P(5b?!HhM)%MmcxAA>F+FGvTeNfE7T$bdX`6t3%IEL5%YmiMWQ;zsl-2IO!e`8$($y zC!(79Rzl=Q9qfJJ#Bvv!Ov~|gZvU40@>ve}Thbi|Vtr9Cn4pRiC$h0%P`LV!rNl2Z zz?(l`54YSEey31XcC6V3*C)CK`sfX%n9)Jn{A&Ek+aB#B|DXA4uV+)j)s29X(G%=1 z*X)9>=Uegx*GQN?xvrwzH7i^mHdBL$gbt?33b_TfG@g|Xa>{#kAmk}N*+LHVt%S#H{#gQsB5WHJiuQL1tSfY1jaL^$MqMrzm-#cl44zV{fm_l9 z9wX61{nn;sxpMtCs4tJ8G#8^yIMsi$xZ6;}LT)9e5FCs?;A)&rBZB zI(*C{dO||U;)ZzU%CLTn-f64%9?g(x>b)JgIcKub`Kg2b9^VZ@XF&~hJe4LZdo}Fe zKLQbQS(p67r~Y8tzmF`)IwI+THz^0*0Znva&>%$5s*WSlVI6{t^+=<7QWgV#-)H>< z)tRJ3Ri~iFWsVS;x!uqps8{B`H*_}mo^z>dLr9ndBw(62{`Wi1V4f|4&PuuN?py)w z3aw@AsZ2w)ue4{ZQ;JoHxu7gf0Fq0-s-&Ix6%262HqI!VqLsw}vCHLv+Y}>xu+S&*;lX=fMz5k$q?6sJuPCVvbjle}&(Pomo0RlUM1}~vFD}Dhm&MnvwT+`t1A^C5CfBfcWPds*6DxMV14+Cm>FJ#} zYVI15r(W2M1qhoXZ&eyQ0O-WfPRLtBWKLTr%nMJX_FCp+%dm%2j>5Lxt`Od56Z;$^`Hug)9rNJj)%!1p0O764Vk8~ z>x7c<1d(fMUqeQ9x*a;d>cBV$gSu`bkq{|_G!^V^&pwH=wndx=@^g{2%*0_8gK2YPen+U0mL}WN^)1|%yRztGakP8|3WaNHGzDTh&v0c7RVVEsyJ8gRMT5CjBf=m>oq! z8(>c5dEm@wk>3YH=TM3t0p|={ku~9Nx_klQOdX%VN^TU1Ooam>*$ebgq%b%P*7{ls ztyZsY24TGfK2L~dk|O)3e?E%)Fujc`qHs}}|Bs?`aY*vs|M+A%Fl(yeC%~z)kGIcVO#%sFli84#uG&R)-m8*D*yV34 z4l~y3*{TffV#IxhFTA-M8mfW_h7t-Grfk5!PX&TKlTp0-MtRkR?{s#XUeBIL%$`M6 z)4X;#?k7g&?W@+E!Tny!YnZ>Jq$A(BRl9%t7Bh{=nfR&>aUc@iji0gc0DH`DI6PPT z+)@Ud@)&OO}PS`FZW&P?zf1DPs;q%?rh@l?#qN# zWLbg#!w2L)9CO8F4;sHz@n#9)&!H7RU-?MwvfVn;;DdtjDLGMwI9wn|!0 zvpoT00=*^t&nwSB_eL*y0=kr0ebE*aNlagv&CeL?9mQlj$M^H?BOpt64vgb*CEyI9 zkYq&TBVSZg_j*c+FBm|QWD_LBr4b@9a%@Pkjg6+-&Y&y_^v z>R3rg(E+*uvV^eX)}hD>S5Z>+aF!%;RDrd!9$8tqRXa1!CATuLddke z^86!DtZ^u;G05@wS}c^;o0;0gfn@2kQD$d=C*2#|0l65Z&mASN%o?1|EU2=?gTPwL zQI}}R$wuIqWF$09_)T?+L&|%wGgWYVrT1LcCcgV7QBxka{modUNI3-2jP{0Nde8ny zJy(2t^Fe1-k?Hd#Vf)Hs(}&HMEMS0~Wz3oCuwemmYQ*qHi3bfrePzdV zS|#@bUcm8HtxlsSp?$}V*b2UaaXh3`xNBLojuT?LR3&OrkD$|uXl0fm+%<~9i*Gt< z?rJ(>T3Z1}q==r~q|P(Z@zJ)6X8Y?%xu4LrYCnn_A@3jD@e>=`G;iIp7ME})@suD@ zDS$j*c5|^v-AGH-AH6Bp!uPq!R{owP_IHl>=DbDY`dfu(`*n8)U$%)QKI{1>UBQR- z@83~mKjV`78S)hEp?)wluBQNb1v%$7Sdy<#7D|&1h8-xqhb6OwyPGn z$&Fb;1*b)BU4@+Ixoe#TeqS!sQ4qb?OSqU-lBAK=$)jw^gin{qa=0=&pPQWSchy!osGYQdZ^pa6|^x-%{-c&1w5$q75rj>7gFn z=+8QJP*?ml&qXNfc^PGciC9>MKsbw|lE2g)Vz4qn?qVN>@icO`TJrH#idSCZ^Wg zWTj)YuhB{FdX>}y)M+wQ6+Tz6igDO=sI)>#*8WwIhal*qy6tw z_}Nv>6I+nFs_XE-O1QzkF6RoEaUM!KpaS%-XI%>EhJdYAJJI5M=r8gGg082sI)4-r zNovo_AkE;$2nY@H0|{uj@rLjZ;eAW!z}ly}tHj7OUB1hE+R^Khd}L9sWYZM}IZ0zi zrC_8Suuf%t*%~sZ@L5R>a9;oNb;l?9iz-V9mT>P9wj!YBHg14CF;ON9-klJEY{XpUaa_4lMyl;KK z1rZP(9K=OMa6%LFZ!5Mf7k^h3NK4X`i`=_!aZZq(&ZX_sG^G{a0bR&#Ax;@9T!Uj+Vzpe_A#OI{2WgW1`LOu@8gt zcZ`@)(heah-w|jICrx;Ulm#fee>EuLM1O(Cuk6$ zdTC70#$-q~Hnv-FsA2d|e+4fq+D@O01`^geq2TrV9oIWz((z|Qd{Gc80d-E-3H@LC zLeP5_aaQ$<=m5hPI3u6S6*sBETqtsqqT2{>$UYJJZAlk1F-wdw3E~y+pg(>7h^ps7 z`nCT33h%uT@N@!+@VS{mjohj&VUiUP+WC9^)U*QDcC8kc6gql!gQ12ff5P6Sjgs8# z3R1voJq=y-`R(S28ez^m!o;s7y#&-dJHn#s_L%2 zty%A<+zVatsI-Y?_zc*T9Pi2+M^uEzic~WOFUrd$v=+KFbnYAERm^J<;pRQyyB9;c zU8*Pb{r{s$28DBy!s=!$sPgC95V^H$d*^%P*xbq2cvdM z8TBgN%%6`vx^3WfSU>M|uR=UJmG_|!RZGkF3XD)b@JV}d<5=e?g5PbwsK`ouU8d2k z{A)nnyjB0(xg{=!@$D62shA>Fam)$V(spyAw~suudL2*xy?+o!8Kap_^|rlk84(}a zRPm3+`u=&u+jB7=_p;mSS;~4jK&E`v?N)Yz*Os%8u5-Tj&gVnS>A)?B{A_a%^7V=k zRW=;!!HxOh*hKk#Pc#;qc0(6Zu>{N2?WQdBLX{iOjZkmg?ccZ*nm}xm^Y#OmPE(Z? zam}u^z>`hg5fc^tL=rXGG-Ghez8}}PlJ;`$kYfg|pSwM=B24VS%DnM3a>eE|di_tb zWIF{fsRe}Rlrm$w(TDsD&+iY0e;a?AxCaKVtM@{JV2MSJSv?R`;{w%@+K_vWg`QPV z)-LQj_?QFu9$3eOIj>_}-*nu39J}MOZFY;DQCV_Zv*nx~OpK`%4xggkXT;r=Zb@_2 zz+?CYp>F_YmF+%`s3kVzbk;l{U4)ts&YdQvTwjG> zK`DWPsB>O#dk((*q!^fU1m1F>pI%yzm*%sJD)|b(%FujjhMufu9BWu(rnX-|W^ejC zTVsglh3w)`k!2Sw`X34D8<;n1UX=5ga)7 z&GWi7D`#!*N1)Kh@EFyq$B_2tS3Vw$5~7YQG8!(7P(B&x&f87?yri<^#*@0df_z4z zQ}a)|c1v6M^hAe>$koXqgF|{rqp6co7BJFpakf+34DMaI>H}3GpXp}($O}6@-1cw* zS@Tj&_+ru;7&iO+mr%wPGE%%i!@d!j*dcO!fKNr2YwtL&VhPu!iL0S|8>i!75e-Sf z16t=oXt&h$fUg^~Q|vooo@p0+vHHKHpu>?&Nl&Ddo1WxC`JuXI3kV{ZPT_GC(>x85 z?cJnMc%Kw6RiUE9(A)9Z&EL59sawNvtlj*(j|}xW8<sD4qD#0SCqv7=>*m$L2xrZKT(~cvudFmfZ2AVLAwG!fO8Q~&2$emZ&^LoLU}F~mZr1t-_D+F#5-Pom`i;*G z@iY&z-&{#eL+~b*(deT)=q9|Y{fN2SQD~zYb2I$X1^8ZvpInFK>iq*O_95N;rOJpY zUnqp-9acUt23VP;V4?V8w|ZI47`BHjAN*o6ePjiK>zV_Qz2+Hy&h0~5-SXwjqcyeF zQGepPAVt`nS*OJ<5D-xqwo;;c`-l|06xI$5Sf*BI(f&F|Ligdp9q%t?*xh#*I1~hT z+7&rO`!rrx6FaKdRGDF-798Ej7d;J|JKBHy_)ogl)q)MoDr#|N$LkCBnvE&1Ks}rU zM#oQs3YrVH&41Q;UoVkF3TGq#J=$22v7B}g66Ed7iV?~X0&kYryt|#>LMSikJO^Bx zV&;);&X?DL-to`tVx5Lpzboz6D{z@cuwfR^hWS4XAJpo$$N|1Q^50a;R{eiKtnij1 z1#2`Q&{&&$_o&$k;X$__3od6-`GJ;(bv?<0ihTOrvRH){*fmxo4iS1ro5S>4Q;qtxH8AQ0EX$|p2HV`T^V z3XO&FEm=D~KF=o#+ZCFRSA(cU*z=>OC5+ZIU@~Tk;}myu^#GgXLC>SvoG(U7PM4Tn zrV-gPHLHr2>_orm5|}IGvW%yaw;*g(r%lW0c+i(OgnVjKRk;8}GnbM|cZjWVJ<-EE z$LcL;a2vRnHluo|IHF2X{_hlI*t3|QrQMoldvJrVj}NV49!whv*=rBu*vVugeY`QUV$uvJ#EB=Vc*yPqLI8#Xu_wkz2RyS|_EBg+&} z@_uScjn-?mK6uH}kY4TvISg-`DF(<=O2^7Re8d;i0k29eh@-Q%93C$o=wrRwf<+_& zxUe5r#Ll=mC&C1F7?wXaT@$R@k{UeDPM~FdN^VLi^Ss0;D50f8BFaEuTGWdJKc20a z2f&siPKw2#@s951gA&l0oh|bipAAE-msG53Av#4wENr^xhe8#WN?uq#4qZ~JbM67T zDW$wFxq8nUEKH+uvBF$7$jIl5Q0APhgu#<=vxFXInXuI5jKWz_R1P@PvAbaW>TCz7 zvHgmIP2wK)n;m>`k~Z#8p1j%)ba(`RTT?NQrz zy@nz-kVRJr3&Ep7_`25B1N;7pUh)Mp7BEkYH!6rv5Nh*vINBGUQe^T$`6;Jcn+7c| z?*XYxcv#8M*@c~VF}i{qx5&w`=mp-S3_Vv2^oUwsq}d(?vQj=5t!B|2raglD2l*%P zUZ205m~JM_U4<{_-=~T_Mrg{OE<)GAaQx? z87+-2@fR1o=uzBrhIQ6az!FbK{V=NDdEm^qK~_X@f=<4hc=l%=qlgvU3 z{p3%ViE~$v96X0U*9@9xL5@mWf_C(GCC3@c`}!;A z|9j)VB}%vkzlYR%LF7%hf6U*+Ro7xOob$rnaq|nr?v554%Es}C{NSVfB)5i+L!>S^ zxx8k=c_4j;O}cbeA`qVbKo;c{6sV);m2xaTpA*9b^Dw^8@SFw z6vL>IuPR8!^<$HU0HM}?1hz!u+7239+Q7)~iT##_hwF)4#xg`()?M@&2h=WLDbsTP=nZ5fx7^!LJ$r7_sR30I(NbvN-O_4tub{(Pf}B-`mSv`HI3{P61Ua0 za;S`WL@|RXH~Fu?9+MAcqj&P0<_}ym&?#*J&`DWOsIL7QiLCB!2v2spXdGS#rx$UU z8dE@}5H05q(ZUQLv`#S(tdAvzpc z^fjpQs{d01RH^L+)fW`zMA>x({NyIFdyu3+HD%)}A5u~7GzNMBB^m_J7tyu>Z~0E_ z=|HuAubU(@J9cbVT`{3wO}vW#ChIAU_ejk>KWUOdtar|A>j`Xbugeh=G7JpZK?+P(5&endn^ePH_H5hk+w7)9Sd zW3&U2TO0~VTyxXbCn9z8?(4RXgu4tVZjd#Qss&e6Q~lW^qn=Nrgzo#oE&otdH}J5E zy{IwDGvQ@@V18xp`p0A;Z9lhrhtNj%5h04pUUSRfGJPYuI;nIi*-Z@|I@z=pq$bVu4=-Ix7z zdE8+7Y?xCXs5Ppng}F!DZg1=e4;S0#Yg43QQYv-h*u+Z12w>0Xq-D!~xzWizec+6T zQsI_I`LTJpzBvPxZ4G;GTO>;MNIx2>L*QekK&*qQO$FoHyL&UIYaV9SVuT1NOp{(1K`L#>xK&FdJu7nLEnX(V3^!0L2o)NIQ(DqI9!F_IxtF~BN8Q>dY@K+aX ziT0l_&ywWo?9abb|2^sdUa;bs|JKdF1th)I0(+wHYH+knswS2U^w9LXzeqo?zj(Y77tyAti@8O4^U)tA~0BQ9kb(WU@fEGB76D$t^+f#z~)I^t&D zHGf=h5k|tH0v`)89L6n=b>F`rd7Nlz)@Uqlk+k<0aA0tO?vqol+1O@d`S{KUwG9Bj zza`)}>aa%|A|YgkD+3CKEGdOa0gQ?mYQ{Rpnx)k+NI!FNvK?Jt7M!2$PJ{$xYO}qg z$898cPxmj$*#I4!%5*jydW(6bOIUX|*9e{Y3natir0r9p%cugoum&~d$HfY0;R859YHEEd4&G27EvVDQX3>&t@^Y^^y+#OUYkCAA0Nfit8@dmvg5fv}jbVmAXhU#jc4Hi14M7 z^4K<`eFWHjCM>}q1(4_j6RZVO`^;DOyA?^UYEn^Czna-p7m`Jut&_;Qkqj`S%ksiv z9L;hYQvS_Bv19Ms(i*7{No)TO^zk@It zxnXZ=N>=vw2yQmYNb`8dbJ>pWj6uA%o_GFf1jL}KJ66>TVBV4Xa+A(*eJHj;poMo8 zUcShzmh+IlnGNB%#yfZ~mEgx`X?N)3n#);^Kh_Mwh4oxI3(p)_K#YIj}0$2T({LqKR^ z&^?4h*vFOB6c~%Jczs)`7iH71o|WQsQB9b-9GiHBIMGa(h6m(|Ni?2!Df1Qr1P3}R z&TqizGQW$$F%e^jV1VbyoD{MlDe~GoqjwD>+>@ntnXdD5aQ_?qW+GwZll6+XZGEK+wm;}e?l z(r%+$;FN_2AESzO6yuY6apG*C^c zhy&kp%1?_QL$_-59Et=_CaZrtnRT{S!~P!c?%pk^=i{#_Kg+o#?0HTmeDdZ!f+sxl zl%{#-JO?4Rb(h7ej1X`C#kB3(jVBN`X&|)qUkW2eDQ`-_n!GC^8VwHZi1#Y1=sgf6 zK9Z5E@4kWpQqX@wAsS3%86g%;U>_OZ^#{Vf%3FXa%-~I43T46ci4S}Xoe-W2j^RZD zV)Us_9TZ8Dw-t8o+H~zn;jqG=UNY-pMQuxk@s={fHs>%BdD zO2!ntZBJli;c8b zr6)@aaw4)i)QBa4S-hb^sR3Qlo(*^mpN!O?AvwRshtr1V*Q z&7^rqGMyy3Jz<=aHy0vr9SIG=RkXKoyfrDJ0OJ+hy>+xo$Q%OM>|c*+URAq;EtU0W zhOCLbKj~ootibdY+j@;rd6Bx)EU}Kk@?nH0&`MtKbh8zxrrcf|-to?gWMgLLjBkfb zANpJY=*RAuKMd>DQQxd;Nn6$M)PJ}jkqyyPr=gWm!Xx8L{HoRAWwGC1wV#rdTcWfw z7-5=5m)^>my6oNp@;c2Ypt|R)oF)cfuRZyfBs%*#{>-t4f|rj2CXCqs0%yBdCRL

~?{9r>0kwR6nNi_r1`s^k_2*qUw_RVzAuz z{CrFa*6p$|$G2<3Z@I6s(=O;zMlZ$IoTJE%S=nP^@pVXKds5%v@TI#WtlQD+nSXtd z-m8H%q1iz8lB~P${w8}IpC;b_%x^Y$bdm?tj_i8<-t`IuP}Un90oa9 zc!xnN%>H-I_bGlTuk~9JbQzj!*yd~P){ZT#tQxe^i|m65U*xUjn7-mvV1zv3PZI9) zkGvp1HdwRz30n#aGE@xz{NBmV0+q`09V)L$RFuel^=H_p*H%>lmwx?SK(yDc zj%Tim)Igy(*0umnP(U8JuQnY$s>`kNc$w#dvH8o4W_SDi{RWu8*E~^Ag3btVe6r|; zgW#BNPmTAdxnX1y(6nKotG?5-voAiPk@$|WkW3K(EY}Xj*ZXue%e~LGNzQD!(Z@Br z%>r=+GkimziBXn%g{xSf+dcJv<{}hwS1VJ>Rr+$XvQIMp-~!4(SS9B)MDDK=-9f_- zm&G7W3a$Xa9*bp974224tGuHeD)%#6ZOiZIP{ky@MdU0RbruZxWE|~_>=nY|amQJ> zSdeN^O%K2%NGj8R^_Vy#lx-s|&k8mBMo)|3w_W{6gdDP(hxFUsXPV%rRN6*RJAso* zKK@1!)HS5&9ck!_tE`F}m>G^Lxoy;Q7O`Do+GA5*<%JRLp`$lHTKZR`zTEB_VnjLZ zhjR|(HXUkQXZL}5B#!;A`zHS*Gp=7yy=wG2WsSL&Zv&UdCen(w5YZcs@kV>!GTzCa zx=lc!YI-a3pm87h^V_&x3iqaq@6PG-$i;rU#NV08%sf<7 z=-D&L^7?|4`$v|pJuUw`CtHt1rALkujVc{w#7$;Odm%;cdmRV?Au8RYV8g>!I9xLz zr-CloVp+1XELcL%@3rOn8*+DguL}t0YI^6pB;RSWjJ+>c?8TwBIv2R*WmuF*^k)3` zckU^-z*fM=`Fk$rOFX#L;6i2Wm*~q)cMxmk5!Q=!a?T}U-cxc2wNT5RiDa?GL2i6<6=mX384EkIJ8 zRtSZUnnFSq(*>SPHXDd9Dq+)AHG&)JbOZ*&HVo9aoU8_GiWnpEWs=87Axx$aO8-ze z^MB{CvjlDWyP=d_Pb^;bjV~R^qaGpczMqG&M-cR+g}(rj$wlh6b{t^=jV2l*?lLFx zsVx_b1^gV1p>2s{zO^9kAuSzdeqNM8fDMXkbE>$#}`ED?) z#BE3Kiu$KDF_~7<^U$BR&$(PZXC|1N)qBkm0Eb`swXiSfC<9fmKNRTmY#Rypz2nQ0 zaRp>DDXe_nWGEf!+zsw0ndH5v?fe`YVp5zVlH}Qa6MRkeL9qsA*h6q>rZ9q1WQPi} z%{}ohV5*G{#YBl+QNM);Sb5lk^dpQ6Dnj*6`S6>Oi`^44xm~eF!Te>tml~Rh!fLPg zT0m}{a$Z6Gl?}ca-*B9(o&8n?RC>3K8!Ko~%`9{I%{4PDa=H&lH(>S2nO7S39Q$LK zwJ@Otr$yP``~tObpHR@XHg`?9PtT6L4bi=9QQna;*MTMW({`KW7ia!!Sn*4U|4V%W&T({E!~z+ES2+Mfmg!PuOfwAZax2 z6_?cXtN-pGL*`=CUGKGP>3!I}I`__c2&46oL>*w&9|k|BEOA-})ZGMO&6Ii@z_uL^ zZuW@P`ZYF2r-bH-Iz{g#60@&?*E{&pMzcO;Q$_MOk~^0c%_s5 ztXVAK3Scx8;zos*WK}0}I^LC|+;30>l+Q~R*QVc1RXMofr&spHT9l^3O|mS$sDt0FX4<)tPJ{m^G?`> z3+Y}yMj8B)?DcipO_^njD?@qQGi)1npY~VT#qgTKC?b|@$uQY z+j58v)1vdvf&7l&xpS|dUN@6{to=iGbmmdv_dy2pK4~dYczlKFFj?{t)uhR7&e+jq zSNQp)u9+jb)(buwZS{8$3S&b%LwUY2!3BgqTy_W9p#-YcG_HxTZmP9~V`fpOmne%v zM(LQuJ9J(VHw6@Kb`M%2TlgrLTe9j5~n!XK&LChXx%mGa8C6@!ol~Nx9l6`e2pgulpe&Jcd zLqC}wZ-*kXgi{Ay?}-=A^x!i7I1Ty+rNx{A>|p z$l)42h42@LdNRv3rc~cDGYfUUj$9N90Zl<=*sejUGv6#Ihq(|HtCK}a@P5jHCf%kw z$vu(!wN}q}oJGbh3&;76{1D=5r5L04rG|!cmD4dK|7cb&e-oM1zh>XMccd@yY0&er z-=a^k`QAwX^UZy6wWkv4CpWN#CWDWf?JP$50ALBT;jPe02R~R|XtGJo~?Z< zjQR-)8S_AJ8xDf=5F0XV9$L260Sb=q+tn%bup7ff>Rp4bFb{QvbEe3xgCQw@ca(ZY zoxi|5X4S*4*V-iP4D*JR^J`w;^8Yb474@IK4d0{HnvwTT z_$hJ~8|3m$CC^F#&1cxJv0kNrNo(q)93`^cm7^@wYq z{TMR$`@6EX{|0?uJMf2E01;}NmnzMhKID{SY~pXL3AZ)Oc*c^ROfR2m8yMN(iCM3M zx+~vp)-m8a(^v2&m|wCxz+nfR5zEK}oqJCfz?S5$uGrPrrtY51u8#3bI zLO(a*iW6lN+I_OFzTD2NyHwX2djh2)pb8};Syb8226n`I_?TsqS(LSk*G9U!Sb6De zt@PTp>*jwzX)u;!xoV4>XE4+>mNiMdis)(w9?!KO(*`%T7Q)yL*yJa43xcc!y> z`@qguhzc$ZThj*Vg3t+a8_Q+tgQgCSZ)aa&abH=>6uAKrW-o^13OA4SbtkglNS6X? zVfSFlajc%=9&#i4J-Mibu{?p6#+iAUtInS3XgXE-^q|1UHo!e)>kV57oUUqL{i$tQ zf7Lhad3k}!hEs>Tnu#CUK+%}=XMD$*AZQcmQRB}%`qZl8ANicI^Pj3SRxiprw3@{w(xs;b`$E2cYmC`ud;%9IEm=P%PA-97P#`P4+=md8uT0-a*tTV* zo#oS)VYCC$fIFQ-MmT#}#*TJXVex;P#R)Zr3Z1H@Ifbz8nH`9d@&vGY?tu&lzS$et z6e@d7@2jXqmQWa#VQFBn=tIGzBXp|O4fHl?;n?bw)QAWQm` z!OwaXEvJd9mr?Fp9&=%{Zof5)cwX2O&ZsZb)YG|!GgIT~Q^KR38uzyfK)m9_4dcmre3ymGd0RwCPq?7b-kU8WL68t$L~- zVcR0PJhC%&!1z2mS)x31Fhylrh`e=L2=Sd=TR;p8yO z+-OSkrV5JHKiQnUVyoIh&a!f^UOcRfiNzqo?;lY+lI!i&AT4>pTcW*)kgp@MXbB3s z#)beY>6lO^M_TU7%^KBShVSaQQpI+*{gXV>8IuK$+sU>>uyHQ<)n;$U0ayuvqk3aU zsW4Je>#X0K-JQ6#?&RGKXKy`-<&*v>kYL(12^#|BhMB&*w0U$Dowt>Kk* zr!-Jh?D=0RN}D|ft=YKnu5p+4YTE_o5BM@=m5$DhqEL? zjwm3leQmGjOn*dQ`YsCr)v~x9AXZRN3g1{_$?VXGk3nz@M#mM1xk+vZx9Q4MSe;}B zThmC17DL5dEIGt*0#$y8#mpEO5exMH4mft^Eg*SKlBaC8I7Yr*QBRq4VtZ37D$_b| zWP4g+R;TfRp;FE3vy(C3lT=@?ZJV6#26M#4=VopoDGv^xZx=^QY$4PO6l(A4P9jINrSo3=!(wKMv2*EEO|G zbFqmuooqEF|i@{2!U!IIE*gV1E zaqp|$-}~8zOKt{Oq%_ZwTUX|}>cHK6zke2ochYoaS~EZb*`?&`=?E!ekJ8|A{YRLVRE{^YyKH$mcw&Yge>au=#>C?uY zmP^0imfR9P`S%{st%~csqetAs!oUmSA75&HzfQUuEFE}(OLwUo{*fDl6uf$E^`jc_ z``ypKs<^eU&-7m@ijRE3F=V2YfPGk!6y?kctDrL#U5s4n%r1Qr2UdIq$y^L2K6*S} zWU6!PElnO&X_l{WICCRswLt*``V8WdMHcWTHfHg?;?!n%G8!M>)P6*Kh?piyCnG*y zXcWNQl8Gh_2lIW!e!hIEe!SqdN$xe0#!%soA!;O{-1JcS;7V+w+1Uldpi+*wD4l#a z29cD&BzK|;)0W1QKl|n;<`aqLKVA+{I?~-I$}NOrQSSZ+($o^~*E!j-C@Fb~VSypU zK?Ft3H7b{&o{x(bdolD~@Hrw;Bt4+Zff$`q1QFk8sP;p+dL<0Gb||E4`$!eAN-<+ZP= zEa~avQ?eeisDy;T(|ZA=@ig45C2BrP?$ycAK@rDV zm;7te)VmWc5I6_ew_DE!d3cq-)K1|kCO4|g1^qti{c-p8*?}}P8gc5Vg#+))^RSiCRJJB$Oq6_cW zrmy6|Xo~kw(WaHyOl>DE$sR)UZSVkv3<@F|N)+PU=D=zLgZEu%o-soW%Evfy@GIu= z7eAW=H3-?zMqo&gfZ|Aaz%Q+YLSWJy8WsR3(GE0D6pi2@(RetL7qa%J9#{Am%Jp@)C6e|E|E zMks>UF==Y*sh5e@`LS!2h0%r+1L?`y3N0~kGlc>~W}4HNy-p?olY&)q_z&X|zGY@( zW-oSWh_+S+az=3ig@Y*f>Cv!AnA~QuF=NO#NOPo!#RQ)+LwO4it=eyygbGogD(?|3 zHao!**7gIP5nElqOZ#q9M3!T3um9NW943vTsJJCrs$m>sjUu*3En@7w`d%|;u9!M8 z3gU%!M^uG6m3KXYnKo$L?YPHTOSoid_ky1J&N=d~*i&x;n|*${QTpUb^-Q0BO;vWw zxTKf4XzTNdF5ZyJI|RSnmcOocTiKkimGm((N+RJ@NK>LEd?-yORpZlUu}8D$znfr{ zrOe9UO+E1uG~C7=!EsGn@i)`gSx{@!6`^*5Yaalq;j3o@vlmjOO0q-dZI4V$sOq<) zTP{c6Mo-rQ6?!32K?b6n(_7J{>P^8Wv9`HgB@ z${}k9Jb8R#s+zIoNCB(TRCVcB&aUxPW92aoWiP(=*0!+W?uks78a$URP&GQiIn(6Q zAi`;h>bl2Pq^_n}cO@Le(g~#>Fr+fb3+}ev$~Vv?y2cGKd4w`zM8ivL90ZLe+4Vy${4dTtb9?m4DCP@`IQmlb#J^!A&1PdJmteS zuvG5Yb6Iq`VR5V2IFZ}yK%FI*|L@p1qM6g&U?{Ma-op~ujdM3Nho~5k{j+Lc>=b}Z z8AD&2$Ex;z1_*?d@poY_7#TDOdbg6(c|4#j>+hGkK$Gk>bXNMM&E9^#h$2^sgJmtd ze`kJB4Tlg)O^M}BohhZKL=CN_Tc;KFep`M0a=k?_$|W=owxrjVJljE+vj$7A?Znke zTz#Expscc{;10;X*187!5^R6s)o--qtGDl-XpCLcGqW&9FT<}zj3iUpOk=&)Ju$&b z%Ly|C<_2axnmm_!e@QJgZa?Y=EB$J?1|9E_lzT^?Q$Fh?J2 zSS*&P-ZagL!L3k6HY5@b{@XEC?1_DaP_A5;(xh?JDcSUDv#Fri>m#d#G8Q2ae%C0q z!eXfGOWNl~qOd>O^7?UVIGza1c31Wu7_m^WOf2!T3Oi-o7(gM+HKl5}whR{~ssdNk|0gxJ4$e0_I z{0CLbDA!J^Yq%m)G4B0i7_S6@MbSF zZO>cejh}5xyO95Nux44SUK!I$_E?dZtDO_KB)+#y4Jt%S0 zTpI%ekm(2cE)fE)_E1yMb(h~Dm2%`=GC64vN+>-Q0Y9u{<%}9yyesIc2dA2rJG+wJ zYw(pltzkKsA~>+NBShV}1It}N!bQUGLf8AFi-x5qYs22m0mlFCuX#{QN%Z&BS))}* zObqU(oE?+GvMpf1Dfeti6$cgJ!;+V*h_*)4t?=whju&v-KALNKXMgz-NqAE-IlzX% zwF)RAsjyVTQLv<`VD(!Fgb))sZZbG*=pa~p8`rycHVrB-aKzySQ5y0_gMa#;!2zkp z6ftJArMl&2{}OP|JVspF;Kk0)l)mHC2dUAwWy-^>7ZB!fRb+>Gz$!d=_~8#4Dg8B2 zE33WpX)wnW=cWhJFO9r*`U7oBJ*)j#Dqb&f+QbZJW~z!(=6L5sU})&S6K@ti+|8<< z`L~_Ro$l$X9C=mazT7u|BREbAcBExJ{f`c!p^1P^BG~VQmoQLT-Z4s?zvlA$m$Z%C zn7sARnj+^@`FpO;wX)|yH~g7*ew<|v`#1;)W%UL^slZrvkBE|T3iVX7FJlDJ!*74y z;>T*(8kJ2&sH(>|xV5UVPU> zpyKS@YNQt{&y}qaW%4CzQbK9|BJCvsKNvy zmkFTjVrQ7*)43P^&?;pVj8^b&|M_Y1Bo`<9MOS^|^hgm`$J?fXBb4?jEP9z63p?i; zQ}iExeVCxDiy3E7*d%nU(5X@uI=UtPu5jYr51Iz*PQ$F>0^}FdB@a_Ku$z-A_TVB3 zYpmICAd{byc_3t<8=27;yn9?f{`>wQHAq{<<9Ay#=hA(Th%Kv)a0;moiT<@arb*%{ zb8QWt&Nhr6@*_!876pIS2=83u`?6a2up@~C6E?ZDC@R+c{PMV>w(~2FnAbGvbIHcs zpBd~QmFzG5x-iBRhTgp}rsw$el}T(OX%$y<#XTuZWTkY+&>p?0v}5p;x^X4gCB=N} z=9MwQcd>)QCpYh31m-wD=3iC#9pm)2I2d@R=#^L*qi1aVq4;Nh_AwKWc+B)i*yp%1 zV80OghjIA14$%Wpf2$E!TLSR)pMTG5Inr?YVs~C?e8%!W5PKVP%d11-R>(C8e(A@e zNNXvn&kD^~xevr&Jr}Vs8t1-x$C;rNH+KD!)aS>-?_=A9YdYA~ZbeVQ|H&ajZrK>`ywDR4bVNp!x{ulW8t#-ZetUEdYHEVbNcWLV%uaS8& zXXdyjCo}}Q;e_khOi%vN8xlDtSxxtIWyC*28zR{y#xpdPUx zfL4ahq#&FAN_@+V-1&x|vaW>I64Q;P=jjGAm zD)xc*_{v0t;`)&3Kpo#dX)l!0uWY>XkMbLI+ec<;y^iRvZC!f#-Z0mE$R|?$bu=|xc$YOfE5@X%rnSPowa5W3hh0#lK!OT@3$eXFJ3ACIC zld`h#Y9(Y(#-_6_f8@`R*+gvA6*4C4`L^WHQoNkOqu)Qx=o5o;zH3HXVLWCvXdJ6^ zmvE9@ii7(cv-VCVL~Jtn;!6(H9MPWUZH$Fzi$4s;{)`CNa{d>~5Yy6lQ^;EL@8@f#qIZ$+VtkA^&2J^nHA}GQDxTGjQATH<-VZF6_7D@0cVN6bi|X(9`{BZt39XVN`pUI&mpgZ7*bY zSj@q&B#+V!W~@R|v_qY**Z-9`7!#j64N7(TQ5*F!Ap}$=?4iL`-?R-eO`Ekk$yWbV zVQd$EUr$m@jgep%=V~%M-a3!S3so(?mA0DkzjL>P5jZD0HoL}C62Lc-F?cIh^*}O% zsJNi5jpmSy3)^GiSD?=-YdsEKFB6#aMqN>w8A}ACiZ)HYi~fsuZGZU#0l_IIm_rR) zrSK4+&YMG}Ex92L2g;@kW*1W(yEqoBS(>bo=YLXVK-@R5y3&)zN=Gh*nY2gC{bF#= z)tQA|KSx4969K{@Dy8s2o7yEUrIb|V46cR!7tRc6w@`Kn~n|M(rzn4 z-LNaCImbMJ#lQ4KRQI!XTSHVs>N*>}{&x;!n(;)T?(eOR{ylavP+uG?*{m1E@qLATwh?Mu^ zu!ZE#&@dIfd>@=2P4+6Rf2}&Q-~OPWq;s@_!yw-%CdIjWW=l^RqIpm`)?gYX6O&Gb z4*PUUr;b3M*zRjWmKb#upA&)5H!77PBa=~nt25J14yxggDrVIOb(dPsHGE4*JCodD zbR^LSRUo9G8p2sC05sy1>0#flA)stSe@~}0(OkjE?zJ*&wm7&zb=PHq2TQ?cIUiQ- zj!`}NUBB}taq4$Y73;uL1D|%bpbpY?EV7fs}z}P-mW2s zG=<|LJ8jqZO{6@MbAFfc{W=}JbXV-M!jSWU2G}^^V8fphFV~qj%N?PCFgsCOD^S^VS@Y2n6t-1C`t5|ZRF3KnS#S@XA-}aXD zHUfi>YuTbMt8=_0r5;}$^8GeO-EEio!dyK#g*^`JA1t;wD>D$s zjL#pPKL1RJSZArTrt>JfMn+zFNc2RTJuKl(u;M#r&;!D#P8EIJ6w4~i6D43 z3&bz3VTApGqgHa}BE-y!>C}0=Rg4FV_dggWG%|P;!jpaj_!?`AetWdd;%HT&#!~!y z&D!&s$1cMWUpSF+t##FrZu7^3$5I1r!E*^dH7rqKl9#gkWygKLdKITrO*L_>aT8^!R> zp;!NRE{?UDJhtU)l4YFYTC<|BpjN3e`q|$vx*Iehep>%>baW6}UHH>p#vg7<2&?M2tno!il z_d9xaGpsy^G6rL z-f&_?nd4o;Hb(AS0Y6Ek0=ET;l4O7h&ZMha(dm~3TZAU3y>`~lVVX&5p0#>+v{C6* z^cd1UUo+=+3{+z^L@nTM@G($8_fSjmDClL%EWd9MDUu)<4ld7Un9;yvs;_&n1u?6q zJ*Vjg8D!CZcwdBwHd^SNaZO*A9k``gHEK^gwBeK9;-oEg%mE!P&{V=QzUvM3y#0^sXf265lqP(N8by|?8%)Sx7sR?IB zTTe)58!r82RLXEDBPtQArZ06E^;{?IdEZD0pe#`0PAjbU0uxQX7|!vU=a|guFI*Fz zblUwPGD3J%UL1Ge_BMt1d&rh3hkreUIVGhjWbWNDbSE>e$Xh-L!pg@t;i*URRjh|q zS6{k3V3?}NU6amGn-w0a{gTGL*01?O0cVpYcW9qKn<#>Rz=AzMywE~N;)+Jv*y|)x zy0R-bPQ{Asw#-}hMGOj@LiW<%Y59AcX200=l)QtCb698Dyw#L>BiPca1=aCnky?hk zws6g_X}=v47pdOUpF^LQpX^NH%o&ScYsnH%0BRNOltm-2Yd;qD+J#M~LFQw_1&>fh zOQ3tS&l-nvz%Q${%^X?H-geRIJAYOemvZJVrSd#K)bCEg6kWv6P)B2`T=H)ETk8lk z-7t5%^mSiz{Vz6nlR)Zl<()(mHQdE)3$PM(l=zcD_00|aqJT{>Uz`=~P!@3}6GW6H zPJF_De!`2aQ}&GJ?Ks&iN^w>-F4clBoL(MVZM%*y8soXr(D9RzIft%Oi=NEFJ(t=C z&p16w*F{28yWV^oA7y{Hv(p!$ql`8)_Y}E z_ZRz{%c@SyuQt})Uqupi2NC>cT~(sPLTU=Dsh+Q{t;G(QUc9KQO~Gw-JrxW#)JiqSUcu-+o^YD5&?$tXXwWs>|;*TmG zs}|ji6S;ioU(pEdPrUL+fW7Ym z;$gV~>(qZ8;}Mn__Cu+Uh`-X2LUtE$S;0D+-J2z*oEx|H-R6gIb(z)!1JBx!ZS|c} z8e2#`*g)!KE^1I^VNzQUTc-PoPhc0Ef7r+M(XoQd9q3Tjxu5f;y}bn<`!jR@Y3iy% zRb3?v+?H0`2r>N<33R*eaya~^IVU!20+jsoPGEjgCt0{Co?{0y@ZnNsBV#mIUpti(UH0*mJ9flv~NO6iV_PhonK&rJd6sZQ-WT;IfqjCNrSATUy}-O_f7(G z9Xo_CA8az*`jSd_e*hu>s1W`A=j2m)-TDp8@bUP18;e7hzu6&uN3{^GnOH={3Ol-M z2u9^Ic02GkneO)3D$UV~yrc+_)V|?jSNtqub7f(dH3-F?WCavUMZ#P3_&)^h(+5gi1w)*%K`RUD z+k^ivh#|@9(nZPP_ua*+xr-RZW81YD@ZUAk;5}mt5?>J#H7e=D)jbo#ij4&p$4@n9@6d;UirJc%`Mq zg%8qTyHTE>bUQo`8*GmykvmS*yIU>hF4ST;luxSPxxK_=VJr3Mv&GP>mMcW`%6$6g z1WM1bF}Jelpjm7D{Tv+mH2wpX!1soMJxc2<(R)<78r@mp46`o-#_V!*5`jM3dl}i+ zB9sUvcp;Qb=YZilW$2lt+LuB|vTIBhRJ7$-86Xu&{dJ_GBBqp5_GO!4bS_I>OZukBH7zfdHLWlt2lDkxTo47(I`^b3_KFFoFG z^RC>}2G8$Hu|D}{)pBz-Aejp8kybp8@>EeNXiRIfm<;+8t#I@v1R*lK2_eWBs3Yt3 zD7C47d@&j=^Nzjm#fFC?e_yjgEAa+Nj|#~eEU6r+4GCf(EPk)4uDZM;74vms0ehEO zFex~A)+e_l-?7%~xMX`uIlEB<9O}NNeaJeriR9i%!kMd`At9#$<*sS0UjH|PN%o;# zoR$ZzQ4Kn4yXKr^2j7Si*KV^WQ601i~0mx!Xsiy-S{&F+Rh4K3r}dD65LtLj5ZUAmal z_oeHeSf8Z@Q$>g+q!ICE{Y+Z@0AO7(k`W*%rYC5X;QA&)*p)r1n)u_$vJD|JVSfs; ziGb_tF}tPHR&F?T9z{+3=M}f><*rbQ(O;~fcV}iPF7|Yw_qE+iZ}D{vIl1fTt%l2& z`B`~GvOwI;HyH`;hR#n2d43UNG_$o{R<{C7gRw*dE`T{cb5fl`Lk1~(w1Oy`4abV~ zo53pqDg0YCya4|;Ra*)RzuK8d)pjl`yUH3_=N+o5kV=g8w;^N-x9AqJHBanG;DOx* zjq4=KzxU}8mF6dSEUu2RS(5R=ru_dnIuCcY+qVzvP_3%=Y3-uRp0zjC)(Dc+u3fu9 zkl4GW(?}{2Dnzsqd$jgeJVq!=#E4xnVpr|;y!pNVf#dK!a^LrLeLm+IRoed_lQpC2 z4H^S7vhVyg?53pI?jeZ!UXW>)IK%0%VlxkDJ$u&fc)8!HqHb_>MXXgkYD9CODoi~} z2)$wVHDS%)SK28%kxPGS^lS-#A7;$BBDBl+1XMRq%(q{d{-)YLjAU8Sx(F8)9!Q(r zJPSlsS9Nz4*eFTs@0bWjLw3rpS|9tYYKAVNh7PsURz4GU>|zg3?%=Yl@U*_5GzW+I z?qLC9>nOY9!h~?;j_dU)#m0&$DV~gm;{t#EET7L=dPH9&I`z7s0aw(x%kz0UQ4n;~bU3S9Q>%0|dvK7fX7syTL5xgessGtOX3((nSqc}Z zk@Yp>3_P?G`ek=CfM|v64m@N9Z5v|)5mWkTeBao1#GN6o(J1%9?mw9jZNr7O3XqBk8;*Of`3Y}^QAM)u|~|8%jX`1Sfs+Z&6h)GMS1(;(8fyJSwH%8n&8 zYkjZz4zBq%bBv13NkJVl=tpTu!+hpy5aaD2M!67-*VzWqF~;t=@6 zoWO#3Rez$e;OQGEzzUaGV!r34YUyB2s<8x~F)aksd7*l1F8igI{QUwiMN&D6zi;Z- z+>ob{m4wP&gXx0JacGF-vDcNW_Sp@R)(W6?Oi&wwN+azeE5d=I_vL;UxmrcvVU(aG51 zApgWvhAr?Nq;^#%xs5yQ=19&i-q`)=*6RH>%UYmrB znsJeXHP)HxuW}T>WCFmxK@QG6o+xB?84AB}bv);n<;}Zg42Q?Jud`oi1@TrUtf;S+ z--KS7Gy^R$&shC>?%U}-{YT^j(~wtB&5Qi%3wqhrw&(P$42LcVnrTQuA-sv^5reDm zRL1=Vm<`12tw}?*FFT*B7J(3-sVN+>oFbT!GM^nYl);tWwRQV2b-k6lmel3t>f$9; zBXPjG3XPCPU;S~ryQ=hwpx(tzJhSG^!b6Q6C|JF6rB2hd?x132$3fCa zeoCw2$&?aLettU8K^fXfE}OODF=z%gWK#4LT!;e$`X`xsw5km=&Dm=(ka>Zx_ZJpu2e-_o32+G?)o;YY}S3^ zts-sn*GJ<{oO0!$l)ts1;LApp2%-Tq$~Sy=NK3kqVy6FnA>Mex6J;ZyxV!v)U*|TT zD;)$R2bl~7p7H)AqUEuSDlKe+FSB_Oe_MfG_ismra6PgK_uwPR`$#^;Wf)r>h%{&O zJ^7Dmkc^!lzN24dx96nW?PQrW;pg*Un9&}n@C{vme?ETdu90e9mnOrMOXQ_eo6nn( zP*qm1kzb^KJtG?-oM`RP`sV8rt;(aHveaa&N=o7cyGBG%8z#U10-B$&fTz*nO6Ut+ zJ!4N~YVH$BOGoI2V6?`3*W~(v*3Ho4n-CoaUr*2k*r%AYOClo7Eq+rC%7|I2b^6kJ zDyKLF_eAdT>XUtcmb6^Dm7%5|^3J6%zf?ntfYXVkg!14zeY~Y+>6i8erkz?xKrjDP zEpFlUJEVa2TP3U$=>sv6J`{L7! z<#{a6;FdMZ+aby&4!KdPzY((4zGYn%{eOzX_^J6VJ1pr@0EXIzL?rjAM^=o~C(7BP zCdtrIp z{GH&8fWCW(7i*x6oZjhRMbF}b`u2f(t$Pms;z2b7lwNBIl~7a0m58`pf1{Pu=Y%#< zf1=TqBx<(f;Si8es3)b=UKxjI~GJ%{T-ryD~Rb_l{8zXZPT!n|;~&yVF*xcjI+b27;L&13b9VPQ04%;#CUw6#l*1{EusfK+@hb9lEspjkW>!jvMCF<(z;psTvaRy5& zj4^4yFeTQ;@Ne|83BT1+>BGy1(Yc2D{?QAH{e}8W|xnk@W zS3f{qFZsuQs|~`~g64cHuADF6rffv@6pdCy_2v9-5%SWkyF0I#>l+Gdgb+!H5;b#% z$&=@Aqmxe=gT_u!oh*eew4=~bHIS^bLgFzluP-504W`BK{&7a${1;vPVWoj$3sd+dh~4gVV7 zdIugr8_Sqn_x(Nactx(I-1|`7{kbQ(=V4J*nH%MMGhRl#eFGw3?+h%Ee$ZC0f3|Ex zZ~fT6zM*~1C{<1a4fIJB+541ld~yBkTJdRV4?-@ZU2%pbH_F5=0qi~aaUS6|HHza1 z_wl<^5TPd^_`DaAk||ud7Z%e=;>Y5?lGr{|Mw-5UcdVUnPi0s5k4YE#^(ai6^zx6) zQB`pGy#Bu^z0-eU=HW)Wt+RcxcA1K!Vn;Vtz7v6KbK+{zqk0DYHt_I}wTTtD zNJ>@{UjH8iLAB6B9r2zcoBE62<fKrjaUrB0?<&sTmWjvbH*fdOnnC>y1Lh zJQmk0nTeC93$3;>qa~WhrnEsV3q9V|MxXgx+S4X?$lyb5QS+nlE;YsGiNT}GLB4l< zD55lBwy+7%N0*+H9{LhrZwE&veY9f3gZ?G{W{Mx~!ThcoqtBskYP< zr^O)Hs^|P>e-a^pLoqs}VgjkH^el1&iJ6>BHYRH!xRYpy<*uMQ^Ry7p4NrVMEO%pY zxr6IZ@yFFuoHM^on)`Zk5G@t5P}_r&QjNeX2Cb;S{2e~wiK}6mt{gqSUr=0|OIiNw z)`e7nG?m~DWDGsqxmtptceFznc&l}P_kL_0I?Z1oZIXdX6}>{}z^Ab1Eeqil@^;iR z0|3}kU$E`rV6B_iCq;M5*GGOs^&ho=1{DlxXMr$`SXEGi=TeOEz}W1jzvrr;-f-X+ z>OUsbS$d+C)$=~=Y2YbHXDxR$%AP^L&PmO8cJOvsZ}P5I}S>F zX9Z2r>vBpl*TF(~*A~*E4a3r*pPA!nyR}PeE2@(N*9(+kHOD+Lrv>%uN@)Hen35#k zem>O%o=C%U8w{*|;T82x!h1`YzujiEvg$Ko{+JST#ooU|s3Tmw#oE!SqUg@>{prSI z9R>XKMETrl0O#>mU!S|AA}ZD$+#kE5Qb z(C?AK+0}==O@PqB4XA;V0hGXtp6`8HBnW!PYJObXG`@e2r!}fERNDaHWGXSZ_b@+S z$ffEctza{*Ph3rOE2J+0SYWGJQ0qkEbFUHl{V?uX_1eFS1pw*s3I3GXb?85iAOd%! z034Y>k-(nd!$SnGUpJ4tZEdmEmt=IPRejxdrw?R7mE(1alg?l+50pSbZnzI}!U5Sr zAM7pSUS0dj2<70jS$*;?0eecuQ%i4B}L^%0Kb zOnAiqGW0x|b+_=kvfY-*Kk|d;&o@XIR?5C$?F}LMlgjRypfHiw&ygMqTS@a)e^eg3 zx?@_(Q|W$=D8MOXnEA>SuKRT)v;@-F?-q^liu8+}PV!g*2JZIqG)-nPgoiz5=e={^A9_ExY$+>mnxYe)@Bk zt53QkKk!n;#!z2bQXBzJ_ZrTXU^F(=uj;m3oSjl&*cvu=V#RE?4`=u0 z(Ab$bVZ$5|=5{{2k5&A7B)HHP4j5MO7I6DvRQH3Fucw1#)^HPhpN`t*$MrPe3PY|d zsDY@Z+NF-C6Rp<>(uFzssj8y^G#U*}D5Q@3xWBKf)Xt*Bdy?o@E;1^{4wX6G>J_^E zv2-+o>$5f1lG9yWrpN9jT{Xl!`f8QZ9J4?y*Xgk&*PEG6&Tnnii08f)SAWZ5Dn8*0DGa5T4p2wl0SW^4Mys$tr9V5=^hd4PG7_ z@XQAW8MR!RSDb1fWC2vv9aWXr6$C2RYSm{uX&7IFFnx&Xq1sNlX$JVrWQP{tWc-6C z9%fZX@UnGS={K^ZZSr)?`=T+5Wk0wqnnxN5NVlAIe*}8N-=ezGk6GUfxZ1-ye;jjv zTa;LmIE0Q^$Yd;;Bj1EOW46rEdoXY%8H*~HlxN5xRjW$DU-%$-OK}|(S6HP-ab$6F zg#*X!mH7itlkb63{#wkZK{7M6C5H#A`>OLsWgU0lI7Jan*11qdlwbNV;CLpavcbD9 z3sRHnYX&+IkoPbPn&r|9&M;&%xE??(y`{zTN9^E=?p{dOwu_nv)_&ZS9;FCTJ_Gss zC#*YOBh?e7D|)gg=GH!|q)8y5TICG+`~(=M==>X6rgx^%^wMS3??Dz&Ihk5 zo_@!DhEcWQH&58|l8bTVe7{Mc2xdj$ef+k(EV-ef8oyV*@0mjsY(M|q6E>>O34WGf zZ%tqvHDLQNPk!9fuV-B^y5_!1*p#Yi{5h_}V*GqSs!KAKM63*M#oUb`~0Fw9mbwvw76I=JI^d=?_Ta8XJ%;TD7pys4DwfZ zh}1S=+j1}CK6=9oXL+~8b%3X=exv{N^R^^#e2%^XEjmtK>zouk5Psg<{b$nSK$FyI z?Svl1JoeqKAn`jiYLqcdTheC8xL=bR4=m{Z{-$vN9Txt&=u`uqGe-Nv&>B!BT2=9| z)q%8eaGjU!F)S~KnsZFvKzB%L=)_N^KCrdf6iu{PEIv`vX@);CQ|TYm~!Le9V` zg7&8xXs8T0X3+n5RM50PATcK7llM}T_#An5H?YY{O(#zROq(eF1W>qns>7e1cRP^^ zKI9oLGpYYDn?^KJ-4%7BPpHvUHzR=h z;0N#uFn{}wvCv(rZADTDLV-TdyO+lZhLfD(MyHurY=|%;+cvmNug}eRQ+p^SC>$p| zY3_6>3lg|)>la~A;Qj{tMr+$QN=ROG@@(Gh`|u!p1OLeA1^yXZsNT!%W}I$*&uYi* zH;lCbHk#)sGH^wmYjoHSQzFCu?7e$kQA*f>R>10~~D>7)5%j(nxnsg7&iTMdHpdFY4?P zdE)0WDyQo3LN!~Tt7*wS_`EElirYxs&dJ9hM-&IImY@)IF({GV9%g^9)oOPu4`tNq-&9rXiPmL5S;-U z@OWBkRON~FZ@Bw=;l=uW?-#lmKD|JNK&40F>dNyL`<~0;?0G^ufp|~O*^_==B_)GV zx~Do_1;Ih4 z#@?in0BDFz*aa<+sA5wdB+G=?{q0`=RD;$5yh}MF7ZFw;P1>KvvMx6$%;kS>=V`4a z*(@48!*x(_Sq6e5XRH4)>07aV1McPZYHrAf$Bj3Z{f7LW7bc#lrIy~Ss`2$X?{Y2j7p0v8swlI?ev zsmIvo3!L7hI)ndzdIYZP=>Y&_ZCXG~@>>MhAJ+(Rtpvh4)5Gd6>q6AND$#vo3PX9;!EU&TN!wD zrK=o6a9=Rh6ISUTh{9&01UX;nj@R_=?748qes2-Q<{1%a?|MvSZ)}&++_|QShpA^^ z3z$erMV60$l4@`CEBbpjx8@C?dsK^;MNwQIfB*5z)p1b}_)$Q33yzPG8Jm!8yP{XR z!bm`A4&0IaR3wxtG8bYXzPRC3LV@ulL$@qia*@2Purno)d! z$U|*}YNsu{tTld;!739`t8M0`^{QN}jAAdm-c(>-%1gs0V*atG%O^JD&d%jl!q9%; zaZXC};&^*u=drMk;U-^>ALu5~F*UYpaXTjiyzMNhrRC<#JIvK27>HFHePmf=BHDaj zvhsDzCBE^J_uV%u&zN)eo)(|0RE@u)qw}v@UN`x>*N?G6Z;gqI-#j0Ux|C#g z^%2-nx8YK?obe}-$}3o?&JS&-+rpCikqLtqtCf9xzsj#$cN^=KSG%(YzvZ>Rah2IM zw@!!y!!5?-^`W@*6~B%2^`LmbD~%VFc$yEJ-R!XbC!edq*6yv(IQd2i8sU#Gi zFGLYG-eDqI7tOj=)ogD{stWu_+wvJ)i}N5p!;wFBj9P_ndBQIjGz7Yc+msZF9lg8SMO7V2N8 zp)YsjW%8g4!yxNF1Gk-IZddC4`{Dw7HeS>_IKc*TUj(~LR~d(kYWU-XN6WHtr>xz7B|bC zb)QvkDrzg_C~ER^sMFNhhoU;V2>6yid_EAKTY0Ea+~WRFwe#!QdgeB4uLPH+X1rm~ z3lM3DRn;|q-#RGF5!937WgI8aQ+L1Wwdw=`Z~3uuW;^&TF)vrZq(qG9n@B;XX6H>d z2|S#KL2%B+aWxc?=VQOn1E-$}DYLeB&hT8s(VIc?_ z8sFh`9MCL3-P~r$<8#238AuvYZ9P+?^!3*t$OPc-BEchx9;Jwc{5YI#ZX~@8T578> z_M7}oyU|fIGsbz>mD|hZ35ndQ8=93oY`=#L8!a= z+Y)VMIKy^*Rb+;l%&rLm<~CE#`hrwp#jup1io~Pt22<<5lZ-)_M4n!nc`2+>eyz*k z%kD#z>7r#tUtAuk!HTh$y;UGfBXh~g5a|7=>LhS+s-C^V&5o&Lk|LfI#f5K4E{F|? z`lx)i&imZHu(4h%GW_}y!MKqjr4Yi2T#4)diBDM>Za#U<430Kz$**1Z+2 zyltiUIMqL@kPaO*y8i$pkoF8CT111q3r+EcjLl<@?M*xeZj&oxZvl#O@TgoZH8iRv z?&o%+ebL9YqvTW_PqgC1kX3xF7D&DR?CMElz>opr`GUwXyK!Eh;3B!@a{kU@FEyj# ziO#U|Sjtz%BIicCnpoF^?U1gcNIP(vY~;C4EeklL<3FY&(7}I9&r+L-=!aJ9j{@sd3oFoEZi3!_a(obuX03=-wNx9#%woyGUNkI|8Z zo~V#(1bUaCnnZI{=3nQor0rMR?fGdFQ`~L!j8AyD8K4{da+=>1-`;FU9^fwzNHL)u zk%9TEt!o41Q?7!k`UqN>Uit+%sg)rY1SvMD^eZKC1zg2lf^?Ba|m5RD~7O~ zzERIiFt>|Fy1`s=n;xSks6h1P?c$*YSr)01zI{L1UKhLe9+psc@Oiy5?Z+L-OD3P1 zs+t7TFaV)LErkvQkqbk3e{g0pUIO~IGiC8zdqxncZxZh{x`pKUF4n*4hxTSmo4Q0* z>{EreF`ksw;lio(735X=Hb5Pyx}MOX`Xu<1fcx-#Rw|D1CP|X?{K=To?F{)+XD6o( zMR`S<4eO7e6M0)Zh|#=@q*xU(<_pn!I?GNWqDKD^@;mXuV{7-g+R*B&1)%{vRjM zb68`H8ArKr-rC>xe0Xh-lHuRdf?9}0tqR#a?pQv0rD)Gs$@BSAs;^oG^4#u2k_sc2 z*nxj?B4q;y)Ok5o32Ntkd2TJ$w@`K}80zdTIviVhBCs?}imKADml{m^O)5K;S{?q~ zB|JG)b=H;XJ<*|A8W=<(H~vZjNJ~^CLcmqatKy!>JC!bKU97DImE;{YIZ4Z)*(I#a z3o>lMYrtN?84Iq9W_`Y}Q?4&CM%x_h(bBg)-o$F*tt7&|@R#h(a10m*TuvzquL#*> z)8NB3Y363D>YH0_3en=?1{J+Yw7s3bXt z5U~TjPc4xlmuiO1@5`JEVz*@Ea`x1nS2%;K8azIi7>F45>F2InB{ha-%xD|kw5~Cn zHX?#igszOM2?6cWVFsMo=l?Msew;bYsy?%id(Uq5!M({+?$UGIt`^E+(8%?K$k{!kN+LA7=Xb`&mlg|>n!Nrp=! z-YcpXVY51b(kxr{QI`ZplV-oY5}Ix(3-sL*=K1;{U(byi}T0P6`@>^juU z&W`%(mpQU;-cC8r-WWaRZ<6f%P1_i0`#`cZe#dL}R<%J+wayHL2#7PzxSKP&0bKjN zU3H+|Ss)USr8Kw9*7GbgkdkR16hcsVlQx(gL<_lpR&%(L)TFrM(nY|B?ZTj$AjGT*wJ=e?eo5`)8kZe?J} zMWWMo>e@mXYawV+kvw@SuB1cipN*)B2v{`lK)$y`El(C(;DP`@jFzglSJivJ`MoYh zs5x_F1yKhRS8)VCXlo#wmEd)SzULe|8e)2LF!p!4-MS>bH2Ev^a?ACq?6Zo^!(LFQ zIMd$Ue4cxpiuCgDL$2?@Q#eYLDiYzEVH)@I&$Z5DVKJhGyqTkJl%t1Vq|!uooB(BY z7<-1dNe`rsIuXo-$1g~z1>M>leKicr#0XU{FCZB7H<^W?XwVonOtFU4o4?xUDI%^9 z3n9RsL9K*-MB+G$NR*iI)1oo551(oP0ePA?hOsa*B5 z^0kN*PVYp184N1CCN8ZqKxj|nc7ZVvNYzQmVdvGGzQ*1icfj0syBxQSs1rJ(zZPJD zdyRLT2z^d<(btR^Z;=^_)$h@5mz#dV0bF5!NHlza#(uEk@8aQr@6(}GTrH?jhmcaO z+w}5~0&#!&9`xF5eusK((~+DEy%0KP1O%!cL})#%bFEWsjD9rtE+DC+DO8J<^_dx5 zlk9-@-SK0yU%`Kps-+&!&9?QW4<#?BU~2C^FU|P#gZBt|+eB0q=;S-ykNS68nx1`R zbVYK^T3lE#$ZOy>w3@azzS-Nh7h0~jUUpHyzpu+1y*%6?qm9fM%01geuP{=J(ge=h z?EwW}yg0wG%qB;8cznv_Nky-?KCEph2^kt$qjT&F_bI$_ zSxUAGiqJZA<tB=MGB!4k~ z)dQfaAg}D2uKIoQi~f`?%2=R3yL!M3RP0$seS1g%B{vD#N}p3cAc*tLzUsZ0Pt6Xz zWLqzIwes_`)}klPmkO9v3me89_#zY@MW_xwdq%kbbQwK$>lgP$fQ{Cqe@NT8h`Eyf>8&;4`_Z+c5uh zTz?{)^HB85P|J&wS2EAvRJ9gXGBqY`&T{|QtG?JB@%)^ywX3dAnA2TWJFeERRu`UH zF{wYry_I_;RQc%hHL|GQavjsey=&hjl(GvImp}BHRKI_<^hvC;kKtQ*z(dG^g@|m% zBBm7i(K`qP_u{7)zWf5BX4K1aXaAPAc>$(gx-@aDpA(^5$7K$o$Ep9+Mviwr$A&~) zc$PG5mDW0hYS@Ck$rQ;@rV)SeXBJQd<^i4BluRPWp$co|+O+|`GqB=@!Up)|ZV$0b z^vXQE5~qJA26=B5dgXg>f-G%48i+_}iR`_%b>N`>xMJh) zN<~|y9YD8@@KIs31Ve+xOrD1UXK3^311$5>ERG_1$A7uW$L4_a~?MK#kDB ze&y-C{QQ;zw>*BcC5J?V%s_SH^rAp`YPu;&F!4qt@lf@TZ=IHa;&9-}>yIx0g02k{Bw8+ir<|}& zRTnRKSB8lW8^bEH)M%NUbZyW!eR!%A9;j|d*s5T}g@&lA3#ibc zH`o)w$S}BQP1!}t_Yt6e@2dI%XA*E*mRYGO{PO&+299zDjG>W?iWnDnrQ7N+0MMrF zGRb!F!upp#omK0*4`=t?bIA4;Bnq&ry1JuCZ8Y*$XiH%8VCvJUQ0<6})PtDwnJF@k zeFhqLjLQ{?EESEyND&Q;?ZO>l_e=+oF078-(Q-?%RP0f6XPKTx=Bc`7cz>phFTLYYBLj{eg&iTP zhJzXw_*+PuIlKQceP3Cr7km5_{WwYx@(jxP+4v36-~^dPNF|qsw9D-fr#CZb>_zGUd0c0pzpzZs zoB{dxcp~XtMaR}Mdj;KD7#6+*SNhiU#j)e(wbi^!eW zi@LNV@ON!EK$gwLo7NWgon|Q!4K^?xp4J}s9T~9La_yOjY%xF=(9Il5SAVqI-~B@h z?rG*-wv_S`U^LDKd+E8DG6YDjBaLJIOF~KSg@6c1nSn`|2%ZeB418~T6=Z~G+t&>J zOB5R#jdgXzs}%HL0Y%^68((=+udWfKb=zWPGp?JMk(cq_8-nI0+o*>C@>=_!AHAu8 zhBC03`r`W~@4=L|V|duMH}7bcT9&rubajx~8!DXW332V*=e&eR-(1ctqgO?LZg)if z`O_E<$@`CKIP#=6!gKG9E1vD_px*vS!=>+vZkw@Bt+#-7$nC^xqp9ly?pkbZ7Rncg`1t6a38Q=e?ww=;sDR(#5k?9|F2O2&Krl|92$ytnSGD6^P8t zi}ey1oYgLZwq#xR15KL*i9&123?~E1vMRuQQ6!!2_5w&pPfGN4kjd3%07b;*xj)I$ zZ`>v*p$iiN!D*+!Wu}E~Ju~irHDdx}AjNg|e5lBC7_^Lv&{Phc`_L<3o%oXbS-H~Q zOof`df!e7dK%__UyYA`8t=O#r&2Nis6jFn_lp4RrarH?!6JDSAZ}osiYqGbkxOX%)5NQl*sLwFyWZK!4|!g*L~%qbXardaaLbEZ@E*?K>ucu9R%o%FuQ94m|augX0z6S4emIV%2DEle;Y@D_fR5Z0%6X zda>Kw`{%vv!o-s| zE~ZVn0qf`?4Na)D;$VC<(_c-5j<LE|@P(l-C96>Wwm`VW_uVh4^_a(jWfE z^aTdu%KF8JPdO=%3vNA(|+s?&Gz$q4VUI1MnmR!Q#7I%eiTWvO1o`T6z39vLgWPn+`iCY z)^3sNt{m0D9P5iJQh!{S-ex2BcdDwZd;TX8Rv-CQ8oQRgLBYA?R%c|td-{b+!97T; z=CqSUnl_JePObgf#b`nSwpurT;>r|VLQ_pbf|^nvo`4;YaFo-wXFn=%?bKl9PM%vl z)`ISc>|cEEn%ASQ7xssiA_@zP@B21KG@bQ{kJ$Oy&Ot|jMnaUz#tw?%n^bdStyzP13^>HnMFKQO9vMe^b zN|q;YTrb*Lok-){b#f7Mz6yZ|T#-o5@&maQ54$g7LbKXHgT3>&ZL{&yt`lL{16ZJQet> z^09UloibgVB!@`hOFLHWhk#H@;=)#5S{nE%uMee7ism5jlVJ~wKlP}x`ph0*)F`^Ij75DckPA{`6Yq94nA`E1;woC|3fF|t z=$DA>OPI*mN|{017px^;XCWe>!wcF#k<{4U?h95M-9>KaDRGX>KKQ%*7p6P%x>d3T zf3t!mH&0!UzzJu z$*Z^8KrQC!SD$2nW{)u6BoFS7*?m(SdW1=WTQD1@9ZCL~Ext1NK+*$s?*5=k`1zuz zjcKvA=Wob<62ROFuKd6nuQ4dX_=BXe02U_x``?9rU6wB=W($*E+R9A57JXWh`ubeq z#r&a57oP%rK771K42n>1=Z<;B2I;kka7l5dqM(Enx!Q{QBu@TWG*p(wk_%J8no&b4 z0MweDk276z*IHXTHqDuMQl;_Bbw9w(5<^Nf2Hgyzbe zpME7nd{*CEH1lF4EnKW2V|*k`p0OcqO1K&HF+_yeBFZ1X__gs|VMVmm z7;xR+xr;ZebC$15c8STx9P=jSdHSH9ubcL##y^~yX}KpRtR*0jNv|92wjlTxk9x;f zF59jJ1qWnVO@(F4FQ_S|Bs>T*Q@9$&Nl+u&n{xJ;1Vd;R22+Eg^a8JT5LgwQN~wq8S!OUZx~YQeiM9}p@Z&XX<{c~pkZyL-<6WRWd*`@eVb&XV9(u$^Q}U?oGkp!JQhyg6 zqb{yjPDH+5N0|6Wji6K)R_=uR8whiY;NJwj2bHV%qNG0h${%$V*=H`{MGFmVoK2&u z7|Luhk|HJHisN%yM&Qh=luWF>P`D#c-d=W=w1b(QP*SZvt7lAZmJV%%A2Qr%iJN%i z-$1bK)cn)2pEKm?6N-+YKm}mNw@jfEL=Si~6&4rdQCA!!m+qXBMKoMR+z8I8Wsv_b zFU0Hxq(Y2B1o)h13feE0cpGTmG~yI6AB(a9D4y`XB?>iS(rN^QI2}P{MV+b3hg7YZt;gEmc*RvfK>CkjIXB5&|a7&pmAC}M%iP2-&sp_5;J_a~zO zbxiIgC+{3Qi_~YlYf7kn=7RR(__#+GPjGwIFvB+J|AHs4&E1TkT2I$ z*?Y4!-@T#vO)E;)JgFwBvZ`o1h=k@;HIhIgGYCJ6$|oFS)jS!#hdZVrzuOvu92DIq z=<_uUp=%l0_gO~5;#bvxe611zhS>JtML?8Yz(#c%X@=v=up-81PCJWN$E^~U%0HzO zIA2PM`J@%)1j=LeCe8lG#N1)LQD%@UU`t}|8?0S41Ib@lay=T$BInKSJSpkM#sLVF zoyrCWf+_uB0~DHaRaUkmbTT;8{v`S zk_P{~_QzPzvt#v#;GwPo+r_sr~h-U?mjLe6-4`FHh%Ma>Twidg%3Zg+a@@*Onq#sKk>0RgqLQQ5S zIZ7Gwq+YzbiPJH$PynPNxPG9iGw&8pzG9&0nX}FawD+Q|o$fk!-M6D~$kUvBM#>D_ zF=6^&P`q|=hS`}97v3t>(Eb`<_1R;O||n;>$!XpT3m6cH19j zc@GIjbIgzZ$8?WEdlqZ9lxi7}GBp1RX3p5GD41pBktizM(M-c~L=9ABNH1B!vhTU@ zuG|z^t#L!L+RvNxbfU9hJ9IllrO*IeSPtA*;N{ve{oMY*u^-YSoz=YeogS(RnQF>T z1UbH35cQs2M_rru75lp{Ok&|}tsz{~F^HV;KDKEyuQBPf;*rqDU26Q~wfgGz)9Q=i z_1ZO}M&#JhlrWO|{gy}dA9p3xa*9%rPEW85L)$QRaI0RXZs#wfbmSDbJU_roLm2vF zgFT8?b{(_ ztaqoHa-=Y5Tt{(Wv3@OuqX3U2)EEA(5FsBYFBn&iruYe3Gxzrob^dh?%->soU5Hp#Z0Vs1t-AGX(WhgcKYRcxMpyo!e z@x8mI9s{SXvf+}tNbrfhfS-Xw#0jebFj&mJw%KanR#N{dACdul6av+a7RfNrlb%M0y#4|yQRGu5y1@#yRKqiqNnBY-;rqbXM+mgC=9a_Ilz+(oQFI>uY_IPd z@7AhuPD|D9*WPMGjGzk*lGNU`wMUHDyQSmQh)RqKLItrx?M>A&LeV5fty;0gh&|6Q zzyBb4J)hV8JokNH*ZWF?6{6zlQ4VZYPY;^3ux#bVV9j<%tCqG=P;0fmt-wFdU^OWm zHE7UuC>kQNb8xcAn>yH2cgG4Y7SOLwqwV09`=x(e^i-2R(DRp%r2!;oB9pg@LcgRp zh+p*6SfEwsS{YotSh4EhB~kpw;@J(0TfV8J&h^G8X=s(aQ2=lt*;aAU*B$VDsj660 zxZI`><*_ydvNmxSGRKX_fK6Nrt#}-Rq`zf|_QD6{9&5b}ydM`LjaZu|qY)CJV{-!ot2CzHoKuFi=y`7;uWVxK#TL$7kn zNt18drdIXyQdmNHay%K?cyvZ9a65fVe4D2aKWBPTz zSAn+8q(;Y){*I3sdO#07S3rCBo7w8egT?uQaWO%4Ng6#yxDW){&tuh%{Rt-VY&&#O z-dU!k<^X{*wg{7O6}cFf{{Q8B-_l_H^(f3NyY>i!c%|)}q(4O%&v4vOEr2w?va5pF zxB`uX5}YsuuiR|4zwKnbDW64u%AD zCtY#El;`y0@iyaDyL+mzLuq>$IpABFFB)888j(Xj3F*UF`?yY=7fa<;+EUwhEjls) z`RAGGntz~yQyXkm4UVTa72>I-8ZO?dGuw`pUBTKq|InS`Dh(_M_|}`Otf}+?<~Xo$ ze!?h~65#Q0ejNE{snlg;XJvvLFuOQOY3EDc`8no@(Lf#dxN1BD|b|$K`@|wNtkfhWS0=LJXoNe98 z4-I(`0&j}71CuPi^?HC|u!PDNsd5JJ!px10)wp!L4ChF|#f zYd(BWt33jp1L2fKX$>e@&2nX3BB+J}iQq$Ybrm+a%9qT~_|Vi1q_XY?6!oSE=)>Ok z%NI3;L#$Y|>xtL4`_cll2h66oP=m=LpLRXrSB*ZrtTeGu_MYZ!fB-SOWM zis{fjHwjmiX2>GCYEY?bMg#p|*U1~LyS=X%R|uc8wZr+XjvBg0kjnH|DT`pdX#b>o zpxQ!eLAw)Y*o^^uhFimQp*tX3`eQ}7VGZ0Z=r2s(lYRdf#Fv!0{h<@XQx zGE6r8+{xS);G6S;Udy?$h7%d9A{IyUY{^B#!wwm0Q1)V{IChwK^GOtpGmn^?EKHG^5 z#IVjGZgNPO6YPhVwq?{mXMGfh5Yi+2a?RJtuL5iC7_U$Xp z4!!(yqs0L4&x`9q&7PaM!6tVg*n$|zG2`<&$zrre$~*Z}KR=#Zs^--m#b!Ifpb$M> z6Vpof!x+am(Gde_Y;1SN=dDB&gSuhc#_<^A`6xEq~~06 z6)>MOK_1DJa4R%8CFa@bN_GU1GE~M8zZG`r0o4gJaAJreeTbKqWv%s2NmqXJ*J2OL z{siIeT+eXOIZ3l}eP^*d_c#K7mx#_!1DcXm`>!n=%APz5*Gh1jcAMWOo)fn`1Au1_m&fCp7@C^f#&e-g#* z1cm3H3IC*14Pfpb#K2Iow*8t%OXq6y|6QWiEvz1a*6&%?p`)s|o>CUWo-Bir&zDWlJtv1HEVuEG|j9#v|mcBAsoNO$pI zk6z6TKZ7E8qgm_yPIerS%V)XfUew5~u26LbyBJjp>t(6Zzooh{tk^LYwbe1;Y(V{E zsDNouM_C--R1Ek{H?54UpIy{K3rz$S4UMF6G2BRpkzVr5426AFQnqx=LL&de{h{i5 zQzX47Ny@%O+cii085tdy|9ze1h#q~z^}Rm4%YSxz_}e?}7a@NRS}(8X=$`T-&WsjA z6dfap>IXqdYs|1Z(;NvJTQx`S{Jv0e~?HETea->PZmU zcI>mJ5-P-0q}19qzLw%BQAerYHV~=Y>d<9q;zmN$H0yCS1$;RHfubvEOen5^^+zGq zPlcwH(mm6u3>llSZ@M@q7k>sWrCgXFXKaC!4I=#IE~T6xDaY2Dm`~zN8_}@9{SBPl z_=$ohU&$wJt~50)M54|K=3D}>Jwn~gThucwABq~=NY0W&w z_EwKlHJyNr6vDjMl{!w*q-N)>qXR}ZY^v(@>LOJaaoFG9%rGNAqYoQvs6w|YOt?g zzl1&+U#)MUCRQ&0=AFX1zMJ&V_8liE!D=+rHnm&buv+)8-ia`u*ct)lk|ODcIRTt> z&)T6_yW~a9`wm{4vY+qv?2V4*JHP5JF$zLv>so+3KsBy6JfL@VWqdM`Y?Rs!a&^lB z7i^r^LmKRXJcKJ%3^(avxt%U1&IJBZ9sx`Ljm1@+`qgFUuKdbP2{lz9{XhST@B%Bt)tva%M!`tuJyXQHw{9NKEWZ)h)2dmZEa!Dt}T5>d06K zI#Ro1&beQC@pUT2yp`vpLq;8XghV%CG#XzN534;

;{?Sq%y31-kw`u?lmU42&`&b5DR;1%KHwwJ z5<%MY?Ep(_1>G(h)Q(-z>900w{oA&w3~m$K&T<_2B+IU(9lhexODS1KcVQ;St`T%7 zU5hlt&OoT9JHBEs)E8E%HR9@a2vO73^)yP!iT07i^-sd$T&uVQjEY|y#}Ie*mbG`f6nIQqt$eX`sv0?=kml4C}yd$6sFyh%Z8~_|Z8Tz9a(E zgKPQmsQi}8KqpervGkQOkqK$r>$%cZ)-zQFIvsduSdmq{4ea% z(}}AdZ-nvH<}G(_(0)BFd~1~MR3>c>c**)unZsgM?W%i;;z-qR+9eUKUq+?yjRz&t zx2Nxb)tSw#L}EUCw~zJ8e8B&{VkPRz8%)z3);EVX0#8z}yp)IOIDPnaceD>cv=O;c zOiHY*d%!$kOjT)6eHom zC;uK}8=>kyOKW>B?r?tGb@TJA#Y8#&aXqYo;e)028`FiJW_i+uEHx$J_PlI)axV~{ zzI5-;rC9+E$}jC|lq9DSc++Xes``-?&U{W&Hg&$>x}tTI`Io(KFtH<`mheUtD(eCD zd_>S(7Enzjzk1QVy0)>u`bf#P3JlyElP4BAzM4R9X>`f6+kfA6_KvJ97l{dmwBK+R z-y6DXEUli6ng`%T4nf zsdZ@AAL+SX-(V@ItodThrO(=aDZre<2=nFzAnn_e`!?8A<=2A#yQC79LN=Yb@$j+f zVNE~?WkM*aEK5$2ux%jZVh#|bj(OPRQp&{sR6GV;Bs5Vj2Bh5L7G|0bWNXk*L7s3N z|KR@^)WVgKv{@VI{E{H+fP{jgdB--d@p z=&OX{dfZSBlWRaaN6{*%PbG7j&lzL(wOPBuo9zPvV{w!qcHQoBz_Fr9Wd zwh8(W&=O59ZD+zYw_2;7bE`FKU5`z0I=ypoRklcK5nCVM|8VE=ChRq$LW`l<#lr;> zRHsbNsz*NZB;1|LI-OsZj)Fj~Dg?abye9#aJ!;OtubS2LyLJcz-id;_tgLh-OPg^N zv=kra7nCDvWoZmE{mFQu-OKc30wb4YG|6m{l^ z{*e~AkuX+W(tjU1KSo@@dWQY!IrBnWgWr8S@X-Hal2k+s)KhaoXBOZndkrYc|b)Q5C3y zn|oWd-`ehlbNTNQ@m(F}0w!Q<9EQswrD^f%2pTghQOdQeL;uH_C}kdTk%mAP1QJ`u zohFWE*^rv$vZdzoDq0|1AKi)Y1zt1Gx#zbJe$tGpaLtheq&ClCwf06rad;Tk?$bkp zK>^KlJPDV4(eKWWHQ0XfGp{&Y*ibZ0$#1&i0@q@DD%*D5>D>~>M!jeR*f|>;yNNOD zy9qYfKuu?3W$SsYCtT4*ogz~>>D^~~QPo${xYBMPbZ;Er?cFS6=({pd_ooCl8&!X( z(y881w30XQ-_e(g;KFq@g{@*0BHnOpk4*lKJ17U;&jI^Jwl^%R2ejx_2zR1+(zKwK zctEmV@B(QL$yXT`7}kDsYjRkp8-kxGA43;5TAu6}VU;JL256+4^yX-)a6eler!B0H zrO^iOY-qfCf!j=!3{|XD1*(rz1Ol&5%L#aYL;9Tow0>yQwQrLKKr zAN#JohUIHyi20{dr$~aMuuuzQe`Wo)K{s{P`)*mueSGY?d7pcm#w-rKA?%%KROPAv6t; zTA0PHMx8NbmPqlv{hR8&uQ&L*ZY-EKvmWiv%~gY-Xk@w*aUu4 z>&X3kKUy?1wp(R!ZCd3<;93}wVZ0IMD_1{Dc{ta*aBn?mrpCx|h;PfImbx35t&EVS zQTLtBHT6QAnj^KDvdey3Lu~bvV$*TNByBIoNUu5_io&>`xUML^Muxymah+NYYgURf z4lt?Br1;y?%OJu5Q8QF4-5^yNoj!X6ZU|NHR5C~=Xswr?9=*!EV;#R8+63+aNw)@v zsUI5fO;W(AmRl);UTGgqTHW>+hCVW!g)a6IlYZ_Im**^3$<`W_cd~FHA`@phWT7{& zxct$s+7s`IIz&HG(ONT!P0d3t6O>VTt})#k+k=`TVyX#EL5+%e&t8>;Y2J4#F8uFO zEIP3!FYp6k%d>-{FNcY{5ILB4qy&hN{o58I<^n1IOKgK(Pk-3I8#Yl*Dr>`8J3ejnF1BQV5nb`k`NK7P0=(ww}q6829^ zv@SG1=fGLupTKCNq5??OnjiR_G>yX=LY21S2;(hQn`mb}bW)v1yLjQ>5NlL!KPM{8 zmam@G%8b^%U{`yF^(ryyJ+;RG-b0&{ouM8JeNyS~` zfH_D0>UL~#+>29)L<8-AnGKp=3@P3sW$~REv52TGo@d;n8k|p9wLo{Ec{7{x+s7wa zd%)Y^+kvzn3;YJ-%WI38MWK3Mb)2n9sZ6{&h>D@bSfoCN9??FmaHyd_p{;jIfUbE<4@TUHGAv3l3ME;`A!fG`$Q;xR_0%H>buK3^_}ehWa}lQLv?QEO)?qB6@u z!mY!=R@;s$$fIRZ4Vb;rZNQ0p;9u(s2VDd>u6KBL6C^8O$ZfAuAPBnuH+Z$~}ngf}5PScq_D0-R=uzV}`#>{%E?tu_l8Yo!59!Tx3_lS|6a7=NiuCNH+ z^S5Cni=w-fgZo}n#OmV<<%qIS+f{)-Iecqdv~8oY;uJ(aM&F)2W0+VZlD2W8=1W9` zD0XyhIh6{8belyD9wA!vbF>D(Ol&0mC~SiSJr%~!c6qBW3?Y5tr4dHdtYyBewh6__ zE>wD}(MZ>U%80r!EPJeD=)X%s7fG3gTXh-xCs~LSxS+UUIYcQRP%L8xSD4yR%;7?YBjoxn&CnU>~18P*~7_z!4l*dBpO-kZs(L!>~XoDtm z@R2J^`l0>C3&KbEH7WIj0R3LYI(K1^Ql5KWf=UBN^D+5ItyS|>eF3t;qTI-_M&>RR zsrj2&=q-WJt6@ytuOhE0e>O5pGL#^|X zFTyn%f_+^qQ|{kJzhH#M%8fEAMII(@mTz;vSND}@;Ck{va@26UxF`?&EM=aWDPc;n zR)s=GKj~+x@Bh^{vBz{9l2BPRjc~2pPA*BYm?}!V-$kbTVxePTuQj@HIB3vrp#3wz zytTesj#9BQNCt8@8>DeQCW@Sm7ZYdvYHdtkgTdsp%UQ+x7z(a)xTiXb{n<`~3WYj{ zf`UcsERz6-zLD&d6AMtB{ohNS6i_o1<2tkITV^@GP->%$-02y=e$IFD>EK5z_T%YY z@8reofrCF8Mq}4-dy#bsFsXMF_}a>X#Gs~p-&AN&`}N02u=K$Y(9FL9+wx_~Z#dLz zd&%T|U0vd{vLA9%Ey8)JrH{EKMQuk1ig5W((k@>=IrR4ECO!!GoKJlp_9|SD8*NZ9-KeaP zh1pJ@RyRMp8U0j7al(m%Vgsh6&?dUV8lonEj%58A!m#r3Vb$pT=!!aT-Qt~cv zqpg`Cwz(rd9l_e-c#Z|W$boJD)rLh19?8m4)h&zj@XMwsfPnC|pIw!DMR5zxw}SuF zAf=e)HxGiW9^`a1a(h(xIv1K0dd2UY(bs;H;W^4+;=RE+j{IlW!+TKewnWkqdwo%NWmDA zRVC_Pzerv|CkS@3QIDMTK&k~&ty``Z?-%evE!kmV^%1=N_J8RLf4CEaqPY0=Y@Ox_ znmvWDXcLnLZotEw<7CA-bsviIWmhHI{Bm3unuyQ;So{+=2@pB{ny_AB^vM`?>Xf6^ zeY??W`D9anCNJ=;d~CpM_FwDuQSl#rI!s(ujQShKe^ z4A_PI?J$^=hw{m%uAoV^bMx%+;#VHG`heeviDIeO9oHELb7Nt6tpeR9kjXNe1J~B} zgYB)zcGO%vnysp@5N|GAiz?*2rTH(t6F(Ur4!i(Wnr50FT{RxsqiY0Ywq_79iW4Z+ zKY^VtG@Uhm4*lA&HfOCxylOI&r zn`X(eYBDTe;`@^L_Q$sd<-_AO<>i3vDJ|C#A;$B{1FwD*Xp}r=%aexDS9_c6It(A z?HP8&YK<_UKS`KxU}>EskxGaPHp7HJEQ>XgR6QhEbSNE2JMmgV}X%U09@<>5FpOe~ldV6zhlpa+3h=rYGbsPISm`KJWQ zyXJboQ>J)LvmC0s&^JJ6c*z+XsMP|b$jPPQRM;tF~k@ivwIMioyR!)JC{c>6T(r~Sh zBRy%iy4}|um}13Yx9TVba$4-{zXlTfG{!z_O*!L_FNQ!T5&qubgEqJAR|A=Bbrf~E zg62)3rZ%j6{I?W;35hOEKiyZu=W3_bBWwgYM~meK98wFfB5oxcp_*-BaU(sDH)U?5 zyvMFTwiBK2TW1<+pI!}|I8ctJN z-9i)N_gRtzy)tA5{TUg{Bb!#?bCAfQsoM%GP;a;HhjVls^f0 z0HED>r}O`7ZtRBRa-o>sOY6nQk|<6g%Ph&;L-NsnwaJlMV;k(l0d`JdslTWD;>B*g z{y8~M6O{h%Qs?oVwfr1uw{tz4>ma2RyCR%KXD=S97~vbL^x6a(GOguC@1R;qnMBt2 z#IET6oh57V$Zyr@IQ;pZb6l3JKW>N{WF=Wqi~VQ7fNnI#X6UYJ^8AN8q~e|+xV=fL z!zADb7AH>gP1P2@e>moU{brki>>QqJ&PoT(PKMe*U0)wovH!a7pNu-rVu z#{i~9mhGCX((g}-jb~8Oq*&pE<5TaVy*^2bA-$-;R1r-P=4|=Rky=QQdVX|nF3N`) zt&4O{9uOHNEv)T`2UiFQQ(?TA;F^d*Tw!rv!*=Aw?=g?r-9Ub+QW#qQY_C#PJ2qtu zx9aj^L6aZ=YH;?cwRB%D6s?&~Z09`DZ1Iq4N1aOQ49)noK;Nv5!=|+yvgtzn1wgId(Y7ic&`)^Js(-~nsP|By!~7I)$vs@`jN_|_s7?( zd`z#!JAI~qFZ$;Zuk97cHe~Ed&9(O#m4>&U+epDs6UUdVsg&`Y-or}@j}F{*elf|& zcAZuSc&HNaQBMGTQZ_+tY@`@7cO&xsRhwCHW-$Kjnx{cb-!k|ba4;v9Fi{+Gb+8Hm z*5pTdEy@_Q)98c08bZG*{m(-LQm(rGLyu3gAVQJMsc?#o=T6SO+}$ya4!nL4J1c3AvnI%x>U)=u&ztbA!f#q@pF) zMQ+m~AAG0UFA=)#`SjlGX=HY)ShjpGpsUSosbp}7w-v6Wt+V@`&&C4|@Ou@D-^2uZ z44g9z6*Ci&0-1MWCZ^vGF+nTq#cGZ;<@8I6ss`LeoNLFuYpeY*l=8l61%0`*jKsRq z4{G@f`lUL0ER>VPf{k@PyKn8vjsiewIDdg^ndVq+1@&8KwX;!ARES`*GY2%RnLC%roB-kFGfH@>v825!=rR`N@SWQ;d3_9vu|B01I1f6ni>^1gEy zaTljfn0>!*p7E(}d{WqOS3f%Hvw4bX02*HH!>`?uR66qmSL1}$q`f6NLc=w)wL~)a zLh}guVf;UGJ}`v^K1Ulx3$s+7I*Qg)hskggiqJ!HL~BNx@(3X%ZzE-t#APeWb=TLs z>wJpuVx`_w$J1_Vq}1H%WdV}0>#s5z+lCl}j~HZ&~Q%8`Ho95gD9qYCD=+Eh3)s6!izq!SIRc-c;m5;_)jr3Wyv5$f#($@nv6Os~|e|uMTxHUPF6s`6Ch}OktnDy6IG} zXiX@GAz>9}LyJ?4A<8axylHzgpD_#jQF*S0A=5J-X9eW`9ob+&%QUWj2su|f)KU)Y z$mlUJ_qKZNw+a3&@TuF1vyF$k+t1sDy4|Vw3+d(8!9(4Ff$YJ?O}7)v;G{`0(cjHT z>Ed0}K`Zqv)Tz&7KgybneIUc0dDDOyZkHpx2F8~@O)5*{J}1_$6uY#JzpNbN2s%+n z5eAr!4rQblM?S5%&ey2$w9p z-Ddlxaw}yI1)>|F5D@M(?n251o8M|ycqtUtYk|YB;ci24#sl2b>PH|e-@b>Z!Sb@0ZXS1smR+u|AmeZh?CrceI#{0B{y!>EhxmVVz6mkI>*Nd#Z0mJU>~COZoWDuQhnkMK(qDX#l$7@T&mmVwh&n>Y5XMx`669QNQW zl^o%Pt7o5G)J_FGVRZMvN)w}UJIkd`ZgB}L_*E9gNYq1*Jdj9@NeO*UFRO|*3i8SQ zIgjNVEd4N~YimhYZGsfI4c5jNu5nz9%lf(6e!+<_jz7Uh%LTfL4d!C_K3}i%mB?9# z31qai@_zy-*PGtH-47I9c;~ntq@G4v>5=0LaVdDci)@D~esr48sV1Vo+HI}24Ts}Y zLrN6H3L;wy1CW57<&R=~$idH9bFhF&?jK49?eAVkg6>2j$6~5&xs1OCb5-My?=Ksm z(~mZ7WwWXR#{Gce_ezOI8^jo;Zp<)J{;LB7tUrV6xqv=brja#Yoa@dV%09d`>%pcK z!DnwF5d@ATX15Q-x4JpTaXSRq{}wPs2Mh(nC;=3GX8x_TRxGqMuRm!^pEQRJU6`wR za~POY7|H8vVsl>6F(0A?KJY%CO0s8bg7MUcY1*;Op&|#$m)B=;i+X2Bv=AauJ4d{B z|A_8Uzchp&-aFbYTpaq)BFJ96;*5+W1y|u(zOsC7HCpZ`7n}mA!-}ep3tLcqW$f%$ z|NpvgDA6+L9yt>%-R#K`bSfE28Vg9eHj{Gj&u|3x=Bh;9k;3ytk%`&8fYx!vB5@1q zE+zrPkiP#b=pz`!X);rN*TKjxV>PVTxyWgGd_7Gaayo_AI>W*X_(z{?i$z7M9v=!hB}1l*D)nFJ9Zk{t4CiuA~ETRNO6z+2wq*C<$ae8Al--Fn zAPmfKK|o@v<(B*TLfaTg;F?Oyd;Cvd6v*NzXpXFVre_jMi|UK^02_uc$GevWAw zI6cfcM!Rt{PW>fArJ4B5nJ2P$Za>A$OsO86(QGoY{o)Cv*#nv3lHb0us@fI#3zswb z(nYi3)WHyBs59*e#6 zFSV4o=M037U<0ze4_y}I(V4gHMw4>VZ@br)Xl>fU>mvjV8z`}+JV}Gj@g~7U;>yW6 z%)q@#D`3a0Fcx~x{XvSvPq-qkK9C1`q~;K76q#?4$68y?|vOpJR>Y!+A5wk8*gfLbA!#)4LWwzy=II?jAu{pi#s%Y1YK>{yO*_WRQ;xBVE5$9K5sXxesaiBdE4ta|8%jt8lyq@S-=q`vv) zwrUZc_C*~Sl!IjR{)fpe?#K5av&W7PzSjN_sX&1dK>A+~Ma@TrDvJmLkX7)!RS!d0Ofsl}Ub0g|XNN zmHM5(%6XZy9yi%=im~Q?3gH@fVE88B{g>Z$oJA{{_1|?5AaC98xL=*QZhCcUoDePc z^rc&aXbHE-uPbffs%Il_Es~$#C9uAkj=F98vBTjFfIrr!1J2cZ?{`cPm~z?ZmQ7eR z%E#&Itjecv5tsAqJ5EHd{03VRd~IHF>ldvhn6cPcL|m@tF%Rr0&L92qs=MmR;DE@R zy{EN>6Em@F0tPG;dSK}*0q1+z!Sd8l1+nI7cjn*RDa}^-8&yx6lc<$lx*IpZ1>SFO z2d$LE0Kl^M`;!_SZY=0%+h0=X*~q`kF(E(ootjMY5-B2l<0qsRnz`d7$F8#^4?wJw z(8c|gGU*pT>PDoueN!+I4EuiljXb{%l>T>GBDofFq7?LV0c}g*=e0|x=3=7J3huB7 z{+Dgtkj}%+?}1m`;2d#KNWEz+n_+LARkese3!O*=&(Y` z1$4U4mM=~HohNCOb8r2l3rNepTs6+rH95ve*k%7Av@FwPLBOYkkW=G-GbC1-Xw8>61$rEBc-=>gyWlg zj=|s%4Ff4#KA|k@6gwYRa6zzuKyQ+*5aR=gO~_5zr>=jL+Ax}(%Ef$Bdqp#6SX0ud z`@}yXC=1t{CD`-#V}>TdobnxIzoHxA{NWs_XWV9{9Ti%mB(h+%%t{^>`FBgG)N`KQ zeFeWP^dYV@`JgZ|oio{ldK~G^#Z87eRW>>(EMY;3g)e??QfV1$!V?n&Y$%XF!^c6N zJCYYd4WGucglf{mzAc)g1GwyS@KX7QvtyqBT}l^bS%+LJEa;S~sa|wSr5VG zyZ2A%n)nVd^7DsmWg%u6%s7Ko6$s|8Tfh_N6U09e#46A$ zI(lMSGVCH$>QoL|j@85it?Ao!>#_O>CdU)vCjFyQZOC>c;adYeaZod{x$yJ3jtJVx zeRfS0C0`$X;{+5r(5Sf`&TG3)caf6{nvo$mD*YH9X)4#!c|B6=`gMp`e`1UYtrD~< z{>;|%&Q@gc4MT;j!1+mfdT@oYYvN^3-fRh^tKd!DO0`(_v zAxni~{`9OBQ8vev{Vl8X_h0cDV1(t3*K#M~Q-8yRz{aGPs~m011uKZX5q;@5NHtNe z>(X=CO#*prkyQa_+OACRXFP4>xAE7OvfJF)9V|o-08QR~+z;l0kM#zTw&=(<$hk~1 z#pWt`w}a|it!>sdvF-oSKvHYzNT$K{ze~4Y*?)1i(zJAfD`(~p30Xa)x|g{9yr?rj z;N0v4N zlm^_1P2yFO+N#CXh-Hg0aW9}JNZ3^ZASkSgC?P3==p2`%P}!B|=Qp!6{fVesD&hEv zS&D6_+CtAQz+-{u&O!5=q(5pow|m5KViG~>&zCeAzyFc>(=^fz)v%A~AT=h^gPi$#B z&Pu&NA|01nw%-2kAd+KYe41+(Yd*$3S{!z^ zI3WCbxt?7hZdR(<5at#7uus-B2fggPGK>J*K(tuq!on2+87AdwY6~mVQM(_sR+^TQ zycDN2LQvFD1Iq2&@8!JKuwDk%<~{EI`cs~nnd2OMAK4=&yJ-u zlej36q(VemNOQa{V&9LEDlop3GQ2cpsjC>Ex~sM5ar}Hvo|0FyMA}-N+_->dROX|R zG|qG)@mB-mGEI2usZtW-WU1S|Xv$LvX)U;?(Fq57n z(8XRxPw`5?AMvxx^9dUGNlxO(UZX-ZHQr*|Hy*2YzU~=Ix-i!>NI90+HQ2vZTtuv6 zAYCrU(4u*FWlx6GJH0vhxreJDH!1tSOW)_D7>$HO$IeMx`dI`@e^pbk zJ|^LRlZ8X*lZr=G!O+_tlxH3miIVCMi|`H=tvEwds>A&~1lQ)L-wYNBQ`o zEw4>xJ+KnRgsD6)X#3sD3|bh z9f|E;^?G;pTUUCAH6r*5l4*b2oZa>MQCWBWv?UI1fqwB`tiNu9^{F(>7`GRlAIKQ_ zJN8jf)5oq$lEUp^Am1;1`C@duQ2Wyi)yQp97Qe3Tf0w-S4^{;l=B<3ie_B`f?_Ffk zH1IEjecgRh{E(qNLNnWueCz^V8vEx0hOO#8P3q-Q`kEoUnD#eWDRzKreiiRY$USgb zVpAS6+3vqfo@AFa(WK3f61yGw#R6Gsrq)}tal=Z75J{=0Y0Op}dBsW}^b_VO&DEYj zWeT3r4t9cVTQF6KB@QdE7*Sk5&0a_2_7#R za1|ZFTDl;N;}@p|MpU7vNn&n}CY#984x{CiMP!cD5fH72Dgj4VqoI)2G37fn-?|36 zbFs}kk&6}c1mGlqFv=JN-QSnHTP_rkB4D*Xk|)=&pQugTT}&3w4^w|PmI*@C5l!tT z4~%E{uA1I^9C1e%Q{wbyz~tVim{(x?-XSfw|Ggr_i-Rq>XTfmFlkY@h#|TU9ms(f; z_5R-Zr@ICc@z?dgnSAexI~~xHE}7w!-7~_jl1m0W8WoEwoOt9|=%zQ&ZI5TRwLVS% zb%hY4%xQ)=;K+!$LOHrTTa?*~PQUfw?+OCuA#V zOse(sra>1^A1(7C4~b)q{kr0xI%aBo_=eRMkEIHWueo|+xf_X)@*x}5MQ_0d;c9HL zX0qeKUvt0o8cil^sPyB*QY3la+iZSI!2Pg|t~QH)0~E>D zj+Hz}ilJ@NIe=4SnA`e|W_~&%f#(b}!Ca29XBv#DaUL+^x?G5wpVae7o(+d{Y zI#39Fs;(-M=_30~ON_#oe~vTyj?{EA$W=i{sMp0c#zq z2QS5il*4eq{NlMwRYBK#$3#0o)w$wJNtPj)E>OvRoC?01LCd42O}q@XdDV|jHft;* z`iHCbmzYq41L#K~ShB7lepvjFaL5v7SGl?NldH0aGZk97Hqu=@lUX>TE7w^1-7b{Z zYFULkE9f{nRvY2BcVMAA%{TR_@199vkF+e4a_YI|1}Beg&+pY2YzS&NFpnp&I*aE` z#nuya(djZgpmINI*mjSf8OjvRfz~h0j8Z2TzO~Vc@nH%NvmsWNk*Z_bph1PsfX|&1 zefKse;F@QSEQnOZ@bL2w+i#Sm)?*_5zvEdaH14Z1SWxD?`aZ^+nJS zXLvnK#VeFCs1UzWVsTjUBG~{_%#3-kA4NgMRYM#vFT~vicagkyg9o({7O+v*cIr zDK$NI$*=@C!yQ9FSNg9f3JDbuZ~m%rw7CAt;X5$Q1!+6KBqBGiK>`x~nKq#Y{|Inx!`DGeAU~5nAf;a=2o*@po`mlfR6FHm8U5rcBT>ij6HJPhkBqW2 z=l#+|H01F1j!SRTXntVC=c3hk+aK5yU~~o1Q{LFo*Z27TE83TJoc!=s+Reh*8+&xZ z)4tL=omf9~;PzC%tT@ph3#fFSK#l*v3NW50&IOKaRj{IWyKI=IY`Dv@yT!MK=l0Sr zTN`vBHW*I%k7ZNsVc0#I&l@>oQR|zeHo{^>+RMh6p#09!GK1O%Ex%NGSvvAS_8y~0 z+jqXlcuXz0V7(dNs>=o~nPUY6TFXZdb~*H+zNe1Z4{i#m=VGJR5Q71od4%$_Td5pH z=&{qS0Yr;z*M5+8g72Vfyi9#vaZV(h;0q8=hy6x{z6ZWI75-+j|qs@l;^)DHzh^ycW!cV46Fy7%Q z`s4kmd+=_@FGU9Bo3{W#YCg1SP_p3>dSio3+@3Oq6~=FwOlwws$ydf!Q>#0;Oj>Ct zAp*yk-hu<)XOT0d;k0kziSIo75$|EKBqX??1Ua=NMp7VO5NlG zF+GZl**eyD$J=hh7yFYW#A1Y!L17SGA%f=cE79o+R*#`uF+nA}I)asbh(d3LF_eKH z!me74m{7ez^n?HbZc*zJNY=RDsPz9>6jFKg_L*3gY;7k_-@A2dhuS59%xc^MW3Ea` zY9VdFK#_bY5mJHSeh~hdthkRb_MT;)emB)Wk=O7A7=vbk8BH7(Vi^bEi!Jz9wFq>q z-V%#WWtVa2&8P0z7jLYuKcW3t<21G97p3Z%QO?)6l_crqv0nEhQ@@O&rIo8ZV^h;# zY%!N^meXrYRS6lzjM1~Bu*%)D`(S_-L+(Z9jof^(xVqfsG) zZ7->1T00+gx;5rBWg*WHGM@b1wM*cw%`78KK5)~aeC%TK;1I7*O5anDqp8L~f`6`P zKG-7(^gOmnLyryhWFMNPh=!{kO^!x6ycFS@W5v@8VYDH-+2?k!V?cyf=8;>IJMV{8 z`$jKs=7pI4OXU)EYMyRdv(LPy`pbGi9Of}p2bMJx4j%1u2s^-Gh8HDZcXy#nCx zEhhcBEkB)f4THhUXEiGEorh}W-)b145vI+oY-}YZloPvJ^`MjRb0yU(Mk7f(YXY{< zB&G%jx)n2m5gj?uG6i3L!fB3w2^kr_7t$|$3}1_TmjG=Rd_L+}ol^+?RNg^KlLR*% zHv_Ge(~@o6^Q3d!CSEs)ul}H#tr6K=3&TZF^;2`ebp8i^@pkgTa+kMx8>gJhY9NswM$*A*wNT@z167D;TD@=(}w;RsSq(MB*lGUTFh%-3QB`wk) zb+A{_SBCgQ*c2_mu1zdVadnm#9;y~}f6J0*9^W;#0*;2IOW0OZ#z9kKKnF;-XSo1O z$ZV)Mmg}Bj`y$=H$t>z7SY%qH+W7jvr8aIDBc~giM~ZzY+<}J8j;n}S$DdY~S==47 z8mDe-tL6n!9_54IO#Ur*?Z&|8G6{9H`Z~O2Z`qG`Z^>iaqRj+ydn;$RVEK#fVikN> zw!lgOQ~5YqVlim#Qbt-){5LVM+Mlu$@#{B-vHM{Bs9-Bsg*TM{{6=>HE~e*wScE;k zhq4laB$a60Ak^p>-o;ES2%`_~tj$$#plps=C&EWg2g}OHVBqlhCi4UHH zd)WBqayp|&sz(b-?LL+_1|FE%z{tOM0{iL2h}Ns=vCsv2#+&~ z_cx5o1bLz8E#_uq=I5qkdtpkjNGE&YJEbxqIiPyM+eTgUJfGiHq{XIj+p zNxP3G|1kRM_nxi0kqk~y4*9jkUaObG07|#`IZ%emPloc&NoUH7d6Sj}^Dm~@+Unt1 zIVN7zFDEhoOFf4x@+Hb?#m90*6npB_&tzl1L~}o#dB0RQO2pMdiAH9uf+R8R4%(tR z6^%BE7Z&=?`7eh$j&7z#<~;&>40Q8W=5X7Sf_0NQ{p`m4atDVl+-63#kS+KC#1$>6 z`Lz+`+f{g1BHea_PBhG!t?KLctaH|rb*I{v{-PX<)PM2Z+=ed7`tORoyH zX-l9ixSeeJvF0w_wb*~-Q)|3}zGeHMwded2r|B;#OWIbYN}mq)P0@zWSbmxn4L+VC zX%h(WSK?Yb_zNoVCX#y{n$j<5z>EP`i@}oG`y?~0zH$p^8JpeG=l97yyUNXgDxe_+ zC__)n?SZt_wQK<*t7liR9rKFzrT z8HK1AFI%G{le8vJCqRjRP7G%mC>pQ~dwpWuFbId+?uDDaqQsrVc2srP66((nqd266 zYy$%ZUg^W)1}H226c>LubN`S3e$=G`F^TS&-04$@^O2y4FOgGJmf^q&ab3{Ty;PB7 zzBV18$1?e972;=H&a&vHB=fV8rOyJ>M>I1=(F=apCN8_he;%dfBtG=d_Q#a3Bx^QC zrjX4Im_<%C{$o$aQ{irz@H;QTH6(`o8q(AcTk~l-S-L(?g-q`mGq`TC-LrE){nc_V z$e6iF-taZ0s6!Uf#lk1UZ$_4~M>cCpnlFSSSLnM-o4%eNJD?;(p;u*f)qu|{?K2ha zpTgXw;pDB_^V$@Kv;7S7%;@qOLag3&8q8JV-YoN4Qtb%NG1+JR_GTTR9#dQMTrlD& zNdulsf7T_b4>-#~Ba`mE15}bkl*s*}0^Y?!*V|L%>M*69Db5h7{Y&|m6)8^^Mmq)m zKi3zk9hBTpzXToJuitqpy1-$_zAl_QM;){Z5FT}m8=FE|9DL_P1^x`z+UXRSG|MMM zR(xQN>@jg`^QVS4G$qxTgS;hL$jYCroX6H8kVgrKLBM*4%DcCWA=WGXX$o1Xa!DsI zhl)v z1rwY<(*Qz?v)uN5+6Z@9;+EOi)HEw(F2JX>z5f&JT1m*x4Q+)Z)Xk;AV@nN9@bKPI zy=J;hp7K-p+2xT4?i#SDcaM&p4ZC_bTb-K--!orMYD`>B;Y`>PxdBFsd|8ks+M-|p zZnw@I{qe5~e_8x`Q;LK|68w;Tl|1;A=<$A zU>K{FkF#Y@`Qg@L)M2C>Q^UBn*Rag)89h`=S+q_K&^Mu-Y-yroZW(@9CzNn_No{nN zSTRJDs;7VZgO%Wf-urYp?VLuo#3vGDg?wUX65SNy(B_vXVpY* zC)Nn?@!eV5{jJ6n+9D&-rHjiQTh#fVM~E)K5YQC?G5(pxlcEvifV#^1V{9hs@b1KL z2a{7=T=aFXb{6#0;VPDgTFktJ?C<%Q#4N&1q17P<@aJgE^{6~bl$F2yALz^i5i zv;{cEBm$boDtGp%HWpSVkLK`iCb(BCbhoZC!URT|Y@Y?JONk(Ihm^fwK+r+cez;g| z=Abuey3wU%r>j9D8Wy6L>TS?uof!IY1*3Hs{^ZfENXIy#m7uW%g{L-qq4F+~J(#I6 z{3bH7R*qV+p@R=lkeC z&*txDk5yg!esi*c`=c}bWl3ZL!QnE;Yoj4t!e`M69gDF8?k{W~OLT`=rq4Gzw^GEt zUW)KBk@8l>qUXsnTCvY3uQZX`)l@!LJ^s7cM#QqJ;G$)_+@tP<2af04u@Qo=9(nsY zJi@*7dR24vnK+=3&w{u1AH5CxzpmWoe3Z;e zEa0&o`5Sh4pSxUn#3Ca96`bn%MbA%1A163coto#Kps_$~V`7K^ZpK6sK9otwH}vzI z8dxuW+5FZzel%F3t{3d7b3+rIO5X|U#YV$G8ZdWHB>PGYTw_O8yf6A+1;wU;$KS@c z$wdYfZ*HGbH*Y^Uxh5dunfGHmqmBa=iT!frFMT_*`k)o2V^hlW0Zek+Ej1_K)1zm^ zTjfj7rn!^dOcny01^F@c)GH1g0=bh7E@)2WLmy7*$wKa_JNeJH;IF?K9iT2DK6Tr^ z8kxl%1-4GL(KrpS#dDeLB|GR9GV`z8O&j=6+*Gp=lvH8xpq23NjXmA*zGUsEt+~)2 zLC*?<^s4NtLzLVK;pbAvE?~@~jcUiI%37+Dg3a!pZhWJAA1d9Lq5qn%HW$9cjebMB zeRg^v-r1N=F7JyAJcqWYe)ivtbN#?a- z=5{MSCN5bL$=m+=;_Z5+?LVgVQc`_5@-SNEV8zV(9b={W8zf7&wf=-1R;z-s(0i1) zwo86}mXoK9zJp-pb)u;)eCMgYxsU-EeGfxvBcIVESdFHPt>Pc{J;(L>DeYQu{PC(5`c`3M!>JZW7O_0}- z54)kyr`0`&<6zxLHK$$%Sp2lKR~iz~Q@!EbLCno6R;jLO6tAJ6a2TE)AaIqGpA5xL zaFF)`0?V%i=eY8|4JMI!9XR~5%B6oxCv;YGTn{(^JWqYtg46Jph|DwOjG)59LB0mZ zrm)vhplspiZEK$*y?Lejhf?o1kiocUPAN^!?=;ri0jL@Bo%VB&!0X1 zQ#}L`nmDD7I-e`@Km)eW^MW#)rNia1HP#$2#iO8X=wAdI4S9+eWi*Q<65`8 zwLyu&w))~BksqyH2$%%PrQylkJZ&-Q43h>CW~8rj0M{p2>`(486oT~`_S%K#V}lhB z^An(R%u=au)iiBW0PcMrU>%)n7(-$txeO((%MJ#BNuG8WKI_Sj%vIIQ$nOgl;uLWj zyACjrrmXqQ@c2`MluKeNS_LBtdkjcavd65Fhfq45&>Z*IYv|WiDJN;~f*@;d5)Jgx z5B>X#9dZ*~64()C8tWmHwkfcDxv5G;D9xz)8zls%+NKJ%$W8}GC@)6xQZ zkm_LMwe6k|Uj4z&OE`0(;Uv|D5T4bFXkf9kxys4s{yc%L%q1i;(zE^TQV!K~|Fu1S zOj@n&w=R8n?xZ=S+?v)C*VLfodzMt5We}+q?_-_HP{i>kTa1G8x~$&@4qgZU+K5gL zq$9rc6E?MSGgdZVfy8M$2AUcUP#BSV=G0~87c9cI3)@wbl*#`yP!Mp)+vmYwo29;aXPsXG*=f-@* zhJ+0lqGa}u>|W!Eqw#xVk=5mD`${}5IX5oh>Y@`>R9kF5%!H7yo}es;nCi(5%Yj6< zIUV8`robmR}3IN$#V9?L`TTz&*W#fEC41b zeS&)(@J5a;`f&bm7xhGon%|Qy6pt64vAz#HBiv8ba4uHp-&+n|ZUuC&eE6An3Do?n zSy14u9L7Xk{~uyXYOv-Vy8S^d1QDO-sns2u;)&lo82G70*5F^pZs%D zmkrzZ@2(Zgx|a%UV76Kb9ZZTiOZ; z1eg)hE|SfWcjW`=+^88Jw7xwuwSR23Q(~73EG7DGF1(+3!@k;{=@?PxUa>~HP#PHK z%FAoSW~0J(9{liI@jv#W&G`GZ?rniuvmX!=8@6s1dbLpS&Qq<-EkswQ_jlHSNZj>d zvi!mM8l*!~_jVG{g~}9)>7A|UVrLc8hFdXGU852e`T!Smy^nu(_>xB+{l2-1*mF%!cn)OJwe+NfUdzS=ta*+83xd!DfGg z!;iV9Wd}ZNsoxdFGEM}m7ERs+KChsH0)=NLe3A>36`mSb)Q(KL4snS)&Js7)7i^XV z0$kz(n=i-@ZGtBSsex459qhWiLzqT8g1X~B0EJhG<7?;z!-EsKT*ju%HVGd8b&)9` zc@WYA+^m@Ywki&hY;>1a8x^Bc9L4NNlIUmN%hrl}D8OwDsmo`uUS2ON4|R?tI>OLd z&9Mb{qaDs)X<*jJN33Rq1~a5=^$>J2_R;1lGdq@ATa)~Fo`7+);5m5G3FXz7u7;Vn zxf@~UgzIgp^C(DL#~0;H%o99Egu^{dgnQE*kYErZ;=yDp3Estv%Y;t3lm;C?0+y** zcJ5sgd#Rk|>u}W_{8qS4Os&JXV0eu_V?r3vlVZNvCJYW@=pQEn1uf%-V&P)Y!907S z!$3oGlu?xqB^i^03~4W+?5_QGi?K`=$c@Yp{#?fud0Uf#nXe=_Y^-umY@hys<_Yr} ztG1WSWPS?!0i9G~9-#7S5w^~+0ZO8!WS;e(_vAjV?p80lA`}ilgAOG-!Agf-2Zv~k zf(=Rrjcvi$JV;5)3aq_h87|M`T@Cl>zb)bapvF_rab8F+dmj!^#Ip3IpY#3lJ(9(H zqi)`OL-vIrme2cM<9S84%Bl~tIy+|XnqSDgYnv~f5WWaMAN-a;eky1hbaLLAHlT5E z-mHr!-{!W(8;nZ0V#y1V8wPyQk!Ab!0}ZdR3z{rb3bK=PFQnY+6{df-+qU)cRn@hl zA>S?D$_W>HW$?^N^W4Z7*}3RZbMcvIy`HJ)X}pY8{Vs@J%+ib9qOpiCEdhT@*q%t#yF?&6IwLkKZDiJPRt_HL;fI-sU# zM2X*$!DBS)a-g7$8GkWxN3N!lY*yym{`zc3T~D%N>J5M1Y!_}3^D0bDb?fw^{JR|= zi89Qr<;$%ymYxtdq;O|TT&H)kb_S4rNtXO;8Wtc^)L9 zdNlTOWpgXs9aM4EO>vwW94^E@3<-_o4r|m*Cq!`ur!eMQQ4t0c@ll#c)7VIL5y9hJ zy%ro_cr@2dS7~qZfreV$$wrd89{3lzPK3KAbcPyE+mk46*YeX@>k<*ycBW1buOvp| zD*}?UFLA!@xf{}>eMc(mV`@{w{S?)m{p zJW*CVL3rDUzK5};Twdh?{E?73&^&AmQb+0&LUXK?b6d{sx>M@CeQBNDbK*zqH&>cu z?59=+UfKGw@8~F%YTd)U6G_r7vD;W%Olzp(GVPnoh55qXT?v^(UA^$Dbq~#K-DS3m zf5$N6)Fn8+T6_Z8<=_D(=aQ9bwf)kbtInAY0iDC9iA_uNN(xz;Bz)SC-o`-6L zYHb#DJ`!hEuxVv!ndxjW zE3iD)Z3~&|^9!KZ=zr8EmJj}1zBr*8#HQW2j|eb{v@E%QrU2=xpzS%whlK2ZSE8lL ztL#$^F}?!Fis7kd#OZ$DIHt#|{=&As0gH$XCNyOOnB~>P%LS_CPqxY z1~!#n@goBAfn{1*&N(k+!O(-xmCLM&q2+6q9&=n zeVjiDz&xNw%x^jUCxPpN!)es{U#E5%yV4mG%UsY_U;iZS_o(ulzgFz2$#G+1tQU^4 zp~uM?j87+Lw5wcJW%9{^RUP{je7`2HK_)^IllH`Wsx<6cxz#Oq&LzwR7pB1SZM%E1 zl7)YdK>dMFbJ~;IvY(G{8Fycif(tNcJ>02b2Ot+DEPh&Db)aMRgH|Y$Lyk0EZs8+e zcC{2#j2ta!*!K~9)DuN+`(jy5>R>aWE$^*+SBUajRY}e<0_`7PJKW=e@jW%8ABsEM zV0WBlT{V5iDs??77E)} z)1=?hN9^(}@dL@iXE`-<)rQ=l0z7eO@1I|ltK<>4ZQHmQBq&GwX-_)8;ilcj`jH*H z#Aerhu%A+xw-MtR;qc;?X-b`4sI+zwRHg&0#0U!I5$jXXyhm6Pe1$>|X3wWih+4uV zp!I-x|1zqO|A^ZB%nb_Rjj-ulP-KLfK1UwgTDKJ~{wH>6UU5%HWdBfNsiE7zaGCiv zpximYwSV_5Jxy0(29^e1k2SFEe|Cg2n8jFbD?KtPkke zo7x>0K*k_77P@X0;5V*5>X_kwofnVnxsiiUhcdJLd570KagwtXjUZENF@P?k?oiTL z^PZj85_LO-h&L2653#hI-(TvecHT}-;u)-WUKf~isfhEeD$~js80qJoV{Oy(UkC`t z8q{;PaNN+Ww?Q+*^ZG-15cOtH{o+5)NC#+WI5t5(z>@2xewLt*4=%Wt9_p|4&wA>I z|MPdp#+=*pII!R3wy+s(`TBQRJK4VVg44pYIskaP* zDaSJz1I82!l0*j2=JBGIQz9$L+`9wocEDAb*X|IveSBD9n!1ILgd%mfy+@Sw#w_5~ ze=IEFXL0DJX<)W2G*9eI*I2Dn%8+ynYJK8Fjy*OVBk1&;G>2gwS@RU{4{na_sD?Xi z^pi#yNx$vScQ@tNu)bOAzh1MK{MhXWFO{AfqUIbpu`;B`nXz0#Fs58`D>kS>X!)q5 zbOg6V=D5D9^M^%W1wqq=d?3MX$Cr-{OL_$0O%<#|>x~%$bS9HXMDf@*GDdH)?#~v* zak71img`{_DRgfD!@5=DN$mLSQo9$__iN?VMWlTMhB`H3ZTcq2y8px_qZz@|rop~a zwNHbT!@Qh+M?@*Pr&%>oJyBN?UHiIS6No2QT)Wc|sG2Y$&$9JeR=x6}88QpG^dL;^ zl@B;qpuUv=qaE4nsoeAo*G#w;bB2~8Gw)yccx(E%EVDoQ*`%_}^Ln6c2r|I4 zX=NwFl2vAd4ldJqIY0YI(fXs3JYQ#sH=Ba}k{_Ye7LXW&_qwwwON|raeI}8Inv>ym z$(s?$adQ+`lw<#S+`dUG%*drlW@|{+qrNFWHrw%{Zl>_@$C|&h8!=5w^FMD=5qfqJ z;#WSwOO2ML(cN2;f2s+ZZzc5SzqNei%Ck^-<7w-OK}VGrjY^a{=;|u7dPoPnvVqZd zX>pJZx~6%}H>meo4@4hrHB&J)DDO{M27XP&G?~+Ommy8~EA_6JbWMeSSD1}h!EgA0 z&5hDxU~o3W=QwZ1z_3OC1uA1#G1V>mQ0sj+Lub4uzzPpXlxz>LbsP&8y5V^~`IAxH zi|dCf5)d8A@a*ruJxK(=0i|-PawmsU^m60X5?bEO#1|#$_XKP?7xVcf4?THVyohpC z9F*h!cn9fK!j~sb}E-&G1{7a9|WGq*O?K#|`B2IEIsD8vG z8o&@8b)O!{dn7#bi=;ijqu6wu7pGLeEI#%ZR$U#o6ChuBa`F0(w3M_e1Mz8qg(})! zhSj(%bIq%u^zh=17qc1D-U-3nU(BZrTF~s1=agmc#;~|i0)_un)m*@CWFQm1T=2d|SHFEKk@^xWW7dP8{JC=dK(G zDZl@7Te*n;qSP1%%dIcgMWyFe?UpfHNxIq_drpY!za3b)L@_}_amYB|1_o;dNofWv z#5Fu|r8#)dY3#cmR%q zHSt?G*G&z(nzvmFlKG%?$;WJhJfJM|5=#&lq(=We4|`L~va~_x;brwHk{`Y1o8}*; zVN=)3@9AC6sbaLAm3~oEX7l#VK^e)3YJi0 zW*MxSOu1_vo(FN7{j_0Pks3bo5oG{Mic&)CH8^*7Y`gL!lt?kGs;A9@LV9kgF(P~+ zDQiv%bq$R*$gRzrL2Pxr&yYEJ5lnk|_c76giO#eQR}B%XN)BYolDzdu^S>Z7ObgKh z=;-Z8sLAXWGyB8o3BlQY#LBT=DLdj76Zd1~5nYwB80fhbxb>~r>^NH5Y;#0h9xQRH zEykKuTf7sP1)&6<=8Wcqp0A+Tq&5p2C8j)WV-rBSnr0^~D0GeeR@o;OSpU;svVJ{^ zh&J(_&G4Ci=A#!_=k{@ev!hAl5nqI>zSrO23jIcPTyEq&lb~;WwBH@2(1FK+Th&kP zdjNg)Msr3#vRe&MqZE8jS);??E_~v()2n|b7_aZwas>^>UJLQy`c*2i1V4twN!k-q z^Q7W+U4;tnhxoR44SC}#m;KGg2&^hxGlO!S6XaPDJ0voF7PPImltMT+HQmFqSn!E1 z*LpQ34sBNS*C>}|aA2xP-~3n8*<8v_eu@AR9HxY2oPv~lO%dZp8I42Ek%&0Z9z z8ee=2Yu<^=N};Vf`IE<0JNZ8#%BSpva+s_bpmy<$i~N7G2d}-;AH{UBCnZJ;JmN}1 zZIKbzADj9n|CI&P!$ZV2tM2$ z7=+AvVz+NqMhP40SJZ%YVtt8kwZ&7c2EZ`y{h;PH_xy2gH_nz3xNUbK-{A0beGkH2 z1@-5r9@TBBsY+IJPlb$n*IQZKpwa-rmTA6z@!UCS9vC*j-fm3L*uK{Wb`5evkRZR+1 z7S+r6B=)f@F!y)jO8t)KHPf~nCj~z?*#?|lF%vUdJ)X}BIbhPQ5)6C!3sSMjn(%vI zJ@)MTde7*kuJf=@Ne zSK+5z{&`bPE0p0bYy(#y!c=Yy(2A$col+}{mk>2hO9*U?n0SyJ|9&&RZK6jwiwT$h zGA z#0QPQ*@syMb`_#XGMF4%8=5PfvGT`r{uvQH66Ek@&$*vdwxDLnu4*Z(<`Cpk3DH2B z+@HqqlTI#1%*^A&f_;?*kg@6PX+uE_2WF=k{q%cjp?+4>h0h%jn`rU9p3EXWIu-S=tFV2oDK3z z(Fc}vrc1yA*Sz2Y&HK*TvcpxY@^-) zp~(Odk)?;#kL0FH^%HJIDs@#WbP8`r2>EXs-1nY!C>C&sO#RyFq^cX>?`-n@rBk757j-)GVMAx)%ac-S30N|HNP`dwK&ngNttHS;9&EvZBgArggi zF?*p$iD2cW51wNf&H&lpmUg)ah)Ey?uNt80TpIk`QVJa*r9`(X#Om`cVI&3h^;J~Q z=A`Nwr(W;<=U6Ll8B4z}i^RU8{EV&v(J-37)(hj*nGH}*tJ$By&P8-dLGq;QY>?Fv zRuZgO8-S7y%Z`J?Hx7&4x-grOxEt0I)85)IG?v$@om&>Y~}TI0rB z)8JO4<`U1ybHzbYLUq>-OZ+{96ZbOkX+F81CQL_)M04sEKB%ZU5116p0>c*UUxty`l@rXFYQ=-#5K!@5|Q>1AQ_vB7*r?9SsfV z!DYCN@#26maWb6|t}b4)`En~^%V4-)rrXyMPERl+i`N@Wyk?DskPT42SA7&ua$m7Y z4~=BH9R5{1*8+HU;qisr;+Ow@aOG?7 zq@~#=mzv`Efm*Qw;)xP)Qb`h<#|*|ezgv=<7Fz6ZFH_FvJrsgfFyVP?AXwp%{l19I z)ZsV5#}xa~#V8+4aj0lc2y5T%V?h{f-_Butc(+hOg&H9?-uiU}qP-U;7GU4M>-xAc z#D1bwW4=?4nGQjj3u#H`kOs7JbH2ZpYm|HD%dB%s-EG4RGH9k732;H7aYB<#-FE%@ zJdxe-P*Q<1v>m)P#-CLZoqQJx;43RwBQm%Fw;QM&WEJ~5i=sQs;ecJU!7 zuAY$mEHG7l@6f_7!d73cOU!b=4fHPoV0mr6Pyo5`#>FP^kG^P?EDst*>G)Wz&6*YU zi;#x;m6LTmoP#U5))Pb@p>68?c}9enlAqomylfvNVP$T zJFCFzsf4p9AO9(A{Rr{R-y~Tb^g*mw*=A)(ki=Vf?_xX0!mcU=qyGwpoT$GiGP(pzTSts&2=G4p1-k!;iNN)EBiDfteOvcSE?`#tV zrKwBH%p0f6%*2Oe1GrGm@(x_P+S5(IHHO957xU2~V17O8>EZe7;5)HK-?H8A&PBi2 zn()kbDEdZN?(HJ51jZRV8MeD`izy{KeBdc*TC$TUUQh9$b7xn)NVVeluyy5?;tP2ArMWmT z^@&-C*~QCDL#kWBCUH%&>~8>1wBaM(s&~fT;ZE$1-a;n5_`)0C)Iy+B1x~fwb^<^2 zTA?WB+9l2MvRG5f!-`C#Nok{6H>8vH^QOv$zHB5%n`61wp z&nyA;o&4Jd3h9ba9k8!E6esK0?6F$wNg}y*cdK=3PIle1TgEi| z-|cVRHV_BuTT*0_0!+4t+yGW#%NLt=y-<@AZYv*r(RD{z~rF8YQc$wmU1 zbs;yq>}T*RX)j`xfL_0y%57BVduH2M%m3LdK!}7Snj!9=;36D~Rx(HA$1@9DLu^{5 z_w}Sw1L7?)BtqGX{bHfKv=%{Uh<#?NujhN!W&7{mG9{PGxf+xG-xtx@1g45i$NsV1 zd|N)j-S!vSZ|NmrulB*Ca0N+7{sM*BRQ7{ty>LR;7SyO+vy+KX>&DxvNHj=_ca>1i zE>%3k;+iP0f;aGa0UMhR)}0FQ(dXeGD$J_^PB6&1Rb&5Gt+4E|Ram1Ebf{Fk$>31G z=M=^lzaX-*eQ>@`7wa7NV``hevRoS-1GEIl=}*$tu>ul|f0K!lM{rrAIp=ay&F|}= z;}}x)oWRq8B*XrN6xW+S+e?D|a@=2s1pUX-FrsEI7IDR3dTdIxAlvBHOtzj7dq}%& zuq_iEaIR*yRF?xxtcKffaQPRumrv9tY9y!^Mp(1$_UnL!h45DXcdw;i7 zk*-tGr7`bSPbENq ze+p8M#L2rFQ+GdD*Ob}r8`taFhd36PbkA%GjJwPhDcPuN4ppiWgFdr4N6{)6^px=^o=<}LI-P|R6U z+F`BK)tzGus>xRrW%NPegY=Slpy*s6_9Q+iaob5k3{kh?d_~d|#b#I=vDE9@CwCWK zUlaH)0B5^0cqqi?ob37koO6~kah4a>>(POfIBx%kc|hyMFxRPGMwfw{P`bK$>6{svFvO1vWV!*o+&8s&I zi?syJs!S4U-&;z*_sfRcL_FbaX2;bG_;6KET(LSlaUvdjZ~UfnA_kU*Oj9RM%U*O%8udM78&ER$odhQ5UsfH6rn$lE z90rXC#LLcR zan702aPYa6biLy_O6|AS^x~)39y2N~V-1L*ZV zNO=U5ohm@}`kK!~@zOX0+lnM~ma?g1ZQ_g-bbEHAz>^=)tk$)*ntlQv5IV0ScqdO_ ztZj{$PBL9;Uy0JrAiVAHAFYU00lvxv3rpQ?)J))Bp>%K0)Atw;3q`(I7Mt1l2K!cB zjStIp6_*|R@@Y`JomxIN9)njZgl>bB||H-Rwp zV7Pu=QeNt0v+idVUKZ4jOB2Av7Rv^PtGl%p8omw>&&}iOUbZ>loIu4upVpHF%!F}Y zL1@#78{rn5ZzW_17M@pyQ_ads7(qSb+Jq&s*_KFP*s?!V!kX5ZUMB)J|J=V3=x-gM zW*ApzI^ygzjx*jJx4@Q#>BDw+T}ti3F1sGZglzVkM9R6NnO0FB#WU#0&+KD9Vq|Ky z@+8>`ICY0_^WUnA)r6@#u8)95vu$;jIu>@5jDqGv&5^ZdAC{Z6 z8p3aO3l7J$cP*w)%xvCPebx!k+DZB?sF8ftsPnav_da2`Q`x$uLF0kd2&UMg1!r+{ z8NprAz7+2+I=wjLBj9bW9{pu!PzzI$miVV;dup9LLL;k4+T~O^VdCDQD7SZ`ELro( z(g8UFH%>Bov&K`R#K6S5wW2M;B3;4r4^dVQGZZW10-kOYYZKP~Rlq7xkD=~8zn`Y$ z^^LaZI?z|PR%CYwd+ldj{4-&mF1)pYw#YsoSacU1t_0sn^pOkyTs|Dz6s<2`Q2O44 zWVC=;f9P%BQGO~fmAES6dCYlj+P58?>KtHRVEp5Lm}t>a=sgm5Kc3EgyHM7VwNghe zo>hfc@Sa6bj%{E@k)-&7G=#+P>*qTTb+4?eh#OK|RuN@Q3(ba9u)dM^&eIo7SvG78 zI0#p8hTX6dsV@Us*AC@pZ&t7Nr#V^G!kl$<&i!+aUFL5SvnLP!eZfAU`|@`Aa#}jWE0M3#_{d%*AXSN48zC9Ht*-kfueLDdkB! z(JT|Aoq76b_SW7#=4%WZihD_Cy2L=bDcYc`dIHHxUCcHpuQaa=wVDi&&AXfBEhi#K zOwx@tX?E!G?Z{zbj?LUlsKPXXP84P4SH%(;IE=4Q5~4lF7h&^B;>mcTGC0%9YLO+rJ3~wJ2+U;j_QACO=ffCl0Bn z556_;5%Qn&{2xW<;g)pUhT*1`nZ1?fw&B37xly#Nlu*&YmAi7H;=qmC(6ToNCO8sT zrQjYZ?v%u2R=MBuiOMbesiUamChzY|0A9Vs0TckXM56v}wA zlRa{AaT}ub*5g~4%4Fxf)wId#X1>(tjm^1OY@^3;iUz*uHa$wGDfG|1LMc?D*;w%R zkh)_o8@>Nd)DH2zCh&_7rpq{>f|AFAu1h(;l`>rUn_hhVj(jH16;ID8KzVtme9*NP zvulKnXSIU%aiUj0OAo~b%i~%4HW@BZTQ2V`g^v)Hnd`lbxpb!82Jcs=DA(tLsf%Z6 zl23!lMkVJLuXX-yYQ;00S5P)4Zu)A6@Xi-lo$UMd&@j zyELTaO`ZIjv3KV8-Bp~&+0T_VEPV1A*!;ZQu>DjJ?q4eQI=LnQ3$eB55oCvxVURoSKn+rwp6S|T zQunQf^0OBT+W-v$ZKbbelW6KjFPWbd_aV)~CQ@4?nZrddm!y{md;$a4k+6YB>sEI= z>&1<@bN@Rb&>1$Sqcw5^DAE5-2oZ1f;p&jo6=hZdOMf&X*Ma_PM3NP+EHD)|(zi-r z2Yl*T-rQ>Y&NQ#*9Ckw~8YCFlg!-`0D|Y(5aIx)1z?xwr{=9C)2_=%x*VLCosisap zG)~Oj&AX1%V^fu>>HP!oy&-MM{WrtxOC`bAGISy}_Oz$JrA+;C75VZsuw+e=-oh{C zo>qzMl){aY)8}n&+B@B}i66_#H_VyR-dvsj_=z+*x+N!<_+EP+_FBPwh!H<&J;rZL zrUV2)L*0~jW`eU;f;lGD6T$2~8M{RrXxcg(t2hLfr(v^dbGtJNjV>zsf=!F7HG?C_ zVAx|zcjr$vOZo`)A7#GpM$1LYI@H|;qItL zF0+?+G^Oim`H6|{;3SFJ389sP;6DI9tDUg9*vAu^M@e!^hE^kfkuRuj6J5q@^R!KE zk#|3LuXFzFNGdYZ7hBI2${tkswNVzQj6KQdVnC36mR^rsReppUTi%NoNS-**?!D;u&ge4U(R-_}212Eu9u%;b zDV|te(AudfKG5kbc1lV%_q8<;^Xbba`PVwe_ZpEB7|XntTI}JDM*ck#Q3hj@Wn`NDddnsXwSM$^>KM`H;TfK5O1RR! zbK?P$gUNw5Ke&S*dp@|e zW|cX;)}oO8!SxZ0dni7od+=sb^p!OEpN{wuiLk?Q89*CF6rFOX9KD0=F*N$XciRiHhA$(?EtrIm|Cv&CE zi_K^sTvxNogor^7+L@G$1+rP5ADzzSqY9RpNuUQFiTiyWL+X}u$~+-!jA!}2ENNqJ zjB=ZJ!>Oq&ZJp1O-D~rL4vN3DTeR+;MOQj^Yat{zFj||E+PJ)DK8B+z5L3}ZZ_l{$AhIn}Al@rtMatc#TibW9c2V6ULM>*-6k6SJN7YF>KW3JHifq&@ zJ37789NsDu3$fMeUXhrAcCGHr^@}7R)!dgE#5C#HKeN$34udZM;I|S_eS@#|WT_2o ztoS!Yri*HAviKOu?n09o)a#7(yjDP!)I--b$lrolx^YBAOzx!uqw~R$WVRLxCxoRM zj+$YT@uUGSUZ0da!x*(RPAi5Qb-NXZjsH9$r>>VpA=~NOJM!}nq*IqY6F z>&fpB(bpx8GPa)SYaMbOGyr;tnPxPXSg)JgKY_b|nBedl!V$30F31u8&s2FI)e&(m z?RTU#duv>4FlfnuT@BVnNEAnwW*0oqdaE5jzqN0Wu{9?W`RhmyG_3&rVzWS+MWuzUsz|JoX%zRb&wr2<@1Z6Gcd&|+PovNb!~Kd zPA`2b&^3^=mBSLiOFzWu`iOW2V`+?lOj`>oJFS0E;V_2y$G!6Q8@2?{%|9>)m+uS~ z`Jm#!Ub&TDg6HN-{3>imjyVBd1=19Y_jPZxjY2;Ery|<~^;^xr8K?a5aCYTu^lCBa zPeBn2Mu8dnna362R63+fpv5#wt*Guxv%SR9|PGH zACxRM^fEAc8zd#rJk zUB=!pr9;cB#Xvw_N;&g4cbU=}?jgJOo7x}wkkGcatW38NZ_qOZ6)BrSodCt0j|n>t zua=e?ic8=};zMZ{{D`T4#emamJqr%;apgK6SoHH%(%vN%Ri6?;N|BQ3+6` zX~vO>tNKc@k%f?Afqezb1hu&fzP-8v+V2bs24m2KxOxB|Bf4K&a;LfiL-Q|rXDXx7 zB;qWLrd%qS?CHh^XW+6g+e(;Gvj1bwN}ere=@_m zmm8NiZnZe<*%)}oxPksl|E)?W1=ncj7~g&yS$<}tOQ`|xO2-|B-dNm}Ww-wfYJASv zLQfBW9O{;TmiD#7-VPq+g;$x)M8wlyUrCf-5H@8lT@@Psk{?c#RTA58-3q@w)13u0 z_}hWI&~0t-bSB#kTerGG4Kh;p#~%r0t4-Fr=Sb;wlXfgZ`chSb<3v6lmaehm;c@N5 zI%!o2#@#p2%tCEo@UD_0w;{?tD||@-;T`>2_Tp5pHt4?-M%QWr@Z*ID_GDS{xG~MQ z4NrY*J^a^_?L#lvAMejz{a^+B%l5PynIjcVtUd?UsZ4lZt@Mb^UTl@fsVKgMmp!v+ zH+?d!N-BpV!p6X@WMSOn{72_X3N0qUGOJ|+> z;kU2;F={n#@T>9_FJ|9{^>l8f@Q|$J!>{H$vZYmbZk?LIehehbK9MsCrkxI|P?D^C zK)2^ryH~?!cLVtz5UlBOs&q_!dVt23j=OnZlzn0aVf8M>6`iWtQ-}&QL!UNwb$PF> z!r0w)>}ShQ70n$&b?YgwCrhxMm~9je_;P;(<_pmg;BCW&$mrSUWNqJyG= zMe5I?xBP;IQWhb$Rz8m$*rnaJo(ciX-{`3mVJ`T(eN7I^r|b@l5zlCqtgzKq5Y(Cs zzxBbuu}(W!YDpkLsjW`oPmkc^LwAoQjKaj8;NMo9OoQ*s1h_K&9`mm<)C}T3!uz9ZgY5-6Qg-Yn_I2_yubP=KB(Tyh{+3oTJXi=q z57wy(4iUCyPA50gJ)Yzz>H8x;nAI)LOc?cQ&EnQm_(Tq&_pkTJr9Sxhch4Pj8y5bjDijqm9&th)`Q`CC`!d=5@F}2-=YlGtOUYN?CbJ%r z-jz}f=;>7mAjcKHDk(J3o0^_x$fzZa0}ZxA8}EuWed z?P#-^Q<6!d==kQ|^tr%+4D6UPMXfL6p^&D&0-wO=hc@2lCEX)L%H0Lz+-vi{ZBf)O zuXLQjdZmWI956!zsxS+*`1K>tNg8Y(!6U%F4?X8nI1;9LmcCx{$&$Z0dha~dgC6(9 zO=fc79n4TVUs5#ew5dgWy)CeGiS5bTXgcd9QtY%k0^p$aN7gQmTcZ~A4b!HV`1Ab9 z#pQ+@^>Pn{gMXNN*$veZzQn=2hB(h1A2aeQ39UC~{yULW{S|5hmT`Syj-$Ss8c5Wn znub`RZ|DD`twr3Q%Dsq6Xute>{i!S@*$)q!ii$=KL+j`}y3MU8{Q43g{#9*1EiA)q z^@wKGQW2QyeB}EuW!D26oy1~l&}ESAKK4TZl0P1ZGIQ11GrWRe8&x$c5E~;rz@LQf zFFs#;p{I?beU3z2=pW3rib(;<_$pWmExxch$Tazg!UQ>Xxeu+sQ&9A5|(Ey+*M{#LxYpBGXasIZde3#@98zu{*{z(&q*K`9XFrt5G$Qp!j4o>AV*Nj*Bw_xV5a3DAw==wuW?S3GnR zgd3Z9UD{MSaFm>3o~9lg;viQ%5*L?c7@g&3bCpfb6S5I?(hbwwFXG039y$c9&S4d_ zO*<52uVAZge;o@%JM(@DE?Z87ck)5cgx;iR=`@0i&p3<0Y2-QL?BCaw);#aUwz++- z)oX>6Ajg;tP8m5k$xBMGAAokeJ6u&5N&(7`=-2^WI@EVNHvZ{i+cLSfnSI>v%-IAd zH>qeOP)C9j9I#brqJc>vSziv?~r2;#YkBxBG-*(3!7hSLyX}R9nkPnD{1$B?c%OX=q)|xrQ~b4ayM5h zC?hd?RQ^j1J499x>J;c8H+@NUsq$V%YEnVhb8|2i6JhfaPkoBL&7S&Md@~q;?p{SI zfEUgCQ}+!1y(egs6cai=Q|=x{e-p|a(7cwtyQucZSZ!01qf`QwW+`Xd`Wjs+T{Bwg zpa*2o?J43W9ffg-W}B%M+4Z|K4JIEIrA^U zEAtTvpNz@~6xW#NH_Q$iPM}DjR#&9iT?vKMW3I}?b z=+GSqd)Woto$bl6sB?YR{LgZD#oxXlXt?ycSdxKZ`QOZ2lMjQd-Fmqvyz6cjXG%LxJDRbxE+rr#U`FXF}tn*W^Hbl zU0Sz(Zc3Ak@k^2HdhfjGKlc_DQ5vBPoF5>jgtEduWfjw!J^wxw3j99yk zu!92i!_QQ`?+x~$?&sU~^4%4y+x`w`ltsSUpc)A?`+VD|ihTUl9_&(^Ou z84u?s+iQVkyY_-%*4Lh<``ahkCLd*0uGnJ!Na z9D{ZsmUD_A-Q5N&%l6S)`R@cxEFS2xbZ51%E9blv;(q1j7c7&4;Z2CI>3~WH)`~hb zxf1Fbv)^cR;Nt6E?;yp15wF8(i6*a6`k5DqU+M6{`TocaBaKyk` z3winE6^+rLtOr({k|Cwn!77g_wGDdK7UT7QzKZelwS!n$dhEOmOpEy~XWWhSr)NG0 zUkp^e@#Kd7<$uooy2({9@Y$#H2FHWGm$Jeh!V-LoZEE`Pao(uC>d`c1w0XOJXLG&fR)6U8xH?7_s4PaGauMYUHQ^ zc+w`-!@?y4$n2ujUk4Dv8p3whz8&nLo5B!!PBENXZlUvy=`BPrT`gA9)RDK%jDq)! zV(Mef;Bu$4;H3MyW1i)1jg!EHYPgz4F0d0EBujxW2^lvmr0o6I**bFD%{1wd!Ex0jle zXx|WY0Q$F7Inm4Cb2`93pn*Z2i!pg0k(X0NW9QX5)&P#6*I_Izb^2Uw8w10)EsB^N zXub!_>daV5cCR`9eZy&A*++q};dPV_wICoxQBQN62_6i-1jO1==(!~fQb(sxO#+)7 z7{(CTrMpSdeM4IAu4&|zX*<4q)-73EH8?(Zrs*bpjmWVO%kQ-K?aLjWQAtq38V0m! zi+lcW*$X4c;hrStg{s!!cw(dLo_yKI-#GPx{bui_woqckQRBniAUmofZ6#w0xLWrfO^L5-tf@2et}66#YAE^-<G_UE z!)m6E^-I+W*O|E3%AhtFz6I(<>I=O1vRFoWUH0S;MD&p4D$m?etWk8F3SZ^4xds1| zdo`DmOgN@CE;yslepCdL>i;?G98Ppw^xQdZ`$LC^^NAgA)5*sd%@w&iPn_32OL$^; zFVFfT^km)y4rwFx@9Ct5A40M4z%!rbRH@W1N{GqAIY4fcKv2zvb>=3HFt(bt|U=95PC+hY zKxKbOmM0y(>`wjt9$RU^ya6E=tem%&lE^xP>eTkymnQH{($A{%TUXg4{(rcM^W9Jm zOg=ijj$Hst;Dh@+o6B+fkMT9jWu5)th-Bhc&X%cY-`!{C4SBa62Yzi-Ur1$lsFXJs z?4ABW%&iym#jABXC`i{u6sj$~*sM=_g+7BS zKUF1N4IzNL90}Hf~gR2?t#oSLVmXA)R&!M_zY4N-) ziBSa(x6gS~M-GBZbR1M$dN)XmArjmglpO+(vV;nwe4%WTf}ME+*NRDQjX6f-@E7(g zl}b!DiS=xkals;Bf2yEHk)7XAWTkzP?43ZLeFbw1D;q<5k8v0gCp|lgyQ)JLkB^O4 z!a?-7vo`KGAjwv}l^0PODihjem#Ug=1DjxT@sr>n@MncQ z1J~}(D?`OBiw^>xWKGdrBrG}W=xkv8@CzI|9L(1&3Li>BlxYZW$PG!e2V7?n#lXK= z0nYyUqBs6OmEn^!n7PU*FSpF_q+XzoyCo4VBdzpai)!Y0;TAgB(6AS7|lLb3qO}RF(y`ZC>>Bgf> z1*B>RmZcktKE&+le7RpAliQf+6Q#mNo9-b2Y=gsoCE|3U)Z{9FcWEE2Mg9e!9X&DT zRh?Vn(uz}!olDBc$@*tU^8nH$6;0D`Tar<}7YhKE68ksA6i3b4rcO^NUQVC0v7}d% z4f3KCbHxL3AZ%Q~cxiTecl{V+dLikajM~2RH~T$!`eMhs0jkyaY5$~U^T+rnt}~fH zOafR8w6fG^h6|HQ*4W?pC(-kF?70OTA<7o|6LTTg^snfhz88Tm#c1GvC$OKFHZ0p4 zg05v%+w)W58PLR$00_~)>3!jy=XbYfI)%5;BD`1DhMUA5E+D;3mJj0IG@3n>3VChi`>EZZt1DofUFWX~gIN?W+tpP)ZMU9aMa-9bNcZ29Imp z5DfEY;ts=s3#{O7P5v=o%&CjcHCs&f6HQ6kJ2t{xZO+*O$WwWhY4`@1Wm7N$ug&?8`(4^8(XR2G&{18ZGzZKjNV98!NZx_Bnz0QQY z0Y>r}A>^h?jIFWbfD-ZbQ{>cFfm8&UPAWGPA7_O~1s&KKAIopgw3m94EW(lx-RR8a z%ED4odUo;Yuv?%0J3$=qZ(p+NQ2aqY?-Dv+Ox+3e3tgDW?Pi1eVgv?xN-PKz9{v%N zlo+mgEKkfYg^}GJp@RbxZ4Oh;_`Nkt)gY`Vtzi_utNXf3Z`U1CvRiDX)W;J9e>TFsHTcDRf=6$!7USEMZd|kmAuQkq^}aMErKvBf zi;cd~3Q?Ov^ggCrWv3GkqR4M+WSdqQBE1o<)9OOeII8>y%W+svNv@{H(zdhYpmc(k zv{67Q{5&)DTCVz@`zP!5I7sGk5w1|qTRN-|S>*P-C^4m~vON#K$p)olp2TGQf-ghh z>c4V+g}ina02@aGT|Pl?s77;J5$jzu@?gQaJ9i9Qnj|mGd_JI*$8ZWxCsNTSEO4Gb zKGQ)7Jao_tAzY2^6Amt>bER7H{(g=TL=$AqK4j(iK#DBf927OmgGJbPfWLMNS^<{@fI((xFB* zt!0X1dpFhSUy>`>*v4?v>%5e9b_6!y0I3^x>hVItFkfDX4eCk0Ix%T^aru0qWbL5G zcHPI#>Xl&5fn_%WR>5D~>t=K)? zl?)(Mlc`5(rXnlvOP+s9BJZ|2wJmgwy`qF&wPn?W5oB#|l~j5~UvRT?6+Ap?eU0wD z9r)KP8ny;42V|K;Ga0%KfxU_4pn(Vo35CU3{dN}-Orx$vOb)u8Z-?l&oCB$@C5|an z^=Zd>>^>(Xbauh4iqii`3i1ghr@51R`k>GYfqZE`-*#9+EKv=N)t>G7 z;!;!>kJG8+v*SK553??rop&~#-j0-c7KGjiYX~*FaA>o>Zu4ZEv^X4JS8PHl^!OgU z(nh%rdvVtA_OuVOFDg?Ec%+7QkPfj~Na=MIz(udsw%+|BlfPL7j|y5{P^x`ju0~k( zJ3jQX{FsT%dY0+GlaDy-w>&qc+N~vn*wYn#FgonjKC}>ix_j7p?F}((Jm*;}=MRJq zJhy*1Z{$J0brc+#$z)$qi_kqftqn17RK&1hi zZR?gyXR_<4LpYAKX^XGn#EB~M5YtRFB0EJbOVCp<--w~5E>+v>RwR%G0bJ)CJV;K_ zGjlTLPdC!c`?s7hG~cbUnOCro@i|_luzN?dJGbYPUV0&*Lyrhb)YCnX{tf`iK}UoR z(yL0@L}dCr`yXGcrsb0Ry&1JbHVPsJ&X8c&aEQ95i9JMC%T^8LzyX{!3B<2#6NJo1P zG^?gf2$pG}mS1d|HRugJVkb-+&(&i1=LhN}Dwm4fKLy63P2YnvE?EL?lj)A>X_8`M zNy~i{28qBQ)C;nys2f}hxgt)DE^3q9{aa3R%T_BFuIl`S|K#ZxW&9+l4QT2kBR;xC z5JFic{kfUm0YE{mabtm1J!DW;_#Kt5ap9tgOqDsK7w(+aBl#HYgm>(>n`3$l ze|aE4UGMKG(EIfwD>r|fHGi8NEWYmWu$8mFkCQ7-@AE<0E-Os?TULa~itk0vla1Gk znbGR5`iKT~^yd>msiP|uToUj1b2VKbZ1?Cv#xA}B3f3sS_hQP}Dy|qlzvvgMQeyrj zyR-Ggl?vogzJ>ey_%9Cyicd@(6IK-HxM29*=?e58v<}Ui#+ArI-R8~ zk+b1d{bn(w=WaDwBMGH;cKieJ$wU#?0IjZ%hMBedArv?n{Z!cO+1D`DN`adFQ)Zn! zoum`jMx`>xvDSokm5D_k(rFVdiLqd^PT;9A#aXFx%-t~cSn3NY=MYEJV$snG9Y!oE z2UnD!ea;}VnwUU_%iWa7Ql;{bXBE4rD!J!hn61CDAd#L?WK0{oWxZ1b#`}4R<0oHk zwdWO9sX(-E3kpF$@;_{Jy6Jjt_V-tz&z5ckbwxe_SE04W;HOuR3Mq>x8=WsS^2l4z ze~876GUt?JbYp9t8tk@e=u;H-)%z-<6DP*^M0wev=pj{H#uLwgW{tMi#eb>|3_r=W zz79w=8nHVw70J#ZOa(3GUasxcX}_qiu|=*vTh*`lX|k>|(QY-Ig*qs-1?Maqt&y8y zkk%8yVXlg6lnwW##;F}G&$3o=oGki*wfdRKeyD*nq*agmuPZ+ZuZl$wl(=~W0li=? z10%#HIII)SaL;zwM1q6Yzpj4j-{Vx5`k}X^uVkP3Q!0OAnrG#T(QuLBO}Q+`p?A<7 zwR-k7b1cr3W_VEO>6GD~a|t%4hqxn-kKD<@E%cfywiw?ysBfs{V|Ur{W&B3@;`%^1 zoFhca>++bC>Ab7XZL-D?Y_wX9Z~7ZbV_&1@vLAfv%n!M{qBX8<<~xBZ+k+>v>QpuZp@1v%j z%52i3?kJCIB!mQ~jM^*1iBMOqT0N?lu*DeHZ_MS_a_kX7CEr6*D)r^~NSif4jea(X z7=tRVJQe1&27X-qs~q?J)A(@tTlf4^@F1p~?ugp*PRQ?gY-u=9Ex8l!B6^FGmBpuS zFIN@oInWBvjG%D>4Lbijv9%{LL`3KZYtX;|W4GZn%z1_?iGzuQ?5lJ99I)EcAgppq zuz|z6;r(!3p1ib1`46*2^-m(6*Afmziorep#OFDM2@JN;?sQl3m>2YY*yDUKWHuuA zO56j{3K##Z|4wj@z7DTgVkJfkYBlQqy>5}a)6cSu*?lU&zw#!H^&{LVj&h~RT16BL@2C-JT#g_Lj&wtQZn$Vf9H z^%F^`pA-c)Qe3s^Ju4Ae zZd3@#_zL5-epi4mPV~PM&YQV4_ThQetJBoDd}i#Q;`lMch#;@ekqEFok5Ic5( zOIy;QNF!z4BCK*Y)g~`XVwDnSXP)Z>(}h=@n++Y++!N5${IwI(y{mM1Ti)M_@j0Oh z+EzS0yMVp)Wdg0vUApJH;iCXOdes%0Dh1;OLq*v*mxIy8mszbRHH z3U@QW-py5SLeJOL**k3n{6@kd5ivIL;kLE81mcpPKMXk{`aJ$~X{ib!(3;b;CWwv6 zPk1?==8=?W%4+R29{3PY_NZ?xvKIU}NbVQAq;Q4~e1TzL82fSxZV-0MK$Ac2EqOq| zB`v`1I2i|zuZhyt)sx5wpp=Te#i%-7o^`mNc)JF+tfIN=KYi(eKf@}++D&C9W+ght zr1!6POJmu&(z^BIi(oY!*&R&eZzn0&*c@%Htq>yHs2hJz=}T*t&^qLYmFqjBa4@Lu ziY0RmC;LXDc3xM5rs-3M8gkDEM^>H-_gWO zf{HXc28kDCx;Gq~>>AYaLG|uci4{v)q?EfISFuQekB@r^wm$~Box6s>lBu$dq;#5F z7sH4hGl{Q|QBdT$ZhR}tKsWUCuWXjc`1dRTaSXL~o-ZcPv$L0D8*k=G0%grsCSjJ^pnsOwBx-bH z9HJ&{o^{U?Mr`J=*ic%hs?I%D^MokDiEQ+AdQ(YMElH3B`Ah!u_zmRM3PW z&wfiMxvfu1a2=#1DfVvuAna=C9X11Kk1Z2l-0ywCv{!in{w%8GtvA3gA-s%YWa@kp zka47Mg=SmqsbfDLT3-|q;YF6;bht*%{mxvYQI=?Zx8NCAt_;KLBseRb(8R=m4AZGOZUqL_}K2yYV5Ebil7$(E$RySR{95mOK(A&ZuOYT#9 z|A{jywF5;;DUJ&5L=I$|3T-Yh>+s1}XYGaKU8X!Ae6FP@NKn^j@L*sY!!BI?TH)h4 zPoo?h@7L9F`z!3z4*+p=YRiZ6M$hOr7jl2gpIpQ43tn0r@F@F>`e7|8jA$-aPm$JD z_4}5pw^HklE6T~>Vr}zOYNC}}R?0bMc-cW`AR#71(43vcC`5%3klEj{W`j%?n^&?1 z-fx76L6%79%<<=!}}<3>;n^@pQ+SuT}KEw#1^Iz4qD7{Rzehcuj)s7U{zR(!Mx73{qY?;SofwJW{ePM`fu1JZWhfPDP zeW}0-8N;&DlUs*b5n_&|Asy8o`+x5&_wg}$%Sh=%K|lJjxS>Z+UT>)TP_a60-EkV4 z6UB!4?;u6ksp!EJGk!YfVEkNEU`8=*(Z(XRCaCSLnBD82Q#c&kIjA-n)hwzQBsC*r zm1wd_EWLwk%Qzivb7Sq%lNA*M>S5C(!-n|g!ZHc`iyw@>?N5s9NTF=OZJnY(f5_8A z(Cd&m#Nu|hqDg?!>YwRo@~)!wp$JyH~`wS@UoNYk$w3(acIEE zKMQHiIns^4?I}M$3946Xnfj0%Rski@kFxyR?ebeWb2Xt{c}Ta+{ zJn6wnC)C*1LvA(L;xrtY9|$=e_DC z*kGN7wx-*rp6vYiv~psrJ7LxnA8{&bM7+wO4*u{)?4QN4scD9AqfVP@yj0*pI;2*$&0)k@PhH1#ifJDvO76gT*6B|HOe$hwKdHGnLA*Z7Mxx&rWE{>&5XY5p7kEE1vql!> zA}K{Cyux<0e_c!7hPgsQUYrpTmBO*m&c7-~0PdJDUSi>du=&Ud(@9L09-rENHMt$` z?vqgvO7-%Ju|Uo~ibw$v!x~x&Ws@B`vt54%=POLbN$JXxY2Zc04FGN`_W%ga64>#Xg#n!jjy+w!b4;PJejtJJH=F zDDWs38$uo`JAt}inN#&T&sPd;5jH2)E6^AismOjox~$mh!I5f%&w({5sxYzd7Y%tw zam7X8>?=c4Y%FP-E)*D;$Z-vmWP74PWimngOq5DGL9Jd@qIcw@+9=L8&(on)HYFjzR3ogRmIfpdo+T-aDd3?B(B6M?k5VH1JEHXq1AqHd;--~X* z@#_(}c^OnEjj#>kd~i=zz*iB}aE94FNEB?8vA#JYR#K74_Q)5^sbkNFZ0w!>Ra$Vri!a?(z70Ks{HQ6?4$s9}OL+fR86h6O{!b$oo zHpsg2`dNxmZ23n#Zt67157GC>0) z*&meoYjmb+w{Xjt+251I zEOr1TDp|*e5b+^9s8ldM+c@Hp*xNUGIYGs_UioGacIj=-ay6IR7k4k~j|qeM{&;M} zi>eZoOk|}7Yn(U5`Wo8{-Q8vnEfxHFL6LtTUhY1l($=Xy zJj&#d+PuGFuRn46qI{o<1tl1mTeUy7n-NQwgm%SG_vGgvrb)jpS1}cuPXO9lx2q4g zXNp>#=1f6#esiuaVN)Lqdv{*Zb0Yx5*b-u9^a-*N6HVZApb`)VWlCkXK*vZ(=dnGC@GHrX;> zD%JL${zA~P)NcwrD6=SvQJBz;_;BDeW)fU~>x=xl>S67FC+a#>dg+cBo0L)Cq^0A2 zgfi7vo-sHrp5CnH~uTR7<9Jr`Z6VX zKN*$$kymLnY-AZ!%T0^JjH@RegKiUt{w1!BvJk+Xot;(5fNXxronlpPxUqjA+iq4Q zSHrH(^hT_Ic-BKBC>{@oZixHyqW3t5plGhbr<#W5n3B$d1Cr(y-#1HAYnRw}QvmXjn#U(BI2WcVrQyFxn!r7G-M+WTq&Y zBN|q!`t=L5i>^v>$0F;)rgxawN9LqsEL|PwHI)c4eA1j)d7=rOW7h_QB8*n zFtX78>C`iu^Dta^VECWFwR>}>1G*3V2P*YfziE@Ed<-@&chqmucBO4sm)4fhn)}BjN}5tK5@RQ*Wjcr z{uz6o9ar2P#!H#=WrZql>mWTH&Ae52JJ=kqm=pU_`BL-*Pd<-FusVJ4!{)B(cxwP! zTVCnf^qO*_{qm&@cK42}7n21fwXEU0Q#x&{AAoBdbfUZ&QqOb)nJx45{qszye~-`_ z&gxxzWDcrW{jEy8Yzbkwd|zSj^!FU^rBabh0>-D#y+~`A>Kio8>Q%f-5RFJif6Ai3 z&7vaCBPSDq(0*%hQpde$nz49>2`CilWGfvXqO63yRP8s63L}9wGm}8@mk819> z#lzp1(7|b)*^2pF`+4Kfvet((F0+`c^R%YIHY#WzAVo2Hxr)!_7Pqth@==v6r|uzt zD)#V&1k(*#p=bkHj{fR>QSWrUKeLQ1@l~Fr7C*u|>+*|ocj#^bC0CaOK`M*s|Js*o z=Teok$5*aZXvZ4mRM>GW{`=TE3DZd;y)@4SHDB-;JmHbTDZ%jpu$o~a_kib}iX}5m z^1{?f-x!TC1%-;j2>u>oRF8z6?S|F625ECwH#_f$ljiDGqWv34yC2*ND{YIfujOoc z5!#F9YS1EllJ;jxzOIcIwFtH$Xmw^mOmW=|w&%FfQU%e%6HKK~^OO>414et&9GfU4eO0rq}WVNvoaan{)JbCoSm?1$@;|lb~8+MiRmXAXJ-(v8&$N8SD-(FHy{k%-+HM`8iEZD!q>^fKB5uEW? zor^5a2jf=tXP@6k*r{$`Jw3!-B-AX>m%5dW3CFgc`!0keW}U1vs5#~U@<*z-t71Jx z^h~d26rUafwr5zr;1tlD8n3$@)l@1ve5HME;j5tvGhjqu&GUS#I9KuxP;7ZMH(Rj4 zZM}@A+0RkuZuua|eh(X`4mQ4Vh-F5OJ~3CuohTD+u#G;q`YJstn14VMFFK4#K zKY2k{7{0R2x7`W525Cfi>8%O-Y>hgOTS&a9`!AwT!M|x4&vnBfHq* zo!op9%&PaYL+0xB)atuIFcX&~82~f@%rW*oP5X2eMDiXzmuGgA80;SxwLw3f5aPkW zap9mW%#p4i-xF6v*~n~yjAPEpj$cJY4umFH?=yb_WqP@ZmhJ*EpO*VJ6Cu=nHjqYn zWc;>Ry7Mez24*!;89F?o=0k?aiNof}uO$7!Ie-UwrHcoCo;y*G;As1poJ)(Qise1# z-$t1S^?t*9T0suF#rpP)B$Rnp9a*(EGc`NDH*n;iND$JfNtKF4%Eq zp<-h)bA6RY84G{$64Oj>r~=;3ce>;zgeW@S-}{66rXZZ)k*)2i=5PnqlQ*y*R68gk zrkCH!r?ugGlrHbp=ouB3Tqe%|N~PuT^+`eIpw{o(Z5(f%9%WJ#R*{OJI`HRN+@WYRbezT#(ar*y*Mh<$9t0e(uZJD%WH|)f# z<-WHfA4ibab!QQjz@S8{^7RQ^AI2{3v>z#13H+JwBAEip+EMK#CWn^Y=Uc^f6?TrX zT<2loPGsgu4UEDAjB3fs8Ns>VwRq&8m-5(pn-7=KPv7Ctc6!!oi?O_am>v)A<3ZhEvi@f)dnlaKfCI1Yy=hC2|eokY|8q zCcAsG<+2-rHvG=^RdYa>)T~dws!k1bu5Fhr4T_>=dbil|*j#q{(?e0^a`7rylzDkp zM*N-a(CY=cYV^?xjoHuf(uFQ4V{bx=Zs?9%NP-iK6G5DDOyqf}iTyo2elW0HY$W}s zPc;DUxGLV*kGqza;izbDJd%V9vtcU z7>utUl#rR#V&1bqB``%8?a zU~?@)W=DRyD<=L@+bAJY=Y%JC_!B_w|kg@^?`bb=Ua=> zs{iDYHGEXF4R-xV@dGOMMO?EP5qMaYOjxde5acrP7-#H#tLWHt%7E;(FEEW7c^@{Z zxX#Zp3Y97q`@P8!39R>Eh=l2^Bw}+red)HgT}6Xg8z=8F8u$+D6I*hU^qj|6f$a~Noy62Dw=1z9NwuVR_v+}I8x}$E zHF|`oLPK-RdFj&JM!({ZCDK5z4*39B52FF>VQv?Q!a1xdxmY#4^*eN2Wa(>+l92s< z7|x4^zahys!pXw+nVaLs;U>%%FFZrMCj~$kYpW$-g9Xt@n^9t_JfxnL`ljCnDdDUq zwV{@>e_GVXQb`V+q%P?(N0V5pf&Pq!i+l*rz>bJG^Rv?eA<)g*r}A(!FRUVH|GH1o z0uEX;k-gM*V8-%~)JB{BTesoTF~un@^~Tw~`8MI?EtswV%V=sdjHe&0W=i&AvitF67&h&{T5AW^$^)r>u3YfHzc zMpR-{>_)_1u@yy)P*h?jR8(w1Rqguz^7{jFl5^hgbMid*{d!$jgo6(dD!7_bj!s9D z$3%gWZ?4{Z@ZXiRs~3TvgfAIC9~%1lM)Co_Cl)g_WmxjJd&U2)YFUko>%MoysR*R8 z7SC1QVY{;K$@-&NZ0?I-XBDL4i5D5<+`t*zWYG(GX2<^uJPLRo`4cp-<@;Aw@W+nPUsnXr-dH!#R?`-1 zlkz`?LU?U@i2OiJcUfpeJ6{d;j|U!Zqpsg;&#l!zPisYS|reN?+hSEe;Ue@JY0}C;ROXx!kf0pJ<83M zH@644j(NrGwLHyYEAE_V*A4D8OT5Pe1H!mGo?&1GyHOLr`}CR?aG4+sMBsJLHBo`~ z95vGNkV1LB+FV8V+WN1O=7z!QYg;5(HA?`pkk#FyE|=NLVf1rG4)3tirfXwS%x zTudngc+~#A<&CM!3GnaJv+ms*9d0{1Cw|IyCiGFi1XzizGyT-4(Zi0%gekq zeGl6mFvyx*9(@z@Koh8{Ff;jD5}tf~#9z%|r&PtaeWWzQmNc7gWFkd!g>j`x-IXX2 zQW!k6efM=eN(RK+fIo8j8s^imlfa(?mXkMG`bM^nWsPXR7gmUxHZ4Y~Nr{I*vP*8h zKYyp^)6k@1Wx`f+EWz&{<9ziMnj>|66nvTp=l#zzq8lPbdLy<>u0Mp;Bt+BBA-#|Hw@WlSWO&d+M^CGyI)-w1WBCU>thqNK;CZn019eS*wtI&K}g?zLD}+*l+rZ_ zJU%Y}hu3_7v^0ibb?YaCcj;_qG0MgtYn{mjCV^}&{n4)z?qtJ>y*VjO1C&x(Fq90VDGLq6M z_qOVi?JjXhX{nK2XO0Ui!>#yCTam^IX7Nr8D#?esf6(xKZ=Uv7FObe6q2B5U56={d zxL5t2Amw4Trm*3(-;_V9GY_6THWw-FEYSBn-YXceZFW+)OD*r#cy2SDge>wd>9!kx01BYml^vaJOXS1EJ zg1@mdXO}{~r4i6Scq|VlczIXfEVHrqF~@R1kB0uyqR2RF8tBQZ0r~!ONbpc5IK%^k zLBA0{@0_YYq*_3%aF`~9A{W|c7{FUq$k^_PGs=|}@coW_;1NbI&`#=T?Lt5@>ap(J z%jXx{hpzjlDks=c4-FJ%{tjuhIQ4wl;kPXKSm1W60{114P(=@}b#EAdeYP%$4*GWN zwVZSyL)7{B(w%WBsXuc(Lx!?%lklcaOm!NeBG?Ho{bwk>)rG895AP-|GQCJ56l$BV zXHA4QQ%9ZNYQtLhqJs^fM@(E(ua0l83s{uU1n#NvP9te!f|#vcQ#8bH@~L1HZzV2* zTymC=UwqmyU!aBV({?oPB1Ple{MKJd{*Pf@U)x`WjBZjVEJwSi^*n{{-CWJL9-kI6 zkOt?XKVZS@N&Slf8MF)Q+rOz31W4w;0>Izf37wL(zYFb`*2EDts)66PgUW)NcY{u< zwnt4xEUs=M1!M8&Cd0P_&3Da5Ple(IjX@G@HzA^B?MvO+1`<(mtl-W=2@L*ABn=>e zq0+y-FR*ik_U1G4EZ?(Wr?~4f4swdRM3^w&cF3*RFj;B)pw{5cn-0r-wg2+nBKsyd zN_m#^nmKmu<#%WBPH4=rM_!q{?Dawn&-qH^YPQt+)PCgL9+;pksa3}4?0UyyK1IK= ziVm3TlMbxVmE|a^&@yOq=w#_jZ5E*OD-djcte$-7Vk38id4pdBG+BA%#~`3x|6_>o zyz{VkD;NU5zp~(CpK7GZnEVgF24n+_7pUUeJimuUWk&M9sEc`<+f$ZPDnlB} zwL0r$Os%;EG4Puu1dXL1aO4$_453MZW}A>^OZVj?tG3w*2wBme-F!&>ds z5+VrasArmTDCLJPNA7qap(JYQ@Yk+iO)c6(wu&C%eBOD=4Q=o0Eo`b|`7?QzNzpN9 z88V&PN2{q((@fWkSX29St5>!|DDaXt+ik4HYU=Z<7*PJX9^y}nUtm)_b4Wm<3Q>ta{60-3h zHzA&yq8{s+wpG6vX$d8!o@&Xe|2joBXz(TTwNJ-i7jt*^`+Yv*(ofzoZK9KTXHKcq z<6?OIz`VaE%b#Hd-S54nNsZK>4l3=)Gjyu29nC05xV=>4ajT9zIgbES~b%+E-S5up`Nvg$!2AHF4JFTKPC6R7V%jNA*G-Vt0Z_=1TLJm&KS#bhZGp` z*_bgV!xw&zYx`hVBPp|$Si4kmPi=KWT1z`rpnb0S*VyGk18xvcW!Cg(@CG*RzTh7a$KC1(-amRy&|TgG!_E)i1|h_hR^ z0)=$?u1|*N_*y;eUyXP26eenM0&|5M*U2j}I>z@V?sGVJc0B@<68q#dtKb!JKB4Ss z0U=LZYQ6CUOCymBwj};)rZ96nx0oB@4En=ScLXU+brVh z`}0~Fofs6CGc~3YRaWY?jJ;O=Ji6Xw{Hy1FE_R+>J3|n9q#=uN__hEuB<*_Efz{?8 z1f9Uaq`jy%hlcei%Br@X)i{yEJjh7?-)EUG^>zzP@aP=heIsI7Zm_}BIBFgLwTNl{ zEGJXniX$jK7%Ea8A5vpD{7B{tDfw5USJuoqv3O5IEbbCOAH3wJl$VuESmSBMPZFY+!%i+}Y`Br3Yk4ZmxYBA}(MJr2 zZBGu2&#jnD2J38_w0~0luLask1Z(2h*1mYXXuRfG!_Hr(YpQ2_N?#65ytU#jOumIR z@CnGv{?G_~usLoMCJLnw$Dd+M-Nn}UM89%}r-#C`@FR8RyNo1Dc>w3^K3b)M;CqOJoS#~(_ODPuwDIsW)fCnNcld5fZS zK5I`d>&;gG5$YgjCGmdwIKlQ-n2@PvHT$O^P)A2Ji)e94-XA zv<`vXgMSyWY5Ev^ZZGT`fORHvlU^FH>ny!p2 zITH||jjKfg{!^W4Sd*Qi$Hp;e>-UZLz?&ns#cWUew@OJViJ)6?jFoJ~tujE%?1oA< z$zzE=ks#}`#BV=)S}OgHGzV^jRAJ95GT}$NWzy~j1#2Xe^;B+O;(nAaUEOp6A>Kh0on)P5yJnU-@3pNb)ub@U3!uh)f6-Oj+wgcV=x(R zJYE_<(bS@5{Xrkdw~PoKV#5w zHf1-XQL7CX^sDNB1F2Pd9xl9o9Skk%d+<-f<&aj27TKqDw!Q7>5)_73LQe8cHSyj` zvg*6x+1Qlb4x(j;I0zG0Kh{`2FNW)Zn=#ZmPsyGy(Ey&3>Op1Brh?bag4sN@{60?!e~{@FlFd&}|Br`YZV{vebeiC1)f@narr0YB~uFB%AEVx{DbhboIb*X;pd4`Oh3eZ{Gp(WY{Mz)JCB7 z-gO8pm}4(*H~Rj0Tor`(FBO?qKWH-s3!E1O`;JZ9@|Q%l+3?XJF7&wvsufTGdAZFD0~NO%~1kr*Rs=WFkxNhz-XPAXToUfQbuvmaOu1ALv*; zjfQB9zAO6Iz(X^Xz%as2V#uIqhl(T!AKBxik zf(X$1pukfw9gnn43cb~|G}rFmb%s?a{@niJz-I#+dXOx;u5&sA)J48C&f@T1C!tfV zCQAr*24WMcD8xc-s!ganA|tlnj)osG82^?2tE{i#AAc`ODYo@xZK{*W#!GL@ED2us z5Qq#=$psR#{y&Dlyah&e6HBTKpX)aXzbszD$nxk*-Fl6GYS%*~H9K;K^E8IrkFE*0 z@aj4?$=^x}D+yqa*jBPy&p~dtu^FU<@hXng7&xsjgRp13_}A?n&E3R1Z!Ld7`|Eqn zL$g0fRWC=y2u&v*OlC)*XW~1<-DozdQLYol{AD8bJ?KO0WYb$n(r1~#p^~P?wbiU^ zsS8(NQTI5CHXG8@YrL*=6s5^{zh}2~_SuU%&&jVbkQk>X6X8FsGf186gmg8qmB1YT z_$pl?9x!xu)Doiw{tOog$#uwUpa+6DC4(9%d+wzjax*rc72i0vJUoXGd*Olpdj2g2 zsn^nv=~&2g7t+tl()9!utKZ9+&&o&!YWW~%KY4}O>H?9FGB1&5ziYIdC7>^oGx~_- ztSyB8IiZDwH&Mx-rhTmAHGy5elRwlYdNgGBOna4O>jPR@Jvtnf*{+8>ZNoJN+I1PC`&soWgKj&QOd(fPhZ zcBzcm4*+A+*`-X4UWtHvf{PiGf!D=!Hx#fW@0(zn7kll7*5auRBNshn#nW-DbHDG( zH79mc0kM@V6te?{J58q3*EOl-a6mxheUfFWJg}~r+->WxICTVcPkU@-I50UP2J%|f zD_vJ_xF02!4FdwYWWU=5P`}**J2TOZ4b-x(cz8MJ@j2ejPXhKr`Mqa2QfD{B%>0}I ztGY57$EppX9SMClcdXV%7fh!Y&Sg%E38rfyoyS-5uPj*DKNg^AnQRpA#g5@Gy_1vJ z9G`Brx|?Kr=R~6_wp8uj&Wzg-s-$JtNX1Ce-kZ!e=w#x3&GIZaSID3mg0#-HQ!4Oy z*GTOfVmYl@Y9I|B9w}<4W$}4(s_vYyMTYL!bcqLp-r5>9wzGUk2ec1%mx>l=Rizhi z+I#AiEIWR)URJ4TpwB|JZgsp5es~LV*xh`2Qsj;mpjdHme)~TS2#f5ZY?&zzzm5o? zeyO8!o?~5}s1OIwEy$4HalBzgg7KGPPt% z6{h(qzaeRvaxD2Zjj2?BpYrn1PvbSrx+}>h{Nh9H72jwh`I=?MA!!zs*R^aYV2q&R zv;u;a{#xG0_m#BVC$tsq(1>_0?WLZ*dn3xv&f(-dKW4}$pLCheRHZp#8TG2Xcu`MmVrf8PUQq89-F2#6&<<(UbR9vmYFV`8Eu{*Ox2HBn z_tq?3<^{@4#|=-gcKRBOE(uA27ecV+E}StE{w>nPh2bN5EVBHG^VpvJe7z~?4zz07J{ z2S5MQ6@OOI6q!>J%Z|g~Q$@3UXioHNF3|1;`NgkIs=5b8bb6475(wK3xQaX@IHv_lR_ps^VyK)tULezcF zHsi_54O}xoWWD8#9R+#G8~~U5Wbavn{ZYP7c%B;DY;sr$6$t9ya8Mp!aQIZNrEfk{ zUV&5Dp(HdwyPL-na*XXL<#%K6Qakw~G>VJr^$K6EQQO0a#B zq(N_WU^50pwB1G)^(MxpI^k? z9?B{HawRmK?1x3O6C2EuwwkF{Y9lusN6^yihk|uIdARpx8R0DozDyegbf&|QB|2=a z7rfIw3VyWnMDPWrD`+*}X?g@*V3DUUy`0aT(G2r`xZegZa_zl*M&Gc}<^yd#_PRA+ zLRX=vG}aQTXFTM;QY${gzO8k>E*EpU@}nB8-co@R_z2>E^X1+f8;|cpD*I1Ck(X0M z`AzPDP^OR29_LxX)hx+C`7PQNPp?G+rtZY_?Ptn&dI%!wcP~jcR?Bi~-glF17xQ_s zQc{kjS&Xp4r>Ock7P0t6HjO%(OuKek)aF}pn;Ilk46Sb^q*?*KyW^Mq`_28xN|&EJ zxq0)#O+G{PGy(MFL^0gp`#J(y63e4$>_yT7I`LLTYV;j5gWtf2lvOF28u8C_tKmyP zY*M3mPwPPpDp_v&Bhu_GsvB_niH`;)a&i=WZOWr161k zB{73TQi+^+&MAPbw{D7zodKOAj17sVt1@ryG>47~PO5PoX}O?Gsr`mOfzubvGa6FH z-ncwG?3=gHq?YV_tQ2i!qCPb?VU$lE^6Fx=tJTmibA#r8sXr?O5hO~H==K+WxV|WnWjV!0yoBfwskE-i&6V*s2>$^*V~jz@ zBaPsR^pOHA7ZIO$#MrujBwXaJ7osg_eW&!GtRz+-7z$X<^-KYgoeXvLEE0o2CG_JI zHTy*N6PICNOiTv{F|HG19`Dv!n)W@iUhTCuxdDLf~16|C5 z7B`BWOg&DOsj~&5Nqgv=oPhr^gd}_DnHL0n;Gk@IuBDEecSA%j|mC%=d(zEoo+vg zqKnW;#d(8yX<%u#0d*&U*OW=w66Svl+k7CEpKzT?oj~AH6LYIVLM8vZ>5@PTn$7BX zW5(2x8LMp-br5T~8e4sKdk{6LYDH0YRnEYQ!sX3<_aCXAI`gCm7jrn4$$2ms>^EC*RC0U399qw9SpF@wJ@&By&YHbbb z(~#)ZQ9pqnS-cEKA`nPBs?(igsi#~%$P}nZiNH|R(H``1tHA2`aL2(==S9tdCi@Sj zEXdiF6Zjq1=tX6-e0@OZ*Bx0!BSOh7v*kY3;7Cse%V66(6Bvm-GqUXS{YE0-yHp~_(!~P{eko=W7Et;Y z5ZfhTqG+oZGM|f?46nu;G@^6-EDK5QinO4o(NRxU8LMyuva`WryupeCfNM{B1vBVt z5Lr2`RK{92WAJ1SK11PzQF%X4E;}L|x|Q!~*{b-L%nXN{@|#t3Nf?UKteS!bvbw-R zxxr7vcUfOVAgBElT$64kOTEOQ!;h5aoUgl-4bk1ZSf8}E=d*&M_nQ{-yp}a!9sI#T zq}e{1P(e{WbMrZbp=i%bA7jdAq8%IO9bM4o*BZpjyRMxmORf5J>lEp$k{H#~M(h(3 zBpDAXX3{VM(-U=|4wNO$3@EedeDlY{eL@tIG1P3$NXhOubybz#(>T1OIL!R8Pvi|b z#3|LlH^zoaCB)na3i9JUVKGjXa6n-xD<>GMue_Pk*>e@riiNp8+gueB`T zM>P8C(ek(6D=s9I$fHFqwyjgDs~z*IL~?NxgbEXp+>I$>@@K{Ok4WXRj{{f!3;KCAKPM zI!&7O#hbo|3lDy1Xd3kr(YSQSvQ+wo)h2VvV{cc z&v|}H6cN8OKH?dJJ_-kTIN$O5bFq=3rK2yKl$D@pdKSM%C+Cr-T*ajfm6Wp1k=nui z(xsnAPDb#Jz^aZfNThyyeiqceSaX@)#F$}9T0Vk9}Z zp$y|RD!6S?pW-^Wgg9%6;>elIg$5sMQV<#EEE;Y}A<*D8d}8I?%;dzB{ZCqqPZxD~wYOQHo0-g*dRmbvk89 zlR9-Tg)q&SE=cCAVe(`FEyRBYi)N4J&sMk}Qy8V??3a1c5qnAq>fS2;Ks8?DudNK8 zj$7*z|KrhNyXyaa9KrSE>5~VKE}0Z1YQktD_pL;uimp`vgst9P?_L9CKYdD>2tYm? zuH+qeHMcs;rDV@rw?BBEYZuT>u;oA{d8&Eo8luH8#xqM`Ia-!?9zQL}P-@%kEZaQT ze?&CLx{_?Pp?%@acSt~o86gOrpQZi!C;K`-ivIW%Gt8#IAW6aTbo6~oLzgP~<5Et)-{C^v4-UD$vvA2>G;Q1}Lc zqEkXAN+!+vh7&a*&gPvO%B0ew#75syFAH$clV#F%X;^XV6M-eQmlx(vw;QA@H{mRx z^k9TJ$ucM?k6=)QSwK6M|5w><#B2*MYVNPxw7XA0g|3${F_y{fDdwq!;ot2xEX-xX z)qkOF@K)<5?cie2R-b*S^A&G^ydTT=Q!VGc@8V>1xlskUmiTUa^rPP~_Bk5oR~5zb zeOXS#alb0b$cdHOfY@c6$Z*EVbtnyhMWNkC!W`S5I-u2%XyCGh^ver&-)c-&waxsT zrBF$68cc@)Lo7qzkbZr(tDBl)1^nTM^1QU+pm{J{gJ{yPn&`^^uR`3E#lYTp>u zbQ?6a7!+v%<4_piDV3%iz)g8xIu(2pg%>Sg(ksubIF?-3kdHXynQQd0@Uv|RIAw_F z%&S=SSd!qVock@`C#4$-XKOjXX;7W+;=APGW!n9+1` zis-Mk+;)pDihQg3d-t^sEZnr|z$1*ikm5IDSa5W+auYMW1_4o z4gvxBAQSqep}NmJg>08|@Im&twa}JD)b+N?Rrb7wLyy`=&#*Do7vRc$vuwkKpn08!O*b?Z^gNSscCC9M@^VSMG&3|?+O{J?T^5^8@86gK#>0pR+KtgQC zL=L*z&~+y)%u{^R=^~D65fA_>uwX4haG{R1lWyU!_KD7O)S0ph)D6SUxP)r3|6@px zz>V#f{1v)MzGL>+<|hpuyKcw!Wv@@(25A-&PlNe^n$1?nr`VNB8xI}t!`1(&+rdVR z%R6EOJ>MFIHqvX7EXwB54+67d$Vf_2j;PZ?+IqTWhP2_gAh`_QlZHyt*p)K5^twk? z!K0>JlW$-rWqJ+k67y{p+PH7qOMdzAe0fBHirGUoO|P|INN8e9?s8V^!HF<{N0PiU z(kDq1ur@6@*dIBR@OybdD7CU)o+GtJ(-Xyp4RkTp_j2pYs^zc>^R18+IN(vdrq_owu9FW6D1ns>8yu4 z^Bl)iY*s)C9e@l9)KGF}DP85vK0lT|);x%aX3ApBv8R4-i-eT5oBY*=9#Pi$Djbk@ zd%t`6Va4U}-Turh#=RqG@Mw_V{YalW38>e!Pgi5e+RCzoE>sXL3k|95d{TJEyZFu| zWnN%O^_UG}nFFr3h5Dz+mp)UG+)@UUf`!YZsV->|%9%bL(~K{eK5IlfqR~TGrYL&66ydkV8q+5mzza^hSYlOy>lc=Y+=_5nw z7CYQ9nVFQ?aXB)Giht*A_Obvu`#*+n9E;`&XW3aMb8<3Hqe|<025;%kxlNTh#5BHF z)oK6qbH<6Lh9=D?N@JKG1-+0gz%*e#w^8ALo(DG2L-UA@4OTA-Gul~R@e-%4(o$ta+@<3oGfv6Qix{JJxq z#N$xp_2;i5U3Yz>a=o_+EUlgAqz^$IsV{pg(6n1ckGr&a8q0W#bK-0*d`EC#vrHtB zsxYHOqF#9~?pJpKF`n*|{^C^?eAux#`$>+q!%g372)Wx`3=S#gOj|j3W%*)K?pZ&L zPzL6Auif<;9xwT>9I^5}=e|6MDL97jfw%N)+u~Q|_4e*yR*U6VRh$W#JwLcMpR2h% zkpA#P)!3J@34Y|--KXDv5EW$-l1}i=aQcZ-bg0VDoaP8gzV#ai)jYnhlG2c?YpUXL zj?umT#IEEaoDNtj%v7_X+^`UTQ8|C*g8ultpINs`!$4apJC1qL|u0 z58Bck;4sI%@toIb6KJ)Jn`~gxt}h#EQGPF-dFlk;Rbk;(RWe*xw43dPyU~wSJ%wGOc_-# zrmI8VMhLh1PVqGBy^1&4cj=Yv+&Rh=sUal3aktlrRdMa`J62}(sk0^A_nJ#4ZSwTo zCn$>60KMOeLph%BkO2XMQW5eDX#_LJq}fm3l);aU$C(P0iw|NtE7Z=+wl@A?Qc7a7 zLHH*8gZYftyv#SB-jcpoGIY6;CIGHtRkZ7pD7lI01RIW22lhIkJGBG+Asw88Hq89r zCih7;6oiA|Egzs;y5k#fDG{@m;NpLu-sCJRSH8&qWW5@*_d@Mw_N0!*Y$B=Ju=>-w z7B|aF##=$FFQ_D8oRnl&0(5tL1VEj$d!Uq&TSBIgP$4C}-se9VmgknN!{1bi6nGaU z8eX@&$BT{0UB2}O75W90?)*`E&mR30LHaycRr4Hq3H%fO2`g6eJ%}=A^FM|!GF5hv zR-n6gMH074hST(AQZ6^1>DiL$ozRy^ev6|1(|f~c#jKe5n~ogq!3qY@a?)b10DT3M zZSF(!|4PH6^9@@nijso8J5WPkgj2O)TN_mh3Aw@(v^=M?k0IvX;dD^PRy(+tuc{F6 zwjA5tx2b~owI5b)rw*CeXM+3%J8i&kmEz1(eavHW>m5+;=6fx;dQ`+Dw9{0FKP97d z^GNxx?*7L<5G_!rOskM+{}OrMq2ForLnKh3Jm!jDe+y?P%-PC_*$VI+1NllSR-yCb zZe$#)uAwuu8oOKsMKdIpgFPC8(idJ*=jdU%S5>VC&bVCn`ngZVS@CS{$iO3wU(k^# zl@+u#fXZG^=K$wsfe1kq;Zq=Jh;Id-FuGgW<&%g@T61PL*V*l-$4>lJOuXq0;gmLl zAC~}k_#LJni0yJ}XA;O)W`tFqK0(!5MPj|dTi#{Km+y^HmH}iv{FH@vnF8|GOCgqF zRLqQ@jEYWGhJa&pJsrs%if~K`9Mis?JkRNr{0T*W&^$jgcHiBni)!{<*IR-amot7- zrc_E$2wC;^YV+sBfwBCHs!F8-sFYSLO6xqN2DYS89Z!CJ3;=(U`7PbHgjx`YH<5* zYd^J7Uvi|O+unY%y@PB^ENolZZMEK{e*3+gu|DkLO4Vg2x>W|e)pCYdC zx}7@9vH1?=yg9o1hdQAB(uZF2)WnVmil4RuxY8M@8P|Q?t?&?Vl|bSAR_R8bZRv=3 zrKI46FRhmIVg~s#JHGZaJ;t4%_=_n-YiW*#eyei!jjMz{FR}Mc-gz|!`k4UG`{3|; zc2lqJ3C1kE*GH&Akz=YJbRCfa%w)RoqD(KnbXzKyh;NC zr@rWH37^z^f)X5)ExEtP_;MDmE=*Ybs~*QML16S9#Pvrs?^9Hly8!&U_m3Wyg6lxR z-Tx-S5D8K9XaHwn3g(CzG6OYACNHxD3^6i28)#XTg|?~jnlH$XsLlvb3s^-i)%di< zMX-J>FZ21qRfeITr9J6;_fitdjC6LnzNGz8eWflo^)d#F6RhbraDu(C!&ft}`(+Ps zi!Z#=xnq6@Qn9VBuI20bVJT|ySnVAle2pD+jUO}}pMmk>>kRGMoPDQGdRnHk;BdtB)xzY?8 zIHO^h)HsLt=qG~j%2K-u(z*GGNHvmTnU#M5v%F>L=eP@8Vz(wavE#8Ui`nss?Wxc)3F;jsAx_b@eDi^+9#?;TZ)p`!j>}D-Ll-6w2oI+KoADY0 zb^ShZ*LB?bX}Y{5jXJ6oLV74!w`|}o)XR0EOs?GHr?%O@FM8*N&x)F11@WoQ;E7V~ z-1$#EWRpo+F8B5EKm2cQ!#steqC0=2`qt}Ko6ide^GksVhg>zG+Q$4Ja9;*aX`sD({y@~8QbS)tL^_&{W5;B2edw|y3LH$_A zZrNMbG#i`-zbUw5?XL@>dNxu7a$^|DO)4ObDOgRYg^%Ipv-nCPa4yL%qu;0UnZbj- z0Ci&Mcd>R))oeEt=oL1?wNTZMeOSs}!#g4((&Sndwr3xCy}q!Py_?~h*_HAdzTi9B z7mybEG!X23%3a2nDyzBKO@-sRiT4~i%mv&lcphi3M)bSZrjBM>Diu92D)(ey~{hlPD zX-+wLCN`7q+N0=I_M2+YYn9!uRN*^kZa8+am@zl!2M@R%z_G=iMkw*DwLlw|_k25! zV>LD0q7rBt;2JTYR$0YzCWugcte8eK=M>t0(3|9g&kf9Imhhr20JY9IV9Dkr*)Cr# zxYAG-DgV1iPNv4ojheOVrp6gCl?CM3#Ik?XqQHGOqgyFkqXwiLmNlG$gh^u2+|Sg^ zJ^YAder|`G>$}{zjn5Pz{S;imF<&@-Dbp5A_HdWjYSFo_6s=*Z{CkW^{vcV+{o7P} zPP7NoQ&F00ocG3mrH|b&DBZnr@1jatn)M*&bNP)nmL@;v1cfBE)USfiQPhbn7(82= z=hp&K-6o_KKy-0j*X7qm^b@e%148NzIm<%L$Twx>v#q?9KsIdkHP0t`Rxueevu1Dk z1tT;c9^{^y2Uqjgqi;1H1GuBSHU*3f1uWx8q)WGlidcT73i-C-nx9Vk9>-eS5!}ib zCznr~46ULg8~`QeN-WwTr=w1f!&ho=>Xq&K0LvqJ0&C!$=l z?r?$yR<=p%n3n|!#VGC#mJNE0A<30~|7TgU& z*x-V^J6Z8kyveaj>?bwIYTki&slrH}X4tDkHU9Bo83=o;hE&tBnA+X?M!50&lF(DD z#6E$K;NTF1H_;OKSMpFXFh-Zvb;rVcbYej}eVX-Sy48_nv!obyFG`&F#s-Zt`r%Rg zTT@wlg3lqZc;dFP*Qg2>TCL$C^z7kqp6}Xcq}CUO9(DIqKJYXiNP4=Ekc)F_@Q@Di zQ!6}CE4vcbbMCwLTcgTXamlR8Yrk8%{p#^=(+$|95FFoqrea&1=|oM>F7rrEw?^*Y z?1z4OCtkH6;lC^w$+CN4pHkO#%K%^&6cBCTnL>!kB?QBM2)*AYHA1r^T|_CzK)ciH z^3Uc~)?aTV(WVo=Y;bZMTSqMV@Wo`>Hrt2YP$l@aEzhp?ie>?}e)mi|RMFp{ng`Kx z`fC4U;7S!PK9_aXB+mH!5c>Yqh#Om!UNp0Aq5a8?h;8)LPideY)hZE?kftxl!9~4y zDWa9D2%A$QUbm(K%#+!?&0W=aDfb{}ac&FsriPR@x81{sgRI69A?=zj!y3BNjgVB8 z!b#=v6x}|#yJ|?0a%E-h%`t`Q*zOnQu3l7C|tyk55Fj_xW zmXd!arLum+nc@cOw7B%NEIY4ca`$G7YLY0if6|v#&38B_!Lk*QB2Msh8aTd6U73Dm zYta!Hju9hGeb;7|XV{aiYW4vl!8M*%#FB&1h@kH3`35Ec!VDU$Ad*8uo8I={@ogY{KE56Ov)&Q1L7o^Hka ztNcqa*&6~cT;(9|SuG-ijNPB)?X7*Q$Ny{Z@@PkDfGUU-X8W_$dFXv}lE<>28s!zu z$#zWkwsDNWPaE-H1(%m)K!IlYQ)ShVk30Tooa)hI)(S3t7e^)irUj(5X(|&vTenDu zO6A@T=w{Lvf4B9`e{Fr%i|qw1+FoNN(Lp?I2+OVTPxq1lb$_ao-BxLs{}VXo*DqD(o5(S#LW}$j=<6#pz@NvN%X+(yUsDBV9lU=-_o`LSFZ0q zIf2rCPpkQ%lm4D3ep*z^F+vILVJRs5S~N7w#uA8@6HYYD;lbcJN}xQ}I%DxF_MBk7 z=X$U1layvFNA$&mO^+vPV;2?as(VQR8x_J{Y?=%Da3%- zpDEhjxNBLp!)2UQ9;GAm_lg)9dC2$Pl=bMmn11Zp3$0JyvDqr!L0|!PCab#d9N|GE zb%=TVGQGnXS5Ol%?Yk8BNyuq{{h=ov?>4?YJYgW6khs{c(eh%f1mUjF+M()YSK(0d z!kf)I-Wf}MKhJ_r>IL}8#2Hq{n($d>haMnyry0?b#BHKMgeljP3?gQrRwqI;8PPwn z!1q|Hr8j*{WddE=?93=sNLm(70kyUn55A;Ye=f^ioK$b4Ei}c->a@D7r#bPoiZwSx zMa9%*NOdlJ%w!6%>on!^)wq||+(Q}aYyu*Rw~lpS`hTV6|J9zsZxVXX? zX7i53M%bR-n)z}4)#U>)H%etQKKbaP-CdQ^Va}~Pwc1r{LobF6Ja3_ZNTn8l^?x!= zk|JEt85S99_xf6ZVeb>__pPfIqB|y~jM>xJRC*67^kzcT|50@AflTlJAJ-8|=v2tP zi~F@=ZbMR`jnUlilIzA?X6D|-t&?jum)vD$<{D=13P&2DZ6rg;UvkP+5?t{5oxc-HXKoXx&gMt?*)#|co$p~~Lrj8lZ2iq}BG#Bp%>{3ztv^yR zFBNEuvKK-=N)%6$3IL3ow4d7gcz)|Xb0BCQvo6=P)HVu}iT**L{Gb%DbiGA5q2Boh zzUnbz6{}P}`!zP`i)=iL6|MQ>TSFhv&gV^<=;oQL>K7hfK7Akhce2y{*Huy?&o4l) zUT>=5zg{S8kx>4(a9#1;*ImHASQjk;$8#QI+e32CR;MPDY&l5urp2%t^D}LtS#g>` zt9%JHa2HIyC^cvZ)%6o1y|Z6SLgcIEi{r)Z54A7rlwr>jfUyRV=z1jd-u3^d3n3kG z96}hWvL-|IJ+5c9yu#8H(7a+KgNx!{Hm{CSGs;R z8M@KmRh}izT673G7UUsooWUCztp<}{1(0P^j zgR7(DuuR?yTc%2AR!ihe?ah_h15QLzatqXZp?yYAxkrUM!&}tqBTNfe!SFw{(kR2V zz!=+Mg@J*O{st3Ya|+HQKxu}B;$I#;Z&8UVOy3N9&eg|1*nXv1jQK5BS=f?2aMH@hpiUY01fHAQKY z2m*M^O(`gf!ZkX~<^b888znQ=Jfz+z_?5MkjTObz_`Nt5$8PK_MPFjF>u)>+KJ zc{25Fz_k?Xix|@CaOz^HDO_Kw^h6?BR=TZHGa)YA{Pw>kfK!HVDPWzDb74~{7y26$ z+o+%KS7I%Z)f~$`GRLPo$Dm%=*c3_9te*UEBJn%zMb+c*kc^XL!}*N=Kg*hJF(Hp; z^4^b<4?dz5MxCqYGw&?vfep+Sts!*XLQQ;4(62v+89Mn~F`RQ#7nk@chvDXtKCxGI zUIfw`blysP&p2TuxpPvgd^WE25a>I+zCU?7MCLLpPTJto&i5=G!>yat#)cD~76D`TJ-Evi61MQgFm-M1YJv*P)$DlY zB<${TJ;(O^A3tEb!@y1acd%~2JSPwQq$YB>CO0h`lBn>a*-}K zo0zt>)9;AEbv|>E^D>-^;4{>9#*b2qj>R~K5gcm$v(yYcXBK2XH`l##(QmgK6Vq(> zA%>3qvFmavmR<`G%8nS~FwiDtl$P`EbK<2*>y3}XvB!+<2#CrKF3YB#s4mu*cah%O zxLM6IZ0h|&jMX25)jVjvL9|I-7_fe(WK~tYhL!79Y*Y{qe7%jz+2U9J_3^yb?6-M# zB&bb(&ADHq28eLx&7mI$e4=fKZ=z?kMX_%IjE51UGU#RIhAW*4s^XJ5>YKI7{={6X z(WxlBfqx6AI*^!Zf;dX@@GVT(garRnqu!|CVOAfdKcbn2%b#)<&7&)A;Wnd}i@kNh zyFL6*TtAyTCXd=Kd}`W1)RiJW`77FP^1hH=*)@fpi&cWcj~f$o(>=5Rz%rynF7b!Q%tIt`LAY=7Xpz;AYmM6_13HK1v_x?Cn{)@?qWQvF46?L&t zD-MTMLA!?94-H7&8RO@S6yZH3Y;R;CV$CDdTDV@fuW$m+&j5nts{0>!^15V(*W%yT zUxPW}D*(cEHD9Z#wI2VxxxtH*q-kcp!rqByo5$WHO8@>x_o=}5*LO3sUG*D9Hd(^^ zugx3rSX&Y)tKm(Sj$Rvy`YYx^WgD(L-1=O@iV|TU8@guYuCOp4=#M)oj`nJ#j5&P? zf=4aG8qTXE(VvF!$fV#VtjaQl!dF*(d*SW!OH)k`u%!Ry1qj(zI;l#Iy_0PMFEHSL&5GFmzuPEfzZ&f*s^Zun_#Udbj91q-SHVR$#TbK5u zR1vbd&{A#ZNAnojSjwTLk;x`|xXJF&G^s_RjtXX7aW8`R{UyG5d^LVR_c(>WZ`W1qmbt7NZt&q8 z`28YdnOw#!#!Qytm6j5Fb;b*@@*UbgO&#M3a$HDtJA2ai!oh}8Y#&eR(7Gp&0NYhx zUq`cRI-%l0(HqU-7nm@)5~26Q-pw_a-=3}ucvGeE+Tl*X>-&i*N_R@M0BWze>P0DB zY`c42_75e%11W!DbzYekf&LCTS08md3-Z#(p%8t6V*0dj((6>T)8ISIEUQ55| zc}fm%0JKRL(F#)q%B(=HgC}3leC%p?%A28tYycHs)m(#&KI%R;zsaGDs1y z$$zD-dQ!j)^ez{gnAC#WLrok>a04G+`{|5vx(P2cH(IUDy;op&BcCm@KE&U&NeBp{ zc}(X%KIj#4w*c8Z{WyL-P2aUF{4LPirNwEowWZzDk6s zGHn*D>+ziYb9z*lbi`eeT^q0>(i&X1rl-`uDX6dF_;HbZFq9ISh$I~WmqKKEo-?4G z_`YOOhS@RQ;%i8c1vAk?8i`JSw^Os|{G-%uK1C7&|-R5s#1Dex<7S>b< z^%yBnbOdOcC>I}Z_!R{g@sIA`wt8EXzwoJ|vanHD?7#WezazFHjV2hdxqRVyR*aW4TW+!{JeB-7+JhEz=0~c_pZ92r z%v_w<&zd{zVUWubbzH6_=B-f;sP8r^nsDU=^F%z}r#qlH=g>}Mo*$$@l=h>~$vn_+ zl-Nf2ZY4}Y`Qj{kneEp8erv3!d>G9zMqFX3-}*4nFBt3k#40SW;rZC&>e`Ep`kO!{ zGnnyyG(n!v+%Lx#wz`DTa6OGqy5xRxY^pJCOkGK%@PMs)q_2r!%VtHC2=DyuFfN`S z)t(qhL@{ZB&oY$0!xYHTZ2}n@LUZ-*FX&qxSH-;37mjmfDZq5bkwL2-sf?99>g}ws z6w={&H5`eU$mgpOx|FQ#uk01vh=>+@4C_SkBE2$=8M}I6#Q1Ll!}@X??+?1I7t{y} zH>$P_wJuQRZcp`fcOXG3OXk8QSc$1Oi?*yt(kI`39EXJK)2uCcc)n)&`}d1edOr0Q zsK}qmihi;gKCrvmn~Et8OVU(l)c?4ynW8z#+)@?z^m1re2$Ln{{`KcE_vBuak0(N> z`!zPTW?9Cwa$#~if>ks1QF2HXA(8pu06*GuB9#*eD}@an^nN+&IUU<#!U;^jyC)C^ z-|$b>U&bqvVnqGQCpJOW|jAQQ=9)^*yJRt+v{Mbs2sCmYQG2 zFL=wVqr2GLSq91RZ{o+}T1r9aPPY`%pB-%8ZQzmoS`;&jdzWMMm8vFHj7IN9&H6;`5>ArP4kcGd7(@cu zm;4ju-$Xijs|@)v{f~9;Mh*;uMo%rvz)RJ>kb6X~m`jE^ln!jj6=pBLfI?hCA`XF8 zE9X4!_)Zwn4QV6Uqqlz$G^pKqGY{ZR35Fq#09UCx*n(I_EN&QAm z+M23JFb+{DpO_C(P?XCfk9y@upoyOa+kwH<$~{=Xz=K;Ot*shdvp*_&Ef1W#+m~(| zCl-P(SU8ZpeBg9W0Ff{%#^RLY`%SSQM@Un@=^c3GUwVkF)m(Cf zUB|+NH-C=|YB?lEaZ@N0y5Mzt#(JFZ_GlzGS=a}m6P|uWeR^)F!+rB#A8aju#oR?J zI&fUVX}0crW%u6*&b;)byXa@_fq7X6`TgEK0hca)zl2RRIn&x;09jf2g$DOg7is86}YbaAQ5DWx{vk$xa_yXN43vW`d8pk zRKF2*#LxgTg{xgJwf`Si<@VD0@-foX7;6(2F)jC6rrj4gd%%u)l2Q46Ydm|dDA_1Z zy7LK6LAht1dERnIrye*x-uW>0X`(X_uY|-hoBRd}D<+K}MGQ!9 zL0yqB4{HO$5jTnL9U2x#ir!g#5i>n5C$HL03KYS!`D(;U4Z`v=jVwLx_uz58d~;Qt z=w{B9Yn~eL5ZQEW#OB(WL4}4gTwN8x7&DZtKjY;zu*VU8BEl_MQW<^9^|&@#we#TQ zEA;{go$3*8P*TZXP4L}21aC$WX!TzdoRZSKLz>%Mu^MC5XgOGjI_h3DBl}_{CU*OB zkI!9C1I1w;W0)nUfbdUMLw$0041Aj&_@vBQAl6=t^bpcZw|Nm*t1H~-=jY=aa*vVTSGgY%<|FL&c*(43orSEXy8!f! z`WpSZYJ>&L=V4F)12qQ6`Te90Bmc+sk+uybU~;F2gQF#KVe01tQ3!ZK^4CXo*3QUFZO+K4`bS zhFa;g7i@!4|GhBKDTVrWsH)zfL-M;Ei-t}9GHV*_X~sc|K#hC?&FIMDz`(>IeZ9z& z=JN{ib~&bukPF3=P|2zsStdV&DT;{5^b~tWA&7&2;MpHjvOGN zatnWQ>H}hcw9R)4a@|E<{b3PrhC~YqeVuP%gFBT_(w~ajX;>u%d8aSx@nop7Lmy`o z#gPimrUfL{$w&GXmvJ^Ts`gDkku%pjm>=foOdP+xzui3g4MF z^`i7ymjKl^xV8BM(%$CM?2&$5rVDrXVu+cr{U)O<5t;CVZ0a@qE&#?RYX|p-u@kDg zJO)APjpnK$uhJsh%Fhr#AhCG`lebwV!nTgX5B{~WdL|#-D_w-3TrZCG1m4QD8aB8m zg@uIs`+Bln^$xaCnFl_$E9Q9@%XErkxZ z&b{aV_xMJi9A?EogqR7(;aAzi!Qv9jJTbQ@t=0onqQH- z^tg+`WHu?ZKDdmMbfud88fq=EbulbZm?5LmTOY;BpP>XMWeHhw3Is*Thrl|lqYbrm zY;B@FprTJcThVnlA}PmzQ0%vKH?aQerl4WkJNDSPJaickD{;MpqGW@GvsZ?4tESGj z)Y!Kq?M!?-y1J(ei*b|kmzl20H!OB=?%N+J^ZT#9p7ngbbFv5|KNq_jjbOm&I#nCN zU&V76r6(mfIsK0}7MA5><1nD0^Y?em9#+pFl+0DNaW+n;Crr*81Kxh4u5j%qN(xy&oa54c?HYVAz~FC3_nu>-xxF z2QU>E%|;@IN_jOI{{CX4?&_zJt$@aya8A~6f{e0k+2}Fj zGu+y>&+2cfiT%_d5@tlg%#Ev;{p@T@THhd{Pq~KleJPTxxWdHF-h1*^#Q~oXz*-f@ z_%J3pGZG?M{3k}BO~8pmJRA|#Inp-^W_a*WYWKN!y`(3$tjD!r zw%=E-iyCQ|8fNO$E9_qgc8QbDJ9DE){dsy`Z7a1d+HUJ&?1KAk=;#uR!=rc{`f&-} zExrlgBgeSf6?O?9VH@Ae1Ps=Ia+-)h14g(tbIh;R)VyTz2>`C=(y+pYi#ry`KH>-pj91&LHt3t$TU$=OR*UAxoMUW-z}|(QPa_re@GYcYOO%fAZSPK%4X^ul zw)1&`vPm#pDtQxcMdz;b7u}Chd?AHtR7@Un*+vbN46q)BC;;k_1N^WxYE^OD=3+MU zU%J~>qh`#{UduXV*9L2~aDDR_zwMQ59p6Pke=T0?Ho3N&%!@n zL5bp$*>Bjs-A(?PdEOI3uV%!^6zn%l2mD;YG`XfIinPU2^Z6M%MQ+*74bTt7!$))j zI`hYHCaTfj@HKsbC09@yb(PJ`f`hs&**-J=ZPgLUNB-joSN+{tm2c@5=B5L;WQ)Sa zhy4RSEyV&W8O;D!OPfmX6!@kfG92L^ty9>O=~VT7>Xe&Lu2jj@k26J%?>(Daz_CU> z!V?NJaobV3(l>{z+DxvP%W*)K#sAgD#7WV&mJ?C^2HFCJ@UoWTaxCoTR;!so$#a@k zj=i*Ru1cWgFXH_3To4c1CjHAylN)@GxqvlyqSQSV5PCbJ#GdiI{!2Ajby?44m~s4(8`fkKapxyhD7TR$t|pf#)SU(F#nC1 z9DO^zXa?xLmBxxILE4lIJ<2tx&vx&cc@e0O#<$oUu1u$J^-uk^3%dGT0UE1aUeD$+ zZ7|GvZL0Y0DnR9B1?`bLSxBXoQUz4d=%&*b&RbTLEi9`&ffffk&BN@mB+)5e`=BEA z28&m>UyQXzxl!_|0_!VBAP2+$q*?icJUco_~&3~hTLy5I%DBpi+@3=%v zFF6jJ^LoS?9|H>O6HcMgeVHdx_9GAWcGG0z>plL*Rbd~q?X{aZ`SqPbIfhCL4VDCV zi{XmZ=XUN+UU#8-CSFa~4e^KR(4O2qYyN(u&&>AMvM`g;nDeQF-=U7CkB2lxZ_b%vd63-G6nqhp{1|eC z4yAB5kP6M#s4T;ke;sv1#^p91EX?TUO&n4)B>xEla9dXA8z`DIe?9BeOcFubox>vj ze*D4*dwH81RPLhc>v5+;J;%(%xgtmha3M+)rRBL#fN#mDoG38lqg~yFKaiz7o;f<$p zqYBG9e_o5eAFjAwu5#Tc_j#zY4oLbck64ivOX4BKfQSF8O{d4WKjd--J;p!3D;21g zA(2~nurZwz-lq$gu{Zba`{JN#)OEIzcU2tvPs5zF1jxO>XBl42V<4eVty(=hgucSt zu**r7-csGZX>F0s;(k90fne7Yve2W>W^GwAVUX) z7mho&16w~l`zc4@^DlLpT#G08YTGYk5Qi>TzgOT4tUWTf(PcGy;GE5$2^jk>{@}cB zDSUQoAhs*Mz}P^4wCZ^Ay+lG>uY|tN7xQ|pspQ70DfN08zp2=?ZBrByTu9g3)5}?8 zk6H;fruf;0f?8uMGXEaiSKK2jyew?I_TN&O|6n}Gfe9U)Si^;>b#hLb zB34LIJ}hE&eH)mzMea~(Q$Em{`SqTFlYTi4e=R@r^qTu(w&JM3FJ+_K=MCEhjNd)E z+h*Vjo;HI(sd%e&?TSRz)=gQ~T&MOsnZNpF=CNk7Sk#w{q`+-}@42tiHNqs|APoL*at-?pMzmD~fj*z&0xg4yjpgaFze8 zRAn*;T)El2Pd*r|Rr@X<-ggT$*T}2oQ-^3k$Xeq+WqFq`QV$}5% z3I@M#G46NaB)IFE&3ac3_2SJH{bD_{ht9=vT99(eKDMK~WWKa3hUD2fBm;RQzgAZj zHVar>e`gkZu&J1VA8n}^0(o9Ve^`Wk;YfqncFydsbMm=*-#X+DGUYybtw!@$0u`tu zrW+dH_DAfuZMyU!cUgF{VUux=YoCt`@6XIm-Xz;+9gM+0M?5ggpKI&n-3Est)bU7@~&I%PHQ7 zSO;#o5`b4*T%lF&!+34o=JYNMxdTw~y*~6J7*GTt#IXLmvod$Xl(9^i_SUCcy_ZdE zT`Rd=_~#4Eq61GeDD-*?ps?p2ob(x{mfP|M!+v-4)!>Xo$M3I=rr^or-LPia%R^L_4g-=J~B8jVJE+`av%XjYS)!YyKd2y zn3{x{9**)w=Oy%LrX_JYUBbdF6(K#?xWYWywh(FKjjlDBSw|9 z5*BbzK})Ek8`Z=rG$j}08e1;|VKvKIUvGdcVM?@0aG~)N{1|k9n%a zp4;5h(?aw243#U7jbEjhmV{Zd#H$C$9$e#)r?#z=fXI)S554Dwe|({NOYLBxkzVez z_MW{$!3Hg{Y)EJMTV-09eury(Z69^%zac@h;EQhARX11gN>=M7Qjy;_QX@-KHZ>I#W)4HGKZJm@R3UAM zl9;##Dy<^sZW*rV0ZN*qUfg)a2R39KXK+Gut z#TX3TC{<&74J!Mz_TSEvDZRmubf8n68-TgED!q{&PtgdcinhL1;dWKwZGnll@6UB6 zq0Zowq+C_uqBx00f%68|eYv8&KVoLU($zbA;xck;&ws2}7Wu!#2WQd>%fjcw2Irsl z4G|t7UNk$XD-@&@ORE(v{EPvGhK2oqt||<_ZT5^P=kttawK>x}a3L>4Um>CU*Gi&L zX0txYK_zwjHcY%z?gB##*UCw&qN_KgGE5W~go+a@p(wgC;D{f4Z#F?$9sBiM5&R{n z8a+MU9^1<`73!W_GhOu7;NEo*Q8P0lo)~So*CjX4=4%B}cF5tBxq0g(F?N^ru>P2p zt&bx?GPrIldk7b}9zQ%OJ?HUE%(DW^Be`&W8Qb(Af?|dSm!1?N{(A6_B>g@FU%}+< zLkebaF+|>alM>4MUi^=%qZVhl`OxBGejmjfCX++Ivj}_LMD%#v63$6o%Bl;ou%eg+ z4py>8LhH$>%QiPdkT0~&%h|4#86Z=VkABattY$3yi{NOr?KjTrY|M0vtq7R;Bp+K3bqOl_5j=9xBaxNF%)?Y)6i1#v)vta-Verd z#AMKAX28fD$AtiI4+xDrH(CSI0{F~tIfR8B3;jD+emcXxc@b}+irZ%@(}xZN9>j&8XK5slWOkN(`hcgoL1zf1r?B^1%9pl&+;y4 z(x4^8lBT{XDamUWjt=Qc`|(O|8sGO2AL!TBWiRIo^i_7e{Xma7`nBi@DK5Qv3sGiE zBxIRW%G5^1ugm>Y>yy;Z1t@S&w|}Lgc)!5=C9V3D%7aN4X>{th_u~j?=;%XGfZ$!Y zx`EN#GUV+@xWVpGsZQ-7Fx;tWvm$_o!PtE1+nf=&S_?Ez^2?VOYufSvmFL9fp2D*v zwjUeAA52ntOzu@dXs2fE!1m>7mM(M2)~VjO>GIbpUt;+t>C$$Bm*+FT>VhYuVvY#-m6# zg+J5**ysb; zWV>MT%{jIN^N7?N((e{ll}h*Sy+U|eWE>#&e_T85w$^Td zu-_`ItMsDbSpaeJR&r~T6Snk!TrdXs7MKJbE;Q#OnB&RR%#lE*taSC9<1jE@bxOhG z_J=>G!K3y_uTh|pU?ah;le@Sn+H!^QGk}Jgivqf4i;TS*lFWT~Pq+x^i8$mQ<5Ww| zUY+Ztjz!;4$QV)uCHw1$uY)~d)h1KnK@(c~CX9u(L^-*f_(N_C*qAckT%P}IG_9?* z{#Xi0s_54r<2S(R?72bL^8+D((kzu@_b8=Zy$;*M1zKuMs z#)=lNz64M4HjxS2;Tat#(iqQ=(nz!0`kMKoWRB)U`Bt0Q?Q<;U!X1{UgJEYn`w-w# z1;vsKR671n_*N)tKjy7p`v16;1`={qDT7O;x?zYY?l$NdWum;pV@?^r9b0!sz36wy zE4--@5Qo9oe))fk|8l_kTkpaz&uH*anv0%Z)qaXovAt>686vogvx)QqH9pvyl}4|f zr`uul$1kCduKrX38Cd>;s3JrFH&e0m zEF~`jcQdVEN=KC8eR;Rb-?g(MGrWI+&bZ&CKNrQ^BR9+aNijs|%KT-x){Xij&9QD5 zDD;H-U!Qq7)S~7JTki*K<4334cUd*d;g4FtWnpx}`o;E9$8c`39eLoBl*nqm(#EQy zEjO9Cmj`ptDUZly$rPG9FnG!riaMep9BV{Y7U11fpdom0f16%_2XGx~T@)*8aJmj8}nW^Nw z1}iJ;=hmiz2XlE-d~Qw?<;>DQ;?Nn(vw=Cn z=uE(5o4>fmG)Jo%VLc6hXXdSo`d0UIMqIsRc;Or5$^2Dx@y<|6SRN-Yc<1}XaWvan zBIH?$2LBB6Q&Ah3I&wX4W4dzwas?iy`!-M9BJ63l-R>NxN&%Ox&~Lb1yz{5Nil``dAK*#~$)~0RUfkE!=-@B=6i7@mbbG5tzW07gG7QJLrl2N0tY^j;CjCE86D8 z@BNQU*l#jZ2a8`oB!t3G(#%z6FXsM*_N_8L9iV7QNBagz_;8e#hJgMbW0~C-oFJES zwcH8HYY8un6(pMw;!k;eu@8n_wY@G2pe5u^PECTg5eDY+lrHWHO$vBdqgqu(yQ%hE z#3loRLl9ReQwVYo zf0Cl)a<2Y@Jv533t@2(!R`ivNsK}3(mEFnLrYHL^+XQ;yH>>lwoM#e#m-tved*4naWLQ|=$CohPia6UW7tnX`>=2B5llH)-Qroi?Qrax_9UD zGF0=9w1r_6RA_j4jN-PS&Uit<|=`yybU* z94hobyP|+?k+pxT!iS5AmCx!pHL9pC2L_B*eqXhd+I@!Eju<-Uv(r9VG;}$JyZdS` zbahC$zNJ`Q1;dVj?AxfB?74-w>WvbuzB2#E<>xKBKWgS46D6;!9*PR<;gse?85~p< zE#^usi?)m3GmW3LHamfu1MO~Ap+J&O*qAdbN-79srlvzg-u-%qtlMrik`K zUIW~x({IUJR*x%8WhE*HO$Moiq^PT$yN5OzvnoyNX}wSPQ`fzN$~e+(px>&hmLXox zu+ifEVrqSFTOG=0-U6lJ3WFH13*O+FO2CC&Cx$E->R!lP7Va(z8ht2SSff8Fkwet7 zxPr9l1#2#e;q`i`Ykhe;xKB8Aw?tZ4B4kqa>J}|{s7VSKmuekC*@UZyL#_)DM^X-j zeta8xm4u01P>wW{jB_Sv2e;o+pF4%;-Sux8T%ETDA|6a^bpPcWG%g~mt(X1^0 z?@av%;Un^>4;X9V=ba7ONUU5N_{=K-FD-k9C0O^3X@-SRh}LLRKO_tnmWqKJ7FW^H zC>QX%XAN_=?T;1O{N0*{8s?chA3UFoojkqz7^Vl@jxs1L@)(cTMuahc`F_A2sdNlo zL46q=ZAE!8;K>w_h}$=qxw)k{UKQm0x)0LoY995NwQY~gv&{b3J8ABgIobMvF&KW5 zK1D--LQPhflJLw1xNGH$W+Bld75QAbT04IQkL4rkfEKGnFW0Za%-Bj6KX&cJk@Np?`MFS6yek0gHr$SBLa(Jd$7X+|;#O*#d8LLCb z&hyC;L=e)rJoUf*RZ{J1cg#e=g*Gl~7%RUn;l<0IsJm@y#HtMx(%DT91pz#M2y<0p zy--6Mt(C~gAJ>N@MaxP|6AJo5e58?WV)B<)Fe$#eUm5TG&$DIjf1gyLjycV_px*EK zz+h6|x@Q@l*t27eIhn6rFz9Ey2TK&jNog=n%5m;;mlohlc21$g{srZ>s?h@-;_E4# zD_7MfKEH+Y0p-by{QkRUX%(kA+^lHkg$bQX`Fj9RQiuRdZVF{-HMqK$y=!m2K17F z9hed>L@P}BuB)2Iea%~*J2WKG8E4kc-ikkYjSO>{ruwBN*i4?jkOmr=E}i?C*kDLq z$_b+=w26#!Y31d=<2!2wqHl-iG{;+QOufM(Avv+vUi!YrH>@mtU)Y)B*uE2qVJy$z zsF>oV-1%pjR~$}k%-R?sYacfHh-POQk3utpr)E%NHP)U6fVcf9n)`>q_7-fP*)Ka(OxUP6csP+=@~guh6gH|ZHk{w9hnxY(ATzPM`&8eRz@%7K?B<;-5H%B zc^8Hk=4WfZWh@OMjCtSv>wFoRtHs%8g}E{IZQXAzy5ONkuXpM|t|#I{oHQCc6<+J5 zJ=1xiXUpvSqCuK#z1E7gZU}`gFgG1m60MgxRu>(4ojhBa<~w*_H#R~xI>sF9aD#Kl-~TM)TjuY@EU^2w#+Ydd7q@3 ziKNsG*i&fWcHVj&vn5QfaU#0%xrw#o-P{W-~ zJuNDuYn41_ZYUhvMfrKlsU+x>5Uc};)1xYJtLv{M!28P4a;xZv+D~}ox-Z`S+0QMv zf6I#O=)slyHXsPW6*Ro!SNZkfrE&|OIiUn;ggET*@`IxCnt4p*fqlc{W>x;S|j18oq4cMYf!69t0enz4leNB4Or~T&gB1xUjs%{ya zGlS7O6&+(8P!2&%H_UE56RxDvOC)b=n;h} z-K9?Y*xO}B!vjgx%i{V-I8Eeqw#S!@>#Zh|FyhlU`BypEAHgSr(@B#eT_)1ylYhqF zkK72Xo&)%HTA1sPI5LHzo6%c!J1b)Z`x*nkkFC-NS}VS>@RI`EMf9ia3wLPT_eCV2 zZy#)&i9R*N<8N2SBb;IOQR}M_33ihB$nW)>=xcd2FV>zdyqQ-hsYJSu@mamLWo_=J z1%`!Y0!?)25Q&_|>dHcDzCuZq;a4p%W{0^Ksmsp9G(l8a1P!#AB_iT1UYb2~#^shA z>ExOHCK#}ry75X3uP(*uV;g%(2TUO!g*#f@W1N6)JPjD)ux~B@vRlvohddv`+yG~y zc1CXkKd7r`NotEn*1B;Lo@7(s(HRfHzBWn*Ik;M-WILmesNyGif5z3sUH(jKGsH~f zkJNE7dAJ_Q=hP^hZ3aG>umd&bZ(J!lW0g?6#LV>YvMGhP7LkP+A+|M$}8CK`paQv`oMDb;UN8@FpTuYer-i<@`MET4M8 zvo)rf_O6fN^4SHRAu%SI7PPxKj6X6LqdBr4!xZN(79N;^HBNB(w)yL$Pg_0_2_DgU z)~cDNV%r^JfpjI(9!a`4=vs-MeUP`t@>}@k&%kK{AAkKqpxie zP#+sY)0Vd*Gv#OvMp;1;qz~f4mpr(BEe|LexZYW)q#IrQR1_9yuF68E-rO;uQIoR} zGVQ~cT>pHK2hIKM=QQsh6oET`$tl+J_V|6I#=rS3(JsFc#%F}2Z_6viQ>VW;&3gQN zV(IlUKd4(L=6ETWv&`J7?d;@$whFPg1>zV*YCMmH4(u&QE<}?H$tfm2U&m zM_a7~YIc>#Cz8jq!!5&|FCk-@0;8~EAvx?dj<9H>+avvW)7Q~3@J zKDKS4rjkpgwCR$qG&JZ}SyFJR`oC&-kx6qF*;$-Wd6!3a8L_Hcpti5jz3i&L^dD0< z=hZNKT&jzr4!1(eCFrskX$~EbBQZu)fImre- zoQ$RIF@nIpK=F1!RAM|bz!s7mAm9%?=|X{-0!#^kC*g ztvby*qP(X9(zHJ6%qL{~y0yn}@RWjFPa)K!Uz`2|8X@b9Jg^!EyqR6y^-juBXYCyQ z^?UjfiQ;yonDLE%XLt3i?%A3c@kvg5JLsKX;haPN+r>b0T|T~6+&d)X^i&j&isv=! z#1cJVtV6`~DTtB4_o^PCt}w%A=}u^-xV+tT`}edzjKFD{%|pWFAm+;64b*VP{uDP) zWUStSo*kk&+U?P-eCvX2cbC^Wx0VZ^+DYN@<`c=GW48u7U3p2~@l)y~BJ_mTc-o1(DsU?N=K?#gz=h@#T% zi746Z0U*{PTVz*8EM6O4q~EX_P5rqwR7Ob4QilFoPE(x7k8JZyP<3Y^k)H?-?FMy5 zj2c@#-|{-0CsbN&e|G1#aOarKAsdp-9OpQ5%CXyFPTA(PNyOxkNlPNt_qX4_usyEp`h4E+ z*X#K_JGL9BWWY-AfzmAkN(^DS(Dm)GmA#L3U+DcS^!ep<>`nlS{>>uSKs5sHQxQ6l zFO&JY(EF6ENtgB_QPLX3OuRH}RYl(UNm<)MR+UzN&U~Y}?!XQ@cnEX(IK#lY86vC6 zlUk@)?})JL2+5icdeL)b!~A<;_L0taT~rl{>Gr+CUlq9=Yu$Y)W*V7Y=b74iAfc}P z!Nzw)Iy)sVHlE|zWi$qmY4y7 zUxZ3j`68new4#?D?oRB@e@F`bVIkekz}}IChj82a0O|Sf&B7{(rcStwg*HShVO}QQ z*jt$`i|#JU!vge0@(?m0WZ2o5O9iJs7azP9fKjSCwjTf=JGjtf-8UTK4tzzDSQbQ1 z7!cs*_Cdl9%kL3#DmRX4qg0g+lrZNW>t)!Lf~Kn4Yz2|IbFGP@A7=vmx#%n3)JIp! zRyLNbx|sL2_v~c7I?QF8YK?}}hJ}s2Jz80dkNic{uG3mPsn*1wQi(8_6NdQUI=ay0 zbZiwYx9*OuZnSC>2b}eNI$%D7+15_8B~%V z#xUf`Q4dhi?X?8ED4t0EC}cOUZ>7;l>{qH|S{Dr(am~qW`mzd&0IQnr5uQ4?=T9!N z@0PY{7j=kf!E40YeEo0FMU-4qtQoQev*HLv?Vk|ean#%zaIMx7_<=T2ag|B? zBohTe?fJ97vI|0cAk8rgF=U#D&RGl!Ho8eu=yI{G z6soi|_zC#1*jVa1idz1cQN+})znF$#3Tds?X51SYg_G`oh%2i{x#>u8eOA$tke;)p z|1I9!P7@pb_;FYm%P4M)w0lIkpA&W0B^i8G&92mC&&`9I_RA0I3XT(z55{M*^H6X|C(csvR;AZ5 zllgB8y#f$M|0ZLu6|gaVi7Oht;DOUuT2(A~E0Y=-TuQC#*I)QMSb2ddL0-5{kF4dy z{WBM6Y)G10e%8_(_LvjwkmUN*Wuzt z&tB@@zIopygv!K;V*+;0fjt{TCh@Bv;RV!G75?MClh$1s@B?2}T_ZA`HCSQ9Jr$Z0 ziG}<)3FiSI+Cw{YRk}x7W3k@k41DRTH?W8K7py|4J~W}e4zms!aj9`lR0YH$^YSW_ zMPlQ08%uRXd8FQV*S$tiD|JH#OA;My=A|$QrP8-3H^zdjch6UTIm1?Y9tAZ?bL%)j z=!XbG!8mtWB>Q8;(hesbKDvu6mNv2k#(mtx>^P*=+>gw3c+N4cPq1(!Y;KGr4I1me zy$gvwEtb0IeBn^8HULn#b-Un|bhg3h*W3bK=Rd{YM(r#AU?{mZ$pZ#My1Df(CpU#1 z!aHcci-}x?g{jtPMJ3D(B5D%qeX3R%O`Vhj!-uTCv#WNB78`A(s(5~Mrxiu9$H<&--@F=!5K?e@s zz%ibhuY$Utxn-W##^#(@M+MjEYKqThzt_n#4&L}a^c&;g+~;ShkZm1;UX&cHxOS*< z(X{(_$u5QTHUIBO?N#E&nryuom?&M0AxB0bXn%TAO=%MuSL|L}mcqeIr}$})=ysWq z0;n}a$7Ybaktx0ejLejQb7CW6CQfJ%W~nD8-#C30r}<+c!Sp(O>lUyKd#_J+E@L;X zyMcB2#uu+UKv{>7pk>)Sz4M?f^X?Ln_i6iHSv{iEIK~NDS-3Ig)*jL z_*1w13FZ;6Oq}F2w+*bV&Mw@YoM7%h{si`PS6MYw+1~S=kT_+RoP7}t-Y1>=@0$nu z$jWHU2tB84QpBYvmnhNgg5t|`ByEt8#gZm+i9V&LKu&>2Xo zO)FlL=$EKTcYy_V=yRfe)pxmeJNUtN(p?l!CFt#T&RdpLB&B9=%O(Z}270DL#^W;Lw}o*L#s$+Hv?*LJ@RsqraCW6Y(h`eszYll@R!-rc!{Dev;*?Y-by# zIUkAG;4C=KW*7Y7mf1|A-t%+6Hu66U3?pke?={V(0Q< zpD$N^a0E3x+StG!i9ZwtV%pYkq1j=j?J6UtTa&KmVRRezcS|efF?9{amD)b*ryF_U z{+8--li5wNk9{1HFrd34x78pon3#%&Kz*gmG4)@M5((xHe*NasgON#@w_!Su2Yc#1rjyNl`>kL*n4LAn>Z2&~c47y?nP8%-}cWLEFZ0 zev7;zB$PSDnWv2?7A-(Qsd6{`C7~aG$STcTXcnH- zA85%Kgly$NUknXt_9Xda88B6{^zl;u{he0aev11N?w{-3pzj+$?pzG}Q$(`&tc+<< zMTUuJ%)Im`^d7_BQ4Ub-bEi%W!Upvg6ZH*ZEBs|y`L2VbMIB?14jsM^*rf8TJDsr~ zb31hT29^~-vYa)rV#ngszz%X)4KZl_JX~k6_G3REqZyFJC+dQK zmj<;dE$oMDn(I4xYiB58sgpv_+*R_xbeY@UkM&!eaVA+)jv!*bz23X~`Z&;+fbb&; z4)0h%yUsfx=2)f08wnSd%#_8p2xwXq&Y*Tpso+z@d|{?|x|tl=G!L$5Xv}Z^4{QaVb4e_?9$+~QF2lheC$FlcgQ;CvK;w&qC?Bilb zPGpfcCZKw^>iK}D(v{1&v}YHJ-c#K^vDj0fB}H17+2Z3`k#y%KWSNFU!t$%y<5$~# zR<=n{5Fzc`hMSAi`F7r6{R^S{_s5ptr2tOcJHW*>WQ*_o%DCL*FY;O~Ccmn-fLTrvdoI>b6W<%jmq$Mf?mi?s{w^I? z%%Zp4gw_z864yU%uc^52octUvr*nOD;WHIV(|6`bIoNq+8tQFrtQR;t_07d}8m$D+ z>_%jS4=dXQr)(M!*s=jfO2zj~N|G!7+FI*3t5csr%$MYU8MtQwp${2X)v1c)q6uYr zDKaM2`+U)fg&B+kZQq+pmv)$P%nk{epSZT@X2MWxVucLo{+LGF1XXEQw}u2LPT#N4 z(4jt@0wd)tZmh|#^;<-ezma{CHeBYrK$abOQE0HEp@9h^b~n0MrY1>8jPSdYbp?z; z2cJ#qRq~z8UaX#Zigfk+(a*4RPcYvr+vjR-;%cuHfj~h6^}hn-R-*$EODuH~TSGw; zVyvHS?JR}N^0OH*i_H7*Iu4yzsoVA_4S!{Nzm>J|jc`DrOs7%spvwY)? zKZ1__v-rF0#`}aQnG2ePiEyIWDO@9Zq+;;8mzOgkse1aIdX5K5p%Y zsSf@=ODHf2cZ6xG-E7^Ex19K=_I^}lcv3n#Ex5zT0t)*hIcA`TnN97wqh|dUR{*NE zZ8D&kzM`CXjg0e_u3(WdRtlQzc%ya|8l-P5%{}zLPZy%I>3L4_UoEcBLZbUvs0es@ zx{Xa*o4+2CO*npkvsPai%(-cUa21er44ZFP@7>paH@`iHHlN-2ubpxBr8v|^pzYy zvVs%XbbZIchiN55bMjTDqYwGO&FovYTGdA1xcjb(EZ;dLZ+!u~XWAF1ND{%-}JiLx#vo2IZAO)WvJ?*QX0ThqqqlS5vWHvdLW^> zPSzDHJf&Cu@nzNMW{tFQba(Syo~gOI$@I6$19*u?S0VD$x50)*_HIl7PnjInpht$Z91N?t5D55tFXu zNpSHUSg`q_Y1qXBue59OCe4GDt&gZ%t}t-RAWvecE+3%Y z{Go>noFq|X*^C5j?z-tOew;bn4YNLb6x_F-3c1!1G4Y<;t^Z0Hb<9kj&NLZq=#wkQ zJS9ZsVd4UObC{3qn_5;+~oOrRdKrh!0@(^ zQJ2MPt{sA|WeTJcTqn}CL9_4mm!w_R1BnyX7t@}FKAc`{@V_IDryGG_O9I0200FS; z#xrv?I~a3mIJjW5g`hHUFAkxNX2-Z0jzUD|PPh=P3o`c!o}(x7+oJ+)aPxns4ww9$y*zIsVI{ z&OvO@9dkd=bZxjrpIb;uX5ymPF#H&a&Eq$;@}(eYX*%jEd}zx<{x`$pK0AD2H|g56 zb{)E_)2~)OylR1*@aqA&A#F^OX8TJ{7g+ZWY@rwP#DPk0NjGPnahAe`i8l1vyIf7r zFQ(h>(N;PPZTN1iafw`!SOj#S>Z>y3ZSk6z1VKSY(7LES~V^D-X*w5fcOgN*>$ZlwM@r=~u`J=QtGy7}3L9cpVHVz@Ymo@8 z0P($aX3$V=n%MfSllOT^G~EI5 z3Z7pPoBCVWXDDU#Htlu;)q%sCp!O+W!NyGRN)WC7cAfBCN^lkw3C+tDx;rJ+dx8{v zeFine;zzeS_uZv=c4}M_&jMCy(W7IE#S2=^qK4>FgZjxrlA8oNMd39L%)`i>uB~>=$#Cm#gc$NApU@O-d$OvW&q^tXZAulpUrr+G+l1g}!9tu!BZk9q=b#BC~bF&kImZTI5{dr??xD)C555li4{HrR|e z@E={---DCLZ{=^`G}`<~au7f23PQ&r{}sv4_C>Qhul`9r>MIMzhV{SY>PPxANzc*N zBhq(ZvO)idh*`m=jE}47X{B4Z$A+noNaeW!;4<_8soSsgo?a=FXJb zJ1=y1NXeaXcPh@&ieB08VAYs#eXSsO6Gr{sG%*bLm;*D(24B0nsV*_o)g>Ukyr^+) z`!F^0Z0ok)U0v6rMa1szSzj1!@COB?; zkEWyIwv6M}Snw#d@U#Xxa#`H1%c+k$)xnc7`g=QAB^2%WbLi7a(bpVtxY9M-Cb=TpHswB3_4Rbb{(596_jeMioh zrU2dqrWjlA>wTzK)4{A0yh^-e5v$?X1y8f9~p$y~q zzi^tT@FC8zczui}tdlcMkT5|w+7_ef#-E|vF_g7RSjrZv#?tU%m(JB}!KA`zh%_sG z^JF!Li}KAgj!7`d0X9uiss6=>3DU-zC7_@Bm0Z@3nsJU&#OGk7muUURdFtRAxkIZa#2G^Zq!^M-~8)-Y2nkwr5nP2eOPDdMq}?hPat&;h!-khTd}2v~)u^wKy?uukDLm~Xqf7X@!$_f85e;qt zl=1=TVLSxueoZYW>do3meav~l5?B`dfqyk ztH$kCr4=^t7C) zA^boLvv?6#1N7ryvDFrQdN(1c81b@$j_|2UBP^(dG{OTY~!rv=}5owj8ulxq2YbB6k(jGp>+y-z$0=v@;V$EAtsVZ)b& z+k#N#hcZ_h_eNdj|A={|$1xf7YiJCyWce`pf-zgOH^!(-6A?=DW6NrDKqcAjLXCRO5W3_SXa8 zzo81+q34`^&6tn+B=G0F>??xWr<}Lmkss^8PHfib^t-wr%hwHgnc8Cxb#vogy)~x! z*=c7==FJ*3@5}9?6KoM9aF1L4E zSl@y(NpM@E2x*0D!S`-|Un|)ZZt=u>tE)5)QrH{QT#AHS16E;p$O_B`j5UNn~Uu3;c@b$8$YZ!-$K65L(#vcoh(38l&5?la zUD;>>1B*7+MjOtvN2CtKa}|~icMtk14H<~Yd4yE?;FD+i3XeV1=WeU&s*GI}3Evia zz?N%4Lsq@fPSMXGEFVYP&rfTE=(@mX8*pe5)D?bHX%UubZokz0gxPX(+V7TbGf@n} z*5(1V-BWtaWJ;3K6dZ`W`XRSjxTD8=>neklflhUrl$T&f{b2#!^T8OQxv}%pq*FUHVooCx<%trF$4xl! zd)%{+|w$zQP2dqsJ=>2-824HgQow^oS>z~GDU-rJw)9=kDXndpQ|-#ZfwDW{+G{-4S|gUtmvDuCXP((& zkpI1ZmPVBrf{>8-$-^snw(Kh3xy(U>UP~L>|IkUNrOSZXT=wQze?LWmZ2aJ7*SuVE zc|Nqm<4v!?2~=S6&b<4lm&l!n;LL!M!=*Ri#gEzXksVHG=6z_~ZnR@0DA8nVr)b8n zb$(9XY!2%ixBMRIfn|Hln_Jv_e{2bn;CCPddr8wl2q3e_{K#9~II^f}O~byu=U5ka z8`M~j_N>-z{MzGMcuig^dl=S!yw0t=yF^yb?NR^dkRziyV!wH#9>xX@1+j~l^D1Sc z>%QnRfAk3;in$-2|8WM2L;7+4EVwp!ZePuX?!@YjWVB9`Y zoyv3~4!Q@H5I)ZtU#fhj?R*c(i73z)ktcUShY*WbwmMV{f%RE_L(6{}d`e7lBxZrG zl|rhH;?8erunYwN%!3sIEYA~x<95tRLQ^ z%SVd!@3nK5KIAUyx^p2;l?!`oE#n;Lt?>@y#G@pEn3QT_++_rGf{lURSGNhoRboKZ zt`uMg%m=<$S=lG2J^C@TlBJ-zX4fC5zG^l*8$i?y@>cb;>m=%AgbXVkk*d=?<$;)e z$5yYi6n}K0g@2*qPvwdML1KHg_M`BZnN-72X$F@ zg&5?vLMi~9T#<;3ht%Dd2Ar5p1&Ovwt^td+wmf7FH>g3G{;`l?z=JrqRXcah z>KL-;5^*B?-zl5V0Ics5bU2^7bctTwc$4DZ_0d(hkz=E=VYhgbZB`h{En=vd}(1yNFAqIJ*hZiEuG>>OVTmaqNo{GsS@6n z*QQR)9UYLHq^W^hs2nXLk{zrbjgA zDh$)2jmnGp%}cLL8$IAeQL=CVo>Y->cZ@^4hTwQ=ud!-zl;=&ZrkX7QR}GK(#7 zEM#dnDhBbpQ_CT&XvP=v8fWFS3jCyI@ctYkHaNRO%jH9+sw3g-t9}yQ<5;>~(9eOh z#TN!l;>8@JNh!(2itRdHqOUjrLb^SIIVBkp;W-3@F`Xwft|BdrJ%iWmPQCIGqDaWesh&$ycB zeKFA1Fz*e6oiFw7Ma2ZhrfFku0BR4fI5g=%?!RZ-{Evs`6ed+pv_V5)Xb)K#S4Why zV&I92i;L=BDQwL*mJa@Cwc;Byp{x0~9Z9LZWja5k>AH%udUVFbyRfV(a+S#5qAe|Q za>Qh)SY*ab7j=QhC z$U7YnrYg$;wI$mQnzWu5z*SHdYrk7gW&Db!aeCAG>3j6dO@z+YRaot^AgeJWwy5Yf z^tXgfON#N;i4WJjK%mM%;qcg=P_|lmb2G&O=I;y`lb?Z&S62|=Lhcd|e=UpIMd}MU zy7ZqldDOr51#s2c{=k%>?HkfzWOXPP?u4)KvTEY!=znByuZka0>+07-BY#UF_BsvN z!S4w;gwAu2|OBG9uP>(*g2GfZg@hIwQkcDmlS@jA$-xZl4qjW8Tr%bY7DWzI07ynwokZ`~oZh`0) z4tuJxd-i?nd==?{I@jW}x+xyTUS9ZjNP|>s@wd3TrqNJ^4P~mk>k2~z=BPHB`zM4#pJcxEkV9D^_<@S7X(k#p)n$3UA` znC7uh6%To888zi|DV1ZqJMy<*xHMMhU%0%@KbIPvIW(br-$>cc!T~sS%)GU+39@%$ zpBMqk0BP`@fW5d<>t|1x%|1XXlD}B;CPDpe6J;PphFxAcSGAp$CjX3nd1quj5%$wJ zz@&M{{PvoS1?*6l;BWp*O?xI(+Q=nM1UORL=RLZzx-(>F`+c>|m?J<#@3ODfX*f8XgG{ka{E_SuZ(Sl@{hi-=*|g^8ab6=f_4S2d9jq!TRqE4STCl%k1gZ zkYrJ4hD~ag_10V(aukmO5xn(gTu_fTuDD)g>;F)bVXfPsv6xQD95+ zWW9)91!Wym+hd(&s5is)0CgybN5`*h<=EbTyCGb+qlPsatlmXFpeNNBd1oa-C8$d6 zMus#v`SF~}f9S+3)&4Z-H`YMU|9D!1!e4Rg!y&DS$SWP4-t%ZWIS}ZETmx(jZK2Db zU-=fO9?njZ{?nOk`@Qe(3aKF1V)@H}k?(N#Sl{PC&@9d4@%lur^`qb!nE~eWICM#T z(AQ@m{9EzBR^wjf8;d1t;@EoO_65)hWW>?mGoyNBaQ@4as@2>p?xhZE>Sv&w=m%H22CTzdJB{Ch;Gu`RJK$b{a;c+W?F5w> zgUO1rQvdUX*WUNP*R%Q4b!ft#jEJp%yhnE&j*TpC1C=Ie7Y2sxg4_0IHx#I{>8gv+ zhH8GIy#I?H`=5aO=fW0)BHnM3@H+!_n+wvFt0UmJuc=SlmcK)i=&E=Wo2j#~Eq$F8 zuCTLA8~yMB+^$HJ!dMr>e&@}(#&!~0bdt}V7XpZ}jNj;=;MBwJ&`t|UVykBtV2Iaypu@m={Y40#nTAS1PLDIXo?8NB> z?|n=4MYk3#uhKFS48P_8z?yW@WJZ;<-ELHR=vvW3y;`cHC(_^Jrm_mfeABrp-SLT^ zqbCXT_f~y|dQy)l(wYr_;?~vT04GL>oE-z~k3CZ2+J{5B_bj_8N4qCC+D; zjD}0dCZf2%f=dNu=99zIs#o^Vw#}&%P5#lSIrPO_bTIq%9pP^zbP|z2)U(kcGkjU0 z|9Q`~Kv#3_5ug#G9iHB7A6_6J7+dLaGc6*_ts7TKeX7q@sfuWBG-s!WG!eqY?AU*~ zp-C(8l7HmB`*7n#lab*vmHoX{A0*gQ78pYP$Quf1eoPDn(frasX`iRR-1x(^g&iaW zTeKN4QUPTDE;cGy`>7bc*-|M=`5zCTS+h|Hb)ro(-Za#Ty(hg(%#HUVii_Qyaqm$O8idH5S>dN!w|8uI+{iII z?V;apF9j#bSDb$nSKdruT8%pvDs2Lf(quglp0!iLK6E2B4ipWkp$p)c{pB}6-Jejzcb8A}O7NiS}N~P%x0u_6#ygW2Tx-kLhO*q4FGjmG0 zQ_|w5)+lm=7q83o@BYoB+~TI4kwO3yi@JbJoZ`8Fyrjp<%Y0+eM&9NPJcuA)0aP>w ze59S_kn}XcfKhnoy^=H;DMOetb)6GOI2r~7YjP9Y#jMYHaoaODEmlxR&%2TK`ukie zX)FXb4sj~73zJt=ZxGAosR5P=DG&I-3V1x?RTF+92w$-GgPcc)<;Di_~mhO)4oMuv*1x^N>k z9IA9%*|f!KdR=_31s$j7qj{+31UK&yv3R?g=NT&hvB;j}r6S|6DfIXxu_;qE%>jMX8H7u)1qE)u8}vsPJg~fKDZG8uLhG|+(JPd!}ji{ z|9d$z^uy((lEibcTX#-<2FsvG)e&bvobZtBW6f$8THO45Pe=yQAnC94j>BL=2j@|| za#C*cu{?9TqLcXnZhL$cRx}Iu#TFYg5l3yV3N?Cs%GnFl^zBw3Uz5A&17Jx6Z(k_g zY@S!sT;kvu;Xn>m##^==!*pYxJiia2s2$2*;5*^d5okqG4m6GiQT?s=stH7CkbI0n zIYwXIU~6bxxmJmR_ne%tK1{aO5dL^LBW9_}OiLCu*kLCcf#X8@Z<}`B^7Q2|j6*wH zZyV=%$M|PI>eOa-^Uj_DyC;foPDNaJ^fK#sGlB%~4pn~*IC+Yj=00hPHUA zj#Ns00n8%QWb0(0YUicd+C57OCuyMO(b^zfWaU^xZX|fK^ua{M#+U5iM-2XeqISSA zD&lAqNJuTR<|euI3Mr$YxLw4(3+3vDrY^^GlHe$Km$6FK^pCzf`iVqwMJ|`VnDB8) zKq7yLyKKt-4l=SdREk48tRHEWWebuwRxz4VVy4;pL)qFH-TAV4AGnc#iEdmrthN<7 zSz;P-xXa(QLuWbIzT>XmNOkG#RFEbmWWdyb#jVeO8^A8z5Y1{ens*`Fb*S$m^BmpH zeibU_Uvfo9_pL817$t)ZNeRj)L27i|dlgec>dCboZoh|RgEm#V8ij0w)MS269W;$! zK7EG*zS0#aVXA?UezNah37dV_d8q8ccs7?pBx|L^8UI#j#9QF2%hjb6qF<+>oQ=AS zL3)|D1t*tOs+dosD24g%IV37A*a_cm;Qyq;;a|D)!pOLls&y3;N47;HsM790c221w z@1;c|c8Um`j6~Z7`a7shI%lK*$5YslR!DPj-`8C1MYlm#-?7qH=W%!+o#2>1@^9PU zxSVPk-(qj2_>~4OEUy<<V@AKf}#s z17F;tEx%8&hvTBq$)ZTlF|(M>zafT zK}6PokmySakRd^n^TQK|>efw0tJanLho)qnf$rE)W>`@~y~qq0+UV!)Yj$RHs-vZ= zW^XkiR?`h?!S>(~J@K-N3w^%f?(nIf*45j%^YgHxLZyPNVRR+;51?9lp%rD- zY+@+uTQl74*DcFu$Y5r#zva~^&^|e~*xma5je9N1(GQlVqdH7EXAN2hj4W=>wf)yaxzXWq)*-hWqo1!GrmH6rE1^Adb7fOFOm*-EYlLaF&ZefT1|-N z9;rTNXDJCtC>{!q3f8Z~Vt-kKREVWXi^hwbc9qR+8oFB;n`kK;33rT55P#6NtOAMj zdhGF9AbFnRysaS5C>PZp+9{%~J~nL#Xwg+}a5ss+vKA7}YuEt>MH~uxyWaSknr`Ej zrP9lvyE&{rEZKWdV`|`==ym!g*fT307aZ>~?X$AOSQJ~2ejCG36qxWvOvsF z7kvpCAcf&yN#e~;?YJ+uH2=;ydtfr1h(=z5t*q&C8Y!RdHj ziiYtdD#q88b{P30v$&7(+4hpq)oCzb(F+(C6Lw(o(1HB~!?Xw<9v(A%i`rQp$j^3< zYNI?ES544?X=eUBbDDY@V5Z`VeZxCtKd4SN3;0jP9##VMf79y^;|E_t#RMrNn{Dn! zgP@}aMyhA@?^17echn!o6FI^()_Az6(Qo?EdjSj2MoZ_K1B>at|Y#GS+s1@6Of*Y1$j^!UZNY}z@T zMxqcwt*QiC^0?AgrJ7{z^h(HXOvb9=)?I1Xh8r=}mfm^kYl1d{=qdZL=xMYg{!g)Q zSRBAxwdiZIP&PxU3z&1Yk`t~T!saTaJlot<*ph9h0)NH#u1Dy6iEI9Z zbTPi^PtACLHDZJRtxTp2ZCB(R_{!7ow>xlh6B-C?CKl$Tp^%B~v;n5becb*Fk0-Y#(Ypka3Y$XON`y`pYFAzW%YD@<* z&COc-!%-e0ZkNRCrbAHKKro=*N?)mpns8Y*`*Jkao^5Q!SKFt1)=juu@ zvdtnW)#`)KS+EYtp;<_aO1pWfe4?sG$T-etl;L?r=M)b17h;Tfxovgwwts9&!#iz;aTD9)F~I#)<)agS`7V_& z0BOYIk|~OA%YOd6idjLEb@4o4uhHMfSTnq;_DpS6S17QL`kci_ZrA)wQHN0^q#fXL zzChZ3M3jxK&&*9dPusYt8S`^kPDE7YNZr%vVW$~J{gy$80tEJwE@RhDbBbRDEWxSF zc*i5hRf=uGmFYrV3nw%-XnCR5Hli!~PhLVAf)h|RYw%%TqN?&d*3wlU5TGouKby_` z6|DZ!!KNDhTSt?CB2U%&1e)4)H*i1d4y@T&c775{9i?*9$|(t16GDBq%vB|j6aQ6+?WkR zs8(!qd}H!K$m}Bg=h=0B!MvKOwfwHEdvAWHUlX;jFyz{pVMOn?G)UMVsre>Lj?h@MRmU9XTEb(&) zVQgeOdy>d!{7~>~mf}mYu0?*;Q$%8l>ee5rUj+k+5J5O63Nh%+%Q%uynvpF$|2ETq zfg%8FKlz79vJW=T$4|rNVV{aVlph*SYD{`zHK$7sCCcl}Yh%xF2YRkcr+vZX$WdB? zn$DPTZV)bL?qMbI@sO&)?o7r}tU7Lwl<+VDhVWGn;2oqNeY^?*)-T+K?<_SDg+ zsIS4T+tY})-4wQD<$D3E)vmor$8a0eFQVog;T99Ry}+^40V$CqGS^t-j|@wYzrRaM z#K5}fM(r&ny2_9(sNZ$H;Kp2J;2ZKd2)J)Qgs4bP4Tbun@)Qosg-*3^H*I|l`tHpw zV=EKZ++6O9{6zW>QwfD-8Ew0_o)yPS=(QX9p|+uL*;##;#6AAC8!W6291SrlFHil! zy&gU#y|=wzT{&h_biLiLmnT6%(j>YdOSwi%R1COOrYpHv?7_s&wi^*eEqyuQaZ2r2}t*3+`CHs#SmrZYJQhD30t8Pm?$O<=Y{LvA-rRy2WLT*rx z9|KpuM`_;?TzvJ++`w66)I{CP)kSz9J^1f!hj6gHYV70UM)*%y-A4mLY-V0zXu+#G zHIoP^?Tlx7!S9S<5hKRi7^lgHck{T`<565VMIBAfF$usP8k^_K^nB!{ulrMCpSd7_ zF`4eWQH;%Z0Flnk*J#0U-IV*3LEEz~T3Uoz`JOA>R-UHxM8g0#|RgI=R_AUFvL z)ubyBo8^C7oS()48ZLr%y-AYRFIw;pL8C1o^w3+G1f0oBr#xXDMn$HoNr@q@WZY_VG)rGv}O!rN9=eC9~RHA_>RJ(|N$4`P&PB^(EpQ!#p|pcK0# zOoK&oQ5Dl)2*PfuC~^nBlm@kgSU3a}UY3E4w>8<8j>>8C1$p-H6ap5y_Z{}2cIi5*ANI0qK$o=3P;n!QFT1xjxn<<^E(Dex9ef>HP5~CA zyg3mL4WS#ZVxkQLWEgN1@-`1G%F%z1)g~#_IW(a22<6f5T9btU5((1o zrwEMaXm7Vjn=}pmH%O9@xl=bkR-Krh&JtnT<8phVrEU4dmL2@#$}`nEft!55bKDn~ z#*=oXIw^alQARw@BLbd#s3wkP(aZSWL}xCc64k`FyFyaaIa$W$Qtaz9(qBUjPde%- z`zrcdGAx#}4h`Ksq<0c7ZI@=pdup;a-(-ZYP5VKI=xZ+aulc zgZ}`-fT%{JtHDo|vNW%@Uvdncspx;QG36XP`UhoBIL4(DIo*|B4+!xoGHxxCp9P=l z)E=l8)Mc4$i_OdI%E+_kMwC*;-##fAeA+0{kgM}Sl?=7NmgbgJD0@b{5Rm-%Nc@!( zy$n)htVljKf8Do{AFd|4q(t215AmmH{6jcyHX|}mQ`269C-Q4tJJHAc51mz4hB70H zE?fYe@XfY^( z;6=`Z%lbk4@2-R-<;xZ4|88<1#IGq4inh|h$}AibYY*2H9#&X(5k41JMkMADLlE&WR_K;T@uH2jPw2wzkQndG~JhF0g`?)v^v5i_l^E}@Ngz5#J^F5yo#3HetntS@Zll4dUelB^Im58E~*)YpXZh5=p!A2AWNL7|V}VxCfE1wy(*hnMqYqQIzQe z=FL^BInHV@dI;$lDPO>FV+K`r`0{j~Ol^I<-CZ7u$u#?4AaiZC zqm*h>UEa;fxWM40e+&;phC6k^!bv9R6|LDM+q=+LY%*I-_zAUIdpS5$5X@C`3H?Y4r)&uvX4ZQgH^%NF63q{$!$|QLr=A@1Q^GO z!teMsDBs%nHthxQ-+(wgYCLdr%LqydB>TLxx71E=(T0<{cr=eH#Z5N9 z2Y%na*9fGY1b#}(Y1=mxn=-b|#tuy)2e#dLLg(70-KsmmD8uEm$<|3Ld;#Xml|d_e zK6g`|Z-b{-nYH$xJ>_ZLnXU5HTasS~p3eRK^kw62*@l!}k6%lV{{SRe=EsfSwtY^U z6;o@Ie7W@XW5Ta|9dXIUwJqNU;~vOwXE!aIVP)3`H71k2+d40}=QPvMs>7(x(a~Eo z6rEZ+#al@ktwy00%k*Woof}WJq|SL;i?zX;)5+FkZV%b=GfMT~_VrWy8WTcZ9zyu3LvDMi(pan!0 zDcDte6!r}d1QTde1e0dmKnYnTqDj~rP54HszCkpe&Xk!HX(LhEvkyZbjsiENi}Ga~ z@KD`wXcHbl)Ma{Rl6;t2G@pQtTS(e=jC0ATru0zqTN Date: Mon, 25 May 2026 17:44:05 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E5=85=BC=E5=AE=B9=E6=80=A7=E4=BF=AE?= =?UTF-8?q?=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: gong-guilin --- toys/other/free.c | 8 +- toys/other/lsattr.c | 321 +++++++++++----------- toys/other/lspci.c | 128 +++++++++ toys/other/lsusb.c | 256 ++---------------- toys/other/nsenter.c | 93 ++++--- toys/other/pmap.c | 118 ++++---- toys/other/setsid.c | 40 +-- toys/other/stat.c | 51 +++- toys/other/sysctl.c | 32 ++- toys/other/timeout.c | 99 +++---- toys/pending/route.c | 625 ++++++++++++++++++++++++------------------- toys/posix/cpio.c | 214 ++++++--------- toys/posix/patch.c | 368 ++++++++++++------------- toys/posix/sort.c | 158 +++++------ 14 files changed, 1204 insertions(+), 1307 deletions(-) create mode 100644 toys/other/lspci.c diff --git a/toys/other/free.c b/toys/other/free.c index 6be1cdc..ce0df02 100644 --- a/toys/other/free.c +++ b/toys/other/free.c @@ -2,8 +2,8 @@ * * Copyright 2012 Elie De Brauwer -// Flag order is signifcant: b-g are units in order, FLAG_h-1 is unit mask -USE_FREE(NEWTOY(free, "hgmkb[!hgmkb]", TOYFLAG_USR|TOYFLAG_BIN)) +// Flag order is signifcant: b-t are units in order, FLAG_h-1 is unit mask +USE_FREE(NEWTOY(free, "htgmkb[!htgmkb]", TOYFLAG_USR|TOYFLAG_BIN)) config FREE bool "free" @@ -13,7 +13,7 @@ config FREE Display the total, free and used amount of physical memory and swap space. - -bkmg Output units (default is bytes) + -bkmgt Output units (default is bytes) -h Human readable (K=1024) */ @@ -31,7 +31,7 @@ static char *convert(unsigned long d) long long ll = d*TT.units; char *s = TT.buf; - if (FLAG(h)) human_readable(s, ll, 0); + if (toys.optflags & FLAG_h) human_readable(s, ll, 0); else sprintf(s, "%llu",ll>>TT.bits); TT.buf += strlen(TT.buf)+1; diff --git a/toys/other/lsattr.c b/toys/other/lsattr.c index 93af8cd..5a10201 100644 --- a/toys/other/lsattr.c +++ b/toys/other/lsattr.c @@ -7,35 +7,33 @@ * * TODO cleanup -USE_LSATTR(NEWTOY(lsattr, "ldapvR", TOYFLAG_BIN)) -USE_CHATTR(NEWTOY(chattr, "?p#v#R", TOYFLAG_BIN)) +USE_LSATTR(NEWTOY(lsattr, "vldaR", TOYFLAG_BIN)) +USE_CHATTR(NEWTOY(chattr, NULL, TOYFLAG_BIN)) config LSATTR bool "lsattr" default y help - usage: lsattr [-Radlpv] [FILE...] + usage: lsattr [-Radlv] [Files...] - List file attributes on a Linux file system. - Flag letters are defined in chattr help. + List file attributes on a Linux second extended file system. + (AacDdijsStu defined in chattr --help) -R Recursively list attributes of directories and their contents -a List all files in directories, including files that start with '.' -d List directories like other files, rather than listing their contents -l List long flag names - -p List the file's project number -v List the file's version/generation number config CHATTR bool "chattr" default y help - usage: chattr [-R] [-+=AacDdijsStTu] [-p PROJID] [-v VERSION] [FILE...] + usage: chattr [-R] [-+=AacDdijsStTu] [-v version] [File...] - Change file attributes on a Linux file system. + Change file attributes on a Linux second extended file system. -R Recurse - -p Set the file's project number -v Set the file's version/generation number Operators: @@ -44,121 +42,77 @@ config CHATTR '=' Set attributes Attributes: - A No atime a Append only - C No COW c Compression - D Synchronous dir updates d No dump - E Encrypted e Extents - F Case-insensitive (casefold) - I Indexed directory i Immutable - j Journal data - N Inline data in inode - P Project hierarchy - S Synchronous file updates s Secure delete - T Top of dir hierarchy t No tail-merging - u Allow undelete - V Verity + A Don't track atime + a Append mode only + c Enable compress + D Write dir contents synchronously + d Don't backup with dump + i Cannot be modified (immutable) + j Write all data to journal first + s Zero disk storage when deleted + S Write file contents synchronously + t Disable tail-merging of partial blocks with other files + u Allow file to be undeleted */ #define FOR_lsattr #include "toys.h" #include -GLOBALS( - long v, p; - - unsigned add, rm, set; - // !add and !rm tell us whether they were used, but `chattr =` is meaningful. - int have_set; -) - -// Added more recently than the 7 year support horizon. TODO: remove -#ifndef FS_CASEFOLD_FL -#define FS_CASEFOLD_FL 0x40000000 // commit 71e90b4654a92 2019-07-23 -#endif -#ifndef FS_VERITY_FL -#define FS_VERITY_FL 0x00100000 // commit fe9918d3b228b 2019-07-22 -#endif - static struct ext2_attr { char *name; - unsigned flag; + unsigned long flag; char opt; } e2attrs[] = { - // Do not sort! These are in the order that lsattr outputs them. - {"Secure_Deletion", FS_SECRM_FL, 's'}, - {"Undelete", FS_UNRM_FL, 'u'}, - {"Synchronous_Updates", FS_SYNC_FL, 'S'}, - {"Synchronous_Directory_Updates", FS_DIRSYNC_FL, 'D'}, - {"Immutable", FS_IMMUTABLE_FL, 'i'}, - {"Append_Only", FS_APPEND_FL, 'a'}, - {"No_Dump", FS_NODUMP_FL, 'd'}, - {"No_Atime", FS_NOATIME_FL, 'A'}, - {"Compression_Requested", FS_COMPR_FL, 'c'}, - {"Encrypted", FS_ENCRYPT_FL, 'E'}, - {"Journaled_Data", FS_JOURNAL_DATA_FL, 'j'}, - {"Indexed_directory", FS_INDEX_FL, 'I'}, - {"No_Tailmerging", FS_NOTAIL_FL, 't'}, - {"Top_of_Directory_Hierarchies", FS_TOPDIR_FL, 'T'}, - {"Extents", FS_EXTENT_FL, 'e'}, - {"No_COW", FS_NOCOW_FL, 'C'}, - {"Casefold", FS_CASEFOLD_FL, 'F'}, - {"Inline_Data", FS_INLINE_DATA_FL, 'N'}, - {"Project_Hierarchy", FS_PROJINHERIT_FL, 'P'}, - {"Verity", FS_VERITY_FL, 'V'}, - {NULL, 0, 0}, + {"Secure_Deletion", FS_SECRM_FL, 's'}, // Secure deletion + {"Undelete", FS_UNRM_FL, 'u'}, // Undelete + {"Compression_Requested", FS_COMPR_FL, 'c'}, // Compress file + {"Synchronous_Updates", FS_SYNC_FL, 'S'}, // Synchronous updates + {"Immutable", FS_IMMUTABLE_FL, 'i'}, // Immutable file + {"Append_Only", FS_APPEND_FL, 'a'}, // writes to file may only append + {"No_Dump", FS_NODUMP_FL, 'd'}, // do not dump file + {"No_Atime", FS_NOATIME_FL, 'A'}, // do not update atime + {"Indexed_directory", FS_INDEX_FL, 'I'}, // hash-indexed directory + {"Journaled_Data", FS_JOURNAL_DATA_FL, 'j'}, // file data should be journaled + {"No_Tailmerging", FS_NOTAIL_FL, 't'}, // file tail should not be merged + {"Synchronous_Directory_Updates", FS_DIRSYNC_FL, 'D'}, // dirsync behaviour (directories only) + {"Top_of_Directory_Hierarchies", FS_TOPDIR_FL, 'T'}, // Top of directory hierarchies + {NULL, -1, 0}, }; // Get file flags on a Linux second extended file system. -static int ext2_getflag(int fd, struct stat *sb, unsigned *flag) +static int ext2_getflag(int fd, struct stat *sb, unsigned long *flag) { if(!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) { errno = EOPNOTSUPP; return -1; } - return ioctl(fd, FS_IOC_GETFLAGS, flag); -} - -static char *attrstr(unsigned attrs, int full) -{ - struct ext2_attr *a = e2attrs; - char *s = toybuf; - - for (; a->name; a++) - if (attrs & a->flag) *s++ = a->opt; - else if (full) *s++ = '-'; - *s = 0; - - return toybuf; + return (ioctl(fd, FS_IOC_GETFLAGS, (void*)flag)); } static void print_file_attr(char *path) { - unsigned flag = 0, version = 0; - int fd = -1; + unsigned long flag = 0, version = 0; + int fd; struct stat sb; if (!stat(path, &sb) && !S_ISREG(sb.st_mode) && !S_ISDIR(sb.st_mode)) { errno = EOPNOTSUPP; - goto error; + goto LABEL1; } - if (-1 == (fd=open(path, O_RDONLY | O_NONBLOCK))) goto error; + if (-1 == (fd=open(path, O_RDONLY | O_NONBLOCK))) goto LABEL1; - if (FLAG(p)) { - struct fsxattr fsx; - - if (ioctl(fd, FS_IOC_FSGETXATTR, &fsx)) goto error; - xprintf("%5u ", fsx.fsx_projid); - } - if (FLAG(v)) { - if (ioctl(fd, FS_IOC_GETVERSION, (void*)&version) < 0) goto error; - xprintf("%-10u ", version); + if (toys.optflags & FLAG_v) { + if (ioctl(fd, FS_IOC_GETVERSION, (void*)&version) < 0) goto LABEL2; + xprintf("%5lu ", version); } if (ext2_getflag(fd, &sb, &flag) < 0) perror_msg("reading flags '%s'", path); else { struct ext2_attr *ptr = e2attrs; - int name_found = 0; - if (FLAG(l)) { + if (toys.optflags & FLAG_l) { + int name_found = 0; + xprintf("%-50s ", path); for (; ptr->name; ptr++) { if (flag & ptr->flag) { @@ -169,89 +123,127 @@ static void print_file_attr(char *path) } if (!name_found) xprintf("---"); xputc('\n'); - } else xprintf("%s %s\n", attrstr(flag, 1), path); + } else { + int index = 0; + + for (; ptr->name; ptr++) + toybuf[index++] = (flag & ptr->flag) ? ptr->opt : '-'; + toybuf[index] = '\0'; + xprintf("%s %s\n", toybuf, path); + } } - path = 0; -error: xclose(fd); - if (path) perror_msg("reading '%s'", path); + return; +LABEL2: xclose(fd); +LABEL1: perror_msg("reading '%s'", path); } // Get directory information. static int retell_dir(struct dirtree *root) { - char *fpath = 0; - + char *fpath = NULL; + if (root->again) { xputc('\n'); - return 0; } - if (S_ISDIR(root->st.st_mode) && !root->parent) - return DIRTREE_RECURSE|DIRTREE_COMEAGAIN; + if (S_ISDIR(root->st.st_mode) && !root->parent) + return (DIRTREE_RECURSE | DIRTREE_COMEAGAIN); fpath = dirtree_path(root, NULL); - if (*root->name != '.' || FLAG(a)) { + //Special case: with '-a' option and '.'/'..' also included in printing list. + if ((root->name[0] != '.') || (toys.optflags & FLAG_a)) { print_file_attr(fpath); - if (S_ISDIR(root->st.st_mode) && FLAG(R) && dirtree_notdotdot(root)) { + if (S_ISDIR(root->st.st_mode) && (toys.optflags & FLAG_R) + && dirtree_notdotdot(root)) { xprintf("\n%s:\n", fpath); free(fpath); - return DIRTREE_RECURSE|DIRTREE_COMEAGAIN; + return (DIRTREE_RECURSE | DIRTREE_COMEAGAIN); } } free(fpath); - return 0; } void lsattr_main(void) { if (!*toys.optargs) dirtree_read(".", retell_dir); - else for (; *toys.optargs; toys.optargs++) { - struct stat sb; + else + for (; *toys.optargs; toys.optargs++) { + struct stat sb; - if (lstat(*toys.optargs, &sb)) perror_msg("stat '%s'", *toys.optargs); - else if (S_ISDIR(sb.st_mode) && !FLAG(d)) - dirtree_read(*toys.optargs, retell_dir); - else print_file_attr(*toys.optargs);// to handle "./Filename" or "./Dir" - } + if (lstat(*toys.optargs, &sb)) perror_msg("stat '%s'", *toys.optargs); + else if (S_ISDIR(sb.st_mode) && !(toys.optflags & FLAG_d)) + dirtree_read(*toys.optargs, retell_dir); + else print_file_attr(*toys.optargs);// to handle "./Filename" or "./Dir" + } } // Switch gears from lsattr to chattr. +#define CLEANUP_lsattr #define FOR_chattr #include "generated/flags.h" +static struct _chattr { + unsigned long add, rm, set, version; + unsigned char vflag, recursive; +} chattr; + // Set file flags on a Linux second extended file system. -static inline int ext2_setflag(int fd, struct stat *sb, unsigned flag) +static inline int ext2_setflag(int fd, struct stat *sb, unsigned long flag) { if (!S_ISREG(sb->st_mode) && !S_ISDIR(sb->st_mode)) { errno = EOPNOTSUPP; return -1; } - return ioctl(fd, FS_IOC_SETFLAGS, &flag); + return (ioctl(fd, FS_IOC_SETFLAGS, (void*)&flag)); } -static unsigned get_flag_val(char ch) +static unsigned long get_flag_val(char ch) { struct ext2_attr *ptr = e2attrs; - for (; ptr->name; ptr++) if (ptr->opt == ch) return ptr->flag; + for (; ptr->name; ptr++) + if (ptr->opt == ch) return ptr->flag; help_exit("bad '%c'", ch); } // Parse command line argument and fill the chattr structure. static void parse_cmdline_arg(char ***argv) { - char *arg = **argv, *ptr; + char *arg = **argv, *ptr = NULL; while (arg) { - if (*arg=='-') for (ptr = ++arg; *ptr; ptr++) TT.rm |= get_flag_val(*ptr); - else if (*arg=='+') - for (ptr = ++arg; *ptr; ptr++) TT.add |= get_flag_val(*ptr); - else if (*arg=='=') { - TT.have_set = 1; - for (ptr = ++arg; *ptr; ptr++) TT.set |= get_flag_val(*ptr); - } else return; + switch (arg[0]) { + case '-': + for (ptr = ++arg; *ptr; ptr++) { + if (*ptr == 'R') { + chattr.recursive = 1; + continue; + } else if (*ptr == 'v') {// get version from next argv. + char *endptr; + + errno = 0; + arg = *(*argv += 1); + if (!arg) help_exit("bad -v"); + if (*arg == '-') perror_exit("Invalid Number '%s'", arg); + chattr.version = strtoul(arg, &endptr, 0); + if (errno || *endptr) perror_exit("bad version '%s'", arg); + chattr.vflag = 1; + continue; + } else chattr.rm |= get_flag_val(*ptr); + } + break; + case '+': + for (ptr = ++arg; *ptr; ptr++) + chattr.add |= get_flag_val(*ptr); + break; + case '=': + for (ptr = ++arg; *ptr; ptr++) + chattr.set |= get_flag_val(*ptr); + break; + default: return; + } arg = *(*argv += 1); } } @@ -259,14 +251,17 @@ static void parse_cmdline_arg(char ***argv) // Update attribute of given file. static int update_attr(struct dirtree *root) { - char *fpath = 0; - int vv = TT.v, fd; + unsigned long fval = 0; + char *fpath = NULL; + int fd; if (!dirtree_notdotdot(root)) return 0; - // if file is a link and recursive is set or file is not regular+link+dir - // (like fifo or dev file) then escape the file. - if ((S_ISLNK(root->st.st_mode) && FLAG(R)) + /* + * if file is a link and recursive is set or file is not regular+link+dir + * (like fifo or dev file) then escape the file. + */ + if ((S_ISLNK(root->st.st_mode) && chattr.recursive) || (!S_ISREG(root->st.st_mode) && !S_ISLNK(root->st.st_mode) && !S_ISDIR(root->st.st_mode))) return 0; @@ -276,62 +271,44 @@ static int update_attr(struct dirtree *root) free(fpath); return DIRTREE_ABORT; } - - // Any potential flag changes? - if (TT.have_set | TT.add | TT.rm) { - unsigned orig, new; - - // Read current flags. - if (ext2_getflag(fd, &(root->st), &orig) < 0) { - perror_msg("read flags of '%s'", fpath); - free(fpath); - xclose(fd); - return DIRTREE_ABORT; - } - // Apply the requested changes. - if (TT.have_set) new = TT.set; // '='. - else { // '-' and/or '+'. - new = orig; - new &= ~(TT.rm); - new |= TT.add; - if (!S_ISDIR(root->st.st_mode)) new &= ~FS_DIRSYNC_FL; - } - // Write them back if there was any change. - if (orig != new && ext2_setflag(fd, &(root->st), new)<0) - perror_msg("%s: setting flags to =%s failed", fpath, attrstr(new, 0)); + // Get current attr of file. + if (ext2_getflag(fd, &(root->st), &fval) < 0) { + perror_msg("read flags of '%s'", fpath); + free(fpath); + xclose(fd); + return DIRTREE_ABORT; } - - // (FS_IOC_SETVERSION works all the way back to 2.6, but FS_IOC_FSSETXATTR - // isn't available until 4.5.) - if (FLAG(v) && (ioctl(fd, FS_IOC_SETVERSION, &vv)<0)) - perror_msg("%s: setting version to %d failed", fpath, vv); - - if (FLAG(p)) { - struct fsxattr fsx; - int fail = ioctl(fd, FS_IOC_FSGETXATTR, &fsx); - - fsx.fsx_projid = TT.p; - if (fail || ioctl(fd, FS_IOC_FSSETXATTR, &fsx)) - perror_msg("%s: setting projid to %u failed", fpath, fsx.fsx_projid); + if (chattr.set) { // for '=' operator. + if (ext2_setflag(fd, &(root->st), chattr.set) < 0) + perror_msg("setting flags '%s'", fpath); + } else { // for '-' / '+' operator. + fval &= ~(chattr.rm); + fval |= chattr.add; + if (!S_ISDIR(root->st.st_mode)) fval &= ~FS_DIRSYNC_FL; + if (ext2_setflag(fd, &(root->st), fval) < 0) + perror_msg("setting flags '%s'", fpath); } - + // set file version + if (chattr.vflag && (ioctl(fd, FS_IOC_SETVERSION, &chattr.version)<0)) + perror_msg("while setting version on '%s'", fpath); free(fpath); xclose(fd); - return (FLAG(R) && S_ISDIR(root->st.st_mode)) ? DIRTREE_RECURSE : 0; + + return (S_ISDIR(root->st.st_mode) && chattr.recursive) ? DIRTREE_RECURSE : 0; } void chattr_main(void) { char **argv = toys.optargs; + memset(&chattr, 0, sizeof(struct _chattr)); parse_cmdline_arg(&argv); - if (TT.p < 0 || TT.p > UINT_MAX) error_exit("bad projid %lu", TT.p); - if (TT.v < 0 || TT.v > UINT_MAX) error_exit("bad version %ld", TT.v); if (!*argv) help_exit("no file"); - if (TT.have_set && (TT.add || TT.rm)) + if (chattr.set && (chattr.add || chattr.rm)) error_exit("no '=' with '-' or '+'"); - if (TT.rm & TT.add) error_exit("set/unset same flag"); - if (!(TT.add || TT.rm || TT.have_set || FLAG(p) || FLAG(v))) - error_exit("need '-p', '-v', '=', '-', or '+'"); + if (chattr.rm & chattr.add) error_exit("set/unset same flag"); + if (!(chattr.add || chattr.rm || chattr.set || chattr.vflag)) + error_exit("need '-v', '=', '-' or '+'"); for (; *argv; argv++) dirtree_read(*argv, update_attr); + toys.exitval = 0; //always set success at this point. } diff --git a/toys/other/lspci.c b/toys/other/lspci.c new file mode 100644 index 0000000..c208484 --- /dev/null +++ b/toys/other/lspci.c @@ -0,0 +1,128 @@ +/* + * lspci - written by Isaac Dunham + +USE_LSPCI(NEWTOY(lspci, "emkn"USE_LSPCI_TEXT("@i:"), TOYFLAG_USR|TOYFLAG_BIN)) + +config LSPCI + bool "lspci" + default y + help + usage: lspci [-ekm] + + List PCI devices. + + -e Print all 6 digits in class + -k Print kernel driver + -m Machine parseable format + +config LSPCI_TEXT + bool "lspci readable output" + depends on LSPCI + default y + help + usage: lspci [-n] [-i FILE ] + + -n Numeric output (repeat for readable and numeric) + -i PCI ID database (default /usr/share/misc/pci.ids) + +*/ + +#define FOR_lspci +#include "toys.h" + +GLOBALS( + char *i; + long n; + + FILE *db; +) + +static int do_lspci(struct dirtree *new) +{ + char *p = toybuf, *vendor = toybuf+9, *device = toybuf+18, + driver[256], *vbig = 0, *dbig = 0, **fields; + int dirfd; + + if (!new->parent) return DIRTREE_RECURSE; + + // Parse data out of /proc + + if (-1 == (dirfd = openat(dirtree_parentfd(new), new->name, O_RDONLY))) + return 0; + + *driver = 0; + if (toys.optflags & FLAG_k) + readlinkat0(dirfd, "driver", driver, sizeof(driver)); + + for (fields = (char*[]){"class", "vendor", "device", 0}; *fields; fields++) { + int fd, size = 6 + 2*((toys.optflags & FLAG_e) && p == toybuf); + *p = 0; + + if (-1 == (fd = openat(dirfd, *fields, O_RDONLY))) { + close(dirfd); + return 0; + } + xreadall(fd, p, size); + memmove(p, p+2, size -= 2); + p[size] = 0; + close(fd); + p += 9; + } + + close(dirfd); + + // Lookup/display data from pci.ids? + + if (CFG_LSPCI_TEXT && TT.db) { + if (TT.n != 1) { + char *s; + + fseek(TT.db, 0, SEEK_SET); + while (!vbig || !dbig) { + s = p; + if (!fgets(s, sizeof(toybuf)-(p-toybuf)-1, TT.db)) break; + while (isspace(*s)) s++; + if (*s == '#') continue; + if (vbig && s == p) break; + if (strstart(&s, vbig ? device : vendor)) { + if (vbig) dbig = s+2; + else vbig = s+2; + s += strlen(s); + s[-1] = 0; // trim ending newline + p = s + 1; + } + } + } + + if (TT.n > 1) { + printf((toys.optflags & FLAG_m) + ? "%s, \"%s\" \"%s [%s]\" \"%s [%s]\"" + : "%s Class %s: %s [%s] %s [%s]", + new->name+5, toybuf, vbig ? vbig : "", vendor, + dbig ? dbig : "", device); + + goto driver; + } + } + + printf((toys.optflags & FLAG_m) ? "%s \"%s\" \"%s\" \"%s\"" + : "%s Class %s: %s:%s", new->name+5, toybuf, + vbig ? vbig : vendor, dbig ? dbig : device); + +driver: + if (*driver) + printf((toys.optflags & FLAG_m) ? " \"%s\"" : " %s", basename(driver)); + xputc('\n'); + + return 0; +} + +void lspci_main(void) +{ + if (CFG_LSPCI_TEXT && TT.n != 1) { + if (!TT.i) TT.i = "/usr/share/misc/pci.ids"; + if (!(TT.db = fopen(TT.i, "r"))) perror_msg("%s", TT.i); + } + + dirtree_read("/sys/bus/pci/devices", do_lspci); +} diff --git a/toys/other/lsusb.c b/toys/other/lsusb.c index 3abbe47..031dbd9 100644 --- a/toys/other/lsusb.c +++ b/toys/other/lsusb.c @@ -1,261 +1,49 @@ /* lsusb.c - list available USB devices * * Copyright 2013 Andre Renaud - * Copyright 2013 Isaac Dunham -USE_LSUSB(NEWTOY(lsusb, "i:", TOYFLAG_USR|TOYFLAG_BIN)) -USE_LSPCI(NEWTOY(lspci, "eDmkn@x@i:", TOYFLAG_USR|TOYFLAG_BIN)) - -config LSPCI - bool "lspci" - default y - help - usage: lspci [-ekmn] [-i FILE] - - List PCI devices. - - -e Extended (6 digit) class - -i ID database (default /etc/pci.ids[.gz]) - -k Show kernel driver - -m Machine readable - -n Numeric output (-nn for both) - -D Print domain numbers - -x Hex dump of config space (64 bytes; -xxx for 256, -xxxx for 4096) +USE_LSUSB(NEWTOY(lsusb, NULL, TOYFLAG_USR|TOYFLAG_BIN)) config LSUSB bool "lsusb" default y help - usage: lsusb [-i] + usage: lsusb List USB hosts/devices. - - -i ID database (default /etc/usb.ids[.gz]) */ -#define FOR_lsusb #include "toys.h" -GLOBALS( - char *i; - long x, n; - - void *ids, *class; - int count; -) - -struct dev_ids { - struct dev_ids *next, *child; - int id; - char name[]; -}; - -struct scanloop { - char *pattern; - void *d1, *d2; -}; - -// Common function to read uevent file under /proc for both pci and usb -// note that %s is omitted (because pointer is into toybuf, avoiding copy). -static int scan_uevent(struct dirtree *new, int len, struct scanloop *sl) -{ - int ii, saw = 0; - off_t flen = sizeof(toybuf); - char *ss, *yy; - - // Read data - if (*new->name == '.') return 0; - sprintf(toybuf, "%s/uevent", new->name); - if (!readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &flen)) return 0; - - // Loop over lines - while ((flen = strcspn(ss, "\n"))) { - if (ss[flen]) ss[flen++] = 0; - yy = ss+flen; - - // Try each pattern - for (ii = 0; iinext) { - if (id1 != ids->id) continue; - *name1 = ids->name; - for (ids = ids->child; ids; ids = ids->next) { - if (id2 != ids->id) continue; - *name2 = ids->name; - return; - } - return; - } -} - -// Search for pci.ids or usb.ids and return parsed structure or NULL -struct dev_ids *parse_dev_ids(char *name, struct dev_ids **and) -{ - char *path = "/etc:/vendor:/usr/share/hwdata:/usr/share/misc"; - struct string_list *sl = 0; - FILE *fp; - char *s, *ss, *sss; - struct dev_ids *ids = 0, *new; - int fd = -1; - - // Open compressed or uncompressed file - signal(SIGCHLD, SIG_IGN); - s = TT.i; - if (!s) { - sprintf(toybuf, "%s.gz", name); - if ((sl = find_in_path(path, toybuf)) || (sl = find_in_path(path, name))) - s = sl->str; - } - if (s && strend(s, ".gz")) xpopen((char *[]){"zcat", sl->str, 0}, &fd, 1); - else if (s) fd = xopen(s, O_RDONLY); - llist_traverse(sl, free); - if (fd == -1) return 0; - - for (fp = fdopen(fd, "r"); (s = ss = xgetline(fp)); free(s)) { - // TODO parse and use third level instead of skipping it here - if (s[strspn(s, " \t")]=='#' || strstart(&ss, "\t\t")) continue; - - // Switch to device class list? - if (strstart(&ss, "C ") && and) { - *and = ids; - and = 0; - } - fd = estrtol(sss = ss, &ss, 16); - if (ss>sss && *ss++==' ') { - while (isspace(*ss)) ss++; - new = xmalloc(sizeof(*new)+strlen(ss)+1); - new->child = 0; - new->id = fd; - strcpy(new->name, ss); - if (!ids || *s!='\t') { - new->next = ids; - ids = new; - } else { - new->next = ids->child; - ids->child = new; - } - } - } - fclose(fp); - - return ids; -} - -static int list_usb(struct dirtree *new) +static int list_device(struct dirtree *new) { + FILE *file; + char *name; int busnum = 0, devnum = 0, pid = 0, vid = 0; - char *n1, *n2; if (!new->parent) return DIRTREE_RECURSE; - if (7 == scan_uevent(new, 3, (struct scanloop[]){{"BUSNUM=%u", &busnum, 0}, - {"DEVNUM=%u", &devnum, 0}, {"PRODUCT=%x/%x", &pid, &vid}})) - { - get_names(TT.ids, pid, vid, &n1, &n2); - printf("Bus %03d Device %03d: ID %04x:%04x %s %s\n", - busnum, devnum, pid, vid, n1, n2); + if (new->name[0] == '.') return 0; + name = dirtree_path(new, 0); + sprintf(toybuf, "%s/uevent", name); + file = fopen(toybuf, "r"); + if (file) { + int count = 0; + + while (fgets(toybuf, sizeof(toybuf), file)) + if (sscanf(toybuf, "BUSNUM=%u\n", &busnum) + || sscanf(toybuf, "DEVNUM=%u\n", &devnum) + || sscanf(toybuf, "PRODUCT=%x/%x/", &pid, &vid)) count++; + + if (count == 3) + printf("Bus %03d Device %03d: ID %04x:%04x\n", busnum, devnum, pid, vid); + fclose(file); } + free(name); return 0; } void lsusb_main(void) { - // Parse http://www.linux-usb.org/usb.ids file (if available) - TT.ids = parse_dev_ids("usb.ids", 0); - dirtree_read("/sys/bus/usb/devices/", list_usb); -} - -#define FOR_lspci -#include "generated/flags.h" - -// TODO: -v -static int list_pci(struct dirtree *new) -{ - char *driver = 0, buf[16], *ss, *names[3]; - int cvd[3] = {0}, ii, revision = 0; - off_t len = sizeof(toybuf); - /* skip 0000: part by default */ - char *bus = strchr(new->name, ':') + 1; - -// Output formats: -n, -nn, -m, -nm, -nnm, -k - - if (!new->parent) return DIRTREE_RECURSE; - if (!bus || strlen(new->name)<6) return 0; - TT.count = 0; - - // Load revision - sprintf(toybuf, "%s/revision", new->name); - if (readfileat(dirtree_parentfd(new), ss = toybuf, toybuf, &len)) { - strstart(&ss, "0x"); - sscanf(ss, "%x", &revision); - } - - // Load uevent data, look up names in database - if (6>scan_uevent(new, 3, (struct scanloop[]){{"DRIVER=", &driver, 0}, - {"PCI_CLASS=%x", cvd, 0}, {"PCI_ID=%x:%x", cvd+1, cvd+2}})) return 0; - get_names(TT.class, 255&(cvd[0]>>16), 255&(cvd[0]>>8), names, names); - get_names(TT.ids, cvd[1], cvd[2], names+1, names+2); - if (!FLAG(e)) cvd[0] >>= 8; - - // Output line according to flags - if (FLAG(D) || strncmp(new->name, "0000:", bus-new->name)) bus = new->name; - printf("%s", bus); - for (ii = 0; ii<3; ii++) { - sprintf(buf, "%0*x", 6-2*(ii||!FLAG(e)), cvd[ii]); - if (!TT.n) printf(FLAG(m) ? " \"%s\"" : ": %s"+(ii!=1), names[ii] ? : buf); - else if (TT.n==1) printf(FLAG(m) ? " \"%s\"" : (ii==2)?"%s ":" %s:", buf); - else if (!FLAG(m)) { - // This one permutes the order, so do it all first time and abort loop - printf(" %s [%s]: %s %s [%04x:%04x]", names[0], buf, names[1], names[2], - cvd[1], cvd[2]); - break; - } else printf(" \"%s [%s]\"", names[ii], buf); - } - if (revision) printf(FLAG(m) ? " -r%02x" : " (rev %02x)", revision); - if (FLAG(k) && driver) printf(FLAG(m) ? " \"%s\"" : " %s", driver); - xputc('\n'); - - if (TT.x) { - FILE *fp; - int b, col = 0, max = (TT.x >= 4) ? 4096 : ((TT.x >= 3) ? 256 : 64); - - snprintf(toybuf, sizeof(toybuf), "/sys/bus/pci/devices/%s/config", new->name); - fp = xfopen(toybuf, "r"); - while ((b = fgetc(fp)) != EOF) { - if ((col % 16) == 0) printf("%02x: ", col & 0xf0); - printf("%02x ", (b & 0xff)); - if ((++col % 16) == 0) xputc('\n'); - if (col == max) break; - } - xputc('\n'); - fclose(fp); - } - - return 0; -} - -void lspci_main(void) -{ - // Parse https://pci-ids.ucw.cz/v2.2/pci.ids (if available) - if (TT.n != 1) TT.class = parse_dev_ids("pci.ids", (void *)&TT.ids); - dirtree_read("/sys/bus/pci/devices/", list_pci); + dirtree_read("/sys/bus/usb/devices/", list_device); } diff --git a/toys/other/nsenter.c b/toys/other/nsenter.c index bafed47..dfcfbe8 100644 --- a/toys/other/nsenter.c +++ b/toys/other/nsenter.c @@ -12,52 +12,45 @@ * // Note: flags go in same order (right to left) for shared subset -USE_NSENTER(NEWTOY(nsenter, "<1a(all)F(no-fork)t#<1(target)C(cgroup):; i(ipc):; m(mount):; n(net):; p(pid):; u(uts):; U(user):; ", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT)) -USE_UNSHARE(NEWTOY(unshare, "<1^a(all)f(fork)r(map-root-user)C(cgroup):; i(ipc):; m(mount):; n(net):; p(pid):; u(uts):; U(user):; ", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_STAYROOT)) +USE_NSENTER(NEWTOY(nsenter, "<1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN)) +USE_UNSHARE(NEWTOY(unshare, "<1^f(fork);r(map-root-user);i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN)) config UNSHARE bool "unshare" default y + depends on TOYBOX_CONTAINER help usage: unshare [-imnpuUr] COMMAND... - Create new container namespace(s) for this process and its children, allowing - the new set of processes to have a different view of the system than the - parent process. + Create new container namespace(s) for this process and its children, so + some attribute is not shared with the parent process. - -a Unshare all supported namespaces -f Fork command in the background (--fork) - -r Become root (map current euid/egid to 0/0, implies -U) (--map-root-user) - - Available namespaces: - -C Control groups (--cgroup) -i SysV IPC (message queues, semaphores, shared memory) (--ipc) -m Mount/unmount tree (--mount) -n Network address, sockets, routing, iptables (--net) -p Process IDs and init (--pid) + -r Become root (map current euid/egid to 0/0, implies -U) (--map-root-user) -u Host and domain names (--uts) -U UIDs, GIDs, capabilities (--user) - Each namespace can take an optional argument, a persistent mountpoint usable - by the nsenter command to add new processes to that the namespace. (Specify - multiple namespaces to unshare separately, ala -c -i -m because -cim is -c - with persistent mount "im".) + A namespace allows a set of processes to have a different view of the + system than other sets of processes. config NSENTER bool "nsenter" + depends on TOYBOX_CONTAINER default y help usage: nsenter [-t pid] [-F] [-i] [-m] [-n] [-p] [-u] [-U] COMMAND... Run COMMAND in an existing (set of) namespace(s). - -a Enter all supported namespaces (--all) - -F don't fork, even if -p is used (--no-fork) -t PID to take namespaces from (--target) + -F don't fork, even if -p is used (--no-fork) The namespaces to switch are: - -C Control groups (--cgroup) -i SysV IPC: message queues, semaphores, shared memory (--ipc) -m Mount/unmount tree (--mount) -n Network address, sockets, routing, iptables (--net) @@ -71,41 +64,62 @@ config NSENTER #define FOR_nsenter #include "toys.h" +#include #include #define unshare(flags) syscall(SYS_unshare, flags) #define setns(fd, nstype) syscall(SYS_setns, fd, nstype) GLOBALS( - char *UupnmiC[7]; + char *Uupnmi[6]; long t; ) // Code that must run in unshare's flag context +#define CLEANUP_nsenter #define FOR_unshare #include static void write_ugid_map(char *map, unsigned eugid) { - int fd = xopen(map, O_WRONLY); + int bytes = sprintf(toybuf, "0 %u 1", eugid), fd = xopen(map, O_WRONLY); - dprintf(fd, "0 %u 1", eugid); + xwrite(fd, toybuf, bytes); xclose(fd); } -static int test_a() { return FLAG(a); } -static int test_r() { return FLAG(r); } -static int test_f() { return FLAG(f); } +static void handle_r(int euid, int egid) +{ + int fd; + + if ((fd = open("/proc/self/setgroups", O_WRONLY)) >= 0) { + xwrite(fd, "deny", 4); + close(fd); + } + + write_ugid_map("/proc/self/uid_map", euid); + write_ugid_map("/proc/self/gid_map", egid); +} + +static int test_r() +{ + return toys.optflags & FLAG_r; +} + +static int test_f() +{ + return toys.optflags & FLAG_f; +} // Shift back to the context GLOBALS lives in (I.E. matching the filename). +#define CLEANUP_unshare #define FOR_nsenter #include void unshare_main(void) { - char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc\0cgroup"; unsigned flags[]={CLONE_NEWUSER, CLONE_NEWUTS, CLONE_NEWPID, CLONE_NEWNET, - CLONE_NEWNS, CLONE_NEWIPC, CLONE_NEWCGROUP}, f = 0; + CLONE_NEWNS, CLONE_NEWIPC}, f = 0; int i, fd; // Create new namespace(s)? @@ -117,17 +131,10 @@ void unshare_main(void) if (test_r()) toys.optflags |= FLAG_U; for (i = 0; i= 0) { - xwrite(fd, "deny", 4); - close(fd); - } + if (toys.optflags & (1< * * No Standard. - * - * TODO: two passes so we can auto-size the columns? -USE_PMAP(NEWTOY(pmap, "<1pqx", TOYFLAG_USR|TOYFLAG_BIN)) +USE_PMAP(NEWTOY(pmap, "<1xq", TOYFLAG_USR|TOYFLAG_BIN)) config PMAP bool "pmap" default y help - usage: pmap [-pqx] PID... + usage: pmap [-xq] [pids...] Report the memory map of a process or processes. - -p Show full paths - -q Do not show header or footer -x Show the extended format + -q Do not display some header/footer lines */ #define FOR_pmap @@ -27,77 +24,90 @@ config PMAP void pmap_main(void) { - char **optargs, *line = 0; - size_t len = 0; + char **optargs; for (optargs = toys.optargs; *optargs; optargs++) { - long long start, end, pss, tpss=0, dirty, tdirty=0, swap, tswap=0, total=0; - char *name = 0, *k = "K"+FLAG(x), mode[5]; pid_t pid = atolx(*optargs); - int extras = 0, off, count; FILE *fp; + char *line, *oldline = 0, *name = 0, + *k = (toys.optflags & FLAG_x) ? "" : "K"; + size_t len; + long long start, end, pss, tpss = 0, dirty, tdirty = 0, swap, tswap = 0, + total = 0; + int xx = 0; - sprintf(toybuf, "/proc/%u/cmdline", pid); - if (!(name = readfile(toybuf, 0, 0))) { - error_msg("no %s", toybuf); - continue; - } - xprintf("%d: %s\n", pid, name); - free(name); + snprintf(toybuf, sizeof(toybuf), "/proc/%u/cmdline", pid); + line = readfile(toybuf, 0, 0); + if (!line) error_msg("No %lu", (long)pid); + xprintf("%u: %s\n", (int)pid, line); + free(line); - // Only bother scanning the more verbose smaps file in -x mode. - sprintf(toybuf, "/proc/%u/%smaps", pid, "s"+!FLAG(x)); + // Header + // Only use the more verbose file in -x mode + sprintf(toybuf, "/proc/%u/%smaps", pid, + (toys.optflags & FLAG_x) ? "s" : ""); if (!(fp = fopen(toybuf, "r"))) { - error_msg("no %s", toybuf); - continue; + error_msg("No %ld\n", (long)pid); + return; } - if (FLAG(x) && !FLAG(q)) - xprintf("Address%*cKbytes PSS Dirty Swap Mode Mapping\n", - (int)(sizeof(long)*2)-5, ' '); + if ((toys.optflags & (FLAG_q|FLAG_x)) == FLAG_x) + xprintf("Address%*cKbytes PSS Dirty Swap Mode Mapping\n", + (int)(sizeof(long)*2)-4, ' '); + + // Loop through mappings + for (;;) { + int off, count; + + line = 0; + if (0 >= getline(&line, &len, fp)) break; + count = sscanf(line, "%llx-%llx %s %*s %*s %*s %n", + &start, &end, toybuf, &off); - while (getline(&line, &len, fp) > 0) { - count = sscanf(line, "%llx-%llx %4s %*s %*s %*s %n", &start, &end, mode, - &off); if (count == 3) { name = line[off] ? line+off : " [anon]\n"; - if (mode[3] == 'p') mode[3] = '-'; + if (toybuf[3] == 'p') toybuf[3] = '-'; total += end = (end-start)/1024; - printf("%0*llx % *lld%s ", (int)(2*sizeof(long)), start, 6+FLAG(x), - end, k); - if (FLAG(x)) { - strcpy(toybuf, name); - name = toybuf; + printf("%0*llx % *lld%s ", (int)(2*sizeof(long)), start, + 6+!!(toys.optflags & FLAG_x), end, k); + if (toys.optflags & FLAG_x) { + oldline = line; continue; } } else { - if (sscanf(line, "Pss: %lld", &pss) || - sscanf(line, "Private_Dirty: %lld", &dirty) || - sscanf(line, "Swap: %lld", &swap)) extras++; - if (extras==3) { - printf("% 7lld %7lld %7lld ", pss, dirty, swap); - tpss += pss; - tdirty += dirty; - tswap += swap; - extras = 0; - } else continue; + if (0 -USE_SETSID(NEWTOY(setsid, "^<1wc@d[!dc]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_SETSID(NEWTOY(setsid, "^<1t", TOYFLAG_USR|TOYFLAG_BIN)) config SETSID bool "setsid" default y help - usage: setsid [-cdw] command [args...] + usage: setsid [-t] command [args...] Run process in a new session. - -d Detach from tty - -c Control tty (repeat to steal) - -w Wait for child (and exit with its status) + -t Grab tty (become foreground process, receiving keyboard signals) */ -#define FOR_setsid #include "toys.h" -GLOBALS( - long c; -) - void setsid_main(void) { - int i; - - // setsid() fails if we're already session leader, ala "exec setsid" from sh. - // Second call can't fail, so loop won't continue endlessly. - while (setsid()<0) { - pid_t pid; - - // This must be before vfork() or tcsetpgrp() will hang waiting for parent. + while (setsid()<0) if (XVFORK()) _exit(0); + if (toys.optflags) { setpgid(0, 0); - - pid = XVFORK(); - if (pid) { - i = 0; - if (FLAG(w)) { - i = 127; - if (pid>0) i = xwaitpid(pid); - } - _exit(i); - } - } - - if (FLAG(c)) { - ioctl(0, TIOCSCTTY, TT.c>1); tcsetpgrp(0, getpid()); - } if (FLAG(d) && (i = open("/dev/tty", O_RDONLY)) != -1) { - ioctl(i, TIOCNOTTY); - close(i); } xexec(toys.optargs); } diff --git a/toys/other/stat.c b/toys/other/stat.c index 463aa46..840b29a 100644 --- a/toys/other/stat.c +++ b/toys/other/stat.c @@ -33,8 +33,8 @@ config STAT The valid format escape sequences for filesystems: %a Available blocks |%b Total blocks |%c Total inodes %d Free inodes |%f Free blocks |%i File system ID - %l Max filename length |%n File name |%s Best transfer size - %S Actual block size |%t FS type (hex) |%T FS type (driver name) + %l Max filename length |%n File name |%s Fragment size + %S Best transfer size |%t FS type (hex) |%T FS type (driver name) */ #define FOR_stat @@ -141,29 +141,52 @@ static void print_statfs(char type) { else if (type == 'c') out('u', statfs->f_files); else if (type == 'd') out('u', statfs->f_ffree); else if (type == 'f') out('u', statfs->f_bfree); - else if (type == 'l') out('d', pathconf(TT.file, _PC_NAME_MAX)); - else if (type == 't') out('x', statfs->f_type); - else if (type == 'T') strout(fs_type_name(statfs)); - else if (type == 'i') { + else if (type == 'l') { +#ifdef __APPLE__ + // TODO: move this into portability.c somehow, or just use this everywhere? + // (glibc and bionic will just re-do the statfs and return f_namelen.) + out('d', pathconf(TT.file, _PC_NAME_MAX)); +#else + out('d', statfs->f_namelen); +#endif + } else if (type == 't') out('x', statfs->f_type); + else if (type == 'T') { + char *s = "unknown"; + struct {unsigned num; char *name;} nn[] = { + {0xADFF, "affs"}, {0x5346544e, "ntfs"}, {0x1Cd1, "devpts"}, + {0x137D, "ext"}, {0xEF51, "ext2"}, {0xEF53, "ext3"}, + {0x1BADFACE, "bfs"}, {0x9123683E, "btrfs"}, {0x28cd3d45, "cramfs"}, + {0x3153464a, "jfs"}, {0x7275, "romfs"}, {0x01021994, "tmpfs"}, + {0x3434, "nilfs"}, {0x6969, "nfs"}, {0x9fa0, "proc"}, + {0x534F434B, "sockfs"}, {0x62656572, "sysfs"}, {0x517B, "smb"}, + {0x4d44, "msdos"}, {0x4006, "fat"}, {0x43415d53, "smackfs"}, + {0x73717368, "squashfs"} + }; + int i; + + for (i=0; if_type) s = nn[i].name; + strout(s); + } else if (type == 'i') { int *val = (int *) &statfs->f_fsid; char buf[32]; sprintf(buf, "%08x%08x", val[0], val[1]); strout(buf); - } else if (type == 's') out('d', statfs_bsize(statfs)); - else if (type == 'S') out('d', statfs_frsize(statfs)); + } else if (type == 's') out('d', statfs->f_frsize); + else if (type == 'S') out('d', statfs->f_bsize); else strout("?"); } void stat_main(void) { - int i; + int flagf = FLAG(f), i; char *format, *f; - if (FLAG(t)) format = FLAG(f) + if (FLAG(t)) format = flagf ? "%n %i %l %t %s %S %b %f %a %c %d" : "%n %s %b %f %u %g %D %i %h %t %T %X %Y %Z %o"; - else format = FLAG(f) + else format = flagf ? " File: \"%n\"\n ID: %i Namelen: %l Type: %T\n" "Block Size: %s Fundamental block size: %S\n" "Blocks: Total: %b\tFree: %f\tAvailable: %a\n" @@ -180,8 +203,8 @@ void stat_main(void) // stat the file or filesystem TT.file = toys.optargs[i]; - if (FLAG(f) && !statfs(TT.file, (void *)&TT.stat)); - else if (FLAG(f) || (FLAG(L) ? stat : lstat)(TT.file, (void *)&TT.stat)) { + if (flagf && !statfs(TT.file, (void *)&TT.stat)); + else if (flagf || (FLAG(L) ? stat : lstat)(TT.file, (void *)&TT.stat)) { perror_msg("'%s'", TT.file); continue; } @@ -195,7 +218,7 @@ void stat_main(void) TT.patlen = f-TT.pattern; if (!*f || TT.patlen>99) error_exit("bad %s", TT.pattern); if (*f == 'n') strout(TT.file); - else if (FLAG(f)) print_statfs(*f); + else if (flagf) print_statfs(*f); else print_stat(*f); } } diff --git a/toys/other/sysctl.c b/toys/other/sysctl.c index 7a2cba6..3773594 100644 --- a/toys/other/sysctl.c +++ b/toys/other/sysctl.c @@ -4,18 +4,18 @@ * Copyright 2014 Kyungwan Han * * No Standard - + USE_SYSCTL(NEWTOY(sysctl, "^neNqwpaA[!ap][!aq][!aw][+aA]", TOYFLAG_SBIN)) config SYSCTL bool "sysctl" default y help - usage: sysctl [-aeNnqw] [-p [FILE] | KEY[=VALUE]...] + usage: sysctl [-aAeNnqw] [-p [FILE] | KEY[=VALUE]...] Read/write system control data (under /proc/sys). - -a Show all values + -a,A Show all values -e Don't warn about unknown keys -N Don't print key values -n Don't print key names @@ -43,8 +43,9 @@ static void replace_char(char *str, char old, char new) static void key_error(char *key) { - if (errno != ENOENT) perror_msg("key '%s'", key); - else if (!FLAG(e)) error_msg("unknown key '%s'", key); + if (errno == ENOENT) { + if (!(toys.optflags & FLAG_e)) error_msg("unknown key '%s'", key); + } else perror_msg("key '%s'", key); } static int write_key(char *path, char *key, char *value) @@ -76,11 +77,11 @@ static int do_show_keys(struct dirtree *dt) if (!data) key_error(key); else { // Print the parts that aren't switched off by flags. - if (!FLAG(n)) xprintf("%s", key); - if (!FLAG(N) && !FLAG(n)) xprintf(" = "); + if (!(toys.optflags & FLAG_n)) xprintf("%s", key); + if (!(toys.optflags & (FLAG_N|FLAG_n))) xprintf(" = "); for (key = data+strlen(data); key > data && isspace(*--key); *key = 0); - if (!FLAG(N)) xprintf("%s", data); - if (!FLAG(N) || !FLAG(n)) xputc('\n'); + if (!(toys.optflags & FLAG_N)) xprintf("%s", data); + if ((toys.optflags & (FLAG_N|FLAG_n)) != (FLAG_N|FLAG_n)) xputc('\n'); } free(data); @@ -95,11 +96,16 @@ static void process_key(char *key, char *value) char *path; if (!value) value = split_key(key); - if (FLAG(w) && !value) return error_msg("'%s' not key=value", key); + if ((toys.optflags & FLAG_w) && !value) { + error_msg("'%s' not key=value", key); + + return; + } + path = xmprintf("/proc/sys/%s", key); replace_char(path, '.', '/'); // Note: failure to assign to a non-leaf node suppresses the display. - if (!(value && (!write_key(path, key, value) || FLAG(q)))) { + if (!(value && (!write_key(path, key, value) || (toys.optflags & FLAG_q)))) { if (!access(path, R_OK)) dirtree_read(path, do_show_keys); else key_error(key); } @@ -111,10 +117,10 @@ void sysctl_main() char **args = 0; // Display all keys - if (FLAG(a)) dirtree_read("/proc/sys", do_show_keys); + if (toys.optflags & FLAG_a) dirtree_read("/proc/sys", do_show_keys); // read file - else if (FLAG(p)) { + else if (toys.optflags & FLAG_p) { FILE *fp = xfopen(*toys.optargs ? *toys.optargs : "/etc/sysctl.conf", "r"); size_t len; diff --git a/toys/other/timeout.c b/toys/other/timeout.c index 4c4288e..55b3dca 100644 --- a/toys/other/timeout.c +++ b/toys/other/timeout.c @@ -4,13 +4,13 @@ * * No standard -USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):i", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125))) +USE_TIMEOUT(NEWTOY(timeout, "<2^(foreground)(preserve-status)vk:s(signal):", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(125))) config TIMEOUT bool "timeout" default y help - usage: timeout [-iv] [-k DURATION] [-s SIGNAL] DURATION COMMAND... + usage: timeout [-k DURATION] [-s SIGNAL] DURATION COMMAND... Run command line as a child process, sending child a signal if the command doesn't exit soon enough. @@ -18,9 +18,8 @@ config TIMEOUT DURATION can be a decimal fraction. An optional suffix can be "m" (minutes), "h" (hours), "d" (days), or "s" (seconds, the default). - -i Only kill for inactivity (restart timeout when command produces output) - -k Send KILL signal if child still running this long after first signal -s Send specified signal (default TERM) + -k Send KILL signal if child still running this long after first signal -v Verbose --foreground Don't create new process group --preserve-status Exit with the child's exit status @@ -32,76 +31,60 @@ config TIMEOUT GLOBALS( char *s, *k; - struct pollfd pfd; - sigjmp_buf sj; - int fds[2], pid, rc; + int nextsig; + pid_t pid; + struct timeval ktv; + struct itimerval itv; ) -static void handler(int sig, siginfo_t *si) +static void handler(int i) { - TT.rc = si->si_status + ((si->si_code!=CLD_EXITED)<<7); - siglongjmp(TT.sj, 1); + if (FLAG(v)) + fprintf(stderr, "timeout pid %d signal %d\n", TT.pid, TT.nextsig); + + toys.exitval = (TT.nextsig==9) ? 137 : 124; + kill(TT.pid, TT.nextsig); + if (TT.k) { + TT.k = 0; + TT.nextsig = SIGKILL; + xsignal(SIGALRM, handler); + TT.itv.it_value = TT.ktv; + setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf); + } } -static long nantomil(struct timespec *ts) +// timeval inexplicably makes up a new type for microseconds, despite timespec's +// nanoseconds field (needing to store 1000* the range) using "long". Bravo. +void xparsetimeval(char *s, struct timeval *tv) { - return ts->tv_sec*1000+ts->tv_nsec/1000000; -} + long ll; -static void callback(char *argv[]) -{ - if (!FLAG(foreground)) setpgid(0, 0); + tv->tv_sec = xparsetime(s, 6, &ll); + tv->tv_usec = ll; } void timeout_main(void) { - int ii, ms, nextsig = SIGTERM; - struct timespec tts, kts; - // Use same ARGFAIL value for any remaining parsing errors toys.exitval = 125; - xparsetimespec(*toys.optargs, &tts); - if (TT.k) xparsetimespec(TT.k, &kts); - if (TT.s && -1==(nextsig = sig_to_num(TT.s))) error_exit("bad -s: '%s'",TT.s); + xparsetimeval(*toys.optargs, &TT.itv.it_value); + if (TT.k) xparsetimeval(TT.k, &TT.ktv); + + TT.nextsig = SIGTERM; + if (TT.s && -1 == (TT.nextsig = sig_to_num(TT.s))) + error_exit("bad -s: '%s'", TT.s); + + if (!FLAG(foreground)) setpgid(0, 0); toys.exitval = 0; - TT.pfd.events = POLLIN; - TT.fds[1] = -1; - if (sigsetjmp(TT.sj, 1)) goto done; - xsignal_flags(SIGCHLD, handler, SA_NOCLDSTOP|SA_SIGINFO); + if (!(TT.pid = XVFORK())) xexec(toys.optargs+1); + else { + int status; - TT.pid = xpopen_setup(toys.optargs+1, FLAG(i) ? TT.fds : 0, callback); - xsignal(SIGTTIN, SIG_IGN); - xsignal(SIGTTOU, SIG_IGN); - xsignal(SIGTSTP, SIG_IGN); - if (!FLAG(i)) xpipe(TT.fds); - TT.pfd.fd = TT.fds[1]; - ms = nantomil(&tts); - for (;;) { - if (1 != xpoll(&TT.pfd, 1, ms)) { - if (FLAG(v)) - perror_msg("sending signal %s to command %s", num_to_sig(nextsig), - toys.optargs[1]); - toys.exitval = (nextsig==9) ? 137 : 124; - kill(FLAG(foreground) ? TT.pid : -TT.pid, nextsig); - if (!TT.k || nextsig==SIGKILL) break; - nextsig = SIGKILL; - ms = nantomil(&kts); + xsignal(SIGALRM, handler); + setitimer(ITIMER_REAL, &TT.itv, (void *)toybuf); - continue; - } - if (TT.pfd.revents&POLLIN) { - errno = 0; - if (1>(ii = read(TT.fds[1], toybuf, sizeof(toybuf)))) { - if (errno==EINTR) continue; - break; - } - writeall(1, toybuf, ii); - } - if (TT.pfd.revents&POLLHUP) break; + status = xwaitpid(TT.pid); + if (FLAG(preserve_status) || !toys.exitval) toys.exitval = status; } -done: - xpclose_both(TT.pid, TT.fds); - - if (FLAG(preserve_status) || !toys.exitval) toys.exitval = TT.rc; } diff --git a/toys/pending/route.c b/toys/pending/route.c index 3b0e5be..acb6766 100644 --- a/toys/pending/route.c +++ b/toys/pending/route.c @@ -6,25 +6,30 @@ * * No Standard * + * TODO: autodetect -net -host target dev -A (but complain) * route add -net target 10.0.0.0 netmask 255.0.0.0 dev eth0 * route del delete * delete net route, must match netmask, informative error message * * mod dyn reinstate metric netmask gw mss window irtt dev -USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_SBIN)) +USE_ROUTE(NEWTOY(route, "?neA:", TOYFLAG_BIN)) config ROUTE bool "route" default n help - usage: route [-ne] [-A [inet|inet6]] [add|del TARGET [OPTIONS]] + usage: route [-ne] [-A [46]] [add|del TARGET [OPTIONS]] - Display, add or delete network routes in the "Forwarding Information Base", - which send packets out a network interface to an address. + Display, add or delete network routes in the "Forwarding Information Base". -n Show numerical addresses (no DNS lookups) -e display netstat fields + Routing means sending packets out a network interface to an address. + The kernel can tell where to send packets one hop away by examining each + interface's address and netmask, so the most common use of this command + is to identify a "gateway" that forwards other traffic. + Assigning an address to an interface automatically creates an appropriate network route ("ifconfig eth0 10.0.2.15/8" does "route add 10.0.0.0/8 eth0" for you), although some devices (such as loopback) won't show it in the @@ -43,13 +48,16 @@ config ROUTE #define FOR_route #include "toys.h" -#define _LINUX_SYSINFO_H // workaround for musl bug -#include +#include GLOBALS( - char *A; + char *family; ) +#define DEFAULT_PREFIXLEN 128 +#define INVALID_ADDR 0xffffffffUL +#define IPV6_ADDR_LEN 40 //32 + 7 (':') + 1 ('\0') + struct _arglist { char *arg; int action; @@ -65,176 +73,103 @@ static struct _arglist arglist2[] = { { NULL, 0 } }; -void xsend(int sockfd, void *buf, size_t len) +// to get the host name from the given ip. +static int get_hostname(char *ipstr, struct sockaddr_in *sockin) { - if (send(sockfd, buf, len, 0) != len) perror_exit("xsend"); -} + struct hostent *host; -int xrecv(int sockfd, void *buf, size_t len) -{ - int msg_len = recv(sockfd, buf, len, 0); - if (msg_len < 0) perror_exit("xrecv"); + sockin->sin_family = AF_INET; + sockin->sin_port = 0; - return msg_len; -} - -void addAttr(struct nlmsghdr *nl, int maxlen, void *attr, int type, int len) -{ - struct rtattr *rt; - int rtlen = RTA_LENGTH(len); - if (NLMSG_ALIGN(nl->nlmsg_len) + rtlen > maxlen) perror_exit("addAttr"); - rt = (struct rtattr*)((char *)nl + NLMSG_ALIGN(nl->nlmsg_len)); - rt->rta_type = type; - rt->rta_len = rtlen; - memcpy(RTA_DATA(rt), attr, len); - nl->nlmsg_len = NLMSG_ALIGN(nl->nlmsg_len) + rtlen; -} - -static void get_hostname(sa_family_t f, void *a, char *dst, size_t len) { - size_t a_len = (AF_INET6 == f) ? sizeof(struct in6_addr) : sizeof(struct in_addr); - - struct hostent *host = gethostbyaddr(a, a_len, f); - if (host) xstrncpy(dst, host->h_name, len); -} - -static void display_routes(sa_family_t f) -{ - int fd, msg_hdr_len, route_protocol; - struct { - struct nlmsghdr nl; - struct rtmsg rt; - } req; - struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)]; - struct nlmsghdr *msg_hdr_ptr; - struct rtmsg *route_entry; - struct rtattr *rteattr; - - fd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - - memset(&req, 0, sizeof(req)); - req.nl.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - req.nl.nlmsg_type = RTM_GETROUTE; - req.nl.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; - req.nl.nlmsg_pid = getpid(); - req.nl.nlmsg_seq = 1; - req.rt.rtm_family = f; - req.rt.rtm_table = RT_TABLE_MAIN; - xsend(fd, &req, sizeof(req)); - - if (f == AF_INET) { - xprintf("Kernel IP routing table\n" - "Destination Gateway Genmask Flags %s Iface\n", - FLAG(e) ? " MSS Window irtt" : "Metric Ref Use"); - } else { - xprintf("Kernel IPv6 routing table\n" - "%-31s%-26s Flag Metric Ref Use If\n", "Destination", "Next Hop"); + if (!strcmp(ipstr, "default")) { + sockin->sin_addr.s_addr = INADDR_ANY; + return 1; } - msg_hdr_len = xrecv(fd, buf, sizeof(buf)); - msg_hdr_ptr = buf; - while (msg_hdr_ptr->nlmsg_type != NLMSG_DONE) { - while (NLMSG_OK(msg_hdr_ptr, msg_hdr_len)) { - route_entry = NLMSG_DATA(msg_hdr_ptr); - route_protocol = route_entry->rtm_protocol; + if (inet_aton(ipstr, &sockin->sin_addr)) return 0; + if (!(host = gethostbyname(ipstr))) perror_exit("resolving '%s'", ipstr); + memcpy(&sockin->sin_addr, host->h_addr_list[0], sizeof(struct in_addr)); - // Annoyingly NLM_F_MATCH is not yet implemented so even if we pass in - // RT_TABLE_MAIN with RTM_GETROUTE it still returns everything so we - // have to filter here. - if (route_entry->rtm_table == RT_TABLE_MAIN) { - int route_attribute_len; - char dest[INET6_ADDRSTRLEN], gate[INET6_ADDRSTRLEN], netmask[32], - flags[10] = "U", if_name[IF_NAMESIZE] = "-"; - unsigned priority = 0, mss = 0, win = 0, irtt = 0, ref = 0, use = 0, - route_netmask, metric_len; - struct in_addr netmask_addr; - struct rtattr *metric; - struct rta_cacheinfo *cache_info; - - if (f == AF_INET) { - strcpy(dest, FLAG(n) ? "0.0.0.0" : "default"); - strcpy(gate, FLAG(n) ? "*" : "0.0.0.0"); - strcpy(netmask, "0.0.0.0"); - } else { - strcpy(dest, "::"); - strcpy(gate, "::"); - } - - route_netmask = route_entry->rtm_dst_len; - if (route_netmask == 0) netmask_addr.s_addr = ~((in_addr_t) -1); - else netmask_addr.s_addr = htonl(~((1 << (32 - route_netmask)) - 1)); - inet_ntop(AF_INET, &netmask_addr, netmask, sizeof(netmask)); - - rteattr = RTM_RTA(route_entry); - route_attribute_len = RTM_PAYLOAD(msg_hdr_ptr); - while (RTA_OK(rteattr, route_attribute_len)) { - switch (rteattr->rta_type) { - case RTA_DST: - if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), dest, sizeof(dest)); - else get_hostname(f, RTA_DATA(rteattr), dest, sizeof(dest)); - break; - - case RTA_GATEWAY: - if (FLAG(n)) inet_ntop(f, RTA_DATA(rteattr), gate, sizeof(dest)); - else get_hostname(f, RTA_DATA(rteattr), gate, sizeof(dest)); - strcat(flags, "G"); - break; - - case RTA_PRIORITY: - priority = *(unsigned *)RTA_DATA(rteattr); - break; - - case RTA_OIF: - if_indextoname(*(int *)RTA_DATA(rteattr), if_name); - break; - - case RTA_METRICS: - metric_len = RTA_PAYLOAD(rteattr); - for (metric = RTA_DATA(rteattr); RTA_OK(metric, metric_len); - metric = RTA_NEXT(metric, metric_len)) - if (metric->rta_type == RTAX_ADVMSS) - mss = *(unsigned *)RTA_DATA(metric); - else if (metric->rta_type == RTAX_WINDOW) - win = *(unsigned *)RTA_DATA(metric); - else if (metric->rta_type == RTAX_RTT) - irtt = (*(unsigned *)RTA_DATA(metric))/8; - break; - - case RTA_CACHEINFO: - cache_info = RTA_DATA(rteattr); - ref = cache_info->rta_clntref; - use = cache_info->rta_used; - break; - } - - rteattr = RTA_NEXT(rteattr, route_attribute_len); - } - - if (route_entry->rtm_type == RTN_UNREACHABLE) flags[0] = '!'; - if (route_netmask == 32) strcat(flags, "H"); - if (route_protocol == RTPROT_REDIRECT) strcat(flags, "D"); - - if (f == AF_INET) { - xprintf("%-15.15s %-15.15s %-16s%-6s", dest, gate, netmask, flags); - if (FLAG(e)) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, if_name); - else xprintf("%-6d %-2d %7d %s\n", priority, ref, use, if_name); - } else { - char *dest_with_mask = xmprintf("%s/%u", dest, route_netmask); - xprintf("%-30s %-26s %-4s %-6d %-4d %2d %-8s\n", - dest_with_mask, gate, flags, priority, ref, use, if_name); - free(dest_with_mask); - } - } - msg_hdr_ptr = NLMSG_NEXT(msg_hdr_ptr, msg_hdr_len); - } - - msg_hdr_len = xrecv(fd, buf, sizeof(buf)); - msg_hdr_ptr = buf; - } - - xclose(fd); + return 0; } -// find parameter (add/del/net/host) in list, return appropriate action or 0. +// used to extract the address info from the given ip. +static int get_addrinfo(char *ip, struct sockaddr_in6 *sock_in6) +{ + struct addrinfo hints, *result; + int status = 0; + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_INET6; + if ((status = getaddrinfo(ip, NULL, &hints, &result))) { + perror_msg("getaddrinfo: %s", gai_strerror(status)); + return -1; + } + if (result) { + memcpy(sock_in6, result->ai_addr, sizeof(*sock_in6)); + freeaddrinfo(result); + } + return 0; +} + +static void get_flag_value(char *str, int flags) +{ + // RTF_* bits in order: + // UP, GATEWAY, HOST, REINSTATE, DYNAMIC, MODIFIED, DEFAULT, ADDRCONF, CACHE + int i = 0, mask = 0x105003f; + + for (; mask; mask>>=1) if (mask&1) { + if (flags&(1< 0 && feof(fp)) perror_exit("fscanf %d", items); + fclose(fp); +} + +/* + * find the given parameter in list like add/del/net/host. + * and if match found return the appropriate action. + */ static int get_action(char ***argv, struct _arglist *list) { struct _arglist *alist; @@ -249,135 +184,271 @@ static int get_action(char ***argv, struct _arglist *list) return 0; } -// add/del a route. -static void setroute(sa_family_t f, char **argv) +/* + * used to get the params like: metric, netmask, gw, mss, window, irtt, dev and their values. + * additionally set the flag values for reject, mod, dyn and reinstate. + */ +static void get_next_params(char **argv, struct rtentry *rt, char **netmask) { - char *tgtip; - int sockfd, arg2_action; - int action = get_action(&argv, arglist1); //verify the arg for add/del. - struct nlmsghdr buf[8192 / sizeof(struct nlmsghdr)]; - struct nlmsghdr *nlMsg; - struct rtmsg *rtMsg; - - if (!action || !*argv) help_exit("setroute"); - arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host - if (!*argv) help_exit("setroute"); - tgtip = *argv++; - sockfd = xsocket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - memset(buf, 0, sizeof(buf)); - nlMsg = (struct nlmsghdr *) buf; - rtMsg = (struct rtmsg *) NLMSG_DATA(nlMsg); - - nlMsg->nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); - - //TODO(emolitor): Improve action and arg2_action handling - if (action == 1) { // Add - nlMsg->nlmsg_type = RTM_NEWROUTE; - nlMsg->nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE | NLM_F_EXCL; - } else { // Delete - nlMsg->nlmsg_type = RTM_DELROUTE; - nlMsg->nlmsg_flags = NLM_F_REQUEST; - } - - nlMsg->nlmsg_pid = getpid(); - nlMsg->nlmsg_seq = 1; - rtMsg->rtm_family = f; - rtMsg->rtm_table = RT_TABLE_UNSPEC; - rtMsg->rtm_type = RTN_UNICAST; - rtMsg->rtm_protocol = RTPROT_UNSPEC; - rtMsg->rtm_flags = RTM_F_NOTIFY; - rtMsg->rtm_dst_len = rtMsg->rtm_src_len = (f == AF_INET) ? 32 : 128; - - if (arg2_action == 2) rtMsg->rtm_scope = RT_SCOPE_HOST; - - size_t addr_len = sizeof(struct in_addr); - if (f == AF_INET6) addr_len = sizeof(struct in6_addr); - unsigned char addr[sizeof(struct in6_addr)] = {0,}; - - for (; *argv; argv++) { - if (!strcmp(*argv, "mod")) continue; - else if (!strcmp(*argv, "dyn")) continue; - else if (!strcmp(*argv, "reinstate")) continue; - else if (!strcmp(*argv, "reject")) rtMsg->rtm_type = RTN_UNREACHABLE; + for (;*argv;argv++) { + if (!strcmp(*argv, "reject")) rt->rt_flags |= RTF_REJECT; + else if (!strcmp(*argv, "mod")) rt->rt_flags |= RTF_MODIFIED; + else if (!strcmp(*argv, "dyn")) rt->rt_flags |= RTF_DYNAMIC; + else if (!strcmp(*argv, "reinstate")) rt->rt_flags |= RTF_REINSTATE; else { - if (!argv[1]) show_help(stdout, 1); + if (!argv[1]) help_exit(0); - if (!strcmp(*argv, "metric")) { - unsigned int priority = atolx_range(argv[1], 0, UINT_MAX); - addAttr(nlMsg, sizeof(toybuf), &priority, RTA_PRIORITY, sizeof(unsigned int)); - } else if (!strcmp(*argv, "netmask")) { - uint32_t netmask; - char *ptr; - uint32_t naddr[4] = {0,}; - uint64_t plen; + //set the metric field in the routing table. + if (!strcmp(*argv, "metric")) + rt->rt_metric = atolx_range(argv[1], 0, ULONG_MAX) + 1; + else if (!strcmp(*argv, "netmask")) { + //when adding a network route, the netmask to be used. + struct sockaddr sock; + unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr); - netmask = (f == AF_INET6) ? 128 : 32; // set default netmask - plen = strtoul(argv[1], &ptr, 0); - - if (!ptr || ptr == argv[1] || *ptr || !plen || plen > netmask) { - if (!inet_pton(f, argv[1], &naddr)) error_exit("invalid netmask"); - if (f == AF_INET) { - uint32_t mask = htonl(*naddr), host = ~mask; - if (host & (host + 1)) error_exit("invalid netmask"); - for (plen = 0; mask; mask <<= 1) ++plen; - if (plen > 32) error_exit("invalid netmask"); - } - } - netmask = plen; - rtMsg->rtm_dst_len = netmask; - } else if (!strcmp(*argv, "gw")) { - if (!inet_pton(f, argv[1], &addr)) error_exit("invalid gw"); - addAttr(nlMsg, sizeof(toybuf), &addr, RTA_GATEWAY, addr_len); + if (addr_mask) help_exit("dup netmask"); + *netmask = argv[1]; + get_hostname(*netmask, (struct sockaddr_in *) &sock); + rt->rt_genmask = sock; + } else if (!strcmp(*argv, "gw")) { + //route packets via a gateway. + if (!(rt->rt_flags & RTF_GATEWAY)) { + if (!get_hostname(argv[1], (struct sockaddr_in *) &rt->rt_gateway)) + rt->rt_flags |= RTF_GATEWAY; + else perror_exit("gateway '%s' is a NETWORK", argv[1]); + } else help_exit("dup gw"); } else if (!strcmp(*argv, "mss")) { - // TODO(emolitor): Add RTA_METRICS support //set the TCP Maximum Segment Size for connections over this route. - //rt->rt_mtu = atolx_range(argv[1], 64, 65536); - //rt->rt_flags |= RTF_MSS; + rt->rt_mtu = atolx_range(argv[1], 64, 65536); + rt->rt_flags |= RTF_MSS; } else if (!strcmp(*argv, "window")) { - // TODO(emolitor): Add RTA_METRICS support //set the TCP window size for connections over this route to W bytes. - //rt->rt_window = atolx_range(argv[1], 128, INT_MAX); //win low - //rt->rt_flags |= RTF_WINDOW; + rt->rt_window = atolx_range(argv[1], 128, INT_MAX); //win low + rt->rt_flags |= RTF_WINDOW; } else if (!strcmp(*argv, "irtt")) { - // TODO(emolitor): Add RTA_METRICS support - //rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX); - //rt->rt_flags |= RTF_IRTT; - } else if (!strcmp(*argv, "dev")) { - unsigned int if_idx = if_nametoindex(argv[1]); - if (!if_idx) perror_exit("dev"); - addAttr(nlMsg, sizeof(toybuf), &if_idx, RTA_OIF, sizeof(unsigned int)); - } else help_exit("no '%s'", *argv); + rt->rt_irtt = atolx_range(argv[1], 0, INT_MAX); + rt->rt_flags |= RTF_IRTT; + } else if (!strcmp(*argv, "dev") && !rt->rt_dev) rt->rt_dev = argv[1]; + else help_exit("no '%s'", *argv); argv++; } } - if (strcmp(tgtip, "default") != 0) { - char *prefix = strtok(0, "/"); + if (!rt->rt_dev && (rt->rt_flags & RTF_REJECT)) rt->rt_dev = (char *)"lo"; +} - if (prefix) rtMsg->rtm_dst_len = strtoul(prefix, &prefix, 0); - if (!inet_pton(f, strtok(tgtip, "/"), &addr)) error_exit("invalid target"); - addAttr(nlMsg, sizeof(toybuf), &addr, RTA_DST, addr_len); - } else rtMsg->rtm_dst_len = 0; +// verify the netmask and conflict in netmask and route address. +static void verify_netmask(struct rtentry *rt, char *netmask) +{ + unsigned int addr_mask = (((struct sockaddr_in *)&((rt)->rt_genmask))->sin_addr.s_addr); + unsigned int router_addr = ~(unsigned int)(((struct sockaddr_in *)&((rt)->rt_dst))->sin_addr.s_addr); - xsend(sockfd, nlMsg, nlMsg->nlmsg_len); + if (addr_mask) { + addr_mask = ~ntohl(addr_mask); + if ((rt->rt_flags & RTF_HOST) && addr_mask != INVALID_ADDR) + perror_exit("conflicting netmask and host route"); + if (addr_mask & (addr_mask + 1)) perror_exit("wrong netmask '%s'", netmask); + addr_mask = ((struct sockaddr_in *) &rt->rt_dst)->sin_addr.s_addr; + if (addr_mask & router_addr) perror_exit("conflicting netmask and route address"); + } +} + +// add/del a route. +static void setroute(char **argv) +{ + struct rtentry rt; + char *netmask, *targetip; + int is_net_or_host = 0, sokfd, arg2_action; + int action = get_action(&argv, arglist1); //verify the arg for add/del. + + if (!action || !*argv) help_exit("setroute"); + + arg2_action = get_action(&argv, arglist2); //verify the arg for -net or -host + if (!*argv) help_exit("setroute"); + + memset(&rt, 0, sizeof(struct rtentry)); + targetip = *argv++; + + netmask = strchr(targetip, '/'); + if (netmask) { + *netmask++ = 0; + //used to verify the netmask and route conflict. + (((struct sockaddr_in *)&rt.rt_genmask)->sin_addr.s_addr) + = htonl((1<<(32-atolx_range(netmask, 0, 32)))-1); + rt.rt_genmask.sa_family = AF_INET; + netmask = 0; + } else netmask = "default"; + + is_net_or_host = get_hostname(targetip, (void *)&rt.rt_dst); + + if (arg2_action) is_net_or_host = arg2_action & 1; + rt.rt_flags = ((is_net_or_host) ? RTF_UP : (RTF_UP | RTF_HOST)); + + get_next_params(argv, &rt, (char **)&netmask); + verify_netmask(&rt, (char *)netmask); + + if ((action == 1) && (rt.rt_flags & RTF_HOST)) + (((struct sockaddr_in *)&((rt).rt_genmask))->sin_addr.s_addr) = INVALID_ADDR; + + sokfd = xsocket(AF_INET, SOCK_DGRAM, 0); + if (action == 1) xioctl(sokfd, SIOCADDRT, &rt); + else xioctl(sokfd, SIOCDELRT, &rt); + xclose(sokfd); +} + +/* + * get prefix len (if any) and remove the prefix from target ip. + * if no prefix then set default prefix len. + */ +static void is_prefix_inet6(char **tip, struct in6_rtmsg *rt) +{ + unsigned long plen; + char *prefix = strchr(*tip, '/'); + + if (prefix) { + *prefix = '\0'; + plen = atolx_range(prefix + 1, 0, 128); //DEFAULT_PREFIXLEN); + } else plen = DEFAULT_PREFIXLEN; + + rt->rtmsg_flags = (plen == DEFAULT_PREFIXLEN) ? (RTF_UP | RTF_HOST) : RTF_UP; + rt->rtmsg_dst_len = plen; +} + +/* + * used to get the params like: metric, gw, dev and their values. + * additionally set the flag values for mod and dyn. + */ +static void get_next_params_inet6(char **argv, struct sockaddr_in6 *sock_in6, struct in6_rtmsg *rt, char **dev_name) +{ + for (;*argv;argv++) { + if (!strcmp(*argv, "mod")) rt->rtmsg_flags |= RTF_MODIFIED; + else if (!strcmp(*argv, "dyn")) rt->rtmsg_flags |= RTF_DYNAMIC; + else { + if (!argv[1]) help_exit(0); + + if (!strcmp(*argv, "metric")) + rt->rtmsg_metric = atolx_range(argv[1], 0, ULONG_MAX); + else if (!strcmp(*argv, "gw")) { + //route packets via a gateway. + if (!(rt->rtmsg_flags & RTF_GATEWAY)) { + if (!get_addrinfo(argv[1], (struct sockaddr_in6 *) &sock_in6)) { + memcpy(&rt->rtmsg_gateway, sock_in6->sin6_addr.s6_addr, sizeof(struct in6_addr)); + rt->rtmsg_flags |= RTF_GATEWAY; + } else perror_exit("resolving '%s'", argv[1]); + } else help_exit(0); + } else if (!strcmp(*argv, "dev")) { + if (!*dev_name) *dev_name = argv[1]; + } else help_exit(0); + argv++; + } + } +} + +// add/del a route. +static void setroute_inet6(char **argv) +{ + struct sockaddr_in6 sock_in6; + struct in6_rtmsg rt; + char *targetip, *dev_name = 0; + int sockfd, action = get_action(&argv, arglist1); + + if (!action || !*argv) help_exit(0); + memset(&sock_in6, 0, sizeof(struct sockaddr_in6)); + memset(&rt, 0, sizeof(struct in6_rtmsg)); + targetip = *argv++; + if (!*argv) help_exit(0); + + if (!strcmp(targetip, "default")) { + rt.rtmsg_flags = RTF_UP; + rt.rtmsg_dst_len = 0; + } else { + is_prefix_inet6((char **)&targetip, &rt); + if (get_addrinfo(targetip, (struct sockaddr_in6 *) &sock_in6)) + perror_exit("resolving '%s'", targetip); + } + rt.rtmsg_metric = 1; //default metric. + memcpy(&rt.rtmsg_dst, sock_in6.sin6_addr.s6_addr, sizeof(struct in6_addr)); + get_next_params_inet6(argv, &sock_in6, &rt, (char **)&dev_name); + + sockfd = xsocket(AF_INET6, SOCK_DGRAM, 0); + if (dev_name) { + char ifre_buf[sizeof(struct ifreq)] = {0,}; + struct ifreq *ifre = (struct ifreq*)ifre_buf; + xstrncpy(ifre->ifr_name, dev_name, IFNAMSIZ); + xioctl(sockfd, SIOGIFINDEX, ifre); + rt.rtmsg_ifindex = ifre->ifr_ifindex; + } + if (action == 1) xioctl(sockfd, SIOCADDRT, &rt); + else xioctl(sockfd, SIOCDELRT, &rt); xclose(sockfd); } +/* + * format the dest and src address in ipv6 format. + * e.g. 2002:6b6d:26c8:d:ea03:9aff:fe65:9d62 + */ +static void ipv6_addr_formating(char *ptr, char *addr) +{ + int i = 0; + while (i <= IPV6_ADDR_LEN) { + if (!*ptr) { + if (i == IPV6_ADDR_LEN) { + addr[IPV6_ADDR_LEN - 1] = 0; //NULL terminating the ':' separated address. + break; + } + error_exit("IPv6 ip format error"); + } + addr[i++] = *ptr++; + if (!((i+1) % 5)) addr[i++] = ':'; //put ':' after 4th bit + } +} + +static void display_routes6(void) +{ + char iface[16] = {0,}, ipv6_dest_addr[41]; + char ipv6_src_addr[41], flag_val[10], buf2[INET6_ADDRSTRLEN]; + int prefixlen, metric, use, refcount, flag, items = 0; + unsigned char buf[sizeof(struct in6_addr)]; + + FILE *fp = xfopen("/proc/net/ipv6_route", "r"); + + xprintf("Kernel IPv6 routing table\n" + "%-43s%-40s Flags Metric Ref Use Iface\n", "Destination", "Next Hop"); + + while ((items = fscanf(fp, "%32s%x%*s%*x%32s%x%x%x%x%15s\n", ipv6_dest_addr+8, + &prefixlen, ipv6_src_addr+8, &metric, &use, &refcount, &flag, + iface)) == 8) + { + if (!(flag & RTF_UP)) continue; //skip down interfaces. + + //ipv6_dest_addr+8: as the values are filled from the 8th location of the array. + ipv6_addr_formating(ipv6_dest_addr+8, ipv6_dest_addr); + ipv6_addr_formating(ipv6_src_addr+8, ipv6_src_addr); + + get_flag_value(flag_val, flag); + if (inet_pton(AF_INET6, ipv6_dest_addr, buf) <= 0) perror_exit("inet"); + if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN)) + sprintf(toybuf, "%s/%d", buf2, prefixlen); + + if (inet_pton(AF_INET6, ipv6_src_addr, buf) <= 0) perror_exit("inet"); + if (inet_ntop(AF_INET6, buf, buf2, INET6_ADDRSTRLEN)) + xprintf("%-43s %-39s %-5s %-6d %-4d %5d %-8s\n", + toybuf, buf2, flag_val, metric, refcount, use, iface); + } + if ((items > 0) && feof(fp)) perror_exit("fscanf"); + + fclose(fp); +} + void route_main(void) { + if (!TT.family) TT.family = "inet"; if (!*toys.optargs) { - if (!TT.A || !strcmp(TT.A, "inet")) display_routes(AF_INET); - else if (!strcmp(TT.A, "inet6")) display_routes(AF_INET6); - else show_help(stdout, 1); + if (!strcmp(TT.family, "inet")) display_routes(); + else if (!strcmp(TT.family, "inet6")) display_routes6(); + else help_exit(0); } else { - if (!TT.A) { - if (toys.optc>1 && strchr(toys.optargs[1], ':')) { - xprintf("WARNING: Implicit IPV6 address using -Ainet6\n"); - TT.A = "inet6"; - } else TT.A = "inet"; - } - - if (!strcmp(TT.A, "inet")) setroute(AF_INET, toys.optargs); - else setroute(AF_INET6, toys.optargs); + if (!strcmp(TT.family, "inet6")) setroute_inet6(toys.optargs); + else setroute(toys.optargs); } } diff --git a/toys/posix/cpio.c b/toys/posix/cpio.c index 321bbfd..90a2e2b 100644 --- a/toys/posix/cpio.c +++ b/toys/posix/cpio.c @@ -13,40 +13,35 @@ * In order: magic ino mode uid gid nlink mtime filesize devmajor devminor * rdevmajor rdevminor namesize check * This is the equivalent of mode -H newc in other implementations. - * We always do --quiet, but accept it as a compatibility NOP. * - * TODO: export/import linux file list text format ala gen_initramfs_list.sh - * TODO: hardlink support, -A, -0, -a, -L, --sparse - * TODO: --renumber-archives (probably always?) --ignore-devno --reproducible + * todo: export/import linux file list text format ala gen_initramfs_list.sh -USE_CPIO(NEWTOY(cpio, "(ignore-devno)(renumber-inodes)(quiet)(no-preserve-owner)R(owner):md(make-directories)uLH:p|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN)) +USE_CPIO(NEWTOY(cpio, "(no-preserve-owner)(trailer)mduH:p:|i|t|F:v(verbose)o|[!pio][!pot][!pF]", TOYFLAG_BIN)) config CPIO bool "cpio" default y help - usage: cpio -{o|t|i|p DEST} [-dLtuv] [--verbose] [-F FILE] [-R [USER][:GROUP] [--no-preserve-owner] + usage: cpio -{o|t|i|p DEST} [-v] [--verbose] [-F FILE] [--no-preserve-owner] + [ignored: -mdu -H newc] Copy files into and out of a "newc" format cpio archive. - -d Create directories if needed -F FILE Use archive FILE instead of stdin/stdout - -i Extract from archive into file system (stdin=archive) - -L Follow symlinks - -o Create archive (stdin=list of files, stdout=archive) -p DEST Copy-pass mode, copy stdin file list to directory DEST - -R USER Replace owner with USER[:GROUP] + -i Extract from archive into file system (stdin=archive) + -o Create archive (stdin=list of files, stdout=archive) -t Test files (list only, stdin=archive, stdout=list of files) - -u Unlink existing files when extracting -v Verbose - --no-preserve-owner Don't set ownership during extract + --no-preserve-owner (don't set ownership during extract) + --trailer Add legacy trailer (prevents concatenation) */ #define FOR_cpio #include "toys.h" GLOBALS( - char *F, *H, *R; + char *F, *p, *H; ) // Read strings, tail padded to 4 byte alignment. Argument "align" is amount @@ -77,37 +72,20 @@ static unsigned x8u(char *hex) // Because scanf gratuitously treats %*X differently than printf does. sprintf(pattern, "%%%dX%%n", inpos); sscanf(hex, pattern, &val, &outpos); - if (inpos != outpos) error_exit("bad hex"); + if (inpos != outpos) error_exit("bad header"); return val; } void cpio_main(void) { - int pipe, afd = FLAG(o), reown = !geteuid() && !FLAG(no_preserve_owner), - empty = 1; + // Subtle bit: FLAG_o is 1 so we can just use it to select stdin/stdout. + int pipe, afd = toys.optflags & FLAG_o; pid_t pid = 0; - long Ruid = -1, Rgid = -1; - char *tofree = 0; - - if (TT.R) { - char *group = TT.R+strcspn(TT.R, ":."); - - if (*group) { - Rgid = xgetgid(group+1); - *group = 0; - } - if (group != TT.R) Ruid = xgetuid(TT.R); - } // In passthrough mode, parent stays in original dir and generates archive // to pipe, child does chdir to new dir and reads archive from stdin (pipe). - if (FLAG(p)) { - if (FLAG(d)) { - if (!*toys.optargs) error_exit("need directory for -p"); - if (mkdir(*toys.optargs, 0700) == -1 && errno != EEXIST) - perror_msg("mkdir %s", *toys.optargs); - } + if (TT.p) { if (toys.stacktop) { // xpopen() doesn't return from child due to vfork(), instead restarts // with !toys.stacktop @@ -116,44 +94,31 @@ void cpio_main(void) } else { // child toys.optflags |= FLAG_i; - xchdir(*toys.optargs); + xchdir(TT.p); } } if (TT.F) { - int perm = FLAG(o) ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY; + int perm = (toys.optflags & FLAG_o) ? O_CREAT|O_WRONLY|O_TRUNC : O_RDONLY; afd = xcreate(TT.F, perm, 0644); } // read cpio archive - if (FLAG(i) || FLAG(t)) for (;; empty = 0) { - char *name, *data; - unsigned mode, uid, gid, timestamp; - int test = FLAG(t), err = 0, size = 0, len; + if (toys.optflags & (FLAG_i|FLAG_t)) for (;;) { + char *name, *tofree, *data; + unsigned size, mode, uid, gid, timestamp; + int test = toys.optflags & FLAG_t, err = 0; - free(tofree); - tofree = 0; - // read header, skipping arbitrary leading NUL bytes (concatenated archives) - for (;;) { - if (1>(len = readall(afd, toybuf+size, 110-size))) break; - if (size || *toybuf) { - size += len; - break; - } - for (size = 0; size=0) ? Ruid : x8u(toybuf+22); - gid = (Rgid>=0) ? Rgid : x8u(toybuf+30); + uid = x8u(toybuf+22); + gid = x8u(toybuf+30); timestamp = x8u(toybuf+46); // unsigned 32 bit, so year 2100 problem - // (This output is unaffected by --quiet.) - if (FLAG(t) || FLAG(v)) puts(name); + if (toys.optflags & (FLAG_t|FLAG_v)) puts(name); - if (FLAG(u) && !test) if (unlink(name) && errno == EISDIR) rmdir(name); - - if (!test && FLAG(d) && strrchr(name, '/') && mkpath(name)) { + if (!test && strrchr(name, '/') && mkpath(name)) { perror_msg("mkpath '%s'", name); test++; } @@ -179,25 +141,16 @@ void cpio_main(void) // properly aligned with next file. if (S_ISDIR(mode)) { - if (test) continue; - err = mkdir(name, mode) && (errno != EEXIST && !FLAG(u)); - - // Creading dir/dev doesn't give us a filehandle, we have to refer to it - // by name to chown/utime, but how do we know it's the same item? - // Check that we at least have the right type of entity open, and do - // NOT restore dropped suid bit in this case. - if (S_ISDIR(mode) && reown) { - int fd = open(name, O_RDONLY|O_NOFOLLOW); - struct stat st; - - if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT)) - err = fchown(fd, uid, gid); - else err = 1; - - close(fd); - } + if (!test) err = mkdir(name, mode); + } else if (S_ISLNK(mode)) { + data = strpad(afd, size, 0); + if (!test) err = symlink(data, name); + free(data); + // Can't get a filehandle to a symlink, so do special chown + if (!err && !geteuid() && !(toys.optflags & FLAG_no_preserve_owner)) + err = lchown(name, uid, gid); } else if (S_ISREG(mode)) { - int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_EXCL|O_NOFOLLOW, mode); + int fd = test ? 0 : open(name, O_CREAT|O_WRONLY|O_TRUNC|O_NOFOLLOW, mode); // If write fails, we still need to read/discard data to continue with // archive. Since doing so overwrites errno, report error now @@ -220,71 +173,73 @@ void cpio_main(void) if (!test) { // set owner, restore dropped suid bit - if (reown) err = fchown(fd, uid, gid) && fchmod(fd, mode); + if (!geteuid() && !(toys.optflags & FLAG_no_preserve_owner)) { + err = fchown(fd, uid, gid); + if (!err) err = fchmod(fd, mode); + } close(fd); } - } else { - data = S_ISLNK(mode) ? strpad(afd, size, 0) : 0; - if (!test) { - err = data ? symlink(data, name) - : mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86))); + } else if (!test) + err = mknod(name, mode, dev_makedev(x8u(toybuf+78), x8u(toybuf+86))); - // Can't get a filehandle to a symlink or a node on nodev mount, - // so do special chown that at least doesn't follow symlinks. - // We also don't chmod after, so dropped suid bit isn't restored - if (!err && reown) err = lchown(name, uid, gid); - } - free(data); - } - - // Set timestamp. + // Set ownership and timestamp. if (!test && !err) { - struct timespec times[2]; + // Creading dir/dev doesn't give us a filehandle, we have to refer to it + // by name to chown/utime, but how do we know it's the same item? + // Check that we at least have the right type of entity open, and do + // NOT restore dropped suid bit in this case. + if (!S_ISREG(mode) && !S_ISLNK(mode) && !geteuid() + && !(toys.optflags & FLAG_no_preserve_owner)) + { + int fd = open(name, O_RDONLY|O_NOFOLLOW); + struct stat st; - memset(times, 0, sizeof(struct timespec)*2); - times[0].tv_sec = times[1].tv_sec = timestamp; - err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW); + if (fd != -1 && !fstat(fd, &st) && (st.st_mode&S_IFMT) == (mode&S_IFMT)) + err = fchown(fd, uid, gid); + else err = 1; + + close(fd); + } + + // set timestamp + if (!err) { + struct timespec times[2]; + + memset(times, 0, sizeof(struct timespec)*2); + times[0].tv_sec = times[1].tv_sec = timestamp; + err = utimensat(AT_FDCWD, name, times, AT_SYMLINK_NOFOLLOW); + } } if (err) perror_msg_raw(name); + free(tofree); // Output cpio archive } else { char *name = 0; size_t size = 0; - unsigned inode = 0; for (;;) { struct stat st; unsigned nlen, error = 0, zero = 0; int len, fd = -1; - char *link = 0; ssize_t llen; len = getline(&name, &size, stdin); if (len<1) break; if (name[len-1] == '\n') name[--len] = 0; - if (!len) continue; nlen = len+1; - if ((FLAG(L)?stat:lstat)(name, &st) || (S_ISREG(st.st_mode) - && st.st_size && (fd = open(name, O_RDONLY))<0) - || (S_ISLNK(st.st_mode) && !(link = xreadlink(name)))) + if (lstat(name, &st) || (S_ISREG(st.st_mode) + && st.st_size && (fd = open(name, O_RDONLY))<0)) { perror_msg_raw(name); continue; } - // encrypted filesystems can stat the wrong link size - if (link) st.st_size = strlen(link); - if (Ruid>=0) st.st_uid = Ruid; - if (Rgid>=0) st.st_gid = Rgid; - if (FLAG(no_preserve_owner)) st.st_uid = st.st_gid = 0; if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode)) st.st_size = 0; if (st.st_size >> 32) perror_msg("skipping >2G file '%s'", name); else { - if (FLAG(renumber_inodes)) st.st_ino = ++inode; - if (FLAG(ignore_devno)) st.st_rdev = 0; llen = sprintf(toybuf, "070701%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X%08X", (int)st.st_ino, st.st_mode, st.st_uid, st.st_gid, (int)st.st_nlink, @@ -299,9 +254,14 @@ void cpio_main(void) if (llen) xwrite(afd, &zero, 4-llen); // Write out body for symlink or regular file - if (link) xwrite(afd, link, st.st_size); - else for (llen = st.st_size; llen; llen -= nlen) { + llen = st.st_size; + if (S_ISLNK(st.st_mode)) { + if (readlink(name, toybuf, sizeof(toybuf)-1) == llen) + xwrite(afd, toybuf, llen); + else perror_msg("readlink '%s'", name); + } else while (llen) { nlen = llen > sizeof(toybuf) ? sizeof(toybuf) : llen; + llen -= nlen; // If read fails, write anyway (already wrote size in header) if (nlen != readall(fd, toybuf, nlen)) if (!error++) perror_msg("bad read from file '%s'", name); @@ -310,15 +270,17 @@ void cpio_main(void) llen = st.st_size & 3; if (llen) xwrite(afd, &zero, 4-llen); } - free(link); - xclose(fd); + close(fd); } - if (CFG_TOYBOX_FREE) free(name); + free(name); - // nlink=1, namesize=11, with padding - dprintf(afd, "070701%040X%056X%08XTRAILER!!!%c%c%c%c", 1, 11, 0, 0, 0, 0,0); + if (FLAG(trailer)) { + memset(toybuf, 0, sizeof(toybuf)); + xwrite(afd, toybuf, + sprintf(toybuf, "070701%040X%056X%08XTRAILER!!!", 1, 0x0b, 0)+4); + } } if (TT.F) xclose(afd); - if (FLAG(p) && pid) toys.exitval |= xpclose(pid, pipe); + if (TT.p) toys.exitval |= xpclose(pid, pipe); } diff --git a/toys/posix/patch.c b/toys/posix/patch.c index b66d590..3234a1f 100644 --- a/toys/posix/patch.c +++ b/toys/posix/patch.c @@ -8,39 +8,40 @@ * TODO: * -b backup * -N ignore already applied + * -d chdir first * -D define wrap #ifdef and #ifndef around changes * -o outfile output here instead of in place * -r rejectfile write rejected hunks to this file + * * -E remove empty files --remove-empty-files - * git syntax (rename, etc) + * -F fuzz (number, default 2) + * [file] which file to patch -USE_PATCH(NEWTOY(patch, ">2(no-backup-if-mismatch)(dry-run)F#g#fulp#v(verbose)@d:i:Rs(quiet)[!sv]", TOYFLAG_USR|TOYFLAG_BIN)) +USE_PATCH(NEWTOY(patch, "(no-backup-if-mismatch)(dry-run)"USE_TOYBOX_DEBUG("x")"g#fulp#d:i:Rs(quiet)", TOYFLAG_USR|TOYFLAG_BIN)) config PATCH bool "patch" default y help - usage: patch [-Rlsuv] [-d DIR] [-i FILE] [-p DEPTH] [-F FUZZ] [--dry-run] [FILE [PATCH]] + usage: patch [-d DIR] [-i file] [-p depth] [-Rlsu] [--dry-run] Apply a unified diff to one or more files. -d Modify files in DIR - -F Fuzz factor (number of non-matching context lines allowed per hunk) - -i Input patch from FILE (default=stdin) + -i Input file (default=stdin) -l Loose match (ignore whitespace) -p Number of '/' to strip from start of file paths (default=all) -R Reverse patch -s Silent except for errors - -v Verbose (-vv to see decisions) + -u Ignored (only handles "unified" diffs) --dry-run Don't change files, just confirm patch applies - Only handles "unified" diff format (-u is assumed and ignored). Only - modifies files when all hunks to that file apply. Prints failed hunks + This version of patch only handles unified diffs, and only modifies + a file when all hunks to that file apply. Patch prints failed hunks to stderr, and exits with nonzero status if any hunks fail. - Files compared against /dev/null (or with a date <= the unix epoch) are - created/deleted as appropriate. Default -F value is the number of - leading/trailing context lines minus one (usually 2). + A file compared against /dev/null (or with a date <= the epoch) is + created/deleted as appropriate. */ #define FOR_patch @@ -48,33 +49,15 @@ config PATCH GLOBALS( char *i, *d; - long v, p, g, F; + long p, g; - void *current_hunk; - long oldline, oldlen, newline, newlen, linenum, outnum; + struct double_list *current_hunk; + long oldline, oldlen, newline, newlen; + long linenum; int context, state, filein, fileout, filepatch, hunknum; char *tempname; ) -// TODO xgetline() instead, but replace_tempfile() wants fd... -char *get_line(int fd) -{ - char c, *buf = 0; - long len = 0; - - for (;;) { - if (1>read(fd, &c, 1)) break; - if (!(len & 63)) buf=xrealloc(buf, len+65); - if ((buf[len++]=c) == '\n') break; - } - if (buf) { - buf[len]=0; - if (buf[--len]=='\n') buf[len]=0; - } - - return buf; -} - // Dispose of a line of input, either by writing it out or discarding it. // state < 2: just free @@ -84,14 +67,20 @@ char *get_line(int fd) static void do_line(void *data) { - struct double_list *dlist = data; + struct double_list *dlist = (struct double_list *)data; - TT.outnum++; - if (TT.state>1) - if (0>dprintf(TT.state==2 ? 2 : TT.fileout,"%s\n",dlist->data+(TT.state>3))) - perror_exit("write"); + if (TT.state>1 && *dlist->data != TT.state) { + char *s = dlist->data+(TT.state>3); + int i = TT.state == 2 ? 2 : TT.fileout; - llist_free_double(data); + xwrite(i, s, strlen(s)); + xwrite(i, "\n", 1); + } + + if (FLAG(x)) fprintf(stderr, "DO %d: %s\n", TT.state, dlist->data); + + free(dlist->data); + free(data); } static void finish_oldfile(void) @@ -113,7 +102,7 @@ static void fail_hunk(void) TT.state = 2; llist_traverse(TT.current_hunk, do_line); - TT.current_hunk = 0; + TT.current_hunk = NULL; if (!FLAG(dry_run)) delete_tempfile(TT.filein, TT.fileout, &TT.tempname); TT.state = 0; } @@ -134,168 +123,156 @@ static int loosecmp(char *aa, char *bb) // Given a hunk of a unified diff, make the appropriate change to the file. // This does not use the location information, but instead treats a hunk -// as a sort of regex. Copies data from input to output until it finds +// as a sort of regex. Copies data from input to output until it finds // the change to be made, then outputs the changed data and returns. -// (Finding EOF first is an error.) This is a single pass operation, so +// (Finding EOF first is an error.) This is a single pass operation, so // multiple hunks must occur in order in the file. static int apply_one_hunk(void) { - struct double_list *plist, *buf = 0, *check = 0; - int matcheof, trail = 0, allfuzz = 0, fuzz, ii; - int (*lcmp)(char *aa, char *bb) = FLAG(l) ? (void *)loosecmp : (void *)strcmp; - long backwarn = 0; - char *data = toybuf; + struct double_list *plist, *buf = NULL, *check; + int matcheof, trailing = 0, reverse = FLAG(R), backwarn = 0; + int (*lcmp)(char *aa, char *bb); - if (TT.v>1) printf("START %d\n", TT.hunknum); + lcmp = FLAG(l) ? (void *)loosecmp : (void *)strcmp; + dlist_terminate(TT.current_hunk); // Match EOF if there aren't as many ending context lines as beginning - dlist_terminate(TT.current_hunk); - for (fuzz = 0, plist = TT.current_hunk; plist; plist = plist->next) { - char *s = plist->data, c = *s; - - if (c==' ') trail++; - else trail = 0; - - // Only allow fuzz if 2 context lines have multiple nonwhitespace chars. - // avoids the "all context was blank or } lines" issue. Removed lines - // count as context since they're matched. - if (c==' ' || c=="-+"[FLAG(R)]) { - while (isspace(*++s)); - if (*s && s[1] && !isspace(s[1])) fuzz++; - } + for (plist = TT.current_hunk; plist; plist = plist->next) { + if (plist->data[0]==' ') trailing++; + else trailing = 0; + if (FLAG(x)) fprintf(stderr, "HUNK:%s\n", plist->data); } - matcheof = !trail || trail < TT.context; - if (FLAG(F) && !TT.F) fuzz = 0; - if (fuzz>1) allfuzz = TT.F ? : TT.context ? TT.context-1 : 0; + matcheof = !trailing || trailing < TT.context; + + if (FLAG(x)) fprintf(stderr,"MATCHEOF=%c\n", matcheof ? 'Y' : 'N'); // Loop through input data searching for this hunk. Match all context - // lines and lines to be removed until we've found end of complete hunk. + // lines and all lines to be removed until we've found the end of a + // complete hunk. plist = TT.current_hunk; - fuzz = 0; + buf = NULL; + for (;;) { - if (data) { - data = get_line(TT.filein); - check = data ? dlist_add(&buf, data) : 0; - TT.linenum++; + char *data = get_line(TT.filein); + + TT.linenum++; + // Figure out which line of hunk to compare with next. (Skip lines + // of the hunk we'd be adding.) + while (plist && *plist->data == "+-"[reverse]) { + if (data && !lcmp(data, plist->data+1)) + if (!backwarn) backwarn = TT.linenum; + plist = plist->next; } - if (TT.v>1) printf("READ[%ld] %s\n", TT.linenum, data ? : "(NULL)"); - // Compare buffered line(s) with expected lines of hunk. Match can fail - // because next line doesn't match, or because we hit end of a hunk that - // needed EOF and this isn't EOF. + // Is this EOF? + if (!data) { + if (FLAG(x)) fprintf(stderr, "INEOF\n"); + + // Does this hunk need to match EOF? + if (!plist && matcheof) break; + + if (backwarn && !FLAG(s)) + fprintf(stderr, "Possibly reversed hunk %d at %ld\n", + TT.hunknum, TT.linenum); + + // File ended before we found a place for this hunk. + fail_hunk(); + goto done; + } else if (FLAG(x)) fprintf(stderr, "IN: %s\n", data); + check = dlist_add(&buf, data); + + // Compare this line with next expected line of hunk. + + // A match can fail because the next line doesn't match, or because + // we hit the end of a hunk that needed EOF, and this isn't EOF. + + // If match failed, flush first line of buffered data and + // recheck buffered data for a new match until we find one or run + // out of buffer. + for (;;) { - // Find hunk line to match (skip added lines) and detect reverse matches - while (plist && *plist->data == "+-"[FLAG(R)]) { - // TODO: proper backwarn = full hunk applies in reverse, not just 1 line - if (data) { - ii = strcspn(data, " \t"); - if (data[ii+!!data[ii]] && !lcmp(data, plist->data+1)) - backwarn = TT.linenum; + if (!plist || lcmp(check->data, plist->data+1)) { + // Match failed. Write out first line of buffered data and + // recheck remaining buffered data for a new match. + + if (FLAG(x)) { + int bug = 0; + + if (!plist) fprintf(stderr, "NULL plist\n"); + else { + while (plist->data[bug] == check->data[bug]) bug++; + fprintf(stderr, "NOT(%d:%d!=%d): %s\n", bug, plist->data[bug], + check->data[bug], plist->data); + } } + + // If this hunk must match start of file, fail if it didn't. + if (!TT.context || trailing>TT.context) { + fail_hunk(); + goto done; + } + + TT.state = 3; + do_line(check = dlist_pop(&buf)); + plist = TT.current_hunk; + + // If we've reached the end of the buffer without confirming a + // match, read more lines. + if (!buf) break; + check = buf; + } else { + if (FLAG(x)) fprintf(stderr, "MAYBE: %s\n", plist->data); + // This line matches. Advance plist, detect successful match. plist = plist->next; + if (!plist && !matcheof) goto out; + check = check->next; + if (check == buf) break; } - if (TT.v>1 && plist) - printf("HUNK %s\nLINE %s\n", plist->data+1, check ? check->data : ""); - - // End of hunk? - if (!plist) { - if (TT.v>1) printf("END OF HUNK\n"); - if (matcheof == !data) goto out; - - // Compare line and handle match - } else if (check && !lcmp(check->data, plist->data+1)) { - if (TT.v>1) printf("MATCH\n"); -handle_match: - plist = plist->next; - if ((check = check->next) == buf) { - if (plist || matcheof) break; - goto out; - } else continue; - } - - // Did we hit EOF? - if (!data) { - if (TT.v>1) printf("EOF\n"); - if (backwarn && !FLAG(s)) - fprintf(stderr, "Possibly reversed hunk %d at %ld\n", - TT.hunknum, backwarn); - - // File ended before we found a place for this hunk. - fail_hunk(); - goto done; - } - if (TT.v>1) printf("NOT MATCH\n"); - - // Match failed: can we fuzz it? - if (plist && *plist->data == ' ' && fuzz1) printf("FUZZ %d %s\n", fuzz, check->data); - goto handle_match; - } - - // If this hunk must match start of file, fail if it didn't. - if (!TT.context || trail>TT.context) { - fail_hunk(); - goto done; - } - - // Write out first line of buffer and recheck rest for new match. - TT.state = 3; - if (TT.v>1) printf("WRITE %s\n", buf->data); - do_line(check = dlist_pop(&buf)); - plist = TT.current_hunk; - fuzz = 0; - - // If end of the buffer without finishing a match, read more lines. - if (!buf) break; - check = buf; } } out: - if (TT.v) xprintf("Hunk #%d succeeded at %ld.\n", TT.hunknum, TT.linenum); // We have a match. Emit changed data. - TT.state = "-+"[FLAG(R)]; - while ((plist = dlist_pop(&TT.current_hunk))) { - if (TT.state == *plist->data || *plist->data == ' ') { - if (*plist->data == ' ') dprintf(TT.fileout, "%s\n", buf->data); - llist_free_double(dlist_pop(&buf)); - } else dprintf(TT.fileout, "%s\n", plist->data+1); - llist_free_double(plist); - } - TT.current_hunk = 0; + TT.state = "-+"[reverse]; + llist_traverse(TT.current_hunk, do_line); + TT.current_hunk = NULL; TT.state = 1; done: - llist_traverse(buf, do_line); + if (buf) { + dlist_terminate(buf); + llist_traverse(buf, do_line); + } return TT.state; } // read a filename that has been quoted or escaped -static char *unquote_file(char *filename) -{ - char *s = filename, *t, *newfile; - - // Return copy of file that wasn't quoted - if (*s++ != '"' || !*s) return xstrdup(filename); +char *unquote_file(char *filename) { + char *s = filename, *result, *t, *u; + int quote = 0, ch; // quoted and escaped filenames are larger than the original - for (t = newfile = xmalloc(strlen(s) + 1); *s != '"'; s++) { - if (!s[1]) error_exit("bad %s", filename); - + result = xmalloc(strlen(filename) + 1); + t = result; + if (*s == '"') { + s++; + quote = 1; + } + for (; *s && !(quote && *s == '"' && !s[1]); s++) { // don't accept escape sequences unless the filename is quoted - if (*s != '\\') *t++ = *s; - else if (*++s >= '0' && *s < '8') { - *t++ = strtoul(s, &s, 8); - s--; - } else { - if (!(*t = unescape(*s))) *t = *s;; - t++; - } + if (quote && *s == '\\' && s[1]) { + if (s[1] >= '0' && s[1] < '8') { + *t++ = strtoul(s + 1, &u, 8); + s = u - 1; + } else { + ch = unescape(s[1]); + *t++ = ch ? ch : s[1]; + s++; + } + } else *t++ = *s; } *t = 0; - - return newfile; + return result; } // Read a patch file and find hunks, opening/creating/deleting files. @@ -308,28 +285,28 @@ static char *unquote_file(char *filename) void patch_main(void) { - int state = 0, patchlinenum = 0, strip = 0; - char *oldname = 0, *newname = 0; + int reverse = FLAG(R), state = 0, patchlinenum = 0, strip = 0; + char *oldname = NULL, *newname = NULL; - if (toys.optc == 2) TT.i = toys.optargs[1]; if (TT.i) TT.filepatch = xopenro(TT.i); TT.filein = TT.fileout = -1; if (TT.d) xchdir(TT.d); - // Loop through the lines in the patch file (-i or stdin) collecting hunks + // Loop through the lines in the patch for (;;) { char *patchline; - if (!(patchline = get_line(TT.filepatch))) break; + patchline = get_line(TT.filepatch); + if (!patchline) break; // Other versions of patch accept damaged patches, so we need to also. if (strip || !patchlinenum++) { int len = strlen(patchline); - if (len && patchline[len-1] == '\r') { + if (patchline[len-1] == '\r') { if (!strip && !FLAG(s)) fprintf(stderr, "Removing DOS newlines\n"); strip = 1; - patchline[len-1] = 0; + patchline[len-1]=0; } } if (!*patchline) { @@ -340,7 +317,7 @@ void patch_main(void) // Are we assembling a hunk? if (state >= 2) { if (*patchline==' ' || *patchline=='+' || *patchline=='-') { - dlist_add((void *)&TT.current_hunk, patchline); + dlist_add(&TT.current_hunk, patchline); if (*patchline != '+') TT.oldlen--; if (*patchline != '-') TT.newlen--; @@ -351,11 +328,11 @@ void patch_main(void) // If we've consumed all expected hunk lines, apply the hunk. if (!TT.oldlen && !TT.newlen) state = apply_one_hunk(); - } else { - dlist_terminate(TT.current_hunk); - fail_hunk(); - state = 0; + continue; } + dlist_terminate(TT.current_hunk); + fail_hunk(); + state = 0; continue; } @@ -372,7 +349,7 @@ void patch_main(void) free(*name); finish_oldfile(); - // Trim date from end of filename (if any). Date<=epoch means delete. + // Trim date from end of filename (if any). We don't care. for (s = patchline+4; *s && *s!='\t'; s++); i = atoi(s); if (i>1900 && i<=1970) *name = xstrdup("/dev/null"); @@ -411,24 +388,13 @@ void patch_main(void) oldsum = TT.oldline + TT.oldlen; newsum = TT.newline + TT.newlen; - // If an original file was provided on the command line, it overrides - // *all* files mentioned in the patch, not just the first. - if (toys.optc) { - char **which = FLAG(R) ? &oldname : &newname; - - free(*which); - *which = xstrdup(toys.optargs[0]); - // The supplied path should be taken literally with or without -p. - toys.optflags |= FLAG_p; - TT.p = 0; - } - - name = FLAG(R) ? oldname : newname; + name = reverse ? oldname : newname; // We're deleting oldname if new file is /dev/null (before -p) // or if new hunk is empty (zero context) after patching - if (!strcmp(name, "/dev/null") || !(FLAG(R) ? oldsum : newsum)) { - name = FLAG(R) ? newname : oldname; + if (!strcmp(name, "/dev/null") || !(reverse ? oldsum : newsum)) + { + name = reverse ? newname : oldname; del++; } @@ -443,7 +409,7 @@ void patch_main(void) if (del) { if (!FLAG(s)) printf("removing %s\n", name); - if (!FLAG(dry_run)) xunlink(name); + xunlink(name); state = 0; // If we've got a file to open, do so. } else if (!FLAG(p) || i <= TT.p) { @@ -451,18 +417,16 @@ void patch_main(void) if ((!strcmp(oldname, "/dev/null") || !oldsum) && access(name, F_OK)) { if (!FLAG(s)) printf("creating %s\n", name); - if (FLAG(dry_run)) TT.filein = xopen("/dev/null", O_RDWR); - else { - if (mkpath(name)) perror_exit("mkpath %s", name); - TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666); - } + if (mkpath(name)) perror_exit("mkpath %s", name); + TT.filein = xcreate(name, O_CREAT|O_EXCL|O_RDWR, 0666); } else { if (!FLAG(s)) printf("patching %s\n", name); TT.filein = xopenro(name); } if (FLAG(dry_run)) TT.fileout = xopen("/dev/null", O_RDWR); else TT.fileout = copy_tempfile(TT.filein, name, &TT.tempname); - TT.linenum = TT.outnum = TT.hunknum = 0; + TT.linenum = 0; + TT.hunknum = 0; } } diff --git a/toys/posix/sort.c b/toys/posix/sort.c index d69b622..4b3fe24 100644 --- a/toys/posix/sort.c +++ b/toys/posix/sort.c @@ -7,42 +7,40 @@ * Deviations from POSIX: Lots. * We invented -x -USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMCcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) +USE_SORT(NEWTOY(sort, USE_SORT_FLOAT("g")"S:T:m" "o:k*t:" "xVbMcszdfirun", TOYFLAG_USR|TOYFLAG_BIN|TOYFLAG_ARGFAIL(2))) config SORT bool "sort" default y help - usage: sort [-runbCcdfiMsxVz] [FILE...] [-k#[,#[x]] [-t X]] [-o FILE] + usage: sort [-runbcdfiMsz] [FILE...] [-k#[,#[x]] [-t X]] [-o FILE] Sort all lines of text from input files (or stdin) to stdout. -r Reverse -u Unique lines only -n Numeric order (instead of alphabetical) - -b Ignore leading blanks (or trailing blanks in second part of key) - -C Check whether input is sorted - -c Warn if input is unsorted + -c Check whether input is sorted -d Dictionary order (use alphanumeric and whitespace chars only) -f Force uppercase (case insensitive sort) -i Ignore nonprinting characters - -k Sort by "key" (see below) -M Month sort (jan, feb, etc) - -o Output to FILE instead of stdout - -s Skip fallback sort (only sort with keys) - -t Use a key separator other than whitespace -x Hexadecimal numerical sort - -V Version numbers (name-1.234-rc6.5b.tgz) + -s Skip fallback sort (only sort with keys) -z Zero (null) terminated lines + -k Sort by "key" (see below) + -t Use a key separator other than whitespace + -o Output to FILE instead of stdout + -V Version numbers (name-1.234-rc6.5b.tgz) Sorting by key looks at a subset of the words on each line. -k2 uses the second word to the end of the line, -k2,2 looks at only the second word, -k2,4 looks from the start of the second to the end of the fourth word. -k2.4,5 starts from the fourth character of the second word, to the end - of the fifth word. Negative values count from the end. Specifying multiple - keys uses the later keys as tie breakers, in order. A type specifier - appended to a sort key (such as -2,2n) applies only to sorting that key. + of the fifth word. Specifying multiple keys uses the later keys as tie + breakers, in order. A type specifier appended to a sort key (such as -2,2n) + applies only to sorting that key. config SORT_FLOAT bool @@ -63,8 +61,9 @@ GLOBALS( char *o, *T, S; void *key_list; - unsigned linecount; - char **lines, *name; + int linecount; + char **lines; + char *name; ) // The sort types are n, g, and M. @@ -74,57 +73,50 @@ GLOBALS( #define FLAG_bb (1<<31) // Ignore trailing blanks -struct sort_key { +struct sort_key +{ struct sort_key *next_key; // linked list - long range[4]; // start word, start char, end word, end char + unsigned range[4]; // start word, start char, end word, end char int flags; }; -static int skip_key(char *str) -{ - int end = 0; - - // Skip leading blanks - if (str[end] && !TT.t) while (isspace(str[end])) end++; - - // Skip body of key - for (; str[end]; end++) { - if (TT.t) { - if (str[end]==*TT.t) { - end++; - break; - } - } else if (isspace(str[end])) break; - } - - return end; -} - // Copy of the part of this string corresponding to a key/flags. static char *get_key_data(char *str, struct sort_key *key, int flags) { - long start = 0, end, len, h, i, j, k; + int start=0, end, len, i, j; // Special case whole string, so we don't have to make a copy + if(key->range[0]==1 && !key->range[1] && !key->range[2] && !key->range[3] && !(flags&(FLAG_b|FLAG_d|FLAG_i|FLAG_bb))) return str; // Find start of key on first pass, end on second pass + len = strlen(str); for (j=0; j<2; j++) { - if (!(k = key->range[2*j])) end=len; + if (!key->range[2*j]) end=len; // Loop through fields else { - if (k<1) for (end = h = 0;; end += h) { - ++k; - if (!(h = skip_key(str+end))) break; + end=0; + for (i=1; i < key->range[2*j]+j; i++) { + + // Skip leading blanks + if (str[end] && !TT.t) while (isspace(str[end])) end++; + + // Skip body of key + for (; str[end]; end++) { + if (TT.t) { + if (str[end]==*TT.t) { + end++; + break; + } + } else if (isspace(str[end])) break; + } } - if (k<1) end = len*!j; - else for (end = 0, i = 1; istart && isspace(str[end-1])) end--; // Handle offsets on start and end - if (key->range[3]>0) { + if (key->range[3]) { end += key->range[3]-1; if (end>len) end=len; } - if (key->range[1]>0) { + if (key->range[1]) { start += key->range[1]-1; if (start>len) start=len; } // Make the copy - if (endnext_key); return *pkey = xzalloc(sizeof(struct sort_key)); @@ -202,7 +195,7 @@ static int compare_values(int flags, char *x, char *y) } if (yinf) return dy<0 ? 1 : -1; - return dxdy; + return dx>dy ? 1 : (dxdy; + return dx>dy ? 1 : (dxnext_key) { - flags = key->flags ? : toys.optflags; + for (key=(struct sort_key *)TT.key_list; !retval && key; key = key->next_key){ + flags = key->flags ? key->flags : toys.optflags; // Chop out and modify key chunks, handling -dfib @@ -283,22 +279,21 @@ static int compare_keys(const void *xarg, const void *yarg) // Read each line from file, appending to a big array. static void sort_lines(char **pline, long len) { - char *line; + char * line; if (!pline) return; line = *pline; if (!FLAG(z) && len && line[len-1]=='\n') line[--len] = 0; - *pline = 0; + *pline = NULL; // handle -c here so we don't allocate more memory than necessary. - if (FLAG(C)||FLAG(c)) { - if (TT.lines && compare_keys((void *)&TT.lines, &line)>-FLAG(u)) { - toys.exitval = 1; - if (FLAG(C)) xexit(); - error_exit("%s: Check line %u", TT.name, TT.linecount+1); - } + if (FLAG(c)) { + int j = FLAG(u) ? -1 : 0; + + if (TT.lines && compare_keys((void *)&TT.lines, &line)>j) + error_exit("%s: Check line %d\n", TT.name, TT.linecount); free(TT.lines); - TT.lines = (void *)line; + TT.lines = (char **)line; } else { if (!(TT.linecount&63)) TT.lines = xrealloc(TT.lines, sizeof(char *)*(TT.linecount+64)); @@ -311,14 +306,12 @@ static void sort_lines(char **pline, long len) static void sort_read(int fd, char *name) { TT.name = name; - do_lines(fd, '\n'*!FLAG(z), sort_lines); + do_lines(fd, FLAG(z) ? '\0' : '\n', sort_lines); } void sort_main(void) { - int idx, jdx, fd = 1; - - if (FLAG(u)) toys.optflags |= FLAG_s; + int idx, fd = 1; // Parse -k sort keys. if (TT.k) { @@ -326,34 +319,42 @@ void sort_main(void) for (arg = TT.k; arg; arg = arg->next) { struct sort_key *key = add_key(); - char *temp, *temp2, *optlist; + char *temp; int flag; idx = 0; temp = arg->arg; while (*temp) { // Start of range - key->range[2*idx] = strtol(temp, &temp, 10); - if (*temp=='.') key->range[(2*idx)+1] = strtol(temp+1, &temp, 10); + key->range[2*idx] = (unsigned)strtol(temp, &temp, 10); + if (*temp=='.') + key->range[(2*idx)+1] = (unsigned)strtol(temp+1, &temp, 10); // Handle flags appended to a key type. for (;*temp;temp++) { + char *temp2, *optlist; + + // Note that a second comma becomes an "Unknown key" error. - // Second comma becomes an "Unknown key" error. if (*temp==',' && !idx++) { temp++; break; } // Which flag is this? + optlist = toys.which->options; temp2 = strchr(optlist, *temp); - flag = 1<<(optlist-temp2+strlen(optlist)-1); + flag = (1<<(optlist-temp2+strlen(optlist)-1)); // Was it a flag that can apply to a key? - if (!temp2 || flag>FLAG_x || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z))) - error_exit("Unknown key option."); + if (!temp2 || flag>FLAG_x + || (flag&(FLAG_u|FLAG_c|FLAG_s|FLAG_z))) + { + toys.exitval = 2; + error_exit("Unknown key option."); + } // b after , means strip _trailing_ space, not leading. if (idx && flag==FLAG_b) flag = FLAG_bb; key->flags |= flag; @@ -373,15 +374,18 @@ void sort_main(void) // The compare (-c) logic was handled in sort_read(), // so if we got here, we're done. - if (FLAG(C)||FLAG(c)) goto exit_now; + if (FLAG(c)) goto exit_now; // Perform the actual sort qsort(TT.lines, TT.linecount, sizeof(char *), compare_keys); // handle unique (-u) if (FLAG(u)) { + int jdx; + for (jdx=0, idx=1; idx

1G!ZRSs>;3bR6npT^ z0lxp}Rm+wsIrWnQN_2KS5MkI4^bcB!>CfK+oDfz8%FXzT<`&|d1p!JcU4oa)H-sPU zC(#@a(Cdvo+uWOmrlHVc@tpJ>p5zAjr{6ZJ{tMK=|E-KqL4EHr;Wx{HC7DG5B%#>E ztNnVyu8EcUaI|q)ERYdxOAC;Vr{ZJ}Qe^S0{{qI#ey!t)iomS9wlyzGV1Hl<6^{zY zEfwz36QDu3;O%e5IWamfYLU{OCt5|VbZBiykkTG%x#J~!F5!u(mb4kj>O#%<>3^kx zDsrVo4lIlMStoVd6KbKq2|@WJWbZ!~sE?41tz7;}K<3RMxKc4v0>Kop5L4Gl;LFbVtyduMjg-d7*#LG(Rpp5$*J2$Rflp1!+``d(Ks& zc0m(*(CmKx6=?K}r=#4FheB3~vPvV1+=Opf(KEqV^GIpy3O6Sd5k znmq@sd08PET?)&7w+?$+?E^V0p)4$#@d@7RRqMNb8hYpswki3{J`Hw^+?^=nHF z(?3}?D zg8`XJ&wl%=w%X+W6pk{XlVcHSRq;{64ze(S-yHGH&-z0oq}QwRNhai}V>Mqgo$TaV zwJWS%M%=`;3CD&ODD^E*>U+URtPSolLDkPSh)%o6jsMf*rkm z2_@?;rBNBDC0o#(KXlgJ*iypN2p8(nGb#UpL^xBu1&Z&b8HBAomYntQpTYorIjc+9 z;W;~p)4P>T*HnEt_yB)CitR@h!@!O_h%L;9bUg5zRgf=Gs^v-PZz@m^_+vf+p9t|Y zJkDWVXCaqU=UG$cxk&pVLGNP69{~-uxs7#YP*u@wK+fhsUGE9e@r76t9{70lB$u8eNeO>%! zF-B(@*az@F!*yHe39xON1uFTb0QYgHO3Mt%T|qwGq~=z1&)r01oEIMaP%vX@P1^taWWu~>tNA}pnS`@9RNEM*M?JX=2)d(DnO>4Gehn~Hg7=fy_^mzyq`Ct)0)V7gm> zdB4e5#E3;cTdK<0y5lo`Bw~iv6MR~2-w@92U+>6 z6Bavj;wrrxZ^y0g#w)z%?8%i>Jr$sD?6~MI|A1bMA zNNLePWi_+r0Bt0FO9a}OOr$XM?h_@^ipUM|gxArS6{K(|B6$~MO(M(l;t%NM5P`Px z&ATQB9=UpRemoY+uvVBld8Aca-1rO^K^x5jnlj~zm@+kVcvZVM(mzpm^jd~`&A1bm zK^p~rmAL#=2$yj2OG)FafAyTy}4%zcNPHj;dJfJi`mRry$;|w#xFX3Q9P*BMqrwlAB65az+5U$lkYnI;2XKo=vtQrjf?S=RSzmM0^}Lq7 z#h#Bqy6Syjs%r5ae$Ly&-R#o3emIIte+sesyehx020cr&hQzyPBR2`lF!#QcUAQt8 zlQMBc2MSn>yR&=juIqcKW2jui%12zO)Im)Bt#0~18sH^RCOs1<5Ra0qyY#q6Y|{}+JU zr_pn{vIUXv=^0>j=DPh-(X|?zt6N7{dW@($_u!~BZJ-(31ccDl zBLLfcQz*T}2dqg&&|Ezvaa-ao`@u_nPeTW9+0y@IE>kGTRe?I+a4pj7^RW&B;!)oa z(ax+as66+Lt~CtyZ(0Q9xsY_@k*_{^$Fq8m44oY$M0da!z7lwLPGNT@`(MiYOzIzL z#=w*vL)506jY{2MR7%NpBa~y*49fy_7hp7u!E^y$(sgM(83hiO@|j|!;m$e+cBxlV zJo+bA1DFb4=~XY%5&afPdIL2pIJndPCSTz5ZpUvhS8oXTaB=YD^#BGo7F8>>qOTdI z!@I9jBN`RRij|xd`TJWozQNxeCv$P2iuS8U|5C-vGBgR`C=?VYhn$2xw1U8n9$ z@0m)h5xQt&3^`*8AIm1$>0)1`P+kOn6&V3vTI15nW6E!46~dT*ziQ0V(Siy}=Re@#+@tA5N^60~h&ARl^*^ zYzCFw{DABe*(=$m5qqCwBhDM`=oik!|18Y{gTGFy@JD3G7s$GR$ecFP7AkF&!kVyW zK%*v!!t{UN|M(6_YNKm*cv-VX#m>r2w0LKp`Cw4E(6*Nv1h%2c8s_P3c4PGymn!4e z4b9Emj3&lmN!oS=ahgqIR*u_IU)`eKO(kZ^3(X({ZgE>%dlR(|HiON5L2$X{@W69U z1nb%U-KI0KI#{)u>aTbISqGM1(GH4-t4O*%NKhP85N`jb`vbHhk+yCvl}01(lhN~p zKXiz)ofp(QP?LIoV38~djTPcVW}0mM1a~eQ0RLhhI~^>e@dOm`=M>d zwu#IPY}N zmc`ngncQb4E1U@_H9AoqVFs|0o-Jh7VL>5*jd`2)^u~#85T&<|8Pi4R6ji>z8NGeX zsG3E6SA~a9UA|2Ief8PHYyJx22e$x2+^L@HcsoI_?lAp2KFB+m7qG0`@kP0tQjJ{j z=SdleWWpU1ohaSkp9@ymKb5{#`=DmWmgUQmU71 zkx+4qAaNNPC4)XaSR}i3v}WF|!Uc6!(rv{BA8I*YvIu3|DyAoi_p8Wm%F-&ydAeb! z+bL3u2&+n7z<;ToxUq5&d8@0zfY1hA^JXTgNv?>`J9N>t7nAKwbuA5j>TCrLYxOSa zgj5~7a%AY*e979yY&j6R06HdPQ3_=xSrm7a+MRj_AGb7LqUD5AGY&V=$)((QHUnxH zYu3jD4~tw9IhL0jQmSwck{smORxF#WqbQI z?_6;QW>u-e&9bc@<8IjohBR7zyrmyw{LSspXAz{cORFW#b&Ye$Em;rIQ&Oxg2}^1~+dP0RxI zMKB9EJfNZz4k`*{-~9zGDDU)V8(_$l0tOHITn@_JpX@L?BIlgUd+h#M&NhGK0_sU8 zIs0dkLTLs=VhAvx{*2V0z;}vW{ge!BtJ&5XU6+iUuVy)49;68W&YKeCfI%AJy4NXr z0m-m4FGhaEN$b_a&M8gW}vo&5vcg`BR=oo+PlXe@icy8TLbf5MTW z#Y}Z{i%OQ)(-XzR|PbCAtZz0PBUfCk78yn=vJ3T(WTZ@jhFV_FQZX587h1@X2 z)m3sMyHw>t=k&WP*AF{JOw9x21O9!BBvNIOGrcS>72>a+Zhji9K+)3b`B+71m~rqv z8IblVxZ8(D`kT~fAH=%j*%5=2bMYy9%LbTw6;x)70G?n%vit=1SX+L^$bO;1+pRq!oas8D9y9Gv^CFa=-fzE`uC^#2M1{vwUfr@uZ?`g;bABCzXTPjTim(l+ zYt77mcP3{%V48qSqWX~Yyxzh8S{0yb@hcyUm^wC)1zv-=ZFU(=KU71GIbL`ltrg~$ z^ilr{04!hQezor?;fCh45IOhxh)|&pX#cF8*qg~*nsZ1i65esg{lhs+q98DiM}-LW zD)87CVN-wx{|%ql-S0aR%7*y;a6-xkR5E-^z!U5egqWsZEFe7*Ic45f>7*D&TrzgQ zr926MGNpKA{eFv=a7bPAnb6*6`7E9_MWU547q9JIIO4m*n z7CEQ_T-&Yi`eU*yw6Rmj!p?|g(n~$jn~QzGNNU6l1qW<;M#@=sQcjWZv|GCqZdGJ) zI4N4E6COj01mAob-C4kZwE6;Ctl;e5IZOY6&ch=9vLXiLd50(_EF z3Vm#o+J?*L zSa8eStMY*9Hr4$2W%o&#>p$k?os^$&vitaL!7D=E7bOdOsjk+F7bW~e5m*(y?=!LA z^uLDWq&JaTF#CXd??FceZwF68lwTuG5W9jdDYcI=<2G5sMaMAV6ngp;L;yl-8g{%A z&LzR?65e_dD%@Kr_k3>uaT<{{j+||B_M7cTl;o$>W^o`}J1y8xEeFw*C$OpnLId*O zTyKl3!wz4IO;|lI@v5h{-XmS#Zxej-1tMKlQ)d?*o{$^N^-d9AzTyIH`K~-_^x|j@ zvMt{4iwa$;B<7Y2*alz(l75V~e?LL~bfufLF`YnT{0BN@SB$&`Sv+Fgl5e`q>yZT- zcaHk1>kKI48J8AE)*`TOourM(>=`Z^->TCr_AfvqE7Nx89W4S*#TVHSx&cFuevkmh zE91^*oz@AaB@E zXlgkdaVwwWt{Vh03wW+QyHF{1tW4t&sU@S&2$=Hdy$9%!88y7jJYHnKCF-c4B%uz@^^x1}LU=w_KRHK$#L zwuI)}aXoDhb0fNBC3t=wdl*}$@KHsuyO2+m-q~?H_#=0H1S!5d zHf_wF2O2f(LfY`=Uft^wCY*fKV6S-(ggs72C}?Tp9apK7lGYVll+9Dh$&|cZ4^yxr z2pXK;Rj|emC7KO1uJkP{MHXrJU6Emiw)gQ@9EtK~uIp3571aBwJZd}$NC}M2hqw<= zr>;XpO7<0E~=3av_ z1_t|w5X2i#KEuz3xSaW$WiC!u<@?S_GyI5webEJ5rIuZo48$rWXtNdwOMiX$tQR!aKp`aDTh4)pH&4l_b++P2G( z$AWfg%KiTf{OVz`{YS0)s66u#(3;Wjk)wq#mz@2f(T|D1Y*nHCt!OoLAm2FVMN0pn zH{GEF#vsi;H~|Di97kZn0)93BJ(X?Veg`joK>t)AG#&rQolz^^wH8&0I4i7mTb-0w zMu7t0%a*bda2vAsV+jMsScfu96k7ot`}ubj_kf5~jTKr1>&BCt+rh28SMsCjdqYNW zkHl_P&fi0**G`IEPLH}NsQd*~`sOpRdC@S;TV~uZb%1JQt;fHUk(r&9B z;g{Ialbi7N1apq=YZJlnyNXfhIYaLiPdE11^i3_T178iLzAg@_+m=TVwm*fmJoa$0 zk~Z!EpJbI5lk11fL&63{&T}aN)OwQp#Q|Pv^^kP&Aa$k57DTJjivaf@rzx5A1>RDt z9*|Wqipq|#f6(c>uO7)i%G*9KzEqH1&tR0b zlW==S@Q&sx{%^)Zd$jsx;huqf9l`j2jJO&4k$baRWR3ns&gAP~7T-e#r-Q+Wbw>w? zdPnNBmn9d$w1=r+;DOfysC1}vU*S+2%<%5RmWiqW7<&_>KjL&QRG-Jb05qWE_Ut;0 zS+1!?3_kGZdYe)u)L9@?VmKj+*UZI+b;#dhLq~(4Zz@O5M9AzSpl#kaUYypSpfcY1 zy_!0T1Y>Uf7Q_X3l3C6ZirgYN-N25Qt35hH_Jt&6rxjkp!HIYsbKu?ljo|`%P=#!~ zj|;L0wfjWOB>>wOabM7lhbYs#MTcVa^zI$^EVc<+kpb;PMNme!>{YuxM=~1|uOeS+ zmjVA^9>{)~hT~j9-a@|l*g0=j!4Ad5$fh`XG zEHcetr|`W?{;mXg^N`7$?0+IktP!t}bWh@(iW(wiBLB|d=!7#`IDZKrc|!-Z*KSAE zt5(1S#!3KsRg07nlDN*0{cMlu5{p+V*yYvTC=JyBQ}M{Z?8R6&CAUY%%YV6Oo@rH( z@&BBlOY$-5>(DQj%7C^Rl4~tw*Xfe$J43{19>U*ZKeB2UQqzD=eyAYx>PU{PMEeoM zx65vi2tu#wY(I6mN9M28HW%nSQf^6a#tV2NGayCgrz9)cmaD-mBWL4$n~zSj5oDg! zuncGvx*~nMJU430>HE*iMQIba8<+^X?0PuoZ1Z(7u}#pn4}V%utS9es&Sl;fuRx~~ zP#Zz8kMsCWg}Yq1ddeStKMQZano=-Q{qo_nzKAI$y(i>;)ZNVT1?ZbU|D@l|Urp0} zz2;(wT?YqZu~Nwlzzgqkg^+FKmmZ$2LnJSkg6T1)9{Uurj(F#NM4L$EZ%dUKoS82- zeOzTZ<1TjH4veADC=2A6e7^K!a5QckOJ9rQ!04x{{^Ji$Iwd4k-v8>$l)L;P zvomAYr(9S@v^^+awhCI>&{+zQj>#Oo%fWKP1pUD8B?ibj_A-_oymJV5Ay@>lk{wc9gW?-;xSh&C= zmB$WJchc0B9N|OO)a9{WDS5DYRQ9tGJLK$~B19P;9Q%@sk4O`uo>cA8HXaIOVWx@u z3X4|Im)k?+a0#jDU)&5+Tt`{}5RjR|V*>|L7iLIsT|=}sl5A9?Z2GqRpb;PBds1H( zGmftBjZ1b~%;&cxW%qwd&&HtUWXlMrKe|bzEForP567S0$^AqyHCN1VyBM_&vog}Y ztQKt7j?tUw{?-_;PQ{L&C(e8~#r~n~YhjIH7zzUAcR%nJlfaSLb{KCalZN+Dn+Cv& z>6g(RQ?G7|BM){ff6Jn2V>@zgzx_}f#W)hf5T|yRa7N(2F%Cs2dDzx<{tBoMX=n*N z|MCI5SA2vwM>>4+83pkI>5lv7-x<>vkm>S#ulWNR3h(Gfo9!AnW2+=a%TENbS8?7I zK)fb+LF-7{&RMF$&lOd*Jf(hFc@Fb2&V=~(_TZ5JqS@Kg{$a>p_KqwFlkH7XL#{&z zp0<)jc7a6o8?QgSHku@7rV>h!csx`7B&&a?%kF&9{4?kyi3JUL1@fC?kqPv)o6G- z2#uK(Ud)%jP2!%E4Eo|oa?sAUWmge9F#fj!yxqA?@+pcjm_-K@h(VyRiJnJS+R?JY z<`pe%kQUa|!u_Mw!CK}Ri3IeAz5ZN|I#_qrSpb}Xy1?jxx>#=uC;Kyc={fMBi7&Jx zGB=?em?etH1gz~&0PM}N0q}r&*?ItcqY;(UU1Jja^AfFgp_ku5hYfmsij*&M9OXrz z)>8-iAFz4Q>g0EX*xWDnNL49f+Ccv_f(2XH1c4(55_MUkmR;8u$;yYB-6zKF`?mIMg zu;xV&=(MWq)|C7KIrfjEQ2Zih*1+@}mJ~<25hI{R#g956=hIH}w7kG;SQMw~nyN^L zg-Km-uM?pC;aL+H5ZYV!l@wo@c5F3TQVh&w_?9p(TG~N26%xE%2MTH)*=bkm5S#~e zD06HOD5!fJ7~x!GY`%udx&3Brg^ED+TE=(6^bKd>6SV;+b87c%kg_I=LlIVEfHC}X z%AT4dP<~HiCFyXP!=k$YNTT>Okl?Vh>c=j0R%&ht-lU!q$;|t^5H&Ng zR;ks7`YAzYa(396ZCZ|Xnr^Q1y*}T6pdZNVdSCC?>-l^<@a|=9#)=d=T3=-DW;>T` ze;P>#NDz#blcT$8DWVt$uHF7GFsv@uHxx=@=-15nBbh^n z!|%5`$Qlt8eByhkF<%c zx!*%PKlNkTc1mMH8j>~-upRCiKeml&CtqD>ngnQ9pq2V*AS~ZFpl*`z1`Xgc)Ux4~ z^Ben?26axMs{wB1ABbFudy=*+HM9NssU&++xQCHAE=gHb*iAI9NB%q$vZ#Z&3@Y-) zp%~O)n#Ju-Iy@5jn#YLF?1XQptKe}b#`Lr0FXNbdvnJ_VW(D1_iFoXx8t^DR?AoLC z%aZ#c7jJ|Jc(0z@F!)5K@Hu#z=bZ$&%o!U~{`LLPRB#b^sP0mdl+&Z4w>>lx5_=Kg z@CBp$y2TuCFOJxm9Q4FEG>LkTtW()LNO~V7&_X8#r;{Xt512tF0iEfxxZdT`xHv-+ zcQF+zzqFqvLJKM_CpZrYIr8Ju$rgRKE4!M*Up;!ej(PR-J+tB$M+;Y>I_I2X>IsWz zSC-D^gi(Hk^-^=?JYnfiaEoA9Y(Qn1qTck5xzc`J5hS9e4kDYh#svzfKKz~^2F4Mr zX+Vpg!1C`bq{@9qfbiyujSc!1*rPlxFamEom=VJi)+`35+P15A9D+_R$pql2k)wKA z+@6ygm|Zg$iYboo&kePp^U4%ZQJGt+(q;GTpeE4;aXO;X`vbWYUfWI?4GPUJ{rdAA zbs1FxCu?4q)~CY@KXD$?Q~bgd(YKf$`h?S02tt*eqR+w0o}qWaAlEDQM6xEY_|o(O z*hvZ3Cb0e!@s!-wKtu6vd^4R-hB~MG*BuFxj+v?;*m|8uz2_p`5&NFUCk1!|*ZOF- zi@b-d4}f%4`f4~Ox+7&m{Bo3OW5?18@uA_pff`()JZwWB4U#!xFHQY%k+@T5cXo9) zB=^f=Vb92JR4wM)XP>BFK3@+du(J~mBMCUzI-`{C(1+f_W!tcBhEF^88M>u}0VT&1 zR$bM%45rBYND~b0-`rPJn$Q}8G~?0V|Efv^GZNDp4FUz(U(4PP84fnCrC-YX@lOKA})>E(t7(UuiKSRIGl--hzgmDwbWDV*{cb5 zuM=)=!%y)WbFo!Qu+ipq7>UQnfZq51CSGiG7BfC1-7PCg=BRZ<)-H4^w*;IQCQltc zIoq-dD!WN59)KTbuC;vi8kkt2a&Lg%q~DLG=6i~cfx_$OeO9}B9l)Za)yp+O|K4z% zL2v9^j0ky8P+%yVL2*Vw-+`N97c5^A29b;J(Hk!>7s};h)q(>H&mVQ_!oy!%*;aqD z)PSH0KTAH3mK91`x-riEVWEmbJLkQTALf-|n<-qJVb{8cstqLz`}NrEaNYso=XiK#%_>SK=Ve1n_;)IVa5zKl~39NT{86|62_Uw89{m8Vt)ty zP$pC=PRqwH_$5kha zwAh95G#Qsji7~X;)VEd>ARQmmGYM`yqUKu%TF2F3lXP zaU!$Vd2p;S;CiKft7cZ%O_}U zM*#&hpN4zAs00!b1&0!jN>~p>M3O1~tr-pUVYi~h*5fz7eOcfQ`l?wW#jXW_c3C#5u2Z+b9M^yQyED?ZhLv{%yGCA&PAGR z1f5GhWcBNbKEHgQ#NZUsak#r!{v+Y%Ey>ZNloq+`b0BZ1pM7d2QGwMbW=?+t7C3rJ zt#^ZmTy!?1(7h*_{z2-pPQ3CGEN&yj^h)`28)ib1nkkQ)z;_rF%Tfn71uaLyAt}@F zGGOpBuF5(OwhMc=6n5-lzWsWW)o;&WYcU+1T4FskSDCal0! zh^@j^{PtIU2b6l#Vl)Le3h|a##F#N(gxoiTLJ8>htA%({a@TSAmvO1_Cw!a|(2uM8l3KoK{?EVhyQWWV9ogBz^&e4+TKyOia`J{P6VE=Kc&useQ z$w&U3>rszBd9fZ-+&Na=+Tdecfb?!0;t=bVW=Fqg_WOO&? ztFB$=!CbVH?Nbvw6t$j+5N#8w&PeWXz)|51s=69ohd6|DzemL(zoghjS%;4^5x&lA zmz;O))BP8CVg$P{=~{oBoMWaB@#x!_j)is-JTnY~xq!EeX_I;b@bmzrR6nF#K#Yfe=`ph zTm%!X+|u5DnY7Zbz_Lbk?c^m1MY7uXwR>@Eox9q4-m#wpmfpuGn68UPYVt)%*oJx2 zMBE-~e9_}Aj32`MJZNRBEv$3txN?B=bY>j>eBh>6=Km&W z8##?3ajj+%FCZr3M%f z#OY3{N^{($i-f28{Kv?D&#V7@lHfK``(F5*L28+PwuXqR8hol+fJdi!$pFU5xNHUd zOBN|vjnUdoHL8VlbnW9Za#wE(r`s!n73ewYO-Bf$&Xk8@Y}tzEO+>4cQS;Sh<_=+7 z!?7=GoVJtSYF-B*`qi|X*^BsBrTfqgv^X{pY_wN`oTGe6gSrL-R@Zl?@i8Ks*0zQ>#3m`)LyIavC4WSJ z?gc=X8r&3pRL8Vos#EoqvNKUiF=AiB&QM7ojR%;0Oh$qM81UA8{OE!u*y+CVvpgg) zf7b@MmLxaqP2>uJ#^%-$s#8@AO<^R6Jk5$bwZnITIRgSCdIJqTG!RU^5BBMBxm-nX z+$5?z{i8!OITIdH=20uxNJ0^q+y+!7KZbiAhO{5Dhe8{Zxp|xK36%Rx0`=xWYh>v8 z2X(tsV(kI0W#LIq&x=}-R=W=DV=N$PN{m(l?kh+{4>O} z%K)zl_+f6hIY=VZ=Y!OA+e3>G{rKVQ*reVL zk=lozr!4%c95td+$J~rwm+!tu&nBI*jntFjf+iW45?R8e-?Nw9r#~LEvghBcKw8vk zB;23J=3QnJ(Q}aeh)8Vh+X93fZ6#ul)f~u;Hz>lNsyE4cFPLPj{_F1Vt zUX@8=Dcz)(Wplp;LZTJZV;O^;jWU6Gq!ACiCr?AFl8*<;Tap^S0h29Tpi?X7r`4j45 z561p{OezO#xn6acC5@5Nol`LVoA~v>>c>+;VcT@x69#$GJrAs~aM5N}N zWRuI+U^)fpCV3zdEtwII$_T5tFZX6kk|%2tNqu-`%MIzz&jrcACrmtW1lT? zXJZ&l5B1#q81ToLnDTXO_aWO=5Bv$vFz(~PCjgOON9JKwj%?)?=yS_(fr1r_1${>* zg0`P5ld#v6rm$O6g$O7Z;}4Q27M&=MoX99%x$C7m_4$|IMie19h z=*7EMp6AKIyRui^m1}qLV-5zKWX$#(mzcOmK5OITaY(hgE%6SIUL%eAlEH;deul|z zST*7_4#8a?jYr67js@~o>{@i4h8@Ge@+&D#l!9>hA4BUR^805ExB2MT>!Gq+n@$u^ zw0`hsIF_~^c7|XjM7uZ7`AF9+J0`tlLn~c!_x4IuwlDj8J%-1XXV*O`E~9VQP;0cv z7CfNLf6jX%NT-)^N{&c*!}Oo)?)a@o&Had(>RxATxC*y*rp+BG;T=hX^P3Tw*?^HmhRh}?&XQrRXqDULqj|=B(t6q zwzm=wI+4p-9>tnDLVVRhf)g@L_uRfa6vV6MPOR^>SY@~$Ct{JRjeqBXxCf4-Tfkr< ze$1LPn2TE9c~zNy+{xD%i=6z6yMb%VuvS4PP#Us=EVExrT$4`Ix3mdM3MVz;Fd4Pf zvCDq9C!oIOv=+H~UTXsyJjS{~%B$jEsthlvr~ulni+fgvoKN`;XBP_F@46@T|0UqdI+b|I?sET*|>J znki0fVIt6n>Hkr@L2@^HD`SLDxj^6O3@OXl+!+|E=0K%0v?aODj{E{;h>c6?!#8te zXZ}e7NQYCibZ#Hu>%Ru7veoaOi6#1In{?95jtf9Xnm1=;OHw6p2<(A;Pvqg2F@2sk zqb&dTz}h;F@py%=vH85vx?8@bWIEzi1UPUKp*z(WRHZ+f44lb5AIMYgGH&f|YPYzVp)*g0@QSvu30+G~(8Etd!cyT9NS zhXJu1$VcmYB3L2fTnF*+0z`E*#dW3wQ937rvpm_NW(~t{Bi?wV3^f~iUN?;|#6+&a#v0rkft8o_+vhFg|AMYE7Fd>b{r;xb*)Hhi( zI}$%sd^@dT+IB@euhGQ?g_nFlV%qHHL>$D6Y{Ok+b%hV3Nw-aukH47n{Q)l1pT0UY zYMwohDhfMXd`buHa?T|#;9^X?rxOV_D)TP5Q5+Sevt51JY1FiPIf7=v$Vuhej4+OY zWA`lrR3iGj&n`i=Q+@p^u-Szf&(a;1g_>=+KOHRFN14lGd?r8kl?q#s_q3P{b=$bi z>e0kU`?-D`&~oVy%Dg$kvsC0{^i0edOTw?kpgSc-B`5D0Qa}ZHA{zGXiSLcZ3YLUY$s!HdxdHP^T-9pLMyI z?~!mC6;@kC9mTG}W3o-p(tsQdF0*a-!#od=YS%(Ida8XDpVpPd!U}%0$@UF@++_0T z0sIQ=*2DbNbZonCZwN;Rn|9rQnzA^{^zgzDO*s^TH~#HxO6;SQ*E4E$L&9x!O_CNK zw1F*eTBm8v;IQt7xCtTlKOx8a14Tf0sRaW~h;{jzfwa>Vk=M4q9D~&**Dsi*CNrt3 zMB;;{y?C|SzlDFIqXVq&lnl%ZP37AskXBvx9BNxza`^`#D)bY5%&FB|jz0=ZK9}WV zXTDml3Lp8ujmY$_ae2cO{k8L(oZo@v5?|xI(_h3e6B3`bT+kABR4*FOn^0oUKgbJ) z>V_1~-nrI?p$Ni)Eco}+lrWckJ(3gl@c$>2-S7e2vhvuV#-$%A?l46~waOI6E4#2r z)&V1yjSjr1U`7Pzom_B4h0`)by?X}kx;HSNR%s#Xhb!5q0j+cEdp<^WP_hqHa`*Qg zxQO`<+wttwI~0eJsr6Ir>IKR@zAzTTVXFb%4#^Ls*4(;OW1~;Y?|qe&_z>|3m?3y^ z-XS$fv7dO;bSkdpYRgT{0y9xS-L+3ejLv(~v?o12d;^#ZnU2nTlMv_CdJ=t$>R7xzu}YLN5T0y}3LJzr^5fqc5gYNfr-9M{Eg zN66!3>3!zjVe(}rFLZ>au)SLD03?zWtrcmwH0TCAOlczXp=(*vQ75`?<3Mn_?M!R| z1@Yk}Y!DJu!~|LBtz@9K^ng3F(6raiMMG=?)1JnqWWEhd#Ru~AtkNC8h5m(;OVQ^c zvP4u-1bYN8ADy*U@TU6RrZLt{Ks}taJf=;)`|UDdrz9-tgR@cBW1~ zE!=VG-R=GF@t*Tp^Z z8R?Eh!eAP-w9cC%20xe|{TPoZ&<3wTy zjvM4D$0gZ)A23vu*G5o}`qL2jyElJLKdyuGT~oN$XY;zc%L{<5t8vT!lw(AaTHQ?^ zH_8rPSU+e3aj1GrDHfj3>^LZ5@@|0%^Epyh zPG^U&t%|p)sE9{Muq?N5Md~}X?rpg0I-qV~!BEgN0n9`pyqtkP ztC?kQnP_6X05p`B7Q$I2ba}?>V|}?;Uc;6{=TfN))2kliY_N!{?s%Bp8}g5M_&|EU z7I5zzn%dt>{!8j!KA~r4lhl|=Jo@r6#Wl9l=uy*iP;x=LDs#eZkSm=EVV~}jw51hl zdZ=ujF9*8EgEF;+U#5R;7%?jZWTB17LeK1GsDlnWnd^_*Yb~z##CS$K^BhtlIi3}~ z=R+KS;rwY;SZqI^#BBC55xpwTrf$w$8CUhkuecvni zg-qwHHK$ZL#Tz9El7K$~8OmQ&H{>J{!$K^zBg}PjPXydd^Sw2RWSJC{$?0(yr0lz$#j!Z`=?`wX0;tp@ih8ebhI|Q|TcWUhdF)I*z z^?ifb5%(a{bCJ=st#(=tZAm)#yb%|?C{NLP?RM(x1fE3$bR~XLT)T>u8$Q`FFEk@3 z**P)V7>7EiPJ?-wbtc=|6(;^9|B6olS3cvHJy5=CP4rPQDWS5l@*kIhk7ajlZ8t#f zK$>c`XPQv?a=O$7W$Y@57({XyT$cq@P(qPd7Qtu0gy>C9 zyD~MsGhXh)xITK#-M}8)0zN(&@g8f{OD+meg^7}N5%7F@52y96(%J33QT!wYD^FjD z`7;VTy;A$0hnmWoa$EgqyR+-Vq;l<`ahrM}7>Pf(!VaD&Bo(Z)#_9Jk39`Nd!z@yv zzN6Qd=4G=&E&Bsw~*6M&afkL#QV z#v}+ygTLa~Nv=jKs z)0Te7_?(~ybK{EGEhESrxQtS!$k*BM5!|9aAx632^}iX2K};d(ts2FN)i>Zky)O$r zu}I-*>d<9pGHZlT=yZ93t-c}ex}@h-QJ@<_N6*u!Y>69I${+t#%qL@}FzfVaOzl${ z`l&i|7OiEFE)&t1a`_Et1Rjk<0A- z(=_yw0y=2>m+h2-gRYj4fZHnW(dw}Zp~bN?pPf5d_F@!w-OfptzK&rhm%5mHsd3kS zRTcuD$O6CrCF|rn_Q{o?2MbjCQXQ=CfD!{$9t_93pwUBlO2vTp=1wV@$5El}b_Ue* zuP6P}nVA!E%U=v;X^3GHcHrSpz6M+@8Hs&#IW9xWK+mxaqxWoe*ym>~rF5MG5MKGh zDt&oi)Yv}IfjXYkr&xz(AkKoVW7?tKnv!<4xNOg3Du3~kVnYIQywr2a{OPX|yi z_!+m9(qCYg$TF8sW3hUAC2P)SCgjG$4^PvWYF_(M7xSH+8s$$_8`MkT8@qR$^TDmC zUR4PU%uiCYCi#!%VJa1uDh7ro#O)G#(oLkV6SycuehnlB<>QYVFqfzE(sF7`-9t0^>M_to2FRwC0_CmsWq9b+z91`a4kp8g=wS-x_CI{KJL#CK> z6Er?4>b8c$+*_gkJ!K9gri&b)tx|CgPlJ7+?KK}2Nd&Vgn`Gtnm*3!J3gR04{^i4| zyfP@{oFSY#O0s1yhr%14)_MK_t$FLRD`DO_{h#+m*75fNo}5(y8zpmgw)aZPldNCD z?kC@K>ZXE>-P&_Uzc2*EI1OdgzMBx2+eLf@{Z&QBY>+Ze)m%-xm4=sRrG(8_kT~G6 znGS?6&4-$bi*y3kf)JxxR918&={HYQa~jQD%wOmB`L-TwdvHJc7Htj) zE!@IRq8sjiMBZ|U)7W>C3OJd?aC`aU_Ta@8+^pGvau!s}XIxqZ%xwW3U$z6wpNl^+ z$S=Zp(V9)AcsW07uxFc=SP;8Fqaspz-eD~A_@6a2l2~BJz@naRy8*q;O2&3!{$~^- zB_F-6HZFgP?ZQ<>|Lr_Mg0frb)LCbjdD;L+mR)K}%1K4Qr`Rjg*@t1qL-^v_(*8ucCcJhBT~&E>{}Je0C!dLoK&G>KK)nVxwzIH zpR|U?eK^{HO6=j7zF{7$=$*7O+bRn2W^N_4!eu^hD4=jeN9V2m0lAupt1XQkVAh~) zJl~FYbq&LlL(axlfk;w&ob} zx0NeBnt&ru>{ns=*S1(hX()*`o}Fd}Wh@XOgjwY&Wq*&J-R*9ErQ;rA@gzG>#wz=iEgG?><-N<9 z;0$d~=(hE^u%i^5U2#712GWQ0)L%$0mpWwc z@hl}+@wcS2nd3 zc_ff*LPv{NmHKQsOU1mkHivuX=~Y--kZxIZ>@pb^)eGlx@oA*{xG|T-jPV>PdfO}o zK8T6>+G)4%RJJrR2^7biguy%9it@9)=DT6{Sv-e<9bEV zu3zg!A$_8MPq`RCr8+)cv;pa3JB)(BJh-QHb$WgetpL&%6ETwkV>aB=VLID!nYtpZ zyw}Mq^t%Ucjyl@dI#!k(R8tWm5M!?kh|eC~x9FqWvFederhs*R){#f{*{4z5w_1Vk zk%70S44DBxIAGZ0F`~X9GcdcdAys=R?F}kvhhH~Fa&g8Z+Al>0E$&_dHi7aV9zPxN zJg`6KqSOyT3jHVG=xtwr6t3e5r}Gb&iuL#tCt;hLaT!P3?(#sMyZveS*$;YJhofim zJ!ulIt~kRQiM+Tf?^;Xhs83nMJ#ogTmE~tAS;!viFNkILh1*y)e`(zx;5Kv5a9cYk zh0U>vxjRc(@Oe9$z{j@(72uj$WR?uTGVys}#s}eN0<9s%@WGn%o)_b+( zd-5PTGF&5jeHyq_Gip$zJ1kW!_(%z@Qzb0s^wt^Mt<_#OvI9~bYnxnh`6@rSs9fq~ zi_=O08Dgbb>?0`)E+S>NoJmYTzp&wi#?Iv*wY@ z3uHia^5G-NsmBYL3>6@-Oz4&FauLzFr>FKo#-pAfnfG{Q#0lvY1i^7W*%xrHN)=S? z(c{I`j#i>;ihJ=2LQrIDj%G`uS}NEqT+43pv8V)+ni1)zao0L%eMBbQ+Y}MWM(cs> z5}7>4l>hEU9rJzLkfwB?!wo^9Qf6l`woi^o@!*@_41p<8YeRfcdIt5aWYH}v?Ts89 zSGNVm?%G$9nr*VnCu>4&r=}F$2)T7^meqJtYQs`_MuBdrkDuKB;e*XiaA zs-~d6fs_4*lLIxoW*~*co_tx1RBriCi&>=A(XP(PI521GR$zaqB0fDhv<-hkoRf$- zVlYS4cLaE8yV0FVOO5X!==}uHza;!%11k8e33u~{C&|DL#mr$Q{n;#XR^~f(_u$3D zR<5?vdaoTph^cNoRn@(YF&%rktNquBI4hy&?N;iK6E)|Uo|~pn$B3#F##Pm#keH+Hk+z~Z!;cV?T?k0E`(Y?~sutt~5Z zdUy+TTf@+bo_>Xx2WnQ2byqijys-t7R-0~3btJ=#>9&H`G0fMYBpJGQI?NY& z9e2wbjtGOKHu}@akGr(;aJPNslBqetL*p9iyyPFsGzo z-jHVYZvR?g5AghxF?n;|v-gXmV7R*c_W{1|ip8ISt(-A1N}Eks0>V84Sl6`vec0*i z3l8j*U9}$M`{55(Es&MotsaZx>scE6pDhI&9Gi+gr>DB%_xG!d^X6E=#f3t<|poJlp`-{)b5t zW4#|b5?mk^n+2aLUKM|*AtlU^9g%Ik;*vhpx2zFmsw6Rp2bbI52;5roTu`K6aN zJi6dOil@cVM)Q{=CbVKf46Bf370rlt^elBVdPHgyWUb&uonPj)ms1M2T-@pf+B$k^ z-O+c`M;Dwm`S+7;ySxA)EibfZnz(wHTw8VqSlgagCaXkbbt?@^R`wAmXA>u#KG-@iwP&^yF!I&qs-3WSo;X%anj~SLoJ&KVj0dl+kp^Gd z_ClLntbELd)gbHwvM*ODaSIE^>V~aKYsG>7iXyhw>6S_o;1Y^S=y5PJoOgGxsSvOl zOlXdSo~lPTS}W&;)_Py|lLp)Q$|cYn(Yr(gu~cOu6$)CdaWUeyY`iQ+-7$ic%vB4_ zc|2WiEFPTLSErG9LTPI&F=3~nmYLfjIL)PsoD(@G9Kuk_<0~DSc4h99zzAu3{>Q-e z{b$~x5I@>4o*=`+&itOxqQ(8&DT*33sraqO9J7v-&eS&EHlQ;nvWhO>{Qz{7R}^ko z0`-({+Lh*U*vaNwZ_`f%FI>Zv3wjMYRWN&(tU#_Lh^E_buFcMc!I%c4kM{Uywq z0l|b{C+!UZ4Ij87)3N(Z;?}2ZpB4hx!&w|k-4%T}h5G(Vb+6_^A2n;8*L97IqOMz% zWi>#bs~jVYv$8B>qX6YDJEtsg6=MECFL*^5EbfFQ9L6vONJ)CZD;d73>UN-XS&Tu% zs;o_*u##?zz$-M@wn`8~NTH(_AicX2$GuMLb5xs2-vsEbce~_jQ1DJBieaN&kHs(dh3I;4X z1Cf6*xc`NZBrZ#H(<1H;A7lq>{8liGB|G5S0@S*3AWy_7n*)wWGgKQm4(h|?3kY&) zZb5SW?+1UG^d1$>&`}CS8r+EWHMU_Avdi&cAg3yT8ERcX&T3FxRT1f{O@Ls&KRc>$ zKoo^3&nfpj!yOvbpk@D_zCIHm>^7n6E^Fd0u6qCBB?K37!aM;k9whI2e%|q-hFLrH zU!Z@~(7;T;%lD{#Vj>AlhhB6X`1cbNpxneZNrs*j1IL|4T7(Wgz;z{8sdmC$J8){R z2Ek><(2_6p!d*kG!!jOV+_L=h?@6Vb<}%s<;ECEr0IS=wPnXx{mD=U8%80>X^@cb} z+-1q#^ry(<;6cUSv$Cjj4jNWiev)rHyUy-)CDpW~|LM@T>!M4?(9{i&+EoO>+rprO zQ>VLqa%QBy-@mH&$M5EAj4ce=J`r$vkMf^UsCN;DYkvTdBfd%X>ZPboXpQO^coBhh zmRVV?6kpfbKCZ~tnSjqSu9%;Cf%1L`CGI(wkzjkJ#vD^4N4HtHWIjI(D_k#5oOv>} z4+Ig$@O#Ji%mIWYGB<0qIQ0QF+0tvGOQUo7dmG)`7iekMl|v^8E9Nl@yt|w@L;9pA z&~u&NM3MDj>s=Ny8skEtD(fp$n`)FX1a&Mm{=JYk%2dvJ< z8+Y(K{W zOw3;K)+|rZQJH1*WOph&|EbVH$>+U-r5EV+4A6EiL*?Mpcu|jSRQ4hNtDQ3l5-_tI zp1u=r$gG#wd_x)kC3mK%S^^jnQ+$K zhBFto$1`dPJ|>p)={v3HQ`-%4;N20u&k;TAMJFapC}zO(XHCr4om5Dt!!bXux=Ynr z@%@bw#Og(P7l$}M`{?b*Ix{Y!zSk83D}#B(FJ?rgXg$gDF4qfp@Z(DyPOxE`-F$H3*jrVNp_ClKmq1H+QQYS;=&{v zotNu@J>el=wI~I+u z1OvbUd@2$aU~bo+Ddn%qQqpGojKf+xS0x&*GyHE%O1%*5a34&xZb3w;dmCJ{C5*(~ zTAc&;0umNt9QqFSy{!;rBsKLKWBazdL`uNp&B;?J6u-W9g?M~VmL%zB_=1 zqv^XbG%zACR5)qwi_lOuAn^sFBD6EsWK#^MnoVmLCE zAX{#22cVK74AMqOIt>V{6qNUzvKA|s{uWy)=~aC!B}~G2$1U$J*#anW$h=awBVPFv z+ZKZ~KA!c_KL4k+nvWn6Qki3Q8iGsi)z$i_`>`&p_r&ZPpMh4Z#9fC5vOUen(IINz z6^Cvh?yhZ_zYk&oPC|Gf49VgNyGwlHH~6X3Y?0(#!nt8pSnHz}RLZryO$6(T;yPWF zGnUCO;Jf6E@&;7iJox*CRC|(Q|3{;z-uaAUvSsS%YhyW8Lo4&Z3o8fmP^#iL<>iqB zmWvyHA4O+srd0vEgJ1{BU}~q{m6`5*vh4=^19cfhz>S!^Ps99k%Q_)sM=(ND8VKhO z^}=Cw0hHerZc0wirb9#}oS@?)=7LMB_rzYXfNc9BvCvD-W~|GQD(QI=hlOB zri4K55$Rwq6c;?CNWMn0YUSWQ;5COO5HH}sIClJ?ByW7RVa~I+cKee7jO%f-dEv!F0UDmWs6B=R z0pp99!Bcy9z`Nwy6Qn+HgF5g5 z0e1M%PH1c{IuaiumYD&L%pnvts<$@YfZLxIS10Ah2d$YjHg1JA1WCN4`w+>#^45^i z{KX#MJ3I`JBe9AzwooS|Zq0lPA9Zw3ZYiV2o(AfoZHhvDlR2Y~eR-(#@QG-pL>X4w zxJ8f@%1%mVfVKU7afvjx!@R5T6Cb0_VUzrhobGTPxL-7DhNUv^I+U8}{yJOsw~ zhxue)-2?@C@vdJ?LZyL@EXn09n21TOpnq`H!q2u-@`ZSEcu;+X=6J`w3sc@P?wkhK zRO$|czom%00|&+!mqo{7A*MDlb*KZlnpKjk%NEs0Y^Rr%DTx`0`M2&MATlQYVecMi zEd$+k{kPS!QAm5#eXhS1-Zfqv|Im?wYCake=bCXr`^jLOfpF+Dxsj%D2&1BlwY@2a z?sLBWJO+v^xm*-d4mT18-gZZy!H~GRd1+DULETT<)+ zS0f?3Y3HT-nj$V4cCIz@l`84VzWtt6B6F5c$kU!s(G0Tp00hKAljhzVSV^=ulY$y zb$wBoy5V;788x_^)M+=gLK9U0Co&p(5Vl6mW$?~>rYAVTNBr8(@Pf%Myxn_)+>$6czT0@k7HrwMN!c(g8>vY2s- zTPq1qUH%ZcYC=v;S?5&S0t(Hu>mviw~YBgsTKywi$YV{Q0HldR0&cpbHg2c5V>!5nBc&%B`bq84?_ z*nlhSKc)G%KOPJHK0%(B#!uWb0q?`eWh!%mV9Q0t+JNGwXp~FCK>sOS02x~@J$41i zKJB$u!lb^^oB8`PBT~avp^gaOjpoG+nl}l69q9TVTa88bWjO~|GKz!-li(+kQ5p*~ z4t{6M)(c^Q%YT9IbHU)T=%c{QnfOq3lvwPTQDbErc7bY?3o!#+Ggn@mNGsgsm#W0g zW01&D60&GQg)IO&?FEYF+d0OMxtIszPDwlRPogJDpL@ zd*NPb#u;|K43H^z14=n?!t|4o3 zF)tQq-9Sd`k6MN^PH9Fg3DkGHHSVFX`f*t-1u00iylMiNM+{Bm?izpbw!u;2YD~WZ z9304eAOky>${N{yo;sDU0*LOevD?F>mP{p%|8NCYT;E|P+_rcTTVZUo*jVT8PedfU zTp!VIQ@udS&PsaZ7~oM5R1u7vo4y0HzTe*~36lsMLj(?5R8d9sK~(D;(DJ0XbGMe< zeZV(F*)V8OVKbkwm4K=tWCUG`?0d5`j17P}*{v|6)L!3~9b4zj&m4xr=7f(DH+}76 zjhSi;7j=hQ4`%T#W)k!7+R8Rjp0)Y}9rmrjQ_<@0zKeG9x}3crn?bM8$0?)1Dh1j# zLW+pyMN(3wE)JPz9O1+&eKX(6AUv101tU^aT%=7Kigx(--}zorE-PTLE2%33u4lh#7rRaPKKTTX|Hz76*-z4iI&us)oq?96e1ZMaeVE^0yG^P(jFxIE%7 zD+{{`YCdLc!(A8;OyZNb=~Odul4xs+=HIHg9NDOo7o=N-4ElXazswT-|$d z#q%^nYliLe_%s}*-PLs%8d8tzP({g0keQNBN;(TEDMfA-=nPAsp zgHvWHd6P5_EW|OxR5FVGSdGLr9f6yAJ4YX@f%<$W>~~2{Jsn(XNGe1G1*d}%*zKh< z?>}vUhsBO6Or9rgPDL!%S!x-7G5ngdS=*HSthFpAvr3$!XDyWNn=>Xrtal8H1k7C` z*UPNf;?{RnZ`|%UiRKKdkJ0EyaXLnslQ%_%B!O=R(90y)-iZd&{)GYedzDT`#yF5w zjAy~Io%k_nOJG8R>P0>QJlgZ`7Z@Sq`UOz$q)yrZee;HW+D$9h2pI7QRjB0VyExnj zRD?R{`{a8tW%^Qu`aQ-Vv`=K(KKXEE1mcy}=Uu@#7nL6rH#*rga}JVV8CFk0rtBNE z;1ps6pL-Y_ck-0g*e{m|ZmpWh_ZC)yHK}1WD4pehf#xiuOYQG0)M8KYS7wjwQGD)A zGPaj_V|w;SyG$b?OE2rL5RC!|8fedQ*fs8Eis%-348c|Y>tBIlg0NUmM}OI61KoAZ z<=$5OKZ?%9pXv7h<2%@78p0^jy)>t-sThi6M49tpM&__;)3Bwal|vdzp#yUYx6CnT zqnT4Obt~gm8@CL%?7mxft0=iu>Q>+1=l2hI%nsM*x~})@^?ZgZ5u>STS+63ve8rgG zL^Q~2{>$*(8+W<&$8USL&JA#!s8})@**qH_VRT+G5w@+9y^e=)hVoIJlpK4L-(kR@ zw_U99L_$$Lgf~L&)GJ$2T-pAIX~1%6*|^_;;r;Zqcsf7$M6n9oVeXsE6uR;h;E^H7e4*FmpXL3H%@X_Fe?D76S9O&Lst0?=r zbwKo{$>v%uGb2I6shCP`%MlL;aYlGVSt{O1{>0%Q?u3?%RQEPp+_wi4Hx;1v0W_X8)6pRpy6CxEJpyxAw7Y? zl1W$kbtKLT^PX6V;D9I|MEi=)NhB&G(Sdl4NO@qYbls~ zZ3Q7b0oeS`=~27TPBMXBQ_UyzvbaWYKnp>ivS9d$0{1W^A%>aiE8eo59SrDz{@9}* zFrVx!X69hIU^#&fOIMHa4b!2FBszkAfghzsw&!JFeKr|eFe^ZL1NH?)yc2Rg`n&6R z?r-6+w%cfLg~&?g!i6uY<$t=2&PPst@uvX+kLEx>i*p-IG5!tgOCaI@JU1|n|G>*@Q~Lu3FA zsqqv(rbW2}<8i}#H6r7aqY$s53PPSM6JEU3i}UB+6fRYpH}dKn;<#2WlEBDAup#uR z*iR?&(s(rDY&#JlQIYD2S`(3FZZg;~3|Rk3xK4iTV2uDb+sr;zl4CD~dC1%BMjQG} za~prTA4;EsI)yMqWUr43n5wVN`tESUS}!d`S&VY@?1dz}awp#bOmdnhN1ebvcdtbu z+5rgvQ=X1(_qzUgK@Ds#jg1kHP3mXxz~@7hV};=>amA5KAkU^l#{#w)StCNdgplTk zu2bu3@!j1tT5+zA=+368zo2H0=YN235ry*UW!(<6{s<;kNQaHPHiX1UxByA?dPCfqit_4ymq)VA3Lqnvi8Q`D+cx)mPy$M*bPUi;6T>!+MXlFVo zIHCCPJk|g1;Q_KGWX(6kh2cTl)L6Mnks6H6qXtc(cw6s~4rT8AN+7-;rxViK$I1aIQ$p_YC|- z7CRSUB+UjHr)>i#Ayv47bRkiro894QcZG?nAwoo zI322NONQDh!%%xMtq){-8`PHfi42$67a>fbmH=5ta`HGg>Rol2@vd_ewGpPeSa09b zlqc}Ane3yL{HZzcz-!HBX}Hu52rMa@_SSlF$E{&f>vT|Su~u0CtPoc%gyV$;f&H$0 zHTlb{8@n88G8>a|0`x_kG@93@A3C;XxN3hluR8@=ob16iC`c!^eo zrzO0beh0G@4)tvZ?@`%XNw=7Hq-nCVxs?XFg;>5}^k}e!7){vR8es4^!>3tXs6#Gm z1LS@#5IEWq+puq&#i%hA@w+^v#w7CnDThlGt$P?cB0n%#59ayy+` z{BT7&Hxh>YL~qGAuh48W5>RPc68qRPPxTW#+zLglyROedwV!i{rC~nK46b35RFI|C zat6^+lfD)xHwuak<+92Fkgw^YnHxCiAg^VXiI6NacSuseBxY6yXf@XENUBGWOpSt1 zHqGmiP~KiUmq4F|H@4rHKO9#oz?rd^uAYG%Z}8n$zmdVTGF1BlTNztq*yOxS+X|&&uXLo0lUlw2)>*wcH#c^k-b?66pKv?vTL5 zfMu1jhZfq&aWs3qhlm*A=XHnp0R1?~LyvmOsiabX3UTvdD~8)tYE{%*Ob?KvcX+%4j=9U1J}>BL8NUg`2Tac z4aSAA==`1yuQQgrp?(PX9VUzd%86lYL?NU{EbyQi zo8SS|CfZzx)a_0c0z#-*(<$I@!JxB6b2}7R6gl*`?>S)f5`^!f#SR8uV-50Rxedb( zis)JbeBjP>mVY#iz1NJHZ6(P{f{{uxe}bQgr9p>HBDv6iZen73P68C3=`)%qy|{QY zPpqW;mRQ$K7Gi5XW;WVWYwTAxsN>hGmiwR>Kv@)4U*=l-bT;G>-6a-Dz>aQPZ0;Jd zAhd>-@K)GXcHZy7?H6pzGT|6-;a4*?uwy?mg8-#O)){WHH=~_Ozv^Tx@PwgOqc+Rj zX2U{|$>0H3#;6o3xYePQg3TTo3~q}9Fkp*Jbv0?0CtQAkurA!;3y$129|PP~?9Tw~ zBFB}8x7CA%(}p7;djOsjMQqdnX3CIdmUZrO27A@(>UWz(eol*iX+G6}JF##1p=W+j zF*Lu72^YtWWoml}Blfba)Z^)`t(t>9T^!_%5c7(8#ij`1jg>t#s-~eUQ7!vgARki< zEJ~SJYs4dQaOeu7PHW_UAU?8u(v?GA19y>bRx=^Fjn{|_C;b-s@FBN`Nk*(7O`no! zNG}mZPVDp9{1e`CoPI}d7Oz*+21><@w86P;H3k*@w8<5th3hzDAK>~~?#UOR!DtIL{R zR|6L4`17?+%Q$l}&q&_JN7Shrc>w-CWD961f!K?K3>I?6-PAH>>z|7O^XR=l?-ba1 z!URImm@pi2dIS$kLGg{Z*8T2qKad2>caFzYmNkfVJ5x00+}@AsqN;DT8iB0gH@NbK zQ_KJ@NtlTDO_S)vn#NS@&7`c;*tU3HxS5Xrl879l%EWxFi8dQP@<4w2(d3CL9^y%p z?rc%zfF;L21CTJDShyLwy2%qL;YAh;ut1!c8Z#X_x{W2W#>ZX-u_kRqE?nb?WUMPS zVPTVwN@o?!3Ez44uMSHDUY$3`EQbuKT>&+fJduKX8>WELlNB&>CF@f$YBSaMJeau{ zRbIi`b<>C?dn&~1C&Cv~nMcVndqYEfxl$3O0p#3^{)<`%EiYs=#My)hW0qdzL;a$; zih(=5_o$YQzc`v$Yeq;xS*LRXkh<;Gj>7{Zx~Y6q3kgAL6ttr8j(d=sdEYOy9O5G@X9BLYPH!I# zuLt1ZIu|n!8sM0(MJ+j{@g2N}L&`p$T{K_zW^N`#_l28dBl+}SDV2H5J|g8sjtep) z0Qucq;t2mqOf4+|z%0&9<;%bc!(PO=uF2>yi*n_D-2-z)QB6SU8UUwt8WgDt3t|1r zy)N_sT|EYBoZa6mn1g-B{CpeDxAK|_yz+XF2~c<#IMOu2+KvGzA>5E9YMYUgK#VNj zdA8l8;7DY<`rweFd_}(invTd7de*Sp_`b7_zdREYfK(ZeRqlzHxv5L>dQccT4D)n~ zK7<$`H97Jd%J8~fn^+pD7Rw+U_P&p9C8Vp9*hYkvv}9 zMs7`=+No!`-#n39r^Jo z3P>ORcH%cWXlm7Q8TTa|(xSa4xK+X45*3})4B(XERpyq#|`8qDlPez{6=nm3}m zxU81Ko4jp}s*bB_~!7Cq+d`GFek!KoqUm zbEApy*0%p2Q8wgnTgcn-PhMXN)Vdctt?h)vqQdWMb8e#Rh-n(Zu0p61xfc*skZ(HL z5`B8+teVj_SjqHKr6ai3(UA7C5uSfQm;Oy)9^y||4cu*n31)7&4rW4u_pgqgi!;x# zP)3gi3VZ^=&w3dCygBssfwbF9TomzeK^lZ;nFEjx)CP@6d#8W22Fdl*eym#G=pKLq zvaNH#k!H37(#+K`H%v)0k~+q5Az#d*h-^d5hK^Q2klJpI@UA^PuyjkD*ey>2O1ui| zRKPzLG~1pO;1T}$zS^0aVz&uI@Z~MV4}F5GM_3K6}GlUUp6HF*258avS*B7yZ{`XVN12+>)FrmRKSkPLy$d(6=+slojWRVsjW0KlxY9WgFT&D zhjuU68L=1M$TLD_*b;7TTnz%OT~ho4vcE=Sm-p%^IxutRn5RAnwi}bHmz%$5L`$3F zb3w7*3^IWPE{>bH{2c*2^>QVVnSM`cJ8OoPtOoP#5m8SXdEjrj4j5AiWiRoD%#J6$ z1&16deL%rz~v&7xYb?a+3kw&;9)w?aMxDz8IUcWr7O31L0I!2Za)-l!LGgBnjmFLyt? z(hGWZWJqgxUSZvs<=Qf<1OMojxRk4N`Bp-z1HY15L;j7{N<<@-zaZMbYufm0Sk6OAG)|;ARb>?4o%=a0gf4$}Ck@ zEMAcE<%!9S%foq8AI0#M6B6A`pdArn#6p(K%rs!hxh6ZVwKrR-@7ZYPjCVH)OXD1sy8HX#cy0qKY&yfiV@pXzN{RyoN%#p;F5d>HU^!b_RyC553n(sN zjPLRRiOjj93wz_4)2W~q`1>O_dK>)y4WvfmR72M2N&xKEzd<&XWf=lR#!0_eqB~&(wOa z!uOwen?-UdK-XR#=3`*WI0;G$?H&Rj#@~C(lOx=Dnk$xY!iUeEW1M(J5%J=Y)CV?; zu}q|Wb3qXyN5~EC^px<`>4OCXaq=$v5!h}?0|^UJWw!Xl6eM0^?s=EX>-4njpy4|_IzdL}(&$X^iziZSTG^1h$sQ?$ zVg9ni0|9Bqv}zOtOnPPxY-~x_2v*hz5<3iBH2*V&IU8O8<#U6EvV8A7-J}aVDKxs) zF@Yi68W5?xU#bapN+>_lp+srGJb9)RsIrNYk(tWZS9#Rhi_6&|rA9jJ4AZvKE5vY} zE+N#dv3(r@{C5Tg7i(Se?Kp*ZaP>xB4Xi2#^L)}Dy5DbvMk0)6R2NT7dF1lY0@EU+Ln-qD2+Kk^pJ zsUUw!OoeiqpyG+Rya-eya6i-r4X??bxfWY+r|xF(nZz~7uOaPTZE3$MD$1q_uEl4& zq{6uv)HkT#o^=HM-oHZU3BVob(eL_<8yS_CS=ZFT`-59l5Hz?qz<+cw7GUwMJet!y z8K12}eDO1LpTC_ADLU^NE|d5=I}@CzL&V0-`|PK`qY9j7us4*WVGgzAJI9SF=g17e?%+Dk27U4y~ z6u4$+9iD&;0J94^U@%lEn65La>8qnP;5xgfK&tMD;bC$^Jy7_&= zRNrgEurNfi`6G|;5*WMti}%%2jEN|>JoiR!T%XBBa}6-T&b=`NT*@xLd3j9L?b{K< z<^obx!g%Hl$szBPqs{2u*Ygdb9hgP2zO$$`bAic$*6!ia%U~YzCu6Z&cGX)a( zA!)nK)Lg0saKvV{wzXwc&Q_wAR5jZ@3?DDc~eV`HvCL z&4_f^G|=AAMBo5!Pfhr&eTVxRV*oJ{4HJfjuUevCv{n){!d{3=M3jZ{B#`IyorQcO z!N3mi40mgbvQh6y;mid0j5o*n#!nE3aI){-kqYqfRrgoK!|tBYQ696ZP8>Jf5?g}> z_W;Z-m>pe?LTR9}+QSb0CTLSus~#A;#~|XW2W_l(Q_so6?q%SvdFcRXYXi_wE-qW9 z+4F#^Vv{bUEyvzqo#;TX2(R&=S@vEA6(p*vyA@Sj1^eZ7{pF&vZK+^m&AKQM6Cmsw z#pc_KokLVm_xj_MIhkNQ;H zixOWnqpJNVDLn|9vnJIjc6!eea>reRUELORY1;uj@Ni)1CV*)zm{G(MO!Q) z3GA4y*b(L?(Un|iL|NSUZJv6GbrU3hSRcpWSNm*kVtEAtjm>=HUnC`MZ)NrEURaY% z|0=b4urkBOC*uMo=8!Kt{5WxMQZvm&mpi)WV40uB^baSh$oECtJmmxaLw?0HK)qoxqA997jq2Y9YjQR}N4tJ+=DLf3PB zf70S|Nep*Ndiska@vGPn(racQ#Y};J$xl#D#-)Li*WrGDzg;<(?69I9^~X=;XX4s9 zUuTQM3zVuI2s`GaSLOX*>C=1JlKIQhqkhV@Q+?)z=kIIC+DK^ykww%+-0YP(*3)x0 zFHNU|W8!|gIYpm!N;!opaN%HmpDi`N?VU@psd)8x?|jj8HRmpOp|P9!`#}5OnlXP@ zFf)SAcTHLF6*Wb%3p!sN&AECnCGD}tjva#ynf7VMXHvv*xpMmtc?rZQ=5|&PW8lIF z=n#Q1(Y>NKzO~yZVK(xU7?v2fZ;;oHuszdHffQd>FTcz=693-&L*mbD+n_(gkB2`s zXkEsgKBRhPP|xnSOQ0%tx=*XK@Ad((K(>pqcCb z=I@*VSSVYcJRVcw&n2rB8=4K5*^9W-gkjRcUf*sZ$p+bH-kkZU%Cu>~(uh_3;)xve zMZRCAD^k0r7}pm!{i}ctrolQC86++4R#adjWuqCSpFbHByr6wk-!CUDxDGfux>DV8 zrYJByACYgDzXY_@Yw)93<|f))XaJ|I*%5!j00WAiCf#=)!pdo`5n!$7Ufss*xk}uj zvA1R&EJdV40jTyKAd^o-v_B}rEaNxLe5&oz--Mjc9{IftP*7eT#dEE(sK-B+rwT7K>@4g+we_aRh z#qjSd<#N3`(yZ5Ft`-Pj63l~BAqiX>li=ZjO3U@szwyBB8QKsNCR3;XD?{Z;6RE@7u8uwyj5!b`z#% z)zyI>PXon0k(qgd3L6{*$~Mrw{Q?3rXk%siwMT#f~JjRYm=IiDinMAAZlgHcDAQBtwz0pl)sQO@fxDjzP;G(_fAtWK0kXic-=kk>; zcYX0!5&mL5pSVZX{$G)Ntu0|aEtn2ksG=mNAGLhd|FH*S6u4c`0&G*ffi>6(ceRqI zq=LOzk=>D_G{4+D9t(!<_jS%>0d8%2{uTG-Ba&zy#J|5v@nAzgK79hlSSrcOAVZV1 zPeg!r3Q#8YAT*A?B`}tXFQS?zJyK^MZ=ou0>T@yW!9|Q7Btf8CwbwdK2paugQHgBE$ z1~2r`JT3{uz(!R@?TAq$ciunZ@^#CelU+eLbki!7ttwX_2CwEp^62v>#n}JKKoZiK z2PjGoPEi~AoM|`5n47}V94{wy7LoNX{Sn=LAd--58JH7bMd_v5bBLLBvf zAVDxYa!R2cK4ILn%jWmCF-OneYIpqhVKF~!X2D>id8^~4=*AAo1!2-{KYoQUCakE^ zeDArB=@XBe&XoJd?65DDNFL5CP@AD|cjulRgf(0|`}$0I?2m_m@AHDaNcnNTQ_IR~ zhs(QvueHLTImdpS?lwq`X!p3+toZk^O=?`l%7tV5>M9?45?q_wUn?TbMxkbhSqFO9rn!o9_9ltddm1R=plK89Y^ltr{kjR%j^;y-VN`da>Va{nmuB=HE_y;HCzA z8CiWddu8wt%ppz`_!JXV=`0<#OOP{S`==|u7aJ}AbSs*Oy_0qB3WGe+cF|OwaQA;8 zcyXwCxIb_CZykPLmmQmZCMgO;4(C?0zf1Lf zd7P_u5xZP6k#YxO!^;PVVvv>9`z+$S54lU4X@wgs9aT+@u_madMbg^F zUCb;?3{0+9UhkQMC9LD2>_nNDI8L0*F5<0N)gfm=1``)=56;Hp|lKK z=bqCYLewXW<|0nIb_yUq;efAA5~zdXnGdpxSH%~h;czZWFMb`(M|1$+*bo#N7lAq! zgo`JJANPHEg=o6SOTk7Zh11lgOMrBHW0drQV;vX?iM0`h2#j5Qb{Ty9>$VbSUX-@3 z@pcc7RF`j*dW3nj5}d2kzY<&TEr*`YU0n6i8v>&`J4yG^lvzi!6~di>^3!>F{IjGv zY)3v0Tob!jO=|;buK}u=GTF8PZdQy_jd9>OZlC5#z`AA)O-CN7NhuX9Bl3BkLWpXd zHFF3$tdr=KD?;z^5=FdCjJ;<9F3JUbTZ{QPV{J1U*|@6{f*gVZYeZ}Ki>!Jj`s0C7M)}bV(4mAStT43bqGiwDB z`l946Tx)f4VmudN?ki(y*b^S@i4lQ^1H&=-2*t7JS_grSZ`U^*-)_{A*~atEheXWU zlB|hvE(c7p^X!8|spj6ev@NEWf&ZhCmt`_~Djkk5mz%V9v0we6xdS(W70$58!Oxs!s^FE{iU z#Hb{{k%p?_Fi;oO5L28C^8a3KbB$}hxLLP9=B?d*LIs*Jw!B9Y>1Zh4rf+0S3Dm9l z>q+)74kEyE{QZV_@LbPAdZ4K_VhSQN*Iu!D+2O8r$Cg|W&e}ZEe_=y!aV1F-+P>Kt zg_`Rk)jIz%`S-Je;&Y14l9%6X6UO4!HGEt4y__4E`&R|sJ?Z=K(vI_XoZnxMl_vhX zE3YEWre*rzi=q?4b0x>HQCfkC-BgQhW9Od=9DkhR0DOStwuj16#p>&k{f9ohU)(WP zPNi`^`pzr!ul(v(GM(6HlJ@Dql(dS@W@9+3WnAFh`z>*%4az;yIz~ z&p%G!oNmnh%GsNuw{Z6UWvaQ_9B5>l&H>3c~PON>ux{<9!@x=~vQh3UlUVe8<_@I%el z@{ZW}9wsNRGP3GLV}_Qk5{p`CRifug!^`uv^E;+f=1u!Cld^k0rju{7?fzWba`#_K z`MQ~Y&LmKcwV!^w!}9|D9sZ|$=)04${D#T7zrS*6CAjvUK8I>E=jtKW?8|1?sgSDCW+*iA@L1Fs(Q+(2k%EDGQdu=?=aFud`U&5O4nZq!etGvM)A$-J|KDmrvvu?S)S$+ypAj3PKdBVLVk zn+vkMm%wl@C*qj|A*D)7GO`z^X_Tf%c4fC1kz>jl0!u7gJV>kDfp(c$8qDlbHMw1E zX|?EB{W0pw<`qXRf~Bdgq=Al12j_O|^i4*@Ub3&UXEZnpwU#vY#az^}_T+i^3aq_U z156`Vy-t2a-T+^F7iT+s5$s>Z?Kk!tM*m`Psvv$iBw$Op!HqdA9;sV1hp@cpD$%jT z(XeBNJ6`$>(ZxHvX!&!9Ud>8M+ght86Gp3w3MV?<`8oy z*-UT~eNwzvU#JDR5h!Zyz50a~U>V5w}oZ zl~n?nV|;R#C31_xR>5?mtOPjM~$+1hW$vEtM8Lhb5F7BBztd;dF1Fo zA9fwY3W!C*F!R`ZhZURu9c|y#su9+<>aMorI^;of#6>|pTn%LQ3tO;|ImP6ww5#9E zQUr!w>;Scg@4!A$>$1w5b==fQmi@K9E{I19CVz(CvSL71#fwq+aPyzwhP7Ef(nf}y zLt8S2&$JabsI_N*K5{yHshaPZ=L_NtLHp+^p&s$y&?O7|>NIXHX6D`~f8#o@P#;)s z*7EjjMSLZ@qa#qDHniE3@icA^8Gd74(HasZCb&&TP;K5nPJ-l)HjAeXcXopCYK2Ks zfi^(8;SgB_PheYMWESxDb1%5XPFDfj*Tpr!LQ0;SK^0M0GYqb-poIPfo zIh)r#2EF?k&kC2KKyOQ8U5C`mUXx1;4{Yz^G_2(`dIZgeNM7BIxVh4Cw06g)VW&TD zxllv&wGVkK8sF}Yt?#+lTpBxPhf16)JQ)2NH&@#=+GPJi{35QfzFF}`UhggWb#A<`bK1bha&>_4jc_F?!|0xyB6QFqV9K>^X_QPB`03+ zZ|n0%Gw0%NCy=HuVXv&O{Lmd<)Cxs*{G$4NvO=Q%=d$fY#oMR<17QeNmYqt!Ek((5 z^c}?(*A5=HI8z)tyeyircwQP49_n}Nhk4Qr{oBfqvf%~Nc+8~bnA19rwAclSYw9R; zUY@}8t)BK znSqTc9g|w5mH*4fix{(fODi7PX<2PC-y$m8k#p+8ioN=gseSRsJEtHk$U2}}u2Fi% zjxjfbMt>w9L@jM@a(*IWUS8TE77k`E*2_nlGKbUarVyj%Me`$tO6;m%rr+I?tKeXD=IN}} zEnDn07C~|H{mlbPXU%vqgr)Y3g(5*kKMrnvGmgxV}%m!DF_pK_kPQLJkp_xL_9G4|LbWAvXTQXRusjvrZ;A zKOm0@ihHamZUde%Yd*yRdP9m9>2F~WMS%{=rojmZnHgoa8MXLuJxkH8(-d(I(->{& z4iiE~QF&n8wE8YzFy3Hn377+7G|H7=7bpdm1bYm4k!|Kn&5}Z5`?X;lZ!a#=24Df2 z+8{gOVqc~t4Tgcz{G&&;(eMJ@wk$9isiOk#L&Pot0L_Y#qcHu-Z-`Xpi2NaIgJlNc~1$umZGFo4}M-h1U zJv)FaL)p+gga|8n{c?tt!)R76eg(Q}GS(q(V@4#wko@tYomj^w5OnfXK&+Up{k3c~Vy7{Gv)Qh|jr@00hU)F=rAywV6vzF$&-upg zdVI@o^XDIaOQ$1Nyqui}$EKuTEq{Jo`s=ns{Q6qT)YAg{>G!u@VVAGra=pC%UO!du zW$Qh6aO*#x`U`sx?fy_#^7pb1Coy(5jPy1Xz`R6b0)&_|!k^oaeuvv3y=? z3D@!B!n5|5&kxs&Dh=$mUnH~s`hyNHtW3WVZ^=?)c(|2VRCF-$@A(d->1LCb8g>lvz^K5P3*Ik zey`jS`Q_X{x6c!%CvS+-ZdI7kZn2Rl2MAed8Ze}5@Y+7PMmeK+V28)|CPn3S~Z4O`2q!Ca)ZSx+Z$ifRzQv$l03ZT-1g^s7}I7`!j zA}i6#A{BbboAscAP2o<+^x}ykD1DYd2FFX~;fH&MJ?KkS3S%JU+pv>kyphrE3vb;W zEWk|lzlO@W&>=;TThm*W;dd~yXC<1)ZUx=m@pB8#KZ#!#P$4H)TG zmRJRmC1VfsGp{Z+eM8&GLM1Jrnx+}E8fCcDFwuiU4+Y_~;J*u%@Iot#IMLy65UN;THnX^6NDgp3_r6(VH<}}o^_H1sVXq#n^$#4=%&nqi~8&sWUWzsf(Akf&-eD# z62;5|J?U4O8|D84-G(QF-CGOJK_i!0&BLek`voHGoEn0(aUm{ya$*Nea5OT@@sg!o)YmruqL&iiD<9ZHf@Q3YjyOX(OpGFsJ;Px8qK49Q>OXKWyQuKMGn1}(>5;LU{rVl;nZmPa z0o&sGr00g`yoW;WyBr~PK`cYY-XHb*t)N)#Ex*0@RLHi|@ex2MgcC+tqN zyRx%-2mHz{Cw(2`ZUP|Qpbu{fy_uPQ4~%Ywsz({m?Gx(e(ozo91c(23OqIG9?Ykpn|Z!3LyO0oLaScfP)YMZKfm%TQ*M>B{lYrHpmHYK9U@tDOy-q{47 zKhF8t4!q&v-dfHrs%M#dSU3ARA1O9D{>OONuieUj|N8A-1*WPDmh}Cm=!>6iVY6)Z zMBLeg+ebMyQy*qVhyU$boHyD}@8`8O+@_M0v5>m?wr|i^l0W90O%8c}*?&&C7@Cbe z9T?sF=?lk}`F4N%zf)VATfvucIFLP;~anxQI-R_ zvck~RJm@aguVu%GLy+(zscqfFLmp2#@OJLFdu8EloJ+p>S2xo8%K)X{asIF?-!!76 zB!^2FHn$%K5JBvRo^|4ZbPouFhQGtU9~k-Uny5+v0wRHoI`Cm210TKX&u*{Gr6$m7 z_|Jn-1ccQ-+6bfE5X~zB!5CK-H!}OlaG1e}YK)bvg!-IU>}L#ttO3;NCy!2VIV9cd zNfa(K)^QzzPS2zHTnD7YW4Sr?qYcnGo^m|uru_r1Zi+3wJOz0X>SRd2qUqsE?M#Kh zxzk-WKyP61w!r49%Bpnd;g~}8)qVB-0!UW^O?-M??VBBnC^qV`G2#C?*0d<8y??(#vRRCsv?#z*hVZg65;>werCZ1iOg8w50=%YQ+KxaBch z5&2H3#>Z&{B0=r$6PvRswyRd$!Au`~=g_828rQcN{y%^WlyF8-dG8wR$z9% zd#lffj3cDvr8dJ|p4^5xxo91Wwnk)K=#p#o>I^Zua%@`YmBFfLr&h^f;B0P{ATq8F9^hEA$Fl2O#-4<3gf?i3Y z`nC=nKen7$(XP4U(0E(4g;$5YQ5T}VDFY44I?$5B6Uli2GlElZ3|TQ99fV}`G?%+ zRW$E-{qcC@&YS}+n|sNL5mZI(fw-5M!T$q|PNhHtCHq&G-teeG(fK;*QzPoYI`qx6 zKj&9b_vZ`M&k1koUX3mP;~c3=R9V|fd;E4v4k?q-BW80cg$f2LIv%a6D78DhwNY55 zu4QHXAz?H$xvqD=|MKjGu48)BEHG2}WXhIry-?vZcKVuURtOD| z>NWU@3nqV+HF6x{hyM=dg~yao8t*s{J~s|JN~tZjdKwf_`}8xpxG2%jCGoFSpU?&^ zE5FG3HO^Zbi-LO_I}Z2}eoZUc-mdq-efX=@{gQan*E7kV&dUmK&{b=G@uq+%6?)rm zDuKFLo_MX^6Q&pi)~9tH|vU1VI=dyqg4%K3r{xgUK^A_L=k5+yA`<|FwvI7s+xWbQY_JQKET zrrZx)0~l!=1K>iDRY?WNr@|=W+W8D5-;>)1zZu|Y*`C)2Sv38-IKx3TPO$s>A~|710fYy6-hc~)s~*!LDHo~v&iC*5s88npqUNqvd(yIZhA-;H4=JD= zjPuK_+Z2abFY?^C0XtaTx&Uon!@4!d=twEtRnl%x6gtx6IH>QQ&E+-C+Gd9Ntnk&x zL<%;yvHbx!P0y9VfvJp-l-qjevdF^sxECLQjw}vvr`{_afvIgLQhiL2ZF$ zJ<8HldLM~=2r6%|Iu`mJt=?c@J=_nuz~ou~o$v^pmZd*uu@ZoZ6uPo9JcWSnPsPJy z(O3^034V>o_xiR2#M=DzbauIQ*f+!h*CUm?T-MY=+&{aX_Q~vAX3r@!iDn5&kQcdH z97zkXlk7E@;0?HOQ(x$j>qu?~FM*!HwM)~J?L03&5-jp}%Py2}4>4`A`BClzcC_f? zQLxQiNj+;nfooQi>;3^?l2Oe!v~tdXtbkyQ+BF|t29c1PH$YNPT!pOLO4_}<(Ka+RY8g*a`AE3}kGV5u)uS z;yj)^bZ%zXBb@9Q5Uf_T9?Q2|IMMjD0PlH-*RV+PvSuN>5I*Ldh9W){7esV1)>LqYaO@a0snX9pt* z)sVgPM-bcm_{7=c>37BNK24?W^GF<6VW+{)@z#uwMm-%TGhP9QcHZjg>8%%~E14Cp z$K!{cO|=BM<*CiK^$jO%YAIDaHDx8zdnx;P)@H`RvqO!Gg;$PE;%$&tz01b87!Im` zyhHizW2E~JY<pR)TT zulU%rikBigAl+o_pJmJT_)q`tETNBT(z?6%il@8|RJxZj@-D~ZNb z+4+vk;W0)!__F)lM?9pbHV2F)ue{`9TE;B|3{)f^rGCWZe;}f%!9J1mL0TLKM1&Pmhpn*JWl#m^=5Zh*Rh>K+992;~+>k zR5A@^Xz%iJq!{FRP#+=cXVcyST01~a4zraD@;^$1Ci)kVJV~{*CdfCkQ2Mwz?T|6f z_^fc@yd`Kf^rwxcngZ1mWaFQEM$>my)iZ3?5;dd5^YL6ueSKXShZHTHC*qG4j3lcN+1-l6JDQrTsbkQZzr+u0yW$$%SLOe-^o2Mcu zy3L3cD|g`o7#%Vrb@st=Kb3VTy&kCv|8sk=mPComaC0oXT_h@EM_AZ4s{Jsz%HA=H zd`g#A%t4Fwf)_=5O2(CKYg&PKL{ZLWuK*ghqpi)_e}{n5nfU|x1K?`3)68syS(ryC zuQ(|~#9y)B!^}nwsTCt?!`TBsT@>VkL>~>T$W@=p^u!bbv^j|g38(_u9P(wtPiTr7 zm%a{@n}OIBzSuy1#ga<-;g3R@)6TZg*>BXL z!@2RqF^Wm))&BpUe7;oTlr3zHNGj&M?m?bvEqnLON3A0nXmX#;Q>dco zJYqJng^t*Y-GyI>u?-y77Nb4UDW&8h_^!3N26r&9n-z{(+K z%d>N;VNv6I!iy(7X(sAf2)=M?QsiOF(_V~uT<4n?TlD?y!sSS+eVOkG+c{9-La_z+ z1o+-t-+}rZ$7&Ciyo7%VGi;-~|3zK1& z2!XF2&im~Tb;-|I!`bUbm-$76`yxLlaszDmj8P)sxjOD$6~qcJId?kUk0kRjveV5D{hwCsGeOw zR@(@PTe&nb3L`uXNKt>|GkAlaLttsdGA{jk(?+!V;u_D~tyhi8xEC&@#(vFUs|LjY z>wm4)Z9O8bF#nEHQgm$FUoPKe*xdN{qb(UdaH#grKhuM017-tok1VmmO;5N3qXNz` zKuv?xfvZq+?+-e#DfSJyDQ_tPeUp?CuiuW%K_u zABq`aEi+=H`C)Wq7@8^z3*_ir6HXOCi54p~km({Aa3G0sQQQsb2~CYpLzG!Jkcyl0 z%BXgnADkz}mB~_iFNDV}9OYMkRjJxpEQou&2bYv4RrC8|Kh>kh!1~CP0^|g=sbx5gh z=FitPF$%!3rYgQJy)BE{BCL%0#EV1?C*Vrl(2}lQLGO5rAjn+~$vS{ZYbAh~s%JZN zz%5uxD8xTnSyOREVKyZt$BV8dBO#3-o3HT{chfw~{1}T7(PFh^zt9H~N<2+CZ@f;Y zHr+-7-PCW)NaUuqxByh8%#`AZI!L0-nClU}frHKMcJ4tDD&Zt>^|omV%61#V@XrUW zlT#Rx!&1|~H6*1L!9J_01HOjGrJ7Bh(@6)T8@l&KxOSKY+dw0Sy6mpr6nj()@&=s& z`BQV{{s*#66fKE44;goDR$`_Vr7^kkdTlLCwIgyew*Ljo?24eowjkJ|^efFM#GmxY zp>_DAPWBQNNG}tg4Im(U)LR)))6vv4!-=)z8e4@GjggThu@&r`Y>uE;asukRsX1qq zM>_b#)Khe7jpxppjlYoWDe|sc?Q*D-+Ct2sSVzf-ozvd#pDCW3KJxzmT|7mlQcWnh z_9sZ}*Fz_wK@AnK)=EhPbta|X84qxIfIY87l6e_U~R1s5r z)|TMp8Hl9>P20Rp%st#EjtUD6AQr5=x{OwoCc0zQI2SuJqJx&h-cC9U_H&M0`Np_> z#r|Y#r?pte3EM-I@DDsM^Od=}MKS;LIwwoURSMM4XTbE9mED^_Q)z{f2EI>B@otlz zZ=M=Hig9Us_&c}1s$aY@;Z2^7{afca(s_h@%<_8plAm3GaV6*Uyv2>C_Z#tpa}yIh zpTh@mR;Mnl&^n*U4_4s@=Z+DJ)h;TV5nmex$K8)I-#sdG5(x2{L>K0@J&{zzga)~lW?r&F;oV#ZgQxb~r95Tmq$4_7z*u#b&X58wVWu!u`Mh$^IApl_IFKh<3K)VC2>Y9;u z^*NdE1@=>FJ`WX2nB&b!4R@@9)SHIut=}$c&f2`&!q9~gV)-;}?MhEL*!sI|#D!7; zIRA*>91uRIUg0vGyTQ6EC03rEP<-YIFWV2~2pCQ~SQXjgco&IUKl(Y&3K@s?^z_%n z&05<`_#JuMmo9DSuoS-R+yOiti2h-VStDS*Bto#e=P3;g|(bRGwQik|3m zSijW^B(t|1ix6M?_Z>;n5Fs19$!05>b8CT#=%)1P37cW6ptN%vf&{!jsi0c2PE-MS zIHF|Ju^a)zd)eI-d)=sjmK13Lt}e*T4Z{&WYD!iEEPW6I5;2|Tl|Wp6F#<7#U5-c$ zgto0UDZy%G<#;v?1LO+?0K>r~7%Z{CgD*H1tpxCKExxqYJh+EC<6duwzIL{(m_u+C zdE!`G{#z7$cHDg1<`H)iZ>Tp)JDnG=phdSMsYG~~a0bqaz!xaOt2fDr(%m8<02dM? zH~o2$I|+_`U=iFt%oJ^+`Wolg&~>JkhADw)_x4s|8tSsQTpy?@CedX#u+`pV2m3d#FMBIf%Bf8bB+aWK6{$)}H`wr7IGT z6GA5?*jf8LU{>e)(4vzO4>HYJARkt%5Kod!APhbq6SZ+3){F9VoID6w_uYFamCHT> zYhmv5lDEnJiB{DLA`t;0WwbPb;dWHD*O^r(KpoCMf=6@QO0F^-UPY)RBpX_{TLpnD_YbuasP%Xe^%4qvDSF7OH&7ggxlqy6F~B(g{=Okqx$t z9$Q(~J5=I1lI!Q-ab$2;H0ctLSt8i9Hwu5XC*jznZS2Z}rVEktE>zAb7NOjzqx!GU z+;M_(cVq9%Q~X@h%`4a4V_p^J(fI#Htx(zTdiN$C8}uuj$WihOuy(Fdzjl!oK7^@1 z=2U$fd!xPA?Q`L&r;3dZ8GSx^9ars#boZElEqYsns1Kg-xq{R0eM@ZIamk?A=q%FU&53gx&oGJr5s#maOfS?l{YjMllla zePbIv(>I48vreX4V+!^T4sG~HU!PIi4qtjGe>HTPuDM}xo;;&?JD+enAHqAAptq+5 zTZ~wOeh>Eqkk$cIv^EkTC@5pS?f~j$vD(`f_~AVhbU5E@34(!{5Y`QM;b$*KJS=&C zP6a2@2V$)*0dVKGE}aoRr0>FGq^KuEA%ut*4Ls2Aa|EVSdO-ty%&dwX20V407bQIf z3n?sefef#Gci%Y?++T5WyP%MCn03F8(kT?Nb=fvt)Th$GZqoF=B^ddr&I83^`BW#8 zLDr+s+!=YN9>rqapk2_eX5v5cUEokFu1K@R+6$67VCBI{62Lmoyx-?*he4R9a_GJ@ zN+pt)D^j0Rw7P`#6LN94z`d}gl)`Kj%8=+tB9#^C!HV6Y(L83qkp^!GI-P+Mrf34P zrwQ{(0e&`OPrcGy-=*snRXPnM_EX!4s=z(fa7v;}L=+x06S?H$gZ|j59cbOj)6~hn z1reKRw%!28rQ3!+P`joTIS{!^IwKN(H59_Z$yEujfo2;nt)*o61~r}pUkxX_6(J*_ zOizn2Z?4P(59F2wz%SD^v~*Ah75!1p=`uqrwA9InlU2pnGNq!yGHg-Nsa)kgF@V+# z+YX5#D~XmB9rWT#U)Q-^Zjc_!%}G<-byaThhssK~bV2ICxakdx;6Bg-SaFPyf%Xep zu_Hm7iI}vMisnwl3X~Zbp-%IGe4z4MtmekkkXo%+Ni$%<1W{Z(#Fd>CcEIRLo|2i3 z(zX$MdhLaeYoe!s$;I}BtDz%&Ho?~nZdI0dP}er@O?m-G2M0PhPM}$Ws7fyDQr3yG zk(=gf@v~JMpaojw#8EVCx8T+=zL)tJHU9mHnFCq*Z4w(yaXp9 zS(d(qX(N!PX1y6$7TEG8_{xWoOik@pPnyW5B%)Ek8mB9u>k=HbhHd1zRx=`A;kNKBR1}`5@m1;dFOU=0_*&T%88#a;VLGG`g`JLE&)rIa5qxd= zYQInB+V8!>BJ7#(OO{hs!jCj$1z{--#@`VGProu4?=nIE>pMZ7Kv3eP4N2g~0dL%^-%TkrLhh)u$h-<4dfuX#neF)?N9cVj5)q(VgD z-l#7dXQNvEHS+!*(*HQ;?Puv#cFFz5tMEAF*5UP=m-(B-8~ztzPHX-eryfupG5-nv zWxW5Xbv+tB+D6H_mWdmQ`Zp1f_zk*PDjy&3+(d%Vo4Z9QzXZHx5~ zPn`7Jj5WX@{j{pzR&QQjd0ZBjyOHrkn87Hzm~(ca*zxf-4cLBWuBg^rpD3xJD~D3W zRhXHFVeX131h@F1_sHg;{Q()gP5b#x^b&PbP3a0~w>S~M1Ml7iMW;HJAe%*huIp>1 z>Y-H~8t#hQolCb>q-wx;O`3j&bEpbXy^L7a!a8_FP>TV@8*ui}0=eeM+xOB~BJ4o$ zst1HaD{&s#BqPAXED(!STsY3-|AnkTyJifXHUiI5>ZP=G`b4e!3;!e-E{ZaWP8Db< ztj?Gkm}~BfH4@ZKO&G^M1}WqqA*c{^8C-ENJG+(?QE%O(IisK?Jj`lV9FND6RAs)@ zo2D^ELZU{D$<1ObS!vcu6IxzqbRTLK4C06RqQoG3tt$;~Y5t+KhRN;dOig7sj)q?u zi_Y-BJ#YfWNV6D-ekvu^Djh&mB4I1ugVsIK@!Af+Vhmhk=cg*OsEPk!&C^9MD0ru1 zUn!z)rc2ZdLn+2R1!rj}b)*8q(}#1_nF;^EHXUliIm$3<2xN z-ZY1?m8ltzBvJIf`dQOQFUIL8_?tg>WV&f|#nG|!YDDx_7H z4viqLDD;VngxUr<_?d)vHF1^a_X%FUU6IVHa|x=26xZ7(RrMF57gorArlPb?WHoG% zTl#~CN)984!$=dxel|18l>t+2!*1uBP1pQ_a1=bmp#+M)*jZ+Ydn}K+1NMz(nL-`J zvPXs)x=D)tvmzs?b@;BV^s2m+b+EjF!YtUwU8=$lMpO~_6o@sPtxZQE*HtvoT||r- zPmZo?=+F*`_iVX_mX3=cb5lpn6{wE44J*0>yeo@1=rs{Q53h_0RmPvX52&;6p#M;2 zqzI6IUwm<1nF5HtF$y|xC4qF6FM1vsDfv6-N8jfSSN^=+MQvP<(iCyky?3vX8x6I1 zQsfb~^dj=QrPq1_&-`6poXhT*WjAe`0)TeuM*O8b&d?Vlg{^AJq=A`5MtnabL*iDF)Z0e-^Up3ZKEk_vj-|4>Z!oog!U(hF37cq z*v{W)ngZ7BiOYt6YXQ zl8$b++>@({epR)xbLrVEf7h={_$z<;L%FzZmVw)8v%G>|;q>yubIZQ<_EXoAZA1Lq zc8h!V?<{_BqA9*4FDsn1?`!L1X~kb%cd@gKTIPSZD-@-bqdo{iaUfewH@l%g6Ua)$zo+6F2WsEgyD9e3_bYl4|;M`@v3A)zw|)0ONOg zHJcA^=m4KwZp-*3UGB83^C`@5)1 z_(zLjZ!l%Z6Rzu?yJ7lGpF_IRKo0}|4(_Sg#OKoVY>JKJY4U*G5YKDVz_@) zNA(1F(KWGnvop5ZNbR7J6jc#5zVVb*k@uFGbIe8S za^$LNy65XqzH#H5xoSdu{Pn;SYu?MHKAv+00rXsd#^xr$d|R*0^x2tkTYqrxH4*O? z_Oll2w-g3311st)r3K@i0oyjKYUA;>#dhIOS51prRkZE|N*X6g1*@)Ap)>fXKv40S zO|M}1o6_b3P)2Ai!=WxG6#O||5R<1V73RDNo60WaszzT}Qbve=Y5_(~=M|j++ha|& zVab!Di6V0YvKGziQ2BQw+@}8PMrW76G(fCW>?N05aw&zZNXlY*c{v3q32uVgUj7A9 zrz+;F-;Ge_q$H-nt-DYlhORvtcvmt85b9<{NQLf7lN4_AzNN2=dfJG&o2Gf-GZRFo zGOx?lL;SRuPf=K4JK1atNf!8tX`TU1Ddoqwi8(Yhh^la`BQ)ueTc+hF1HD^8SVt-{ znnCoe_s_6lfdW)wCymQCvdpdA@GH@!o8a|lkC&*4nt^sBMH_&6@Lm3c0iDId)AhWn z9!(fHhIWLJO(v-ji&T%4DULLyXPP0tQ7wE!<^W9F3Kj1Aqge+!ze{sP^Kz`vrh6K* zV!0pZIuMwGxgK!6oyOq7WEjCsNo&&oBuEhBJ$wSm7Uh_?f5>Uei`&>&8Yl4Gc&a-* zP$#0Vf>qh{mOl#zj6l@kyb~tmW@==qDHX*FR zFSxz`2ou9C||bk^)_q-30!H1_L+qy-1DPH&-V$!ww*~pe7#=tKd*ajib)5gTLZ$w>{U!E~gPndJ za!XIKs~xM`xPc6RpE-Jom3+Eo0QS2wj{C>ry}Ugwcr9_;Y_lliHJyCJ>}rxqlkv00 z!DEc4_E+KMipEa8_10dz3*LdF%SPKP-&7pm-A^o=POCmTH|ZJmB>Pio?#TuAa%o(9 z;4T$=xsDj_mMTMuEM?t_b-FKEfA4Ud%gFu@pN1DNTf{nahU5&n_=^neR`zzO7qg7= zAKyQKyJn^qHDWPWI$2TRs}gc&!augC^_tfS{I2@0NE8MYG^8stHKbO2E@<(x)Bix9 zPOG=w)S~y1o(jI(KRcfx|2+0fo_*L=OoD!Az{p6*KkLlp8205NoWUq>^Q6~nFQ4KN zH{X~QLSD)DlfIm1=N=wVx;Vb#_3Zd7l`A_sd3z`027K;Jp60Cl@<`A2>N?U=_0jfq zN=@33!>f=a%YQzqWp}ynoZ5+>`!MD>miOShcB;>_b5{m;%iRM`@}uyVcrvr>r>(!j zhsMr5{+4myA9LE^faAHXw(79=9=Mx!HH1$a$?8jECoAVq85%i!JcV1!efgmKm^(7U zU(HAVn(1@zu=&WfTE#p4kiS2$LoqO79A_#s7-Q)CJ^i8jX}5n9i|6+M^W>iFIqI8JtM97cvGC`v3HR&!KjZ}*s~vQlQm=etty2|I zD6^e#%zB~B^9?q(#+e0-tjiK8!FGF&4*6OCE7w=xMp2uvFQd+Oc$wm z@AKpe-|z=#q8on-w15Lp*U#YSy4zK80g(h1X>IkJg<=+diP+XOqp-)rxJ=hft=JrN zKz*L*B(NV31-&CZ;^McBo$MY@G2IF*tN;TsNtg;t&sL{I--TsW<8d=0Hd{zcLRcR{ zYSZ&n=7h~N!1BIJ7Ngos?F%5ynY??zRIin6Is~Za@!ep91e<9xfdvlyQp=x|5eagj zH9FQ_2c@QAhSCT#Mr3%Yv{W)vN+>x~gJ}v4qnojQOEExZ8#rj;2qZG=E|kI7$#N6z zkwemBs`tg?Po+EcoQEoDf5@6n$AfLgB3@A>2(0&x|={2R4o1uPgbPIl6X99F`9R zth#hnRsU_6L*q6?aD83j6U?_+T`^5|ucp2T8Y3Aa*9orPg1()kb2i1=>U4@};SEr+ z!DWz~VmC!9$kqlg*3PIJK22#hv%99cphVW`E_yGW`3pF>XCa;vc+qbIu<>mtKd*nKhN!)5Va^dCW<`3Ua|BGKI< zL_9Qt8d^AtCrz?1#@#Xn_$6{K!^Jhg$)ea1WMwnAaRvl|0=ZC3#;k{b7ZATtaT;ot zUOi&C|0e>ntf}}167FH1zBj)Kp`oWgX`6__cPQ41T&4H*-iavO1zFgYgC^qcqw#5g zB^+fN-0G`I#CKSwj@rtX{@d_^@#$r*-twdgYQyT|7u9pZ?uYR={)=fez5nOJmBs_Q zmNf0Ser8fHBi);sx7X*_E#^bb&C6f@>u`4S_tLNxi?osMc^Gy^;;VTD?{kbr_y>Gx ziVvkr#G%+tJDdhQfo+9l;_^Xf(P-FT22yt%aa?f^WW8Yc;m#%YKq#=0L1l!S&k+4PpY{L*6Q}@OV6QDc-{T{e+s8{ zPx=iVd+$rQc2W<8D}yJpkq2(fHT}|QxTp8av?%&OS=mYV@W=MihDYdS0aKM#+Wk6@ zzT_TbS}R|2czvY_I#UEBpt8%;*KVdGiQI>@(*Xq+zHA2q?i)SkN_riVlSOu$<(%$b zNv6y!@If{B*%G@qtHqyFK^JHq3R~;x7D8X5!W~t0mICa<1G!v!-Flqkbcx}r2MBXw z{MdYTj2qH<#6*uy>{c!Ow?tqzM7}qdq!~|*yj=nbw#iIVX_)YsG+!tV3cs`(brrqZ zRT(ymNeWe)TO(j1EKAmDeL9aa$_wPxenje0vzq8 z@A5)}1oPPNg00Q|yt2avlTr4Pn^VtsEK74-!UIjG#qOnKeOUX>`?nlBY5BS($zJ)r zQ;gC_%k$zoO6u%Og{K1!M42m`SkKeJ>YuHf?U+|88d((k==CIF2=#{@TwRL&+IaP0 z*v8Q-!ZV0B*`v0X&cgUTyE{QGI>mT>X>c z@L49yCq_143~BO1nR#R-juV)YHoe!I0;f*%)`q4!tk;rH#W67!l2;GiSy1Yc>))y@ z((v6EB4yO>gZ21&(ct6{RPD z(W|s&O8el3^3NUyYOf-qaIv$DrDDln~SsaEU--+9anIwKbrGeu52XVC)4 zd;9Wn`%UY~W;_#fqjX7>0l67gmFfrQdbuh@7b9g&DxIF0e72*J%&-j)%*8mp(H)|i zp5~I<+WrOlz;^6ubavxi^HU%Sp=TTZ0x&n_GonHo$063d0KgvCgeaoOLe6XXH>1R8 ztttPAZ z^}TzPgmBr6)ZEtWtmwC131T5E$!dy;l~13j=bnR_vbt!yN{v-bZPnAqE*ZZ3mlG!= zRb~G~#&BRGq_OOx;l4YP>gylA#qin!^ITlgPEx#gPM^CpvPoIoN@(j)ix0vOG}^L| zOmD!ehr^gP|QU(?qQGQTK-E&g_=c)9Y;SKY5DqY#2O0Phi3O{k& z6uOPn4TI0kYw`)q?mMD8L2DC`N1NU9EjVqvQg?w&><|B=o_$(;Uj*$;u!pw|E#fhN zQ&GAYr)Gkb47e_wq*XXRt5R`jzc6QK@sX@C&GSl#)t|(^uFn>s*0(ap634gb#xy12 zbck;!))$i?etNF)v^?(n-&74(H(z75^+CH zh59eR;5~52$VHK*p})Cm!77i_QBZ&Ksz`;k25{$(vgU07*%%GEKA#D_k!>{3iIr8y0U$UUxa zs5caB0PdR5QM|W;>OD1iYdL|))27+q|2WE9R0}y4>p3BQ{9`=+Tq}EP>22xl(zWAb zHJzBW&Cb*oXLai0$Z5xqru@kthyMG{({ui?w6)no{P6p_bHjju_Vc}=km={Y@*lCs z=4Q;?#rlqfFs35Se9~oRgB7869u=yR5U8G>RDH31vO2EciYaAX&4Sn{X=Q2E!>;@t zwYwT#_sn3{E8S%j8H^i`qfEf@gDZzAk21*sVx4+Qu<2gJM*PO*x&(lyTd%0^2P z+{}W8I!W&nr4?49E6|f6SGjZ<$VzFCf|ex4K+5K;r!Pph1^fxJE7#&bE@7Vil}tBl z9A{bo4MYkgKu7TO!^_x(Eln^^JqoCp-)j2NP z*(K^J`d6YZ4P!p8GK&})?I5lDFLjZiHK7irV5?ASFA*!*+-Hq%klmX;s|CbjP_i2$ z4!u%g{m>SF@k2LiMM8qA-eUM7_y^0%4y*-DxjyKK%o)u{vX4(nns{W36Ml#+Re>Ed z*Vno|96>TQ?-5~UFQ0D}+rr)6QAx?vPftcDij^h=kquNj@uD_8x4DmJy;PjDy68qw zfvN%*900RflEM~i0a@PSBskaSnt?3L;jwsBvIFOVv{rORw50L0@NqU;|4rsPTskef zR0M}0H5?EHP`&e7VD#`C5)XA@x>QC2Y1d_W?Pxf)_1??LwK)62%ROxa6Z0pNve&Ph zgvP>9l*&zy6^N;eem_}kgr{zop_DHP6_TzWI7M_CnH@|!Jc zM*PXir-WWvmT3jIL8SOv8bYkX5JVyq;xd}X!9shab_uL6H z;sIyUNc*nJV#L!glyO!Bh*vjTI8D;|Ln-js%Jjfl+J@vwMpw%7?Nqndq5=h*ChMF+$X^>zy^Ww*GF9g))CPm^VkV~PAu@s&MhPs)xh zlo94q#m*;G``AB1b#xy4^iNq;<%Hsgd20%j=2a5j8@j%=y7K-o(9F%YWPX2)yr>+q ziZe_RO)376(0B3vwTFJ#-*Ivam7}E)$|Q&Qx%$MX4Ma)!`xEDNHao z!l|j~)ptuTYSjuV0^JIh$;Ma()-1oT2l0GEJhmKlhq^|*ads?se$M{$F~Qd6Y3pa3 z9YrJR`kW0(e22^q=(akWOGJ=^BO~MT*fZ(;-5dw7NS`gYBSP}XY&1Hn3x<6Pd4CtbR74jCew9ER z%=LsIXOXKUcyO*67Gm3PoVJ|PY$(WL=i8b$NY0872cGttKHmsLn{&KxQnulwq5irp z-#*F=G?qsp`i6P5I_Ge~=Z?dm9D*UXk0i#H{2E>o z&9>4}#LP3Qf)u4K26N?+QbZZv_J#6hP8`ifM}8Y0Fb`84R~$?qNP<*2Fbd~YK}q8% zr47k6cQcH;NjP9W3=3M+#I!JMpxo;%DRPic3Z`i#F(AZ>>-C8k5}T(zJPUH{R!Xb) zLjrW3yL}OR!9-jy#*s^#$#7hmWQcGBO0;dE(oc*=ZAwLKR8+=Qygbz?bnly@d)IO2 z&YP{IqPqxgkEBt$XuLyFzegbYVSO2e@QtJaYP>z}8|*1IryU&4;z0b#(c&RV(w(~^ zdaj&p0G8K8mcB{Hu~e9Whtox)9zkg1?im1oa~!5Yge?_m8n`LY=m$TdpC{Hz&z42RX3doGt$(& zVL6c5h&a;U=eh*JRlaox@-bEVSe^3=RC|g_rx0|QD1E&Z*=5Gs&m6_Ls?s7xQx886 zw6&knh^<67 znWY}H^fG_>aL^zu?Xk{1AC99EO4D~nYSQGw794ce0qSdbu4G7Nc~a_7{69k0p;>-# zC$Y4r?1XdK$;}&k_RBR+?Hms*>-hBWSWKKJJL%F#x4^MMizL&E&;2i_F>);A3GIOb zFGP1~?EgR+$dlf`%;2>liqyEmqj*~+JJNUZsGk{Hm6c#Voq@s6Nm|rt-8GE)D{auJ zQgg+5AaGcekODp3l7rQs7f@PO3Dw2S!oQBjjHYmQ2H7ozOfd2CJGvh~uk84S#E(gT zvjZ=+dQOLjO|71>olp|?|PEOpX?64YEgg(2&W-iXx1Y7@H#5eKYvAPXH#A{~B#~l&)vyw_j zPwxm)6jv{&y7slc&V&6CpY__GpENzt{s{DWf17YR>f@h0xPcCTn~|+w!z)s~vafkZ zL_Wul;hrFP!*drg^rEj}R^HOK(H}aP3TDDFX$yKH8#MKIk4Z(5X#0vxT)Jb^tAd!S z-Tjen-wC&Z2MyZ-mqzbI5o5Fcu&tHUf4hH~n3gRb(ret0n5=27KDDE{`E}>i*nzWC z-iq;$P%+b#%6`K#$KQP!pM*n3k5L~sZ_Mh(7QRw)OY5v+-+#C{K98-M>7f`U|{5*?{1a zqmpdO?g&F1jrzSIhwH`aVSFr!WA5TjAg8k**v}t1WP6F_y8)CzxU{9MyHp~i4>ZV? zG)@*M<&Q%y-0c9Gb$QNBoRuS%BSS@h& zT{XV54*w%-e(L+MjzeAjZ<*-}yfapEDYNpz_E*CSG}^>Dg(mR-K*3ng^M{eX!cxO? zrlN=C%_20Jisho;cC^`|wPj)>Tjd4NK!@1=aXD*08Q97}7R_`yGfyKLWpH$LiZ+J| za&f#@0;s*vlBOq;1*+0pX*D}0#FJvAhW$)aF^>y=ab-zj+z7TLHOO%^wD^QxJz76s zR@*PsC07CNjEQ(E3t>33qnt7fWO35xbPP!D;_Ei>=!nDkH(Wgrhq6n&A~c~s@nQyM zS44sxKAS*bY1JZTeLFYIte_c2VOF*?P34HMTqE*+zOXFbu((8`)CPwL--0Y%6Dz7V zou3qW)!%6A4WhxrIr_#?;gy3k$~8nhk2Bx}-X{(8;`flHB$(zl@^Dt_o;PE0|baFBp9C^mvbl!%w7Q| z(TP8XXA8}O%rA!146P=?wqtVi7`jrZC+SM*L`YD9GLtoe0Qm3rPj3 zUdWnPp=%Ez!UqbpBxz*l!Ma}SsE{xbfG1jo*nydnLDLKr3PNXMX3|cCLPP+#J3U%5 zW@YM-Dbky+vLourv*?HP@6)OhypvspKwIwGg92_BKVW-L=~~0y|NlIRaF)j)r#v??%}Q!d%rG-y82{5 zKXA-}7D_#4bv(3kk})^=M5cXZSvmgYL*2QFG1NsYh11lZ`2<_yFd8wxa}*(Z_d?nd zEW4I+@!W@mbw#^_N{0J3lMD+>seVzvG)k+JFb6zjl;G6=e-&;x=II%7VDuv6Xe5(% zbM46Fs`=*9rzoapmQ6|@F5+!4ffH68Jsm~-xRHw}%0Vr{Z6oLSLXRkiL)usIPc5RJ z(G3sqssi0|bS4z4=f#z;Rbo`=6*>kYS1K^04y3g!4pToZ_Ye-nyEIE1J4G};SdsE> z!6`@an4)~@%%rVtvsrlDZ&lw`zaaQ5B3eJ0ZajY7?0n2j01E7zWEMS=NHwK_Ymxu) zFoZLqRRZQ3MWC6p?o3=}V6KT#6UF#|K+QL$Qh$7_ty$NW=e?;2Uf`&%Dmg%Ib;NC3 zI4BO*W*G46M7v8@dotnr+rdWnC7B&UkOD8~hcIT__SNU%{i!#KyCx@xcOWFsC>zc%WQ_Y+*)WTA{7z`6tV?STfi43uClSGXPQAafU+W&zdq(D0FsJuZ zB7X$$ryV)hxR}An1ezBb_He$_w%NYHbIB#E>`lz>~M=asz{+T%0p##gaTdlVBdDKy=9JTs!iu&>8`yDAo zN-{)AzI`R~_uqYm-|IZ?mm~+uw04~Pod0uL@EJ!adnkWCnrh77^t_CJ8frLxBv+3S zpDKy@GIkX_g1|7U6QIPpUBqrcIY`l%NUGhXi zJ>eY&mM~Ju->ki)I8s*Y_*5~B@5H^dn=L>tfGw7C!T@^vU)X2>eIIel5*P(AD&~@9 zponMS_w?9u=+)-x30l|%E2V^ZhES>X_R9W=_;g4sr`E5zG9(o;Kb>BudHx^~J(3w6 zFy({DVuN-9hhs!Qed!y9WbI&wh$79YBe!>@4XyTSssQ-f+Wr60=OmmisKArTMnTW>uX>ET`${q0kM`;!mZ~ARx?Ima`2)!h~1x&kT%% zFK{jT4diZ$7JCI`Xwfszgf84JF(6}$bz5V(20gGHvF;+EwR&({1KmVDp#-cNb+SZo zxoyMZ34zd+Gg?#idLv#fY}E^JX8Huw6+K1KAgY1tv~h)L38G(rNuNeIH+X>!6y`Kz z=*JqN5JQ+*rFJsI-&F~HKJ;+DmC)@(OO;Tv4!Wa(MFMhxwJgP9 z>1`7(1L)sJJXiuoW!`j{`ZP%Y98;1kpDHTG)xSMBNZ#isEe%FFTo>~L8#HOoXT_w| z8_!VjqQ#K5VNy-lf>nPMS;KA@G81z|wW3R^6_a)pZ%rV%jvLP?Tt{h`qW!k;-DwCh z7PL=b1q_p^HVqHEWko*P&@!40?$$L?gc6o^Y`AEt;)>)26_)SC-c%+eu#62(?y?9N9jpsN@~-Y zRC|S2R5#$7i>RS^u%5sO6PUKq(H?U_bPLW4!-RvXvAG|cz$+LHMT_X zwuNQ3bg*rR%tD%0g!|TnQ=_HwxHA|)3as^L{{6i$H0N}V-z@wmYErn|$qCm#WD;Wu z3hZz9HRZs;W9l9bpKvCZoLOvfCtr2#i>8ynow<^5BkLL{;lx>e?z7y^_bO}n-*^%~ z2WyZ{?FujCnkAc-#7qjF3n@Q@GPnJ%I+TrbVfs$qHb3g7Ti?$KS;GvM{wkM^i&rXg z&j{{5*5&w`8KmEZx|%<=z=>ZF?n&%2Wg+cKCdkkW9|$Sbcch`nsQhY*7cnYrTy1MR zS3YNBwX|y-OiPihu|wBW;nWk%avc>b+cz)fpM;00`hq<>bjaF{*)vcnoj?$;u*hik zvEA}*Qm<9@R7n-Oh}3X%4a?u&n+{%ZqbauYI-C1I7snV~!jeQkuL!WO;4^}`b^lTH zKYc5QfUoa<0?y3-stSfxY4|@-PpO^x<_lq!%wqVPsY+v~BCfq;KSsl_QtIB1wv}I| z<$`~Kc?Ht)fXAJZQ_AvlmVf;ZB)j{sr!>|`-4fP*eTBU;+P{E(oA5M_YLelSaX{Q# zYJ2s^q0?sPH3g3fUs@}hZuYYhpZ44xO#aQe?WFprDQI7*Z;#3)TtM_QoOH8^Cnh?D zf?JoZ=AU?m*|@aQYj=7{lg<-TXfXBx?Q zi!n>DePeg!avXbO@7$_!PVLKM9^BcY435`HP&Tj z3(AMY|A1bj;ik>?I;7|Bp z$0*NAb@0QK7Ql!s0hWuqc4O|`uEumHMqXGd(r;_*S)wa&R9Iw|{bojUOVE96*H=fKT>$~*+BU$EDFbKf z#<^X@@^nyq^>E^nu6iq%ab=}z(rOiaKK>E-QK-ez85fy%9#022p&DhL4xh}yF|bCw zIBsckw3;uvYcaPTZiIBa20UFA4|F>Rne>Fo3rokZ@UHX!Pk8T89hhLMf16Mp2p5AT z%N^(O5Zw`2CE|d5QB*+2mO^g897bt=M%I{S@+74?h%33vRTQX1jNm}m2b2wSfPm^j zXi_s>>ohfED;3!kH0d(O_C;X7YBw6v^J8w>PVe7gN+)g|SqHo95x#Xjn+w<5y*IaQ zju57hikfJ&pw06KHQBBO*ih2@=Xi}u5~#)w`?kFA`uUPUPrvJaZ;_^29lWLz0SoEa zch}qhM_lV(oVycTg1Y;H=WAffQPV#-v)}7k?ig$sI za4-JWW$9H9z@xUXrv;1xjzF}BUfViNht4PUJs~D&2GfB?n%e`%WvJpv)^kL!v3w#N z?}9(nT4@0XOI5CT;EafgDH*QEMcugJX`i#%F9MT|fKM^Aa>fb#=iu%FI$_ka^(hLVtr06yQx`Q&*ooP^#u8-b;(`x zcR`1FK^XIwLBmP^)ehcQ-v*+KB&soSs!Qx5bnZprqPnh`T`Hu0c{&YHTp{<#MD+MK!3>uW=gR znVF1GN;MJLwR$tL#a(tTRiFN?g3UMPJY$c6Xmmht&0?=;t}w#sg0d8zM4k>a4_c5Q zv;ykldf5>5oo-xM(` zv3N=(BvFVXK<^9ges#{rjy68x?^HH>cJ5+QA^(%8J|o3;S&nlp zRpKupM@IQGTVT*X!U?0P8H!6u^~i1!EYG$hxUO2lHCeTmUY%Xe0JYt=bz=tASY*78 z&mP6;r`ah=LOP5?Mhz^noUV~mzLVKtho|pt3dNgV3pl!r>&UbC4*A;ESKr4lC~(J9bD6BSeef(W{>rOk0(8lX~z z`~8EIA+W#;jgVKem2k(c>fAVyGYyg$gD5w0Yj}qKV((DJ**5xWF<$?+=MN5myn+;c zx&OxAo2m9`QnN05QiSM1|5$?7|4yIIJ99lFgp+cXDzU>Y`!dR3i3Zh(F?$u~bd300 zi{-s*o?YwzGlk=Z&Huwb@LC|wX)nLaeiEi+&)9k_5|Dk((Q4lt&CyZz%o;@?UIz5t zVS4naA#OB>__<_-c%N9=iHA73u%j`T%Z*v_!uU`ia-$ek*j2l)-xLbIHH-FZwYASZ zn3a{*R8r-CWoV39goq$XBT&bNsmiu?dBv|pLO{+`~y#mmqG^ zAMQrj#}C6Nt^acMw5bOM1r`7)`!YhK*!0t9Y#*|D4bkdD51w6Uaj9kuN?j|U+TmAsd5C`>55}HL9958; z0MsbOYFYY8HCIBspSEy`aZcY~l52?}g{+ggJ?o7b_6oRoVV|{>8vTJ0GpR!z7jz$^ zEC&6k9od6#7Z{lJN0_M}9jeKc9|9sqxaVRK!c_$e{V?smCL@ax=oM}<2N&wH_geG! zTWkPNlcv|7n)QzKV zRP&2Si=W*ee%j_k7gKw+Tz}>0vZf?0mpCwr=<@@9ZdLB*CF9u7k!r|g6B1;byS{lf z^?$v=87jU0=HVtzTAGz4eJKGCcQp_By*IN5u_HE*(Y~v@bjN=`Sd0EcSFqnUyQIZD z_z!S(HelE~(xDqMD=3=h;1^8@Ou?R9QG8m71>e4D=0fPHfx8VGX1sqs+FB(?nJ7%L z$$!SJ=|8?}#W$cRJF1yy?DMGhXW)`;&(jgJptlLp5Ce;cNuKDj_mAg3NUWU%y}Gl* zdBY~$N?GE!DpssqrJ_(n0xV4z0Pav@d7~5kUy~>O+IAC}=uF)-d>GMoPwNp=@xY5f3`I`$E!2ny77Z z0%*Nr)GfJ%yoo z$KS@TRZ68w5r|d-J>g>%!P94%_NG3f&P1k0^Dg(iF!A67=OmtaYktYlmu}}Qybljp z2fey~p{sV3YD&=`ht)5n%LyZuLH2mu!YIdau-zZ}tm2gJP9pv1z$LfP zU}4^U(rftk_6DoY{muD*8aPwcb1YynwXD5M*Ri%jT4*Ls=1rIleL+c)nX%mKVE{Ix z9{Cci#~Jk&hoYX;t>m83W^a)eEG`;wE5Vp>3l`Mtz2`gB2CP98;c+88RkG9`AASP% zE0nAj@a-6k1czdYzqfb6bW_~}jK(>*(6gNX0F@NlGM61*?bgHr9;hgRuRlKN;95Lq zDpmpbU}bvWcUGMg50=Q{{A=3(SCHiDpgeWch}pP%CWEJORlKutGI;7-Ks=zWS$Wp7 z;=blYN^RZ?w%;r18J(m1XE0X zG2RMP&O>T5$Kqv)5=NgX4%`)#jI=Rx)c6lzc)ONreSH4$yZZ0U z-oHsNM%=-svjb46DV5w5i*yIG@{c8ZB-JMFJ2Xs!ki+ajRK(BNLpAw>9v8D}y}3-6 z9MadwK9Q%?g6R$-;)O-s#e^)-!*L*W-v(C~8kv5obE$S1C@byCvMvK8IC zuB{@}$2WQobtp~0Z)h6`c5aCJTfZciK3$0~Ds?pb4U96 zR}KDtPkp^}cTj&-S##sKnnQabMzuy({&U$;SXSI8zQJ&C%OR4F+VRP2+4ruF!IEa@3{K)&Ax(b~V=!iqf+w9`L(=iLZI|GV9R zy`Wn)z9#{$GTIi-*~kJ1<;Fj}eay9Y@ z^dapmt{ADYcRE2JvU7->j1^!1_F|)3m%3OLz3k#TaV`A^@+Zo$duVcnb(|6grr^k{r2_zChpqi zt&{YH*XF8O?VuP3RKMQdN~gYS*!Uc8;I5YMnQN=Wh%Ih)Br{0RJ=ME5S$E*XtkwB8 z2!ErNx4UtCt}ch%%g0QFpwTudmBMkCJ0bW(NmE=hbmejs%N~E6giiOkf?RJ-3Rs|Z z^|IoD%6u{wkB@*t`{>L(gahEI?3aU6EFOhDT#+#CZ zXkeyA=1&G|@7Vq(*Kp#F07r_$Lk^EPljWrVXMuc9n!>&mYw+Y2Riq=P$ zU4BlcyjIUHAVwTrTkQl-8GtK~1=Y)v*?)}x1LTrp4AlR{Rk-}*yV>A=Do3lkLBX^( z``!!XlL>+^1Kp_8W;b3vzwuJVyg^WrVo^89C_)faJlS*0?1@Z2!}Altg``ym%W>!b zdr240-1}CLx33dDXEp8EFZGn>;&`Zk?Pb;Rp|1|*A#^{e4X}kh>Av35>o;gm zz(6iJ;ibIgWM#av(_yG1HmnM9BsL@*8N@YpHp0@2Jd|vvw5zN#-h#Lu-a9Icz{X)3 zw&?RNaCbfr@Eu=?T}?i%#1fg}g5oP{8FLBDZptvyLg`)y(@^pEeZ|u1@;46Kt^*++q}H~ZscIeQsh3yd|90>f z6E^BrH*$=D8le2N!Q8y0rJL6%A5YML^g$&GeKrIKFDPulrG+saYbPa9$J#?&PS8Q zrQaznhNR*-w{%v?tGs_~Xe*MzffH(|g$G%>9BpO_KYd%g!S63zilNA1zhh4>b@&oE zSX$nP*ul0W4T{n)=;NK|<^Qgiwn_xqGtQ`s8YQ=Ys`vEp*E;L>#nPeH8QfzAw`Ce{ zKw^~0xqOAjSc+Xx=ADaI!Dp2KXS8-Tg497}BIHU%0J2Mhsb$4wnMaNWcYR2{%n-en zK@ZXv9Rns99-mPkCHX>4>yz@Z#C<)8n(2~4D*m)o8$aeq`SW*QLWUxo=|r?-@zpwd z{o$?TeOl>D7sJ>)s5Wp5fhwuEeFe77jL)<_S{MXJ2lV~SM=0|t{VZL!%|Xxm`24-C_-8xs#`2e#ls7$$3%DNqHhB1I?>LwBk@~sa>*Q@Lc)ToBq#wG>g}Fm> z2G-7K#ABPuoMSga*Ea%jABDJFy(GvFS&lUv-SfxNRXkWf`U+n9 z@FNbR7aireQ}o>~EPgrue4CD;Hm*ENhIoTkCT5Ws{xwx_fS@0v-VA$Il=YXl{MH-} zM%BXGGfY*y;Ilxh!4THTCF-_BhLx`f@j$dhWGz)e0$C~XethBE>yZN7%$5YP?P;m#jPv!M z65bT-KYm%W{6{Uk!Tq_aC9Ze6s=kO}8pLPs8vNj450+?2{K#EE!BJcvdLJhdYU~!} zHOtg-!0hQOW_36ILA|c60PsXatmX5C+=5vrE8>d-D2C#wfRslZV1N#UmzzAO{;^3- zsoU)hIk}*5*7M*dL}@_vKra<;$)41R@Gn16onCjGrC(GJ+A||Y;Y6BUMV|Kbj>@_M z3vW5@Dr=S<$rMmT3-p^h^1_hIzV3kOZMLjUYB*A312}^;%uUaH$&F9BRnGW*dSJ4(gdIt#<&e@IX)D&c)C1|E?tGmWeI^+?AkTf%P8DwWlF`1e?o*p3 zQ;o!{JTGZvXU+(bmRS|6=F2Y$ zfH>OOo2W0L(78c3xNK6Y84a~bK>_0=Vax=8 zyggJCi27e~wQEwS{%g0JJOLDL@)qtU9Zg^7mdgFt;QX1G%br&!tVrITE^u5|3#iRm zj^*BW3T}POZz9i?JcnV|xpR3crYOq7turQ)1cM5aawQqXYdV*fA?W}rm>vW65p9Nv zRw9Stln3(w#~`qb&WQnt%4J5hfTzOQE-I4B-FK#BV~VTZHL)+u!{+BY1A2^-^RES%%EBobTVTy+ znXzidb&~+s0z_kdGn>(5s?pY+?N=auF#MTixHs_S$RJU!!Qi&_+w$ql)^FZ*TOiB4 z%Fd%BtWd#L%*)|vgQYmu2|1lWwpLlV&L7+EODh6bDjo37i4*FJ8#K(UqOK!yfrW%P*pC-hv<`5q8B$ z+xkO~oKcB@1H2Z;J>|kqlr?)aL0Xl}K#JVLgZY~eK+8B_iN+onR+)8U8Co(k6!@IQ zi@5hPSnEH4$#xO0Emsx4D=D@bPxQ#u5aFt7-{iVV&a%A;hjnj8DEI z#a2ntsvO;$DKiexL-9_%o8cPma*Yb2I(AjDV8I*F@O8In7!sJ7INGKQg8|*wrB{6X zMgs@SiiiP2YEEqBB~BG|*OxM{K?%TVwk*)bnge~%77tlT5;a$3K0C4i7VEvPrvl^i z&WGF2bK!$(i%~$m4>z0EoQ~cGp;L^2|Nq~h0=qHQOOYiUe<*zr6OyF{W6EV z348O56DwUYx$wJ5)`%3JGuLuwAQ9k)Dt}|2RsTmacy-w74XW6ZWT!YadHrhekuG7= z>IG6gSj|1C3+J%Bm&0RH828!XD{W`8}o=Gc)R6Z^iv%>ZHA@!}xfx=?lFhw)We|Ml2}_KQt0r_~qT zR>M}ah0CAJwZMXEEAt=ebL{P~z_QZCtg^AW8rCik5X>BlaR&GGC%k*s9Y5i(QTa2uUD}W4i*|SYMecFmdncxsgt<%4! zxWJAo#b#np`wIBFTh+j=hro8aorF5d83>X8Wu;QTp&C1 zY4RV+YzacsE3Mj+LtXB~Ev@ZyCwD-HEzuknWx$j5i}$&&j(|Nyy$!MOKtnGv?wZeA z7L&W&Gddy@qgw1`vq(B%VXOT}q{k1-ZHR46MxN2xHMJMZ3%5#UQ)$@Xq!t<$3}_3w zD2putwH2G8($|vIk3lkjh?Nq1Vu4muhaknS>cs~j$ktWCp!Vd+(S^4AuBkJ(+klrA z8YLYovywZo6KJJsb?Y3*O8^oF2){?-z*wwq{Ij!jG_x&2YFJHHnQVFk@6W(YAZHm{ql|kBZf-qFS>G>JSlDe8V z?Nd|KmkKdl(c1cbUUSx4KaBW7<3iCL=DKd$v7yjy#=4D}w;c+nsJ-yX_J`kFG9!)s z&aR-$i|Dt4xi{#lmc1?aoJmNLi`aE&;CV z=3E=tgQr>|o~A0Pw>ql9?F3~o;4Y!O2wS2<;??nHQ%S;VWe6=QAeYdYhMgU!6knTz zlfHBH)Te7azrMO2{ntVxgxrNM+OcGnwl^xec)dVCv+ym|kjxwrC4C zMX8T!&=nv*jEEn?d5ZV^vJ_+yg?ggkVZE~{El)Fgf+(V737D{_@A7$VQwtkxzuY;| zVZbH4=Aqi>i~&HcvvWAS8q>A&Y5s8!{8U_}lxii9zhAMO6fo%nvqrbGy=-X3($7A; zIo7jH&ahJ~FRyFFkT<|(@0BCUq`U}l&YgQYqE!I8$Wok6B0RZg{`;2PR4O*=b1K|- zONz57CSOy{B!)?RZQCOx8tFpQ zK>R=J8|Rp7x*xNehTG7*~G1vQP2DiWL;GAk!XW0%fi7qD6&IYUT zjIA`i5Y~scvN^KtvfXaF--G!UQgl8{JAb~YP&RP$t*Yni2A)qc(<=V>Qn|2_pIcXc zsTeQp{|9K_&$;kT%1Z374QW2&2QBY~Oq@?e>JPVz-?VkCa;NwV4o5!J96b5e>d_W) zJ^$k00LrkP-9M#qZ86$|9MD$mFzEq80Tx)Irl% zq-b%ZI-lu>cLOw-Via9PDcr`KoqSdB{z*xuv8fL|6vz&`+A1)lB+#sIgAQX$b}Bn? zD=ZNl!aE-n^CINfvxLW;!UJ#=cVjf~u1Btl$=vQ$>Cs$e)7#fTto!_P0^Aex$?ltU zO{Kcd9#2PK(;&DetxbYM;{>^be+fW%h%zcZ#c(n7F{kBMjYsD?)nK+kkot!eNxk%I zN_++PE0-9ez|d2FNNm|oNgLvF!0KK6FNcQGiJ86d@|!>2~xK<8i@dPgaQmZfXHJ1Zv53#FH zNb<3(Z)Z%Mp^t?rK*9SKMiqB+;1~qNYm>>r+jQTcvRgqcWYL#4xJ(H434~SdG zkrK20~VHHr3 z`~8-)P)aJ@g$tvG`rimdq$vxV*21NEDB@_$p$G{`^AA-@?u%K@sDsqMCC6vUvUx4X zu}Z?7BJKMwBH#W404j1tQXWj=gHANU9wq=X2U&WIT8<0)w%uhbRJ@z#pJ zACg+5{FK0WM%+fMoR{FI|@RaK_$I}rF$rp_O14f+J0#XfB6;Up?d#Xz1{5b{a~j0CK6Qgr>9c$;JT9u(SwApvK}48 zcnurbFZBC75hF21L|)eo6%QqwJ7GfT?K|HR;){CLgDFWEaoy&hhKi2v#ud82tic}R zTct&c_(7H+VM)E3EV08VpP$M{NQ6=>+8_Km$(1=TF)%X`AX5JKWR!w#vx&VGr&)nl zc7SojrihOj?-}>!e!DE8=WK{=*e}#0#_+bSgnd}^YB>H4CA4>_IIi}%TBu+x3E2)d z?~#!vaD_+w2VnNJTgmlGYT;TcQ^z-wfDPRmX6b)ML{-=?(wIxf) zRr3vD;kcNzDjw@{fC|gUkC^iX2Ru`c4R?g*(Z=M&WhqDUi?(Zi{RZ)?y~~^a19vGa zOm_6&e_g1|S=E1hBQ{#}NGT!a9nB8|d;UoK{f=S33vV@3=oNd&_3O3@TV!NUW&G!v zriK^s({>a+&x;Tohrm5bk)?tU~PH~Al6 z_dkGZ)ye~WUn8aZ8};7fC6wH|@?u7Q*k-Ikqh9mPoL)AjnR<%J3+#f&XI=IXae}cREZNot3TDR<+0Bot$?ub*VSuMYqOc?0N|_R6 zzUy-!gle-OsZ&vo!U(2#B&A&6mKzfN4Uthw(&aL9f8~=4n&omwB)K%p`5wx&@|Q5L zMd6~)!F@GR3XixjGAOtovv#WmeE~py5fsle`uP3WAb9)~6@y5@^4 zdnD$Z*j(fPQhXP4xWT@MusfZnJx^Z7H>Od$c&}QCX$(2l;Qw3pq0F>u-7S zk3BiH|GJ-cWF@@nlxFrkD=(>t6oi!mByd3RQ3QD1h3a0U+;05phca~sx1?_*UWdHQ> zgQCj}kvgM^=cDKT|Igb?;4T>36BD*3!GmeD$H{>0tv0vlnOT0&Vsgugg}*$b!il5R zR)iENm<|jZUs8WEBU+7A7q!9xnw4+oJHeH%+XpOm?gaINX7q(p9?6cj6{PFju|N*< zPP&6E&7@65&1#YU>VuG#_={>SC0o=T=C>)$mG?QP9LP6rG!JT>`BbpPNfP;t+>?@( zFw2{=NG9c6os5+){H|+g_mZ#w%Hjis9-obN@42}i^O@(fb=$Yu&j zFukW8eE9^V+k3jj&QEjVZC$(R5t;JkO8wP;X+1C}_CHNG8o&D|eD%1?stJ*!w;hx_ zkv+BvnOzPUAFY@b{#ln|{(JlLH6WHD)!O23a9JEqh@F zP9jCqoAqUXjW!pXLSJhe4*1LaAn#I>Um$M_M%i2b7A5_imQg);>;G!RRIgpjWyy&e z9z4RUpi{T?Mz1|GAqxpDJ!(q;l&;DqSZ65gOkXL=&M#zNkAPeutc4cQB9Vx2Lhi-QT_j5UQ zk>XRfX7Q4vwk(JHyKEv#@0gNzF4*CIr3XC3rCo!TcA2ki+lH5c#D@z?xQhC!{fb{1Pe6G+ zFZ`Q&@xSbkC7JkG-JiG#qTY71I{C|#(i-UnSzA}1Nhta-{vEq^-|r;lS0&7ah2NU+_O?lRv$XP*N5deT6S!zhy5$j>qHVueJK0IYQWW zhq(r0c?!v}I?K^k z9sMXJh>9}hs%BfPpPU=oA6zaE|^d5kzu<84aC=JDPT7+`l2Vp^^(Z;+fdu&w@7nRz-6YO>qMd%9FE( z_!AC*L9<*P%N#aG*kNGUqErv@AYGj+i@r`6l~rD2U*5h?CIu*rsPp6(Yg4!K+r)$T z@$!MBNqNe9Ys@ZTJp6bs0P;N~7oVl>huE{52xmU*zMDfTi3s44i?rr{cEOXxWNJ3? zw;nL-vMxzhpQ`$8*zH7l+~dc+%CrAJ0Q!IF_l=!8t9odFekli@Msl)7oA9l8(2VF# zn)v6X6+;Y9tAtMlhtG-2M5~WZscJ-4){X|Xo`Fc>L_1d)Hz+*MKm?~K-%3SqED;96 zsam90SBmZ;?7>xg+zL?m21K%wS&rPK2&8asWc6Ur&!ZInexNCVZpku$&*9e#`ejX4 zfi5r+L15?>r!84AE}^)4BIpFwlA)gfu%c|+ZaF$8hfvP?5tR}lhTgv8;H6!`n_y4b z4z?1RlcFG#lVP3}2WhphDGLjuvk4Q6$(ANSD~HQ%oFuqt$K`bikPT8rHUdULIy+9l z_W2IgL6yth3S5ITb#Mef5CiIBQ!qe;%wMY|CV_g|fjt{kkPf^Z!;=OKMQixk1Ca&- z#ClCI3LKvS!eR3;ioh_%TV<4W1(_u2#rlQpi;mk8<(8PQ&%txOqOL<-`X~Ds=!#n56}r}k+pQ5r&;A$j z*q)%OPHxe*2+H`X$aCXBe{|T%()>Y>EQhpCtA9-tNC=Uz6V+hJ?GAI+h8(#c{lpq=jni3(*DAqboO7! z{EMAli0YpH*`V;vdg$zU;nlv_SYPtU)z{%Gh`*=QCRzX6n?qgO|AlaKHbd-2-+Alf zVBPvITq6+@=Wg<)YV3+~Na~x0;qP*{*&a6ivK?DB7GnQGl}~+^$(TEr%3(U zY>pD|crWG}g1Q>5f2KGfJ-xlS&YaID3Ntf-H+Z}0E_9cewX$^E?M?w|*}Qs|T8BN# zd;K*mbCI_7N|d>BQ!mfGe7(`$H5?~5-A!x>UEI>3cRZ-Zt2y%*OG}MCcmkW2s+$dDAbo2SYzdq-rk4xrEVLixF zF-OUv=5s|iGa#G6P0`xgx*Z2NhZORj6n*%{9*-n>*DuE4Rf15v7HtCO=xBEx72NhP z-1^y*@Ao$1&M7aHKyXlIehG3YBiQvm31h`aA!5Iz)V#yY z3^ZfXnT^@$9sa#_^yQD=Q@^wYZHo`7AEbNT^!;V#Uo4EChuu1eJda|1XY3{#ZW5=# zc?Vm6f`f20A4?m?nWKb;y6yL`KLy(DumAdk7L`Xt@5UGAY)pNpXG)S+dRj~#KgYd4 zjKB@AxNc4222~lAxVGeS@R@`YlGR^cuVTra8qbApMIM6cb3MAA){eH>9`xAIA5LW@ zs|o~GT#bhD?k`eJG-F9GN4ngWZn2}I~?{zW7CZLD1tVW zn`CRdO2T77^y}CuTZv&nSs;^h&Eh}_1?@Y}SxxC4Djs(A9&#ZUN3~XiCVOeo^0SQF zC0!4Dz#)f%z`&rQ^SJ5a6yNeqBxK>y*`OB8b?9a3q5YD=Sl8Gxn3RwA-bBqSIt5^ z`6uQx8O7+b(9yD@+Q|I;UQ{?4SU|bcKnNnX!)YW(j>7A1F~{k_H%XffD1-V5*JG^} z&HN>6W2@3F%5r9{aljv{IuYU9;rf3viqn`7%ag6-e$+1l z#x`-Sod|(D!B_AnzyV1Pe$IS}KtjkU&$F`u={j9(zEDfUT@KROlgOB^A2L#$ACb2lhY z(mow@>nAxM+=DURJebZWgNI0=uk;_g?O*O$7qULDbf}=bs{dNQE&dwxKincUSZQUE z#a{Q8s5;I_Fj~3%go8aH;-74fs0VBI^RE*j)`uo@m2Ju@v{I5Y>-%KofV<7ry+4Qv z;aHoBQDmg$z1QCIM>i&LM(?uKCW;R=u4oRa7UN$oJnn1KG0*e8FV!D?-JnfH-diQ& z!~TldxmDKgbW5X&ljC!0)~&#GBmV88lE>t|oXhmp{{S$Jg9DvHwsRmu+*D@=@e7=% z@UA@P>RV{XKntCGV);M7#DTHD#gwk0i3SGy-rE|6BOma@Q`t7#Yl@ z?SKEKIL&+bv_MNpmR>4x*FNg(^_|{`gF6jpRi|$xyv_Qkx$=Yat6^sso_I0&Zo9Nj zv;4_eY(jV3hoWEE5w$zn9`>`lfy3h+*4pwzEoQ%a+fV69CJ;DF)H-~<-`FCS{U6|E z1P!tBp=L@kbA;E~nk?=XsA?DTw&0-<#`5c^bs%i}MEk<!f{JKM-qI%<@U*3ZFA5{jyh_*)<(>BC+1gr%|1!Ibge+R_!q={{P$g3 z_d^G{Jo@d=zk>Ph$98|Vq`JMl`I>A$8{7Tn3p+nB6#+Zq=@r+#_h#!`ovaY@l(uEqk&depra@JTde4bf1G{%S;2c+ z>ff?+aeu!a8u;l=oE_d=B=sJp-yF7hpgcqz|M-_jyqLONx3z1~B5hJjDPs5{?sXw; zU$`Ge+D-=Oc*tq93-k&`f^%fT$9lsNrQQD7>g>A6Z^mam9DfS6Tb7@ErM4H}vtYR~ zMqO>ckk#HWWdC)Vc5U%(c1*`n!kIa-n43q?Bz+hGjV;dJ zmTLLRS|Q>T*GlxtZaVR`(_zF9zx4&E7TcG-CN@Mk0Nx2F!u4R?91ZpumFjCuvT+6X z>iolY`=!({>Y({sX*ZM*LE1^X@beBhZH5f9WDDIzIAZsFC@cCg978)1*Hssz3Uk`N zs0I7Y+>b46HlLa#Fy{&s&ty2DVh2@Du6(6^&XNBarInq9=0zaX7Y_c9qVo)Avv0$A z?4o$u8nrzn6h&*+)?oypw%Tp2h*4_qQEhD!qxJ}~#onXZM+r3|R_s=cSS7WKzIi|8 zLq6m_?&P?0{m<(>&)*Ac1F8OAIEOM693(T{!g(@4{o;1j0rp3pu?xCmgsZK89b@hW zdJr~Tc<-z3*^upDzNdbDKfBL2o8=jc=ag<9M3-ER0_0H0MGW+T}mCqQf#IXG)E^`b$& z(QRkqC|ljfaQbeo_QZ2AOym49z54lcWWJ|DW%$a8Xm)@VHh^f%!1b~WQ@2A9^_tK` znOfW2Zq+ouZEo-%g=>~NZO(MI-F=!}I7yzV=wpj7;~37|BG29#P$9LP@QiQ#Q}3rcjsyB;IE~ zXBko^foG$zTBbpG;MvnggVvA26yJiN zYpj&?#Lh9z**;ty7m(Bn*b1HK(*49qFYW(z7|<7``yU+aLu*wlbaSz;`K`uno2J7ZWy|1?WYC zIZ5h~vJf`x3nO5=po9-)8kF`DAy|tN#c9+g&9ND|s^+Xyo*2S3J)*E6#X&D#K6K>=uxdODdhM#V((znt# z;6S8GKje1TP+i)HBX{=Sz2A<|$g7uN)Aw2K)A7Rh>rI1`_sc)rsg zNL%}y9=a8GMo}faHnt~Tgji1YG$5+!CcnijuOI{OAj-M)t`Y@u%6IobJeCWYaPv`} z%=Y7@XG-qsCL_Jmq;ME;9qwto-lcrpW3uhj7UAf}8kF29edA+=F@EM2evBvohefGw zgF#qT+CWq3B_0#2>7aDu7p}I@1eIVqo{JNAy*M*@ZvW0_Z{dS4QMkk1)1ux8+O|6H zwh-|}QAxo7LDM>M${EBO;QPQBVxsk@ecv~*(eKc(C6I79f)06Za4BPwK8wE?Rxn)s zfv8KgmCrr2OF=B;3QcC?E^i_%U>)R>>NMh*rqcthoos7^-ZOJS(-CXYmmiOw9$mhf zDna*~*o%%!+NyCxUcA^mskTS#jxftX#B^*Xd$CBuQ{?dUNI&k9dRkFtOC?Uh;JdCT z#6GIx_u3X4D3o#QRX%>kBLAD{2r|TK3FLMYrccI*WS7-9-+%ry7U5lc-Rxr^kFJM+u)P|`fK7r zM0|mPr}zf~2z%O$pkGhV8oECQn)(Ksa7=)DVaG3T&a8OMic~YdSa5M&_qHb4J|wN< zoKhY-o2w-FA$JGGf?Yv8J}toaDXD~2-xTBK?jW^yojfnX=eB-i9c3k5HF`X#shLQZ z&>_+b=k~JjT<}a>wV62!-T!oy_yb*;th-;cjQg3AA5*;-R?E9$xuc7JSZS9%d>*X4 zh5!ev%n_O%>sjy_ZO1+m&Jk=~!QZ{8e(0w(y?N-NdO@I{ouY#$Uiu2A{#HnN8lhm^S*-CTTHWD*MirVqeFG*-mpMnyL`S zU$xeP*(X;A(_NO1)qkA?%_BX6oFqcWNsdQLAEq+4$I*9x4E}LQ3Udz)yBBd;EO%Hq zbI3XS7w~@{i_Pm?`>XQ#WE~OcznSJ$y6;os5B+%Vztx{4c6J7&2sZ81;IiW7%hG1w z8d?=sob~Bd#kZx1kOxoR{HJC|jS-jSOYaB;)f;08_t*XcSjpF+z4^20>FHBJ@~ScC z1&FDI9K2k2S-BXS^=0Fj`L?7hemJig$~Nt2-iqKSN$i`Cx0m6Dr-~W=0`{a1J5}<2 zlE?Ys53A>lRq1fsMcyAXMNad=~9@a!uS$o%03*U!+*_sAqF=hAnu!K*r6?WLD!u_S)&Gw~E(ovufur39eW41&h zTZ#mCv~>o&ehjpF%oy2AD0kw?K^bRMDLlgA$4t*; zhk(?kVaYL4(?qAFy{{?#81T;=C{F*-7w?oH2=}cc<02Cugf&_llU{v8;&JQAx*5N) zyAavXx*Xkqe$F>-y&h^bnV-(9@l@(E$fhyhHDc3zTSw>}(kpO>;z~e4f+)FzRT>VNa!5YwNz5*O^XY98d9uSNkvD5toxQyY( zz9lfmNaXb0?>tgTD zct#<&PMvm#((tqY%8k+H)BZByqMb}yei2dCR;Vbs;3dwz-nm~eJi+K%_e z2X^54SoP35aGSe7+<9`Twi3*n*}=_D23hyAQl8EEP!bz1?=Hpr(iQ>=Kkr4~n4T=M zmLAnk6+v-xj5vJc;scB-QyF#9iN9h^W#DqCd6LhT9W2F8u5Bk;cc2|SzdEa4TRi#C^FOgTyBDeR&(q%=pQ zhQtj%VNsYSOvzhs2d2-En|=S1w@qkFasBIAK>jLgs7_8pb~-Ux}@IhWK;G6T}o zLzA|~b0`i=O312C^e&<-sswzR(%0Nnw_{Y9biu{Jorx>@-`KYLm^ICK`L2%qT-e`k)TX6}jJykX5!QzLAlAE-bVhF8NGcH& zP*HUk|kQ8MPg7^*k6grGV`sq z2-~2#15Y)N%e>7gEs143VRX8p?M5E*gUEB7oqk*0dBpP&Bb$pyC!1rHL8_nnAkP<1 z4O3i#x&zG)ONKIg!=-TZVYtk#y#(e*T@d+XRSU~)WHn*^y-)hInr=!H|6J-x9nAF` z^&hT{itS^he47BI18Eo@$cW6V{q+uX-)9tv*dzQ{Ja&1Z8R5bMuhya6&2|0H2|c3p z+Q1+Sc(==cmSS;*OIbzCU?GAGIRC>YkSwYO`Np6JbJ2NqD8Xg9LS?}do=@!IghNjT zU5X#i>rXX+K2Hzk1M6lNCv=&%ejROJHHovHoO8V%KMIj@4&k*X1rx#duzJiLT-BJkYQC~v%~6=_C95}xM8>7Zk&+Yctmpqs4zhB)u8Ohxh{Rnpya(j%1L+5@`BZ9tr* zNgw?B0>lC>=;rn9b02r?3=NX~*(Z`xeWf~}xaKh%3GYcA;YomSo1Ll2`Zx|4x}`Yt zwGeD;1uYlR)y#?tyb7+rp9WsbC-wz`cqxt?()j z`St^5$;vlNWZX0P`%p>t_>C;Q*467@Lh+KPP#&uu%IHp;{18#k8qxAEmsut}#B(uo z-#wqw%&$b@vz7W+iGsD~EU1O9S)_N2$qMbV7&&%866JUNY)c6M%pIs_4Rk4^6PHaBtfY?Z$anXw=Y8!uUsW% zp@v-^R#Y{P%ef+HgYglFg!~|us>RcRe+DZd5L5VyC|_tki+0xO%LP)|{8(CPdL1^g zTfXp~+gg9tk&X0U0Kt#>N{3{4blrwpam%y#w3y;p{>VCJC8p1CX5(8G*g}frW@69l z7v!zL?H$m+OYAf9M`n#*_7oZf1>2Q#zYnEfDf|m~K^VP|Ksg|~kxdIo zE_+A_GT9Z^7yX~N0-Ng>&l6u`U37G!<7J+~)w2-WP#z{NHwwyP{fm5NyGvg9h6?|^ zUz6*Bjg^5UU34+qJ;+Ma5>5eHK8(aby&t#dZoEr6tGAxO%z2P z7U~nDQ^V+I54_gw(~1xPD$!1`M-SJ$>awpFH;t;V^7zAb4#*^mSc?o;_EM(aHr?%` z)}aXDrEa`x1v1mNJxjWauk8=v?CMXZZS_Z9$2v_XfW;SMv-W1 z10N`8nY0>(6K-uJsZkBFc#t4PjT3A!lDbz>9uI?8=~4ySt}$EOJru4PCR}D(p<7HK z0+;v%E9-{?h*7SCgi!z$qz`04i0-0mvX7Dd6)By;g`=zYk6h5rj&hsMzQ>o?5hDMy zVpLe;?3vTQeKb6q!j&YSyafww{Tyvhx#0VBn1CuLjY(OC(A;>t@4IBU(d#}3e1_AL z1%%S;K@Z`Fkqar*eDhCBqla^)s|#q1GP`s?>$G!gKrU#)we1+)>2H{b&uQg9sHQ!! zQKt0z{9hM38c#VL>zO1jJ;xmPhP#>@LStS5*T~$pI=ZDE9_P-6a2K5*If)P~io}rq za2W!qt(Q`kONtA?>3TCL{0`!*B(+cQDz6>F{Ja zN`mQ3+MMc{Ib&#)wQQ>uQ&g>-z8a%uHGms$B@CZq@d1TnOf*)CxWpL3AwJYjwmA&T zk;WG8wRH@iV{}(QB(tK`68Fc?qbyQ+{bR_pO_80J_}rZ{HjtUMNELPKXBZ)Bu9H&p z^S>dN6lMBc%(Q0$btyKoPVH|L{MKY8^-0dXhRy?;N zoGsbCa4c8~R#hxtg7>l%W-PciSf-;9eGuji#Lsd0w7o6f(NO7?1b%DAS_xhuYp>L3 zRF0%=kNBh6Cu7oCZXWrild(SI@)kX;K6#5LJv@J`6_$2gkvR!uf>=K)=Lp7gC#I)M z`H9$>fKcpoTT{);QEeEey&2J~X|)z-L87`DMm83tmB)dQ-T`P@0&dzoa_y$U9{kJR zu+GtdQXdU@+1JQ?>%Z!gU30O%zW|tWMfF74B}M;H8f11~y1x0O=0rBPm#RSkHXvCz zY(v8Q@HhyWk!}BxUUYW1^Lz!|^G02_U3G`)AW`qOm3uqv>w-U1kg6A(@D$YkN!V6v2~f_J}R`}m~pi>J|5ZN4!i8;XDaM7qv>J}2J4 z}Ykjv-pWfh*1rgEFuD0@o$qqfXB{0J95FEjNc+1;l!zo?Lp4^e5j zm)RPU+uS3w>WGodQZ_W!uIAsKreRl^&VRkvZ>+nYjQxrv>hYq`Zj{27w* z*!1Mk{x9HDfuUj1j_W#Hm%jBVuyFg(cB#FM@FVM(4bEILTP-JOm)E&p*?Rc(-CJVk z&3rxM^vZ|ClFsat*(KPy+R075n{0tr3ZK61q!>=DenE=OTg!=p{`l5Rk(6LMKM7U# zH%3g-gb6432>h)jqf|ocYGCKglO4?P#$6L+^87G{a!Em^jmhiHDUkY z$UohlWTcmay%pCy`KMxY$z%hI?FWNr=|FdTf(~x&4yLL2?JnpwwOpuRJtcVmbhQzX z>&MbCAsc~#EH^bKzXO;c_;*vHP-)dC?M9q#_Uz52(z4RPwKwO=Yw!cQ$$^X|<&}GD zQ`ZRWF}NLFuC$^4Cz-tk(!r~S)MtsEy3U_E^77vQa9v7vSn#bNkMC|YN|cfxCxchk zN2TaWqS}0fuSx`sp1mB{R_1n&LF&AT?P62ALZe JVy4B7vR^K8 zu9K>FBhDpevJmN|x$et63B+M(?SdS4CcXam_?9LaQ#!C|1KQ=&9!Hhpu=0J~ey$QR zY4KD+;>5&iN9v&R03Go0Tf5MPX}z{O3(9_|?A!_H1h!#EtK6 zjGK}*H|wOfg>xAci|6pi%2I2&(u#j5`OMhU-q3_oSfZUG=58rmr4j*_%6|b#jON@L zuH1GMJ-c_gWV*z8ds3|_+(k)P&Hg+7I`msL+`Z(m;LHAh{&6L%*3@UzL%sKCN}cnq z4k7wf1}u-IUle4xTRh`RhFCe zDxLkN#~nPawKy1|F>f#6)-OkpXhb`JcXP$`B9|hk-J6FmIjip3^JIq>XKd9*b|S?p zcierg^c-GZFxEDKOBX_U%4GM-OOjtWF6F)GXuZtVsaP~7Nr)+;jB{6u10WZQr#EwT zzFP!DinV%Z51nAE!R{1FbRzv(QX1vY`j0B7)2e!@NKTqB%wuUkH-C!~hH1DQ2pox- zQ)85zM%c6drH*l*#tYMw#!pk{X8_7yRrL$Z3#@#WhTI`Ad{HJ;eb7+S1S6&UXb}gj z|8zOfG%iTMT^T;uN0QBJNy7BpXaUx4xV13m0Xu?btH#WS=Bd3h5Hm@MBUk`xB2!xk zjIaZmI-L$F0kdjQPez6%mnH@%3&4npI}LHH{#To%ESnLfos{`Lx|zzgu3Z|zMPblv z?qr+0N^wh~!T+N~>KlC~qZcBps_Cwx3eU3Nd~TmG>Ee6b8fo2V&6J6y{dD`BqhBNc zhW{M7qd2}0Xo#iWt!Y&Vjhv)xEE4Cu zJ|blz=3g>iJ?+O*-mee>)81I{SWwa#pneAZ=Rc6Vr74jFLRd$iA9NcJ* zJW@3kOE+XUN4g##*OtT1)WX4b_}HLEGJI)4D{-N#Q`O89BS$yIhSg>op!U&FiX4LBCI3TA}QAy^}{_gLwpZnv_h`T=?;k7xH zS7MrPm%Tj7>t(K@CoZkrW3GaO#kA6)@~yA7LH1hbaQt9`ecK4x`6b5@YF0q+$8nL>p^| z;uev8)#W!z(1QLQ5bUk=gs@J5H$m4gw*pD92mP&m~0LkUp(V2)Z#DQ z%>3ilq53F73jG~ajsBK2zu zFR)TdOLfO2FPz-(_1=%if5GOG+aBfDvT*lQO6Mx4YNoKyajkm4)zBd8_^k3ecV@t* z9unbFU`hIVsuis6cMnya9&}NV#TN*Ii=t_c#?+Zf5bKB6TAE$tpN##MN85c*hD(0Q zh*H1jUb}{ef+M$o+ldqyo9Zds1j=7X76;hL!xtV^H1A;gzDf5zzEBC79PXXTB-edy z8~EaXTnREA#r5!^h`&qUY#@`)B8r*cyM|zXyY&d=CgX*W5_A4tU5#aH{lZvH z-+>45J|1T}i^iD+6+&W=K%4?O1;F8#kEEVixe@a)DL;g3Hcsjf4To5*g}TVILXegz ztDpW>y}~K?SC8KHpV%2DWnGrp3 zn*!8qFznsi+-QSyF%_AZb)Pp`J}rdPc9G^b?Oa)I3xew!cRIfx>YOfCtMpS>JU;n+ z;mGuMOIy<$M95RlQ{i^sM@yrEDIDgxd(eXu@7aYG(=g_`TY(Ez-gweFXoZ-lO9Tb$ zzfr%8tDcpA>9wBdf3%&AB6q*~@y&z(ZSlX%d#G?9I(RN$x@TWN@36D7&_BbeTnr-T zmk^`EId;25oON#P)-&|jHIgo&B3#ZTp{LDd<|>>mM}jn-V(ObXYqwMSa_N(gtLXyP zn^)kZMtC+nEqyC=&Ll*Czh0#sB=>{orM33-mFld);mJIRpD$0>S~V=3BSEmNyy@G> ztxQvusOJyZbt+z$lfv}h1_f)1{6U~XyHu}eHSZZnA>?c?f?C{mFKlIPialogNwe3A zr&2CmUdIiAV9oeMNxK9Gx_Q_M0c_x_ifg|V0 zY;jEt8%1S!gzF0xuXV z>y%X5e@|QZt>q;3_EkB#lp9(VT_tEmaOqYiP zN79|@`RYX7tEHiKklk*V~up2182gGYQ8zYTf*E?jXmR68e z3Gq#g{GOw>u@aq4%;puPE3_aewHn1I<5y>rLc3Z9@=jg12^$lgz->&7gV^PPl9M4q zJXNm9@5>Y3kuQ|V9kQp55H%wcN&**SZGtPU97HWNlsU>+1o%&zCMLI&w@3^P-*>3{ zFTS<`^yG`ou5sf&#PvCi?Ej_qT#{HEfIbxNDgmg?T#kF*L*x9Z>?JozK*BuLwRzNs z*4;=pUgG&3j-nE@gSoZiHH{?`+bvrP*=KQ4;@pgMpP7oSWwbILhy#fnFsVp!67zpK z^}vx-aPm2&ZuA}r2(Oo&>=J2n4*htGp#%V4%1A;ua@@FYPo2sI&dw;egILRi^ntj@ zy_^jinnU4T~Bb=Ydi)j(bklgLlRggvvEMxn}p-X75xE$ zivA*xTAF>y;y1s+3TxdMT>5OK`j|!J`D)AE($F;&v^yK5u%0)TIQ8t`TV?e~w~wd? z`;%%}k7HkFt+bW9WR?E6E<4K}mOPVgCLzS$OgbhS>%}&$pSCH5^6MSb=BceLg{WEO zEAwPbdVol8-0+p-9nnY~Q#Q8e)$|7d{LYp5U%>B%kbM=Kf<7E@ZT4rx)svd=tYg>q zwO>7`>T@*(qqIT(?% z>F%eM5&i@UuAfXdr;$2s40klkzp5RHiTYnGJj9g?+P~6V;N)JfCRbbEgK^1ojyyaUO9gpF$6gmlbyGAHj{228T%qu@TEDq%Fy%FN>Ki5^@-AP|tWOuN> zgZI;V%+y^7otI(xHb2{Y_^UArR=(q-;kC@mwO$@vn>(s%v&3J20jn?VT0Ko5JnG=w ztS4EYn%+i)ss3wVX>7i<%k#ZM{aoS?ZDJ>znDZiffM+rsVr90=RCkYf(@koD99+WA zOdk9LZm-hI^gz2PfW~Rd#BKX|`56zy1(Q6FQk2IsPNfEToTe%+90og_+d|!m$7}xW zYNnkX5MxtDGQ7$EW|$Z0Rj!?t((7&R>NPw`hzIc}tEZ_Am!&#>@bKaBvgr(Y&@q&b zZv%!7C@PpA1zdP$Ew(-gQR@tSMJ!eNO%Kj;;FhS{5ylM2qeXg(qSVOk(3s42i1pwRBpL^&Mf&fnnHuCyd7sOXb=tUz48DqrvQ&lA>Y zGYfhR_Lkf?U%vr4^@|LZjmDbyxEEsNk&PMhpJ=2FVx>JOEUCCD2n@vP)zB@fXI6CK z|FD40#fk_F-I;?OAQH1dGa#c{%-`Ia61t*7W6kY&Mxd;C@?Qyh_n&%7ZrU%A)-PL6 z`>WXD=B>2hv~s@RyYx*Fz7!HlV)dE-r$?q*6wOuJL@Go29P$&_*Cl8oC)_)Lrkaj( z3ZEIJ3mY3viDwk&G_JXPWCSxuBf?4xuIEeZ8iaKp_dlYfsVdWsQ$o`5*h`7gid*MarDKN7B~^21{g z)-0wuGif_AR;jUC>{;PG#+PQFdrDB$*|m2`h9oLrtQd(W>4608hkj5|T!e%G-kJ71jqFHZdU-r8S%)C{t>qQYC%>`;e?P1sV=zTpf)HM(W=#uBH%W zljVNN`U0t-Zw>Hr;&c0;pK7N0>Lz0Wli0(-@#}BRl=w_pmmvn<-3jvq4TJ96XyBw6 zfi@l!dSJFii>GjBIFU-Cqpc01QhpU@0Vh$W8L>rW=}gYOZ72f1>!vxQkf5AZ;u45- zi|e{fm%XG30pb-x0TZrqO4ERNY+8}SJPpvvM zAVMIJc}D~ChB;QtO}s!L9SZ86QxX-5aNLebW#49!5@8c@UWX&FIbdu>M*Bd| znf8a{#Gcd(UEL)sHF&%w+36Vmw)XpPm%Lx2Qmc#3-M<{3c_5zXWDeB={Svv|lD!Eh zN9}nVIjf`9e9I|!EElU|$jsq+{1X;F`R-QMv0&Wb?0_-UtWX3bwCi_zBvE(otRuoN zcU_z^mxcBahm#-C-5O9-()Qz?RLz&tFI)mbV0?Fd&q068atAp+sBo`?Wp@7plJodq zq3OQwyy@mF!z+SNRztK>Q5QO>cRGPrB?>$a6{2RRh*7{6X%{8F6wRsMXP>jE=|6j~dU z{k3}hxU{F$#?xW~qYV3=Ywk3ai(N}L?{2D*Q6Oh&Q{{lU*jN$n>D(miau>;2uW)cY zk)FJ&o!f7euNoIljwe2hnb-_=2kuRVGh2|{@{FPB%%&B@4}!jV$;O`M_)TNDXWtb; z8@GS03DK0F=|*m-H4L`A?NsH}a$?RjnV(70f~_B$1O;Qz^pk3vU=VZWKfSlkuM3u4 zF?;wE0Rr5IWcTr;ds__rR{Li5ViCe~jM)THfoxW?!L8kKQJH<=s3+UvIl21ZMP_q* z6ApKsp9@@c>~cRsWB-T_!~4isub}ewgSv(jU`G>{hx7(_nGd>9PTyye*xl`eU{c2Y zQskt(*Z%@!1R)A~e*w+I+Y3Jmvajiq`Hq2nrqwa4?3w^IadWt}85dgbPzgx3WOQo*&Xvsl}%Kef3RsbJtZg0E}= zKdEY*yg#RnYrQ5M>4uF8rxfxPcLt!6+kXqsdp0}S?YXa|0cX9CS<|{ilDJ@K{Mz6-5WQ9iJ|yD?TLhTHtmbX)iHy1`glg0(ahqtz3Im2w%|xt`jy3O9@h z`L*Z5+{I<-QLaDbJUBEUva;CC4i13W3c;Ewt<1DwH;UDG+4B> zV4R0L!89O1s$;jv?3c?{pLW89oBfrz_NE{_$NkEXXyo0$a!}DA{yXAY0J`ZzBJ%Uv z7Ct@B?l?YIMIf9L0qhDRB+QKF8zw>QN*5Cx!GxQJQaoxH5+R-gO%_v8brig0kBHX#_@GL}?{+oozh?PKDGD=(Tkv8$Nr5i$8Qpq1784ou#YhDV*cF)m*+SoE8>ZKY{h@A;0RD#ms{G5R}!Z6A`0o4o-f zn9gw58M~2B4FYQxGf(APO&Gb?g74KlIq7q7iERCB>>#htt!Lo`r3eRiiceUDMlO{2 zfQniOG+l&ovNcw^Q=qDywO?7bd|^$4jIr@})9SSRNeed}DD!wR?Zs1H(^-M}*DJ6k zfGi$Lvzy}&(8DTddc2`&!7v8&2~eza``8GnQOr@7?#H>>MgUVOg!MT z6u58SW0NBBu8JZWkcof}b5tDl#L+G1Dcn>PwfZ@ zE9*d+UI;ClgB=9bHM{zi&WWd!Y!HQC%EWkBDLZ$PU!5L%VczfJf)%TK(tV@#oY5MU zf&c`VCs!d8T%`>>vb-blu!kxmgLZcdUXo~$mf23VZ5AtJBd9I>KGJK|iu|!nRt)5f zgUD89T+3>tFO%oYRtIaO{E3@HkcdVlMfu};7$%hZo-JpD>lVFxz!ygG@EejVp$Qfm z2!bWsEoBC$+jI4r4zi-0&0;5-=JB!&vQV@RO$oK?q_${B4#0hkMtk~Pz3_|!|^GF0XYsBr5pa;GtQrWa{1Any2+W2-g_ zXpWmG@c?&H&RI0fwG}bR#&kJ}4BReK@I()Zk@XKM41;+zTmag9BP0i_9w~NY_N1y; z&j}Dk<_tBryj(1#<^bmETQe5Pg@4u5#60d|3ypPmr$-A$jeOx2$gN{c%DjwOj&%3> z4Ad~#fA`_rXBGC3{HsWq20k6{5mnWspc#Ij`#N%@V+WJxxjcP0uP~;U%Xe~8;daMy zMR*eIvSU%- zd6cJAE>h}|g#hX8gWf|#t;!9eAhKES{gR*M`=^E|IhN!%&2m7E2*eZY-aBjbIGq@g zsh4^tGq&`rfpqjHejKD3n*SFNlV#)+{g@A`elC8=>0=g9Ka`tW>-xJx_T(l^2wVW& zT|jvK{El&7G`IHPe!*ir)015#H&4iOW%faMo;Ni=3UF3x~=0kb-yxxjU%@jehx0loxWIdG#V;FmT&ErCqbKMukL!vhjqdA@!ng6+KQZyVZT_Ogn0DZ7OyRWUf`NiFOe{aJ zR;W&;65Y!>q$oP5^rj40aIMC|VRqrE2+@_}PjP9wbocfWX2+2`jT7^14J61`b8rGW z27$sv4O<-; zQaH4?ue?CC9P}5kuewqSaq3rL$>>|1zT!@1oyM!EK@=7g_~{D@^0kG$tzn3}Q)s%^ zzdS1zv)ooh@cmd(?O*X|7R;c?<6~(BUpV>g^q-mgOH|QzhIO4Zcjd0}KemPLcEaqw z$)QzVmDEW+UNZRQqLR(ZF~7U15^p+Nq$*xvp4Go_nY?YZdY)AGg4jc;UD;y0r%MC5`(NvcXykhvg7v)-}xG$$i3@!8TkV%B08IITc?#*4${SvP8An=d%G){7pYf1g)KoV?arApBeRjC%F#jaJ%y_}?386+&uV8Eqn z`CGoS*HC5AMp5yppw(S;b!5VXnsTQuW0c) zMG@-du`sJ&{Zh$H1HeiilC7+1D9kW}@I&FPZ-_|p9>_Fk%Ar<$F*$9x;XVD>`WNNW zEEX;0TB**R&-1~u$#ZL*O5ujMBQxgrQiHIjRo`|K?|>fnE9&)Ybz;|g`75@wkN0pvHdg2k_C=j-d3Y{&(4B*vrg#@5tyz+8 zp{!L(8y0MnVXP`0-E|OpQ@z6R%ld-#NpNt)j0^kJVdY=ImGFIw*|(i$Uy2qy)c7w{ z-s~KL+n}L0J1;|pE5^jLw&zTAjcT zC#1&b5@MlUrv5GXyzALsp<&U;O4QnKwxMdif~>vfabJ*)%;rn49&uGeDW|)&gO!Vj z^2P+PJE^zfNxs4v&2;}_W%a{XJ=HC}kIVMQxwcE%96Ps0z_R)lb(8mccaWHC$MEpm z53fV~OW^+ceAC`J;WT>-DpECmon82f<2dCIDm=7D;BB>bn%_t-aq@tF{Y%+mejkW$ zXrcU$>;i#Q*9hOUVcaRH%UYzXvSAE14=j8U0(&Xhpz2S)2^t|1f#OYHF4p;~jvPTSGoQCLL3pyQ7_AL~t`k%$OO6GzKERPL?>86yHe?_L4%w+3%82z+0LDlpP z!AKA72$P#Jyi+^fk|$AkKCq z8ou8=&T_6=mqo)@1;)P#v9hR*RcacMEc;CuJeMYY6PnAeXkJheuk>FijXkz9P#@y^ z_9w12^CYkjlS498MyIMKbn<_J>c|-fZRNsu~k#LzV3c?~(j07oCYj=~a`WmgVOO>5?bp!jOKv zc5yRvrO{f}bHWwZ#aPuh7QH_~vXx)PsT;v<9@!U7$u$nAR=k&dQdRTPqzi(>so8nS zuF-G*Kx9T+tJ@2x+19I`G(zqYA6<^wNmGQe-hNiv#}oBoAdVEsXI}1aZ`KFw--m?I z0Rt_buR%Fx@=@eF5Hg>`x3{o*s4~2N;-+ZbvefySu6C?7as0lM;rnjBdmB*yw$EX@ zBpbe!imut>OiK~H{?!X~0taH`^{r*Ez7N%kj>(BNm9?Dddw9)Y>a5g#{gABsBv1pw zPiH*XN=8(iVPpS9aNfyEOnTy*kPdk?gHLA<&EA*=&7)o8FSI<+v>gnJl}Gvrks>iW zJ)0szDid1pB>LUWy*EjLY@3Af&)kV721}fNFBd|bD=9b_@cAXs9%=E)D5)umtqD226h>xuBmvpCWNFNsS(V996EghWD^l#7%DkG;@faew5`Dw-$jORe zOrp&iYPv6h*Q%)Cv?ftA+6#Bnc}UZE+1z>7EPolv1}|~q0LEW4YM2`4vPOTDgw0`H z5hnm>6@#RPyr_t~l^x~QjgVG^l~gL?9&Tn9V7?tq_Kc3{J}>+rYJb7@^Se_@fagrB z(HD9QPW7!GJMBVg+BgcLAlzO0`Z%5Kb;`My9U9<-=xTi{tgU6n092yTwk#K} z*yK{R6&6vP#fbH$fN4@%Fm@?6#;w?~TNM?33~AFA$tH^CMK*JnoB(79VM^hBK%{HD3-u%{=h3iAA04vg8~ajpO#U}fZ+GOz z`rwj+ZVhSu%e2h4;hiPE7X$qxqm3*#8)Un*?kcBPf7`$zQI9kYa=Z>ag$9pqzdvnA zmJKF777u6Uiqi_=V71K%ABDDPm5D$?sePa`r%22rAd&yERAcW=L#}dgK%hflyEL(& z;)vK4dLeybI=OOihtG+}libEvb`TO4R+{Q?OM5eW!C&9IqpenAp@a8 zjh{EN;+4Bqlho-fQCVybvjx1;h`r9U6BzMi(F)ZZQvbuJ(n@bZ^rcDR((o$R4{qIu z+K#mg$}j(ecR`R5u;^*&CfK9HN}r6-d~+31f9yAKb2>YAw8Lac*4&O|X7gI?kJv^b z^q@2FNbEWXC4_$(0{x%kKTgaostEvKoc2{|(ej7VHzPf~X z_=FyanB4DkvA*!AKrdO{D8m8wRxqDXxqAnGE6&GG^pCEWDS;%t4?_cz$5Quj>E$Cz z_azYrmucKS-m%}prL_LdCd5Fj_1Cj*{y5drexxlD>;_g(GutI+3`wGL#iKVpjLk|? z{JV1cSZ|YtRIr2h!WfOC_n)=h6Fm-cOz9zA-YP*cNmEZ8b8`!Kc zWBb4mHFnqnQ9h&XMVsd2z=NGHUbI8N>dK0~q%aGx$X~!c)f}R?W8PoDiHudsq!vtA z&M^J$e*C^g|HfDE!MiI25n)_0%vi}8t$Sz>vF2o>Dh{^Pjm@P4}8$^*pLdDIK_2-hZ)fuYvi7qX4Pvxv#6b3cLsD(az{ zG`v2JvP9X0YC*8ee4&to(kBfDnvs9>UL((1_kWI; z8W-@jDXD7`Tw>sYD~e%t=?^Z|DSr59iV}C}lAtnl8<#i{>!uFc20)a*7 z9P{yuU?6aJ&aEQQnWbCF37P^sTZC;~Qu8+&2*-A@{@JdPQ^Y=eOH+ zbIJ3|NSJ7AFnhZvqEl`ENXGRF(zrpIPzgLnbP}u17$D3nv7+~Od_Ogsr$J0jC6>1J z#8Bc_i!He5M2NjL&GsQG>~idePEXO-j1aNd;^)$MA$NTU3_eV|j?@4CDT!nM_lM|P zV+$8v|JCC<3pW~+fm4_e{Qbsu?!oz6equTA?pXe}z!XO1@?f3254Rrlrl=)J--G#DhFBRRj z$qX#WvuHf$8V2n7@EbkBo9j0*NH~aonQ#fL{V+z8uu|#7Udx&*Rgk|2*tT#f>=m(r?Y! zMw2KE$P1xCHM=YyEm15`^Y-f;5b;)rpA5_=$TLc*_s&@i_OTtpV&@ zoc__P#CT>IvNJEt1NkyY$|Let6E=@Z3FfWd zlG@!8=~e+Elg1|sWa{80Yu1#P8zmD^^%3HMZD2Di+wR>5RrjzdzBj+|42Ml z;cNRpRyV)CVb^BYt~n*3YD?{@Xfm_0K4A3TGw4|mel@@4A*k9RIHP%Hm?^gyW?T=G z(g@gF>O04E9nPG_8SGWg#TNbQ?G$1O+2-lj?qbY}Kj5#J_NnC4@GGcbtoGSGC_;(R4N9Dq5WmSIDh4w3BC3AP}DF z0yPKOD>XS(4R1Y#2*pkYeZu8v#w#J~D$Z3+Xpsof!>dTvna&B(u|MbqL?!Q|7l9*3 zK~NOW=qK)c&vm9=KiuY4{WR-R?nd}PRZdrl%$kVkmVVDJbq+oNWxc3Eb z{Y1zTZVn~CNf1*UUmPIDAZ_=oqO(RUtvxF=VgV<_gUbH39zI4|2El^u@?R7gy_R4M zID9=45_40^`HIobsO~^5fFrG91zh|9GYNMNcH@$Teo)cLkiNeA=|ssoxNVyw&mq!u z(}RBH5S+uJR0Zum(H{OUFXp-)ym1_ET4ZL88Dm4XzXRc{W2tM?F(3ePVPd?pXg0Sg zG398q<2W)ujM*hG$eJwc`H@;>G~ai!3%mtl-j_ z!p0fw@4q_VJURJCQs)B2m>J8ae7n^2;*wH5j?P@T)R_aF-JCbZBst$AeCan)N$dAR z$?ZSk$lRcRp{bd!rv4-iOCVM&vB7!@ij-&YP+YoR^*E8>EqFsXxM48+44vJ;3<_$T z?KR|na9I4hvH7{O-N*W`e})v`I=jQ>7#r#RqXPT47Gc1UUF0ETVY-81?XbT86s)x< zy5U#XKT|ShR{e;&eW49Ex0wNM8yy2$lDl0QML72IL=(4ZISY7X_P%)#G4@lE&yUG4lf*rX9(kFvXy@R%L9Q9(SbZ;xlH&Xj_ zyA7~`pFH$MHnOjVG>AhY_1NR$Y8Kz^?U%L%RcK4(X9Lcn(L0{&DeF3EU1Rshn*Sp^!gsWf<*K9*HK%*(DP6k zt4wM8K7gmAPmiOqfu+~ml8m};;)3ekPp(=fV%ze+H!YQq^nE_P)}~dLb1dFyyRTa= z+Xo%-R@dL3uAO)m!_@|Jz^)Ii(v+nz2PzR#0q6RHqio?z9 z?e*O(;(7dU&(dWnklWG0M4$RW-N`?C$1DF=QAXNN0(xm{V;g+8W59-v)J%MuAznbr z57x5$G)q{}46fy;z-Ix<3MMb0Be~Bh>x-+{@U~UJNIcSi62JDcUd|O5MQQ~1E2a{> z&tIjZGx6cQli1`;9=pD|HldZs|1NnAX4}H|3pJU(f6AP@DZiv=du;ZpJ)dgF>t#c6 z{kddq26@OOBsdMQC`9s-{BitK4l=%)RBft7T=7Z;Fn#y?EGjo&GuxD;?mzDau^{mZ z6YaG?(y04l%gA`aIR&ZpPuLKXM{4;{eYkf*)U=d1)5@}CHz`ra*#F-%tG5&p5;yTc zMyN+wY8&eSBPo=gaypm{sW1SwA_MVtae_!Ven53W(M#+Ot!fYEm+HDn#VzP^U*2aj zi<+j814@%E3RT_GzY|(Pqv+W-$QRari{&bne3WYVEm=ntOih?z|&ZDJ;^bl$2Qn5<9ooiD;GGl5dNY;DbWZ*9I1IB66d**!DW0vt}CA)M) z$zufF(z4DP+Un<~X1HMC(~hvi6eUmJev<>WWiIo|A-^M4SirzHj0V25+9_24;PnMT zzw}FQ%73jSZKAc}La!vFIVWrOm@FQ|XEAG%!*5g%>mX~Kgt46sr)okb$fQ2(W(j(^U~J_r2<7XAI; zr95`N^l=g-B_P(`%e<$#{{`8YBB!hpWb|7Ja3G-$5;kGXz2)S&V#ncYL@*>fs%yh6 zOZqb$04u6`EbUcv>_B*d9*=ZqG-FV>zA$>((&3`z64U`PR}pKkdkH!KnA^mtIwvGC z6~;~$&!PgqoGJC{(-|^4L40+epxC>VP!Y*EjbF&+>FY z-j#`5d~4RD@;mv)$b4V0+>1!}?sgn@J1w+wV7y^3ACzwXLGyOa@7U`3tw9P}pTge1 z8mxwySBH(ISEV;QM!&}wynxb9*@Q?<>_3u`z`~tul-pj&iy#7KU`X7dq`iW8;8%-~ zQ_CpS|18^GJs<-zcGlW9#9+2_3QZ#kE+D`abQ>6)b{TR)&||7~fTM$lD+2$Kh}|q+ z7qeK38%4UF%#I5+QMM=w*8hEw+lH=Qb$DxwW;LFjD;255r8qVkWJf4E;GFt4P6X^bw|d#@V~ z=-WZJ8%g^VSzfUZ`fOfBak%|1H0BM})(Z~pohVo_8)q_#*(VfNQ3S8n;5W4DT^@XthP)%wlE zhmaHh6$`KSbi4hFKFTy@`8G^)?$6~l*z{^@2 z+l(-vlYr1!S%iA5gg$L8jHI7PXWKXGww#$6ZuR2ggtQP%5x@aJdq%H@2>a^hXCXV> z0d30cGFADIBmYw(k@PLlGJ;vi0eky%dHaFwxDYaunQh!eA2B#DkqET+K0p1_IMcd$n;SDLFom&ZQHop} zoc~CM)ZJ1?uzEMf8^_sOyT_d_9aU4p4zO#zQ<2>MHsGvAYfzZbj{eSC=#N+>576$f z?ZoaVjaS&NMq_m=Kyz&@a6%m*5gZeF&q97T>2;2A z%9|RRct1Ag^rgE`yd`UD?m#^TY#2RUU%q6%M=GpsPxI!@?M^-%&3(LNTX*nzhsof(55oa2PtUhp*uv(m4&<>`j75nfVZj1U)8wwzNXm_&9N3< zF?qD+O>1Uk&ZAF0;fJ+D7lrew%u6V@hBZ+s^C_1*cx+=z=xXh|Q496Ha-5c5tN~r9>#8?Zj?I!++El+XOv}oD- zhCIUk-9X+T==-ZL*?)G4=c5Pn(c-l-1HW6IP0V`O236&^hUD8c_0EN4(4AFneLH{f zeo`t3o&4l-Z(X`dZ0J|ew^9SSA$W(dLhU&lJmY@n^@NWGNOG>@iu*Znk9~mqtyI5K zA>9>YJ$Ax!bn+Zh=aBHXShfEaUC0p-`z@HJ`;cJzS|yTQOAiw`je{YF4-h$)#;YoI zzGH0Z8(~eJ?ti^ZeQL)0g?+nUCRPA@?^}El`_wUj>TM}Yt6e#%Ehze#q1S5Rvf4qk zz$|?EGj=FqdAln!Q?icD2~~9~Qz8-?){{9~VhG;V97~C*1*5BIXYvd(nxN7>1rJqc zYemp6f^g|;GY6Oy+2XdA%wyh<8W@%kJj%yV z#eROTGcb>R0Ebqi{iPp#ZOy03to<3ZIsBEo|4~3)(F2#mJd}#2>U^l#cpmgG$aA8! z5wkr0BcG}tdHr8c4Axrk>b|4k(5095W*;(-5L4@F_BEvD8(Tx5>2G2oc(uZAf$JSK zA=FRcEhuIMjRt8sli(_H8peZ$2+%Yvs5R0Bt9Zwr&C?WS1!UiDlh-+39q!ok z?jH$*z^t7Y%Kf>6-)w{!+GYsv5`aKjK! zrJUGB0|)jz)<`g-#3;f=X)5QX2S#|CVnBDBdN3&D%Tz5!ri0(;h<2vX*rybhR?f28 z4SKb}_X{#x<%^#L8S>blvtpH+I=~mo>rRC+GqC;WwoQQs+@bnjN5}5UVEPe@{gwXI z+(=>H&-|=$tWv#6dQRv+65$wi$<%{yEx9IwcCip^*jUxicKg)hdnu^~bWN028t{Do zOsYL@T9r|tr9sB=1e6V5DZQad>6MGCrU9LR^KsQPUicyelfz-@;)6pYs^>lZ zSKDH3A;w;pgqwnyh8J_Chf$D{xO0lddo^kNo zDZ`OiMjue!uDe%wHDVbc*oHg7@aokjug_}4>X>;Go;yUQ(dg3&f|H;e9?Wj;?7-z2 zcK>u?3Uy1aO3Zvwb@;Q}UyYVqQKWti-V>w%SA$~CRoVHMaD~nX(?dB?k-%zj;~ju_ zSTMe7OYtO9POpnKN*4PCmxh+ct}^YGzvqXX(=;f%X$_K988sx33IgKC!=98q129?X z-xeR9AthsZ$;TLULdP6`PQ%|t#_SSC1VVipy-oL&q=1Dee$Xc`FY|f(Al#jq?r!kY zth>x_Zr@|h=OZ5tA&<{`X-jsOPw)-7SnkXNehEPe%+o}Yuyl!fIU_t<9ogsnNUx=z z4;qRkWu>}I{%Sb5qQXw1E@R8}7_cQPx;{O;2EhJDWk=CG1rw@`v~DA(BF=&{U}S@H!5Yd-3;&D*Cqb~R1qJf=o|oF$r196;en;tWcf?-BqD zeKt=TE;sXfK&ba5qM<fQU8e^QE#NFk`{`y{-swOjFFE-k0r*!!CQJDC+ZUuOSnURT%4-UW(S>%J_z-I?Z?pc7 zG)rw-=OTUj%)6EDl}==49yBhHSAJlkyDQ2f`BBXynVHJ{sRPdUoK^}YX-*TtT1qXH zD>Grl77_Th4U()r0hN5~nU2=lW;7|FkXw-CRyhdZ4ld&%tst6ctVqO!nfBxS6EfH; zE{LIzj|vU5n-e6|qfYm@_%b z@!$P5h1nf%$DMi=8vSH%;IN+&1p3u@d)IxCFT0Jykbl5!_VbX4qgi$bQzru(gur#%d{~?+8F@%vTORLntysC zUHjs!FFlpNHft>2WWw1!@%;f0y>@qy^T83agz3t!M+3;{D@~M_9Wf_D)xog$;kw6@dg^HX*Lv<_$YGgwPL?8f@eKkPh|1iJWxWVvAP=C zM|g@6)2dr5`o6^qDt3d>60ds+bfVt^jOntTk9FrWxzYrUfOralwZPdn&kDr)85HIx z+%waT=XhavuDSrg15GQLou9W;6^fxWjO$rL&sD>=ay4ikroz*I9Xh=HVZ+pBe-<)` ztv9Rl#o*K2AVCKEQCqwUT4O=69ws*!kfqhc*wclVN&m^Tgy9J9Bhz;wV%Q@znN??gG;`FV-Hp}c9e=NIm8SxU`HySZA z7mG+H16osk94W1zycw^)=5((1n#dMgL95Jd{~W)BkE^`0#>K+Mw#1s+P>3pq**ul} z=l)Wf7=jDMZ3$fA*GyKuS=Y2faTKc5Oj?{yit9kWIcaD0801S^g{(y}X_Q7sv*k%< z#QFH`&>#NYI`Le9kA6MS^HNhk`+gv!H~o)$ zwClzgz31}wW}V*brv;(0RB>~arJF#la72v!gN&g!CQQNGw)_aHvA9-(dql{O;>#zd z6Hf#R7cSJ-E;Q2BiT;S+TVpV16-DXu^&6~d;qNI0-LDQ2CkgCAsc(uoYyrh`)w39K zWNmqBqwEy5Ui;JfhZ(H$E)Ne7~$-()!Qc>HlGskH0o#|~ZZB`Q$l){dAx z;i)uB@?Ld{3YoCUD8)&>)ipdsy#s#V5Zw`?26Y1JXRth8)RppQ7qShfjv}CiMttm% z_qlDCaTDPRnkS}N)zPGfx8e$2OUIuRJWWA0T{pT6iP)QFPSZU=Z_RO3&?%d2+{sBD zQM%JK^IT#t5>Uz1cP?#>x%dMEI5wWQ@?F2$f!;Su4LQA#6Krt~gKJ7;p89uC$keX! z!qzfRFRDuPwFH2?_Azo6?`i{SA^!n9e>DvBsS$;iaXA?O2-Vl0cAA>7QzI-$$cngDJMTFVhM#(5^ZIJ;DIM4K zmtuYg$D;Ek0#o)umVW}(HSx;7HTKhWqyXFKJ;Fv+$eQ}%nn-=@oyb8R2CSw5rX4{+Eewhw-N)3XHEmIRKW0t5jOQ}sEVGNvV^WvyBZ>$aD6So1cX+~YCFI$h`7>`n zv2>C^4&6dsj`woXA$MRQ&k)FfUb!{4KLZ<7NiyT=y{f@U#16x6T#AVU7a{VwR#hOF zuOXX8=PCVX4l)Z`!$;a=6pskwU6?YV7H^8BM>@JSv{~_YU)u_jy)CEwsGu`CPeNDF z`-Yi_;`Xr8p1v1MM*8t3lg{9OWt5{S9}N<9-0us%Ns-_ayqnETYE$4)=wdm|eBb?^ z={;>eBbw2BWdcMh9$WJ*Ji*2dO1F@g7JAndqW4#c;Al9cvG~_^LqSK_wSMvxAt8|1 zy1TkOY3g(Ok(;Sh@qG?F>_Vwl{-cS2U#Ds~-%$sO%?uQs==V;=Rw1ocxX+-00vI77 z*L=HZgNN>M*h<{;B*<0=yKFgAWiX7b^W5Uu_Ou*OXo-K>JMxq&9CS(shyxK5A?ibe&Q?c{QDpVYfag#AC=xd*NAC$e zvwD?}A$bkpLRfr+xie^IS?a$sb9gCr-+jeB-V~f156luHhDKKeKhED(JxP7UWHsH( zxOB&-U7=CEdi0Ji$d2LT{k!61o1FSsnrUV!z?m8q z2MdAJcR(ucKiqu$(~eDOi#K&{AJ3@oDBY42u?Z#qzB-c0s)6!twYz;U2>`=<&L_N7 z20OlkON@)JF#52>%p4)X`ivL436}`SlF)D9UXfw{*|1`|f`X>vm*X^MRVb9YQLeuQLtq^NTQW6f_ zW=$WT#bh<57an%#iP)fIFcTdL^W0X{m_O#KgloAD@O_SmW5OZGptVO1{|!G|Rjk6A zjITTq$Lb8)I_lbJjBk#0L%KV>Dr|lYi7ER1#tna;gNm*j&7gYcO9E;GQi;jaxre$( zgDS>iU>S0D(@aSxP)wCZQE}@PS&yOrPnUa;Osu>%cm-hi#mmHLuDeY*0u4Srn57%b^BcE+m-UKOkocLZ%R@7pM z94x%ri>*0`U=P##Z=g2Lj&2bI5ATUeQI>&b*ktzjqG%Y#>_WZtd?Uc!%y8gCpTLuu zOmg(;Q-jsz(x!RCL-6c;$yMiqZf@LV(nAAvly~GqT!wM=SK>V6ma{!)4=Vxn%W6Q2 znAd_3G(jgbfRl?~n3AKt35HnfzeqwP+9fC?UmUB>tq97nS&WW7;{WOsc+r1RSt-1T zU{?W;`COGoaw{ka&q@BIRr(VochJO;noLN;R#npczcgcd%{+Tp+`V}@W5^s9gu}0+ zH>a>u6rnAI)YqTZ15CgP*gEqc^VH^^gAt2aqPjVI%5)xg%7A8jgN(^cWy zTL!>(HV^tqTzly)b^>nQ8xKTYao{gK;DYcdrz`lxsmPtD2Nie^b7Q+$!?d5>_~shv z-Fl~y9LV2y}MCa;@lpUz^d52LbG%1c{kicXZ&+}i_uB5K~ z$B`!{0`v5XXu z$*tzPVKRRGp2B%0aT_aa31$i_8M1^Xnbq{M*P3>2l|I~tl%y&D!Zg^jZhp&CZpJl4 znHf)sn2Y_XpMyFuJ6p?MN*Txy{UC@t0Fi8oRlR%$i{H{;Ru?tKuZ+NwSx3GbLD`2N zo&;oyP+i6uYWy5x==`jb&(IRw_I>h@PRnB$AECorbnQ%Ns#JgcEz(DgS3cwcv}*VI z(^!i%M#s*ch#R?~ab5AUG8Nqmczt%q4y`ORUk4j0Yi0)r#r<9%2 zU@f@v2f=ZW_a1@UpZi)2p*Zihv!_#9mlT?IM zGGSC6C#)C9pRvwlmhvuK)|ep|%bgoT0V@g4H|FcaEXJqec^TcfbAPth^~DN%7QKnM z##0`*e7tla?fKeyCHaa_LL>%Z2!{j*rpeHGZ+v>XzW*S5Q(EIbzt zp9QvIc8VN72pN3DgVWo)v> zy7Y$Rirq9IY>??fRSC^m=cX4(hz5mnIk_yt`9&>_km^3$R;gaW7}rR!}-2fv@+Kp!PI-y_=DxPGatR@mQJyVM($# zz%rV<$5upa4)Kqof|R79MSF}Nm#lU7sndi6nYMJ=zF3nWUCSd?8o-xszBk#{&^N6t z0Mf@6y&Ji1sGSV6xRwl^VG;=Uo!j!EhUN}=h*`-ScOU?G%H9q%DI zU#53#!Lby;!b^5yoC#fzFEkh)80>AfnsU;}==(t|%2uEMLS9`8H>#NqDZfw|sm_6& ztN$Yb>Ty)!+7B3Kc198;oVK!hK`#G-`3*{rB_7(&ZYAZw z7;4wU9n4mV2&baB&G(CYWmOPfyV7Iz0Y4_&A$y1hZFeLGfrYH~brg*@ie$-Nn3hU^ zz5vBMeP>yWA{nwzXM+%9Ad#Y*{r&~NA4j5iY_LiOdM)Lr%qgQy4VDn*AanAo;N&c0 zVX>mW;!7Iu%;zLgsMKCAWQvbw|5n(VYD580k#i-*!7IuZvzVn!Da-+N4J1h)DO5x} zT${2e+*}}8X7(oz6ff&7Pgte{M$4qqU#}+=FL8ijt-##BB*#`uD>7MvmL3=zI_X<} zz!UOfhG3sBYp_U*_{Jek@@$`v4FOAmG)tUhz^wazVLD}`%lv+%>9)a-0MzG917!fH zFoAW5d5I*Ql$_3{`Iz+ajPHQ&D3yR6_X|F`G1(RU6>CHiPzanSkOMD-tW{01nhMXJ zs~cEm1dKt5w3P(&RW$Mq4(HLW2q>BPvSPJJEH&Lt7tT!<%tDeEN{FzKfL=Y_H6yKv zzHO+VSQs|hh2lbBmWkvbCO2InM6`;hXZ(FaWMH*m!Okmt^_!9MSpzHZb zM(44@QHNd=D{%RCc#vM~XXs3_{@BRs?cxVQ?F$1y{^1Llop|fkn) zxn}T9Jn$z_DJy)~{L&&^3xOAd&bkqaDTy=8LpoKkg~=1&3*|AX<5uha_y{48l)bW! zv3c#9Jnkn<(kw-HZb4tuQY8I( z`>>@ocR{fKo6bp_Gpr{fd+|W!Y@~VUfDI1%nc%Y(_+@d8*?RNy4%GN4jueR!Z#3+k zME1`BhQ4lpfE2rkDghQ2_IC)|LTgsM#vos0$bsFL6iRJ97G+l%|^U$iwuu0#!_T@rpHJhc~jvJd)``R)1N zyYGBk@sINr?r_<4tNq?XK({!}GSRs>Sj~8eVdA0>L(V@E%Ce$vWGi-N`v5#_7P1vL zQPQAXR{nK;T$6F3J6OjcrVG`tg*}^$dwv#@{B)aI91Tr`{dEl`x^1GDn$PT9uzH|s zi<&uK&OkUQu+P|W!#LRV+kTqw;+gxWEh!+EfKa-8qcN{-Rc`Qv!j1-oK#xj~rD)gk zk0Ohz1w|`25fBKdexb5;kh1ae)OG~xp1fXi;q~HMo_1-d@SS>-4`Dy!ZDlRGomW!p zUlOJct;&UdLb*afO}u(;#GJ)w_nywHFU11EHPWw;KZE zg4^HL9f+hib$GFB*?!Pf?>%3+aLQmTv7J{kjbZyq3zU~J;#yH$%@!I0Jg=#)Y{6#) z`#=pQ>*(2Tkj^~xo&RMx%qnmD(=A}#A3l*Q9uAMCU(*m1tJSsU-29EKRB8{aqzTH+ zYTkp7q<5!l$Zwup8%YPyTT`)|A1ZE84=5~#;@R^JxoX3~sHC9nan_k1hp7Ra^TqY7 z<}X&)9Bk8?p*`l|l!Z4_3P!e(iqymFfV}IwBevrTtgA8e^=GO36Ex7SxS7g1w+Rpg zdAhruZ6Hq|z?VZt=n>U9c7c6MO(OoujX9iy(E34jHd*OQ__pa_&2Hb>@jsF~GkO(4 zBk;rFdizXFA6&9oBqJ!MZz`W_xl1iOXR){`aZj&yq=cn#Bcaku*4ey^p~SfJ`7piG zZ$M67!B&Z7qVPF-IiFr_$puOWK#c0-SC0U;!jMz+CNmGmb}0Xmgo>-U}<)eOQU~KE_?E-hn*c`n@}m+C(lS zs+zsHr_)-Jw!Pc38E|x%i7xd$Ug){!REr%(cI2dQ+Y*-Feg{Z<^X5Bt42LY&H`+2(CT95^aPtPylV$g9`jZo$9y z)^!h7Q7`1gy6QTPDgSQ7snNoVc!wvYK~i;|-K(#IJlK64=Kss#Fo~>Z75)aovmebz zSy)6w=5+HG^WK*Q_4e7GwKTN=`$M9mr(*~q-=;d!esWu~L#v;#wT1QOr`V>$fjp}vW7hRxy%6|ln-cK)Uhq}xT{-6`>RQ&}gbD^j{p zmbZdsy{VfiJ2JXvA&?_oI}~Zk1An^*77tY(WA&7Ji~V2QoNfLsKe4UI+dz~8712HL z-V+ZemV8l!2`u=vPtDl;YdXQvdQ5@hBZVycXSpX39lnRHYPkx-g(Tjh^9oGN6sblz zk4aeF3oB7AFRMgXtT)ZT;9$_hi6zb;upg5{51qra){IgEr`O-}rRW}Jl6v02nNvWH zbVk8AsH)!mXrc^|2Yf*Ha4eg|$m8?xdlI;Gtg}MJ^LG2aUJBHdy12@~|=pUS*m64&0xS zvLJZ4H~xl2u_H|V39mw-VABAhe=y~2g2-23I7jhEK0GO6J5$-bjQg%NxjHLpBy)Th9mD?``W4>1J2L+C(83wmM70p3 zAhSlCiVtLun`kijppymRD~6ez*6dRY>#{r{ap>Ylg4>U{%5zbL@6abyH-qo%2_i~2~>Um%rrB~!@k_ve_GZ5qY{n7U-=74tpd{#L5GY}_Z+7Jy{Q*RaUu@f>W30u z^>|&G<(@!blg`Y9ci5Nq*U|SloYK0Q4L2oeCIp1DD91-P;w_))wti_6crVWMrMvl= zy%bA4IZHvMz7*fRV^(iAx)#tfku@`zL4m!3^5>oy4^M{!C?e&#{RYFpLC>VByW|G1 zQhf05{isu(y_?m#6(u<8`%L@;m0)+k=-DMDWs^>o>+m#(_xM7(Q`!kxaX!vWf~5cP zJ9D&2JficZ-Cbb=%<2uby3L)l+fLIuBA6#%>k^*;WxM&>Htlpm5P1`U(l+d-m z^{gkyPpoR`j?)`!l2x*n6GIQ*(c5+81nWmW*RPj?0Tdx(WpVpGB+C?X7FjI0O_BiP zerO%pW)mjZfz&t0(q3?%jkDEGZoo0VWk=_$)f^_%^490&n2h7_ z2nTCehChPq**BzWlhHrrL=NdtUAWV| z#STI6i1xsUAjg-jm06Xo0d^lex+h4CsB|n$_KFPelID27nelxiQG!Lu-!M+N5rqG> zvz$~Zc9bck+fB@ntg-zgkv1*K{R`Myc1m(YT{i<;nLFLBwnu`++=%#5G;~B_Js-2q z&Lqz-pnk}dH<<;j|5mgu8osjX<&1znH8Ta%-r5JP!{x++EA~GJs@H15kOFMKh-jQb zbI%R^jkQ?%>>N%)B!VhCtO|PeU~$q03emiyeZ;!Pd>mN{QZhO6jZOExV{Guu7qMf{@;pS=*t$IXV1Z^PCLiUQ+NLT-)N1Er z$|Yo&%22X4l_Fjq&~V2tYGuWc!Z$BG9}7JIJ12Qvy+GpXuF8Kj7Cj$S|0BKSU&QHiiWuj~t?la8 z;<|X~r2HKc6)Y`KQ46YH**stuw?hhnD?0)V6wmNM_|z1M20 zr3T5j=ED;9DrrL75Y>j+K&7b>5x-)3v4Jt!CM#dlPwKk#-QDC{=GJ`k5xu8UIj#INf+SOPWF5fik~;W;4=Xqe}4rH^6T3 zP3_0=mQCyD5BRm~Sli3j^$UAjE|aWUOaxmfmZ8jiVX&}?MmJek`sAFM}=XSo8ZMMOU}!qY-(na)teM5|WIc zN%`8JfnBQ4TKPTovhJ5*`Rm27-h3jUYY6;dg*X+dhjw_@8*X}^#J#*NB6QCCi*K`O zM5OV+G`aVMaHd+MUwE76Ka$}gj=DH1191)|HENT@I9-W90w^0G65!#QKAU2AGyK%d zs-ksFO0|Cg754JY_g8I$teG7k+lcA*^(2J!J(CYY0~zz{1+xTR0WDuljkwWx74YvZ ztJ-8&`x82eR%8FwC^ElR2KV?PMvnmn)-ra>X;YbiZ14$bxiS1g!cOFoM@u~iO)P-& zZ>T4Gwa9kC33%BcXEx6o4?xuSJsR^L#vpa> zoHLwX8&M(&dJz*a<}X-L9#A>+F6auA3+!t@dq%nz4ydQF}CE)%ZO5 zK0oFU$Sd!h*Lfa?Q7QkqHIWol^Zn>2zc|s#B@>cL3ilbQxjLp1R=n&kQ6)-wW5$`@ z+=qu68i2Va4O`Y+ohlmBq>p}i65~1GPDWrn!}*1FsSZ*-O5g9zJ#8UcIrU*_Xd$`y z*(n>WK^-H^haTz zaAK+Klvoc3=wM0}`?QdzLD2be7j!(&=Sb(&poG!)@O}yv*R3MS{*D!r+~BSQ*0p$w zESk2MnF6P5h(+F%vxGm;Z(e3D0(euz0j#A4iOHs=0?C5ET2?%!5@5dK#D0^cKn zHsS2>hwa!0hF*Q*S{b3HG^CZOOT4|Inif}VkEvc0${ndX-XwYawY{NyBTwIFJJa<7 zykY)&8dUpBR+1XJsgaq`D`tD56v4el@>Q7e12&nj`AZ~AR`hi|iX1#e!bhh8`wg|r znO&K0LO~P8C^2`Q)gQYn@DEoac;82yR_)H=h1rcCx-Zrn>C-E0vt7n9vHXW=Ms2(H zecTUBJ;mmKmUt=?d7;cc9~SzMn)>*=evYZ6yq`LDwOJc|MgWtIYC;?YObaeFKZKyr zrNigeK+l0axfuw?FYW-(q2d_V0S{`FYvxQ=RT{o11#m*j3UUlo_q;}4%H8B8I0u6D zp0-Lr4TN=TU7!NNZwiOg41NEdwwpF%@+VY znQbEr_f|^V(Jec75e60ct=T!eQ8^YKH=5gF=so+sSe9vjnAhpc!fje+&d!(O5MQuS zwzYC8e{EcLe6HCSg%?AHJpyd*^Oi!BN5MZh>KD*PFku_1Lbl?u4JUDjR{^&iFJ5YY zux|9@`RI8+{)#SgkGT9tm{*Ikw9M?XEW06-!HyVT1lP5wEi`B)qz?#AbcSr02koQgqO6a0rbl0l!7fIDZ}JPj*tVa!>0qs_-+F~>8P%Sy z$`yYf$mTlAgvWUC{{dj$euQwI3!p6R3^dt&U*N(VAu=0xoO_$Y_|@{ukC)E|!nzPtXi%C$MiDp z1#Ea%i@JxsC8&QDaq{aT3@H!M_RSOSULxe>2k6X4%-#jnVOpDTCWjW?tYk0@)CD#G zTO;KoLFQJO%q%ras{Rv-CMUYlctZV@EBr4!nyyQWIg7Xb4Y~5+GEfGD6rgP7DDBYV zKEt0`?l8@^xvsf+nP$-hCVl>MVL(OK?iRb@*Ky^&IBgR^1;25}=7HoSy9>r+c|mXo@fd27aBOq` zC=dNUG(3Uj29T#^1ak1+0R7Rv(fbv0~(1ruq z>Gp8}h6SkP#O$n;b&qHP?)|Ds(5Oja~%rL;Um#uHz>g5867 zHJ_%9`mb1f^;WX~MW$`U!4>Y1?tJ1byI~$}sMS#S27E5ckm7gp;(WUyqsO&Elq?e( zeR+5;l6Q=@MSjIsSegvY!g&XC)%@5BeM&t2)1xXKDCmx|YSKI2`aFvI62@I5yFuNS z*me(S`iTu`q-@E?lW%K38>|5UJ9{>K!X8MQAF*R3pp8d6eS^&c!l#MUQY(>&0HeE= zs~3p%e37-aSfG9KCI21;mM+ZXZ8-K?k!x-G93*!VUJ_ZA@f#aNNY~_6O#NtVn@41F z)Lzq+QqJw}|H3N(%y_bIa%D}k)(@;pQIkBz0CC;1@iac&@r^`Z=+c-dyY(Xee?+(2 z9WOa|sLgg=*i66jUy1)N*^(d%<83*C<`f{O=bDA)Ogdyvn`+AFj0{qywIt?VwEEtq znORD@+6ay<4cSgF5pD~S4ga}#ATTx$eW(e z<&m{Ft>nJV)25C}oFpyCSh$E#GB2h!SeCR7_H!y>sC|`O-juwIO-e0}%mV3qt3^F_ z!OU38rB+1n&vghYh&Y)irjY|$V6+bwp3_}w8hp0Mr<(dF*qY?xz1c0I>Fc0shHKt) z3~LYR6%qJLqey1z=>whd(r}Jgpbzee8F^3I$5b@2H1F{^SvS#>VzUwYbt1Qs-EV{r zvm=WdOU2gIj2>3-TH5D|i)@1%2fr^?cK=;F#uI%Ob^jR4>)=Cix;i0`;63M<56$r3#Q(9$hhY2BHp>2xzyLb-}}VR zB=WAmw5wj9!OSlmN7gA&;HQE4Oj8wGy-0iTs_05OP^FW^C-$2E+kUqOi;!%?wUaw_ zyRz)Fb@B+mxFYWPZ;yPWT>4bgI3wufILX!$#ebX2lnD`w!HgWfcM*j%c51-89@D<% z6-uQqdQ|`<;vbrleev%Cq`QP6#dS{0}CpOh{ zP8y*mdu19cCUwMLfe@zp@|L@f$=SD^&+rp5wyce0g_9^uL*|>q_djpSC~ruYN^Y6p z&zT;S=o70y=>6xN3YvG3Tf?2>vHB;OB-?z$fydl+3}o)9GGB#c=R`IeN+!K4t6ICTV7D|a-Mn)b70m0ze9D}P`fKzvWQ>jbX7x{FfJ%`RUK7=V$=P8d zkdlpSbZ`0nnn!c8MoXW`z*aW6aS|IF5> zj+JLx7mFcPckrh5Lm!zeaaCf$$G1E=k}e~y0y=v%;8pNx);cQH?y7xEGj0I zm1xB%i1f#f8oBU?C7C$5(w0`5C3k5x_td*^ubQiAH%(glfJA?6ic}{7j*Zx z(MU~sBbcSKTAxnOvUeLr!8c+K8|UZJ19SL;(Mku>!3FWSs*|D3v=7+pyN`xY>acgU z^d7h0#~>1V)Q5g-;b|wk;XlH{gTP)LFj?+LT6C3kzQ{)gFrzc2B|T2R@~`u->9N|r zd(nB{+-6GETKyBW%P@iwdc*py!*ABw!#QOgx&V6>db71n*cbZ7s{@^TFKr_f@lkK= z4GR{ON<3b4mpdIL-MSyGQ-L*a>=BCR9OB1R_v8vQ1IB^t4d4GN{5}@}650_~TmK_s z5e~xiD~Qs^9uKVq1s(oA`>`hw++3%$-QO5Bs@Gmo^kMJphjggaaI<|@LV9Q`Mf1jq z(B?s%iJ46KjlPK03uR+_i2BU1%&$4Xvw=CoB1F7RhQck^%i)qqGEu|h3q2;=$S_kj zpowi_CiEhrVRJy)z>M$k47-cdly!*91w%`jE(^sS*rd~Rg>VS$XtYD7OUrii70dT%Tm~ zAfBPSMk%^u;aDC8lnX!1969AB=IefqxXvcpGqH)h#`m(T%&NxTt9)UCUl+Z9xb zAxLz%1h>Ns@~+z-c+A}gMid~J~KA`FL=+>zE2bYH`XUt(Jldl9@Sf7>HsRWbxt zh}Z}2VHGL}E%1igKGp5#&3=bw+Vz|=>KC?6NZp=#9T~R~NW1@uzFRl-ZL^MWfdg+F zHHMV7lJ~Ia%G937gyQIlCDS7B58Rt7T1|!1VEt{TlzS2Zu~WP8=Yf49)>Mx zhI}t^VKvmCP1iI95@DctN=9Xp5-;84+o$h*`?GORx^)l9PCgxPGF{kp);&HFr%n|o zKa|sVEuG{FfAYO%e9F;?~ol~K6Pw$ZJUxINdSVV5Z90SSVfj>=!h0# zH7H|>T#j}_ZkacUigmgvt&|8>&uB$4EBRfD<{IsnJ~-0{fkGt=MB<4@+-Py4GI;v& zMimL1zZ^vEbW3EmixA6*){#t$fQI<;WRkithO=BrzQZ^T&clk@0|oT z-;==Lqm1%Ut%O7)c;R^Z%6i;^3hh}%R(1=~Vn-UVQplI`CUlcMh{RFLesHYP`U8E;!jw^91xHep;|f9n26#39?o z5^q|%+Aa$QSt%ob^`b`$(hKL%Lr?p62;^u7!z5mY+K&`8wR^8y)S6z#BUL)@YU9x2 z1uqg-t82Btx@7AbuSC;*W1cf?Kaz6aftUY9SQi!`z3Yw)=ANNqB}Zm9506sXUe`ac z?WDi)L@>+EX~V{|!SZlEyD+z6eA^}ke|CUH!)A@8|EO2T9^u(r?d(1r0Y5Tzam*;~ zCg<b$2HcPLD&UWxMu}SxVar+1a`v5s{ok>^)yT5V0p2zFzMTx)l3$s(jBHUdFZl0r zilIC{g1ZqQ|E0E7M%5z8plvXpe5^E%-#ldCsr2TO-URUV= zT-mlt%W`6EdgV;Vzg=SnJxk-x2_l(c1)Zh9PB;5J#?FT`V@opA&6#1$+lT)Z)En#+ zsc$E-ouIDK`FY6vWwyqb>pHSY?OR4Z2PSC*)hH-UhS7?*y1W_Mm`xd`{yE*(K2 z%-d9?4@~U32yd{DC{HYm_;9~2nEy}L8kipJT)29DO1Q@wC6_w`WuhL{#=cGdwQ(?< z?(F3qx{0BNDP)6fL3a~bEBv-$*5>}1&qh7$1a?_hj!@O?+c7`;<0K zO6@mjw!!Fxewd`7$}YVxRV07TsW$Z#?&vyTJ&g8uH(?^6R>tM7{P7gMQY3i}tH-s{ z=6;6SYT|S=b2^>4p*0-IHrvS6#Z5Rzn61$^97w5#FE91N{>+;!b6ap zcxW|82yTFV24~YZc5$Lp%Ozn_1L8D^Z+*f9Pt-deJu*o|%=HMw?T(fByev&Q64>~w z@b}B?)=`e9X_&zIixzARb!yF(^w!vycR#xq26L2ZV1Wnw_%XeVknx1l@tI3abRb$g zj?*^vcu;-$ijQYHLG**8XH<&)w9B2Wf?^Q%NoA*{?#R+J{`)auL2EeJsulW&5GYmaktG?ntpoxOaxQA;`? zr(q)OIkxR0d^^i+kGBX4^Wjv3@bs^`d~qI@h^DUUz+2^Tp^<36Ch7O^`Ao}-=DSRH zO<6c6yjDwnERz?hvl@k%9oAPjlsd{R>RI}osTjDl0^w9Md_B}i0AD)@g(<&g|M2cN zHcjgvQ9E-*CxnG9XGp@WcT(%cTGphDlv56GjkxHA?iqnHHR@`H<`fN^Up}t%Tbgr5 z(kfhPHvTr`^p=%D#1Ul4Tt+mx>X>q`*Ax?XucXJ+t{go8O@vO5Q--ODX_WTtk1$&< z+AtN2v56h(kE>|lPHD_;Hc)K0ykts&&kqIH*z+IV9X$S#4XrC|=g-y>F!0s0r#u{a znhK&D6qCchajWlPa`mtapDLNJQl2~TF2mlge~$b)V|NTG%;OW_`Hd}E(e{0|H!v|? z)UF6nR(hqsyFp0fj;dpmT?h`qu4f;7%y#8w5;sZ1epe7s`IJrTjf(0K@?eLhqKNuA z4wz;nXQ&$6pN)M2VPX2g!$E9N?Z>4BKvSDa+sQ!>OuPPl0I=1WQzfazH>=;m`y}%y z$Huy@Y6UyCHjy&iH~Nu?H8KYjy>H9mKp~V#BW{4C5onx)(4)SfK#34Tjm{3M2*PKJ zM%2uzWEUtxDfF`Bj-^_`#d92gLO@JwhO~^aR|EiXSCVG{P9EP0==PdLAs4M`w|XpV@{xW{p11;_rU_o>HICmlgzIeDj-IwW5~5(E!u ziK*MWZL@bCarwR%fSZRt{=g#Z=j14EcjPC=Ypm5%4PY%%d;OFQgIqtL$(^W4qmTcB zjBTnXpLp?sUnTnq3o zcOof~2lUCK7cV-NsHSM;VpE=VYs?jyQz3_&C{J7faN^bKiVd=?v+q8MB?Ljm-gB=J z>czJuz!+C9xu! z=59{j6gp}2#N-!pbe~?o`C7y%jFThNkSEZtcZrN5V_nnh=yd+2Ow0P z8F?FVV-6dF}aqgVOKlV9~I*!xD=Xbi9CdKs|fI~_lBmKaI4?Zyq zqTeb1_Z)RlA38*kow&ie2K?#`&^L=S;Kw3Z!4MycQA6D~eE25j) zq}hDIr+drgfCIz>G}_=1Rfx{nsCd|sddrZ)O&~JSuG6AN%d^9fH9{NVQ5t|f0n?TH z2L%j23^Ay=pg>Cc9m5&I>$TaAv zeY%GWsk!F7RX81>PEk8?V}>3W>jfz3ik*%i4xRzZgGB;EOLo}75(7&|@$Q>n+Wi(9 z;TBNBHCcP>4wh>)Qh(8Hn%3sx%Vi`sy=B<|>_|GQy%VS{vMVoF(v0IW8$U7;mKRM_ zWVf>@e)<`=42l!(h?3=KsQ^pV1em64MYzt%M}v)GGnSF%XyCXa^D2QXJr2#ZDj$~snS!P#rc zQMO~Cokhb=&iP6xnvAB6xw9}}B01pilySJ5deN4=I2SQSMcSx~KiqapA)rNsTY@(| z+2tc%^SS)P|I;r;)&&5%h`Z6ANfC^qC0 zZ>Nd{!~2{awzm?YCV!1sF2Uc{mgkl(C{ey)Wue<#vU%^E7tp00q3+YS=4lGe15G`ci==lh@>qo z1nq5+oxd3Ub!q=umq#{1rvA6#I`(1{$xK1UXl!GM+9EhlGzUb}GgY;(l?eL}x~tLK zk0eY76Papxs!qPjTZ0^HGAI1(6;hG4t^peZa{??%xgYmcuKl|a0lL6JS^XJhMWODA zLW^PvZJN4$C2hT9NFvVH(mn{o!lyKI5i|h5O_rdub50?4+IO1WH0oC2^gbI$1Ld-Z zkJzOg$I-4QpKXAL7sF+Ph$r!Zdj#Eu_%j+1{?43px%}$Vk&#RuYzFkd&5_yw)9eSp z7cU^D9kR1qv$L*k(PN3H=eYvTHyiiE_Yj%O=vyvF$Bg0{jNJr*v{TaX=1(;>Bzy96&un=bYW{NU%z_{|qDT1KPL(zH9VQcL*WvCkr!Qnf{A ziZ(Z{mxOo!K25>osmlsAw$cRu-iQeoKVq@XwfDr-f!qlL7fFRWuypXa;|+-4ad2sc z3Rrn+bo3WoUSRB43REY>JT~BHpXsp8)1`L+u2YQq;zU%Z5Q(}+=KVi%^^8XLtojd+ zW(h&=Ei(A-iD^xfi=;P|w1fbQ^pAA+O>td1!_sNNDv4~jpQ^cX)?cX7j+!e}pLxEh z^_|RPF}Xz^f_&Mg6VdTNISy6Pug!fq)h&c2vz-$BLbtZ4Xx9Yy80x=HQ`MjC=v^&E z1$_Wyd1vDt%rq_VPMmO+t1h(ASguXwXSNn;D4-gKiWLDGQdC*6uRlHBN%c0z5WmoD z$R}|oX5FB6>Lp8iO9UlFKmYEKW;;XiI-=(}Fu%cfiT6_lin>saRJM|u!kd<*^nD&l z?^t;4KfSk5b5h0~RI!UGal?*NZ!}T1J%Ime$!}Dwb|qg##IY3nk;p@YysZ1xiH4@8 zDcvnmho;8!M}6Mz<)7@Hx|W-ne*i`O+${Q^3i!dCsQx#aAFBbgS zc=k$ad?H_~YiDA!W6cz`6Vs3QL9rM+S~$5iNsH+<-z%c*jhUCdrQswIWUe(;W8P%- z0FYbS;?p`B6z{Ri7}pRP=q*kGQ@rgwh?xDr&V5Dpczs` zgt!OPd8CKr%LZ2YY3i0u%nk0BJ$fy=_xp@Nk*@bC87O$Z%~4{~PrOm7Z6VO$9#_t)Vq(FyYA z_wl(s0>81CPFZpw|4&3g0q?8aC)PSP`N!lGrT9I*1xMqpwmSk{=3kt|>5DBJP`Ub+ z_O_BC;XWEySyJ{_z2cmHbR&DyEd0T+P=!ZMQEi-Q#bSpJPo9s6cphuc+O+dlY9Dj( zr}HxO(#c*x(+%`AlzZuT7pFRSEr~YTPtGa7@`b!{&Oh%^{og-ADcUa-Jvm{ZTx7`2>2`s@c|%^5ec5)>Cs}vLNtW^AL{hhLpp^W z*@30~;V<&dlLE|4nBIliakAy>YpT^^b`yVtTb=|ARNlLqzyP4U`~PPS)a!r(VLNsw z@{6Ct+{4AKyKX8eTDkVg{UY~<(He#8Y3=@L`-#IU0mkWoFa?80#9Yv?4(05jMF7a z^!H2?M1SK?u_8)IDCnC@KwK-Mi{#ewaT0L8#jzs6JM7fcihV~&RZemUbIIMb&9s)= zAyM?Z^S~bb=kvQZ!5!T>k0-V+oOtM!ES`bYeUI$iX5m+gg3PL=xz7X#qQ=0GtqYm_ z)Y7hJW5{-K1qw!Qq-gyqs$XE}!wxXatz4;0kEQzpKJssUJ>&a@Dq)Y((g9u!Eo6kN z)z19^l2dNwD6_5|bUj=Kd+rJJJ*&N8c2IynKpUZat}3Vay8F=} zr$Bswc^jTKc)x>BqLUSP%NcrZx$z0H+8cvYm{9>dJnaFd?-2Wp zE!8;i0rentQgcbyZh8(un_kCBMQ{qb($W(@ zUWv6hIycB+?v&#SCm&3vxG~4HBXUlR{}N}H_4MzeeHUw{TONC%V6pCOw~nge9yabR znkI08Fpu$yk6aQjEzPm(Uby2NLAeh5pf7hlJ4PLx3ndPRR3>Nznz3T&WaL5mUtsak zbBJd$t7)?>r*|9Sjcak#O*1S3vRok$8}Og+i-X&3txSLKnLF>K%J5#+|m1zzY}Igv%Mx8)AyzD*9>lSyW%dG z4g4e>T?NOV8#`;S^nDep@USXSrtC#Y;GR+`BOavPA#wLeFMHP+ZkV-Kx_p<@F_HN+ zE)<_faKYoLYPAdy)C9Vzi2eOmhEm(Q_y5VLfpfaljXcY^iM*}NeR2PEV`(!?Op$jOt~0MhiiS!C59^+ zBJ2&o4)orS0yq4nt8$L>{1BI?gA zcP?7c(a+AG+z8!F^$}3|1UXH$&1q=B&e%-%5W>9aTDz<28}qE`W_6hCtZ0-ubN zUVdd+)FMapvC&T7oVLKdN?ON7z`$J7R+7kSAOhzluC`Q_wt4hDaeMx~9#6pLF+>{D zhv(#~B)g1e*+$B<8TKV{@P#?&E{*qckzM54Un;Es>sm=KE?W@1C^NyscKYP#Gc=d;Ru?FtJ^XnY`!`A@ zr+(ShOW(yJ{my+}sVOVx>)O=Ic4tT*qPA@Lua`ZgZ(h?|K%aw*FpP&*^G(Izl+6B! z%(8TYy=vx(3t45pb+E^w5T7&ib0Jvx1DQef2Lxfth9awG-QHky- zij?eC-*ev4kue`tG2(ok!0G37Q`WK;y>E^3OT0Aoi5|m4Z#E_OBGj8+@QRU4 zgGdn64Hcw|tmKOh7Y4n7Q{1|E(@#FI-K0-^49S-uVI2gC!BA@0)ngH<>Dokoa`!oy zl^L;ra*6E7bJn`*M3H>{Pn}hA#3My|i<+gqn)0qs%Do@cPe}&4dFR)I%z_%l(|(V` z_&Xi24Lnt6yjfdgFx1gEQ1B7XdG%|}ot!qayXgNMB1WTl}`TcsXg%d zv8fJaCsxn{P9(G>!?-&algkRIt8_9YJx3vf#g;F07n#P%J~~%_^48Q0qa+hHC`-#2 z+9k$J7k!PNdP%T+^EH@`rkfIvOM1K@Akhjn+GT1-h`c`w7fgF+j)>8cydr868KZsr zj)#NfF}az&cea!6CpJG#R~XC1gO4%{i!H@^Q+q^aBGRte4f!HdQo3J>+s2wp0rxKt zso%-i>x^5{Wfhe_17x>+8erd#V zuYacaIWEyv%Wq`cbl`-l6kG9C9?1(m~ft-6l$BWhiKNG7Y-}>AhfNvKWZJG zqE@{31PY;|B(m>5mywp0#p!)=&XQwXD2DsKE;UI*Rj+8%NC&tc$vd(or|=hJ2ff~4 zo{LrpeQ2~32Fl{Xw)yeFdkwsMrxXTFu_p6U#j*?{DVna5lph)igXgwy2Qi}B90?%Ki{QU+_tvuAnb+{WIZmV*A@=$N zrZ`+{JEeFO>!?#`6boLDtJ+B!7&v#!=3U9g`)Aztkj_DLk8_&~5lt6AH}-SeF4W@# zKeO-j5aM^k*V#Grdbkf@S^gEbHyNCn$pRk&Z{aNSH>PHo+<*xyF*6Gawf|s*mr;7` zaf6dwJ^puVMPtL~4AKA2-e;^bfW5@6#0UGFgLuY^QBDVBFcj z4~Q&N`3N-A9%vlTmQnX@eiy+YDJXX<_H6HO@<*egme7DR~kQ5@PDq5O8c9qUD&G3k<-{bkXh9 zJPv-P-l4_mvN&sjJ;Q>nDgg`eW@!gjJPCx~V{J-TlqlHzV&Ypsl#V9g7={StVJ;_& z(Xxu34;)TZI5vr7M_Pb=Dk>kId+~&pl*upZU6ll z^q8?n#kXsd1741M{&-}719smtLA2QGp#jH2sF%hb>-qJy|h{#rl}{30>Bm!gdnyTC~FoqS7IP@;SV4-1{SFTHH7ZFZYl78g*N*SB_{~v5%hV z2NyEbhbZV7>TR42-!`}UNH3kU-3XB9rJ>-Q4^2Ll_O)2;;joEEi0Oml-ub=5v(12U z3PbNa-7C-oJ6BOF$+G_I)6h+{>ws@|;q00|8L8%p#;XZk`P^$Vx*L=PdE zyb>1wEx_;pYFc?&Izq?S6wQ*K*V(!vj%{JEnV>7&WCH=ldCgfbK%KZW>QGX?^|wo= z(-jGicca-sY{?sl=iYEjL)7%8Z9FF(X2bjm2IRh-eWl3MI7CVPc2zLJFu(C0iu87x8%M!0ah?(SfiFu z?^$v%Yze;J;v4fbrvXR`rgPxYQa4mi2iSikc9<#N^$gV1Y0`AdA^B^ZY1R5Dnq>zm zpp29?oKeKzMeAUX(j(DAcEy~l>n05d(}hM0nwsOhLg{^agx^KhID6Q_?B4d}pihK| zf|QnJ9!Qq`TjN$~xxXby1rGMb@Qi1d{*Q=_??_-kd^3f29sp3|>^5`-h}IQdgl#6; z<*rsL{Fr3l1LaO(INO|13|p!t5K)|{m9q1`gmwdqy*qz94k8zw>uTHu_=6)4hoHNQ z)T+2J7PWlnwIFt%VCpl;oT^w zV{q{x%kZ2_GGq7tsV9f2Qo@RYVDo^q&b5KKnEZgkL`zqmcE#R}*l@sUsM}K}WxX2v zK`Twc3b7}}fq9K|N^&b5r?i%wVWX{+iE6Hd!aSdXtI=yBiquuRqP#T(wmhT-}i~tAUW|tz=|4NXO`(o%~#Px=dZ%DQMyB<3F`VR6FpC7~u=hx?>~4fVba_zqa6MVV`y%exqfHPI!lZ$(Wf)5X+D% z9|@MV*@!H@;000@(N$g(>3h@buo2TPW<`GXH0UF*OgD72#=9oCct5Xq@VD-QfHde1 zEq#*g`x^-X3Gs`sM*O7PyNn;X=MsFPHL74B8jl!8LgIve$HaVUY{ZzV}m+m&MeDCDkoD+LlLV`=Ain1F~@baS>49@cnOXEz?P1Y93XGzz5<$?kr znXUIu<1KO`Ny6(0PVw)W++k+)lZ!2&4O8U=SYgLgJR`h5;Lw=FC$;+21#?R#WIm?lJ8s@YwkpO_G<^NAeL9kvlR=KC_w5tL=WM4+54f$xp7SbG7Z68=litDkY_5n~H@?N063N+S<`K~t zc{QaId#6P*5}m~Q_I`qOF=zg3Ok!(^qs@Qtvgh4}Aej_3Up~K3LqXFgn_}1C3zbi_ z^}tV8B=3gGoa}W-2aAN0TSy&Zm(0X6gyjrh)}u*)kbic)snU$8ox+J{y|e@_`Qr~K z3^+b$ME=Yy`6<*iJ>$``E+9;JV9i!n;=`n5*FsD@EaFDjmWt@zczmYgbbRA`KfM`G zbf$goY#94hx&Vu<+JofA%()L>XUnr1Xo6)ff<8yG??`AzelVa6yCBKGR^{LdnJWVM zgoZ2OQ+^<q}@8w58aoz1(6#DvcAD+2pGHv`wo1T9HGF;#FyGLbEf>$?k9QLry z>O-L_KBg7cSe2X)8V$ZbX$@DZ@-^LGDrU-+A!GOduPG>Dy{|d`X*_}JAY8xapXdRU zSasvqDKBP_6rN2vFiu8#m)u`B3T&W&?ZWlZA1xhZSF zJ=kPl3U~t1>WDOybK%0`CyOlaH{*Xy4a0f&>^LldCW%?KHQ$Gp=TrEgs>094ORRXl zmQ@Rk(S-e@c-b_AAL9ZD0n7IVx6_4b`z0W}B?PN>In}=8g^79OuG3gC$I=0Ti_cfH z7h=8MCrMBWSpe*EGMUvoSCoE;FsviED)K8*#2X4;GAmV(NS(-l}VO2H*D zaBY4ez$Vt}$D7}q1hcuJZ8Fc|7N*}V%q3avpmP34nq`8)cGO8s<};_@f>!KqN0K?5 zJi2mZUpEcWp!g%p_{L|Dea$vK&ua^HoB}Y`W={p|D?KnCNO6LESiylsN=t7D9W9J< zStsOv$R7J&>+enPwQm7@etPMXrB)CJlQd@|M56h*w(O&F zqioX;Vdg5nw036F8GqqxIc7%F;|F8tv67qo|BJOg*e>m6hRix8`j$TftElejRvBC9 zhi$@&jw&~Lu0W-5wwqe7=`jKQKa-u*=#o1*9*{qJm&CV*4fUS3S9hD zms-h3_7;~s$<_xDxt94otT+3^j8dY;!6*xII6NUQttiEXW`}+l0xVam=1%7*L)T>~tBf_k@E-@n^-=A27ICGCcN z1D)bxFHf&Pr$#dzwwsDhIr`hl^`)ZWSM0FNfB>7wq)E}IRo}Ap%fju8Cyx-O)5Dbm zE3$CrN3dPIf#rujL#`#keTd43HfKZb8{aazE7uM%QrJQGws2BK{3+b?O8;OkaN27h zzjD0IGf9I^SjiNdDyLCOZsXi4jM4=gtWMcfXQL7o>n?xo5crdb7dO1?Dhj8pAp=m( zH=Q#*H;R+g18TctM{vqeRY=hwoHZlp_w=B)7hA}dGaYA^bL<$4th97zXhX}LoSVSH zUIbFWqk5qb9x>6q(}{in{JmFU1c_F^me9cqIsUAH_8e|O`1Pg#hMCkpT0DP-R6?qW zW4pNfvU-X^o5i)zRv;|s@y&M!frrim^C3>=f%in*DewUZ88&pcmA<8%Gs-x&sz!>Lkp1Ih1O?`~ac8%w`_6 zot-~3x25K_8)Z|%cfHA=H@tdK!X75@MacX5&Rm`yvJkfdk)O^TzNkOwa43iDi^lC;|_8WPd>2xX!zK8eNb*yzh?>%b%h)^>|QAI_GQL6UpoA*yho;=^&d+s@(<7QTw z<1Jf998zQk2MaAV}Une_#rR=A6L6GHY?a zHtTMSK&B&=-usyq+uhwWYvtbUbyU*Y1~9{2y$f;cw36IfM#7THr`W-=V|D$Qc?rp; zZ`A#Gfz*!nv5wfQ{-l=%=%&~r?h=UEC&tHTwh!+$AV+FG5ih)Hj2a-48hfYw(8zGT z5G|-qn39y%0+Qsx{SI#zqo961F-| zVbxS1RK`p}V~1iX zpnuxpBcIcc_bd%DZ5sVGg>u?0+_AjO z(&Qve(t}G#@QIp-;0aM?0XIqNx2vebmG*?duEgDjP_)0JL$zoHg=FoLgJ_z2DT*AVH74Q8CPPCJ!19 zya@W9)z#vNO+|jhm{Wc9ycGLLgfFGE;ZSRv2%)pP7g7gU7!T>CxS{?i%Kl!)Zka$Q zmuS4(o4}a2Nu%tA$u;OTqkc@DX4H^veE(t)4TK}bl00qHR9_^sfyPWX_9o_wAIN@e zM(`zA3;^eO_mQ&6=HWf-^?EaxPc7Ggs-%cUgW*{$MdbwoT%~qLUc=QAGtVuyE)b;g zNhE~;%4;nW{N@P}gm7%I=npoU3=lya#&bgeH5;Y|IMdG9qcH@A8r5jICL~GrAgO*f zlCHm2RAXY58$=+pNcfhCVN{rzq^3TQT*qBG=7#vGZVqw`xML%E$^KaAc{mANGq5t0 zaD7A%w5Pu;(0Aj*!O?#St7zAf@^DAaFXbXiU$taa9AoE}cH4@VuzA5A+!U0d67Dpt zglb$wgcK0~iil*Z3=d(-a?N0p_00V^D*in(Zh0CD{W+d|#)jHOU-;6Mnc243yXOB^ z3-J_%A|KaqlbO^kl}RZ$I9wTYUf=4sbKFjEtiNW+M_ea5DVp{yHmfj|`7;PrG*Iaj zc{|L2#BSrsh_<1KHx%>4K5PEHSwhZ#p8C+)GT=Q2D9Hwa@O}F@ccBx|1<}6t*(vG_woC=3- zztymOg@I?)7I5f!h`*ZOX{ZpWTSdR6GLUxFPLvlfr?mo>W|)Q8<~I(EdCQxg#AiBY zJ4)q^!?K0PQEW16#}3$GDU7uc{gNZTeHYO__tt#2>kEhbX{z47*=+-kdvz5!p*vo` zm|Wj(QqM=4AiNgfnHMNA(m2rynf&yZkCd?__0fO5WnT_m-JOiWSJ}Qh|JVxmDim-C z2*3AqY(wRuY6%|;lm_ck8&t9w#pzif&~S28I>>j1euYAO_;#_*<~U?%egl=)suJYN z*(Xs^S`UvmfcfPb8uvbzK5cS!Fwhn6EX7@`1ae`!OFOY=5V@DVCe}(-KC?&Pv_FqM zj6DEF;cJ3&q`Do=_rG3+d~5S1)ybf%y1ZA=Q``3hPOInK;f?1K{czSBc|VsLxtgc0 zQiIW*qnMzAAjv_IT)EUtz{ZK)(SkRR`&BB!5#S+ zh@6$SN!y^vbZMgg0cJj}8wZ!UDy0{)diqNyNHw`vJ)d>#{<=|GYdjfg(t5Uk>NuRT z*@j0oYQsW(xw;OTt_(+bnp_Klacq1^GQew_#kie8y$9fG?J7MEvnIjyNLfzOJ?#FE zf&%eVa-rW~G1o5tk(K11MMcY;+EhAbkmR3NVgCSAX$kf4KKxC#0MkctJbrR=ELe4x zW>1yP7PxSs54Le+ZWIv37M?FBa2hhp$qTl${0y8BSg*1h{Tlv`*WK%Tjpf1IUC|QN zg|+s?So5=d$M4zjy^TG4FHsuIZ8@`+T+!P+X-^5uKPR_liz}*ltDJTn)6prp$$ywS zYl@CWw2$D@hUIbKU+73K_;>P#=OT4w;%wlQ78`7|TJ)Ap{Fl%D37bCBw$R3{gg_R< z>Hik9{0jxnHQjKDo>Hu@-CkxX4g>YbhoCklWR96*jRCbB28fc0*^XZZn}60(=II*~ z%<3J($1^8uc4?x-Qk`atCDa^J&5qY)eb-92hK46co!a=F;O+W9z|oKSoousXbw@OL zZs{SKlq#?Oz`;+#NQm_3}{T3-`UhW@Y<7-FYC)SY+WS!amNsqU?WU8gs?Tkh`8dPqeHzlcIFOCe>Z@f-k#ET-S6=QF z#}2jX0(#bPXUpP2%jAq@SH`tdo~wRGqic0YRB0IRtYKL$`o6WjT4OujZPsq%cKQ#H zLRBVP#$Dur7qeDu5&2iB*;bzRp__*^19O3$u;l#4-l*SH@c+t*(-k)_T#C%^wAe4L zm8QcQ+d&<@nigL25&|7t8||PgdFPQva7WbQ;K^&7_y_^NpDkg22IA3%6)E9PkcQI- zTvuUStwWskOusC#XY*0dWes45|DEnka-_SW1S%mq2Tv-F@i^bz3`BLdN@ii}Y8ET( zw=GlXwlx3vxu&UhD$RIWMWXvGto4X=29=}7ib^+2A8tYZoYzAklTVeD(ziGYGdFkr zDkgjc4>6B8n4a@RG-}&T_BFrFxH2da)!lSmNT&4$=4ofwwpHmxC8jvzWjPsoCGXu1 z;(1-8a(YCzCfO8sa!ZHkJO=^@yy=~;31U}Ks_s%KC}bWO@m6)6L1UcueFW|di_)cV zIE3^pL^M;9Vx`x>TNcIRwSpL4MD4floUVjxQ#A?gv`s*#4C5~yU4j#|Lvk~A*o`BY zVWmdgZ3=iwovV&bIN8BKl&7v8dEgfxHsQl20_#do3biz{6pH_&0C*E;ZYf3!=`)QV)&>#C`VG z2^u$DoYy18)Ii_9tgX`@sezhW3~w_DL{doNPY4Fw?|DmL3C$APM`EvBf)S}PxyQuoeUIzWwU5P*ES?Rt_TPKW zt1ff|a!3Iziwjur%4Bm;i+Wv^p(`H-$>-T6#f@N1;PAGu(7^OOyI3?1Lm0c=YV)% zlA3iRrR4?SGtvywPkSUlPfGyeBHFj918|n;>Cn2sy#S`b>1pE361UtEA!`Y}Sb|qE zbuo1cxrsGlExAj zdwq;yxFmNgc+(D?wp-$A`9vvE4LpopCae+?Y&y^(R!~P9y&*~bFBW7)^O)-3NGX%6 zm~TDG(mqe*EJJ}%qn}dY3L2Hp>jLF95STAkae-W~a?83oyLX?tLIPdd@-q03b*HSC zFO^{3E7FfVo3Z_GkVY|W#7=}n8fwpCEFSwvxwmCQ%X~=0CtSsq_+Vy;XsCF$;M;44iv7Rpz@p~l zhDfV(0YQ03@W8o{!}J+4HO%(aCLOLoCC|ukT*E^6lQpQ)q)Pslu5j@vwWHYX(Hn&ze#<>jP*AYF+FNQlNak!G9zO5^|-&X_{;#zrw=3s&H&ozP1du zJ{Yj^#PEM(<1kP;$05r-+hlS1XNL(|4THNFi~U2G1Zr65g#Hs+U2vYGx6ltw9`2gc zQ3k~>5KoRx_;}mc(<+`68BlF)qp=0ZL-a_%XaU!xW$Wox@+bcRka;L#tZP&|vOX$} z?e+0&-t0-pKLE?4+%HHy%AjNi%UMuaFF5)LTX-Oel;0dM!w&|oC6T&?im6}T{JQc( zQ7!U%{sWY*ANP2lsjhGFJ7s*ouA3MX6j4eW2sDcK!YpHsr=In1j`L>9bStx4hY2W4 zl!@;rTJXB&Repu<8!XK-u@sR&PyU#8x2^cDnjmNeVjekwg`-BAe4v-oPqzCB+NqOR4h zoz`X{5TN0pp@`y@XXbzAeLZ&q!z;Xfr;o{5kGTZr_eb(*}E*iNK9DE)X|lhQ5s zYv}udPKvhv5=5qqBxlkNVii_8>5I%wj3^Z~0=w0vV>kr#erJ=0DEa9cyYEF!ic03T z{z%UcWdS$fy$0z5(oE%GmaC?g>;lV^>0}!ddxIRuRMEXFi7p0?z3WuywM*pzzUMUb zuM9rk6K-%bX=3r$CCLAz){yltVq%TywpHU#OQnMR7`=DzeUHhYom94T2h&d(O`G{b zogtymE9L?;o4P7yk?6tBRyzs_K@&9lpXCwRcl#@Hil4E#MeC~d$J7-%Fvs9qJmTt)*S)pX-q_It^79|yaTxUEl5;KP7I!z3W>kKL?>#3q z(LY01d3%63sHz_D`Dj)ijU%jVXM21K3Z9;Fno=}^%YiM>q0D)-wZSKs0p?Kpt=N8TN=5?3cdAXPInQmUP zkl%51L4H>&An=eL?9JCuZ?@jepwnv**u3cnt35HHC&=X1T$neE|Rc~cZmU1cist#Cn792(A`Pn&Aq(LSblE58+sruWiS zkk=z|h7Y-h8M577@*FPZE67Lxbs;cJt6prscOced~va^=n|ca!KYl~hxd(u9k9 zJ3V^0P?l(O%vBVwr1*o;12TJBoLV4eRCZ>FA}u-<&CzXsWp?PfbTEIVOe+FOD-Nsc!iRo_VT)eNt=6r#ugjz*fZ54AY6Q+Tvq^=c_t- z#qMZ2%3k#*WDEMEK3lq}AmApWcW!0&>=o450x#_cbgFAAv!oOKRe%}7(ej}JGGD!L zT-IJSWHot&kDrjJJ=VDAsvl-ueFWuUv^#b-+0>%ThUZX7cWc=Y-rM79d(`5o%@DUl zHPjxmfB%L)g+7enh%VRZlMSI;cOckLpc_z2^K2)OxcZy4uLeKyvY@$FZO#fom(%<{ zT?Bap5017;uvjR99!R&cgzT46;Gujf8Xaf2lu?^o%(G2_9@2zJSV^Rdq6tl4@#or%ET?lNI{ zOSsfOE84l?E+rEE?=k%Ey#Q)zIiZgUo)N~(fp-REmgxTfoXVOO5k~7tt(QSEIuTQ= zwg|hi7Lo;vig}($vDxMdm*}`(r3IX(C4(ghRbaS>O6hWUZK_h$Z1mDFU;F4Ya&Q~Y zYNYM7$0$rO&zmJx^TC($yHxSfg6>T-#jJGt;CRr1osy9>sr>P z@H;FDtQ%FP`L9X6{-RL-Q=!0ZTF05JRBp1*OtNQ}H|Vh6!$KNUP7LloU;lOe**|dS ztV6dKlRX24{J7Igsx;VcHbCd+nJFXiV156+uL zA-&5K9oWk)fBAQBhAuYV^dBACg`$_&dGRrf@}Fkf&Tz<#le$e+C2oQxyY9!WQeO}bIh_ljzoX) zI(;sSxm3D@4uUJWek%S0{QO!JV1w6Q$n=`L9fH`bp>q~|_2sw)U5l2sM<^L9!`9Yc zEkUT+nug!d(o^mF8@bYhW$i*M4*KQH?#B2|VdEj09&Xd|v_?UPfk_`L-e0-5FNc=X+ZKk=gkEV-i|= z_9!708uh*=CSC?emHmo1x;BYD=; zLblgSN9l{hovnx74-|!SinZ)&>ci55=9!e5hH~p^-Q%9K@2?&FD_kc?K`L^57>FKJ zyM$T13MJS+^q^ivr{>!lv@*nMyHGfE7V0zQU_;8WNL-HoWmz`Y%IG`t&MpA`p(C>G z6d5@1Zj4-1J0i4vHnPdE9lI@zn_Orw&gxco#39tdI}_Ng}cBq&Ve6*o2vU6P!pKN`iNI!qG@Yg6t$L|yi}))>#Sy{W__E(&gdqb zCty8A(F9Z^dlQ0jx%%`2-lhEe5l%u5yyj_hq48&=2CI6Jgn4Hr% zn*l;7ZdvJzJ@d~3O>4twK6{vuqETU{TUJ`_bXi{c9~9nc?b<#_8GIwDMxLx)^GCMQy=fJG z37Dvpkhebs&Ut3>%X*frkX54?t3u%ACQl<46bhu)%Po*=?c+$Epjb_*=x@{jZ?M@x|i`A|IEXl^qjvv2<}K{B7t>2OFAns}wGZ&HsxHyo{b zf>nu4=U#-aK_NvXi1ZQmLgwD=Q)bN`1A$)e0)-oYqg3;$+M zzU7nT85HMQ0#21YRT&r(CpnbqaUi)QY{=TN-}Gu={u%O`C#!5n2v0B0Qu*dN^H<_z zNpV4I#okyTGL0oW#0PG#e{0+5OLw^5m5P5|1f@;`!z_G6O9J#vL!RV$Y)b3TiASfu z>FCN2x+E*V?`(HbnvFtO{&h-NQe~6O250Yd3J7|_vP~&2qhMgL5nI<>Q{C!sUL$>g=_RXrs-t_jDhLi1d;;cUcTzY z)}r&+lA8x=EJq3wsF!MW{4&FqkK7Umfo7U71gyFASqZ!Oq5wH+OHj(*2Qi`)<6QQ- zrXYtpLu%t(Nw7u}x3X3U-%(f@pUB%he6N+Hhj)`8;d`Tp_=ySd=+QGmA~OLlz!O3O z!pADjJnAj}?qb|cyu$#QVZiDR$%j%#txgDu2}f{pmG31xhR_r3eWW{lTda{wbdx>G z;`1rA+5Gi=0Pq`f+Kz_6!403b>{=&cInjHmPwb`xD3Fpl^}Lky^1i%h;i)#wMZj80 zRw00C`#@w)M)XdEz=nWsM_!Q7=f7N?YyhjETb`TGsOYmBimVt)cVk9j#mZ@FZ!hxz z?6~)_!Za-~T>^VQbMMu_;SmwSDW}X$?uEgyAA_v!M^d7-lIQ)jJmdPg#atc>Z!q)` z1bzYuuxYBKk95QXcJQUgz-nhF%wrF$rkxL4OFZ>ak1j0;MTjClM-CCJI^cy=4!XU_LyWqqTxB`|p|uRbmwJbLJQ|9C>< z#rqVAx5NaG0AyqUgwe4x3z)#l&-Pv$UF?jPnU2CUGmJ_-uCowSV;Db`Mu$=ULZX>e zm0U#k^<*wGlaJM5K>3Lk18CJ9QU1zXW>R`-s5~DbYZFnVN0IT_6I`uyz*8q+f}Nxo z-?ezF(Kss+2F24F;4>7)7WE`ge~lcAZb3XN$4YR%2NdlUwOtg6Pb*Pg8xPPAHjn+$ zSx~#mgB@z!U0BPd+ZM&7seiZkpPflIQLy!S^Xz&_@BH@54(#`mWmiVv_PHok$^r7s zU8=fMEQx%JP1h9aY5%C*c#*W6?bRYnGaj0?ceb_?4rJLc-b(8mb~|}-ki{JJ2peEj znpe-8&7etjIwZ|huffgYfr-d}?JSIb4w;m*L8;)}AvJqcwdyNdM1i{s`m?jvXOm2S z_$U2xZ(Ey@hI95{{+21l05o*%GelV;|0Nb?RjFmR_A#EnL*<~kP02@~$Ny&zX&#V~ zB9kv%;t2hlueK-0m6sPq$|(FX7i`kdlrQ~(S4=76ue!87BiHBe+rYD#VMm)UDQ>9h z%Cq^xdDfBAHI+yXvO#KIU}_F)qOO9*yEF*LXR&rr9jn|_H9XHiAphkD^l~&Dn(U~2 z@LFnIq!Vi<(LZ;$*~^#4wm81Rw#0_aFs~!eEUKgc3058ZQn?DuncHmL!!ek9&(w%A z6(6i{i+yJ#-71)Ed4IE})Hm3y9Z;13{H`6gwE z427HY-0S6pjFjFg*;NZHPtZUD=R&1OJ*j;?{pLRY1NMHN;?4--2_Bjg07shJGPg5I zofOcbrOH75&As}nnjjRI!>hpdr3@lGI55q&E7?b)qGR?Zr=UKM%{2@1g(;qSs!Q*+ zA_~`8_Y0=5QOINf0*h&8LmE>psJ`|x3v9l_T-RSq&;7(xoE$cn#*`&}!@(@tpV7ZY zFdKLk8E5g^2irl>XNoLiR&bS_+o}^C75JGEh0T+X{zK7_Ym?@wY$t05KNzdVe&3ii zaeckJAGXN!7C zIaIFGC0Ws%FCtk1=ONE|99}2~77ST68yy(x%7!D&O5oi}VNa(R*X~;@I8LonEw!zo zrER;u8c&}8m$j@U{{)WA&2X0Lae!m;#4v>y&6s!i(^JRYPHXFq`W7ntCIe&kv*z#V z(}@o#Al)@$1D~W`(FTn~au{H{5kof|cWG59{NMx3%8c@A<3`8T*;yE#G!3k(UhEv} zQTe1OeXAW%j3%9)c=Pgly?0GJ8YAiWwLI0lS*{DEy|}UGCwkzd_gjB!?k7lo*mM1F zP$W;6%4ckU-yao|qmUhi4yPV@&Yu(bDM)uSPSzEFlw}AEg=k=VyxA4G>rqf$#c0hsjwqv)l9PiOMXTYx%kQ(8TNE?U@2O4X+y}obcM_ z5(WrU!E1AATv^IO$`{ZvybTTA*Uxf=`45xZx<6vO+gobk>9D`Qof>K{b*P=`Y}r)d zlp;#YBAustX@uQ)!woucOj_TWdvSDK!|}Z4I&1MLOsl1P`G`7U zNxbmo9KTqsRNEsjfSjK%ya+rRPXQV1Z@oiFvHZnu@MV`xfm-jYt~b2KL--q#Gg`jZ z6};-~R4f_F-W&W9dP0*wzgcvoUSA6XL1yJ#+`BfU=(kZKeXe+@_Cn5Z2O!z z*gALy9o$Uhj@ z$z4~NDb(kNz039>vQDwrP%FYH6z@(gl46Kb5iXr7TErxvPXiX&IE79*TQz!aJ$kK| zXfJ5HV@Psp?~yr*5q>RoyY~{PsAde~E79$5;~6DmrX?eLXY{CSj7lbGfNu3;NRp?1 zDc)cMB7~Q7QcUKR->r4A3vu02Y5?#NB)6wnq+K7ijMH^-XwZ0^})x7NbrRa9%ZjqjfNV0q>Ope z7XyTgN~y&pIqDb12m)moU$7aQdbMMFim8=L?7D#jlL@h(iyt^2uD=5SAt*H>^QP9D zYycQYCwmKTYk3%vNQUfuEXYH7AtL&$j(p!-!u>7X=(GOXg`sT6QfvM&s)Z$B;wb0K zTG~uE!G8eQhehEn`7pK|GY5~gwlK*cr4a?KQZ48&Ur3m3C$qs<)^vU@!p~l0v0PmI z?}#4}jlL&(9~=9Skn7<+q_B^#J>{$6@AHMux5-=xA!PdJ1$l6!ybX9q+l(LUJ&>CmYsoaDzbuyif$uaU_p%G*EK%f= zfHaCy%&F=Q(8EaWbVB(?@knsV%G#E|9MJwMM&!swz#+C<%{CVy?&uvxUKxi$fN}^e zu(Q+d)Wr-K5&FjsG$b<)F(Qrl+oZ-;6rjEzSMr$0{6+O8Dw(6O956Gcr5-y)QsMSsIMy)TaKMkSEswz6 zID@2u=7~2T`m>pBu|5g9#Qh(DcU28!WCcYdV_sL(aCb|)-6y7<0g|+i%BY!ZYQa)RZdz8_+o+3>LyYBOEHTiP$32w3pHy+sn zDXV_JCE-8JoC{q)sR0E~K~f*k4*MKf?ZleKue7dnS0~Ny*;5(AlD|6-$4xLrwx37j zZ>BhT|5$_7gdD`RRbI(#l9GDh*m&9s^Bp@0i07Mg zgeF%m^gdHo!p^JmQ)|D1t!Nd74zYv!Kx_*6l5wWd?E{PDgH10pO5wf2+;5lj4qiWJ z6OX{L7t)iO1&s50wtOBb&Zy+o*5MVUVxl!D4X0y)6c9V|H2qvosdvVlCQ=pP(FVLQ z+5rbP4uJv1h5oa;O6v?0q^qbUxQtUfk!kg;p3iN)5I*DQ4wCOEcSC%@wg&csg{i0x z3-GsWeiO69fu{>k@q|@V5Bpq&U$NoQ1HbfyGb>tc8`9>p1@=ZiE@T-Zi{K)irZSD~ z-nkhj#u<@E#xGFdPVZ1<_AwC!arV;PoLNDR;L!NDz~U?+OAzd(L}Pc&10^>51zKIKPti!ocVy%e~#udHwx zEMv%-LJqL%ahFF?)9gY6(ZN*~sAkKnMfnx85u~!6s~+)p*rmx?*vmxAW#6~0m&zT^ z!n>sh(fhBTK4GcYa?{-uXJ{QK5Djd9{ckOewvgF3R<*M4eUlq~^ zezyur3<&)8oi;va%(Ce@^-6HEwlF+*6W`T7uXvCVw9h?}Rs+9w9#r=88>ia2+o_}Y z70+C{u{1@>Sq^`2n8u-RxmI4zXP72f9^8|^iRq!#mB@AV$>Tz&Tm<|0o4hSPS)-V9 zDNEus$%iWVD0mL37@8+Wa)c)}&nHPWxK{eeYWq~PpwIu(MqAZIit20)iSDfezcB48 zC>1DNeNluaL1s?^4pOB>7iQ{`YSFw(rVgI-pGpIF6k6`SCSrbly+ce_L~r9dw#wG0 z%&(#>pYNUgzP4+{o&<&=<^xX(&&n9+$3IUtnMwF|>*MB5d|6H3)@vu#467tnq}sao z1m|ftGg@&V>57g9n<>!kX?#V%ZA?eqzR$q=?@09ie-b@mz4@!EYC*VTzJGvJuuZsO zMtHZP;Jlb?mK@u!ZEf?+9$(fix43c)euZ+34h*RW(uupqnMA+dJlKdQ8>8COKRju~ z7DdxEQOi0t7`B1&9EoBy=KT2=o+&MSOa1-B!9G5vPtK`Lj(PiVWNe3)7?%+^ikzK8 z$FS3Re!s||OlkvI7UH2jGl)2`29KpTE5D@x;lmk^}2-MdwucsWy9qNNu$rvM0CRs?OAgAEw%j zJ>krM7TiO$;R$-#G86?()=4Tzc~pfv`Uy?17h)}~@nU@|iSw0vH+cU_+0~%2RVjK* zCD{xgv$Go!Ukj=0eY@wy`tly{XTroKV=^^D zHBuc%XqP0hH}^?V@ku1z_dll-S|$!2(Y|=WQkir7;yM>)cNyh9Tq}uG=6rq{l@b`~ zbhKJFbfNSy_O<*!fZ_I5=$_5*ATNK^E?A6c@xEUm?(-_#o{D}!c~wQ0IQq{z&Nq7J z70i~>7~PSn0s@H!gJ=mcONBP9f-gX6PxNa6Ld!0(>3)QC5R-p^Zv@5q@UzFP=k*#G zY*NHS4)Wn*q){!y2U`37_aYyuK5&IEg*E_lpw!2@-)Po@LG;ChtLZ?ZnrcTJJeWYg zz5h!Lbqug527IXo+4p|D{ai1e0AfS)!W=8|(Nfdrjih1B{IuDRv3MNuH;qn)9opL5 z28r5Zu$Na2!L)Lc=13wvy>6$(jKI@2xOuR81*Thm+~Nxt{J;jI6c!+p`-LT)X&3q< z`z(xA3j1;&$7SxMlVeOkrtTyKc&-K^(oi7h*NJ)mo{XsCK6i__Zgi`E1M0zwb|3lv zTW3z7ujO3wOBBdz-r5_IShGkM&}!T9pjmGMw#cjArVDSx<{puh*3i_(db`cP@u(T7 z_L5Qih;)8qbk?H#tv!00MkiOTP1g4XqgIP6L;w%4?K-lKfT;=Yyx?3K z3mEU;%DA7Bi_qLy;<-3v>nNtOYxCyma<-%c8(Yb@a^5&;t;PXIi{P-mW=Siq)uRzn zCB16}Jswx1)U~2GPVnH|Mb>E7*6O$GmTdHif*TO-aXRqlFK9|37X05;fztOE6Z-nPR>}TF> z#D|z|{vamUINQH)q#cVL^jeM?^ls@$g$E-1b&9K%(R0C$9OkLlg)8&y`3$-TTOS_f zcOsWXwGNpViKf8wsCw``H70EA4(ee)1nbfcCj&oxYxR^&hfqi1+|el zj=Z`{6~GofcvdpEeHf03l?)BFR z77pTYQ*YaIwcwbmv+-iuY3Qo>Jw(MEsa00C?r&f(r*O*#4=&UDIPOcM^S8&e`wK86 zqnpJF|9~_X+U|KgxD;eDMq;xIK4rNI8NHT6ZUz6IT$Q2!kw1Izjlu)9SPP$)KUr?f)=gNuidw(_! z6YV(h2=#AWWs(2xXkNabS*{r?#(~@4i=*JmEz|$|ed)dP1jDA!`?>n!@8aRwY_RA+ z1V<#(l`jt+{QoBKdbUl9AW@5J(afJ$%o45Ao~s3)-<7xf&Q$&~?uy`uK6XCk+N|2f zHZ?lVOB;DMtu>1eaSX4qs$Jzo<7AqIKOWdN?S`r9in3Sh#bsY~%*_>cTg|dd9d;_n zZ;c#VXLQh>c*r{@@Iq^&NfAi%kiuJl5pUaAYyL-bV6V552Q?wMX22$`%@r5YGqlO@_L-~tXl zN`IHl6(n^X(H4@b1A^x4=+QNHj0xb4AhV4Q@)W2pppP zKMhHaA~?=+7#7O~L)U`oZyqO@!!U)tR7SfDLMGU~go@$xIoKMp3+9w|_25V9H-!(I z2DBC=VQkzKCk1UM;Vz|_)5*rdpRlXLEng!QeVas6{U(-|Vatl^d)$n*eCt1u^3rm4 zn!&xkaQ|IyyC!SIBFyqv0k1ck_=_*Na9(Bx>Pte*h;(F;k_0pKxB4U*&08Jq%_jwl6XoTh%E`zpf-v zDqBHEuT?M7Jg)*`yRoe*znAOuPQ`nR>U7z2=sn?6c`T?o;SQg&E_s&}RlF#ZS)twO zqjHmO-?3$9pt4Y%^-b~FhoD7!7V>g3JD z#vceNh4}*!FsgYgO!;VeWpop@t6r{YHAtvRAxPrNipBtr^t+M{7#3SE6OMcqZ9Jh{ zt)>ii9mzbxIHaTG)#awn`dlf(7{Ph%B4gCUm=f zhnEjR?sf{D|7}4LP46yLufa3`eY=^d{O2glHD$e^PF@?&qkfrdScvV5%ZECK-Q~j$ zyej@qu}mcFYMW?v=~sm$@kwKw9sdDndGT^!cKY5%KJ;hEk2(w%(L8t+@d1yCNHx5Y zX~gE))%%$Fr-nJl4XFg_|27Q3UD4|35}E&fVR7B&Yp@sghh88!fVr< ztSuP)GQ_9wtBjnd8>N9~#eC#=un#j-?8c<`kYZ9Q&m=zgC}7W7ZbELjQqYU)3pnb0 zgJ)JUR@d@Cw{Rlxvhnnxac((2waW))lUnZ`=_{6VMrS{0`;J&2LQ^#Kt#Cs#6@0as zf1f<8 z>m_eE+x8lyW3y8@6ZZCQ66{j~@B|MIV*-UvuB4@>{5zS)lw4qoQ7fYU9H5Z|7uj1f zG9tiUtX+;HevW3yxpH^JMH>yURq0C9F5;h-#!Isu99vV0sj}+$)#az{HIBls-*$aO zR#FgIB@cvhyT8?U3YksXo$hu8h_?by9pZKZ{Xk-rb%yx;<4dhyB$IX%-4~9i>QbLd zQ>KeQJw9z&8oR#cBl zp(2ZQKP1GYd z3RuOJCNIaR)xa7ab3G3xFg7L!JP`zN(Gfww0BYXnk4pP#ybdWS4^oxv=3Y0JC30;2 z189kXbv7sht7n$9V9)I%(44J0pO13I1ZvegnenhVUe#B(timrF@HPqUDxtIM$ zHyPuLv04c3xKyCEAT5vUM8$u@so`N@mG{9*Zd-P(g z!C=2>vCoh96(ny99?}!92uTs`#*Phmz7X$jzkNW2K8X1y`wY#aE_6qdd?_7HsHX<` z{VwK$I_6nxgV1L)7LTN~e)1E81hwTCI&U0=^9U`%8OY!elA5YrDZSl8S0_Cg^Hm`vnX{3vFyePiF*zf6{5GLk%#0xz3iRt#nsQ6kj zp(Nc+TxU~%r(^dTthOKsr?d#wULPZC96cb=dFgn(_fR=bMTGWpZ^p4S4uu0mIj@Tuq96MQ0GDz36n zA6`+n^Tn-U!#_y66+KJc#85b)Wth<^{eG^o?144nYm=d9`iija<(8jksUC+Gwv#t; zlPv(O5P=NmlV%H9ZL>45|24;6w)3CA@JGmd4ey(C=bZXMlY)xsCEjqYe`_=vee0m3Rmx(+twAqX|82A(I0-n@6ded zQWum8gStWbQMslDhDXr19DLy}+w!{fSU!WM{FmfO?n@Zb-G_;x+f#A>m8=~2W_j+6 z%x3L~JJxv>*=N5i^(`MtS@ci-h+^Vnywqk}HH}TgbCQe4S27)wDrytv!Dbz15nFRk zXsNtek0}H2%ggFU#0*pAglVZ`4K8!U{Qw%)*r^mbfjRPEZqhUHu7?e=UOmcUxN!Sf z#>B^Te2EB1^w=3uDUdlYY59BZbckGi?OM;lCy%jJe(S$^-3|A4Th3k9+sGcu4l`$+E($aTzK_Y^!mndA&Fcck>sc7Su}9<=gx%bu3WpZitE&E!#t2-N19 z43n8v3>nD1DsKySq!2QxmUkRaUDnf*`1Q8W(T4wQ^5Ooq1s`KtQ#oVn{szV66QVLy zT*hCQaSs-Vt|K0b{;ZIp?7Ds+k{<->JBzq0Do5MhP7syl^_C4oFKLj<61wtquNLq@ z`)#}{uw02<>UhDX%O$`G4^wCvFHO1q80%fS*gT)Ww=i_p>UI>#?BY?-Y{=k~=wWd- z-j88Lzw5k3H6wQ#rdUnEGPNq319vtBy=yV}p@~`X-2Q{$w-H0k_M0E_D%)+{DUM-{ z-MPO1r*Lqy;W~Y>g*ws}Q*cm?Qpe5R&jfQar?dxA6o%vl)>Z4Dr_Z|$_5#I)%5Bm( zjfv&d9kP1zxu>IVROfZ^|2|Z(j4R)^mK(`P+1*FipXFwcH0s&xHRiQH*cI+ax=3|H zH7lv)1Pr@Gqymf7(*&Vy1))Ns%n+riF1oze&Z4nU)cwun?!YjKR}*j0+8!eR0DSfd zrvk5{^|;IM$3p1VI3xc;>ZkCYbaT&3MVIlk%7S6vyKzRHm2%z16U`vhd}rN4Hg?6{ zP}O*L8@H|0vFT@1A7>e0*l69+KFaROG)5(1vGX@DWn-5^UaawpD&#f3Y`K)#zw(*h zs8;w_rl!(DWQWqD{40TSUbhX3<9K&1U5`A(&WrK58kCYe*Bs2oQ9rzn-Atx1EpL7^ zv^iL$p5lPBa>gZ9z=yJ(`L%4;0|`@?@?E8)-DBR9xyoxLWeac~&DC_k!RywI!d?V< zEC1r3^|L_y+*}^YHwMNd!sFPfA;&MT%Y7{(63i1N@-p1{rqI|dfxyl(no{2W4iF`@4e}wP6A80`wD~cDUNXw(sJ_uO#<#1Amk{%E+ckpeca|hEd;oP8y=(vS9;)Z7d3`Hh{+_;4uj zR{I!Ehe=tuVYn}jq7LDXpLLxMyYFt>=B0X>oDKThAO0&st3=iSx86&+#C7!Q1`#Gc zmQdObML~o56$64(Uc z$xDM?*_`=6f>#>GbU;wn@A%EUjnMjrqn@6HvbESa8-KjmC)}@*FndIV-^S!l! z1vmYAxQhcyaTBk%m#Zh(jLmPt%%aadyI9wzK_H3!n?$+f1)RVAsTam4V41qU={tmS zs_Ta5qSWTgGf44lo?J`5p|W04p8KmBjt90RZy%JgP*K?$&QTB*8mcQ#32&dRMtg9M zMYCZve>l(V90pcSPh?u8&a!heaXQ(HkS7~AWj{aZq?`~V&d=u^i0UFC>t=kKT1NG{ zd-yHvd96FqH<|~mDTsHBU68nH)nGO#N%6JI=tP@`9-JgyFpTVyx0_hPP&n#wInCT8 z3#lT=)CFOyX$iRyc0uIv(+%M@=Ex`{G`cNa9?D9)7}W5PtV9{!*_UBpwBfMSBPNy( zeZ;e8t^5K;vpq~|{R$%Rp5jHCDQonR)}eSY*f%-#k&>o2`E?W&6zb9W`5YY2NS;t~2oUp=)V#*fMWsuG*7%>{ zVnH!&2k8{2ybC`A%u`hY+m1cF+?lAOw602RO#@njypnXaot~I$`E$QH*FI(Z%6E4`esf-TK<1G$ zk%i7`kuo=0rITzR>aJDZw{M=Lt@K(*U@6q& z_)d|Ql^$Xmx=N-2Eyz(H0{&6$;u?P{EyeqIgex3y1d_xa>O3)?#~@%^wjKpSPu@j8 z=YPgQ@|@%ObK(aa&$J|@?(F07Hl8kR1wVs!+KS}jn|`Qz^{33>OC5U1;d}Nn&D){o zsOX1fMz=pVY%Zi)0LyYGR;^(+_FleWbj3%v>sh{|;?|G?*}5PM*S8tj&EZpybzF*0 za^6bJ!+5U<9Z(^>)HvyFjZ7E$ZQZ9OcV3CSTwl*-84FlLi^Nd6E@85wT3d<@(|tpk zI2%aN4YNw!m09!NO#T|byhB|GQFSg_dbRBni@9=}-?*%12MOQ3AE;V3i^bt|It^!5DV@%8l&QjA)TI#IHOL>% zMl7yIJzMB(>lPg-bW9e+wao1H^wmay8x#}!Jd3_^;^a0VONdbH+PVzJRFfCbV}vQQ zTipLUG;xJC4#`HsH;fy@5>I5gkyRuEw~%6eZ5fQJ=#W9VWljdr{u9ur_H#Sf!Rx>< zH1DXeDq-&(T>7_K43AkT=J@4oM1cgjgpjP*m`SzSA7p%6h5ADm=eJW-ze}xyJ1_#i z1DEdy(+L)hvBRU*^!TE)EK9hIinMm3TKO>J;z~7CIG{a&)d^$%_K0xbl{q@p?^~h$ zD*z1~S>5%)4>x(JNEde>H)B6*|C(u3E0bXv*ckpk0%tDmM;<>lcV(+|8K>K2t7L9n zowQn1&TqrmnZ^5sPW!sbOy+Juy`BTl3VqSA+o`Tp*Fn7Iqc!h$zG*C%uD8?!osixXOWS9JC@0<I|EqHvI1ol~ zoazW`XJBmXoy0agg8G{>O18{uZ+4_NSWwZkzR0`19LJheqm>Hd{^xBxzn3-V|MZxO zOXEB)3eru#L&7JFMDEYq`tmt{wC(cu$ZC!FPDpL~VIjAP{vB^n(>*#s-=Lc}cl<`h zVoD{41WUwzd!v?37&qAY94N_7r-L_b`ox>(f7Y79 zx{k!{V*Nb^@Dq4B>XUs9-vjNv)uEQojMsh%0Rjtvnvhn-Qr9Ci#<^TrD?WTj>R>+O zmvg9BcmJ#d+z7e4MV+hC@b{b$9B=LrQVsW9UQ@fv=Bagov-so#8Mz8qy&~=rR-esa zU(LrsjMeBNIA1Z0?KWj5)<1-sUAs(&5W0Vz;$A)NtL#BMu#_tOEQP=g&Q@j&kE@Dw z(x!6_G!|a*f-J+to3yBQmVzPJHHu$y+60?{kDB0CN|X4Kk;!VQed1+M(qH4QQx^DL z#64t#7kP-{wHbXuZiJS>RiukbwAsrW=q*i4vrdZ-wq6>_YI9vNra5Jwt?F6`#}H?J zwg~8QdY8uXF(()LBTzD1zf~?W zB|)xn48n;@zdKjgv(gaIlTq0AcYnX8LKhe2zS)^&@i7N6&Z?xVm+O9{aG~~MBmA|` zrG=(57#W}{2Nsmwkkac7E`u2)tPBBcN&}efCZ?C@3v=stPtEKzT3(l4DmuBl@!VS< zJl9ToTRo(4{Xza%El9$UG(Vm&&H56>OhIhGsgW_slm50CPL%Tgg|l=RgbG4svn}q>F1ip~#X_1@=8Xpwag8G=`)go{}xkh7n;< zFma8TU}K$;tz%qqdW@oS7m*v5iDce`KEI3Tt+6qgdWoq`9`Eism$_qMR0CrzMr@{g z$$X~#nb#IBI$fk9?nJC zx~HLmW1M*&8T#Cvx@m;ro5Y>ruXWnrXj6l0v22A+cg(4t6q1_+KIGvrjzTzM ztA;ZDzY%AER5WyY93Zg=05)wCUf@kB1Bf>4=2nMmxz6*~&ra<+aRV_fLknd<)Z2;Y zEF@fS=?P`L|9_4XNjOY`qIp7Ju7$eK>U@%RKb5rn)*%^PeulU>W^9ujdihkf?^WBkW#IxMsd=AQIG(4{a9xH+{FUSgEH3X1-F}W3*|6K!E|LrR{Fv)XX zpQP(`M}|%6BuPz|{h-l@@&=1A$RvkAB~6~gH-^vYUp*j!r0TlCb;{arn+yTO5t=%- zXqUUu8&DUTBv)R~wv_PHlW)(cps?UfXq#HR&)V?_DEjr=?X_(4k-O zcMbgw`Rjx|xVe$>XgsC=pL6lk>XhdrKbU-+k;rtv;#|3V@3j4WbAn>e1{N4$_`P(b z=5tq})`k(aN8w$*I_S}-C4g^N4nO`8yuLet5=Tq2#;;yq z3ZJVTSSUQ)S$|<*;qtpRBx2?zh8_9b#wneg!L*Uj;gd=IVhn@yS!2y^s7n#oY?;u< zYHG0ogT}(p)ft<*$`IMA&*Df%t;s$pX@fIkK&hQZfVs>@ch%SS3vM|`^_yg0(5R*{ z!!2OAAkBhc#zc)PA(sj(z5cSGbEMjgx}K#7q%=m*=%D01Lh2kxlA*@Q*@|w1ndZd- zyP_RR==ncJ5Q}3-FH;_fOGXV!C+iW$!vwm-_x5zMawH_bTH(3!^ zbT%X(H^vioqX-2f8$6je=-If1&2VO-r=~2fMekK=(-CaWVVt~3i~1(^E^3HALLVPu z^R@_D|6bPlMu~^_49RWc)}y3@nj&0!Y|>BJ@25{|a*1hpFM0m#RehfZKLSusg} zx-S!ocI{;(irQB3`12!d!`T?=jt`2fD6Vl?0C(h6#QF_U4)UD1ihCnm|nHKLs#dK{ecB#wItj^$U_W5XlEGsuy(W?hjjyg3T zA^VGh-<`bCEcom$I-Fu=+jyM9)rw?NQId0Z54V6b{SBVj7>V1|-!IIWsMHEXW2y=$ z(Ts#@+l*kgqGpg95;+1bjlxeZt16`y-$17hovtF}A`}EZ%7J z6YCUjtmL&>){x#mqT!TWKK3|mwzrI~rJQ3c&?nw$Zsz#GsX+b9W^2PTunQg!gHk0< zdQGgNrd}*@pUqd713cHuZO#mgV~>;79Srn6Rs>H4F@xnJ z!4yli8rotBt#T@7W3%-s1ltiayR7$lM2Z385?)#=M$3E*lCNarJ+r?|oq@`4ppts| zBGnZ5J1Hvqq^G(O?~lFFgr&>?Q^O~4X+69vpS=E7H|u-nr0^lk>c-Tou_ur#N6VZq zn@MLt1#-);{_AY?Pn8OR+IqRUH_4MbwOc5FUwBitCg$Z)3)ZOI*}XZ7Pi3~Qx9jmN z{tt5Z_??I1>=0loBIeX)68U7bXsPh_Pt2;eLY)h4WJb^-=|xSM#1PdZN8;yi$q`~g ziW;K6{$He%1Qc`CFfE3;)a#S@WFu!xUrKG^E@YZ%uNdnoGowfuyvDtJ3@)4g2AQ)1~KFoUrJm3F0F9s$XMLdmYv2rW)uP zeNF5Q$isf6RNgkQWQeNT?TzvXA!%xdL5rP}iTuA!@3-)F(~nD4wBM!e?xZX+9_Z)=Vt*ZzsyCSyjYH?EKuaZ`Am6OLZaJZ2A0Wz+ryjy-{{ zvVM3khfZ8DgA2{kKYU2cqZRkz0mZ|IlFC|Nh}2VcAKWV`AHVZ%QNi?On|3bNdu{qc z;ohWr|4u23(RwQ8C1HUJlr{ab$ukb6?6;K+n%09ce42agHdy4sM+qZ}q1EUfm^0%U zN_zI{ACbyv61O^)RgvK72N_=5mX2eG+2l!Y#ejVCte-fRw~=qwjQ zg*zXoq&tlnZ_Nrx{#TZ}>J=;zuemS`PKXC3Q2s!L@<6gd<6E-Ffe+>AaHJ~TR1?|% zi0lXD?aawh{g2d1G72-X5<-_GK$4`FS{7yQq)?5nRGg75={cv=u&uCg&X7=1tqJIE z6up1|8C?^f%iaDDR*#zQ2tXR5E9Y1aG+()*8u%KbGWl>GPcuBRn+E#vhCImy?%J zOx`DHCb4T0u~o*X4A1e2uH-25f3B#cAE(6MgMi^t8P8ZqrEz-Z3>5;lE-&I5ucPMp zp1F*<+6=OAcXL?~t0Ib=p5B25bP~<3eyO;}5w`6)H%=c-m;@D?AL>fX6%Lv}?u!@^ z5<^YrXw!v_ryhZgT5E!$UaCbs$8hOw95ug2A)(_NYBd~zw$}l%;8QZv@7w)%d818_ zXN?qk%G1sQ%OrAfp?OXyeG*fU(Lc~n8NW>>Bl~q;RIs|HVMc$YqoOepW=m8hhIpJa!YKX_Bn8OR!dE49t9BdS& z2A6RL+BErjY@&nE8<~EHpaLDpXt@8%qH~z3c{QD@OleS9V!e!h;o=M2B2)NT`Tr=SysSROScv*XneTAfYyl>= z&;SVhWY23{58}RN&07&u3&Cr$E^6gZ8Bf~!_z@~pJB8^Y=eGISOz$G?G61Xmlk9^Y zt2#%$pGCAHF>EG=i~9*k>@NUl_w1*`&ZF$68yM@M1DY2V5aQe#;xqizL!eR(d$Nv0 zX$VB9@i2p0R5hd$$mt#YGrGRNkmR~u6lDq*2)gF1kQ>Ba@$sKI_3T}CX*}$_uc(!5 z;Exa4;L~}Yw4m_qWo3{Eg!T~}9Nw@Xx7y!?Ey2mIC+|hv2h=q$6*M}q_x|OaQumnr*lBemjvl00S$|)b?Y3YLU9!Bz&yUd^gr^C{XmkL$` zB75klYuguJ=3NA?gfj^~!RsU-n?T56_q|&|^OkaV%t*q{-^|ZOGQ4Z&(iQg6+qT&# zwfe`XlqU%qleeR0z9nlQkNM4mkSAUV&pUF@H^zT*c{fBPHvJmnjiyWQP5PHDw<{BUzT^&g|nFAaTj&cFRSY5;OT_Dt-wB|a`LXi6#gBSFuiuuIy+BI zpy&KEtF>r1shM18v352Fmpc5^e5(}4X|vIG<=<}VICA=^PZx3UhV>ZM%wJzo(Yn_;9Z$&9gH6W0^1b++;PU7p@VP#r;dDF!n?SZZK9XjTOElv&6d$bW;Yl4 zk7|qec@$UR1t$lt$|e;EF7CRAKreK$Ko5KIw3WD((ymY|A~M$=TJZ5eG`tRQyyI(Y z6CjDBXS>Xj@gwAk8buav=&f)-0ojXcf+$i=umD`HR&dw+cN;g^!eC<`xe=qU;qU8Q z8)R^3dzgi0;g)cXIq|=c>EKM1dJJbF$zZXAev(p{paYM8ijGEUi z_tL^}af_V)jVfyfAJ8K3)o{`9>5~q3 zoPCxhBtR|LaiAEE(E(=?^1jmcZ-~pdNIUCmLI(uN)?5~be7vy%B?ubv*}a4;5qng` zI5f@PY}XI3^ay!sux=`sXr(I6NERmbJNPmuMIvHf&yy1t`6<`!9}z3OeEwiZyka4F zy<1Vnf8LU#fB-WCw1{aI@3s|Oaknb0l=R!7R@lupaj0qk-CsACj!e7eSv#I%kK4^= zb}r;qu-$vln~~^0ALXonups|(Uw_y_!8Y3?W5l{IL(ZCZ@y=yE!f@{#S?hiL+GuN5 zc#AQ%@%mA(81~p6=R5gpR4YR4@Ok3ycl^o^Ddq^h_IUA_ zgbklVHga`)?Z>wd$0|qHa$A`o3HcSi>B@CNt*I&BGug`~e~+n6r{y90sviE5?JSmD zDotg-`*#6R?2OFp&)}()R-4EXG^2Cq3cbzvy<4k8H=}a@#|@o=p=jyf|3bA3SaaRs z-QXN!oygCclLY$ruM1QTo`HLI_Vm_`M0 zz5aU#1kD9hg3t-T)~pXFvrXzWU69KSQyD;6_u zK;w*}efDk{Vm4wcVtqzs5E%>YD&>X1bCL=0Ne}7jy$kNHJLfcKQD1eNx?x=$(@CGY zh&0DPc$4ODL_2mWPji4NLq*R?g5E)>&WuU(+}o2(nLUi+&x}0dOYV^sOI53=6MyY; z@;oEGPTXKzNCVHT)C)N18N9~1|FC!Y(x0`Z_)8lK_V5kczftpBcbSR_*H+#0PvOes z1EDjddBk13y4^Ea-o8~PHIY^(K^{329~GtgmFVjL-zN>BoOT%_UTn?kLAmpH z6*cv(P%{=ipnEnK!>_OIy=8Bl3_58hHL=8^2W0l;d0hK^2=$?HdfjoWdUH}G?tI6m z9Dd^&n(q(Y(1ijvUl#-Hjp?ecqYm#~JSFbdpR}FrG5ym(H1m~Ti1>k2zlfqb3s9Gb zQ9DUyoOI#EcJMgSu`$Os<@j>6@r!2>)GhK1LmfoiZ`=(!-$b$GVdxmGiE{AmjHwoj zym?gLC$go4$Mty#Uhb&Ye4W>m6UN<-+RlYEyK6|z_ce#e;~}dR#0{dwJ%vO3Q4^vt zPKBXu@*$KmQGXaig7WK_?E7B@Ts|_EaGbfuJtE%M3a`LtW!Hptx#A@CmW(9*d4XIyGSY+J~iFJ8@E52SD@qWzs>+5>h8HPgW+w;X^7bx z>?eBClpuMGqKRI#F1m_+tZG3`@ce!4V%VXp-Vxgcr2?~$%t%2(l6YaoTn~$wv2N4{ z;suW2j2^!iJp@W1Zue0Q`;>K|P9S;d}JH2Y5`YKte^HzW<)Nh62 zl8s$uWuCj3lTFr9|IdL&YQM^l27I_PD@Z|P^~AAXr1*+qzT%xpszYJ`cAZbwL7zZW z0f_0ep_kvGHpt5#^s|rt;mK+PKTEXCX_hZ}ft%19@!#Wbk$SXae{TR+t_I%x=yM$M z)O1;B!9neZu;9yi$+FI%k~rhmq2RUqi#n_SwU(^Mz&iESe-}WXH)X2hg>ygt^*4UYozCB|@N2Z$ zHf=JjUN^pHV74bM2aF`M$WL)aCz&_8CSr%CH%u^C?Z^G^}jM#TIuVl4A2Qfh8{aXxqmag-MMw0as zU|mEtDmpFP1Ygg<;=YOdR`IUV?W&x;ZW4jHsb+nlQL-^ti}RZQk<<12OLEQ5FK`u3 zgcs=l71cVCoC(;xDnQXDcf8Fe@JBgLHU`!`cxDU6sFOiOUB3xjbzpfb05itd3U-STDH$qNA<*uAR0S6_*NXtJgn)jZ~NC)N%NTnae9UjQl^{EZ^N-mVH46>ieYIZJiet~o;zr(S&Q-}Cnd_2~~5A)S5 zA5Jl_B7oV}H5sxDB1UDoQv~3O)AV4Q9yQ)6Y&JK$S<62nSX@v8;&dS}nWuPc0%hY<*S23`ogNxk8uMgoW#mA=ovri5ZT{Z|OEvpfbNQ@HW9tI%LF}xB} zt%I>2zUzrNS_mdcv$7+dYB$lW=}VKv^Uizp?X|;`o33J>A|FR{joO2DY*xoE9*(4{ zhEAZ$V(lAnN^dJs1b?*{SLRg*d)&}NtFnsoub(aJ4`QJmPO+lj;xM}vGROAesXzAG zplB#^B=#VgC%tFvsR5LFoqu8Dy&?gxVPoz+?zlAwQ~ptOY&Qq?O?=h>pb zdPp>d)cMn9CPqu%y(j#K=0`n-Hiccq)$r!2g56Nqldvm*Xa=ABkIG-Jf* z%NzZdV2}&}6a4o0##F$_Sq87|?Hg;~b%*t`bagk^7b)U~9>?l^7j<8e#v$yigc$_6HsV-VsBc-|5_+K+F4I$8D=iLaGAA=p+lhQ7kT&4n|&CE zw&V$fRQ}E%x0KW!xzhykepp~z!|2>mGbmi0;CG4C+b)1mNh!YjaZVa*@&P*r7}qG= z-ytya_be=P1eHo0J_mh5{Uc%%+5d~8%wnC%RZ0~ue_CRT&ThYvKB#VedK-hDI5ji8 zaz-+Qv!|5zpDJc$2WuVQ=D~&Y&1|dAED4{iMt^C|+J zY8mV{acW}qIe5KcDm{gaP$M0ImKwn=7Yh=zS-T|;|agg3`Dcjw~C7Hdw%Xi;qN;@0QT^(o;3s{j5G1$bY77~>?o9zAQ)a_zK@5dLvWR7%MwRUn#mYGZN}hrZ7Okl{|JwJ{a__y|xwL|f=NU|C`hK5L*}Wm~^U!fJBX79E#fPEP zm7%IL#*oDJkVi$qp%t3!DiTXZrQFv!KSPdmkfq3FkiOh7tzz~4rLHF(*!Q(1$vp?>wB^{VUHFh8Gyx2I=)Ywr9#8Am-KfQ%8Hygr+b-hv&zPd zB~x~$y8O%m9VQyc=H1tyAD}WcH2)F(a&8imn02$(L?$H42ZIrVvraP;wub?tX2LW} z1wFs@d;&XizWmiGf#ei~3eKe&Ix-ID_rI5OTB9jEbyi!HSTLN}>XWwWbXgKsYVpG^ zQGZ_vz5ux&Z*2Rthj3c^NTyCsKMQg(&Rx(^DJU4NcxphBp&<6?I#_nZQZ&iS?|`GqKzP z)@&{(jDxbFG8X}{Zb$+oCTdV7iT-p40Mm7khVV<2#1~LGm$TnxtKB(7{{9*X}zmJ7~jFw6bmh=6O|}E&opN=Cz>!KYN8(z zVB-KKIA!o9nWb5E1H}A|fuGb(DafRIL_sLKWAZc)<^{TWa|aR=_p}NtZ~7YP3$g8N zm^n$4p$%yb7h&J6vgUIp+b5m`TfxD)B0Cj?N2dmatMa2x3?ShhJo=*?ah_y>+}o8N zZd+rT#=7a`Z}fG>L-7((5W0#?Jmst$aT!5AK+-_6Tvw>6jMG`AAw7Tc_D-taGg5#z z0QxqYXkT~`LaY_W?DL*tnYb{)ML)0Uj^Tt!>Yr(1DXU)u&IMI8ckSf=%MCyhQU03! z875f5SFB08Jp1G&?o4?+E95RSJs(U3I?2Gg?lk%vu!OQZugn-B?&QNz!j|D92D^p1 z%BW*Gv2k~rd{{KSJ28DiP~is-foFxr4MZ<~{^>?VF+Sl9qYx`dF~C$Lw15l4T2zXs z;A0I35I~12eflST$rl$9Z?yKHPhx!1Uu;gZaXZy;4OZkmCsuPOxOz3w|LsNdVNH4j zEISX#L~6TM|4Nm*HiR5ael)k_EX?#z-Kqb|pv9R7qGM`G@!SxnS%xOOCLebOx=rW17r^Is4D~!>AJysyARNs9O zH3#+5+KN+JxMsU*)GAl=O`1s#;n>i~Idyr@7!KV1bFSdU#Z~pPjJn;_yYrM;z*0P< zl{LWI@aDbjjqw)O?-`tUp<-ciTP7}IFxiczxoo9iJbw>&_}-Y>w->u^+_=Afr?x5Q z7MSEViV0hkswFH1?j+i!9`E%Gc_;D;uVB^Paw(SVe|1D2*o{PBie;KA85&vt5qYYF zv8^Ej#ZanE}a#R^dO}o0%)VY9f^zjDvIK4 z-;7wPV{ZC_PFB}@ZHw5^a*aosE9Bf5Y1iqK>nFLoM@DLJ-<$veNPvj-(D0A9ow>ee zfxA2y>l8*ExTmWQP<$n$*Bf~_2!NUp3?;IJeKO=2#hHe;sekYmqsU_3)Vp-JD-$%= zKVlskli=!>iw|uDzm|NG-^j^a4*>QEeM6B+!8Mzbv&~U}IHW@zs@4LCN`bhUcBUDz zu~YjaIo97Obt_f( zz^lmmpLIh^@a5oRuWjIH$y9B)+}r=AssHdU!8DSg{SZy_PR(MJUlTN`_#aWsHa3eh zMbD~V<6?ZrLmy06>-1lGT9V*$MmErkkVlsGguWLRI}RLm({w^&6i2tqV3AJ02vmln z;`Un=*H5d@B9auz+?RxzsEP zm6G*1v1Hjj>cKWPFtu75nDEJqO*uXI{5H$#Tz<7bSuV{0d_2bB?zNnqv;WL`^k19#xq{CK_1u+R zB$}Wo5a92$Y0&270E3+ko{hNKo^3+76xXisdbWP~^yQfn z@`X)-wpTj!g%9^K?G_d%T)ehaV7{_s&)q`EG!9x-WX$)kV7IHrp&_ceUH750)G~$8 z%BRAnPMLZHNa+E4u9Q2dzpLro_Pt!Ye*D3IQ1ui1O;`;G#pR*&kBDzORv(*XUajSe z_t~YdlycCiMhU%WjJ&ZzL3veAq@nCtO`7Ez8o7MGgUAeGRtRI+@dJQM zr4WkI(B1sivfUAui!mziD@Ql%e}3)az=25u<1?lN2qau;{x%=qzr^QaD>p#rR$W)~ zdMoVOxuwz2%JXC9Pzu6MfW?7Daj>s{{q{*I_P;5Szk%DAGQQ1)Q=Kynba-aY3>gRH z;h2*7`ou`M!oXSLrjHo`jp62H=m%%bsE(Y^G+`41gwzD76!AcvbBhKGpmknacF|U> z2O8@l7r_cO{jp{Gu^tixkSl4r(d7DY(kN|_8hb*?FiXEk3$MjtsADY8{W=c~k^1K*6AoA+4T8_G5xRm}Sn1DfF4FhE#iwV!_?8XSt8B*2h-y zcDt+uGr{sbO}5s6Jij~;?8 z5F$-OArld3A;xo7kk(h<;2^<22xR~SVOSsQf^`fh#H9}iV_h*}>N2vS_U8ZFhRGW_Q1Se}bIoeLN{UOqwuR z_f zx*|)2mTxGT(?UlLPJ=MpBO-IykIJlraX4~y(<|J+At@9jY8oLar}!!w8cI&)Hts&n z#h_=M5cQgDFoYqB$`N=hM}BAM!!=C}w{FBFDaj1ZuqsIr6ny>`hmONqd^O?EX zubxq_^0pi{EQlUq=4PVq?Z?$n<~tDf6;r3jbtM+ZsSNRNxD6eMyym=3x+x}74@llr zjh8QQbq~A$JbqDr>?XLE&Dl5YsK0jT`qoNKZz=1&%aJUP!pJf84v)B*ZIV$m|8pMY zw^~tON+2YU9PSueK6oU+&q4KEnkh)m@{{L-1NA>Cq{e$m*`n`ECqZW0YVzjqAg?=B z@>jcGXzW)|*QzYsq`0XIh%M0fSVui;RSDFXv&j?SwQZ5uriS<0kaUm<#YBAi6Cb<)SPZ;JXjawB_~R?Q`&Mn#Hqna zm1DrP-8n-g%}?x9#MIjmV<5n>ud#xK5)92$LeS)3A)^CpP|6&h4I=CpLZG zFYFVnxPko3gMH$;!rr+#nmpqtbYvc6`xrb8v6v7&(WePMU(2tW?`FP~z7zvn(0$c? zZ1G)q3jUEKE<}Li(dt8@g|~^gb2^f!gh6^5^Zc%8BGvF=oX*wa@uJ1l-D^u(%F3oYD3oNpU(NBSK*C;T5H{OGe^-LP`KYtYUXL8+lu!Hq} z(WN!L?4M8QoK6uX8)uSc)#>n(Z47iGmX}3C?#-z{nB2q`;ef`bWv!x_X8N*me(9h^ z3XaRRUmXtoL7l8b;fx>*fbYFRHwYAWnpys$Soa&nP8^_K^Z^CV9`!9`z(~sL1~a=} zIH#IOpUa?|w>jhWO?m|+Cw)Cs3c5h@R~Cz_CqrI(xA(P~__eDvQ_!ZNoJms9Vvn`y z9Oi(>ItPa7dFa_mtEN|Y1``UyoD>G@@4Uq!k%Q=r#X7yF#bZXo(?cukV!YV_t_pS8 zNakmLjDP>j(vwo>uPJ-LCt(YBJfOBeI#gb$Iom%B#d62VOo1sBAr2 z#X*ZuNo9c{$M!LmU+56kMc>$$A-jmGpKx$;zq@)hfAUmaFiKox^oGr@s1II9=^5nXrRB(6LU=5U)O;k4O@%NkW#%U^9>W zU3JqS&IQO`7@vn|T-8ehv`m%3|D1vff*yP#ccr;Md+?&}s^sXel}f|DbYm;@6}7AoOmg&J&$mP1OoN*vWfwhfjfi*q^PI z;~K!Hnn(m{?5=gb>Bb%S!C5==K;v<(^9YmTUIg`ktV7n6`3i5o%;s@mV-Xflm}H&} zgTBsw+RTwOFt=kJGPrVhFRtPMY<2CbPm>e$Yh;SNQ5k5=EhIpR1tKzrg4rcDv&GN} zysC@w;Xklr7m#5+?^~d;Y-f{?F~lv_p>@Z=Rr*}+-DlaR!jj3)f*zI62_F@ae;_&4 z2@1Iz<+!4SkcenR^NDkjf2wy25c!YDF1fZMxMs^k!zOmY2R-S~yQtrI=p;ihO}^Ma z-f;6Aaf1*ubhCC_e0&bLjAgrla=yqeSCB`&C^kQ_D! z9b2AbLc!{NmDXa3Z5^_kxG&l&fC)F7nM-f>v7%J0!y zf;(;7kaK>r40zzCe)2vo(sS&-NdRKhh*M;))Iyd7{gePHqf8lhOF-+ zV}#j}l=?>hh@8y*Mpn{E5`gm94NPZ2QA;?_@7?*9ADEVRgE*x{&RJ^05E!+nJz-#@ zRKDj3hXMr-Lqh@IPF91FpqZq;yPAadXUZ~TL$KU8L1x9`C)VR?>}~nEIta0W+nH0& z2F}xq$e@@BOh7`(04Ay7hjXT|3g(mnR@8d^t!CELK8;NiD(k}+#B(;Qm^57Zand&d z^hEEoI$w*6KtIzk8*0Af_efIdfK+@avjQF07nx5<1Ohw+Pw7nGH)xtc?wtxX;(lF2GD; z`chwwNZh(i*{_`wvMonmA z*%3LNA5h=_ekq=In2m;5W^;Er85^&7WR?4T{Glb!_QKu&@8trO5~TJI$LJN>GA!prHBJLlZEURFX3cUQ34SVbJ3Qt<7OAKeWc_YHG7{Y%)-jBEk>f>jC1sv(j?Vx)#8 z&BAJfWbuyb-{^8780Fq?ROhragYG0H73MXEWHtF+qzyz?ZDPOr4doR<24N4M^J8hGT@%|l zW)f#>i=A}dzo>YhNvQ??N)7KK9fz_{Pa1?7qkDHh;t10`0;CQLF#gut#{eRbq}eHF?YuKvS;kBwhNm6dvpT2U4FF zxzbZHq)aV8E21KGquQn`IZV3-75p_|qBW#tER&n#44=}z)fQFz8lfyyoj zv+6ou&AyfV0AkiZeU1TBE{f{Dtp8iOQr`4hgR#`h;GzqOHS zDlbmv8}mn%k^rf!Rpc#*{Nu!IOK9%L&|ET{+G@F1WLGKcHnfN#*p++35P7bjELJH? zzRuRIMz2;x@|Yj%BEQ%3k9aH7m^~&gBMW3u1QQX!x0rnY?uOoY2pJci9BLgClxOs= z+k-5Y9AZfJHr}uuVt8i<^6T>lp${^xvs?$JQ;w_4`Dhwn!w@k$kP`3D;k)-5rk^?? zW@F&)F9#Q0>VDg=kF&2FW_lnCbnt&!JdVAlw1V5r&LV;Tt>5J(U~kn=)bh0}F0EMU zB4*cI#;hcNhKhWcb=ezD7Je^l66~RbI83qhk^U^M8PGFK&akUdjm4`83`|01^SLYf z15eN?<%%JnWAc@3v4#4xEJ`-5oV+}YT)VGET@DN`kdv{GsgYH zLP0!x(vowm@5*fE!7;uBBiX#(n$-Y{ro1KFjp*{P;a4qL*IJ>uQL>4mILuBvqZ~=?W8dI1xa?8$RD!VkP%6ONh1gR=GTi zx?Uh*JvL*R4;8FxXa zWqRa1SXm3RRg_jGtvJ$n8a2_lVL0uR+xsbimfFSAKl3|1%TP8DVNEf=pb&X#k&;$n zpZz=5_4r)2!s{`(_;f59)t*|kg?`wwkruJTuL&0rlH_ST3<{otNvq{@>bjOSyBLM~ zIwuUYne{t(9loPtU9_53tEc?TEudk3%%yQMpl}&_&mR3DNFXwGBsJV zINSaE{`m6$Orh`qO>xQ$R$Vvkx;dxhG3 zfBF9(CXaG_j^v%|eO~AJI!&yKsKr8TIht|mi`0byhB+LHXPPJ`+?SI@Oz&}+^NGkA z`^fej!D-)>F|BJaLd3g+5hXa?)>ypJX|Wp<1F^KeVvKcabKJz|4z0j_KQ1@;RA`_I zl*>bm&;bRb`o2p;RA6OdD*~Xp`J&*I6v|JA+Z%&4E~V1$(ul@m58fV+JZ&X&YI&gS zmav<3OI3)Ha!84mwYuS+aY9I#c0T*zU)eTKkUQP#Nb7eRkOv{kXAO7ZH?X&TK<>DZ zEV!(0Y{!U};%E~x3y1#+apqp&5v?2hQVDmlZV()F>L^ljfEeflFT8%Q!%cEF6M1aZ z3UMo2p{}{1@$r!iEq(??AK#`bcbJje*e8}o5w(6G3CSePdM`u<8UT0ZPV&HOHv!n? zd;-7A^r9YGJSm-9{wHXw;OM#D{g%T1x?cR>j7sylT~vz3(*7~&t*dF3=vYn}D`+`F zi+%o(;5IAt-xj#&B-DE?Rh^h!qT(#3rfo33+{BFC#(fdiyhdnpZD~MKp-1);6@|oB zcd}2r?;&c}@``g)!!T8>lBsgYDxA&g3j^#_8+2|&S!MPq;OS=_W282U3zvAJq9018 z7yGJ)c!hy;!99#-^yAK!E^&wD|D?`0!ngCk?Dbx@RIaYA<>;#!^#dliNfFjOwyNHs zib|KbEH=YW-EQHL93(;xH>xM8!9crBB9IjH&KTR6OE#3=Xo9Mm8En+Kr%6G=SQ=l1 zeof$RB}B@&R~a@Pz;PsZA{uU%?{Z?_jRuQEzLekw>&hVJk7s)Lti)i7yBdr?RPjR| zi;vSXpP2tynJDa9@V$GHI=PIwB>s%xfnfs->$mlKx#}%NSffeI;Qyjy~e3;16i=l7}8WU-HkpIJX?vl6t!sXr@kd@<+xUg$$j)f+ml z#k=*)luJz3|Jwd2c9+#J=Jv=a8a}&IGEH?CWYpMa!OzvC9aH)f>wj!PIUc|~Zhw`d z1j1GOHnMu^J5Bs>{}(U%#NiI*n>(-MZ@qkV{ES)C@y&}nx1V`F^?l^ih1-sqK`@@M zOBMGF=M?wsNV)mmvoZS?diV~d_v)vpT&>5yx1KPkYrGeg5y<2^(4tJL>_mO^6qz=REGwCKUJ(;o{| zjHfesB+>WcNq+fL6fik0_eSpb`EJom8MpPu#8b=fb^%UeQyPBU4`h`$ePfpA^?~;G zBFvYorsDks^~XNE=UkJvp5DMi5#AS%WUr_=mpVdj&xq6p1g21Mw$ihnIoYOjHgvg&G(M9x z0hw1bP&MKJ&|7Btvs|wlsj42inbnTnX=K=$5-R3#Mq(4sOh1T!z#-Mj%qX5YN5Ub-YlsEV^5JQ?ngI@DtII@r30!R&TV3|>#{$i zt5O2!EOtX7+wQU=w5KpS^P|pDfp{P4~cKcjNC15rYP_ zQ{U57Ul5t_F(UG`_-Kb$KKZNmlbxa)B1gccajJj8nf{aoVRhd)v1n#JLfg$(x85Ia zhD6O!)0IBJ@)0|}k2AX-hx+<|^_+7d2bow|B>WpSgKfo7unw!S@7bz(1mbaom4E64 z*KmrWpo!@_yQ_bdZQ%jz3V^+ZjD4RzjMa0l%%T#ie$a?fmA}u0pWhq?kLo4Ec6~9) zs3_m{A5v8q-hMv?yupt5L_cnw96F2gtuucsaZ=UP%)Xl@s8Qqor%+fW*=1uXvv6tK zjP}?(p^u@1vQ=u`glX`wffXKN)nJD+tSZjD%1n?36`je%&<;_lhpcFum-M-czBk|G zZa$O?txd==JE=ltT?7H=7u;ehsp-5?UJe&SD}sssjI=rX5$%{7F3?GV>e{R+ktUlw5#RSa-b*kpT#@scVad*kFZxo+us8f@JV z7Q{cCgd;Zl;JzSWLxZ8mp-%&G`zB>1YP5xPQ6*l!76?T^^>sIgcAdFv;-Wz=qFKuV zz883PuJW?j{7*pvVO6^@AlO&i2%?`pG*H{D zoP*FdxX4jIjTfA7)n7}a^RrGa3&+H7_-gOzwJ=vXS+n~#sMsY|EloS$EyS0;!)u(&>qXW<{VZBRPoM8jxH_ zs(#?oCkE=kJADApG4{RS_sDwEJ2mX)qqao4ePTKXS(dDpQBBuRTLsTO?y5V(WQ*<`wVn+_F^pON?t{ z;xs&}_$KF2ql$1dx!W5Yb+=V)dY$_B)`ZL)-N7g|*Lv3x`AGpphuF7oOq|LlCL?o^ zPmRC)CX0NA$0@qtP=$*q>t$*7)NWP`UB{Xjt=hj=g@vP;WQOOB2-^p$c-#0kLVY&HcHSrf@5_2tha=8mw^+knM{B#5Ez|$^{WYo-`wp0J$?EuCP;kWb@Bl zDxf~IC@5i_+VIz>u)~E`=#Ye9YMLR7_RloqTt+`IS#5S!{5wN};i>|Nt@c%B7ykK~ zn`;Z?r>fiL*Gx2-s3KlIMeQVHuG!>r{k2dHSrzgVpV4k#OMW!5JvMj8IhQLsTfVby z=pvka9`;C(qgItuOWR*WHe2J;Se`?z=_MvR)|N;C5704tt79hulVB2p!6})m?1k34 z!H5LrDx5&hX8}T$-K!)5B$GSqzHHe+jGIegSr>Gpyw|#8lbqMgQHSd1krTK%UUP?m zw!p?<``oLKi^AE;7Gfdl>nELnjL@U!CqQ2J&1kf)wobWkKLDLt@1216KYz|OFcqp7 z(}_3!ByL7W%rCsV;XShWEWx7}j4y+qi?lzmfG&i}<`r#2Vp=2xit-7z7A&0z z&1rlm9cs^sSYrBN-N(qoCP#JQ$$sQR^^Z)67{6XV?EFb(v^uLbS6&b|8QL>jE4R+2KM(D&G4dq4m8Z>iM;>lP5VH3@IM zt%%1ybtx4CgCN7UW6on{M?Hs()N&W9E0){PY|8o{=;V1n~ojkbd>=AQ+<3h9*o5(Zp-A}azcXMac24J6> zswgdQ{W5a+L}ALB^6w+L`NDfxPM@~Oo>wNfV{5A`45=DfG+#Ci(CSFO`(?_kP%C3q zqw}G*^Z7TuH((YGDqySir)uU50G9)$+v36VBKbQNDu62%sR|09rVH}Krn3BQd2zlb zl?9%AKSq@VbK!ponMq%8-}DOlVO;iTuwlu%wBvaLEQG7#L-SA*YU0ZtbV6i|7;knE z=Isz~GMR+l#2MBV_=ts7WaMZvOg3XgWrj*`ZqM$E#n+td<^-?$fpE}~X8sxZ89b~A z&ETo)-FIoc__bePE|LRpZVWy+SB(5kW3~R*DGPI{|6GZ{0etMc+gFC>|8`Nvyy~p) z>x)~E2vJ>#b1eupj{6iMRzjFk9lP+D4Qx^UdVy-3%)R@TcU^m|U(CB_4v-=NzV_`z zu$}D&-&o*fF8JD9)+N539(kTiB^k&x4yd^mhZyaK))gEQQJK)$xP31jA>U;} z^1sa<_CK%ijq~VOtq>^T?x@Y0XDSh_6WtCQW0^5td@TRFboi~VAsOq1WN!@+xS{y( z(SCa?W(vV8^s>W18wqQ-vXX8~DY1mQ%GlItb8nOF!@u5%q!(*dkQYTz$jtS;x#QN4 zEaSiy1*L8km<7?o%2W8LYg7am6)_b(U^#!$xIT?+RLdS8OAmZO)9QQVG+rzFTT%Xo z-kx)B`EGrgmQNwOg196w&O%1lQ>Nj3X*~p0>Z(rhH^SO)Vm$3q*b#l*rugz97_?Z| z7Dlh7UO?R3UYhO3(vpW<_1m5z^zAY^>Lq>o|D*6A>CSucQX}Lx|2uZ*Wvyr@g{+MhXgHBN6WTRiQuWIhu zabo2*rct%#GrG;G<=S^^6%KNEy{O8c^*Gh9L@iWJsSpm96RqNfD+%OJxvfRL5}XPk z3~?HaYD8budezirDdQv?Jt8eSOK3bah@+4!1a?u`b(pyMZTdQ^FojGZBgi4j`uQ4z z<~_|sH7qXtk&v0O)l^ki!*J6ova&sf+(o2%@mfY3bjwXl*Jx{uTbXnlUL?8?4e@Lf z)oYE75LG}1eyoWBb{AaYap1c%Y`NfpA$ZB1>?QOWFzKxerVT5QKu^n90Nc8p0n^t1cRd?CZJ3 zsUG!+xpiR8{zp+j=uP9jwA3!cj{cP@2K(f{YdI$L%VRV&b~Ysmt^RYN?nh4D6GNP> zk#tdMNYT0{u7f#G?zOY3v%+!~^aw7eToj7fTBp6jVoMZ!?Rl55k)v-Gp?eHo&6s5jqc{gK?Kl@YW(D+0z2BZ)~}jg8&LcZf$8RuT8fyk4+n zK-Fids>B{qxYE9dY;et5@DZr<9G0p-^Ymu-KWFxUe=L03AK0eF85sc7dPQA`%YXd~ zI{_DBRXp9+VPehOz;Sq0YBXa(br)byy$2u{(P2jI((9nheWULbk5G>6R`)Ulei57` z%p4aaSNFXpr@XUsRZ5Ngi7g}>Uh+{R)EW<-!m&&hqf%AM0!O)FkTYjOve9^I#?eJ( zMo;VO>o%9-ZQfSJrDb4XaO-GC!AZu_mNZ0(f8!!s(#sUz+*t9XQt{ZMe6WdSP>m2V zfsF#jFdB2q?Cjc>Uqk@hBVGYHHVgCP)JzUVtn41umRQ0#gI84$@CScBl{7chZzN$e z|9T>rFU-(yX|(c>;g}%rYikVZ{mHODK7kATKMKc} zF>(qtYBEg(Ls!r&QD*tGi?Ftlt>GwTzzneM(Q!A#Y?s4~qeL=HhW)!i>v2H47YO?3rllRveLLdkE#|^u{wROcMwSIRj(|SxT=LM2i->kGjGJ z%eP40Vbq41SgA7AOo`{?DGPcv(I2KV zC}~Mt6L%rdRmz$=pdIJmm&PJfcC7BH>vr5pzkTce0TrZg+VT-w0rPEU*`3!w2a1j2 ziANN>;Yh8_>QV}p>h&dz_)~!9tK$bo)PgLvG(H`)EFCl-1Ert8d@dGuhlp%iI{2%m zhbpjsIpGZ5#w{mj&dDhR?(`_|%L1U9T?GQH&^z}&zh}?myzOA}OusU&>;3ctiZ25G z{fWZ`X##hoT3WR`i+*6v6&TQp-@HzFP`j%rwjv_E;>H84JB%IbFOn;O*GK0`5G00$ zDc{h@5yPw&V??gdi!M~J<@e*{oGfpf3~7a@L}}i4vbH0^jIK~o6pmi{nNdwYrZ?y+ zXs6AUp31ob)lcS*AyB`j0d1>SX!o_nO!+5kEd5`6efHz)+wU5=R_oK@6cN)?4*Z0LQdl~dum;738ar13C!Lu zik+TzC#PJ(Q>H1I!N^;9E^3DdFeFtCCHRVC_KKFRT6*_@)rB8QS@5|%CO>}Ks2LD% zdwcG-ZExEW(<}T9MF}#Jy18M4hK;}Sm(^_{>=5f$cWRvL&mF>TNxffB5Rc?dWS^Nh z<0@)z|N3NZ^~~)n82N5$;6XM(G@kw`4-0Fo&l{^6>_#RDdh6Tbo!JBEopO(MsvYBJ z)=ci6$IQQ&gpCB!pUR^1=VZFdj;ZGKD?iAekEv8M|1$fkQ~6_|2pmltcpIGBpK|aV z06M4c%jxf>2&t{9V3By1Mxj~F94kgJm+(= z9eHq00cziX9pKzMU`l!Z>FB`8cY;NCAa&ypX|7ZXC2dl=8LU*E`Di~z6sNFpVSY#^ z|NlW`;Gd65?^?NE`hWt9PO>$+SVq`Q2o}&O>fn{Qg>ic=pg^W!AY3Gb%clvP#Iv5H zbp4$Z>BSdtUt}6~7Jc zJ!+FYoCJa^WC|*Iv?X$jEI2(0WUA66)m+e9?rP(m&;vNCOSQo>t(JdOEPHy~c-~YX z1j7a_bndW`eO$krKB*}8!&CXVCc#v1Y^WsHJ_4&jQWflUw#_Ix1zA?UScS8Qp}??n zXTs+40=%$WksRlQKP@3c1lT4Ts^}PI?AaYWL~Td>wwT6QUPFv8!_1CK3^3LF zbp{mnhN?Y4W{Eci3YY5~tql+&s5@4X8Fv~>bY>PvPJ2E61ov?$vkqFPn#Gyf~e zjw~~`iieUr8c7}ULkaDCX3WS)-H@lv;zd_E+JsT&&5ey#+pQpdDnZG)9z|krlDI5d z<;BD9OX$9{v*J*41)Zzeh&hSqsveP3d>mibL!&;k5Tg82tYAU=jHSXSl{<1GR0bjD zJ?cuJ=`+TKOOf-Cle_%tR@tpXrTrSTBk6)v0RlTmsx+`Dr^lZo$cFe4d)tOR|Lh73 zZhTY#3o=p`BKGTQ?Cf(0UKZCVBX|`@hPEc;)L8K9xacml~i>(>Wkii4Y}&Y z6jWcwR^1a!(c^8YdvJk-#KYn@0z0W^{ZFz%Z}VJpI%Voa%)(Z4jM&exDMv~JmKBj5Y!N7erfP*f z7DxOa#eV{V7#iQY zgBM$4Yq$wvqFc<2@Mti>N_F&`@34ZU$v-bu(q^~N7{-S&whyw>Z50-H4GSl&nUW*7 zOF8Yy3i*kTZ^JOgiooIl~n*%} z9&6;UX#xqdTovPPRtHcV^(lf!U8(883W-NSe>O>GZUKWTWax=V;)|vw=}m46^3~9= zA?gD0>Y`+2*-}yB+vu)qoUVn{0)D9(eXL#a*)`r|r1NEKuJY13M~zdjBHnFK#sAvb zkQ!`xE@*xNdDt(fc(Zt+_8>=od_lp~8L_Rpk8Y|%7{|*CE>gq%v}8EFD!a)1wey2u zRxeEPU@mTZ^P+h8S{0tTGIF%}Sa1P*PWc5q`NGc0DZ6Uk3#c7aGbnM*Eqx3Z9+dL; z{%o-{fSz6MdnV=Ag+v7`hpDNZHac&0+f;VewZT=J=k0|wJNWV$H4Nu6G#7M5;O4%n zmf(q;z1+ft$eFgF>l}@7DFdvh`DtR4o!1?xav6?Vq{(h(sK9?qA{RH6Kg4mJVPi&} zTi!E=wvcK9FN8ke(>1r%_XN)=zRld6pW?D~&MdrsDNj>OELjb|Zd0psD@DIvI2Zcs zG{n$Jn_#pD$E%ZR&EOK4O(a97o&q>&q1Ohub3*LD(dW)jnM^0A!20{g-|oRpJzSQf z8Sng7>L<@N_R|MozuAp& zL<|4wj2>-{St?`Nn}@!=DpqwCXdIPjg$GYZbnbrrb^1{J=6LC9?*(N)+^u~zH-}M@ z95%&q^q#8(#MK1vb5?7)og;!MYh1ah+EJ@BA|hP=O!m7T)4k?Mms#h zIC-3tENbUyttMSG>30`q^lDa((qHk$8GX|0zlDt#AuTLyT%DRy(cul3>y8mj5tE~d z6UUWG1+$$(o5Sz=M{E%l7h)af{j=J`$;=jWMVW9N!Wss~huy>q5qHDY8MVw6BQ`A~ z5!B!m2wRlD0hTbj_4fA^T@2x~;e}KbH*a`+lbbVZ7T@eH&tdOqTHXgxt67zH=|AId z!*eXgoxw036&@fL?cWi}4g5jKo3yY7G8QbvVbP)GM5ceC{*>g*YL;X3D+pRX?~vKr z86)0&Hidc`9Rv&tm9j4g^fOpm6E!fDv;LTwtYTLkbw*Yc?hE#n4V-k`-vTrit6b%k3BbhZSoS)m z2J~8Wixpxu9(iwFxU?oKIgjM9wV7&9Sa|F*=#pmms|cZZQ?9>(Hkr zD%r(J|1z0+lu1%%pzsHnlSiR=(`WDfcZOfb-IE`R1Z|*fq3`Q|wr^y1$m%&#-x4m) zyGZi(ICDl7|15M>Wp*yv3QKbcMDV{^^GLz?K5DxwPgi_5cB8x)P5XG7?Uud%`#Z(* zln%@Tci6{dsa_FYBuy&LAIFv@^zcQ+EYjWQ^I96AgNPI#yRW;MXV&Nl;Y}Q05Iu*$ zr340wT%29BrZ|OMEgAe$EBefqGU*k?6rJ$v&S%GW$=z9O3J%AHY8jqidYpy9c8_4g z-0LaY2B>6`3ZA{Iz0N#(zVdqM5WvCdLHbKPWUXcpfE~OrFH)x2nnm*no zHcyl}3C-nBoA#kd_dEQ(5LGRRqHj#$ZR9;F*$xbx+JuwDD&>pG`>p9<9n>* z!v64AknFMB*P`d5vdtKN$J>xzAeqHEAaRfDMC$ifwmZ#sg0CJ(gIU{NM5k<+ixNY| z3^m6J1)Z3}&V+g!nTNR6)OAG+ta(-w$u-Sh!<-4$;bz2U#Ry^B1vbL&LU!hCHICD4 zsaerG_s^(=6QST-%@deb{NL|uHtNiS-E-;8ebA*y#Cl{dx2;agrC{7eXVFT6Pm7JZ zfqubSLC+nd=&`Y4(Z%?uPivG(H2qxXo(Xy?sKSEK$FX{hLD38$SmBqDj5R}K0MM&z zE88BVKqL=W^+}@PwWNA->r=4EoGqg~H+XnBXWl!YB({}pJ{7g#jL_kU?0$)ma7LLq~0N}Q_R!?Yelfs z%4P_4hK7ZAuIwex2Y9(;aQvTTzr z#5glW^4M_K$heEo>D^*z$3Ji56PUsu4Cv+Mx(Sym8j$pYt{ZA5wIy&lOasO=5C#xB~MbKO;la7RO6ntOu6@oxXPH1 z2qd7Ap}HTWFvRTV?q|VOaXvcK1LztwohvUT+g~p57m)37x=3xLcUDbvZMR@$l&ud16k07?5H=wgsBg1_hY&Bhrk zQ{CS*b&lRu9Qn`A-mBKSO1oA~Lh1Qr#vfF&^K9eyIu<+>Gp-~kEKp{t_RrR;)gJ_t ztK{A_R;N$K&Li?}g?~R>?e8KC26mM2(IjU#l_cVFy0BVXUBP~OPTVa=NHvGs4E!8a;ez_>Z z6IBimAl3i;%l=lW>2B5F0n|1mz|M$9uG9T3qJyysr+2&wwu|yARa*Xe0s0=o=I?d! zNIeUa)PS2*gIGs7O!UEus^NlvGHLtC1l^YLo_;2FE>8)71>ZKn5q3@`k zT|_7?75-!4zm#Ba`81%$csF%*XVD^_8J{8&E;A&NR&)9i@tixBXqbvV=x!OMhecr+ ze1FtegsA8DAdIJ$Gq!5p;O^pWhP`8BtFkT<>iTN!+iV#j+@r((m5Z^a0A##Yhfb8XvH_M_uXkf1uf82oAA@q`Rnz{Gv%z})}t*_M{~ zrSw|e8;!*BJ&#d1JF4K#3YiR-FZ4K5T-yMq1ZlOE1wE?zMmHV$Z{VO_IDpU4{}R3u z?ygt}hYd#58kmHA!w-7@k78Zp55ImG@HjMOf$h|A-=Gpk*h#XahC0UM_;{5Jm_I{N2+4$aj=sH-FxWm`J- zP6=i-1HF1wnpM-c+l&svryYZfYDP`5&Qayt26lIT4Pp8PE(v5LVKd2r?9qWt&nEKR ze9v#_QCcf`jwAhBl`#$^hv_2?y!0~uKHdF)vBc`n+z}=smyXL>%U^&7=w`PsJJsP; z6M3uIlAv5Q(7`D7qFh>Rv*DxxcjOk>B3qeUe4$a~99$`Uok;!wbHu-Iave`aY}H?M zq*|2iy~wFN_SuJ9tKklz;t{c*5=LZ@UR`S`Lq>;7e^p0$6o~7oh3@0J=h=4p$USUe za1ckXl^B{pmc!U5P9*^|{g}KhYhQumT;RkPL)88Hwr`T!>)(JJMdTS2Y9_V_zL^^S z7_pbsquhA9nUg|A)0dNR#Gka1EpDZDMj*G`%0Ac_`lQPlrush$!_~@lS3IUsiOjrr zqynCXQKo_bmwDyK?|#-+PIg={3{-MTv0tlZZ@P;XnX3=ATYTdEuPk!p5S4@0AJJ)U z5J-R)`b2qdYb{-NqI`XZ=#Q+FiiH0pm8 zxY?_Z1^LaCIeNyCX3;&LuJPf@oF4L0^`%3@&c*AgNN+1^K9%C`-CSre!OjbaHeU?e zQ;zUGJ7-=ko0GKaLD5nK^eTc2PU*FA2daGKe`t4Zt^6OJSBTlKcjkV2@ti(B%C)o)A94kdR`3EBZn#qZ}kk+tUcJP)b9y`eAtMRO|!qr= zKVhKXeSLE@d8#c46NWxKf&ea(PpZQLbM4|l*W@RQ5hd;xZ8B15^-Ta#vF7eh^w?v) zY5X&`Z(yn!0T!EoWUn|Y?@8hw9y)scpq#ps{*|+OVH&tI@j;>Vsrz8Tk3@6j=l`&U zfAqk54$EyQQY@rM*&o~~jV;j4ko|GLq2i1e8VfqOvz7X5pJPmAz|9D4U>7^M&6W=#N`+M)c52u<+TFO?VNO{z1cg$At0h?c^ zVDEA_#xAI5%BCmfQ&LFPN-n`cr41$tnQ&v$9P2tqk(pu z+39r!Q`uG@M4RMVcO3ip#6V=S=i)70@snD3tdY+ecaA*DDFxdo?G~dm z?}~~{i7|{%Xo32s1$_=ouul8IbD6iSHyQmDCBqGSUbr-=p8phbK33!=^WQA2BK|cx zL^7Jx^##u+Dwq@>^%QlJf`p0|f#g6pVB4kmV3bt{42&p0^{;%YD}%4pC*qj5>O+f`ZyqqEL+ebsDRV zrC|9OK1i*6tya2RQ!2gMY&fPrvHgUEaKB(l6z-i3hjTwoO-)Nm)II1d5#56%ib)wK zxDnLO-@=8pN;i&(ufMn_qk@)3oL55y@5pBu7Vg#PcAZ=o5vwaFI@9gznFYP#`e`4qSk>IUk{Q^1rh0%&YG5U! z1HRY2tW~>+G%W7n&MRm)pMpz-@~AqQnUh}?@!U+U6P;4^ z&QV}RTB<|cW%`rqOKgibqeJp8Gp91uM1qgppa~6CVh<7Sg;hZJeO`T$2L6WSkz_kj zokJ(MzTa+K^BIeMy|R9Y&v;cN-0n)regKa`y6H@+?)?)nq{U{aQ8HhE6P~OpOe+Xt z!!Bp@kK=B}38YU7uAzni-jc|l%xlDyKr zwp!qF8p^jjWrzRgBe!ku9}a1x6N1d*Mn$? z`|o?sNU5QB@%Em&mcFaPafQ$&QDX?FC~>^NQN~B^j#ql?Nu#2g4wQojJCzdZBKT|% zI_Ak(?1!W)-q;}q>+C_$3^nev>VnvsP+5$c8snDYZ>?G&hvq@hV2;wZ4++6$^Q*j+ zqXwjTUZ=rwtbEn1T$$awSy&VviBQ)UFOgqqq3=tT&iT-GO-%@_Jd^C}#T2X4wI zzcYfzzsuBnkM3X!(_BO~GNl?1Um=b!qK@1Ss0*-Em zeq3i>*`8Uf-1LZWhctMQG2-(YMxNe_%pr%kzGXp^pZ;nNHP^_-_PV(wZ55E9*Pvjc zbbcYdZ%F9J$@ZKxV65P#A#vL%#Rz60be@gx&G0Ol`HM#w;!lfGb?bwr--fUSSZN*D zih0GAl8ta)Pe+%u@10{|IVSOEWoJE_fsBP$AhoCy5fAQFu6p|C7L@cPUZE z)l}+oC{GZT9A8F`-fISddV$)(H{XPYt%Lo;CGo%+ITrDuw2ezWW>Z~Z1?#V2WK?;x zaF@p=BUu!5yHGj~c`Tp5;HF)u5D%m&dVp(L^rE(w_6TgRGfEb~>t_5(#(t5h9NXD-P;Y5lPzpchj4h$hn)EJr`UNM;-mt| zkhI%U9-BOlT+1^lt_$Ap`u=`_stwij<{<-#A2ggyUvzBW&=nNy{*U7B))_-zxrjIR z-eTF!y-c9h;e*I%h2YOzg3-3vfh|+Y~^@27+)(?SK7}V?-t3?H>>IU$#jNX!cpFTsO8tQ&yQ`Z# zIv`(Tvc&n#(MR8Py?0!e)ACb-G|4~1R*9!M6OEzF7(tuH4p4|%$L(IZ1=ePgLD7)S zLOr5Q?15WrvX&x7U}Xi3Q8$}As^q~4wZMF|C$=;|$!>dgftL~d=xcdoiIGhFCTZ3O zQl8S_eu3XuB<$+mFiLoS0VpcRSCLBvS0D>d>fGouF*3aZ*Hc32F8(HJRdKX$81x(EA97kIVc8e#K>n(C2s%8& zA{7}{;1%uFbvNu1VmuViflFyzfvf!-k1h4{eJVT0a(7nitI$jXpR>*i%0_poa2kDtNR6Wbj(b)^U?S}iMN&S|lEV51Y%KGx@fpZc=)gLqHdgXYZ~d|I?xXx& z!`C7&8>vWO*Di~OH}A@umhiE+*aS6Q9)0`$arl$TuQ@Joct$l3!nKU#Y#;a!Q#>c{ zKd;M!MekR~woSK|Y1t|BK6`haLWmNmXU+TtMK)A}!Z`01-~U01H+4t8;j)V+I+*-G z+J1WNjyk3~W9AZhV;N|D-}0B1=)ePSSl;aeJ*4i(4;8nUDiS50_3hvR-_!CQicfAd zb&CY((~A((nRN2`e#l4vDE)Ww5!HaeCwdAaQ7+6Q$mcm)J_?f^*!i6vcX}!-2`UQN zIZoMG+C187LysGJ8ABv|>M{GSqmET|#V1+)FRf?_)1@XSOCMNaiLy!`{YO8H9#8h+ zic$R(ZjLst-P`rm6jIUluN!W&AFk}GB8ODfV1-G}5+oi46Cew9BKg-CPi+omp-q8P z(K@w}GTfJ>6GJUmR9^XT1F#?J{|t)?fyr{iY9-&a)dTB-kC<5ztxhy zUvO-F-rB}{-*wp)h7p~B7`F2*k|35>5_x%eU zRjcCyH%}UqR&zY}CL1@Jhh|B@rn=->ve>V>6~bBQtWz58l`F`O8vIkp^*@SZ9)8x8 z#=YTX99%hdTItlF!Q0e}*CeNFEi1E-YI?K|hFc?z#4dvyP;K&>BOBO;l|{aVSXb25ckYuu=}z71>urdggRh~?w7mHjvCP8 zQ81V>)S_uDDxdvmYzfLSC|y;#9#|Kf0bV}&AH~fZ^#W;o<4WQj@wavQG6QipMCWWI zq~Ov4eH{2v#z9w{^yBzCY#`gjeSQ2dANaJVZ`hzH81HE*+bW0R^KWdwW4OJFq77(l zgo~=bT686DBQX8cg?dvyGw(jyrkfvv95eT7E{sw{u5wxmTC{~7l1=|hbTw!C)JRYr zrOTUx)rZ=>MkktPel`c&><`LVC6FuKz(%H$`u|;M%Ws*4kci1L!u^>Cfe70bbQW+F z;p9Gfe&xhoNWIWBx13Y#*e~dr+|EC$-U1dW3%|zF;*j*K25ntRS8iohCd+>Hu(!II zs-k7X)U)`uwSE`p2}e7=OEjiK-!-t=?XwSDtogKQ<4)JMoOLRqtCO zDEaTHy*gVQ;K8OEkey>nmmM>mz1d40IiCIPGef06s&-5R|1M2R*)V?-v{VnY%><6` zA=sSlu&K1O{s=RJT<)5atuvPq*U`SGE2*k(0;#!}(p2Fav${s^l9CsuCdCH<>2~Aa zePN4;7eC!{vRC2sZLeMCL-h?Ju*09-o4FF2NTH7>n;jBTr}8y7H*r)1#nZ^iXeCU< zSPG`o5UB=b=cQAVJzS3%K`wLor5*0zGFe}xT>o5WFOg$S9S-0Tw>LI|PUPGh0&pV+ zwHjjO<|Or3WeH>PoeQ`PxcMJYi&dl=M~*wm2iYa5%=Scb`y#1V7p>)Oh--dy?M z+`V|IIi#`8Dtb-oiq|e4env}kqsGQ@Q^_MO z_6S*4cjbJsVB?WJl}(O6!x?<0o5V4j$+W4EoeS= zF50fuZj`xvm(yVo?pOY6)=CE5deEXkIaun|fSZchj#`fR)3<6rdN^&v~0+5oX(C7Vjf)_|&#wBm?v$s#pfMuUM--QuLZadfr^pJWMG)SwHh zlSYW=N{aj+L+2gM_Wp-)9jaB;+ZHuKdn@+3wYM%bMu@$& zwHgG8y<0kNZHZB_N)V%=h`p+=5sF5P5{ilu#E3}k-!FfE&&m1bsWT&p;x_NuQpVol}N|VrGgwH)!xgL99(92 zzI*DKkn&sMo1 z)ahlB*LbL{aSy$4l!JlwHAZb!aVYU@huR(5LS)<-SjXZhTXM*USdeLCFX->U(#h2c6A2<= zYPo^|rh%*>w|law+5%M&J;|_wznOZG40}1x3T4mM7RZpEajw&HGXl9vayx6=Y*@^l zamWMFIV>gFQiLn2&d*sU)!VOy{;ziJ_KHx<+m(Sr_r;lEHy`dN8!lkd=L}YfI8J>F zi#iW#Rf*oe$TH`lH2yKGTapef3#>*~EWu-aIHcs1Y5V%e5!p^q1JK1IF+k+GNm@FJYGZa2r6*`*rlEb zA)XFa$qKuryIyL`pV@hFgYIoXB9hUIvwr6NnYwG!sU?-g!}=27L)qG}bqExLc^kf( zd{pMv37T-NAJ3jBJ9LzK-p~=1nXOQ{%$Wg={R1eQt3-i>#ok8#@JtFOkEXx=Ms^ID zn{E*k#1*(!E>wtqMl@z7smwWq9IB-om|Jb`^{pE*`xN13&2A3;2WeU0E#*xX+uo>; zgeFH7e1|EOz{(2wJn7>#$qHj970jxIWfWsG-LzLcx}p*m^)WVdp}>~3U{*X(+qm>c z#HoYZ04KUHeIx3Vokg(K=WtiE-j!1YMQ=p>8J8X@u%n-Ah8eR_ca_rf#sNulbh{RF zTj3!}$fl4mdID`bwmOBPbfsw6xrrC@Si+LrPN80t@7QcI+qzCVo#(^A7ib#aAwc{JT zml~;g8LCrdyn7guDs3moS>osPHq8nCY`3J6hO%CA=IzPPh;W%|^}-o$R`cVLN|<30 zv~&oOxR`8VXe2FOmFEVCe9?r5R{k8rNnf^nsM)4(WA!kj?`NdSLh%mI+q)6>N}ur9 zoKJjJ=j6-Ts~F_~#9w?;F!(hek}E!)8}%BaIKN}_Z7{_mIp;{_kNsqNQXTEsqOM!o z;jzK}-bM|zSz#eFO?ddF3sJ=#DLD$QW`8exMX`&H{EaYeFKgK<2KE7!dyDecZK;(lel%6Sty;WH?fLfUuJdytnH4rO29D>5fn_>)s<5e_iRl3B>6c^fUeDom$-4O z9vT)shlDz%KDMc_z^n!B(fX~z7E{o^%%!*ANOL(|m=bZaQG3FhyA(#Epzi|-k{(p@4cS7k*T)`|-ZclAiW%C90$>Hv#T ztp_R>cS|?ATlN(jE|=1w!v*^s4iX}a4U|82>p1uDhr~36TTlPbbdw0Ne_Uz=;uBw) z8dLSqX*qKa1OM4CID@&>_xsTMU)3e>l(<)NLkW~Bw;4GW7Zc5cI5J}eOGpRA8723& zbR@Mx@#IR$MQy5|Tvxn;?5Z>I%y=!90&R85w~;)S{QYU1CR#zZ>Tl|iWP7rELf2)CeMG;Up`o@0LW90}bbl@2+f0JbiZ(!z z={C~r67;ao?x+Q(8^!vi372i2G_3uRZJyPdoDhl1*%Gb*=5lJrw}m zb5epo7}(AD&0&g9jr`9fKTf_Us?W~pN%-=^gZWnl8~+@50rCUA+ivfQCMkMYqIyJ( zya~i;*%X;bA{jfH$|bfh3YIb1lUg~-HEWkhCm3mW3gmg)dT|NRHN;VoPkZ`MGgje# zkRTjn!YI&cdrUNf2DmEMcTK-dqlPrG<@2i;`dC#ko9n-Njlg30?1X>7iV4v0`)8Wp zb9b7R^HwTBrHkmE%-_v~m@COf3$6Z_H%K;InK}?`rrOITWUP%hK$Y2>DBqD2xRgOt zbz3hv#Q#Oz5>z+i$}+q-W|CH?Bcln$_GsJRH@bC{_IqT9T4$JF=HZoK@OU%7 z=BQ{llUmxFSLc;i7#w2?sxH1#4QYp)AG_WW&k63H&`yIOoe+A40Gi6ksH$h?kVxTa zf#FesiS2E^`P!0HPrqch_(GEz~b+9#Y z^06e@53Q**FW-OS9DQO&6gXF=2|f45W|)39veDcaK2=oTWU6*8LA}Xp>-*9$5F^l5 z<5e%>1kR`|lq6?HZC4&eP4R6E>d1UH-pZzV)iQq49ZYTOd$KX+(Uo%Hml{Y} z^_q+zD!sck)Av<@Q+S1ng;D&4zCyRXl9B0%C`+)_?ccp@ke6tw8}i zk6Unev?}`TTPCuU|9dW3PN+25NZ__~)T9vXtTe`^d$5C9&Q&JL%;1|KAhU`UNWR-m zQge&L)*LBufxc~z{9D~DjtV^y%I#_$1+En7lGV5>4vEjmP5ly1IwoIEaWDT)u_@__ zpOA$QW~^gHV!-MALx?vB5?Ncvz)&kbRoARy-wKuNA5R!(?3s1QnS0PXGl~v07f}C^ zTt@2OlyVrSN_Q6mf%;rp*Gak-gPOu9!fFL*NF@c{()4S^ZvtqK#`n8)w z{@6UJ9x;}l(ly2o){bsD7-j2uY=CPDeY7~oS>@#t0pHHr4LOK#Qvp^Ow*WB762}!?mpa!vXu<#=X}Pu- z4yrimcQSU{0Ao}5X0v?H^r24te_6yCc;Yg^$^j;KSHl3R)^VgIO&IyS%m`Vg+*?kj z@kEFPl-^-c3Ns17kywj`{R1VTZ42BDmmGPeBoOjYWj1I5Xfc#SH9tdiY1IF zxm6*Uyx}EQ6Y)?Pf6o`>hP!tvfasn3=!lMw9ry#4WSK17uQ*ojgay2sY`doV3okro z@;b{1*LHYi@tvL3Cv?pBrl{PcJWB@0`|?w1xzHDWr%Ef!)Vd{@I-GJUNN|{VQzRgc zR&ulGlFJ1kU5LZ|UNakdW-z0$cE8k2MxFjpt1`<;Sp8J@8nY?pJLFxQEAf8B4_hE- z4e*Ea`KGU&R@cxmNw1Xe_MTHUc4sP|1E1#jc;>U86Z2X4!2N3UJl7BQmuQ~cA6h0{ zYdjgDquroG4d*vO852}!e6`7Dj=scNuf&ihb0#)^R3l_AivxJ9jE;>TmPJN=$(zkd zoD)jVpl({Q-zLn9NXTU+8C>hw2|03ZGibAVP7gf72?&JCO?}E3Gt`qm$rN|G9kQ7N zOyj+2^Vpr2A@)bB)+JpzO)_sraxzIru=MGx4q^!YI?9Gk-z*(xcisSr5zjpb62P6@ z{`-r(fKKW__&G>vEk0q2EA3|4uxk3q1p4Qbfy_f;<#R8XJjy?Af>6gsi79p*f$r%y z$xkl^UAq`^@fVfTJCR_URyjBwSp4*I^@jwj^RL&kKF}|g^nv6rJh}us5Vd|#`Bg5V z>t5FPp)?hbPsW6jbJhA6{*u`-HMhPU+V?d~hG?M_NxVSGv8^SYKRhojiQ;|eaCy!@ zP4$P)Jy0Ds@@`tAmEbe!cqmHW=3+szO*t<_b@1GI#|x&NQEyRfDg?tHe>=U_625hy zxM4he4ped$F4q8MwZFcMrM{BltXGR%E)JSKoNX_+8Dk=hOdhv2obbJ|4&RL9cpEE) zdwV$a@^&|YAXS!qp03e&iM6asu|)MBcoxq&s=Finq+Nuh6{|PLu``{fJ`)$dm5~HL z4!J(l(Di>4oN`61TGv!Q2NXTZa)B1|mYmjW88$pivNk+0nT-e|!%xSRqnPL_2Z3(z zm2SV!`|kf8vNA!$YnPm}t%$sP@TJ1YR)bwe%@%BK^G}Gv1FELe7t%0m#NrPN$$$Pf zT0|~ira!(+pp@D1SC}n$DzqfTZl-?*pK9pVVtd_7N)_LTx`6I0Y8p-~q?^uFDw0~G zZrKVKNk}^Tt*wj&KHbR^JG-U#w_~*rbnJFjN|@>I zlY8fgq^odPm%&oDzrk(4I=^NWq}1a!2kmJ^(Afmb7${nyBZLTk2X$`_$UfFG&*_sG!7SZ~Jn{s|b`d zE6G;3LGgwE=FuA$p_`gfI4JIxEf2l5X`yFunXd*F{%Kjg`Q+zPUu_kVQsIUiQm379 zY9K(Rw{15W#ylPl)7{M91N%KP*Dd~=y_IV-YHLgi>F!eSN?wicE2T{crdlJVb!y*b?(ACelFUxoozsvNGOR`EvxjO0j928)GYlu$Jyl*{lCvQ(kuVbl=uEL6 zu3k5L_fL9MehP^)`E*Q~)F$hkiX{^h~mLjh|9Sy+|*ADdb1Zq)0Q_a>A%=kPP`AKfWhJ_iUD?lNg_bF=(+zH^K* zPMWrkc5NjHbflDW9WZrPD8ZF|Ikd?DlLfC{o2h=l*uP;_RylG%MPn9igDCK%hqRel z(_dH)lx6#UaA2)+3xlmy%@g`a-o(y)W-FSc8GKbRK4HZr8bIrZig_d6y)?k!E2dCG zBDt&sw0|Q-Ha#AlebW4hPI2|c#EL?GslOnB=Hv5r%57P9NUpu;BIi)L40>tWP;VC_ zn__E540e{T-i*0{1uj(5Zf2BLIA6>K++T(FldXOy$!>Qp^PPeBdW0Gq1M?LAB?=W* zcj$WHndKwkKd4se;wcf6#}~(vii4K-J1t}3VaN-xe5+b~2DXm?g#Yd1KTWQBbL-gV6f^WcHni&R zxu~Z%R-Hxr)78(Gmj#be-knO1mQ5gaPHmD^JM8&1N$5?tYv+9?_ejZc|w>$SZ; zTLyWX;b*Ko_3cy|(_7e$r9B3vT;du8ywzY_)Fa*mY zI&^WKb5!%cBd0&YOP*GzhOZ^XoI6M6WOw&1!|IevCOaQX80{&P*Tny~u%wEHR$s0u z)&0Pzw@B>V)=j))FRO7g-?7eX4s2>-#0z4{Zcf-?mEGi9brUYS#4v^Z2LimZnM-%K; z=XlE?ggFj@sza?)v(FV%9307g(WxVYsoY+STu{tX&$uKA%Wg(sBA%PV?CGuLVo2u_ zL)~1Zp7gE+JrL;(D^*h4D`qoazo&z%3W~lncJy)48D&EzqEp8_`sdkMq%sY+1Zb<* zF!g_I=KgJfEHudjZ7&MeRUH_bAIH}l#K>-&4_|%HfhrwNt%ys?1j%!M)cUeo-qp~_ zsj>3k@8fLUk!HmT>8z5jY3lm@7T|{A=n((!fx_X7NwpQTcagr+iE=u>pf5(5_EL2|0QfPE(F16$L9q; zR*J5`(qDY84umQgawHF;4I7rgc@@*E0|jcf&>f-7?uSDbq$@3tGF(PF2EUO)DK@1l zz)lJz0sCAwPuv=J1Lt|htTI?lxM}NH4Td+(PSYc@(^>at668hFd zM)P3^O`3b0kzm_Pz45s4wS{$k?vGIOtJRMbR#8TUbkB+g2hLx_W{{?7Ufai$w1~eg zU`oA{L3VpE2efl#vzCFWQoFPhvN!WE=~UcFLol)-LVIM2a%u}9^|P`Rzpfv}V$@~> z*ENlHR3}<_3Q-pCGc+~u!<kA( zE~Z)QlSB-V&xq74EwT~=S5&4h+350;^~~e92T7zl1iYD6GX0&`R7h82L$wQ1i;hyXl#AF+A9#?&tZ4nBJ$7 z-l!VM^Mp<5W}a^)_%&9kuKHEZzS7`DOKsF^Uc7XU$}@Hk2Vmm`Cq+ zSvl|lGF&*{>p$>N&A~Rk9QMUcUuu7s?x0-3<{TfSFvI;IGeKV;#$$rwsX-b?UXW9K zN;1K~{9ZMS<6e%LeXBQ;TO~n|IfVkyxDZ5>u#T6%mD!y*31 z@HP_RcR#Z2=LIA<9aL#LCFTM~K+hhvT6mK*q)usJGK!T<4zmig0^`UwlNeDEOJ7N8<;N)hdivTUja%P&Ifv$PFA8p^PfR!df)==Jj zGmRq}iueiy|8bo{hw`=?Qc5C*Ya(ee^$fhi3_=*i#;0iL6lEwro*W>Rpfmr5K#0Ae zuJ`DpJ5ItSB&!rTtGOK;VyVb!ZNe&S9!8uNo9;AyBqQiIVyvPDF-aJYnS>4z5?OLT z5iJLgbQRqqV0tqFR5Iej$%^nxJQYld27Gj~@J1h~aCxJeynKu}C1g=4>OLqxXs`ax z_aW)ZRbp!8stv=7s-a4rNEg00+oD+>tr9aL|GV>-A(b+K>tGMbdQ!W4;FT*50$CZ? zc83t=?Dc!qy3UnTNe@-qNS=GE?}U;q_vh*}R^^APV$zog5^AvXtwYtJAbS$W`pwMl zva0{FmDBrg2Fu%z@ynO&*FR-O+sYZg^ZsWw;vaOVpo7L}x!u9pc~+XZO>V@FF^^jf zi{m66(6Vl*5p23HlPY`N9ZrU*$>+yV7~$DAW8j*F5pY?ckM)iLWhz{~mmWyS8Nu)x zPyhSp=?0hgmJ^>6DOs5~BdkS0xJ2iBrCxKW(lA!~Z(W=B?HSs$L;^I-7afS45L48Q4TLkZ3!99J4(symQC|3jB~SS>j&O9ZNhuoix!Nr z?nV@K1O(C})a;g3LV^ZngY1sOrq}zq!tYOA6J4bYrThF?{7&ln8{Up22#}h14jDe! zApt1^*VYMPo<()X$JqdPR6<4RXnmn|=i`Ch8JAQ*t@5&@+8;#vSRR(FVZHXf2qp5H z7J0vQ7CuCBkK0a7HMniHWCUqi-O_!hJs1!W8Ehy)a`T(%dS(4%*%GFPUD&5&z#HIT zM*0HbZO~rIQ+>&G;*_{Y)|IWvVFUZ2cSfOi8uTu&s#A*a(Zv5a)I;9Y7iSa+i{UweO&#;0_Zgqq2%J7NHR0SGalEA>rQ3|S&O%`s;*QC z^ICUrpO_o4|4^*qt^gPcD*L{#BNVq4U(yax+`r?h(oYMCtfN3xaW(9jxgz{-x$`xm zX9iBDKtCO#Y18lYTEY*1m+``kX|6x42Vd74a(s;1}M3>{skJnwz{y1cF5Ki3`hp(5a&lc143=R#ux1xmM zGt4S|fZ+_e`Wx*nqzqr_!T#}7kVlw97T#5-@u5{0om z^(!j}vXWw*tU^A}cD9kEI1^J@i7ayw>P+>6iF0(tafhU!1}~>@%%wU1ufd?u7LlO5 z>Vvzd3T7&~Sj%^%4mvuzUMQjM6=rJgk>XJmb(c)}L+O^)(8^$?a%DO5I4MdQf(oYf zPgx1&27fd5mR!UPFRu{Z18dKgW1gngkd_gBMS6zvRak$h0}Hy{=)ob`6I?@B>mDD~ z9`_K8XrNn#?^43b7UlepbYe|C`Y7#y);!_igBXDzgwOxj04<9b?dV!?y1%OKNvtw_ zNZ_$jI++8)0rB3viXGXtKy`8Lt9PI7MiuB5bDFiidyi$c;I}*I`jNJcNnKb8(`!_P zcxi4LhI2U^8_>GfJ;)D4F(PL(KGk#+D3JfLC8r0oz>>)oHYlB&G-Ly&1UR!~`z6e> zal+o#-WB{;hZ;THsjMz^P89z;E6GeSOBJ;^bIoYBGH5CdR%)$3#_VN|z9#UkY_L9Q zXgKksUK1T+H21ES7yx!|M|iEvlnxlln|E?|jj${RVCVbGfh0%O36ZC2!Z_uKB~+)7 zRy)%T((3XJRNHs#a^ShF(s2=P0c~)&FBx!9U#dKDs4?QFG_#tT#vZW118b6-o^VfE z{OVXKxs=UgVUbd?x%4v^W|Xr5BWc(7__toAe5oG1Cf4RcP|a6L(N4K0j{Y_3)CSD_ zDG~=cR9c3f=*Z&7r6RceoCclQmt3q&$?9^ z-IXvl+=);k<~}l5$mWRv_^&VkPb8#ow!eyYwVT!U7e5yH|JTreP^3im@ zzc%s>ie5f!@0yt>mf#P_A`X{D&yQ=WNNV&(6YgB=#hNUS2WQB|D^G}Cu~e*b3-Yu% zczp3f$iqnCb9^V~rJtGUe-pR;bhXH2n5>LEA9Epn`Q*ZnTM_@@=ulsqj{v7TDV#ae zeen%?1Vc@iN9olBaoSI28S|1i<9oXiIERF<`UxoID};uuZAkd__`9(E_u^&>gVo6^ z&HQ@Rdk+&`9{FH(=8Eu({EQ;kG>sq3%D?7bqaVOZ9(bqQjYVGn>JHMB+>ugb4IYb^ z2=*48hFK2~JiT4)CY*%3Gdy408d)ej5G#Aq(*1GCU%nrI*nW~cesKz;$@FMIGHm1r#ax}pn`U} zXBQwLrQG9N9UlW?TZ9F{C;Sk9tBs%D8iTI8Om8W%-f1k)Ni6$MqKZAjsNquvPi1>P zhW&qRqBbzUZOE$WjK3La0XUxp}9@hRae(NX4H!(i^PbQ0>CGPYu&iQV*-tL^H zpBF9cMDq|MjE;rldA0&R65$l0$M~`Cd`A{F}y?*Rk&(ZQsd2TM@p)8kvTZ+)Rq;n5lnb(+&E#>LdF6 zl??IE;j5-+Ec=18g3zNYsn>}PF5I=Jx=J=BXR>*drBNdJQ%AFG_CE>1W|n%whm_}i4Or1p(cgzK1IyZ$Grm+- zWX$Sm<1?KnKsN(=s8n6<1Ea+Lz`b5FTJB3OTQpZ&=Is^#**@XBO~7cYC(|VzdZWG{JrsitQ>StPD+1 z*6&w3v)(Y6x>BH5s!3iLKXQVDAv*?`Y_t4EjD&3uBXl^o zHysR2D7^Z_ zKQ*p@iqeg*2s88=moF}oi%{N?{8mX6bGmb42>j#7N{QP)*jvhyO(@AO5TH0;MQ_J1 z285U1=G(^)K0Pwy0#<9G=w9-d%`VmwtgFrBwUevrKL>V5`SyHH6{OCCmso*vsvB+H zT@#viviC<{&sM!W`a&x-lN`yr^DfK@ld=@?JBNSWZTZ;-z{(AHHW{tW0^9tc?9uAxQlocDKW-S?KH zF6^AEk4-B2H_6j*Y)!L;(G}zao-6d{Sm%#5?^`M#&4_W(xb0?!!kQJ>A*2karS**8 z`Sab@; z_5Rw{mi1r8usBxj&sD$EbDAH6!5vK-be1Y}WY@D6DV}*&=^Gh5Bs{!8ccO+Z{8&&D zj~_{aS|_~mT>l^2IVXTqzAn4?{<4lB5}unV(!Z3J*qB*Yr9OygiW$aArW-uPGKc0q zCIm#xXtkp2r7Sr$BBX$gC;Y=PVu#oS38H1Pv{X8CohNby)gTh@2>v3CMR$;rU6{-;d6wnweMizCeK+R|h zwGjM~M-e11dwP4TRXf_>7?9>CjWzJ~wq5nObLoz@u+npurE2N=^kAzZxScQY&H_vJ@65=f<6rAnM;?tD%M1A#i{kZeSZ z_-L8;IOje|yo_C5fE5Jdth5ku@DYMH_ z^^-Buc~`L)53a?b>K}t}|H%F0tFB`Eg}n%N(6LPM;d%dvyOfI8zCQ1aQ6}f^ zUmSYyl(f);up^;FQ2hMuC%9r7{XElZO83#*as1cjB@YuRIl%}? z8OmQIM~{KOI$nL^W><{5?gQ1l0PkBitX4;Ym%}VdWnkTa+@1QjU(64rpKZl^q>R_C*m9Y2v}C8go5-r zO97Kz4pf8`?y8PyjKS0h6%y%F#R;H!F=08|(30?RoI!CvR`3I&Vt%rE;To&4j% zF&lpc2q5{7g_nlqv12i&c(CnWRlI{wk5?ASDrYquNP`$cANFdkdU0iSlFRN`SB88y z6V0H}hvF>;$C~OJuk+~MGd-ssPlhpE%{nAR`F&|0H_Q|NiljPRTjS8LXUtqHZ5=gndp=I=1KSisZ&yL zk7AvA_2uf*gcQW4c_`su3L?ZZDHs1QF0_&UiD!hg%uzE0I1{b7^licBO z{GS6QZ6&KMV#90qxF)lKhi68)a~bvSK8>p0{LlH?Pn4!*?MjnX$1=nIy>ow=;OHc{ zHE`&tlH2ge!s!!>LDS}fR&tz@q!7eWznKwJVC+?sy-|6+mP!@UzB8^u)8*#PoEpOy zp;38x#}dre`>67y!f6=)QDCN7u4IBY)V^LMDr~lFNlp(nb#^`IR;q4_MqMxdw?}Zx z{hdvcd{vnpT}*CzEc&+2XZk3vzzB65+)ci|E?5CGH>|p7!w;MZY8@|kI9Z%ehteuorhJ-S1&v*d?@wxwP zOlK`e+x4^joMly~g$m+QONh5qm;&t%uvXabi9Ij-RRla-L&+g?~{(J zOX9c1=>B4X`-7Y}BXCOU)N0F~OUt);PNu)2g`p9`)30SxPA&|Y56bEQY+pp z6dy8!lRd_w&Moy%wany&3M&!04(PMqDHdy#IyExWHPx_)!9UKk+elhGI?3W)Sr2dz zRyX*wurxgm#=xRPK8L&9wp`2?aq{1aZwbFk?~$RFVbY^(Y}`Jl0GekpDp3U=#3s7R zGDW~uMFo80cDyYUG_0 z5oG37yU$NSB5aL@AB#&?qOCXS!RogPL-b}?#4jb6kIP0-Xg`74eYp__t6y$b#{%cj zJp~|3lxe_mveEGJKAmykQI_Fa-^;3^+&2@UN_S2S6vxpS>8v>4wBh(pqY6vq-jBOI zswNSUhFpPV=TDsqY_kU8+vc=ZlV9@1RvDEi$R?2}fmy|n zrW1f>*>VmcT(@xSor%9=rZiDWbC&9!5}8vp!~Qe7w%?(Z}bsGXtP9r4P_TSk#<3_qAI4!4yYFd@}bw|#b^8zvu1 zfU~7a`MTnDbOF(Z#((t1SOeg--H|&F8wS6m2poZ4v~)+~nhx%#fRWh}ZP@S2tgmsX zu|idJ&t-|7QxZQnFes#{j~j;HP9JmK-?!{f{lE29yy6jy(Z}C_gJ@^f;)?7i*-1Ov>7vk*IFBJw|N-XF=H$(B8)$b}pLW;{& zaHMXwJ_?YJ`&C7Jc4Q}`tQA(;*w5ah2BYWnU0WddFbR-~*kv?yb4GJtN6Yn)1^t&? zoLTi5vWjSp!Q=_to@L~Gc=!S~zr!&!luA{*iVs(`#cT%O>d(4q-I$t=5=xTWT}&5o zcH}|RugtxvnZCi)d%FJX(xMm~r8~3!TsyJB8dvyn4HaHA$6CXm+^KZd)&4k(QKm&V zh;WUE5(Ge!d*8a-qIP_igDF66){_?R`JdNkU@KTTF_a*rtfN8TA} zUMb1Mp$LF9YoqTK<*lFIwIRe0>ZcfdPuhZJv`N_!V@s#0#s!dk1bHFT8N-`QO8Gt_ zue7yupydheCg|j8c>vM#_!o)mkq%<-Mf8Wylc9YnUrHPC`d-)Ef*00S6Mv2PU1^-s z<@UeYN&cesO**%OA7lh`B~yyt;y`N{k_~tR54~Xic8bqf5Q!i5>W&}kCc2Vp0OCV9tgq6P@{VZhAjzBNpLi?El#<9FH?o^j;a~*zP^6Cd8uDrwI%9x z-Ytvf6K)C>aF$VP+R&&@?>w7f%M%kQmFGs7!bAuJ)$I6pCf+oibf+x$Dc`g<$NRFT zqX+dR9_DbL9iBjfwtrurOpBT@GB**6b$UIlpe>L$CvB?WS*iuA&&&0hxPiH%n%j}T znCUa4-I#Tta@*6I@oXQynl%hwX=aY_OE~C2s>x32t{3*@bEH}%K%0kkRx!C%)XA4d z|6@DtUqvisshLI^XeJLXj8VqClR%39j4dV~D^VuWAqo%X<4i7nZ&P+HU>j6f7+N;} zq%_3$eFZek&Xxi9=uX1ooTt2#ra?VoX8ObR?HDCE_BGN?U z749~W3Avy;{E~m;m8f{(CF@TroXV;YDWvg6K){*SS>0j7iSl5-YSC3(=KI~k2 z`SSLK!Sm#+MKg+|3&yXSC^qE-BTBKVp8dNuu3ChS@!UMrmDfP#l{)Nbsp(%`1hYYuDKyfD%7Z+J9WRWp(+2 zPdsRHFyUVr7n7zAL1CWDcQkK5di9OBB&~shG{r*WjZH(8Kk}A^Qb{P=Z(cmPLFrr6-H`3v z%M5)eFem}iW@Ds0HOTX7JueEHlVPdEGQ}64m+POnkl@{sfzq#20f?nG;15Iw_b`~9 zo5w)|ziQ`Z0<#QZ2d%18@IgrH!E=W$wYqWI?#?xz$DAFnn=KSKz7A#*<1pr+wN;gw zd5puODMTYA$-;)jVf1q;A&LAc4+%Rr5`K?bYSLMZTv(Rwm_|dre+Uab_?@R!^K>OQ z!b2No8Fj8%47ipbE|TAHX@(;5lme^fObF+(*z=4j#t96Nh{FaJv3~!inkY7J{)eb= z_cKZ^$qCDF=MYDT0K@$iBS>K3W=@uDvE=KqQne} zCPwVN`Tp|z7oO+!JokNF*XMe_8-+Bvqfnh+)<4N_0jc}0 zdN%6CEUGWzff5Jm)c4gB?N_R?WKzNSC8EB!w{PP#R>?DbS7@Arxa8pO-&!2(2cP41pw8HymhI)8W`Bas zP|Y%b&A$!`=vwU6s#|FY5VcOiQ8EReV=xB4AO%pCyQh-)kpOwHNrMOyNU+?4@XYLx zr#H)*e>(OkRu!ZQj_r*7$+Bl`o{9xkLxubL3x1ppGu^fe;Fg($oo9mi23!hr*SA=8 z9@hhBy^RoObaR1MVQ+gL;daBtH2tb$%%EmE49jMCmBg%WoweZWo|Ol;81&qmLd|Wk zkqf($?7d|}BYTkpv36{8qCU)gZF3({+NX~}8vgF$U^5ST0=zcDz-X`gjAF z5(GgR+b`7=~iw%1`5);d8>d%dF|oF z{GkTZX&-b9HHNjE5|-=7^fQA`X7?kP{$8t|=y4$u46Z;IIy4tI!c-$`8w5%Md)eN| zJ5~eD3-1jSRsVuK#^wQ_! zu=!C?jfyM!gKsm(!IW}IsBD5~?=@xi)aeut_S+twCVf+OmsNsVsbpmMii)2JaVj#& zU1%arPm<3yd57xtrC;PrGCe@5%01wwQwr|jxkn^8BK%V`#Z*9c9#(q5JXBqlhY7Fd zB3VW$QPFCrqS6o`uOs2L8J)7#P@3fwlM?P1pws#B>V>;PT!>D1kN z*>FC;1b|`JNvX?s7LpAWA&)u{6y&|9k&yLa!vN2p~Pv;3T zKUA0NK^Gc^v$B~eTZvc;rRMeecdT(s9^QPogN~440|I?#w>Gu8f1XY%`~5Sa7RaVttO-a>X@o* zgEOCZzQ+b*&)fq`RdOTU0#K0lnXvR2!ntQwff>83WKk@5b`8C@749-Ib41890GMj( zmXY~z!E>h>DZp5~Kwf3>%pW3<#rMEe3OV*5CrhdL#HKtl54> zXq5VAO?S$2VX54N)=1i4@l*dq2n1V&e@UF2_1k(o4c|N!jHD;-W&~*nr8IuQuFmY} z@uzU%#sm}Hoj#}o1kHI*(=e{wSP^h;DO&$k-j$PoXhuhM^#4aJ_{Q4AF;`U`VuTXN z?WzWx@@PT*f#Ep}v|H?qHG~N(gh*Nv)7W{rwQlT4YE)6nbWGDmqYaHn8x9VK?2>;%mpCu90!DHA8 zb)@dS_JuWUg;unzm(B{r`C2>IS|pt8(mc*7Ez-X^-;tFq->a-u;B@NCwS zr0@SBu<$xj-;G5@N&qy&$vU-ta#suo^xz@|P|7PzY*!1Cj8iEoh3T=!dllgSG)68q zMD1}WgqcDBNrQQZ8h7f|_I!RYBlW8RPK}rY)z`?q9mlt8q{OOy9jMt}SZL5x4{?s3 zz7w{gn>Czlm*LpRVu>&!S?55 zYG>OGTZe(iCQF6fT)A4M47my&DxhB;vSi)GV$*-wX-~fmKgY8c-<@f8iC-Y?YAI9nARiu*11%k z;(WIoBfH-RtgSX43O>J_d)ck9_4)9vJ21~5!qSHhE0@jMmUTp?Cuk*y_ZzkO!{k