From cae36a52ff0a603a309dcf7d5bae55820b89a71e Mon Sep 17 00:00:00 2001 From: ian Date: Wed, 18 Feb 2015 16:43:27 -0500 Subject: [PATCH] add linux_emul base, reorganize docs --- linux_emul_base/CHANGES | 623 ++++++++++ linux_emul_base/README | 138 +++ linux_emul_base/bits.s | 53 + linux_emul_base/bootstrap/tar | Bin 0 -> 753608 bytes linux_emul_base/bufproc.c | 263 ++++ linux_emul_base/consdev.c | 157 +++ linux_emul_base/dat.h | 281 +++++ linux_emul_base/doc/ioctl_list.txt | 612 ++++++++++ linux_emul_base/doc/linuxemu.txt | 117 ++ linux_emul_base/doc/todo.txt | 14 + linux_emul_base/dspdev.c | 377 ++++++ linux_emul_base/error.c | 266 +++++ linux_emul_base/exec.c | 647 ++++++++++ linux_emul_base/file.c | 760 ++++++++++++ linux_emul_base/fns.h | 311 +++++ linux_emul_base/fs.c | 758 ++++++++++++ linux_emul_base/linux | 114 ++ linux_emul_base/linux.h | 352 ++++++ linux_emul_base/linuxcall.c | 79 ++ linux_emul_base/linuxcalltab | 286 +++++ linux_emul_base/linuxcalltab.awk | 39 + linux_emul_base/main.c | 259 ++++ linux_emul_base/mem.c | 1538 ++++++++++++++++++++++++ linux_emul_base/miscdev.c | 156 +++ linux_emul_base/mkfile | 67 ++ linux_emul_base/pipedev.c | 202 ++++ linux_emul_base/poll.c | 250 ++++ linux_emul_base/proc.c | 1777 ++++++++++++++++++++++++++++ linux_emul_base/procdev.c | 732 ++++++++++++ linux_emul_base/ptydev.c | 944 +++++++++++++++ linux_emul_base/rootdev.c | 1286 ++++++++++++++++++++ linux_emul_base/signal.c | 1387 ++++++++++++++++++++++ linux_emul_base/sockdev.c | 1163 ++++++++++++++++++ linux_emul_base/stat.c | 437 +++++++ linux_emul_base/time.c | 160 +++ linux_emul_base/tls.c | 232 ++++ linux_emul_base/trace.c | 107 ++ linux_emul_base/trap.c | 110 ++ bsd_man2_all => ref/bsd_man2_all | 0 39 files changed, 17054 insertions(+) create mode 100644 linux_emul_base/CHANGES create mode 100644 linux_emul_base/README create mode 100644 linux_emul_base/bits.s create mode 100755 linux_emul_base/bootstrap/tar create mode 100644 linux_emul_base/bufproc.c create mode 100644 linux_emul_base/consdev.c create mode 100644 linux_emul_base/dat.h create mode 100644 linux_emul_base/doc/ioctl_list.txt create mode 100644 linux_emul_base/doc/linuxemu.txt create mode 100644 linux_emul_base/doc/todo.txt create mode 100644 linux_emul_base/dspdev.c create mode 100644 linux_emul_base/error.c create mode 100644 linux_emul_base/exec.c create mode 100644 linux_emul_base/file.c create mode 100644 linux_emul_base/fns.h create mode 100644 linux_emul_base/fs.c create mode 100755 linux_emul_base/linux create mode 100644 linux_emul_base/linux.h create mode 100644 linux_emul_base/linuxcall.c create mode 100644 linux_emul_base/linuxcalltab create mode 100755 linux_emul_base/linuxcalltab.awk create mode 100644 linux_emul_base/main.c create mode 100644 linux_emul_base/mem.c create mode 100644 linux_emul_base/miscdev.c create mode 100644 linux_emul_base/mkfile create mode 100644 linux_emul_base/pipedev.c create mode 100644 linux_emul_base/poll.c create mode 100644 linux_emul_base/proc.c create mode 100644 linux_emul_base/procdev.c create mode 100644 linux_emul_base/ptydev.c create mode 100644 linux_emul_base/rootdev.c create mode 100644 linux_emul_base/signal.c create mode 100644 linux_emul_base/sockdev.c create mode 100644 linux_emul_base/stat.c create mode 100644 linux_emul_base/time.c create mode 100644 linux_emul_base/tls.c create mode 100644 linux_emul_base/trace.c create mode 100644 linux_emul_base/trap.c rename bsd_man2_all => ref/bsd_man2_all (100%) diff --git a/linux_emul_base/CHANGES b/linux_emul_base/CHANGES new file mode 100644 index 0000000..138a094 --- /dev/null +++ b/linux_emul_base/CHANGES @@ -0,0 +1,623 @@ +2008-08-16 +Creation of a CHANGES file + +A entry starts with the date followed by a newline and then +the content follows. Usualy, the first line after the date +is some short description and then a loger follows. +To terminate the entry, intert two newlines at the end. + +So this entry serves as an exampe. Hope this is simple enougth :-) + + +2008-08-16 +New debugging implemented + +Debug code removed from mem.c and trap.c and rewrote in +acid. See the DEBUGGING section in the HOWTO file for further +information. + + +2008-08-17 +Fontconfig crash fixed, Debug code fixes + +libfontconfig mapped some config files with len == 0, this +was not handled correctly so it crashed. + +the umem() acid function didnt check for zero segment +pointers so it showd invalid data for the mostly unused +SEGSHARED segment. + + +2008-08-18 +Some minjor fixes + +more checking in memory manager +sys_[gs]etpgrp implemented for pid != current->pid +renamed emu.c to main.c +set UID/GID/EUID/EGID in AUXVEC on exec() + + + +2008-08-21 +Making prof(1) work (at least dont let it crash) + +mem.c: convertseg(): + +Peplaced read() calls to pread() to prevent profiling related crash. +The problem was that convertseg() detaches the DATA segment reattaches +a new one and used read() to get the contents back. read() was a +profiled function and the profiler finds its structures cleared to +zero and crashes. We now use pread() that is a unprofiled assembly +syscall stub. + +Still, child processes and kprocs are not currently profiled. + + +2008-08-22 +AF_UNIX client sockets implemented + +Its a little bit of a hack. We do the AF_UNIX handling like APE +does so we can interact with the ported Xservers Xbr and equis. + + +2008-08-23 +Work arround for mozilla GPFAULT bug + +If a process is notified that he has pending signals with the +"sig" message and if the note interrupts the execution of a +INT 0x80 instruction, a syscall in the handler causes mystic +crashes i dont understand. + +The work arround detects the condition and delays the +handling of the signals returning back to userspace. + +I could reproduce the condition and this hack seems +to work. I should write some testcase to analyze this +condition further. Maybe its some kernel bug. + + +2008-08-23 +Minjor file related fixes/cleanups + +- Implemented in miscdev.c for writable /dev/zero +- sys_umask() now returns the previous umask +- default umask set to 022 +- sys_umask() sys_cwd() moved from proc.c to file.c + + +2008-08-24 +linuxemu.rc script updated + +- resolve relative rootpath +- generate /etc files for hostname and resolv.conf if not readable +- removed enviroment user->USER home->HOME conversion in main.c + + +2008-09-10 +Better work arround for mozilla GPFAULT bug + +The problem arises because of the handling of notes in Plan9 kernel. + +It happens if linux code does a syscall while here are usernotes (like +the "sig" ones) queued in the process note[] array. Then the trap() +function in the kernel will enqueue the trap in the queue and the user +note gets handled first. + +Now, after we have done a syscall in the note handler of the user +signal, notify() gets called in the kernel that detects the next note +to be a trap and thinks the note handler itself caused it and kills +the process. + +I suggested a patch that makes sure traps get enqued on the head of +the note[] array so that they get handled before a user note but its +not decided to be applied yet so here is the work arround. + +The work arround avoids posting notes to a process that could possibly +cause a trap in the future before the usernote gets handled. (this +excluded all linux code because it can issure a syscall anytime) The +only time it is save to post notes is if we are in linuxemu syscall +handler. + +This is mostly the case anyway when notes are used to interrupt +blocking syscalls (waking a process sleeping in a sigsuspend for +example) + +Linuxcode that would spin in a endless loop will not be interrupted by +notes/signals anymore. + + +2008-09-20 +Minjor stuff + +Masking more fp-errors in trap.c. (To get some dos game running in +bochs) + +prboom and zsnes was bumpy sometimes because our select() didnt set +the struct timeval *tvp to the time the select call did not slept. + +I stat function pointer was removed from the Ufile struct and all the *dev.c +files. I have no need to return custom stats from the devices right now +(except for /dev/zero (mode & 0222)) so i move the only case where its +needed into the hackish dir2statmode function in p9dev.c. Here is also +an fd field added to the generic Ufile scruct because most of all devices +use plan9 filedescriptors at some point and we use it in fstat(). Saves some +casting and fstat can use the fd (when its available) to make a dirstat() and +convert to linux format. + +For the postnote stuff mentiond in the last changes entry, here is a global +flag in main.c (notehack = 1) to enable/disable the work arround. + + +2008-09-21 +atime/mtime, AT_CLKTCK + +new syscalls +- sys_utime +- sys_utimes +- sys_times + +the AT_CLKTCK entry in exec is set to _tos->cyclefreq +now. + + +2008-10-05 +faster death proc detection + +Using linuxemu from terminal caused huge network load +because we did a lookup of /proc/# on every timer round. + +now we keep open filedescriptors for /proc/#/args and +/proc/#/note in the proc structure and only check for +dead procs every second. + + +2008-10-05 +sound (/dev/dsp) + +OSS sound implemented, its only tested with some SDL +apps (zsnes, prboom). + +The output frequency is hardcoded at 44100 Hz and 2 +channels stereo at the moment. + + +2008-10-20 +dsp, stat, mkfile, -d, dns, fs + +dspdev detects output frequency now and implement more +ioctls. + +here is an universal Ustat and Udirent structs to separate +the linux formats from driver code. + +big chnage in all devices, added indirection layer fs.c that +has some kind of mapping from path -> device and forwards +filesystem calls to device drivers. now drivers can return +correct stat information. + +this is needed for implementing /dev/pts later. + +removed the /etc/(hosts resolve.conf) generation code from +linuxemu.rc because they dont work in some cases and +cause maintence nightmares. + +use the -d switch for turning on trace to stderr. so we dont +need to always change the if(0) from trace.c and recompile. + +force compiler flags in mkfile, the -T from new mkone breaks +build on current distribution. + + +2008-10-27 +lots of fixes + +- off by one error basepath() +- chmod used access which resolves always to link target +- runlink +- the note hack broke sigprocmask, and sometimes failed + to interrupt a sleepproc(). this is now handled in + interruptproc(). (this makes drawterm work on linuxemu) +- write() to pty was not synchronized/not blocking that + caused some apps to spin in write() or others to drop + characters (curses). + + +2008-10-28 +grow filedescriptor table in dup2 + +- if the new fd supplied is out if range, dont return error but + grow the fdtab so it fits in. (this make configure work) + +2008-11-15 +lots of changes + +- Updated HOWTO + +- included in some modules. this fixed the type + signature errors of 8l when compiling with -T flag. + +- fixed bug in exec() + exec needs to run outside the the note context, because it + is deleting memory segments and that can cause the removal + of the stack segment as well. we have to set current->syscall + to nil to avoid getting a note posted that could interrupt us in + the middle of some malloc() or something. + +- sys_pipe() FD_CLOEXEC + sys_pipe() create the filedescriptors with FD_CLOEXEC bit + set as open() does. this is wrong and caused gimp plugins + to fail. + +- new signal handling code + signal.c has changed a bit. now CLONE_THREAD procs + share a signal queue and proc() uses wantsignal() to + figure out what process to interrupt. + +- restartable syscalls + re process SA_RESTART flag now and are able to restart syscalls + that got interrupted by such signals. sleepproc() returns -ERESTART + by default now. sys_poll() and sys_sleep() will return -EINTR in + any case. + +- fs reorganized, [sg]etxattr added, p9cwd added as an optimization + to avoid walks(). + +- ptydev emits SIGINTR to process group, added tty to Uproc. + +- added fddev (/dev/fd) + +- exitproc() now emits SIGCHLD. + +- mem.c: addrok() now takes a prot flags parameter so we can test + for expected memory protection too. + +- profme(): profiling support for child processes added + +- initialization completly moved in main() + +- fchmod, fchown, ftruncate moved to file.c + + +2008-10-19 +just code dressing... + +renamed some files, added typedef for Ureg, abstracted syscall specific +code in linuxcall.c, more tracing... + + +2009-02-06 +Fixed the opera fork() no more threads bug + +There was a problem of dns resolver zombie processes created by +operseemed that used up all the process table due to incomplete +implementation of clone(). Linux specified a exit signal in the lower +byte of the clone- flags parameter to tell if the process should hang +arround as zombie so that the parent can wait for it. If no signal is +specified here, then the process should exit without becoming a +zombie. Here is also the case that the parent ignores the SIGCHLD +signal or has SA_NOCHLDWAIT flags on the SIGCHLD set. In that case +the child should also purge itself. I implemented reparenting, +because i suspected the bug there but this was not the case. + +Here is a new uprocs() acid function that dumps the proctab. + + +2009-02-18 +Minjor changes + +Added anonymous area merging to reduce the area count and removed +redundant clearmem calls. Changed Uwaitq lock from QLock to normal +spinlock. Removed some trace() calls. + +The biggest change is that linuxemu.rc now is able to start equis. This +simplyfies writing wrapper scripts to start a browser or other X11 apps. + + +2009-03-25 +Simplyfied area merging in memory manager + +Areas are doubly linked now so its easier to get the previous area for +mergearea(). + + +2009-03-30 +Fixed man-bug. (Restarting syscalls) + +Restarting syscalls failed if the singal that was send to the interrupted +process was blocked. This is fixed now. + + +2009-04-01 +fixed awd-bug (use builtin cd), make errors more verbose in exec.c + +bla + +2009-05-11 +fix pipeseek, added pread64, pwrite64 syscalls. + +forgot to return -ESPIPE on seekpipe. +implemented pread64/pwrite64 (needed by git). + + +2009-07-25 +random stuff + +- incred bufproc read and queuesize to 4K/64K (fixes links2 -g hang) +- reuse buffers in bufproc +- cleanup timer stuff, introduce 5ms min sleep time, avoid interrupt note +- fix sys_select() to always modify tv +- fix format mismatch in nextsignal +- dont combine in sys_readv/sys_writev +- fix open in devdsp.c +- s/memcpy/memmove/g +- dont reset segment registers for signal handlers +- possibly more that i forgot... use history(1) + + +2009-07-27 +fixed audio delay + +keeping track of how many samples (time) has been submitted +to /dev/audio already and wait when the buffer grows over some +treshold. this removes the audio delay from games :) + + +2009-07-29 +doc + +put documentation in doc subdirectory. + + +2009-07-31 +mremap, segbrk shrinking, pagesize, doc + +rewrote mremap implementation to correctly clear area for shrinking. +handles more error cases and checks for overlap. this fixes the gimp +invalid pointer bug. + +removed segment shrinking with segbrk as this feature may be removed +in newer kernel versions as it introduced a bug where it is possible to +unmap pages while the kernel touches them and cause a panic. + +removed the ROUNDPAGE() macro from dat.h. heres a pagealign() +function in mem.c now and the global variable pagesize that is initialized +in main. + +some documentation cleanups. + + +2009-08-24 +dspdev improvements. + +do some linear interpolation in audio resampling to get better sound quality. +avoid copy when no resampling is required. reflect queue full in +GETOSPACE ioctl. cleanup code to avoid vlong calculations. + + +2009-08-26 +bugs + +fixed uninitialized values in stat wich caused -EOVERFLOW on linux +kernel build. removed wakeableproc() (changes in signal.c, ptydev.c, +bufproc.c, poll.c). fixed sigsuspend race. simpler waitq code (uses +lesser memory too). fixed waitpid race. added /dev/dsp0 to dspdev +(makes mikmod work). fixed rfork/notify crash. + + +2009-08-30 +mplayer, bb, audacity play cursor, bugs + +refactored timers, alarm and deadproccheck into one timerproc and +removed timer.c. every Uproc has a timeout field now that is the +time in nanoseconds when the timeout expires. on expiration, the +timerproc sets the value to zero and does a wakeup on the +timed out process. a process sets/resets its timeout with +settimeout(delta). the remaining time in ms can be queried with +timeoutremain(). + +fixed missing protection flags in setupstack. + +more acurate GETOSPACE (mplayer, bb) and new GETOPTR +ioctl (needed for audacity play cursor) in dspdev.c. + +handle kill note as SIGKILL in trap.c. + +handle illegal instruction as SIGILL as pass/restore sigcontext +(needed for mplayer runtime SSE check). + +sys_sigreturn now uses current->ureg->sp to find the restore +information. + +preallocate all Uprocs. + + +2009-09-06 +cleanup + +removed dev argument from fdgetfile() + +return correct -ENOSOCK in socketcall() + +fixed pread/pwrite, dev->read/dev-write now take a offset +argument. + +fixed time diff overflow in dspdev + + +2009-09-08 +fuckup, O_TRUNC, restaring syscalls, rc, getdents + +fucked up: +- seek didnt work for whence == 1 as the plan9 seek pointer was + never moved in read due to change to pread. this caused cp to + corrupt the output file when it skipped null blocks. +- basepath in fs.c was broken +- readv/writev didnt increment the file offset + +what we have now is that file.c does all the offset tracking, and +devices provide a size() function that returns the actual file size. + +added O_TRUNC for open. + +signal restarting sometimes resulted in returning -ERESTART to +userspace. this could happen when another thread had stolen +our signal. we now restart the syscall in nextsignal() even if +here was no signal pending for us. + +removed the exitsig function from linuxemu.rc as we can use +the -terminate option of the xserver to get it shutdown. + +read the whole directory, then calculate file offset for directory +entries. + + +2009-09-12 +signal handling changes, acid, rc + + +2009-09-20 +sockets, basepath, alarm + +fixed error in basepath (*ps vs ps) and implemented nonblocking connect, +server sockets, socketpair, sys_alarm + + +2009-10-13 +lots of changes + +simplified locking by making process wakeup non blocking. +to not miss wakeups, the to be suspended proc should call wakeme(1) +before it goes to sleep. + +timers for alarm/itimer have been moved to the per "process" signal +data. current->timeout is still local to the current "thread". + +sys_kill() now makes sure we only send one signal per "process". + +syscall restarting now can use the Urestart (current->restart) +structure to remember state. (implemented for nanosleep, poll and +select) + +changed default to non tracing. + +pty now handles winsize changes. fixing current tty changing. (ssh bug, +rxvt bug) + +added /dev/random and /dev/urandom to miscdev. + +more ioctls for dspdev. + +enforce non reentancy for traps. + + +2009-10-15 +fixed sys_brk() + +we now use a separate segment for the BSS and dont intermix mmap and brk. +thanks jibanes for reporting! + + +2010-02-27 +futex, TLS, mprotect + +implemented sys_futex() finally + +changed tls to use the new /dev/gdt interface to change its +process segment descriptors + +fixed mprotect + + +20010-04-30 +linuxemu.rc gone, documentation + +removed linuxemu.rc and replaced it with linux. + +usage: linux [-h] [-d...] [-u uid] [-g gid] [-startx] [-display :n] [-e emubin] [-r linuxroot] command [args ...] + +linuxroot is now an optional parameter (-r). it will default to /sys/lib/linux. + +dont hide /lib/tls anymore and bind devarch. if you dont want to +patch your kernel with the segdescr patch and use mroot[-linuxemu].tbz +you can rename /lib/tls to /lib/_tls_disabled_. + + +2010-05-02 +exit_group, exec, futex, waitpid, quoted arguments + +properly implement exit_group and zap all threads. notify +all parent threads. + +zap threads in exec. + +implement FUTEX_REQUEUE and FUTEX_CMP_REQUEUE. + +handle WALL, WCLONE and WNOHANG in waitpid. + +preserve quoted arguments to linux. + + +2010-05-11 +select/poll and EBADF, execve malloc, set_thread_area, initproc, SIGSTOP/SIGCONT, +tty, getsid, getpeeraddr, /proc + +select and poll never return -EBADF but ignore the offending +filedescriptor. this is wrong in the manpage! (this was needed +to survive the python configure script) + +handle malloc errors in execve and dont panic when elf +loading fails but kill the process. + +detect empty descriptors in set_thread_area so descriptors +can be freed. + +move some of the initialization from main to initproc. + +SIGSTOP/SIGCONT handling now works for thread groups. for this +we now have stopproc() and contproc() that are called from the +signal code when SIGSTOP or SIGCONT signal is received. each Uproc +now has a traceproc callback that is called when we enter or +exit the kernel. zapthreads() and stopproc() use this to get all threads +in the wanted state. for stopped procs, waiting happens in +the signal code so calling handlesignals() of a stopped proc will +block until it gets killed or continued. + +new fields in Uproc: +traceproc, tracearg - called when entering or exiting the kernel +wstate - current wait state of this process. WEXITED, WSTOPPED, WCONTINUED. +wevent - like wstate, but reset by waitpid +comm - double null terminated string array. first entry is the full exe name +followd by the exeve arguments. + +heres a new format %S for signal numbers. + +the per thread tty is gone. the tty is now in the per process signal queue. +gettty() and settty() can be used to modify it. ptydev now allows opening +the slave tty multiple times. (fixes midnight commander error) + +implemented sys_getsid(). + +fix AF_INET padding and byte order for getpeername socketcall. + +implemented /proc (procdev). fddev is gone. /dev/tty handled by +ptydev now. this makes pkill, ps, top and inkscape work! + + +2010-05-28 +fixed pipe filedescriptor leak in AF_UNIX + +we leaked the sock->other descriptor when failing to connect +a AF_UNIX socket. thanks yarikos for reporting! + + +2011-08-05 +rename to existing symlink target bug, profine -> profile + +renaming a symlink to a existing symlink would cause the +file file to be renamed to .udir.L.udir.L.... + +fix profine/profile typo + +2014-11-20 +change uname release to 3.2.1 to make debian 7.0 not complain +(thanks henesy) diff --git a/linux_emul_base/README b/linux_emul_base/README new file mode 100644 index 0000000..677085d --- /dev/null +++ b/linux_emul_base/README @@ -0,0 +1,138 @@ +INTRO + +Linuxemu is a program that can execute Linux/i386 ELF binaries on +Plan9. It was started by Russ Cox and development was continued by +me. Its opensource, I dont care what you are doing with it, but maybe +Russ does, i don't know :-) + +If you found some bugs or have some other improvements/ideas send a +email to: + +cinap_lenrek AT gmx DOT de + + +SOURCE + +linuxemu is available on sources. On Plan9 do: + +% 9fs sources +% cp /n/sources/contrib/cinap_lenrek/linuxemu3.tgz . + +Another source is my server on the web: + +% hget http://9hal.ath.cx/usr/cinap_lenrek/linuxemu3.tgz >linuxemu3.tgz + + +DOCUMENTATION + +documentation is provided in the doc directory: + +doc/linuxemu.txt +doc/todo.txt + + +COMPILE + +% tar xzf linuxemu3.tgz +% cd linuxemu3 +% mk + + +INSTALL + +% mk install + + +BOOTSTRAP + +You need a linux rootfilesystem packed in a tarball. Go! +get some linux rootfs: + +http://9hal.ath.cx/usr/cinap_lenrek/mroot.tbz +http://9hal.ath.cx/usr/cinap_lenrek/mroot-linuxemu.tbz + +the -linuxemu version contains no symlinks and can be extracted with +plain plan9 tools bunzip/tar so you can skip the BOOTSTRAP section. +:-) + +You can create your own with debootstrap on debian linux... or help +me write a installer that unpacks and installs slackware on plan9... +In any case, linuxemu is not hardwared to any linux distribution! + +Extract your linux rootfilesystem with the static linked gnutar from +the bootstrap directory. (This will create all the fake symlinks for +you) + +% 8.out bootstrap/tar xf /tmp/mroot.tar + + +RUNNING + +Then you can use the linux script to "chroot" into your linux +rootfs. the linux script is neccesary because for linux programs +to run shared libraries from your linux root have to appear at /lib +and /usr/lib and configuration files are expected to be in /etc. +the script will build a private namespace and bind the linuxroot +over the plan9 root. the original plan9 namespace is mounted to /9. + +% linux -r ./mroot /bin/bash -i + +if you omit the -r option, the linuxroot defaults to /sys/lib/linux. you +may put your linux root there or add a bind to your $home/lib/profile. + +You should change /etc/resolv.conf to match your network nameserver +setup. Also, you may want to edit /etc/apt/sources.list to change the +debian mirror. + + +DEBUGGING + +If linuxemu crashes, use acid to figure out whats going on: + +% mk acid +% acid -l linuxemu.acid + +then you can issue the following commands: + +ustk() dump a (userspace) stacktrace for the current thread +umem(Current()) dump the memory mappings +ufds(Current()) dump the filedescriptor table +utrace(Current()) dump the internal tracebuffer (enabled by -d option) + +use xasm()/xcasm() for disassembly for linux code. + +You can also enable full trace logging: + +% linux -r ./mroot -dd /bin/bash -i >[2]/tmp/linuxemu.log + +This slows linuxemu down. In case of race conditions, it often +happens that the bug disapears when doing full trace logging! + + +NPTL/thread-local storage + +If you get one of these errors: + +"cannot set up thread-local storage: cannot set up LDT for thread-local storage" + +this is glibc/libpthread complaining! the problem is the following: +glibc on i386 decided at some point to use the extra segment registers +GS and FS as an indirection pointer for thread local storage. the +operating system kernel therfor must have a mechanism to let userspace +change descriptor table entries and swap them in/out on context +switch. + +to make it work here are several options: + +1) recompile and link the program with a pre NPTL version of glibc. + +2) on some distributions, a non-tls version of libc/libpthread is available. +in my debian mroot, the NPTL version is in /lib/tls, the older version +is in /lib. by renaming /lib/tls to /lib/_tls_disabled_ the loader will +use the non-tls version. + +3) i made a kernel patch that adds support for per process descriptors to +plan9: +/n/sources/contrib/cinap_lenrek/segdescpatch +http://9hal.ath.cx/usr/cinap_lenrek/segdescpatch.tgz +it will add the files gdt and ldt to devarch (#P). diff --git a/linux_emul_base/bits.s b/linux_emul_base/bits.s new file mode 100644 index 0000000..5a6c86e --- /dev/null +++ b/linux_emul_base/bits.s @@ -0,0 +1,53 @@ +TEXT incref(SB),$0 + MOVL l+0(FP),AX + LOCK + INCL 0(AX) + RET + +TEXT decref(SB),$0 + MOVL l+0(FP),AX + LOCK + DECL 0(AX) + JZ iszero + MOVL $1, AX + RET +iszero: + MOVL $0, AX + RET + +TEXT jumpureg(SB), 1, $0 + MOVL ureg+0(FP), AX /* ureg in AX */ + MOVL 68(AX), SP /* restore SP */ + SUBL $12, SP + MOVL 28(AX), BX /* put AX on 4(SP) */ + MOVL BX, 4(SP) + MOVL 56(AX), BX /* put PC on 8(SP) */ + MOVL BX, 8(SP) + MOVL 0(AX), DI /* restore registers */ + MOVL 4(AX), SI + MOVL 8(AX), BP + MOVL 16(AX), BX + MOVL 20(AX), DX + MOVL 24(AX), CX + MOVL 4(SP), AX /* restore AX */ + ADDL $8, SP + RET + +TEXT linux_sigreturn(SB), 1, $0 + MOVL $119, AX /* sys_sigreturn */ + INT $0x80 + RET + +TEXT linux_rtsigreturn(SB), 1, $0 + MOVL $173, AX /* sys_rt_sigreturn */ + INT $0x80 + RET + +TEXT get_ds(SB), 1, $0 + PUSHL DS + POPL AX + RET +TEXT get_cs(SB), 1, $0 + PUSHL CS + POPL AX + RET diff --git a/linux_emul_base/bootstrap/tar b/linux_emul_base/bootstrap/tar new file mode 100755 index 0000000000000000000000000000000000000000..a403a72698294911c7675594668f30d4cf27e491 GIT binary patch literal 753608 zcmb5X4R}=5@jrey$wC5&H%OvUp#}vdfoW z?m{ltMXcbvl?YX;RLjAVvX+ih9;f+b9STl>9$4=ic2-^xNO_=XuE9 zmviRKnVB#**DqF-YpWEcpY=Wszia3}>eBywT+@GgZvO`uhBI{r zD&k*?^`F|PGCmJrnt!C}_JDOL=|H-!fCH}+c zo3Fef9sM)2Lv8w5;nxX5pfPPdJ?m+Uzw*U4(;I-x4s>RETR&IRMQF*1V}WhiElz^t zpa#OPo=2ssxUBIxqcd~a*2rB06?7Jrb1UlLgfa=C;RZax+cSE#K@Y_+S;FY7 z?TdeoxAh1L-0+N|&|8Ul_#JK^w5ar#{Qi{ebj7Sq_o?VVL$L4zE@rCc?WP7+4|XVK z*%Z?~&B)CJ!1d|$N5LN>Hw}N$!C0f+vG7~xTkrwGYQQ%rsd@PxFVRf~UkLC7VqYKp zOjo*Dm06qAP;+>`l7Mf&Cu}5Qz2DbHocn=3Vbe^!4|L{u+tFWLv*)g7q(5)i~hD|YgYQQ z6Nm73-UVFBaCLrf@!z~E&GdDe_of%^IQ)!$dZ0N+mYn-VVO#i?BeQ1DFcH=+?v0n6#Z4y&LQBr{BJM`S`3R^% ze2+Mvu&$UuW-e7?Nhj=+k~M$2gSjq)!(q3G{w-u&h)NNb%UJY%`- zh%s0jvx0u2d7xj7?F(X^)#}q5D8(z-@7MkVvj?g#d_+ za~#V0cHCQxi(ZEMsH};TXZ2TNEoN>zidA|EmhAf%qf?Y<4^Uh!2^sy2uEY9yq$}Oa zIv0WS2VB_)a)yU%H+#20zT5|%Tz$N!M-1ubR1UC!efXabchnsCmPxTaB4cFYJD^v3}10V*fCMG(aP&Cl7lIlGEdqI5!fHXWlFl4y6C=P86 zLmL@g0MaD$>p**S4jv`+sWm>XZ|DTg0^RBA3IoXY3=fff8%NQ15E|h3J2&A1jd=GG zYFWb%s_7NtZOL{i*|p8a(ZV*PYMVKf1gYUZu-0%jnv1s``%Hc5gt5yUPhw@(Hafmj zU-~#aWT4?%XVh*0^n-V-1Ly_<>ir7vHStS8{&C~71Yw|w%en0#Vb^x8f!Z78>?Y{B z)W%u4-{Y_0+H4MGEUMlrJbLUibMa>NrIW@kYQFX@YQJ^^P>{{Y4g!&nKe4FUu{2eg zJqN6L*h5SLS58pZhZ2@<@pP(Fi7E;5O8s2emfZQOR}I%DbMdR(f7yFbp?1^%(SPoG zbRGI-9JTa;n0P~)lB7=?wsTKOgEe($R6UyNYz12e=kCE@Werx(L+cwX>B8$YUiFYV-|AOq&r`dQKArxwAG}k(Z*dEmL6*-41e@ zIZp~ef^Bkvq?EjGB2p2}Y=V*AV`33hA`VHE7EXJz5kNGtGS8tHX$%UAN`j8PnACr3 z{>h?hp&giqBCbvlTv3D9!D(>(1IiQ4Tnx~Qx=b`u&VNTD) z#SlF-7&w`tu4x?6gGbYbb*(3MuDb{=Jxz031L@09<4-AeH8==t51e$U!EV&0i{Jf= zFH+RtKU0BNKH=Nw%iuCWykcD)YFv1&)Ynr)PlZDfEAc0ER6h@hEXavCTY&(|0_Q!} zm8~3DzQp5Dg0apx|Ha=lhZ1b`y&^6)Qx$V@XQ;<$Hpl8t@fYCNK(n543CLpRxkcmY zupf9HRQwYzR71Ce0{(>#%#%0aidDndvL=8QUXH1=k2Cy}u((HzJ zsImcwIpl0QlqtI}It2})Yhl-64HK?aQe1h=nn`IMJq#%c0bF%rE*((NXD*H2i~`{_ zKW59-(Ak>z228ur0-AE>gAD|j26$|R=3Pf`RNTesZ*@suVA&kLv5@e(UueSpLN7bx z47M`{+jhnuU$@@sLoFL)GT9iHe2AYXAFPbl{;}X}8NKs<@6ZRpKk7iO9 zz(4Q>NcaHi_Qj~_`TvE{;5bI5$r!ybxW5uf8xFV%TRtN8twmlnI2w&;-VWPHdHMYQ zin#g#$a!C+pB)aQ-;nY<>u66;^ek}BUveR_%y);4=fzAp^K7>`y)UMx!$ynK?zY!# z+U24KTWD12_oww&qVJICBsG7zIjK4t!9!c0P#X?&Vzt)0L%Yd}_h+pbJ0wh~gV=$%d>v>9x7qWH`%nIo(}1hu zaqHQl1Yl{r3msB#T%t+c3LWWbI0U#f>YqE+L4usHkD>47OGOj^9t1rG(c;mMA`@d4}Ua%mc2t$D8jLD(d19szF8zMpL5{B2q3v9BbS34fu4^tCxX z-8g9urRkvHpN{vAwe*1CYH%F>%JQKdYVbIyV$&mG&#J4)L9g(*Et~e7^T@u-4s`bz zO}_r(5-=sUixmIgP<mY7c;wI6OZ(Zo9?+KGpfs!l_l%#=ljwL0@ecqkX>>C~0 zp^r4W3%Z&_hBocZ+t5qh`Q|eGz$=CU{{Ckg3!X>Q{iReuq&)(`taN zC&gCT%#MOCK)eAo0>pLrkq`rbD1Np|Q6x<6K(q#(>OEgqdJsA|{mN8nDq)`33rySm zCDdOiZn7Vv=b@HOKe89xW)>mF^L)W}m^qkKasOz+dk(gCc(xwIT&Kx8i|v}G2AcXp zjYbyr!Vk~0;;;|F~DL+K(T-fgK9OJnPtXaU|-l)(0s<;(9wl= zB&sc`s>8suDs(}u8M_LjG7>nA^6Jzv*Dcn7+B1P37{hi6m3<5bHFJNxtdC) zxak#EW6`VdjDR9SG!ov|i-r3NkH*3hGfi|tsYY)_kt7_>sp!@DD}XoPV<;HpqD4`88j`F?b%^ttSusKa%rr3D|dZnt~w1tmn7%ss= zRM7gUx?yVuq=-mW*jBWc$+z^3MQKIN^9C0-+D<#1UTIGhwpB_U<#8LXR-S<3qEyRek9o`ySis9ONFkwrQrT z5&w5ue5pAXe5pEQPCo>AhN}ZcqpQQLI;7VB7O>+On2Qfpo4yW@NM8sHq~=pYP1NM{ zLw22ozfoUkMSnY*j%SQ=3B%RS9Vh$_omJJ@bnd$bMASnVVQ8E8{t^-@3nKYPD;8#>YG zF>80a#XHc+(G>L2?alwYC5UI`L(jZ!*!mXy#S~}_ZZ-68^fpjJBOi8b2X4e!(_oXn z{%@L}*;rFUZ{Tr4`qWjAQk{6HVPd`|efnUYRmj%Nt`x&FrMB+OY@kSkCguSy84_fj zpT7*IDH-Q1w!VoCWS+xRJ_nT>q*oA}=TvGMwsF;kxK8-*2vhtD4^TDMY8*ukO;lU= zf3@y^J`i;H73g3b6|oBxC0c@ONl&p4^2Nqwk(2bEx_kiMiNE71JENpO0n>Za2U+P2 zYcPXp;!h6C-Y}Zg;C<-4=FNfj^*-B$Fo$^j+cd&uni`^44fF;hv3@CTj;7}OCIOgR z9F>L8#UIBzGtHsYj$3>^fJqiRATgEmJyArWFob14a6#ZG0G_$B^ zh4|}0*jukAsz#PEWLUPgR5*VIsaUsyW8K`}moU{?4ZVRl9y5meyNLSBz0$nC4dl0f zb(lL$otw;^J0sq?Q?1E?9AV4eAgpo;t4cA*>fc9w`e$SPvMT%kT%Y$ATA!kN>>Vk; zj8G|Jctq}n1A3g<_>~6AW43{E&QNsmZNw{AC&RP#8^tp*v7mQk1=3I9zVPBsc7jYT zXQGy;2D&g5TT=S>lv?%@!Z?`~V&z;(Oo@YL=5o+w9VlBV{`pNBq?R6N;--;IMo-;< z>aeRz6W<(Xx= z-IMf7B;>k$M8rDKp7`x|3^KS+4VFQQiPqH8v^$*%3xtvJg3}a7%WpxgB3a8{l0y@` zKFET4Hr_YleQaZLGgKj6R*3Djs;HrPG-fV$!BP#xvf^tObP?#jOATF%*KY62HYXlI zR8fYn}-_ic+q@&7{6^U&c+b0`_Q;!rtsca0mcMZyj$6yZ6ybgEmbax-_ zSY7cjakn&c++o2|jV7m zLdcFLR0Esn9jIm;wJX|Th@FrBR5$F3KZ|!G%U> zi)@H@%}h7k$xA=r&P>zCCN1*Z1qg{1p29|ZGVKMgAsZcG%s-9x^sqF?_9n%*?fGqy zwI1P^dn)v;x_k?%0IW^~U&BRVBSRQ_f+-Yie3NJ*?Z0G0?@ko0FmuU|uQb`$t+p)e zhfd?}!xx7q{-^)K6OT-EQaL@7As@vf&|dsUki8A35|+g8sEsZDCrH-2J!NDWfNu;CDl$v}5MeH?kFW6&}i zlA8Fk&f-p^J;&t2+kr2FFi3n25yj0CYOGqE|0Kf=bpLYxo1VIxp_T9$UNp5{A#VGF z)vE7Fs>29Yih$jQvfY};UY5bT2GYuAE@@<=DK(4qv9a46F96ak>BQf`LrF6mF27L~ zG`fuAg&!4e&)DkAoLC#2+ke5*iPQb_?$N@odaZ$AXbcbW#1grN0u;iojnJq3j-YGe z=lm{@RW}Z((Xum)xQSE79N&3_=_EBZ%`wKcXSmHOkoq zEREx5e0t}peF1+Z$6q8sWigKiKeK` zauh-H@=bxyq0Ff<$CD&`BW}tDACdNe(aHQVVVBWzGznfW?>AVYK zp^032O@ua>&@|4s(l|dF=>C-&`l}q>vblR`;M$1%ko+be!6LXMjP4p8xdzNylnu41 zp=BtUn0TXgxFxeT-ivdS!9J4=_6mTinaK!ia!QYUgNQMf7sq@7J@e7$jE{dajfCk@ zrv=!epGPjZSs|PXO`J6Wv7gmwY%_+55-r13AA1C$gSwlsPK%(Qn7)o>mByTYKEezD z*Ns8q7HQ?o@sJ$r`anBFVjy|wh?vm2Gzmx+6|WGvG}0QgjkMvSt{9e8bInZam0K7O z!!?`f{T!OlE#7*N8qA~shFIrH;r0JUc*p2%(=ATL_-5Isw6qd?%(9}$ztY6sLp?pD z6?76x$SYXr%X5D9Pu32eosz^}qft7>(M2A}@*2I_L00xKS( zTemk2wZvC&BCa!Y>AF(X7V#*3IvsZ{0phSH9H_@nU8csVADXzQ0CzogFp$|7-ZufV zQ43Ev-wLG-K--CUjT;jLhLblND-+@+Ktd^yN`{ajkDw>T%Z7(t?OFqQL61UDo)0XH zmKuu?S3Ckfj2n(_Ou|)oN?r!B^}$~dTk)Jrd57`J$4LBj3-1@?R^+(FK^ocMbt4WYY)QCN0o*dw1eZ`0YHfv`(q+ac+bT5%Z;3vU)Z`L&#DIQHe;X z6n{4?1IZse0W3q)a4YJ+chbB=H4%XaO4UZ_YI^bC#h?{eG+8%EPz|iELf4~&vGxY~ z>)L2ky&wjCipypLi7tCZs1WE!i0V=^dB9EJwrYyF8 zqhmlv+dh-@vn6GaAoolPuQY3t@HJfBnwdpYFPuZjd@}ytDPBgWyCn6h6-UiA7Gi7=j%OMM1fixk^5OGpfB6!3}+>uvQ zQC<;i=_>YiLSF(isJ(LsvIP4}Hel96Z9Byb8BEFRHyKT8h+LL}?tM&BF`5uGaRDZ+9nID%hqb;D`;QI3(&Tk{fL;f@AZEWVOjTkp zC6-Oao0mEi2|VG^sll5eBMg2NTIy}Tf@ESXl%TBS8zGm{v;Z%SJCL##$mON+14B21 zT7hl6GR3eCqLz(VZ~Ger{50eLAYkr~2-pMyiZ+aA<7k3@>Z;Xv9H$|#Y1u0Q%Mow` z&n3o~)~>ouFCjOPc6gRF5mzF*X=XIXtwgQ;ZUOZM5k@Q13M4C$Hv!`g z!IS!j@CzDY^1tstmTBP2jn6W&`M5rkX5Q{Pq>&sG`(q$#)^-R68*dI;nkhIK+98F= zaCOyessYJfP}RZ zPW!ngCOnGicWTcFT5}AijiuT9bAqug+agJHIc}wzrt~)gk^>;&vUnXwZ5LI05#_{n z_=B5*gFxWl5)LDF)!ytL%B=r+9vGg79oPQK?HbUmcJ_nh57DG?o=D%pkoIR|i zeD>s$e9}C^_c?M&Nv=E6(_Z@C^1l;8oB-_>Pe=lU_UW#xbSr)xU9kt`wB{$~edAB% z<>-Q@nVF@Vwknm84S60sL0es!7R73f7{Yr_!;2@8XsYh=UVG$yARx%ANJ zBolnKk=uov*yl<8mFc(BVU_m)28d`hYeHY;R8|hg%9`1#}`gS?Brg!Oex zx6w|EphSjoc@sQ6zRxNHB1H?AIW)J}u@(kJ`AJe(vG;(VHGXT;3maFa2D&fu_4D8B zQ1lTnPJ9>Q^0&V8{r9HmzG41*Qx)G?z3pcjrO1zmXaJn?@jIh-WTn61gqVMS_!%|$ zGmK+A9NX*RcX*43<9j{aBOgxe^^k@ON?uM6`%9SmZ}8v>deBck@F7LDZa}!It1?{H zPZQT)m8KXGi~*qIpP=~8Bm~oh4}xhdL_Q`S+j_+9)c_~f&+YZ%msf57gZP~=euGx+ z{6VUoDlVn!Epx(U2o2ZKb$NR1HG93g&M8jg_ch@%XYAu2(Vl5SRQ13s1F^!s3oyBeW_D4c)=Q`42LiCv>v`ZUA!miX4B z5}RRMi?8Ui#el-fx9pt}Zc(*@?OXEkjIH_@bxWBe&^=Ed7U=frnc9o8@7_RR7$sK5oIknp<+Y-ynUIk|o`Z$eAT zMOZsRXZWcmv{RTU=A~FEh=h7{)mWTv>&HDGBFBMaQ=B7NcabKBjpE|5EhswNoSHLn zYTmre7NvjBRDxL@dkNUr^{*D+T_DSSg?lfpmh6RVy&SQ zz0ok@kvvz)@~C+yU2zHz19*y`JIIuW3;2B%Ld#PzyeXIN&E@E#uldc#@DqEB`w!eM z)Xbp-%`KssGoY!~lJSI~LA6`Fc>p64a#EQEr#bfL-bhYQLUMETQ&7L}dJ?>*a)h`J z3E)B5T^8mtgDKC=tu2`7P@+%cCGxXi%t7vr&qHnQmDAJpT<&P8vAeKsJt2AQFuFeg zxv>^K3LERU8?N+Owb4_F^>V*PwiWaYyS~@NSFrupe~+pSI}xyg*icTVbYUQG;@2-? zgmop4U&`0b#DsDs)H@humZoXKbu$@=FcKzwX$grY8uk){FvPMkX=jn(REUyVEFjJ_ zhti$9Zh)xmFGVJc68}Rxd;?^F7(;&uG`@#`UQ%+c=cu>eO!ou@NuZO(AFM)Hg-;GvT#o?~J$Td)OYRj{^5fDogN4{Wh1ggD6ESv)_UHH-w55k0D+AdrW@MXmbBR4onS5sTRSgO+t}fJBBD*K?!Q zS|sE}vv{OLILlaGQEWzD-E3VMAstuZ0|)?fMGZaySHY}$B#x&ac#i6g=cC`F${_@x zP{k&ATpoS`UtVt_d?iI^qB?34%sliB`tv+qlcYHHN%lMJ7s~nZ7k|f#h9D8N!B2k+ zk0Rry)h!Opj<09_j*3uBTSj)>`q^gfW^?+h4O39T%$a(w5$kVJLuIldL~WS@ znwTQ`QGbxqK!a1QKLJAdOCCWbEX-iFhyn?Wzb39g0y1@=WGrGg4*f!iL<3dQ#3UPt z8vKk%3gu&gUn`&?cjB2@wLQTSV=b}CTuX`I#@a=6UDawb@2p5#Xdyie5$D{IP@HWp zf0*B3Hx4~B!^Fl4tY^Si0w$HB>X0SgYOofsk}xr$C4haj9FL{F;Pqn|O5U720lO%_ zcZIKjy7=}Z1zSn%LGds-CMQW;L6kw@Z1hvy@OYpnKa2DFH{^AoG0yO2eMs-e#NW@b z-VXz1r2e7z6XWkMvfc}Tm+!@|fk`wJzlVPHU<1^4a41~+h}(_>JEf38&zxdM(WE~C z_~F+kpogA@OcErq4_#))>wEDITIadpmel$FPMnpcay4uIm>S3J_gMHu#n@AQ?G^SH zta{Ub2RBf27|r3*>_7*pzJhJW7h0vq(hhHTT4J}b9FqX@uhP9W@fOC)7VHi%{oI9gz~>$AJy$o@z_NJs&3NwH(W z5OZiTdSYfxpuet-X4MP!2=$FhQY2rp;_KG`LgYy(l0kq^40xO#dqK6>3gc)pPB+-8 z2r^*t4p8oeb3WdaX(c5qp5N_pW2RU+IM{|6TK}!OWotl833TT!xB)h%wlgc;DsIO= z($y{fH186$FAm;oafH|C^h<#l?C7xD0Tb`R#mVp3FLH~E{7$9R(TrX=TBt?e1L~I3 znBTll5%vv0KqfuCD<1$Kk*~JYsl=LW#wY9#lE0fV30X4`LL&V;D7~tXoG(N{g0z)b z1Zr4LWX5(vE_OmzP)q|6hLWUUJb=|)Q7;2Bh!AgQ>Dz738NN(QK;z>-VOjhJ>y@aR zluu8*qG@01n^ok$f1FJJ1SU6;%A^eez36S$K4SWwh+dE9+#ZkrK4S&z{Nh?|>_t9o;m3vB(Mm>(K7hcIhKwOU5EhF~)b~HJ#=X(9 z@E$yNtwH|mYQ&8eosciu7EYw93Dx{dkP@p!VxL`84YJoCAJG?oDiyl|(rBh{4JOFd zuxqK`72>terEMt5{sCf|(ROQwvPz_WCQC$5kSME?MIpOXrR6rFJ;O<%3{|RWxYAX)Xq3P&^pD7kW-8n;5k!f2zssr zPNb>9g?y2!pDCPDI3(Y?uVwf#if0k_QW!pM;i2R&84b)8-%bhnEhra;oT^{wp+!+h zvFMMY*UiG>#3@7A-_g7VXvg;Z_usI*$HK`Q6*yPV(kgxY(ZZt^V=aA`z^4+8Anr^3 z7MB8xa=U~2KF^HJH$ZaWRjtDh)|Wtn02)p>4k#P_4t*B4yGboN_34R)hb5O#g*tni z!aYY2=(e)egR4=8xAXxI6L}GT#vqJN2IFKEO}ucdpAu_L0(N%q%>$X=+PpKS09LHt zVyz~A_c>}FitiVghubK{xx|NE7`?AUFtOdi9uKd5O=LuZ2Zj-MZv#Kbj#3&$a)PKO zpP6+z$X8%~V9Q0#w{Q{55Dv{W%GhOgWgC|mWtkokz8kTcvNU5T6#=kP~alDNDQS6Uj zNMWxovx;!>FUsy2qpoc<89ClTT6i?H4~H4(zcj15!o}H;SED<%EUk2+GY$BwD`jRAs$YCwe(5{CK`+`e2C?RH=99{FuIMG-giEG;4GE zjL-Vks{$SU1C3+V7v4>6il&D4)u&zJi^?rQQa?f@3fnwI@46v@h}6upPF9ij*UGdr zgN^lR7wgW+Meol2TGmUl-?)9a^4V@QU%@#ye*#rNQMtyQqG#DM%C%aiT+*Y&I+Oi# zE8gx(Kh}-@ zjHZm;h#`K45r+dYDDs&+9sqw?=L;YnI)}imS(O8!4AlTjV=dV&YVdq^vWJH=--oTz z6ZmeDx*`pawvd7^>{0)CZ1)#|PxAFN;}~qVmY%=|2&`H94vA?SwQ7&pb{cC^f~+yj zS5VpL8rEH^1|OlzZ;bB(TOIDO%LwdJw2DgX&rDUOfle*Tg$8GcgN0aB3toxR$DSng z0*s!b9ea|{47+w{CTDfvhhA&q8Ulg(X6+CXVYE$YA!;xJydhCxc2KpRsv$;`v3uCp z!w{3U*n+jG-&5xr5s~In#y^dNdjDe|2R=}a9jpk4Zlea!ug+X7Z?RKLEwt%r)E*F^ zPU7cU{&9`OB{hv5!M4_Tg*~Z!lis<8LqhVgd}mJ2tEl!!c$K0N7_geKg&k}&8Jj?d zBYG3y8Aty|Qyq3w)6i7%)RB*~1nl!bG)@5QO}0GrNpAImJ_29V7mho2MWeM)=Bp#z zrOR|RJ$gIcZvhrP@6eyR50Q@r#6ZQ;#oYli#euN(6}=P%~Offf~;dXTo@6 z|A4!$1|I|0&FMK!pQMJT%=)3I$(X)hU4g_DrQvF*IwucwiB;nvpl=@j=Ws_lafO-V&!9DnZ1jG>MgI+7hq3q&N8;K4iMa9rT#5bF znm?>9)zLI6F7++VMY$UFwN1zPNT<;X1x7FD z3S{0%kMJT5ey1~wesBZ*PdE6%4Fra6@Dto|w=>HRejRHn#a}%&u)XwHQ@(YXf2;|C z7k-+3tO-#0GW}Q+VDe?!u_i#|%ap(;4yx{WG+GP>Cg(S$d%xfg#ruD3kXlOM_R@udEFTmRWxp1tf({!e*U+K4_m7!TRxe&8DG5g6bQn+tl?miAR` zc7>ZPEMI1&N9=(6%4SSF&eB`nrTu3eWB`ju4L%H(Tc;GXxc;l?GyNrhCMON5ZuT^D z#uUulFliQEPg`rTuhwvN7*&Tzut(7DS7TR3ley$tbH1iug#ELDMhCXeVoPk%2aEbG zRf}5Y4Fph!rEkc1{4eZzwu2@vKD6kwrJWOf9kGw%8)+ZwrSHs#IC8k0a=EZ5xC49t zRkM8L2n1b~68fX3yzHPw!Ps+ld*F1$Q z*kqupTLi2k6Y!D;+K&p;3OZ2Zd1)TMxh!w|E%QA7pfvEh=o&pnkBb z^*+WndP(_>;zLfR%|rptECwI(a5!`tK}Zjm-K=>zqxNM8vGwS7O%oTl0%sukT%x3o z$7k)5kDEE>N&o^^fD9xVU5Q5()}x_-83|CzfN!3DeI>36H1=r660y2S>Bgt#(AJAP!(A|0G8Szo3H-j!JRYbSOKgzt2CyaOQD zVj~8S5viF;X9TRM2V=+LUu4C7*43?%6~yhaV_Q~`4Tzry@uNhh-c03gjZF38)zmut z|1}=oZaut{9?r%8d3dN>4;RwId+`5WC9;CZME?MPI$yS|xQ2dPR^Uh;z+XW`qkkw+ zWfiEF1#0LA_Mw>GN(v#fEBKC|G0G zb|K#a1tTkLzC>0qTk`Xjm9EbE6%;$CC>VCA!^f;cP6*svQTZCg17R*BBEFQVSe|~VWgLl~j(%_n5~g&#D+&7(${xMB z6%!Wu1C2%t&hu(7=$eM)CPgI7JW~3IDnIt z5VEP`&Fff!zRY1R?yAOEsKFDXJ^CA(C8a$PH@2(N7uNfonUkSFwv}0(Z?sWN0g8#P$F|wox7KxsRnG@tZ_+xwXpsgo%dJ~&5$GZms=LwBque9Djz@#?RbQ?%uZRuk%e zX}>tjMl{Z)>kcL(%D@U6?feD+L;8l*!hfOK`q+~dbyu72v`V*l1+7PEp|f}VPMr0l z{;~f&1ZE5Q&kt@!_mK4yYO4=@3GluFW?&N@!6Zf2;*Zy#(T(scRKO#KUB_da)jZ9+ z;Sen&YT|B|fNcNU1W(aRMf)|1^3JbN2UCp4T`g)fTw-?dU+h%G^Dna&H<}ra1;Z`I z0SIJd5eX*6Ql=1$g$x6N8vCm~UgUf}lo@scM{B0*(}~hE(kevZt&#;NQs{h_n-}C5 zKBObj!bP+knqSI8;#mr5S?sU+W$gVv@>LFg=D0bIi10NI_@mZhk3esDq$nK28M}nl z9}??*)_9fUinj>1sKMW2Kr~znOM#M6QjMu&8#X0w#x-cTfpZrUur4hT#OB#IaTn-L zRf8|%q7w60`OTU+btLo^rWCYlZ|*&?HPh7K25gkXbX>lO;v5%zn^gEo6!w?A2*edNbO(x=EBtt5J{(Ly zk>MVkCx_P>ZRDUS3^Rey#Eb+iy$YSO7L4*~x8^S(p(V-_L$VKrOlAnP%oF; z6Z!cPe*_QD#CES&u@MBT)pGq%sytfl;~MrnKi;mP|I-4D@cc%w$6vCYI-~Qh(jc-Q zKzjsNXnvda7f{%Ri~5IX#<3AnS1drspdk)HLqwLx=cwiO9K{8v={RvnFzEiqH_=;I zuG7O);lf=rwnB{9f&Dr&!&70wP93L-hj5!n2N$ zwRz$HTyP zIHb{y3w8N){N=W1f8B3#dl;B-}Dmf>@x+d;XE#T zM*|eqi=$GMjAmobNEmq04?$v4JP>)Mz5O*Tt>f&I<>UwD<6mU?Bi2>nKI7{*$pcGj zzp(Mjl@3Sq<$>cpzQc9>!(2@>+8@P~x(;l|`~ad^)5bN=Cm5HAr+D1f@(h}#DYYsa z;V}KumApN&_$qyL@zwgs;)VL~;$P|cEWzt(h1VF$>wFdPKu>>(SxsnZZV#+sPd8dd zc4fTf5iBxzZ`*?iN@j+7(BhJ6tNZbM34{5JV#Os0}{H}t z+`1n9S%!AVsz!SDY3Kk>5_WAyg(74GY+iagpU5@HIJoRf_OWX>tD&&~1BVmCr#_dJ ziEA?&MZ;uDuRno9v?8b0DlMyi9@MTIs*&` z8N(UMG%ufGR!l2sKjWK9&CI9}OB-pHiM1xlKT29j;ga5)Nm>$}_^47mb_-7d(Z7IG z=zl#O-@$#q5fAv#>^|d<{gr8y+&VcANFe%QI?PIhUu&@@nukb;h{!MzWh8DT9%XgF zpu)L-7yCC}h_zUb(u-^G8^A=G^DFSp1;1JjszKzx1E1(E*RJ(_8I0O7c z(byZfA?_h}-`#^|qTR4*UfhlW5S=0(6DQ0ZKD*oNhIuGwHa-wrK~6rI7FR7j3xXsN-!LISK=R}DUgn-)GpV@kZh-FViJ2e~jyb7<{@&W(*%7=xFh zzieF=T9|_)f~)qZ!GQ#rXfo|aay>Xb%XICR2QXmB-1rp{WDls@ivRm*WNKnG$AsYe zAaJmFr?~JBJw0=3Pi&yMp$@?c`R)l{Y)-=#Hs9quV3BB!WMf1HFonrt${eiwo`&y^m|Yqx&ZptTT2st z$rTuWzCE1mYfj(K>j7dU=SAj6*TGVV6NnE0gy0O6Nt4m3^s;Jy3uhUPkIn#K!bkdJ z7z~gEY*+d-q{o*0Yt7UAiw5_MFcZAlwE#_ZPu zakRJO_nP0X(zp2+rS^=`kH-E+?U|<`kD>ScZ^cUx3o_xuF3uD6-K-IK_|YjUw2&p2 zPTb;Zm?MyTw>W&RQ(*;a&wmmsjTmHDVAIfFkcEYX$d2Crg*_kVA@$-;q;H9Rk2A~S z()%|lgt6`M_b+cz#u&a%IofKl_%1Uo)YmUDx=5wdQZu)^$pfAC4b?k- z9h^Wt&#V&Rv@^psj+Lc2>r4$!LP?_~)`mR_j;%!>%o`F;n^e?lMCKj?LKq!r{(JBa z6ax?z76&~|g)$PKxt&B0P=w3noI-TUk3G>RJ_1CL8G`+y=P(UKW)wX7;LniVp*x{c zkZ59WQEIjrA2pWt8sD@_w_g);f%) z^SVs4BF%IZeq>$&m&9GT9aBjKQnm(OfCc%)nHN%?$dX)~X{zR&G_#-SNh{ordNb0^ z*_q~obaQED;fcbpGd{}LK8w~8X2U?9W3P^&KJp0>TC5Z=<0BbBQS&z16XAb%@-_>tMmrYZ{DAn_i2Dy_`JJXmhrWgOH_P)(&k*nl za}zDo!yMcxP9XCFSTO0fRK$-UaD>Y++01BiC_PtYW15-r6ipzFHLxIOR)@=>iRU84 z1Ej7Nw_(-+Yo*G^vin-#i+Ij_nu$H)Z`csc(vcY{m(xx;PdgrQ7q(U#l>3XU2*_wD zgbfGDATCKk3r)1`$FdLG=!I;RE^e%Ld&Fp-sF3cM3#SS;m$a$UP?sbL2YNleg=vU& z?L+kBlnQb8+w4hW1AF%``6$~y{P&mWkTe`115)}VDKo64COZF%VT2&ffyP|Y`5%1? ziEjo`L0Y7X8E$M4Bf1)?X8CBtoo19}V?OSO&8{He0nMp25TlsHdOnEnHpkNg#OJas z%Ig;vwfimton!TA4x0=#fdt`v{2|d5xA8%9`PiC4^?YxM`f*TIpZ$=ktmb$Y;-M@G zjXv5x9N*4w`x{`@!`J|C^LT%6eP)({^}YheYbb$ni$5hwVy;eO+z*~&0nn_M@vFS})e!5- zu(pU$I8`TshFjmcqJ z8kq$HlXpeX1Bi|oV0SA3`HBt)K&j~*NR+wVrE18gPZQ+#PXt2KQR#jxaRC-tYI*u0!0Vmw!9;_CxcomWIT&ILMQwR!RcoX7q# z1IECj^;(f9PrI{pq8g+Ff(UyO@cXb&9XOQY%+#c(w?b{=X><;rK`sjSnMu?`v>@Kq$8FVDHXn;c7>PTx1i9gpH}AMXmotAugr2q~$5Je-%lyEYZlaL+GOW zBw9>I4vd3>>;Sf7lQm-*3`DIz0#h_D{nfY$yv@+nV1Ifea~W_@FC-rPH*2Pu)2x&R zwf-!WAmLMA*qgz$(ag$gjZi)?GuD#uCZ#zv^7E2$*jytFTR&q|oAN6n?RNCA4But!i@YZt{G31COz7%bOVNX$8fXZv9(~QKfDLDOFZP~GS^4Ly!5497<8|X-kZXVG_woTIK(Q`SYdOkAPqE&SkZr@5 zVcl8(k>o28W|o?m5QfDv_# zTrrDs3JIhUqs+!R4Q3`AlhOGYmPK!qsH^u~f$OF37rjQ}AH5Vec2-Wb-J%U0S}3}D zdS;nnn(iyaKHOlXNA6ptITK#;f>y=}qOM=gY$d}(GvD^&2m9Ew;hw{+3lr)U;!IwQ z$VV18hQ+_pWy~F5p(=lOyEfJA4SOXS{}{qWCv^kem|PkjSJAS?G1-!5E^^} z8=K^~qyEw~m})n<4o${Pdwqmn(;!XT$}&S<N|7NwnSXG2xM>*nnT#6_(SU;nPFIUfSU0l{L4Y;v<5MWaEEG#} zd#F9)=>}eP!pAj}H-F$-wioCeO_~$i6{-$pY=w=bSK@07klA`1MuzZO2i7@|0YClF zqjdIY2R0!gA`o6Ia0uy)#9kJ46&XmL>f|qh6hp1?*jX6&TSZxJ;!6+5K7%iZd^EUo>Px#3{c*Gw9tFbcOMAmPDfo;78c)TC zEnM3v5J+Vao8%x7KJu`*b#T?5YV!JS!I9iapAPZOMu4rzwUhU4F3N=Fba4nYg<|e| zkOEn>)i)8Jc)(KF%?XT`lv9TJPFUC5D=!59N^!n+BL?&IokX?mz=WokZ!ylXSwR-F zKL3S$u@|K>p{@6TvUn%P(RL5MrwG*=dpqLV4tO{gW+(l`?n|tP+219(7UiNHsGNk( zITgKF@iK5>R;b2ZC>VVJe=21OwEr^N`NTNtwjT_}gXm-w>Ej={Ik`Ag{Pk+?lD(YnX z3t!@;*Pt!pUlN0a!qnm7v^dSRX;c6A5$DYW3I90R=GF7)((=z5S^=15YCfYbRen-2 z>k&L0%%4=m0r9vlXgXsz(g6QUN)WpC`2>A`s=ll_ia#iF7Jp3yagkS0SDbv60YodH zSfswT#}{dZ!lOke=i!4rys^>7k5I2Lw(_c1)2A7!xkMXxY2|u)Ar8!dzf7M!1X@Mh zO?_zjvSwQJC^|WJYtleZ_@jvbJOsW`NA|?WH${lCujQ)@XoE^)x2Nf|3`8+z1J4F( z2zGVBL3}nQ)k)1e4p$>d@J6%^Aj^6v2t2=>%VFmfVZ+GjgX7IZv03fo`-CWMYoKTr z36KnTO8=XoprJRZ3$nF><8G`UIgm7Y@G5RGwkAP;n|`CM?F?epi)@M1rHGebvG&@T zh?uNQ;n_SOF7!FcD{x@8UgIPgfd&Lc#`#$v(+)@1o(0z-{h@d_zQh{eEqg@nfP`gq zv3X~@xiB-(;=o=MbLjQxu5o9&u@GnR*3fl(#&$J?^ghKnp7D(u8ip(J#iQslcay*P zhp*%4_52M_?4*z%@Wg%!bNqe8$j;35-4Hp0#m^%iKZNi}(|RI17A>ZM;Iz84Vmb^( z{>G#{$g!XmpN$GN%Fpg8Onx==dz1+*?!;QqHqz4kQ7Mx@7nPu2(R*s}1}aB^6mb!S z3Y%$HBti~i(^yH96RAF&4LBOe!MhxV!r`(Xu=)Trmj9Nl?Grds4ky?W4C}Oj7Gz-o zQ=3{$BoGW_vxxK&XqWM>q?;Q066hJ-8C`1VEL=6bN~>p>7&{;-7@AtcW;}pB1!Z9i zV_XzvwiX{cfI;?82#CoWtxdVXdV3EK3L~Cgj2%q%Ucil~GPh%rsIj)FPPz{*^cqcS zkaCPA(If$le_WEkC#PORF$XuwkE#KrCNQ_aQgSUeb5)s7_}66tt6k?6Y-0?%O0 zTpCLStvJ5#g1wn$~Lm(W+wQ*REY>Ye2&5Xq|LeG`d9jaq=IlfM{z6$1RKci}Y z0evofViqB5$P)Y(`p&g~pbKaW1{xCY=t7L*B>RKLKiNje#o#&!@FvEmxN4I{1EUH1 zC#3eBIDNmtMU9Ah-_HSUX~smfv78Jz^jtdmJd8>t;OM((o%69Naol;5griM~uE5hi z^Evq2pr8|4Lni$dod7Ur4zATh_^8neAx6|hOzz8g6SP+ zU}d-V_cj%i>Z5X`spo9UUT+2eT^;l3gH;eoN3$eT^cg7oUOm5ySpYH{$Biy|fV>(S z41wnF7UJk?C>ss|4S78|dD1$STs&jQd1C_L@(@6$L79SPXUfDlzcPil^j#b3*z zC-H&PKgeAq6ouVDrYUsCNym~c*4*gJOtJ;&EF*I~X&H~$oPj*Xz*??gsqpAFsa6r+ z@34pVKjIsiCsd2!&$96l+x#Q`W=2V3#NY?eS<13;17oeSLYy;=m3ed~p228>y%0SJ zJ|_sIN28SFZf_XRgocOiQKE0)VL}khdP$n~y`K8yQ&LSU47}+X{^YeFF-`=qzkd13 ztL!pr-eETB8}KA4!S*?^>C04F|AUB%5jaqsW{gHO{PdlmQk~ox=x|;QJ~*8Dh^~uu zPr=R2r3avNsBOKDa!XE$I2YmBTDaoBrSDPh- zYN%&P&avi67*W10{_n=?Z}`8PqQ4qzKGsZ0H25}TTz}zQv_C_QJ1?opjy+g`6Qim< zh&n8P#BMLm=#G7cPI<)E4LBiYnTT&Y!x9o;6HB?~2;Z z++zH`0S3X*dV?$jgim)Bd>z`6@CN`rjPJ#X2cqQoHlm-XSGDb@D&75_(LTzBMfuJ6 zEP`tr%t6;%3nyU#iFa92sti(Q#3NX7K@eL5ZR#9Y3*T5z_t;10_1m(r_Ak`Id{&*y zfc?9VP3)Pz8D~fW7T3|^bsAA8_HJMP4Ov+`#DC&qgvGGiQoq?>@&@{;_+|wb=cVZQ zX!UlwRf8ooyOb|9J&WMCU?#Fc%p6(DP>QdzuEiU$3TYj z=Hu}ZtS!>2eY>ptBEv^7WzGo0dyKSZ76gXp7e1ul%-s)iM&E$!+VY1?9ZVc2Ct$@k z5-z}12V;`+WH7vrYvsR$;=(GYiBK^GVSBABzkw#e#sAZJpfo_I7+bA0X_JB!7 zS-#;Op3EVmY$V2}Q8o@MHO9ST4fklHY(h&}F(;Vdli6h@(3da;jiJ<{O{Je$?+J8b zyi*ylWKJ)880l8^T6jSTjk@f$7VFf|S6~^*R_%Sfo@!37kDr8e20uS!me-h`>&@vM z_6z+|b5f|s$EmOs&%FtslEqlU^1~i$?pcShTS5Wa=r+))Ye>q(U!ywRn3~pbCqK~X z!PHDXcgdKV(=e9r^BTtS?+~LpyI~<0TtpA5hZ|G#`7AawH^>i08&gN(ftTct z#Is|q!?xnoIt&S;dR%1c1X2f4A2`kW<)(EA-_iQSPtp&hu)hySM~&*IT5e_W7=1_? zA3pjm*46DkCG^|G+I0Qk%Ca7mYcIY?4Z_S`{w3dT~qY7;&vCk6j+5FAH>^=L?XoLAU`0k3VK}G5AaL zhEt~#_|&CTd&&6bqGP1^8||J^sL78;-^bH04nrjBpe6p=abb^4lXIn)79ol@5O7zP zoh%-s1{GYQU!jrs1%NAx(T=VLH=D&%cp6a#p$A5L?9Z%U7{7;^ z4>Y}IiF>&W+!Q5daM(Q3kLC1UK#$|QtaBkAt^`>y(&|Hh1%1WbaWLj{X^;bnP@56j zgr_Z`m+)6H)~>;iJQ$&OJj4vso0XRr2OEtrZJcWfJxetrp>_0we|moj#4&-}QGfS4 zJTzaUYUYfw6T{TZypbT;%CsW%{bwW<&j36Bq9PoJ5_}w&N%p`0ZAM59?FJP8+uW9Z zCCxN%GsGte2Xm6!V09(JJ!uN7Q|c3rYT#~!_>eTq4(VeLn%q-!a*h$AXcST^jga7r zA;vBv#9srPoNrd*AeD53muU@Nrp4-|Suwh9ydl?mFQ1^9mt$6Yo2XD74sFJdW^4~M zmS_gA1B-cV@aFRwC`tXRfLu!)0$WS8%DPm832Cf-0uaQD_uG;_-=ZKCYc7KxXAh7h5ho^Iz$ zq+JD-Ik)@L&T(T-SEv< zLKpF9!gnemt5x@{X9hAaQQ0ld_+B0%cCm!!7Sk{-p+@YJxc))8KnZEW6u2yf`|6vo zTK1VA#g{c-4$TFw|6JPQdcNVyHn7(-aj^BTA4Cp9A|B9iI}{eE@j=8%SD-DvedD)f z`8Urp(PD4>=)9aE%$Z}Y=Q>HQu+r2qiN3{5(j-{~Bi3?Hrwwzp;{(UQ7LGzE8h~9) zp`QrSMsb6_FxSOh^H_h|r>utHaY8<)0X!es3YF^WfR(JSBHe4-<6w?$J&G;>5CiXF zC{R`rHFyb10=_2J;we`3v2PvT#crbo{`{g0oJHJ%(2lP|EXP7}gfLSy4-yOa<9zF1 zVyU4S)7q5F_HTafZ&?-5)wOoshl8tN}tFxAi;AWCTgv_SOP!~B*%f1Y%n^6h_2 z)@HE-6ni0CgFCtuA{RXqchI^WFf_@op&p$73TDs0*r<<|Z|ZJkEJk4|nZ=(ntBoGa zykaBtS@Z~C!AzoV6t?3pmS1VxL#917b8+7BWM#w?(p#{ACdX1l+yp4pRkD4{(N)$s z(WeOPPvqM!qRATzf{U{wd~z=FJ`!4lzR*>@YlW>pK++~W@ra98+d8oMZaN(j+g2vU zn{UVRVEraYn-;FZk!{d7sisvNhwPL49Ka?K%KaE6!E8oL@vYh@fbXZ;@b!UQVeV?o z5AWmBIy&~$(TJxsUPxm2tADbaR_`B+fe&EbDcsO((4WA}#a90g? z-pZ;KAe+gz!8T(kL4sRU?;fIOOGCdbUmE&lbmEyutfjN>8=chnSN}lcWh3Qqxo(cf zEE^eU#E#RH0!qA>$cD>CBVT7E3?lZ7h(-JK5&nCN72nyKdDYm8@VzCV-&52j4r^qX zWn=xtW09>31^YR8#;vNLXzIMNVGqrpMXkQi;CnfsN3rJ&6#is|tF`T1%}1RU;eo7I&2!5;Kli<4p}+ zf)bXkp*sT;2kH2-2YWTuB5zfn`5tNLN38Kgg)lj^DTHaT3x|}+ z{w_7_{=RK-IFwHPS(}N!7N7Z>amLyq^rRLky==^dsQ?yTxi}W7k<688da}(h6{F{Q z_{HRO`bgCNph4D{FT9{3}e?4Cr7QY*=HbsC*lwMH8pUT;}1(9;?}U({Vt73 zWV%9Yg=t6qHzOeagrSGiU(xMYOK*8aF~q?^9JgI6ACzb44*08+0GN@~E<$o)Z+!%Hgq1{E zmUD|Aa0p8BiO*)%|3})pz(-YGi~louzyN^>8Z;ct230r63ZfQEzsKB$w7NkEMD z;nkGdUWFMz1s$A3a&jD{R;sqOHHEfXZL6(b@r6mCNo=hmzNlD@^|i-IHHt#S*8IL} zopWX;0q_0&{(n9nGBfAw$J%SJz4qE`uZKJ*p7b|S`R6e#nj*~;H_}Y0YuO%bp3j^U zOI|J}$l5lMU$NOTV9LpPz{?4LIMcM$X(nX0#$0|DY(ga_`?SuwIsP8^c{HL|g{|ww z1QM1v5DIR271w5f2$}!%#Ky>(gkxfgC6VpZgl(n^F9@~)+jkL2Zqp65J)=tMZr(&& zpbNXghxo=VGR21IqwNjy!oKFXW_CkSElKHM6V~Y*xsYG#6492r#QAyEXjT&U^WMC= z*4w0)+Yf8y1L;)iy>}?Cbt}#&uCb&_Xg$odR9R!sDJpZmR2OGJ9)2IR_it>$tz-!8^qEdAy#MM<-yoa)XO)XC62 zm4(!~OU4^EXM2);usKBudxM~fSc=H`0lQhsglt7!$o$R|%YseeG>Eb5Ci$W>(uJ+v zq4?Gl!sdod*<&f2eop|R?k1%#^^;9Mw-sKe?qyzG?2Xevgw4-e;9y(mmJbIi*PE0X9m3jLIuGR&0DiV$UzB5(O_4T0FyuVi$+vhpB)rq-jaUDvO zXrEw2b5Pf_-Y(Ohu)y!pt1Q5We+1*-ky(PyG>UebG1Qq~z31+<5lPy`hP>f>Zl_MB zhV9%X*u|+^tv&iX)t9wCia_-P#LGvWyiwB!s{77WhI^BPrtxD;Q&qcaWu?Mv=o6kb}wzbjgRjZ6RP zg8`gF7H|$}5BOd_I1S&f{oq64Fu6rU{ar}6krRi$M*`zSiTM0hkJ7xCAUz;Qx#*j6 z&p{P@^bwwK_DBT%BRy2O!$SIaJ9|Iz=w0R?Ms=2bI`Ymbnf-xCP_oL7s5S>- zKzX6&S_|+^Qa$+ibeP!d=<7yiv!`M7DW}55yP-kd=p=Pa{hOg=C;KzZ>BR3n#_a5cu+NCmn0!O^}O`c*eD-M!MjzOn6>w7lw z2q(Bt$$N|h5jGMJc*!EI2D|)Krc(5;Dk&)>zlU>sf=PzI>-t!wM)227> z4_tQrwef+YBbVVq8_7>x;!hE$MM4*Uf}097c)}1D#!Vsfn`Y2QQr4=wL+15j9kyh& z1jtke7~+-91?IM&iz)dgdAcQgJ$ya&Q}?HP|1t1=@|_J_8Er-@(Ss-*FpF0ciemAd zm?NJ5D6FCC4qhD3-^lcXQg? zcXZ_B_`XXc1@zIHI}eJzHT7e=>TVFPk_JewGXZR1l?vipbCT%bNn&vHywu0>ecz89 z8Q*thRLZdd<`3U(IN^SJwEy_~j z#aGxK+T?pkY5l*T&;8;n1{!Z+Ei_6g^ZNI7p$$F1;yy817A&g4uY}r~_*NcjlLs_z zCeb7dz>!dpcfn}mM%pvGVQD}ugc>0d3afbSArM#lL823VnUFyjE)It_N;77F-EZ-d z#^1=2oFGqX%+ZBPh^bp1e}bo}r?UCAt|#oX3++{TTt1-qZV0qA>^9%LcW|)f4p~Oh z#S0G$#T44*Xg~S@#APdwiygH&V|~oZFTgeBi2B8vS}`Ax>!!Zp zZ|E~+6qb<;Mmv>W2%B~zvM!IvDSCOtV`N(g^947l{vqqZjkhCL4T+*tYRm}^=Sbb1 zu`eLLl9vcxb3^8BO3`-$_V^`#;3_TO=l--}486o2zL#e~HMAT@# zOW5;JQPjyQJOu`J;o^|Bb?N2i5x!gElI#V(db!t&vPJ&U*lS}0l7Cb$Iv$r_kz=#Y z2gnDxIG7)#*kXELMgP9F(9Y%1_>U}c!y8|l+gy8Aa}zW0$F41qB*!!`21mG$vKnyE z2;S(f&g5~^Dnn~maU}XI33wfO_(1KK4sBnxbqe*@$GcEFLQNX*zh(D(K>2VI3rKte+bQ&ux{!-4C`*ikcB zTi7*XHg89>Lak!wa!FLWb&c&(vCUnN^IJ#GDv5kHYgFV@Js#-_>#rxuR-^B4E~1J7 zBDn%SS>IUBGtD&Nt&jPN{m1~AkL*r1)4@>rAtOIFhycB&ATn>r`jq}ee3p1SN?#!1 z2$mDiL*_Y$Ty%NiX;(j3Cu>F}>uyhO^#B>R-*ag$Q3a3>CA&nF05m5%cL8YM#&~rh zQ~C*;%e_}o4nzE2>6r#Zf_3!-GViY$3`x(aE~}xayQ(k-7P_lMrbA%JK6WSn*)qa+ z!1G9v+#=Xxg@;s|_dwI43&Zy8+xS^;cJkBy<3qG27}yh;X2-=OHD6dI14yjtcS6F7 zSQT!ZenP(7<-O&=$PR~oK$;dou-e=tGvJxrQXc#_Bwyib)#C?_z8(Q6U@y-j3Cytk zk`C&%>qddjs`?}m{lt7q3jk;B(T9!YC+17oxJ%M77|-8RP+eeKKG*0gwse*sV9FZ^lp(NG4-+m+hNcK8G3w#VRnL5%uCVdyR|b&JdI z(($4-{!t-e{+K0zb5-U>($7JA)|GikL$VPreGqx5Hegi<1$E^i*f`>hBd@LfLT7pR zihSJ}#pRm-JQabOa6S2iMO-Cl+36RemiWhF9Ge<61?TIef(mM~?3 zr6nOVAI1MAq5v@`D(?~zKrWbYNqZ(;4qMYB^@0EquLwj6GT9~XqW}&S4S}Vj!{!Tq zq{rduS5$vXf+Vw@jNgDE0s=iqxBBfUS{q#-8@#LVHf0Rb5~;^iS=ay0z@p>qamg)*K@J6dYD* z$fXI3_b9^mq&jI&@pm4*BC9pG4>gg;{<__pAkY3#XQD`!zDVz|>EmdW*(baZ2<@&C zRl3h#+iss-IDX-_cvX9DymlMjw>_-v^-#)YjOP72?I`D>ZR0<#=iKqg{cLEz)PO+D-mGG9B$CrLwDn7BJI-}f*h$;tvkTEO^C*E9c!{&pdPawy~UuK}zPRA!! zY|Cx{2rA%>To7(-_qMWyT)N3f1J3LNQQ5xC{z}SqAqD%YLWdeK^MV+r?wtB+S?6`R z)>T3VWp6+TD}rTvmyfZo4p>(gBwuC4Gx7D+=I6Oeu$n7L2?VafNkZ1erRdI4Dvnl_ z;YfbSO3$&&3+x({Q%uR!oi&M7m=4N9yd2?f$;IS;#)xYP2hJ{EiR32`WY4cRXX}&M z&Xz6_KwO6Ml;GW)&u?HH&yiA-U=gLQ0U!6;NnxvYlR4);kWFAtoEYw5Rc*q;{ciN5 z@>F7pje0ebcYfE%6R}zFmR)1XtF9z4j(flix;(-d)<5f)yIIVT+3`?){&YwWxK zfe}5AbulBvkB5X7$rJO+OL?*+-1QJP^}iK*7yue|j{0|ZdnR@znmZ^04+hOCu;{ktj7jBqP^4lua^FUY{L82CL zZS8Gy9{wjmcWyl4*vu2Ldb7IB_BTD@((2b3F~Kc)#g3S|@^b6rCX$(n|Ak1N3E6kd z@1M~#Tt)C^%$LDPpGnXJ!X`Nzf=wSW-K_`5_MH?dMaCsNJ6nfA;1Buo3+viwoOr=*? zi>}yd^~{M61g<}r_{xdApZMF6j-5x>{WBA#PLiv<=dUusrX?`$wQKt5JEn9SR9o}S z;>U7)l^I;vM}0L^ENzeNn@%m@^G91zJ#46YFLgtP|2b5D49%_nWVST7?4L==Rql;1 zgS?M-DDvX_d@KJvoATF#zm@Ikl(9b39`jYl9;Y!N3fO3v-!fUx>eh<-e!#Csm1U#l zo^_mlPiu6cop4>_H@wC1(RJC2qjU#0pBfL9%LVCPF2sQs@2Eu(e(DWpe&~NVeck(O z(a^HvY5u698e_2I98O;%4+Xc3bg0LPxUlkg^ZhqG3UiJxeL_d|=Dp`RzC3SrvX?P1 z?mHt~=fE2ApUHrK@=);8Y=7wbEBk2W3Cr@zUR`+%r*$Q4|G^OJl+8;8g zn8V5>s-RF9Ih4(>CeQ0n{C}PP?6on0<_AsuP4%igG`#<_57@s{vs$Fu{{4e*>8c@l z@n;9Xw26MH$-Ss4mo^ASERFi!Shj7+h$|CtKe z^5YT(<@Qgc51_ScKd~;b^Q=7ERaR!*x3F018aqUxe7CnREnbzqFM)wn&k+9mKaR&f z?}mW2FvI@NlNF>hn;wszAIQg2z|A%+P)B2QH0M*;p|YW)`D&u5+_s(37;Eki0)sl% z1Id#zp8CvK5-W#~pg^*QlbN$<^wv$h@ zHHX!8+N=|zBPy)g!qkn7gj+)uBO80Yxp{I$_INx5Xlv1rltxOJOW8lotwV|mO`_8X zxLia^AO&brVKacT#*5l)HT25uPOlf>J-S%ny+BCz20_cr8-((gpJcZx0T-2b{6nG; zS$X4eE3oE{7xQF35G@0PipS)5gc)JUY;_8t(3xvw3ZTg=o!8Dgk&TiE^JG7LPO(O- zM~dN`A3(j%afgq zesp5P$Dd(#$S!KYmd$@>C{T{t9(;P^7iNG6tMX7)8XgEoXFm z-Cqyt+AMr!_vh_zFR4aX@SW~YTTxVJE5Lx))gPt(bzP?)PH)0J+_2k`1rFDP^26rz z&$+Hyg#m5>1m|C4&@WEp@GO?Oorvv3TidY5wax#Zyc`lU)<|xkY6zkw`Xj_^6wkOfgm2h`? zK=5jfl$J9rO6(ys<5m4e=^mNbL)Yshbr0nE%KclZQIuvPgNCxz*X zxDUwhu-|$Tb66;~eL7f-cTOjcPxq%Tb&6iXOJtnJcNpFsuonb!7Zloy3JzXWq{(m# zLTItJJR^7ep6)V(->_JRG1^OrS3o3#0{i@c=dRIJwc8h+tS<>;P-xc`+2^O-Q=@sj zCS7W4)$YY=-JXvB1%JnU_gM%*;w6-dBK<1_H0>^*Bna#-FLpkTb3UfXhtw-aGuNYwnrTIA~<^U!A|>B!mL$l&VR@IpH3# zwdy#G_FJlmc3c8_xPlC|)=m+%ceJpj_6;M!C#`M*?dv?V#;y4T8GxpH9Vfonnm1Uv zg;sr0_xuv!E0MhT`BQRYYhHK0vCp0$h8EB8iaF<3$gFn`I@bK+w!68Ck=Izm^O|G= zr^%B#a~B>*kcGVZ8L#Zgo21~b$Z=X|to*7rt1^YIrch>8JM@lX;_j;aK;xDCQAuMn zRK#i`V7b(fK9ri5*$;q?^5s&##4F#DE-yd@o0ou#)IknCb|2VBCVf#&SU%;eCqfYFlcrnWC^(~ujcsM3kEbDc3pRs z$VyyY3bFCg?tE;M55mN>RP8eVsrn0%dL^ICkK~GL)s(&6x*;|f@)4UoO$_2AQ}=d@ zMlmuXwq}hTik~yZ z&rZtRPP;~=DhYUp$sVbUR4I6tgq0n`=)4(o1H@v~qLqGpwOk06=qinjuosqe`M(Zy z-SbN51as$VCleAbGg|z+TAaQAHLKzTDy4pzm1#(L;yGI2weE7K{_uPNRq{rrBsr5M z=aI|NS17rNXWwBw&h($0z|XAnBmLUfOiBJl@ZMJ}+!Pi5BTorK5bI?z1FbiY{8hP5 zsImMF_i*y*cf_c^B)OD&_Gvmtd+=8CkQ{wY_EGkQIg9LSg22e=1x9O5p;cX!tkT{b zSJ)+9+cB^0JGJe?)Pn*hcxHS|urGP6^mQPj89=kZ6(gNrwE%|ZCxW7EF}n!JV4ppV z!b)){R=nqO@`l)n{1x9e9WkP|X;*Y2kdnP;D2*jIi^&zm`ij|81wD7*bCZppJDwSi zo|hXHJ*nRVetf}{08ylym1B5GQdWv>OU(kSwx>;_`B(M0tCI*Ftg}a%r0jrH;!Ciq z(jGgVcX$;{m9d^3vKF?NRdIo*3;BRm3ROwV>|66nlKpHi&UL^CGO^;H{Q6&9$WKMS z)4gDlLSCqW1v2}+Dlk|c1@ToSV&@K9CA7CJyQ=;@iSO|9~y{5U%_3B_M6JmmX4 za4$r-qj1EVuPFZwoA*A1-$kb9407)7IMMTn>;sZ8Ix6&b4PZR*Y|l|?AxkyBs1 zXD2-tHf_?1R-f<5vTu6AA}xs9y-EBm*9WI%+j!vzY6m@Yje#{GqCt+bepEtJ8m_*onxpT{y4VYx3aK{aqs#r+#IZBe=Y#K@ORgR6p?xSTrP0(BRuR>u~c8 zWs%?W17>E`rSbO5M2=g$o|zO`tOYIqBieBGmYk_aS1*^s%4ADOM+6_Z6R zD!LPOrN<9ul@yim8Fl7C)SC8PQdfi_+?_?W5gBPOD8W*$T>CkKCY|PW;9_HntB%uM z4)1tsZ;)z`l^#xV>_Th1MsX90*dQgD#9ynR$AxT@xOHkjOkT+R9^!K_N{pnnOoF>! z9)XDL4T6Eh+#D%U*1hsbNB)U-Us~3^;xk%L%+JAYZ_+J-Y>LQ}O23_uTA@T~GF_2bFU7@D=GD|c?llQ}lDhn>_O z-kg>|FamqXzDqz4J9Klkqw7gNP6J9MGtO6xfYao!oo8XwRHK|W`5II2%v@TZ+K{}Q zhne%BE6>&z#Lj>xAzv?mm2!T!>(yCieqnIkAw22Isp0%Zhn4oie(Mrs=JiZ);!3|? z*u%_AC?*091~H;*p3kXyI>#jcN|6yMAYPEoH|vBI(z4i{;fXW9aAiYR&IMk>aQaKA zc>2(WSAG>M*oOYFeLkJ;`a~P0MNb}bhnJ4eBmTs_NY{C}damb_7!8&_fS%2inw0|STS&r9AoGpn+MI;3N&cH8R?Lr``pb6RcI z^2mXZp(Rh2sX_lPW}uNR^a!(l#Ol=k6BTafbh`^ zDf(E)zXYG>KU*b^<>W>;;)YAQJ!+{704=A{1WlIy&`Ywy%f z-7Aj>30LV*u&aI-qW?;|kh&%~%Zwb9=hE7K_#y~rz@OG^BA|+wEOaE>ug-h?ZMwu5 zMyyQg-$mcV=^2!DSG`El7*{E@I%vk?J=V47^G z*#jnsK)g|;V4>HxdpL@R#6ZRnx~8wj?3$^lV$F2~Z$6gVkbCTmm-I$Jx^iV)k z8Ivjw)aXy^Ho8Apz1PsvY+fTqz5GcNLFVswKY8zPfpHzcR0 zZIFOmY)OjOouF)UpVXS33^D&!wB1^>O;^9#51PL`vEP6&_cL>Uuk=&3*%qOR@f?y( zLKsnO?#@vvvZ*grG;|KeyK`_ihsI%ZFnai)&!z=$4rVVo&>Xxd;|BRh4uKU5MTaEr z_#Ln!k3R&#i)WBRJLnMn(?6cMVw7F=P`6K^{rlrCwAs`1Et#I){UQB^o+GoS=jYV+ zApPNm2ZB^rB@4x>-6?9T?Hsj|r@=(6==PLhL8(nuwtUf5KIlt+j(j0hN_t}Q6R?@M zx+aZ;a2+olV>o7cbfWg{@*0>zkNL%K54gf*z}^b~5Z5oj5OQRN^vtY!I}!krq=wpK z%-3f~QuDxs%=ezmS|C`=LWjRuoP8zg@*jvW*(U}AUMuc88!LoeZH^hz%)ta7G2b52 zOlr%1jN=!lyNu&H?o-ffb25_nGvLLAZTx({Z;{5X7;3NN%42A}=jY9wFM4nHJ|-9! z8~)NAt%c>>+psO5BeGstQI?H=l;g5r%qv_OZ8>a^&;r`V@U+D_ zoG&7EH(*FhRB~9V8pS|?W*cIaae!Ocw9{EYAsewRBp#=aOENKFNz`~TP`}O zXI^S2k_{8cUVee}x|W0M4OA3*o8t8p?cifQ!v;)BTFJU9N!8# zre4MIn6gQUi0|mb>&@*qfG@dj$;L!lKG*pNABBh>;2}}TE?*?kVn(Tg`H7XM%SPqN zpI;OOoBj-0$hbY^{CWtPY$s^UDCmUvMXRoIF!8HLz62AKe=`IV#SSJWX{e_|$`(Gc zZ2ZdnGBA-P5rxyZ2+^v?PUhBui#MK9Ttvs=bn+}#7S4>k>0(CHFt8lz&APQN{b&+B zXX;WfDL>E1$0!df^Jd~!&Q`RhaQQI~MOTVOTdZWkT8-HvvbW1@oK?ZwusPOiKedDQ z#6CuFn{ICTHR-nYofuh271{l{hG5FBr&aMT7UM=EDXvS%Yv16#hdG6f92FccT_t5C znt*%7Ak)NQXz5@h6A}xXpopJU{hC?a8j*U~p@*{ms9*2QJ5Y= zjTfCV&QY7abAfVevxXhjJJ4j=XRqxO?cXp#HM+UZOx_{uvba%X9*1KYiH)2&uh31R z>H!A7)bGB(hW9$WVDlKbm|gY2n5u^`o4p15ZdO3Zo-HC%z4?W-Q~Q8pCoox_h0U2- zmz$no^UvjY!j@M1({!P+Qi%4d@fkLfzr*b&;uXEECeg47quB@Qtg7`cIJ?lJy5-K3 za%hw;EbMWFVQXO%o)#Tte-=-RCacO7iPPt)P`J)*_y(_Ga{R8@ z%U?^V7j*@k2!vm(E`eHZ{wy|GjhY#c;MQ-!R>6A$ap|bw2tzV_03A|3dqZ zj^xR|oN&U$LMaX0y_)#eKy1~>6A2O5_yUE5>P>5PwtN7vSWdK71ZJH`jP(^hZa6f* z4c_0gPKO-byUe*9;klKWePja)VWDLX97r6sV!hqG%q#gJB~t) z%*^ngtbsik8a6OZR5EOTE!|Q2baED5xoQrX1y>02(_wNAbdoJ^c>H;mSbSAXxtFsR zYq9`~e3{(doGs^{&t&FrIWvN<-)_z~ysj^rSzjECKS7Ds+?Je5ReBB1FDj4Zem(jo z5d(VgiNt$FLJB+s->A2`mio~(mf*v5;o@?>y}LwkGD(Y7~SLOKX(F%m{gBUY*}HQi8*bCrOi$0l1sH@370yF zMbqih`kVE7hJV`r#QMQ6zW728kxzTqJF)*%h7K6S~MF~6w zH&(N5Wu9*yt)*UIVQhn$*O+a8ciATH`X^_}4-5dKGbfIXpsbr4I)QJ8~ z$RN6{&Ln=}5TochAvpTWrN2Yzuaf?fX9#sT3v<|g@d?g0L2#<39!4e`3`&O&YP}WP ze2Ovygn>U_0CPf(f9om;NLWR%c`r49fEbWpn>a4eDC@cLL;lp8fBy%ki-sO(K@($y zg%s{2dW1K@|9r`t;6E-_l4+C78CvWYE0kh%cDvHWZlait`Ao^ZC3GtB`qXq_2X>9U zCDA!RQEz&txKKyOd)z3!pHRSEpPgmS`h0CT|8?Jg3#KM>bbko9=l{%y%}rmYPwbJY zA0D8+Tb=cu+$}gx+h1q;+c8UPC&l?c^WIyZTrZKuZEn4pxso$fRiEetPm@@6i|Wh_ zqMI&3bx8h@VuwlwqsN+>i3lsK2Fl(bQ`U9EpZM$dMx5pk0nB&mO%tWf&yVzotV=yW z@ZY3q21x?2txhZk)ef6OP&Oszo+AFHGS!bb^`^cNeIT{2rg7)fE-M~9cNoV?{f6;` zs}bH>5J{<^ba2ZBz@cGQ2mvX@9%KLb;)3YJm=wyR&`8vI7qXP=8h!uZ{g0tU`MedV zYw8+fe3x}ZJZZEY7NmnfppTHr5oQ0dxFs82F4Fw6_am zv{G%ZIn9B(HpcnoeqF;%mxua)El_!jlJ%y90VVQg2t2E>!&~JKHh+&1*tOD7jrr)O zdKoD>^aq>&!06HuE}Qt_1z9~G>~QVazyavG6Fg)ze-$vz5%+=& zP`NN>7gwA1``pog25*;}}tRo%Rei#>fYg zby`x*Dip;yIAP^(u8e!VH%VN-Fzj*Yv z_BpnWyM)aATkR+16S*)E&YAC!uca5YGIJUM@DGXWLH-Eef=HJIkKB z`}aCIVo`G($3}tExeGL-exh46Q#<%$Zk%@{e}Mew!DBxx*TJzfebGNS<(+Hg)F#QU z?OsGV*NZh~{Y=Da?GLDuaOnuKP#InO8Rn~ z30>d{u99_KKy(31pP1wS238)H(QTf@40Yw5GA*(C6#u`hE`mY%e7k>nS=$tzUuKQG z`xri3=I31E6t6SoXSj!jVDr%oQBZJ~G!!pGaIeeylM~3x8!k0XYwg{`zQMY9MuWY7s`^KS`QH)*8#YX%&2_1 zMN%ro2Utxp=galqWrfY87{?i?(>|wY<~ha74{7laRus)UmneRZr}tF#7t$RJIljY* z!I;|Brqx14GegD83%YVf50+D}bWp?iE*(vNH@Bdqv5OCUJxX}3jDCn<$5NeOu$|n` zo$E+0gHhZ?+!sqr%0$nCu6|2mTDOBCZbxJ*_n9_2 zF*ltK@-#Q=KW1@hzlRCZ{@4YukEC$k6fGPXrE6L8*Y4)7bltyr%8p#}_SnFF@|F_< zH|Gqk!5(sTo80y^x38)fB#02Sv>?BQ2CyeD=IWa`18Fm-jWK#w3jt300D}RPr3t+r z1zK|1N=sRp{M2xYYzST?+=jVsr@$qr7fkAvC$h2SB?<4*?JUz#?5lgZ;5Iao83!0zKA41bK~+}f>A4D z2sH-YbD7*-6^@i)dHh3SQtS5MuJ^%L$gZ8`j}@CePg;#O(yAjs)|mlp{AkuqTp1Z@ zy`HQEIh25h|Am9MOyWSU6PGs~Htry5U9uQ?ed2dj6bLn1e|Di7=|Gi~xUZaGoG^3$ z;6%LmV#+6%DvLo)XwreXKRr|lJ1K#0hs?hVuY)Aq;GDL8ByBCBt-HLoCV&xVp_qGV zD-}!2w_XNTfma41DK^E#i4>bNptf)(#b#(uHseu@ILJC8E9>U*LkcCQ%G`|v9DA$} z#Fp-w+GCFm@NMF&^1V8-uxH|{)j+!YJMO4Y22zKU)R-?0arU{Nr{zDBT29o}jQq-a z<$U+6gC&c4hOo#9C}bb9UN&QxAKj^emwS~E|FmDg1>_@XsoGt- zrxYijz~h7P8t zQ(vCiEydmgNYS|M#sbRK?8+{WWfmc40{K!tWG)%a5bE0 zF}CdG$iZc=I6tyf;Ez+vm-Jep>db2t)#ZfnC-QD=i|~@tU{fVUVP{JTlDIl# zUOE_55WaNIT&sEk(nEiLA)6?3g-N%}+-wVDOCezi6<$%BE?S4V9pNjY><_M}9o4&LxpzB^H= zAq+g9rZg%Oa?p7m+Q}v{X0mm<*uEXZJDIzXv3{V7&Z*sDMp>F8&I#2`V|B(9$|%_+ z0h+}(b-R{d`&$hkE~wQ|OwmBsNMEvmCe7tk;2hKyDW`tO3~2qrnL08K&Lix*q$;Xv z>_rhW<>bt<+5d*Q&M7%n-$x%w{WdF8QRggW?OCVATbyXz>EVbF95RoscZQ>ptviwo zN1j^=ACmuEm@}@n4KW)q2AT8S3;@Yi(6>CBP z)NLutgAOzRlbJKg=eq>mQ$bks=bX$>ruAa`!o0Bg<)z$nFDz)8L+S=k)hc6ORn*(gkvzGy$*UJyp#e7MieiF6!;tg&@0OE1lrEjg69 zOET<*&G!&-yEGvSacQI(Uh+-al5fC~x=-gUa*JXyDF?E|!&2FDQW9#jvCWHxMOeEd zhvfVD_SA~W;{uP4;1P20?q5Y_g_d6G0DvHU8PkzZ9bNs2-*Q@^w9(*#;#?ZC-%svqJ+{081R}>A?SK>g=@aEz zHp`r0Qb(|wwOulaR&B34h27R}NmJ;91*3wGZQdo59i3!#w#HuLE8gIHye$#*iuz z^t}=nbxp4||5U1A#m*5#HoLevMb0ia%H=q($*;>7U-x`SO>t+>nQOB*5@vYqdY)K6 z5E-D_-1j?Gob-mw^DF~eM47b|RCLAekcw?0oW}=7NG6sXF}?4#RXYk(9Sst?)^4Ma zq|Skdxgi|095Kbdaj3kbd^EOM0G=CtF!coRP&s5a(K%4lf7d$c*q^#j*PqbGb_ABQ z!9vJ9D*_8imK1;a)%xsxo^`xMSg_tvTdl=2y65PCX(%}oRv?h|;m>(0w~cRCn-jT2 zO+vCe2`MX+M^Yl=&v#KCeG;$+=g;PT;h-Bn;jbv^ zYbW8uWou+vJ2RZ?zkLF>ZfS`zQcv*s7&?}qLC?QVXCzk&oTt6CeTSRK*e94jFO zJbq|5OTjX&6l^*ZpzICuzLl7@wnngT>B){XZ#@?Y3~K4rw&0cy4P-I0yLqSEPj~Zc z?ziB&Y9v5gJF^l!Jne37PnX)}ep^pmTJ$4k`kj7lWBdlGZzlvOr<*cu!3kRHf`(6> z>e;8q2S%=3ZfiwbD+V`p=63YvVv2eH^JKE!Oe@w4P62Df6a4B(<(Bq1W$lE3gXGs? z-zDITdco2s_6DhDC#1NgZ>$Y6JS!ofsdcNJkP2h8IDyY}%w^3o6U&N&>wd=E+X<;s zmXKKr#&41*8w3ZfIkQTGH;eGk&LL9JReW^^)00P*8Oj_8PY1%Vr4zMTPBq7C#s3wD ztrT@|=*1uzx$5c2e`QtisU`)_E zM)8Ipr_xHi$8T>CR9pJf((iV&B43|GPqTGhlxZ6)k6kv-*40v`O|LvO_M|KcPDr@? zaJG`w0KUP2HnBknAo2~Yg}CY%<(7>cv>Dh2ACo_xFbfm2m4r1;+g&n|Q>&?ssny6;O$XxaJRG=rVi)TZFY>V~n9DIalyl^fyax^rPfmIwG`DS~vIe zQ#kG=fWjW{usXfAf@_i6nn4IlMpHTCsWE2Kf7PeIOZ zwia92fiu$J4d~tmgX=2zYb#4gj^e9}R=Zi~34ny{9`F2U^d((-nlw%Ccamqx4hBBO zbMrpF$38nT*fb4dj(v7K{>%C$1`&Tob|-P6Xz7XfmJ>hG8rZuN+i`xKHFd!1==ki& z(p??z9%Sk89hTN~#4-FGe8g^Js)=>sqekHG9 z(;fWUdzZB7vXi}cDYuUCe26^a_7NQ$o81n*!Wu+ubdc{LA~r+9W$Kc&lq`mBk;lvA z>RJXFIl(QPMUfpJJS%vMgrwyR8t*8KzuC|E7+`D#$$nU132u6QM8{hr2;cN^@cw=* zJw^;iM)Vx(Z=*i52JWVxlQ9+H!IThrLI;ak7 z%te$$;dieONy+N;vLh}y+BYybm@g0acFHj?l0Q~H_`${_KltDWq$WT3Xxedg{#fDS zdppbd8_A9Ll<(~i^e#jX;GM?>yY&qXF-YaadJ2tyT z&YHod2Brd1Pl$P7o8v-;qOCL7v=!`13M4$q`DXiw^+QC6WhB42kT9(kh2=ab%D)2P zix8%ebz`sWejf~qvrh4&^21>}|M&J@#9IC@4jQb>&077y@i(e9GDU?GduSLORU9oG zI4)B-N>UGpU>{ZecQ!rU_%0wtM!Fj*_XS(4W#hB@=?@)!KsG(uUo`F08EZfH!_`)d z;Au&!Po70bT07kY4S~jev{i39I7m`E^{&fc)xLP`=b!L0war_v9&$JR9kbM*vGlzc zL-Z+Y`(6`#&%R$~g|!B^h#Z}D^sW2Sc>A&0RnAO)A*T(FM-pw{QjJ`&=~bS&7d(-< zl5t$lVr_blvSVsTn`r4KzEH~7i7(W+`Jo9ibZ^Mn(i`O_#l7=+_}}V<<=lDrgE!aG zP*>H?vlPbkgIeqbtCdO^0|*jLmMsm)*o}cnsl>oa;TuK6uE09SzDlRr>T5 zB@zn&yjp;)o@LLOA=|!gqhfS-!TEyJVx%bHOT^E)A0veeHJ?^ugFLD+rT65aSalnb zfLpBh^>KmY+b!_J(2^ zNdhml#!E89v+8ppo+-9cCHgWo<3r(i=Go&s`l_Xfi&_m#N4N2PWdHp)q%k;U!~O3F z3q(C#K;SRpvwJOEmFeVQ?L;oaU6TC;*3QW;krD{IMMYwjnUlPVS5qU97m}xoNC_)D$jM}%JVbuOV{!d+nSZkc)R7`Jd77v#v)|y}bE?b8 zugA|v!~Uo5pP9HsRT4TWtm&#Umzw*>s0i%tB8dhQoh=~BHNl&4hxkNRZ4+jYn=eeF zv5z^%RJzf}SrFTJ)>5^J?iChLl3E|~Obkw8DDiCp_*>aI`|meZTD~y#L*%X4>?vHd zb5kguO?aD&`zF_hCx0Lfpc}Yuo^0Ii_{>b&McI5gP=?HP{NR#{b+I`9^57zB{p+7@ zt?OTE-m@w{{<&}EGp;`pzdx1VPo#*)BRxDodTr@p$EvZYghqArj+(l4;>)R6SH1$I z7GYTzidU83(pAu!K5(f#Aoo3?*z7-X-S7L7d_1~PS~YPS_WW4Ti470s&H6s=KQ{Xg zsU1Bw_W5a%L)#Yc1NcI1bNRygp1}d6Gmpt9rHa@eN&W37aqIq8cC<~kPZC51>=A@y zYbc0LXLtiTv1%&y_!;*emtJuCZ6@ zlaJOBV(1z<@hR3Eid}bpf?IaZn;-vtbmVLC&o7FU+6()*n#^rEtfF-U7YT22!^7l_ zV~av;^VCnk@QEijITa>$2QP}`Jox>VKx&ICZxj$;tq&gUGljpFyWsh` zVnTiC*-B*O=7Qu9h0PxyWFgk}`}Z;C)C1Y}Z@5&GGDNgfYV&3NIWj48UYm7M}uQaP$Ly<#zdT<92c-R{O2zTXRzHxZ83?Yfj5nPg^^0<(0@sF~sFA|3wZ~ z=TxLNR|L0gZ9Ob;<#t>dxd@x+{*Ls!53txEmS+F<3ah`xpK9syU!Usf@{dey?ea71 zE`LGl(Jue!)I(kVv8ms8`A4OGlYy6xT60PV=ZLDUhKWd4@~6u`CUv9JzcYU_;^wDW zU(r#7VOpZ0ARj}#^;tdED|hZCLE}mDns+?TMTIZ=RhJwbd1&+HmtA(*-ekwV9IK~e z;7IHFsU21iHO|H0{4IUBoldX8UuDwWblwO*JL`~0Lt zMQ$WFQIQjyF)bR*`fYWlo^N^@U5&aBSERNH8w6h0eKTyznQEw;ML5yCxZ3k*RZg*) zSAz|#UxLHBtfO<>2;v)g_&!bo&A}jy&0`wz@+u!}T4_mduOkOz)i^LABF@lIjF_^k z|5d$N>2&PXdBfDXb;c80YG8cumX{$U)_ERDIy&Q@`+54gf7!>}pgtyWMShoh7d}&O zZmCq39GF0en+dNsLIs>qmTj&<=FO*&>#{}}F{ z%KSyJ?rMTR2U@CbkIahAE|(23dJ1D)dIs?w9HV;TgU;f4WK!AA6~)z4NwGOCYzjCW zWKGvpgz-B<;ib9eEe?qKB65Q1z6QKvV=q=aVKb2}p4?5_p@XNlswI5t<21j<*Yao| zY<;$`+~=6dM_!7px!o5TH{16O6fn=t_LcC_18CvKDSOcY{P;6=+ePTHj+0+E@oUkA zZTD~{Lk0K66qP1`TwVs79)}L78M4Pp)oOEQzaBHOfjG~epivCT$@3jBNuf5WBH{k- zC6pZ_35D9E5^exE12^uZ$Xj8P|ACG^^?=#6 z${V`Z#CNoUGjwglTz>7aq5ta8VYfKRC7{veFMtzUgPH=9vZ}(!5!Im@$gAmJN?sX5 z7Z)GS+jr3o!r>hL)R6pjc@EZ!%H`oVb@pk~LCDP0PUWxURtq-W2wh0L?nue3lOl6x zokWt=GNNzcPc{!ut={7<+O7VM-aM2%6HcJ=yzaRr5+dNFi3q(vt3c0Z){Urg#o0 z)#hbE?b#?@2b<3WLv8Z4yIf)D_C@=~I?)=s^JRb{lO*L37g}1_D2>^tN%1;!2Ret; z@6z~i<*7A+wh~^K4MZ+th|T~Breh88D{*R}U5TP6*d$l*#MOp|s?hQsVvk^#Q1_d} ze6$SMYRC>C{A)zWd?VjwJ;CO81xd4|eNV`8$4k+~JjBWiW-051NpqGjXCQJ)GdE8_ z#GQJ5mOLlrD+Aq?c`~|?=f>PM)2ltrT!B|5#-QcZ1!f+5OIcyjBiLT!Bi=0{xyvEu{_Jr=dkj`5gT59B+vFMuUP_} zpdAZ|yRoDpxt(IxLc&@T2AMnw0Uj;P)J*$_5AE+Kx%>BbInVa%&mGTS4p9E~1D0PX zw+tNgB4ZkowalN&&n|Eai2~mtwOF4+;Te;^KRmB!ZErp6bQB_tITtVHQEHe^R%i1M z^UHs+O^G#|Fbo(<`Ra>SFSAQG54`BK(siW_L?ZDc-+b!s_L0*;B2iWvrI@8f%y`Gooq}!hG-P4`lR4fw{LRu1D5F84MYy6VFZT_KhmBwhUY?if+Ax_WJGJujNHf-ii+IbE-Y4O+7sj#3u`~vv;3W@DBYWb z9Qbz~2>y8vIKY4Z{_xX%+bUX^nIGYAWUMUFv^>a0GrC(}$nmgdX`oRGwOz>{^35LP z^KF!`z?$k&egZvV@5(u1BV1XVwI@i4L{VpX+2kqci#AGMk)ykEa;yvUEPZ5F>|a=m zsa}BSzf zA@kI?P|~|&`y=0_fTxJa$a~Iy9h-fq2qJ)&$D_9BhI~d5+I+ zeQiB4X?sO%W{#y5IH$u9plR|$*O~L6G<&Q-2u{m7mY+9FKb$BUMS$n$@wuW%unGCv zSJt^KNp*J&pMNvY*QWK`b3_rcWQIF#+5>Y4UozG@v;2opD4mX#pX9@ZLhK3hJyB4Y zm;?2dlx<%zI(9V*%ck2I2GYjt*i3&^G8sArYs{b{1R|HT+)a^e1Tgw$F`CH)0FKR` zE8B4NRM#XiTHKoZiy3sNfNz||x6Sf1^=xJ^H)GZ-wDnTv(YYV!(nvk(u-Pu3Z^6ub zfjfAksNF>zN~xk9foWq4&o$DNX@bNujr<5B!LPI=06awYA3pRkE9pZ_;y zIKif~m;(E>x%4DvOFH9O`w4v_M|FvoUBnu3r1%r4ip}sxkKmnsZeF$7c{+TWsp{pf zcjE#Yk|B4=+fZJOIpa2F=CpDiTl0$T3DQx#eKJMLI+vfc-oFEK(1+Y`Im93~(=}~U zf%w~BWA^1M<*lwZX`FyepoTJ`#Jou%^Ig0*w6Hxvu+*?`jM#k9ehPj7F>zS5~#6R_YIBUpT&XLhs3M zLls71M0@-rEpLmJmKe9vcD;GwR@S={xEYCfGVwkXXiVjZ5VDw->J#OlYnJ03(k8ef zTZQoE%Dgr~j1$ApT3&1wBb3rCvT1?yQ6wMMWGR)UcTy_pBEbrg_S5{F00f8yL;Q1* z4<-xx9cSCahVu8=wUc~AW;-0W*-uJ$=vm28%((o9ShIz=AXNhT+QanJ*VNAG>e9j_+YMZ3qFPHA(#ik4#E{R2F!KD%RH2?KD-o zkH@9`*7KyDxUD@!c=!SZ6#YAf$KCk?v`$sN%#r*OI{IN-FKTr;b%}8{&CrN8p6PnQ_{ei6Wt8m0{zq1mR<<1Cis&vQ6aIWg`T`k;(Qb zgwj`BGSZ;J-hze<(Y=4qF^}{WCWEMHz{Tv60Ep z$I`EcO`eStw0JASAC_!^ZhV}q5Ax3PY|I{gHs0+XHoNnW1NTzW96C}Ye=<*b=g<-E zgKu~bK1JG>DZ-gIuyhIXVyi0+Mt>+z&I+4&`pMc^dmFxmlimjUhbeQu=|WIZ~tgP*hgGLJOv2hj+;RS=hYj z=mWzh^&MCDveFyNRK}9gHp#8oV;Oi|jHTr<8cSR}iFoF;=YdI2KzgF~bJKp!{~0Yn zcJqbFQ)J}|P?vax%m2x>_wyoO*%ud-y+BHt;0+RAE4WU4B+5F1@j?1a_Vee<_Cv8^ z;T-gtpETB*8!7%-PO#|_@WyG$>g;&`$i%5~XJC%>cRc@v_zm?xzou`2wJ$P0o8A`K z2Sq=(57Q(!*ot6=i<95&03p0t6i__tdV#Pt6JU{V@;TNzodx5OymjO-dOyTE-P&uttlNrxXidG>)^$O9B&^Yz z(Jcn(zSI`dlo3A$dQ$#5X&50zS0Du2%LDec1xiP!bBoasIKC2gLYfotz+x9C*Hs`C z@0?U)erYNFdQfHN--`Ci?VZdDUP(;MvT-Z($~u+>;#Iwa@!Ge=J0*2rro8leZopnb zaE7uMblq7Cjn&W>To;v@tnFJCkm(d_67eN=?3T0S6>mHxz$o!YCFG_d-AGL+aqXmf z^VhpQ!X_U3kt|j_TpZu`ZItZYRlOopuNV>Uywt)AsHztdE9buQL^f>lyj12{%Ta~w zTy!DV3}o)u-2x-4dM%fH9$bvCA`L}hmv7pjsNr8j8Q+yx=xaTqX&1q{9#TU*mtW9m zojDffBDFrBYjsCXOyr$T)O|wPpg+uO*_wJya{1JHimv4Qb=(T3_g8Xx_&dvLdsn*G zTEOXVmn((d$hQ!9`4#?|e}nJ@TJEobT-?NV$`j(JIkpZYN+BG) zA?3)UvedCtkccPUl||oUZF%!*{>@G@{QRi&bA|Z@mVr)yzvdkTvJu=)P(Fo9ynWhkLcP(OKUy_*#P4pD%nOkQ-ks} zX(@+0zea!LrieXxi5ka>SlC>aSBab-f5LHy^({c^N`s4gOwwO(cT{S^5NoJ0jEzqqauQQ+ZZ4bLljk&2KKh^-#M!q3-x zca||Ukt>#|zn-e+1#7FOiXvVaVrf_25z`WRN3aR_2k_{uM_oqd&cE{p6uMWro;`3O zO^4b};Cvo3Gs&Koc5YStYJNuCKyHH6&1)Y!dH|2!f!}@KChy^2TEM;cb>=7D*C@Aa z@WxDjxfkE{XmqljeT5c5kBx?Az4Ub@O*xmZL-Nl`e^@EBNg2*#`}apja!bJui{$=w zxZGhGe9xMOKwE)b!Xav%(61N0sw{EX=GhTm%$gQ`%Vne3C+L003)c3jJ5w*O_kQ1- z(J$-$ch0PnS~?=f$ldhB?ESf-^l1PXrETu6NWHQ|NS9mglcF!`?e(-s9*Mkq93AIB zGW(+l2mc@Rsee88|6ZTkhRaYopCzI~fNYw0RghV;>2Mn5Q4uDf!xSgzL)qngPO%T5BBCXIWOdfgd3xifc8;9!K}eHKTV z>H_Npt7G+J*XNb>FMCL3>EZes*Q^;fJ;t2XAeZuo&@p26JGy>nOMfeFPOZDXqpUx8 z!}nllsYljl=2z(vCrGSVC&4smhWx3j+;ZlZB~vrFUCA?L9Qv;peKOBW+yC{bSa4Wx z?7lB-Uf_~SR^JY$Z=78@Ntf<;Rf&p>sn_{*OTF;mcDdwEnT)>8>tF7joCtlMel5HI zl(+uU@D}e^9&_13{uBJwgBm;x<+m&tufItL7%$V3Rr_sk;kxp{P<)mA|EyndfcpF4 zI}2XfjsIWp!`AV?mrv^}V%H-wX$I8D0%D(p?UCf?;ii>og?uLrIm&rxA-;?~zt}Dg zn~&cSZQStl8}Er%?`;2Yk;hZd4wbjSCl|lx#P?wVyf6?6;t#cJ4h6@^qv%I^%wdMrn8J>$+Kifp-Yh4ODnM*Ie;hC z_z3e2pHlu(7thGo(%*XBZ{}9Eei4b?MG~FhZq4WbWg)oqh3Aws6%!YE`PUs2H+lI- z#CwgNL%b3Pox0Q>8^#a98U-bhFtk#Q->-a__a@7JCvpVp!RKj4ptZW``VRMy8 zGiu&*q1`NCLp-c!|LfylN!{bkHx&{(PI0iKOvp%zNOn!26V-UATdNAdCX|7bP=~^K z9$cSpxxji@&}BU-sPgZ$6VjTcmFe?N8UJ_<5fDl=%U~pvnplZ`jxnr48Pp77gyVY% z0PGD4@DA8;RRNxGQ=IG`lkleHRn?AOqy{nK>v_Oio zTVxY3S3Ug&$uZSr`>-&I9kcy#B@2i_=;9`6w6@RRsaurV{}9LcnH%8=YXXZdw76!R z+R$rXQfRL%n#d_<>-d*h`PLl_^VgT6@;g9)k_uD5AT@@K!vmYI8dJJ5!q4Fm(UZ`Is&% zo35&h6M5zKt=i!v(aQMj$YFVPcF|!q)#f=&pc#TLyrbmUjKVx7MeU1Op#Yjo8DO2~~ z-C~3+v-C51qayKYcqAH&HAeSi`;V1O-HuIa879G!{4X*VnM37Yj6-E{f0}! z4B}`8!BQM8ue^A|KJp#rXQ_s~_%Mp^1&M~ul4ac7r^j223LnF21o2AYqpl0qO1W473cDd~0XORuSUN4kqEAM3KA79mTcSvd6P6pP#ltn-2s~iid=D}4DO22Ei*PDxo_(Mpvs{g~;*}z9tTn&FW z*+77W4G=U+(5UzkK_x0`jA%&QfGE1jngr2+^`qS0{!2wG|V1M{B#cB^lZT?*r@oJ0-ox<*T8F}C>?RU_jk6Z+dZv%9tI|yjK+!o$TV!_wX zV4ZKt<^A!Qrbl&@*|xi#N1yD@;Pm<@lE2VnS5WVT)NI<8IThSCJCguY)VBf`eL{Vs zq&K3m*3{Q4)MxZ(pC#`ZxIQe|Th6o>{TO(@d$V2{J7np4K%Q~KUx2p_o~rS}A<1m- zB#ch0gfdpeX|f2kJT4g5nM!p%fwr--Vx;v|4@j8_59r#TkjlnBA#HGZVK}V%cDYBW z-KW)sopo3y{m^Rl(=n;Vqsv_CWIniSwL0FVqUZ%Iw9iTHBX#?ku-vkA@hH55@U8T2 z$q`ukefY-!)=8CH_=dP4VDTe4u_wDw)ZADpUd>9&h}eq?uqCvFZhp=MWEk zbZTrj1X|jpjRskGEAvK&^R3D}E1ZwN4hq;87ubJt0e)HlF77koYyT6J|z?X2o$z-wz=skuClD5 z4iu4?uz^$d!DT-cR%q>K;WHg;UoQxpfMec5N`jlLz%7vJdb3$76$tlGz-qwU%?ZqxJblhoMJcHAAw zpK8)`m(Q&)XsJj22`_unT|rDx8w0JVeJa8ICvC`^TEBAbrZx3~6Sd9V_61txF!yn< zvHI<|-T`m&woHDkZ!Z}KU18Vw#rp6Y`@e}`y!mE7tx@?(us)!g` zq=h9OL-leS)oyq#tbQoO(ZS)LQ7<_6SK!+o`6=!^}S7T;75@5^5B9=Da4cI%hEPgbH6aGo1Km=*0GAR)y%Bdg%Evh=IB z5;^^YBJ9ZIr)t=9n%$s)okPDv>SErt(8b|q2{CO4(61322;$^x*+;ArM7V zF!lmtKu356&ik~3bTg0j9r{fAN$Cf`IIJO5U=J=^Dwq*N%UJ$=A8?2(S$!wT+6S>X z(mrDUB}^uwAG7{*1ut!wc!q&Sn`IFX_M$7a9rK zH%+g%XIxmV{_erSFrT-;$2IQMJ+MvxLOEcL*Jj$2`E}Z}|NHsnQ2rFT zs_b4wOwF+7=f%Sv6gixq=I6QlAxvHTkd*9ed+<7bUAk5m!=%mJ3--i@R0)M2%wEtP zSikIAvf(}wN|DJ|6>sYKU(CL%KKjP&>&H)(&1%C$?}}cnr{7DwGV6?~#ID#R>p*|~ zn}!n=R+ajd?5oWDJIFz5kDh?t!X&l>teJqbVB^4guQq^lCgAm?rN>JXN8)%%bNQdc zWS0wJWBj(2AU~SoncrD|_Lg)sEZ^4eyh8K0OJu&xWyN7Z6#`i&3mRcu)Lb3@ACV1;h@HoddYv=(cKWHcX{}IAUzAv(T2n9!ue^)TK^da~w?aNX zYh=5;3=268>IVYq-B)R>*mGEOd>l$%2=`{wvQ|A?BU8XfPVM=St0!s6RvlEJ zOplX;5;P8R@rIcmUv6BqZX!io0N~uc%~xYzwagc`{mY0a;uo>8KvTti1RgFsrJxlTPk3}nax_*Nt5lDI@;J(^xXxHqdUHdGp#=HEc;d#HXcINcKeyq>1fMb3yw^6>}Uu4LJ{X?9q>0a%n)u>>Rw7r#i zBg6U7SasxcA+R%NYXXDjDw!TWnGDCIl5Q@jk7zO+kxH6hP!|~^xej1OEBg*D5V@48 zj%S(*S=Q!WY0qHRe28_B)#c0@wn$S`i`*I!QtQ-fUzv&1u$gm%j%ra|U@B6h?#C0{ zEMU?Go0m;y)OjWhX2_?0h@zO5HG&z$F2P8f9ft~@_3}4*>;U;j@{2dqjQk=`Or>F$`Dh{t#APXA zK&_dNWy(__nPno;i@T((kh*G!-kJd$5tI#iZ_|HrB2Ov&=@pS}rg;#Xu5r*=69nUhS<#C2?=GW`jJx!#(G?SYUy z#63nYYg?`5_cILE%2oW0nd>xbj5-r`cz1{1P&ifwnd48F!s>8jlyv-G6R&rspU@9D zl9fBzLyYXaz}Q#zv;SpFKtRwhI6bQ=$!2T07lf9^2zRFzj-QuP+MpJWQ;(bv&7N~C zo6g`D5Nd!vC@)3Ghy(}Xx(;{|~L&gXw-R@3l&F=oPT4E+-W*8p}7h!g-xSTwSWcv5PTxx_V znSHykC8{l3DWB8(I!8kgh`&a%t)XA(!0U716$@%JyS{gIBZxVLlv2n?*a3w}W;<)t9fOY&>4C;39=`f1iTxf0tJ`yzTpe}0Fc!NK(j>-8)ukBwDHE>FeJ@kPah zNV||M=ZRZ17_4R`+s|7Z^*_28@S-_-;Ub{$#K4oA!Np)_5JHik^f;=s2Vc+BH`9~k zu6Xz2P$b4!C*OAGi(c;vB6pCe{xiaHV8MpILIpO-_8arr^V?!QW)uv(3!Zb}JxrBl zz+10o8c^Mt`~2O~(o=}t5AYCK#h-fhRfiF)Hf$v36XVHD7qwY1w=`7XYG5<?yz@23>DP!aGr>pF$S$5r3_GPo&k1?N2!OR(`=R-Iw zA*9%8NWjm%_@ERLz#fSxU14?J3psqyKF2iv^qE}ppPA#$B#7_5LDnE)CKH}~q4+|<# z)!;9EC;CH(EUcEh{4VNOH_*Iz#~GqyA=jx}Hfiuncg99oF=w79rV)Y%mj*d%&fsj1 zDy2wWH8Mxuc= z-qJkf6q@x!$4Iqa!#+9B`Vy+3oYM?0El!CIl8~;^lgSr(5}C<9t4@7uqeB_GXNyMa zIzno;sXm;XquxCu3xA-x2{wu#gY#0ZEUkA3v--*a;SKJO&t`{^`*Wuq^-gh zkQr2-;O#Ocyq%?Q%zK@beC45GlUTYf`UIXX$k`V?rCl@J;@*(qx!(N}|NJh*4M0*a zv3V)WksN{6lPS;^+bt~1ZrWL|ItaH$9^taBryDN&L7L0niJFP8+<`b0myH&J5MiES zb&*f=$n+hgq+fGjL=689q#USZoIHfpWr1wQ&p1DtB!hBpNd1NI=QQIwKRQBF+-R5L zhR=1rMvo=GAr(mOEO&-#QVCcOGMLHJ+IPzG5CPfcjZ=j(pZ^vPJdPC0RgapE>P7gTyf^ok+cT}TASI?$s?X_Ev7LjeQU&Ey* z-2~!_afIcr0Mcs=>Go#M&_!(jn4!$~gT$k;$LYzK4N@Q|>m6sRGz~=D((nMpjb^Hj zu6L&DYe*~_tx>4oZyyy_^Y%MnMo+KSh=kyEOL;Y_4oAXf5QJO<%NIDD0nF^8SW@n| z>B%*PzN*}Ghj+%NOON0=-A<46{7n0=IKh zz*A#Sk;zbRreK{T*whjHj{Y+NV}sRy!OfEdEs0)cPmy_w%MlKmo%2=anE&$yIoc{C z6t3qw;8WO|Q@nI1j?Yq3%DfJp=zX{No!HjI4RB$;k@-ma)Yc&9ps3R^CD~b{&VE7< zR(Y&YeSKVsp-F)iARGunTb#t@gF?@(`0Oyxro2)VcDWq<}Qk6A1QgQ zj8H%NBH%!J8&!QMdUGf0byvFdHZJ5)mt9t`{8@W^E^b$#}gan zv`ZfxOC{KstLPtPHlQn)P21oBRh&%~>HQ7cfqZ1HP-Hhm+&CPcsl?~Q8#TJjMz1;G z?aXyy80-i`>hsCeq2(urhvM3RHCj{+*fr{Bsr*N30X$7#Tx=sH@oO@uHB_PDX9r1B z%Yz5of(Lb{JA*h2u1-~AI=)0&B)^s)^5tek3{R5{{>WKJjL40=CySog_gpb69Qmib zaAk|b0ejj>p~!xDAE`gqMY0i>6?G!uH%#B*U_y)whna_iUj481V7PL z;n#TDg`vo0lK2G-n?%&2(s)&*#Z=F!vUonUM~cPMkohSvFFIMrRZn%(Fesk8h=K799rk z8T=xII6Y0=ew(IG0X)0@JbPN&n>f9a&l;K$eAdPbnerg?H%XW;^0#{KNf{R8@!@LJ z0bD$ z1zP(_a7gqQvu4X87m+;+hOp*muLY*Mf?qI_9`(te*|}h0GZ@i4W4_e13mgcLln2m{tKnl(x~9b1dZfE;Z>osVc_zklCBJd~sVk6iP{aG2Jet2ECEH4f#9bnp=l zDjq{#`3*|oP8pNY4;q%V@757^@G-fFH#E@lJ8Eiccqo%T!GFDa@(&K`C##!JGN@<2 zjq3#gu#Um|b#IpEN_%)N`jBe1h=}icpO+!jv`bDI=9|fB*FHptzT7DLa@me|vK=3Z zW9Q*OBK8_yl5iACHwEV6vAS^ziD!0`Hioq1eV2CPE>1@%`QDxGxI;(p%)W zGLtLZL|um;`{rMS(Lem|6Im@dBYRRZjmyZ?J{^+Ld6v0(Hb|bYvYgrx685qH*C}o` z5)&#E`MZ36%l#bER}<)(xs?!7-5eOqnH!4ik!+8<*)%w7!{Yh>Xt$kDB*#z4aiu+T zO|=?)@4>!y!7auqGU;}@lWwP(bi2)@+oS2=CITtUvZqM)dd^!?GcV<@O7s6q#I}3x zpN+nvEw(m#rkQm{U(rdl0!FAjc9^nHNR79j^A|4k%M5kwbNZvb+nYi=tD#2v-`pg6 z3C3BYbw|(omxJ_u($PmUGNq9|%FUyZ-s0w|v&RY2!)g@ROdscys7H^`yF)oHN8;LLZ^+=sqsU zT4_8da(LxZ*XJA3Ky(#k(wCGcc1CY*V4188T!bn(p1!ZuZWMHj8SrTFG6Vi~hXGMC zk&?I99U<>;rr(9tB)6vD3+4OM>37-5B_B<{A1&X1k$yi`-mUce@w_J%$g(~43%ta- z>!H8B{PGcchM=m9p15A#1Z>;)%TRq}J4g8<+*hg_rm)ugeyPbi_P)!PzPY~oaiL`@ z+z022PB9f~da(V!#EtZERW3n{sI>DRkj&_cyOX0qr)cRm;a$tE9=d4vf5>`PA1V6G zS^TfnDu*Ir^3IAqw5zVv;S z+{h1)9RZbG4wTi6`H|(k0cYbNkpX|CMO-*=$H)=z$$#7}^Q|zlf;^F*^Czs{AZga@ zaN|(*`%WZPqnMu2@*pE5x@4f?Rm%jJx%hA-OUik#e3V`ZAo}8DZvpkl{V&<3TH-EXB zL0)oT4S(5V%`eRuZ|c|lXKkfRZo}a}ChxrWlibYq>G;Uv^Ei2DUE~!8Of#Kqz*oN{ zx=a7_5O==Zz;~Mq?`2wfXi83~@)*sPF$&*Ua9^#qw30V%als z^ik9`6{!fe{eVNhM!!Z~jd`l`W3R3TSucc9k>3jC+9R@vBSisjOcToOb!efCZi!kx2b*V&kz zt5YjnvN@6F!fJhNO+UVj^?=yLLzH2;FjaJ#u|nw&B_ z_NjsRK@d{1eMMfmHOs-w7?$_E5Qa ziiz#0jBV2^PI4RY-BkP zw}Q=9ZHqJP;K{D%a%2lJH-pWbC?6&_VlRU{mfN*Ic2BaRVf%vCzL+?RZp>HbdEt^Qx2=ZY(}9HLTPVBx- zL^I)6R|B}VAy}+AGipj=qsn*?W%y(YDaMl3SU0kGSp-`_mVtQ~%X z)H5a`Xn3)B2VE$A)oR#dy&Guh1MTgL_~6}OPri}gFSi}D!TP-YI?1cc6>louQ2OSr zgRIHA7PJaFnbXkWy22gN^ECfnF}ekRMz>KPZox`>7GAr2WM7UHsk}@iU|m?Zko)QL>ksppO-u}_PB5Us|AT8K3%;F}7PFKBW$Um7yMtQgHsTth*8mZ3Vl zPe#_$GqWb&X&$~DD6t57?La+DPHuUdT3b=hqt&gcbk%*K$}~yHoftjJVui z%&g{O?asD*{K2A(Ys+7j9o&l<@UoDaIt?M6`;N1G07aY4Wgh!RfAnm7kqFife{i6W zGhdu8oxNNC=@vt zzGTmzghj#Ht1udk}vzmazsx{T- zvs8*-2X}pU<%O$cy==&vvP~!gqM2V9Sk{rNcMJI^AzD`D4y*DH%Mk~d@mqaaGL(qB z=#RJ$Q&s3q$?pJ@cJVu$%CxgycoK*s{>NP1b=|?GjGSuP1PT*sm?mR(#{UG1$-i{w zoH~M2(Bu&)4rAMVsyPGHcJE!B}#w4$Y>57*{+W>epD zx@OYqJ9ETis8S|m>fcUEVk|YJ@ZFhn zrc{+!#`n&gmlz&z;%5C^rPF_)pMS5PujQFxLw|Q0{wO^JwD(I>z5=TNS-=iTd$*`+Mgzp#S$She?C!G~6hvJSq)xw}2{ehjD_gR+zC;6tq) zw;p5dB%B-Zz!O7Cwy%~GEsL;GC9jInS)k=yr>Wlwx+l`}G(Q7^#_Yu9CPQORVg^t4 z7(WtQ*_eB%CYrBLq^$E+R#-YY@lW_qU864|U(H!wiXTxkdcvO*67Rb!r$@@j>koCYr**OG z^|M5-^Cqs4=c+t!LR`Ldz-irH9_cJe^yd_<9o)oYcYUQs!A-js)gOjCK-p z6VrHd61}>V(jfnvQ);PeP~rrWHF7fJnm7)Cms|>G@suaFlLfi$Vu;e7d>zM(f+s)h zCgR~wePk2(S8uIjn)q5J8L~ofSv`sN1Fd&cI2`fh%lt~L0awPJNcxd{vlVQK-3Nlt z_sD16T9CM&-_D%VbdRnu4F{VqI5m2TFpRd~Hn`cl`_Qq!2K2^%gbpx!Dp{)|gqO_Zs8`qXHPOSlbZ5>=-QvgkIjW!Kh+Ht^OZdr; z4(G2v5mjE3-rm2Sf=`jbNzO`s>^x;B-#*0Lt8piv`fLLLG)YN1D!B9lj044EwALO$vQy7AnoX*tS;!M2_ zKXa$vv3BiCCA)0=LIk(q&iNf}pNaB*%WA1AkreN@;O#v+s2lteQx~cZMN-^x@*nkv zN&T&0!2W@|zrE2*=mH#?{TaLK)t885B!BdlV|wd$of5dc#Wcf_`9hrLe>2D8_Y>bD z%ev12Bl-LIrMY>DYRw1bD9D=#>gRR(xs+$Z;|TDl@@OVlj*Rfpyh>l5mdRJ|4)+NN z#2QztLzEF)7xD7G>KC}$@Qq(xPMZ2DFNt@0g=TvJATbS!g{?#i7>oMI81#a5>H=`DAwKMQSn5;o8AZ|8oJSgI5C$yQSnXIh_uyb2{*tR+ESf!K8Rr zCIyEa>oOC8mw%tvrYvu!4+vudQbUWJd8_0P$)V;q?zh;9b2p2MUx zBWjbYrqSz;=oQ=#N{{IGO%?SjD@lb7PK5^RYaF1zywk;cIrj?$=rwZ5z@5nxczIN= zpXr4%HhhB`qLn@^ht2|KoX|=Rspvjl!IM3?2r0i#ef1AI_Gi*MbGmiq zH|yu1e*OW^!adsX0Dugr1tIk%mW-?Qm7L&fE5tRl>_LO%Y~FurtVpt#qeFaEay?5e z7|M`GuLebA3PmoJ4&KG1_V4Xbz#|SafBO>`nNYxEg3L!STumVpw)n{E8>uz%8WN6B zz;~s#PHGdiinDB4x8^6-n(vK+)f@D%Gv_hgq5DW>?%+n$U$1^CV``DonRA%NUo+3< z035Hr!>lPMNkiWyu{zQrL-a0>1N1Xg(=`gLR_JPEHhUALJZnAKB#!tW4fC~z6lpSX zE#ummvs`BzrL%b=x6y;hPvws`9eME`j;R(wvUcU9khwn z`eZaCb{5*;nSSx-UV&4H=n3(S1(Fu);hmqo@tZjO|0sSF4k^0hH|2R^Z*i}AleI0m z-(+}EXZWkm@VsP*zMFi?(Kl=TVkGig`ioB?VIo~YeJ8M=FQxL%;1*A3aH~fpy6JB4 zrLLgttw?%}{5y5A9MN){Cpq6SPa?P?9x`)&%#-INdigNDlszgIX9v8i#gA=Q&La7U zWm{K{>>z|6r>B}>%HFOVp>e|-g^o@A!hKA3S)Ap`k%|Jhe?krk>sR3u)6|x3UOR)2 zoYEQmt(M?X@~US~W9NQh0oS}&wfR~_=9DYRgSMkF3*IkYRk9JcEwA-fe3Q>EvtEV~ zb^3UgN!AgawGs+g9(+{n57ehpXwaupHCev^5=a=t7S5J0S`d5UbHJ+Z@^rwA|fM922mhsX@QT;28o10h=zSyEHM5Taw7n_ffF^*x4N zpV-8eFW#FSScat^cDgz!c67LD(-&xgQc!}))<}X`d?0GfKJS9z4g|!#A*KB#8bA{= zyj`dmS<`xm)Kmu!cl|e_JCpLgf-fFuOn?uxJO{&YD{TE0FV0a=pml|O0XqVaAk7zO zy+LP|1;`U6h}AY8sEhK#S*c8XhDr}(gUoiu^szU0;cU)KXR*!KAL)Q>oXIchJd^~0 zUkXQ_Y!8@8)Rd#YU4M!{RNHG{`hVeAyZ^*EDM0-Oalp|d*fP7ZQsshyag@R^YQI1K{7APchBEAiw}m4w3Y9<1 zVYr@Ts~@2@Vi}uuar7|!Bmrk@^bxZTd6ExOQ%F_4;`OYKuqsn38E=<$H}Nfqn_44( z#2z7}F1*drL~Doxt<&{aS)-{C|57V;i-{7Fne1a6rO5fKb3AiV)9CQ^x{GbTXG%9p zJS*vEApW*&zRhv8V~mwcVh;Iq${$V_RO>M|W_GEo_Nu;RDsKA|_XSk#nJqn$D^oV$` zFPbmZ-&#zrnn=0S@C-FnN4ht$5#h!q`-)~s6-ALOR4M(dp6}e2DcQAL>?U&U9Pwalk&ufNTH%y<$=Q%Tj*y{>-_hfoxRXS^mXu58`^gM25Z4fRdZxZJ zTpi!j!&0S+zOT=B?QAWZ=#LlG-t3Xl`Uu_h7A%)Ta$v<6}@FRjD=OWQ!rbiASpFt65kt z8SCJC5cE~}C)l&xMV*2x_(L2lx>T{S7>0BU3@H?8c8yRze=vOz+J%g!Dz@SOVqfzV z5ez+n)(>d0T@*u7S*N^6Sr-SHlmg)qxIe8i`oZOG+) z`EtrX%$G?==H#kozNqgqM~yPKJDQW5+@|i$V>;tY#ORmQeHbYxgjtyO(Vt7rF98}y z1tN&fVBG_3Z6{#v!^wqO?*3zC^!?8;wBYoa^9%C?a%S^nUyPx&3h^}CuFQwu>aY58 zIq^lz%NCm8egrSZ0g8wz2MeSxZu4RF@~OIQJrF7Qny2J5(mpYJk^@QcQb|BsAO2(Z zmF`#vruuFfwoc~gVxM@w=OEz_@f$b<`v18fJ#Gu9JDL0&E2o|RUHsEQ;8^1`zwsPZ zNlAOmW305ASws)BvR2ER;5CE4t$u{G{`d1>V|k5&arJ{PW+EM9nGR1f)dZ|8t8!9b z<(ZB_h*dc;`LF)=9IpL99_JudXYs+S zu0Et{paZM|#3WV7VZJ}P&g@SFK7D$N50yTr2C8xl$o|5aCIT&$EYFkT<#eIaFT>+N z+<*QUeCfN|_%}n^^i!kc<_J$v<$M>yd=H{E--V^6zti6(dnb~Xc8IRzBEIpGPH&sf znICAa6=|+Ecg2jXmaT#H^SsYXC4rV^k^}3BRj~X89Tuc{|5e1=BXJr9SL6@osB&;% z{l?Os*oXQj(CX)7+gz{pfcTfxnX+59FMl<9y8a%W(7%7pUs2!3h-sA>S)Cd`aB37! z#C*jm`Q5TwbYaB7zqXLqx?6l%nn*y`pH~+BmWBVkta$Ht0xeO>$ip3F@}MShV>7U9 zEYGsgwHYUy_={lcW-7HRa&2AP3>i%QWNoX+Y`}M3S**SJ+_FH+7ElA7=m^|WLP)`|TBZj9~Of&HGP?o>^|(yQBDssgSK*y@#q@H}X4>8H{63cXnv~ zdzc*B)o8W)6FFE&J#L9=U^BIpTN?z6y2j+He*B>(-GUrm7(ek?znIL{hQRs?yqugs z@px}2u#9_HV$isJq82EJwmCqmF|7G!;K*54F@7QTJZ!NX7x)t2z#%ic&B27VMXwrI z$+50#X{Y_39M3G|haaIhr*z{9Zt-&{-e-XCkPoRI-ob|?>p^a#FPW5|YzZxx+jv!S zv-}KBJ|{o9$tUHCvBKsBv%@k9WImDPR%CZzysnxHVm7(`9!Y=`N|;BqaORY z5%GLfBW%_o@x(;3Gk1{s$eGxkTL0p`|Aoz6@1ofIaZShG#D^fkJZB}|Oe5NnM^o?! zk4ikv_i*IDq)LgjB=T@AL4YtV3$zU5TV*0gYA_3kUjHHG=355K_xr%Ww1k`j31}_C_p}4SA*G9%5mU8&ONSz2L#q z8ybN2RiuT~A`zY;z=OVpNA`_>;-yoNVTa47!1|Hxd*7g#Z{>0?H{?jtC-dTgC!Ir) zgP>&Qvv0I)cjoVO=iec!nA%3B4f8%sKHk5KzLp{RrNWFRQ~Be1y~+(6ur1vqWp}WZ z6UgX+@%z^gKdcve{W}K~VpL?8k7vnf^ZiwnM|cZz4C1?j-Mpj6+ATjJHHb>BptuAQ zw3aeD-K1lK6z|~H(*S!h<_k{nu{5(Q#+Qfe*xowrp7omRgz2F<543&>Q{#Rs;nVB_ z`ywBn`e*Kpo=h+_L8xzZS|nZ zJ%PCFuuUEI%$;q%k*1o3auIST|4S{uq!!-jgf~Yj@?tC1-=2beq6pT`j~#$8&C>ua z9NQ^ZSyZ1cnh|kskwW}sntCH0)Tt7qjF6_glYEgcY<1+rw#*L+@zZGSldjC0h%V;@ zGYjl0R4$xqUe5fHho!%xSL_U<3v@WjZcP5L-vMdf+P&iY?-I=pu<6-bqdbLTRWJSG z29Qtm-VWeL3VG}PpOAsYM4@3Nh~xs@>6y?a|K{?~y4?7F-@>JR zag_~x3kTt>*P2QEy>9JHcqNDQ=wGj}ZQAhLxLc69+?`Of51JAEU zW}@-04m=_IR)4)c2S>UyyQ|fya1UHr2fL@!SpjDSq5Q0w-45XzbH#r2Z`WZgsBGV> z`f@!td;9FmUlrR-7>>w9I|z?Kz=h&X-Y)#`wVVrz0Gbv;Y6Dy)v$4Q3K_IB$mX6!t z(0(X=*O-?YG$cx3v9f=bp-`6}nHsAg#F{Z!McCu+WXkLf*QkGu?@zCZ|4;`g5{fpX zh9_y^Ri=sL=CnO(^OU`eDh^G#J+eyA43VUDguuoO`I^cwS^7x0?Bp$EFcnc_>1WYv z+jo-U-N?OTelY>uXuZGZ)k5067EZ-Epg+4ODt~VEkdqRD$&%OdJPAP4~ghZ|VCowX<1 zgD0Og&i!BxmtmqJ(~qOSX%|aeO)~s@$=-(;QYX)&H|u>g%EQ*{$R1e!Q+P+cwx@kU z66!FiZC&8x3AAc6#@M>neRjjz!0l7Wo4Nj~UDxOxbI8)+eAEpX6=~;ZrLS9Eix0cS zSGs9_p7rLE_q4mpcLS}v00gJ3IyVBCG{}1Kgcfj_V@P!R@)BI*Y68}5BGmj5uOIJ` z;UV~+DW#s0Hc^A@TilFO?;-I6grJ#EMKW3LmKtl6&+TMC9jM096*t2n(IcP(&2MXs z2mp}url$mi{E*Zkp02L_Oy>$&JL5gxAwAE00XM@2D{v|{SXvys^L9Z|kvh3oI|^{d zQ#(8?-clX1?-qY%jUMk?HEPv|b;cn@$-ifYI*Ivz?#Vsh1uvinF;OcYuYwc5G+rW|d zNU8;}u)5@S4Hw5c#E}+5BI6t;99g`bYi}=a(O)5!E(1(fbfgRp_v+PWFKHsdvP0_m z0?wc8q5cCnu~D5j@Mq+0z?#;NxBI9)q$d2>sWQ-Vsxt@@J3hKVl2M{B51EXq+Y|ev zZh_YSFr1hQVQ}8wB9%GFw2`sylFG!!wj2m~3(E@8K-D~TK9Eu#X!%2C|q@;Cq ziCL6XAFLOxsPK!r@QQ}qtvB6*1SNeOnP;%6>Ho_Fc@Byl6f4hXdbv)GBxToW>fkoH7?sq_;OSSD;&C2B^GCn8!t_3`U& z(aM4-nF@0x*{UqEgPw=F!5Qg$V7 z9Y20M+c6wA)g@jh!I3YARf|U$Z;kquz9*KKeBqGeCH}<3QE@2G`+_~I5JT(Ih90Y7 zxAj&aJ_otn-OtF9J2xL=gs_c+@_o@u+I)wVO)Cm4eG(-NzKuSgf4V~s&iRfU-kXc4 zf%szbbY>OxmFG_@{c;|LoVoTzg-Da|LJ1!IIpHBf6NvrVNL;C&kkzzT4L@9SUUm$f zf#pRMhyAu7etWE*3G8N?*gh0+AEb0!;KvoD>Q4A)v~IO(l{IruVEGiCsET(IcNPUw zS#8fe^it(sdw4gStZ&=Csqt>_w~=5A9Wt3ASpw{g)8C@WwwrL-(^J|dM*p|n1U5=H z&O5`NELB)v^{1~H7g0fA=~OZW)=%xDmMYVN^-Amm3zbn1o2s@8M|w+x-GP?DWJA_0 z?wMZ3j;4+4;q0ZlOPh8FZj*QeP^w)~WY_MQ5ZqlsAMdc8#2E#pF9()=N8Z4p*Truo z3Ffjd7$r7LF`I{`9&giL>t##!5s+xNJ;PtzQ@X`^A<)(%^wdBW06N+L)In0ouF@S% z#ZI@RP-^_{;Hw!wFki{7o6;9=d8aJaJr6fFJ;ekCZ|E*=*lT^3t@kQ-cj%cpoxDRf zSg{_fzRgvdb&cL?b>*F=rMRRv><$$lU~96czU{&ij)V`6;a<6rW%e{b zY^~cXE**c2FQR>s^}5Me(T}tl6gG0-k#T6s)%w1wu4$ToFjcr#QEw@&z>cXz6 zrn-inM3(eYc3Vu^qh;S#kv}1*iZ@tWtQm!P9Vd!>;&bVC=J^6#9ujZ!9cfoH%jcoN z*_}8{c7ap#?TZQ$o4%2sYggsE$&y`(SJ>tZ*zY^^Qu})|icxZeC)D=f${nQkRC_-U ztI~YoIezcL!n#HVqzCy=dOJ<-Zdfoyz?7g8226Lo(EUz2}AWnpRWy#I@T(=Vg~CU-DKJS{uy*Et<3R;wth=d(k9)oxY>K?iqPRVu+rMqRZW1-Q>eACeNOc z8~>^(%Dt7MQeR2aiSdnoZU5E%d^bYM*H6nq?dQMl%JFf&2@~Wdcki6JDCvd*Z*|0T z8m4Tks9Z!U=&wdiJ{7FIWzKK(esWeMmrQE-U$Rhq;gWgn`gFx@P3stn){f7A9cXD3 zo*P`dqM(J#oHKYK{JLICmhY4m<9JVz$HRIOJ+hBlYS*qfQlzfTFR_nIO8!)9+wmS& zG;#-8o?sz}zn}Z<*6mJ+D#!T?eqaJExA4(jf2&p;M)_@Kbs~QU{_ppn5>?In=SDwp zzgS;tI3_f$6;Q@xlyTvUCB;PYa4K%zKat?D`e6#!Q+4;&2uKw?HXr!D39AA)yrONA zA!^awsyr) z%?Bn%j{}pfPr@6>u0wfArVp9d;M|q!1et>L{;pOU=CvW zDZ_Do>()hMIBdgS)4Kemb=v#G9onx_3Z$YEhZxWfV?p~{z`eT+*YL8DunJq{fOa|10m@gRedmuTZjra>h&2^ladk;9lpdX~v3zH+kchKmx% zlEINW;V)tsH#4tv%Zl3E`9sdhiw-F#%-5G4Z_@>UV{CwUukp_97EvUwJv zv-7PAHlu}B1vfU?FwOQyp~U=h#4LOPSQP~pZU^LEsab;^H0k=K>MB3H^DwQip2e2( zF5@GJdUnQ=98y3b84iyZ$X8}OK32cWBLR(>?cXv*h`W0TeSlb5;G@Atc1JnuI85LG ziZIJ~Nw_{GzuIAcN#Bm{e^}CIrq6hH;HJ9+7aggUDw*wJxH$}kKe02) zDt*yopuJ037=k@??je5MaZ9!L@dX;` z$q$UY0O^O}C)in*>=H~zv+b$AE5(Zl`UIK6%=ldf5osnW%A*FC@(r1`$l>;XkUdtD~d5f!9}ru2_sF* zPcHw|_& zkL&oI@y-H@#a;uS@vcH$TkOvz+cPA*e(j6;Ld>mYrA57H=Bu?aI!NfY!mrPCQ}Q10 zl96UhU*&Vu&q`j%b=_QROEIFOwT~^xmgK&4{e0V#{73qo@_0XI2RIYeNWNX2U%Ktq zF&X-K`$|bK-L`mN>Bd`!avpb)zjSK;;$G;^p6bU-$5H+H2f{cY?yxrMudyD6dP1cS z_3hN$5E2IQ6-|#&ylma%&<9)Q3YkrQz@lI|&#^D{tke2_#t)>|`lCnT#`(yB>yPA) ziqo8bv*EwXY@tPFPS025Rhh6OTDNNIkKTgtFiv>d3NH&xr7Pmm6X>z6+G`(Ia-8pjFCK8hu&5-p)%C=DYQ3^u3j^lU@Yv z0q{w@MdmyA&O>?l5U{7^m2R}>`U5TKyNP!dy@J}0hk+2^UStR3NEN28^m$iW?vwjC zxome%(79x_`jBW6Nbsc<1xIwmw|J9(AAmn94Jp}VOQn5whzr-rM>EDBC-0*(s;H-q zP?#_PBEHc6_t;u8=kN{3N~h+UH`*g|2Wk*{GT5HwkAIYF<&Rfl&l3M2SLf|f*UB49 z@;(tsS}`Q`NWk`G{LdEp#eXzvvsRs{DX z-&!$KB}K(3x>WTHOQw#yl(<$%S5i5VdPq{SYsyOmN$oeE8WM6@Jk76&eJGCpvb(_{u00wC-yQ6~w*}F=(ovrEe4I*dt*jLCES8N`_ z>L*{LWksQbE+?N=tXegbz}*>bo8^=2wQ{cvp)9y9(1Hbp7Rp3sT++1DNMv{N0we4v z=!ulabpnPN7abYmU#|XuzL3x2+4=Mw+3#RXTC5+4n+kM4!jpAuTb&w1n&=j5kN{jD zIHXt6BF;0&IALn8Sw+yjc`H~cVA~2+TRyG*|?3m2+kxfVH#etIOY$k=nV4P z^Z2|6!k&?99~nKZKYz4?-CEvTe-5^T5W9$c(VViIebGx~?Tx(~5AK$>VoyT6m?;a8 zSzwg@bZG?75>Bd)mlrTl` zW9?r)aw@9czF>FqcF1;sy!Qi$`5S96&T~jM@}4L@B3Fy(HXl{7k-v&gM~M4;K0=)S z@Wa%H?}}jdp)(Uw{)`Met}2{RRUn?1s_nXb$VnYN{g{m+pKU7Mp_kcP3$=WJ0;8&s zu-R3Got~mTG$XPEFILWp4Dy$D&GhSkI-TU6n)vmpQ(>i3lI{KRFYBG2kClZJDhtXg z^AnJ|Qc=t)R0 z-smU`iD0H5A1LDe{9THkG<_`lP=5uGh>bpL2{|0?cOMmZ#IXG?p z&}|b=L@Mnj=JETD`J(lGxcBm7TEvj+b;dYeW*fr>g}9=kNw5#IZ_10F2m;|~MZ&a^ z4=enP_MFscKMsBjzT`^WD#6xZ*@^#*Cru`8lB8GnBJfwbC=dM!NkCk0m*<7Fe;kdc z*tgkaAu7Uj8MJA6Aq-p8Tp=3WTAFCyU3d~UX;!`8V8!tz0E;&h$6eO0Lqn}`Id+f1 z^&DTaOZX$dVxlG|?BxmlNdTS#l?%c$j!RPK{}hR|!1>h5f4fxI6}_F@-d>mL>ci@D z!=IQAhoSM4{NxC!LXRQ4n+vHXA^Yq@kH;m#6W|3kOjl-=Roh`#7urAKP+&76f3NkT zeGw`n=VESP*$CVKus@KMt0AuoUyJ;c2zr&GhqoHG*bRhGUTcXb#Jn!9T*@#ITsZiW zELOsm2@7+wv~;5!Viu+49ohRwhmtFcxSE3)DOtq+QM?~9XPnY=bv~(V4%uJJM&*Z; zr{?I@PPgofW&gUt*}od~&%pg_dNX*){cFIk?ckKAw30iQ<8mAj9=2z0mQAN~4U;<` z6@?Qj3d$<->~TFBu0bFH3|dzRQR{vQTqh5hU%Gz*Xw&WFT-coMB>S#Bduri?sRd1o^|RS21E7CZ(w?<*HTnt9P09>P;@*7aVNS;fLLge~JMfg^I4sVy97&qOVr zXU#|jv(rSn;5|)=$@h4xRuSjzc@F)zAp8)!Fh6A9oL8?dbq-FT71SMh$veP`v*R{8 zw*Re$hd9E_{~BZs>^iQ<$3l2B5^Z&jRr{EFR9u3HfMEq63ACg*{wuogd3LDKUQ|HW zge*~Hamcu=N3(A@M?P+dWZ~!Ld2w&)7JKp+dT2zI3LzHnhrvTgPO2rT;9?$jlajBk zBAd&5w;hmn7>eq`vg)Eh{6$_l)1`P{x%eT$-2nWbTSErKP0@2rX7NcjwP?ZN%c_~eG)iIb>_RlYjHE3&K&^TFrW@)nC)uPZHLn zTTF<`jceU=fG4s{IL=X@ruvDK!c@IZU&orh{wQ6S*s;6))iu#Nnz&Gl_TZnb35 zBTCF=LJHKVXZEK$cvE4bM7E)_u|CcLAi+W)b-FWK>kRC1do^LoRY7XAX8DEv&NI9W zKVNdZY2+t8uSFx9C62V=h5t&trk57orFN+%q#kAug>1#iOvDz(FUWn|fw?a>q+O2E zAjOja3{a{?H-pUr;o?`4JMwF}GaB5C|9pIAa9oi5Id76+O7(HqL*Bv@p{SI zk|$3a-HH4We-Fi1U$7(by08WZ(PM;D*Qh+eGU%j)o=mbXcV?Z!!1h(3q0p0gW(UL_ zSYJB-8mo7Cmivjz`Oy^M69jM*IR?N*NB1)U{CvEgX~0i3fNKwvF6@C8F?veFpQz^* z0Ez#ix(tJ(iJuwxk}dj6wfepL1;0L0T!Wa9K(P+))Wy3E0)&cK4rOIaGziBM!TVJ6 zF-?y$vq(7`&>m;2qdCaYY+oZRVp)-^Wmh%x3k^NIl_0;XC@d?{ zB7+1yLXVIp3`UQT?$eR~bZ1B>z9=4Q$)#^vxPMuK>i8QaP!<2gUm#Jwn4@SoX zC@P&=IR9*FX6Y26t)g(r0Rbw?ECYAHTBq9?uwJ_ItusEuQ*CV$Z-$I!wfgE^r~N>S zCXd+=Lk0Eh;9+rjf_!Ke)L<9ij`Tc028C=@nIyaaO6HHvtCU$a2n`MmF|9su68B4 zn0iILV-VZvzkG@Xgng;6I`SJ`@__D5QF61RFPP29#_mZzk=Fk>@-sRhbf&DLmATfd z!!AW_DG<&5a)Sn`h|}!YSSY+S{+>5l91`B}N8P}^23={;9P!@Bn>ypOWK8}&WS>zN z>5|76()d6`Pu_nB{1jrq!hQm6{IxPuroolo`v(KsAxmAnyQg?F8h-0~WnJH6pWW8b z|4>p@3D_Ujn`*2bVnzq_D6E;h=wxkPajNl z%fZCqFyG6fKoET!_yo8ov{!wZn(Phvzne|2EWPwMxgAXlNh%b11^h8cbvTz+Mb45S z#>sB~UHT$kl-8Ng&BIszGQvbtjac19=W2+qaPuQ!v(M(JUD$ICGXMtR$H;oZw7d$UXyJ&Ov6`{KLraYCqmsyf64Rpl*D5$i0#8|_ep9KBM7yFMd z`T|72SqS^NCHrVqL>!4xu>>P}T}d=&y|6*Xevm9za=*7%>Xh>WAqJX%g63)3+v&FZ zG1eclez^VD`f6AM9&bjW+_P`Y3x^^|`dQVOLgI~>`aPUgIrpVh>n8?7$cWuWEH*${ zUFxPUP!k6C0GaT%^C&C8q4z?j$5Z7A+||j@Ur@|$+S536T0QX*bbl_UF#L69UZ2-) z*i(Ff9Nhm5*|h~VY6O^Yd#71ZWcu6!UF zIj1l)LZ1*|b~CewOJlcwz}4sEJN@^2G9JMKXcLLPDX(5npApsiesa8%xmu)avk%iX zi7GWe_L6oiK7jw|@ml03=mM*t9vx?B7&)%?$fgD=8CI{ZeBYsxK+BD?rKv?h8)%uv zV>y~0dW4o#HgZ#?AFW@AX+V>b-Mm~+G-xQWXBN8R_@Bw3*QY|(4?0t?Zu-b+0J~t; zC&`(yJ|T<0`DVtj!2HdyWFy+zwX&LOLA)>}paqsq5g>x(OzH8cwyfH>V2lgJen=_> zMQfivQjCr)Yf@2B5!I{L|D<7peKCC8-eMH-NJAlTF3!f%9lnKLvICCjbH8gC8b<$m zt=Mt*|IlY)J+vlAoz=|zT1+AEA|gc1FdE@N4uZvqks%zi$sVMKF$zm5pt0wem+AN+ z(1~U4*O2_Hq^R2Va33SW<~6HNEZz_ekc9t>`O3P($*s^Kz|v1L20&wN)f!S5KrJc*3j5%N7awvUMw)?`KRPuCdI%DK791F< z;Da~ZM)w%#lRIdnTG`umdh%hNkNBrD+z5mB@G>G8cO_GZ%MSIa#^~B1VD94NK86_;k1se#$$w<5jCkSPeBHQ147hI zdN$VNFyv7SP+Yw_DTh4ChOI%_cWl)t;Bn?ekL7sFd3^sem_H2K!ElSY&kKKf`7zbLVo>N#h2U-;Sx6bULXz{D-L}e@(Q1>F@YH9#BYA({1-OXQK%K3>-(oVl2>l3MK zgsE$OwW%l5A$Xno!RxvY(rtL9Dk`Dd+LV}rdee?_SDkBCbu!hdmtSGymbwzdnAfQ* z|L))+x|*hv_Y9D488;lt65-nj5{OOrvXL6Hi7BjYVKkL;zU0;tXc-Ar6S!L zGT~&=3qTcj6w0cMyZHJ#^}-H!KrovvtghE((4UI5%3{DVkr5AE{_Dr=Twiu{@pln8 zp(IyRFDTRsfTvpwvq>m*WYv(W=@gXx`tu+Ws zEFgELd?)-YzBlwWPV}4BJv~AOs5bZW-)Z^BSv_U1*g1TpJU0n{`%~@Ty}enOX|iX)`0BV=*qsmL*Rnqqg}_U| zv9AgUZ?@-5q{S$e5>I<#w&#jj9NRbLg##`32o|bN3aQhsfuU6rOo-#Kg89)-&$)fr z>{RB+gP2P*bs@8d5CGY2kRnU1~s{5ziZL%M$Ex9EDUbe)kr%k()A$9D(kNogzu;`sh0MKFOf z+{n$GuddOS6f8#!=9qo(0i=)A304YIEbHO zaI44d4<`d<&@WJSL`TW^;<;3MS`=u(&sXa7FTFo^@cr+b4ZfeIntFAj+hXkYb)PbK zlgl#Zj~p4B~E z9B-FDDgWj2a_e!LUSv*r)suuv`ev5J?{kIYA;*8Y%l~>05`4Dzg4nX^h}<-@E5&8+ zKH)*Eb+1reoi#a^*mSC9E(=lRNvkDL9hSWDZrM>!$9pRJm`TZBJN4B>ywrlLD(9X4 zhQskDm~ca0bd>I#dtk1Yo%UR&J$rz&+q;QO!l)lb$jjFx93OzFTTj%(DqJLb48U&| zdxc0dF)_HW3yjJ4c@Nt}Sn1m)*7U+pX5T^F-%R^HM}LIAfwNL{6V`!faf%+;i;SiY z9YgI#_u3>F*_5ZZ_9z{r!`DF(DKUqYt{2Ti@)I*(>7VA8qP3vz*ue3_p24abZ&wyr z+*heAw5p0wS6Ef~9J%@M1JNDz)=f#e(=b%Nw-uhCpbUw8Gc%ZdTGX=+nJe!+W?4R{nU+Ypx(U_8- zm|;gg_8S&0(OhlKBJ{acY<3Fl3g3!4A0EeM7U=UauNXoh{AjIuQD=qs+;p;T%o`o5 zQ-cL!z^*GmhoqGOe(XIH;|1kPKerk*O(N|?isb|DCo#PK!T1$z)iSu@3U ziWOiXUGchhhUId0jhXVyp8rGH+rURrU60?p$pQ%kHb}&%2+?9oM1?4A2}I2co1{W? zNl1c1)V9{9)b?j#meAJV;$}-G<5slNVujWh+S=AuTcJe}NCHg&t%esh2m)1XXI+#C z7DAxr|2=nRHk*j;^YGE^%-pwg&pr3tbI(2Z9Mt-B4vgZ+*|^1{pPpDcrR8)|#PfIg zoV`8bX%Ukw`PExn-tlI)4!g}0nX}7{?xN+L%w-%e^t$xz?mejZsDxury)dP{Km>VL z^hOdP&xh4M*CRo{3YJM%_%-3E6lEqxS56PBGM zb3#PsYT-5_2pL0Xhg#F2B=cd}k%?h{k*w9&V+Qub+N=)9?)}Sjsokf5CFEY>d=mR` z6j#hpG<}@hc_7VGZGv5+D35+(XF~nD?_;f;vk!tW0hx>~%7Go0H$8orhCeL~0Zo*5 zQxLEhxMrJ4vMt<}r589XEooxxH}Uz${FceW!X^fwbK$6u z084L43WvqJwKAI#+47{`!A*|V%+L~#EAY7x4xGk@mf*e{T4LiL7^xI{iSktVWFWa9 zwj<*CihGFrW|6xso!yMe(u5 z&E}$eHwYDY8`Sk*-{X-Cu1Ai*zhK1U<*Z$wIpu8RhiQ?31Grw12LP)X)pB#`0pZ_@ z0tKcDa}$FcshFX^Y53NjRpO~$xeD<#SudKw{6Ip{i%RLbUNK8AnqyQP1?6kzb)=-jepBeE6Ab5$4_8MaabA?i=3FyL_PuA!Lz|`skP40Ys4| zYVqy+gbWw*!XiFH1|Ie|ll5w_%B_2W6}n-iB-BkwHijjifldQe_%>kfrF>DSC%d zJ~elbzHiYNBKOPyt>A;xHaBKq$GahSu1jwnZ}?@nI9PwjD4i0y^+4{+3-JP$5?MGy zKS_Q}X(WFu`9m?dmnc=H&z-X92yJN>l2LfpjrU^jx(1ss(Jm-fwq>+*`EZqz!DYhF zy+?1W2(`_qGD|EI&FEa2aisXC(kgA!a4~(Yv>I3Pk)`^R_6uoW+%2UD5T#QD;+GeW zM8-WJaJ)$+(foLQf(F|-zVy=e=od(kPvNmV#F$elC-dhdp+3yN2QD&n3thxH9TTuz z(DsA7t81@EX2Jgk!5C>Mb}%k~fKvyfmn(j^C{4s(;qYJ*hxDmH^u9nOa$_8ym2~T? zR=4`|H~PmAO8>sbwjHfm0)`ikqppKe*FMe#Wy>%c5Cy_Qe{`_0i+;U~y~TUr_1LOJ zeyDDa)IvjUhLcv#V$)z@5mNXsP=(~L_UH-jZASTrcqGep~sAaud9 z`lFnqxG~Hw_D}b9Elr>3>v9)i@G6Fcax0>}STT&)2S#xzt;XI=>|gIqv?T{IZq=YX zn3>W?0vV>q9`5sh#YuvGu;r6X&aqH03glSA%czM!Di)aKT%4l}9N_|^dWv!L)ZCqm zhOfviOsQKMY8{`ubK%FK`mP};@jMAI}aEw{*DKcu~AKK;iR zalUX8@i%Q$%$NIpioX>3PZrI|w;Yy0Be0hoa)7M}$9GVbI>?g)y;=NU>NmG4uS;2+ zOp2GMHod~nt!2S)_YK|#@o#U9TIiErZC>aTw+1cYngoi(PmyqB;lk&LSBtMHN~GST z4c0G+9oA2(%Yd;H@%acq+=eeF9wP)B`|Ap$!au#Dv>rvq9FI1<9nX<2m)ga|o&r?E zvqG-57rK?)pcnb0-=k}xA7;?KukmDg=8C-<>VfI!Q`F`CvB-*2bjFIa5+x%%jdy}r z@-$gJB|%7Xb3?C~A05kY|M~rCge{s?Wy~y{ZC-<+XLKi>x5?Sk=WV8j-l*s+tk0q! z5`Vp`fd%cNbO%jJyYj0g@NtLq64~ytix8s-Bk3u5Glt8qPlk236!2wWMWA-`thp@q z)<#AY`c;ulwKQ05j)(h*T-hABHnQf zXXH;Y{8NnlskyC-&a?2Fd!q-#CFXbQLR`CXG+CG#JDuQxz{0;YR+FB?k(2BhE&tG# zyk1_x#*mdlgN-XH?$!PQnufaD-EG-hi66hm{K`|}XF>J~-0JJAkNs^4^U>Ro%AguI zXE9n~xdt4(?-nTPW9e}9-7pP=_KeUcNsBK>u+*!$+?0-^6j}4Dvk=uRS9CCHhu?hS zSv(I|#T#<_eDHsl@#sSJo5w_|&c>R0OYR03u`vdWE89yZQ#~7^Db~+a z>qjZUqqoUu017~;ruUtCpC(&QkIUXk(MZO_{MYhM64xJYpB#J1o;GwgIyGS!!PhzL zU6via7m|qhRz+6ULYnR2JpM}CdUz^N`pU`b>jeJFVRU=A0iCRZEu3%XY!A=iiFtnS|xE5+rlu~N#ocN?3e9wQX)vx_5PlHc9qT#IyA- ze_=Sxt|voRH7p+59(G!f*J;5P&Z3Ckk*v}qo*=Xpnc=$Fudf_Kg1P?zdl}Nh65NrC z`_x+%NxOKqu~J|v_v?{N$@tFhWLzFgnNaJ+E$=67xSTsgr{1w|2pQ|8baqF?^CPRG zh(~O(T-lwGF=2Vo_v!1UKH>ly>jhnBjZISI#IGWitDs~bwtW^^_?vdCwzt?hMfIPt6a1yQdMaxT9n=D#JcGxkAG=I3TR7{b7WU` zi?Lq7jTD`YO!=|J(;}NnrS|Sw`pWSHZl z>o&rY5pBgp`ynYita7SXcXPHPz>xYj#!xR-Q^qFM_9KquL#+W=Cw_I8!HvG3 zY^MX>EiLZWP|qFuO2K8_@VmU5@0Tv$MHs9Ly_+8^C6B&F(n6}{q^OKo&W@%g7rAsb z0VOwvt=i>Nwi4 zCQzYROXvkxwij5ep}=Ad1&TFr*RCKVdX!mb;ngU9j+XV3D~)1PFPYq*kDw(g$%x8r zMW^aVtg4N|SM-9(?fq)TkB|^VZ^1oiOpL;#exnd&MIq;aHwvN2aBvEi9wk}?gsQN= ze2MgnyPJhq8YR!^g_DhvBStlri}uhNL(uz0Z%8OGKcT=Qs=#)m`jvQr_F@Z$;(NsF zXbCWs0KJf8Ld|HB z820q2SL#rc#wb1_pJja0qumf7l;zwRvSUPNyy*MS>EJ-$H zi93sJ25@ zYo6+~Dn8YT6l!JU(&e_}zm z4hbOR##U*Lql3V)Q8v zE>*_FYaQbq3C^1p8rzWEUB!@$@uBYBwbZ@jg#jyDogS{%Xo(UQs3bJl{far z|LWu%sb@1H_Gffb(iL1kck+A&TmsZwsqA}B#!q)r(p6<5)Bi8X=1XPc|37Ag72`cv zN{G(sGx{Pms^(vH@_bfbOy%|Zzd~rLE9w6&i;R@1LVW$VTx3}cm-`=CA6Rm}j ze}o+!JCG+ywQwC+EtQl zT*zPk z+EoRZ*X$7O;vXb$k|n@#eu&@ z0;_VBKyHcI>aohas!@sA?5}OB$fn0cY_r5bWY-@{ke|?*G;N$MK5~)Bn%Wjn!VIJT zL2HD!LoZ{y!cbClxOeG9&L=9=wWEkg%d>0W5I`fxWw4bDPHqvHiiL95%NO*V;i^b0E2uHkp-@K8_Yh0UirQk8!j6ePzu z>J4=dT|6SxlNMKU$KD~Bt*EJ0S{;`K(_*ioA56>>+MX8M=nUKd{m7Cph3eCjv~+v} zt^2$H?&uS`XWTbzPzM-^9TE5j-7Sy%;`%qlmRAz{+o#_ce)u}JS33|b(3xzM7rcy$ zzZPQ@5s}vY#o6Y*rF~mG0*Yb&J4T5XZjRipX`gq%l>EtD7idVxKRurRiLC2sqj{tJ zXqO3mPX0Lw`MH>uW=ux||4yP4qD1|gY-|H*O@kYn^^dem6Z@01yRo$|#!EpHj&AUG zHT>~u(zlKBqg|Pp-|64p^>+Wf4ZjseX!r0iEn&aLRU*qbdv`MGTyMiilBFAq!Gk&Kn*_O6+ZWI4pfCQ53d3aYw!aM}D-+n?^TQ z9&4&RHVyk0rw3iJuub;*ThV>Oy(E+J2-#NB>v}?Aqj{sk23GwRNq%HsifGn$I`teO zcSTg(#1Ew3ZDfvCk`P>)e6=^6$ohg{tv82vfwXQzWpRaI>c;1j?B#i(O5_V6$>|lFY#1(1#RlV(@gfHd?Mp)CfNp z5IK~w*v5^qfAzzEAt~|2%e#4$Q=7hrjLg@FZ$HjxlN(1AM=DfLMmEh%=!XT9z9;Zz z@buLI52aoO{cVF?k(%w8Jz_dI9TCJPfQ>iM7i|S*CbIc+8@GC5A=O!Nqb;2ce+bIk zecJ@_(j52v)1|rnjW2_O+a2s`($1kskwV$>?=wI+_&StxQqUWjGr1%8xN6CJ|7CQ) zq{%Dat&nnc7cT30^wCEzw9=O+ksk3p9GT*ZZ3TYqo59mpYFwh&#wBp5w-FEV+rrID zrm-+#(Bb!3#Tq4OfGB&rC)yiiBe0Fd*FMjTtQraY`ZN{!Rqr^}$;2+Qi$16wo*}1r|36&^lPj}ZGfTA$SOj7x%Av<7t@u~ z`ZsS{GE_klm565>Tswb0J$t*}65A55zHv|8Bf&G11Lp%_!pFrirA8@GS84S(P8|%N zw0iX5PpK%HEeN*=(O$n8ZF=U4=FwrHH{Qw9tF{pG&U&S;KRWZ3_X%yO z!1`-P;CHbDefA_`o>TjU8AxBxe$BGm;9bV%7?r|-FMcFpUowGaZYT$ zq}Z^&H69*-iR!G^+^55ywvwAw2kic-^CO9cEe1`^ZDDa8jxcV@5C7W2|KbTrI~zrr zEQGjV^q`II=Bpn0n7`(m1Rl`CGIqvF`3pc|#Ab03W?|FCxR^U%T+At7#)ZyAYFzUZ za^n&TMA-24!YzDhO|HhUBq6!0UuIvtP~ZR=US#6Cwp;;H&T^Kx>%^}EWqb`avv)cy zU@$wjHWhjrniUh(CdcKGJikfG$8_HYU`BFNFh9-bKId_A0|p3?Mix_Us_oS`0oDN`QA@<$Jk;W4slyh_R1 zELsdp9zXFbeWfWAd%bFDjUX1oAdG$WHCg0t4@>A;kviMLV#mNwf&3uG$`Ak8gvFoc z#GhuxpZxKsx%QJDp3hUeimD?TMZ9AhYt*=^^TT!(97n3Q%&0b<1*T?f$8hD;`-zkW}(33$i3Ju^hxs4Hbb?^5!e@vm2$MmkvP}m#c^HB zGq&^)xX3!cBo*K!lfYmt8!}(Qf+xQ@@}D+m#{yCq_$GxG%XyBW@NBFU%qeEC&-Mg2 zDayv-g`#aSt!r8%w5`pp=KjihA^0k5qJ8P>_C#y6G7PaMfh)3p2~Fp!tkyovikoQu z>IVvTp;xJ~@4Pe3s_=X&jQzn_FXLjYkrC6oQag=JGGeLS#!4BbVPP2|ePt&VB(Ib) zN@a!N?dNT9Jw z4-2)Kzd;oS*hsG7H&tuWdC?IpmACcXREgd6N8ZNSOnbIVdShebsrMseMp`?jGlPf| z<(X$~we;7r+wGC|Hh9QS+j>_uD{93ci7xK1odnm2tVyBL3bX8t4D=Yh8Cm0W-B;~_ z$JRjEGus0n-2$BSX#-IA?~}zZ-R8UM($1&4_6iCa)irIsq2?!8`Wl;T>0obQt`Y76 zj)}gGQ1cLFE@MRG^h4kC=&3UarmX472m(8nCWp)+g37>#D#q1DzWFU$wh&1}`Q8Tc z_F$}6C_E{|F6fKzL?`+V7!g5wc1y1M^{#GR_Kr+2FqGTs4G*KVu2PIO<5f))z$_?6G7Z&EOM zrTk^U9MrCncRl<#f8*ay=;74~@6#1rdU#dBH+|(=DcAPm-=2T+A$@z|tQwZRRn0U* zL4WG~?$~atJT|nkjaKEMd`}-K%CkaeVU(k-v${{ z+eH{6uX+p13L;O@p^_(jH?L}`EX>j)vNY-8d-&TP)|`A7%Z%^6)B7_kTJIWZmi8;m z9LvrvxJhJMD9_}_8(u-(k@dojj5UY((v+NcnSNZmz}au^D$JX&eXFR^EeU}x;#Coi z$zG-&oNse0?;|Q2uw8XvTyT@b40hp{4WNtkPMb;THEGciV#p&(hU1IWk`>$2Z$DPl zlpK4Q^lGz^=oFSm*RRtxNB%8p8l^&dZ=*B8p34xQybT+9Mu=}pZH$Q26x|{n1>X_@7Xsjg}U?_uAe`+8`wiT7mfpAtNBpNWz^? z?@eW#mvxq-pacPqHeQU1)tNcXztGYo%eo`F~`edu#32 zL{Vr*Hq3K7b+@%E$wz3ZJuKW=p73@>TM6KA9Ktsd)BWJT{MpL`-=JD0r^ODSIf{P+iY{E!=FZ5#iGtJ^&gFRlB=8;uqhp zq2^@v7lI=5gKr6smW-C%2o96UL$$vJQ2?6Fiu2NH6atXo9muTcHKVLM#szw zb1m;ef~z4(qCCV0Ksh|9FyFgJB`a#W5R4fSRkaw*Z@in~2Ghg-%EJG8?1kNk0_iwI}d$a`rR0pgGJ<8tGiU%;leB%80jPf?zOI_yw!G)Px z>@EwP`3`nP|M!GV#hNcT{R(XwtyGvTPhrkIANR#voLn!@o=h-|>7xsKr$8H_PCc!ZtMcL$@o?tjaHwGFqktdW!AdELc{1_x=o`PhZS!f&lIuyD(40-pm**^1CRH z(H>NJv{4Sf>@ZwKkTlxjkHx<6UlxwIvcf4?m!C^<090-MyGzZA+D=60+8^<$_VE(= z{x;vMYj;qH=*)-tZvOuB()Fy+83JPbnD4#g*UTrY`Ft5P5WqQPS`*pg`%9gWa@Ga< zXhEHm$i9cKY`J;FEixL_XyfmJ_5P%{m}_h^ZmY^!p8fgMXa< zaw+`L@`?q$3wq6eE^^}vBt`iqV)$}<^ni(nqPm>vl}8tPa=R8yKxNe~Ta;cbI^;)L zPdVG71b(L5>Os)c*!CE~{yEHx?&@VTD;my|q#LQIjP)?qJG9S0x2KCnmIsLs7 zqxVqnc(FRMyqtNLM_|dQ2*PZO4&ZB#x26$}>wKktjl0fg{dUZ}3!8@8TGx1LAcoRW zDKNCz=B)3F@u&R5B%b(%)m8ddv;Es}AmZ8O3T{sc9x9LM^?SVyvJY1t^|C|VmswS0 z_J`j>u`-MIb&{KUGG!h-E02|XB~GQc;YnUV436sY#(`U)y2tG4y-}3BYO{|MKP%IM z$#UP!oa1D{sZZrz@8&|z)@FDcx+D`151v#Hg`80EpmHRI%)(Ij7ro1W%NH!*CX~u* zO)yyy7m@hbPY81?J3t?e+*C9jl?Ni8>-8k@6yyn&bz;keMuw5kVDFB$wz4xijxkhK z0HvzpHmRagRWU8L(Z66YCzjMKTkzOg|AIlWW3v~?DJI|c$hSS^3x>o9x)FPj(~;Oe zk)&dO_b*6_9jIR5K@p7_N*XQ8pj{fJ$y}tATO}EV80S$mda5;_zkga3NcvMn<1ke=iw_A! zPqn7YhdR41Rh(hbZnJ`~^ef8u{zV0O{fqL2>u&G3|D@66oW@~oE*BwDI1l90lEAS) zT@{O|^K`rRC4sekdR#wg1G>Z;u4D-Qbd?>6F0SvS|F}^4X8?X>T|~;n>6oY(@&C01 z+TE7!uXW*vv4We%%^8F6k8-vzGOpr={l`!0^P4}F`rx8el;U^l%NbbTJ=Dk2VWuqo zBrZQ&<*UwB{-Zh@o)I=YlP!2Go@|3bd39gY97Pq88t#&%7?W88>7?4fGSFkj0}=6B zo$4;9I()R4{}?sM)z>%sQl1(;A{(z9Y(!|91J@_AO!z;IPnZ)L=YXx<( zriuTm+2*eo+o&wG!G7<&bAT=LJprB?@|sFn@M(BxruOTSE<5ng=+6R4f8evz5C4Y# z+90zc{rZ6Z3O;aYW2L`z?(za(=I3ZX?bGQ(FInch3mM{?3`H+|!2PP+E44=3 zY@5~>{G6)Hy8;S-GyNM}H*zLAXhS|fej%dOBhNuZ{p}yD_7$`VL-qG=MAiZLvf4lKz5mkRH*)*;cX7P^f&G03RjJJ389yM( zdw+VlN5*?K8|=@Z2bG=%e4mm2eJMZx+~tKH?vwPqbEixHRr)jMP8WLk=1-xwv*=F- zw9n{|H}?`~KsZ=NKRVbvwLe~NTV&6p<-j5=a*g*~^Qiy)bmq5eKkd`-srKh2w2!Ys zzY3XPUO%AygA48UT{ErrFR|K}9$BMq@vp&lZ4N*9>77z8BhH$C>eQ2E*Yo;_bM>SD z_^uKBr-8o#^yTzVrLRWYGXTH+(xv=4(q(*~RO5f{@-n_EUG&H2C@li=r>~XzUpiMh%Y=1hid#O~bug21|BbUZ`H(qo=0I^sdr^eeysNdP{nCpH^CoFuS8aV98S`b7w3D=%`l0Mo+A?vv zMoZO-imLQ>d^QMP{HAEPX-Rfs)v|9OpOLreZosKlkW958iB2OCeJnMejy~Z_aW3fqQ|AWys!CcYYHm1J8^H_`!G$dy*XNAH`P4K8hEMq4=q~ zcCpp1qPQ_pJc(GpJ2YH!h~`ZMM19JJreP)aFf@)RDh{5WuYI9u7`brrdCtoBVUbfD z3n_acvVGKeQ*Tl1?~YHC#PPP^g(^@lFkhC+lUO&;c_`VHuY1OuzrR+^uLYclV&8>6 z$W-)7T&;8jVP9G@QK{F+hC?zhC9~XSPt#c$()Fp_c^JavL`8{5Qt=&%wZvOUn4vaL zByTxmvO?u(^N<6gJszKpajZJ<5y2s4OBP(lNINup^9&x$hd%bWZZ$jy{pNC>+C7ID zl-_pFLHRN7{1)pi=l}7L#C`ln(J%EeOTW7!*>&6P3)~Ba;|TbBxuT;=tJd3?e-%dB z-=vk#cKdty(bLyh&xj*dq6gHcIC4AVG>LRJB2xb>Q!(|FQX3Ju?)#7{HI^ zaKZ5WM|y@n`zpaTPN8N5uOI4dcn+)<6^G8;uykMOOujZrOd>}&?B=Y3h10g*2=-(J zh7d2fVRzu;Na4mjcckXT;wyqZ1={%vKH_hI?>;^U&y1Z&C{Ozp@%E2hX}3Q(Sbt(@ z;9YYpw`iFnLl?ly6t}&qB*~?V`Xify&%l2PYaugu4Ao6_#$+}x!w9NT{`ezfdp0~tgqt;S$=wtIak&*po_JZS`;!i;-d?;^n1mwO9yw z!*7v2*n@%IE*{+NPX8>Ms}eEhTds6IHIEyCwcfP{len*&T=V#-z*?hnHSccsezDLI zz3-#CFUZB;%7%&j9>2JeUIyvYom2^Phm%O)FDE(9_3Qz$bGwVYgSnPv1nV`a@VXF z8F(c2fZ==Gs9YO+MJ;vF%c*AoePZqfinMOfqn(Hz2C?VDzldOX;tr%#)P9X!WR>}i z{m@CL<>O3jDjLMbPab~!n0mnTo*WKAK()(cSuofANC^U`wu(j!yxDaQKi2VRy~T! z31mVhJ3aqX>3>t{Pul4hd@B9#D*b*t{g{kNTpu9qzoF8|Dz4Uz`XgQOjoG>KDgj{OoxVCxd?(;a?2Cg zp?rXqb?mN>R1~ayZ^KdO3MeBXi$j;i6ZVi`-WFqwyXo8Cc$3PHP?L1Z^Y8JBqkr&$ zhiJ=r|CWlY%|pCNY7Nl=Pv~-n%BCq26#b^(EL6-FZ@T8}+B0C-&yqB@Gl>IE5o{wf zrgO^nV|KT<%&UC^|9;ixvXGp9;-Pi|dlEQBzB(tWGIh2<>Q%y^;P1Rr0zM=2nD_9c zcP0DM6ZpTwEX-@a2JZ*9%xbfge7DPbZC)>BZ{a&DA9vFjL0r*1Ja zP7uGPOFHX0;WtknRZstx!S` zz{_Y%{V>^OK2KN?dD+hky<>Pw*!4VdhqO>#>ngMS!Qz`AAN3g(<`m_gcT8#Ew2u{b4Y@WJ9)+p%86+IxcDPeye>dqI^41a?%z?{NM^cJ3RS&L~wp*VWm6)ok1 zJ&ib>VG8cE(#+U9=3}bvOsP94z~&w240k-AwE@t@X%gxlKkWgi=#b!W%OKE!K@ZwYcVNva+&#t|UN+UH-S8&E-KK#1SqPU9l zn|~3D1EIKk6dw4JK?{6gGH{xCYtG5swuKpbr$b$R{D)KHY{YLLr$nB=$*B9y??V(; zQEddv+)#I$yFK^d!q;P4=$9>F{aY*<`snXB=YSWF+qDyHc+L0V4-T-Y7O*+H^R=%? zjYxZDc#FghFyaSpX=yEvX9X`tv_UI!sQF zH41nkY+kY_w~G_1zw2$W7wr8(=;*!T9EM#z5fg$)0@x0}VJ&5m`qgc|)ohn7wrUFn zzFWaj^JLI;bK?;E+pcPuCLAPPeIQ;)^I(J>tcg6(04X;DH*SWuwlCNE6{=G(K%I$nf5LFZb zI`j`(xXj(g0VO7gTfL12d2O6X{dV8Kh>$c8A1(Oh1dknHB(+n2gLvoEbt7Sqm(1YC zX1aIFV1MnG94u_xZ4roE$T2PZ%X|eAs|aY2VU)>qoeXwsRIDykr~l$~S^^%(w441s zrk$YjDZ3IDHoH7JCG$TPg4ML}Hl&b3NRf%5T65qOXG9kI_5xb_EtChzy0^U zP9K&{6VsKYV{>-o?jsOwAiXIgr)frxyU~-=I48#)YDvo7x6tGw&L0&s6FQYS+RD|z z=ui0+-%H7ZGd^j`HYaDlnh+J{`KDU%AfU0__e@%<&TGYQoUM|HpYNK(<^$=}RLzsq z8{2D4S(6V&Ibj<|MOC%4crjo2r>ZNr({W)&hg7K3+A2x@xs$A|q&aao>^Ix2Z?WGC zd?-nuxLa|C*QTOR>Oa36_~pRckK-^f+S}$k<}#nUA3lvYQx4yRn}{~V+prA8*~fZz zHDKK&l8GErrx{aLVXwm~N`9DbF`s1I(^kzkE2)jI1QXKVA~JG)SFeT_JmpV}IpBR3 zvj|QtQAyPkqb9lj$l2T-Cw{GYfP`Bt8AWc1#()&CifSXl`eQEHg!KlJ=g zv>i*AiDK^$H`Y@Le8)kfgFieLiRfhvACG@X@%Y|0?*rPC8s|-buT$2NM0Ui65={4D=M~vz2Z@{JTo0xu&<| z!oE=(AP<*Iphjlf44He|(yEX?(ko1vcD2|+34x)cKaAd#``tB1LC}Gm=Fm1GFcXqV5R9W-&qJ{f>{;)$ zC;F3$CSQ7^?}RDATM(u6HuGw+N0(K5guZo)aGAPExrY{AMBe0@wCoePhw73exA!(x zo@mUtwrvK=9S8AoesGsD`Ys|a-PJz$YL|J86$w6I*;lZREJ&V>NN;w&XHT&uLq$2% zb(cOkwn}{Io`*ZFW3gCbe;s&hg*aWz@FOBtVUhu@DcSM&2+Mlw#LC04`pQm`2)`xW z@$J3Kxc090kn6(GdhGFti4OW7Bhi%&xKbn%~{$u<3gDtYh_gTZ;*Pu4I;)T zwl55zel7HYHz#ZK34Hf6GU2BeFRO2)w&CBB{lKD8Rp#WS$>^B&hFU%3%oV4$O^${p;<6}Ga$)$3bl@LzpLQ^wTz1+2w~gFQ>VVdXS3&|%cC3*P%> zH~Zi(L4LXQ2Nq7^k2gGlH?i!fMDR~aGJGBS>D=D40-5(`&EAJwCv>11eNZmPiK=!( zGJ$xpT4(VL9N;xFYjiSBS97`5=sQw35ywnq>^=2Ss5{Af|7rkd#3N%;32%Rxc!(H1 zGFadP+gGFJaB@w0Tk_RKZOKddf3m9HjTEu29N#%`gbsg&`Mc#{v3^}-{y1!LDVUe5 zrQ|mzt60#KnTbL+S34OyYK@Ws0%yCUg8)JCPZ_Vk+A4GJYt#bCKo{u-TRF=nY^ynj z5NnC!>(CeLD9ihI?@e~iGlti)+RPT-jf^~xPmz1XNG|kAir!T>XhW*%V_M|iOn0@p zdaNweY1~#6*Q7ZALLq8ent)I}A<@SX!%Gy==Cv04K<9yf#cub)3&EE*ZG|ibfs6bL zmclm3Rf7!Ag9EO9lE5^~?(+pAEM5KP1Gbjo@6?_IwH%9Wvb$sH%Vq70{jN{GrhkkF z%!Nf)neC2Hgl-*au`Ed9`FBd~3p};?H;Ew&e;5jQSXU3gc*62E_G+KLw10gUf$`c8 zWNCP4B03%wY@pqja+EJL61`UjCgC6Ozz8auxYUVWy+HQn+Sn#{oPY3fOv8R|c<##C zuPzn`KA5L(@3T+fe8Nl|KW=<0{rq40;FJB2C9OS3;|!cKsSI4s*D7-sgLf-&?9AlP z3HRzA7>}Mze)9`5!j#L)oTQDQyZ#F9>aSC3jr`a#yQTPf6wRUSLgbrlT|#1X7Y_;E zn~BBqNM`|#>tjkKHEN}vh#eOB23*%XHHMRktt-^}Bl1obi;~~`<>g8WL}<7Wxa<|O zDgN=RGKx<~Ne1^?a@5|zP^lhVj?Fhc@Yl}AMJYWn?<`kay_DHV26L6tA}e;O%CAmf zBufEVZn+hwjb!KK1rKGh6-~6t6R1hm~&J3;sNWS z>SVZFDR9w%FgSQKUXw|1M_%zhJjZ;zirq?Oxduu9a317&OA zqGfy-INrAY-k1Jc^oq(1vnDO@1&ITC9!xI50QH^#tiv`&oiYY;llexr>0qteOj;r< z3P+T5%dGsqv%Ith&hu{07#r%D)TgS#3#7vl6v9iSFUrmA3I=Y$B)|DZ>oNht$yO~H z+buqe!lv4MNukaP1qlyWDzOOLk|)L5sR-$y-28SCUG5?xO#ks<0!f$|mrE46TUJB| zrvwIV3I1&`Rwzoq>3tI<$xt<`T={`7HH~W6t?$#W=G5KznH0Ijtxd2~Ids!( z&TY*ft6%o6f?~n%yC-QEeX5bz7Dw#A#T|RTzkFo>kW11ucLONa#+I$}pbr%-_I}i$TdV}>JvZXN@ zDSpHplpoPLBFiY3!CxE$E9n~#=YGQ+DZIZaQ{8Vk!yVx1^<9YRWoF|TZ%Kmx5C};H zuR;rHW@QT5nGAgFjgy13w4vsRn>d@`+Yx~R;fZFYK#5z#>~izj3*0U|IZ&oCV+iuT zyBl}GkWH^Pys7Ni)Y4vUkn$Id0yL7BlzT=$xM) zAP<*&dU||<#7Al|C-}y2x#&zej#mquo6hXo#wqzA_qdjg*iqPrt^ijjvxS?>8@CPKhQa`h?I3 zY5}>D;y0gVlcBEkgik7=$=gs!*=Q}9#BARizKSQt*Bu?tgM!!Lk8&Q1a!NJ`>Bb0c zDq`2~_;>0=?)Jq#=xdkhZqY}0pM6)sZ%Fl7cGGu zFNm}mYc<`tzYu;2y)8SM$HyigTiTSy@S6bBG`6^bki}`**G~B|#qzW*EKZPJT3XKT zCrr6n{#MhN6~eSb=B1(T>E7k%A%+c1yuh&?I=?e)P1-5Wm6>=dw4 zgEw4_+h*QCN$|`K#DwBGc;*UkgP#ZB&ogQ=L+`q^VNM59bN1u5M9y`x>(l)()~W@Q zs?FndHp`Eb@ugd1Zz5)A_t+hA^d+UULx*=EAy49P^(Chiw>{~%#RYj3Z+OOHFWrl^_a6GcxHZ6L=fn0Xd(yYWo-Ko>P^!(sis2W zX~Xht<>Z@f4}Q}Hxnovn=_NTkth@^kMrV;())>`hc!%HVjE#I#ZmNNIWSbq)N#wQp zJ7Y8?w=?!UEr3m?gecaU#zw!!58tF4-iC?1Fc#jB7@8|`N-t6GZ`$vHF7+zc5%VT_ z)y+|m(DI#3b-n2uneZp*LC8RORh2o|0!Fm6?CyApM);%XhlBPJ!7~#X_cNDz!%qV# zB;^hNk_Q_ehC7fPdiRWtCyU0sp_x)A;*nZQ;D_nu9L=mjzE{2WHcTVEB&XR%*z3`Y zd6(5ejcYVpP-4-qHWM<_MqA{jeF&*(QRPci(&?w7cW=XCs+O6OToT)BL#BNk-N{#* z0jT>*q-4nmKcg5GR(K*N=5}eT(MGSa=aM)4Af=*X07&(9km|{2PoY(+7JCYX=Stb# z{89W1JckYs3Uv?Cu2b(aE)A1O&DjAcLTruuw2R_oG(ByFx530_F)D_e@CQW?EwT4i zxo#4sjq`@P__g?tJGzl)r&CYLY+oa@om&;>ob8-V_09IMc-Rwc}`$+My{EItPtUoIw9C_Mmy;TuEzfT-!b6H;&3Z;!3V>g1>AW?Oi>1IAU2 zJG~7UrPH+f7D$=4Mw-yRTO91Us&S2Yt!dn&jR~Hq2^cnbcJ;qvqZeJspQgciHQGT3 zq{fy&iumi0S%;NV!}rON)9l^6QmSfF@aMMPmohaX%`6@SfySO}8d2P^qw!_C;#rjF z>)&bWdWo(EPtOY!(bTlSS95mg>$g$5pm^D77tJ({wM(8y$>5o3nm1DP%e>6RhYXc1 z_IPlqyCx6|wI!+PP!o7i<+r;NJ;}+ZBPd$?;k@g#%kr)c>@6;C8XHIFda?!2T+Kxw zsqMYkbH!Yi^EN!nXP&Nu{v`ux-f);FZa&2KZz|(G@-e9Bnf@v(h^AuBnY|5iB$ro1 z2pE2?v2jQ5<~ex%G;4Lw*{y$+I3JhwNVmNWmq^=9W2MU%sV>(IZCrhRCq)Zm`}J3! zpBI(u1q$B7Ox;LWlKgEASIDo+{02v@AVq{Lpy-gpH@2-ZdHh@qLmFWhwM+7-4B;vY zDGt3te5%UDIG4#yTM=E!lv2!Pd$fti1buiFLQjQ$!Wt#+TeGd{(yk2l%+m^j zJvV3>!JaE{ebzHk8yf7%M>^@bG%y&ezzOm(JMb!^s=d*baU$}&`7|6xiG#5}8Rsh| zv0KYi7r8IhCfN*U$)&-bnm~&BTBGSjs?ooq>#AvN4SW<0P(`rkw!m9th`mn57ShIE z^jS!Y{nCQpksl{C`7$xgM3zTnl`V|DSulUd4POMyVkiSe9VUw0%}daiEBc^u1cYBO z$ax4FUxA0_A@}6SFJe!&{9*`osr=?69?LIGaisiaTTVps?v32!MyvB`?2p*tEI{QE zGH(!7NR{3-(P!pgH+>w+=4qkkQK7?K+1v1!_AZ}it8IkrkIaw1hZ!&SBIxq;kASru z9*jg&b41Ss;@GcW6d3!&L)4qG-zze_$sN;g4k7mZJKm`GM|V*=q2WlWN0qPJ94YWx z0KDs0>BtDCUjeYl0)Y1Irim)wCDRGUD6(O2X>YbS{2EWH9c18f-sK~BMFuu+R%#gu z?g7d9j!~->;-kuZKphBXS&~N{eV|r!{L||*yvv`bhepj9WJv~`&=$Ti$Qg&v*mZDX zh4@kcZc42>5TzFN%g8M7dzIB-&hFrSy{uevfflj1!_R5eY{ZcfPD$;nWY(t81T`pO z(QZl=)NZs2Rc3I(HLbeTZ@#WhH<8}72}oWUb)s#ER@vaE66LI{%bxPGxrtsu3I~$& zd6p)WKoI!-pWU(F#9zH(adYrzcXU2Kq7`)nMrA%|p``9!YJOS6ec3=T6;>^b2(>7G zt|DA&^~S}7UU_w32|dN+vP#TrzHqT*8{fxpG!54=cs z{-X4`4&<@l)AVM{ZWHFyhQqVP*P*vT#KYR9aK75#Fi&b5+=We7D)3Hm4YY;8LyBk)oDN zLlU_nQso|?4=9vX)=NRQ@Od@*E^mYQ4vCpkye9CP5*zbw3$*ec+eDWwK6x_ZGx_B5 z{_+C+QntSm4-S2PBw~UX{Z%80tA1*|4|U&u-^@^VLH&85?yP&Kgt|Ys6jpY5sQaS2 zFJK0YEZdaS=;jE>NGf1Lb^t<~+f`+5<3QbB7P$WU@4ofV{A#HCl31&QA7}q9Xk{HT z#5oJd594yj$Yl!N2c`uld>_sZ0sVGId%PtxuP zbziN`Or+nj?7*o8gX@H50;>n^!9)LJ4=#|f2Y=2M2Gm^99**q}$!6(?5Jlo1oXl$T zg{Hxr2k?FSZ;2mFc+7L`zr{RII+7Za{dW>?##kW68rs{i95x%09r#inm~;8(-hqp< zg&lYcj&$R@@cEA7G;tTcNA1E}dXntkI1@T?BmOz3AR9LNgRebECL|m2l{`=|3tsCD z->tsFdXEY-@7st|Brpu05$~MQSH8jvyGH)1y<+Ihd|WiCI-jDDJ=)%Ih?mfZ9yW}U z7WzLZ(@qQAk~@wVyX|&byDCj?7yIy1vI!FjODj0Nkc#0ywvc5HgBgZ}SiA8TqL&NF zC&Y^g%rEk8X}Dzj1Q;Nm-iiY?V{BABVDXKvBNYzeg@N;i#7xej z!chXMeEPlfDe!@Me@wvWX2-6Asa7WL*jag;9lM7e`(9W809y?3TNI1l&L8e8%YHp1 z`}L6Q*Oy7}g#<+)w?T6D>v3FKxc7!Gk=FZm>>T{FM1I?9NLaSq?F4eQX9tT5_e5hP zLM!@88ItHHl79UJZ6-vijdsXWI|^xP|BBwJ;G>)N!njTyWN%GhojHtl$HHQ)RA zKi{)JqrG3h=~Mf4+6C`lr%d#yR49FYT^ar1!{+d7W@$TENf4OhJ z?jZ+&$o4ZZV){65Lz{H>L;i67kS6qT`MVZmAm1KF^fG%|*F@@w%acl05io$fph zpn<183&@gwKpK=&{zQc>IO~JAftzC12K_G5g|1)=xA9Hrr{OvtjM1THIOoWjkJK(B#i9zRr;E3 zydmI>39(0CVZ&#WOPGqIH}OX*tkK@KfRV~sX|A38ah+S(s zyVg*5=Hdf}${G84XsM?r@P4R`HB7okkCv~2M0r6BXQ`#NG5cV8ViD4E(k#@IM2 zf1+x>IuKPgy&GF6$%ADgYLY^B*{avdGfjEJxA7EvaDcpEC_l(WB&oZ1+%KYFbT8b1 zNZ)b+;~wDI zP<{2vPS92u>(pP}I1i*oKM)=vm{C?EVM+D3H>g}ByUcsVPP9!9AEcv()XGNQ@Xj{R z`$v5HJ{=I0(o{bw2(gFp>tuFj=}?$6{k%_FlN0UmAyqhL+VeGFy+=n%h8{Nc$C%>Q zTp`?qHQg=tEmp+3|;D#~+d%e@J%xA=&YVWXBI%2@DSX(8G>D#E$&PpauZ8Oh9Xy*2Qmx-G&01zuOV#JjeQ z-_H&GEd0h+Cg`gM?Eg8;MFl|U*Dj$pQ~~D11#15<(J~ycLPy_r<6-N)?QF?%1i~m#EkH)vW7(YP zAF*Hmw6-AlqV%Z0w%`GK*K(65dK-yKWpF$WoA*be501lU0hSS_Z?3vO3vhq1f%{dY z(AFS`UPFHu*b=lD2T``z)^O+2BOI0`W$9V;2%C6H&?5}y6{&tMdq<)Db8HB=SsQXo z9=ePU$cC^ceR>Uggue=w>&JxtrSu5015eZ=aGY=J5rWR9J`kW4jvDQZ(`!VJu#Q?d zzWw1@me+^nS?Li_WN;m%*eR)fgUs5OKD`xH1+|;vh0r5ZgiNN&uz1lcAaUTebP45l z7y9WEZWO*Jv0Tw5T+g#KW9brp!{+;`_^UU3JDJcW%;l$_E@37gEVR@uWvlzzXX+9x ztVrD91#X^mCO}vMoWs+0W+aS6GyMIcXaKB>O(IxQTIM{)$ z^kPdQys`B(xIGr>lLzDagIr41Za9bjpyyRFy5!!_nAx_nNvUnp5t@Jb5kNRX^MByN z|39<_Klz5OHMrHPM3yJ+u><`8t--IT7p;K;txs#PRg?pFfCMXHfYv|^7OnXw`hs6j zgR>uItwf@gLmo?C;P6guzO1%LCzon79i{4$Jdunh;Et{jU+lyM(qYos!n49vQ5n=z zUF{t+0sofTm902SYlPQ z#FEh4VOo^Jmc80Me-2!>GL2*Th$r1&u8a*II_&TuE{(vx7I0xzJU7IxIb_S|GJgj{ zQIYWMDJALmW8Ax!^W|ydoi8GB7)Jt(U(hhAB4Wz3m;sto6lj{Z%Q?yAf!1iiK_(b&(Z{8B&n1Z00}>4oEY z|AQjxT=la8^edAH)9JxZ84G@h5b9L!W3IQY!I}d@Y!f7a6}rRpRPL`bne}AD_c6Yc zt^ZmMClkCPwmWW82fTEG5FTQZX~D1F(+0e!_q{8FF(bv8>){&MzD0%hLTjDR8E(-b z6T-n3W3|!!^>IeNr;sgIckc1UN%|Y1mK5%`ygUy>zrOm>B8hR#1;*_(z@?bC7G?L% zeG z@@!*{g<|w}Zcj4w#~~bC1JtC%0=u_lS-RL>Y+ap=nNViuKBUd7YC9S4S+XqkE!?NKD$^Jp@rZ-c>BXK!H>z0oqZ51pBHk&x~Fy^6*tR9^D-|yHA}g(DyWr#9zb_@y{~w{-PQW{FkI`6tnl- z69ltzxfWds<#wI=2$(M$u-`6-j4=vwPb?bhsEZu?3R2$w$DzX~xB0wbaon(P(8qjzJ8FOWqkJkz+c&6N^6q%c`TisM z?%wy|z8zBf?R_V=xdXQyY~5GjdHWqzG*G(Fv~s%xU$%<{KBt&Te|j8<-}b@`ft)je z_ulS&yEAr7{_^`T`}fcQ{HnyAee+Yjzc}E%zx}_WH#i3x1OZ{8@O`*0?Jg{C_kHK+q58x7zVjg~wBEjqLG2y4c$6C9_eLi= zU*c1>`3?du?a5Ya9M-dSZ!$F&z;~O*J8OEYo~*SPMJcd07AZQY7r~}8^ddT&sTXDG zo$W=k(7W1;L?6e`c=?H~0>8Q<80FD)pgzE!pUS;%iHF%)o|YT_|Xj_9Xwu8HxF^W#>EhW8da8rFF~v z*0!DBE*}L5V2Un+9%RpvPM4y0@zQ^O!LNjc5@EGqJd8bEZzUfQob{t(H7fB9(-x$d zqlZK4%3=4v97fc4A3!)W*Xc^f6Dq4+lvFO=Ki=Wu*80E|4Ok*ab6lSzJ~9_bomF;> zT4itZcte6-Xwn#^udW!F)Sqw})3xEObx_Yxo1D61TZvvY z+e2nOS+DTS#vcj7!EXk}k?#Up)AH4Z7_EO688J$E)v{8C3>(qD& zshtEhQgbfL=eMnH3uz3+I=@2S6Nj%F5kqcHnZ4DS#s!fn3War`uvID>43tPEl}Eh| z5Ax2+s(lqJc0ko{!;tv^2B}wh9&+UcJ93; zX>z+@G$yz*D9XXydaI~1W-wDe5_GQnJkX1LA@`HyRs|LvffO`lY&Ap#|5y|$Yn&#L zgDp{I;Q0DJ7V*s+K15}`#aKP*nZ3p7gz7H$JA$NfQwBt--aEfyFJp=+`_Q~~#$*>+ zWxk6_H;1_O=P1V_DUxVQ=Z<|Zza3+}dn>dplO;3ojeR?U-%odSekCnXb#O<2t}jbs z>f4<;&B{mfzWU?v{o{)7ckTO5*P(iI-*=9K8`0BMnZLHW$yvVbS*i5Wtz-O zmQQLC=i}6JQp;tFW2E%Xgh2)-_Ra)}Mo}UjW@fOeSolko#oR{TD4mOW1LQ|wtT2Cu zj{39f*yrxt@%H;7HrQy)6(njCJHO)juV|_;?-R8IlYBW@8Oc}&h*<)b;=8fF`}c*GYxo39N~H??wLw;k>-F1C*BQ8)n`gpP4;Sa02b)#DS{ue8KIj6ZEm zlt*ICHpA;D|IQT)B=eI(Sp~RjB<}I5@Fuqo=V1OVbg|cbvMkxi;1MR-&#P4Cd?-?dIN}aV5VZogLXEx`=c2(S|7Z*r4 z)X0}X4t0Ccd=hT?QB=ClN6grsf7Zy~}^bBc9#qe5V+lXL1Qd7YQP@#T$N1;6#yP;D4C3)uTvLQCdVO zTPcAqxTbsRSE8!(KD$QV2??;zs5!!$e54xPx1I0+c7zqZ<`qMgr^Z+Ke>?VQ)Jebq z@mR*xQLSE1tL;{(N3J)656l`e)FEj@Dd@r4r1BN;CJ9iRcR5$f>CANBcJJ~DJQ8E@ zO~ibsfs*q9x~1hGNz`ssZgaOpJU6n(2oT0elMeXSRqyJqij3(4Y5jTXCZ!RP^yxJn zbV0?U+U5cNFbs5S5S*Z z05?>>9anE5^|aURZE#C<^?T9VUTOr^g-OsS#H zXVh$CB})Y@a_J@T#U|f2qih2{O^nJ7jRgdr^F4E;KfBfNJ?AgiD_=50H*%COa0|7v`|Bh%2yb7yplf4!1tEDB!CFqFEu8qWM|kWi?U3Rx}9dp zG^}iUU<-|jgzODpLo>np43}45CJoh#IMKLV9(@^?i;?s&U$a~a%E;#%@qj8fv?XQX zFlmDk5T1US;oH#b+dz62>46k_w=>E0T-oXjrD&V$ zTWCx-s9FnEtz{d~<{E@Fvqgd&tXK}%G^!ZmqVIx0UBySV;!@Er*YcYcl{6F!A*#ev z|1{Q!(>L$42h~(sQ>$JMNr(+K!Hi96nyFbt8^(H>XA9SRReaj%fzE}$i*~c@$x|fy zAy0|>5#L${`Dy>enwL1Tc*$=*h9Z|hY@k>9e_=R`vX|Q2lbJowa53?@?3Rkz@C&o3 zR56w4n1S`JdsJ^>IJprip^4osBlqZUrS3$B=Gz7h5Ar_vXBO}130cAGw-MpNAuO8; zqhBD!B6+HS_{1;3gWarT(czL(#!R~~*ViE-(+R^9eTbBP{W%~DQKz?28Bh3*$lGCW z<1ZMvYEh8M>H}dc_c#U?H{;FoJ=TQM>ox+xx84gCB{~v}~_hIA>dRn=25R=HALmidbKp4@n_kHi=q} zcope>GbayjzLgC};2Chu02JfYz@f*%@~JsFw{zk3d{Xz?q*U5--hyigpu2=^tlI53$%gs z+G287nTK=>T5(Adny$uQ-Hr z5P#tsVQ>6pnvj>JFjMQl-jjOV+c281@bj7jsH!?=n|0oV1*!)sA+7QNBgp-{Gii!* z3pQ3693ikwu_&%Ki=+%*vT&;jYP@UPlEjh3>XC1dW0m;V^t<=_L3*$Mzn_of%s%_etPqsaLlyPc*!-?A`-G_1!}GD-zE)+DfNk3raHBy>7V%TfPdx%f(Y1i(aB(3^9I?1V8k>zh*j2!f zbm}7%A1|_5YlSo*m+D6alGIySytO>0@4t#iMTB;Ud5 z#vgkzS7GR2{4sTsD3R^pes{kACpw$IP64=yo3Ci|Ifd(vX<(NMIY#WJtp9+IL5jFD zbH42~gYVy0o*@P3j(x5fYh( zym>*Hc_KkKgNjIwy)_wPse`n{P3H`p>p4ox#iLRA3Z6%_ED6dF8v06`hLb*xE`Ix zZ`#+tSF~LXVGgyU)&fkyo9nU9Xah%&xv#I@E+H<&%A)GO)r z<2N)y!mZfrNu+_zJz6e-&4Rscc+MLiwuRira?P0OA##dl938)d<-||u(l*?eFJq!) z8YSM3OygJO@8ob^xl*BW-Hr_~mz%%ci?p>vX0IGfPFCM%t+WQ0cEXwa(>zSV`qb@OI(NDIecG>3#B7`Nfj>7X;K8^}wOKR_N z<)L-R=e+1Sr1ByW)FDDj*ntw-%W>AzmkQgV$znd@u)BfpFfV*`hg!TgM8C@3i#yT)%8ROmln{~`aZ3PeGv3DK?js;BVn z$fFq!ThJ28G{;OYw_c@m98qPBsOnAaPR}p+8cq-(VT^I~@Xiq*8eNCG&XFOE2wLOo zdQ-cM4~NFb;`_rdj;DoR*t0bL==c|tqV;$1=j^K9!{^vI2aj< zG)Pal)Bk&V`KJ5x^zyf#F|*n68vhr1c{ksGj$VG_mIDwAY`m#=|p{W$chlz(zq}!BC_}I#!@^O zQ*UJJlj#;@$TM9ckG9_*9P+*n`3m;q5-#L(`8po^e0%Lx-nMNW)-cGtl8w}3`ViY$ z8`txRM47Ljjve%CXBu6*EckJ3Qi`_jdZXxNW3=e{AVUw#6ae?7@sIH{cm%X?I9}xK zq+vt!%|>zLdN1vX8 zer}6~NR(z}zFWKm2l421#C#lS0-XQJo)4vuS?EM)PXu?<5UqE~pU&V5F6&pn!|Dbj zDxzUM|Dx5lRs7{z3Wm2Wdd|1@QYbiE4%&Rzd|01gfspmi;B)Rs%X6;qsf;mDx1lMI zA$PjZq6>C~H0q`s0fcU(#7RLY@Df6mOUAh(UvG1yx`L3n~S=*O;*9S z<_5cpgZ}0+qLWEb$l&hWaJPhili+XXOV{>wmq`l*H#BjVMPsp{QyaB#Ah^NVxXygD zf@Zk8z3>d|%DSWdE87-r#Gnu2%8)u3lJt!0B~aSUD$P%eH_#%@iXww%a~1+pDNO@Y zQwg#ob5VDD#fb#YTKUzIz^bnxehY-9J}R#u^`}jtt}`_!h$>|fg(pfOf#;@3k0P(@ zmJac6)#sW)*h63IEzA`0;o7SOWA^{&kqvV*#w{mZ48~ zJFmR$-0M}(ZT=H^L%#5&umPdyw+lbT7N@0<5UvDhB((Dodz|Mu5z-fS=1sC2{0UG2 zGj%t=4+jauguPUyJN&2Qh3xJJziCh4`8>DPZB+952fvki=RqA5$_c+N@ap?hZJG1> z!5^mHd~jK6`-9&}{o}#L)LyCPi;0(*J^xqJ(C6m!%g38fS?lB0R41rvxAjSYyF$o&3(BqCK(vo=TFN~aRKo}J>9pqk|I#% ziF!d9!9P*wGGcp~jjC>zKfThI9}lv#jyDEXsIbQuEt8R_G3atNyRf;cvsMYjku0T0 z{3D-d0Pisjys=fFp1O^M;t73cvo#X>#1%;vs4e(VwB=!Uv)^8wpYeI5nGtSNC;w zDQbO{sU%{Juk~)ei}rt}e^>%fB=O=pU^ckLnmP6?p#t}9(P_?0HgtMp`i@ICTq-D3H4h^$L z$B=H$gAK{m^kZY8be`IPzFcAgcSlMtaV1~m+RxGBO8!a70+h>K)=^}mY#u1vnfGPq zDB>7;=WZe(h{KBorq@wjFN|@5teK-$HGQzvX zeV-s?cH1ND?a-!O;tZy0w`4~M_pl~4(mdKA>-&>zxlphxIgDPdIlD!9nX#7|M_^FLNh#I$XZlrR?ypcf)WNYmyFJ$*{?6GP$=D=lF@ z_KmE8ogzrCVTunMlz$Wwn^*MBqPMZu!VO|yBH_6D=+|se3S&!eR6t<}D_TeM5{?V6 z*hVSSOzU?@xWwGWUyk0X$J(`norEf??m&$s{Gq;#12F2>^jcI;LjW2l>7@pxT>uVnu8drS!b%fnCFS>z$8<(J2zBB?S zrjKJW#J@g=T%kIzL|T#Mt zAnJie9TqXJ{fl9`$iXGTr!g zCvA@VJB@ACSfGLmHCFU%HeTAZ24_>!X)eSByv|Qd_YektHPP*Ev?PAi{toVi8lap{ z8+9GlBZO^d$pVSyn)q^&hphfsX45Fyno``+kCB4z_K5!*12`4DbONqVqumN4seGkziPV89tckZP31isaYaezZ8VB@yn9a zT@YN&fBJp8C&57LTBn9{KasmK?K9u@-6eZ}$`U&}hdO`E)KT|2zQ_Eagr4Jzwjtgt zQ4t`#q!p(-mYbp@>R9_E1J989UZ?)x76-V*;1>$M?EcNq_3!NO^x%Q<)^wdg{3~7R z_B|<%DT_ANeiCol_@vC3bBVBq<1>P6&z?NKpF#g=%97rA+4JqCDPp8EM1Z~B=`6D! zx$iCgIyg}?c;6dB@0d^COK%dv|LQcX_MMRND#MXu?SsG@KXI^)7lgN;w=l57*h;i* z^wm4!SGf7L2Xp4#QeDn$M?1a&t{deQ z?PUu(ER7x2Rz9!2d`@czavVJu0cUFmQcMhV!~W>Xh3=JpLfjXC2o;$1RE8%I*_ z9M|40f9ZTux*5+)6zx9WBjooud9rLcxx|N@fS8IF8hZiJ_|W>jfOcqslL%|Y9Jsm; zcDo2#`xPX*4rz@31W2xCBo$n|7|E1C)3~N$UYKPIbFtH_oetRf#o`v5&-H2%1e~E^76-! zE4Z335;|D&Hy+6@Ukdj9-~&ZyknqcU{ZxavpydQ+4PP8#4#W9I6UN~7pj8?SzONTM z0PDh`AMVHgDYaAN^_~XuF%NzDlu_7-Z9O01et@fdYC`C_8RFe?jk^W5ELU0Y2c7c zo2#MSULL>Uw1L3KP5$)yB{bRZ1Lc8_8$Y7+M#H$K(6}bUhtLasjBfX1K~-uSMaMcs z^N6B)LwF?G9uzm_nR z3NKSF=b~^LU9`R3n@@f=cefFYm~}U`YopA099)q$jXT%6wu(vjPUmZ*Y8}_&BTXJ4 zF@iUxo+N!;@>9tEarW!#UWuc|6fY=LvwV}9<(p)dZ;Gs(%|&TGQVALoPo7yKpjbJV z!pnIoUUXB_w`B#^FJxXww-{^#R3q4GR<*gixv{7r-WQSf6X7a3Okz?qWKF+7%|Cne zlHc|c#Lm<<;zQl0odWODuH~`@_Uy0xTXO(<5l;%yQ_(30hf!79E)K1Z!3?_JyW>mz%%wOHld;O5}bLa z@grNUv?#OR1#7Era_O3{n#v9pT4H7Cj-7!BK|a$NGO;3GZ} zN17gs`|kinT)FkE%sThB=|&E&c7Y>}AEfS#ZW0h+&P5$##^nbVM3oZ)n~k_KgjoA+ zc`?r`27MRvsNgz3p~8nqqk%glkVw-QtP{=k@@V+iaRD{db7OC36m>qz&2BszHIrh$)i`?`;BtULm7X(6Gl;opJAdrY-9lloB(yCIA9 zQNHHX~@Plgi7oYF(~s%TOJwu=*g4a&nu6+c3jGGEnJN zr;0E3N0uomH|cBwmb{2kg%)O9P?L?2d`<4mP*E;S;ax0bZY8clL#bgnz#O;vbJZf9 z1@a7l4Q!ejzSutD`?5%6a)%VKXw1C#w$!e6e~wL4Q%{(cAv|5cPO)uixh(KP6D-%d zTs)KV+*|C0wU-*7)f`i8r*SC<&akJoc$4~43%t;{M<}C2QiX6;=jJs)h1}il zmqRtl2^6M>bI~Aew}G&S!2lfS8@R3vfxhzCg0E9>j6TntH;!!qt@cIVqO&*@O_8(Y z8`RCx&rddj&vFqI)2OS)nFkeDBs}#@hTg&7a-N&ZCvg8Mof1gE zwkFIug;0PiZ1~Fy+}q4k1ZB!I2Wa@4O0*|(y>mpJ`5n%T%J`ZHJMhv~vh|y^=5w7h zWF2X%rb9O#v6)q+6wA^#7+tAXY);*d7Y{Gg=69!_8$_*Jj&nW=yzn1sc&{mhB{3FG|A0FagDv zrR8Z6Zk#FS^@?%6s0jQcE93}IQ8(UzT&x4O^e?C(1PDIFEQ>elK0wqX9w)6RoU~$` z*%U&+lj~lA(fVnZn;|llnt(ZA_@f`kn2# z3y8`O+-Xm00avX@NLgHajEdLW6_a0MuSgqBUT!jQcUz3`z3bvrN4<#u(7mSi00H5H zxA<;sXR?hC11~mWj(cyQHj24-oQC-9k_fR1%KFZi^( z-(Ds4NPfBUX|fI&4F>NG-0;9B`pRPTA_W+OXXI7Uk_QIDd6ksIPlh*;m%9pB{yM22 z@>^c0U&!IOnS%?bBzu1)v^LrE(~IX6HXf{DIH4s-3qTWC`UmWRQpkOTm6EBM$0Rxk zl6s2~#I-bf+x{YE_%3nGlG7`&{1ad7Y98hp;k$^Xka|PFF4^ddok`)OmJP5>)%MKB zr*4vUWr9aD!3C#tP?qD+>6Mw+ob}1EODFXgZ3l6L9oo!@d{GI@Xx4f$M2`C}Fmw1M ze6M&qa@@P1$}LMXnCW(M@Hn0OaIkI z#^y@hqWIBmXWNdt5NqrnA}2LT+3TS^>M6h9j&ONBenX@1=PBSCw(Z3 zxy843AIkw#?OVH(he=(k$7hF`q$-F7ntt!eULmfe^D1E{a!TY?OI;yF4{|Ncl6uz zU#|ezihDrqL|{mNSZ35t6er+8_?&<<~66T z3y+I_c7i^#2=^=7Tn`7nbqYCp?alT!oz+|a{nPYL8y5X+ao7$$o_fxqzq9EL!+mM@ zqtqYJ$JCbK1p=UH{rcc)z%eK!CBE=6IBG|*O#UaEF!~x+xhMVoOr|Z`^O4aT?NHGZ((+lAzbYYee-|ytpQV~emaK1N zHKLzr+ifa$q>Um=N|dYKY?r~*o6fP3=3ffze8fvVwDgkkyyeeo%N9kA4jk5t0*7fv zc3#tHW14Xt7?vfAl4&!hV0@OcGW4G60--TYOqh6|?$66?;wgu}+%7!haeqA5c7aX% z!&ZuoNH(M4tQSja4GHucwx zz2;=2%2LT*4WyOgJNO#iq1G|4)8zb==qBc(K2E=Q$;i0&bz!}~6j+4s%oK(b*G9Sf zPJOF$+bRf#*%IXB`Sb)BiJJu(dm(u}UG!-!)fpl1 zO#g#{UCrmkwW4`==TOb?N0%KSu^|@GOiO&~T6qTtJ zPm|vjM5eDv=&u6)2bPVTC%?W#djbF7n?IBvBhiGKTDuf!e}~xA?H;H)pbB8KdJOF` z8JutJnY2Wp?Ltyoq3=-=0Yuq-=4>S8tBIwkoNQw|)l&Id7gL+*Fiv9@m<|ynS9{E6 zjm0%XS~AALlg6LPa>R8TySOLm6Tl?UU^D(2;#h;f2DyACdSAE(dz(Ba?`1Bj;oaP0 zuzA@{pbkmO6PkCyKiZeXU^OaD|1vU70}`GdyY> z`jd_sA%rdv#J!-GwT6M|lRv)bo&3vr8QxtyPiqR9E0E`TjFuiFxF3(Pbuu|zQ-Nn^fV~4oXmYzq3Ks!R3 zvM8j*ZpaPr=GJ?1VOBc@8;qAQw<_1D>!u}k!=i3uj+Xp{NtU%0uiW0Ix6cdqW;fL< zQ@beLTCFD+*k7{Sqc!yzU!?w&!KdP?E&^19EfTF8-oICpjg)Zg$_11$U`?G~PvO9v zortp5rD%e$^$>GqhWogI#kFeljKEgknmXR5jg85K^vKn zx zn%8?Ae#m<#AO&7*{t6xjvR~=f@ULC=Ha!m6sab?oHFUw8*@ky5C=z}m_4=SZ-9)V& z((E}>5iD(xQtvusVqS7An`*HBoYcDv18O2uG+St z@fGPiWS~pGh8M-tK!B#Rz=Icce;fh5I`0U|h2-!ZZ5HwQQJ0mp<1VVLr$xbwzztJA zFjsBty8{pM6Y+|Z z$uR^hLOm25KY1w&4zj*07*c;v&nI*EX01-*cg%xlYQ?iP(!_LJoe2{nWVX4$T;SpG zk@FQ28~`2mtFz@pn}2)pbM_f{aFuE^i-@lJiSbhN`9=EzTblxAwOnJp1urH8CGAb; zl9n&9wekHx$?a*=VA7q<9N*~}GC25UY)kFs!&G+~UZ53bka)69<7N^SqFm#GJjaus zPqX6Z#KHM_S#hn_c#vUa_P>eEI`XiPCvX&R&Z=Ht^jTSCP64bhEyLSoEbP)mkCV6h z+AeLt^$FQI+VJDT<;=XsV~(_gmPGsW^c&hM_U3JVU5oa-KccIRXZfnqo4&vy-LCh? zwbHn@CL!1$OqBgc(SI=CtS|Uq-q(BLWo~$m!JLcP{GDQ~m92zbDyr61?{u{L+KNaX|5rVks$)djy+;4pWfe@USu>8gHw%dN^$H0(nov=Zm$Ap$??L#P2a1dJi;oC_qKiG{5zh!wec5r| zD#l|Lg2gaK-2PeSjB)Y8b>=C7T5XxgxS)ixS8}s&3Go{g0gP+5#7U9B=AGFGjgBnF zV<3?{CH43^4lC<6uI*$YlEu~^0mxKj@H~Tl$o*5a*X0EvYZDn1xqwaxSr73<;9awJ zqLOatruf^2+C)2|6j z-Am;FV_R^uu!ae5o1k$H7X-Bu^(4Mn&RouuBSig_6~-HZy(_11V4JacqGR^i^pm0U z4*|pkV9^0t0xl%E@u^+p<-CS~*_efjBE0>IsqK|{Z96*N8HU}*h%V8^?;wa*^z~Oq zY>9rH)7CImzdri0re7QV*sFWKbGUNYK(0O`x+B6Hp|5kaf#88a)q%#FFuFeBp%)$D6O8`!NiE1~wL=q%odc#c4K9jX7ahkg9{bSz`7KfgR=T9?XQY=)ZF;C}131RmwI#3xXk zi`r*QZLhPUuS>LDj)58Et`SHfet~Ck47r$p}FnUZi3B@!h;m8e%|Qc@}<-{&hi*TQEE#izOh=ZOXJeyawy zW&modmdeHe-x{$ezzotuggep$TTen+Y%K6*umH-a)@_Z5M-?7mKBW* z)tE6+FQ9#Ec%L}8_5h75a>dk0@+^8*ZCoa8bfoSc+|M>bDz`gZA9Jx0?oPf8SoSb% z{7Qku!KrORR&A|^H;~;dUS}tn{#faiBR#9PbYS>wyP#$3!_>E!buBVZ zm2T3J=7!Q^yfz4YZ?4Q=B7&Tyt_EvMp6K8?4WM52*0szPPxM(_K2blJU0FVn;wp+M z<4tk-WK}$8GSQwc&MKaw`!HoFpF-_3?c&J1LdO-es3XHTI`d`kx7EFK6&_;EDb`UK zB^xpEoF(2p|6;n;C58)C-b8Gp{N_J+zQB6<(X<$=S&>3+l3&<-7_kTBAuit&m1>jx zf?_S~7m_8GIhTT{(T{PJ6Yeznah~ME7=~Tz$HVn6L9g`D1F9Fh`NrR-iBHI$DR;ef z+c4Wc;$s{zICvsg6uMJ;9r|LfSG|{X9h#Bv4i}n>3%Ki_YF<%DD4&sL!#Hjs!%c#* zb?8Mlh|%k@Ks%>spYuKVLOj8ds)JHr>+1})9J^KiEIUxNuXwTF7v0K_P(0SbxIzi_ zhXPk3CV2|w$sBHKw}s;CyZK#Xoj1yEOaCg;SXI`kUEn6egVZMG*`U_5`Fkq&qp7Xy z)TUt5?m0AlWT}pMjm)GnS{qdk0F6bW>C{G~!34qb!fh)b%uemXG(^7Elh``(XoP_%U2O9x2ddlT)8zZK z3sZHlNCFz&q2^lKYZh)S+HUVpqg#pY^>hF2C@;+(<@B=Xkuz8Bm=PTqzUl;fq^2kJ z2fTdD29F9Nag4qi$P*&g?$2)j^sHI*tvwyI%I*Ue?ojDS7E>I;&Gl+FF+24AG%1S} zS7XR&*R~BP#BD>;r^Z!$l_y@cx9Mc_WV7OB4Q;?U*QIX$(Qk+&czPzTlHN8<+FA=P zJS$6)P?=e26I_r467po-bdp+uK({Xzlu8Y?W?X8MuVJO;T6`5+d;%Qg`O*lN1B@bk z?^d&YGBtK9i+RJV3a&wc6WqR>qkD(y;MZYB@9CHl3&nj^o zLrB!2GMSgNDYJLFJ2{4D=fHSM29Q3F30#zaIKj-i5Klw9iuSc_opU^qt=gs5KR4yL zR=;ua133e$w-R~54H9xRSLWMH+=|0W@}}ple#Yf0bsbK~K}EI*{*Auz6ovEy(TZ$- zKiBAQC$wyj8z<-J>+g@)(pF!Xs8pJC*glNSzyyDy;{&4#@;v*qBUpBIS}76&^%I-?hO= zy4YT-a0-PrPGJvQNfj1Qc&Sq;L^;iG)K<&wB$u_b?-`Ti1J3YTaVN0VF^+Dg#`<(K zxM)Wsi0k~(o^`8NXlt1*wrnCO)8;a2y@_%q z-f*l++NgS=@5ZmQwrnHbwNZ<3UyGzb?bTo_=8F7cTlqIzqwCp&A#;u&ha0bPBpz(D zUVl-s+rieWe6dcxjt5(v#^sYxEp>>0J~rK6!nh0lynW(p(fq)H}R4NVO4_i|& zsAj(f(cxYC;^FW^3cN8+H)qn#f_iKDu}!8PrCRDOW^ReVUT-}D_NwrKu|tk3L#+E) zRe5b(n7oZ8w|8}Vz4FzU_%PwA5@vfo{fT`S4SH68b{{gLL}}@nd}~Wy*WSe=dRr*A zA#pgd>&wNSaU)v>KNPoe7jlLfIM}SY_oe!QtrlLqh0B%tYFxa0z$mM*Zo;%uKAOIe z53HYF!D-O>)NNlK>3NhTaeadSvG`g?W-j2Sg$tA4kCS68i{v5e5RPvra9=a2Pu7&eSNC zI8ww0FKOGWWS6^oMre;6?u;;-;zru1JJ5N`E2AHuxpK2G4l6e%MR=P%zrYXqr-Pm5 zrQ$?dv^{hGG_?kV7+9PU(Cp}9{Y;w!u_~jV4PV)LX&)nt_W2)Z(W8iBpY^HCumi+^3vT1nM#(BnCF1_za~a%i zJ=iN`Oo~|6vI+XCL#0zA;j{Q{lua?pCL3iKA@=Z5$oewez@X}pBASq@ZdWv~M504*RAc%SLJn1<2vpXoH>250{d%1eWfx|h z2il!+|K`X~v=REXn4NEHe2;&8t>*|OZ+TYp#lFOo%x5(+;bmsU8sfUcBru~pEn7?@ zp)BkPkNdB7X1nrD$}$rxMlr(h+4A=f0y*LJsb77LUqFhs7>k#{%bmf4#1Qxpxl(V_ z#pWe&dlh@MXulV2DfCwS_Ss))e2tXsab14`pSbO1D=V(+ZG2Xul7#ajEg!n{v&BTI zx2Z6F{}~>+p-Xe?4f1E0UMhaMDYId2*$;HgLK{v#z_uIqmvxwb3s)s)3FZxq(e^zKQ+i#dra{6uNAfB4FeRA$~| z_6GxZCohrhYRiL6Z4c3Le615?K?sxMgQ+6^7r#^_=XEMsl%$A^c0^Kd8HcQWzdF|6 zl>Mmg(&aW~B&|ooLy%*xq~#h~7D+WysS3C@qem=t$}Ui4L~_k4JA*Q8*<|M;ge>s2 z#+fWr;=q#6PabRiqAP0VQ6x4$lA=msFh!eV^*m^A`VJ#1r>Rq+Y%h$4B3Q^#tYrgYxDqXVDwM)K{E;&n;sJ6o2V@T*DaEx+V z4>ugDEX)s|1dON<@(z_RaC1=9imCDWuu#ludJ^{iH9ZOCg*K^|T+FprlSeg%t&Y9$ z&Uu@$+@X?b%CuEm{TyrwH3j?>2$CK_7$xPm&z9}k@)n05=gNq+smOsb2GEBaTT}l7 zexio};1Q7f_spw3sn_LPtRHwu4GUs*itGt}tbrwgS>p+8(O>1~K4Vwv(KMJ&bjLJ) zr_XQrai_l_ElyIshtFV6=yHs4$avZO5`FB`eH5YTd>`0Sz_vXt)<`!zD35`|s%wk5jcc{xZ)^UC z2xSI#b&M|$F(kxf_8c=M_n2jV)4N)oZ^mpt2Q5{8X~(Zo0(IFwF}dm`f*;8`=y}p9 zViOsf^8<5=kDODhMobJp3R&mbO9oKo9=*pXNbRq7-i#W2qv?$+B+#y=?j)h+_yfiiXEzwVEksC1R8od0(SnCz^0CIYf%O4LI5wB1_O8jn;IZqfK}D~FA1sO)@|vZEu=G@x*j7XKleACs zq9!Vc_MfO<4n5VsqSWu$CYI+LWn4TYevtFSMlBE{-9GAPtZmA?#`OHuez_lM#)AAo z`TZdNS4k(+(c;N9n8WSLLf=}UM;u@u34?eg+)wxfL37*leaX0Mh&uQ~Y^|4kO`Zb$ zll+7aoau4&P16b7UuZTEEvOHFy~y z;fbyztB9cGH~4`?g*mgZ`2x|5kI8(BOKSdJ;dYgOrVY%h3^26-lWMd}S7u7DlhTF9 z!k7Au$=uw;Kf;P#VS~2X`j)_T7c-)CWZ_X}bv_qwv!)PtlH~ue4ei!{o@xCiTDN}i z&Ewzq3$ZubG@!r8u7qUXBI3O6!b z!C9NIqD%^5JySTXZ)}UrpDjw^fHnGPZ8Y#&N__W*^e&c29vR4K*ZFO<~G86o- zScgd)CI!7U^&yfLS=I<{N6=68t&OYLvdFDl&Gj0ZIsC7|W=5J}%UIN>ER=;UON(E& zc$C_OyJ9c@qjs|Xngx^H?#>KgcW1Tu1Qgf)iZB4!K4G9?{e$_vGS+c5VJ+@Z)HEyT#scv!U!mYDVp85sF0*}}{wQ<*$NcJra)um?j7??DO5_H^Jw{UiF1 zYaUA*1kl$yhN`3>6rDMkY-$fOKZe#$VwyvO650duTNY@REM$W}QIVt82*ZG^`S>lX zb__UAGE8X!b6;BUx0%5MEL_r{cnH6#7+R0%o_(mID-WAOtSwRa@5EB>%KV}9qPXN{ z{YzBalc$l>RE%`qeH5?~4Fk4r zb8a_I-kiqP8LtReCpb3M9JBf@QJk3-TeQt@i7V(Q10(tjM60Hc8*t=?4_J-yk-2+; z7l$XL41S#NypD`4BabuZ@5NRUh)&c#P(W?&@rXq-jcqY_XZli)A46}``fp^GwuT!K z+F*N?h3+m}_dnDQq@23ZEnXDe`*BQrq3J3Zy|i5)b}IApJvhSCVx3jIFkfrBKM)*=WEwBltdgE?dpAijOUfk6l5V4%_+#-}r&*x-FYG?2z zSJ5H!3T4>i%y-~<{ReVUX()&n#$;cc^T)Dk`tZus0Xg57y6ds|?+#i2VPg+m$Tb<1e0<8>?I)j}q>(ia;nhLjC!DbU4YDPf<*aYPeTuZ>?Et=QZg9R?{ ziFCVN|FYCtj4yb8TwBF{iQbT_xiIyt$c54!s_4vvEGnll;we?^uq_mPt^XxcLl6t^ zEHmUcwiONVW$_I;nvl~DUsSxq6@P73I(~G zYNNR7nY_Yzk3U$gbP>AF_)xBk7rw*@M8+oj63!s)yfSRk^;CU}>cU4z4i{rK;f8zENl-m#4TavR` z664TPERRT%!7%Hse8>zh<+c%SOAb@-<-_EeJVKPKjiU|N>jb^l#rUi&j&%_3)%HM= z>CI1GCDhBiCHZAVt{y&@$r1em(Fb-}exBXs6Boen9AEw+4En@C?KCkCJJ4LvS*qm> ziWCOv5&Vkpk&>MD`n>4tcN+a|d2Kw2AKt0voQe1&p}j@oThhkT>ddOqUqWj1!z^-V zrRJ83zMtLAuXfxD%u_{l)V2*b#WGuDTe%#oh%tb)gNEXZ&w+U6n}Af*=oOxp!kU$W^l}f!H1d7>(3AXFOOn{7DO8O85y-*$}7mm3LT z>(d?FL;)R$@HZ843VpGr%z`2Ua4GA>tmX-wX1_m05sA!Kmu9{ar{3gO`I&h)-hOv> zvLL%ukO2V3Cv&n(g;}=20(e(aQu<`t*_(MMO2Xuh?01{&cV{UNwagdy*rnr=zu_SZ z!2r=06JsX28l!?o9{ch??Uw~f39jTEdn)!v@^lIWa~$+5Ttmk69$+ek&z zMzJr8YYTA)6Pkt#)A+~{$k|4^i5$6Fuf;+Z*TO~mMf|hs9Em=BqV%k2PU4MK^r2d5=&_nAS9dm052ti-i2~Y7w#nTs8 zIOz~a>^vnu9K!Dk4Um|l`Q~ND-qqU~zGyup&+wfQS(RUM9;sV{pRibHrpPhH)jk5vmi=1_b;9XBbfKcCH{oE^fZ<%!k&~2?`gs9Ry=%4L=>`o+F)gtL+ zjC8UyJaOp>Atf2H?m5FAJjQgoM|FBz$|%~I7_RJXSKCSCn^~09vn<*+`?oj^sX%te zJ-?mth70Sn$J>$*OxfcdnHle$K-NA(M$*Jceq@hi6eGEI$bFn)F&cYVc3U#6WiqVM z_OPlE>CBP^6EP6irfO^3>4U>N%kINRALK8P+4@t-i12;cbIic=)WCaTGmz&zC*uv@ z$MW*m7i*Atz{Ebozb&bsKxhy_z1;{`WK601x@%|(S0RObM~6S6(_Pw=gM0bn@| zu3ep(nXxq6NwZsM_DOy}CMhTF@h5UmqBdA@tG!HolzLI^gTJQ;AtbPIUC#qL zGDtXMT|&GA*SAT{^+_;OBc_K`ztz^kFQt*7Q{Tbh2TJFnHOxsq25C>^ z%6XW)m_`Pn+>-UduV_jP$9z9EsUDfdcWZc1B*v;O4KdJa$x+f*L;OqD?nE1{S<0)a zCez_-6({)QT={$z%BcyRGc@;LXLW;}`C`H^C(i~LwG=t7@UBFzVzk!t!W@1d#zU}` zjPg*~0+c6D0Q;qKI{I3Fp&o%iKYy_8Bk1HEWwozWY>_g{@}rOFNaEurh9jkN4Sv!o zpTJ-ZmnhwkMf*+oK)rQ8Z=f4?HZekDa;f>AYM(oYT0UQ^m|;35uGjFGYD)~4=^bo+ zNxF4mHd1#PUDm~jLe1f70ggF;n1XTC^2KgeTvD&h11?6{qqq+^T*nXT4%~ zW8=2<)8QaEd6-W6@a1Qx4JcB(fgv*g1L_hO1 z`-~6LJEN*^#Ps<~M-f0%_P+IJLXM~#o<7XD&YyaOsOk1;mbx$DmfOij`6qS%V0%S7 z`K2@yL+x9MzslWyA_2H%SCar&jHQWI(@sj!qhru2_*yrEqLHH~r)Ms5^b}-b>0j~r zt^u6g$m9$KJ!<_E+irW?{2Bnvpe1+m`84np$n7d_v5bSh8}n^489*Y~tv9Rrf~r`7 z(;R$9;Tl<4Fv{2ZG~d7{52QPbcANlTKp4;bYU_`ztbNJd>TCU}Q<1Zw#fm0!idzmW zJ0Fx{o=;V3gkESSHeJj3Na+@`)Dt#66e#NQYwE3sm|8i3gs|~Nh3aM;r0skfo3!)< zqARKw=MDC+pFBf4d?FPeobSwvKw@ucFt72~F6oL6$HM900gw?@nJ7|+;cbqDO!UB*|Uu&0|J?k^Quw8lOloYU{y6>}XzSf&~#W%FfvPN5|vy27#Our6n zovfCo)y+$28msBM@qgH)YUDzH6s1p}H;03aYn+R_DZ&l#O=Kkd$=B0ItjkV(&y{at zWQCTKc4CWmj4Rh1fLd`pn%+JMcXlRChV-Q6?L=i5Afe|N+2l8 zk!)zKzn0>DBQS{z9*s{GNw@y0Q=9Bw@()&Nda zQ6n(QcvgBdk}FgJcA~w>NXg95I1Jys)}F6O&i@6ipw8$zn?v z_CubAdSg+SbxOMRm4%MM_R2RPPPZR(eh65IM;oj+-gBT?)n)9J1Q;(nG;#Aggdur) zg!c2)-{bR7CdnP2;9FOV^9bR#9^h8z~`mDHS9@<&JuO65D3fQEy8_Lsqom#Kt2VfDWRfF~Et28ctL&^K>B2yw; zzAdE^S6<2H@{`Nt-O*fqc+o&I#Ir0Tfkj-&5_$1)uI$|n%$_UxCG{3-A&CV72f86S zLaK0GA`-WCC)Q!~MbI$cjZez4AH0i|bE(KFEV)#hPPOHkYR&&jwo}i(1pfz5X?-!* zFHb#jEUKugoNA*--5UiJ!)Vys^GMM9+L^?6+4cGee^d@Aaf{tTxWYrW{CIWmEz}ij zF~?vr5flo6A+@-{si$C@{U;)SI+cWiKaIS0Tetp!UWs!~0~4)we#lZBLS)k_e%o?X ztmiNHBaT_9p>-?%C{SfW?v`mK?6+_6w&XP8Y02MHeXYa!8xIm=XhR6-RVSRg%J#8_ zRnLd$c?i{bSIuBv$4*5(To$4yMOQm~v2QYd>5y)N`8KF7tB|hoj}6|%{>I&*A9n{J zao~7?*)JLGYbE}pE3!$*5k6kG+gn@c!P@=2;HuEiRl(u6fsfEvGQH}Wz3X*md4&JQ z!8QqKFJp>LrZvEJFPIqFB;p-7Mk1#iI_$qu8S5Mt>T}gXTr^e|n6pceE8FLnsr`Js z_RF&9QS5op211_CM`X{RbCr2;_)L8M_*F2+_tQNrjJbG%Xnn=`f)u7wfG}D`>l5a> zMp*t(rwVnMXQ+gX*7xDyWVz(7`F(`0-W&1M7_VSD2m;ny@BEU*?WT&7*QKv77#OYU ziN}^R63!+r62wQg8_qTUCZ*QN>4tUx(i3V~G7Qn4_c@(~{|=qE{YRaFruif3vRS$$ z?e~wU&N*wUt@$l}WOqn=Lp%JfYJ6Pkx7qv<$78|;*Xc^#ZOa?4@CQ^_PC}Lpmq1hg zHWCrHd&1L3XGi84%iI(7Vc8WPCOW)Svj=e zdabQ?>p0^-Jq{i{?Qc)0MWU>Qmb#VvLhkahAIO@hD65Du;&tn4tQm41sN1A$BYQ(w zC%%=Yy451BW?o7js3D+f$*v;)ir3yc%(cj*-LEX zh7pF94&G)xgtefdU}+Ejn%n(ZC8z3MdzZSCcQS0im~<5kLq6g)cQUR@KnYo^?=bJLzpVJ@QgED96*1D`8YU^Bat zw@UNQ{u2D~N}*h6mORM4VM!~$Z5xTpbaFP#nvTU=n%_z5+Z2D@5Hjj|+JobwE%OdR760ZNIo00xiHYDem0$k)-U~iCfw85KwsmBQ0bMI3Z0LY$ndj>f@Fq4_+5S+34ypl8Y*(HT~Wyxqz9KM01kkYWLJ*RED zmU|`ieAH;f`4l1JyqYmQMiKCFOE5{QO)OzGa|>V*bk}L4B6|So(~bQ zaHVP=P9irXv-p-1Sn)>jk3D98e|F|ZS>jdS;dXR}ogaew+)xO@Tq+uuTG28sYyNuG zK?!Sv(XH0z&8sAcgu3M*gN&5qEDFCVj?L6o6*{#%=!BBz6iQ0L!e%J@Se=Mm>uk;L zA}Bs~L2M!P4$=NI!s0xg5Ug>fUZ#yij^IV=1^7`#E4F9$7mA3z521+ItQ0bi{jnU( zWHz#1{fD9pGdX+}9}zA9irmki2BIb8#Ld<(dk+QAC^Z)q=;s=*JoPkKm$v^VB>F2) z?MN434?gViE_Ippn>k#Nil)QQKzx>00y`M>6P}XWvhj-D zBIM}cAAGS5j1QGIX|F4=bi7D3YszdLB?_(uVvntlCI>lu*sHtVHoA->5)H1!JHorR zZ1qv$09O7J7c<*<1iOSGZDiv^R(os_VRQ-JB*&v__w%j3kh<6T+_q<6@O#pO#~Pk| z=mL1ygCKV(;0{{Z{pEZS_>};B2;f;Ci%quxd&2(;tjel*EWDY;Qt*n%2l+9laOIG` zvf$0cT>U?0e3vpT8PbYCoca6eO@FA}GLhy-O;Bh9>`TPsQceM@Q{{Ii~m3~3^u%yRQE~cJIr{^F9Zb`tA!sv^)7Ol27cbUwP`iEO@fl z^LX>6eWCgK1x*hfkUFK*$6pA#7m`=dgv^&p5e$$Y0i@99TetXr(J^g{N+#`#shr^I zRJ{Ci{hj0D0`mpH-IaU;{`lX2{)GU3F8Lc85iz6ga>J0vwZB345J^tw*3^BIbNzT! z7*$_uSom?o))*qf`%OuHa9GR_ZGJY*j}Z^0Jy@`G91gN=!B-toqV=9!thPEA_1E|z zH$T!xb=xu{xwVK7Or+&imw0-0M`q>=G@>UW)sM0i?M8{$f~4d^I#qWHuKBUye56vD zou@e7EcvG`(NYY+OCo@HdqhdJQmTgERK3744LMH_`=ic%82?^wB`e38Gr0|C&IffL zzS+JH$6IR(BK!y4;; zsUIvP{^m|~Ureu$(wDd7Kh9lk$o){_EqS?iA$ zpY6~KlwZN61FMLOFibt87~Kl~lzeU3fs#OTPxRx@^nKBf2lSC~|9`1Ph*yNVQqS48 z0@jq&R-|sW)g$cOpnMzn((!z7aeVJa8TTAKix2QLBoNOo3T*!Yxv~bqvQOf{ey;f< zo#7fPQ^|$g$=#dLe+-UaJVjXQGi~Lpex+wm{ z$RAf-5c%fVgUFVq`xDb;H@Fhh*{1TRP^J<$jFGqxP05i8!bC znB~)qoJ4u4d~VcE!(rfhT1aoCLHk-n8rx-c$DhiU3Y7 z&X}GY&C?5&Xi)6vpg+J&`uF7BF=i|4w`dO2_&`2*kt}xLZTx2dFnXn*G#G>PMIfw} zK68q(HI84ht-A&7?x!K}T~y)5aJ|%+Vh)eY97{BL%uUTpFl1DYFlS`;kNCroVCZ6r zPn_is6JO~`-Gjo+;qWnprJt2r>gK`z7Zn`d3gLnB_?4Yh_@cvGf8L*v+Vqgi>+k^$vvn){oi9cJ?)ObIj<+q`qr_*d)}K&oUpBIv-^B z&-ejB+Mh5G9;d#`d_x1$PeAa86LmD?N8|F*bhX9Cm#qtq~}-U3Gt~uj1>aU zBT;Azro>#~8Aq?9c6j4uF=QIO`f!-`nNSJf;>Hq8vDdJ_kc_+OY`eH_d|)+7FW@T* z{VzvB{IuiFmcs0KOEgk%-Lr!C`;F$(&%ssq*5BY43>EgI;R25Tgn z%kL?&Kd>DIk2!w|xw9#gY;h!U@8K*8RFQ1;ZGv+pftY3nq~yWTN~)I~=494E>tRvps|N|gyj50j!iynpr+=ioKA zKBZ`|Sn;4(mzn8d%JUJ=AsJy&Bz`MyPb3U*6%AYizU%c*$@sW$j}eKBehFw6AWyKe zV_u=1*CNYTYsFWq9tMLu`Hzv8n8m3*+4zWjiy_|2R&6gU?6PQtQLsooZ0w-l+QeYn z5W)3DU(Y3|l6z;g=Lj~j(}{;q#XMWbrpA)|CgAajXvq<>6a+T=)=Km#tevWQe6d43 zCXlo7-;nd!v7$@v5xOJi-X9;K8TupMesy6oPBAVTzUWLI6ul$btY}4+d<+A&oF9a%RXNI+7*l3%o@%ZA1Qbf&3yMw_ixk1p7q|FH&zFBR?xX!*`>^Z3S_h zj7*Z@N92Z4j3zm)a^0ONRCr2A$&SeYt^ih zfFj9iie#b(<>%=*&**`z=b8ums6?SDRxB(V3jC1uSNk0kTnJ@99P zNtCT$<<-vw?ZBz%ww$&ybiRIxts5*bPBW%^Fda7zMZQ&d#FjTErpqqmV3tDy>scF( zYdTU_~g;Es2kt3K+06kC-_%V)R*U*>!qF`!~Obs?X^$? zXatJ9!upq7BI`? z&jo}Xh8w|Mw=jYmh@J6}(+Lu*a5?ro^db?o`IsPILjezU#=cCiE_{8})R@cX(!_AP z)NO-QO}sB+f~b%nwI~bJ+|Xrd$W#+)XKIS8JXO{SaW!u-v0(Sb&Y?%nErSeDRW}i^ zq{BjDd$+ksjr5AS?(L}7oey7}B`3Gxd?)YQ!T#lme_?9$2tdSNm@=nC1CAX;{D^@VsBa>w{ z*1LG=TO(JYfP%=kM)dopYJ@~EN|pE@*d(D!^g~ z!b+CHY4(63C2m)EYFV*r+n$}KbL`K6TGd97*{)4f6KcjM(^90wDGTVX+Z zp*Q|A8$;wkc4jAhnyD1P2#0;9$_y5r2A+2@NSXH*kMGvat6vl8@T_=jKD~_JjHp|o zUHf{$;!MJmr>O&`cgP=WnuxFQO53cjBw3`dbsGqSMjvdvPY&xac_Q4+CplRC6G8tm zXz)!mbv8!dhUEw9c7$iyxK2X4#)>Xwmtu4}44`wEmwn!|Cxa zpCOAQ=;c0DutaOiT{i~L%UP4?B`{mHp>Y$-^0nxgU4 z^Vk{vS!wM9ACP?r)hC`(^vAigL;g$;e9)N0(#ocsX7FLiDnuhcJW~copfTB=oDW4E z>^?X4<_+TcpYwoQtuUNOf$>3H<-cn{VNo#B<+>_8&?|khB0eVX(FaZ6Gu8Nr@*|*?Pm+%-BFQ6-H=h4ff7p+^Mk0iZY8+Rs!Dw%gE5712l zeW2U8V;p~tO`q@vbiP9dYHCphT)L17fm}tuBGZ`d zd7q$6UP6n<@E0~HV&ZPIQ8wK$RpP@yqVY%efnd{?EHK7=j>_0W%>XmQj0-h1H>H8` zWq=8foo$mmhcGL0*YwNe4nVYDkYoW-YVuJE9Qd}FibMw#Rr5u4UVjdPK|+Ot3l(M< zsmUq zF6?G{YvNR(z`ss9vJW{0TN=jXVqT~KFUji=XC{JTS&ThkKWLoR|0_l!c_Z!EY)?Y5 zGOFHW5eug8mvixFZ(eAY&c_aJ^=C}L<>riU7{QK2@L{QJN6lK>&!ZgOg@2QyyVm;Y ze`b#EjdZWh_twXFWnxWNI#*#Vs4E`0k;ysrmZb#s@U`Lx#a1m0$;$L?(74y}1l#xp|?p zbd1Ew3<{^;L4x=C5Sd8oHbS!>u8?UkUIysXnC7fmSW2s@U!?bUdOatb%jd_ox7EFG zHjqluw?4R!{7{TMqAu76q-(Bf;19VlNQARHo&F?twH&qtom$i-R)**E%dEVX1Adfw z#axL@zuhQY=ZkG&st9zh&SPIpnIKk93{;}h7puT5dKX{rE?zmoSoEwfdb;p9m4$(X zFFJxp2r-Viebzge|C;sJ63_=Pnt$=N^c&-Eq-CEgd~LcHz3#pUh2hK0`iYU2_kpp; z=tBGcfDbInLz;c#-n#=EuVha5Ii8KaONiYAGCw{KY_$(zlG_NT{(|J?1@%rq-H#7^vT$* z)tLD+68erk!)uxNQYM#ZPyCWv)>DP)8m$8yHOURq%1P_8|BJTtd3~*UKw*RZUBFIO z@Y6Yej4iXxc?)RkAuiPS$>JGXSgVGK$+fh|RG&!nU;MU{EBJ{-LJ~$?d|0jwF1+Js zOisOVm^(Lkz&8_;wN#?M?1q^WYrrN<(I2~$51;m?9y1%JBrl-CvHniA+$x_j=Mpy| z>47_i)sTe$2EZyp!!&?Pteb?d5>Z0KM4sSiYd7SfJ!TM0^ z_Ung5-|*=Ft-crQG9_h9?@zChAjbb5^NIz=mZE*^>fir@bK@FZ*83W(um1@u2-yY=Dw`qaVR+g zd@+%yIUc!)*x?*2KFrH++vbZIyinH79SjKdh2V?qM*@Yw)k~lF%r5?yX~0uld9SdENObp zxmz{UzZFB6pnuXH5Ix+jmqd>a=r>K*4K+-n3bk}$MMZ~ke@-qTp1T9v2}qF0RtyS! z#0utLdun3h7tK|WfmwInChMcrF}~Kbqzb(UNt!|onprA#9cW7z0%$DSL{?dr_m z>x&5$P(~etKuSx2X`JOthdb|)8qe*du+Y7p1b1yQE9Fc{06?#Hm- zj*bV}lqGoCI(TaqtYWZCS}Ve3tkeP@BSDo>Hj#lDeB5nMyd&=ssyg*{f#iS{`juMK z5jb%V5cw&*UlQ<=FKvJFTs0^AWlqjhm5dL3u~AYO#6(r!uVkZkV3$6`M;`MOURZLu zLo{m7U+i_ZZtB7Gekn9n_J%$28ARFaE5vCBHItN{SV9>&&EHcQ?MU%dy7`Sf|@|FQS>@ljOQs2|f_9ty)TLtH~^&5)AAvW*Aq|7PVGdsn*)cx5XB1tbhU1Br2`p zK@A0KYWdVzH?8sE6QU^jz0aMQ&4Ye^-{0r=cX?@c=FWXQ_uO;OJ?GqWB-!V7-tSpj zFI$Sl)wQ>jhh$4xGX`m^3-2pU_TK=zECE5cbC@Q+E0Bg@>=p9noZFitzlHC;oRmJE zN6kUQr7yx1nb+dLxZN(MMEY* zsmyYuX~p#p0=_6^7nrr%7vWS02C0GDqFXHn4k*M7FdS*RUW z0!7JMv2}rU>^mxJ`Vis?y^6fx^jV<6A`p&+x?PsXHcRk*3EK4Z15z&9QE9zsms3p) zQ=0JfWAd$PYq|BfQ>Qgh-YuUI`daaRMNg61MGRx#Q@9qqQrWiR&)2EtO_v{J+yz(P zpe0hW_^P&X*LGfhV?ktl&DkzJ#aq(u#7irQmAac)sY46-Se@bz&h;t7s1!8)0KIg3 zvZ6`azL=$XJtIp!sB+lwdwXdg{DvWT-s5}|fHm9S(w6v>#sA|sGRKCkT3q}PfJxqz z+#=#TT>r8u$a}0Qec9U7P>BLYrKtziroQb0!3P&#iOGY3`W5-T1=DN3n%|wFpGFq} z^#nSFs827W8@O#|G2kG2wlzh1)hq_t1qIcwOQyM)v7Tx7)t+}-)jCi=i2eZ+2fw)e z@<`Z(HUAI^SL`_Q@1VXUc6_)#;%adQ0Fl6-)U=*i;lB{vxGE6N`Jg5wYkKhqKA|wE z9*oxsgt@=Fq-Urfp#>gxXLR-Iqa=?L0x?;Dt zXq2t|2l)H({SN=KA@4ERab0(YoRs^TE#tzLkRbtiPR`~;I#i{aK z`NoAK7|HjgIg}fjZ$q)(yg&c2%z!MDhi~quj@&cfwRPbVq;=`z>Qu&ps38;f6<^?A z&(RM-UD3N0E`&~s?y~4cb4ERfpIA!};nO^yLyKyDAokR3iV*&s4W>80ySt=)Xi!V+ zq3=L}(10aHNRc1r%RU_)8a`G{{3K^rar`}+#WyAwHsGslLeC<( zBq5VlzgaDJqh@KkErXu~>!^aeXk#CIwI(OCUd_-?G37jiXsm|sr#J-mSN|*1go2mv zXTam1$vHlF8*$NODvSozqlZ8*PC4R=xEp4g@`6x8JZH;CeWEdWwGZWk5~GS4_c?n|g0Vzibsgl-lZo@T_|#z@>Z*yd7hLl-;BL7v z+6NyxFZ?ZCD$m4B8^;mCULOR6el7hPY&=z*Ehl5)uWk?&lmi(a7GY-QpxKR%WA(tx z3BEB#Y5Zh@xiYIjr7r!$4LAy*16SztV~xQw;{)T6RI~!rR25yTH!2T8pFc0;2tOjB zv5bA}d66z>eAX6ljc9~YKVGHAm@|nvKtS!=@nmlqA9IeITTh1FcGNd9U>}5!v0XWY zdX;cyM~zplpRQA!Zlp_*^XN+;!S%kHZR#N4_?INSpPY9(Y037UT`VJrGRtJG?nvJ~ zaDR01=}IAXh%E|LsTmu-knw=G=i&hls`euE(PEzfUs?Ucv#V;;;?Kw{>}}Til6Iec zNQ!SFP5gx|2!YJzm03{zZ86-GdClQQX{)ww6ibjD(JT~B#Z*G7OYW61MnQyDGqTs3 zXun58jNqQ>`FAv?K{H_0_bz2dx;_KR|of$NfidKeTz@ON#PAz(;K2j{{0 zHulXQQTQ6Qwc-hZfC|yZnhAs8Z;wxWRJB`=DNUa~Ny=3Jla&E7V$a4d+kc4-gs%{@ zz1Gc?L@q6lFYpK)0bu$MZWZs`AOsu7e#>D5n`A>ub+J*)w7T;n{xUPiQ7qVc;NPvw zpmsYJQ#*4tfC%kl;RG%$j`lQm}POv$mv6j&`P;w)dMYjmnjT)U8~r!=#L1rCBela zT?JSBsvk(Nb^AX9nt+vo&1H&~Hv_;B;uR z`q!6yfIiRf+{)$QZreV=r=LF?--2!Qc%Ox$wvRU-2>)1ZpjUrQoiC5dc*}I#y(pA3 znX_I=m)4gaIna1BJi5R;Je6H|PpIzBPd(8Or88If(hv4qU!2v(tr&f?S$xX*XlXoB zR+`sek%KlKhfCWeq=xAsejzkOE}I1iUL@ip&W%T{e=)7b?rS(UU)><{ti_A@3&qy* z8;?agfVM|VOd(0y(e9S(pD^k-pHqB_3Xw<_*|RbfRXM2}aK4Q74A&CEc)GR4U*m;I zEAV}wC0^ngJG@u) zKG$3j2;29Kt4|BR;&wND0He_f%2|r?n``rU0wf$8mHNx)L$-1I*<-cH(e=&PU$WBs zUHq8@83!{F$_Z9;B|D7S20IO*Ib%2AHsW581+QE{20-_H5ZA`Oq+iv)Z)6*FRR0+lr;%1*7|1v$f|pT) z?I8XQUJl@^(*JPhvtnz~_W&34Wxu>l_cmjYV?_GyqlU-oImubU-pH0%0L5YXMq_F$TKplwA<4Ieoe}>8#HqJB z;*v*?lEsW}H)m``>qG_CQy(aBx`qYdzs%r_!+E$T-JRt>c zTU(PS4UpGQ;wW^-c$mzB)l1Eyg~ls#bopfY4Jd|;K!ZV| z$lK9)vP}BLFoT`%N|yVCQ^w1Nh0;b?OsApt?&4{MdYiC~UDmh6EWC zpaI5zsKM(a8xn^R!GS3r^YZcf_sr|YqgqQ=aqij6IPO{vR=IW9PD9a_ISQAeI~0La zF1aC32d(?TL0Ee=0607QAo7Y9{|cjbm-nbeYI&!|l4g2gdT=-abB3P}8fE86|NQg# zM7;H893;+N$}DXtA~f^ME6(Lu2*&glk7vzxqH_`z+-9)RJPqDKPPlD2oY-UE7qHLo z$Z)_dJamY~vrxV_5a?ohT66vYenI$K7(g6uXNwlcuaAYh?Wndyg<=Py9ys4-P8)%V zR>0MA)+?(&wb@F7a_$B)!e`e-Z1PWW3=5eu3X;4p;1zK1JHJn3$3G zqpw`LDcviS6;8@)%M#*6WV!iRN&652`+gtw7n8#HpIOQHk?P|b%7h5xGuserDe&M| z;IK#Rlqu)Y;&Pe&R~!}lk`n*oE!L4Mc@)nT7x^kGrywB^wD_yMIK!5SoHbsAJPf(b z+JOd7O|%yOHJRv$cQ=S>R;~>1uFW!b8ItP$(tA6rB@#2wF==Q2atYxnks7 zub<)~-NOqN(#ba{sBbOkafZ3b-y7Kr*tNAk25&N~2nOs2`QDL!&yi;iKvFO?wH(y# zn;)0{ECxr}r5P4;QPJe=ltfHtTEi^J$ljVs!Zu3|q~}n0Up+go%q@DMhGb4Fo`!sS zZZE-?Dq5@C^AUm_1PY#vIK<{}Za!8lv%XZ$c}nm30{ua_X%hQ}3E$1Nkj#XSIu_Yx z&-hR#)t+&J3#+u~GMR44Vb8bdz2p0DCnDQ6g&A|5`DRBEgMlvd`dh3v@~ElM5;x2G zt-K|CK#TpF)THkx$r+;4Lm{twdl*pIuZN0bEurv%>P{K+d|ygj!fv3x@El+Cs5vJ9 z*HP%l9u|)h6$>d>RWNNCTBPn9*~QxHz^D1i+B6^V`Ls_66zag7Jv^1;c)Pdj>U4J_y!eJpu&; zyF~c&{2Cc6c-072iLHn=djawv!KOyA$(91mF(X@8=_jdH$+bOP=7%k=yd~8wWT|2+ z3-ng3W*qq03N1-|0p4CE>I4vwtP200N%?q|u2glDTW`#_5jesvNL!|ji~u$PDD0IU zo=y*45Z@_{bWQ2%2!a2M3^LjC?Clg9oDoG!d_0qR6o%M)J ztrSBHgflEMd*_-TG(D_je6?$_2W4uj4w}UrJ@+#I=GDH5RYDLTt@nX^q|X< zv!{&bI^VS%f{d#6Yji(MWd8=z3K%(n~!<_FN}O-D)9Wj!nP$|Z?g8L7ZPd1xtZm0jYX)iO?w z6>Vm;EfX(M7JxeG0^Lp`o^5hRaSFyw~Ks?tUdJ?=^22zVpZx{`2dp47vNVvuUvH&%$%~~efFbPYgx+fp|}C)EB-NWiau8~ zdH>3(C?)Rb`tc8eWne|q?XlV{j`@Ea$X~eY(~rd{as|w44@n%kvF06w zB$V}>b6+y_H<;BTpNLp;XBL!pR==yn4zVV`QXAQVVprb|`;!$tgmev;F8$X3mB_Q? zILN_87LuHYWEU6H{YX=?WCWX3n|Y0o&=`AMulQ@2|I1X_g_j$EZ^Ayg1|U=PYNqE|B>DX1bg>UJi|gO-@TLcU8Lfm3wo zla^ROUYa;ZRt##aeN9VFtRRm>A^x>Q)hz6o8qk&*fjWgzmx4o-w zJf}@IxU0sCv%`S(Cm1Yscp0MPm^NmM(mDtMg+bEdx3L((KLLVlQPWgA2h*<&lK0S> z?6E^I`L;nAf0%`Z$iuEhS)9Bei0I(w7eMR`xK|fq@qmO*jkt1Nb6|aC34jDz506lE9&UbZKO8!jGvxdn!65Mq>>W_Jb82G4D{XbwB2sc%_0*s}Y8Cd9odf0L(EMI?V2Ccm zwkl)}RcgV#!nQBxq8*ZnQygnB)Qi)YAW|-n?M7)Huw|&`pxXhg%B;%E2g>x_^skey zc7}|+MuB!Oi=s`fo=riuBxHMR(e9O~6Sj?+Y|a&J-jCUWm39k04f&I+_dUhInpZ(G zHF43);PYfElQq&!Zb4BK?3vg;9<;6oS%o~!zmYr0SWEcUYl#ukl#OG(G8N81l5>>i zE+tqnfc|kF6JBJ2V3SeWW|iC`O>2p}0W0vHjeb`#8^i??{Vpf64YI||L|%U#V@^)n zp!5U`8&%suD`E>U3-uo;g!Z!mv~f~agyNv}Z7RLS*mFH}bDD4ok-aof{iMKdk$-_@ z&6ZBK>k)zim|QAaXylESKGW;6jqg?WWZ+dOGe1~x?Efto+4jV z2-VCrKC@(mAMQYn8Y468SCplv%)IP)==?@eSGqZK%^bLRT?7 z`mXBFMliwc&t7h*X7*Qie}sQ!A+mw7do_tXlZ~v^y67}vJQ_uw+1QJC15H=R5LAJG z&9V7cEXDR(uQOZ*bs0=ec1>VnjlUq;s>QnmJn&@MsLIV51fy$HxQc;-78fgav9`n0 z=O@-fcJb4b#X-@PAc1_UZ@O@HTCrUw<$bt7a7_H5wOdWh5KIreS0T%8hC0YL2{M~3 zL>rk6-|y6FLMr-tM5wsayrSaKA1dtD?@2%8W_m_4Ia=xH5Vb66P6e~?gO}alZcgBl>VWFDY=_0w_Ydx!0qLw(5%yt#uq6%plMF$G3$U!{+u?aR=Peahy zvv`xs;Cro+_O2!-QAV>h%I>4uTEu6S59yn|#mKpAF)LN@Qmi3i+1s^5fwb49;^o-< zzdhe;IzQn6#3?aOu1W0JH&!m@;4Tid9(&vrTA)IJ1HbS?+pL+@x zpX{qCM95fsk6Cgt{OVRwgNw+85WBs|%tM8Wcx{j|eOnPb4$oSGOTM0B3KcBA*jK%q zek#n|N)%`esD2Bc0~1qSKr~Aqg*N-{=YBgIv;>3Zj+OvHb=9nRSnLQ&A2of?n-xEj z=cJqs(7tX{hE^^=>brk<$;JldVCBPJHEI)=mCBK0Af|?y8&)?jwY8^FzOMFZkCwo+ zPI#-OBFS3jv+gbf^J}-Lg<(YMjoFh>5#Vi^uYG{*^DXNL{9aVx-N<_5+KqtMzF%Z6 zOyc%f*}~`(cKtoeP&ap6dgaK_5iS}COo3q7@}GA{p7-fz zi)qRe;xJ%CccIs(C4NrB#j(a*Qmg+}SNm&E^e%-EEcAcP_SXEQ~x8Hf=`=g_j)wnB5l0wmYDy_RHMP`YTi($*7_@}WG zl)#!L8XTnpWs71eG&vt}B+=*!o7<8y>ec2HgUJSE1 zF-7mOA&fa@Lci|@xnKFPne(uj6yjsn)*CtZrEG=kqv>}PL~!Shi$@sP!-WGNqDE&A z4SQk*5obDWdk-l#wbJ?pr5t!vxbRR|Tm83Zyz!T!!_fNv8)_rjtL`bcqU0Ayh=T(K zi5u;Nlx$oI)4(_|?d+?B5WAQbH;SexOuL+1ebd&W9NsGIvJrY=qQTt3xdg?wu|tMz zW7nB5gGQvzMycqR`k_;&i%wD~gHF$+wg^+}+(T9nzmM%SQs?c-fH}f~%fK~m`0leI zZ9ICPQe^x@##gbGNbDlp+Gf2e)R`6Su31^pf7C17HXaT8?jzC!+^)Dr@?9NSRrzRHZ!I%A`dCu^_R8LP)WdV$t}c5FAkeRGSY-zF(y7W>U-WuDC1 zxabXrLCOt9hSjOuNU&k_qDJ5KZ@(Q8bB-2Y%61UjAs=w7ep%*ah!#J}(z3q{)5rEI zL<%RR_UilXyU~4IATnms(Sz@`f7dwiFhhN}Yu_-iu=2hf zRzzHjiFa(Xs*dXuNgr2n%c#^qIm=OQh_}nwbc4KaS}cE`y;}X1)oQ4#SQ$7Bp?;gp zQHrwpn4Kj= zQO07baay-AI$10|WbIWORZI;mH>SP^>xg4H%MIjAC=5mVtu^i1(w+A~B-ZS%qF#l|M-l9eTVo?;+a zyc|hVzxh;%wBQlr;FIJEtM{6T-~@ZC5SNkjt)-X9=AjTyaR-!_{ZzR%uRh#BvEEda z%FG?YY_ZCRq4rewY4JFR>sgcnN)4#RMSGrVlfgZ!hI*ARHGW_()S%6ZxLNW?h16fL zWDch;kQz2gW8K9=3$^$`>X6Y&kj}G4vX&4e{FbI*;~knb*XP^`&J4GILtnTu-%NcY zGp6p@G^2>PG!tnpv$o*_LEvc5#udJlc)*m8rLFU=hwP8(wMfBs5e2g|P`^h?#s1;7 z?zF$f59@o@l(xzylzPynMzOotQ>K~oaJPq-J-EPDn+pUQ-D~TZB52o|n{3)u8n1g^ zl02!)oOY#CmpSeJ*#?d|a!^gFF=S?bvXqlubE6Pk+hhL1L~H6e+ezR36tRc#2v4wCX0Exv@M9=n|&q4CRQ(MSA+=IuG*9wig237MDsHz@zwBZ~OsZ438QqeE$^ zI}RQHvk)4Dz1&Bg4F!p8pQGI?nlf}-95jd29UrO>utXHU%8ll5b7n5PUMZF~TV=HP zPm(gqBTS+~9&ZmNp!i$lqkdX!wRG*vl8%iF=2pH9Mq~>%Qpan?h)IqY{;Xn4LFi#E zF1D$1o$5W#2&LN!uF2EZihU|5Bwlca@@O3U@M%6)Y(tY(aBVK<>6)2D+!(}fO0`@9 z@;wYwBywV?h3{b^a&U@RV>-L`>M%C8+X}AEt=>m<-923CTo&FVx{W;AdSKYG68qt+4 zm?_G)VtZ)Se^pU&sGXCcx{EC$&eE+ZcopPNJ`SwweYPu$l!}K%op*#B26UUbGL(8; zx>DP~PIR@b;Mx2kXtPQXxBZ-RyX)$J83x%>UxgnZ;Ul#8MEO?Q)R3V_}Ay)QMK;)E^VE-I3^+1m}Oo*C4YYyJ<;ouL>tH7T3>RB zcZC*ftea7I#{Ssb1hwH)-SPKU%&R-zv6CItqlX0*yX=CS_^O;)VAXaeqfRc=yz8^1 z_+{GNtNB=5gvHvwj1N*ZyyWkn@iT4RcX&#_PPe$*Yupzb{Cs5}1YPC+Z%1sHi_*mew%M~UL*)%5|dVv}M$ z*~S4a{w5`O)b5V+Fh@VRf2eFV|I36dR_@#NuHV>a z+d$aYeDz`KjTsyiYy~gN{^$gKN1fS3nNI(El z-N$!ncMBb<`*?(YcKjtRafd>xg>?;el=f)#au!J4WG6aUqQ$52p56z2mJprhDaNjT zI|XCxU>S?SVCq8t47M*VUtpHyt@#A}8E5QKVx1KidiSFU+kl(|w9hrmx$PdksqW4> z9(}S36nX|<--hn+l}Msy)u$PMfqV#GMPE{`JqIC>?>f^gE`%z*#>c(#ac`eINb=7j z>3P>4pqPCYcd@pwCH#$kO%#Q){`RY`=?@s?H>Lzu_AI&bxd0dan~hhEva4GP7xT4O zFIw~Q-o1MR_JC$5bHo}QLxMBdJw!-ut({2CvveaT&sxEwd6h1$ykx8s)X={~f6p^k z-`X;3r7R_5XB~etLN^?PIr0TnJ@>@i^Ju0&xNR$2h0@z`RaZANwbCjesTYG${;LqLbOlALs{e>uucdGmwRl>!g#nQiX+MgA|PD|P=4sxaJO}+{QUbAyaXWmp~tlxOb z7Z`i2JV0tI)+t+N{et}5Ow-G(pYY@$K!1H-fXg@xwd@wN@Ye8d+fq-9iH{wTmC0O) z+2ve4S+b^+i?!ifeV9w>w@D&~ZJ{-0F=6TJ_+sd5@ct<;#;P^wI&VRJBrgls1kCbV zu!$xrc|mO;qpWH!IXURLnYiTKIt)zc=`Fq`T)eU-+bCX{nLUjBGV|A581zyPJxRxu znA=we{>c?wvqrD9E}Z7Wcm(1843_C4B}q&f!(-d^MAk*LbmUu?kSiGVY%M zo$$hSK7Ev86CD-o9)$EC+4UPQr`znOjDJx3ew4X#Re>TQVn~GHM5jKNfi4?Eq?Mlv zK3YE)k*4nj^InCWvsRg*HT{;I-1akyd?x9>HDYWT?47kL1JONd&5H2QQ zEf}jZSc*WEF^8+rftEtlOtRKcYst?T`2maU{pQm}>NJ5VYJTH~PcOj_>cH`U3(Ins z$);OoUHDzqe?=~p8;T6Q5LX4!;D!a2>Qb(?u-V}{j6JvKTBj8NE@kEXR;=f=sJ7;W zr=$0-<->N$Px=*BztJwb_&phZ6XDi?`lb!2ulbVyMSb{BMu_EaDfY>*3EUU$9Obua z&QT0I++_AjVnn)ofb{}g0*S@G%!*9^V<3Emk;EQlWk`7vPPbwxp=lO)uw9DPyp>d{&}#*5L8E=J!= z8SX>hkmV_aUU{3zT>#yRa0_7vnZn0bT-1kON#AAR9&POt@}bLUS^gfFjW&}g!XMBl z`ah|g(8nx5Lo)IAE=9{S$D$#@7%CIPmq!KR7WPAed5ul~(^@?9v%&H$<=+`x{>C$` zZa7Im*jbf|!9UpR!TtU|7}EFsTk?H_;Af>mDB`vyTk($HD|HA5p70U{Da{G|Mjh(NKnuu8c41THM3A1egeOuPn_LDv zmNt5J^^Du~$x!SKE5zK`h;&E*n^wNGx?ebX;MW3;)R{8rmDbjmL4DgnL&iV0HSuEb z)pG%D01XN538XK!F`i|$$Najp3P$g?3B=)*CpAcJW2o8@(!YuI4sRGvLexs9jc%?S z-}`xbB$vs+hy}}fE!nO5$dSyCOpiFcwu1KYSDMvNCvvLI<_*-ZSa*Jno=y)$-?H*A z`%Im8DKAq|ZsnfqqS|n$tkI2R=R<3lj!Bx{=3Rp37cQK#>+wAa`w4!) z4Em%}tF*7>%zkB$7ZTucsu#$XSqr|MVT=aSzYKhCU5r=#YYWTpuTM#5xGv-6@Ru(B zLC*!b@tDost&?<4a}B@d-?0zFt;-`seA{PvcNr&XzkS8vqCL)-)eVHI)FxJ)GyS&y z0?Yh%$`%5pV;?fJyP_{CKaT0$)@_&C^9R4MrgJ+P?Dd36*oTOOcqhIib1lIg{|Oy6 zsv4RjPeEIiu)q;0G~-Lmj+fW0eL$a)o)R7Ct_DHidF49k_R~7mJ_a_R}O5kjQMelYmM$k&k zEnV{}satxzu`2^++de>I1}J=?Kb*di|Nku;209v-Ae0R}PuN3)uvxzvM1N^+6*eJu z6X9L@yzs7?3369S$-_Pkza%j_8np*@k8eZ7`7`_863j_&N=_-pu5e>t=Dw`j&crcnyo04eP@4MOb z%{^aKrbHR3KY&+HB~V_UQ@{Q+I}D$xij!@=%4R#iCG+~zxQA}5os>*>eW)IJ-q6{z zrXSFMiuk*+{!o3}gk7n5vWE<$KWcr0sC9P+@U>P~fIQ%GdH`#y+xPLRKKwfK;U9cR|Chth zNauW4vso91Knxo2q()JxqIXU|(kZ(J@o#8X{fX4@_yz`_Iz=)&{}jAo$b^W(VpUo{ z!IGC+L7vcru0j`t8Gf22lXlC>FXe^4a!Xf{`!_4x(l&kl*K}x5|9`5z`Aa>+y_}dS zgrsk1ne{b-TC4cjiNk8GuRPNn3V#>`er49j#I934ijFkK$`M7)>e7>MILW_k#?mwS zS^6dZSTozW#!CIpeKHx$*O76lqU?Ahtj8uZVbk+;d`M)rXntyo4WGy~YDB5v6Km8JfgPbMq;8#8@;x~LkyF1LU(KDe- zm0kV)LG-0;sf6uWHo4OJxxHPJm2$g`Zg>^H6ll704^QooNWUhWI zD5+-cGG5caw1!iv#gj*Jy2;KWNiMLHx`h$VbziMZNl9OpnirN zQ2`3;TyWg`xAj9)-=H4?p!qQ?yKLFyWM%dO>ksy&QN;+=Q3H^-A$`4D0g%=bB7e?6 zTOR_X)4y@Xlm5UVTg7!Vrpow446C}i>`uFn-;+MJ2qb0H%kHpKZlfJjoqUUqw4No^KJlp8a9J;rC#FAe+m>H@y{ze+#NkOKF^L8Sk}rk_iG@71B+B>; z565~@x#+N?%K}$mN2&zNNWz#=8l*fqQNaT@js;}lo zc}^}2Vn~zA69ckoiH--8g@RA0@Fi^+q_bN-r%t@6BahDsSP zDI2%(x6If$i64AU^LK&55UQKpSDpU3>Ldi8TU`myMq-MjB&BLxqw{xaQqrd;R8G~* z@Lk5nX`~UXB`NLUo|wP+`=+Y0!*^-%IUKT01v)f-d}~hhqBZk!l)I=<{XtnQ$sFy< zoZgzxO2$h1d)M#W;U}7?2Nf=ho7h$t|H7eHT4L4b#u22ju`$G7do(scjD%mM2UNQH zx>0gY-Ka*EPypQ6Mc<6wBh|0AF)>UsZ<5~7Y&lMf`D|T+3nZJSVq|`y3~!^10N368 zZT4kNE;3YtmH%aMrFjtjqoNj2L8M9Z-BAbuypf9P?i_tlbX47)A&=gEbL`I0Mf$$F z9Ref?8kg1Issq3`@?NUqLUWHZ@>Ja7AHNd(zLbrQYyKz zK~mJj+Sdma5U_vFQ7~e#W>Sh0pDN@+O=Z#IAF`%QRTpLj)Pl&xMvx3#FqX2|p3zuCQ7$_vW5M^sCX0rwEsC_Ujs@|{>`5sDm8NaYpwjJ-A2T7oy3hA? zvB!A|Me8ExdH6X}?Nv5aA^uc__#kqkSbg87zHjC4z`4*8=gD`qjv4htwY1HIbcvu*9p8i26b}_N&xa~jx?}QK89a6x@xkFHm@QUVf95@D_5nV z*tm2jW4W0W5;EVa9P2_CRQiYw*iC3`9H#v~PE|~bNJXOx0z(%p|8n~xKF7L)B zsR4XmV5%;aO;E{B*G=`pe3HL6Df#UoHfK#X6G8PFl1dbmY${~h3r*qZ|4ck&8PcfG zk4ZdgiFP#4W>S*v3Z^Ef@tmO}i3Yy2{!atAtpCl(bKIRym_$>JB3l6^SqzR!SM?P( zH4iI{8-SnBouS}WHgS_=EO&-!D&kybO^1m?+$f8@S3gq`a9rd@{Hd{Mz-_8+RQS{X zVjjxPX9a$h)2He&6g{bVJW3yJqV2IJ(^ZN4{*e620W$q! zes5|5xib0(=~z;5bH4R{P>@W(W5bO6wq@}fJ)FsU_>Y;Ftf<7($fNN3bhP&TzWzx4odfDWnyJ6ysJEgk{nP&aart#5(ql}t zI(bGhZG_-JI7kOM%`guh{qh%C(;nl_ob^LzT&Pc+F;PFAfHsa_G^2Qou=B`#ko2{1 z8sguqbf?`4?=qF?e>?md^Rx8(S8-Z6?>K}+opYW_Z|=iSw?9|nqp5ECC2kvLZQdMn zk&jDHf`&|yVym=!|3%&AFm8p-7PLcczY0;EIh*5C^a8;gGm?vic#!`&N6LvLAjlF4 zw#*L7h=8Lx54UPY=@xx>T^KU_x>PtWyd=*!v^v{Zk{4c*E5Et1yT-uqKaSZJciRZD z$%9~t?~%Ic7e=qBTj{+pT2i+%OZ=11%f&Ud#G2S=4AbH_lQX8p?kO zrf_)OCl_k*J;T@{OT1oVHmZRwM1MorS7eR@7F3vmpK$If|*N(&GPCZ0eTvrZe0>YJL^%bug(N^obc4j08 z#-V6dFLR^CS5T_1_SH+Y`1j;#`XVhZP6g<6-6xkoQ@T>{2)%&s68#6vf_@U_hw}n0 zvla5CJIvDyD*Obx5?9Xm%fJckK8)mO7k(piuwml54R_}1E}^cSmr-%LX(tL9PkIM8 zak>8+Fo(}y+5zWpo|^ZT#)X}Wxo60L(oMvp3VWjWJJ^#>svZ?+2{8w7?r1qdX`IW5 zj@N!cm3RciRvk501dJ6S30hhod0HPm<5FFlwf?mD{`!iiu^js3(&+oZUKB2la~}O# zkPZ)tt)LwiFQ_ZFB0b)+z$Bt(0K+8(>*xd_Wu!d9r6E$n5nQE8>^sD10@EjWq*Dru z_XJJuDPz4G{XG&oL2F1lmOv+0 z<(4d|yCk(1o=A3ug&RIyV0y}yK1REnrP(s;ogdoG{=w*OSOi!d%uUUcA{E<=swT|L zQWe6Xxo@DAOkL_qNmTefxPMP(z%bUpGl3$9`l#l}tCwjzBd?}DWY^^7lJxsH;ZlGQ z40k$I>2#=hg90QDbC%?=_|I2andL-Z)`@mje03O${Oay@rHnGTVCm1uls*JvI!?=4 zA|3P?q&s5&v;#mKSXt|{5FDQl){lma_p*VNTI;OVLddwPL@1J-aLU*%1V@WOGnUVg?Ol( z{#2w!ZntkYjzW-*)P*leC1jxB^x`$2Dw-rK%_ZHdHUN0lWwtatxe_}Wx^tm;u#oL| zy#mQtE)%E?%nNEEtbGGh3YgqO8(io1+(+C_qp;N0?eW@ ztQOZ^M4nhvcgcmQ>h^HU2RW(%o;q}%FA`W-sN(I8r4Ex4U5iLuq_AsGxu%oj8g~)> zwy68tifRPvrX$9_j%NzNt|F35ZnQvv1=cJ$IfcN;(Q7k+D!0BaLrS!h#}d99AFET6 z2`NJ6@E&!*E4MC3WX`*(@`=dVeljk0dCAy`kp89Ei7fqGLaOO!$4-nQUgn7l^wF^s zOQM7B|1q-U#tzk~{5MrQ(BmH^-Xf)Lhy3+KPvAOZ~T0*BP|D($(NK3#{=1Z903> zeCtvk2itcleJx-CF0Pd+PiQEAD2RFwgjcD>;)H7~?h z9j@kumddS-o0WJ-%?K6~u5CsizU0!LlFD*sm~YQ9{EAzz_|C`zai|M>p8GiWHd|jo zB5bN%rCSfna0K=jI_)SV3AZ>RfG^oSv3j}5YV$ z+2a@Jr^k*j(NDqtpga0_dMg?G>GzyzJo&H#mgsV~`Kz9Aq+t5l)&n$#YsoQub0>a@ zT_XK!(rl}!(tFidZYHle#z-6ioOWgr%zNAh^cT{34!zCqeqjlCml_7v$fQn^lAdc> zzre~=6-Z}1W!5)I=!5To^>}x`^(Z%xKtE?aD!7$d&vA@V6Dp!pgZ9?chD6wI$BqWa zI1eH8d+}j4TwKolf}@PxeRqXsmgldB*_#tul9m4cOLCel$;q-Lzbs4gd|8sYEJ^(= zdmP3Tqvvz>+yc!VvWym4_aAcLZ(kvjVG9|2`RC3YL~nC?trKs}p}TZ?gxzUT&a9k9 zNOh4BJ=j;3xIj^9zjM;M^sjuz=oduj$?w;Fger=_2^k^t!1r%Iq=uk;2o2zr8cHQQ=@ zgf`z0EVw{=i0g!DFZVk4W%~A^!ssF#lOH|6ZB@Q8ND*5NZS@IITYt3|xQi{J)Wz|2OPO zQxcGuA9LsbX6dQEK;}b;;rnWMO1B z)0vFMvzX9Jdnvzy+h&AMpw*>M!0DA+SFiJ8s^~6Nhw${#KjIDim2FwcCtG1I*d`h% zXWa%?+ep%))eAJ2{xa_*zg=l^lOF(Y|kXw_wrPU^d@EM(E>jvOU(mzuWcz zz;tP$e24lt-yf9k&p6*5duoiVdb5^m?iT3Sk5PD$^(BCX%+FeaH2~W{v&ewI#o{)` zV%7~v(=TVb=m8QV!|2HuBi|9NQF&1U4E+XA?BKYGLr zQS8w7j=ojfe&~Dn>~Am2@2-4n5)xoPy=YlE@69Uf&4~54ci&9eRgXQ?(La0s{t3-z zyn*-GPtrdWHh)}xCzp0X}jOpKMOr7m7ANpSBTeWoa-zNV02aTt1BzZEDzKs1w^5Z^w zH!z19&yoM@cw(JX#GD%o*9vt#uS4c4Bjsqf-yA7NoBgK57cG&G4F2Jbh)aq+_ zg>GC9nhM5svV~b?iz=;+lCAV3j*Oi8&oFi!J7lyS<1RI>igfQBlnKg#UmLbGA~f%J z{_4wmtl}f1v|Z&sf(Sz#45>z$$LFLMw!NhBK2?_@3+*sIK6WV4O&0~Lb?@aw+z@Bu zwMSFek>Hwnsri)gSA-ab4TZ#hC)$TIJ|~=}W95x2R8ectKXE_lZjZcn6 z`O<;P#F6eG+GDh5;c^qHkW-p8Kq40$yavuy5pHnAmHQGOv3$%5AT&iFkima5x)+wx zc7Kt-Us@1 zaLRCCpvA{fA6+E3JzpvUw9}LH!G>IlP3S@ZCbEzB54?@fo%N+{2dv?{dAmtK%`1fu zyld7TA?Y3JvNN<`7mcVuQfU;hAG>MqftxxCcy3uSiywW)z@FU8_9&qnP%HJEf%DZP z#>-OE+mw%VNQ>gHs=_*8rT$6If=I{m7kf+FC)UaUxxT6XW$>l{dij4_A+3y$wY1m% z>69O}UI5fltv<;s>*X5Oi&zY?Bf-~oEMUbvE*X&+v0@ta^M$tV0zLZHO{-c7d&QL( z0)IYNI2h!vA^ZH_w|)jKH8oX7)9VJ|i@kmz_wDZb>16)w^>ak|89kDkOTvHFk4OK{ z`uYF0eh?R= zMWk<>?c?Eb>(rpX0VL$Sqz|#O(#MXOqZ*|ExA4_=KiX(Vn{{e`epceSh=1Z^JXk+F zS=?-hWx`qRDGYrNkAA6DPGhBS@jESVv$SWd%JUg~vmPEpG2#FE7aP;R*l84Fz0j}h z-ipxR3VOM&O5NxD4f(b3yQy+v05begtaGLGM)*Gtk&^S5_hfqt^{kinSaLxTYvAZd zExx?UQaKX`=#tNhm>lHs<5wte$;iWe=FI3(-MwaAHV}%!^E~Ak1m<$vj0?gIUzH%Q zBO}}OUu5{>kJ6tFmPaFEpgL;v*g(qiSRykgO}Z#)S(sBiG7WqiS zhCtBo-q{m+fdy10vtDi-H@dZ*rx}MfAK~sw@hsD~p&mtVG<}_sImRgZf<-l#W#x1M zXstsRh&bgGJJ_$z@E7O#<&OMjdQ!J4Z-hs`!}yyV>9_XJ$Yi=1S7tKKG~RNCD(8=6 zX_vK_@x(f@Js5EDl1O{r6(g<5GGuk2R}!amhtaK&gvRCmz0s0>#-KyenUK6UEBA4O z;j7NS_>;v}lNpz5z<%wyaa^d8uu7W`0t#*C0qqaIGMrFw`6jx!4$=6cy5kq>3+j$v zq?gtmpR8Z(@{hB%=Z0Y?vM1UMi`yINnzMRT^H9&#>d-95z~-ToXSrWKOdlmjthOs# z-!JLu-A1`is;P2hXqA|0~)4e#wwehq|5ELZ0ZbAtj)X-7-}^s`n^2vDTgH zeWAQJ17GXA>Rp5~>F0Q7Bty+{5Qs?wa9)XL^7j*2#_8}jRBrSXH#M?&8J&!cv)>4B zs|+pOM)3}MA(NXS`kXTsOH;5=YyKqoh<4 zH$sUsDVUVgY)6u)oxVJwMKq4PU_ZPi2TbS!y14{vpA%Rs{vNya({M5^Y0pa9ET17a z7Mr7K0F(4J;v_`qsxA{W+HSw$CBJw>45{%^$|hw$ zorvXXMW=HX4p$u1Vn_Mr*gYu`MuXS@MM0Pbd>|&ejhQ+6js4^w5)m{|Ge^k`8qC^l z;;^@(sqcI)cLOW^W=S|8L-suajQo$uO*)2dy~RZd>GNC&UorLI&uBRwYoouWPsyU* zFwtycDx#Qpv!)}KaYaxZD3t^!8GCm9!#{yPLxdVU$AwIK2z1ch$Jqz4YdI)kC>mms zZO9fnjhTVit~>^4ls0H<#R3%{z;ba??Aok9e8M}T4vxBbGuCgxXtTHUAlOi*;wH2w zefyK*d}9Rw&+m`F}88mAR*jLoI&{VddvOk2MN(9+fhpPh*;@f z@-=YKHp+s^Fwc>UnX?dc=sC(eGB%}oX2~T%0ef#H>007`WC$#(kN{x$f5}Z6W0(G9 z%S_Z_f@}b~i4M#P#dePuJerjQY%%8nl43r#ay%>hB>Z1xnO6sisLbF#GDf6kNTp8a z#oq3ka4@+rFCce3 zKv)q^fi%WK5az^}Q7;lBXd4zM#$P9Vk}L|UZ7K;B;3|cOOg38qjQ304oILb6ew48+ zK_cvCGOLWfJYzAH{)&tY9-GgzSv%TuSu-YSar8T${Fktb$~E>S7kIVjX0mhY!6Nx4 zrXFNtCq=4(L7I3nOC6_2)70v^p+ZySNzmN-bb>q`REOc;`LzBSidC-r+8C zq&>YtoS9T~snP0RS0g>I|42tWY7z#lpJ5O7BX_U~q%Qhb}c<={DHNm zf5iZPk-7xTlwmqBneLEm1ruw$z$7{x%*|5f?wEa|&N-}46r)Q$hcbev^jVP#t3l=0 z;#bPf&`vlQknJ@AQKO&<*#?8+e*)PTf(VWxpyThvm+BORt!Pc*l|hXxHI@f=aH)TT zr9doonBTrVTd_{La{sjf=3E}u+tXSc1-Vr5cU}AssP(9ab$tN3W-hkeEUs$ zh#a^*)-*#Hp4|b@29;Nfua&+y`ERFF$x6Qnoe6Jk_wLzY!qRskFNA?3f~AiI7oZdP zh6~brx}nfky2bMV)b1_A9~dS~*kB2nK0Qp}>U~@nm6ut*?6TZ~imv4wQ+VGJ@>OUf zmhV;v!NiVO2ES5xf+dLc$x}Bd5BID*>Az;q2mf1sS&ds96+N$@MyIUdP$lHCsn z6tAGh7%2U>!Rc$nyo!PQr=08eTVLb&qI82y=gB74f_`&Bz`#<q~rHr^leVHQ$hPl|IVRvC%u&2-#Dov%BL^ zEh7)f@3mc8aALVC5PnsQtrBdk6qi!nX33fC#jD{s&J=UgIkRJXrW3c>GBKE>@Bd<> zBLHU7_Zme!6Nn~9&fRKH-Y=hK`6X8ik47}(M4~}nN$&80)RioZ(Y*C8JTr&pc~AdD z{y4WZ)O`76rtsh7pToXV?8k?+y54@CkT+f&KO$;8)}1TBQQ6Er#F=2cw6l_S7Iod7ZwAyjxH9`JJwfDcN=B*hI zE>NPDNQX44*pGn2F&MR$5xfEmFH~i5AvZ>wR6ykFKS^Gp=gg~Y=5l#k^fE-ChLao{ z5aUE<{H?-*EG<4>K9-CvF-pD;HM1Icl}vspRLR(BW>FB&R?2p6gH#%tYTXS106Svo z=`uKVrKY!(cB1za`vziv3RK7V3e1O#8h+&xPPJK(RdX&>Rr=YQ%xQ*L?U`;Zhj7y+ z+izS!R&`3wweJ&(OxA8iKu9wXP_!4d4wp3^2LjT~^dr_^YlL6H81rhwHx^U4D`1+& z`oJGH`YXqd;U&bd`N`4ZLWGP!p4FTMvihbfv>=OZ^!#N$GK_o}B4r$EtQvbXHBrcx zV=Ow3X8~fC%uejoEKf>BwXv6Ajl8Rnn~)jY8TadQ>H~(?M{nf3W4wNq9O6ZVrc^j= z)ef8C`)?^Zp(~lw0-IV0I?llktm`VE`wylpW;;sR14Sa`bhB7^#t!Mw2VMsY$E4e= zKUFh@U@u?^nT701z#zy)B7sD4PP(;suAexbT{St$e(f3Uvt%+-Fl&6P7MxWut{>7QsO#0DBl~Kl}*F zM7r_aX2F_(s)HXjFeb%jKsAY)a6nTj-Th#b7@GX7}sAF|}l zv#-R07XE0G_?zue_`nj@_ZAA|2|hTKCpy*@)oqn!E(JVkCS)gJ9*gW-BOY9Sf3GnoDm3_2iu|c0@=ZGg1$VCSF z4;MSw7KPw!7)@ve2XQ2D6b*xFA~aZ-2VrbygABm|K~nN^NXjL`VL|P|Rg?IGxmZ=J zc^$PfZY?nsQ^#sAUgxvvwR>tFqh0R(^p zI73j+;-L8Zo9j&u+-ARdpNL%fDgc=w4 zzGKgSb4t8sa4?r%Cm({gr55u?wy%K%<^k}7^9i&1zoIah3Z&jgxUWpC>TM1x2+wCkdQdD0WecxSeAICEE<6CkQR2g*$CYO){zUjwT-}1W zusNSXB3{?O4WHa(vyi^W!myCp2aX*xBmh|HN7m^}WlM~X#(1h9pQ`v5;m^b(gGtHa zvH@6sEpNsL>U}p~K?I-m`Y1TS$Vcg&8#YTL)b)f_#HmfO29HaUYbB%j9d(|P-m#~_ z#i`>|jICpEAD8Suc1(?e)&hKdii)f&NCYm#RVoz&FgZg3Ed&v}zAg0IPhc&?d0+Zm zxl<^Yu@aOx=xThM73n1i=;!jky^qG#e)fv8Zv$NzK4!Al@N_I95k?tvXS5t3m~3eu zLlLDX(l^rB21^{?SqN(%ihalXk#PKN?6l+S!~N}p@zNL)&C>I7p__-~F< z6UcFtb8fFyJHlfhG|(jh{M7eVJ_Wgqe>kHAEk zenL-j8uD{=7+v$MCqF{Y_3!Dd%l8Br6`j_$?|?m$uRqx4Q5g(Pexnl4yZ2c@B94Fdq9;4HcxF4HVkdZ~ZeL7`t{} ziM5&-L!GKLBF~`|*@aT1=zLGEa$u&Pso+|VUp=Ghptdu+f5lq-b*kWe z^o9MYFKv-}9;2SYeVsGNen{I{(Ne6GG6;uz5u1umO!bmVw0IR&d|^jb1DEj?Q7fXl zLZ`2h!T0O_hyFDqzmC5|_*eaF+H;ZSVqWXBinwL!G~KL^{laGViwE={OS91i^iH#k zXhTb+m3Mg8HYJv`{c$=94g6{;A~EM21jvzw32pdrS$)Q*JpS={gdoqu344q;VwTTn z{_shi$TfS_i88t!i8pd$?A$_midse*tp`Sm)HEZG4T?C{jBdAc_(czA{Q&kC_A&`a zW23b!3ozd=8Y*-{ij5wgcjXcMYx&5%zZ_HJ|*FcN6X;IV6vHUy#tQ~}f` z5bl8d093>m)<_DFJF*u-;@>P#GK!EgD(Q)J0%2N89L`J8S}gN~XY;9s4!{5zyVFmb zwGC)>numF28?_Cx-_d;m0fbQ)Bqf@(mhTcj1V~p}k%L1bA?EiL@SB>fCySd^Ps}9!-ZoI5$#}<)`bWzTom+sup~e7V);@Q z2HHo@hfhYq1EmND@C<%)D>PjQE>ppb7Cx@tiFXS>#)h5#5ew_ci#L`~ehB4dO=-%> zr|kF@IC+*ztWQ{UDI)wJ(;Zj0vX>a+dzkd*!d_46k36`9*|EQ7{?)dokO1CC@ilve zuc?0N*s;xv)xM*j-GA#-Ul;P#8Q324?1hr;u$K5H_`o`Q6s&#`V|kDD8I*XSDnt%- zGSiX=B=B30z_)S)cd*N~EhdMER)J{#VHI6X06RHLp~P-Em^%FzKN`Dr?<}cO3W)EA z1Clq}$s2u>(_*r+H<_aZ698qV_S=2lu4GA;I+v$kcBNE;*1`{5ne8WpQ)6QP<5x2* zEzWHYrKQ0=h1?&>KdiJg2b7kk*lTNP_*QIZbG0<^KcWFK0w?MUChK2vc%Hw@ydgiZ zmx4$rg@_zGDGLOjZ0wrJH^nYhXjz;!dr8uLe3@y6P8h8d+6sn9myD7VzU4zy@0w-h zbYBHNYy^ear-HQ-8%?*u(#Y-n5;pMZPSb03$v=aeg2 z%g@viL@{}1?elWwhWCb}U`hIrQpr54v=*1>J*hiXWqH?vAE?6CRYZ0rGz^bhG~Lfhz`Dkd}VQu&kXg{#!}w4OiA5dhExmBZVl>OlT}i}mVB zueZL-SyXl2zV8Kn68_hrL)xW+bZF0*qzC@O_Vh*i3!9XQeUZazj@5SV>CfEhBrPCY1`1XR}g@I!T|MhW5EI6A@IsR<|0_X)87^e2Zu>PQw!g6wo6 zb&`oaG*Y;ut_d7BMHWWfJ@_PB@iKrv`}+?RV)?>j^hb;;x1rR_dogo)Zmg2k*!OK; zlryj}B{}vX&N=kxGgx49G?jZ3F8vpg>feN0vV~-CekT3Y$DyjdGqf^0d1sDzf8sXQ z8}!gP-5s0|UIt++$$q8EM9(0HL_;9m(UWbkD+?r7k1>~z!OEgF{k(Jl86RF7cCR{* zohPISyZ)pA1UP;63a7XqOHKH4C<@g69fJmG8NCGt4MjQFNEk(9jMG_$>HW_6OXTVK z-R1d*)quMO;+I$^#TsS738+tes)jcbIX5@(M=t++`X+u>+>i_{-}N?fQ)1mTk+)^$ z7_f&RuOV}7t9rMU_)s26%{ABVRjG>b88h?DwQbVE%<<;hgX+T+JL4o5UeLS0xXxY`9rNX(L<+5SM2mcRs?;aRcb?*IV!axEBchI1qL1T*w zm7_#aiGYS&054c?QLCc%X4})7!VF*)4NgYb-A<)edTiBJ%CW~@)p`L@F#(zcwHgI& zRMe=bdz@6`r4W&0-p}{hdnO4NkF7oL@BQVE%-(CS`+9Ecd7kwwFTxd&POF<=ooI@3 zqubx)amU=1K!qxbod$()N=@TF_jc!}jn4gDsn{_C-L2-o>LhPa>jdKXQW@He;m`}S zt2oJNNZvT+g8tD-eoo1C8!NDAe_eRg8G4k>^|={=H;#mvD2k#_eAKJc z_5zh|sZ8mtwwV@4${Eq2_oY2`9T?D2d1?(5XQfLKM}EQ^Gt`6qk^V(kMmrm7OBf%L zSa*s`m}pLRag`|XueA6j-jK1aY#^^b7ALQnC36ncx}R1Ot{7X`VlTha&1c2tMBS#$ zE2{VO5-&f|(an7l`I+AVrC$%B^zsl&Z(?NyrMG<>C~b_R=6RHrA3H4lW#p8^#{+|K zf&f!_kAa$<ayJP(9|t{57ybYFcPH zV9};F%`Grq5^!*{6R@~$OL``C?V@jot8cpUAT;i#kDzD$=~pj3&;c>7k10!?Ga463 zsKS=%5Cd*)NuDcQpMjNmlZe5+ESZ80a{tE*Ub&O}x!&cQ>U|NPlf;73JQU>K%+Y8b z!i^~oFOPFt>>&2k>xttuy<|&XLG0`hLnCCs@oStxvgQk3WTO{nfvA~s@9>D8(ba`Hy-C1n~{#`!pv6_bwHcJv6p9Rbp) zm-)fntU52+0blJd$N*n^rWa{uh?t~|=>2Ou=U#$)9}*K>3r*qr&8%h}tJ#rVFl`vs zdtg#-8?sLdaRJLtvIM-~6zn6ccu(CXL)N=kc{WCwLiu-XEyU8Kv$?; z=FU$**G8f1Fl@j}viN$T@O42u2rI{&tS_U$BF%MnQYkYfMAfS8isYwH61oIpyU_Qh z*987;A^M_75(@XhU8yAucYh$15g8Qj%0RRWm^XYpR}Glf*<7O2ic9XrB_ez;+qLOC z;G`|-N^4+~IyFF@s>NGUh#27HOf6`DlL77Bf#@1e8o|}+&h)p1csmmeL&_C=GyP5& zjV=8vVJ7^q7au>7KNEtd4}Xs84S3*!4?>E&PdZ6oNC^whvgcW&@I+lp2S$ zY-8$_yn&)Ih$FM`%0+YJB!6XX7J26iaZ719+z*EQwh#WomU^ZZNz!qW<0(}U?*f1E zhwsqtl5NvqF5dQ-Fr&uCQ3D__JcUf>NRqJ?_JbiIDPsL|%yy;WZ*6ykKs@XgDz_yr~z3HwlF$xIn@_6W{&KCm?c%5P8P;dikzSo+K7*1dkQG zH-t{v9tTK5m_(YGCpP>6=5e<~sM7wj2p(R~MWzikdS8EeSJzS+>_q^Da5>QuQR@yra~0B~}^E^Bag)0r%BP1Mg`Cv)V=P49n&5oMpXdf#89?*7m;_WILH}Hyi1WYjqetj#LZ1oBfKNQb|>>!c|At^7OP&o}S2@^vcusg;)YN8?4Rs<4*D>B@L2D zeQK{r9SYL{QnwjWx5)w~%4>o%W^?h-ob23|V^qDe^QZ&*qSEKp&V}Nb7`Y} zo`uL1J2@-p#t)T{yPzE+zE?1Q_#cAr<7u5R=p*2ceRTd1C`;3}{ISC_k0sU&$h;f& zUovR?p{$UnIn4agM8;CZ`ss>9IFtYlI9S~dSeCYnkJB4%7q856t(?fY)gTByP5?wx zp7Z48r!Tf-ZSj7PZEM;N0;SE_4C}X%ojC;l`!wA1=|Gu#PpQ_5Xbq54HgTNnUXZbL zi6Ej9_kzRe{iQuYlXNY;`#OSQ?W$`g@jcWBMemfv_i6Y+PT6YsH!j@FO|)P@PI523 z8#9>JE8*=5`Al`ker{T*Ur|&eC%HoSXKdor;@`-CN2svrSHQoCz4{fa>K{YILbTf= z>_Lb27dV`a(yz3jUpf0z>Q_31f_Gnx{HC59 z2Ne=k6RpIH8oid#NaRw~RLtGf(U4IQL#Uwt)BQwSIw#fn@gwkvh!2~DcTjRaaH{9j zBD=p3+{*iIU@^$*V!zJ9WES$_jvZ8-U*khsS1Pcr4hVn(H)i>sKr_8Y2o~sgjLqh! zn@J(R;r~-P{Fk@P?(u({&{?&NNWY*Kc2=Gk{@ae(@c#`^%)qKwud_=wT&eDb_-~#0 zEcpNa?pbg81a&)P(Nea4D*PYAMz~iVY!JS)Yw9Bpej`T${s(z5AOYuxB;d@@;!;TQ zev)DUF`{n7nCq~w9c+I^qn)$KYesC96OzGfXdDe>BX^p$kTyqOZOMbh8r1)aJYl=v)4Vq~Q) z6w6SQrwFSDA#vGDh{)i)FGAxFM3xE(N`(kYam6+#soNPvPaYA_@0y-S7A=F$_?yo+ zpORgXmdx`3QMtl!r=9)>{4V|PB=jdLmysfha>-wE6h&NHGT*Mn_d9YF1@Zm3pr5<6 z;GD@&B(DVG{f|E-T~T=OaDTZ0*U8T&-rxKQbmd;U-cMirG;SFE^~JlF@6CQAV{SkD zO;*0|wcqqtBJE1Vz<#r%eBW!o`Ly!I17-b*%RepO2Sh3qD}F7E-mQFZ=M#6Z-<0>t z7yab>!N_C(0{MQ0;Q0SV`My&4c-FaD`QBaA^wxn9iTO`;BjroD4)F0bp0Z3{`@i%F zB!u`F8gh;&Mfmva`W!xrYV4lF&j*e}4j;xi+9fvDQSU!vzqwD<2YkNTy7XE2{5Pp= z7b1ws`8(m`&i0$#da@s!+&zWy?gxI1wZVAf4%(hpTfU?oF9Ja zUxa^O{$JvUAFlnE;NPzKp^nnV`|7)9jz$XKg|H1gBW(X~I5X4>|7uRp;@`i5A2zS~ zlom_~B93rO$$g|9|6$c8gK+ALoZ-|3&zCI{Z+sD(rkYwDqs!hu>=a zm*C^B`C(l<#Jw**{(semJ3pNG$fv}{e~ur13Oww?4}Z5>o%t+y_z&|# z{50ilccyMJ<#;^2j}q{qQj7 zQ3Adn?-t{MV;->Y&(5$UQXtqxpd}SUVk6Bvr_}sk8&frS=d3y3ZIk!tO6eyu#K0`e zTO}iK>`lVN;VW0xlb^ZUs6O=R$JKP1j!@t)F)+QQUIQ#8b1* z#8b1*#8c%=?2VK&ap2aFSXXG~`>nNI((c6l#Me972z8EKV+QWA`($2RqV|79diP*7 zwOBSHX5G%L%i32po=>wL^DpgPivw7TYjLcrX5j6qp2K8Rm|_ z?wXxCZ>BEPaF`h#uO!s40>(W>TLO;E<1Y7Kbm{C|rm0Uno3qVDSBD_g4+H^XqH@Xs zg8)zreeLpwRCK#DJA)HllV+AW2^kJk=i;vIERv$kD^6^?dfvYY7zkp8^Dj4BMyFDe zQpCqFjaWV%b~*Jh&h4^QZhEU=;kG=qm%)izW@)uLNuU(stl~i@v-vqo@Qc#^ca@MD zQ zcjf=9vkgSt=^!s#E#gC$=n%!FtUD*RHZ#dv1{KJIzEe(XKK^wa!wc}!xxeP9=;G+! z)dZI9D5zLF`)%6cC|o<;Bc#WoP1@AemU1Kd?4c{wmoRqRT2%p>%D*tWUp0lMCO#TK z3-f>{;xjR4Pm#roq$cl|=)T%nw3f2Y{lie&E=K#@$vw5Wj1u#mfs7EAP;nKD3}5kP=YsYeveL_+F;^;_j?dgbT3il3O95);GDP{-5?X%M7k)mOn zx9WKugh>&j$SBEm_p{O*oIo{?Ga8OPfsHRmOxV$nV5zgt>yVs~jNmz_1wJ95BY$F2 zn2yAP&8+U5TD1VgUg0rENH!`RLSmEGlW%%_%B7{;qQ6E%?oyrwo#{LH?bSDj^kNee zXc4tx65N42*gFTROw>mmRKa^ubR$yGhh%{7e1T-_5s-{XKr%97l=ugWW{8n5I*;YS z$Wt+M7er%+^RifNvLu9Sn?*u6;8WW8BEq4cP7fp~0c@N_Z>rlO zB)aWdw;V@mB!HdO3J_qRx()Hjy99`0>Bk(*Mi!ds zLzQK@Bl7~_%X_AzXCH{5V7i)+%DC>|8Gs2>2~3}^b|g$V4byns8lbe(P{B*0wjGC# zGC%_c3AH*{d0{AA(!8G)W9o~UW_Ji@m~0VtJJA6NTYzPzuRMY%Qo?z7qhDY#Tm^v? z=;|_l>3b7#q>ud@5QdO#z_q!e%~`Y_x<%mm#79@p8#~=TZgxiy(}B$K4U)={WuREb zpNPX3T*Q_$DLRW@VHLAxMZf{t1PQ8Wn|G<5zpr>=P8sybxxeIix9#9Y@+KPV45d)!2`bF25BNzxv**yP+0zbt^s{^p&00Rw@3N%3F@8Q9eEOJS zOp$REyQqdFQ|N;R2nIj$E##`QK&W>bqP$M8Vo{vLSI9)Xe~r&c*71_q7QO1V^vOIi zKEr?-o`atEk8gBJOAu||m|HQ&UHD-SbLA|$34m&@ zZEsv9d=2HRlPaE{Gh}hAv6q@Jv^S*(CALN8r3-Nti*Izh#ytuvfdzfUVKw zEU&I{QeCOD$WvW?_e1z|k7}=`^t8lB1L?)QfiTV4Z>HkysnVm|S)Ava)lM>C>MXhn zkWIy(Q(tGUBj}3SWaG6iRo7neZ0sD`$8OZtOD4yLqwYf{*#!{i4&-kWnQYCTZ`h6K zl~$~$#Ddm>_=Z&NOHfLuVIwIz9R$G~mYTVd(@dcx=E8Mmwn2#%tA=im7`w(Dmcy5M z(|d8H7>j#&G7*ITG$wc|cE1$0(}w*Zveyv*n||QfYlxTOUa+>KAzqu>9P}A}MMv+= z=?kR8=#J9j7@f-a(`5GJTXN%Z7QIKUEwv8`IB+Z=kUp#6wnbVE0P0D7gw;Y^dT7pp z&i!W{!M7Y4xG*3C(+6+h^Xyj=3m(d!JH@MgD0NDGmKGcy(1QK9);_KlVw_Sb112IL zLqp>m(?bmATa6;<)@^UWL-sYTyNXq5W9Bz*TV>D%PJkj0M)x(sFnD?@x-Wg4{kIgC z?sbRMq1C9vy{gHm!}4sqKi3U`Gt1oOo=AErpZiU}!}x)8c|xS+4cl)G^id=kcZl>$ z!N=Z(&_~;T;1kgleC#C)`j{mQ|H;R2*QFC?A`CVm1ldrru4z>!q;7N)TX7O_?yrml za0wGr9B^N1oztp1Rsb0P!rv!ldra*HCKz(xu zUlC5p_xBY+Ol=GN=P!?@Z=+$iduO+iH=WFXlHSeS^m7||vmkRF>AjN_x{bU!AbnX5 zTHlPM6?YpN)x%Y~dqST&*zAfg;DNC4!;gCo3{tyRY3|dfgsD9PEN}NJqmsvvO3Ce} z>`9N|EvwJS?gw)EhG0K%WWZFJYVWw%_)yhwQFPL*(s`GpPR-}|@w_@`#XhNP^2QLI zY)VhT+%gXBKjFU27lk^7=oGt0ikhJv;4}%h&gdFd_<%1i+S={j%f$_R2`0;nrDUF125NuXn#(j_^BdYOr5!n*BDB zidqwm`PwX3fWXL}!9ICS6#9)6?JT;Q4pD>I_XvMfy*N5#YU0BPJLQjNze6yY+V@hE z3!N3Tk(&_4NwDyZ2_k6eCpxrWBY^-nh3~=o zX5lTj|J3RcfV$8d0_d2N=?LOW;_<5e=UL)^^YSOfVWgAw{3Sq;4a#UO)UbubaRDbT z(6smT0EBsYa}T`)INa*SJ6*1IC6H)I{E<+!gZpiEkIQaHgJB_gnNlZK9n_bD?PCaE!7g&z9sTtM{q#}VLaHt8BGD8`;#P20 zN!)vfyU5AeT_iJ+-9<{bWW9D86v;ZbiyRfCqai?~E7Xe>N^85E-9p_w-MtKn?I~WC$!{ljAJ9-teg_#HUZxKcRifuCudfkRYIUt(|Mv8;<#x zNI5RWSg4UwfCYl#qmk-GvN4~ej^eoey2*;ph{k+(79R$^3#! zcc+=UOo%pZLLWpk-H#B>bW;{=nRO4w#u972b5Q9tXvMQ?EOBzDVY?O>wAxvXb?5E< z!SbJ0V@=+*8tWQs*>q|1c+9D>%#l!Ptn_)x!Itn`er^3lHFl@JT4gfIm&v-b{tAAT z+y6-y-6bp^0dWxTnHVci&5J@c=6x%5vN5Z3ofQKvX=;T^xAhWf<5`cnTSXQHT>EQ zzY4gO%Y|#T@9i`r&b5ylO6|=z$f!DM62fk(q2B+lrC8Kp|swyyZbS*Kjd_v z7X(a=OdLY!F+%F$h@KtC=2VkWZu%Z`c%TF@hz&m8{>A|96|)`))<+p?&T zv9c`xijbV;Uw;<~;k)?Q?)VqRllgs&Ck1_tCkM*%k<*8x44o7xLko7YpV-EZ=!-is zu%8=(?Z$pG5OOkGcei*)2Aa>O7SdC>+#a=`C7X>39Wzma6 zMrNj!Ght*-Khw%iyv*sN4*fK|>~!N|8^y)Yg^ttKe&4f z-1Z5atX`;h<-hHolZ~^*&2qAQvx+snU9j-uz#vm#iyRna0#2qarsS;IqQap*GxLl7 zJj_I^tUfb1+lYxzPV{IQrJH9X=<^etAVZ~Tc9>%VmFDkr7MLY!Du7N!Qp~Zgz(Gjb zGP&P~bzNee5_DBO!di*Xkt9mzqlo>EYu<(ulnK zRN6&jG{KW1j&VHG=U_W}$AaRoyy-No| zz9_J-Tc=RKML*$ms2BaA@oU-1#_8Lvjc#kI!~<OK;b(8t`05*k_nNtqc27%H?dJUVq6MFmqrwz7%=l9$6ETEddFrgEDPs*-bF37E7bvSW@E+7l?x8{_Qc}8&!nrO z^M=VViH2fsp;q!q@^H1$ohuiw1iZWxT zP|R$xk$nV}Q6%#xYDz^P)@XT9g_mi$)uY4vo47t}t<>vl`Yky)p^25S6e;zQI9e_bISm(%))LP z`%CzxDg5%FeHjs^s$J#3-BL@!)ThJL>q#wt+N~~asTNS=rF9d)PV!<_yP=q{%*$rG zYoH+yq%$z254u~SOEHMwzI5wW4Y!jQFE~-#mg4RSda(8Y8+=P_f$kgQ5OZuuW$aM2 zDjMHT72QrnrmG3m3~+p3!aO#Ljo!Xk$uJ^DJc2)R@njW&5DUGkB3(RLRqRfs_7X)> zBy8hUBKLDGWob{QZ>uuwplGIkd^_SKVIo|KDL-aEqRB}`MzDIwm&*n7H7{y zOQAw>#_u!tt8kst|ITRq`L`=>hgyt|euL>zuOi>K-@>VMf%hVZcfaT0Y?#U1B?j=H zg`JBT4P_4|k8T=?Z}(rnfO{gSo2Hj5I5qTu4{hOX%M2&u?+SIOXOFZeqY2 z(ca+_?u|oiVisROe{^k3?K=6ioyr@(KRQ#r#My&Cn2KhT1nakc-Qk7*O?b_-kTfqa zx^J}2zii}yyyX|uId@H!)38(-i4OHU`r-|@&z{YNYqR6n!I;(Z=8o_-Ln}T;>u<84epueV|<0g zepSH&#-Q8GOM_gI=89)$KO02xK3dIMxD6{N*j_)E4h0?Iz6z$`jy(q;j@WWI&LSzy zGyabH+;Q^YsqL~#2pI3II4vqt0wyome2H84w6kJOq?PNQYdf43(+5SmkvZ685zx21 zKRs%CB}#BlsA<>Wd(>IPK?48mlnFM&WdM=?Fm1`O4aIe zu5x4It$g>dBy9DjXx7+sK0UUDYZfrfZX!DG&$T==145DY7hKU;(JixZhmJ(qbcq-p z{9hK>6(fnYg+?ht$l_-Fo$|-7?X4`{X#ONwZKzkP+&GMYz5H1L3d!+ zd`A5AJtTGcpBo6kuA!$~Vq6@X&=SO+RH&6I>2{b)3qXsJnj7tAKsk|DeT0#!(v~XJ zLFih!->1X-`sQqeY?HqfuF~fcGV0;6=XBifIx>Il-OqsUx}S2^2kzPcfa2ZAd5!LZ z?e3HDy=->8Ss!@uZjThALvzDUU6-%WKP<&wKo|5e>fe7mLnvUzHn?55&-n@g25st) zcigPbvF({x`@mZQUmU6&WjIN5%|ypquw56&&GgN*66a>K57@TuiCl}SNUBb^mzB3E$xOeN7TQfCzxO)`v{TkwEZCG!Uyf9M&vkrQ61%O4n& zdd*73xs7{nzf$uaHGs<90@Ut=5&F6y#s{&vz#|fXoH0b6;@8pu$+W)dlEnOIUMvMv zFgttV&~QoOays8IALPSNyHL`wc^5@TUrMk1)!&Zf8RrUkk%&u+lvoPGCrH@hj)G=gMUbdL^^?`uOO$t3oKeV^_-6OH$b0^= zHd?NJNZ!M@9{hPb>F~F_WAUcwz3{K-&uV$4+H!05pu+qCXuxasD7IUYc(4l~_n1IS zGB(stF3yXc7HLZrVPS5>RYo)ee2W0Xjs7~>wrX1eg26on5AQY?Bj2IfnA zs`Ngjx$VAt9R%sktp25rdZ5mAo2j{Eq(AQ0e0*4?-n$GKdpG?GV+p2$e1-1wkygKu zJmx$JY!n4>6={#Ore^dwD=N9nccaKgg;DiNkbCO(imuofh%oA`;9B*{RCH=;Mm{<5 zJ+Yilznoa7EGKn14a4cNRD#*-N8NqHyE*^q|d^>Evz4kOs8B3fAo zS$~O5T?-+Hm~qMYFgfXTdBKfsnieyku0iq^vbq56G~!#dg%e{FKOF;qgp|`cYrmOG z@!s{%z)Ht-kLuxuzUGOvm8e}u4D?9(`ik1eoz!nwkW5Mw`I}26R&=Fj(7z?LOtqY$ zXTII{{D=Qj)6G}Inu_)s2xJq8u>_oA_=N<8yMBh)wPkOU zE*gHX8Ra@Smb*Hd=>FJ2bwzE-3s)<)$m8b+)c+qO&D4(T%FHCK*)RLC!sMb4kE4LYfe?P5E zbVsfnNObp%XDF8Wz++I>F8G^~=x)$)l;t5qwZ)Ne91S_u~$r zau%rv`aly|S+w7z+|4{o7Bxj!>FDwo{9k^LyC-CNN7kAj3UEt<-$HItHG%6k-u-6@ zC(vj8F_I#Cvn6C0-iTNcPV^iS2(HDQG^&J^h#l^o8BJB?b6MNGH&Rsyf*&)^1url$ z;NnIN>%Hp-1lN~dHE4z=<9>Q`&knu0J?zao^=1aW>BmnBm#fWX;b^0$-5hLb z^7duDbfZE4`^1MU5WPlClIF-dRX9)X$Y%H}g#cS8a{4y}h8xLM2_Q zr1<6DeX2a19BA`CK|6*}+u@Mz1e>B|-OOD@cWii{&oAop6n*F!xRSp2-Kg4Fwttzi z*iGc>^&iRZ*=W-qVBgc0N%S3=wfIBw5ERpfLii4(v|M>FX*169M!}@ku?LGfJx7s#2ckgP=)9~6)T=T+<`>y#aW#5**89t`%3o&7I%HCdEyjOI_$;Q~DJwOc4wqwckKg$?fjLH6(*b z$+P&4UA81)34>w>d6wcWRWa{A<>ES{=zX_=Q%=Km)X|evVvxV+s+QnkXzVLFcnd2~ zu1dFN=^X;RQ8X$d)65llX#u;4P;Whh5Pa}Ia!Jt5ibl7|n|$BJbkTiQcWvpWq_mFB&~$3)*LVnN8ndHriiAPmiv0V36Ob_{ zxe*_ZbfGyU8C8cqpA+6EZ}d9R1DX3J<-C%I4F*deu7@tjslCgKK3qoI6}4Tlq0Iz|B0#meOu#T}no`g-gVG zVh_6AA~fOtUx-|x@y1SOtg#ca<1}V#kS4S}8-c1hZ%QTeYx~t{_CBf7hVzi_a&x=W z7qJ-X#*vvO{Y?0I<^kQ;O+xz5;C8s)jCsOMcI3h&R>2=z*Rvt%XL8r+7H9Q7?sImD zQS~&tzli(GGUvP9auw-4kCEuSK&SK$sGpSQG^BCQ)1k4a__c&q9=s`yucKq){*{|} z3nx_b?4R^gTat!v_omxK9~q{f8KnEOzhE2~73XnbN+ZbKpBzmT9b(oM0>=L`lfrLs z-VHN>4kQuw)8_{p%VvHsjdk!-lCt|cM&KXH8-ban;4AZ+j-lH#i`W5S(z!s6N;>so zYL8SjDJ*0ULj&Lu&|$kB)yaNIb+U8h2agK1K)f)uUdEQ1A4;g9F*pwMGmd(_2}xVq z*y!>8wj;8VhV(Cq*9r-AC4m28REG`NIplH7g{?}BHy!C-(n^xFEI;;6i^>*rp0Zs^ zF#h=9YOm&(DDLC0yVHxkD}Pyx_ym8mU?=BrFU4lnD*CFEmeEur(w29@HDy}B?~6GA z)#n28iN?K*=4*1fe{7-#qJsV}pr(}MVrFSs zmgsE_TaT?t(N{4Y-3r6-ynH%*yjLKsV>(QRsNyUevR3^MdX9({=kVnuy~jWFE-J*= zi3r-ljuA^)_yGY@i}IvXI|H$bF@@91;{s76P*4LD8-7Tf6A{eWy1fH3GjcH~lW=Iieq zB1?}|Rs$?N4HQdxysU!&sVH+U8n^&T@TETS@gD#z|4xk2npecuywsHYuPzDj@-#B{>eqr|52|eX7*!J;eI_8|-&jlU1~IDx zk>?==_%ueB8cZ|VdG^se05&sN6e0N{43Cl@sN{L}(OsKv4H7>L2Sv<0~N>n-_M>Kee{#U}x$ z!a}ztRlArqhDPX}^91kea!zM4+YzY7O$X;;iJOvFiEgi9Uy#IK^KF`0(BKuj=7UHv zul8QIuAX*Gt)xl?2jrCyCHr^@k9q!yd1g(}O|ZwS?z)u}l+|T_z0JB8wtn=O!o0GI zK~C~jRSOH`zO3ldN+8O)#wRINl2lcxs=|tPC-JO3mI+V)cD6Ben9l7awkT7c_poLJ z+rgz#_3(-!z(VOQ)z%*rFI3-BJwj;}O;|hUjPcI;QQ@RB3$H#+%a2Tj8$@FA;(>T7 zqe5BZp5>KIopf^P>~^oVL9Mp@J)d3{y5|*Ub>CapXday;I0=YGy(r?0SC&~X0{fd; zkvzWvOIaYGGGdWfk=rl!P)|^0j4CsC`?GS{G6Y?i|7xPUz**D|9YTk0@@HbwAsk1q zy%!76QVmnx@wvSOq-u`c<0j`D{NIa4@a3xxmlc5ey`@Ser%}8w&=R@VUUfjka*ySb zudAP-SAGY_PP_&6?!@%BMET$#Z(}Zl)7YIoeiNTV8GAbQ1^v-g_A5WI>F|zJk;j+< z&0^`pw?Va1XAKx$9bEDWM@e0Yn~TX?h5lODMN8fhXMX9-=E~?riN=8}ySk|TZ%CN{ zDE}6+Q6vk@{-*bHkX)}$l4l1ve(P<6WA|hc)L)PXAg`YnSS6PSzlARf9Dvokc=x|G zSXRl9{+s?KpjQwp(VCsK@Eu~LsG+GC9#PqN1udOX=4 zxo$S^SEtzHX?mP#k7w!eRC_#Ek6*OMi}iS#J$_A(r}H?=tNNy%&#>h0=9WJo8(WEN@wN-j`hiiJ$@35kM=hO7J=*XM8i|2n12b|cbM0e5E zBfMzRwqtpdPAi=~te4(f&9l~eHA~7e-PBXJOg>NU>$AMdv)1a}8yX~>UH!($TZF|h^^1k#<8X8YS{k}xzcAf7{J?+Bf6ZtYr9Si^Jplh$s0{e%a%WR-G z8vZqYmw!jI7Rvjkz%iy)3i!$w!!%Q0`6Bm0s}L+~*0Qgue|L$t%Jrw2!Brf6F&|?G zF1vsqz#AJ?DtawuD&d%a7L;JQzIdbbpDpDvvib7Jk5V3_iad9e8rVsL^yaBsw`R9u zLCZ@z$sO(^xnRk)w&y&^?jOHILK+<9RYhGQM|hZAYK}5@j+&#)pQGj|bLgl!$~-!1 zjxv{ynxjgCetD;mnQ!jP6-~}{FVhIW4Zqpfasffw!Sb`}yYy9ZG-;R_By`YoNZW|%J8wVsh28>-}SyN|lJJt9xh|PWKs=7sW zp?NuKT%vhG-!EhM0^{c+lE9>anndRs9d=DyoP|>uDC4SVy7F^$K3UBN-b*VOxpTc- zefz?b4eQ3n^k}6cUu0L$Kk6JLSY1QW-(H@Tci7bwT1@G7?=)g~5cHu4OKk`rPQ%}s zF>i`3?+X^y9s&3pAu9NpeMJqQ3RYfaWuRM;&eX0{{ zCUA`BC9Z)84+jPL3oE*=99hv7f0^wSSFBzgGPdk}T-4We`T zM1+l#@~{2_J-IvRMk;zfE)%KfHJM*2Ti2EQQsXtMG2gj<9&?eU=ju??mCc$+sh5B! z>ReHQY4UWvQg0QS^ID!W%hW&rL4@YC{CR^}Hhv)ey-f{Dd{Aotp!IMhc6rE_y%^bcGI~S9U$%=gyy4#z?g)*R3#YNzeAAGn`~|Om6RB3e z8~u4T$eDxKTW*Zx5!T?V^RSVreCDO#m(qtMKK|;wgS^r|?Szl#&~Rd1vD<$Bg|pU{ zzmRze8X?KW|HVwOT-6{TtcksD(V=&OpI-U`dqfw}JD{GE7|}eRrUY##))i?? zs6H6fi`hY~KZQZ58zv*Yo~aKymAOmnoyCXJ>>)P<>Do?~zehfQT{|^Xsv%CX^+8wi zx`t@;zS_v&(ZRy$irj`f_!f+`Cs;rB-)?l(^uDY2(hXWYhO|de3k5Un%d`M^9&qP3 z@k4NmnXpoV)$B!)yh-RT*3J!7W?honO`==5Sn^+d5Z;=cN<-84)w!)Zb^NO*GCvDC zdlXd(+rA%R-^w|X;IioKRQ=*)B~z@`DKz2sK8VgXftIHQK>$6^Jez$dvhy@-J1_Ev~by z2%Qv^QzMlbs^J%WTY21@2m=Uj>>ChsO`}uHPl?+eo6j%iSIZLn8ZRhzl()>kccWHn z^vjZ?F2IyAl4`x$ZsE~QU#AgT@Qc}fWE@Q?Uo?Pso?{7BMlWZyQ$vvcS5%$;)8pZH zTTGtFEDe4ZhVSNt8jRZJM@q|J9}9HRb4DTY({?3P%|K|MMr|1Wm-w2SwxB$gXGjl@ zaj5-LCVZyOmcuyu3(Uf-eW}TzsD8z(Zwk_oU!&gHQO;~qIv#9^b)K%g(S(D^2;5Rd ztwomU3oRETarj412h1_IHaZQytcrn&;hhFpAU&9YiD8T`q3h@xyG}Z{RL$to=#)%= zN6z=Jy`GG^;#Bv{(@zboh!IC`1T^?9JZ&Xb^G5a!L@a@#ax`U`%xM)>0Nv>{=$|zU zpc{IxRO#}7MlUzBGgc1BLP4=_Xa}XC{$Zhp?=*~O`=!n6L8kKRluIh^n{iI>`UjoB zn%9>4PY+>Q@s=ocpZE8XsvKQTZL2IFBN&$YS=;6hwGp;n;?uHjCe!6{{BQD{M-K}^;fnQGpNvj&G!5Y(BCr3N z>!sz~;Ke&6aYkvu|Ni+jg9pDZFm%VqF0x=N9Yk##E?{uy7r7ji!I^DFu}grAnI-tZ zmhA+pKdcd`c4T+rh*Uamja!TLy9oEDpx>E4>zKey)^?b=Is9pw<_dSwU*p~&3#kxs zqZFS&R-ylSk0qT+^KJN`qe-k{VT}&_mUT5?)unJ(6iJV*}!>WpXfQVskFew+ThP4?s7mDd-WbHq*~yrX*SR=@f@=8D)|#X3n2?FfemFW)<`I-k*H&;{h-`N5SdTN25Vn8bPUfM1ZsFm~$G3ylSJcq!- z!XYTtPdvhDcudqMI{J}(sFVM3Ju>V~d-13nAwl+(F3f%j$2tEVjA?sx_-Kqm2n(tPLIVr5YrK&N4#9vK(Z=C@a4TU^g!7vj7GF0F@45DDeqPvg%ztIg$9 zhj)JI*7x03cUGhOtoyt?SvTOx`o6ro=9hUhig06Xb+^cRaFvK+=?dpD>{;~XLvT{b zbw(@O6nm!T`EG|eA&<}2v6AF^Zz2W=eXV(ZyVIJAPSl(V_*mjeR|;)nhk!}mmrA|M z%e)ggoZg5J9z<$vTSfDn0^lvuoJo#t&MZt_0Uc>07-uWl)aWCcpwmWpHQS=984pWE zziqfh$xn~O6eWF zga>3#pdCpt6oV5VbN9P+0UjRYjn0qSDA7Zdp7=Tvn%*cJ+q~>1iZ#6vb*(%wX||LS zFkRHfX=qRz%H!oN*AGhfBZBv!z7s~b8JGD?}k%82H81t?wq0$PAk%HR%F zCwCgo+!Ks_L-2GWv%L>|g=BI~6lushkdM30T#kgDFUAdGFn5_py4#^S#CkXy1667t6OpiAt&BTeDJ z-JvQO8%{TEHRm1A@ByMT@G^ghKsa9X0fK!Z&=a|+c4PWgwO1_#briG|N%5aSg=!YA zrE5+ayv99K1=Fjc2+78DgNQ}RHt=a<`pbHzV?c0hTQu@MHKr#k?Ud)YyWNr2^wXlI z$^emmyaK#R-=UP!73r%*jj->wm2dIRE3SBHPPLXr3~BOFPrc)DT4fkBbb`lGm6~ot z4oOW}v|Be{@H2lO!Q5}DDK#%r@zU%j4m`=z)%}PIE=@lyY@>!#x}!L0wq+jnZw5os zXF|?$``L5cC$$?Tx*!tbc>g3#!8W&fQuTz9jR!TmU8mBGF^F!dQm%DlX|q(=ACKoT z_Q38cVonXVeDMyqEfIJO08zR9l>6+N?poa5w0{|Z)1lKa1%T4SkBaRfKQ@*-(c+v+ zD#Qu26;NPYH%d90gV%m@7Zv@!EHety!Jd4r!D8#6ESv$QeOuK2UYJGp@r8qt}q6BJt?q`zU} z-U@vrGOvW%Ej$PaAJip;0>({G6BKxwbyI=ND~vrU4<82LzROEP51(c@$t{A@lMB;b z`n8#oIva>Y5f68=^{&&I!`D|`!)WL2&9E=#Vbyp{bRoT(<+iO|RlI|4*gW`S>VYpyyatxe zyn>ql&ZA_dukafdi?26EJ!M2Le=Q%?na06G3YSUpQpv8rp{h43hmKN-s96Rj2%N{U zf;QsuF-ZIB7pOIRB**5=ral{1@qhQPXS>bx`a3R%RjJpXXN{RhL8XL65S|&kMa?2U z(hu9=^)-T#HUbDxAMLOxzh&Y3=c@0?D;m|E70o)J_=7kvM(|w5xmz zleeGnOa27Jpsi#!AUZ6NP-8~U)biGA6dBbSS^>;&%X&A@3R zHkn|?EPU_mc{Y5x2Kk+EysfpZ%t;<6XjK!!KGU+1^dKNAlXtAiN=)eevI@5Qhw`5J zNmhO|)t@&?-~oz^@2^i2UgOUdKQ!mL^#k!(W@$FRsM%jaQ71KBSj_(&?tz)7a}a9s zsoPxMc&7Upr-s-n*LB7Qc$|Q&>wLh>CRJLqmZ~w(jGmUN0J@$R?05MG{vEoBD6Vav z(@=oGL^>-IeF((z%P%*PWsTGDHxcbMxCq-OH1=7^_B7M! ze{dMAi@hfDMa=0k4;toZnBRNT(`Q$ zewdoNm-}JRx2N&sDRSR;<_?H1RT@}gUJv{R9uHFc z^!0i;^W~rycs-L7VzdxykH`OwEdM7G(P0LfxMzUKjb&jJlMH^5JY6WH(Z8RNC^j$ zst(R8BZME2HF8?;dDspg;%`!C3O5SSPs@UyPwd5bus`{5LSDuj-02fyV^h@yfrCWK z93Mo+hwi2J<9m9^D>cp2$ER;cns1ptz8*zD%k&AXOkVm13HTR|a0wG_F=@FzHSLQP zoLWp;Yx;3LUsz<{|Dfk-h3Omgn=)8k9~&q7V9=#L`scogGDz?+2?VkAZK)E2$xDx+ zB~8#=zGODBl<6a>XUfv(UN51jQ8ym8Dtlu4_OEgvRi@w44ujGYDV=^%n*`*8pjn2x z<|u3lT@yr`q&KDxn{-6y)lB-5WEZ5kz@S*3`<0qb`nHu_086V=` zjTcB_Vl?)uzTB%X-=g9yVF>Ay(Fgfch(3&;qG^o{^bgqw5NY=x0cO0D#e&NM{WZ)` z6Ljn*Ca83@|NY~bpnzNieij#!ka<1F{CJ>#+XRMA*-#*v*hYd*x501y?~dQPH4v1; z{}|UvXS)4g}SG6o5Q%@^KjbOx|`| zr@EF;kv;h&b!?{M(b*H2qUGLj)!;r=e_URCH3>IZ*8p7^;jKR}9w95$IJeujHe;W1 ziL&T_lg8b(nTL%noF3`l->}Nm84)}wkBhxyk>k-sWtNN8$Xzx%df7S#B#;*r@mt*Z z3@4H_lcV+ns`h7%v&+JKrA``kPU4j=Igp)FnU^>AajS@v?b3m(&=};i5P`eFS>Y7o zRGwd~^uX2nD>!8MWsUUPL_?=x9|$CHQ2qkBqrw`0udckjEd|Byt(za=5MvSm%>y3w zSqPm<`Y!`{h#~kW`EQU{WMHKzAvuJ?sg&hFW7ds_y1ssgHk{Q~F!<;lZFFKqbej8q zmP`=hTV2WeYa^IIFV;Xvsc_eNw1{$~`LJWr zk*W_S{<$4K#NH%vC~EnsVV|Mk3wi^m|AGFq6 zBHN8{Xt@uuH|w_zaT;`mI{g_SGr{A4M#VruypwoW6y1g@MdU%Sg-SQsPFr45Es7u( zk?5r?ZTLeJBEv>LrJ}>UlpZ+bJG4<%5}V-1sxBzM)V{S%$zvNaXW&ZxM4FbZB%_;Z zQ`I_yB6KOLCH?SUXys^+pM%Jxlkj3eWnp-0HM0SjnKToB_6_ zLt7!)(x5n>jf*!_*}bJ2j~jhI7^XK$1M{pI<%~QZH0D|Zkw=4phE@3w!mHex_oJT8 zLu8=J_Sk&(Bn8=kxYKZm=r&aFV-Vi6^vT8(a`DV^{J;S7!LM20Vgpj8!Mp$?si?g3v;&U%FC81nh5?BUT`q_~ z0vsifHnyOkI$6Tjm){iHT(ATO*<|xjXy-D~w+RSGOn{5L9ny2&3-$g?@J|0weaZIE zu09*@XW!YJ2M+%D9@qmEM~||bpiSPa;>g;qwIyYAn*&QkJRWmU^HicIR0j0iTrR3>k=Ydj}=JN^U<Xy>OSKO7_6BgA#|NY1(_0FGu$3p(Xe z>t&hRPW~624X4LRUK6}%PMqW{UTh)`a@SPeeHfW4Nss5>UAcIO)i%azdxwZ7e6y)Z za@sgM0kH#`^Tt*3#fG1JjUQfYz)4+p(VTZgrB!2w4gU_RVdEYu?7ZVp*-glTm15q+ z4kWW_@sIy%$cd-~lv?~1e%%FK-qqOSh(uI78Bp6xBG`T9%v^Uin7zF1vGHrtnCFpm zXl^Dh_$3QpsjJJ&Tlng4f7{dZ)V5UVF!%X|uRi`bnDkuJw)};!9CZ|IP^nGZqN&oo z7ry35BmZ2}hlLStq~VZ@9GX?-8)IoMP||qrv9zUajnp@4)4v1ooe>;Oc~Fz!UWBhf zSIczGwNVE2bxc4P7BRLF?hgq6(Wy`UyZsevq0cjjjsn=I1c3*bgAXic$=0V`qDXH+ zJ(8^To#Zp55<+o0Kd~WOYW~S%bdPZC$b6suF#A%`sy{0eCkEoh zk@s==zUEfmE80hHK|dXrAJF9j#aZ0))ajoGrGd7B!|E{%3%I0yfL(zoO_x;4G!zHX z_RuA1Qivxe5W+)Y9B*Y_-FV)B(h0nc@#-dqW?+3B?-z=;=?uw5J^an|9d{poF`q(= z&RSHQ-W$ToTuX4F4|#qe@Kl+eNgj6#mcf}ti3P>dnX+Lz=n1=y%Waz>!^bKL(q$YT znfBw-3halSGMpK^P{NL;*ooJqHYVwx@vk}WqDrq$C`<36{#)Nc+W!LSBzUzNN7R2- zX@f2AVdNc`&AVFZe`Pq$d54oeI-CA$rF&tz)l+8m{7C7SSi0IQ&E}u0^r@tCJqN@h zKm7!fytzQ7f5nsga{3Z}LVE!5&@Jr$+2vbgc9=A?XwF1$lq5IpPwLp`MTa>nN~$p> z*Z~V%8rlw)1Tl?WU*NJi($P&OCaW`rXtSjuEn+QEDOZNl1sw|w5$A_H(b8EE>~WZ{AqEqHS?&n zC5jARWMaBkRGk>L-%+GEAVNDbxZ;h|tc2VrG`fZi5L+ZC37CWeZ z+qhV1C8l@2mFSzJZ$tELAm8Gf;a=h=;d#<5{SmRixKIN8w?JP6)gxL2-B69vY)V-+ zWpp-WOpp@1;W*D^d( z7PpN%Y;t7oY3laLmO6y#2=^_J0i6Jg$tf#7Ao1qQ3eXqnlkkp6hjKio9L)>gyqcb5 zR!ja5u2In(Ow;`AO#|^?q9yZYj+vHtGUAqJe$3l60q2qQgQ**-K9q!F2s!|zF@G1u z-%mW5pLxV`!&}D(UrJT$zACa;n3oA37Zi9q@nkgPV!~kbOB*Wz9Tg^Kgiec<$M#tG zWFvEs-U=QDs)n~G9~OIS^0?R=lP`+BI*n<_kG~Ln*~phRzC1qpqWBw`X4IIh*fh61 z=){Tj+YXP_Xtht!NRLwwj#i(>hkY8O44+qq1C)Uwjx!L6zr2G#B>5gGqf<`uM0yn( zl7be)MxUwY@KGX6fx^2m4!dy$GRB5_lJdC~O*#KL`#3sFQt&IOUpXQmYO zOvH}G8lyVx0+ZoN9+^lT;ruhc^dlfHL^YqP`=X-bPS$T>0ZG}+t zlXWmlP}-G7Fq1f-|zEk72U6FkV#CNG6fg8RnhrFMK7YiaBF^E zNdBDVaji1V9~O+C?|v(OgePDAch2&@DE#958FdSjR8MWU8ZgTrJK=b-vLmXWzzi?YIpRU?tm41SxpyE``USUiyIAG8zZ>bteMfbPO7=xBP zDET#2BKDf+-KW~Ul=_v5rVusqObwmbR$w~v7T0<-cX*;BipSm{PV*FVH(cT^iGoGj z-KUz;`S0KErPS@Ptt)@FJ*+Qw-p8q$lQh7@m!LmhQq$l(a!cNAQ=P(vALZ5a)^If< zT;gF(?Z=2Kym(2yzy*HPz}rImG?3z}P}}9r8|v9IIIDkLR_PQ*HY7ee+F3M-jLsts z`(IqeCbi8;9%k>i?B78ZR zD+quN>PhLzy;a>#A4_Ju1-J;gyfM4I3zkv)>IV)*P_n5K4gMW(nf7c&xYPoOiHB9$ z0H@(pURKc|md-#$`nsJbCN_+7<44ci3#cNWr$6Bevd?Aom_IFH=8 zf2C#Nlf0<_ccb=8Q&{}J#p=!)iW4P*2Qyrr693HUk_-INm23bQEsc&lLyZeH&} zrHyU{#43Fw?=G%@CjpdUrH~=T+Umjz1Kbz*q2}l zYYE$cWhXy&EdQ=Pc!tFT^iNXZKuN6eR~MQ;NoI4v59DU@9QI!S9zkGrk^eh>Gtc!s zpOK5F;Oz-M$c7f2f0R_Tki72hiA#~- zzIXonIq(Pkd|~Hgs)wC&{6qG=!O5Ph1}1t=xN4Mt^AViaqkMQ&bm2PX42K^CpGjHx zrIiQL1^j8E*YcY;1AdNk!StS0tI87^;_=Sb>fh%v~$F+k+DT}8AJFo^dE$sPLYkBDI zjDyw?tYhULdHYNK`BU<`sDJI;J-5E#fA?@6M?9$cLM(yN9G84y{xJkQE4J=2f!-?F|%FVgjvHsXkQKxwxN4N$x&?_CbiV3 z_2o*lHaGjS1& zeL<%=_tFXI2259BJh%@s|82qRgGv+_Y?~OU6J$RY}+ZfoE=@ zKZbBxQ|YlPM|Jr9LdL9rz0>;Wa9mG={`+rF8_>@;)#m*-WH2FNrST{mbNVT!mr|&z*nDzWc{m9Fr_>1A91InMKFP{(j0Uxw9#O-g`?5$jVGj!7| ziprc{Dt6yDLU0%vD>z>*Fxy=fc{PMtTnpBqbj|axd4OYq>Zsc^HFo4O>vH4#(R@CD z&rRL=_R&9|Y{7t*YYcYX6^DN_e|><7yV|v(Du5Hd+PMu^aHOD!zK6rm5B<_bYDKZmnXAqA#aDl;&bS)klZqu@;LNYbV2@1!dcnP}1z5_RTte(>Z1co2sKDBT? z;X3jbzS6^gIalbw)L)k9Ua5-O&e$I8)D++){&r?1w;5&b%kJk_?3+mEQ+{Swq9vbF zfb0vgr8)a)w*0;-KS+xe?@Z63^I>_6KvDc!sgh+gzpgK!UxGtC2?Et$IU;{boO3r_ zsha%;q>raqmL81Ldb2&cg%IpIeA6NugLGF~J@%yyq@$qeG_o0Tp-s%ce{-=s^#fXU zv`x^K*s&J6rrhM0Qh-PINCzghTbW2YGo*gtQ2`xDrmqhGpHKATEiBT%U5=YH?kd4j0R<>{{yGW`Qk-?lC4f$RMl%SAAuF6H^F%PrBv6FC)snS z5k9}?)~zdPtS4<+bYRkH&`mUULdR)%OOLaP0m1_J0IQ6VMYiE6!OcN-dX5yHv7K>pEc{MO9s{!WnGDIenw? zuR=emC1TFaUpx!GjT}y*jJ;-$Et5a+-yM1vGBCbH(1Lkdn5_QtOHubxfVDcLhm3EA zTNQ2UAbfmrisOrwY>=Y!_Qy4U1RLQcvRhRde)gQfoe);(R8`R!U+1=ZgK^-sME16f z;(IX2AQR%rjSp_ROS-ZS{?8c`w%4qUfZk|s$__Z|%j5*q|$hRr)V<>ZE z4?t*?M#Owf$j--^(ef=7&(0aiQ6^2BXL5n|eAB>Y?v;2uvF*UQ-|+T_HEp1#LRf&m z@6Z7CVxwDv5Z237E5ShqxK=;j?H$ozr+C(YelTGjnLlLfR=Th!hp_PUC8VV}P@@k1 zd#WnJVYzB#pZJ5BAFDqV&pHVSU761Q^6)7wwc3#|CnYVl9rhGww769w=Csr{DkZsP z{*c7S5&WD#9(~n5{xZ~NL3|{Iyg0>7M@yvko+7i8JFC-KJ-}VSEfmq|S*IYUVKmkK zANIZlJgVwkdnOrRfWbXV6woM9qcR0;Xt6Q|YA#HO+(fxZl~{Yx+G@|SFaxLrf-@u8 zZa3nov3ibawWTe#T5W3}DhUbDBr2@|IX0qIyj1sgsN$swB9i}ozqR+wBm`^E|DWgi zpXVGN&FsDQx_;|h-)(*CTiNuX9EXLqO(T`VPpO+PX3-%Do4jt!l$t~5Y6*ClVj~AT zH-D6eA}wL!0S@kinrR9^i|ELaVc6CcE#wOg zUt+shJ8peaZc@Rpz8ZkR>+LU@1+pd&Ae?WF3 zTr|{Kx#|R**?Ew0d$i_Dd*f5qF9NT|{wul^w5KCy%}qy&Vt<34qIXp4L+nXN!l_8@ zHgGfN{FKv>+$b#gETIYuChjg?@bc0;+K&+6r+VLH--WTkWk_4{F8)NvtDVP9yD<54 zWp9&Pn~673zv|AW!1@~d0{cPf?aS~R=~agdM4Lc-KAuiPc!%&do`jEdEj>ZnnT>Xa zNjqI^2cgjlQGuGitBt7~s9wEC&EB#y_OWZ}iO_w}L6pyNJK41)#V@C8n|2ZVHv2Ah zyNE#I)q4!zkK)z^nDP<5D2mN}amjy(oBuF2`cen3_#Ez}{ zQSl?e=m|V|c|8)uBqGP?wB~tU89e79_9`xFZVsZP9YHQ2&PdtQb~d~)oz*b7jk5nv zyP-tTGRnr`8VH1qg1K(p^+;yatZ(u2WrYwG;DV_ouf(gfdhJR)tNXmT9|S&&wg%$A ze(Ph}S4salzplIrPk}t>#>A?Y=rnTD*KxvH6j=oL4QRLB!|LF%upn<{F+w#g*uMnF z35L{0-iEk@&`#JletYuV-snhvU7Dg(z>a+9gqybMaxb(Db^3oL9^zJx%YQG#(Qas# zp3+KfQ(hQaz`H2F1Wv9v4^hG&mfOKU6+@dJ<;!BT5x-aIql1`-+y5+HY?rN4>;B_T65$rD5dCcXm5C%wvH#| zIEay$!qW;%a4q7C!o#uo0Z$10U-1LR*E)(H(5YJZ!Gm}> zHb0mODhpB8;RpQ*KP{Xf{EUNaT|CAm{KBm4$B@W%2He`PPfrM z^qsLK6n&@Z^H}sfhHpaOufRip`hFJD2B7Z@rce6*!@l&L9!oBLU-7x=dk8r{2Yt`M z@umNz@BdTi`^xsn7OsDI@wn1GuIzaSYEgTHI{~nI;ONu}y7GCWgYma;)hO#xEO3{? z(_p?*WPXS$>JWfly;Ex*0(mO!pAQKgHD=Ry#0wtP-@9MVC)_5)c|V&L;yx1vMc5C^ z1Bm;(;5@Qv6dTeW35vvDy_2$W&=L9X(2~Q2J9V%YC|4jyAUQ!QzI?n7?mkfJp%Kw6 z`K1w97LYd=7vc;IOv6roH!~E+*~|N*=NDIZM2lQ~ zflfE%54(yN1glRjuDV}~lV_m|*hMh&b1-<6i#@AXbvj2RivhBPA$gRxfVe_oMAm?| zfYXeyrbn+^Q_aJaHOu`=M#QT-JB%?>!>o(iu}9LGlF3+fMyW82de-|gwy zYgggf`t$@qAhw@8s>W3&D4Lewqm1cEz-6^P5wB>62fyk-p2a4?wGC($ITb~>8G zy#AWRdmv&pw}>VI%Vt-lKG=R4Ntsas0Qc#Ujp(Po;NhNYHev=4#JfMAJi0o2A( z_}{X0M7TJhs52iI|1{#?ap{W%c)1`qblqa&|ZEHDo6d3~h3+eWyIbq*-X4gI!1P z+v*PLQ(N>%qv%r?;sRw?pK2sr8nt*kc!eNo^eW?2(Wj~&cl4}#K`U+E|?0ROFW z^{bbZes$|t{+Iv$&(W_2=D+_AUiTUN_lsaz|0MsNiW)x`|3&l#G@aqUpMaAJ|Fz`- z{P)LrkVW_z{Fi(4?UB#Le}4yS)<5FE?+*E=`0pN!p&$SK9f$!s{Tci>I>WWp3Ga>V zfCAZ3?f)I?bou2- z@+0!gVdO!|zWM)-U(#-w&4^rnSucS2Jp7WXqwq_B_P>o^`ucTKV&J*_^3@E#>?!)Z z{Bjfu{r|x)&13LOI=j34au%MC%`eZxSK*h(Ns;{&GGT~R{PM+t`Q;@@rlLBXpJ|9x z{PH5ay8Mz}r~eeceDU+~%ges_zx?ul1i!=~Dulc6<0J!j-aLamR?m+?BDX(S;z0!6 z?a?n%Eaa&?e7+h<^q7JcC*ex0S;-vNJ2Ey|Kb1wCY%mWq7s}BASq*^-WrcK(*?N=dyY&+}K>I_`Ime=#vFF1`jw`_Th7Or!%5jIA z+&JhE)L(_!2WkSuZ@-m)K2}xRoZ{GlC09`G!ub~*jiSr%0EtC<1?7d}{%AN30eepZ!?h7HXXV6>Ox~pP?t5TB5;mY zT~a$LmBJtWy!ExWQ9kHm9cvwtx>AoXMmh=nx@>OGDy zCpY2+VAVENRzSxnH0CablUbm>WHES(a?&jmUL76ko?=+f?XZ)3wi6AaBdtHdR^RTS z)3D@&SGLFoT4EyQ;8537Pwh2B7ad)z+>&A9$C z^bfX%Shfe>Q?e^)7AXju$X@x zK$UoFlULwQ?8M98qhto!f zpsQ=i323A@$x6Hec%!Za&*3_zXmQ&34S80j&A??0r@LwKae>;d7AsfZ#vh;FR@)VS z-K*F6QL4on#N_x}-WF@H-rz$LzqeRJkOV~0lLtSB%P6vU=?#bZxXm(0LzRWyQ1eFd8>J9slplzDw_t)0F#BY+dHo2G2(}oq$8-JY9Fad4gLU#lP z)29t%bhp|)Ks!~DG0X4MO}69!t6t_qfMrY@p3f@AW~#`$P4DdKWH`a0ow{$ho@5UY zo}K0uE9<(rXtF!)Z58 zhYeGuqH$`C+T09Ayh9bVlD_ZOV{N!~Hu{p>AuIc@y2<6iWib%?uk}*6ZI<=G?l`@g z4yVxls9X7?$Vy6WaaOJLNl(_G*3?Glg)2ApABH4?Jlv(S7Nu(AO{9apPPP?z){-|O z;lZoHSd6)adR+_BjFNGRV2Xt%I%3Ld+}X#??g+Ay1h5i&GG6q(&N%e?`AF02DS2v} z%Di!_h+J#7-ijO3%_cbpm2^JlI3G_nMwtN}0rAXPm7R2(8Imcv>nSqNzS9Eg=mp}t62{$lZyY<=hBJ*||@?CEz z#21`9V>#b4_$}#DBDBL$X;=)ptYI%HT;gUx%BYu`o472EI+l!oGGy7f9w7tdAGk_7 zNA6N%Y)r|Zip011;wj%@M2N6+^cQ+~vxDcupX(n)1KQ@n@ohQR>O)`%jK2YwCCqFs zjyI|%*qpaBEFHp03=~uA3_^@qN01mvg2xn|E9AOCdML%=^h@{VfLBPjH50tES0HXk zCa)BAT3TE*I^I^K%ZChTlXC5mYRHs%s4GBo;b)c()=fSc@;v#{i|`Sh zblIv%Y=>|v*=S%fxxp%#KF3llfQpv-lfFq1L z*6ti>G0G3BL{lZMK}nu9gIqVcQcK(;xFOXjkj{J4+X42D90IOfL&}-HHNxKV>Pn6e z`r}C!D4HJMUZlFVBr&nKxQRVqI@hRY7mWrsXZV_V9%d~5tX{?QZzuBn+jVF=fHO4F znGZ2KLG(N$`4}?C=c49-s&C0AL#8Awywl!w5Ca3EL8L4fd8EBuFbS=h^nl>h&$KpTFJ{LX*BWgE(^}pLI)&VhJFAE2b_c>AnJv;b+b$F~H%)oXN;uP{3GSJnNmHFAXOcRSWja*zgm{ZmPt5s&NqXJm_==FE zQ$Tf3Mq?RTx%}jT>Ez|zU@4T@?AdrSZ}GzY=M!DzH+F5x7gVqnh&qb$Yil0{UYJ+p z=&ddnobGar@gO#6v@c_j!?c-EE_=xx$kl|H3@2>zc~!_r@;5Chz%!_h!lhZ}@Phey znGt42rmd|-oZ6#CS5OHquoPdY46xqRzp>ZCx%GM;;YO!ib|$ zfMc#6;-p{4On2aj3_M}W^NP!%EBpZ%MqMH1@2zlj1trykClbFFd7Wu|)rqorDwM!6 zte47TcWzDWYIu;iTHf0#Gn6=em)2ZXVcZ=Qm5n~@P}`)+kVJW)UZL;UDSHuUavb-ycOu zN8T|6#kg(#cM2*dGY&UdNK3wflz0=H^BYZ%Gb!;qW=cvXsTq#5qS&y2A&MYCAuDtB z>4xM0XBlNADX8LUgj1cCxSACS1qvMs)e+5A_axM~RNzXrT5`S!5t65Y4Vk`?ArS}Z zF=W(B-G~>j>JDm&(abM!fTufznw4xE0Tzs8LUjWX7)TjwnnWGe5|5DOCJ8t|C&Aqs znl8%nipzjtaQ+8^oeKrDx71Zer5k8{WV-dCPV3Pd4Iv<2X+e>!TYK`z|H@WAU7c`q&31^EiZ{g{-dkZgk2(o*gpWDmL}Ap)l*1eTUpVD3Kb7VcmtxA{jVn`LGt+&GbeHpX&LIsA3)XSM1@x&<8yh{v-4#?28`5(WXKV7ig#T#gZ+XF&8gnW67_O z0xa2uL*NK#1&W!*kM-)yW_+=K#Tibf@x!(IDEttckIk(>N4@TkA9S_N;0JK3KrKys z9?N85iE&BWkRtm>gpxxev_y%#aY6cAGv zsA-~vu?UCofnh=G)3XrS%cO3N6NgXZ-h{d>c!oWDtWn>>RIGEMaM0fHYBd#tobJbE z=|*xfGlJCo7Qc#c4AaEwNY@R~U}+q*{=;V7wt-xVSBh25uEcv|X7>)8W(QyD<55 zZEnmq#3-r13QjTJ5aR=$PB+9>0h<}9VKzL!f96r}ya@u8+iLO#ybzuyM}YbBSu-tx z=RAB6d8#A6Z^%qEKe^S;Mf>jwA6DhzDt|e#9a%Idi%+sFLaIam8vnR+=gyfuueH9D zi-;WvG}DS@xycvScOmENHo`HeIChR0w-WgIgt*oTMv*LW*7&>9`Ay&#QPA-h13%Se z?m=8Fq1Oo`7yfL22|5>|x2tT3?fWU?PPCoB>I0`1CDA%UrR4c1I0gC@5i)e)!lhDN;9yzceD zlH9`dO;V`vupgdD<_#$6hYBO;Rrnz)X?ZONxm#Y(mG2z8e6PVn;dAocC*KFjcP=#f zPGA*Y)biR8`8e3$>F=nOhR_}vx}NNit~y}+ni_aU?KAh`vtIWy|0cVbm4RpY;D*@Y zILHT8upLrj%4>i80vzBWJ$;x0$Zp{jWD8$e^`(Aeh&YS&ms|&^Drv)L;K1MjajqiM z$(PbnK>9XJ^EETn4*kah5JqAR5A zLhaGdwj)wTuZeculmo0HVdKU&Wevf-K~NqnZA0*e57Kl z$+_b^Ophr6q7;PvgfK_W3sA{qEbRM%qv8}fBve)h3``<02Lq&Azt*rRxZRLOL&E#D z+TY`w;h+I#!jTp}VMA^}*$9G7TgNVKTn2 zK~CfdUfc^YWfoo_9cj%K5Ppk9-(uTxDBTr+R;AmvPi5KdD%@J=lhAhi6G{XE1U18I zemB1Mr^jUA1hMJaN zV3wa&^da~-o$#*xMOJeIdy%!fI!UO-GE>DyBhlvS;g)RWjobyh+Yx(_9b zL94W_HQ2~_3YKz@eFJ#RR$dx3@g@7}4Ev!?KRQ`SDNm8*V42k4fzo2yz%+>7rVEo$ zdz5XmNwUHo*F)aUhJ0JUiDp(0Z-g2Pmc=B_A6KmXWf{#^S3@@Z z)E2|FIIj8ZdngnM8fGJ^P|3RAZa^y771Ppmur_G_SY?)Mh&;#7y7xZ)QlFjR7jq*L z%;Z5uDrq&~RB<=UI*dytAr2`@Ef`lMLyl`q#%ol8?+; z7Lp)d^}0N%fqRPN@xMUHwj?)23k=yNrG;d_7(bYCc#?%+@j*&lx{|h#JjP5?+weNc z13iUl#sXz=r?n^p{t407^SQqkiqR`0=I+DTXpw-ey6vXe-6!K8TI zv)T|v$V}uF3ki2sp_hy@WOEvNiBmUJ)svV%NjN}G#0KhNW<_Wduoy{Wj7k=-X%Y}< ziC+u#nwJ?|2muOIsiYwvfoy@PHN)KPnp7LejDkvQ!@n$#0MXPW_*+ z(Ly5rIgTt=kFn-mZrl`!uy{~?0|lmTMnTKsyZyS@QP%zvyotxg9CMk>8?ZKl{jy>dTMt#j%g5H3|Iqoc0m5 za&bc#X7Qh}kK6|uj6k1U_CT&OyouQ)%w&_a4jP~kGD;m>Zmv2F{|QbkCDocHLMd$+ zc{7iO5&F6uWf+mIFDe$BN2?L#F&4Z58f;Gr^sW}Kg>)HtGF`FDt=D_m)3ULTHhUu zNX*EoY$`&$#o+`%s>PL^Y33j#8OG&FF6zg=(3c@3he&&BHNa?(J_vy6+vC8^F{ZGQ zv@wOC9p)NS-hiCrLPNIHoF>$9ddHX|lhMbRA~WzR*vscIrWBlT)Evw~58YOirFh{S zxU=uGj=UgqFb*GFd+<^AloePIstVXs=5uu%Wlvdv)X!&68IPQfYmLk{t-?k<;Ru`7 zDX#O=Nx-cM0G@a(-72T=l#`gV3qI|5qJ*~97q?GD1&%?6z#|>xl=GL1kH1`0hB#*d zj8_}7YqPO|2Xc)i;32iL)!;DPIj>a^eD#??i`wCMSx+Y!4lxjD&!mc!@4nkk1l_ zYmf||Nd8T(z+Z4ATp1{@`_U>Q!}A$>wn)TG5^?zI(PC`U&j(uAYhFY~#3OdlOxz)| zs!aX5@3iI;!%evBC0QLzFi`3wfYVQmBtb&acq^t#$#_N+M~~yK=Nb*di@RYgzzsI& zPXXtXBwyGjHZQFEE_bDdfeC5M(Ffou86@MHlUF?FD0|iKK)oG%l}NT^Jo%s|b_v9P zFb8)RYVl8KuNnXu5xyjzdceBlMo^a)O@CV)0w?l@Q}{pa!)QdLAt)R6aBjeF23)yX z5_)$Q*_SC`d6|N#Bh#Aow!cAK5g`e2#R;tz8o2~opCw~mzc3B0zLG0pDm|l}1ANul z<+&2gxCR#03=LiBX8IXeWKy_==u0;PL)Y_rFy#ZU-+X}mSb+Ejjb0A_n~YnUfUTVX z9qxbQ2XQZ@3Z$%Iz`n;!5X#$xs`$j*Y1W6yiI%wf#m9U;%P-}O_NObYFNW6 z6|GLY6uFiqR$xj&PNLFE(O^Kt?wg=1W?RF4DcH(7K?1GoL6yYRw83$JB?_hV* zeDTz6hGN@}$h^#zdR7|3v$2b~$fy(fVmHlfWXNLCfIY3BnQd|>8tI!11Q6~ zRR4F$Lkb&eRUed01hSE=L@x#~5*{TL(MYJIA`3t>lQKLp4PqCLNdTMQI{Awrkbi|= zn8Fmts+TPaZR#_xkU*w+l{6Fwrn4H<327+xE48QN(UN09K<%->w)Mt|B_{@P6^WLR zO%#Rpf+k8@MO`>Ma4WV#0Y{^re-Rq;X0<*h?sk+)brxmT=n2{P-h`i_<$dYtuK0W! zd^IL;pcK^NI-=sW1j=yr@89VqLiPAz`RL#T96|iVi%#NBZyg)9U&Xq9qO)=+E=861 zWxzO6YyZ*X)-&`7nikW2R5<3_p{7x&!bc!{up^4s+V*0>l7x4+kNvJstkw?dJr1 zLi_N}3~cn{=4a;s5f2x&dCpXM@XTm>+NScr#u}(VP96kkMV@o$HHZ-MP8}tP^FrIG zlc!YjjCb-lbreKTbiWUh@8_n!$8QN1X^9rGB?P)lM))&Xe@LT&=i+p82<{3p`7NW$ z(p$IEkpT16F|+6GzRm<|=vtxt2@pMV>7>5#OqxeRz%GY`t;Gvaz3d930SuxQW5%#h zX32{fTl_*uXe$W^XJC|jac`gMjWo~M7>?d##e@0B{h4r#{w)1=x-ZeN(~sBK!aOXm z=t+)RFfNlx<^a=h&3^avAoLx~FYvfG^;VWnQ29gZ1ghDrNJ7~_9U$v`!CBD|T>7(%4B*JUawUw@p`}1`~?uKTGV~1zG4_?Mlo`Z3I==wy2AlLMX6n){|vt_OL;;0bxhILo>J`Z(d$%0}wmYpUZLj@zvhmg?bwRT3?N$rQC?pu>%N zHg5AE1{W}UX(mS_iTL&R$9Sw2U_-4|2SSvm+S2Ra+K6|13CfXPpy)nfaZXX;Z<|ou zz6d$e_lM+TEwBk(H~}=!HdTx;=i&Mgf6cXlA%!2-R*Yz`C`A6=_KIKuvLXH|c!$?< z@V~;GhvN(;j(bC;-&F1%3MfGPpLrw$dtm`A81ef*%&k4d*oWVd9K3e$0q3aVRzdt= zX!+>~@%<5|ux3smn86plvy8o5fm<5(bXNolqC=b>wpS2xz54U*6#_d{#IqRpgHxu@ zVl1Clmep-{G%f+BIq0+d50j8S$Jbtw@3b_Szcm-0rzIwW2?BHnYpxyfDOzS9fYr6?y9P8uNs|d zSZ@u#3Y2^HXl&F`Fq5S;4*kb&=5FtHSgtB2P`kecfbxRGAOy~wLi_u8+vr22bMzF@ zvB^I+=BRf}o_JzUZ0W8Tk%L93SB$g2f(*!vyttZIJ#&)n)6_HJVxNF#77BP#2)9|` zbr4TKrJC6c%(C_YP;@*~^|Ki&8qdHEY=O!11U!0dxe97hp;_?DQ5OB_j9GgMzFpYyStrFTA&pw&r8?+LN zz4{@&no9`hhN6oycv>b44EeN8>BBVPM z@W{%Lj@xN6Yqi9jmz%W~KKIcbg~Nr>bBsCp$){KF)y_p5lph%;`Az+*d|X=;;22Y2 zo<>!kS=y^tAU3lxJlwGqvMfSE5;D3s^_1Q%Pq+a)LtH8bCN5_xwERc(#qHmCV4J{!X9w0{imJ zJE#*SC7Ws~=f7so2)}&+UI>`EUV%*^+XY0@;L*MIM^G*pVgvCg7S7Y3jqfkeXMbc| zydNZVSo{|2S}kz~kkcsN$JO;`v=A=usxCuZh;q5G34ejUMtK*oI3UglcM8{c`qB&Zaoa%c4~%xBX1?zFYk)_jam5k zbo6gX!XT1mT+8KMvX*bavoqh)OC!3TH$tlJWDl#7-Lux(?8(c<%r1;hpLY9UoBL#H zz07+PCzZ>3_3}4CNbekpbh`~}&y5GZ0OlXu82$9b-3uXo1kgIpb|DkhGjk;$s)BkQ z_wW$)V}uhstvyo9#nm3E;ZJP`0ueX?CM9S=dt?LCyITQ< z-A2g-J+i(%l3+Gh?W(EW7X3ELXvu#|=f?TSNWzFEMC4m_5d=J3vC1kr>$8we@_#v# zzetbNW^!owU_@#O-M{6tztUj$HZY2@csFv5(Z-EFV+Af#=+a{^>(%>6#$g1cvTgmK zD~H-{B613{&JGxt`kni}8FC+hlF%N<<0IfOdA81Wg3GF$@R7u`3`+IHaR`X3TXeUb<5Eqry_us3jJ&e^Rf0U=EhRW{gzi zl`mtMUvzsHEgE2mp%M;JOO8PeUNx>dj?Nx|Kk57+pacob8$+jp$^D5kHmN@n#!d5h zO5~atB&Z`?OGng#PqYFKIKY*99l03)6yUW!0ZycLVWtd)tIhrr+}jhWiIxKDnX#Ro zH8yTKXOC^0Jr)UB9r$HAI*@wd7_I*`tMzKOzDGF-szX%Qvn=+)(1zSX+sF0eT=WUy zQ0y7_>S%7FZhnQ;JngQelIZL-fX~jJ`lZ9$Jyb!3`#Oz^YmuI5We^T%rS5eqmyZup zK~=s$YU$yaOBOz{(gIfcDA=G=sk~1FP%`z(A+D)}lTy$n3r}hfFrya~qq>t&oiTeS z42aLbc^VnD+WJu-PlyaMR;#@okL@ZDV(Y;{rY|`bU64IMb5>zKc9Q1vFD`#Z!5rwF z`TFb~`hrG1^0>Bc&k1ljg+~j&!DSSG8yTKT-N#6C__vwVnZ#L3Icl)t|<(hxl7+esE8l%S>HGFq& z7DFrt#CmXU-Hxs33ob#J2uqxx~ea}mGc-exGZvv(L{c)?NZ)LxY=f6+T5Fa+BOLyY_!*i+xonx<_& z96vBtU5~0IUmtsYIPVB$+5M4BMEsXoVOkhG7@yv;^VxzWTokSyIB zZ}p-3`T-nF-M;coRS`_;Qbp)bIs)ZlIuD?t?bXsn>6DqbW`J6lPt_usLTx%GcmZxU zVqg%ejARv;`8e!6ygg_k*os~^g&!`3<#>Jk;5Ay}HQ-zPAcUB2-~p78H{ofu)9`{# z9Au?F>})ad)rdf5(UL)Cp1sqkXPR0Q;TJWteK?mw=d}ciEEJW&XkfNd6s&8Bw++%8 zCg2q!cID}k(92DUd0kt52!666F*zv`>&8$;wVIUQr3;a8K zPkg%{0lV-lQeJf@094PiNwypo=vH4~)Dg;|P6h2=lW*p1%i0ZNxSoq^#_=BC4#R5f z4b^2`3OHUvG9Y2Ue%!s)t*twO4Mf)KxhNCvSveK~E>J=B{CIVXS6lZwVwLsL2hq}W z>pRj^{9qB7ATl^J#i~^$d!0%&RupbO-i7iOyTW*P-gF{4#Swp0sVO(unipOxzsuA&94i}l_hqc5g zB*1xNH1M#yEZ~o6#_aX@5T1!|A3+Z?suQ>xAQ@cZU8&v-pY|6uhn z6LD+;p75?!-SI<1wL7Px_4Zt904S;GV7#SBfZb0Pg17M$cCA5r(iK9~@&GQ)3?-Q= z0@8e=A_$mAXBmu@rvb< zza^zf0Uw+-QFr(i>@^Jr4rz&pA=MQ}YPCch4-lqnbIL&U!>=rPRrS$mVgWRln1>7w zHoOvQYdw;a9z^1-^rz7F)=xmDwPaf#NpmvZMB0P=A5bi@NL2x(Q{+LYBqn1Gzs$G#8~OGU&>SdG&rblUQ4$m} zk&@mn1P-KJ+4>Q4GlN~vf-xw`0S9TY?N%0~5l3XhN5ACaPO8JGCy1aQSfO8VMo6-_ zTu#B8{cXNv1khiGDS&mcnU+uY&Ockg?d*g@lv~_=h7?*m`Df>dG&E{4I zXJ+;thM+pTi%N0d-|IkwZT7dE?+fmx7A70D;MlSN8nPF_pSS?Lye8JVpgw&?yIKa| zupgi^?;b{HWW9q#tdq;UoFy5(kYXq45~p9t113|t@vASplddK2K`jc+oZ*c{jp>qr zZ68uogZffCvW58{gZPwaiA&g-HX1$9Sb9^*73Lqo2S+FtVg9=kxDhl(+f*9ZSQ1@s zG;kttP9Vba3teP5O$qaTbhsD_RRqK!<5Woz-a1M3qy2r%#eZgSVmN~_D3xd{ri25N z>~Al}K%S%6e;PY2cmS3>p^rx7iv8SC&z9ly3T+?p1|(EMDRBR+03FAkmc0#mvsDBa zp^ln_^SQ|%VeXD6zjN)i*Y>=lceEZH+j=NR-xX?AjO4zakIfRrOX437L-<8v=YpN9 zciKP1=D5Dy9)zDu!aX>ly6%;O;JRd&C5NN??M}RrMV1{7&j5?O7<8*xalma$PB9>v zf&aqq$5UK433V>w}i2!fxe2Bp%I_~li;lq2dScEuj-Q_5Q zkdyKj+6LhVnLVNHp+DPG*@MW=N+@l0`Di07GD`H6_#62S*JJ+O&!9 z+ZS)S26u(nr=s@u9AW0~_!MlkTP-g59DpqPrWlXomQNgi(;1B%VL_AO#L8T6+h^km z(hh~2s}Ri+0k_DOXSc*Z_JZ6+I>`0jK@yKJpUAwC9j31~zT&Vab^>b0e-~?s!^jYy z?1{e5T<_t<-ppE*q@^YPf|u0qyUP!HQ$I08ie;@=A+=YeTXK!Lc&o5yWED%Eh8KGw zQ|c%Ml4@9B5h4rT`2v}R+rTco*gNp27Xo?RWc&i5+ROZej{bandr+SdupefLdL{VQ zL!R@HSlx~-MxmS(kXcYmOnMiUb$bycxAUD#Z?Z#z-- zIgVx^QU<%ZTD2%xs`LRng*ru?Hm`EC%OF$FHA~AH)nDkgqHnAiuG%## z{HY%~ePusVmQu(NmCx{vqA>DHiWiX`+Lpj5BcE3|57V~zf?>u0Z8ho_slK0R)GfwS zV*v&Y4X#7Gb2N&{t5MG{x|})))z8Os*yP{UD|w7TDrkKp$Mh8|RgtVMbcD>!Xka!x z1+4#g4z^WM_4wM;>BTK8ksX*wZO6kQ2u9NQ?dH0V#)R3fjEchY}aR!eAX~WHnaB(_%`kZF~=3t2!K; z1grzFNf@z4+;pxC61eF+{tj)ulR-e$^N?Y%Z1DL!T5#Muye(7*ng6{8J7_g6xk^4 zVSX^B__zQc*(f`{P2m3SvuLwwr!Ln9btwyxE+(NBXYv|U2;hUfM^JMNtDzH~;jo|R zdA}vl>5h)3v8z@#HuVesNhr3MY*|qb9*fouvD;_$L1hpZvVgUOZ7 zdS1ci0B1+lu4t7Gy7dSP!$Xv`%SiI2cy2(uV*~51gfX`zZs&xb(aaxE1;RwuOJkVG zTmVm`^vq0R3GN_ltzx2b9dkMzpw)eg-r?=Syev8r%QY8$iePOW#)3xJQZN--Q}o{t z$GgXd4wx$tI*pyd38j|YaJ!cHk*GXOQVDmgLIOX0gg*9~9QK+fwiN_yc(R6Bp%~ps zdS(dNAQsp!bLOOGD6JNRCE1eV;viPC-{`(ziM54T2;k9Fxdrz?=?Ane!xYFs=T{AC zgf8j{w^q-lV2rYk6;tgF#1A3Ilg!~zLxG5n#ithUw&)Nxmv?V@+~kyc^{N(o0Un&n zL{&nU#G;1?olbL7%MYK)@jwd#)FStrfq&P{X&mCkhr_K)P7b$f$#*FSsKm&<;nIUz z{AHws!~ly0Oi-zM0T5vdT&wJai?v!U+_ybO&cvrdEdw<3A|^*E>=xbeBV&R67?rkV z%I(lZv_{%tLkGeKwL8~vuzb~%C8#Xaz`x-p{$2fL3^w$vAxX~AOyBKFSo9c?13%895uep?DJYXHyf!tM? z%^jEkEMm2`^=ZF5Y&WwU@DPpyC*rC(bJFdQ6bHMcZ$|7x80j!OKSDMIA*fETvV#j6 z?O)(Gek6bOsqGQ^D|w#4W|k-Pej`j-Ac43y&z_EA>f-}u~h@u;H&#T&0<9ZA;l()YVw ze0Q(?>b{A)yLUuzZG^sHgMAG^wRHuvHZg1JdCYJgi51B(cWptrHc(4(M~t}& zf(4GYieKdFAgO(8Q2gDCbTP!+WvJHK9(48wRo|_hxPb}%9!{g}Lcjga8|h(LYt*ON zFjByw^(b*VTHp-n2^o@XuKp$B3`rt9I^%d!#vz+*saHPFIIQJ=&K|=g`vQQ#+4t1Y zL2TCoVGu(U&oDUEO!n9*9BO}XF-+4&4HuUE2fSl1H5OF6v?YalWT^(O@ zXmzwav3u1;xUzC$cX<1GkRS;g6Sph-T4_>AOp!ZOIKH=x=;Ky)@Pw>IKx6zN>`@rtG}zp4K^FM4OGfww;#YR`Wh-Cy@Eeh&B|n5+Sj}=}fo{^5h}-zd zkNA+}EAT)|yoFpw6BB7m?cy8lq*nyGw|i^r8JJs3jAQEH&)j?ed!q=9$)92`A)9Pe zOL`QPoyRNPH3n}d3|98z7otEj3CB~i${IL+yM}@81tWkUu2N8_xbx; zsacomN_tm+1Tyv6&oJr)HchNIy}!>SL?~eMS$p7GJdTtsL+w|IpMo%IkN@{r*zjv8 zh+YUFe8mMq^nw8Z5Ghc$ScYx#szE<~HfqOs*=w6UNEgd&^`8cZj#N7>hO}40Vm`Hy zmewDjAC5ngbU3eg?r7}z4?`ww^CQq{>LoA&cgKI|HK^nnH@~b9oKixAn%eu9lxRiv z(3fT#UZ*2#_OQ2zy(_Cz8_%Te^#$dBLjWG#pM6>Vx#`=qwq=_QzrzaDp1XZMTFY4M zU`unN(AxR<;27=1Z1^Qs)7J+;Aq&DsWNl}peE=93)M-?<`SnaIjt@Y_BA5~%fX74@ zaVj0y7|07!v*!)Q!1DmDe#XFvI5AwA=>V|7V_%4j>$3|}=E59@;9_I{2~vhyV=x$f z={REM%M*9wBx&8lfCH{c(mZg)igXQqLH__36(Mct_Mv;#o~EAMNm%8><{U6ZBL&R_ zW98|=KoCvGZ}OMA5j{`Y16~Sd-%bLHJSJR&(2;c36_OP@rQi{W|G5jflXRvuBZ+YP zvV5GU#!LJ?U%0(}ND&dbjtOl!Ma$pRHb-7*wR~swV!KYOOFqj^wbjUP5zSNMu*uDh zp-y}-q|cCqgCZ<99|0lh`$bFcffg2TnuH}?WG?8!c_}>M>@!Mhi4F3NL>({7@q#0` zQZ0KE+7_x1f*^7XuxeFDbt znFY0w>~`t7v3+_yPi!cHj3dff&Qya0P^w8&wzrK24lDGcqSD?2-VQa;&~|fPH~dPv z6z|j4wPO?4VN;3{UT%<#us#Q-;mMYA@bwD@bV@MW=p zV)R)#99^(?*EgN56-7msi$ zXFTN&!o=;c6AmM`HQBxD%V{)Bd<(gWKtfAq%W*y8qi+ltD7N07L}_n>OrN-M6u>=p z1-GJ~q__ZYUj>tym{E;DxtYm*(1$y|S>5?=x;s40NPxzhNRU9Ww!s=U|Ag^~01iUa zcWR#}>B&rOVY(7id5y^8hc!|vG_M%Mt^`@8%e(?$qQjc3YbA#eIE+5PBrQQXsBf34 z^k#K=6Abp*5On1bE%6)l1K1~?8b9T;DJv8cd^g{mB@!*7=+6Q5S)C7#%B;?L+!GLC z&z_BvlAqgutPVU1%-?UVfmqwe5Agvk?-F}4Iw##fS_*hQOA1)J7^Qa!B5H|ZmJ#Zx z7QRR(IN;rd=03wQ@e%GvD1l5+X^Z^<`aol=>u0}P3hSzBa(t4NG?+f9Dnn(r*tp&TKZ1uU$|YInbKnR=yOf! z@$Al?tfC$n3_a~X$6%iSTGn8mXBkoY`r;>v^k?Abh@%E`J1WZR(Z7uTyn~s>?(Ct? zr_5nT7swk?I)1p;_k_;(>9ssALH)d7Re}q_JFYUDkwmSeUejj05Lv$wtuIJ zs(AOL*46_o4(}}P{@({)L+qCuOXNr?dP7&<8hk)kUHr4IXEjgrl~TQ<<}6e-{SiAa z_NlBXdvpC<)QjVZwvRPsh1)+}Y7)5yC!7iRoTRHWwafcBlDKpO&^QWG^-jI=eJZ}g|D1r}JC&0@ z)OI97$Jkpzmg5%g5F{P8zeyrZw~4R8+Thm`m*L$Wk3V`xNw{<6_hEbwY0cBd^wzdY z6sZ@D5&K}7g59z7EFO@e0U>!IPzpPZC9asKB^gE=ffXp%Xw4>n3K4>;E6e!cmVF-` z6}Ko7Ad#srUQw(R0E8=owJTg(#~@f*g7Ss9hHA}w<5TXvX4Nf95^wX3DQfrKN%Xn+ zra#m)v9)k?mea13h)=1#X7%tR(jR*r`A4fQ!x!FU#6_>k5g5pWBAYx5R`Ta_Xy`r|%@ zCVHE;>6BinVa;0%Zc|%NOo@#*OscvJuq4RvG>+bYW=JHRW^W1LR44e*h{QEVn4mzI z!%qr?_4t$xLVmKjvY()hUD>9^IHrr`$o~0$Le??j&*WlCTjQcCW zQpbH6HQM)}Y$JCHoGh|mAX!o`x%L@lC+us4pT2?C!B{OxJm75&k#{<{4rmobKWGu$ zsQ(i!zPI(Av9=%mXy)*q64QH@(J=LrGJ7u?r;7$+)X=tV4@yCx@D=Vu z5g|}>(Z3lxFYHlG9> z?{v?i$Bt`6Till!BoYCs(1%FK_*)%(cROH$PQh8|(;wj6yUtwCbHS`2 zImF97aAig4pmOK+m;5%CFWoG^kLTZ|JVjaS3Dg0A+1#OB&(bSq0fnF7n}eSc4*_Qw zUP3qoUSb|Qs5Qew_}~&PaX;80AMU(a9=;Jh%^F(kZcu=nR^bF1gCHOR0&r6Ae#!&* zHT#0xI?0&=UGV~xoAOQEsDNL1zOa8F${y=yBYB}Z!gWQT;!Z^$EnP% z6h<}l=laW3O=r$jzr$|^aNhuMG1w98K1WQPlEwST2^@8amZVwDZbr7AHh$ESBbY#T zRCBX;yq4UBr!{YR07p5l89%sOOEScBGjmHmWyWu2Z|wE>q851edZD@KDOQfvf?J>m zSS=Dk435<|WxhtMxEyde#<_)o3xU?j&4abOw@Vc>Aty!8L8)On4$e-HjSCJEEqNTY zt@u}88~f6Ai8+U8t$vZIB#ZDu$Q5XE;) z8EB^N|2}4FA4jnSic@Mgh)ej!={gt3F5c1&VL3$HpG?uD7?a`izV&Pna@ha%xA#3C?g);%*AL4Wa zp12TEg_>srh(y5w(2WBE=!$FrO_kGCu~Wi5;IPpl`ax_eV{;#UN|QAggB?gqfD zQ{gWmHyH|I>IwBu;N7i&C+=rNC87}A<@NT|+V#wT5Q>_8CE(`hH(B^k&<#cP1YHO@ z((y{BV8|Rj$TK%q4+Av;Kd$ii@dyu3`54L5HU~4H=mW7%VzW`zN#=Mjo-^4HFPzma z$m`B2h(?}5vrwZ@lQ)e-XX7jo1TI$%`*m>iG6r8=ns4u#A3P>N(hpeVx0 z2BhZp9IO%#7BGLT>hGiB*?y37Z`3d%dB%-?W05cP3I?`r2Qm;|2rNcECK~x8zaawX z?a@J@E?jVese{6Y8+PI2vCZ(7g|{vHink@a4Ph4G`KTI1^?K0o5{bF?URutfvPGPv zI#CVP_!^eDA2%GK8E<3{_It3uxQBaBBYuzEaGIWt06}}aKY&#-b}C1ioB`E7dIDgE z7+%H_#1IL+B4Hvq@C7^3-1BYlHBomQ`U+*_)?60S5+w30xWtb$nknov@d6<04JxZE z@X1|F$l@^Z`9O+~zT4d+lXARWj!agf#%02doG<&^4hJCppV&)@i5OD`!IU!)^T!Vs z&;e!7Wk}&6s=TM5KYR4<+G*pvX5REMTLLVrg7HtZ)f2FNdzuJE3--8d0Eh`v?oa}6 zJE(~x$n?!e8VjC9vY*L&Fc^C-o|39}rGA!84IKGnGU$N}105A&5Qrb?1#O5fz&iK` ztm|JgcpSZqi|qq9bg=hQ^rJ0%sgJ|-X*vRR9D8lwaUBr(Wv9(e$Xgo_Z98j%b9oI1RVB!OQ-&SQOT z4)Os7@qeH_vcCj!q$(!at$$xclnfBU!kUy;FzseA5^RgyfEtVCkjqj862r~uOwi%> zaM-9iC?cBE1Gdx~0dsx5&x9PsUtjv}f0@6opM4a6t^Wc$@A6j;H=Dn{Hv#WQ^Vg12 zBx4>PyK!Lts_^}W_Q-~s{`kJ3r60cQ)9N=0zHihc8;-{J4aday8~Wh8oA1Ad?*r?D z6Nlv}6 z|C)kc5}lGo^sAjaPOlsX?-?C2#OD_1@aCBd3Q4UD@*48bV*x)sYFJ>A&p>YETQtsC zrB^@wdk2KvUr|<*$|_= z0DSG6@&m6TuU_hh+-`LfTB!GU91+sNWdeHHVKA_M+796bM&`i=n)Ud#=~EriE)>;E z3kLCnUOG-MElmAfKWN>B%~X-l&|(9!q^JhNm=?}mevh%#C#Nk<-`~+WkD31!<(XzJ z&eW?%4rqy+u$&RUD)gb5e=pXAF@2FyqBaEyt9O5gk*OrGlo~SE*nSQK!Rp3fY~Ylo zKK*Nj_8F+gtU$atpIyirM~QE#f-iUnV%@1zhn&qp-MQW<6kccn(Gl$>BIgUXeF!BO zV>!#1SBZ#@$2YR)$Rkc0)4tW+gZOyy9eGz)*+agHj+7OU&F#Mdl&`#yS>ecoc`wU1 zW{eJX^qchLahUXZi`vWR+3IO88_ge4f8D?}nfQ6|pHuj!HJ9V^I3;}LLv0BPv`eDL z8Rd(Npm;awLH(Ju?C#)u27k*DaW4uNL{#I*aDcGL{*d1=`%1@E${;YwW4((V6j!rJ z$4Bg2pH{mKIl15YOZ;}1d;MB+EuM(d-r`c_ zqVCjQ<5HjAWreY;?c&wm)E`JG2XnK@X<+Hkv+04sO|c4nHK(}D zx8xgrVIk;s6fA=MU}3|a09af`Se)w)1weFQap(}b>@=|S=SDEKjbomKLTb#hHL1t4 z$HD!g%IFaJn7T*w2j||BETH@y8#4AqfB@*F_**ifZ8fet2*6=|DG|%TPs){~qL3dC z;4rdta(dOd;ZK(3=%2(t$ygCuMEs5q{lBx%B4IOj?+D&{3u4J%@nv;Hv)kV)G>ucC-Kr~LhESP#ctxtd$ zv$yD#g9usuU9rgcy3ZKm_Vv8tssqb%5vY^Bor&I-tKJ^ZVH)hJPQP{ZSJy&Sh;erS zfz&3Kh%i&drI9F>WOV!c_Loom7I84r;k#>XBsL`Z&JX-E zbC}QgezAv9csrQZQTs2*B3N;vU&onTss!JQ56?j(AT#EAwlhdNLWLu{nO)F~U>+@B z)LxQ@OrG`LXpEDb`U&R~>x*27#V|fkf&BVu zWa9|WP`^RoDn(6c*%~|)yAaz%w&GJF4u#jp-vfR9Gie&jcO_v&Tf90x)jpj8z`IJQjQ$@LOKsB3V8SP!;^ z9NQTj%yGU|c%j6!GJWdM-083qp$Dmr`m;Et=%qc_^fhrzJ+JDKedrSRoV*>fdge1T zJe+wtyuiCX^a=5?OjZ{9fe=BVF2}Du1tZS>Z=)7-=C}0@efFCOzQdLJK^5jZVktdr zfkn@;D=-zw-W9Wqh&4#Awgtbg5P<`W*6rrazmjgES*}Dxk2Yq%sb5iG`V75noH+8b z_K(ma2lHzGy2ha%j0Y3X(O3_w+8k&gwO{Wo0~u0xX2%oH@E1tQiqQL)4GLEtjuvLG zT5RYGT8*a7IX8ahdk#uq?-3~4k+NL*H&Y)f8&*8SzwBhQ^st^|lpgL^6!KeE3w@Ax zbn1DxzIl}1tAb|Uec@-8ouWI0#-X=7?s(y_XdwK|a-1hXASZ~`A_Z5b{+=zLK$#3c zndCwFT>UEFL)@>9o&S)mxie(c#tdIb&Ms8I%b-XftS>Es$_K@dTA!1{AL>xb?fDcC za#LGLKVY&@lYXg>fw{ZOyx!<&s2fIbJT!z-Kd?6hOQ%{dPP#w!R(kyqUhG+bD(_Xk z;wpbsQx#^KpW{cc%rj)cYFw~${bt_ndXPsKFe%nT?C2VE{HZsY1CbG#!>4+Bz4IxR zZjUUYvIsn8NP9^UmxD345EiCE=py0+NBmXxpFeTU;SPNyy@DA8Bwa8nh&D-@t>X@1 zvlN>N(J|8GXnk&;J~yb(4Twqv1qt-pC0gnBo&6|C$incA^7)_vk^M02urKrS!t+L} zkd)@kJbkv+pTF*>Jqrt|d=aQk9;0!>>J)F8z^<9Tb)W`#f}b+aqXeOtAXh#fNB2V5 z!{$1oJbH7!;nj2Ctzg9vAGujtwHEos-`D5 zCkMSGfH7L=eH^3=IhJe8%?AK*NJVB3AkD!}?KbXNsUU+&Bzn&%4~DlPG=!qla*|cx zYPFp1%2d&3(3pJ~T9nXNP}f<++`$j|0HWxr@s0w0u+{o~ny}y>>yh^2J7$T2+L)sR zW9`-hU?@h#dI%N6gfczs7*4+f*2= zo!4{H=cA|b;|lCt>e)GJWhwp1@Yo+hWTcJzE&N*dr}Nu9u&^FH1|l}>na2g~L8uV5 zOx!KEf0O*8T6YU-`+S7_#>On&Gp&1gt=eJP5lj9?Fj65!Q6>JP=)6!yZaFRX+Q+1NQ&Lr4aFD=3}U;;1= z!mUSBK!TNaC#p!#fJlz%iP$3(!Bc4i&%^9Jmq^^oX~ zf%wDZf8^ib0jS7t<=iI=1qncfP>1OYBKWqw0mUdyUZOl3^3|{ZJj@_~5I6_#RxLbz zT(Ml_G05YNv6FHjMc3%q_|paYaO*GM?X$)+3<=^QIoK6sOl1M8$7|69u#5rDu2qkS zq+3S&y1pc+F9|3xmHYJaJiS~9WBF*hA~7YPcZ^_tdhSEAj&t=TdGYE1kZ2b=BW>hE zPK~ZG%5VZNAil%UhYV!L_t?n~(Nq&1gfKExTmtrdV3xv4V<{pkt~#5Pw=4j-0n>xV z6@j!gWu>tBQxbbdG+*W_r#;pO@}~%+^=nu#Qm?3L&h1;xjDTX;oQ(~vMoYZF$(pxF zIxtXeRjlpLS)C#w=Y{RC(&5x;F!T&VY?lv?Pb)S{Cydj|+* zvA;qdS+oCT=#o5MTCMhba0lW_crX~3FT#zJc%aMG4NSml+7&MI<9Z`ljf@Dj=S#b% zL+JVnLSclxOADz(>D>X_n`LhEO?R6nsr_Ng@oBUh=sN{ny^-%uE96*ZbYyhyF6>?7h!^tiATy>$TS=_=Swu zk^ZphHpK)97R<8e%(d<;lbmMxOBe(Gj_+jYS@PYSFz14tYe>T=Dha3bwQHsE0ij6> zmV`tm|4jA$iFeBP#2(x4!%IN7bmx^)M*SV5vWb>J3I^ zc5xiPNxse%wF^;Zcy<=Gus4Jqr$sW<9^~Aq3K3+(zEg?_MRS@P*e@))`GbJh>lr{XQk09 zjc7i8Wpl*Bi0!mKOdgW0^qpYBv=otk|9MyD}7OmW;YW6=H=aTh(T%R9UN1Dr;Cml~{(_EQq+k z5P_9irSSH9t#-ApjF$N#)~Sz6o;^xc7Eh>ZexN|kXIKcOxdnxAk3ikiMOPPTSz_Sg z$@g#_kM3mYEg>w4lwMjwa7BKn*^f%UEH!=to%0+IN7b4*Uabj53X+#WnFLbmTWdj> zcr(>VHe*=D=o=Q~TMHuAf`WSj&y9K6n~nOpDk8Q1LOUCulDv&-r0?LThcSvE%gAel zWPAj2*p+u ziVy|u6V!fg{HFCDP&Ru+Ag$G<$b(7U0Yy{BD4LRbQ0ywxLiX{jGZaI@R8M64f#%ze zYK91k)oHR19TTz-2z;`Cae-Qt3!VG|YeCQ+!?lL?f>3lPd_z|<4z&`N@~L?c-!l|T z`Nn>d_QHO|%ELzUG~TpQPEBaCgm+J0pybm+lstklqUN3LX?i&?$$|F!B~?s{A^RpI zEq#N}={ciNW^JecQ{~BM`XimiNM@SD+80mb0WWi9avO!uZKJP-JcL*<8@iG5Sf5Yl+3|Yl=nDDc>Uy zs~eKxKV23c8|}Z_)ry;glmRPYaNY0SO%F-McYD&^$$t@=zqnA1i-8!cLOzkTa2_VXLkj(~-kYX<1&6CXD!KaH}HA0V_ z%%k<@o(DKDEhdDDB0S(bDkjPhJ1TTt6_e%7s$f9BtO`8Vc3Tx_|GM!n z(;s|YGCL}+(I3oJAH?sb9reL=q8(~b%I{S|;s;Axny=D_%2hQi2>BJNkB`o@S=PmK zfgI``Ywne-fp(ydC{pT)A{Tq2h|MQL53dkWnM;{X=lm&p^ zq^=!75%!7VC(V7sL=G8|cuTGB)SJGbm9cjY>A&GBcUw;Uy z?`nFp`c7ZdeUG2u_jycqpH+Y9c<&MVoPEF9p#B#7@SdRHO%ws(J;j6f!1tur6aLrW zJ>h=^-lA_TEA~{5O8h{dI=85FZn0kQHRHQd@B7mPeJI%|&g&k1^z~n7*|o|G;ynwY$%|-UoVyj{T8)bzD5VL)P2b0uyfeExv9DT2t-|mT4~uRQ5kEl%RTY( zK@xmw_aJv6`QGz4I|~gpZcrI_tSq&e=Vxp*F9+o_CF0lG`ZVZ;T&G8<4zAkcAX~P_ z*6C~_VbMSm_lN7cw0}Ew_*$Mf%5m3v6A{<_{fiiNwyu$icoh_Ki^kK~=G1Q_yGgQt zbeMlUTZelOb%_7gHPDHy@k~Kui5qgJw?`U}Gk84T7jdl;M|znUvB2FU9f8n@NG_A( z{_Kl+Ipc||J|GU&cV;aFuI12Qtu6xrW3XM@QT9Jl`7> zWjnIJ+Cyi^N3WNOpPIHy(ut7NEZn&JSLIS2HXo>={X-p<-CxfgC9n{%bX4x%IFdI{ z9lvs($oN)=(IQ>Y!7gm;c8_h;yx7F!&;Xq}w9dHV2y9IL-^lK@2HnD86vkA8&@o9vspsZE*0GjvVF zOyge)IvLVtTSXjjQ_A|M)|citS@rosFY5~i^Ay?g5$$>LpJcn$ksIyh;sGjJQz!A@GiB7Q zu&Y5PS*8^5%Z%}KxHIDOr&Fq$5Mn>u$S%Kmxa@iUG^^=7Y9asv!S$Mxt%w5dqZds7L6JpxIJttwmNB z_w`|*z$Ic!>cez->}%p!pXINt>2evIr(ut2e0yM}QiWP$;(1ut8n@&ya%)U#kJO;% zN8)c``Z_I(UlAceoqM=rBg7E4u15RE@sccvrn|eQmNS-Ofk$W)FZPJ?Ov6%#;SG=W zH|av5Iy98N$Wuvb){ve$sHup2SR$mlx|zG&>n8Jn%!?&!>P!8i~G}hz3SvTFeXXR3|?Q5AtKwqJGVGb z=s-vOtMHbyhW@bg|Xhm zi{2%7sm+-l(Eb9Nz7+H1JnMk9*LeC^>(#ZdbLn~84ydIHdn3UO|R4-PcmzR7&>>!%XCMaOLVf7 zL4MF>hj^OgOP;iUJ<2`Q2fZD6;bQKF)nghw;6$#Kb}o|rhp(3Hf}#GtOq{nnNJ~Sz zGw&n;lA>cB6}lG{x@Q%t`Hl+3e|A(T#wB?@)d~EyYI_1T%g8;|c7_JXKI$%$NP->7 zCt&HN+)(AV_`{{%^!P7@OUY8HU3tYSeDwT>WPajjE8JUg(Py?eoEG=yp#zpHe-|C> zs8r(;TF^882s4=4aio5&1q!Lhe8wjib8Nn%#}#m)e=%g(9! zhn5vQSrwCsoLSp-`W|#GBfI)Inj)>|5$2p)vans~4xRrrq^2;UVKLe}qj^7#x<8tu zHSJ9zpUdvQCeysN*Mlc^Gz9Uc?^A^OgEu1>I@JLY=6(mk(Ok}a`Tp8YXW1|ak(4@% zrraf~R469&MeS+d;GDH=)FiP{yXO!dq~Vt0V_D8}5v}!+ z2;<*dt6(|S-F*>fL$B@B^N~CkRMj%N_*7~c{bd?G2$E;S^iP-jWm!+6JPUfV{Ma`y zG&`nP%OA0Bo{wl>`v}s#h4&GneYqCx%XRT`E!yLZwETIrQ61$q^0^4>h#wMrDdIvo7}F`d6Xof zS;>tgeG2_JS{AADu~G7{M?7KEI=l@-mV?>7$LuU)4P3f1 zJFFe49}o5?$zf0m>geOJ+Wi=arz)*+6 zP=upf*rv+9WmQa1{iiB#&&2UP65DDteVaW)x)t4`)cag5t6j?ljb(PkU;$=|799;1 z(dpXI6UQ5@d9$pUbJYc`X?_Cxr+99OmHgN~E~;1d@hwHxvF=mcT=6_D1tc8W-yKO(?y6Y~$PipySHPd_Kr4WxupA$Z=&8DT9jq2MOZ1vYKCM zx(|Y`nAJ-Q?H_1<_0pj7wZSi^K>OMVTquhhNZPaj^=(^mo^n=E+p^IV2F9P^u~HS} zTJGd~kO0O%4402ooAUQaJt0(^&Y%Wxg|@$Z4$dCmq%zr+9(o4$WVV{U&vy1G@IJVX z4?I^6(ez5)-OAZR>XvDC>!{x=u~7&TO`bLdSw=g1Tu+jZ_Wt_u{UJCABNQZlhRJ-7 zb^lc3&$p5TaM_@|S>Tvd79I_Oafc8MK!c#XdBe zjuM2JnJ>N&Gb7egmbmhW;6~yrJ*NnqgKpnOFSJXXL9vE^l^Tx$=du z=H(|&)b0?&DULscvA#B(L@;NFVviQaERyB)^&F-ZHI}l(pOa@%OmlD(rJ(Zd5F&xq z!p_$7uiv4(9r^(rZ71twBK(ReuDHAU3|}jE52NWcsbFS7@Hj^jupP6>mkp4FRrQAms;Fhj|UCM|+~kQZ@4z%5ezD^ly-TdOoD zNGi{t0%qc=j|u z4woloA09)_k&OH10j&5-gpoZUR($bV8pB)jUc0t|3nz)9DRoxQvKP$Nm$cf=*U9Ma z{4RfmL2UZ7Y82x=5f9|?{Jza%wzt$hE{U6LuJ^5NI#YdhqWa3a+0`@ItI9-mBqn=) z0!rOvsf$wdbtz-+lx*9Pzqqnah^hCLMLJi%89#WJHe>2rV>Qev=WF}aO)f&KjOHRz zJQMPw2JT=SY&@=Qz;ZmhNt=zANngZldQE*ET7<(hJ4o7X(iVPUNH6O-z2)h+sTE#j=00ayBRwvp)FY zJB*|)`(nax+$8vK`Ti@)IxU0!zqC$kG)z6FF9J@E5Mba+@b2HdyJgO|?-fjR@&h!_ zHAf5ZSjoo>i37_rg4F}cdj((ZN2Sye+#jxl2p-lNsWecE^6+du^GKY(`%pPnGe^`=`4Q6l1=qCcT4P$0EmAsbx#X&lZKNO~C zylb#}cip4s#7>*MZ{T}QK(pC>g?KZb8Twql)ARu+;N~5WV((^EL6Kx9@7_s`^m#bZ z4V6t+j@gHv59Az1f3xy0U;!PLk9G}d0eYacOw=SBief%P?(B|$cV^c6jQuyOzUzcA#a5^WD`Zhmd$E~<~dX)l~>*UV~u zZ5fq>tp0lf`+PTBb5qz4@grWUMZys)&NB@U|Gb#*1?L|dL%t&TK>?aRbetIDl}+9= z#}9*apiBJ9Aanc{!Rf6SGfaA0%cTQfg@+mgW1TrG zCc{R5XFNR(pPbMg)F!MC(4m%JUzl&=aMXqZyKn9}_3WJoxn0rzu>z|vdoA)kDSWA(#t^JK} zOH6l9`_%ZJRzAfS|KE$=@DIM|9d!$*Dpo6gipV{u&|1mHLHiZrrnVXt7Q1D9iG=Q< zxbEPuAW+H68LvkL{PFp)(6$+4{V7ea#is)AwJ%9T%BQ!>WjJPc2&p%5Wy68R@dXVB z&W>NwaNtYv*$oH296#TeH>!>GBbqk0v?n+@**j3#TeD&`Mw-Q{PqH315C3wNpR*^m zn-X}(=Eb*3IwxWGDpfe`Ss!Y3I0r5V{ZsNx$R2lpFd`c1lb(5h!^Ri7>|<&$mg5Kr@6Msq?3vJR&e^X`*!FQ4Rb(3{t<& zBxWGhdD;;m6;BKd6Syc?Stkjwtcpq5AejDXAXr9yfM8TcwiyD21r&gG2)@a{Cp-)& ze&^8(-b3= z76JHnornyA^ubuy(%*Q9=&fgCeI)tM0usvIM>+LrnbDjeEqMul7)o*hzn?nZo&Qhc zUHNI_Ra1D_c!|TI#~Y`wYP<*hPG`ou3?b+<##@GRDk(g#PcI*&yg%;m$+%}o<|AtV zl;PK?;p_K7^k*ho7Oqb4gY8U^b;?;ByfMVhQ-bY8vV-kNdlGNw16k4)MTZ4li98!X z$>>M2OUo;n^U$c>HztY*w!kSMvDmM`pMS9Nn!pPhzkL5O=GCdFmUZY<)W|Yq13)HV z`#XnGrM5F3wZ8oFYJ6zQC7#gG%KdmQqnI=BM&OvnWuG77dr!Vn65b+-$8nPk+}}T- z^*0pQW%<#~a?eKW5MekDnTPUMeuLM`tdArN$GEKW#`?U9Ya;N7g&@se&K(SJ32Lpv!*vzwI(yE9V=YxlUJ(zmH zN0cMN`v(f%K8hlDc%VmMmATd4O_70%i}gOJ;ji@*g42pEDpJ28(N{=C>>=_S0o#QU zM2`>dul^%q%jd=4C}QS;gF6xmqnv@IQH>u|B(fILTzWl88&Y=$SzLKncrsZ3;Nc5C za3b>N+n3gKR4H%L(K$K4K41-R-quk?+jP51!Ly?Z`A^<7tgBQr9aU;19aRN7Z<78g z)<25z=%~_cNYzA1{8mTRbnNF*^!79LKO1s&D81J!B-9o4#z2(WaxTKBtJkD^-lyIS|Vx=0t% z9j;a#wr-V<`FNh@4~P2mBEIPOSR&SE2gi!Aj6IK=&y9~&EMxNHW4*^&OBv{k6KbkjPm7+>Xyhh3&2N|0@}p{RJ<8Tw5Gvx;D8MYwL* zZ2BrJkZkBfqhr)SWC^G!(J_2}IaA-fVClSYU7`4sNvU!S?Sn#V0FSITz|ahHd!+an zM-d<61l9ixJ)BY2KzNLigva;URu2S*|Ff1l7{T_L_CtCf*g#_@yUc1H~Dl-mZ)SjDW*eW?;5v1PH8UGtZI5T zaSm>GU(!}D4!YFgPuQBV>SV?qF7q76M^?T>&B)Z5aJ|=hLfBgAOl)&}uI`+L>d|PH z^DIXk#mnOu#q2IPw9g3j`&$a7y6r0_rG77qZq>iD|Ncm9UASDY{wh_=C=|0H+4IOw z9#5)Fo0eWB0*z1}0kC^DwIv@Ct<58fQOTyKA!K&7kgp0_y&;fch;! zsn7c=`lC=5jqv$H?vL<~w2wJm;;@?iA&*r{9P4rUbQZr30(TRb4}bc>{Zms*m;7N4 z%sngBF?79Y`O`f{=C<8c%=->c$jropM1OI3yqa@#yqc4Q=bkBtWsexDMg$PfUU~7u zflo^>J6&F?P1rK_KM__h4w`Kf<=vLZO5s>%95ZX`BSQG*`kZ8%e-)OfHq4QSkc4tN zJU*tPIC3GeJx>DQj8y?}j^jW%$7Q3fnAM}5fXe5`B{>{z&AM+`=;7zZzo|kqwd@on zQH7BS3EET<QNh91iJ8PDg$zu~)VdhMUT zUw%*+v#U5{9=6_$Z8ugQ%__Bz)}gx+qluZVgJq_`@W!|Fxo2)I$cepD_dWZnV({h? zR(1m^?xwMx*AL+Tl2uh~x(!y2vLp(6_6PApuzl^ zwcqM>AEn1?$3nn(Gxdahkw}|=Nd0I{?3Mbb?G>TuD^BohsF-D!yfD)=$7tdYOUx|} z9CEN83qjkX12oKO1#hkgPe+aJB=Ig@_1oo*9;y2G( zg2hAtz>+HWwcdI+c$ZeU*HgBDBN3^;G;h1{m(J=O$nxVt?PI-<$#Y$SwZ#b&f_#Oc zEJSso#(m6d-U`>+x^-Xkv-HEbeF-T;=%*7rp2H=>0$V5`oHYm^l(&C6o|HcT zEghueN=f`!^C)o@J_7$O`duCWJ|qq~EJ-;ua4Zodw6BmLHIw1-2&|S&AbR!-E&>2# zQ)P@s(?@{BzAE3oC}OX`zmGsc7a{ofM)w6evCgiNmAZF0G)19c>JewwNEA&yNUH;P z2{f&CmBBW051ZW0GOuExQ>O)$kDC)bL5U!%g%EL~k;QaXB)UJ^?*t2+{1aG35)6aD z6p|oefQT-m9=8i=uPU;t3RF1AgrU}d9%tIhEY4Q8mtGaBBaWGfAnJw5^ zL$N(|!>r3f)-H}&`v3%r@$qaV3Iwr_mgaF`iOqL{npyS>Lc>oh;0%exG8|(~%jf)s z$AF~cD~s}!Eh!)ERxWo!*cY>V`C3xegW{Jt`M@qA@g(sNkkb&6M2!&g2o7INWt=l( zp6l10?G+H(GtIX{L?shzbWp{cxn~d}sEmAjMudQFvLOhK6Oa5pqyym6s%WViS^PY6 zc|Ptnt4Zf6H(rna@~g2AZ~2NePyJ;C0qsFINjv;@TIC_3$mTn_aYw$4e^q^TCSL_( zA1>FC-Bk6-@5o$84Lvlr*dJp1|fYucV&$dU<&yDpoNs6;B)))S2aB6j9!J zME2*sIidJ55{z>6BGqs2{2^`w7Mpi+606tS|1E5fK3XLQK7~R2(zHGzpl$Lm~U$ zLcBE|kg|!_*B0;Qp!LINj|x5B{UuJbjh6RyonmY$;>j8(f7d-P?~jZ_#MqcPf9-T0 z*5Q{QTKlk08ly(NPSVXy5oKy33={u^7XzpR16`?i>8K>}=<_35F`zn^{1%1O`#Kp- zQWR`5UNuyEoN9-Ajqzg7St~88w$@5FGh<#SU0*9*U8lOf7Y)ty%-F9VIb`hJ3Yg5o zSRaNnDdG)4<*B};-dE3->SyXHZy2fe*53JHbpMhSS8U?gzBGwvcg= z9C=)kMg_Ly3zTLR_0T>#PRw0-raMtFjiqNmCpO!&-4bpw=X6*+f6CN2UOsL)_oJV~ zdvMCsL@ySkhAFvI6V2xSP~z)wmLkE5&EgX;p8qhKuOP>V3k7%# z;Yp(OgZywFW28`)t^?{^gmg`H4kQAt**+ra z@(7#lEmdhruy3F%*+7QEKTRbsPrnLwC%-744c3=xc^CPRqn?ViyY&D~?=)}<$;aqM zMqcptd%538j&n&|RE5DT$qBL~OZ{*AExenQ>vysm$JlIn4SU|8@6VDCbC2-hF2ax> zLH|;)b_Yv`#W99ffSwy~Gw z%W;ZwNDtzV0nGd6vOg39Nr9*=D1JjK#t5vud-7c%(~l2_A19ma(7@4 zgRK(WP^>D82tlN8l{?!h9AWk1?ADp9jLN~a(o(c5&|5leBTwt(wQkrtdA4@SsS)o; z@?r4w;zJkOqk}-f9w(g&bk3M6_ih@u76OE)a~c$V1RObmm;1H}Y;tmna>lksMd@QAF&f31z+x^PaM@R*kg#xuaPB zD|*yUNb3Uw@q7B(WfVr!WkOkgGF2+54{-8NNI# z>j(KruG>)4FNbClp_=Ax%dQEu^>vLWo_6Rlp$AT3ZX>5-A~`G2DD+#M-Z2V6EhjjU z@q8|9a_F^OgGe0xr;tTTo=vS^V_&6f4{YZqq!s_+kK75KOPDWUMC}rjK3jfYV7;Ov zh=+xr{3%mrCe2*N_9V8a_g^`5 z$($%A^{)e;P!xUFo|LbzG@6>T3Oq{z=hiZM;9dYVL#*#Gnr2bJA(TMx6T<&lY5y?` z_P>niJ)xO5evFX4S~@OKs#b^fLtxk@&^^74i{rO-o-nEDzIBnL(TNsT`&{yLI*~4N zH$^g8QL>12_>uYc2xs*Kn&C39E_xs$v-_pt_*4heGq%;+;9}u@?dQ9(ujLt ztej{PEe>Sl)ivg!V~tx|>EY92Zu8_Fqgf&x+EZnOxcnS2ny%&D>$=f&9WTzUKIV*K z05a5aQe__FP1X+UBiH0rP&s~9ddQjO^YTGZzB;6&&&Oyt$FilmR7#6zMpFZ6{&(NW zeD`y{^OLK62wIXTprX~$v}$$uMZ^zX0r^`LQM2A8mo%rERDpF0%C4Sd_57Gh_onKn zEU%Buse-II=z3~SmnPgN1%ulC2Fm>ghS2~+|2H-C78y{w6Gx}JuY@Kgs#?ESs47Q( zvbyp#(`@xKiUq2XNR->IW$c>$d(+fU%aW?vPK?LomZFUXE>FAdu_6TT#Dtq7U3P6!5M zGtw6cI!i;UXyVUirJGB9v|jtNh}jy#A&N^Dni}X5b)cG{8f$B`)!b*SI1?7w$v@eA zAz&>HT4B7}d+kKG7dN}p>;Z{t?kHe3Ii zCr7Vx6)3iM)Aq<|+?=u%g|oFZY|-PzD7^5FbK!|wUVvg#_!&OW+5Vn%^ul25MT`%fq~}z@x2HU(vie&XHqY>&@BuTF7q@E zAZzQHvwIkt@xz(4LTlF9{MuJgB=N>Tr3-oNt8`_jPjukW#;N+<@?tdYp*I7wcbgxC zVs)W7ZI>?JQGWtP!eg74??^aaLR)Hyf7Tj zk}+#T{KSR{yJoC9D0u+5w#WSiK5L$~oru-Cf>%`qau-J9e!=MYD0`fIj@oxb$;RZk z3}$I=Zg!8qFV{F%h7@aeDj&16w~PLf%)t!I759s)1gr(Bp=oQSOJ$|QHt@4fo?+nR zSx4dlsX8T4vEuXsJC4BTbqQfv1p!okxdIOYXV>q7P3?>fKAv4bH@Om{hJ2+GI6y>H$BcGo+Q0d(Bv+8yI%xmn!XSRPCcll3a=Dtns6TiBbjDvo)UPhu2y$s;v&{UG7l&?lU|S&ka#`ThpSU{5On5B z&13A9q0;5AGrwNSk`L_qusSU->ps1TM$(_BrSkq@{V1tU=N$O+C`rYUETp z%k>JfsN{IdM|wR8!C}mrSO{{jJWf{A%}_wSn%-nJCGX;OV78muBb_ATH+2p-H*!YF zP5p+B)(gT6(p$<8%=X z?1}z9$+dLz&;PbnTD^$RJB zQCDt^OvX^5X3Mw8LobW=6v8jcXn4)e(MEu8`P55s1HaIWE)aqI>pE9~0!z%!85)9eq(&>+R~L*S^I6=$n$j z$)n4>@0`N(twlWqsp$-e@d6_RZuWB6>AXe`atf(e9Pd6smn~|n;Ho0_PT^LJt&n^% zSaPKVn+BWFq8{_+pi%#Y(y2KBz3$}3<-Llojgzj=MWusflu2-=aY#6>b+yC}YJ67` zW8V)Z-bWM}Cz7JlA}$7ga6(qUVC^NA^a6-Eh0oElwP<&2XZ#F+C@ZXE={WXl##vgs zn?>d&vdAtEtIL8jD@^ZG$tqB6jb3Hcu2$Lo-YWZ*$cE2}JdBcJ*U zjWo5;1Y`9=yMiZX4%k8Md#$DU*3yVR-Jkp+ghoi^+>pAfmh*|&WFeHxh0N9{A$ErK8<7;S10RbUf?g|4vwGv7YUx7;_wUfPD`(N$dN__ib}HqAA9jOy#&jwjnO(#L(8 zK4{f_lQI=38HwIo``uX>`Du`bMgfAhQ1VyQCP=Z`x-6h?AX+N}T=O zb7BYxZ<_D*C%IxITmP`YdXWt<@B_RpDFbB35 z6Qb2+b|2b1JtV~IT#`Tq$=wT!CUmUJBKB@&C|3FUEmzyq!uG}797MPtIg=v(x*Vkx zuC~6A5wmJW_QQghvrf@W@>$|OOvn(*p#~vVv{#6;1VKx&x@wT-EGVbDct66(v*!0) zsB#Zm2;Ip8A?&t8 z);$E?LaJ16L7L+F>-js@fnLw1L!L9Q04`H*>X^nE zi3nMz5vZl^IJ>snE{|AS%~k?c6xikY&OE}l6zY2f)#L@_sx6s3btkDq<(#e%QPfpx zJ~Uo#74D|VR+%=tayPhF!(qk92J2nx0vP3mAK z`<0N{LxHXsN&hBmQ$~1G)(Gbud4!YIFxU*zL*t^HFk_Z6o~VXjWKRP!03lxuwNO*3 zBaKsat|!S!LtIhV2j!F)=S9#p>7%eMUjb9OI4}rOb{`CMBs*YB{cY*%K9QK|%Z|^! zFUvfnL|9kZKhyH1Si6(}`v7H>lBBthR+D#7p{hsAuzyiYUOzvIBD*$pnA{p6MKM)C1fX>9pv~87XdrnWBaqGY8 z8TtQ%ZQ@oY2Tpgh!lOiD*d5p*0w(708*l-@F(M?3v&#ZCJCj$lH8==Si_~0!pK=~n z0+9cTXs0Q{ak)-m#2aP1@q=$uT^JvwqkUvi~^Cj%prY zm+Dx0j5{^g-=)yrv@t(}x2m-MMy{?>Hfu}dB(XoKSg{`N8BLSPPtUqrozf~CxpWyU zqp+Aq;ZFBkuez6$2uV$Ux>LH8)*FWTl=Z-xhO6#{PfSMnwDFK)!M@>r6rbhe@f&~j zbv07;099qGj-TL{`U9olWCY<84<+UJo2p17xK@Pd=SR@d>^}_G3;yci`V(pZ;vSsU zN;bA@E#uQl_jJ4RA5aZx{MR=vSN1sbOC#>4Bmmi*+(ln(HFy}-cl1uHUXz>1q5rgn zN+#JX%f#ENgq6=`M`Ar-%OL%E0sECRe@sMNi!czs0Lsu|w@mhGwDj;UXF4-O97^o$ z+l!n&3z^B<(bvXqkdwecMkM0ygXm=Yjg^Lo0U6$>UAFDcfRlf`Y_5905?@B<%6$vs zn(DGX>f7l|Kbo*C&a!^1Ke|I|$qn=g`PvtKRq;vDE$~3Z_dM6!LUc)vzz06GjMPt+ zJl-WTI1=Wpud)FO(UVmtO;`t{ky~hlm=#LXmzJ+M)GF&??qFa1Y^%FHQaj-{XL^y-DMttkGz@6)jCcu&dW$iD_0L0O{eglUS6m2l2NYy`aQie z4|*#zejDZ#taG3T9YDi|1hvyW4vb1G9E~Q?cx6`TcccmoR=)*XmC) zc(YcDYXisE1Y-S6bD3&ADc9F!)WS-=X({-kKoX=}A&{`B*5M*bVz_ z=%cv&p%3(jJMKQ}!QkKK1TToSCH@S3l;aNrDxKA`cA}yb^mhS(~FAG&Bf-H+HQEdz9o*?>PC4FG`+OA#jnmur3bvKd@RA#gY4o?J=6b z)w&2EY{jC4*c|_7dj;%Cule3V=&ew1XL_!TH_=nFjUmHzaM4-mqMqfWGIx1Gn$(cQ zp4{@b7R?gf(UZj5N5B{~&MyWOMn~@!OQ5wPsQvF5E)6J=M(uB~D>f_=Y&Q&Rm%XSv zu*06)j|MBcU3L`&ql4z#0cTk#5w!DA|I~KtKE95YFOp-nMPgmXswrxPx;SdutE9DP zucdz1N^cNGP`8qb9Xwnd?OG>^?ltIzM8bhjOGNB*qkA|rAQA95uMcOXx{pkGbI@8C z5+K3eUm}Zk8@N9dzL0;G^|pASGozp3c>CbV8KEvQN?fw_tU%qI@`K!YA;0}8H{pjGRFsUU~(i&nEels*3p z3mwt%>HPwM#l9pwP2_;hv7KnF+e341+)14pa?A~Pd(8-Tlo!xIPDi<-rXA(V2_d@_ zATBRd>U3*K5&X8&nOo%%qz`FAaR64ixE+YuEq+{j6mag^jUf4rw-l=;nv5LlVHu*o zgHc$Mkqyc$H44aQ@h4!j(ju5WS9VlBB4686#Tmmo1ld6E;#6)>ZVqditF)_0#~hZ4 zh((W(7jhpUgiJbbo1Z7&g2fmVFQ(qq#9VV z!MR6paS=FDL)a}{>a{HXCW4q=>+UgrvvLeUGQ6hUeH@WV_w{%xXB=zAYN2}cT6XR^ zUaoRVk+D2QuDR(-b%t22H}DPJ>vXmEBM-ne#uZ;KC-1IiqUKY%Q`t$?LHFxWVw>kY zyYhL(qjia-UilItI-z_;G$(3s1!2~^MNXBwe#Crb$=UH9tD@N4G+q1^Q9dY9GWCGa zNBwx0PuaRss}Hjes`W$!53_BUxj$Hc%s}y--7>DxqEA)Sn}bKA#SJO-p*31;MX4li zA^UQ0>8{^xjTReJ>J>|Q+Z-vLEaMN-ehmocPJN3VNU^j=lN@I8@YQHKofirzVRyr> zPx?CYrd7_GzE;GDl5Lz9COhXbrGTsmt=%MhMnzc0ht49_NjyO{-P=WZVNKtjUoOgx z{VP|zr6{-7ldV;h+svmMYh`4XSs;(1oV3hBd6c19W|2I~m@HGM*jgF5Ws2X!+J^-R z%(Hkz?l31xk{GotbCNtN6fq~uqgo#36nRvrVG8rJ_9azzI*ic5+w$85!&Xfgslw@ew$w zT$KAb8qC{@5gS;m1F2*h{NK$2e~iAsWtP0A;Jr?O&Ow583g~<7`MV;p63r*_fpuZn zx-e8Bus*PI#b2~s#0jExvR&G%1=7v0trM`@s|D8U1nl-|f%Q58yS-Xqy-vVxuNGLZ z6R_LL$7G#=-CixQUMFC;R||}l4bfgLFjjU%d$qt=xrW)R1;* zL0+qSR7r0O=1?xBS=8RrIiag1czkNNnH-k#!eG@pHT~9Kkm`E4%pJa~uT{=oYqw;h zxq)X}8Lwim8ml()rdQ1_Hm?s2^EZ+`E%*>3ib&MhN3bLvbb>~#cWIS5VAM_PtLz2v zIf=<^DZY+1ekQ4cqlpr`y#FC-k2t1VqHVFAx5#zXlp_QTM*@!l4C02FCB9l)tWE}n z;ot1Yo7jd+FDFxQ^xYBLt^~6#6mYSQCC^6R)LEn_+`Ww2usTzZQlG6@N2_b*cm&%} zd@fTy%j}QDtIhtRc)8hM7N0t`fgZ)bGF8sc#lNhNpiWQ+cH-mJ{SWaIroznR#K%q@ zI0aE)^+MK;zdw4~F;@e1zslMnM{+@Q{^ocb^k(zj5jgXV+#s9@%*bZ@Bt<#lE_(wzoQ07Y zPOx-*R}^-0c5mQ0*aH2bcSsP&l5x^0{5f|-_n<^mWC31%o#Ou&25|ySyL8g>tRwB| zC(*Rp)Bj2KU?C96i-3n)gJ#*E59p!TZ~ge)&Q+Y>Suw(?3dGu%9_JqVz!%T4m4a$I zk~3m@{W2w@`>Xsaf0duTu4W*T{bbl?5Td)0AIzNlE)K! z%YwP_62D{OtglGbb(G6C5Er~Os7t%msE{wa*qr^3#}Ohnn*O zoy~7&_<4+e5_}MxH=63QH7b|$;`eP4eFJ2BF}!Hg)>i5lZ)fbLU1ob2J{_7}=^D3- z{~AQB3{hlNJQQBH|D7TiVU^plWEgQhQ}?@un(1YRKVhfrRPACs0`Hn} zd-5!F2@xyyh}Cf4*t!~fxPwm=DF|9(WP{O*V7IILGMl_}6%qtF5jiSYo^Q5s`{8MI zgszV`S7DqXr$FwtD=#$Lim>*z2Q6jt$;0G>hOeApUGA2x$Z4Ws5iS%%oj6^JcPQ?! zD_OCr*IkMK&H6bBqM&+CN3lef?^d0MIZSr2z`XlU)h#iiv-npbgLOIXOafGCe?=d( zV|>uAdFhirRbGoU=LxS+2w6W& zd0sk;HKD2&LUn>$`&WO|Y==Uqe4U5Ts^x=d)z$tW8@EHNdiE(;&+urK^`ZN(qPb45 z($oFY^C%70+(*?Ato^dTLCQeRf)_LkR2v%EWkg0oxV42o(Pp9pH&--=-IDMFW z!FdeyM$X)$h_(^LF=?z@pZq(vWj(7_4@WTD&HcyL7e6l64ozY!Uy)%rb3@)e?c>|H zrb_66`F4Nm^}+t!RL;u#f-|>&Z#ndDH201r&wEk!Ny;x$^(8-nWb1eTAfB)9lKp}S zk@!_<1??gFp`^63_+lTIssNn$2D>M0^XMRrj`tIXk(fDQvH$|>fk8u3KIDock;kAJ zIqTH>1ygmP8<#`NB` z{R+Ch?+AFUHg3}cSn%7aT^q#lI`zts{eH42K(~w4rvpyhuJ_nD?&yGve*0kKfD(K~ zL3NVsM9X`Ox{y;5AsBPLF=&*?2ff-CWiIN**R5L7fufbIC5j^kT%~20@q+X!G10Dl z-Cl$f$`15_(H((XMUls&)fU|n7^f6@wXX-dqRPjzDu}hGqR9O@;7}v7EI538B1D;( z{*--1A3q>Mvgt;|FjUSJFh-Lw%;xucA`1CsSDAiJm=kREC-BTJ#m-AnaRlqJ=1yyR~Cr5s6EY@vdatVLaDJfbaNIwtc?vuvqZ`9^udk1{ zM(4ch4dqeJc8wsc9mUtoiR_F? zg?f}2o%|k8D6M}_f9O??a!IEnN@P#YB#$Y|?mL2G}^BTT2VV_k+$UBW< zBK&&vcVOHk=ARn|y?38q3`@L=Oo!~{g`e7|6~%+P6%ITYpkwjb5*Gs(f=u_$^}Ba; zcK6WdrMnkaD?qb*k8oO4#4FXmKdN!29?go!p*qL$CY$q8RmAzjmW|P`zjc3Jpp$5* zmlg!NoQ2oe!(;8sbFKEhRTBsrpuC5ww9gdAk4p>TN?BEc=WhtyaG;?M=n9-0k{BQp z$EyP8N|ab_ecQ^$Z#Vn%jNAW{vf_J~YqiZVT15V^FY>h9cJN15@Fz2Fgfhb`$NH_d zQON+R;6^@Ms!sB|XRbG+&qJ@crxEhkl0-j`zUs{+P0B@OZkX0Q?x6-rh;|rsqSPIVASC3OQ60+rQ)zUCZ8z z2>~nu@lv}&9X6xK1M`X+W=_aO6!|EAa_rF3*JBqf*4TdfC14cZjoxYV3^V zK|lFxndC|Amwxi;ndB4f^01$LR605S@AyXQ|2fBgba~V6_{efpGXpvCKzVdaU#oB+ z@#(h(T7qMWSmK*_o#v%}DV_S?S*Zo-)Vn1$6Pd@nCPt!3*?(Q{B`eS?{txRtOe{N7d* zD4V>+XqwG)!@eASyJXp%ME-3dN&bqTES^necIYXYF+IpXmHa92qEi+$%wx&3o@@M* z@FRPs>}$cAG0MRC&XE2{2lJFWD9NQ{xM&=w^zFe3-82Pf{Ygq9;i>ydwMX ziV`=aD`^>AN#f?A6)e4ING}poy^oj9$!a}Oe%OcOhJ0u=^+PsMJ8;2)QPwmNAJv+Q zlrP9cUv*oy^ZB|$OI6=$$K*J%T{j^l)Hf?lP=EZYc&Hx=Trg$S$ z{7^>UD4x?qWzKOH2H1q$i#BUZJq|?h;V4GL@6f#tAwV42Gk3{lMwqD&|3Op+6oNWR zwdbJ5vf7<8bvf!MfAlIoc5^UK&YPzzKk73orx{(pP~2`fLrA;O;~cmu>JUy$$uODj zr1!sBV!V89*>R;O&0lAPmK%2P-mk9}A1-}oibtlO{?qf%KQEORLe8aTMd|x*I)q+i zleF+_Y2nq)m9KL^(pmJn*fVDKy^DI^3H~^+G0LuDtZ%&UR*3i3mb}EI`QSL*_8Q&H!zUjEh6OE={@Kz=dcgchK zQEvPwd6P9-#`T+c$_Arb8e0W1XDnS;ZLRb&LMR^d zP+r|ktFQHMxmm>F;>kM_v$Ni(@jf-{y@dC(v);$^e$t%8Q|7yQcxH{w${9gU-bIP` zvnY*RChp-p@Y45>&khq}k~0hg=7GGrN!G5`52QQg(^?<|ac*((AE#sVYS?hWD4)dZ=j(wXt=vNQfUD`QS}#wC)GnnFG12SNQm zam?T;5=T2#MyhB~>Of9nm{WChYLrtIZrYp3bE-z0ni;Ki5jZz(ix*9PK?Waa+8ZC&`qv2Q zyycQHLTBXpyMdyw${q)44TIH$z2|U#ZEyUP3-vN$vnv51%Sscyi9N>p8*sHkQb>&A zY{;gNqDGV$1FL;f%N(|So29tr!91f$c)H0Eqv@Z> zDZ9vM`WnBIE~ly`U9KOK^fSEld%bjtYF&0w@^)T*dTVRcF4-D*(J;l!rR2r*a{7dF zoZ41stP6CLl2u@R(ctoj*U5f1%1hxa7p_{7ISosN}z!7tfy@iBBxMC?4i_0>43ii-3Ni z%pPa2BCbRIBvCH=VSLzd>b0+|(jw{|IMFLESe* zO_FyJotR^_{(aB=2f14^-I0TKb+_usLHp8vtNNg|px>_g*uJ#as{YtoK!n^L`%>4c z?y(lQcGYhC(r&AIx3!?#sq1!DcKaQfa}gCv&B|YT%>uaNkf85vpmED4tF^TotnECA zTer3Kmh-KBeXp8tyLm?t3dq%djz-Y$ykRA z%t!oVAm0SYsi|M6>}T=+)r0x}D?Gk`p7%TRALRc}()_>Kp+o?qr8>al6vP{TTFy|H zMyh!IC^avIxdI_$&&9uNpXYKTT-yIrt{E}b=dXqjnAQYy8(a;(x5ZuM#j;V!Ihqm5 z@E=3!tBlw;O#UC&cf|R*^nZVTglg`&KeG?|n~IQA!N+hm1&v3ANNO$V1oG8x1Uh@|fROxoi! zhYae-tykNl;q)(oYp68jM$-)t(tXV zOLz`Yr2A?#EBc zY}6+SqvOdRNW#_bKM~;ow%LhK3?x1<(-*i3&dK-Q*XU~{z_Tmy5S$wV=Al6S_zv+t zgGlFh$ghN+<7*vAPUD+M+IO!fxVaeWFglEgzhSjzwQsB+YdqanS{aU?*D_{WMboy# z`>=B(B=+^h)Wh}&3n6#IQ{oJfuiMfMrFMCGiBh93+;Afryy2S(-1hXEi&9VcOGW)= z^?)0SI-xYLjEC@#{kG^;*|?=A3%ewc>G zm}~$j`#6Lefg81ZE-`|UBHb%CW&>lZNYKYP;aqrHRo;h3etet{4>b0a;`7|peQO4f zcNgP*&F`8&UYV{So0x>0#uquTrKPfud$Ni=H7vHE%Dx$s@wX8p@`dq`XL*H$R`y{v z-Q4gMX1)b|OPDb6vJ^g3%0l~?q2m9#aAG~O6LO}?I7kr!W} zA9CaH(2^2}m#T-n_?OLOkoxMMv^p;Y6L!YOG7XYPXW~bz!~|y)FBfZ*)s@13H?;>t zd+GzHA~%(ED)LghJ$)Cdd+vjJRoR!vpaDg9-iND>dM9?0a6J}0zZY+ID17Dt6xM=A z4++r{do?~1W*6((hxiVqMOPo5+Mj!rfUwLCuj04B`;BQ0V@;!|Ta!T9u8O(_1=SWP%SQBH-yBks?ue;e`PBubn}k7vZk@-#+0jpS*B!Us8d zVQvhk_F?f!eN1NRZ$rn!(gqFTk%WaqC+6fg%UTpx4&KGwKf-#mZd_lxSo*k}Y`Al? z636};wDJfjC7cKzcD4@uEzFJ>-7`&q@6_DdooiJ_7A0T6pCOYcPlNL>z)j+?e6!~| z)NW2?hxD)VNqh+%vw_Y`T?wH!ZWZRAxQSZb?pu%GcM< zhY^SkE8C4Kta1n6Vg&BeKQ1r4b?cwq@)IBCc8^2QSk$q02VZdGnUIou`Oc|)65nOg zMDJzOA@1n~Y-PNLIeUT1O8jQTGcup&0(s_F;#}qoTq2jsjWPR&#lu7%iI3>0WH}3U z!p+w6P}DKy{b>^Tc;{<|+sA;D!<5M~4Px@~|3G6V&44ySBLuls*F?v5v~@(Yjt!~Zqk+JcT{$2IBk?4v0h%r1AuMb3nZnG z(R7}qEZ^Y3wpKow1!~}UH-|s0#KYNetbK*6@@78_xYD2uI+YLOs7lA3%3ldgxv>88 zVYyhqIM(bRPL$~W5v;@hVMenU_!&cm>)3w((@9s~pQE_{}KBUYglu7Lb`cHosL);Sr+oZ(P+|a`uCGX`avwwb<&BuN7O6t+G zVs137_q=&AyL+RXz$B@F#pcW}E|NL={$X=;v7{t6xId*HiSapfqW*S_R1g*=@@DE< z+nquD`~NJ6&umGWkIsT@4kwE8i%Ai83N>W!chDk`t|{cl`{rtymaCv#^ElqZ-0hdS z+b?sspSk-ZCCNKq^3EY8ezv=#ZP1K)+lf9OX&uB<(4R5b5_{1GCMn73Bt|e$xdA^J zRzu~(*<;!9Qr1}X7#|)CzxupL_;mwI$C7fjo~p>*b^z zxZ3cs%$*!J2PymszwZ*QegB3BpR(tf7)&aRxs7|K& z`*2HB%mcx=VICMBA7vgGfw*>HSmHd=i+PBjBAF*h`Z%|Dqd(8j$jVgvSK}suj1um+ zJURo?qzBT~9!NhCkbe0aGG0!4JdPJ!q6usM`QB=MR5=*O4R%#76zAU@6%H+H;rbQc zOlIoWWCZ4^NelWJH%Z1F0H$QXF0k&EI6VT-e|`_{ZF)h#YQCQoniKEk9P7?Ucynqu zSj`X1gZj(lS+!3Nw$B57$C1-N-W*KX24QrhD(6mV!aK~TXFv{T$GVf zRw&xp+MgTf5OA*n?jr%Y(Jb`Ko}t3o^zz7nH)@ck2Ye+1PD%`^4F?6M8}X+7hw$|q zpMkH1QaUy<73G9)5zE zjWAmdD_fzeGaWdph+t_Ho&BW7(jh217(bn6g`7}+$ONGO*^7X_Sz#ts<8Ea;f+86z z@jt>(jhsJ8Bj@#0*>KZ9j?v`d=Hej(zDjUY=6XYLB)EB}!cC8#RPLO?T%iJjQwk ztGWT9(+RH1;O*iUgzFI|QPEfhMj^4{r7v5Hq7t8ny+g?CN+Gj10lCT}pzj5wpDpM+ zK{8JhGJCRXJuwJ}2hX?F-?&L&=Nn@EEv#t1-ohFHkl^##yt~g_aY*h{tV+{dqnXpa zLIed>rh$L#1KD_8I~cG1N)uF?dMJzL`WZ_kgP68LbD^+`rUE>8Za@Bq(9b&+ibCsX z^x*S=SW`}2Tw|%@2pNWch5>fIx2^e*=65iceyK;3KZ}0eMtz!oHm~F{c?v^A9(_D4 z`1n9JK0?K?A=ZY?SWf&)ur^;TGp97>#7~=nJgAf3>l->3v2RR#grW4!~ zn|qMZ!|kcxaOW1DwnT{|6an6lZnVvw_OTsYBl|$}`T~ z8`T>IY;xe?tl68a%8mbrxNm`vs=C&nfdK*zo`^vs20=wj6cq$3V?e`W03WHSL{Xu& zx0b$aYr_msC6F*PlH=w8DzVsW6biQ7O0QH2L?oC5ngqlekZJ_9XlXmgK}FOM9wq

r-4-_xA_s{P~X77FWbM3X)UTf{O*N(dm$Zs417IqE0UFWAd6F(LDXSM!W1^6M$ zO8v7;|2(aK2u2j3tp!vuod)2YLV#~^oqs7)lz%~;p9XsXYVdmD=UV(rLxrm_${t;PQEzUJPPe!(yc?$w?d|HA^hPL>ZgbG)p zd^2FH{40I_r;TtU!Yz4EtHP(1=M|N|OJ&SM$ya#NTM~F3y&(HsB^^Q~`dm=?7lSzA zw$&0}{!$~t{>uJ|4){^QLgjr0SZGFOVW3R$miR1&O=~v(1}C@VEwDzzW9sUbyb|;h zjkKV}syR?SqK&!hrGC6HM>3ID;>%lLg#W>?e?c}B6Wf(PjNw3iu-$$J!~#eVJ?p<9 zjee|3rNv6Nl6@5WQ*4*=FGV-;qGz41DPX__W#IxBEMu*)u9nyuM~{IU1it)+=_}(? zn+Ic^hQ!>Sd>U~UqVo0{FI2;fIlmESBI#c?kG{kd@)B`;3O6I1oI=hbrf{L2h=rU8 zPGMdNbY#gX>@Ve4`tmS^UjY8eDa8ECPDR6)X`iyK6Rgw#OE?WBSfi8PlJ86Fyl7&+YB0UYDeD9~82Egv7-ixj( zzvd=58Ond!{tcVUtMI|*lFpr+B@HabXa5=-_kPg#wJLw3>0eE^GS^6^)zT%7UGtYZ z!zx&4U!jO$YiVMx@dr;R(>WheC~bh#pN*r<{I$63g#N7c<*x!ekiYgOGk=+xzuMjf z%vX6E*&#m6Q0EsMH5+pA(e$si*YbOQg)e`N5&GtFgbUZ$efSFpqIngNvhr494NrFz z0I0^|F0I&bB?^vN#4ukbx6jgCU|RA4^2+;8OKcj zF6{THs^UI$lUozmR~JE=@wV^-#=35ptv*S77&GE3Bm6Wn;UF6pY&hAe&pu|2?bFLV zrda_1%&ax>X1kQkG&VJupDy1EK4;80>}MrzT`n3b-0q&)r?%@=VBWU_qYVk-J$2~* z`l0&Tu2@P3hwMRLKxHg>bpX3GdiwD9&rFN#-Fy8o$=srlpehtWTfSfq$Wuo)mLvpRX8HBP0yE;^? zln3hk^@N%@^(OrkeLe5DnwwiolG8!!+KuM=UjYH-tU>iK&tA^qV@Dk05zK=#qUD5z zImR34+=9#Ga83l`gm7+qxP1O3YC@|#oJpmZ=+B)N9sqmm4!#-IKRIQjp?a>fp)gOG ztj{@Fb8&Bmw|~w?dpXHM2jcT=;Y#_z=qH<7l{~vh1(L=Po+X7(h`IhhH0z|Q{Hl`x%7J>Zh|GUE{70Su8` z&R?SU;SMS@w}Cg$Yn+vV{0OGq$Crz+=&y1LB&d_<+noGZl;C($>vw%={Z9VW`rqiS zJJh;nC{LC@pZEV8TRbG6+sdmF51KgKC>bHCK@c$@`S z|Ei_X^Ya*+>Azy}^kme9v!EXLCq`#kjXm!*1T=Vz+IOv^JLr^Yz*p+A0f z2}9v{?ZJXoT98cMrxH*-Ag8omtb7kK-;q7m zxRzNoUd7`zx59!y*KEu_xC0_Oj$>BxMjKCmSHdKVHRJVWU30LDnz}MpbNprKn?&_l zXTJ5>b1Jwp_TPALK)*b4UVq9GEC2V77pI&B0c%;U>;`fnrDKtR0IK)t{F%5h z+1yROO1j5M?;=au@|RJPWF)GT6*y8g$%6on^Q#1+)SWujLqEkc zgGo3!j?3azs0X_UFE;T=IX!w47+}rMnh=W975zpxUGY2Y1q8l^Qu8gTzSPy%EZM@jm#uatNf2*xXz^%J~&_=Dmj z?g2k4mJ?R1u*i58EdIf775`(>NTa2=_RjClawYVMa{SO}yif~;9SDxU1kf?_ia1A6 z7a*^seh4zqV1Pu7!EJ=OT>u9@c^g*cv(dO&kF^GuOr0pY2%ZGSt1?+cfW1T2DUCRKoz8&=s!$i=5$WU?}!PCZgybSv~J|sRxF>v-Q22J^t&8fJ43P@f8bjlEXeh5+lP*fEb z5&wA16Xpg=H;h!1r@=Q3L}I=UNH3$9e4k*uv9L3e>_>>oez;8tn-QXp{RI-*`NxO` zao)t8a1hvfz3F{z3jec1!NAAc}*pmqDL{0)mFG?tQ3`2u>^i(Y_wQ<+rx_7uJ? zU(6AK*OC2%@|3zJ7X{IM8;G-f2?mF9k*iQK@?jn> zlTNt;7gct?^66-F`L1jQLwc*o9Igj<@Aox-4j4fYr#|IH%sjjVnsqbYg_jWb31ZN%%xW_T5&DAgo|37M zW?uq>RCMg}K)<46R|NVj-v@Ep*z~5c-ZSwMI9+v3BLXa9p29s>7~vo5(B($>K7{m? zu@CH^=T6{tu)*>lVjnHIAItlg9o8pP>Cx?%c5^SJe4VkrenuBzJHWO|!u-ELt!}JuaClrRqZoM)sh(^e8Q}i@za&G|kE+sv z+3<`)MOUx^=WD$=suj$LaT}X9V|1hAqYLz*GLuRLC=^q$lv8w`r$RPOdM79xE1%{%Mx>4!EG}1LeHCT}$8dX%`I871 zD2vzi5uX{NJqF=(8R6^j1wFFz4q5(><49x$`D!=!2MU|%(I*CI#K{!I;knF&Kv$s4 zp-x1a1nTH%#&qWqrfnuuPqQF?n=g8ryI`6FSIHE=YOK#cXspjWWH$V0D^W>gDkOev zd5Wo&-erzXx6kAt{SC40bYl{sRfGL7{&2lS=w#0R4i=n)TAR}Kx=LMC5%MASb;wk7 zEH7|(c#jde0ug|-_=u2`Q69{_E6@ipomMvvvx&brS;Ak;qx}Pvewid)^gy7nW=|4Z zhe%FaL9%I&MkW*o>eY>t+CWwCmIFNI0X;%ho$!O_3;Bav&HD-dbgW@SGot-2Cd@Ii zrC^JV_2bfn5GX`2t6pP%u}~J?!pG4n}Ro;Mu7G_19Th#%JU?CUqyv2n)b)BhO|^1 zxIilS*n)vG$bA47f$qwB?zIOILY`QqR~u2 zqd&o`!8x*2`f^^e(?8^P8Ig2EmUDtJ7t4io0PNBQPRerjfYTEFGy0>hk?pxDh{F#O zDEm#oRQLqZUPd=q07BnoC}RH{tfqr)$oDWJsY~OeRCT7W1B%K%kfP4az#>kirt(g^ zHSfVOxKD7a<-@!T%zIw~odTKWbiDAEkzJ?8q~kTjhcgrI0<>_fuU!Wxye!Qfgw_Fv zC5NCO5JQ}e7$@`vNp(OcUu_UiQf_RcBd8_!3Hwk|>RmiM%-`j%a@QN{Z-#lnJa>Jl zy1N@E+}n-yMRUk&`0IaE#csRn;<^t1DzhHPIy-Q7yY{KogpjjKIIO$$Ao*2&D< zYkoSjGeA}$8V#);0a!#!CR*^lT9W|*)N?zU-SC5H)2895_XG~b4u6QV8i0?gwHmYD zy$$CmB{Z`Du-(vbHG;O5@PaFW?}aX@k$xO1o)~cB-E^PV!E_su=TM0%jLPFIxJ7P{M|?<&9l+1x z_w7LTup4lE_Top9`mz zgmCmVTpV%)j9`Y)ycOiih3c&!Bi>Hl%0^icSQG0L@>Uk)bjNrf#u0a->UQNmokJO} z_!za#pcT>Ek}VtI{pbsBOTlEskvR1>0bBe7LTSTLJmR#c6O4{<-<1aIXFq{Pm&B~P zG5yh|t8UD|`ygZ0jTyjL>{sw_GJ>YFBIdEN=oD!tm^oyRFi9xlr(Vis_Xbu1AI-+- z5nw_r?#Cv2^AcLmLGw=2B5omV`)tmA)j@8bQjfir}(D@|7B5H1hO|B9TG?Fcv ziBE|V`(xWv)blC#?`C5sN2SYG0c_--mI4W3yELT9yhf5BrW7zKAN)Iyc&|}CtNzaO zqCgJM3@^qvj(jKu5IIJO?%O0fg^bko8kg5}HD(jI*C3GgT9k_JgBgR!-rXUk8&ka63OOw#NP^o*!NcPkV5e<9W>*JG?W){#BL-EU(usGOJw(AyUthSsi#pNjsK@ zG#RFF*fJI@XLDxn)%a^=7*P6~z1K3#1C3QHDiDldJMFQe9w8WTNLQ>3$QbD+PB<30 z|GHed-kXhJr+{ogXvmN;bczJg&wtR#h38`<9t^k6znX39j`*goHbP%u0Vq9a>4hp> z4LgBz@p4D#P0;D_zQ3Av4j%Af3=-%MxBX~~$0(iLyHzIur4w$U!Q!SRZ#Brm zM@|#IJXHQP$2oQFt6XI8l$bLP@A3u_f$xo62w!9(x|W`_z!;}4@= z=kLwQw5GzEXSM0s79YulCoO(+yttaWT1jDZ>~0Un!oHXs3;jdHr?zm3uWM`Mz!c5Gu;*BJc)jrm?H z_MTh7;#BY|L8) z3>VDj8zJU3+)F?bL|a89y$ZOvoNEdo#ub0J48&MBrNN`TWs(^dCs&{jpe=BA$8udk zuwQ5-n}MUa+c6f65UY+Yu}iYnB# zVk7vSxyyK6{=+Dr zBbOBk8uPY&8H$iP{|AmGaiwZK@Sf2-babQ>9jl{n1H1V38XYyA=%qUP2_!#$Jy%C7 zoM@Vk;*w~K#%TLu1of`RMZfAL6!R~$b09Ju7lp3*mR91{L5Ul#h4_TqcNN^@UgmCR zO7aUxwqM5|V_g@DOn9I{B&TgiVy&a+;`_abVEY`2q9i-F=-;%Vz(T~m`G`k`fKic2}_O>31(y zJ-Om!s)0ynT3Ham$9PISXBIu&%@xdcoZUW(5=9Sp1=CQ35RT!&o1mDI9Sq+8JTDsS z?)8k*&6!gV6s(dOYoSrpSd^b$i_O7IH;+oZWMWSzJ)<_)>&8RifDs|qn@d$Q(6S%6 z4mYl#3Y!Kb%h7L}&o%SZv?muwynW5btwYyb`Kl*}j90?2s(R>cFQ;?iMA@=ofnGCx zr0OXf%WFly^tb}&x3P~OjU3&IX1|G5`Z>HBb-mZ}?3lWF$j(@k#y4F!wqG<-u<1On zP5B(p6SHC4zOA51uTN>q4WLeJiM^!dkvM*M!Sa(4EG0=?Vt1VjcMa%SG#2u*fg|E^ zEucb?1foGbqVGZjun7fSBhSDeI6tgKl?b`4af5~EO@%0iW;X|}HK(Upxr1SA=g6LB zPT*|mA9Ih%BxsiX975>s(3aV8k0sETMK8yRG+~NfM6bdy%%NEPd%!PT;(`)>q$?Mk zS@>?iGq#KJyE7yj8c;Oi$c}W^gP-+Wn^L=hcG1^xd{XDiKrdpy1%5;?N20*feu^CXdHPjqx%qQdKu5oq@nv z=p?l(pzUb;O4i_5F={C}j$b5((HFviJyb>UH$>4g+-9Nvrk|qROb_6t?n=+O%R7C9 zm31|APU%oj@|o4~>3sWi2mEZQ&fTj6yOSW&<=3HnXbW7M37u58rr|}YZqbJCP(?B4 zEI8>x<#<3vKg1kc0fw+g^dfc!Dvfk#CtlE<)$<{hYa;Hbg!Xe~-HtYvAD4v@><_eX zLCD#_KW6U+et-PuScCd|;2I(d_VDHWLaf|{UoZ$r#_hs`_(_lM1YU=lFA9u|R|>@@ zFc1=RNeF9rvr0VRL9LW@;f)6{=R?63Bv^$eC5=lJ#Wl&^0H|agqObo_)=OQWo7rrW z(nT*v<-~s}LfBt71H^GCUR%-otLR4(AN%rS8+*$3@X6?b@n4P3EgysspxXvME`Be- z@zM(YJqaIvA$&aeZ37=Z?5*MBRGNU#5Ai;8Tlh#9Uie7|@L?0uuQ+^|5QonnuzED&f;7*a3XvAO8iLss7#6Try=X--0awYCqu7dAXtH`&TeNr-!_o z*qdR%Y9tkUnwz8ofrta=NI;b6GWbbE=W1s4aV%@_Oa-5P7>qk(j%IDK>OC0!O|^OX2z_?XzvAnk3BXsOkAJ}WZL|JSxvYP-%4N|9dYpFjG5+yiFcoaO{@cUnX;c;Mh0ZNs zi%@&`%;xvoe+2mG?@9RZ3*l3Q-!||`(nqJ#1bikGwug^&VbQb>;KL@QUvc;_Ar2o4 z_r+VoCpO!G4_hUCj$+?y2cP)Icloi+{*g)_tJ=^<#^dyn;V-3+{ox<>tLb9}`kA_3 zHzNMk>(#?ywx*Ax@FaWmz{6i}y*~S;>-9wTm#^1$rK3;7j}ylnr;i+6V!blL^~xb8 z*X#cpyj^$+;;Z3Zg~n6iy#?{FhWC<(+QECr0>FDV(!UOU{QZ~0d*Q$@hj+Wu(Luvn z!<(Z^z?%`mn?p>(Ti*u}J{fx`6dzb6+(Pq@i#7*)WE7f7=ivZ6&kz`psbFlYZh=o& z`1GluQ-#-y?)|o7+jUw?AQ^eQ*`?X zwOD08#LigJ55f5Tsya^yG1r7&HWgd^=zv*W{qt@l8OZn)a zSwb5{@=0*MBT9Q-Gq5A*Lo7Y(QC3v#9&4CI9HI2-Uj zjy@(%MF2+4RYuNd4m>|ZYX7C{FMZM6H;<4q1 zz;Meer*Js#4;|@dzBS{#!~Q7xf6;NS0qRC01a;-q6f|`s3er^IuPSPS`px__xORC<@`*8}hbIuhN znYxGzrupXP8D}2e6I};u4Kou8Yos(f3IdH)(f{P98kJT-g(wT4=rem2(1e$lv}0_{l3FIR>3un4M#u^ zbL%q?|25i$y&C?iOjvVld-RI}-AiVtQBpj6>z=OUOg)=AQ+5HWNK968zP_x#CffSa z{$82V0jIzD>|aWM{VDx@D<%8?tiO{%9-+;_&J{VE`z>sJ_0|x8Dqks3Kq#6!)1~BKtr3Z2d z%a3p_W_5uvT&z5F{iVU@&Ff=tY;b{s$DWh(oY*ta_5K=LKDE@A+#5`2bua#0&!jlW zk{e8pLbwRPsvhaO4|?Iqofuhc?S}p+Zr{pbXMsf_^CTU}MF(UYWN`-Lvnu5``;vA! z&q>Y6H&j&V0p>hL2E_b%9RF>4q)A;pae9j_b|e|1ncV=Ew7YOPF)2|~+Sf6JK#|?&`oFNwyZSt;yJoY{b0gvpr5WMY#x(sXh=r~ zS1>WI)KwR&CY$*0r(tgqL-#*AW0meb2a|s`Qf*ofG zz!unxvNKRc)^WArW@tJtHNb@7Qm`JmcnM@?FI=**ZgZ>5JFY zQReNn3ZqysqT{0}*>`?N>0r*yID1oh`)fUC0h8Q;{%#nj;(q4-kbkcSMq4<2G-p5N z8J)cu9>#*@F{|kMDZxFeaBuMq&!H>9hPrN3@Efb=dNIV+765>A)p`tf z?6=OT72qwZ`HuXpJ+>Fe>nSeRfP)E_YqY-V*yY-X1ji}3Tq|xw?QySbiSxd;{?>ld zvA?x%eVMN1ZAN&$w7A`w(^&P0RX~Ime5eDvZ^QBPZ*MX0pyX9$Lfw6a`gREK*T@ zwM-GQxrmvbVC|6%w5Q7XmnOJ}$0PUD44qIn#>0#_zme`K$(vKk9-5J-#P-W}{`|gG%g}@&U2z@dI zfu4u`=^Tv_9)wIBtu&@A>FS+$+mU%2j7L5|CWrzfsS!|R{A$c!2e^SESe{;zj5lv) zbgLT=BtRBeG>rD(&11(a2v#}n_UoaVqxb@&?1{I02g3kO(U2)+wPrVY)txi_E!3YQ zZQ%;G&LYT$w+Z(hcS}dz5kduCEW5fRf`{eAHs}EmXGoP13jrR)8FB_DWwYziD-2KF z#u3iHfUBa6pswqTHx6BJl4wdah2PSXqo)OI5U9N>W3y;rASgSpjwH0T&l{@F@cVe{ToC_Yn_*N#Kto z@W*N37iOsOsld+{;LDuiJ!swepd~A;2B@Ta)3X|0e(4$ngV&3Dz(Xo)HDlOT zRxws4OBzJw*Nnl%5J<-&1~GJ5VABmzvkOuw$%FvpBSAvA3USly+ z#yX2s)LHT6GTesjgQ}b@<3&m!j``+w-od!<$i-R449eoTK=uU!c+ZWQEW#Wnz?tEy z5+Te+cpTTJUQqRzWI+5*KpZb~8;|_818j-`m9dVoZ1qweO>7#iu_-SD&laaF<)C1b zu6vv(&f6E?OG>4YNKb)AB@nttP5z@pW7(2(>eTQ0SDV zR5IIG!rAgFI@a-pDeD+u<2u$?>JtlE32xwRMzfr?YA`v~>xoi?t zFLmLa8r8e=9@LN@UwASAFf&vMb6c2<`u|U$)rRUR0fw0aFtv2ml+X4ald(Fm5mF31 z0tq-dCM_q@pB5%@y*WfllPrfQffE>k%3H%mm9`;D&W<3MBAE2`cZ4)&M@YpaEGL)E z8^n4ED>#=a@#F{!8jCv^JPYuzFtDj-!{Ud5CE-b9Z-)pPYDbq zH`*U^ zc4Ysz|B4{10d2zVQ71wY6EP(d*QGOyt%*TL82;W-WInBZJ#d%Q;Lw9^N;a3o)-nV2 zx0%JZTZ=P`vX6i54JNUMN$zA{$KFiAP?eEepir-2K}1eZZqJ%7fPwUeYSW^FZvZ1bE~-Vp%qB*f*^cJ(9R zG!whJ*|iNRKGvK7eSBB9+AJVVbOq?8va8Edl3l%0h_EnQLO9W*R3dy=k_dBG56QPQ z7{v>BptG97u-Pv)p_hWXnwd&rMCpp-Ym($EFsjpo+Gco>N0x4xjev4RaY|53071RFh`_ou9p6-|C0qZ zBGcDet~vH>kE&$Ds7TKnIem*wX1{G7#a&f%QfIFwkLE3%(~bFeu!_8Wp&HyTHqA*P zIPuP~V1;*wFuNVGC$|t?PHS0waQl}{@O}W+nc(=cXiJe!m)Z4_774SPx6fHMrG!zP ztD7^&>6mC9bEM4ps3BED@V3Z4&Gl*eVODZ2nii*sYl|z<{@!O8ULN$lkl=C8&tZw- zZ9}dicNTvCueYHXCEUOnJH8Jk%0BxTHW#7F_TN5F!I?V&jksK6gkkoCH%*coNNcgF zL&2~NwViCbPK`wxioPaTB>3VIZc}d{5Dc$D{r}GOnAkwtut;^CdvkUm7&dAKDZUSA zWI0Gv1kJa{1-|Ep19<*@IQ+bYOA>p&5jo7c62%3rjNgGHH%C_iIpj6Qw7vpzNF`%h zxOox53hrU{aGC1H30ew==Ny^hPAbE##=7zR+Y@FfQ3J2j2SUdkMLaD7_nFd5+&C3R z52fNy#f?47t7e}CYub=0LbKK+81v8klwA|rG3hMa%gV@ZHa$-`$P11eyC<=_LlpS+ zA(TDIiaj+H7z`D90tGYz#<^s0f>)#dHYiZqW+VGw-~_!6CnzLYlmQwZb)6{$0ucZa zGR$UY`}I$tH&X&X%6IhrQycjqCIT;g*G7K8fZ1ehl2EAS@<8WMy}QJT-C%60mIS>e zmQ*_$hVT<;MTtT!RoPS3k+LV$a#>&*aEweQ*vc;GUJLlgA0wb8)ErTF8R7GoQoaNo zvhdbXrlUY=LY>FR!9sx~AB{~}Lql+yga@B)2;vbzn=TVWf1vIxO*!Je5srP_(!y6) z`Iuj;O*^rromAwUg`?cBCn#tY1w>BAH`H?7E=TT znI=yC8Vc*e+Y~?4EJDGeQY7JKO0!nn8JoPBdRCeJ7tHQ}eB713Y9qW6a|p_U^FMsx z&PuwXQ43OTl-^Y1^yTJZm}kvrQT3s@F)gQo8~ZXj?FeS>^uWL9<^=fL%4thbc^mM5 z6Rm*%T_JcU@sN6YR7eo~A#-;S>DEI$3P+DG8RICRKu>ty%pPz$M@+tiFF} zg4N$zRH@ghXt0b(j{~4Wviq0^0bxgTbyB2W@zLo*)pt_@sv6&uZ0Pl*nHgCHnDWWWVn$=rs2;u=BXUrS&$(#Zi2Mx^bPKM7&@EXUBF+(Ivf;5fu`-6h z&O;Mmx4j!>w0-%}9&i@kD^?mha;43?&g!OS`xpF`^%^^v)Ci99Gj%mrA9muQj3qRJ zmoTRHC(Owb?EaO&4^>CHk&n@s6`p< zVw5_Cz3xch{|J+Ivd#L`q|VTIso^9FzzFJZjku?;0F=d{30^k5PKzS{t|Y(a$WKs# zxa?QJD{yBNeaJ7-7kd%}#@fg<02P#)!qg^^kQSdHfmwbZ5{&Ti^MPR<5}iz6gSo?a zWIyV*MzuZ?S%K@-0?F=LkD96+JT*xA#-%{gVSADaupHFi8j@eF0Q)M!+Tp&T$2dO= zU~zU8P2+{ZNt_CGB&#ipo-PUeCf3wR_VpZ8-e&y&9ug>+B>TEWmIrP&OG#|>Uh4_@ zz75VgRMiEJG!P8eoCjQ)8sC34YThAh?xYOzFaPOaigS4O^)baDEr_4rwA1vQh5Fk- zs;#ES;ebTR{y*3Eh+r!WvgSUpWMqd%al!I7csrW%ng;5He%${O9ABZtHVT|*Bw z-y68V8vVJG&?EHF=l4RT#H=iH;w%QP2qN8s=y2?4XqqH9@Xb)mz3`4r$G=(7Va1dC zF}V-b<0Ii>UdA{AT@5u~X@rL&5U0s0>IN(;XdG^q6PbGBk!S}*_sabaqI5uXPfga` z0^uehYqnE2)c&mW#eW?4D{fd^qAFl3L;q2)Xrs>6)q%kjvOMMhkkQ>mw@8;eMf zbX9>t#m7!ywr2J*H{qI1&J)j5VcDNEM}JwClNoHZk*24^GIyT4@c8H? zmWpsyQ0DFi?M$d;B%D2ibvK{OMY1)9?<{6-frVCJK$clP0%^Y8)3c0q;ZmZis$@!u z=ep6p-31@&^`=Hmg1d{zM1H<)vRQfy0@fWZ>c%N%nHOQxn!ryMOU~(IY;vn|CXcc_ z2hE6NXt)Wl4gNz(L|IZ%Ics?;WOq7TDRG^#bSzU3A5j(q%KK;28h`-a6Dl2!-@x$W zx^7=NlZ|i#)~AZ_ld@Q$J0j(z8kMobES1tbgIjOL8Dk3NO>AHqEA^3%VA(cO-ZmCM zeNgiB7Trxb@kNJgC0&hhC0fDTDx*$cKuBPdX2+5u(EM)-Q!A6|;M4*@$n323WWcGG zaT!`$rV&e3GOsDrIFO+*TS}Ng8s!BbRf?BhiJ!a)Fq>2N$%|;~c1S^zpr9?aT5z`vX z-q+{iS)aFjd&K=w-pN8FN{~p3!@G;9(^uFY0cO z8kOdTeGEU-f_zGGW>SrX(*-dkJNqJdu-us=Eloeyd`D>_HQIECsy#ZnAg09hL5)kp zX5p?0)pW&^0HwlGNI5w48kc5oC(a5vRpZmttsZrk7p=PM-3`{1`I&H~jTWI{kygtt zJe5}E)L03{b&vLHdhE7;!gk8ZpIEY-_{Lb}T13^jv1Vk81a2`SRT7wJMm92Mgc)d` zb@46|ZnGwEj^%mQC&>k8m_nHCAW=1Oo?;F~Oxu2<+{26|&QpHMm|kE1fuK!Wm7BpK zvx0=R*#u@vSesqPSlrqyR;f{*+;5o6#*Q^H^BVRNmSR06^J>PR$7#75-^7*+=8n-% z8Pje51Hp=JT-6V}lUy`asHy{g?fc-TwPGyw2!QJPxV@*ND;r^iTEK2U!~Cht+Lh4j zqp`Vt&E}GDUsAF$so#UYvB#n1T3OMq*oQ;5qMDKgmosC~NEt6KqzRwaQy&6&=$k6| z83fQ&{Q%jLFEbepH3AB!Wm< zHv3j!o+X&aMZOTi9Ts_=5&pGsFBK>iKehVJXBecl6e&2h()$S?o*mDOsojCJs10XN zs(5j|g_=(5Wb#Qa<&)a)E&WS7fPZaS;grrk5{ehCl%gLXvI<&R4@d0Q*ia@9{`LBd z?;P(?ttlVRr}Nw4p)sD$_Fb|G2pNNtc^obTBuVIS#^NN@9!7Jr1i{PVx3W0FJCm^l z!E-UD3EmgDLt8<;fc-G&cnVEfgxc&$_!H+GUg43m$Qo^!2gmBbG0p4n$oU(r`?Myu z64$a46WdM=@amiGCidU6k`nqN=VZ^)7RLJ2QM*bi|U8WJc*m%Yg)97PNPveycnvM4Q zomhnW{$9uWaXB69NTU})$&<|x1dmg&SwH_OsiBonn$(W>Mg47P-DwM@34urprO8uO zp>^@ISD|pAVa`->)#lOgPc&(ecOmUZ~zPGLHtPy8b!>SIKV1kEFoyz&zPRn zyAiCQnVJ391{Pn2x}6mY*&C;CXTE`|x8aDXwHd)p6#ZyGpp{9QB*V6-12ckC)#Qkv zhF4Y4kFO_GYAzsYi~a=>wPRcKqZmz&G?chgqN#IpTU+!~SBobk&GHNauZUqCJ1__!ADHdu}%PyeXewl|-TBZ8c%GCG&jLf8>9Vk;zZH>;nTxxLEsKDAx9%BCz^|x80r%go@YgBUr zw|1mVjdQ6BFhzAPx!_afX&D78{HKv(mqvkfFwxa&qrp6SNF2z1^BagIreq*vi76TI zIbuSex_d$Pz$cE(ei(-qlL!%67wbKBd^>A#2W ziYR>nwlO`MbuPJ4z6TqB9Ila7q@KZjd{=b%+d5sW?iMpWF{R(T1hK@F<};R<(gcLk8o{Iwo~XQOe}@Rf_WXh^B|ZOt$SWy|1nTTpBg$Ehz3)( z%Q!YSHgfY!OG}8 zudXYO5?r8uCP|=O|1LV2(2TutG2taU9e9#z#^UrW`VwP$!&-%41@EQXS7H>2Ww9KQ z5G1$&45^xO5P3!a~HUByqR+o|Dxj{4hx=l_UmWDz{ifz1H+K&!#^4Qg!v z6jD$EpKjymQzgp;PM?&_{>%1QIw*lB`Z=Id%9riO=^(9tF~Y|7xn-D0TV6f^A(I-17xDojy`EX(0 zU#6Pl75>pmCDR&2(Csk7#VDJiMAwcI-~u_RN-tf4tlD)#L#Vm?%uApG)Gnmsu%W1z|jiDq|Y%ljgETLk1p0R{(ri?Mo6+Mn1jMOTr9P(9C za|)Nvp{nk7ch=TQKzRtDq2xZio#Y-rQAk*Tgjx!X$W6$fyx3qv$5>p+aS}4dqF>W| zJtE_-G9nYm7a;i3rj=&ZMK7d54}t9x^eSlv6W0}V6|da zOx}`q%6bolrp_DXOH+VsjfEfcyViv^)4+=F<9Jhm zFO(NElpdW2G&ZF?s-xKclw&zPnoHz8O-?TFp5k%{S-^oPD8)Z9OC0SK11Ssx9B zz0(PSy?uZQ35wN?wIwLlk=}^KDVCiBnIx5X1|HBvD1|JG4zV32vc?NR9+@OPb1&9p zM+mbJAXQ2-WO*L9#*S|=og|V%hfps`&?%E|@>Y<5>1_4{#AQbbJMUY6_hV4v^(0LR z2wugJBp`SpV{r)DHh5#cPm$P^urnEvo&cdejm8j6FOsV5DXcUh^zP1JHLE$t5PG*W zmJoV3F(%Uz_s<)B9!d6%U@9GZe{dEzn8H@to7ba`5d-uGOTYIMeOM}#>Y>^HDPv`# zJE=Yw)Xi<*gK?f}>VzS|mkb{x7@d~LrTuR~ z@1Ul7@#q%LC49TBo6-Wk9eoO49qa_hqQ2dx8_!n<;TqIy7N*mQSS*zCK9Yw%!%zjD znhK`*cB3BaCisCp7|3evPo-Ubd=yKnH2oQqS*?8|Rza#it2;1&A$#EA3{M{AM#=gt?e zx)GiZDx-SS##G(ZpFX*Mi;X_i+@kKeU)?^%ur5Ya<@G<@*g2zEby8#JdH663-iPjN z;t^v1e#|88?mi=7>^iaqKgOcJ@-G~^bFA&P0Ka^Dr z-<-y8>SphS^|+?0yj|Ich5Rx-{lY^QZlz|q0fxhVkZ!jt;Pp*C0B^iJ)|eT0d=Ju! zLBKOrfAfeMO&>Pd)n+}e({YEk#vdnXjlKdcAwK8#M-G*LKxewG8ADT}q)45KNAE`g z!rqVz*pIhXv^~$3~n>biCOz&=X=Ah1;dAwzGy8U}}dodhyF9EQc&CT{txNh~q)KYxX@CAN`4h-b` zSAXLo3#LUMEr&Zy{W4CdiWZ!8{?9Q6SN0Zh9nFVFe_6Y#oBaJ@%3KcfJ$vG0%>BL@Z3J^;fuS{p<0(GF-y(qL;cWO?@X_e31j`&02Sj znZHZ>scwYXPu#O(wCZN?UhBqZdD8}*C>fuX6WnElBU~n3<%RTzxPWTm(&YkUemTN8 zAAk{k8Xo4PDFpc&;6|+{6OC8E(bulnCVYy0Y;3xt2c22zWy|lmGM{rkZyN8-4R$HH zqetxZrtw+sK;4k4xVtO3GqGo*Mg{MQyU?iRc?4IO{A;Gq)$Sky7h#)G`D@IK5kfUKX6H$ld6E4QkFkhL zldwz2y@uv z{RIB(A~?jiCa)~OPhM7?f4_wC;{F8jBsp+O(k3IVQ9}Mq73`0fdfe3!bp3$kx!x%0 z0#?)ahOy{hAYcGeNgCAOIsOB4x~SlR>=UH+6G#iri!{yYtw2*y#_pNIP--3#P~M}s=gJi@|Q^fRdoZ;|0`Qt4`@?rJ}#=Vf-()lA*h{qe5u zO6lr;R%8O0%D;=jISl4Ca4(rIsr$*gl&^}H~fcW|_^&YM;=;yhRIidOvIx0t6- zB98}oGF<^X;g2*sh{@dIu8Y%;NYkwD>}xmx3IvG)dmj8eBxWl^&latS;L)FN;=s}M zK+c(h_2yW-Oo3ysNdvOr%@v9L;Q|(k4gnYlQiH@nNEwVg!=i(UhgF8jf#Zp&KmjiL z_KGyk?KbXKdkfamx5nF*_;C))fKGuO`|7{kNuGS`nYUC;G=roVuv%q&j?bOJ95*0arY7faTY(ZURMLd^874s|Hr0#qF&08B_{)j#X6@|vTFYD7+wgZsTnc>H+|kN+WRRio3XdHpq6Ty8hB(n4EAX54&uDmIq`m=;wWCA{*| ztgOY#m!Nn5Hvh}wd>LymyG9l7XUqRK|H~o*-bE+OufX5BtN6eaY&7utghnC8=HpEt z?T`|*I1PFrCes_US@uH&ttkUw;Ifn*9Gxk?+Hp|F^a*af>uW7R5aK~49=X7^J7LnN zHacvs!vVwJ@*r+cR+8I8o!C>cS(s%dD2OoLCo(I;I(!SlrA0c-)-61h#XOQ9Zut}W zA4tiMXLpdUBCOA}?!#a999=7$g0K%n?nTmS$@)x`PF8p%6D1R0{m+;Oi)+Jt<@n;!}=qEt=2eMq~G6V~;_ob;D)I`8{ zB`S2kjgtfXnw87&_f<>^6mXwoQTj^xclx(ggyX?AnZ?RtiYjC8R3&?i>-zXx=TZCM zv>pBkC9;T-I?to}M?Q@TrFAaq|2N*X(REa^N#Buz)oSW6JefRPN2*ZtKMUj+>WZD&x&wLd=X9)JePd~bz2N8UW+j$C<0eCh6jPG zhyqucWROJ?l7b#}J~5Bg8{zMR)rR_#wMw%_IGZV!bm1)RgWp)x4Bp0A$3|eD%l68p z2b(f7O`VhCjVXz~n&6EsGNr-asmT4v3`C2J0c96Vf_qUUnU(wi%{Kn}njOD!rNpWt z@4U?1oCrU}{<&BToglo1#{vu0)W&b&=_KWO341nflCz6e!3ib}dr3EL75pn6Werxr zaojpWKqvY>g=;Xx3MR(+L)5P-*{oBs=~+a13%gh)G1n?+34PcJh2bgCvj&#!46`Z51RBLIfUo&A_-;z|vPZ+9j|t`Mu}`LbdX7e2RsJBpXh5pc2N?D|gEJEb%B7I!S%ufL zJWq}BeWr(8@Fz93KUv0UvXG!N8=ti*-H6zYPcrGw~dY1~CZcyjd z33?YYCEX8$Daq>(C+y1YBsDc5X=;D!oyI_pG{WVmkK|9*YY@$h%NlUR+o@z+mMZM8 z&IRLl3sq!gYuVSlEMFO+3IDVvq{|!GwW;71`-6T&_Nd#^0x%IK)Knf`9v^|lo?Wj- zWV%n72Vs}S`jmf=hE?7`4JgY~Y>L+(GeVF3Ihx!y%;>5|<`W`ggf1b{3x!b@(NTFG z8%Yvsgb#4gf{QBS$>O6r@ogr`FhKwb#brqF`3D;Fe}zDfG-%9M%vCs?yTxnGtJ7emt%Fx5cV(1bDMvDu|Nl*@skcgvz?w-AFz;J+=i|6d(rS{ajTD*roFt z{0KE{xcf7h;s2B?$e(V1pT7X3-X_N50w1>$Bx08-;ZH2Ak?W=Sq?ByC-lA7y6ZLlS9+}Z zS=kNt%WME=2{KQyzYAyseIZ6-m&z`cg1Qp#8M=G1YPu+7zhdi=Tiu=p5aBUD$r>Dp zBut2BIB-W``6`+Qpbm05 z2Gi`4U8fRH!#HbW6rXU=D}?*e3Vs39pwz|^*%^3}MEeu=aY0CW1|O&^*;2Pq9d6_@ zX!inluJn>b^Q{QS_ZdyLP%UIV9 z_aUTEx(gRBS;)Gd4Q&lxskgih*2D28K}yk3hP(g)yfUJ;I*Gy3QJOkaNMXOh(z|a$Hg>_2%}M6He8G z*P8m6kzDprxD6zjWfKuK-y)A-O+pZNwNx)NGM1TybBNr6pylzo8;9(H0v1n6BJQs7 z9kaYoYPQChcY=J0RLm~E1&%~_Kx$OEQ!MX7-=EFMaFm2!0{opm9s1xh#?i2Qv)*DL zQwYUUhVQlMH#iG<(o$R^TZ)XV`DauU>J{&KR*(KnNdC{l_5?XF{NcWaV3#qz*9xkA zuNmRXkRA2sl(WeKOON%i2{%Y!q8ZuFfZ)3oX++p3HJXungpKgGWjBoXd~~8k?5oM1|61cSdY%R!C9DLR zbLeZrWqT1GgZqj2;nsNpE~kMrtP8>?=A0QOT$td4Rmu{|lCBGKL47hd?YLgtzQ%$} zG|zw|U*v;pgWf8CFb>OEr|zqaUC9{nWGO4ldW9PLTC=P_b44cL@AOL}E$U1ZE+s6K ze96gr)CeEJx=F%!H_pKMU7**h{sd-pMgff(6Yp1osHz8!-9~$i(0g2hQ|4uGbKJ@B zO?WV7GV6QD`)2gQW;~jsyq(D*$N`=HbWi$)xaW=!ZjF|DCGnJ}&Ua#Zrg=&o<%KeC$ zWe4~-vJZdtusEO^-&;nAth)I&pZS^fpeZkhzU^i!O{#1x3ZiM$mc2Q)R+Xm2YhsJ&nV#Me zeP#SRy@wjR!Q&hIs0R!~gt+6=Oig4D!uEfowH8T8{W$$Es+5J5RG}gRkk!4-ER7<~ z$^fs*fSR<};{z24yc+v$V(r_1<`=SIsTJ%NBT7VQ9DVy>j2_zotT_5^{N zPt4U~mpeh7-FpAQB2P~&a%Y8W!V!Ox=&AA2PPhmi!EE+)_M0)X38cu8Od`dgK~R_EbP8-e&9>hrekEypiB*G*@HaLnw~x3|lizP!x!w7fVn0 zxyX+lc1{}b4vu{R3^g*ror)LhLGqSg|;-Bo#V6EdYXd)b| zp;`{bF1Q{-Sg5(P5iTRtL}G99yomTbLCO*i((^}=Qph9`$yBMNS|nJeXT4^0P0x!( z-amSrRyr3AC_c=!en0aMEv{UQUupx*@&yRNa70)MSca`+C87ni55j^;%k!~)y3*J} zt#E>dAQr-DPns3k!u()#MG_ZSy^NE76OzmN&^i&OQ^8B$g zRS$BWRu)D@sMnFde$1U#G=C18fbSBH(7yR2bWxCq$g?Uv zaL0g8N{9q9e$6|kDLiUj59MU_^s6v4Wr8&2f;5%0IYf`5#wBZ!Z*xI+ttBTZoaubZ!C`Y+5#IzFYI z{g*PYG5_&HC{f0+<#{)$8^+ypb2kwRk4SughacqV)Dzt@2o37v;k#MT58I1lTcM5 zHZ{RMI|tfLQCx^jNnzp%A+M`ZR+Y1iqjurzY)e@oENMybK;gX6Zm=3A(RlSkh!Q7PTtBoz9?Z>2$YGJUIEB;9M7roBpkCGel%7fD)$e!m zZ8hJb8!mem%Zrvft|7P_ed{j2`&e+m46s!?jpTIgk~M@VMKmwBcm8D6d95KV$mf` zp^6}@Ka6acGLI?2fqL#@70Rk0OtgmKLRG$zF`2`_8BW#1d&E{kjTO3~N9<`l^xQNi zD;Lr``x7_^_@et`gng)8`V=eWt}8XhqRug-Dwk%YVtG8=QSXahM0K~OD6Bf-ynX1O zs7B~#9PxW;`khloGVf47S)tR1sWYI5=xdFEUo%;)r`;av@ha~j-X2gSxsU&mub!K7-X=dA*VC9LKkS_Y(G3Gzz z%!C}6U{m6rBqy2ZvueA=2*a4zz#i>r1C#yH^m1e41}5#qcO=a=)!d<{hba8hj zx7Sr{rNve(RBCN&TU#MU1xx}a0j)-$8UYa%_1?-_K-W6z>Ez##kK`kr=&XToRkr%jQ}X^_p|^?B9Ongs3g?D>aBSVA6gG$UR+8%z z`46f|SO1RLFHv#`pU=Kt$*l+ynS9n?|5Eg~EQm!Mf8hFkECRg)5zVCsBy=rT2+#BQ zgSluO3YevSWZFPAbDD#ba`Z;k`{v?Kxc7UK&$7lhU?fSt(Oj+`EsZt?7~v6c9xfh? z4G<71!BAy1_YevfL#7@bDe72cI;7Z_f;f{DgPk*f(Nm9lm@xf^^eCasG*VNfn)ZM) zpp;8lkl)Jx4W@E2NgN%fCY_qX)VrJ#J{MCC!_KKDmi`mW+7nA^{SS<+cQ!g_`{K?< z$i2gqo*Cq~7LYdN+COL9nJflJE*=1$bfiE`+@U<<&ysDqnn~n)t#2^?~2s zz}K=dKFOHkv!CXwSmd}XTL-J&DC@9!G3+?7MLDXiFr6{hj!Pw?%Q<@r^nO+T0?u}9 ztd37YqhQ*04GW9;M6?jNzr*(RI+QplCue1758qDzMxUxo0nsrxYH^Ps;uau+X!#I> zt28yG?5m}+Fd=P%O&t3p*aUya=dwu*)#t|KuS7w)F^Tz<8jM+TKs=RcvCXolldUs0 z6siIDkv0^vSTaMmZ6mCUL@Qdv$q-3?4#US|e4_N;-o(dC@EIj&ClI^hd!cZt_r!_d zJQ}8YV9Kfr!`N&2D1??%N=(GjVlh?>bUCid(Xccao2f!R;Z!XJ}F%atba&R{VA*^Z5@GKHvunQf;_<|3fDl(WE`IEM(DE& zp-(HSjou9ueua3e7zIwTr}0wxGfg;PbG^nr_}@yoCn=*fu%!1dT0$u7J=pldDYsA3q{0XM z22uf*ct=tuf2J_LSb^@fZzEQPjRgBsy0KrLfqijq7VPg+BdjI}`$xPBkHlVdT(Ix1 zS+!U%qMUAU@b}w-%M4<(@T4i8b+p(|=jsZ$0vrYQQ{3$wK@$uB`)c*u6Z?vh#P{Z} z4+tNfr*P(!?}>deL;$tKKB2I`4>Po2Uq%_5(>EqR0JTOhCiYz#2KI@Km6Jc509)NH}N%x{8ycS*0X@0i2Yj1?i)VGY^{YfzqTpk{&FKX0cFOp{C@`2h<|8YHZLOL1T&o_&7RE0&hpDAodzMY!19>HTpj7FSA zWG)Mehn6ADt964G?QA0vC`(I?d~_%OJ4Wdjly?<;a2zKP-;0Ltg$Xc9$tN>kw(I13 z=>-^iF$f4fi7&*jokjW*pE1X)Vo|*uhf-e8^iK)pyZH$3LHJ2Cv4TzGl!RE8)P`6h z_daK{r~ECxg!ATb?oDu80b`GcGG~nDn(C zuP_Wjo{aUaPCu^$-&_N&BkmQC>fkU8A5;`^g4+%;%qj3q{sQ@@qP~Q0W%t+cxrL{0 zd5%#vir>Ozw_sg-tGM@8k3(o_eg_2o2*0qX!7X-_SuqYqUP-uzlfVdZDSHUi zQK8Yzz9>f6V?VbUP0jW&oh|qAxkxNr4;SRs-{ci`He${%hmZF&)bA1wr1BwFUd4v^ z*4kyL&w9M}plcZDaefsiQgHZ~PNrB!B3FGU&JNESW#&Zp!8xoR9g6PD(a(cL(8uF} zzTt$%8Vb=|xG`3*_r;+btf5*9H%8axJ4a2N@t%l0G=5YTUq5$CbY4xTrjIly9^mJw zC6O<#MBX+*!kuVFs(Zm()scK!?19~dV3X)%F=;vE7JUdU8(R>oeM}BwDphube{N3l zVNr}0RcmYT&}hYWVx^xQ$&w9lyAtwL>WBY5k1xWVmIr_VzR_reb_d&Lou&GZdi8T; z$2qjy1)I*p(odZ2*^R@(Tcu8T*c0-qwQ%c8jK2>(*qwZr=OcOkviblP)kf8B#xo6f znkN{obZhaTZ}2&s^L2RE9-wD!sKXqE#{3G$I5I1EnGUgD(}3-cXVqSresW=bY(3ny z0NDvjT}92Pw)y~%X3~(bp?fdt+2k82Xl7wEC_DT*9KSZQ8Giynp>ZN*eIob!bN_-X zY?b5jL|+0<+m#(K#>2q35yf)NF3S6&qKgJzoNseYB&96 zbQ`u16L)#F+GEVzh4kF7w6HcSGFs;b+_Md`ABoa9>HBOOiWf8~U!ahs)eb4gG-a&E z$T^1!3)ctVo~11WR)a;p!f>yW;M+4l4Zf}4?L3)tzLYpQkvk;)yoS8Z3imE4b;_T@ zCyCq>i+tzJ8UzEHx2YJTq{j=I60?2w-JmnA_V~i}@aHOO)GundbtjxdaqeehL2HRO zv=e>kr`6^bvXbQcpi%EhCQfKnwii_GH1FFCAWU5N7K_4F;+8~ByOJvkH{wV-9f`)E z?QkzQ@Ns_U;n%WWIn?z+0H}qlPpD0cCQlPoik2jv?(meiH{bmx#Tyqu{?^?Wl6h6wP+Pwi+;5eQR9>#K4 zy@}I6*`&D(DT;r}f}vb2>G{@Jxp@z&k)S|up+>Kal@a`h2O(sXR(oQJ0#Bukh_A1a zn9w@h0D^lE4lQ;i zN)XPtbm2VnD&!?9hRfkD?fze)!2EqYV6VkA7CUkve|jRI0=NUybMg+H=oNJ0v^y}0 z)FRD*0Xr5Kn~R9=f;O)-<&{L!&;ojU<9zOBoacRv_mZ4m<%8w85y`-w>gM^(C*pK- z!L_6ndEVsdR}7~y;L^1pr=e7fKT8F3H5-G6>1m)2?qlMx25XnEC@B%I$I1pIe(T(G z@m56^%>xJ$$jJv(kPW-LGE`EV5M$f5`)P)EG4>9wKPL(gF@~T?B?@B~Zgw#iCdQBy zXAwRH#)_B}hrLr6gI}2m?&FA_b5w}bA7XY-mq+m9=4hQ`;-iTac9=@&q(sR3YLyL-9dy0jt4DsJ+4oMD)#gws(l;{7Dz zqhZ7yZ@93zjCe6VC2W~wv!<<7&w5A!LO)(A9MaD}(|< zA9*sk8&4Ks@uR-2;^s(b+=;xF1a0h)>yZk<1PuWwx7oZ>j8mc5#rpyoT|m|!p#w6OSC@w zaJuS;vPVT%w~!e)ry$UoV(CTzE_Ah$4BbNr?nyDWP_<%o5rQs%fYCD<+odbjOQ99!*5e`p3a#9Avn{);8JwT<;<4-M~T5xU+;;*2tNH{gklfe(eUPg?|Q0f z6L4|QjRO%-ciPMg3q-Fm*5beiH6B5*;XBS06@YqE2|trN%(D8< zZkP#~m!@Xt6nv%*Dl_|lpS-2DJ#QndhTpoEfsYUm;WT zO2XCDyH7cUC}FMXUEb8&Q?9f_?4i+xSwI*3xU`COGRnexO=N}LmpAox17x9^nSr{7 z9b|c*^(UyhvW=+sSK(Rg>rhMpv`+zGsX@I3H%cy0S^b2Fg>sb&hWaRZ!I0?IhyLwut})VN5`FB+o)K9}%&#e8wlOux-ES@Hw z(uArF5xUL?^RfcwjI10l3{({Qg+6 zL3I{i@%n$T>{7L!sXqvMq2_{Z0w7pDg=TUmZrr-B#5L=dP>OEdm{imQSS#2`$4Td+#kd;e0W|l2!l$mXu;3cbM}it!9&@o4S7W1ssY0(i1j9_8TKEzH zf8n}RErZvTpM(50v0^*{SJTRAibPdJ0>c}{EmCfi!>>LCHbJHG92F1c0Pv&I{J!4g z;>_itmQNiX`uCQ+2+$0Q%4eK5N39Q!18L&c9?Gv$!|2^JR4ymU`JFbp~9v8Q76x}=QV3bol4gRyof zYmNZaV(;Q6`8K*Y44;Gz-~hrSPL?2!OsnUK^uRrusd5&1 zBx9<41IGlu0P+R5rQrTttLHFBV~Wf@)SKP|m;jZpSaUHga=2sH@Iwa259J|##Dd*i zoXfPDlv5{bv7H@-T-+fX5ugu!7gcg`b{NmSxP22qqZ8=8M<1_hx<1@D5=6FKvv>Eag0xpi4bQyW z?pFidSG^*^b988?0;nqx#MYtJ3OeeoY^}Cc_JN}B_1Uz;??X7gl|+k5PjCMUD=amG zIPr964%4ek3b(3f3YEwvfW}iZN=hkI_=KHb6e1OItw=po9Xtm{F{nGLE13NLUZp1K6L62pQ^sa z@oYW}R)t}pm9Jv;HSs(=3zxz$1OEVFzC*7hCU)W8XbJCjw=Afx*TMeliCo&>AGx#_ zX!Rz1=OEKIFrCHsgs(sJO1M<@P>WHgDN&eJwFrIz7#kEct0~Cu!}ik>c2_0cgIASW zq3O?q9XZ3>ljtt{z3B<*vpI9B3Z9J^#zx%F?-u3YzqyEz8;f4W|4C+?KR9)Ex|v`R zb2aa^HC%e|KH)Ot7d|$Swuh6jI)S}v+s?w9vlJT|J*l>MN-Sg@Sl|TlJk~{ z0L+9yGaexZ;w`9WOuj$7@&Go);%vc4cu*!W#hX}3lm+XJw~U5ePlkVJ$_sM|KLHUY z^gJBxmPW9!Vw_(1*z<$cBk&)djG}<>+KjV3hg`MV{NCk}{a(GSrtQ!xbnQ5ipGueH z)DZUn29_IdVEk$vQzV9X)5jod<1>FCM|~B3O%6n%Y;TpjJoyJ<@e|RgUCEZ!2>;+Y zj4&o(k9|8_Iu+rh7|d8XReE|X%J*P3{Ux%QEErri+mm85XFILFdpk|B0i;7|H=B)) z?APX;-h<6rh>i?4vmb$G)}3Y>mF}vEox^fYrQv+VG?60Vssvxf*4HG8@C#9R6kLV% zyu(#}hAFPHe?fI{HNlT}g(n|+MT^^bGB(Zu)M8#-BNm)eKf%as$`L`e(yn4U43Ti0 zh%+~#`wkk-zBe+lL_jS?XrvYRTGG0YW;H6Odntlp)Z^AH9o;jh`@8UKHR7Z2k_+~$ z*cIOpmD^pruZ;b6(w3;cR?<|AzAoYyjxdR{B{^cIvb-RreS90J7U%HTreuD>A&dCN zd$2@gS#veN25Fv$F})Dc4o~QkJONHx0pRbRwf0Hi!@@nm?Xz%-UOq~&PH&@DTk0(; z@#<#=w})F`ofwI#YFdFJ;|;aQ(rVk%QFR<5tw;rlN#4ZNB5!$V2`WW3-l8o1)8zB8 zM{u@61nVxl9si6yuE4tk+I9~@)ncUHe?Y8@9uUY#tU2j(0%o};%)hE?9ds&*Y1MS_ z$cV$}bcx-Lfi2vK0yXNjBF`~l9OSA)6Ch9hpHQPrh%b#HHC_f8e_0=d#G#D0PXJs8 z>Neak5Sc9#E7|t*n1DTX>+odPlSZ@LsTT;qawFcW?BIBZ-PuRtH{gl&wQoXci%-WT z>E_S)K9f*JqW>Ti1+YWSu|I=)5LamQaO?KZVD#iVm^E+>Y(0qzaSCmy@u|`A4@T3~ zd-TgAtJx6`bcogLqemaJwNKLs_E5Y{sA0gPNy4VlBFEg$dFN7QwOYK6JUV^aCI!Z~263z~TAZnkoj%TDM zR*7kN9+JLWjBN}zF>5FewY&rvBmbI8>URz&bB~;ghz~4 zA+xdAqjBuFI}x&Yg(2%P3@*9JpFnRzy5eaB@;WlAs(SMu03@UJ+rJjC{zp)wlAguE z1~??M6RTOZR(lQXTQ!^X^RX`+nOB5CD5$}d%Xf!1;LT))(-M$Tpk`BKo*%>Wj;ae! zlzHCX_pUofU`H9tTdnpwZ(>rG8exSx4BdbE*Q_c`=mrED&K(r3I)~=0OXDCabn>=>>O=-zv ze@Yj9$MgX9h9GrDUuCW776JCJ@s8b*)Uiu&a}-<3=d;++ta1|YpvYY1B!O62+KF35 zkJYIdfW<)U*eg^13QKA*;A)$bSD(9umSMObU=Q7{fExx}$J?o0RPUgp>04ZrXFtCU4-}t{L=2f1D6#D< zbjjG0qO39k_s_kC-hKzgj)t<%r$ETS!Nd6UKSf!C?U%QvvCVDl^*;yh0P%D*z)gFm zCvYq9sQ~xy?W-J>aUkEOhdf3O8Z6jjJw%E93}(mFSk56F{~X)_M+@%Pe%}*ZVpqWZ zq7A){HSgLq5*}iI_I4U@KS9$+W?kC24s&xG!Qw|VruUIEG6;4DuuLHTFTHDAK0V3K zO$Tx%nm&pVyo|+;2Do4RwkL4e|3Tma_JwU}BiIH=j{>-Fv)Iu9*Yb2v;7-S_ldn^`x*|wnJEl10_DBzDv39wu^R60MzjPbHu%7ZcMleSS8aZlQ-#Mo{ zsXLE%Q+GNq6S>IXIk{D7&f%qMaWfL;PUzT(o`HsuzjMe~xw`jw3PmVSDu0J6J43%* zMCCA#KGb<_tgHh#O=1+hTZ?@amDq(CR%h1S^W-78V3?BKFepWln=>)Fuf3XhNz(-N zC$-ufu5p!uF6hBFvkR~&+E8Kvus?l;SFOdrE)tKXdajAaVeK)0HDOUXI#0m8DfiTl z7$*s2qhyCTPdeMe^zlu16gUgrJE~oZIE>$AN=_XVWH3IrR&}%TW8Kb9$^<< zO;i`xxs7U@?C3QD1oE%0f+TI7XX#DMWaQKkY$@2dh-t4kpc1Nhd|ug+sK;gYzty7~yA#b3WP?UE52! zC82(9BVf_zoN91jTH{{ATMSwgp#Mm-XF~xvU0Tl!r4w7YK)G8PIxV<)ND$zy&6YJ zU%*C$$1d1}V%=#kwOH>DL_Vv%(EbPTRxNU+KlUO%NVD_lb7`UxTZ}8QHm2-}hnr~9 zCDz5=EPF2uzQlscZOJQleJ2!~$bBg>ell!beB(&68ubO(-x#|e7Iu}~k_GIua4+IF z<|Malz(%{>kEELt%2@kr@#>9ue%n6hc|_%_+y**z1lid?N6wOWFgpVO!+;EvnWTG60h=xRtoM7_H5hR!#+?`9qGhl2Fou+x^Z6 z@=LxWx9f5Has>G$*Bl|gS%AY8rbDH9%-BlA-ai%`nke-eUWqJQxZX(^XwJP!ThwrA z^u51F8~XFO?lw@({Ex)aRqGp)@D(9hS$r!nuf-@wXtf)XucNl)3jTW(|0LZbI&Sk^t%42N zyljR6mGp(lb=~JB6#b^<7Ys>$L7Z%o6oYr>AefAsX3#_VDS-?}r;-YK8xXy-!p$~l zRpG=xzJ*;{Wf?5ZA6vayi!G#vRoiDA&f3J~vVH>2$B=TPd0;o&<7DLcBcL+wqcpKX|MQ}bXXxOwb`o359vA)ah+MYBLWqp$GXtmfv zZp4=uVUKF%-kgjkQzcmU1dL)0trgP1DMx4^>^5K{bW<1G*b;5dvfnxcdAeo-7-uMM z8+IT7RU&`v*s9H`e27CS@bbHpA^rkiNHRox{V1+80{0wkGQ^iKTFTqjO@{cWidR1u zH7Y3rF&UYNl!ylUrFwXR)5As_;EfMa-S9^L=Cj8FA_oY80Oxe)jm^>?+9$gNea7YJ z{&C2Xn8fV_1Z>JEx>1V8UdKBC8l1;$i98zNWJ`RGm$e)Ogpsj-g}XxV%i2iYj($0Q zuRhSFfTaBz-f@7E?HMHm?QW@$?Wa&mC0slkG=qGLrJ30j@vx>eJ?-MS8vA^Hg9CpR zLfx6k7fbT(GQ0*`SwH>W5%VvO<8h;^e0Z#z6LOdb;2QYjb|Bf8QQ3~fid&5e3wupk z%>!V21Sx+ghF{E_HPgW%V?!1bD_RBH%?Zr#pt36Vak457MG0nA9DrZU2h_G14Fi$! zaI*LvRD@2z9Fj)%vKK(2U3z4-zNgnjZ2+ zK1%)$P-qWMB7qZ<4rH$LLpurL0Rhw=JU8=|Zrdm02^9&Iye`{rVM7%;n8N^H&b$km zfuaqIf=VC<$?GcG@dimK+LaFlv)OrDC2t+0F-u4lyh&QX3Cj0RzUH6}XtG8OOUu<3 zW9|UkkAHwCg3VRXWqhlEp*{2g9;-xcOde-!N1`?|(_!=J{PuQ?ZF8sw|3N>P0iKbo z5U!TXYOQj(`CZ&B<#d3cY1e|9>%1LCXoXpl2T%X1rxSO2ajri66qBW5t7XYNfMF!& z@_+cSj9woNMlID(A#eas?5Py4$mz&90goZ?7B8w6vmRW8A3+!ZUQ{ncECDGKbIU~B zG7mUqR_kaG*w_?@^iLywVge~d+d8CuNC2jub*Ue1@xg}j`NOZx6IvjBPLHvVF! zzk$E715Cp&xS8VqbzwUUU>=qyDTI!G5n==<0ZzdoV@QXS z@B>0?4$<}pg(cUh$a6Dog`n6%P0IDIrR1q<^)^9QGQzKF4_$r|M}X-)t8)$HB&=SC z%`?FF;9$O}$_Z8hG>z9`)Q+q>E#B9RaJ^Bzss}t(D}q&ZTCDGhtgns}uTfnmll=sI zjmbXmt32t8H=eFgokEZqdYV{&2nG+ca#gSr*SGRIT#qWuil=eH8z)ZspgmSr}|PRUudn2`#7Kaw<570YKKn~vMpI)X33fI=&Rp*p0Vs;HyB zDT$|UN+q5;#|+n*RdqlLLf37EJ_Lo~dgM&8xuuJVLh1y4&tA(;FV`c@St40vy*9-##&zlT(vc?QOHNlV) zs1!*rhYgm*Qh(!{&{M@ABsYT;GDf-SqM9F&TsQ_|cV^>!3wD03 z6?}BJZBKV3ZxDXFgX z=`(_%z?^HwhFa%b1wKSxB}XtIx6`9o6q{FY=07u78JKh0*vi&9!%)a*MX^Q1vf+EU zOB!dAt7GgtgzBZs$V7iaatl}Z#x+ zLmEd~Ccrw)XO<(5y9{Op(lUv4wAm*!%7n2&HHb*d1mz}F4`WGH3owY|(#WcXRx6O5 z)nRh!pv^deP$CLiF_(qvA&v^#>|cTs9ez^!=e;PD%9nHlNfx71@syJ+sR9pMas!s^1SAWegN!8!WJ zNnEcAR)Yw?FLZ#^*I22XWL7_oqy96^s(LdFIhP$7HwwDkG}Z*odoFSqorD(%G60ns zWQZbJqI<+7AK?-aep}%K8Cot*M`n1bnq-3=2!m7&!M6ZWphHV%@9GNG+f3t%mLg+4 z$lYFDA3@;bA0qp5FhuzA&Iql7K7yFmm4tsfKqK6ht0Hr|n11Lxcp$?0mxQ45X-4ot z)Hx-m(X-xIx+2db@bd;N0MO5|utL1yAvG=VOc1{@21Y^4zJ!PvXT|zxD$V{ zJX{O8mKa}zjH;MFrn0M!Y}ewKpoEe#m1*)F2E^0x!mI$840cU0Z6)~7kl+5Uyi~*M zBX}9ZJC<)DPb(jwEM~<53_pNP_&vTKWg|9RHQ4ya!p8a(v+5CkR{faa z`=N{p%JnvK7vMj-;p2~!8-as^isx60%4Ae6)wO7-4g#dk2tQ+Q>_m^MmNPs-J={Ve zBz+Gje-2F+D)Nei7`1UW4cv$Sk>OD~)~Y!(yfheIj_?swX*hQet=V#D zgAP3$bHN+Ybs7i*CX!4H>}P~F5}=ou|O zgVk2lMH{llR;)l2>Wa-~WgXk5Xo0TyCgeWS%5V~sdVls>%quH!N5g^!8clierWAK3vB-JxzwA06>+>g+pU6BZC!F}@EYqd^%n8a%Ak zYBVtbF|&H1le-}Ip`)fLD}@R zyL_xI_&dpeB`Zq!{@$$9|?31Qqevl4WN90reLa;7eWBha}ImO;}oa^=4I0VqklSJ?|5N13v*q6*Dk z2cg51;`mm5Sh+Oq!%&-7fR=A%zA;9-EmYS_!EwCjm3;?{!e}S?+l9!at*t4AUUMGR znx@!RHOVGsBB>_~Nrx~oB#?X}yxHD`OG#Mp7hJ$1`7#JCF)_>T3$c#)KSt$JJB&A( zgbA9qZ^TpZoc$3E9F% zL=JQnnXYn`(MIhz!S~EIV8}EdY-FARnTaWV_1R!)bER}bm;s5qp-S@{VJfx2jAKOe ziTenFeCiF=dsW9%;+2b}BZ}AS315bIy%bN7LqPSR9@*V+?`983E{46mdR?#@)5#;d zfuHJCSpG898K;voP!j&6RqOQA5R+RfrkKK>FERx{JwftjR)$OgTWuyzb&>OHIH%d$ z@c>#7RG?z044jfrmqm|06eEf02{{6I-WJNEN>3i%}r<_Nf%PY&T9bRi%b6JW)w z!HRmE3kbL3UdNiGK7xz@wV1LwG5*fY2mmriyW!)g8B}3_PGTKga8YOnP(?CnhF@is zKcQ}3QKzjb1O4_T{k9~==Vf6&vp>WU%kH<)wONS8Q`^_5+Khz;3pR|cTA_r*tO88W z;o}iv%D^1ZlqmL&4X>D$3m856UwPR#a&aFmHcUj}a?JM(^ zB{BYDC%`;s@pJ)$Q2&d;Z9QUD=NviIKhM{Sfl!OxOcqTOEDaPYtQ-O6Ayh(3;TE|d z{)GsYBS{p8>gxciw-Hh*L(+f^HtCvG~9|D$%TVwT9fUc4$ z%s3pwUk0}w9frr@G_HeTFIluFhFN(!hGh{E{&&Upzl>p`x7oKC!|SmZlNW*C=NP6x z+?JRw>*U$q8q7=)pIPn5pt8)@;p6E zKXoX3ImL4rV-Mac_ePsA6%RJT!Z=70Nm*p{}C7^QP!Ie z`$onxQ2$!4hmd!N-@yO^#^cQLN0ifQkE>}NnZ?f*3xE0d6z50uzL9N-}7bpjbpC0zq;kMwb~u@^c>mKOUBFa`A@dt7h*%5lBIuV}SpNN}&! zcGSeLK)bLX1G5risYE$XK zW0Mi7YJE-oO4I|JV6dV3bfZbDoqA$cY@>d>6mO_L4z&h*VIJJu8b|WWlHe!vK-lsP z7?tVlh|#9pg=rmG-q*HnfNcLDZ4AgS$U zxM|duXA^OA`W9W$ON&zsX|-2p1Dm~o%>@YC&=uK-^}!nwVxj}h`PAZIXpeS!sl@@J z9;%Y~jhiI6in^u}w0W=8)L)0XeOXpA3f^s+fX6g(Adi}TQ0De@vB(q$Urq_c!&4lX zkQPVBst@DjtHY|(C}J{MRZ<*45JHFkWX`?d3t2QExyIvAA|X#q$*hqkwko?dE2h=R z)T}C&8v$3X3NMwfMoTS9@;R)?YBL^C{LT4{L)(^o5fZC=dN}mY@DS}dT09syKI`D& z3VifAc%YU!=NrVszo%gyNmTU&+rEbDWx)J;YK{dNUn(1Ej_w^_fA0{bw!oenpT=w{ zRD5^{gy08u*1-e?6EFKzwTcSgcG!-ggK~~h${Lb;@!X~KON&K_0tac|hJ_@pZ}AN*Co16O zwC2!)bAmjA3aG^bysWIB1nnLRWmeG$M?lt4*5!a_e~S~l)5)aMNyN(zP-pz3`{E(z z#R8?~-lg7avCE{JdE9j^tQD4U=|X6REvGb!nHzujRPKn0uToRqOH_> zg}pUS`_z8{WHOEoD(>)dkI+?phq6AZzOXOz(rL!eYX=9{JN=p@=CTqO2PB?H8XZ%b zBF+<}gx95kM-Z3WF58Wos?nhdUCECyZXn*|c2G4e16>H9%ek^o@@b`Z+usBrh*S&Z z99sv{OD+s>sI$A519@5fstj7z5qp_|JuC&-OOOf-er_)XRT;hfJy!tEcz)z`YPM3>W1tBHhBnJe4@42+vVqjiJY{!>`@Jioz{4a3biq!ha>qzA*Ey z^I}^dr=S&UJ^N=;3=gsU+3NkYV1p5AF)G*Dzy6TjjQ}hUf^P3IWOJ@L6yt;D&_ea| zi24y#vpK{JBnUoB{joXp0B`KrPUeqA+wktYH2I9xe_NQy}oK=sMUqV#w{>;qo%!JC2@<*0<5B)czYr++G6z0F+3KKkgvk@&oH~w}gRhW$)Vv zZ-M>sU{3f~rAKa7HTpbC85@!@talS7S#Hp6?i9nHr$@Z)hKzXIsQa*Z+eL04u(P)t zVV8Tv+lDUys?gRHV_Jg89Dqms(|*`4+ns#{Y0pf9Eud@S&A#KfM0FZ~CHN5ZlZkqccuwBuC z48DlQJu@pY2}UK1Gq=>P!_2D&IleW__~yzYb&?v|!1>8$1nl-9y(awS=syK{lo_vk zNVk6#v4CYo8BC8wMZPf%&E1|En%iBttU3e3!1^$#Rm&C+%JCb6_Su-n_7a4+D$9Ni!Ee zxoiMl9!zX79*!QYmx0(_{FC6vQ3|2w*qyG%GG9DvLNKl8!`Tj-M@DETy+*KL4UK?V z)cWvbLLMZnaG?xia}yt-opvLHW-KEFd9u^MrW4NDg)pygGeRTaRYHe5`&VCANcxvQ z?AR*!jJ8kgj#MA^OYHi9b+z>9PDXb`)NKbpDa<%Xm0tx7XC}^S=-;imf4yf^ytFlXXJgN@&Jawislo;cN+PG zC*358!R*;@z=NrR;AC_kk1-^}@HhhsLeIe@DSQiiJQ)BTu|21Lr@r*>2M9nt`D4W4 z;{$yzf4Cip)Rg60PYot$*8z2$Ocblq=bY}{Gfoh11Pz^nLw_n~Ky%0+Jb$3y^u?Fn z0{4fi-SEhO4+(Z=I(rSfSwk`d(5xeb~d>L@RT=)mB8{}Vlr2LhY4DtkW z{PB(9aqxm};W*cR>rmGd@(@}Fx1;fbo;r1W`-Bgrtsk;T4!=W@AK`IiKtApw)&_*{ znyB9hL|X4Yb*Ky253nXc=YX0|)cdWO$79!KBP^o<5gtaX@IM+#1609=FHwtl-dnGp z3h)|}ud@2cKdS!Or2zM53!VD#y{{iI2lMOzfor|)E8Lijr@i*viq}6@@ZX^Yl2 z^VF%Qxm*NUbGe^)m(BlR!<7f=j81INUwhz>*&eIln}@pgHe)l(W0b?IVT*hv9opN> zkCn|oaz%b5`oT-{U*VUqT;@0YE!*fE?ycY9i@g6ge`C$GeCu4q7@4Sllg=1$aVWvI zIT#B`GDvzpKu7(<+LFtCksR33i!golG4IJioQ;67r8{#7$jjOdD*>G2!XKMRu7b*T z&cslc=b$^D`f1+~6~HNJkXtMi|1#Op^?i)1og z-p`|#6QnoCB#!``j~*g}Z%$L_Sr6RM!hY!3KHPYb%I}jm{wMOD*Z8#lJ-F1@A;oLt zeQ*8yy}8|82G*-L%K}DdomqxCpv5f9HA1hNWqC%Z)hxsGv01APZ65wJe~Z5r^PVqK z{j8@Zv{^sXsBTO64iaH(e*D*I6xn7dk8S#l(H5S_f1ZX@vJmgkHb#QmcPkQ=Z6XxG z7oXn=(SnF^=_fCmAQs zxUQileQpq2OdBs#$B*EkA@XX|X7%uT*!O#>69=?jj&!n_sX8=A494VhP#(_E%luS{4Eug;I$oWB?2 z*IXuaj3>u5mkFaO^@jQh$Ex!?XyijxKkqHxukF5XPnzF)X5%@ z=jLzVS-eE3vpIA?o?-VM&jS`^J5>~=AN!-#elKSx#H+@Pf#o#3cl5#5mGcgCHG6pa zsSZrhWugRLOO6@%=45M6w}Kg3VT^#r+PCp)V}Bjbm;EVn{Y4%a-i)=g2kzZ!^+P2_ z-ixfjfYmSEIUd*2|Jyk;4|)vBBopvd?M7mk>H}fjdi`#E`U&MnvzfPGQ{7sreZeNF zes%M+CFc+~$3K+WSg?}OVLN*%G2ZoA49x;79n124o(Pu%w z@NKNfL2g`R*6qyJPq6;>y-fUylDk%WF1uMNr70CvU@^Rq49z7J4@VywRF ziyXgmQCm8H{QqtIuAG}We(V0Tl!-AHJ6DF05{RXjn1sp zT$Ya)9wc1~J>6WUVrS8tEJ8a^9pO!6B|4Tpvrr7)cB zG-l=)rMb{3^6+JffE|7)5KN```$ss!n&NNr|CyQZXQ$pf`Y=RX3EerNnQ=LzY#)>1 z0@0HJo%RBm^_`xo&v_rueBV3uUfNUkn1I-qVD+H#dIt7DPO=7m*PUQsY)M~RyeN7( z=o!e{tCG4}@7;-^f7{px>mYPpo{{U;m7q9wK;88U&0)DU0@eGB4kp14Fb^!^zkXsI zO8!-)3-ej0eGSXKrlhqH=EG>wsrn#psLsWR^E}q#n{g=p8@S=an@I*dm=oTX9Bk~t zD!|IaAq=CLu7O(oHoUe=@DEFjVX$D(&~cOc^?wgtug9|Ez_;OSwLcGgi!rm*AoReYrw+lRyR3Z7-cgOzIP!+#l`H2K|E>Phj~dH0jY(%xU4`TkgWpE?-)lAy^CsXegKO0q@=E<*q2s>o>0Bl^a^Y>l0 z4!5w$45mc~f7ZsSDUD`sK$Qk_Z3!dWawk`Rv|hq&<{QGf_!lU=w-v^R#N@8NTaw#S z@+T9%FH5&l^M9&Wsp1POmuIyIBQ}lx-6NOvIv%)>et=-sh{W4KYu$an;z)%jv?5yJuOEdiJj{3f=6|B7#bYudbJdwq(J zo7@ZVbWd>r(Fv+~RT}W>a~%6`Mwq|!vh&l>`J)_V0x5VmGz zo(UZs$@H|J)uYm%H5Rt=n9G#NE8dzL0+A~N-pE%_M`dn&7?p~QL;JI0m!in-_D=-p5~Cn3oZ5$ z{+qo4c5lG|u8dv!_fWBGf5gIv91klixTOme7_x}nHTVv8W=egW)&(c!vObpK>H}6! zYACdnT}Hh|zNx_gdZKInH1p{v;7hZ5C-!8G?FhOddvZII4|6|&g5BCY6*k9uqgg+Z z%O^ik(L6y6frd(yhGX=*^a4z`>;f>#F3^Uj+P2uX>OzjyauD-&XLQ|2F_R*pvV9jY z24h;gu?O^2xKT1Dqd4|xXhB2w&NLxg^ivU)cAN|Pn>>529|MsyI8o;3dt+^Q2i*y9 zP)}+WGgz4~`9Y@r6}8WA--fOs&IbO&+a`b$%i!HMs0dq|LpvzE^&8Feal1#qra4r9 zBOW;8iH%tp{mpFTx_0p|XY&`bqRJh?%5D02(NMjQhGU%VI3F=2FaS8dwhbLUE^&k3 z9EMJaKPmGZ#S+ z6uoieIuLF05y6Sl0|$(T20LrlFL0p4sBE)EHwt%{{mm&jlw+BfBLB4(>xBgu8Sr2h z2Br!gBoH3kqK*DUAVC<7%2$nwb&fi(_Q#>=21DDl*xmRd8d?Wx!dK`iGQ2(vmx*m= z<*R0vQQ3+koYdQJ-Wp4h#@T)oktpR3gt2cGik zXAD17Qxb?=0d6h9e)l%=GWbCK*mgOo(Z zr(GOJbp4)z=*oT4l^Ai0J746hkS|p`sbUVgme&6c`*d&@s8UsdI1SSR*vJfE`(yYa ze``%yfcqbiFJ(UCG9UKvjahzUC`AiMk<5ZEwg=Tm=)dz3u22A7Qz8)(OhJ=;1<#T{ z;6c^(`bc=>5%l3{phJd<(8HPqp!-nJ7#A(Z5=+3uewFIwEFg&zBmk(CP|hNJE)WGr z0z&EqD3|%+j$q94*{1j(lgTAG&xPSI$3KcWccTUtUkW*ftJt+E~FO4;#e-Z9D zV7#GN19qEGn|%T63t9Gb{3?4r^?QgGO#81vp)lSui!t$FE3XWQ(Z7Z9rDlTRVIVVq z*IO7qybd|fMaVhNQ9eAxc@9c<%V*5#S9pzmRvbu3zDcnzx3hWsd&(>4V;V-14GTpc zLfmpyRIeWgqSLAh*V_|N+>uAv1YU7EyXMRfu0GXCcn%nEDo_dpl<(Ay4Pq+j9Wr8)*Dvz;|?Rs59;3 zqK^kCZ2bJs2fLCDU9HL0>Gmhkqw>ca@U)C9wySd?AM=7242K&6j$IQgCl8}bf;>s} zeC^w*o@;Tg&AWTAJ&2_ucQOx$;A}vEIb^shJdxvh2?`R*VYgvCNu_WZuzIqQqB}_Tdbvf7H09I6=w-8D0=e3&w>q{aZ(t6SA;_=6LZHGC8d%9Z1Pjnd}#8JQB zpf=Rvvo=Rb1X|pKQprW#D;mYW(e}%mLo_X7|9c%yqk~Aw(S3sP)wc%tt&cACihiZV zeu6UT_=YG+K&%SUtRpBil#>IallY0AhxKjiOOsKV80L^%=1(SUm9VKp2EC4IaooMG z7_TAQ&ZVgx(xMZ6g;h1Q3eN^3xJ^rk_J=_uIDC&ZXloi`h|9l>x6yT%3-f_btVa-C zBWa`GmFfLH$L)7~D&hX2$W!p*yIoz^yxn7E#pGFAlLOxfshM;dk8>j^8seX%7nsb9y z;zv+eBLiMj2CU<8$QH;v`M^tepdDoE!6(nNDdMv$&Ki<0vqyj0 z1dft>S27*WuLAfrFLmLMny^5=npGrLvtEc={_nKj1NWiVz{AUEiFmjqc4Ry}mq_7Z zRyrQeOkCIpt&FI&`(`L?h&J3-D5vo$dMQta3vt2UB>;ZT-MTU zPq_t6q`hMq)E{ln6Ejc%Rw0PW?dkGK@T=PQ|Ciez_Fry)_sKi84}XB<)fA0zT#N&eAZ?=I};wNc!g@QLOuEAuh^ z(Kx@1`r_-C`B|)y`It{R!H>I}tGB6en}wB>I}6uMbZoh5)PqaucV~ z)F0+EOz2m`G`Uf~+C&&oznVuUnKLktZq-YHM|s#ik!E8eJ&WqVbvc|y;YG`7G^Z?ZC(a^uG8^`V5Ya)S+7GH!S<^`AI zA@*QnzvUgZm{S+>#1srgP}-=aUonS@Ot=>fV13NNSarW^_Q8W|)wb^i^3l2J^Eb7Z z?(YYF&i5x`#Rvtd&|2LKnra_0{3`pUbRMr#eg4hZ-FsmocFzR${u(r=mbz2%V@0rB ze?nk@)~i2h2fO6_hyq=lp98Q)!-g_XIly$lMq$80AJ$+Go(MBsxVMQjoN&ANGWM_Y z@GuWY1|H!SR${yd>pK-d8u;A}^D#{^tjS{7J*lR}JdRw@ii%d7$D*RQlPfXaPWu&k zstfV;>_la*uAAA>xjwHRl15b&LAQbpK8IbaV7O!|^#V*pG~&a0V-i z^&S*+u1E9>4ja@rgCInoe1}bM(kq6%!6nl=yiGo9O@Y5e`o7o`Bon0{k~=B`!B)iD z;z9=&ZFX>}u9SHUSZ8WIAhpY(D)a?rL)nAuCa=rURs#E95J=TrKl^WL>hF&>=gUa{ z?L#aXnoJKYQ)igdeQrlnw5j1^1|sUD@(qO@A`UQcm&KYo7JX6ChHEjN+^wnXFc2^+ zfCz~=ELMtB!r?QPDeA@6?MSd9o<|#pS-*l|2$$$?X`UAQ7Qdb-y8*vP#h@UZbY6xl zK>iUW+;y|j5;T4}^H6~s#MC)nmcCq=U4^lBmcN9qH~UsMi_5}ZM4a>lH4%Nw5Icfp z#1N`h$uD0CN-Hts(|U!(OLFDFnFx=vQN)m?!QO%61>l$CyeiZ zdm!j}rabp$qmF1jnbm4VQBunEJk)R9_fid%vUfkB&3H4*(dZ_``#w^%pJ*d zn!X&ydcx5wWup8NDzPR-^cNf$H3g$JVJ-UG;ZAZ^5n2a7{uw7k zdK-O==Hx3z?_{THzp1zj#EG-I&Z$d*A7Pl_>VgeZ?nDk^LQSXZ*x5oHKI|H3Yv5My zD>=_t z71?)v_}w5Pb;q^OJNrD{k@IdGolQaWtmNAjdMm&(p*b7`J>m@hvZZ5&h@f^#qmx(P0H`q1wA8!!y% z@j9F&bjOQI(QZ_<8~yC(K$i;tEFasd=k@&NV}~A*vSR-!n8P4FLOXOHivxxDqP*u9 z7ew}7q>qa1uhP$QD*aN=BFD1G02B$oAmLGf-cTz94fvjl7=E!PiAv{8PL& z_8jrO!ob;tW&{ELk^}JI9*rRVJc{%ZfJrhP1ufCe-YPVF3UI)?08J5+nTNwWZkQLl zf{^XEVJ8+4UrkdC_9QQ_2^f=e3^COEk7S#1`NBYt_Vwcz9dQP|nrWO1l(Qu6!V)k;ZJd$lTws=DlnD3&e{V6?({KtzKz2S|3^H5tsQ4@=Yc zaPa#$E%SXi8QW;QtHrpV-18N3v5e7&@FOt(-96ue>&JxO$D6MN-_@&{vOQ%`U#uXG zD`KtW=ijvkR}jNbaO z&BY>O%;SwBOiy_0fl}poNElm^pSp@&>O8z!?}#elXdLjo_n1h?Vhj@fqDwDH*{hA} zcJoFbRQ;0`Kbd9u#&(eF*UZ73BoAUnLdR_L61t&anA`cFE8GI?n0NYM_&YPReLB|c zV!BHm7P_`Z}u>KfuQ?WxOLIC5JgFP^?m#Kt;rw2489<>bdd|u@034l#(C*w zb!wC9Y@qC~Um7S|9mEYP^DhN;;y1t|*8Wc^L*||#A3$lx8AxhLc zP~yN}wOd>o{nrb415W`Efsn|A?g&-Lc<6vdMiWMoVu|G7{0iY)Cl)!IS2g$Yb?Clx z&-wPOx^riSzbzhhM*JSgoEOfW{gs&saVfsdNt>|fdz5RT`)|JG##zmw?|X#AM@sesT!D84E>j&8O1)*qkRZR2YBQ8IF4dkKvZ(J-`hEt)U7lEh7f#(&j zCf=l}KD69A77g$qV6zsx2JnE;6CosWXrmrrFcj_%w|QatO=u(98PJDNM9s#HBEz(< zGzKuAas3rqPCo|G(U+iLup|5m9y=##2+wfBbCpy$Jp6l{mO($XSUsLj)K6ml$K##l zE#rEM-Kpb0gto4H2BDM@e8;GK1QCynRwKOB+v07*k|(s(i+?TNreHW{*2z{=DL*|G zU$M59KcTn0u`2+xti|dutzl_%S+*JkJ_PqVghjd=6$?9yqP2L!yP66m>>FL!yi+_TJ8Uq zAMP9dx%}|fEB}}u76VlB_okFfPP26!`7N}(Sc`o|Zm50++Ai5nFgL4&FK$48CL)&%*b4L3{taM_k1ok%jb(5#VeE3SM%&&tggiGwF>Zs0k*MOa zL;!x+Xetx~v$Q7k-aazPFl!kqK}^AJK3R&Y6`x!IWE8*(gxFHFTs-F8#as{51Bn?Y z`iwm9CNj!&9~o|!QHqd78WzV!#nT1rWPOYfZ?AzPi8|O07E@(0LeC&qi;F07+HxG~ zM|?cs=uud9f*lCh8(wZ_lR>Nfg?kENt9;eGHxPu!6r4mKA_TJrnIQx|UuxtpM2xFa zZ=v;2h!XB`AiT`-0p(w{C+5MrYBfv?d~a_}zzO zM006>^IPlj8XC)KJnvLHzW*Wk7Zi*;7g3YLdC*S=jvJ$&)V;pyaqeZIu}|{SAk1lN z@r8p@Vn62tC!ibid?aue(hUz`8yx%Lb_a*?dfn5?^usuNX7i9)o*a?2yGxQ}5yB;R+JRtdUY1w~PgkQ+wjmw>%?htg z9WHc54z<<+esN-hOY3v2cQ3y>^c8sNJc%n3t&}^!Gi+-miv;wysfsjx-%!8)Q7A)t z2;<&522tb1->|T2n(?M^%ye#foS`%+bzYK0Ag=M&`+`Qj(sMZ~@*vQTdePP__bG>WO=ZDd^I+<`jEnIJHD@Kof z!++ZYkV*%TMglT%3jlHSLGqW;Jk5BGJcd4*C+CAy=%I_WmF>zcnJ~#>(r2v(T8*Z< z9bcz`T#MZWR3m01yof%9(9ytO;J$fC43`BC+@0_xL|g#zgIq8}fcq+?e$n$SOP3o+|NIa&AG$R#f+=S6_WKk-O*&G_?2qhQIg{xj&$V zxwTcD@L;zvRd87j9+BB*r}3iSaV- z3Yfknd3bINgfo9)9`do2&2NlZ9rTC7W94_(G@ z%p&mY0Ep0&r{YesucGQTgu{vzfZQ%%spR$-ZX&hj4p+3g5!CwSi-b$}t7=o?CJZf+ z;wJYFaOuzAgi93XXT;AF{T=!Roi!2gqzjJffFY*=^W|3kQiL-CB2NP;eF-0;f>eb` zr^4s{1}DlXsBj_T=s*Yu!Lb8!!RTEBc9KXS);Epd<^m!MVLgP)!u2>^#&CL^TKrag zoW~>Qy_$=KQIk#G^j^3g#9p{{etQ78HxjZiT|$Rmbcjm|u8z>B1R|A!-K~>evx->8U!Ln;8T^y#0>8=7BTE-w?1GdSkip>PSWE1Z?eTV3O^}q z>)Q}M(_Ag@68ysa@$D?J;1>MH;WSCJ!t-f}>&|xLQnDC$GvqS-%fkA-p<$#BE&d(+ z?CI4B#1w1sS3v)WK=5J&V+aK*+Tb%PVuBT9sf67(-#RgfPDm}_XENUgV4bS5>P~r{ zv4Hy?+H;7!B@299jl9iRuv;EZ8`cp7!gp1|_p*ko({OUm0}Xrp@X<#6CKN5KXU*{$xHQ_3$Y$-V zB11Abm@E0(u@_}WI}yJARa5MNET z3ocb#we9Ot#0Vk@U=q=4K-93PQBj}Ai5k(8u$1|Izvn*B%w%(U`~Um-NanfwJ?GqW z&pr2?b0IfWFfJ=%Xc1=;#a3lT-Tm~60whAP$j|LS#GYw9euE~9zQJ>5=s>s=!q9@i z%&TxvFrE|4u4zTT(AgCjWnp8!*SH|Z_xi#!G3Z%tJu)3b&V>qh@)+0Nx|TkC5oqJQ zqYX(ngc4nic)fiY+c2hO8nZmUt=jT(eyP^`Mzhi}3<(yl{8g)`pv`E&8*gtOQSX!I zZ|3(^?-u1h$GipO_7!FU(3?P8hLlDyhV{(hdPUP|m;~e)t`HBTA65D$HMb}KatV}ODcZU_lH^kXCCNOZ@Za9%)UjRaC| zsgK7&@h_l(b+sgSp*ZvRc{m4x!UO2qezpM~Xj0Wp>k%BK7)cD?U}{-`7c+{d*DQd_ z*@oD1!3WmHCtMo>W8)mWuBTwEC>t$8W1s(`Qwc9xqDQ#({4NI+P}MXt<8UKhQ)2^a z5%6(bEpjaqY%*Mzb_2r6g&9)G?)PX_)O8?W`an|-O#4LwB8Dhvp+lwwEl#l?M7 zrLe90$ijk0sBWBn&bV|Y3~OS>SM=@hMnp|G||LcL??HI}iy{w&3+5M1W4pi~NJWZA_? zjk+U;(6rFwW!QFA@5bFyudxr?)zhtgXxMz^#}I+uXBWZY5DN88{3#2X?aJ~se$Vxt z+e{iXb2lf@QcB*S<<(ygYD(QAxT%Gv-;yTvq_}T8klqV#ek8&80&I zfM=4)Yxq;hx*xu>>@TkoSwa|18ku9KWE;Ydks{m!HHNExkTS+DGg^CI_%kxcn&W|&S3Kt1D2KJFAIZiGILN2*i}E7hzl<*$ zWv^hh?P*|Bl_U^sQeW$p1^8vtQ_?s&jpbK>Jg;iQe&ED zs@`}ieB(9-e;t=?uYFsnK6}L0_ILCdCb&2@A83}UFUH8DlhWfOI`pDeq#3t)z=?|5 z&;VeDv0!g0%)w4!Ov@%Dn(&%o-3RGdW>Kp;6`vt**NT=m^zPX_mVg8z&rX z&(h1vxtj30>O{uPC7F`e-KOz8s)2Q4%LV&1o+Gi7C7VYg1CJ<#H@D*?{L=%x(WJcY z9$A2aQbBhyYM17t=b?9%_Ark*`ZaCFAQAC{coWN+3H^o9{X^tZAD@F&OE+_jB-1z39k>+A9ca6Lf&%}G z0=CQu8MOSIwBYd%SK&k_W<4HrC#zo5mf)CvZ|qal+LWwiK93^2;lO+`DWh-RE(3>Z z(s5s-fC7le%$kka$pH&TA>W(x1NM0sD{#sESuVI=1lj^(M zL5lfaF1Bt7ccgD>V3{<^uh2Ud*YK@LAtcl_KS4D_1qgMVsrS`Z+>ad>?(k7Ex}PjDu>- zM7YE$W3=aY)O`e*>nMu1JW(p~cC}HuT6?}u#IbCMswdd^2<5w>+78-z5J_EazD@Hi zIO9455W3AfJos+tww#Ow?{Lu;XGSLu&vz|c7`=D^p7(rPt%4W=WMBuzIM}A%>{gM4gjza7BI+_E((+Fe4(g#bM9`)|Jh&!T;0&1I|DGU zMn{M$$KgXI&9%U3eDN%JL~{_YSOXqY$5^zFz%gL$RbUZXcoM!WUP7*Yh`j9<{%YYb z1~I}4tytt<>YrW|yb9IJZ*_w#-Xq!G+Q$_buy**(D0nUl^69L;OuC9mr!Xm4Q+|8D zG$g%&IaxP!eqBy(uDE{L8*-GR1wI@;evi+V)U3sr4 zsS%HTQqO^z1Yit%*uHx4a% z0P9H^KvuH`R;MvUah;t+lQC(hVmQA}MD-5`&A4RXlV<8F94vn^7cSG=hf-ItHypay)oi$RBnaa+L2culsN#OBYpF zM4p$k-vSC~QXWg&HW0yHq)ZzJLOI^RzyR^sZlDno)rp967PAa>5svR?y?_K1PT5c= zcmqQTM|EVu(HVG=?d~N)>p<*5Zgtz9LR%|E<3koa3{7JjkvmXmD!82zn>Rd3ZAEav zKL77wC`DpK-3=%kH}6IR=8};P53op#D?>scE~^$Fz?K?9F7C6+>nC~Rh*(r*cOnav zJxqOEdO%Q$`$whWm~w7f0cCdRveX-$f)`*TO|GI)`)ts^VDq6fkqB`=&Oj7G(=dpF zR9(@h;VGN~CMvpX+R;pLyU!e6o zXMODp4aHc}gC|qtW<;1xWRyT{z{A)tkg1A*sN0|oFlN)Y-CjfxH>p?vQ*zU>7O|c93E9NossJ9KSXtF~ti9rA{#_K;RVl zX!SL-I7Pk5f|fm{_6pQ4a2RN$5q%0V zny3Wja!F^I{kpe&6k1jQg;afxP4&wHm<83xxrE~h5a{%+#{USs0n87Osg|2UPox;>j|q1r@?4v0!W*fw^K45EWi? zNn&+bE76c*rm?#f16n6!55{rH6PVX3u0d-K(hk+Aiy1vqizFjc?qiH<&fbtD=f&l~ z2=#4@mm-C(jsje*sA>xutcNf~iZ!fg-GdKjcZ=-9hb5u65T_iPrOSBnG?^PoV;`c~ zwbR`D5qgs)2xep~XKH~KKEz4L%8f>a=oRw=lQgP@ASt4#y#m}BQi(K|vH%E_Yxg-= zwf@fb3dg_~9HUsoQCxsgsAc0iVBW|^yr9I5bOfu8MPd7ND)t4& zEGY00=nBc0rZEpv4;dv@d08V}uL}omgqs2vQ44lry&0iz{`vvLW`m61Weq#Pow?h2 zPePHwuLs9j_^wfwTZUGQ*^EC&53e>TyUzeopVmdbWCe8}eUbDUJV8`|C{6bGK?tU} z%0|OE|IT*DKJ20Y90;AFtJA`7a)}RyNi@{ZW3j9KXah+2X6L+iV1#y8J4)!8_k`*^ zh6~%;M%drV6;+1o7OJJd?A0&$45?qUCZQeZp{K(g2z-LEV_X1T;Y<|hi?qSP>x@E# z^--lmo00E)%vyx1!;FdUC&11SEn1&d@C0@k`E^;S#lw`Vj1Wz_P=yxF)O{?Y-qx5O z0CViuP>9PdFbdlz5~#lesHc$fnELCv4B2D}tMP3PSSR~zE%w>eGBZ&owsWQ4L1zMO zp;j{@E$tTQ^fN=&H&}eGZCelMP3_3TCCf)hsL5l6_nh-QV)L~k0Y^J;6fy}8aAapk zd<8%%uPZ!VDq?T`1aLV29>}Eq;FK~PVnV2Fk88nTT%$s_T)rede!3Rk$Jv$b!Sy5_ zRBFRDnyU?tG^Ypp)~B5wM`*NC*ez(0t5|9%?^;1P+4g-<@gCna=#}?#EOe0k0>=mP z(hry*_km^&C@H$g^)L@L=riiB#NlZO1&Gu7IJe_ZqHR?mxCbF&qmx2eI$lnIC~BWt z&HsV$RF9*C9M1Il-UU#i+t&W9VC(yZ=Aw&iv+k#sT%Cw*ej%qEKKz?R5Z{q`)T?tf2_hLMoAW*FU=6eYk`%>y=7)jVeG z`2Jeu-_c%Gqxwxphq#~4AR&`MgC`?s4(dj4X;P)Db}l^0IZuE#w3oI`j=J^e#S@$O z_THLdPN@YSLh430s@>#WZ?pcO}WjO+k| zF$UcN8s#y?f)EOmhy9Qp-p9;g3yOc!9u6Rzmz#J{C_ER7*BH78DXty4bz%jAh>Tn@ zhtL$loBtlRl8Al?o5m511$=a`xKZSOQ2%2a-)z}eHyq)qA@!griaGXcgV=|0Vb)Y1 zUKc9av-DEL6D`>T^1=nh#=6gjMRU@(9*nwYN6&hhLoz1iot5w3J%6lGwjGoTr8n3N zw~}K(gkX*{U_L$xZ@BM4Bfb)IsmG8GVt=zI9)vQWIkb6>0%vhE3=F>jlt{`-`I}#*86V^jASd9WFgqxI~AX%rS(hGJNmcb`bx^=9ms3zzcFa6B@VDz zjYr|UZ4dJc>Pe&QMbzXoRYkC;tr>BEO~m6J@eytLWiQUtw8{%G%6up+D~mW>|8_j` z>}pfvV}SX{=}5LN2MR^MuegD6VWx35=?(S)Q=^kA5T>=o^?GhS(5k~oxnz%a7tHuz zU(yS`L^wL&EDJc(LLvl~wGeL|4&FY3R0BYr2%5{7@QI>> zpQuE%=^-8b7z7CNLwI430&#VZv=`Js2hbQS-Rj6A(v*Y$is(YG!g>*~ehs^#DOn4# zeu;zi^Q9#%QpD`ZP(u^zcppe%{eP{~m_EakU%Y$%=SEpGHp%0SA;h+?DY)ARwz$6X zHR;98!6wVil9Cm3{1Izn*!R~Gvf{j9-Y}HwcQqEe7JiN}lT{u%=v{ElAsEUHMAC*s zs3P$dE~0w^o*0rjMcoCsWc8u|?4xSO`ZnpZ+7NK)^$ng|pswcC9hB9BHUPhIU1$&Z za@bBp$24QthssvR1LuVfj$JScD*M$ntR&T?!@=lKHLLoJ)k=9ylsCcER003-FNo(S zJ)BL37JC3IdMLW5Y{R*l=Zt`udjvW|#_4v(B=y&`1-(%)`X#>2ubxr%k%))!u<%9% z4C7&w8j*(yF~Rex=r6DK5f-3djl*A|e!*>i;MBZ*8*my=TUWjSFr$h|g}tag8~gYH z6wr!7oBB)+9nQG@17Y42U{$nT<>w^r(Fb{!{w8dieQ(>IyIS}QG+TrJMGVAT9slBOf~XEyr2v=)n>dDRz+4+;V+(O%eD#C z_P6lm+#R@?kFxE1SWKHBIl<_k9`{+&9xkFNO%0K>;8*^1;ahK{zk#su!S(#AuDiKjdc$pR`V$gDAfYMvP7Kb3J~h{=qF7_wIY0ASFs)d5yPxXCG%fraqQl>0*5a^@k?U8;w@1A+ z#_I+uMsaN7riz={?dG-EI^Y*)O+rCi+yOeAaMldxJsB^c7)NlgIACND#_S1y<=z^9 z$@b*_TEUtlGoJyoL@YW7;(Yw?1>w(Hcr`ji|A=m_g&*g>1+L#07q`|Q@c~q@bt^2t z8`&~ugF)b$uL+=m%p_>i7@oP-bdQG_RwW8S>|VrWQLt>p{15Ermqt~Fh-JE8?}Uyc}_Ci`SNYmDtkU2mj+{SAf`ogUfN;O8|A_8aWr z&7hHqm>Z<2-MEH=s&+8o$ID(^4*HuM5%FkyP1q|X-G_U<^C68o>m&aPY@|F}8tH|o z?904A*eckDoLy{khXx^Pq#8&`RI>qjV$?^Zaxmrr*i$OBi9uu#u|R0T!X-fUn!RQg zva~2qH+h77lVXfSS`)gwx)-=n5NY z{yosUg3Xf>IM-g!kb%LYNO>fanLl7vm)NVC_63PmjgtgsY2JM-7C|jnihp}-owF+b z%vFKg86mNM;)6f}Y6%-)7hX#V=-629hT&{y7Fy&qG~fVgjWt|4^B7+170@mwR{`Qi zb@zShuji-4ItbB`M&w4!~e{x^d;pm0sR)Q`RiH6Fu@cjHk4gRM&<_7<2 z{?RJeZb#Hf zEk)#httt%4Cwm_VKKTUvN2$A`>Y~BV!KE=cR4S>4kOe` zh%tpL_XK80~C%zv=bwmgN;P7szyw?zF^;$ngah&%X z{ErJ@$nJUOPZNwxmR0vSS6#_NNrulRgn6Jj-~%`oauDWk9tpnJjoten$I!x$3SB+w z^Z#87%k^+Sj>`YEUb5P%LLRWg{duRW*eq$G3kB?q44|m;vrzr0lTZxj0g9Qy2bH3_vn} z_ERu^l={r@-;18W;P2%=J*F$;2f^mmLalNH47JJfo%Q&ku>5-w#maww>VS(M#e)fY z@%;_{#+Ny`;>^&2!+H=0|0Xq9?7II#2H8JcHT*3f$;a-R|KM++Pt6SonhYhOOfYX( z>VASLHp&#ghGz|@*suE(Q_BMystTuj!B05dewc3Q{a}%p?){wZupXx?JDt^^R913- ztQFkhD$qK}Ai*6XHg`B`KDfg_9|L!={Slqru8A;FyPaOV-QMtUmu_!(G}nh|enNAK zDJN7TM<+Ktgr|WZY0Kxb-^J|xuFUHjkNqwxre3slOD7j6`2*rYyk!aRLND9yL96LHvI9J zn~{nw6{kpW&?au{c|EQQFoFIe=eqSnz;#}&JTH#}whXQ*wv#6nu+h%w{7!15VocDf zMV5i5kT`w@ih9-x^t>_m1xhfZ%!hynbKIY?^z377iC$+EjOP1WxNF2+32dDWZ>ZLz zSMab9b+6R#rX_-2K};h8CgfB4ClMZEVuMw~1q6YfyRv_0^ewh>@vnL3-r$~8>xjy1K{m`uBEiVU~P?<$d44?;UkcvcnR*# zuEd8id8~2YS^UA%k4bL=cGLn_KwSR#Guxjh4vk8p)W})MyN$=tEZL-iP~FXSEpCL% zn1LXDfu(apw;z!f?2n#NI4yXywx%d^q&7Z%;WhB94;*J4ZTR;<-Kr3V1z~;$heRCR ziWE2b1-z;_WGyU;4S$RFeAZ^GPjmFLH~TJ@!7#%ZvdK0$Z?i8n2i5}gEdIhgy>-ENBL0T~j2B}?2&$`WR2N~&J)?RIauTR+B4j3yg}&;p zZpg-KO}(L-mchyXJL6jm{|&hc=YwpVYmxhiKpXE`C6ySx;Qosdg8SbH?zOPS0Tu&> z?#qqh;V{md;I8;Uq)eV8Lc@O!DG1 ziKpmQOAfUJN`zih>4TbZ;1=&Xp%LiWg2$5({H;De2;qN72-`piPV6aM5kfg+5*(sR z1j}oRz2BLfG>}v9@5UgZnpZ$Icq)VhqHd9%rFkNc_jd>>#X``}x(65~!?_%k9&9(x zNIWj^sv-6W_3guri5OboK9rIEuR#C!u8f_|`8c$NJ8J4&dZ4W*v3*$3u8|DWAntL4 zIvo-eH>m5;7TJ0}C8}Ti$v$t9{<_-_OvK23i2cBkGL!FTKah+$mEd=L1Pq!9al6Ky zpgI3U72gs>z@RgIp2H75QV8ig8}|+Nd#T~)ksPgDjel`d5qxUHgr7aVu2Nu zA3Pn`Ia-((O3ucCm*;lDjW_f~x!l7A+w7MvMiN@khakqGfa*_G&;am3vEX2{cK<;< z{d{7e0kRARcO~kRn9ypPnFWxF$$%Rs0|lxPz*V45kOxkM6r{@2l+(-t*N2jO2yXQC z2ghOHi3j%Ju3uk>7l{~)%e=AmKk;=~3h~$uV*2nNHO$}xFrJZkhzb_g*Pw6xu(gwa z&ZQ#VdJ;cDR-zAkhw%z3TBq~w)P9f&EJF)Fh<+blO~BjO!xBMKTD(c$gvWQo+#9*s z-H$ZL%;uNF7ztKhZL;qf-bG6E78joSz=elP!o6LAF(Sw+0Cc8Mo;W-TESp9vNl-oe z1YUwvk9ox@NX>{HrRLL+!LwLHq5A?XNko!1p4)?#(Gd2e_57K}eXM_*G{$*K}X++DRO@mVUyS^ML&!I0O|O*_{kyR+19TD7s`1a zl6m=gX@PTV&QZ@8Wf@U+huKG7LD6hUM^!mMLR163hnx4~xQcGTx6{nb^22e-jn%#I z5ZaGDzLsU>m_YuuLB8$-vef*Ip-XVnW8F^d34il@s!+`;VF{EM`4RDR@Lt%%<-8NZ zqhYT@VgtC31;LOBQlyL{um~dSFxDIB3Bo$;roJ-2gmCK!u1Wb3_6ilPG^Wv`IiNIM zcrVT3#^6iHL$4l=pJdChuSt8DBgAeQjf`b+kmW*XC}@tWMZ(B8hCEG@+7`Sf8sW9X zBP_4zC0@-Q5(UnHETnQH?7*ld*KZ(t(i->=w7eCYIWpd$V;gf!?5SIW9&9N2TdU)AR!Bb6r(M0F_E5Qffm(?2- zs1OnEb-)7Auu9s30fkP5DSzw30+`z1R9xr@5YH79o;5OF(&prypbG`?NGqXX9$^tX zxO8&X4atwjpkD$*l;{V9NWNd%}rLhw9iRCa%zs# zK{oy{9_>l$FXlA(8(EQW;9RpE{(qnXIF;ZY9;$h{I+B>5a0o{sE(Du?d2V_i&pqO4 z*+z1)9I6@0UOP{_s|_u{pr8he5mFMk`Bu!TS$A)RaS+rVwy!kfh~}3c7!~*l43V(^ zME=H(lkKZe7=i5ZqhK0VoCyj4^YH_MRzegqJK!nZ${q*tD7Y5D_ zt}Z_?ATTBj!{I4_yIKz%7tJIQT8v=6Yd3~^&34CA%Xg5^yuOC>aeUIoLl`o7GX`{2 z>;+DYUu+;}E4$;YV!P`7n}!$SX)yEvM{x8z76%CwEI)9H7UuOr`GFg>@KX74h4L6c z5Y;!;htlLLH+5SB5&~2Yy`hGFT5divg^@ho7XfgX*tm7m5?ReF6}jPPzv^ zERgLGVyvVohK^u-+JI>34y)rkpL-Ss7@uHTJjAVQ+9Hv$SNETe4QJl<+ebw11$# zUH3%>EU#R(xpg3`H5F_@{|cqvwHSn#XtXN8)G^^g8)AMq)*WTRPcjZPj+GLV@B!dw z1IVesr@4vRBS2JIf8)6iE_bCZoQs>cXz{x* zF=h^w#o#Yk;pex1Qui6gj<7kKF??v`MQzUsR6*6Y8RYF>9ef)%mcFfeY zPVqI>r;T0MQSX2EoO=Iz{VhC2&l#NDWd=8MSTFXtA3-m}xe9p?3FG(~RoXexmQ&34 znDvwiU=3l%dy(=ad83!|zw4nq1_1tm#(B??DUD}Jer{ShyOJF^}|%sTiUBkwfUd9Hh%bC52J zruGpJ>-7UdV_6$eH1#Zj}}NYxdl!~RAfn6^r3(g%fX3Ymdn?) zpql5Q;9Z^NCGZDr`6Bcoho!G9dh{fKcJ@%VO@DAYWAg_IKsT7 zmQ3gy!GGj^KM$mAlNr&$5s9AYRrpN3DAV|>o45I{N*PEayPUx5U-k_8>shR{U^taa3 zvKHUi0H1(n?i(C}p|ONo5md?7Pde#`!Q@J7yr*V*tOrrUX*enWQ{CZJcJ28?8AL2_ zLb5)@Mcx3aHZ&{Fq_XLA3y3(NPP7Q~&4-N}L{LhQ-bb^!Hw5Y>D{8(9jT zzdi~E2ddz_j}fJ!^MCElGtJTF!T9C6N4)ToFL|nRsy|pZGPEG>XUcecKwG~C#>c>d zT8J1tc)in!FRg!VEP!JTX>A6l-iim3L9n|8;>uo*o%9S zrTt-%-oCScQ(ecf_NKc1CyZ!RGh=y%5Y9w-yM#Hc0{&3(rTyz_STnb=&K#Bi1Im+s z6w6&7U<&tv4aIF#E0USC>RT#4NGr#B5H<7Jm|%IuUTU2CXWdk`;bQ!Y+h4UEp{u9pUnYymwRkF*40t z@DEBuJ=6y2jMO0FK!5y5U=%&O4Xh-185ozR8NYQKi#^75nZ_@?ap7?2KQVSzlJR)V zYi_~rnv^guc^O|$N=j! zR`G{k-NJHDZ9?GdYLf|(-S{^eX~Vz1jd&uS*~?Co*1bklDuYJ{J=#-s3?{^G^vG+h zlXHe|(}Hw98RdYh!ISO}nZcZOW-=U=yR5=_Z<%0jLEtVAV_(2EhlxMHo$W5*dOwa9Lx$6f5<*_UhH!Q8xE| z#i$*9iX?V#W-$X>xFW0(lP9s@|EYSCF)Z$f#h?2|>|@`07STd9Qbc$9eq$9A68tq4 zmotD%Y0)R6MyN?t@~vmPTKL~UX`;*A2D{zma{wPX z8w!yx@Ee2y0E3_v8rx;lCx%xMfJT&f?h0*b)Fz))8qs%i&dIu*PM5mY3*TL25sFMv z9K-q##4p9)E&&>6(cq^vbIPKeF7ly~x!lMK`L+i8Pg(pmA96$rFsRFTwxB zpKiMCmtX|5LS;F)@%{!hFTYl7Rli`R%pD51!(e-*ST{MjjnN$3NoRw@=^*$f*G}G} zE;?jfamXm>@EydZ(7dS&Pr&ft87bVC`g`{jtR0V82vmT3oWiZM6ash-mFTo~;tm6a zf%)g7&{@fDj0?S{u}!km92ahmra(KcRq>*9CD^vFc#n4Hr}zln=tTfXT(EkLv!2F< zx$odh=p-JPExwuFXy!6~r!nDBd%IrJ$Tv7z+CPC(j|{TWw^3VO&GuYnjWc{@jq^^< zFKwBh5r@K!F%lbdH~C6lTlgXV2@cA?yklXrBX5G~WOkW?ItxC6;rsl}f#W$T(9#S7 z4m82t6lREKVJU)x!nF;;MzI%OlKNTqV|K=zyANsl$s|+yOH3C==i3;(ADwEIS0X(Y zC1K(kdD5~&r~r?vsf&(Do+mgK_ucxWyWLY4;}Ha}vgWqFBFW-;9zL3x9d$n+o!LQ) zKD2Z^O%ZVy3WROZOE=FuYN6ZLJa5pc{>{gim=5q^!yGu`KiHUqt&6S$o%u>zwL5t; zPmt%An1Nrk(4{Yu|LFV}tQWy0?y*YCMt(NR#3Y|-10y_LJYd+h~_zoZU+pajZUx$@FWN@hWb$bzaQ z+n57er0FAv!!Iy+bQCdkI<(LO0CLp*06lT;;m;)x9*PDYIAk0h`of)8t3B2C7-lM;TkaCwC(jI#mS^DYv{TeIt4eed(SS{Nl|I z^7Z)^~wXq&YZcRvQy-j55n)A|k7|M3=U7gD3I;&qL9rhZ69_87D1mYXYRs{s4O+ z;T~lSpjehYEIO(35RQD>U55$9l57cp@>{V#fm^s%$;+mx%2kan>fSQv{@Gp04&bNm zU4s1xD@dGOboYILM}BE`*^fJ)_hAKYWc?mZE*zIvI~6i(i5Ev|yYP|^oU0rWODSzr zeCPfGKAUO2e<|+y|HVAGp@`QIt!Nd04qb-4XY#h?mw2@(&fm`cFiL`d;<*-NEcwd1 z965I5;6`Yaf*%_3m?vUew8oQ_fT`jgm7IU!2!-u2EA&xY?r!pV+^@lr*5pBtQgb$| zAzk$0W(F?OSks@KTkHk5a5cF6*vh=xoiCsniJOPYaGuW|rrm|R1OR!DOd|`s2mtO( zL?-$+^4SNAWD5vrjlyka_a)e7@b)q`7@o@!PcYq8XAF+!jN@6ljykOjod2OU3^7Uu zip~g(prG%_tAkN!qD~zFRDzK=QD#OE=H=25hs+81y*@vnIFrC&cs5!*wZT^R=iGI0 zY6lK6dk$ORPrh!W2xIh`_fzsI0VoJ3fw7H5^8^_^2oIafw~l`PaWevjkvi6(XGZV~ zi!t(iuWDgNgUj1C!?#z9{(_?l7)`fNvUSHcqhT^A9hN&faJI#;|ltZ#0 zt`OFs29DbXH$J^4{VT1TFm-aIPW&CWZs765qvP+2p0%QVN8ac9zR+7~hI>W(N3NIk zF?UVS-wJ+#6x~d4G-v&ZR0G{=84SE+f&B%Tzyjk<6$HADvLn?oqrn|QN?L)hW63wT z3UIFzjXW`YxQuE?$_H7`a6O&P^>h?Uz=IXJOFp(WE1Pfx#}s)34oV`u<9-^3O0n&7 z{zIljca;@JA~bz?%p>-E-ZtNXvMA*-2V@79?6npHWOjSFtrpw@V&*4+j&&vS684BW z?fxvvdCIg1Qs|7(;bBXchL?G3A^K1r`;QkWO-*<=W@k#`xUNV$hl9 z!x%J9x4Qc)SvOv00dHKtK$qcuMoQf#rLy#mQFk6oXH_KNK+!IxZ_=>cJ!*Q*2^hac zRXawz^)QC3-4({7mLbD>6Rs>}2%CR5i5_+`?&pXX7tfh&gisw0D4;avzzJY@%rQ^- zhJvwHbtKyY64(&Xfx%EhX1yxtDQt0Fs-v46X&sBt#TS!M%58r5wQA=H@F`Jha4KH5 zgE!&?k{M#2>AK^}e9E{KMKL|$}2{)b^f z?j{-8wPbG`8JL9PDds8tUT2fS#>+w$C0~`OW3S&TYo;y0hc52acK(Q&~cf;@#Bj1Vgv=Nfw1IA>>vY^1cIR%V7>b?Viyyp1;xOIoH_THg> z^-MzDf+MPG62!-CQkGxT{j_TDZbi{va~}?~)+!7agbHd$*%U%0kE!kho_Yxj$Y}}% z)QV7aU@kzmEvQ=-hLHuHP%TVb{& zd@x)y_X6U4ZakUHC1|C<%vak%WU@VYzc{UI7r22beKp5c@H( z!lY=<)52n(2LvD}Y{5dEP!Rdlt zo1Z?fSP~f*x&b1EE3s|2d67M^XwLKO8DlfQ{6ew38(EF46Kssz4q6BIFlU0J3O}LK z0xNL&38xdr>q3KIanMKzivv?7i|eXS>CW$P8YjU*3xXA1(&qZgZc`ZF9SFQcVl!tI z15`2%eG|IFLGqatrpFS`U{p54n@fgA2-)C)GtjR<{0d;H?2=1VvCFTa7X_QUK*wDQ zywPz1nz_>9bl`I?a=Q1)>Gn?5abQ2$k#Bn9cjmY8yR7fY@3L%ucLfGUewPmLf=a%H z-?eax-T57TdCBkU@Ym*dYw)u>zk7%U6a3Ene*BJ5CKqlKI)t@fqfqvCGPyfRVx$ua zu(Erq07WE3lK)dI#NjDiF+d1IrCBSO#CqMymOC7)23Yl6lyf$g}=jiqiTRIF^6|yO_D+>6#cxCU{qT*WHEyob?7(-Dxf^ZMFi=%I^Sp|>V z*sBVDZ1YIAiRD6CG7la==KuE8j9o&8{~Y*btg5SgYwItJQ_t78z?v)cVLx0;-*6G- zM(6}^pr>-eeXMC*?>_c3<9d%ixkbNp4*~%jmt-PvMDE6FX9j}HH)b@OhZ%g zjnWe^HHWFg^~oLjrH803m_7Xb;2*AlE^H#S14duz1P5hm3ue2g;>dy|ya{4Eo<#eTeQ1`FE@g?A4v+xoou90|3*ooj*c(oUQvUCksbP#Q#%UmD< zy|4aW#V1e(Bi@U?(2e-?SfbeIN8?TuQnudhjGR}0;5eu!b|$Kc(QzOgk5|JwRttaxz1 z?I#%hqSOAZT!G(gp~j=cTk|>ajN<9BO5>Op1|}MfP8)&()B(kIRKT{!n}*h{tH8aJ z`zL%}s7lMBmql?4cXR}YTI2r!I^tTqp17uoOTmjP$QE^H34qjMl(2wn1}ys03vz}# zI1H!BQvr~ZlfYPg62{@2Pa&)eL_|@_^UcrQ6^PjxUy6wAKX(Vt($;U%8$#{F5Wntd zegB&uDah<6Wc2<0H=zt>adb^Oy2gC*Dp7ju?-HHkIBk80Ql6qVYPR5+`kD_nfb8FC zD55n+Ii}R$Vny+4w+X*dWq%VdZp_br#4P;~7_# zvDBh*Ym=mY2G<$4rz!(hqELhLg;{CfN;ZHv3IL62pdT8@NH)OQE@(db*KPeQc#^&) zWj(P5a#7*44Aoz-)jEws3%)iD33*i+xSv8-2;B4ie3@3S0yn=<|`_^y-01il$X;J?E6v4ZbV%&jnvXNf_cMEIu);bW2rKTi2O7OzI> zqK1sI=-Ng)RNReNmXCf9q+3e>%FcL~=stqBpLWALx)RJA?n*slsp%k|$)C9mcZHr2 zjK!*mjy($Vmv+OvGy&Y(O>{zi4(oyahj?)r1RrL{v_-`g`i>Kva|G+4#3W3h5FC zkl*4s0S2*t95Qry`Aa>aP=0GsT5uF@4lbGln;1NSIR^z)kok#XjT7)V3Q+KxeQEB% zk>+ii58^3#+Du4^*n4!{**z6*C0D%zBAhu~mj{Q8f{}WJ3OksMDl670Bjj83zmV6& zY!!wJ;yI#4ZbJ(YKegx69e+nHdayoTPHB13|^osP)%?r%2ps#b*kEx5ux2F4z_}ilUmQhth6NriSJUj`MFfww#&INlyIi7_LZ$VP%4L5y1+aYc!8Z5#Z z8CiaCwsMs&Klm#MIOwbVV4+gumLI%HdGebdZ#Wn?Y-v`n&O&zyvYU#QQilE|eKt(cSj0Kd@+n$4ajvCc8;VaAI&7mCKE(?wJBCoS6k&rTdX)|MD*t_W+*Za2@}pTS z3d4emBf&8=bIOROXo4o49ts>>de0`cjjfmd&FjVysfZ9cfE=M zN#%5_mOm8}^Lue0^$3U@FL+K39lp!=b^guUoW)ULT2xX~lykZ)f6SFDpLi$b;4g_P@2B|*x^vx}tEupgAIt?4W^ zI0))_%l!if;IBg0py)gKrx)G>AqkCFl~hJcE2&Pwo+C8vP`Cm`w73dXPt^_R9E;za z`fnld;H8Nn3#6UZH~;irxQrIY$^b)=1*vGgs<<*1Ap$=MI|z(zouWl(w`G&1-Z6^H z?OcV84=|eqp?Vel8mokGcileTfyjM_<;OCnZpb@v$y23SH;_2MY=J(bsweUu|J=Xj24Mwr~sHDJ=V3Tmnj;b zVtrwZh&r_OF{vnw(jLkH*A892tqoihGCsuDz>S&x7RK^UEySgD!=_T1j0<;g@@&TSxPq%{|Rd)wqZcl;-AUC)_< ziM)ziK zrCRuWG+2J{$6B}mKZ?S;s&;DOCa~4=gQbC7$=KuOy4k3jfoF#cRqcX*RRx3ZP`LU7 zJ`%v=E-caxywHF?N^LwP0DodY8Xk_qDckf7!rOY>reV(sHm24m9}(17En+*-CHNQH z@>TewhGqym8Rs61r*;4idl`qRoUqx0v#X5~SeU#p4M;h7t zH^nvn%A5@AA^frVMa|O0lHiHBs`zomvFN!3eiM~v8L`M5MvlZx^Bx3ypj;vkRJ9b)Mx~?87$8)wPTAgIVwd)7b)!v?m+Y{AH*)$(E)Iap_*y z7G3hJ6IejjgOOpVO!vq_Y3rNElWhf8+we(qDf*};ocLa_EpQIOaC^i4I&7};&N{Jw z`x0m5Rz3&<>j`X)Y>bB+Fh4Eq!4HJ6Iy|mft2_jDRkcwIxshh9qRtE)@$klacQ(1< zxSjJpvn+D7sLI+@RPXjM$9fW}H7LGR7v^IX;9D_uSCKV}RT5i}7JiY^pOQeIA$zfR zn(lj7BXbiKQ}-?&pR@=YQe=ggjLr@?yUPx!w0LT<^BA=g{0n;(CDpYet_p0;$Nq%9 zO35+l3Z?2{bTYw<(U0|K)Jqx6U+j(afyD3!cj-7*lTy6RE^b2J#_GV}I}((Mb6{e# zHIUh*oi3vZUw}Jwdl744n5v`-&!^+_hY8Q6(%RL)9acdLMrI2GIJGI}B7|F&K2dEh zijEzLR9mrkZ^~dk#z#VupFgUXfoS3ND5p3`?Dv#-b}V*?G`6er9;U6hJGF!G_v~O! zvV*7C!J?E7Y+WX2Y#ZVtzLSh;dk!77F0A7q*o+per4z{L5%OVK3^a_8_<9XE`6IYT?uH;ou-JNR=6@ zg@49qP%oy!3Qr}>B%ZL~L(BS{N0e1#zjYn}x5Ax-sVtl}09Q`p`rwyJyd%iPvhFq-6Y3hcHS;`a6i2;OOd z{6tejyEe6oP0iJ_treY{*kd=b8(TSs6Feu;L{8Txs@a6@wWfD&;bXgnca8NqC;&Zl z=gx6EcnC~MY(U*WA)Omh0fkqe;b1Wzu9$w4UxoV+h3kac^BK1*B7BCtqLA$ZMq7aO-dEPqC34}IXEO+TIGuUbM7v#7Y9nEIs->5rEKLIh&iEN7^ZkMh^p>XIBhzK^yvZ51) zw||6xyY*{isdnKnJ02|h4%m#0|FIUyMut^}4?`{>xH50Xm*O^~wa71-mDGMs03r9X zZ+7N3A+!8kMzPW=Gf?GWlUIjwGBn&(p^lrhNdH7H;D&E;+R5GMpRCxk3O~_{u+HeR za)83_XaQ=ej@&Cl9oK1*zkKXW1YOMN+{KvxQx{*Nw`3PLXpzFyE=ur`GK;5B_&a8y zz?`c^4(>tK6iy0j6SZlG)&Tr->;zpBtUDpC_Dm`3gi@rDZ>N;NBi*Sa*bLpRfpjx= zizDq=Gsu;itRwjcIa`xC3~jO&$#K!up^U-Z4rTt4EW?XERfe$<)jgz@#&Qs8BFS)Y;78hK@nei zO~y1SzaHi7;_t`azuMY`yXn#qn29%}kvoxT zap*XLQFj)8gochoE(Ry!g&k#Us)!TqW|J@!*?AL<-G`JTAW83E6WRWIm!jhoWG$(-49+OmK+cq~1ifbK!@PtTGL1 zjmjsXa!gPzM&k_8{9t=xR2q2^^^H`UEvUr>J>TkJS7F_T*zZlWu!*zc)e6htaEZ8d<>8d*U?mYdOmk2=-}Y zn_k_4(%Op82-Y4>BYy@oQ%)nn5s6C}oJ3eP^(12PsOWILL-6wX!CbHXw+XJNE$>S` zp}HRL%)ygx;!%NXlGoKD(U8Oh zulom*$m_OUm!OXSe|eqN)S1_Dh*ns29hm4F|X`Jd!(#|ox1mJTDT)t?yPK_y`!AFGFVaEssGYyL%LFY zUl#~k7od}ssxXkff*}qV(-Q$_|LFiAkYqNO_i~NuFq`h1=|gBRE@MgR4I&hjj0gu9 zsa?8Kp35fd30|8ZuD>eQQ?m}Wm+Grn)bin)m|KHQbhKiEtWH)1#$4j)lLcPAV^M$?|qTk<39KU zWbmueTK9c0{wg@#`N%S=dBF9FOdD8aOS##$E_5Wax&osRz(~je0LHf?FGUWpeic(w z%}7i&1sG${74C5%FRqo-lne_o%ixo)t6PQel_-MkfYQ9&<^p-`TleCZO|)A0SUiN7km})QpG`Xd zAtortpi|WvRJFf3ntHxpqZjA_O7T%2p_K#V2c|_=mSaX02h`#HOk^vf7QL_GzMqHp z@R3mPIzX&Ncr^Ak2=G-%X)KzrYvj6W1ocFpA6brAe$GNB#EWPU^_= z^@agBLyC!F5 zkEuiUx36(7OpfQggnD#D@8j8#ZCy{uCdbfXkD(c3cmQL>pW9FZE3`}Jgv9%;J@@Qf z&V5VIyT+*)NImyFQuXXY&#qT0_NS=v1f!kR6{4bvx>u#D@lp=J{mCl39e}sjgT;Jd zt4bzcnP3^XO$9i}w`>-zbRwO5{0;?43k1mS%=wq>@nvM$BpMisL*-QheRii5RO2QM z#;JK57^Xw*fphHgyf-9ANp6u3p_pP7N)?z@rM)CAB6?QXlx55P-B@)o{^b=+D?p*G zcnLY>>xDaLkw@^MPQz7Oox7M0EXeRHTBMc>K`JAHYxosdd8TeF1xllUO92i)RfmGI z!R^kPr?A9+m9V6^lFQl&S#j%QWGV{S%KC?R?hI+s-$dP!UU0h@4RptC$7Wgr4YX#n zdHWQoM|il_!hO(%J^A3tzUKMI7~*~bHLg0fjRPN9gEGEVs(c218&a3LT)O`ml2e`! zu0h`nb)-}PIxSUz3Y^y~fbc3?Rb6nK10rq3E{csQV6xUC;X7r7Zv{@EqB~&T-=qLT z+$VO!$Iz9d-Y9j{akgp6&#{+UVJT2&Q-_MhC!|CXm*y&QfsY~aG4ZmY7Je0piS}Q@ zM`E{~U);-1tA&3jE!{wzMG5=3zypCWosNTS$nJQlVa>%S&yjb`b{22!pGJhFu17XY zThfaBwy_yHiA_{9SK(A!bL2R0;4OJwQJn`A7I=PH2IN9`2QlM56U#O+p%3tp6qaW6 zc8>Y6y7P9Xf#}8w5I)Ejs<|p6+m`;(T8U56EABc(YOJ{;F{5XzwpzbsX_*+hvVMgx zTaIS~))~xARdqvps}{B*+gtJ;RBg@n0#rhR%en}sfS%Rg-&+_B+|`}eEr4&h#QIsO zykV?gVZAE={@4*$PQ`5rQd7sSMIM8K(PqE)^^Ll?v2NoENnt-)nAh;y8YM?57Ni7R z8&A3|tlYr`oD?>GEwy?UHXM6nX;W#Qoh9mM%^a;I!i7+?*@YpKe|44J7HN^+uxKYy zq?(LF>aErBbOXO&4m;H=_|Ut96Ntt=(WeGZakt7w5ZBfizgmopi>q=xtlq_r@vM*|3`uX5=yR9`c#>B=eaGP-2L-zx4vu3_8El~wNXsDlI&4hFS(Ps5 z9i_Qb)Ic4y?^TBqu^WuRM}jwLp_8PI9!~;VI1OcOL%1$z-viZk>OO2=$-S|KW0Vv2 zD@pnhLE4r`uM?{UX^mmb?W@YFi7r*mR@s98gwa%j|14y?$vecs(nv3Zo`$kL5Bd!# zySom0t`<3tjoV7Eo(KICRJ5(!m5icH$Eq^33lx+SG#6e1#cN{NH&Y9nT!_0xP2S@| z)WU@*+w-vhfNEWr*mZ%^620_i&0d^~Rw7c5Wl}-gMHSGQlHTF1~22+w%nT6GC0`{D=G9BiVS@VRyRSQj%Pcr?Rf(^zy$_hFyb?lwqGQ0%0%1 zz7u789(F%e>xLICGMkNeC8tiqz8Qqyine zJ#g%R7I_pEz8%{6vXAr(Z9!9V*q5TQ1bt3r%gI&n0akr4+7>N*La)Q7?^{=BFV-TT zlbxkNH*jJvkX3`TcDH>GDf3$oJAntfZ~lv4fGTFWy$_K6Q4_WY-gfprkb|V}lx@GY zS1rt!q`LPUufYPRTxfSbmZi}7waLy+obUeI&Wm|$>~;yYjGe<}%jx_O1T%L2+04Yu zS0ky%&cC;<0hCv4wp-_Y*!f|KQ?i>_jrhsVZ%lSxj->zkFuez2Sod>9<@4zM6-sET z_v=^+y?=Q@V(KH1^k4TL+22#|{n`6(kY)FN7E9q^@Y`hXtq^m26F6Y;1JTB|`MC)N z0o_ib%@c@jWR*w@JbzomSO)D6PPYFJ#a0nQy9+d(+P?w?Q`*NePBYJE`xB5seUCi{ zPb(V~S;3ZLc__7aC0ni;gTYh{>?+?_4Jq7aH$GCh&6UzdFHG`jlJZW*Y3xZT&0SLfi?XdwU z4_W0E`>D6qw`i4*BxoS1xxP*dmt&x+kX9K?6j2x#F*xuGHPoaMstps;R*XYoBYH6> z(a8>2l&?;Dcw2CK;^sZiHh7b#H==j5XXSq2(4e-W+C+QmjB^KmstT&8wHOKZE=;G} zu72Kui+H&%;|c#|GakHvQcPV>LHR0OQrmGo+E*8PaOlVH#J0}c*Th1b|J^zP0Ro@- zaEwrt0gc@ebOKa;VhGlLMHl9yaDlf8_SqWbE!Cy0Rg)x-70I7h@uwgvdJh3*HQUXU zCYrH6=Pm{|HZ_^l_fyACY%qR_eJU@)=b%5U@7AP+@FzIN*@mza5{9rp>ZE3d;i`D% zG!#Id1*WpCXHY3c(fppRXny;EW+16KUJB>fldCE;Bfpr;ywdV~0lJ}6}D-im0zXGC@udf=*QZJ#D;~#SMMhCpX zq+=!)v7^O@W2SrsP_e6Tws;uvY+L$b!rA48s|j^+PBLp?X_0iyoP-t<&Mx6oP$fad z+qo!{Cre(K^rEhFZs`^0Q-oQdg%1O~-7Lt4+dO3=nzQdI&Qgw9c;CkM6B9qJz);&+ zO8HbBML#idx6;B4Hjog=9mim?C3U9?RknZG8buVGB2poSayZk8o#jyu^;1&vZ8SJ-x(65+OZ%iM@_TPMx-b ztF5TQ;!FCr+%mg_?AC0z^9O2RPhFF z`srJC3O@0-{NVh+pz?#W0v^OO31rNOeS{()AZq~fLvN(V-jqT&1U#WPT-Lv_Hj6@U zWW?4NN%ARy>&p+G9GF{v@O*4Q)!CRnB7JlUyEgdQzz4a_Fs*`I4`29^4QO# zh^0+;13oGy#mx!R!2z9ePJ}oqzX;=jz5?$p;mylKxJgIrQUCqY`YspxZ3fK_XfJ%IW_)sOSZLMvewrXJ)&=Lsj zE@XPSNR_m-H7!)zhxV~g-wXyckOZ1UMGY_2AU+xu>%DGjBUln%>i)jpGj}(eh_=7a z|MUNSKCroWX6~6YXU?2C=giC*PY)wAl;FI|u=F*XBGD5)sW73b_=1Iq4uklh2#|#s z<@pTa6)y(6wuSP0e~N~OdN>Ut03)=Bcqbllb>w17k+nbPv1PAnSv<6QSwZrOFqUw8 zteI!{gQ46-1@^S1DLP7Tq(Y&SkdK)-1AvHzxZy9=?tGk^$VKc^(kb6wiSdWaF`v60 z(d8Hax_a5!fPOTw32lG+&`AGN{IUNAnLw1j3;sT}gGmi=uz9xeC)$^zdXN4pl4n1; zgjisJdW4GyK=^ML!WDp!_zUktf0Td^#CWBoYGp+G@%Cydgd4(-I&=*49#b~{UiVvG z^;CRFw!dPB2L)K>v<gKodeq8)g@h*FX7q{HW5X6O8RN1gt;Ob9vAs$87ry96U!p4Q=(TRvgY_!<3_ z4QD-`8j9BP{#GM?7K)nIQZoHB_gC2833E0)!-#_ulcw~wDZRXM*O$)&sF!f$Te@De zfzE1VO7AbSJ;u6!;xl^!fV6qeKs63lSCV!C-pyKShAPh#_IDu0DRf21@y_oW$~N}7 z{XJ|%i#OdLwclJsRI-ftXrkhNUfkpig#4=BSaS7oXK)~PJ4!qktN4vME zrlhxfe~qOg2TUvuKO z$2|(E(;Rht!Q%@rch`f@uu40keZ!Vauu8M_HhnpkfQy&pWWo?By{Hp$A61XY3FN4; zxB)vopRMd}M_=L2YXpwP-Pe&m)i@im3G6Q~Op7Q|01eloY9ccyp;w?<;C{KVVdAR} zGty^P$wkl9(7nIL^yvwo(U#4~l2S0yDgL7f?i)5@zxynRQ|Yh%UYW;;EyGK+3qAcs zJg};Zgmig4iZKb5At){s0dP;uc7@L`kX7Tu0`VE@ax_^bMOonn0+zQeT?JA`QQSCJ95 zi?9P&07v_7S5_C&EZmY)>qDBVxA|?|Vg6cEnIdQzYp%oqHR}ifcg#~{n7(+?I>T?( z5$$9b=l7fEo27YEO7o5A?=b_ndDwYS+f`{^Qa2F?{pzI~t#$x1cFZUCVPmg-@tm2kLc4#GPc5au0axucdGieeC+bN=*44rt?BFlMc4DQ76Dp~M`P=} zQ6oFvRUJ+5W}&=_FO!l@b{4J_jI(V3uIiWKLBld<`hDi4QBVnxgmG0XW$Q}6d)#bXhwK{xjf7tcqGm0hgB?{_ z*d*OlG>zN4mQQ!{EXiv}UOVIv*N>zHoM!)xU8+bUKd=jAZ3~6V_p{PXRM-yaT0*k4Z1PG{$|* zNRz7=L_pvoX1uHVJ3$P0<76XeJ=4&ZN8U>*z0g<9wlwrjCSjjVFghTe9v}L!)h3I)@`A zoWtv#hicgbJmSO)!D8{19_GBWE>2=twIl}SXl;25E(no_DZ{><6T9H=m7+Kr(lEBR{gHbhnX0}DikGV9zw&aXFe>Bdb z6K8=K8nuP7V%(Tb%op8^OT`ICs>3y20tslfx2_y5D4G!Yi`sJ zV@oO3v8enqOl&qF=W2Khx0)6k9~3Rl`FbB328M!?Fhi81wi#3sapU3u7?2)_p}#HK zmmPi-SD%M}mZ+8U;NdayXGKZ0Z%7zncZVAB`yo&=zlAbF6y`!Qh$CP?-v4?Ip5uH8 zUrZBMUTj9{am8iRhJunt{L_QL<(jiyh&T8(&?5N;$QyCW#7y`;<4Moms8`ZQBmAXl zvD0u_mZPL7N1fTo-@P_358s0)=OWrRG9b-bH><w9-F`!;4LDJ;e;4^?+ZGT6Yo%n6=g5xB(M^ zDF6MF0RpJ7h_Yte{DKDcpW0I&59n#xk1GA*6Lp5j5KvqI^_xN( z92JVwsb8wM(8#+u6sB(qu0cR*KO)PeP@!+_(w27cz! z8Hwxy?O{3()~G&l$P&Rix5@g`OafYhgYc}eW(S}R;4v*yU`5SMTUu*T`A01+Z4H?+ zqEdP-;^*oi2H%qXc|>WX&D$1uUE9me$t>EwDYJJO%wD+m5laweuRDfXR9~ReHMw?z z8C+uv7ON4z9zo-rZW_1}>{9G@`&}C8ER1`!V<9L%nuA>l z`%?3J<5c~4WQT|LcH-is-d9w-0I!I6k6%4(X}p|-pEW}zHXF(6yqfzOWMHuug-6u! zLnA%`DbYq&2`gbLlVV38yRl@+LIPc1TcK;hDjL!QeQc}@pcObDL`azOK82tT`U!{G z1fmh!NLZpI22M1e$O_aF6OAZMr1J$vzf!%BT8u~`#1jsP9{Ta%5%lZ|mXDJn@Gaz< z^*%lw!&SLr$HPU9L$Q=>(Z(+P7D0N7I-ox=Vz@d})=FkKf@Q%yu{ z5VwiLbQAN!i!{Dok)T?2#_8e<$y>Azz)Ct%uLt?<-@SP%>D5L*wh-0{$#;~N1x|`< zB`@t-fx~;5@K~@DyU&k+spKPP3TML%m%^f3+dQmj0KVdESX88?mGUERI(!XA>;k~i z9tS5xwxipDGQ@SN+PD4{e2L4LQpjtAZz@xb*xTS_`mJu6pV$Z=4GWP__PIK{m5jbe zrg7{dh@y)c#b5ki;YjEANfq^g)O4zJ6L{+3^6Ie!KnQ}^}Bn4-%q{t<@2ZP%w^ z2+dVklqBN&KFawZwHyzS?lr}ABpqz|&eL4YYhQ`Eum~KqVOjOEAHuP-G;`pRC`g@; z;fVQ=C~ip^CLrIsA}ASYa-gGxCDBuK)!A1Aw1XXn1DWRwAIm(Ke1v)4Mq@Seq-a=p zIvyHzm^{>xZk^_#zPE&jHUoysL%gVzg@lI=>g-j>c79AQs1qJK^f5eiANfJcQ$8(~qx z!GvG{X%3FSPPR{o-PX{R%clsWzt!8Hk_c&aa~1c!+mO z4N@(H-3G|Tsp zw<*vZ-)H(BRC`h84JXJlpgy!v={0VRmdn?aG*&By91&EZ$ms}|0nX4}iK+M8l%C)> z=fhb^3M-lBop-2k#;Izd7#76E+XM5_Nm3MuI))w|m1Ys=9T5?_*pt|FhS#TPf~qNs_Bg^6li)gTk=T<$)`(h{EL$Bll&aX=YW%Z z5^ntGo8A~InM3>?{&rKm`!z16LQAUv^@B)X(6s0`Y^{mNz_}^@#wXy{l3z@2SWg3J zD^ASmM?h3_ymK<8lO+dEN@`=jeHV1QmR6Ba-&N)^;3fVC5znND5Fi<(c4wDHwhI9+ zFEi_9vOAVB*+F&Cb|};Y?%MdmE9r?N-Njr{d1F0S!v@U5H)iMqX96{M1)^4JgQ#J& z<&+*{tlNXP^qewCmvI;yg{|c%`P6F^`;WW0*9o*k!2P2W^8BQVv})lp7l%*fh2sZnGk1Y@h^WfaqED_onH&d~#F`*dGS8XA zBgY*V694QQ_=W@Q+xTn62|;%%)49CJ_^bDA;P!*0LhSRuKoITuKF5<;pI0k;8XZ zvUnL-Z@AcH+ELTmfnvdy{qbh7vZGLEo|$l@Npy0W+%&;7EP1*@%p;1{yk=F9(MS*$VDvbYcR zqfc+q9sKXf;xN9k;>27JTfV1c@pXTM_Iqzy7dnj$-fA|+(7Enj$Vb@E5FpjG!Sw>W zIFYUV>zd#6c{?zg;vcck_clA@l(hw)X4vPO@TzJb!!IsABvjWvZ;f+3vIyIdmO+UDYjkg8~#vk(Xg+kB&bf^9Ak zafw{oHa8pBBQx-dSm?w@qpB_R6n=|oYs>no^nTQ}d}@Mi9^W8Ow9UhKs%{iQ7_oK=>$rRXSv8k$Vr{0D&M!jBX>4=B1UQUN z;{eeiID{h_UvE9@!S)!|yD!5whZ!x27)?~x?a}+wLK!uSdQI^dc&%SR`qWVv>aI}* z4-l>MJ=hSdWx4a&E>x6(^(R~BcLAY{**FF`u60hwJFWa$DI)0k>?dC06#KGkH_Unw3q#>y zk+&E*gN^fhkdamq#M(>L=*8-kBcuqZN^95yDPsR>Z;IHIvCMHkgp7^;mXBFZGoDlS zd73F|$P^jQ4My%7$+Vl!S71Lh9 zU?0MYfDuymIdQ?TUpMx(3_@|fW`@g3_KyJC!6`E7_B4$@(LNu`*_vgKhH<%#SW9<1l z3IN>4iQoCANJ=WH+Nh&x&ONr!o7rcy(7zwYH&zY1V;AglET7Ee1hkJEJ7(ia+m5a& zISJ=&&eTTaF5Jn3kEmPSO=Lvwz=-?;UNH4&i7Zr~e2}o^mX-CcwnV||oPr9i7dZ76 z5_9J0@!W|rZ?zA*QJgK3J&7o|^UodW`RCU%7~JK1>|UjFSSBAa?J1u%XTzsa^Bt5; z%{a2t1X1dPnlO;gSGaS6Ylp}G$XeYA!-zeGw|+AJkZ+o>u07A1()K**4)?3LHDmkH zsVSPQ`9=B#_WVk`r0scr10zM>n~iJIWeX7|t}dX_H0R$9_=~L__yxJ{#8t8&la*hhxyEG~+)LnEpn2wOEHAO+l#CbWPFdS(ClJ{T zW$N0^!qsMYHC9KYXg!CPbCnd$WXY6D1V@qV+yVxfo`(BGbC%{0@HEU9VVCwaTqnhr z=@t~8W=!Db_|?vZEY&E$lAxvRVHiL#6ZHC!J9T7Bn>jOs%sBS3%7ObmxlJ4z7J0<^ z9ca6qHiN2__n%8=N%Lf{nB`lH?H^ujrkla>CW=+Dt4yfICpa=KiW#$>#7D=FKWiVy zFZ>(KR@U$3rItv;t-)gBPgPs>NTs_1YPsw1Sc)8RSWv*7#^J#}mV9b!r*U{ten~!= zVvxfleym2YL3A;yTPogw>QCeA=pe?md-xk~$1hw5mOS7*5MC|aKJjqC-N7V?H@|%r z0`5Wzs()Z1(0TeGz8m1{=!XYihwd2J=t0NC%pp|1xUU=RZs9(9ftvUQc0mJC0FC#c zceJi($|f~vk+6U{#SKs}=vQDOg*tsAp=Z*Qwcg}7NPefyI(Ltw5iYGEv*O*(k-2HC~zr1}!r; z>b%8)@iC^*h1B(3Q?{UZU;L1&y%Qu#xoc7#sogakEopZRCqeis=YWx1ca0cCE0dY- znnu{YdOIZU_Q;2SLfkbs;~R>;dH8F^34t@M6=%A(`*Q7O+{{UL4g3@rL+}S0Nk!LP z^GUn}+KSOY?F?%(_VLyvA+U~ zq~m_IzwVcY*o9ZaU2_mG=%i1qdq%o$=Qk*B`5Hkt;qCjc&?nFaxNBICcGqChp$)r3 z;X}~w8tE0dYpBBTSyY$`B_+gNgAdBSr`U4xrr$Ud@l zHZ{X&MW=AJgGjLupCWPxv+TVou5vv*n@~q_la|*q2fDfxB_?@+BCtXHJR;3;xEu-2 z2gj(h5I^5j13WxT;%8(~Id5?~-uFclt3C$3W!h}y_}kQ zBe$4wslO=l5~7o~eDE>J$c42pbZa1kg5uJ1||mna-p*Pf;fGa)o) zVRVD%Kh|?~3uAoma+ZGVq&ua5(mmuF>vbD2!1bhy4(DL6U5j5#x6Dx1dU+W<-PR)Y z|J`(}`=&-Pa;Mxljx%fo5~p!$eZr(WA75Cym8nUWNiyk%5rfcKjd?}$NXn^|G1+eY z*OM)wC4Qr~cyY3gAHYyJ!5$q`GA7T@#Yb^&Yss(ozWc6&N@UK=_M1~z(MwiC{>vP| z`ozM#f=ex5ZNKc6{hbRqA;Z}E>E~a@ZXC0JJ$(Nl6pqlQyvlAQ3?*ZQxCuJ)m{2`E5_7kAhM8un-1nzQA zy`~PHh`Mm>5*&Ozu?G#W!0}I46@S8>RGx$k$s$SQ^u{E?o^j z&;WmmS4tD@bm>_F7tVnvWR~yu02$FLr08uOgGv{{avlJ8l9cSlY{p3c;E%Q-tIdNGOfE9~2)i9&Uwp%?>{iV?xU7o)ZRgVJUk5I&T3lb2<<+h! zmEDW3=D{krKZw}~Z2HO|X#h24c;a3}m9y~K!UL>vbo2z6{QF4bm$T=a)AOcG$6212 zkkaP4+(mDC9&nna_6kzkapBhY@8EaEC*ilKGL2jKYQgJO;&p=`zKXLm>Aa|@zu>4@ z=GS_7q^(bmwC%)*NG7_e=RpOfL+5KP)IwiY$1>7xk+h#6h1|}jvE&q{h8xY%&RGDm zKBN5k%+ZcT{RumZpZMjI@9RkR8Jw`_%*-7pvt&z-TN@pk{Pg)|z%*hv5Z~9_JTeaR z{-E=&K%RpTWz>j>C_PNfen@BMF`H(m?uIRJ_wnq@07|8{7R~5~Qlz0I8eFIx1%x_P zL*=<;r<qfEL~>kg*7y!&3U=r{ z`?r5aMQsgW3MjRDb0rVo+=Vhm{D(-z!H{ZJB_u>xebL?>AJPkdQ>A4Uw?o-#`Ybf3yha zFW-QbYCrQ^oWGQC7Qe%jvn1I+ai)ulVbp|A91hjf&38EQ&kD>4Nbbjt)gvuTdM~PS z|M=cqU-jL&L)*&6wz9k;ly@0p`ZkC)U7ELbo5iUY1^M}VyO9GV@hM6Ddi5IAO@iXyBTN!VG z^q9B#tNg204dmK7!k&Zs}IGkEQ1w4A33-OH6V3kcs$r0+^$$cP?D!bJ2 zAzODu^^GI59L7cl$#c*Ja-0RvKIslzig`csQblvUiC58+ClW8E*0<1S4%x-I6~%a* z<9?qy4$fsjnp<7A`aIZbmT$m#xai*)58$V<4r1vi+h~z{^rWfB6$hdVd>VN+*SaCM zKj}R6+`kxSn%BldgS@dY{>)OoI*59nOrkH#b2;#CES#?Jblj0iwld!;O{%g&oYx&4 z>{x_Du!y&!iK4O++ucEvffbuPNCXtA=b*2A!uY}XPvP}x^NU6CLmIE&V7i4g65e)k zD{^srkh8UmThZxp+lsD)HIBt6kw18=c3#Gt+kbd3aoPsF7dcP%ze99Tw{ae064X#| zi{JSXURdpHpHO8yYXyBeIP?4((ONJW#^xd^(KSF?r{X`X?B=0acKD4gQ!egNpflMA!dNytvt?CK`= zJjCu+p>DYEujH;}Ob|F@!%Q%M|I1KhEBxNd9`Efw(CNRC3qw56k(xuW#8<@>v>|Xb z@G_WuL^9NEmKWdHv5h+V_~WPO3=ZEkC6o^> zp`z!e8eAG%(UW-4tSo?9b*8Zu0iZ%XHF+gyzViUtlkg37FCWoLG)7j8eYW2NpX_`j z^S;_hA@_~zJ&T2HoNSWLRQ$A+ERbhzyvXP>>96_Gs{AbK&Dcf?O*#UB2~9e0@dftC z%TMaTrh{)a5CnAQ{@@|TlcU>02S8R_5{cf)8Qv@df2QeAbF`Jb6DXTf#l8OpUfD3vPbQfGWZqzbxkR1egkVWogw!1$(nMrQSPsDE_ z9V$;*a^V&rwuLq!MPPZV<*%QFrZ{uc^Z3`k0%DCrieGe>ybk@|6_{=*DQ$mq2pKMy zrCb0k2EQEs`0aDP&K{J&FJuGh{|VCH@a%vCa%r$k#rXc}KZS1WKFI12#4`jmTK_H< zjAqlH3@s^x5#0GlM4#JVJV=60fxH}Udf*Q*{#p}%V2dX+YlG&O)AaXsfWiL$DhMR;f%@C?FB)USt4Q7SzzKiNoSQ#o zZo#7KjIAN4xIHyl$r<^XExHIT%I$BFIyR_1jib??*NODQn}GD*^#sID00-EfBhsed zG5aoN|3H5~h=1<~47TSkw>|8fJoF50vC9zo90ua9@YQaAW3r;=D`X?{PpxM-sO7`G zt$|&*J+Uh>3c;a{*?$g5)W&ePedy$X0U`WXpep;YU8XWmMNUEb7{7ZBpAI)GA&P`i zDM~5t7>iGM-u^Rg(1BMpxGOxQcwg@^)vVgld77X90~+6Lu=D0ZoCTi?UxfX5=bO`b z#XYY1Mzr1hHE?Q7$IYr+eQUyblUpLWH%8tr-gk%U;0$PNMMhJgCGmG*QWw9-Dc}cX zjVSre^RS>Y4snKY73uKm`@NpX97Gnr2wTIO!e_R6JSA!f!~3PM*V=BPTl6*D%cD9c z?+qU)Zo&;%FIY3Y#*Tex7?+*xn{L1|)^>ImS7y)nHEe{(`-e&jpSe(OG5KhIG`;58 z&|9M|^h6FryrcI5op)63Qk}h>iC#?)P8Q|4W)|h^`e1u_7z*<(aRkUf_vMZ9cmjL3tD2gc0k!5ZA3kb0*aP^< z3N$x7JpQ_e)CjY@8?Enk1V1Z(ICqV)U|HJXMe*&InmEHR;-ft30n&o!$2|sb&=iO`hTA&^P zc%Y?$72{-7U~li=6^^!!Q{{)@MB3YYd}#B@95krB3j+&hiHsWF4-CdX(R>_x1ueEI zz%x~r@23GktTrQufvb8}9S<}IUbzwbfd#gAZ|{2w@tOem)q#LJ0r;5eF=`kbS2g#8 zRY!>DPX#MY0YH79Pyy7t*9;vtD{Ql}Gtgn>H(B`|D65W|5fY3Yw#)lIJS5Et@K4|s zwco7h!l@&j7i?R4o*8--N#2gYt^igRDi5nlTkc$N`5U?A-N@~<=J=DUygTtK>WemI zIk$5@I)=XD3-6o9WAP!gA`j&+`!@R7?Ne2nudXkQ%u4raguVLVI8|{#g*u=ruycEW zk8T<}7xBE2Iyw_hkWBSLcNYQ4OKG= z9E7$_Cd<25Hggy){fF}qjKs85;QUDeJ*O@7DvBk|X_Lkx{v$lH6*kzU{BT>Slj)K9 z_Vn?L4|f=0tP0F3Xsh;Ib+Uox%JbECOr!a=co@#%4G|rWs%d`o+IPbSyt0C$EAQ=- z7b03?fsQV473)HGY=0waT-*C5p1AIjzb^o5`f&8!VZoNgS*istV>4pwNDa8W0ClU% z1L`1aw{-0)v|A&PZ$AR?y_LJoirsCY-9%jFMXPq}FbPIXbl>vbmhUWFtR0o5zA78l zdfQah9xY#=oB`pu2Mc!5u2U-1RTb)l)qWdv$tDb+A!ewv z`S`fI)opp!ZKGSu&V(-r-HBiIzhCbeX*5Cw`x z8KTgLa~r;p!tvo8`=Jp>dpR#u*!b(kJE``#99*Kacw0U2p|qQoI~#u8G=T4e#``4ScPv{Sb-YxQ=9W&#`Zy z8@|_fk{VH~5gSf<4Ap8FPBhU|x72HYh?w5iyrFr~H+upH*c5A~Z=^cP`5qRG{c~Ir zfT4=P4XM!Txq|cqjb9^%2>sx(Uzq#PWUz+^ydJ}=C|NBi8aP7!!LkO7)jJ5IRjk`w zYI*%l@UjVWa1a}Zcj4>shv!E7MuxA&&x%VC8pw#hiUL%Luml7C?V(Bi48JMBH*KDg zsL|Pm_h?^!xHH-pMgUIOJKAxTXmjNODbr&ggNQPRCVmci9O*8T6>rc70Mhz4ngmZL zn)DQYRGU?jrv<{y@grNo6W3Vb@wx6NY=w|#J&HQN0S0Ml=h`cbvTTm&JB*O1`<7qU zfB;h9U6CLz>)tjllXbPVqvsQQ3bR^Ff5Wr=Uy0z$5?%p6ycoUa;>fdVxX9;6Sxe%P zGzKYskn2s*F=04OyV-|%Ue=w$UzQi+K`4QqX@7@CVLbTXe02C|u|4so?M)cHPeIY~ zgr`c_z3^x3U!9j4|HVx%VN?3a02^zuXZy&<@b#w%=~%b`-jDp?(IscYV50&_J=wng zW-vr&qE)vSIaZlmu_N;9GGw;Syf*1cysGoJfBR*`ZU*Oi692?o678od zEodv9pg$_aVKy`j%t(z;ux77dVT}d zL&cxQBZ^B@5UX(ly{)2llCiPr47oVDi)z?<3Plfaq=7XRNbFZJFV8D70K z%VWfbA?uXR+j%93A_1{g>(O5su{QxTl@%PAMWK+&Dq$8E2=rIw>tj=S(_F|+J7^jW-;#;Zy=Z01!x?) z2}eu1acxD@025E-*KBsNcOE(s-@#6W(}!QT!~7QJdh&6^XKpo-kd5J_5F0Jjp`cfV z4yYNq@F;ZF%*eAs2cx^r;B*C8lG54`Yt_ z%#oK;n}qYRa6J5|_Yk-Du@9kP4xkNEO%|V(mhaJ%S~(1lUU@C}d`0-OmCLypwj!Jl zBOEh3-G$IvQvQlEW>U62aj~E&tD1@D(zS6_bWm;n0kwrkZYKmahUg}KYQKwHXQPuZ zjyQV0V5(Ds0=kKA`!HWs`nOX|stzoJ9fDkZ?$JHaX>et9q5WR~(dMCCk~t{lo})ZQ z!2O;yvG5@9;4guv4&D-wQ~PTD_E!Nzy`tWSK7o*Wp&lxA^nRz>vKX3PH}w0|G`Do* z0QVgf)M#*QBoRI7U;bD4_S6w3J=9G_(zat&C)TnWm^gC zBudE&Xwf!(+?Y)4CvX~2g}O9-D!MT5&n>3$XI6CqmSW=rITpJ@be!*aq8U z7hX-@5zdR?a2jA79}{rA!&_+wUJnl`BPP#PSH13uj4(r8DPxCb5=QD3!{Lvy=?(pJ z7Bw|nS6^;puhRiE-Rp3T$hOC(W^c()Y|H4dkL|BN;iJL*_0a!6{gv*mwon&pAa~G* zQ*N{OsxFmIOKqz$E4$ENpnP|#%XZ@nZ$%d1JMtOo<6AU z)2&%k7liX_ywednwG^87d)V_-xvTmC@5J!LsS6^1m8UcC^eUbfL|$t3E-0Oz*jN2P zR&m6sejxkG$Xl)6Vbh~6*@;FZPKrE>#EFsJt=^)9qsJ$et4kq$DI#dveD?dl2cE?u{MUh;Y+agxzXLKsZVx_GLU+`DbGvW9?z+ zGr%8FgMT*l!thVx`>c80fnRm zTkvrD7H}iyX<88ZRzL7uGcLfJB9jo`36WgS$Io&^$;n;0o4qW90S5PbNNLkrEn3}H zDvE7F?$Uu4EQ>!_Az^=i5gHKqA=fs3jG6Fp4x9=o6^Zr5&m>k(H+J~WLI;G80rl|v zvd+a4pG6606vk2UGfp3R&YeF87k>g-Ebe3R<#<8yZ{W}1a-Tt(w9z?^lDHNdo(EQ-lv<<7^ixfr6Zhg|-Uw{!4 z??Mr?q8-AmVz(X?MN=_uXedm@u$jfb3$QH2dh0S#iMmHu$MEYe6agC;qA*CgP#y456oD=OFY9jxLz|VmA-e5NR}CV-Od!w#p3d;6>Gn?s{VxRl z#}1%>I~dmaF5sn5c&ne{x(oL8C+IF{PK_Td)#job>_XrG{}P-b^K)4(@S-wrPYTKa zybvo==T|RHUK+mGoovLiyn7;Zv5c5KBk??>+U{KKqg#xV0Qp0)toFC+6Vt+W^@&R( z?TI%s)TvDWLM0e4!@qFlMXrA#+AZtZ*gS$Oyr90q;R!|g+=)=kp~6A)!Q$dKaAVOf z`-+j!!9rbT9yDEOd`GA=Hw#qPAr25-X`PuqacmyVT=T|$i}J2$@M0VkuQ)7mVyU|1 zsgbNq>pxIY%e#MEycZ7Q;*Md>;WO=RxdZi($FV&P-uQmVSZD06bS{AQzJC2&6MG!s zQpq);!!~famt?*c*0eI!t{FKR?b>%ysg2$+HSN3p>W zmLmHX`n_ru9SVv>H?7ac7!B3SxHsM-y9F@M;N=jN^)%a<2yS@dah z&@yj9!ELj;@A_B-&CGe0E`vIkx!FHb_Q~I`=VLScO0i1Xo|f zk4APN#oGZ7ad!00Q}pNWf3zJ*0W5E-+NCJ6*H-85K?-7a;O)^T@CfUf63KpeE$7^G zWwUBN-JIx&Vl}1-NlQj-r{7S&n3A;e#D~}$w%EX_LfnDFpGR2|Cnf>h z%9TaMor}Kq)q9n7A5w6X6*o#+Ggp_GwZyj>_EEeV4?FI;YY_}LeE}H%hBc-1rmE<*;5GWcB@)Cb5z~UEPqIeAIcsMJ}7?XZY5Udjrnmd+=(_EI2E*7*n9?))OAI#CieD zp?mNe_y|2$l;8vXAyfu?HxfA?4kOL9N_;?+-g@#D0W@8r$ zR=k2>HRc$yCSoE;CH##xO^!C_Kl*zld$C&9V9obMoBWS%VllWmUsC5jj>loGMtlt5 z=-b}RS{a5}2=u`&hh7`MPaXi7EJo+3@R+Oo(M>8mq^6^&u-0@GC3mUmC`y*$83?&! zR!zsPF2YdL3(UCi)bv7qMXH)!w3*nnm7Jp3KxJ%3J;wk$foa%+3HEvsw%DT$C`m)& zzd@qnXEyT{(PdCpjoi)Wkp#sHpuRyUGVr{wt7%U&&&w^R<{4_t7QDeAc*%%W1NvqP z0&|QIM+$l|nC`^q2#8=6#!&)t=q`eb^EaBvE#7C&Dez$rK|PC^wX93UC*gN9kx9&AlHs#-U*lR239Avt(6;ectlUgNia0#jPMjvhm@ zgB7FX%H!x6&*C$i|BlTU0MZLD7~1Gf{7>^6{zUfxowEb>QYP|NW&m6BC2?!r@ydL|E9sWO5`BZTI_68Kdgie=&JpnCOZSn|6-d zwEF(0xnzQLpySDYCLgWRQ2!HljJ?h0E*^+r17lcm#zGdnpZ_%4hL17`zZZZVE+B|L zajdie6X?!9fP~4i>->|rtA|v(9rwVPb)0Lhnb|np&S~`gzMy0-%Ee{Gcvq4-CC9r;FP0$~Bp1b^Q+fCWmx0`|$#vNp1UJ#jChB&H_z{_|L3UlnX7 zH&YI9ld(BchRzty)d6fQukfGM6e!OP9+#jqm+N0e^x-3>0jfcmHLWHa)yIFBSO@9Pq7@yMI* zSLfg>oY+y7J?1n@BU9v!ITY8F#U4h(Hgnn!LX++?qEWt6^2j2Y#sF%hh$M)i(BXmh z4$lKu*xYynV{uNFk0PQsav1)f<{qPYj0m03tEJgcM13z|DHrl)HssAqK0M#8@@MyW z&!z0^$0(dLrJ`%$7C17EC$rF|NJNB%fHS6yB&LgQ2k0DvF2U}!yHyrSnqx>R(9J4r z(UQ~hU3YgIFLD8LR&JgurUpMkg@NNS{Q7&MIjq^Mo|OuEytou&u_wB_!1cDVe!z2< zP;!iR{y>X5tuKl{7)x`0bNwk0EclYzzvyDKrVq~p>0|o-aICrz^v{cUXO#tCTlk(i zaw?Q%I)?rSrE!NPyyDob;q42yFZn|8vEWOKuFJ4zxc6-(!J37?H?~FzX-i(@g510s zdEP8v<5n4La`-_hHe9DsDhs}}umu`Hag%OOHV2;1fB{(qXfpVI->aQ|I$-){qJ`?b zZPqNm`2#lVQ6@U}5f*Kb7FGvFc^C%)A5lc}CvmY5Zos zuAzgCpf>YumQQXoUWRA;@-rcFQrWSuAPF+rh8U6Ki3mxkhXSKI{$A;wgztGRya9hJ=8c ze=MgDXq7}JG8>J?&i=|h$AmNU!e^L~J;~JtNKPrHB==L8XcBJ&iK1VjE2sO&iRMa{ zl6mSmg2za&JB)Q> zd`9d-6r57IC!Ay6k(Y2>RDe`JD%Wz@vi?Y_{=(mzQMRl^$shPJ7oXt{;_=y2vy9kg zG{;4%RW`DiW5#n7j+}sBa0CcoKY;ZjG})zaq>>M?_#(3#NDN?orP~Yo4u=$Ffd@Q& zB<`v-abL{EFaI z1xN&E=(}oXQ4Ot<=WBbqXRQ!xxluZp<%vz4NH*n9XfZ4BWRpkfZ-DrLz zD|&qB;uc$0np&i!~2eMk6x$FZzzp zh}UE2pk2R0YdCQ0^EKd(F3#m)~m0b1hsX@4?yzo{4I!q3t@wYLSb0kVZVBvSmy zeNCI*1};U1kMycY7mkW-D;YbI{A^WpSXflA>Qt4lwuKJkQ;+={^a{fWw%SF`hp8KK z!)YLg-a682R(3$ZV!`roN|KmWhp`vvQgaO{RJ;#9n6c*1g`W`1@Zr*AbpcuD*@Z_` zpN(zh7~&liK3_baC!G%93da&Jq?_d{2)2Yvtm#^Yfy>6;*a9gO3!CF@q%>Ev1%Ea)C}YEhM*xhhO(ZGK zelRoU(T73Sr)}8nUrJ628mB zausH%9nRBMSN^IU8vw(UX#}$#SIB+aW|@IaCO0}=jUzvU&gToIK^eo?`a`L?Lzre6C_4*R zE<#|oMqZ(MCcr8QOpXH9{jvY_uHLHbO2NzxO8I)k=bx?w_ ziz3{bK%|#$o;}Mpee3NLkaBpzhj56@@I~HA{P|c5uK3`odUN$u{BmMkP4&zC8D9gm zaDDcbCN!7Z^2iC7GOr9Pp}EPChiNt6mJ6Z{G0XDkB3dmf0s4SFZ?2wgO2>Sg$vpZqT_ z8#8}6A8*!bnax^I2gJpnk*+ziI`Ua_H4o0G>;E;sSsl(y`XQ323$DXoXCddw)szmb z;d@9*H1g@Y%v=g4i{kiFdAAHHoH#XC$B4`vI2I7M2QEG5GL+#5CHw%_*(X7(N2&12 zZf>pP-|XYcpy&GFr1g;#X8D0q417r0bD$rE4p_OhsNI7YRNhiG{S;251YcQr6e2Ns zz0a8sfRGnO|D|WC2lZb+jUCQXKf$|waV=Z|LKcn&Y=<{uSX_dn?a@j>2%FoVxR`Z7 zCI>^i!)Lf>cu+Lb?O>Y<+yOWNq_VIbdq^Of`U_Ut?R5 z*e@KK{YLyTrr;s=Q$C1hn-%$b>Wgp*@2wn*cvnzyR@V@<5+4p5;K9>uX9ah`0A7Ss zyx1<$W%^8SOLAWlN=fL|V5oBuK1>|eD2$*8DBSLRiPB&cV0hb;9f@~AvBy9e6P!k7 zz6g_IA_jXj)rO(K31u1vQFRe&cYdiUe&?XPdzmM9d*0&HrwK7MR>A23d5fXk0DAnx zS%`O6t{JhP!w={9S>k3N6p?+NzqgN9b>9YUxj8-$La&i8 zu=wj)(7d%s)rt=pDBW@vrsFYP0Mr&@e?sNL!V3M0ZJ~{Pzp*W}fq&frzmv~pSedSg zGbW3%qlwPbi#$ah5B(HxS2r^GG9&hfvmuV<D02wb(|y5AW0~miwm$jdfKRzlg9$=$B5!Quu0~m_cMaCYWP* z&`-5Rr>^=YmcnDysiIS#x+h!+*Aj?Ki=LZ8h97)y>ZQUkYFmVEE1E@9PU``E6%l3(bIcxcd=nJ?efux>j z#FiC6l15_gf<&a6{5TtFrm&o`b@Y0-X!!EtL);d0E;EG%vHO8)$zEIH5_L}&O%Xy=8qjelX_EZ(HQ9;L~f@fhhJ`pgZKo{j$*1c z)xhA9MfpIOthHoFQrcV^JhHG`*RL8m(;*nNaJgnsgyAG+O*qdM*+BaW%QMM?IqTVt zNytxF4EO3`W^5g9Y`xWMMq-mkg>#ajSmJ}&J~4_BN&qNgu?LVi22lhgGU6AeQ`u*q zMW4NG)>BsC+5&GVrm6n7Gw^A}#=wby&`4Em)ah>+NUz)ot?r;vL(SK$C8p3F>e-6G zk>Gv=2%Em5O?mO+Aj&1igS;#=dh$S3p8f5=oxs-F3&6zAr_m&{mH?ph ztm}o6XI93n&?6csx**rI*bJ+J0ZOc@cC(HMOOMr)AzMHh@kqpXJ%07lomneD zfSX>@t0lCk;E?BKNNCZpt@15Fi#bMYC~5~NqcL2&J9>=ATDx%aQN64}HPNQ(<$3nF z?ldt^!P1RZ)f5=(?gOpOikj%V1<*e`jG79(GoD%wPLX!ysQrOmM*MR~<1rIiZEP3^ zBB5{NGf``Jm^Bxxt9&FzF=UxD>)Tam6Zx7akf8O0Me~#TrB0HT)wP2;yUkFI71@M% z`}i6H4(l6um`yopR&6k6*5c|k0(M;rxy4PvI(9$$E^pb`U@bu{8-?Qeb}bgkoDrDG z8-S@Nkx=!_v}PrPtyS~nojHKBqpTAAA{;4-)*N&ZoEL95XU6yJ ze;47?S!jRPuohg9#NwaA&Bj3~z3f^2RiFn_;z9HvdRapu*86Se6hO*+q3YQAXv}#;u2g;_1bj|46R7};<30Iy35DF_U%^HWAGkZkv*Vj%7bi`w+lb+ zz%+_Gk0$_#Rk6ji2o0g*@T&(C?vcau_S-5dDgy9&)dX5q4-RGXa4-)1mU@HD#=38! zZY+PIoKQ~1C*skXHyl^#5nNknE$V=L;ERSDXbrrk{b}`+kQlGyZ}{uQ^4F|d3rQKT z!!`o5Q81Z>h-p|XS!)h8E7qEg#0Wlgx4FOPFj#zHZ=A`n{yc~~QOz{hX+Bhc0J^8z z9+mdxc@ODhFCc&X0ep+Y4M727#73}gX%R>X2^$c)AZn3mi8#oIXm~`k8pa}47%4Nr zViN99Pi$bRJPcz-lPRPjVYh<4%*a}!ac2Evh@5zI-13?H)~eb<;lB*dlLZ*cJtNr3 zk673%hz)e}v`a;DUP2DWcMFyTLXRl07`!GJ=h3RQUbC_WQpAYO;GUVPwQ!Q&4?=)e z#m`uk?aImpcK8UI9XxGwYB!nT+Q)z&kCxA?-J+~KVvo9T^qqAq2h%(Pb2VKD1j3Yx zwT3l}P|*#QH8@uo_v0(dyWc<+!j;IZqp}9Wo$|N9c#u1G+CrPqA+WyH-sNM>Iw>>L z+HK{JxDIwK*nGvixFil=7B`g!JE}%N`<)YSF9~)m`$zLHnVzGwy%jaZ&!A>X8*@Rt z9i_Gy4~8gFZua6_ldA2*h)@=XOz`MVe3%U11wO(_vzd*%5&NOGllg794jEa!?@ZC} zdvR)V^-)0a)-(U6p!`|*=_z$Aw#a*(S<8pO{wWsomvrjC5d<1qJ7qy_(~>s!Ic`)# zCI%E;u9aITqDfr)m*;>Y6>EcNCeBP@W=D~mSSGXu457cIB5FmPi%%gB5iStndmF?kW?1zH{B?eYA62!f_Z95tQc}5? z+-7-Aw7DpHyg*q*X!+L~FpUC7&8;;7{UE<+`#PQ^j5ac}D0~gIQc7vu#f${2VpIQU z$+aH`=jaTf3<4tb?YD=8Hi>H&h&b58mDDVNE5`8*&YH3Sb*%XS%8xSvDNhj<5M4jPHUvm^9}=B_$9jkb`8-~;x)Jrqca z%c|x~YH=37z^C4JdYI9Jw(t7;KX4Gj1lxedC4Q)h&EfVIlUzDnoI=2*gLIP1T)FmR z7|jmCctHYhB1~rsp8vm$MH%FQ&_?H80c5QEI{pseQ>Owc#m5k1q5Sb^)gxY2Ux0R* zka2bScu-a$evNfQsCdCyU)e7D!-LJhZc4r6#blRQr#jLr@3XffO^3lH+ujdw;`jDj2-Cr!;QvT zzMpOciK9YF>v2>|)IWI?eI6w+IMBh+IxtXXoAMFlfKS(pVT23oW#~X-)8mEPP&i&N z*{NRi4E&ud`4>9S{rKzLjUQ85sfP}FS4K}Fie^QdiZI;uh+Z)YS9=2kRk2AT^2k8K z@KSMN9uY7o8b64h`-Fwaa@F!KY;v4W8`}!`>U!$=*ZGES>q zuff5-Z($~h?kb9&ELi>@+hr(5YLrkkP%n*j>@^Rh06>MEu#} zb zLp-fPIpM!npMaBzYfA`{g>P=Vh~aYa4KATaBEE~hJ25HxyXmVffnNirEY7T!*96+F z+1~wcLTKW}MX=5C{Q%wwEJ@Bksa+!w3>2Exz)^34#k{{*KUoXn`1CYL!DlW$Q7=L< z>Fhz6(JiVmvt}V6bN2ED)=mG!CG`8>95&j;)%D9P$Ni)8L1#or+hEF;Zj9wIZv7RT z+AxuVJzB^foPXGs4b+9S@0pak4_+}XBz;FD#$$1`K2lO}7jcVp2{`!A7laMkfi2-Vm1P2S8 zeCVa{5$6k(_!S%7i+%zv&d~RgNx0YIzx^9H;&uGUdT8B!_!74`coT2o+&SjM*ur=5 zAwsF**uz=Q*Z3manvJcny_TUcbj{i(Y=;-u4nn2g6vJMz@ia`torAN0blMpM9md_P zxt*$r?yXk(@H#J@sQq|w8n5{!GsRn+>CaC2D~CmCdEbLF+SS(YkQCo;r#Q0WrN~b? zo^w}4kJHn8{O;x3nXBH1n-BT$M1KuyD4@6I!=N;3`H<@SlM`_(tavPf;@Lp_ux_?q z8V$aj$Tzyl40RD6jjgvNUDxtR1PDyMEIG)5v(fYO4@itu_YtulSrxt}<&uK?e=n)bfFlb)VjX&H?@+*s1prN;n8; zo17Icw^trKfw_g1Xonxd$4`QkhCluy?WSfQ2^bs# zy~JNI3ZQ5HLpwtgt*(UVLj<#%FLtg&6&exUJD9`r>$t*)Ymg;tDSi=m4RPpNtcR+R z;f!U<3(naR>q%*Ia19*XaO?BJ zXQtRpIM%^!r9}opjB5 z7$9yF)+&s32SBT6AHY0^2hIJ?aXb#dV9F|4MUCIZhS%PS-(|T|R&6AUth*b9iw{*d za$Uz`#Qp=XDK~SLIFCb2ku{){!{2s_k%slX)XG7&lY@lRLPPx2JDRXlC+fu}$QuVq zxO>2>kq0rC8u9y(hNUr1(NP>DY{H<|D`I-sJ2xYrf?>zoAkeSzW4hufJ7J^WM%7s4 zduIW1u-1jfaD8j69t}X%DVYfnUJ z2}L0*hdTaBYJbb6h|kIZsEBr3UVz;#(UYsp5H8^8<{}*Si-ZmvRt^x_xe5pdUXLaV zmXERTed$E<;CpSJ^V#0rm&#I>v9;V5p4;CwJ09u|`=aHCy(o@l4tyR>jx*N%4iDB% zxPF0w5r2XgM8GriSJRt|umH~@)*{ZT?bA|G7h*s|6oSq)$}IdLMvg!&O}xR{T+J)9 zJ~(T2j6&`_zyZ<|I2iUrnYR!mu=&j#Jo*(z8~*0n0hsKlPLol8_BS+0*Jr4`r7-f_ z={;KQOLRTOs1d9cyDawG(Jt@*Vef6=qpHsQ@f#R)#OR=+r7hL5!a^(qM5PjtHX#fz zN+2Z!P)cP;CXgDEI4`_d(cok>*Xt;@rfXZXi%8}OAw?rT!cBsaF8mA3n#4a9n5V?ANgUf4o3-&+3*K)UgSmDL9f#=&ukWFD#XhTN zz5dC5H?jREwoky0DL7(Kkvy|%p79;k<$5}ny|Bmv@Hqhv4#gu`2A@h};l&sVf*%ee z4XT%R5>P@5)4zeWD)Tb@W0yi|UIZS(tEw(+ErtENJb>L{4iT2XcV)jCd*Z8?f1Uk3 zFl9O%9kbFTb^RoIu=JP*U1KBDw!I>4aq7<~&K2S|1!#c}m~PNtup2&lusi6JJ5$3S zNyRGnl|w4eN_O4+1h&n#h)cP1H@v0_rzHM%n(wYh@w~%;3TIuh$r|2o`$|K$ zDu!DHP!!MQ9>x>*6p)hGid!8Pvs<*A3R%4j^+xP3D`96tv6+3;)-bH^+yjj{<+%hq zc40ffQ_K&e#dBh3TQt~DH_wAH8GxJ%=EVOjTvGsoLH!?&t<12+EM{ab+t@Rx`_7UN zV`mq?FbBtEosQ#yP0!b-W4!QJ?9cKGYj`I!o>?fYSy~{-DNGbz1bR>Ld?H?GRg9XB zS+b#+CHo7-&ugfmf)_J@7NdI_<$I*8pgXPU%XGOnE5raG`g;bkti`apWbS}xHXAHz zB{vz*esij}i>23LxDJsqP;oyyC-yPLZ~c||NbFuIGvI1YnQujzKd#Drww=GMqVJXU zqO$iNQ{Mob4JX-WVC0k@^fzF1m@98du+Vqw*HI89;I5jA{V=KzM?Q%u;I`l&fVfL} zK7sAd|1i0-Cw30MM!OLYYk02~X^RVFaV=+%i6p{k(`MT#Ktg>HPGz&Pa<~NB@ctR$ zBV85=2;Mud=w+G6h!v$4egYF3W_97sgVA~c>E_Cl+qlme*b-|=qJt@{cXqd3g|!;X z(sumZ*Bz@g9x+v~2i; z5&N?5k&g*5{Zi3?rwohFb@KW)%ldKOlR+$oKZyJmy@CB!_oGOu`!5LTyQLG%qSK$&n?BO^XQj>96Q4j_!d=22 zi0@5_uS*U>WR!IiWg~E%Ks$^^|zV2wl z0~&hk1`Ib`7?qcxn&e_s7)D`KFy`kAjREv17!__I0d`M6#C@O7!vDa*A#tj>b} zd03&W9a?ag@+fVL81Fpz6gmT+@*mi#uBDSNz^D8hUaU^0=ipPincdbTK4^T(YWiuX zAL=iC@G1WeeuSdT2q_?ax3Z6g_CyDzt@Pqkd}L>ZPZ4NI7Cwd4>=&PM_IE&A;8R{3 zGW*{+AvXB6jwMKdFctDX&>ji}@LH12*vb4Yc7vkNoD4qY33e=CXG7ytl3Nf9yKs>{ za8GaS0z_rIIPWxHj9V(9jjw}P0#lrxri+IDn>L3%;8BNAo+ND@Sg|qGhi_uc(R}@qqnfIz9jeYkYQuLP?lp4Ackxxi*N;dsMm%}1gT_ew%3MC z=HtDHPgOk(4POE{!`yx&lMoIxx39x92NI+3#p)neVz{e^iwe)G1KaqPKYfziqdaPS z3u~Q_5<1%IuEw|UlY?)u*=5ZciB7f~KwPFEHkBbZh8P?-W$~2{-yP4tB4|qTjiO#* zwKF}kus~e?9UrAVXnc!1GaBC_nbG(bwmX*l7?3emCljUu@+Z7e<*+(Q7nPRPPQPx5 z%O@^W5SK^bn-G_;<32S>syZkaKby6nT+ZWDJ3lI*3zNzLy3k1jUHE8rHsd!J=ptF{ zihtbNP6T=<~O}R)PntcD?~CqY4JNOgl5D5i@rKLIe#Dh#3aAIvA@8 zL1iy5Vy5UX06t3`0-BMh_^DT7rwWEmsu)ykD&Ks@1_1a?B$*eDo>|IHHpK#zr1%kF zLJTZb%)ZWt1=G?CqnT#FN((Gal3fq5G%xOTYT+xWg+wRaC;9GV=YUnqcuD+ml0wuZ z;3Tt*GL>lls_1Ej?oO41s1xB+A!^<;ljHCLM9m^(myeuP;%LTd?U+r$nY?XxpNp&(^U{mlr zr)WEGz^&ldlH8ha>0I9<03)kaKu!X|p;raLp?3|zksLgw!WQkH?Yrx*jH;c^=3BZ9 z0O4?;WOlHunQzjAkRXvhPbRZ$eOKB@$#xRL*E$3JmbT7Ij#EP3HJ?^^oKLFV7E+z0 zJW)B@RYkCDHa^9YmWP#ZzB@CRQAp{HTS>nsFK4?+4a1|AVtT1|G9Tu~@cS46W*7*! z7xJ^@Q~`JLV0IFS7s9p*A0KiZ45M)Rw49t&e>8W?wh$U5p{JEojGfVn#`xeH6 zv|*aONAXYUDES_fVCnC_!K~bb{XIw)pn!6+qTHuSrTS8`lvh=(J%5nqSu zw32*NvW(CmKvX9A?ph>`0lz1GS$WpacemA`<^ayQil3fhZ#0 zIU7L+ntES5k%4P>lH~v!#wXR{(Rrmhgp+JUQPunGwHT;G5hQjYEgQW13rpV>@U!lTGmJ?VK zbC+3+WU;*o-F5<*G|5cS`KHDllcXG~LGg1@Qa$sRYSZ}$nCRr2KUT_75!eCDPlr^y zWZFUV6{ZUhQ5x`@kP#A__#`1%=9h@vl1G(GmBY-Fa8~T*Qv+V%>#6q%sML4cEr=`g z1b!Z8MwRF{g(fPaDr*BB_dmu`;`%jZq%^?))*eW8HXMO(b(a+-Y;O+P9)=64Hkhn4Jcz zPl^BS$4R|*4_thAei8g&Nmp-)aJE&wyMBiHnz~&)wGM_S>{C`;unQ{ zO44h=&Z#Nhvye~H%-Dzz3rjG_V}3khLKc!#m(mzr#KAa{@k6%y)LvRoo!r zMeO8SM_)&vpW&iorb0z0(>3HFq{rC1;rM)hgtm>RIbT5maQH5LIiTS*pl$8i>%k7E-rWC{o_ zA*}=AI`A$tuf`IhuWuhtZvx^Hz0!^6>5Jp~J*z&&fLS=M?My3v8lhZDF%*^D>SQ`* zQhsF&khv7elQT%?j0Hl9K1cFpP9s^Rz6IXphnugBR(&hQXASQf?rVJ)giHMG9N%5o z0MX!J^Ko{(CLvy}QtMjZ2GEOGEEmx0wpWpR5!-_GI*NJA^MLh|K-st`E6Fs~@Y8Ci z9&$3EuTelfF+)3;p_EjK!hOkbP@rFjX*WT&7N)O6m11{AL@o0|IJ>7+#1oq0##|Jb zY>4ZQ)y9H7p$e>qn)^baj9WuA6yvo1|?a>*Mz(IQa*S#`$EHxB5Sr>JwLMs zZ(u6sSDArQ+o`8IjCCBS9@`jm89tx~=m#Wr1;2s=i`j*Yr3*(|DQN&M zgc$tI zv|8KYr$_3wcOFaT-1ply6C15s#G;6m>rA)S#BlDKoxr9 zu2t}+xzr<|G$|NxX;)|(DqNbf!VsFqw1wi&SV@Txvm%ed4bU|4lz9j*vtpI$H-1QT z8ofB^w98eGLV%i@d156>WHvzU<3I#qt_}b-AD+~llm@6t+ks{FZ8eF}y<1BmV$?QW zhGGaTQ)Nn3pTN{Efnmm%WCK!WJ_n{o^K^y~62R2B<$f)D zSNjHNPrMrEc@1Cd?W`1;-9-ju&SxE6WKP(gz)NRp;f7@GS;4`XNpP^I5|iWbLJAYF zMq&W3#=6eMtKInqD6i3K*ozY$HD>K4WJTA(m*Iz!?7P1MsoL>_Wy|o0jood`npD+n ze36NknWl`)1h*z%#xsn9sm7mR3+O#)=?iSPG?tSKw?TLK_UYrxvENxs%9%B^DntbGg* zsjD<#Z4h37838;DOMr&o!o^oln*^-M{HDUI{R+t{{McFCAj-1|17i(Y`%RwIi2dT0 zU*>*a8;@SVj!hc0b~iP1QOJ66Yk;tcVs|}`1S@_aXTKoCgGgpN>7Q*rU$he@+P#Q# zx_RS9Z})3IDG*AfO(^&Pu=!fsD4-OGjd&mCc$mhq4U^nz%G8kGmK_10 zz%${XrYLr%I3imSW49OVM7G4sSCF`iUx0oL2AYj@hZ&+vFlT3FIK0`E zD!(A}L#B>9F(|3>^D`VI^Zp)KnPcF8$2($3MSGYPgPZ&-i=3U7*x`2pJCJEnUZ13{ ziM?jv6@yU(8h#6;v~Wl%PglWKkhZ>1fej)z!nYwGj$}TE7&i6F8{#+Z^RWKw@p7C0 z*Wl$Yhh_4=7B9z)|8K*~&3@^>BVO)>=l>sG?*9tB+zxE1V91tE4FoqN*ZqavKERW} zL*bD~l^nd>!x%v4;^pX7;pMQG6JH2Qr$@odErA1sV0yzZfR~GVG1Zr>q#t19+FS6T zFm!-yh$Bv_z+L3IQVXxeYaYBDogKU!UmUy~({u20FEYD*@pANI50A3;b7CI{Wu6f# zyxhOi;Yo2&s$(x+ZX`bSiz5%oaUhY7^|6Re$9q62l6#L-i4m?EX|2$sq z9>hZJfgK(DkMtM5LA;ze3tsLg&^8Ui<$7+0sjPD|{r~#3U%;G-ryU3ZZS6ihY6Km2 z%}w!(xgQ_|qeXo?@DQC;Niv_McAp14m%foD_a&c_&W`}y{`P@-5PrDebab3 z#z(vy9c^`66XM6PBs_=5!v%)93Rcj z<@n9T%SjdmFDKQj@p1&TzEOUD!d&1!wQ36;N^nYc+1Ai zeHTmSfR`(Jz(Hy$yd1sKqNnh3%wj6p%1E|Lq-f7piUmqZ?gTHlm&^G(SS;FoEfL7j zI{U-RF-ti32fx%`6<%%>V@y7XUyYaZ<5A<~M$+eRZM+=ch?iq5++M1knd^g>V{og3 zv0@7a{6Yaoj`Hj<^|w7xz$Ffamt$;#m*az#R57U7m?Bor^S7Pui6mpr#>>H>B*l+5 zUXIZaFZUE^@Nz7x#>+{qq%X|I z%du!B+ewIQyc~0!67st6a?D<;lav>{oT>==?8VEmye_<)6i8b0lhe@;nyI76w3T9d zz{}y+Z08h(gO_`kpM}XzfR|&wm2DttFf!TWiqR_BVM&p)@NyoemW<&y*}-Sx<#>m2 zjBXBIPO_l!a{NNPoZLH^(>!=NI%71P2QSAjS$H|dEqFO5%uZh6ITlZm zAG;soxk!;L*}@E^D)9?_G1b}1*N$4g3SN$KjvyTo9UTk#mLD(20tsG@Ub66Vba3J2 z{@4X^Bl0vQjYE&s5v{_@J&W%)UXHl|q)WJjcsaf&gu1}Xk^7VowDEGxgM*jj3-NLc z46F}Gp;C45a`;ZX9AiP+3NOc()Is3;1O;LC{csXW*csYJ^bsEi;rq{t=sEn4qP*#>=s~`o+t!^|*v5;w(|9@NPw;XWeb5IYEb-KlvL0B zrP_2ph9QYgzGdO%=#XlcOuO)MucC=zuS9&y!pqT{#>+_v-QO{X8ZSq-vGH<>b&_Nq zH(rhzRifV%ny8GbtO;ID<+wk*oFv@-64D`Fj(**EIr(bi<<6s^aN*?`C?&PtKVD7} zhiFiEIllR2T&D4Iba3j}!OK1LxPzBt28frFj3k9JZoJ%oN^T?>x=0wSFJ3OUiMa7{ z$P-}8_yt}rjW_`K!Hc>55T4EU!}zuFa_s6Y5ze-%r}1*)$%U8WJEk4^n|G%$e$jY2 zdL>?t_iVhJ-G~)ljzQdbIlc>Cu5WWzcscPPjk=vBIhiJXDO!PQnyMDDu>&a~csZel z;N=)G^+KytnlH3N(OqSRLGKDR0A7xvvzxBs2GxM;B?r=giI*!?vWIv%vW(R(X$#|+ z(kvBTP8v;$q0A)nS(M^0&LGDxTlYEAy7`ucm!m`9*8SlQwr+lN@N!I*z$WHFHj`G_ z!O~4;O4YOan2VQV$dkj%QDWxc<%9vy+7w<+h{8R{N`aSSi2)Bs>{wNROz09zO)I>d zDy<7IC#4nL-;I|WCZ%QKrczFN@p8S5l$Hs;5icj%AYP7qXXE7)vC_m7uj z1|7T{)5>O0VNK%Yr0hEZ#I`#Y;N|G{B&(xeyqwU{g_rAiBnK}?51FyT`Qo=jewBFC zcsceKf|tYHfi)?-+)>gzCHngI6l6R zl`(*Wmm{5f@p4iag_k2~1TW{sgN>Ktv&PF^VB_V^&ySa5R9fE#UJlE#-FUgh*b)pY zBH$$(FULT+csZu27z68W7~&*0Nz1{@FBY-2#EI~7 z5^FDBj<0&S>B7q~z0`L0J^kY4nDR;B<%EB+o0`VUF;uEkMgJh49mbLhPx;|wb&SL> zHMabls*U1T^Z*(!x16ti@p27tR(Ls9qS-MP?*rlG=-*25_oLwD7*6nVwT#Boz8NN9 zWwIe&jx?}$T5G?9KE%mtm%J;yTnn5vUXF1nS;vk<;pLcpylK1~-+-5E!>zwO6E9bZ z@5IYFrOJz!(|ON_my^MfSqCq-Ttce-+7w<+`LpqIe6uHnTzEOD-KQPA9PR7_;pO-}8!yL9 z^v+}1c)4|O#bzNIFUL@7-z>Fd8}M@CAs=3j??drr0!IPSHf;n9H>mtMPIyn8wR7HjS5~t8C?Fr?3WiIf)RnA~s%*o*cZ~k1XQl=*5kf zV*?;wPE9tk5(mJ`sc9%1FDF5Pms67%#LKY=++`}YX$Fd+@p4QNE8Ce4w{@FV+KZPH z|Ms?S20rw~%dvP_csWK#R_%~4xp+BoRCqbonzAH1csZfI;N|G=@5Re4k(6z`+(LPC z@N&Hoo)9m`jM;d(-e5Lf?lOEe+b80;FJ6xGyoQaJQ`vFj<>+Eh;JNT}DyKGHjwxdI zLN{KHPW|BJzLkZSBmFd9?qOs_*TLGpcsZ3f2QSA2GQ`V?xfh^EvX0>8e+X2V;N`gOq=T39-~|w;0r7JC zF{}O&@pA153cS`y;N{A;_KTOB?WCU76Yna17_^0xt)28@6w&@Vd5;`z21UBo2+3 zz4Z_OWDuh4eh40CIS@9rwHd(#D6)}$_q))NHEPai>E6gl&&r&Mfe(>#+mIITHF7^c zk}j9HX)77P0H(&)HVl3%n5!3`S@>)ms<|?^?$+sa;IW@!2epj95epE z4KFtW82A5v`^$a%yZ;X__kTWKZt!mToDMT;q&0Vx-}=lP^Vyx5!HL&T z#V%(J3uK+?tdXgk{njbgBF|ZA>pIU_f3&XinzIg?i*e2*4?Lx7Azg==v(o0`!#MoZ zS~Q%l!|6K0oOQ%p{H`@~gtcfST}RT@Z_YYuE>%J?7YWS;u*uJ`vZUG}AGciS7}W#xY^^8dwA{?rwBr7pYk{g;dXWWJa9 zrD1vQeE*Gtv^DOo#0y5jf0*O$Dl>l>e-mHKbXHu9Q}Fs8Y-*{1V>=ICfa9lvg?ziY zCDC>B=pM-Fod-WgSMSEx0Tk=8jL|$l@HA}edi>?!aU%Nd-0Apm)G#(p=l-*1{5Ut( zdv@pR7nsj)+}(p!P8*>p`C2x<(Q_&yeq*@#>}ju9U-FpGTc>RN9d>c+!541$24~6$ zgkN^ltbX@3Y?8`^)cOK$!dc;M+vR&C_*U2Jr;fVWyALNRwgul}7q+YC^MZrB*)T>U zyFI7vt7A+!kUGs127-qVtWGIk;~xJ_X=&-k zrwnw`-e=;2aiXV-*sA`ZvOZDmjSYrgvTQf;>9VNdW?blx}{`jB9`x;`c+NT&cT;qmPo zu!HGKRpzlBuYxOn*0R*Z(cP%Bo;9cM3GQRG@9#HH!Fk>|YuDFWjJOh?`=hUQEFKUJ zCma{-HLJH^*UXCNe2HCD0C29$hUa{bt{G;{@>+`vEuVF*$HW<#e;$%}^SBv*$_(zX z;ycXhC#>o%zDH)AZ4Nafp0?nl)*#HD|El;m#d}Y~=2l{ahtG;9zES+VRlTEl-)TGJ zaSJX8Sb<3PgHawet6xPdZPSOE`x0HJ*O^zUy_BCzbe%(k3ywobr1|7ckJ(C;>prVu zFAn1^sCdq*eyZR|0dgLE&Uedf6tfqvZw0rYTo@CE-xZ{&g)?b4yn5l$()ihF{{}*k zu0lCwjehq58@dT~&8psSRlLdxKrkR}J?UF6{II8cTe|zk4|>cIqDq>dV#T2*9=jmf z{e|~m$J5R~4lV+#AG)$+*P1~l4psBv9BNfXIOh-bV^(Z2-!kLdSV<{WNm4`R?lot= ze;}Pwv9qcY$9g1NzSdHf$a5*qkxT^e$kVbn94h#mS@EFxp6^jeyA4}Xm7vdqiPw)8 z{My{GwPerQFXAL!xMTTmaR1J~U0|J3XD;@dA0&32Zl20Rb}L>@%^Ujktf7WE%fmQm z)xy+yB0W?n{MUy-;ow#?xP>1Jp0)%ykY zgvbDkOV5WMYHdiDY}n#UMxj@xzxNeV92*3xgL!ej?ve-8G083J?mdKWjB_G@iRqqC zx{t;cAqGeSDg#p0v*U|GKJ&$C1>kUB;?rOIut(&Nb#Ch!td<#=Rok`mj~5i|d;aMA zqub!-+q!RPa zKLY?gIvW|sD2o~OG{6aW**Y`*n~S(7hp&wb65$8iHHx2ug)Tqez3P})K-s>`NsJNF z^7(G_Q_R(qmEStUo04-XA5f(j4qz=ReNT{6nZq7nF;a3WFnBY4KGM_a5GcWXFs<}V zr12O+A)$3w>C6@%kUe$Hj^*T`B!97UE1%o>Ys%3SDfu0i*(NHWm1su0(p;or&o0-t=U+a~aU##0Ld84c$>X z9o^aU-+2FT*0P+=QpxYd<}YN&W0T{!Lf<1xJc(o2_xf#Qg66ZIAYN{MTa8_Vrx$jm zJy^Wg`V2+t^~bFWQX%lj^UOUdY#EIStMk&A*MEpZ&kN0+)~w@LB!@M6 zh>!h;*|r(KFt81=ZtxU*$mDOk5-vTXI!d?VRA7vkvHN_nEgbp2Y(&AO-G9anyLK1- zreI?C>j?fE9#DU!*DCjx>|A?s?e`dop>4#6?6^LPCa)S0){#l|qp>pztDs<)>_l?wZ4ewt5+y13 zcHd2P!c76e*Hb8K3RDlLlm;O_Tunj@%e>9;udH`WJToZMjyIfpa_m&=Zj#z}51xm_ zMwRUIB|$jgrY2L2pJEBgtZgKjSv^e7p-Ai<)ctF#?6_rE6Xg&GmZ)NcHK?FKCbKtj z6X{oqpGhAAdujP0{rfnx0@*JVp5c;7YlF97A8=k+tM;OGd8`O$$$!c^$2?N7&-X}` zC-K3lzT|~Sw_qZc@=aZqj=W*-?{CeTh~pBubnTFJ+ry}NFvwtQwg>I7dKkK!l4ns5 z&!$H1gFym+u`yW2cTvgy=mo0$me+i{;MtaS9!)^{rIU%Le?#JwehBN)42)Ls))*ho>O z2V)oD^x98aGe?n*%{c!Td!PA|cQY@g-sKOUfdkgNB;}dN!(Kk^2|mGFvTIL}osEIB zU0KCDteMN@;R)+Ys{lJLvS!ATJ*e&YFse$y9R>SJ_WD}KAZD}TDQl*mo;N&a2KN>p zQ1uObLdvrgXJVyO+=0JzUkWR8K_FO~NLp(;8(Lwy8it|7v4We2fEe9BRb^j@=axH# zB-XNrlLz8nTP|4D4`W+=oM|SJATDXG>B^PT!9c}%*qVb4sLhIpW6yQJ@ObIJ!+0SZ z^3fjWMK-}^^{6e1V4Z1XDZMF%5&f+bNly5Bk`DyYt^=n$-hw##5gKG6DGSsXmgVZi zhdqcK!wDn(vt!459`8cf?p>PyL8IB8f@h(kX7+=BP$4a-ko%9LW@QQ*y=WT~?uwma zP8vZ$5cg)dYTWnet?XH>e`3cA{AS8CqGY!(F$kX$Qww4j>hIWwiLCK;?F$vOJM!Di ztDx_D>yo{UhYJ?~x-wNM3>#GCpTLHUkV6|GVHH{muEZN?P!g2jag=JXL5Ok%54{-gl{| z`}N-8u^zu-d@OchY!gB~aIh6czu(5D(YANgU>x-@#C$gK`e63H<(}eaSZAm*!+e`5 zTZ7Z5RKS8(jsf;jbjw-#63b`)dXf3_s*Q)YGDvW1$}x5?9<;C=L662C7x2a+78 z|H7{|@;0PW2I%=7P7ff8U4t>ItTBM{Us-5YBC>=NnLPCb??Xh9N3GyN^aCkR zDPk~p6+F9V`fC zwqS&^C)g%omU{M_%EDQRqxdzKVF`bHl9jj-Zg~0(1|ucAW21bJCf>tm90?a2;d`_T zbo1eL^buGjKrM>{UL_s`82i zeZpL}LmhC9bKlKimlfP%23vSAhp#n`09=odyn!}*Ied;MO14^8QyCMrv*0)8JLLMC z?PkFT`@t}<%B2M?49UqdYBuKVAGFe%v=g({XvpX&QQPD8xFhvLbQ6N!zMchKem&8gm&3O<}3z7`w|o6@$#VNpag zklj}G-uG@HgDj6vwl-{rSYz}yf8ss@<)z>nZq7m%yV>`M`0fcF6!w%C9M6lo!?D+G zJcn}l#!?U%|@uDI8PgxYF-O<44W*jaY#8GHrS zB-{WQj#iWM{0Fnm%tn?Vvm3UeN^etHEvFZr1F|(WW0S|cn@!4EcF5ew27soHUkotJ zWm|CMQQXV5Oh*PW#1;#Uwqh%TZrEapZ*%0P>9ovLA8dJm74*+6R4M9U3!1?$2o&ov zMq2VQU$PBxXc@ssOdji1fF%aLESjr@p9W*;QCKS;*bvqxU;~C2>>?&2qs`p>P zxsuk%N2iW#^R>Q&Z!?weNE(s@n_43v;)eWCL(C7+iq&@7*1e>`nseXZP4a*rPSq#8 z55^W3YAulY+jqW$jKUiUS%P!YTLqcw%R7 z>JfE9pSpL&H{*Y?wo|{*`HU`6C$CYg$)FncF*!fbe9tbQ=Z7k<=xaW04F>OyROiO0 z3V#3uC8FY<97kg-`7QpAUH-i+eBET)_w=BHn|&dC2e*N55DdeM@b&B>VV?j@igAtnVQR)%Yy;S61od(|Ng;Z< z2aqgy;NU22eB(G)?9o4rLt8C5Brx$1tqC4$6GNCIZ%191nes(dd1eZuhi~zanT59= zYXtrN9A#sLMu(?{Z?gu?G+`Xwc{a)fz_<(jbn2`jx_O!9JWX|NvrTDqZr$<2`cBHT?M>%1&<|qV2wunXY0q%kjDUQJmwJhqTU=keH!Aq86y$| z#I9&~QGHQ)Z(sDZ>WkjFUG+tjGVF^UM|}57SJVZuh;C>BGLX{^4WUGuuDYSuAPCYo z-H_7_b-`7-p-1t+ViY2lkJt@uMB$_xiho*({*9k!8GXsSKn2k8AZQG#yq!6Y3P2~N z&bQn+Sv-k_#jX_@YT11lIxu96eOM(nBXjp*quHq}Yhk}dV=UD~DCX3%hr`T(8#L*? zdq4ve4Z~pRsbI5hgDZL<)AQIY6E8Sw_(k2bJ-8eS4SZV&YQ9BfVKd%JmUyr>X(!|_ zB*NRY@wAwOXmg$J!PFFVw#@0MPe>AsL0inVUaJfzW^InuP+*sNt*eH?a&nqgUTBt~ zui0x|$)kNvv!)L-%P)g#8l!?E%PT%QmNEAXvv>E{K&38 zs6szlu^RD1CCeRec+;Pcqw#PDGF_4JEBaQV)d&`})sWU{@H}lk3`oA#r-{1<2i|~w zHS=Swdexg0AG^ml-t;f9OI`PUXsL6-LwQ>2FbIRpO$%TgTBJCg)jL7ved1w;#ayGG0#{}(%1r{%o5mP z3O;}e0NrGcsVp4-6<*1kU^v9~jJ_x{xxs$Rjb}G7!D`Cj7 z&NXPMdm%oAipOAQykCF_DTNW(!0~u05gC#8CZVFf;~KHJbw6Mp?cNHKKBVNh=cnrK zovrdwco6UD2_D$<^}x$@{-C*Mk@+i_vPeXl&01&)NDbeF`7o+rzU222i8X7SwHO2j zbq`syVjR@}oNXcEQRIDj=kGiPZ-J&2N3cu`0dUOT8H%37ygKHtk0T0_(YlQUB{6rQ zlF(A?Fa!Ak35ob`BT-r3B%e)2A?~5{vsE@Riw_7XcO!hkZflZ|@;#6eHog(U^_bEF z554>)i2m@q`uIXCo3=VFq3s7M1v|a;#H$r;@4-;RI<;VT_qXy8_&X|xh5Je1pAR}_ zZ+xlgTls|r7KTL@Dl=UPKECGa+dQLod2u+D8gKLaTr_3dPXij5<44)Qv0og9`7#tf zib1|sb074^N4M>Q_-uLB+xE4QAQVS0tq+mbRkOiT=UP|Y2z~iX3t{6&Em#LQKdf5% zpm;5Olv!0big7v7%1~2_4_GjsP512l^Pqw~CGSN4gZAhkL>i4Cuu*A~h_`3>KbM!h z6FJnqh0W_6*u`OJ#ZVmnXa*K*u(xMl_hyjmO^^i{bISowdJj4VWHLidu;ul^JnxRP zhM6zm$0m%MKYy`ETKG53?MPBdUhhZ1TDSyQa8e+V-D}6Rurc}^c5CG|NyjuT6 zEydh_71-+^&@hV+zyn;2^k{rFKXo5QhT6^x{ZHw4w~AH^n~mpQnM&i5Lzeys`X^5s z_H8T@V0^#o{vP`-h?YUIjW$(hTjyD~GJ|Y7m7YC8>K;Zxb-HBPAzxxKiibGSZNSE$ zCRY!EVqQ#;_6)zJCwK_HBEQK@gf~#`15hrHktlsTHW}>xM@4ByLRAxQh3V(PUd3Gx zkTjVz@+nr8YU_;I5HaTO!3v`usKn#_!|@~574zIuJU%c3KRf)7p5c9fK9}vF<8Hee zU@vjQ7ewA-KF0i2Rq=t1ho4|k<#c)!ND*NeCmbLp^r1+VD2y-p3ur5}gWa+f0oYG- zt3mkUUO z=<8%~QG7rKPJ4nae!2x)VdFSAa;6tMPmtrvLA3Ns$>Ti*PbZFf)?`vMea;NAc?8=! zp}K9Ktn(76Snz1RwdVJ*&j-Dz*p?|q>;kMTDel79;NvU7&2mB##_INPqR>!=yROrWaJ+>bWg&Z@$6#TBDqtwlRd7u+HmZ0i+p&v1X) zr-MH=p8VN!&jFBddTQBE_XO{qg6wDaC+6O(-hUH)Ny_upg07;S(BmK3NPDCCeB0po z>*liia6Y*z@UpFFj=|0IrHUSem5NW+jE)DTsF$^>`Wdw5aK8ExZ!G71DVB54nm=Yf z&Fu3WMj)h-S>QMhGD5{1EH#GoBrYmA_$t!bFX=p&I6*q_#&j-I>0r6SZQRYZ`*pNX z|2C8h23JVI91*9JMaaVRE7w8E>Vd9;5FzA<-P-l)?rmbW;OQp3r|&-gfzoHNzahHf z8?|K)Mg*ueX7y&^U%SkT`#^StGSsQSXhSeaV3cIR*H_uPtd=eKg&7W>gPVM<8M;+$ zp=2*8c)Bj}IkYg@3VFjl?7lxmX;82Sqies&KU6=v&h%3pwm`ssldW@UA!O|Dkx=o0 zso$hX_1!W9?#0}5qxD=4T7vh{^C#&UD)u`Cj{IQKWip~^eG8R<;XpYi@a`d(?X-fM zNn78e<(>o#0MM2&sdi2p+jjMw7Q7dhTYNcXG@w+*)L`41!LU?8D~C#UI=bS2we80< z>2hkp)0xk({+Hc>l3Whc7U}T?kL4|RHk&_vk52bwM=7d1w2*EL2iiDfg@K9c!agxm zv$kmdwU)J&bJX_5r8IfLtYgCDB`RU-pZFQc#Ww-%ajzexv|2=Yd=Ir_Zsgvim9pq$`M90zbSYci4DRIsIcC~ z$C2u&QR{XV0x1YlFuQ0jEP(qx*5|M$Hdatr@PRqJK!mWb6^GRHAi*F0csUsVC+tXB zY>|d`|2_;t(L4&Fc-is~WxTDWU}gxyzIU58BJ4*C%1nm~@Y$FzKsLpS#x9p_Z8ma* zEMhC!E#*`1CBH@XXYN86=Jys_7OX4o&9uP5{B34EZchgf7^!6ke2<}XN+1Vzy{i-CeWFj>eah=?5`BL3~>6Nm_SW46ySwJ0**%8*oO@BfT=-0hv} z0NeX^xTo(ws@wab;`gesh09@7Bh+swIk8VU_OId|ZF-5Fi)MJ*hiHh06V4$nbUXjhLx?gnl-!dQGcRuISH-eM@j`^Iozw*)NbI@yoMK40nN@{ST9CvRE z?&)KcTEoAu}U=MxtQ+8)*CxkAqK$gGy-WehN{`< zKf$k5Ex5n(9VmK7dmiUKW-l=K2?865ZcooXYcM{>-(9jaOVyEt>E}Q52mPsP0cSys zr5ksDaLFKJQ~JkU??F%3E}#_(GI8u>ca zA6{3_=*JH=Ce()4Oo+wSgC72xrq#$rt-mf*-w>`f{5r0s#etG#7uzwzUu_cz)Yue5 zo?|tuP?9IL#5#Puxi%CFyRu-1jt-PKzLmxNE5a-58ymr7{-!#Vx;fm-)!?1A14mHZ zh}N>QFaL8$5xTRfqL}3t78q(aQHfDCDB+@b2Ew(0`YaT_pfM*H`azC>-#AK zg;PyaV=PqP7)6P->~J^jegbOe%1F356bbA0?!Ra>8Z~4Nc!%wK74jbm$KsL3DgONN z*y5_bu{j|c>l@jW*e3c-%J&?TLqe!vJQE7K;Ik!rR6rba~pCC3MjK!tB^ibm^KAm#odnc`oC)`1K< zyc~{{?pPaXf(ViR4_&qYLUe*r|7Y-1)Nf#X5c#VL!h*6{LJBG7 zssa7&Y9R%r!$O8)?1OcG#`f3CsWEiV4a^3-pv|K_N8q1=$d9hczdGI!tJjIC@{aR6 zD0=L*b-{oct~d zE|@d(YX8i#IdiKQ1fgG&-4tu+7F7#5(tkrgVR+Q|KoQvO!)dopV}NFie$L+js6)8pNG64&w^ zKil-XeYfM_JN>4+Jl<{pgD>XayVK(xhHHer{QCY;%EquKqQ}&J{J7IAho2ml&3et! zUqb$x5QG#MA7dWK&h_ZMSE7+l?Z2T`LHP?oK?zUsPYzvn`Q>$!N-nRxWO8ls<(FJO zxukC5XD3ho?Bt1`T~RZM=T`RpQ^PFu6k77*DH3X2N$q+3c&1EMBUT@aa}<;(8z%^F z%nCP(j)B09;gw)>=r>I@96hS$c_IOk-M^rRa=MtYt3&J1+>544e^47=4b8D{`34AI zqf~$PQ_T(avHTG+ya-wuqPo%_I9Vqn3@<%GbQS1&9aM7=Q55v4{^n4vhz5}p6wG;u z(SLZ<%}GSyl%JhD`(f2Jpeqw;8eJcag;x(y7KZO5pQoxWAFRjz-6?Cn2Y33^QuOd( zfE2T1al+TEB3nvtur{pvqIwK(`CaI%#VXVesLRlXTz;}+%zks?2YsM$sYJ4J*Vwe8 zsdl}wvcC4(n6Wza<)#Sl^(Z>;=-P3^U>m>Gy>O#Sf?Hey#G!?NQSm;0!uScLbD+h7MH3JA3y&FviS^AYmGsHlgEiB`sl z*-F@ehZ^88AyE_^emLg0YphqmxMDN9e(CYH;M%6|%U$oAe+Ai!s|D9`Tu1PYcfQ~J zn#Vi#6|6wPHR6D}=*IUMH2g4MjZKXK-6PjUnpWG4U{qaK-`q?O$g&J@lqc5G=>*U&Zw;T%EXngX@UvqQB+vmtC{!jrB{aLKh9KZf4se2_eo#+?EW;Qv0mO=H(6v#pKE8|V@O(g{*Tq+|g;|(yh zh(mVD>a9E;L88y8oN#gG5by_o+pku>H~F z+xW)onST5fyWBtZi6LH&(8##78N1q$3^1iT)}li5iDQgYt%NKqV$fI(jT^dDc-<P6{$biQIZ7)nKw|t|c2<5WG zxRvqTyzza2`mx8s5@o)yCkP*2R|9K0S_X2|U+|Mle+sDYm`gAfRKFVL7#LY=PBQ;f z{7tp6gXx)?IyE;Gg?=P9!j%n8D^RPeY9r%Ci>eG^J}T14Cf(a4Vg0K$#&LF~GPG_s z1WP!g=T?k`6_qpQEHEa_LOh`ce#4{-XIv)4RyWHO3oP4_Xn4GyQsLwe|8!zRB3RgZgeOXlolE_TcjQHdE)$AR_vkX+=^PV#pg995AjaK z#cwYjIKzAJa>!_0d_R!$9(dslZ)M34ZwoGd58}NUVK?jW7vg;%o{#AF5qKX4zZ13p zuUvGdw~NnHRQNXlmM_Kga{Yeg&NIB5@w`vJPeT5NO&#Jb#l`f-Fg-kP((i8|e|C7z zTfKw#ma-wUli8}goZg|5X z5C(FXSkQJjq1MD7N^nh8FF7?fM(Jqkj{b#rXJnwbNc$1KRFHaHo`lYTfm{KCIC{FsHHA6hId$~ECIHA)zj`pZVX z^zDg@9QuvE=u+YZv;Wxs<}tB&1a@fGFqM6}J>=pTG&`8{#_-P2R`i-|Ba{+nYEDO| z;e_Rb79o%}w1&f01zyB#uq#ds%#kmCX`mM^43>WxGWjE1`*fHiIbkdHdsgP~QZ~JO zMsVh=*>kSEYHr26`GDJ3Ev#O&c*)h*gjUqlhU-?Ys{itJ4XYcQny-&UWAQa>*R8){ zV(}%HUN-5ou+M&G!oJuL@4@TQ|KL6`if3GYJeT2GhKugJZ;Pm_>}%*N@BE*8G5;@p zb%^&hTyNs~3$CNM3gABUBb1F{`=iG>h>zFge&Ti0@^a90&fU;^{s&*o|L#pgytm`x zbME!`mTe$aan$e!J_jcN6U1sS27sY}v(fPC zr{y&ea9-!Zh^1c39Xk~!MTwq46#U|h8mS%mWonXf2Lg)}!bA-u=o4C)QBEP1EhHs_ z%-Z;i5iZYP7AdC7ye#^#dDU~HLJVjAUCo*3*%n0R8X}_f{S1C7pEMkYmbefxGR%oa zL_kNxORz#a%T&IkoA>M(mE`0Cr z`q}{B`U~@)bzht(&3ob2k>mdDocG*x|260PR{j11eg7ry3usLzyP=x3prA~0MlBr8 zYZIc|D-F!Hf9*FK!n7vrh&h&gfLo!FH%FRiNHH)(y3W9KP&lGpfIh(#Bru=@{Mq`C zOiblTg4#JjkILS?HWVR{a6H70Qrnbf4DJdkInXC1+gKui9@2tV4bC4#U{i;BPRSAkf&9<=cs~!JD{Z()J+DMRTdN%cwZ_EJ4Nb%3GuPqUn-vW0WB*9zyU+_lsKfD&_;n;K_5MR2-2`1Wedt@ z&sn5mUo?Miwa&%#x%11f0!s7RnPue*b;MQG)63^qRFuJ9#gH@SFQ_P6s2-I|`Rri% zRh9GS%v+d;Z5$_8o@{Fq8#)-ksWZoKzK4Fp+xC9wWYGa2;PV9`9`!GW-D-?re$~?o z%v1s=iZ%rb5r9ld2#9ZP9%vM+gW88VWT;i}uX3*-tk4E23AOCCZDd(d*mIxP$D2fT zjK$^t2qL{*a=*Ro>{!l2)}wrZfcmbptK%mu0jwBMW?0ay*F3A{!*laxzw7WI`bP_23)BWENg^m& z3!H~wOhH>0Jj!!`N@{TzrYC=kFk#@wkAear+ z=opczQKT!Ym;7NsKEb^xJoBid=TMYLPXC&EENg*#V4=?NR9#$POzT1bM+^HFyXm9p zE6`>5a@qc66o(SpgHCsyYi7lW<0A(2kJUNK-zs`@NF@*UOiCkIbh(DcRXKW6m@CAn zTIOpfYHmQ2QOzN1R2<@2&1hZm^(N~))iyPb;TjDX|DY9u4FOg}QmR^{u5>lR)d}Q! z7%)795CkmHWG|o~CmIjbeXq2w_av!PFu>2n-Z;VURr<-jEZ>QR$n*}G?Qs3mptt-dia@n2 zSE){l1?@(vDjZ~Sx@+JA^7Pla1>{mho0RCfooFz3gPM+T@CL1Kb#5@{J;vq-5!e_I zsV8!<1`}}7&x)#usVd@^WX8=2<~%@PgKw#!(GylLAWgXW(FpA9IDYdzWQSiFX#(`= z?+MTTXSK|C<%CjQ=8Uq1!NB|l0qU>$k~dWPW25U=uV})IN|B$4NPqe4vU#(DRVR@X zhrdEKf;M1QK!-*Raa0vD&X%i~=m0-rK9X&mfo;Xn{{YwU`hZjI$eS!0J}pt!c_W_Br5^vXIMJIvd}Y(dhykv=yiSC zx;ve|x|;5bBw>KHFeMGDF}@Q_&LSsKu7ML0xMU_D#VldV@vJOoy+AF%`bC>r`pojG zWrFV9i+I!!OlOvgayJerBvy8S5?VjDMcn|S6ZGGq#+tAWhJ{B|N9vTe5?T*UMXwVG zs8#_bBX_~;45czeG4HOlAnGtts6voUjVq@ZprTC#X=`jDTFtNrgIV;*eX%=23QnqD zT(;8S$LwySzrK#kKRM-&bq!5G?d68$JJVOH6IO!y^@tMsD{y*%8KA6i&KJ&hV|ezF z=wR~&#^`?FKQ*y9f$H4^yDi8vwSZRibpiW}g(f_#mf#Q~N83?~tbd)qc;Y3O$)rL) zJprry{Lz|7Jya<4BQ|GH*_9}J)&7e74Gra=pcfyLwRENx`h!EKeg(HKkj^Cttyc4c zl|fG~xF4sM>jC}9YM4}fNnR3Qluo!kJ3sVS3by1LviZlZ4u>E+fGx^8?yg35Pl9w9$rbg+wIrJ6R} zvKm}b2ZQ?~ru1Xtlq)&1-hI%wAv53$k66 zm%$_or0MvDWwW5s0$mICU?XFVbDbkbtHkY>tC z?@tXuUmXVu83|GBYJHH5cfyvX(pTdbPIos0wXOzG%O^lj7s(*`k!G7G>*A!kN{~yI zqhn2?ExM>@60#@UEv=7F>-}n7e#}Oi>F}6ztQEw@NyLDK)a)@-46=C&`!_XP<*x|R zd>D`cwI0y*M>@&B7z$;d0wp5crk)cHeh~vZ5n703`xlm3#?n%EVl7enp)yG3qoH1{ zq!)CFwI|dt4zsXLn1OOE!{zhmEnF~vZk`DG39nb6aMUG>LsZW^cFav&XU}|MsbeTB5ejD zQDA80Y(owkab%x0?xFs`X=dAk7hF=gAXvrOH0cX5;TEN$rDS6sPMk@ZNq04%V^dLM zAhdvx8ZgOJj@`~#MX;1gpJ2=MqA7U@tDS5=dc5c~2-OW6M$UI_!NdktXhcpM6e%w` zpp3~PVGU-}K>(}?l%>%!DrF%~viZvRsdx6>4mCi6IYsRas|Lg;nQbQ)aM22IDny-F z$W=qvMty>7p&!7Mf@V)U8uhkIxCuxc`U55&$rEHB z4vu=a@^EsJ{{hxc&Wqd0Qj?s^2U4^6rC_1y?jzYwP%v)5=!L09G4nJUMz}k4A9H#oq^pYku1q$7qtNY<|c6H@A z-oH>iXQ4b`ZdLBpRSSw`E(#y3Mn#HonZSlQBeoPK P z+h-X+DDMQeRK2ht%}><_))uVQ`;F*9DHT^pX1kvdJyz;eb4GSO(pW?t3H>q*bL?2b z0(4O9qxW~o4�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 hlz^ybWiM;DX`PWDHj{dFuhNSF@ba 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 +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Bufproc Bufproc; +typedef struct Bufq Bufq; + +struct Bufq +{ + Bufq *next; + + uchar *start; + uchar *end; + + uchar data[8*1024]; +}; + +struct Bufproc +{ + Ref; + QLock; + + int fd; + int error; + int notefd; + + Bufq *qf; + Bufq *qh; + Bufq **qt; + + int wr; + Uwaitq wq; +}; + +static int +queuesize(Bufq *q) +{ + int n; + + n = 0; + while(q){ + n += (q->end - q->start); + q = q->next; + } + return n; +} + +void +freebufproc(void *bp) +{ + Bufproc *b = bp; + Bufq *q; + + if(b == nil) + return; + qlock(b); + b->fd = -1; + if(decref(b)){ + if(b->wr){ + b->wr = 0; + while(rendezvous(&b->wr, 0) == (void*)~0) + ; + } else { + write(b->notefd, "interrupt", 9); + } + qunlock(b); + return; + } + qunlock(b); + + *b->qt = b->qf; + while(q = b->qh){ + b->qh = q->next; + free(q); + } + close(b->notefd); + free(b); +} + +static void +bufproc(void *aux) +{ + Bufproc *b = aux; + Bufq *q; + int ret; + int fd; + + setprocname("bufproc()"); + + q = nil; + qlock(b); + for(;;){ + while((b->fd >= 0) && (queuesize(b->qh) >= 64*1024)){ + b->wr = 1; + qunlock(b); + while(rendezvous(&b->wr, 0) == (void*)~0) + ; + qlock(b); + } + if((fd = b->fd) < 0) + break; + if((q == nil) && (q = b->qf)) + b->qf = q->next; + qunlock(b); + + if(q == nil) + q = kmalloc(sizeof(*q)); + q->next = nil; + q->end = q->start = &q->data[0]; + ret = read(fd, q->start, sizeof(q->data)); + + qlock(b); + if(ret < 0){ + ret = mkerror(); + if(ret == -EINTR || ret == -ERESTART) + continue; + b->error = ret; + b->fd = -1; + break; + } + q->end = q->start + ret; + *b->qt = q; + b->qt = &q->next; + q = nil; + wakeq(&b->wq, MAXPROC); + } + if(q){ + q->next = b->qf; + b->qf = q; + } + wakeq(&b->wq, MAXPROC); + qunlock(b); + freebufproc(b); +} + +void* +newbufproc(int fd) +{ + char buf[80]; + Bufproc *b; + int pid; + + b = kmallocz(sizeof(*b), 1); + b->ref = 2; + b->fd = fd; + b->qt = &b->qh; + if((pid = procfork(bufproc, b, 0)) < 0) + panic("unable to fork bufproc: %r"); + snprint(buf, sizeof(buf), "/proc/%d/note", pid); + b->notefd = open(buf, OWRITE); + + return b; +} + +int readbufproc(void *bp, void *data, int len, int peek, int noblock) +{ + Bufproc *b = bp; + uchar *p; + Bufq *q; + int ret; + + qlock(b); + while((q = b->qh) == nil){ + if(noblock){ + ret = -EAGAIN; + goto out; + } + if(peek){ + ret = 0; + goto out; + } + if(b->fd < 0){ + if((ret = b->error) == 0) + ret = -EIO; + goto out; + } + if((ret = sleepq(&b->wq, b, 1)) < 0){ + qunlock(b); + return ret; + } + } + + p = data; + ret = 0; + while(q != nil){ + int n; + + n = q->end - q->start; + if(n == 0) + break; + if(n > len - ret) + n = len - ret; + memmove(p, q->start, n); + p += n; + ret += n; + if(q->start+n >= q->end){ + if(!peek){ + Bufq *t; + + t = q->next; + if((b->qh = q->next) == nil) + b->qt = &b->qh; + q->next = b->qf; + b->qf = q; + q = t; + } else { + q = q->next; + } + } else { + if(!peek) + q->start += n; + break; + } + } + + if(b->wr && !peek){ + b->wr = 0; + while(rendezvous(&b->wr, 0) == (void*)~0) + ; + qunlock(b); + + return ret; + } +out: + qunlock(b); + + return ret; +} + +int pollbufproc(void *bp, Ufile *file, void *tab) +{ + Bufproc *b = bp; + int ret; + + ret = 0; + + qlock(b); + pollwait(file, &b->wq, tab); + if(b->fd >= 0){ + ret |= POLLOUT; + } else if(b->error < 0) + ret |= POLLERR; + if(b->qh) + ret |= POLLIN; + qunlock(b); + + return ret; +} + +int nreadablebufproc(void *bp) +{ + Bufproc *b = bp; + int ret; + + qlock(b); + ret = queuesize(b->qh); + qunlock(b); + + return ret; +} diff --git a/linux_emul_base/consdev.c b/linux_emul_base/consdev.c new file mode 100644 index 0000000..ace58d0 --- /dev/null +++ b/linux_emul_base/consdev.c @@ -0,0 +1,157 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Cons Cons; + +struct Cons +{ + Ufile; + void *bufproc; +}; + +static int +closecons(Ufile *file) +{ + Cons *cons = (Cons*)file; + + freebufproc(cons->bufproc); + + return 0; +} + +static void* +bufproccons(Cons *cons) +{ + if(cons->bufproc == nil) + cons->bufproc = newbufproc(0); + return cons->bufproc; +} + +static int +pollcons(Ufile *file, void *tab) +{ + Cons *cons = (Cons*)file; + return pollbufproc(bufproccons(cons), cons, tab); +} + +static int +readcons(Ufile *file, void *buf, int len, vlong) +{ + Cons *cons = (Cons*)file; + int ret; + + if((cons->mode & O_NONBLOCK) || (cons->bufproc != nil)){ + ret = readbufproc(bufproccons(cons), buf, len, 0, (cons->mode & O_NONBLOCK)); + } else { + if(notifyme(1)) + return -ERESTART; + ret = read(0, buf, len); + notifyme(0); + if(ret < 0) + ret = mkerror(); + } + return ret; +} + +static int +writecons(Ufile *, void *buf, int len, vlong) +{ + int ret; + + if(notifyme(1)) + return -ERESTART; + ret = write(1, buf, len); + notifyme(0); + if(ret < 0) + ret = mkerror(); + return ret; +} + +static int +ioctlcons(Ufile *file, int cmd, void *arg) +{ + Cons *cons = (Cons*)file; + + switch(cmd){ + default: + return -ENOTTY; + + case 0x541B: + { + int r; + + if(arg == nil) + return -EINVAL; + if((r = nreadablebufproc(bufproccons(cons))) < 0){ + *((int*)arg) = 0; + return r; + } + *((int*)arg) = r; + } + return 0; + } +} + +static int +opencons(char *path, int mode, int, Ufile **pf) +{ + Cons *file; + + if(strcmp(path, "/dev/cons")!=0) + return -ENOENT; + + file = mallocz(sizeof(Cons), 1); + file->ref = 1; + file->mode = mode; + file->dev = CONSDEV; + file->fd = 0; + file->path = kstrdup(path); + *pf = file; + + return 0; +} + +static int +statcons(char *path, int, Ustat *s) +{ + if(strcmp(path, "/dev/cons")!=0) + return -ENOENT; + + s->mode = 0666 | S_IFCHR; + s->uid = current->uid; + s->gid = current->gid; + s->size = 0; + s->ino = hashpath(path); + s->dev = 0; + s->rdev = 0; + return 0; +} + +static int +fstatcons(Ufile *f, Ustat *s) +{ + return fsstat(f->path, 0, s); +}; + +static Udev consdev = +{ + .open = opencons, + .read = readcons, + .write = writecons, + .poll = pollcons, + .close = closecons, + .ioctl = ioctlcons, + .fstat = fstatcons, + .stat = statcons, +}; + +void consdevinit(void) +{ + devtab[CONSDEV] = &consdev; + + fsmount(&consdev, "/dev/cons"); +} diff --git a/linux_emul_base/dat.h b/linux_emul_base/dat.h new file mode 100644 index 0000000..f3cf4e0 --- /dev/null +++ b/linux_emul_base/dat.h @@ -0,0 +1,281 @@ +typedef struct Ref Ref; +typedef struct Urestart Urestart; +typedef struct Uproc Uproc; +typedef struct Uproctab Uproctab; +typedef struct Uwaitq Uwaitq; +typedef struct Uwait Uwait; + +typedef struct Udev Udev; +typedef struct Ufile Ufile; +typedef struct Ustat Ustat; +typedef struct Udirent Udirent; + +typedef struct Ureg Ureg; +typedef struct Usiginfo Usiginfo; + +enum { + HZ = 100, + PAGESIZE = 0x1000, + + MAXPROC = 128, + MAXFD = 256, + + USTACK = 8*1024*1024, + KSTACK = 8*1024, +}; + +struct Ref +{ + long ref; +}; + +struct Urestart +{ + Urestart *link; + char *syscall; + + union { + struct { + vlong timeout; + } nanosleep; + struct { + vlong timeout; + } poll; + struct { + vlong timeout; + } select; + struct { + vlong timeout; + } futex; + }; +}; + +struct Uproc +{ + QLock; + + int tid; + int pid; + int ppid; + int pgid; + int psid; + int uid; + int gid; + int umask; + int tlsmask; + + int kpid; + int notefd; + int argsfd; + + int wstate; + int wevent; + int exitcode; + int exitsignal; + + int *cleartidptr; + + vlong timeout; + + vlong alarm; + Uproc *alarmq; + + char *state; + char *xstate; + int innote; + int notified; + Ureg *ureg; + char *syscall; + void (*sysret)(int errno); + Urestart *restart; + Urestart restart0; + Uwait *freewait; + + void (*traceproc)(void *arg); + void *tracearg; + + int linkloop; + char *root; + char *cwd; + char *kcwd; + + void *fdtab; + void *mem; + void *trace; + void *signal; + + char *comm; + int ncomm; + ulong codestart; + ulong codeend; + ulong stackstart; + vlong starttime; +}; + +struct Uproctab +{ + QLock; + int nextpid; + int alloc; + Uproc proc[MAXPROC]; +}; + +struct Uwaitq +{ + QLock; + Uwait *w; +}; + +struct Uwait +{ + Uwait *next; + Uwaitq *q; + Uwait *nextq; + Uproc *proc; + Ufile *file; +}; + +enum { + ROOTDEV, + SOCKDEV, + PIPEDEV, + CONSDEV, + MISCDEV, + DSPDEV, + PTYDEV, + PROCDEV, + MAXDEV, +}; + +/* device */ +struct Udev +{ + int (*open)(char *path, int mode, int perm, Ufile **pf); + int (*access)(char *path, int perm); + int (*stat)(char *path, int link, Ustat *ps); + + int (*link)(char *old, char *new, int sym); + int (*unlink)(char *path, int rmdir); + int (*readlink)(char *path, char *buf, int len); + int (*rename)(char *old, char *new); + int (*mkdir)(char *path, int mode); + int (*utime)(char *path, long atime, long mtime); + int (*chmod)(char *path, int mode); + int (*chown)(char *path, int uid, int gid, int link); + int (*truncate)(char *path, vlong size); + + int (*read)(Ufile *file, void *buf, int len, vlong off); + int (*write)(Ufile *file, void *buf, int len, vlong off); + + vlong (*size)(Ufile *file); + int (*poll)(Ufile *file, void *tab); + int (*ioctl)(Ufile *file, int cmd, void *arg); + int (*close)(Ufile *file); + + int (*fstat)(Ufile *file, Ustat *ps); + int (*readdir)(Ufile *file, Udirent **pd); + + int (*fchmod)(Ufile *file, int mode); + int (*fchown)(Ufile *file, int uid, int gid); + int (*ftruncate)(Ufile *file, vlong size); +}; + +struct Ufile +{ + Ref; + + int mode; + int dev; + char *path; + int fd; + vlong off; + + Udirent *rdaux; /* aux pointer to hold Udirent* chains */ +}; + +struct Ustat +{ + int mode; + int uid; + int gid; + int dev; + int rdev; + vlong size; + ulong atime; + ulong mtime; + ulong ctime; + uvlong ino; +}; + +struct Udirent +{ + Udirent *next; + + uvlong ino; + int mode; + char name[]; +}; + +struct Usiginfo +{ + int signo; + int errno; + int code; + + union { + /* kill() */ + struct { + int pid; /* sender's pid */ + int uid; /* sender's uid */ + } kill; + + /* POSIX.1b timers */ + struct { + int tid; /* timer id */ + int overrun; /* overrun count */ + int val; /* same as below */ + int sys_private; /* not to be passed to user */ + } timer; + + /* POSIX.1b signals */ + struct { + int pid; /* sender's pid */ + int uid; /* sender's uid */ + int val; + } rt; + + /* SIGCHLD */ + struct { + int pid; /* which child */ + int uid; /* sender's uid */ + int status; /* exit code */ + long utime; + long stime; + } chld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + void *addr; /* faulting insn/memory ref. */ + int trapno; /* TRAP # which caused the signal */ + } fault; + + /* SIGPOLL */ + struct { + long band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int fd; + } poll; + }; + + int topid; + int group; +}; + +int debug; +long *kstack; +long *exitjmp; +Uproc **pcurrent; +#define current (*pcurrent) +vlong boottime; + +Udev *devtab[MAXDEV]; +Uproctab proctab; diff --git a/linux_emul_base/doc/ioctl_list.txt b/linux_emul_base/doc/ioctl_list.txt new file mode 100644 index 0000000..5caa948 --- /dev/null +++ b/linux_emul_base/doc/ioctl_list.txt @@ -0,0 +1,612 @@ +Ubuntu Manpage Repository + +Provided by: manpages-de-dev_0.5-2ubuntu1_all + +BEZEICHNUNG + + ioctl_list - Liste der ioctl-Aufrufe im Linux/i386-Kernel + +BESCHREIBUNG + + Dies ist die Ioctl-Liste 1.3.27, eine Liste von ioctl-Aufrufen im + Linux/i386-Kernel 1.3.27. Sie enthält 421 ioctls aus + /usr/include/{asm,linux}/*.h. Für jeden ioctl wird der numerische + Wert, der Name und der Argumenttyp aufgelistet. + + Ein Argumenttyp const struct foo * bedeutet, dass das Argument Eingabe + für den Kernel ist. struct foo * bedeutet, der Kernel gibt das Argu‐ + ment aus. Wenn der Kernel das Argument für Ein- und Ausgabe benutzt, + wird dies durch // I-O markiert. + + Einige ioctls benötigen mehr Argumente oder geben mehr Werte zurück als + eine einzige Struktur. Diese werden durch // MORE markiert und weiter + dokumentiert in einem separaten Abschnitt. + + Diese Liste ist nicht vollständig. Sie enthält nicht: + + Ioctls, die intern im Kernel definiert sind (scsi_ioctl.h). + + Ioctls, die in Modulen definiert sind, die separat vom Kernel + verbreitet werden. + + Und natürlich hat die Liste Fehler und Auslassungen. + + Bitte wenden Sie sich wegen Änderungen und Kommentaren an + . Ich bin besonders interessiert an Modulen, + die ihre eigenen ioctls definieren. Wenn Sie solch ein Modul kennen, + teilen es Sie mir bitte mit, damit ich es mir per ftp besorgen kann, + und ich berücksichtige seine ioctls in der nächsten Ausgabe dieser + Liste. + + Bitte wenden Sie sich wegen der Übersetzung in’s Deutsche nicht an + . ;-) + +Haupttabelle + + // + 0x00008901 FIOSETOWN const int * + 0x00008902 SIOCSPGRP const int * + 0x00008903 FIOGETOWN int * + 0x00008904 SIOCGPGRP int * + 0x00008905 SIOCATMARK int * + 0x00008906 SIOCGSTAMP timeval * + + // + 0x00005401 TCGETS struct termios * + 0x00005402 TCSETS const struct termios * + 0x00005403 TCSETSW const struct termios * + 0x00005404 TCSETSF const struct termios * + 0x00005405 TCGETA struct termio * + 0x00005406 TCSETA const struct termio * + 0x00005407 TCSETAW const struct termio * + 0x00005408 TCSETAF const struct termio * + 0x00005409 TCSBRK int + 0x0000540A TCXONC int + 0x0000540B TCFLSH int + 0x0000540C TIOCEXCL void + 0x0000540D TIOCNXCL void + 0x0000540E TIOCSCTTY int + 0x0000540F TIOCGPGRP pid_t * + 0x00005410 TIOCSPGRP const pid_t * + 0x00005411 TIOCOUTQ int * + 0x00005412 TIOCSTI const char * + 0x00005413 TIOCGWINSZ const struct winsize * + 0x00005414 TIOCSWINSZ struct winsize * + 0x00005415 TIOCMGET int * + 0x00005416 TIOCMBIS const int * + 0x00005417 TIOCMBIC const int * + 0x00005418 TIOCMSET const int * + 0x00005419 TIOCGSOFTCAR int * + 0x0000541A TIOCSSOFTCAR const int * + 0x0000541B FIONREAD int * + 0x0000541B TIOCINQ int * + 0x0000541C TIOCLINUX const char * // MORE + 0x0000541D TIOCCONS void + 0x0000541E TIOCGSERIAL struct serial_struct * + 0x0000541F TIOCSSERIAL const struct serial_struct * + 0x00005420 TIOCPKT const int * + 0x00005421 FIONBIO const int * + 0x00005422 TIOCNOTTY void + 0x00005423 TIOCSETD const int * + 0x00005424 TIOCGETD int * + 0x00005425 TCSBRKP int + 0x00005426 TIOCTTYGSTRUCT struct tty_struct * + 0x00005450 FIONCLEX void + 0x00005451 FIOCLEX void + 0x00005452 FIOASYNC const int * + 0x00005453 TIOCSERCONFIG void + 0x00005454 TIOCSERGWILD int * + 0x00005455 TIOCSERSWILD const int * + 0x00005456 TIOCGLCKTRMIOS struct termios * + 0x00005457 TIOCSLCKTRMIOS const struct temios * + 0x00005458 TIOCSERGSTRUCT struct async_struct * + 0x00005459 TIOCSERGETLSR int * + 0x0000545A TIOCSERGETMULTI struct serial_multiport_struct * + 0x0000545B TIOCSERSETMULTI const struct serial_multiport_struct * + + // + 0x000089E0 SIOCAX25GETUID const struct sockaddr_ax25 * + 0x000089E1 SIOCAX25ADDUID const struct sockaddr_ax25 * + 0x000089E2 SIOCAX25DELUID const struct sockaddr_ax25 * + 0x000089E3 SIOCAX25NOUID const int * + 0x000089E4 SIOCAX25DIGCTL const int * + 0x000089E5 SIOCAX25GETPARMS struct ax25_parms_struct * // I-O + 0x000089E6 SIOCAX25SETPARMS const struct ax25_parms-struct * + + // + 0x00007314 STL_BINTR void + 0x00007315 STL_BSTART void + 0x00007316 STL_BSTOP void + 0x00007317 STL_BRESET void + + // + 0x00005301 CDROMPAUSE void + 0x00005302 CDROMRESUME void + 0x00005303 CDROMPLAYMSF const struct cdrom_msf * + 0x00005304 CDROMPLAYTRKIND const struct cdrom_ti * + 0x00005305 CDROMREADTOCHDR struct cdrom_tochdr * + 0x00005306 CDROMREADTOCENTRY struct cdrom_tocentry * // I-O + 0x00005307 CDROMSTOP void + 0x00005308 CDROMSTART void + 0x00005309 CDROMEJECT void + 0x0000530A CDROMVOLCTRL const struct cdrom_volctrl * + 0x0000530B CDROMSUBCHNL struct cdrom_subchnl * // I-O + 0x0000530C CDROMREADMODE2 const struct cdrom_msf * // MORE + 0x0000530D CDROMREADMODE1 const struct cdrom_msf * // MORE + 0x0000530E CDROMREADAUDIO const struct cdrom_read_audio * // MORE + 0x0000530F CDROMEJECT_SW int + 0x00005310 CDROMMULTISESSION struct cdrom_multisession * // I-O + 0x00005311 CDROM_GET_UPC struct { char [8]; } * + 0x00005312 CDROMRESET void + 0x00005313 CDROMVOLREAD struct cdrom_volctrl * + 0x00005314 CDROMREADRAW const struct cdrom_msf * // MORE + 0x00005315 CDROMREADCOOKED const struct cdrom_msf * // MORE + 0x00005316 CDROMSEEK const struct cdrom_msf * + + // + 0x00002000 CM206CTL_GET_STAT int + 0x00002001 CM206CTL_GET_LAST_STAT int + + // + 0x00435901 CYGETMON struct cyclades_monitor * + 0x00435902 CYGETTHRESH int * + 0x00435903 CYSETTHRESH int + 0x00435904 CYGETDEFTHRESH int * + 0x00435905 CYSETDEFTHRESH int + 0x00435906 CYGETTIMEOUT int * + 0x00435907 CYSETTIMEOUT int + 0x00435908 CYGETDEFTIMEOUT int * + 0x00435909 CYSETDEFTIMEOUT int + + // + 0x80046601 EXT2_IOC_GETFLAGS int * + 0x40046602 EXT2_IOC_SETFLAGS const int * + 0x80047601 EXT2_IOC_GETVERSION int * + 0x40047602 EXT2_IOC_SETVERSION const int * + + // + 0x00000000 FDCLRPRM void + 0x00000001 FDSETPRM const struct floppy_struct * + 0x00000002 FDDEFPRM const struct floppy_struct * + 0x00000003 FDGETPRM struct floppy_struct * + 0x00000004 FDMSGON void + 0x00000005 FDMSGOFF void + 0x00000006 FDFMTBEG void + 0x00000007 FDFMTTRK const struct format_descr * + 0x00000008 FDFMTEND void + 0x0000000A FDSETEMSGTRESH int + 0x0000000B FDFLUSH void + 0x0000000C FDSETMAXERRS const struct floppy_max_errors * + 0x0000000E FDGETMAXERRS struct floppy_max_errors * + 0x00000010 FDGETDRVTYP struct { char [16]; } * + 0x00000014 FDSETDRVPRM const struct floppy_drive_params * + 0x00000015 FDGETDRVPRM struct floppy_drive_params * + 0x00000016 FDGETDRVSTAT struct floppy_drive_struct * + 0x00000017 FDPOLLDRVSTAT struct floppy_drive_struct * + 0x00000018 FDRESET int + 0x00000019 FDGETFDCSTAT struct floppy_fdc_state * + 0x0000001B FDWERRORCLR void + 0x0000001C FDWERRORGET struct floppy_write_errors * + 0x0000001E FDRAWCMD struct floppy_raw_cmd * // MORE // I-O + 0x00000028 FDTWADDLE void + + // + 0x0000125D BLKROSET const int * + 0x0000125E BLKROGET int * + 0x0000125F BLKRRPART void + 0x00001260 BLKGETSIZE int * + 0x00001261 BLKFLSBUF void + 0x00001262 BLKRASET int + 0x00001263 BLKRAGET int * + 0x00000001 FIBMAP int * // I-O + 0x00000002 FIGETBSZ int * + + // + 0x00000301 HDIO_GETGEO struct hd_geometry * + 0x00000302 HDIO_GET_UNMASKINTR int * + 0x00000304 HDIO_GET_MULTCOUNT int * + 0x00000307 HDIO_GET_IDENTITY struct hd_driveid * + 0x00000308 HDIO_GET_KEEPSETTINGS int * + 0x00000309 HDIO_GET_CHIPSET int * + 0x0000030A HDIO_GET_NOWERR int * + 0x0000030B HDIO_GET_DMA int * + 0x0000031F HDIO_DRIVE_CMD int * // I-O + 0x00000321 HDIO_SET_MULTCOUNT int + 0x00000322 HDIO_SET_UNMASKINTR int + 0x00000323 HDIO_SET_KEEPSETTINGS int + 0x00000324 HDIO_SET_CHIPSET int + 0x00000325 HDIO_SET_NOWERR int + 0x00000326 HDIO_SET_DMA int + + // + 0x000089F0 EQL_ENSLAVE struct ifreq * // MORE // I-O + 0x000089F1 EQL_EMANCIPATE struct ifreq * // MORE // I-O + 0x000089F2 EQL_GETSLAVECFG struct ifreq * // MORE // I-O + 0x000089F3 EQL_SETSLAVECFG struct ifreq * // MORE // I-O + 0x000089F4 EQL_GETMASTRCFG struct ifreq * // MORE // I-O + 0x000089F5 EQL_SETMASTRCFG struct ifreq * // MORE // I-O + + // + 0x000089F0 SIOCDEVPLIP struct ifreq * // I-O + + // + 0x00005490 PPPIOCGFLAGS int * + 0x00005491 PPPIOCSFLAGS const int * + 0x00005492 PPPIOCGASYNCMAP int * + 0x00005493 PPPIOCSASYNCMAP const int * + 0x00005494 PPPIOCGUNIT int * + 0x00005495 PPPIOCSINPSIG const int * + 0x00005497 PPPIOCSDEBUG const int * + 0x00005498 PPPIOCGDEBUG int * + 0x00005499 PPPIOCGSTAT struct ppp_stats * + 0x0000549A PPPIOCGTIME struct ppp_ddinfo * + 0x0000549B PPPIOCGXASYNCMAP struct { int [8]; } * + 0x0000549C PPPIOCSXASYNCMAP const struct { int [8]; } * + 0x0000549D PPPIOCSMRU const int * + 0x0000549E PPPIOCRASYNCMAP const int * + 0x0000549F PPPIOCSMAXCID const int * + + // + 0x000089E0 SIOCAIPXITFCRT const char * + 0x000089E1 SIOCAIPXPRISLT const char * + 0x000089E2 SIOCIPXCFGDATA struct ipx_config_data * + + // + 0x00004B60 GIO_FONT struct { char [8192]; } * + 0x00004B61 PIO_FONT const struct { char [8192]; } * + 0x00004B6B GIO_FONTX struct console_font_desc * // MORE I-O + 0x00004B6C PIO_FONTX const struct console_font_desc * //MORE + 0x00004B70 GIO_CMAP struct { char [48]; } * + 0x00004B71 PIO_CMAP const struct { char [48]; } + 0x00004B2F KIOCSOUND int + 0x00004B30 KDMKTONE int + 0x00004B31 KDGETLED char * + 0x00004B32 KDSETLED int + 0x00004B33 KDGKBTYPE char * + 0x00004B34 KDADDIO int // MORE + 0x00004B35 KDDELIO int // MORE + 0x00004B36 KDENABIO void // MORE + 0x00004B37 KDDISABIO void // MORE + 0x00004B3A KDSETMODE int + 0x00004B3B KDGETMODE int * + 0x00004B3C KDMAPDISP void // MORE + 0x00004B3D KDUNMAPDISP void // MORE + 0x00004B40 GIO_SCRNMAP struct { char [E_TABSZ]; } * + 0x00004B41 PIO_SCRNMAP const struct { char [E_TABSZ]; } * + 0x00004B69 GIO_UNISCRNMAP struct { short [E_TABSZ]; } * + 0x00004B6A PIO_UNISCRNMAP const struct { short [E_TABSZ]; } * + 0x00004B66 GIO_UNIMAP struct unimapdesc * // MORE // I-O + 0x00004B67 PIO_UNIMAP const struct unimapdesc * // MORE + 0x00004B68 PIO_UNIMAPCLR const struct unimapinit * + 0x00004B44 KDGKBMODE int * + 0x00004B45 KDSKBMODE int + 0x00004B62 KDGKBMETA int * + 0x00004B63 KDSKBMETA int + 0x00004B64 KDGKBLED int * + 0x00004B65 KDSKBLED int + 0x00004B46 KDGKBENT struct kbentry * // I-O + 0x00004B47 KDSKBENT const struct kbentry * + 0x00004B48 KDGKBSENT struct kbsentry * // I-O + 0x00004B49 KDSKBSENT const struct kbsentry * + 0x00004B4A KDGKBDIACR struct kbdiacrs * + 0x00004B4B KDSKBDIACR const struct kbdiacrs * + 0x00004B4C KDGETKEYCODE struct kbkeycode * // I-O + 0x00004B4D KDSETKEYCODE const struct kbkeycode * + 0x00004B4E KDSIGACCEPT int + + // + 0x00000601 LPCHAR int + 0x00000602 LPTIME int + 0x00000604 LPABORT int + 0x00000605 LPSETIRQ int + 0x00000606 LPGETIRQ int * + 0x00000608 LPWAIT int + 0x00000609 LPCAREFUL int + 0x0000060A LPABORTOPEN int + 0x0000060B LPGETSTATUS int * + 0x0000060C LPRESET void + 0x0000060D LPGETSTATS struct lp_stats * + + // + 0x000089E0 SIOCGETVIFCNT struct sioc_vif_req * // I-O + 0x000089E1 SIOCGETSGCNT struct sioc_sg_req * // I-O + + // + 0x40086D01 MTIOCTOP const struct mtop * + 0x801C6D02 MTIOCGET struct mtget * + 0x80046D03 MTIOCPOS struct mtpos * + 0x80206D04 MTIOCGETCONFIG struct mtconfiginfo * + 0x40206D05 MTIOCSETCONFIG const struct mtconfiginfo * + + // + 0x000089E0 SIOCNRGETPARMS struct nr_parms_struct * // I-O + 0x000089E1 SIOCNRSETPARMS const struct nr_parms_struct * + 0x000089E2 SIOCNRDECOBS void + 0x000089E3 SIOCNRRTCTL const int * + + // + 0x00009000 DDIOCSDBG const int * + 0x00005382 CDROMAUDIOBUFSIZ int + + // + 0x00005470 TIOCSCCINI void + 0x00005471 TIOCCHANINI const struct scc_modem * + 0x00005472 TIOCGKISS struct ioctl_command * // I-O + 0x00005473 TIOCSKISS const struct ioctl_command * + 0x00005474 TIOCSCCSTAT struct scc_stat * + + // + 0x00005382 SCSI_IOCTL_GET_IDLUN struct { int [2]; } * + 0x00005383 SCSI_IOCTL_TAGGED_ENABLE void + 0x00005384 SCSI_IOCTL_TAGGED_DISABLE void + 0x00005385 SCSI_IOCTL_PROBE_HOST const int * // MORE + + // + 0x80027501 SMB_IOC_GETMOUNTUID uid_t * + + // + 0x0000890B SIOCADDRT const struct rtentry * // MORE + 0x0000890C SIOCDELRT const struct rtentry * // MORE + 0x00008910 SIOCGIFNAME char [] + 0x00008911 SIOCSIFLINK void + 0x00008912 SIOCGIFCONF struct ifconf * // MORE // I-O + 0x00008913 SIOCGIFFLAGS struct ifreq * // I-O + 0x00008914 SIOCSIFFLAGS const struct ifreq * + 0x00008915 SIOCGIFADDR struct ifreq * // I-O + 0x00008916 SIOCSIFADDR const struct ifreq * + 0x00008917 SIOCGIFDSTADDR struct ifreq * // I-O + 0x00008918 SIOCSIFDSTADDR const struct ifreq * + 0x00008919 SIOCGIFBRDADDR struct ifreq * // I-O + 0x0000891A SIOCSIFBRDADDR const struct ifreq * + 0x0000891B SIOCGIFNETMASK struct ifreq * // I-O + 0x0000891C SIOCSIFNETMASK const struct ifreq * + 0x0000891D SIOCGIFMETRIC struct ifreq * // I-O + 0x0000891E SIOCSIFMETRIC const struct ifreq * + 0x0000891F SIOCGIFMEM struct ifreq * // I-O + 0x00008920 SIOCSIFMEM const struct ifreq * + 0x00008921 SIOCGIFMTU struct ifreq * // I-O + 0x00008922 SIOCSIFMTU const struct ifreq * + 0x00008923 OLD_SIOCGIFHWADDR struct ifreq * // I-O + 0x00008924 SIOCSIFHWADDR const struct ifreq * // MORE + 0x00008925 SIOCGIFENCAP int * + 0x00008926 SIOCSIFENCAP const int * + 0x00008927 SIOCGIFHWADDR struct ifreq * // I-O + 0x00008929 SIOCGIFSLAVE void + 0x00008930 SIOCSIFSLAVE void + 0x00008931 SIOCADDMULTI const struct ifreq * + 0x00008932 SIOCDELMULTI const struct ifreq * + 0x00008940 SIOCADDRTOLD void + 0x00008941 SIOCDELRTOLD void + 0x00008950 SIOCDARP const struct arpreq * + 0x00008951 SIOCGARP struct arpreq * // I-O + 0x00008952 SIOCSARP const struct arpreq * + 0x00008960 SIOCDRARP const struct arpreq * + 0x00008961 SIOCGRARP struct arpreq * // I-O + 0x00008962 SIOCSRARP const struct arpreq * + 0x00008970 SIOCGIFMAP struct ifreq * // I-O + 0x00008971 SIOCSIFMAP const struct ifreq * + + // + 0x00005100 SNDCTL_SEQ_RESET void + 0x00005101 SNDCTL_SEQ_SYNC void + 0xC08C5102 SNDCTL_SYNTH_INFO struct synth_info * // I-O + 0xC0045103 SNDCTL_SEQ_CTRLRATE int * // I-O + 0x80045104 SNDCTL_SEQ_GETOUTCOUNT int * + 0x80045105 SNDCTL_SEQ_GETINCOUNT int * + 0x40045106 SNDCTL_SEQ_PERCMODE void + 0x40285107 SNDCTL_FM_LOAD_INSTR const struct sbi_instrument * + 0x40045108 SNDCTL_SEQ_TESTMIDI const int * + 0x40045109 SNDCTL_SEQ_RESETSAMPLES const int * + 0x8004510A SNDCTL_SEQ_NRSYNTHS int * + 0x8004510B SNDCTL_SEQ_NRMIDIS int * + 0xC074510C SNDCTL_MIDI_INFO struct midi_info * // I-O + 0x4004510D SNDCTL_SEQ_THRESHOLD const int * + 0xC004510E SNDCTL_SYNTH_MEMAVL int * // I-O + 0x4004510F SNDCTL_FM_4OP_ENABLE const int * + 0xCFB85110 SNDCTL_PMGR_ACCESS struct patmgr_info * // I-O + 0x00005111 SNDCTL_SEQ_PANIC void + 0x40085112 SNDCTL_SEQ_OUTOFBAND const struct seq_event_rec * + 0xC0045401 SNDCTL_TMR_TIMEBASE int * // I-O + 0x00005402 SNDCTL_TMR_START void + 0x00005403 SNDCTL_TMR_STOP void + 0x00005404 SNDCTL_TMR_CONTINUE void + 0xC0045405 SNDCTL_TMR_TEMPO int * // I-O + 0xC0045406 SNDCTL_TMR_SOURCE int * // I-O + 0x40045407 SNDCTL_TMR_METRONOME const int * + 0x40045408 SNDCTL_TMR_SELECT int * // I-O + 0xCFB85001 SNDCTL_PMGR_IFACE struct patmgr_info * // I-O + 0xC0046D00 SNDCTL_MIDI_PRETIME int * // I-O + 0xC0046D01 SNDCTL_MIDI_MPUMODE const int * + 0xC0216D02 SNDCTL_MIDI_MPUCMD struct mpu_command_rec * // I-O + 0x00005000 SNDCTL_DSP_RESET void + 0x00005001 SNDCTL_DSP_SYNC void + 0xC0045002 SNDCTL_DSP_SPEED int * // I-O + 0xC0045003 SNDCTL_DSP_STEREO int * // I-O + 0xC0045004 SNDCTL_DSP_GETBLKSIZE int * // I-O + 0xC0045006 SOUND_PCM_WRITE_CHANNELS int * // I-O + 0xC0045007 SOUND_PCM_WRITE_FILTER int * // I-O + 0x00005008 SNDCTL_DSP_POST void + 0xC0045009 SNDCTL_DSP_SUBDIVIDE int * // I-O + 0xC004500A SNDCTL_DSP_SETFRAGMENT int * // I-O + 0x8004500B SNDCTL_DSP_GETFMTS int * + 0xC0045005 SNDCTL_DSP_SETFMT int * // I-O + 0x800C500C SNDCTL_DSP_GETOSPACE struct audio_buf_info * + 0x800C500D SNDCTL_DSP_GETISPACE struct audio_buf_info * + 0x0000500E SNDCTL_DSP_NONBLOCK void + 0x80045002 SOUND_PCM_READ_RATE int * + 0x80045006 SOUND_PCM_READ_CHANNELS int * + 0x80045005 SOUND_PCM_READ_BITS int * + 0x80045007 SOUND_PCM_READ_FILTER int * + 0x00004300 SNDCTL_COPR_RESET void + 0xCFB04301 SNDCTL_COPR_LOAD const struct copr_buffer * + 0xC0144302 SNDCTL_COPR_RDATA struct copr_debug_buf * // I-O + 0xC0144303 SNDCTL_COPR_RCODE struct copr_debug_buf * // I-O + 0x40144304 SNDCTL_COPR_WDATA const struct copr_debug_buf * + 0x40144305 SNDCTL_COPR_WCODE const struct copr_debug_buf * + 0xC0144306 SNDCTL_COPR_RUN struct copr_debug_buf * // I-O + 0xC0144307 SNDCTL_COPR_HALT struct copr_debug_buf * // I-O + 0x4FA44308 SNDCTL_COPR_SENDMSG const struct copr_msg * + 0x8FA44309 SNDCTL_COPR_RCVMSG struct copr_msg * + 0x80044D00 SOUND_MIXER_READ_VOLUME int * + 0x80044D01 SOUND_MIXER_READ_BASS int * + 0x80044D02 SOUND_MIXER_READ_TREBLE int * + 0x80044D03 SOUND_MIXER_READ_SYNTH int * + 0x80044D04 SOUND_MIXER_READ_PCM int * + 0x80044D05 SOUND_MIXER_READ_SPEAKER int * + 0x80044D06 SOUND_MIXER_READ_LINE int * + 0x80044D07 SOUND_MIXER_READ_MIC int * + 0x80044D08 SOUND_MIXER_READ_CD int * + 0x80044D09 SOUND_MIXER_READ_IMIX int * + 0x80044D0A SOUND_MIXER_READ_ALTPCM int * + 0x80044D0B SOUND_MIXER_READ_RECLEV int * + 0x80044D0C SOUND_MIXER_READ_IGAIN int * + 0x80044D0D SOUND_MIXER_READ_OGAIN int * + 0x80044D0E SOUND_MIXER_READ_LINE1 int * + 0x80044D0F SOUND_MIXER_READ_LINE2 int * + 0x80044D10 SOUND_MIXER_READ_LINE3 int * + 0x80044D1C SOUND_MIXER_READ_MUTE int * + 0x80044D1D SOUND_MIXER_READ_ENHANCE int * + 0x80044D1E SOUND_MIXER_READ_LOUD int * + 0x80044DFF SOUND_MIXER_READ_RECSRC int * + 0x80044DFE SOUND_MIXER_READ_DEVMASK int * + 0x80044DFD SOUND_MIXER_READ_RECMASK int * + 0x80044DFB SOUND_MIXER_READ_STEREODEVS int * + 0x80044DFC SOUND_MIXER_READ_CAPS int * + 0xC0044D00 SOUND_MIXER_WRITE_VOLUME int * // I-O + 0xC0044D01 SOUND_MIXER_WRITE_BASS int * // I-O + 0xC0044D02 SOUND_MIXER_WRITE_TREBLE int * // I-O + 0xC0044D03 SOUND_MIXER_WRITE_SYNTH int * // I-O + 0xC0044D04 SOUND_MIXER_WRITE_PCM int * // I-O + 0xC0044D05 SOUND_MIXER_WRITE_SPEAKER int * // I-O + 0xC0044D06 SOUND_MIXER_WRITE_LINE int * // I-O + 0xC0044D07 SOUND_MIXER_WRITE_MIC int * // I-O + 0xC0044D08 SOUND_MIXER_WRITE_CD int * // I-O + 0xC0044D09 SOUND_MIXER_WRITE_IMIX int * // I-O + 0xC0044D0A SOUND_MIXER_WRITE_ALTPCM int * // I-O + 0xC0044D0B SOUND_MIXER_WRITE_RECLEV int * // I-O + 0xC0044D0C SOUND_MIXER_WRITE_IGAIN int * // I-O + 0xC0044D0D SOUND_MIXER_WRITE_OGAIN int * // I-O + 0xC0044D0E SOUND_MIXER_WRITE_LINE1 int * // I-O + 0xC0044D0F SOUND_MIXER_WRITE_LINE2 int * // I-O + 0xC0044D10 SOUND_MIXER_WRITE_LINE3 int * // I-O + 0xC0044D1C SOUND_MIXER_WRITE_MUTE int * // I-O + 0xC0044D1D SOUND_MIXER_WRITE_ENHANCE int * // I-O + 0xC0044D1E SOUND_MIXER_WRITE_LOUD int * // I-O + 0xC0044DFF SOUND_MIXER_WRITE_RECSRC int * // I-O + + // + 0x000004D2 UMSDOS_READDIR_DOS struct umsdos_ioctl * // I-O + 0x000004D3 UMSDOS_UNLINK_DOS const struct umsdos_ioctl * + 0x000004D4 UMSDOS_RMDIR_DOS const struct umsdos_ioctl * + 0x000004D5 UMSDOS_STAT_DOS struct umsdos_ioctl * // I-O + 0x000004D6 UMSDOS_CREAT_EMD const struct umsdos_ioctl * + 0x000004D7 UMSDOS_UNLINK_EMD const struct umsdos_ioctl * + 0x000004D8 UMSDOS_READDIR_EMD struct umsdos_ioctl * // I-O + 0x000004D9 UMSDOS_GETVERSION struct umsdos_ioctl * + 0x000004DA UMSDOS_INIT_EMD void + 0x000004DB UMSDOS_DOS_SETUP const struct umsdos_ioctl * + 0x000004DC UMSDOS_RENAME_DOS const struct umsdos_ioctl * + + // + 0x00005600 VT_OPENQRY int * + 0x00005601 VT_GETMODE struct vt_mode * + 0x00005602 VT_SETMODE const struct vt_mode * + 0x00005603 VT_GETSTATE struct vt_stat * + 0x00005604 VT_SENDSIG void + 0x00005605 VT_RELDISP int + 0x00005606 VT_ACTIVATE int + 0x00005607 VT_WAITACTIVE int + 0x00005608 VT_DISALLOCATE int + 0x00005609 VT_RESIZE const struct vt_sizes * + 0x0000560A VT_RESIZEX const struct vt_consize * + Einige ioctls benötigen einen Pointer auf eine Struktur, die + zusätzliche Pointer enthält. Diese sind hier in alphabetischer Reihen‐ + folge dokumentiert. + + CDROMREADAUDIO benötigt eine Eingabe-Pointer const struct + cdrom_read_audio *. Das Feld buf zeigt auf einen Ausgabepuffer der + Länge nframes * CD_FRAMESIZE_RAW. + + CDROMREADCOOKED, CDROMREADMODE1, CDROMREADMODE2 und CDROMREADRAW + benötigen einen Eingabe-Pointer const struct cdrom_msf *. Sie benutzen + denselben Pointer als Ausgabe-Pointer auf char []. Die Länge ändert + sich durch Anforderung. Bei CDROMREADMODE1 benutzen die meisten + Treiber CD_FRAMESIZE, jedoch benutzt der Optics Storage-Treiber + stattdessen OPT_BLOCKSIZE (beide haben den numerischen Wert 2048). + CDROMREADCOOKED char [CD_FRAMESIZE] + CDROMREADMODE1 char [CD_FRAMESIZE oder OPT_BLOCKSIZE] + CDROMREADMODE2 char [CD_FRAMESIZE_RAW0] + CDROMREADRAW char [CD_FRAMESIZE_RAW] + EQL_ENSLAVE, EQL_EMANCIPATE, EQL_GETSLAVECFG, EQL_SETSLAVECFG, EQL_GET + MASTERCFG und EQL_SETMASTERCFG benötigen eine struct ifreq *. Das Feld + ifr_data ist ein Pointer auf eine weitere Struktur wie folgt: + EQL_ENSLAVE const struct slaving_request * + EQL_EMANCIPATE const struct slaving_request * + EQL_GETSLAVECFG struct slave_config * // I-O + EQL_SETSLAVECFG const struct slave_config * + EQL_GETMASTERCFG struct master_config * + EQL_SETMASTERCFG const struct master_config * + FDRAWCMD benötigt eine struct floppy raw_cmd *. Wenn flags & + FD_RAW_WRITE nicht Null ist, dann zeigt data auf einen Eingabepuffer + der Länge length. Wenn flags & FD_RAW_READ nicht Null ist, dann zeigt + data auf einen Ausgabepuffer der Länge ’length’. + + GIO_FONTX und PIO_FONTX benötigen eine struct console_font_desc * + beziehungsweise eine const struct console_font_desc *. chardata zeigt + auf einen Puffer von char [charcount]. Dies ist ein Ausgabepuffer für + GIO_FONTX und ein Eingabepuffer für PIO_FONTX. + + GIO_UNIMAP und PIO_UNIMAP benötigen eine struct unimapdesc * + beziehungsweise eine const struct unimapdesc *. entries zeigt auf + einen Puffer von struct unipair [entry_ct]. Dies ist ein Ausgabepuffer + für GIO_UNIMAP und ein Eingabepuffer für PIO_UNIMAP. + + KDADDIO, KDDELIO, KDDISABIO und KDENABIO geben Zugriff frei oder sper‐ + ren Zugriff auf I/O-Ports. Sie sind nötige Alternativen zu ioperm. + + KDMAPDISP und KDUNMAPDISP geben frei oder sperren Memory-Mappings oder + Zugriff auf I/O-Ports. Sie sind nicht im Kernel implementiert. + + SCSI_IOCTL_PROBE_HOST benötigt einen Eingabe-Pointer const int *, der + eine Länge ist. Es benutzt den selben Pointer als Ausgabe-Pointer auf + einen Puffer char [] dieser Länge. + + SIOCADDRT und SIOCDELRT benötigen einen Eingabe-Pointer, dessen Typ vom + Protokoll abhängt: + Die meisten Protokolle const struct rtentry * + AX.25 const struct ax25_route * + NET/ROM const struct nr_route_struct * + SIOCGIFCONF benötigt eine struct ifconf *. Das Feld ifc_buf zeigt auf + einen Puffer der Länge ifc_len Byte, wohinein der Kernel eine Liste des + Typs struct ifreq [] schreibt. + + SIOCSIFHWADDR benötigt einen Eingabe-Pointer, dessen Typ vom Protokoll + abhängt: + Die meisten Protokolle const struct ifreq * + AX.25 const char [AX25_ADDR_LEN] + TIOCLINUX benötigt eine const char *. Es benutzt dies, um zwischen + diversen unabhängigen Fällen zu unterscheiden. In der Tabelle unten + bedeutet »N + foo« so viel wie »foo« nach einem N-byte-Block. struct + selection ist definiert in drivers/char/selection.c. + TIOCLINUX-2 1 + const struct selection * + TIOCLINUX-3 void + TIOCLINUX-4 void + TIOCLINUX-5 4 + const struct { long [8]; } * + TIOCLINUX-6 char * + TIOCLINUX-7 char * + TIOCLINUX-10 1 + const char * + + Doppelte ioctls + Diese Liste enthält keine ioctls der Gruppen SIOCDEVPRIVATE und + SIOCPROTOPRIVATE. + 0x00000001 FDSETPRM FIBMAP + 0x00000002 FDDEFPRM FIGETBSZ + 0x00005382 CDROMAUDIOBUFSIZ SCSI_IOCTL_GET_IDLUN + 0x00005402 SNDCTL_TMR_START TCSETS + 0x00005403 SNDCTL_TMR_STOP TCSETSW + 0x00005404 SNDCTL_TMR_CONTINUE TCSETSF + +======= + +Powered by the Ubuntu Manpage Repository generator +Maintained by Dustin Kirkland diff --git a/linux_emul_base/doc/linuxemu.txt b/linux_emul_base/doc/linuxemu.txt new file mode 100644 index 0000000..0c587ee --- /dev/null +++ b/linux_emul_base/doc/linuxemu.txt @@ -0,0 +1,117 @@ +SYSCALLS + +on linux/i386, the machine code puts the arguments of a syscall in the +registers AX, BX, CX, DX, DI, SI and makes a soft interrupt 0x80. + +as the plan9 kernel doesnt care about the interrupt vector 0x80 it +sends a note to the process that traped and if not handled kills it. +in a note handler, it is possible to access the machine state of the +process when the trap/interrupt happend from the ureg argument. + +in linuxemu, we install a note handler that checks if the trap was a +linux syscall and call our handler function from our systab. + +after our syscall handler returned, we move the program counter +in the machine state structure after the int 0x80 instruction and +continue execution by accepting the note as handled with a call to +noted(NCONT). + +todo automatic conversion to a plan9 function call the number of +arguments and the function name of the handler must be known. this +information is provided by the linuxcalltab input file that is feed trough +linuxcalltab.awk to build neccesary tables. + +the linux specific syscall handling and argument conversion done in +linuxcall.c only. the idea is to later add support for other syscall +personalities like bsd without having to change the handler code. + + +MEMORY + +unlike shared libraries wich are position independent, binaries have to be +loaded to a fixed address location. (elf supports position independent +programs that can be loaded everywhere, but its not used on i386) + +the emulator doesnt need to load and relocate shared libraries itself. this is +done my the runtime linker (/lib/ld-linux.so). it just needs to load +the binary and the runtime linker to ther prefered location and jump into +the entry point. then the runtime linker will parse the elf sections of the +binary and call mmap to load further shared libraries. + +the first thing we need is an implementation of mmap that allows us +to copy files to fixed addresses into memory. to do that on plan9, +segments are used. + +its is not possible to create a segment for every memory mapping +because plan9 limits the number of segments per process to a small +number. instead we create a fixed number of segments and +expand/shrink them on demand. the linux stack area is fixed size and +uses the fact thet plan9 doesnt allocate physical memory until pages +are touched. + +here are 3 segments created for a linux process: + +"private" is used for all MAP_PRIVATE mappings and can be shared if +processes run in same address space. code, data and files is mapped there. + +"shared" for shared memory mappings. + +"stack" is like "private", but lives just below the plan9 stack segment. +this is needed because glibc expands the stack down by mmap() pages +below the current stack area. we cannot use the plan9 stack segment +because that segment is copied on rfork and is never shared between +processes. + +the data structures of the emulator itself ("kernel memory") need to +be shared for all processes even if the linux process runs in its own +private address space, so the plan9 Bss and Data segments are made +shared on startup by copying the contents of the original segment into a +temporary file, segdetach() it and segattach() a new shared segments +on the same place and copy the data back in from the file. + +with this memory layout, it is possible for the linux process to damage +data structures in the emulator. but we seem to be lucky for now :) + + +USER PROCESSES (UPROCS) + +linuxemu does not switch ans schedule linux processes itself. every user +process has its own plan9 process. memory sharing semantics is translated +to rfork flags on fork/clone. + +we have a global process table of Uproc structures to track states and +resources for all user processes: + +fs: filesystem mount table +fdtab: the filedescriptor table +mem: memory mappings +signal: signal handler and queue +trace: debug trace buffer + +resources that can be shared are reference counted and get freed when +the last process referencing them exits. + + +KERNEL PROCESSES (KPROCS) + +if we needs to defer work or do asynchronous i/o it can spawn a +kernel process with kprocfork. kernel processes dont have a Uproc +structure associated and have the userspace memory segments detached +therfor cant access userspace memory. + +bufprocs and timers are implemented with kernel processes. + + +DEVICES + +ealier versions mapped linux files directly to plan9 files. this made +the implementation of ioctls, symlinks, remove on close, and +select/poll hard and also had problems with implementing fork sharing +semantics. + +current linuxemu does it all by itself. here is a global device table +of Udev structures. devices can implement all i/o related syscalls by +providing a function pointer in ther Udev. when a device has to deal +with asynchronous io on real plan9 files it uses bufprocs. + + diff --git a/linux_emul_base/doc/todo.txt b/linux_emul_base/doc/todo.txt new file mode 100644 index 0000000..a24cb3b --- /dev/null +++ b/linux_emul_base/doc/todo.txt @@ -0,0 +1,14 @@ +- AF_INET6 + i dont need that too yet + +- VDSO + we could gain quite a performance hit when we can avoid + the trapping overhead and let linux-code directly jump + in linuxemu handler. + +- dsp / mixer + implement mixer ioctls in devdsp + mmap and trigger caps for quake + +- ptrace + implement ptrace support so we can use native debugger diff --git a/linux_emul_base/dspdev.c b/linux_emul_base/dspdev.c new file mode 100644 index 0000000..822ce51 --- /dev/null +++ b/linux_emul_base/dspdev.c @@ -0,0 +1,377 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +enum { + FREQUENCY = 44100, + CHANNELS = 2, + DELAY = 100, + FRAGSIZE = 4096, +}; + +typedef struct Chan Chan; +typedef struct DSP DSP; + +struct Chan +{ + ulong phase; + int last; +}; + +struct DSP +{ + Ufile; + + int channels; /* number of channels (2 for stereo) */ + int freq; /* frequency of sound stream */ + + int rfreq; /* frequency of /dev/audio */ + + uchar *buf; /* resampling */ + ulong nbuf; + Chan chan[CHANNELS]; + + vlong time; /* time point of the last sample in device buffer */ + + ulong written; /* number of bytes written to dsp */ + ulong written2; /* same as written, will be reset on every GETOPTR ioctl */ +}; + +static int +closedsp(Ufile *file) +{ + DSP *dsp = (DSP*)file; + + trace("dsp: closedsp"); + free(dsp->buf); + close(dsp->fd); + + return 0; +} + +static int +polldsp(Ufile *, void *) +{ + return POLLOUT; +} + +static int +readdsp(Ufile *, void *, int, vlong) +{ + return 0; /* not implemented */ +} + +static int +resample(Chan *c, uchar *src, uchar *dst, int sstep, int dstep, ulong delta, ulong count) +{ + int last, val, out; + ulong phase, pos; + uchar *dp, *sp; + + dp = dst; + last = val = c->last; + phase = c->phase; + pos = phase >> 16; + while(pos < count){ + sp = src + sstep*pos; + val = sp[0] | (sp[1] << 8); + val = (val & 0x7FFF) - (val & 0x8000); + if(pos){ + sp -= sstep; + last = sp[0] | (sp[1] << 8); + last = (last & 0x7FFF) - (last & 0x8000); + } + out = last + (((val - last) * (phase & 0xFFFF)) >> 16); + dp[0] = out; + dp[1] = out >> 8; + dp += dstep; + phase += delta; + pos = phase >> 16; + } + c->last = val; + if(delta < 0x10000){ + c->phase = phase & 0xFFFF; + } else { + c->phase = phase - (count << 16); + } + return (dp - dst) / dstep; +} + +static int +convertout(DSP *dsp, uchar *buf, int len, uchar **out) +{ + int ret, ch; + ulong count, delta; + + /* no conversion required? */ + if(dsp->freq == dsp->rfreq && dsp->channels == CHANNELS){ + *out = buf; + return len; + } + + /* + * delta is the number of input samples to + * produce one output sample. scaled by 16 bit to + * get fractional part. + */ + delta = ((ulong)dsp->freq << 16) / dsp->rfreq; + count = len / (2 * dsp->channels); + + /* + * get maximum required size of output bufer. this is not exact! + * number of output samples depends on phase! + */ + ret = (((count << 16) + delta-1) / delta) * 2*CHANNELS; + if(ret > dsp->nbuf){ + free(dsp->buf); + dsp->buf = kmalloc(ret); + dsp->nbuf = ret; + } + for(ch=0; ch < CHANNELS; ch++) + ret = resample(dsp->chan + ch, + buf + 2*(ch % dsp->channels), + dsp->buf + 2*ch, + 2*dsp->channels, + 2*CHANNELS, + delta, + count); + + *out = dsp->buf; + return ret * 2*CHANNELS; +} + +static int +writedsp(Ufile *file, void *buf, int len, vlong) +{ + DSP *dsp = (DSP*)file; + vlong now; + int ret, diff; + uchar *out; + + if((ret = convertout(dsp, buf, len, &out)) <= 0) + return ret; + + if((ret = write(dsp->fd, out, ret)) < 0) + return mkerror(); + + now = nsec(); + if(dsp->time < now){ + dsp->time = now; + dsp->written = 0; + dsp->written2 = 0; + } else { + diff = (dsp->time - now) / 1000000; + if(diff > DELAY) + sleep(diff - DELAY); + } + dsp->time += ((1000000000LL) * ret / (dsp->rfreq * 2*CHANNELS)); + dsp->written += len; + dsp->written2 += len; + + return len; +} + +enum +{ + AFMT_S16_LE = 0x10, +}; + +static int +ioctldsp(Ufile *file, int cmd, void *arg) +{ + DSP *dsp = (DSP*)file; + int ret, i; + vlong now; + static int counter; + + ret = 0; + switch(cmd){ + default: + trace("dsp: unknown ioctl %lux %p", (ulong)cmd, arg); + ret = -ENOTTY; + break; + + case 0xC004500A: + trace("dsp: SNDCTL_DSP_SETFRAGMENT(%lux)", *(ulong*)arg); + break; + + case 0xC0045004: + trace("dsp: SNDCTL_DSP_GETBLKSIZE"); + *((int*)arg) = FRAGSIZE; + break; + + case 0x800c5011: + trace("dsp: SNDCTL_DSP_GETIPTR"); + ret = -EPERM; + break; + + case 0x800c5012: + trace("dsp: SNDCTL_DSP_GETOPTR"); + ((int*)arg)[0] = dsp->written; // Total # of bytes processed + ((int*)arg)[1] = dsp->written2 / FRAGSIZE; // # of fragment transitions since last time + dsp->written2 = 0; + ((int*)arg)[2] = 0; // Current DMA pointer value + break; + + case 0x8010500D: + trace("dsp: SNDCTL_DSG_GETISPACE"); + ret = -EPERM; + break; + case 0x8010500C: + trace("dsp: SNDCTL_DSP_GETOSPACE"); + i = (2 * dsp->channels) * ((dsp->freq*DELAY)/1000); + ((int*)arg)[1] = i / FRAGSIZE; // fragstot + ((int*)arg)[2] = FRAGSIZE; // fragsize + now = nsec(); + if(now < dsp->time){ + i -= ((2 * dsp->channels) * (((dsp->time - now) * (vlong)dsp->freq) / 1000000000)); + if(i < 0) + i = 0; + } + ((int*)arg)[0] = i / FRAGSIZE; // available fragment count + ((int*)arg)[3] = i; // available space in bytes + break; + + case 0x8004500B: + trace("dsp: SNDCTL_DSP_GETFMTS(%d)", *(int*)arg); + *(int*)arg = AFMT_S16_LE; + break; + + case 0x8004500F: + trace("dsp: SNDCTL_DSP_GETCAPS"); + *(int*)arg = 0x400; + break; + + case 0xC0045005: + trace("dsp: SNDCTL_DSP_SETFMT(%d)", *(int*)arg); + *(int*)arg = AFMT_S16_LE; + break; + + case 0xC0045006: + trace("dsp: SOUND_PCM_WRITE_CHANNELS(%d)", *(int*)arg); + dsp->channels = *(int*)arg; + break; + + case 0xC0045003: + trace("dsp: SNDCTL_DSP_STEREO(%d)", *(int*)arg); + dsp->channels = 2; + *(int*)arg = 1; + break; + + case 0xC0045002: + trace("dsp: SNDCTL_DSP_SPEED(%d)", *(int*)arg); + dsp->freq = *(int*)arg; + for(i=0; ichan[i].phase = 0; + dsp->chan[i].last = 0; + } + break; + + case 0x00005000: + trace("dsp: SNDCTL_DSP_RESET"); + break; + + case 0x00005001: + trace("dsp: SNDCTL_DSP_SYNC"); + break; + } + + return ret; +} + +static int +getaudiofreq(void) +{ + int ret, n, fd; + char buf[1024]; + + ret = FREQUENCY; + if((fd = open("/dev/volume", OREAD)) < 0) + return ret; + if((n = read(fd, buf, sizeof(buf)-1)) > 0){ + char *p; + + buf[n] = 0; + if(p = strstr(buf, "speed out ")) + ret = atoi(p + 10); + } + close(fd); + return ret; +} + +int opendsp(char *path, int mode, int, Ufile **pf) +{ + DSP *dsp; + int freq; + int fd; + + if(strcmp(path, "/dev/dsp")==0 || strcmp(path, "/dev/dsp0")==0){ + if((fd = open("/dev/audio", OWRITE)) < 0) + return mkerror(); + + freq = getaudiofreq(); + dsp = mallocz(sizeof(DSP), 1); + dsp->ref = 1; + dsp->mode = mode; + dsp->dev = DSPDEV; + dsp->fd = fd; + dsp->path = kstrdup(path); + dsp->rfreq = freq; + dsp->freq = freq; + dsp->channels = CHANNELS; + + *pf = dsp; + return 0; + } + + return -ENOENT; +} + +static int +fstatdsp(Ufile *f, Ustat *s) +{ + s->mode = 0666 | S_IFCHR; + s->uid = current->uid; + s->gid = current->gid; + s->ino = hashpath(f->path); + s->size = 0; + return 0; +}; + +static int +statdsp(char *path, int , Ustat *s) +{ + if(strcmp(path, "/dev/dsp")==0 || strcmp(path, "/dev/dsp0")==0){ + s->mode = 0666 | S_IFCHR; + s->uid = current->uid; + s->gid = current->gid; + s->ino = hashpath(path); + s->size = 0; + return 0; + } + + return -ENOENT; +} + +static Udev dspdev = +{ + .open = opendsp, + .read = readdsp, + .write = writedsp, + .poll = polldsp, + .close = closedsp, + .ioctl = ioctldsp, + .stat = statdsp, + .fstat = fstatdsp, +}; + +void dspdevinit(void) +{ + devtab[DSPDEV] = &dspdev; + + fsmount(&dspdev, "/dev/dsp"); + fsmount(&dspdev, "/dev/dsp0"); +} diff --git a/linux_emul_base/error.c b/linux_emul_base/error.c new file mode 100644 index 0000000..fe4cb22 --- /dev/null +++ b/linux_emul_base/error.c @@ -0,0 +1,266 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +int +Efmt(Fmt *f) +{ + static char *t[] = { + [EPERM] "EPERM", + [ENOENT] "ENOENT", + [ESRCH] "ESRCH", + [EINTR] "EINTR", + [EIO] "EIO", + [ENXIO] "ENXIO", + [E2BIG] "E2BIG", + [ENOEXEC] "ENOEXEC", + [EBADF] "EBADF", + [ECHILD] "ECHILD", + [EAGAIN] "EAGAIN", + [ENOMEM] "ENOMEM", + [EACCES] "EACCES", + [EFAULT] "EFAULT", + [ENOTBLK] "ENOTBLK", + [EBUSY] "EBUSY", + [EEXIST] "EEXIST", + [EXDEV] "EXDEV", + [ENODEV] "ENODEV", + [ENOTDIR] "ENOTDIR", + [EISDIR] "EISDIR", + [EINVAL] "EINVAL", + [ENFILE] "ENFILE", + [EMFILE] "EMFILE", + [ENOTTY] "ENOTTY", + [ETXTBSY] "ETXTBSY", + [EFBIG] "EFBIG", + [ENOSPC] "ENOSPC", + [ESPIPE] "ESPIPE", + [EROFS] "EROFS", + [EMLINK] "EMLINK", + [EPIPE] "EPIPE", + [EDOM] "EDOM", + [ERANGE] "ERANGE", + [EDEADLK] "EDEADLK", + [ENAMETOOLONG] "ENAMETOOLONG", + [ENOLCK] "ENOLCK", + [ENOSYS] "ENOSYS", + [ENOTEMPTY] "ENOTEMPTY", + [ELOOP] "ELOOP", + [ENOMSG] "ENOMSG", + [EIDRM] "EIDRM", + [ECHRNG] "ECHRNG", + [EL2NSYNC] "EL2NSYNC", + [EL3HLT] "EL3HLT", + [EL3RST] "EL3RST", + [ELNRNG] "ELNRNG", + [EUNATCH] "EUNATCH", + [ENOCSI] "ENOCSI", + [EL2HLT] "EL2HLT", + [EBADE] "EBADE", + [EBADR] "EBADR", + [EXFULL] "EXFULL", + [ENOANO] "ENOANO", + [EBADRQC] "EBADRQC", + [EBADSLT] "EBADSLT", + [EBFONT] "EBFONT", + [ENOSTR] "ENOSTR", + [ENODATA] "ENODATA", + [ETIME] "ETIME", + [ENOSR] "ENOSR", + [ENONET] "ENONET", + [ENOPKG] "ENOPKG", + [EREMOTE] "EREMOTE", + [ENOLINK] "ENOLINK", + [EADV] "EADV", + [ESRMNT] "ESRMNT", + [ECOMM] "ECOMM", + [EPROTO] "EPROTO", + [EMULTIHOP] "EMULTIHOP", + [EDOTDOT] "EDOTDOT", + [EBADMSG] "EBADMSG", + [EOVERFLOW] "EOVERFLOW", + [ENOTUNIQ] "ENOTUNIQ", + [EBADFD] "EBADFD", + [EREMCHG] "EREMCHG", + [ELIBACC] "ELIBACC", + [ELIBBAD] "ELIBBAD", + [ELIBSCN] "ELIBSCN", + [ELIBMAX] "ELIBMAX", + [ELIBEXEC] "ELIBEXEC", + [EILSEQ] "EILSEQ", + [ERESTART] "ERESTART", + [ESTRPIPE] "ESTRPIPE", + [EUSERS] "EUSERS", + [ENOTSOCK] "ENOTSOCK", + [EDESTADDRREQ] "EDESTADDRREQ", + [EMSGSIZE] "EMSGSIZE", + [EPROTOTYPE] "EPROTOTYPE", + [ENOPROTOOPT] "ENOPROTOOPT", + [EPROTONOSUPPORT] "EPROTONOSUPPORT", + [ESOCKTNOSUPPORT] "ESOCKTNOSUPPORT", + [EOPNOTSUPP] "EOPNOTSUPP", + [EPFNOSUPPORT] "EPFNOSUPPORT", + [EAFNOSUPPORT] "EAFNOSUPPORT", + [EADDRINUSE] "EADDRINUSE", + [EADDRNOTAVAIL] "EADDRNOTAVAIL", + [ENETDOWN] "ENETDOWN", + [ENETUNREACH] "ENETUNREACH", + [ENETRESET] "ENETRESET", + [ECONNABORTED] "ECONNABORTED", + [ECONNRESET] "ECONNRESET", + [ENOBUFS] "ENOBUFS", + [EISCONN] "EISCONN", + [ENOTCONN] "ENOTCONN", + [ESHUTDOWN] "ESHUTDOWN", + [ETOOMANYREFS] "ETOOMANYREFS", + [ETIMEDOUT] "ETIMEDOUT", + [ECONNREFUSED] "ECONNREFUSED", + [EHOSTDOWN] "EHOSTDOWN", + [EHOSTUNREACH] "EHOSTUNREACH", + [EALREADY] "EALREADY", + [EINPROGRESS] "EINPROGRESS", + [ESTALE] "ESTALE", + [EUCLEAN] "EUCLEAN", + [ENOTNAM] "ENOTNAM", + [ENAVAIL] "ENAVAIL", + [EISNAM] "EISNAM", + [EREMOTEIO] "EREMOTEIO", + [EDQUOT] "EDQUOT", + [ENOMEDIUM] "ENOMEDIUM", + [EMEDIUMTYPE] "EMEDIUMTYPE", + }; + + int e; + + e = va_arg(f->args, int); + if(e >= 0 || -e >= nelem(t)) + return fmtprint(f, "%d", e); + return fmtprint(f, "%d [%s]", e, t[-e]); +} + +int +mkerror(void) +{ + static struct { + int num; + char *msg; + } t[] = { + /* from /sys/src/9/port/errstr.h */ + {EINVAL, "inconsistent mount"}, + {EINVAL, "not mounted"}, + {EINVAL, "not in union"}, + {EIO, "mount rpc error"}, + {EIO, "mounted device shut down"}, + {EPERM, "mounted directory forbids creation"}, + {ENOENT, "does not exist"}, + {ENXIO, "unknown device in # filename"}, + {ENOTDIR, "not a directory"}, + {EISDIR, "file is a directory"}, + {EINVAL, "bad character in file name"}, + {EINVAL, "file name syntax"}, + {EPERM, "permission denied"}, + {EPERM, "inappropriate use of fd"}, + {EINVAL, "bad arg in system call"}, + {EBUSY, "device or object already in use"}, + {EIO, "i/o error"}, + {EIO, "read or write too large"}, + {EIO, "read or write too small"}, + {EADDRINUSE, "network port not available"}, + {ESHUTDOWN, "write to hungup stream"}, + {ESHUTDOWN, "i/o on hungup channel"}, + {EINVAL, "bad process or channel control request"}, + {EBUSY, "no free devices"}, + {ESRCH, "process exited"}, + {ECHILD, "no living children"}, + {EIO, "i/o error in demand load"}, + {ENOMEM, "virtual memory allocation failed"}, + {EBADF, "fd out of range or not open"}, + {EMFILE, "no free file descriptors"}, + {ESPIPE, "seek on a stream"}, + {ENOEXEC, "exec header invalid"}, + {ETIMEDOUT, "connection timed out"}, + {ECONNREFUSED, "connection refused"}, + {ECONNREFUSED, "connection in use"}, + {ERESTART, "interrupted"}, + {ENOMEM, "kernel allocate failed"}, + {EINVAL, "segments overlap"}, + {EIO, "i/o count too small"}, + {EINVAL, "bad attach specifier"}, + + /* from exhausted() calls in kernel */ + {ENFILE, "no free file descriptors"}, + {EBUSY, "no free mount devices"}, + {EBUSY, "no free mount rpc buffer"}, + {EBUSY, "no free segments"}, + {ENOMEM, "no free memory"}, + {ENOBUFS, "no free Blocks"}, + {EBUSY, "no free routes"}, + + /* from ken */ + {EINVAL, "attach -- bad specifier"}, + {EBADF, "unknown fid"}, + {EINVAL, "bad character in directory name"}, + {EBADF, "read/write -- on non open fid"}, + {EIO, "read/write -- count too big"}, + {EIO, "phase error -- directory entry not allocated"}, + {EIO, "phase error -- qid does not match"}, + {EACCES, "access permission denied"}, + {ENOENT, "directory entry not found"}, + {EINVAL, "open/create -- unknown mode"}, + {ENOTDIR, "walk -- in a non-directory"}, + {ENOTDIR, "create -- in a non-directory"}, + {EIO, "phase error -- cannot happen"}, + {EEXIST, "create -- file exists"}, + {EINVAL, "create -- . and .. illegal names"}, + {ENOTEMPTY, "remove -- directory not empty"}, + {EINVAL, "attach -- privileged user"}, + {EPERM, "wstat -- not owner"}, + {EPERM, "wstat -- not in group"}, + {EINVAL, "create/wstat -- bad character in file name"}, + {EBUSY, "walk -- too many (system wide)"}, + {EROFS, "file system read only"}, + {ENOSPC, "file system full"}, + {EINVAL, "read/write -- offset negative"}, + {EBUSY, "open/create -- file is locked"}, + {EBUSY, "close/read/write -- lock is broken"}, + + /* from sockets */ + {ENOTSOCK, "not a socket"}, + {EPROTONOSUPPORT, "protocol not supported"}, + {ECONNREFUSED, "connection refused"}, + {EAFNOSUPPORT, "address family not supported"}, + {ENOBUFS, "insufficient buffer space"}, + {EOPNOTSUPP, "operation not supported"}, + {EADDRINUSE, "address in use"}, + + /* other */ + {EEXIST, "file already exists"}, + {EEXIST, "is a directory"}, + {ENOTEMPTY, "directory not empty"}, + }; + + int r, i; + char msg[ERRMAX]; + + rerrstr(msg, sizeof(msg)); + + r = -EIO; + for(i=0; isyscall); + return -ENOSYS; +} diff --git a/linux_emul_base/exec.c b/linux_emul_base/exec.c new file mode 100644 index 0000000..73d4669 --- /dev/null +++ b/linux_emul_base/exec.c @@ -0,0 +1,647 @@ +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Elfhdr Elfhdr; +typedef struct Proghdr Proghdr; +typedef struct ElfEx ElfEx; + +struct Elfhdr { + uchar ident[16]; + ushort type; + ushort machine; + ulong version; + ulong entry; + ulong phoff; + ulong shoff; + ulong flags; + ushort ehsize; + ushort phentsize; + ushort phnum; + ushort shentsize; + ushort shnum; + ushort shstrndx; +}; + +struct Proghdr { + ulong type; + ulong offset; + ulong vaddr; + ulong paddr; + ulong filesz; + ulong memsz; + ulong flags; + ulong align; +}; + +struct ElfEx +{ + ulong ientry; + ulong ibase; + + ulong entry; + ulong base; + + ulong phdr; + ulong phnum; + ulong phent; +}; + +static void +padzero(ulong addr) +{ + ulong n; + + if(n = (pagealign(addr) - addr)) + memset((void*)addr, 0, n); +} + +enum { + /* file types */ + ElfTNone = 0, + ElfTReloc = 1, + ElfTExec = 2, + ElfTShared = 3, + ElfTCore = 4, + ElfTMax = 5, + + /* machine architectures */ + ElfMNone = 0, + ElfM32 = 1, + ElfMSparc = 2, + ElfM386 = 3, + ElfM68 = 4, + ElfM88 = 5, + ElfM860 = 7, + ElfMMips = 8, + ElfMMax = 9, + + /* program segment types */ + ElfPNull = 0, + ElfPLoad = 1, + ElfPDynamic = 2, + ElfPInterp = 3, + ElfPNote = 4, + ElfPShlib = 5, + ElfPPhdr = 6, + ElfPMax = 7, + + /* program segment flags */ + ElfPFX = 1, + ElfPFW = 2, + ElfPFR = 4, +}; + +static int +loadelf(char *file, ElfEx *ex, int depth) +{ + int fd; + int i, l; + int mapprot; + int mapflags; + ulong mapbase; + ulong loadaddr; + ulong bss; + + Elfhdr hdr; + Proghdr *phdr; + char *interpreter; + + interpreter = nil; + phdr = nil; + + if((fd = sys_open(file, O_RDONLY, 0)) < 0){ + werrstr("cant open %s", file); + goto errout; + } + + if(sys_read(fd, &hdr, sizeof(hdr)) != sizeof(hdr)){ + werrstr("cant read elf header"); + goto errout; + } + + if(memcmp(hdr.ident, "\x7fELF", 4)!=0){ + werrstr("no elf magic"); + goto errout; + } + + l = hdr.phnum * hdr.phentsize; + phdr = kmalloc(l); + sys_lseek(fd, hdr.phoff, 0); + if(sys_read(fd, phdr, l) != l){ + werrstr("cant read program headers"); + goto errout; + } + + loadaddr = 0; + mapbase = 0; + mapflags = MAP_PRIVATE; + if(hdr.type != ElfTShared) + mapflags |= MAP_FIXED; + + trace("loadelf(): phnum=%d", hdr.phnum); + + bss = 0; + for(i=0; itype == ElfPInterp){ + if(interpreter){ + werrstr("multiple interpeter sections"); + goto errout; + } + l = p->filesz; + + interpreter = kmalloc(l+1); + sys_lseek(fd, p->offset, 0); + if(sys_read(fd, interpreter, l)!=l){ + werrstr("cant read interpreter section"); + goto errout; + } + interpreter[l] = '\0'; + } + + if(p->type == ElfPLoad){ + ulong a; + int diff; + + trace("loadelf(): phdr %d: vaddr=%lux memsz=%lux filesz=%lux offset=%lux flags=%lux", + i, + p->vaddr, + p->memsz, + p->filesz, + p->offset, + p->flags); + + mapprot = 0; + if(p->flags & ElfPFR) + mapprot |= PROT_READ; + if(p->flags & ElfPFW) + mapprot |= PROT_WRITE; + if(p->flags & ElfPFX) + mapprot |= PROT_EXEC; + + if(hdr.entry >= p->vaddr && hdr.entry < p->vaddr + p->memsz) + mapprot |= PROT_EXEC; + + diff = p->vaddr - (p->vaddr & ~(PAGESIZE-1)); + + /* have to call mapdata() before we do the first mmap */ + if(loadaddr == 0 && depth == 0){ + if(hdr.type == ElfTShared){ + mapbase = pagealign((ulong)end + 0x4000000); + mapflags |= MAP_FIXED; + } + mapdata((mapbase + p->vaddr) - diff); + } + + a = sys_mmap( + (mapbase + p->vaddr) - diff, + p->filesz + diff, + mapprot, + mapflags, + fd, + (p->offset - diff)/PAGESIZE); + + if(((int)a < 0) && ((int)a > -EMAX)){ + werrstr("mmap failed: %E", (int)a); + goto errout; + } + if(loadaddr == 0) + loadaddr = a; + if(hdr.type == ElfTShared && mapbase == 0){ + mapbase = a + diff; + mapflags |= MAP_FIXED; + } + if(mapprot & PROT_WRITE) + padzero(mapbase + p->vaddr + p->filesz); + if(depth == 0) + if(mapbase + p->vaddr + p->memsz > bss) + bss = mapbase + p->vaddr + p->memsz; + } else { + trace("loadelf(): phdr %d: type=%lux", i, p->type); + } + } + + ex->base = loadaddr; + ex->entry = hdr.entry + ((hdr.type == ElfTShared) ? loadaddr : 0); + + ex->phdr = loadaddr + hdr.phoff; + ex->phent = hdr.phentsize; + ex->phnum = hdr.phnum; + + if(depth == 0){ + sys_brk(pagealign(bss)); + + current->codestart = loadaddr; + current->codeend = bss; + } + + if(interpreter){ + ElfEx interpex; + + if(loadelf(interpreter, &interpex, depth+1) < 0){ + werrstr("cant load interpreter: %r"); + goto errout; + } + free(interpreter); + + ex->ientry = interpex.entry; + ex->ibase = interpex.base; + } else { + ex->ientry = ex->entry; + ex->ibase = 0; /* no interpreter */ + } + + sys_close(fd); + free(phdr); + return 0; + +errout: + if(fd >= 0) + sys_close(fd); + free(interpreter); + free(phdr); + return -1; +} + + +enum { + AT_NULL, + AT_IGNORE, + AT_EXECFD, + AT_PHDR, + AT_PHENT, + AT_PHNUM, + AT_PAGESZ, + AT_BASE, + AT_FLAGS, + AT_ENTRY, + AT_NOTELF, + AT_UID, + AT_EUID, + AT_GID, + AT_EGID, + AT_PLATFORM, + AT_HWCAP, + AT_CLKTCK, + AT_SECURE = 23, + + AT_SYSINFO = 32, + AT_SYSINFO_EHDR = 33, +}; + +static void* +setupstack(ElfEx *ex, char *argv[], char *envp[]) +{ + int envc; + int argc; + + char **dargv; + char **denv; + + ulong *stack; + ulong *p; + char *x; + int i, n; + + /* + * calculate the size we need on stack + */ + argc=0; + while(argv && argv[argc]) argc++; + + envc=0; + while(envp && envp[envc]) envc++; + + n = 0; + n += sizeof(ulong); // argc + n += (argc+1)*sizeof(char*); // argv + nil + n += (envc+1)*sizeof(char*); // envp + nil + n += 16*(2*sizeof(ulong)); // aux + + for(i=0; i -EMAX)){ + werrstr("mapstack failed: %E", (int)stack); + return nil; + } + stack = (ulong*)(((ulong)stack - n) & ~7); + + current->stackstart = (ulong)stack; + + p = stack; + + *p++ = argc; + + dargv = (char**)p; + p += (argc + 1); + + denv = (char**)p; + p += (envc + 1); + +#define AUXENT(k, v) {p[0]=k; p[1]=v; p+=2;} + AUXENT(AT_PAGESZ, PAGESIZE); + AUXENT(AT_CLKTCK, HZ); + AUXENT(AT_PHDR, ex->phdr); + AUXENT(AT_PHENT, ex->phent); + AUXENT(AT_PHNUM, ex->phnum); + AUXENT(AT_BASE, ex->ibase); + AUXENT(AT_FLAGS, 0); + AUXENT(AT_ENTRY, ex->entry); + AUXENT(AT_UID, current->uid); + AUXENT(AT_EUID, current->uid); + AUXENT(AT_GID, current->gid); + AUXENT(AT_EGID, current->gid); + AUXENT(AT_NULL, 0); + AUXENT(AT_NULL, 0); + AUXENT(AT_NULL, 0); + AUXENT(AT_NULL, 0); +#undef AUXENT + + x = (char*)p; + + for(i=0; icomm); + current->comm = buf; + current->ncomm = p - buf; +} + +static void +clinote(struct Ureg *ureg) +{ + jmp_buf jmp; + ulong pc; + ulong sp; + ulong ax; + + pc = ureg->pc; + sp = ureg->sp; + ax = ureg->ax; + + if(!setjmp(jmp)) + notejmp(ureg, jmp, 1); + + ureg->pc = pc; + ureg->sp = sp; + ureg->ax = ax; +} + +struct kexecveargs +{ + char *name; + char **argv; + char **envp; +}; + +#pragma profile off + +static int +kexecve(void *arg) +{ + struct kexecveargs *args; + Ufile *f; + ElfEx ex; + Ureg u; + int r, n; + char *b, *p, *e, *x, **a; + void *stack; + char *name, *exe; + char **argv; + char **envp; + int phase; + + args = arg; + name = args->name; + argv = args->argv; + envp = args->envp; + + phase = 0; + n = 8192; + b = kmalloc(n); + p = b; + e = b + n; +again: + if(r = sys_access(name, 05)){ + if(r > 0) + r = -EACCES; + goto errout; + } + if((r = sys_open(name, O_RDONLY, 0)) < 0) + goto errout; + exe = "/dev/null"; + if(f = fdgetfile(r)){ + if(f->path != nil){ + strncpy(p, f->path, e-p); + p += strlen(exe = p)+1; + } + putfile(f); + } + n = sys_read(r, p, (e-p)-1); + sys_close(r); + + r = -ENOEXEC; + if(n < 4) + goto errout; + + if(memcmp(p, "#!", 2) == 0){ + p[n] = 0; + + r = -ENAMETOOLONG; + if((x = strchr(p, '\n')) == nil) + goto errout; + *x = 0; + + a = (char**)&x[1]; + n = (e - (char*)a) / sizeof(a[0]); + if(n < 2) + goto errout; + n = getfields(&p[2], a, n, 1, "\t\r\n "); + if(n < 1) + goto errout; + r = -E2BIG; + if(&a[n+1] >= (char**)e) + goto errout; + a[n++] = name; + if(argv != nil){ + argv++; + while(*argv){ + if(&a[n+1] >= (char**)e) + goto errout; + a[n++] = *argv++; + } + } + a[n++] = 0; + p = (char*)&a[n]; + if(e - p < 4) + goto errout; + argv = a; + name = argv[0]; + + goto again; + } + + if(memcmp(p, "\x7fELF", 4)!=0) + goto errout; + + /* + * the contents on envp[] or argv[] maybe stored in b[], stack or bss of the calling linux + * process that is destroyed on free(b) and exitmem()... so we need to temporary + * copy them. + */ + r = -ENOMEM; + name = kstrdup(name); + phase++; + if(argv) + argv = copystrings(argv); + phase++; + if(envp) + envp = copystrings(envp); + phase++; + + /* get out of the note before we destroy user stack */ + if(current->innote){ + clinote(current->ureg); + current->innote = 0; + } + + /* this is the point of no return! */ + qlock(&proctab); + zapthreads(); + exitmem(); + exitsignal(); + + initmem(); + initsignal(); + inittls(); + qunlock(&proctab); + + closexfds(); + + setcomm(exe, name, argv); + + if(loadelf(name, &ex, 0) < 0){ + trace("kexecve(): loadelf failed: %r"); + goto errout; + } + + if((stack = setupstack(&ex, argv, envp)) == nil){ + trace("kexecve(): setupstack failed: %r"); + goto errout; + } + + memset(&u, 0, sizeof(u)); + u.sp = (ulong)stack; + u.pc = (ulong)ex.ientry; + current->ureg = &u; + current->syscall = nil; + phase++; + + trace("kexecve(): startup pc=%lux sp=%lux", current->ureg->pc, current->ureg->sp); + +errout: + switch(phase){ + default: free(envp); + case 2: free(argv); + case 1: free(name); + case 0: free(b); + } + switch(phase){ + case 4: retuser(); + case 3: exitproc(current, SIGKILL, 1); + } + return r; +} + +int sys_execve(char *name, char *argv[], char *envp[]) +{ + struct kexecveargs args; + + trace("sys_execve(%s, %p, %p)", name, argv, envp); + + args.name = name; + args.argv = argv; + args.envp = envp; + + return onstack(kstack, kexecve, &args); +} + +#pragma profile on diff --git a/linux_emul_base/file.c b/linux_emul_base/file.c new file mode 100644 index 0000000..8c048d5 --- /dev/null +++ b/linux_emul_base/file.c @@ -0,0 +1,760 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Fd Fd; +typedef struct Fdtab Fdtab; + +struct Fd +{ + int flags; + Ufile *file; +}; + +struct Fdtab +{ + Ref; + QLock; + int lastfd; + int nfd; + Fd *fd; +}; + +Ufile* +getfile(Ufile *file) +{ + if(file) + incref(file); + return file; +} + +void +putfile(Ufile *file) +{ + Udirent *d; + + if(file == nil) + return; + if(decref(file)) + return; + trace("putfile(): closing %p %s", file, file->path); + if(devtab[file->dev]->close) + devtab[file->dev]->close(file); + free(file->path); + while(d = file->rdaux){ + file->rdaux = d->next; + free(d); + } + free(file); +} + +static Fdtab* +newfdtab(void) +{ + Fdtab *tab; + + tab = kmallocz(sizeof(*tab), 1); + tab->ref = 1; + tab->lastfd = -1; + tab->nfd = 0; + tab->fd = nil; + + return tab; +} + +enum { + CHUNK = 64, +}; + +/* assumes tab->lock aquired */ +static int +grow1(Fdtab *tab) +{ + if(tab->nfd >= MAXFD) + return -EMFILE; + if((tab->nfd % CHUNK) == 0) + tab->fd = krealloc(tab->fd, sizeof(tab->fd[0]) * (tab->nfd + CHUNK)); + memset(&tab->fd[tab->nfd], 0, sizeof(tab->fd[0])); + return tab->nfd++; +} + +Ufile *procfdgetfile(Uproc *proc, int fd) +{ + Fdtab *tab; + Ufile *file; + + file = nil; + if(tab = proc->fdtab){ + qlock(tab); + if(fd >= 0 && fd < tab->nfd) + file = getfile(tab->fd[fd].file); + qunlock(tab); + } + return file; +} + +Ufile* +fdgetfile(int fd) +{ + return procfdgetfile(current, fd); +} + +int +newfd(Ufile *file, int flags) +{ + int fd; + Fdtab *tab; + + tab = current->fdtab; + qlock(tab); + fd = tab->lastfd; + if((fd >= 0) && (fd < tab->nfd) && (tab->fd[fd].file == nil)) + goto found; + for(fd=0; fdnfd; fd++) + if(tab->fd[fd].file == nil) + goto found; + fd = grow1(tab); +found: + if(fd >= 0){ + tab->fd[fd].file = file; + tab->fd[fd].flags = flags; + file = nil; + } + qunlock(tab); + putfile(file); + + return fd; +} + +static Fdtab* +getfdtab(Fdtab *tab, int copy) +{ + Fdtab *new; + int i; + + if(!copy){ + incref(tab); + return tab; + } + qlock(tab); + new = newfdtab(); + new->lastfd = tab->lastfd; + new->nfd = tab->nfd; + new->fd = kmallocz(sizeof(new->fd[0]) * (((tab->nfd+CHUNK-1)/CHUNK)*CHUNK), 1); + for(i=0; infd; i++){ + Ufile *file; + + if((file = tab->fd[i].file) == nil) + continue; + incref(file); + new->fd[i].file = file; + new->fd[i].flags = tab->fd[i].flags; + } + qunlock(tab); + return new; +} + +static void +putfdtab(Fdtab *tab) +{ + int i; + + if(decref(tab)) + return; + for(i=0; infd; i++){ + Ufile *file; + if((file = tab->fd[i].file) == nil) + continue; + tab->fd[i].file = nil; + putfile(file); + } + free(tab->fd); + free(tab); +} + +int sys_dup2(int old, int new) +{ + Ufile *file; + Fdtab *tab; + int err; + + trace("sys_dup2(%d, %d)", old, new); + + tab = current->fdtab; + + if((file = fdgetfile(old)) == nil) + return -EBADF; + if(new < 0) + return newfd(file, 0); + if(new >= MAXFD) + return -EBADF; + qlock(tab); + while(new >= tab->nfd){ + err = grow1(tab); + if(err < 0){ + qunlock(tab); + putfile(file); + return err; + } + } + if(tab->fd[new].file != nil) + putfile(tab->fd[new].file); + tab->fd[new].file = file; + tab->fd[new].flags &= ~FD_CLOEXEC; + qunlock(tab); + + return new; +} + +int sys_dup(int fd) +{ + return sys_dup2(fd, -1); +} + +struct linux_flock +{ + short l_type; + short l_whence; + ulong l_start; + ulong l_len; + int l_pid; +}; + +struct linux_flock64 +{ + short l_type; + short l_whence; + uvlong l_start; + uvlong l_len; + int l_pid; +}; + +enum { + F_RDLCK, + F_WRLCK, + F_UNLCK, +}; + +int sys_fcntl(int fd, int cmd, int arg) +{ + int ret; + Ufile *file; + Fdtab *tab; + + trace("sys_fcntl(%d, %lux, %lux)", fd, (ulong)cmd, (ulong)arg); + + tab = current->fdtab; + + ret = -EBADF; + if((file = fdgetfile(fd)) == nil) + goto out; + ret = -EINVAL; + switch(cmd){ + default: + trace("sys_fcntl() cmd %lux not implemented", (ulong)cmd); + break; + + case F_DUPFD: + if(arg < 0 || arg >= MAXFD) + break; + qlock(tab); + for(ret=arg; retnfd; ret++) + if(tab->fd[ret].file == nil) + goto found; + do { + if((ret = grow1(tab)) < 0) + break; + } while(ret < arg); +found: + if(ret >= 0){ + tab->fd[ret].file = file; + tab->fd[ret].flags = tab->fd[fd].flags & ~FD_CLOEXEC; + file = nil; + } + qunlock(tab); + break; + + case F_GETFD: + case F_SETFD: + qlock(tab); + if(cmd == F_GETFD){ + ret = tab->fd[fd].flags & FD_CLOEXEC; + } else { + tab->fd[fd].flags = (arg & FD_CLOEXEC); + ret = 0; + } + qunlock(tab); + break; + + case F_GETFL: + ret = file->mode; + break; + case F_SETFL: + trace("sys_fcntl() changing mode from %o to %o", file->mode, arg); + file->mode = arg; + ret = 0; + break; + + case F_GETLK: + ((struct linux_flock*)arg)->l_type = F_UNLCK; + case F_SETLK: + case F_SETLKW: + ret = 0; + break; + + case F_GETLK64: + ((struct linux_flock64*)arg)->l_type = F_UNLCK; + case F_SETLK64: + ret = 0; + break; + } +out: + putfile(file); + return ret; +} + +int sys_close(int fd) +{ + Fdtab *tab; + Ufile *file; + + trace("sys_close(%d)", fd); + + tab = current->fdtab; + qlock(tab); + if(fd >= 0 && fd < tab->nfd){ + if(file = tab->fd[fd].file){ + tab->fd[fd].file = nil; + tab->lastfd = fd; + qunlock(tab); + + putfile(file); + return 0; + } + } + qunlock(tab); + return -EBADF; +} + +int sys_ioctl(int fd, int cmd, void *arg) +{ + Ufile *file; + int ret; + + trace("sys_ioctl(%d, %lux, %p)", fd, (ulong)cmd, arg); + + if((file = fdgetfile(fd)) == nil) + return -EBADF; + ret = -ENOTTY; + if(devtab[file->dev]->ioctl) + ret = devtab[file->dev]->ioctl(file, cmd, arg); + putfile(file); + return ret; +} + +int preadfile(Ufile *file, void *buf, int len, vlong off) +{ + if(file->mode & O_NONBLOCK){ + if(devtab[file->dev]->poll != nil){ + if((devtab[file->dev]->poll(file, nil) & POLLIN) == 0){ + trace("readfile(): nonblocking read blocked"); + + return -EAGAIN; + } + } + } + if(devtab[file->dev]->read == nil) + return 0; + return devtab[file->dev]->read(file, buf, len, off); +} + +int readfile(Ufile *file, void *buf, int len) +{ + int err; + + if((err = preadfile(file, buf, len, file->off)) > 0) + file->off += err; + return err; +} + +int pwritefile(Ufile *file, void *buf, int len, vlong off) +{ + if(devtab[file->dev]->write == nil) + return 0; + if(file->mode & O_APPEND){ + if(devtab[file->dev]->size){ + off = devtab[file->dev]->size(file); + if(off < 0) + return (int)off; + } + } + return devtab[file->dev]->write(file, buf, len, off); +} + +int writefile(Ufile *file, void *buf, int len) +{ + int err; + vlong end; + + if(devtab[file->dev]->write == nil) + return 0; + if(file->mode & O_APPEND){ + if(devtab[file->dev]->size){ + end = devtab[file->dev]->size(file); + if(end < 0) + return (int)end; + file->off = end; + } + } + if(len == 0) + return 0; + if((err = devtab[file->dev]->write(file, buf, len, file->off)) > 0) + file->off += err; + return err; +} + +int sys_read(int fd, void *buf, int len) +{ + int ret; + Ufile *file; + + trace("sys_read(%d, %p, %x)", fd, buf, len); + if((file = fdgetfile(fd)) == nil) + return -EBADF; + ret = readfile(file, buf, len); + putfile(file); + return ret; +} + +int sys_write(int fd, void *buf, int len) +{ + Ufile *file; + int ret; + + trace("sys_write(%d, %p, %x)", fd, buf, len); + if((file = fdgetfile(fd)) == nil) + return -EBADF; + ret = writefile(file, buf, len); + putfile(file); + + return ret; +} + +int sys_pread64(int fd, void *buf, int len, ulong off) +{ + Ufile *file; + int ret; + + trace("sys_pread(%d, %p, %x, %lux)", fd, buf, len, off); + if((file = fdgetfile(fd)) == nil) + return -EBADF; + ret = preadfile(file, buf, len, off); + putfile(file); + return ret; +} + +int sys_pwrite64(int fd, void *buf, int len, ulong off) +{ + Ufile *file; + int ret; + + trace("sys_pwrite(%d, %p, %x, %lux)", fd, buf, len, off); + if((file = fdgetfile(fd)) == nil) + return -EBADF; + ret = pwritefile(file, buf, len, off); + putfile(file); + return ret; +} + +struct linux_iovec +{ + void *base; + ulong len; +}; + +int sys_writev(int fd, void *vec, int n) +{ + struct linux_iovec *v = vec; + int ret, i, w; + Ufile *file; + + trace("sys_writev(%d, %p, %d)", fd, vec, n); + + if((file = fdgetfile(fd)) == nil) + return -EBADF; + ret = 0; + for(i=0; idev]->size == nil) + return -ESPIPE; + + switch(whence){ + case 0: + file->off = off; + return 0; + case 1: + file->off += off; + return 0; + case 2: + end = devtab[file->dev]->size(file); + if(end < 0) + return end; + file->off = end + off; + return 0; + } + + return -EINVAL; +} + +ulong sys_lseek(int fd, ulong off, int whence) +{ + Ufile *file; + int ret; + + trace("sys_lseek(%d, %lux, %d)", fd, off, whence); + + if((file = fdgetfile(fd)) == nil) + return (ulong)-EBADF; + ret = seekfile(file, off, whence); + if(ret == 0) + ret = file->off; + putfile(file); + + return ret; +} + +int sys_llseek(int fd, ulong hioff, ulong looff, vlong *res, int whence) +{ + Ufile *file; + int ret; + + trace("sys_llseek(%d, %lux, %lux, %p, %d)", fd, hioff, looff, res, whence); + + if((file = fdgetfile(fd)) == nil) + return -EBADF; + ret = seekfile(file, ((vlong)hioff<<32) | ((vlong)looff), whence); + if((ret == 0) && res) + *res = file->off; + putfile(file); + + return ret; +} + +int sys_umask(int umask) +{ + int old; + + trace("sys_umask(%#o)", umask); + + old = current->umask; + current->umask = (umask & 0777); + return old; +} + +int +chdirfile(Ufile *f) +{ + Ustat s; + int err; + + trace("chdirfile(%s)", f->path); + + err = -ENOTDIR; + if(f->path == nil) + return err; + if(devtab[f->dev]->fstat == nil) + return err; + if((err = devtab[f->dev]->fstat(f, &s)) < 0) + return err; + err = -ENOTDIR; + if((s.mode & ~0777) != S_IFDIR) + return err; + free(current->cwd); + current->cwd = kstrdup(fsrootpath(f->path)); + if(f->dev == ROOTDEV && chdir(f->path) == 0){ + free(current->kcwd); + current->kcwd = kstrdup(f->path); + } + return 0; +} + +int +sys_fchdir(int fd) +{ + Ufile *f; + int err; + + trace("sys_fchdir(%d)", fd); + + if((f = fdgetfile(fd)) == nil) + return -EBADF; + err = chdirfile(f); + putfile(f); + return err; +} + +int +sys_fchown(int fd, int uid, int gid) +{ + int err; + Ufile *f; + + trace("sys_fchown(%d, %d, %d)", fd, uid, gid); + + if((f = fdgetfile(fd)) == nil) + return -EBADF; + err = -EPERM; + if(devtab[f->dev]->fchown) + err = devtab[f->dev]->fchown(f, uid, gid); + putfile(f); + + return err; +} + +int +sys_fchmod(int fd, int mode) +{ + int err; + Ufile *f; + + trace("sys_fchmod(%d, %#o)", fd, mode); + + if((f = fdgetfile(fd)) == nil) + return -EBADF; + err = -EPERM; + if(devtab[f->dev]->fchmod) + err = devtab[f->dev]->fchmod(f, mode); + putfile(f); + + return err; +} + +int +sys_ftruncate(int fd, ulong size) +{ + int err; + Ufile *f; + + trace("sys_ftruncate(%d, %lux)", fd, size); + + if((f = fdgetfile(fd)) == nil) + return -EBADF; + err = -EPERM; + if(devtab[f->dev]->ftruncate) + err = devtab[f->dev]->ftruncate(f, (uvlong)size); + putfile(f); + + return err; +} + +void initfile(void) +{ + current->fdtab = newfdtab(); + current->umask = 022; +} + +void exitfile(Uproc *proc) +{ + Fdtab *tab; + + if(tab = proc->fdtab){ + proc->fdtab = nil; + putfdtab(tab); + } +} + +void clonefile(Uproc *new, int copy) +{ + Fdtab *tab; + + if((tab = current->fdtab) == nil){ + new->fdtab = nil; + return; + } + new->fdtab = getfdtab(tab, copy); +} + +void closexfds(void) +{ + Fdtab *tab; + int i; + + if((tab = current->fdtab) == nil) + return; + qlock(tab); + for(i=0; infd; i++){ + Ufile *f; + + if((f = tab->fd[i].file) == nil) + continue; + if((tab->fd[i].flags & FD_CLOEXEC) == 0) + continue; + + tab->fd[i].file = nil; + tab->fd[i].flags = 0; + + putfile(f); + } + qunlock(tab); +} + +int sys_flock(int fd, int cmd) +{ + trace("sys_flock(%d, %d)", fd, cmd); + return 0; +} + +int sys_fsync(int fd) +{ + trace("sys_fsync(%d)", fd); + return 0; +} + diff --git a/linux_emul_base/fns.h b/linux_emul_base/fns.h new file mode 100644 index 0000000..d6005d5 --- /dev/null +++ b/linux_emul_base/fns.h @@ -0,0 +1,311 @@ +/* error */ +int mkerror(void); +#pragma varargck type "E" int +int Efmt(Fmt *e); +int sys_nosys(void); + +/* linuxcall */ +int linuxcall(void); + +/* trap */ +void inittrap(void); +void retuser(void); + +/* bits */ +void incref(Ref *); +int decref(Ref *); +void jumpstart(ulong addr, ulong *stack); +void jumpureg(void *ureg); +void linux_sigreturn(void); +void linux_rtsigreturn(void); + +/* trace */ +void inittrace(void); +void exittrace(Uproc *proc); +void clonetrace(Uproc *new, int copy); +void tprint(char *fmt, ...); +#pragma varargck argpos tprint 1 +#define trace if(debug)tprint + +/* proc */ +void initproc(void); +void exitproc(Uproc *proc, int code, int group); +void stopproc(Uproc *proc, int code, int group); +void contproc(Uproc *proc, int code, int group); +int procfork(void (*fproc)(void *aux), void *aux, int flags); +Uproc* getproc(int tid); +Uproc* getprocn(int n); +int threadcount(int pid); +void zapthreads(void); +void setprocname(char *s); +int notifyme(int on); +void wakeme(int on); +int sleepproc(QLock *l, int flags); +Uwait* addwaitq(Uwaitq *q); +void delwaitq(Uwait *w); +int sleepq(Uwaitq *q, QLock *l, int flags); +int wakeq(Uwaitq *q, int nwake); +int requeue(Uwaitq *q1, Uwaitq *q2, int nrequeue); +int killproc(Uproc *p, Usiginfo *info, int group); +void setalarm(vlong t); + +int sys_waitpid(int pid, int *pexit, int opt); +int sys_wait4(int pid, int *pexit, int opt, void *prusage); +int sys_exit(int code); +int sys_exit_group(int code); +int sys_linux_clone(int flags, void *newstack, int *parenttidptr, int *tlsdescr, void *childtidptr); +int sys_fork(void); +int sys_vfork(void); +int sys_getpid(void); +int sys_getppid(void); +int sys_gettid(void); +int sys_setpgid(int pid, int pgid); +int sys_getpgid(int pid); +int sys_setpgrp(int pid); +int sys_getpgrp(void); +int sys_getuid(void); +int sys_getgid(void); +int sys_setgid(int gid); +int sys_setuid(int uid); +int sys_setresuid(int ruid, int euid, int suid); +int sys_getresuid(int *ruid, int *euid, int *suid); +int sys_setresgid(int rgid, int egid, int sgid); +int sys_getresgid(int *rgid, int *egid, int *sgid); +int sys_setreuid(int ruid, int euid); +int sys_setregid(int rgid, int egid); +int sys_uname(void *); +int sys_personality(ulong p); +int sys_setsid(void); +int sys_getsid(int pid); +int sys_getgroups(int size, int *groups); +int sys_setgroups(int size, int *groups); + +int sys_kill(int pid, int sig); +int sys_tkill(int tid, int sig); +int sys_tgkill(int pid, int tid, int sig); +int sys_rt_sigqueueinfo(int pid, int sig, void *info); + +int sys_set_tid_address(int *tidptr); + +int sys_sched_setscheduler(int pid, int policy, void *param); +int sys_sched_getscheduler(int pid); +int sys_sched_setparam(int pid, void *param); +int sys_sched_getparam(int pid, void *param); +int sys_sched_yield(void); + +int sys_getrlimit(long resource, void *rlim); +int sys_setrlimit(long resource, void *rlim); + +/* signal */ +void initsignal(void); +void exitsignal(void); +void clonesignal(Uproc *new, int copyhand, int newproc); +void settty(Ufile *tty); +Ufile* gettty(void); +#pragma varargck type "S" int +int Sfmt(Fmt *f); + +int wantssignal(Uproc *proc, int sig); +int ignoressignal(Uproc *proc, int sig); +int signalspending(Uproc *proc); + +void handlesignals(void); +int sendsignal(Uproc *proc, Usiginfo *info, int group); + +void siginfo2linux(Usiginfo *, void *); +void linux2siginfo(void *, Usiginfo *); + +int sys_sigaltstack(void *stk, void *ostk); +int sys_rt_sigaction(int sig, void *pact, void *poact, int setsize); +int sys_rt_sigpending(uchar *set, int setsize); +int sys_rt_sigprocmask(int how, uchar *act, uchar *oact, int setsize); +int sys_rt_sigsuspend(uchar *set, int setsize); +int sys_sigreturn(void); +int sys_rt_sigreturn(void); + +int sys_setitimer(int which, void *value, void *ovalue); +int sys_getitimer(int which, void *value); +int sys_alarm(long seconds); + +/* file */ +void initfile(void); +void exitfile(Uproc *proc); +void clonefile(Uproc *new, int copy); +void closexfds(void); +Ufile *procfdgetfile(Uproc *proc, int fd); +Ufile* fdgetfile(int fd); +Ufile* getfile(Ufile *file); +void putfile(Ufile *file); +int newfd(Ufile *file, int flags); +int chdirfile(Ufile *file); +int readfile(Ufile *file, void *buf, int len); +int writefile(Ufile *file, void *buf, int len); +int preadfile(Ufile *file, void *buf, int len, vlong off); +int pwritefile(Ufile *file, void *buf, int len, vlong off); +int sys_dup(int fd); +int sys_dup2(int old, int new); +int sys_fcntl(int fd, int cmd, int arg); +int sys_close(int fd); +int sys_ioctl(int fd, int cmd, void *arg); +int sys_read(int fd, void *buf, int len); +int sys_readv(int fd, void *vec, int n); +int sys_pread64(int fd, void *buf, int len, ulong off); +int sys_write(int fd, void *buf, int len); +int sys_pwrite64(int fd, void *buf, int len, ulong off); +int sys_writev(int fd, void *vec, int n); +ulong sys_lseek(int fd, ulong off, int whence); +int sys_llseek(int fd, ulong hioff, ulong looff, vlong *res, int whence); +int sys_umask(int umask); +int sys_flock(int fd, int cmd); +int sys_fsync(int fd); +int sys_fchdir(int fd); +int sys_getcwd(char *buf, int len); +int sys_fchmod(int fd, int mode); +int sys_fchown(int fd, int uid, int gid); +int sys_ftruncate(int fd, ulong size); + +/* poll */ +void pollwait(Ufile *f, Uwaitq *q, void *t); +int sys_poll(void *p, int nfd, long timeout); +int sys_select(int nfd, ulong *rfd, ulong *wfd, ulong *efd, void *ptv); + +/* mem */ +void* kmalloc(int size); +void* kmallocz(int size, int zero); +void* krealloc(void *ptr, int size); +char* kstrdup(char *s); +char* ksmprint(char *fmt, ...); +#pragma varargck argpos ksmprint 1 + +ulong pagealign(ulong addr); + +void initmem(void); +void exitmem(void); +void clonemem(Uproc *new, int copy); +ulong procmemstat(Uproc *proc, ulong *pdat, ulong *plib, ulong *pshr, ulong *pstk, ulong *pexe); +void* mapstack(int size); +void mapdata(ulong base); +void unmapuserspace(void); +int okaddr(void *ptr, int len, int write); + +ulong sys_linux_mmap(void *a); +ulong sys_mmap(ulong addr, ulong len, int prot, int flags, int fd, ulong pgoff); +int sys_munmap(ulong addr, ulong len); +ulong sys_brk(ulong bk); +int sys_mprotect(ulong addr, ulong len, int prot); +int sys_msync(ulong addr, ulong len, int flags); +ulong sys_mremap(ulong addr, ulong oldlen, ulong newlen, int flags, ulong newaddr); + +int sys_futex(ulong *addr, int op, int val, void *ptime, ulong *addr2, int val3); + +/* exec */ +int sys_execve(char *name, char *argv[], char *envp[]); + +/* time */ +void inittime(void); +int sys_time(long *p); +int sys_gettimeofday(void *tvp, void *tzp); +int sys_clock_gettime(int clock, void *t); +int sys_nanosleep(void *rqp, void *rmp); +int proctimes(Uproc *p, ulong *t); +int sys_times(void *times); + +/* tls */ +void inittls(void); +void clonetls(Uproc *new); + +int sys_set_thread_area(void *pinfo); +int sys_get_thread_area(void *pinfo); +int sys_modify_ldt(int func, void *data, int count); + +/* bufproc */ +void *newbufproc(int fd); +void freebufproc(void *bp); +int readbufproc(void *bp, void *data, int len, int peek, int noblock); +int pollbufproc(void *bp, Ufile *file, void *tab); +int nreadablebufproc(void *bp); + +/* main */ +void panic(char *msg, ...); +int onstack(long *stk, int (*func)(void *arg), void *arg); +void profme(void); + +/* stat */ +int ufstat(int fd, Ustat *ps); +Udirent *newdirent(char *path, char *name, int mode); + +int sys_getxattr(char *path, char *name, void *value, int size); +int sys_lgetxattr(char *path, char *name, void *value, int size); +int sys_fgetxattr(int fd, char *name, void *value, int size); +int sys_setxattr(char *path, char *name, void *value, int flags, int size); +int sys_lsetxattr(char *path, char *name, void *value, int flags, int size); +int sys_fsetxattr(int fd, char *name, void *value, int size, int flags); + +int sys_linux_fstat(int fd, void *st); +int sys_linux_fstat64(int fd, void *st); +int sys_linux_getdents(int fd, void *buf, int nbuf); +int sys_linux_getdents64(int fd, void *buf, int nbuf); +int sys_linux_lstat(char *path, void *st); +int sys_linux_lstat64(char *path, void *st); +int sys_linux_stat(char *path, void *st); +int sys_linux_stat64(char *path, void *st); + +int sys_statfs(char *name, void *pstatfs); + +/* fs */ +void fsmount(Udev *dev, char *path); + +char* allocpath(char *base, char *prefix, char *name); +char* fullpath(char *base, char *name); +char* shortpath(char *base, char *path); +char* fsfullpath(char *path); +char* fsrootpath(char *path); +char* basepath(char *p, char **ps); +ulong hashpath(char *s); + +int fsaccess(char *path, int mode); +int fschmod(char *path, int mode); +int fschown(char *path, int uid, int gid, int link); +int fslink(char *old, char *new, int sym); +int fsmkdir(char *path, int mode); +int fsopen(char *path, int mode, int perm, Ufile **pf); +int fsreadlink(char *path, char *buf, int len); +int fsrename(char *old, char *new); +int fsstat(char *path, int link, Ustat *ps); +int fstruncate(char *path, vlong size); +int fsunlink(char *path, int rmdir); +int fsutime(char *path, int atime, int mtime); + +int sys_access(char *name, int mode); +int sys_chdir(char *name); +int sys_chroot(char *name); +int sys_chmod(char *name, int mode); +int sys_chown(char *name, int uid, int gid); +int sys_creat(char *name, int perm); +int sys_lchown(char *name, int uid, int gid); +int sys_link(char *old, char *new); +int sys_open(char *name, int mode, int perm); +int sys_readlink(char *name, char *buf, int len); +int sys_rename(char *from, char *to); +int sys_rmdir(char *name); +int sys_symlink(char *old, char *new); +int sys_truncate(char *name, ulong size); +int sys_unlink(char *name); +int sys_utime(char *name, void *times); +int sys_utimes(char *name, void *tvp); +int sys_mkdir(char *name, int mode); + +/* drivers */ +void rootdevinit(void); +void sockdevinit(void); +int sys_linux_socketcall(int call, int *arg); +void pipedevinit(void); +int sys_pipe(int *fds); +void fddevinit(void); +void ptsdevinit(void); +void dspdevinit(void); +void miscdevinit(void); +void ptydevinit(void); +void consdevinit(void); +void procdevinit(void); + diff --git a/linux_emul_base/fs.c b/linux_emul_base/fs.c new file mode 100644 index 0000000..4283991 --- /dev/null +++ b/linux_emul_base/fs.c @@ -0,0 +1,758 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Mount Mount; + +struct Mount +{ + Mount *next; + Udev *dev; + int npath; + char path[]; +}; + +static Mount *mtab; + +void +fsmount(Udev *dev, char *path) +{ + Mount *m, **p; + int n; + + if(dev == nil) + return; + + n = strlen(path); + m = kmalloc(sizeof(*m) + n + 1); + m->dev = dev; + m->next = nil; + m->npath = n; + strcpy(m->path, path); + + for(p=&mtab;;p=&((*p)->next)){ + Mount *x; + + if(x = *p){ + if(m->npath < x->npath) + continue; + if(m->npath == x->npath){ + if(strcmp(m->path, x->path) < 0) + continue; + } + } + m->next = *p; + *p = m; + break; + } +} + +ulong +hashpath(char *s) +{ + ulong h; + for(h=0; *s; s++) + h = (h * 13) + (*s - 'a'); + return h; +} + +char* +basepath(char *p, char **ps) +{ + char *x, *s; + int n; + + if(s = strrchr(p, '/')){ + if(s[1] != 0){ + if(ps) + *ps = kstrdup(s+1); + if((n = s - p) == 0) + n = 1; + x = kmalloc(n+1); + memmove(x, p, n); + x[n] = 0; + return x; + } + } + if(ps) + *ps = nil; + return nil; +} + +char* +allocpath(char *base, char *prefix, char *name) +{ + char *p, *s; + int n, m, k; + + n = strlen(base); + m = strlen(name); + k = prefix ? strlen(prefix) : 0; + p = s = kmalloc(n+m+k+2); + memmove(p, base, n); + p += n; + if(m || k) + *p++ = '/'; + if(k){ + memmove(p, prefix, k); + p += k; + } + memmove(p, name, m+1); + return s; +} + +char* +fullpath(char *base, char *name) +{ + char *s; + + if(*name == '/' || *name == '#'){ + s = kstrdup(name); + } else if(base) { + s = allocpath(base, nil, name); + } else { + s = nil; + } + if(s != nil) + cleanname(s); + return s; +} + +char* +shortpath(char *base, char *path) +{ + int n; + + n = strlen(base); + if((n <= strlen(path)) && (strncmp(path, base, n)==0)){ + path += n; + if(*path == '/') + path++; + if(*path == 0) + path = "."; + } + return path; +} + +char* +fsfullpath(char *path) +{ + char *root; + + path = fullpath(current->cwd, path); + if(path && (root = current->root)){ + root = allocpath(root, nil, path+1); + free(path); + path = root; + } + return path; +} + +char* +fsrootpath(char *path) +{ + char *root; + + if(root = current->root){ + root = shortpath(root, path); + if(*root == '.'){ + path = "/"; + } else if(root > path){ + path = root-1; + } + } + return path; +} + +static Mount* +path2mount(char *path) +{ + Mount *m; + + for(m=mtab; m; m=m->next){ + if(strncmp(path, m->path, m->npath) == 0){ + switch(path[m->npath]){ + case '\0': + case '/': + return m; + } + } + } + return nil; +} + +static Udev* +path2dev(char *path) +{ + Mount *m; + + if(m = path2mount(path)) + return m->dev; + return nil; +} + +static int +fsenter(int *perr) +{ + int err; + + if(perr == nil) + perr = &err; + if(current->linkloop > 8) + return *perr = -ELOOP; + current->linkloop++; + return 0; +} + +static void +fsleave(void) +{ + current->linkloop--; +} + +int sys_getcwd(char *buf, int len) +{ + int n; + char *cwd; + + trace("sys_getcwd(%p, %x)", buf, len); + + cwd = current->cwd; + n = strlen(cwd)+1; + if(n > len) + return -ERANGE; + memmove(buf, cwd, n); + return n; +} + +int +fsopen(char *path, int mode, int perm, Ufile **pf) +{ + int err; + Udev *dev; + + trace("fsopen(%s, %#o, %#o)", path, mode, perm); + + *pf = nil; + if(fsenter(&err) < 0) + return err; + err = -ENOENT; + if((dev = path2dev(path)) && dev->open) + err = dev->open(path, mode, perm, pf); + fsleave(); + return err; +} + +int +fsaccess(char *path, int mode) +{ + int err; + Udev *dev; + + trace("fsaccess(%s, %#o)", path, mode); + + if(fsenter(&err) < 0) + return err; + err = -ENOENT; + if(dev = path2dev(path)){ + err = 0; + if(dev->access) + err = dev->access(path, mode); + } + fsleave(); + + return err; +} + +int sys_access(char *name, int mode) +{ + int err; + + trace("sys_access(%s, %#o)", name, mode); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fsaccess(name, mode); + free(name); + + return err; +} + +int sys_open(char *name, int mode, int perm) +{ + int err; + Ufile *file; + + trace("sys_open(%s, %#o, %#o)", name, mode, perm); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fsopen(name, mode, perm, &file); + free(name); + + if(err == 0) + err = newfd(file, FD_CLOEXEC); + + return err; +} + +int sys_creat(char *name, int perm) +{ + trace("sys_create(%s, %#o)", name, perm); + + return sys_open(name, O_CREAT|O_TRUNC, perm); +} + +int +fsstat(char *path, int link, Ustat *ps) +{ + int err; + Udev *dev; + + trace("fsstat(%s, %d)", path, link); + + if(fsenter(&err) < 0) + return err; + err = -EPERM; + if((dev = path2dev(path)) && dev->stat){ + memset(ps, 0, sizeof(Ustat)); + err = dev->stat(path, link, ps); + } + fsleave(); + return err; +} + +int +sys_chdir(char *name) +{ + int err; + Ufile *f; + + trace("sys_chdir(%s)", name); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fsopen(name, O_RDONLY, 0, &f); + free(name); + if(err == 0){ + err = chdirfile(f); + putfile(f); + } + return err; +} + +int sys_chroot(char *name) +{ + Ufile *f; + Ustat s; + int err; + + trace("sys_chroot(%s)", name); + + f = nil; + if((err = fsopen(name, O_RDONLY, 0, &f)) < 0) + goto out; + err = -ENOTDIR; + if(f->path == nil) + goto out; + if(devtab[f->dev]->fstat == nil) + goto out; + if((err = devtab[f->dev]->fstat(f, &s)) < 0) + goto out; + err = -ENOTDIR; + if((s.mode & ~0777) != S_IFDIR) + goto out; + err = 0; + free(current->root); + if(strcmp(f->path, "/") == 0){ + current->root = nil; + } else { + current->root = kstrdup(f->path); + } +out: + putfile(f); + return err; +} + +int +fschown(char *path, int uid, int gid, int link) +{ + int err; + Udev *dev; + + trace("fschown(%s, %d, %d, %d)", path, uid, gid, link); + + if(fsenter(&err) < 0) + return err; + err = -EPERM; + if((dev = path2dev(path)) && dev->chown) + err = dev->chown(path, uid, gid, link); + fsleave(); + return err; +} + +int sys_chown(char *name, int uid, int gid) +{ + int err; + + trace("sys_chown(%s, %d, %d)", name, uid, gid); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fschown(name, uid, gid, 0); + free(name); + + return err; +} + +int sys_lchown(char *name, int uid, int gid) +{ + int err; + + trace("sys_lchown(%s, %d, %d)", name, uid, gid); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fschown(name, uid, gid, 1); + free(name); + + return err; +} + +int +fsreadlink(char *path, char *buf, int len) +{ + int err; + Udev *dev; + + trace("fsreadlink(%s)", path); + + if(fsenter(&err) < 0) + return err; + err = -EPERM; + if((dev = path2dev(path)) && dev->readlink) + err = dev->readlink(path, buf, len); + fsleave(); + + return err; +} + +int sys_readlink(char *name, char *buf, int len) +{ + int err; + + trace("sys_readlink(%s, %p, %x)", name, buf, len); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fsreadlink(name, buf, len); + free(name); + + return err; +} + +int +fsrename(char *old, char *new) +{ + int err; + Udev *dev; + + trace("fsrename(%s, %s)", old, new); + + if(fsenter(&err) < 0) + return err; + err = -EPERM; + if((dev = path2dev(old)) && dev->rename){ + err = -EXDEV; + if(dev == path2dev(new)) + err = dev->rename(old, new); + } + fsleave(); + + return err; +} + + +int sys_rename(char *from, char *to) +{ + int err; + + trace("sys_rename(%s, %s)", from, to); + + if((from = fsfullpath(from)) == nil) + return -EFAULT; + if((to = fsfullpath(to)) == nil){ + free(from); + return -EFAULT; + } + err = fsrename(from, to); + free(from); + free(to); + + return err; +} + +int +fsmkdir(char *path, int mode) +{ + int err; + Udev *dev; + + trace("fsmkdir(%s, %#o)", path, mode); + + if(fsenter(&err) < 0) + return err; + + err = -EPERM; + if((dev = path2dev(path)) && dev->mkdir) + err = dev->mkdir(path, mode); + fsleave(); + + return err; +} + +int sys_mkdir(char *name, int mode) +{ + int err; + + trace("sys_mkdir(%s, %#o)", name, mode); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fsmkdir(name, mode); + free(name); + + return err; +} + +int +fsutime(char *path, int atime, int mtime) +{ + int err; + Udev *dev; + + trace("fsutime(%s, %d, %d)", path, atime, mtime); + + if(fsenter(&err) < 0) + return err; + err = -EPERM; + if((dev = path2dev(path)) && dev->utime) + err = dev->utime(path, atime, mtime); + fsleave(); + + return err; +} + +struct linux_utimbuf +{ + long atime; + long mtime; +}; + +int sys_utime(char *name, void *times) +{ + int err; + struct linux_utimbuf *t = times; + + trace("sys_utime(%s, %p)", name, times); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + if(t != nil){ + err = fsutime(name, t->atime, t->mtime); + }else{ + long x = time(0); + err = fsutime(name, x, x); + } + free(name); + + return err; +} + +int sys_utimes(char *name, void *tvp) +{ + int err; + struct linux_timeval *t = tvp; + + trace("sys_utimes(%s, %p)", name, tvp); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + if(t != nil){ + err = fsutime(name, t[0].tv_sec, t[1].tv_sec); + }else{ + long x = time(0); + err = fsutime(name, x, x); + } + free(name); + + return err; +} + +int +fschmod(char *path, int mode) +{ + int err; + Udev *dev; + + trace("fschmod(%s, %#o)", path, mode); + + if(fsenter(&err) < 0) + return err; + err = -EPERM; + if((dev = path2dev(path)) && dev->chmod) + err = dev->chmod(path, mode); + fsleave(); + + return err; +} + +int sys_chmod(char *name, int mode) +{ + int err; + + trace("sys_chmod(%s, %#o)", name, mode); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fschmod(name, mode); + free(name); + + return err; +} + +int +fstruncate(char *path, vlong size) +{ + int err; + Udev *dev; + + trace("fstruncate(%s, %llx)", path, size); + + if(fsenter(&err) < 0) + return err; + err = -EPERM; + if((dev = path2dev(path)) && dev->truncate) + err = dev->truncate(path, size); + fsleave(); + + return err; +} + +int sys_truncate(char *name, ulong size) +{ + int err; + + trace("sys_truncate(%s, %lux)", name, size); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fstruncate(name, size); + free(name); + + return err; +} + +int +fsunlink(char *path, int rmdir) +{ + int err; + Udev *dev; + + trace("fsunlink(%s, %d)", path, rmdir); + + if(fsenter(&err) < 0) + return err; + err = -EPERM; + if((dev = path2dev(path)) && dev->unlink) + err = dev->unlink(path, rmdir); + fsleave(); + + return err; +} + +int sys_unlink(char *name) +{ + int err; + + trace("sys_unlink(%s)", name); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fsunlink(name, 0); + free(name); + + return err; +} + +int sys_rmdir(char *name) +{ + int err; + + trace("sys_rmdir(%s)", name); + + if((name = fsfullpath(name)) == nil) + return -EFAULT; + err = fsunlink(name, 1); + free(name); + + return err; +} + +int +fslink(char *old, char *new, int sym) +{ + int err; + Udev *dev; + + trace("fslink(%s, %s, %d)", old, new, sym); + + if(fsenter(&err) < 0) + return err; + err = -EPERM; + if((dev = path2dev(new)) && dev->link){ + err = -EXDEV; + if(sym || dev == path2dev(old)) + err = dev->link(old, new, sym); + } + fsleave(); + + return err; +} + +int sys_link(char *old, char *new) +{ + int err; + + trace("sys_link(%s, %s)", old, new); + + if((old = fsfullpath(old)) == nil) + return -EFAULT; + if((new = fsfullpath(new)) == nil){ + free(old); + return -EFAULT; + } + err = fslink(old, new, 0); + free(old); + free(new); + + return err; +} + +int sys_symlink(char *old, char *new) +{ + int err; + + trace("sys_symlink(%s, %s)", old, new); + + if((new = fsfullpath(new)) == nil) + return -EFAULT; + err = fslink(old, new, 1); + free(new); + + return err; +} + diff --git a/linux_emul_base/linux b/linux_emul_base/linux new file mode 100755 index 0000000..e262acf --- /dev/null +++ b/linux_emul_base/linux @@ -0,0 +1,114 @@ +#!/bin/rc + +arg0=$0 +DISPLAY=:0 +HOME=/tmp +PATH=/bin:/usr/bin:/sbin:/usr/sbin:/usr/X11R6/bin:/usr/games + +x='' +e=/bin/linuxemu +r=/sys/lib/linux + +fn eprint { + echo $arg0: $* >[1=2] +} + +fn usage { + echo usage: $arg0 [-h] [-d...] [-u uid] [-g gid] [-startx] [-display :n] [-e emubin] [-r linuxroot] command [args ...] >[1=2] + exit usage +} + +# extract options +o=() +while(~ $1 -*){ + switch($1){ + case -h + usage + case -r + shift + r=$1 + case -e + shift + e=$1 + case -startx + x=1 + case -display + shift + DISPLAY=$1 + case -[ug] + o=($o $1 $2) + shift + case -* + o=($o $1) + } + shift +} + +switch($#*){ +case 0 + usage +} + +if(! ~ $x ''){ + # find free local display + d=(`{{seq 0 32; {echo /srv/UD.X* | sed 's!/srv/UD\.X!!g; s!\ !\ + !g; s!\*!!g;'}} | sort | uniq -c | awk '/^\ *1\ /{print $2}'}) + d=$d(1) + X11/equis -ac :$d & + k=/proc/$apid/notepg + $arg0 -e $e -r $r -display :$d $o $* + {echo kill >$k} >/dev/null >[2=1] + exit +} + +# rewrite the path so it would accessible after binding $r to / +fn ninepath { + if(~ $1 /* && test -e $1 && ! test -e $r/$1){ + echo /9$1 + } + if not { + echo $1 + } +} + +w=`{pwd} +r=`{cleanname -d $w $r} +if(! test -d $r){ + eprint bad rootpath: $r + exit rootpath +} +e=`{cleanname -d $w $e} +if(! test -x $e){ + eprint bad emubin: $e + exit emubin +} +e=`{ninepath $e} +p=`{ninepath $w} +a=($e $o) +while(! ~ $#* 0){ + x=`{ninepath $1} + a=($a $"x) + shift +} + +# bind the required plan9 stuff +rfork n +mntgen $r +bind -a '#P' /dev +for(d in /9 /dev /proc /net /env /srv /n /mnt /tmp){ + t=$r^$d + switch($d){ + case /tmp /env /srv + bind -c $d $t + case /9 + bind / $t + case * + bind $d $t + } +} + +# change root and run the emulator +builtin cd / +bind $r / +builtin cd $p +exec $a diff --git a/linux_emul_base/linux.h b/linux_emul_base/linux.h new file mode 100644 index 0000000..99bf1de --- /dev/null +++ b/linux_emul_base/linux.h @@ -0,0 +1,352 @@ +enum { + O_ACCMODE = 0003, + O_RDONLY = 00, + O_WRONLY = 01, + O_RDWR = 02, + O_CREAT = 0100, + O_EXCL = 0200, + O_NOCTTY = 0400, + O_TRUNC = 01000, + O_APPEND = 02000, + O_NONBLOCK = 04000, + O_NDELAY = 04000, + O_SYNC = 010000, + FASYNC = 020000, +}; + +enum { + FD_CLOEXEC = 1, +}; + +enum { + F_DUPFD = 0, + F_GETFD, + F_SETFD, + F_GETFL, + F_SETFL, + F_GETLK, + F_SETLK, + F_SETLKW, + F_SETOWN, + F_GETOWN, + F_GETSIG, + F_GETLK64 = 12, + F_SETLK64 = 13, +}; + +enum { + S_IFMT = 0170000, + S_IFSOCK = 0140000, + S_IFLNK = 0120000, + S_IFREG = 0100000, + S_IFBLK = 0060000, + S_IFDIR = 0040000, + S_IFCHR = 0020000, + S_IFIFO = 0010000, + S_ISUID = 0004000, + S_ISGID = 0002000, + S_ISVTX = 0001000, +}; + +enum { + PROT_READ = 0x01, + PROT_WRITE = 0x02, + PROT_EXEC = 0x04, + PROT_SEM = 0x08, + PROT_NONE = 0x00, + PROT_GROWSDOWN = 0x01000000, + PROT_GROWSUP = 0x02000000, + MAP_SHARED = 0x01, + MAP_PRIVATE = 0x02, + MAP_TYPE = 0x0f, + MAP_FIXED = 0x10, + MAP_ANONYMOUS = 0x20, + + MREMAP_MAYMOVE = 1, + MREMAP_FIXED = 2, +}; + +enum { + CLONE_VM = 0x00000100, + CLONE_FS = 0x00000200, + CLONE_FILES = 0x00000400, + CLONE_SIGHAND = 0x00000800, + CLONE_PTRACE = 0x00002000, + CLONE_VFORK = 0x00004000, + CLONE_PARENT = 0x00008000, + CLONE_THREAD = 0x00010000, + CLONE_NEWNS = 0x00020000, + CLONE_SYSVSEM = 0x00040000, + CLONE_SETTLS = 0x00080000, + CLONE_PARENT_SETTID = 0x00100000, + CLONE_CHILD_CLEARTID = 0x00200000, + CLONE_DETACHED = 0x00400000, + CLONE_UNTRACED = 0x00800000, + CLONE_CHILD_SETTID = 0x01000000, + CLONE_STOPPED = 0x02000000, +}; + +enum { + EPERM = 1, + ENOENT = 2, + ESRCH = 3, + EINTR = 4, + EIO = 5, + ENXIO = 6, + E2BIG = 7, + ENOEXEC = 8, + EBADF = 9, + ECHILD = 10, + EAGAIN = 11, + ENOMEM = 12, + EACCES = 13, + EFAULT = 14, + ENOTBLK = 15, + EBUSY = 16, + EEXIST = 17, + EXDEV = 18, + ENODEV = 19, + ENOTDIR = 20, + EISDIR = 21, + EINVAL = 22, + ENFILE = 23, + EMFILE = 24, + ENOTTY = 25, + ETXTBSY = 26, + EFBIG = 27, + ENOSPC = 28, + ESPIPE = 29, + EROFS = 30, + EMLINK = 31, + EPIPE = 32, + EDOM = 33, + ERANGE = 34, + EDEADLK = 35, + ENAMETOOLONG = 36, + ENOLCK = 37, + ENOSYS = 38, + ENOTEMPTY = 39, + ELOOP = 40, + ENOMSG = 42, + EIDRM = 43, + ECHRNG = 44, + EL2NSYNC = 45, + EL3HLT = 46, + EL3RST = 47, + ELNRNG = 48, + EUNATCH = 49, + ENOCSI = 50, + EL2HLT = 51, + EBADE = 52, + EBADR = 53, + EXFULL = 54, + ENOANO = 55, + EBADRQC = 56, + EBADSLT = 57, + EBFONT = 59, + ENOSTR = 60, + ENODATA = 61, + ETIME = 62, + ENOSR = 63, + ENONET = 64, + ENOPKG = 65, + EREMOTE = 66, + ENOLINK = 67, + EADV = 68, + ESRMNT = 69, + ECOMM = 70, + EPROTO = 71, + EMULTIHOP = 72, + EDOTDOT = 73, + EBADMSG = 74, + EOVERFLOW = 75, + ENOTUNIQ = 76, + EBADFD = 77, + EREMCHG = 78, + ELIBACC = 79, + ELIBBAD = 80, + ELIBSCN = 81, + ELIBMAX = 82, + ELIBEXEC = 83, + EILSEQ = 84, + ERESTART = 85, + ESTRPIPE = 86, + EUSERS = 87, + ENOTSOCK = 88, + EDESTADDRREQ = 89, + EMSGSIZE = 90, + EPROTOTYPE = 91, + ENOPROTOOPT = 92, + EPROTONOSUPPORT = 93, + ESOCKTNOSUPPORT = 94, + EOPNOTSUPP = 95, + EPFNOSUPPORT = 96, + EAFNOSUPPORT = 97, + EADDRINUSE = 98, + EADDRNOTAVAIL = 99, + ENETDOWN = 100, + ENETUNREACH = 101, + ENETRESET = 102, + ECONNABORTED = 103, + ECONNRESET = 104, + ENOBUFS = 105, + EISCONN = 106, + ENOTCONN = 107, + ESHUTDOWN = 108, + ETOOMANYREFS = 109, + ETIMEDOUT = 110, + ECONNREFUSED = 111, + EHOSTDOWN = 112, + EHOSTUNREACH = 113, + EALREADY = 114, + EINPROGRESS = 115, + ESTALE = 116, + EUCLEAN = 117, + ENOTNAM = 118, + ENAVAIL = 119, + EISNAM = 120, + EREMOTEIO = 121, + EDQUOT = 122, + ENOMEDIUM = 123, + EMEDIUMTYPE = 124, + EMAX = 125, +}; + +#define EWOULDBLOCK EAGAIN +#define EDEADLOCK EDEADLK +#define ENOATTR ENODATA + +enum { + POLLIN = (1<<0), + POLLPRI = (1<<1), + POLLOUT = (1<<2), + POLLERR = (1<<3), + POLLHUP = (1<<4), + POLLNVAL = (1<<5), + POLLRDNORM = (1<<6), + POLLRDBAND = (1<<7), + POLLWRNORM = (1<<8), + POLLWRBAND = (1<<9), + POLLMSG = (1<<10), + POLLREMOVE = (1<<11), + POLLRDHUP = 0x2000, + EPOLLONESHOT = (1<<30), + EPOLLET = (1<<31), +}; + +enum { + SIGHUP = 1, + SIGINT = 2, + SIGQUIT = 3, + SIGILL = 4, + SIGTRAP = 5, + SIGABRT = 6, + SIGIOT = 6, + SIGBUS = 7, + SIGFPE = 8, + SIGKILL = 9, + SIGUSR1 = 10, + SIGSEGV = 11, + SIGUSR2 = 12, + SIGPIPE = 13, + SIGALRM = 14, + SIGTERM = 15, + SIGSTKFLT = 16, + SIGCHLD = 17, + SIGCONT = 18, + SIGSTOP = 19, + SIGTSTP = 20, + SIGTTIN = 21, + SIGTTOU = 22, + SIGURG = 23, + SIGXCPU = 24, + SIGXFSZ = 25, + SIGVTALRM = 26, + SIGPROF = 27, + SIGWINCH = 28, + SIGIO = 29, + SIGPOLL = 29, + SIGLOST = 29, + SIGPWR = 30, + SIGSYS = 31, + + SIGRT1 = 32, + SIGRT2 = 33, + SIGRT3 = 34, + SIGRT4 = 35, + SIGRT5 = 36, + SIGRT6 = 37, + SIGRT7 = 38, + SIGRT8 = 39, + + SIGMAX = 40, +}; + +enum { + SI_USER = 0, + SI_QUEUE = -1, + SI_TIMER = -2, + SI_MESGQ = -3, + SI_ASYNCIO = -4, + SI_SIGIO = -5, + SI_TKILL = -6, + SI_DETHREAD = -7, +}; + +enum { + ILL_ILLOPC = 1, + ILL_ILLOPN, + ILL_ILLADR, + ILL_ILLTRP, + ILL_PROVOPC, + ILL_PRVREG, + ILL_COPROC, + ILL_BADSTK, +}; + +enum { + FPE_INTDIV = 1, + FPE_INTOVF, + FPE_FLTDIV, + FPE_FLTOVF, + FPE_FLTUND, + FPE_FLTRES, + FPE_FLTINV, + FPE_FLTSUB, +}; + +enum { + WNOHANG =0x00000001, + WUNTRACED =0x00000002, + WSTOPPED =0x00000002, + WEXITED =0x00000004, + WCONTINUED =0x00000008, + WNOWAIT =0x01000000, + WNOTHREAD =0x20000000, + WALL =0x40000000, + WCLONE =0x80000000, +}; + +struct linux_timeval +{ + long tv_sec; + long tv_usec; +}; + +struct linux_timespec +{ + long tv_sec; + long tv_nsec; +}; + +struct linux_user_desc { + uint entry_number; + ulong base_addr; + uint limit; + uint seg_32bit:1; + int contents:2; + uint read_exec_only:1; + uint limit_in_pages:1; + uint seg_not_present:1; + uint useable:1; +}; diff --git a/linux_emul_base/linuxcall.c b/linux_emul_base/linuxcall.c new file mode 100644 index 0000000..26e4bbe --- /dev/null +++ b/linux_emul_base/linuxcall.c @@ -0,0 +1,79 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Linuxcall Linuxcall; + +struct Linuxcall +{ + char *name; + void *func; + int (*stub)(Ureg *, void *); +}; + +static int fcall0(Ureg *, void *func){return ((int (*)(void))func)();} +static int fcall1(Ureg *u, void *func){return ((int (*)(int))func)(u->bx);} +static int fcall2(Ureg *u, void *func){return ((int (*)(int, int))func)(u->bx, u->cx);} +static int fcall3(Ureg *u, void *func){return ((int (*)(int, int, int))func)(u->bx, u->cx, u->dx);} +static int fcall4(Ureg *u, void *func){return ((int (*)(int, int, int, int))func)(u->bx, u->cx, u->dx, u->si);} +static int fcall5(Ureg *u, void *func){return ((int (*)(int, int, int, int, int))func)(u->bx, u->cx, u->dx, u->si, u->di);} +static int fcall6(Ureg *u, void *func){return ((int (*)(int, int, int, int, int, int))func)(u->bx, u->cx, u->dx, u->si, u->di, u->bp);} + +#include "linuxcalltab.out" + +static Linuxcall nocall = { + .name = "nosys", + .func = sys_nosys, + .stub = fcall0, +}; + +static void +linuxret(int errno) +{ + Uproc *p; + Ureg *u; + + p = current; + u = p->ureg; + trace("linuxret(%lux: %s, %lux: %E)", u->pc, p->syscall, (ulong)errno, errno); + if(errno == -ERESTART){ + p->restart->syscall = p->syscall; + return; + } + u->ax = (ulong)errno; + u->pc += 2; + p->restart->syscall = nil; + p->syscall = nil; +} + + +int +linuxcall(void) +{ + Uproc *p; + Ureg *u; + Linuxcall *c; + uchar *pc; + + p = current; + u = p->ureg; + + /* CD 80 = INT 0x80 */ + pc = (uchar*)u->pc; + if(pc[0] != 0xcd || pc[1] != 0x80){ + trace("linuxcall(): not a syscall pc=%lux sp=%lux", u->pc, u->sp); + return -1; + } + c = &linuxcalltab[u->ax]; + if(c > &linuxcalltab[nelem(linuxcalltab)-1]) + c = &nocall; + p->syscall = c->name; + p->sysret = linuxret; + if(p->restart->syscall) + trace("linuxcall(): restarting %s", p->syscall); + linuxret(c->stub(u, c->func)); + return 0; +} diff --git a/linux_emul_base/linuxcalltab b/linux_emul_base/linuxcalltab new file mode 100644 index 0000000..031dbf0 --- /dev/null +++ b/linux_emul_base/linuxcalltab @@ -0,0 +1,286 @@ +0 0 restart_syscall sys_nosys +1 1 exit sys_exit +2 0 fork sys_fork +3 3 read sys_read +4 3 write sys_write +5 3 open sys_open +6 1 close sys_close +7 3 waitpid sys_waitpid +8 2 creat sys_creat +9 2 link sys_link +10 1 unlink sys_unlink +11 3 execve sys_execve +12 1 chdir sys_chdir +13 1 time sys_time +14 0 mknod sys_nosys +15 2 chmod sys_chmod +16 0 lchown sys_lchown +17 0 break sys_nosys +18 0 oldstat sys_nosys +19 3 lseek sys_lseek +20 0 getpid sys_getpid +21 0 mount sys_nosys +22 0 umount sys_nosys +23 1 setuid sys_setuid +24 0 getuid sys_getuid +25 0 stime sys_nosys +26 0 ptrace sys_nosys +27 1 alarm sys_alarm +28 0 oldfstat sys_nosys +29 0 pause sys_nosys +30 2 utime sys_utime +31 0 stty sys_nosys +32 0 gtty sys_nosys +33 2 access sys_access +34 0 nice sys_nosys +35 0 ftime sys_nosys +36 0 sync sys_nosys +37 2 kill sys_kill +38 2 rename sys_rename +39 2 mkdir sys_mkdir +40 1 rmdir sys_rmdir +41 1 dup sys_dup +42 1 pipe sys_pipe +43 1 times sys_times +44 0 prof sys_nosys +45 1 brk sys_brk +46 1 setgid sys_setgid +47 0 getgid sys_getgid +48 0 signal sys_nosys +49 0 geteuid sys_nosys +50 0 getegid sys_nosys +51 0 acct sys_nosys +52 0 umount2 sys_nosys +53 0 lock sys_nosys +54 3 ioctl sys_ioctl +55 3 fcntl sys_fcntl +56 0 mpx sys_nosys +57 2 setpgid sys_setpgid +58 0 ulimit sys_nosys +59 0 oldolduname sys_nosys +60 1 umask sys_umask +61 1 chroot sys_chroot +62 0 ustat sys_nosys +63 2 dup2 sys_dup2 +64 0 getppid sys_getppid +65 0 getpgrp sys_getpgrp +66 0 setsid sys_setsid +67 0 sigaction sys_nosys +68 0 sgetmask sys_nosys +69 0 ssetmask sys_nosys +70 0 setreuid sys_nosys +71 0 setregid sys_nosys +72 0 sigsuspend sys_nosys +73 0 sigpending sys_nosys +74 0 sethostname sys_nosys +75 2 setrlimit sys_setrlimit +76 2 getrlimit sys_getrlimit +77 0 getrusage sys_nosys +78 2 gettimeofday sys_gettimeofday +79 0 settimeofday sys_nosys +80 0 getgroups sys_nosys +81 0 setgroups sys_nosys +82 0 select sys_nosys +83 2 symlink sys_symlink +84 0 oldlstat sys_nosys +85 3 readlink sys_readlink +86 0 uselib sys_nosys +87 0 swapon sys_nosys +88 0 reboot sys_nosys +89 0 readdir sys_nosys +90 1 mmap sys_linux_mmap +91 2 munmap sys_munmap +92 2 truncate sys_truncate +93 2 ftruncate sys_ftruncate +94 2 fchmod sys_fchmod +95 0 fchown sys_fchown +96 0 getpriority sys_nosys +97 0 setpriority sys_nosys +98 0 profil sys_nosys +99 2 statfs sys_statfs +100 0 fstatfs sys_nosys +101 0 ioperm sys_nosys +102 2 socketcall sys_linux_socketcall +103 0 syslog sys_nosys +104 3 setitimer sys_setitimer +105 2 getitimer sys_getitimer +106 2 stat sys_linux_stat +107 2 lstat sys_linux_lstat +108 2 fstat sys_linux_fstat +109 0 olduname sys_nosys +110 0 iopl sys_nosys +111 0 vhangup sys_nosys +112 0 idle sys_nosys +113 0 vm86old sys_nosys +114 4 wait4 sys_wait4 +115 0 swapoff sys_nosys +116 0 sysinfo sys_nosys +117 0 ipc sys_nosys +118 1 fsync sys_fsync +119 0 sigreturn sys_sigreturn +120 5 clone sys_linux_clone +121 0 setdomainname sys_nosys +122 1 uname sys_uname +123 3 modify_ldt sys_modify_ldt +124 0 adjtimex sys_nosys +125 3 mprotect sys_mprotect +126 0 sigprocmask sys_nosys +127 0 create_module sys_nosys +128 0 init_module sys_nosys +129 0 delete_module sys_nosys +130 0 get_kernel_syms sys_nosys +131 0 quotactl sys_nosys +132 1 getpgid sys_getpgid +133 1 fchdir sys_fchdir +134 0 bdflush sys_nosys +135 0 sysfs sys_nosys +136 1 personality sys_personality +137 0 afs_syscall sys_nosys +138 0 setfsuid sys_nosys +139 0 setfsgid sys_nosys +140 5 _llseek sys_llseek +141 3 getdents sys_linux_getdents +142 5 _newselect sys_select +143 0 flock sys_flock +144 3 msync sys_msync +145 3 readv sys_readv +146 3 writev sys_writev +147 1 getsid sys_getsid +148 0 fdatasync sys_nosys +149 0 _sysctl sys_nosys +150 0 mlock sys_nosys +151 0 munlock sys_nosys +152 0 mlockall sys_nosys +153 0 munlockall sys_nosys +154 2 sched_setparam sys_sched_setparam +155 2 sched_getparam sys_sched_getparam +156 3 sched_setscheduler sys_sched_setscheduler +157 1 sched_getscheduler sys_sched_getscheduler +158 0 sched_yield sys_sched_yield +159 0 sched_get_priority_max sys_nosys +160 0 sched_get_priority_min sys_nosys +161 0 sched_rr_get_interval sys_nosys +162 2 nanosleep sys_nanosleep +163 5 mremap sys_mremap +164 3 setresuid sys_setresuid +165 3 getresuid sys_getresuid +166 0 vm86 sys_nosys +167 0 query_module sys_nosys +168 3 poll sys_poll +169 0 nfsservctl sys_nosys +170 3 setresgid sys_setresgid +171 3 getresgid sys_getresgid +172 0 prctl sys_nosys +173 0 rt_sigreturn sys_rt_sigreturn +174 4 rt_sigaction sys_rt_sigaction +175 4 rt_sigprocmask sys_rt_sigprocmask +176 2 rt_sigpending sys_rt_sigpending +177 0 rt_sigtimedwait sys_nosys +178 3 rt_sigqueueinfo sys_rt_sigqueueinfo +179 2 rt_sigsuspend sys_rt_sigsuspend +180 4 pread64 sys_pread64 +181 4 pwrite64 sys_pwrite64 +182 0 chown sys_chown +183 2 getcwd sys_getcwd +184 0 capget sys_nosys +185 0 capset sys_nosys +186 2 sigaltstack sys_sigaltstack +187 0 sendfile sys_nosys +188 0 getpmsg sys_nosys +189 0 putpmsg sys_nosys +190 0 vfork sys_vfork +191 0 ugetrlimit sys_nosys +192 6 mmap2 sys_mmap +193 2 truncate64 sys_truncate +194 2 ftruncate64 sys_ftruncate +195 2 stat64 sys_linux_stat64 +196 2 lstat64 sys_linux_lstat64 +197 2 fstat64 sys_linux_fstat64 +198 3 lchown32 sys_lchown +199 0 getuid32 sys_getuid +200 0 getgid32 sys_getgid +201 0 geteuid32 sys_getuid +202 0 getegid32 sys_getgid +203 2 setreuid32 sys_setreuid +204 2 setregid32 sys_setregid +205 2 getgroups32 sys_getgroups +206 2 setgroups32 sys_setgroups +207 3 fchown32 sys_fchown +208 3 setresuid32 sys_setresuid +209 3 getresuid32 sys_getresuid +210 3 setresgid32 sys_setresgid +211 3 getresgid32 sys_getresgid +212 3 chown32 sys_chown +213 1 setuid32 sys_setuid +214 1 setgid32 sys_setgid +215 0 setfsuid32 sys_nosys +216 0 setfsgid32 sys_nosys +217 0 pivot_root sys_nosys +218 0 mincore sys_nosys +219 0 madvise sys_nosys +220 3 getdents64 sys_linux_getdents64 +221 3 fcntl64 sys_fcntl +224 0 gettid sys_gettid +225 0 readahead sys_nosys +226 5 setxattr sys_setxattr +227 5 lsetxattr sys_lsetxattr +228 5 fsetxattr sys_fsetxattr +229 4 getxattr sys_getxattr +230 4 lgetxattr sys_lgetxattr +231 4 fgetxattr sys_fgetxattr +232 0 listxattr sys_nosys +233 0 llistxattr sys_nosys +234 0 flistxattr sys_nosys +235 0 removexattr sys_nosys +236 0 lremovexattr sys_nosys +237 0 fremovexattr sys_nosys +238 2 tkill sys_tkill +239 0 sendfile64 sys_nosys +240 6 futex sys_futex +241 0 sched_setaffinity sys_nosys +242 0 sched_getaffinity sys_nosys +243 1 set_thread_area sys_set_thread_area +244 1 get_thread_area sys_get_thread_area +245 0 io_setup sys_nosys +246 0 io_destroy sys_nosys +247 0 io_getevents sys_nosys +248 0 io_submit sys_nosys +249 0 io_cancel sys_nosys +250 0 fadvise64 sys_nosys +252 1 exit_group sys_exit_group +253 0 lookup_dcookie sys_nosys +254 0 epoll_create sys_nosys +255 0 epoll_ctl sys_nosys +256 0 epoll_wait sys_nosys +257 0 remap_file_pages sys_nosys +258 1 set_tid_address sys_set_tid_address +259 0 timer_create sys_nosys +260 0 timer_settime sys_nosys +261 0 timer_gettime sys_nosys +262 0 timer_getoverrun sys_nosys +263 0 timer_delete sys_nosys +264 0 clock_settime sys_nosys +265 2 clock_gettime sys_clock_gettime +266 0 clock_getres sys_nosys +267 0 clock_nanosleep sys_nosys +268 0 statfs64 sys_nosys +269 0 fstatfs64 sys_nosys +270 0 tgkill sys_tgkill +271 2 utimes sys_utimes +272 0 fadvise64_64 sys_nosys +273 0 vserver sys_nosys +274 0 mbind sys_nosys +275 0 get_mempolicy sys_nosys +276 0 set_mempolicy sys_nosys +277 0 mq_open sys_nosys +278 0 mq_unlink sys_nosys +279 0 mq_timedsend sys_nosys +280 0 mq_timedreceive sys_nosys +281 0 mq_notify sys_nosys +282 0 mq_getsetattr sys_nosys +283 0 sys_kexec_load sys_nosys +284 0 waitid sys_nosys +285 0 setaltroot sys_nosys +286 0 add_key sys_nosys +287 0 request_key sys_nosys +288 0 keyctl sys_nosys diff --git a/linux_emul_base/linuxcalltab.awk b/linux_emul_base/linuxcalltab.awk new file mode 100755 index 0000000..d750cdb --- /dev/null +++ b/linux_emul_base/linuxcalltab.awk @@ -0,0 +1,39 @@ +#!/bin/awk -f +BEGIN { + nsys = 0 +} + +/^#/ { + next +} + +{ + i=$1 + if(nsys > i){ + print "BROKEN TABLE: "nsys" > "i + exit + } + while(nsys < i){ + sysarg[nsys] = 0 + sysnam[nsys] = "nosys"nsys + sysfun[nsys] = "sys_nosys" + nsys++; + } + sysarg[nsys] = $2 + sysnam[nsys] = $3 + sysfun[nsys] = $4 + nsys++ +} + +END { + print "static Linuxcall linuxcalltab[] = {" + for(i=0; i +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +static void +die(void) +{ + exits(nil); +} + +static char** +readenv(void) +{ + char **env; + int fd, n, i, c; + Dir *d; + + if((fd = open("/env", OREAD)) < 0) + return nil; + n = dirreadall(fd, &d); + close(fd); + env = kmalloc(sizeof(env[0]) * (n + 1)); + c = 0; + for(i=0; i= stk && (long*)sp < stk+(KSTACK / sizeof(long))) + return func(arg); + + if(args = (struct onstackargs*)setjmp(jmp)){ + args->ret = onstack(args->stk, args->func, args->arg); + longjmp(args->jmp, 1); + } + + sp = &stk[(KSTACK / sizeof(long))-16]; + jmp[JMPBUFSP] = (long)sp; + + memset(stk, 0, KSTACK); + + args = &a; + args->stk = stk; + args->func = func; + args->arg = arg; + + if(!setjmp(args->jmp)) + longjmp(jmp, (int)args); + + return args->ret; +} + +#pragma profile off + +static void +proff(void (*fn)(void*), void *arg) +{ + if(_tos->prof.what == 0){ + fn(arg); + }else{ + prof(fn, arg, 2000, _tos->prof.what); + } +} + +static void +profexitjmpfn(void *arg) +{ + /* + * we are now called by the profiling function on the profstack. + * save the current continuation so we can return here on exit. + */ + if(!setjmp(exitjmp)) + longjmp((long*)arg, 1); /* return from profme() */ +} + +static int +profmeprofstack(void *arg) +{ + proff(profexitjmpfn, arg); + for(;;) die(); +} + +#pragma profile on + +static long *profstack; + +void +profme(void) +{ + jmp_buf j; + + if(!setjmp(j)) + onstack(profstack, profmeprofstack, j); +} + + +static void +vpanic(char *msg, va_list arg) +{ + char buf[32]; + int fd; + + fprint(2, "PANIC: "); + vfprint(2, msg, arg); + fprint(2, "\n"); + + if(debug) + abort(); + + snprint(buf, sizeof(buf), "/proc/%d/notepg", getpid()); + if((fd = open(buf, OWRITE)) >= 0){ + write(fd, "kill", 4); + close(fd); + } + exits("panic"); +} + +void +panic(char *msg, ...) +{ + va_list arg; + + va_start(arg, msg); + vpanic(msg, arg); + va_end(arg); +} + +void usage(void) +{ + fprint(2, "usage: linuxemu [-d] [-u uid] [-g gid] cmd [args]\n"); + exits("usage"); +} + +struct mainstack +{ + long profstack[KSTACK / sizeof(long)]; + long kstack[KSTACK / sizeof(long)]; + Uproc *proc; + jmp_buf exitjmp; +}; + +void main(int argc, char *argv[]) +{ + struct mainstack ms; + int err; + int uid, gid; + int fd; + + fmtinstall('E', Efmt); + fmtinstall('S', Sfmt); + + uid = 0; + gid = 0; + debug = 0; + + ARGBEGIN { + case 'd': + debug++; + break; + case 'u': + uid = atoi(EARGF(usage())); + break; + case 'g': + gid = atoi(EARGF(usage())); + break; + default: + usage(); + } ARGEND + + if(argc < 1) + usage(); + + rootdevinit(); + procdevinit(); + ptydevinit(); + consdevinit(); + dspdevinit(); + miscdevinit(); + sockdevinit(); + pipedevinit(); + + kstack = ms.kstack; + profstack = ms.profstack; + exitjmp = ms.exitjmp; + pcurrent = &ms.proc; + current = nil; + + if(setjmp(exitjmp)) + die(); + + initproc(); + current->uid = uid; + current->gid = gid; + + /* emulated console */ + sys_close(0); + if((fd = sys_open("/dev/cons", O_RDWR, 0)) != 0) + fprint(2, "cant open console for stdin\n"); + sys_close(1); + if(sys_dup(fd) != 1) + fprint(2, "cant dup stdout\n"); + sys_close(2); + if(sys_dup(fd) != 2) + fprint(2, "cant dup stderr\n"); + + sys_fcntl(0, F_SETFD, 0); + sys_fcntl(1, F_SETFD, 0); + sys_fcntl(2, F_SETFD, 0); + + err = sys_execve(*argv, argv, readenv()); + + fprint(2, "%s: %E\n", *argv, err); + longjmp(exitjmp, 1); +} diff --git a/linux_emul_base/mem.c b/linux_emul_base/mem.c new file mode 100644 index 0000000..996cafb --- /dev/null +++ b/linux_emul_base/mem.c @@ -0,0 +1,1538 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Range Range; +typedef struct Area Area; +typedef struct Filemap Filemap; +typedef struct Futex Futex; +typedef struct Seg Seg; +typedef struct Space Space; + +/* keep in order, lowest base address first */ +enum { + SEGDATA, + SEGPRIVATE, + SEGSHARED, + SEGSTACK, + SEGMAX, +}; + +static char *segname[SEGMAX] = { "data", "private", "shared", "stack" }; + +struct Range +{ + ulong base; + ulong top; +}; + +struct Filemap +{ + Range addr; + + Filemap *next; + + char *path; + ulong offset; + int mode; + Ufile *file; + + Ref; +}; + +struct Futex +{ + ulong *addr; + + Futex *next; + Futex **link; + + Ref; + Uwaitq; +}; + +struct Area +{ + Range addr; + + Area *next; /* next higher area */ + Area *prev; /* previous lower area */ + Seg *seg; /* segment we belong to */ + + int prot; + + Filemap *filemap; + Futex *futex; +}; + +struct Seg +{ + Ref; + QLock; + + Range addr; + ulong limit; /* maximum address this segment can grow */ + + Area *areas; /* orderd by address */ + + int type; /* SEGDATA, SEGSHARED, SEGPRIVATE, SEGSTACK */ + + Area *freearea; + Filemap *freefilemap; + Futex *freefutex; +}; + +struct Space +{ + Ref; + QLock; + + ulong brk; + Seg *seg[SEGMAX]; +}; + + +void* +kmalloc(int size) +{ + void *p; + + p = malloc(size); + if(p == nil) + panic("kmalloc: out of memory"); + setmalloctag(p, getcallerpc(&size)); + return p; +} +void* +krealloc(void *ptr, int size) +{ + void *p; + + p = realloc(ptr, size); + if(size > 0){ + if(p == nil) + panic("krealloc: out of memory"); + setmalloctag(p, getcallerpc(&ptr)); + } + return p; +} + +void* +kmallocz(int size, int zero) +{ + void *p; + + p = mallocz(size, zero); + if(p == nil) + panic("kmallocz: out of memory"); + setmalloctag(p, getcallerpc(&size)); + return p; +} + +char* +kstrdup(char *s) +{ + char *p; + int n; + + n = strlen(s); + p = kmalloc(n+1); + memmove(p, s, n); + p[n] = 0; + setmalloctag(p, getcallerpc(&s)); + return p; +} + +char* +ksmprint(char *fmt, ...) +{ + va_list args; + char *p; + int n; + + n = 4096; + p = kmalloc(n); + va_start(args, fmt); + n = vsnprint(p, n, fmt, args); + va_end(args); + if((p = realloc(p, n+1)) == nil) + panic("ksmprint: out of memory"); + setmalloctag(p, getcallerpc(&fmt)); + return p; +} + +ulong +pagealign(ulong addr) +{ + ulong m; + + m = PAGESIZE-1; + return (addr + m) & ~m; +} + +static void +syncarea(Area *a, Range r) +{ + if(a->filemap == nil) + return; + if(a->filemap->file == nil) + return; + if((a->prot & PROT_WRITE) == 0) + return; + + if(r.base < a->addr.base) + r.base = a->addr.base; + if(r.top > a->addr.top) + r.top = a->addr.top; + if(r.base < a->filemap->addr.base) + r.base = a->filemap->addr.base; + if(r.top > a->filemap->addr.top) + r.top = a->filemap->addr.top; + pwritefile(a->filemap->file, (void*)r.base, r.top - r.base, + (r.base - a->filemap->addr.base) + a->filemap->offset); +} + +static void +linkarea(Seg *seg, Area *a) +{ + Area *p; + + a->next = nil; + a->prev = nil; + a->seg = seg; + + for(p = seg->areas; p && p->next; p=p->next) + if(p->addr.base > a->addr.base) + break; + if(p != nil){ + if(p->addr.base > a->addr.base){ + a->next = p; + if(a->prev = p->prev) + a->prev->next = a; + p->prev = a; + } else { + a->prev = p; + p->next = a; + } + } + if(a->prev == nil) + seg->areas = a; +} + +static Area * +duparea(Area *a) +{ + Area *r; + + if(r = a->seg->freearea){ + a->seg->freearea = r->next; + } else { + r = kmalloc(sizeof(Area)); + } + r->addr = a->addr; + r->next = nil; + r->prev = nil; + r->seg = nil; + r->prot = a->prot; + if(r->filemap = a->filemap) + incref(r->filemap); + r->futex = nil; + return r; +} + +static void +freearea(Area *a) +{ + Filemap *f; + Futex *x; + Seg *seg; + + seg = a->seg; + if(f = a->filemap){ + syncarea(a, a->addr); + a->filemap = nil; + if(!decref(f)){ + free(f->path); + putfile(f->file); + f->next = seg->freefilemap; + seg->freefilemap = f; + } + } + while(x = a->futex){ + if(a->futex = x->next) + x->next->link = &a->futex; + x->link = nil; + x->next = nil; + wakeq(x, MAXPROC); + } + if(a->prev == nil){ + if(seg->areas = a->next) + a->next->prev = nil; + } else { + if(a->prev->next = a->next) + a->next->prev = a->prev; + } + + a->next = seg->freearea; + seg->freearea = a; +} + +static Seg * +allocseg(int type, Range addr, ulong limit, int attr, char *class) +{ + Seg *seg; + + if(class){ + trace("allocseg(): segattach %s segment %lux-%lux", segname[type], addr.base, addr.top); + if(segattach(attr, class, (void*)addr.base, addr.top - addr.base) != (void*)addr.base) + panic("allocseg: segattach %s segment: %r", segname[type]); + } + + seg = kmallocz(sizeof(Seg), 1); + seg->addr = addr; + seg->limit = limit; + seg->type = type; + seg->ref = 1; + + return seg; +} + +static Seg * +dupseg(Seg *old, int copy) +{ + Seg *new; + Area *a, *p, *x; + + if(old == nil) + return nil; + if(!copy){ + incref(old); + return old; + } + new = allocseg(old->type, old->addr, old->limit, 0, nil); + p = nil; + for(a=old->areas; a; a=a->next){ + x = duparea(a); + x->seg = new; + if(x->prev = p){ + p->next = x; + } else { + new->areas = x; + } + p = x; + } + + return new; +} + +static Space * +getspace(Space *old, int copy) +{ + Space *new; + Seg *seg; + int t; + + if(!copy){ + incref(old); + return old; + } + + new = kmallocz(sizeof(Space), 1); + new->ref = 1; + + qlock(old); + for(t=0; tseg[t]){ + qlock(seg); + new->seg[t] = dupseg(seg, t != SEGSHARED); + qunlock(seg); + } + } + new->brk = old->brk; + qunlock(old); + + return new; +} + +static void +putspace(Space *space) +{ + Seg *seg; + int t; + Area *a; + Filemap *f; + Futex *x; + void *addr; + + if(decref(space)) + return; + for(t=0; tseg[t]){ + addr = (void*)seg->addr.base; + if(!decref(seg)){ + qlock(seg); + /* mark all areas as free */ + while(a = seg->areas) + freearea(a); + + /* clear the free lists */ + while(a = seg->freearea){ + seg->freearea = a->next; + free(a); + } + while(f = seg->freefilemap){ + seg->freefilemap = f->next; + free(f); + } + while(x = seg->freefutex){ + seg->freefutex = x->next; + free(x); + } + free(seg); + } + if(segdetach(addr) < 0) + panic("putspace: segdetach %s segment: %r", segname[t]); + } + } + free(space); +} + +static int +canmerge(Area *a, Area *b) +{ + return a->filemap==nil && + a->futex==nil && + b->filemap==nil && + b->futex==nil && + a->prot == b->prot; +} + +static void +mergearea(Area *a) +{ + if(a->prev && a->prev->addr.top == a->addr.base && canmerge(a->prev, a)){ + a->addr.base = a->prev->addr.base; + freearea(a->prev); + } + if(a->next && a->next->addr.base == a->addr.top && canmerge(a->next, a)){ + a->addr.top = a->next->addr.top; + freearea(a->next); + } +} + +static int +findhole(Seg *seg, Range *r, int fixed) +{ + Range h; + Area *a; + ulong m; + ulong z; + ulong hz; + + z = r->top - r->base; + m = ~0; + h.base = seg->addr.base; + a = seg->areas; + for(;;) { + if((h.top = a ? a->addr.base : seg->addr.top) > h.base) { + if(fixed){ + if(h.base > r->base) + break; + if((r->base >= h.base) && (r->top <= h.top)) + goto found; + } else { + hz = h.top - h.base; + if((hz >= z) && (hz < m)) { + r->base = h.top - z; + r->top = h.top; + if((m = hz) == z) + goto found; + } + } + } + if(a == nil) + break; + h.base = a->addr.top; + a = a->next; + } + if(!fixed && (m != ~0)) + goto found; + return 0; + +found: + return 1; +} + +/* wake up all futexes in range and unlink from area */ +static void +wakefutexarea(Area *a, Range addr) +{ + Futex *fu, *x; + + for(fu = a->futex; fu; fu = x){ + x = fu->next; + if((ulong)fu->addr >= addr.base && (ulong)fu->addr < addr.top){ + if(*fu->link = x) + x->link = fu->link; + fu->link = nil; + fu->next = nil; + + trace("wakefutexarea: fu=%p addr=%p", fu, fu->addr); + wakeq(fu, MAXPROC); + } + } +} + +static void +makehole(Seg *seg, Range r) +{ + Area *a, *b, *x; + Range f; + + for(a = seg->areas; a; a = x){ + x = a->next; + + if(a->addr.top <= r.base) + continue; + if(a->addr.base >= r.top) + break; + + f = r; + if(f.base < a->addr.base) + f.base = a->addr.base; + if(f.top > a->addr.top) + f.top = a->addr.top; + + wakefutexarea(a, f); + if(f.base == a->addr.base){ + if(f.top == a->addr.top){ + freearea(a); + } else { + a->addr.base = f.top; + } + } else if(f.top == a->addr.top){ + a->addr.top = f.base; + } else { + b = duparea(a); + b->addr.base = f.top; + + a->addr.top = f.base; + linkarea(seg, b); + } + + if(segfree((void*)f.base, f.top - f.base) < 0) + panic("makehole: segfree %s segment: %r", segname[seg->type]); + } +} + +static Seg* +addr2seg(Space *space, ulong addr) +{ + Seg *seg; + int t; + + for(t=0; tseg[t]) == nil) + continue; + qlock(seg); + if((addr >= seg->addr.base) && (addr < seg->addr.top)) + return seg; + qunlock(seg); + } + + return nil; +} + +static Area* +addr2area(Seg *seg, ulong addr) +{ + Area *a; + + for(a=seg->areas; a; a=a->next) + if((addr >= a->addr.base) && (addr < a->addr.top)) + return a; + return nil; +} + +int +okaddr(void *ptr, int len, int write) +{ + ulong addr; + Space *space; + Seg *seg; + Area *a; + int ok; + + ok = 0; + addr = (ulong)ptr; + if(addr < PAGESIZE) + goto out; + if(space = current->mem){ + qlock(space); + if(seg = addr2seg(space, addr)){ + while(a = addr2area(seg, addr)){ + if(write){ + if((a->prot & PROT_WRITE) == 0) + break; + } else { + if((a->prot & PROT_READ) == 0) + break; + } + if((ulong)ptr + len <= a->addr.top){ + ok = 1; + break; + } + addr = a->addr.top; + } + qunlock(seg); + } + qunlock(space); + } +out: + trace("okaddr(%lux-%lux, %d) -> %d", addr, addr+len, write, ok); + return ok; +} + +static void +unmapspace(Space *space, Range r) +{ + Seg *seg; + int t; + + for(t=0; tseg[t]) == nil) + continue; + qlock(seg); + if(seg->addr.base >= r.top){ + qunlock(seg); + break; + } + if(seg->addr.top > r.base) + makehole(seg, r); + qunlock(seg); + } +} + +static Area* +mapspace(Space *space, Range r, int flags, int prot, int *perr) +{ + Seg *seg; + Area *a; + Range f; + int t; + + if(flags & MAP_PRIVATE){ + if(r.base >= space->seg[SEGSTACK]->addr.base){ + t = SEGSTACK; + } else if(r.base >= space->seg[SEGDATA]->addr.base && + r.base < space->seg[SEGDATA]->limit){ + t = SEGDATA; + } else { + t = SEGPRIVATE; + } + } else { + t = SEGSHARED; + } + + if((seg = space->seg[t]) == nil) + goto nomem; + + qlock(seg); + if((r.base >= seg->addr.base) && (r.top <= seg->limit)){ + if(r.base >= seg->addr.top) + goto addrok; + + f = r; + if(f.top > seg->addr.top) + f.top = seg->addr.top; + if(findhole(seg, &f, 1)) + goto addrok; + if(flags & MAP_FIXED){ + if(seg->type == SEGSHARED){ + trace("mapspace(): cant make hole %lux-%lux in shared segment", + f.base, f.top); + goto nomem; + } + makehole(seg, f); + goto addrok; + } + } + + if(flags & MAP_FIXED){ + trace("mapspace(): no free hole for fixed mapping %lux-%lux in %s segment", + r.base, r.top, segname[seg->type]); + goto nomem; + } + + if(findhole(seg, &r, 0)) + goto addrok; + + r.top -= r.base; + r.base = seg->addr.top; + r.top += r.base; + +addrok: + trace("mapspace(): addr %lux-%lux", r.base, r.top); + + if(r.top > seg->addr.top){ + if(r.top > seg->limit){ + trace("mapspace(): area top %lux over %s segment limit %lux", + r.top, segname[seg->type], seg->limit); + goto nomem; + } + trace("mapspace(): segbrk %s segment %lux-%lux -> %lux", + segname[seg->type], seg->addr.base, seg->addr.top, r.top); + if(segbrk((void*)seg->addr.base, (void*)r.top) == (void*)-1){ + trace("mapspace(): segbrk failed: %r"); + goto nomem; + } + seg->addr.top = r.top; + } + + if(a = seg->freearea){ + seg->freearea = a->next; + } else { + a = kmalloc(sizeof(Area)); + } + a->addr = r; + a->prot = prot; + a->filemap = nil; + a->futex = nil; + + linkarea(seg, a); + + /* keep seg locked */ + return a; + +nomem: + if(seg != nil) + qunlock(seg); + if(perr) *perr = -ENOMEM; + return nil; +} + +static ulong +brkspace(Space *space, ulong bk) +{ + Seg *seg; + Area *a; + ulong old, new; + Range r; + + if((seg = space->seg[SEGDATA]) == nil) + goto out; + + qlock(seg); + if(space->brk < seg->addr.base) + space->brk = seg->addr.top; + + if(bk < seg->addr.base) + goto out; + + old = pagealign(space->brk); + new = pagealign(bk); + + if(old != new){ + if(bk < space->brk){ + r.base = new; + r.top = old; + qunlock(seg); + seg = nil; + + unmapspace(space, r); + } else { + r.base = old; + r.top = new; + + trace("brkspace(): new mapping %lux-%lux", r.base, r.top); + for(a = addr2area(seg, old - PAGESIZE); a; a = a->next){ + if(a->addr.top <= r.base) + continue; + if(a->addr.base > r.top + PAGESIZE) + break; + + trace("brkspace(): mapping %lux-%lux is in the way", a->addr.base, a->addr.top); + goto out; + } + qunlock(seg); + seg = nil; + + a = mapspace(space, r, + MAP_ANONYMOUS|MAP_PRIVATE|MAP_FIXED, + PROT_READ|PROT_WRITE|PROT_EXEC, nil); + + if(a == nil) + goto out; + + seg = a->seg; + mergearea(a); + } + } + + if(space->brk != bk){ + trace("brkspace: set new brk %lux", bk); + space->brk = bk; + } + +out: + if(seg != nil) + qunlock(seg); + + return space->brk; +} + +static ulong +remapspace(Space *space, ulong addr, ulong oldlen, ulong newlen, ulong newaddr, int flags) +{ + Area *a; + Seg *seg; + int move; + Range r; + + if(pagealign(addr) != addr) + return -EINVAL; + + oldlen = pagealign(oldlen); + newlen = pagealign(newlen); + + if((addr + oldlen) < addr) + return -EINVAL; + if((addr + newlen) <= addr) + return -EINVAL; + + move = 0; + if(flags & MREMAP_FIXED){ + if(pagealign(newaddr) != newaddr) + return -EINVAL; + if((flags & MREMAP_MAYMOVE) == 0) + return -EINVAL; + if((newaddr <= addr) && ((newaddr+newlen) > addr)) + return -EINVAL; + if((addr <= newaddr) && ((addr+oldlen) > newaddr)) + return -EINVAL; + move = (newaddr != addr); + } + + if(newlen < oldlen){ + r.base = addr + newlen; + r.top = addr + oldlen; + + unmapspace(space, r); + + oldlen = newlen; + } + + if((newlen == oldlen) && !move) + return addr; + + if((seg = addr2seg(space, addr)) == nil) + return -EFAULT; + + if((a = addr2area(seg, addr)) == nil) + goto fault; + if(a->addr.top < (addr + oldlen)) + goto fault; + + if(move) + goto domove; + if((addr + oldlen) != a->addr.top) + goto domove; + if((addr + newlen) > seg->limit) + goto domove; + if(a->next != nil) + if((addr + newlen) > a->next->addr.base) + goto domove; + + if((addr + newlen) > seg->addr.top){ + trace("remapspace(): segbrk %s segment %lux-%lux -> %lux", + segname[seg->type], seg->addr.base, seg->addr.top, (addr + newlen)); + if(segbrk((void*)seg->addr.base, (void*)(addr + newlen)) == (void*)-1){ + trace("remapspace(): segbrk: %r"); + goto domove; + } + + seg->addr.top = (addr + newlen); + } + a->addr.top = (addr + newlen); + mergearea(a); + qunlock(seg); + + return addr; + +domove: + trace("remapspace(): domove not implemented"); + if(seg != nil) + qunlock(seg); + return -ENOMEM; + +fault: + if(seg != nil) + qunlock(seg); + return -EFAULT; +} + +static void +syncspace(Space *space, Range r) +{ + Seg *seg; + Area *a; + + if(seg = addr2seg(space, r.base)){ + for(a = addr2area(seg, r.base); a; a=a->next){ + if(r.base >= a->addr.top) + break; + syncarea(a, r); + } + qunlock(seg); + } +} + +void* +mapstack(int size) +{ + Space *space; + ulong a; + + space = current->mem; + a = space->seg[SEGSTACK]->addr.top; + size = pagealign(size); + a = sys_mmap(a - size, size, + PROT_READ|PROT_WRITE, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); + if(a == 0) + return nil; + + return (void*)(a + size); +} + +void +mapdata(ulong base) +{ + Space *space; + Range r; + ulong top; + int t; + + space = current->mem; + base = pagealign(base); + top = space->seg[SEGSTACK]->addr.base - PAGESIZE; + + for(t=0; tseg[t] == nil){ + switch(t){ + case SEGDATA: + r.base = base; + break; + case SEGPRIVATE: + r.base = base + 0x10000000; + break; + case SEGSHARED: + r.base = top - 0x10000000; + break; + } + r.top = r.base + PAGESIZE; + space->seg[t] = allocseg(t, r, r.top, 0, (t == SEGSHARED) ? "shared" : "memory"); + } + if(t > 0 && space->seg[t-1]) + space->seg[t-1]->limit = space->seg[t]->addr.base - PAGESIZE; + } +} + +/* + * unmapuserspace is called from kprocfork to get rid of + * the linux memory segments used by the calling process + * before current is set to zero. we just segdetach() all that + * segments but keep the data structures valid for the calling + * (linux) process. + */ +void +unmapuserspace(void) +{ + Space *space; + Seg *seg; + int t; + + space = current->mem; + qlock(space); + for(t=0; tseg[t]) == nil) + continue; + if(segdetach((void*)seg->addr.base) < 0) + panic("unmapuserspace: segdetach %s segment: %r", segname[seg->type]); + } + qunlock(space); +} + +/* hack: + * we write segment out into a file, detach it and reattach + * a new one and reading contents back. i'm surprised that + * this even works seamless with the Plan9 Bss! :-) + */ +static void +convertseg(Range r, ulong attr, char *class) +{ + char name[64]; + ulong p; + int n; + int fd; + ulong len; + + snprint(name, sizeof(name), "/tmp/seg%s%d", class, getpid()); + fd = create(name, ORDWR|ORCLOSE, 0600); + if(fd < 0) + panic("convertseg: cant create %s: %r", name); + + len = r.top - r.base; + + if(len > 0){ + n = write(fd, (void*)r.base, len); + if(n != len) + panic("convertseg: write: %r"); + } + + /* copy string to stack because its memory gets detached :-) */ + strncpy(name, class, sizeof(name)); + + trace("detaching %lux-%lux", r.base, r.top); + + /* point of no return */ + if(segdetach((void*)r.base) < 0) + panic("convertseg: segdetach: %r"); + if(segattach(attr, name, (void*)r.base, len) != (void*)r.base) + *((int*)0) = 0; + + p = 0; + while(p < len) { + /* + * we use pread directly to avoid hitting profiling code until + * data segment is read back again. pread is unprofiled syscall + * stub. + */ + n = pread(fd, (void*)(r.base + p), len - p, (vlong)p); + if(n <= 0) + *((int*)0) = 0; + p += n; + } + + /* anything normal again */ + trace("segment %lux-%lux reattached as %s", r.base, r.top, class); + + close(fd); +} + +void initmem(void) +{ + Space *space; + Range r, x; + char buf[80]; + int fd; + int n; + + static int firsttime = 1; + + space = kmallocz(sizeof(Space), 1); + space->ref = 1; + + snprint(buf, sizeof(buf), "/proc/%d/segment", getpid()); + if((fd = open(buf, OREAD)) < 0) + panic("initspace: cant open %s: %r", buf); + + n = 10 + 9 + 9 + 4 + 1; + x.base = x.top = 0; + while(readn(fd, buf, n)==n){ + char *name; + + buf[8] = 0; + buf[18] = 0; + buf[28] = 0; + buf[33] = 0; + + name = &buf[0]; + r.base = strtoul(&buf[9], nil, 16); + r.top = strtoul(&buf[19], nil, 16); + + trace("initspace(): %s %lux-%lux", name, r.base, r.top); + + if(firsttime){ + /* + * convert Plan9 data+bss segments into shared segments so + * that the memory of emulator data structures gets shared across + * all processes. This only happens if initspace() is called the first time. + */ + if(strstr(name, "Data")==name) + convertseg(r, 0, "shared"); + if(strstr(name, "Bss")==name) + convertseg(r, 0, "shared"); + } + + if(strstr(name, "Stack")==name){ + x.top = r.base - PAGESIZE; + x.base = x.top - pagealign((MAXPROC / 4) * USTACK); + + if(!firsttime) + break; + } + } + close(fd); + firsttime = 0; + + /* allocate the linux stack */ + space->seg[SEGSTACK] = allocseg(SEGSTACK, x, x.top, 0, "memory"); + + current->mem = space; +} + +void exitmem(void) +{ + Space *space; + + if(space = current->mem){ + current->mem = nil; + putspace(space); + } +} + +void clonemem(Uproc *new, int copy) +{ + Space *space; + + if((space = current->mem) == nil){ + new->mem = nil; + return; + } + new->mem = getspace(space, copy); +} + +ulong procmemstat(Uproc *proc, ulong *pdat, ulong *plib, ulong *pshr, ulong *pstk, ulong *pexe) +{ + Space *space; + ulong size, z; + int i; + + if(pdat) *pdat = 0; + if(plib) *plib = 0; + if(pshr) *pshr = 0; + if(pstk) *pstk = 0; + if(pexe) *pexe = 0; + + if((space = proc->mem) == nil) + return 0; + + size = 0; + qlock(space); + for(i=0; iseg[i]) == nil) + continue; + qlock(seg); + for(a = seg->areas; a; a = a->next){ + z = a->addr.top - a->addr.base; + switch(i){ + case SEGDATA: + if(pdat) + *pdat += z; + case SEGPRIVATE: + if(plib) + *plib += z; + break; + case SEGSHARED: + if(pshr) + *pshr += z; + break; + case SEGSTACK: + if(pstk) + *pstk += z; + break; + } + if(pexe && (a->prot & PROT_EXEC)) + *pexe += z; + size += z; + } + qunlock(seg); + } + qunlock(space); + + return size; +} + +struct linux_mmap_args { + ulong addr; + int len; + int prot; + int flags; + int fd; + ulong offset; +}; + +ulong +sys_linux_mmap(void *a) +{ + struct linux_mmap_args *p = a; + + if(pagealign(p->offset) != p->offset) + return -EINVAL; + + return sys_mmap( + p->addr, + p->len, + p->prot, + p->flags, + p->fd, + p->offset / PAGESIZE); +} + +ulong +sys_mmap(ulong addr, ulong len, int prot, int flags, int fd, ulong pgoff) +{ + Space *space; + Seg *seg; + Range r; + ulong o; + int e, n; + Area *a; + Filemap *f; + Ufile *file; + + trace("sys_mmap(%lux, %lux, %d, %d, %d, %lux)", addr, len, prot, flags, fd, pgoff); + + if(pagealign(addr) != addr) + return (ulong)-EINVAL; + + r.base = addr; + r.top = addr + pagealign(len); + if(r.top <= r.base) + return (ulong)-EINVAL; + + file = nil; + if((flags & MAP_ANONYMOUS)==0) + if((file = fdgetfile(fd))==nil) + return (ulong)-EBADF; + + space = current->mem; + qlock(space); + if((a = mapspace(space, r, flags, prot, &e)) == nil){ + qunlock(space); + putfile(file); + return (ulong)e; + } + + seg = a->seg; + r = a->addr; + + if(flags & MAP_ANONYMOUS){ + mergearea(a); + qunlock(seg); + qunlock(space); + + return r.base; + } + + o = pgoff * PAGESIZE; + + if(f = seg->freefilemap) + seg->freefilemap = f->next; + if(f == nil) + f = kmalloc(sizeof(Filemap)); + f->ref = 1; + f->addr = r; + f->next = nil; + f->path = kstrdup(file->path); + f->offset = o; + if((f->mode = file->mode) != O_RDONLY){ + f->file = getfile(file); + } else { + f->file = nil; + } + a->filemap = f; + qunlock(seg); + qunlock(space); + + trace("map %s [%lux-%lux] at [%lux-%lux]", file->path, o, o + (r.top - r.base), r.base, r.top); + + addr = r.base; + while(addr < r.top){ + n = preadfile(file, (void*)addr, r.top - addr, o); + if(n == 0) + break; + if(n < 0){ + trace("read failed at offset %lux for address %lux failed: %r", o, addr); + break; + } + addr += n; + o += n; + } + + putfile(file); + + return r.base; +} + +int sys_munmap(ulong addr, ulong len) +{ + Space *space; + Range r; + + trace("sys_munmap(%lux, %lux)", addr, len); + + if(pagealign(addr) != addr) + return -EINVAL; + r.base = addr; + r.top = addr + pagealign(len); + if(r.top <= r.base) + return -EINVAL; + + space = current->mem; + qlock(space); + unmapspace(current->mem, r); + qunlock(space); + + return 0; +} + +ulong +sys_brk(ulong bk) +{ + Space *space; + ulong a; + + trace("sys_brk(%lux)", bk); + + space = current->mem; + qlock(space); + a = brkspace(space, bk); + qunlock(space); + + return a; +} + +int sys_mprotect(ulong addr, ulong len, int prot) +{ + Space *space; + Seg *seg; + Area *a, *b; + int err; + + trace("sys_mprotect(%lux, %lux, %lux)", addr, len, (ulong)prot); + + len = pagealign(len); + if(pagealign(addr) != addr) + return -EINVAL; + if(len == 0) + return -EINVAL; + + err = -ENOMEM; + space = current->mem; + qlock(space); + if(seg = addr2seg(space, addr)){ + for(a = addr2area(seg, addr); a!=nil; a=a->next){ + if(addr + len <= a->addr.base) + break; + err = 0; + if(a->prot == prot) + continue; + wakefutexarea(a, a->addr); + if(a->addr.base < addr){ + b = duparea(a); + a->addr.base = addr; + b->addr.top = addr; + linkarea(seg, b); + } + if(a->addr.top > addr + len){ + b = duparea(a); + a->addr.top = addr + len; + b->addr.base = addr + len; + linkarea(seg, b); + } + trace("%lux-%lux %lux -> %lux", a->addr.base, a->addr.top, (ulong)a->prot, (long)prot); + a->prot = prot; + } + qunlock(seg); + } + qunlock(space); + + return err; +} + +int sys_msync(ulong addr, ulong len, int flags) +{ + Space *space; + Range r; + + trace("sys_msync(%lux, %lux, %x)", addr, len, flags); + + if(pagealign(addr) != addr) + return -EINVAL; + r.base = addr; + r.top = addr + pagealign(len); + if(r.top <= r.base) + return -EINVAL; + + space = current->mem; + qlock(space); + syncspace(space, r); + qunlock(space); + + return 0; +} + +ulong +sys_mremap(ulong addr, ulong oldlen, ulong newlen, int flags, ulong newaddr) +{ + Space *space; + int r; + + trace("sys_mremap(%lux, %lux, %lux, %x, %lux)", + addr, oldlen, newlen, flags, newaddr); + + space = current->mem; + qlock(space); + r = remapspace(space, addr, oldlen, newlen, newaddr, flags); + qunlock(space); + + return r; +} + +enum { + FUTEX_WAIT, + FUTEX_WAKE, + FUTEX_FD, + FUTEX_REQUEUE, + FUTEX_CMP_REQUEUE, +}; + +int sys_futex(ulong *addr, int op, int val, void *ptime, ulong *addr2, int val3) +{ + Space *space; + Seg *seg; + Area *a; + Futex *fu, *fu2; + int err, val2; + vlong timeout; + + trace("sys_futex(%p, %d, %d, %p, %p, %d)", addr, op, val, ptime, addr2, val3); + + seg = nil; + err = -EFAULT; + if((space = current->mem) == 0) + goto out; + + qlock(space); + if((seg = addr2seg(space, (ulong)addr)) == nil){ + qunlock(space); + goto out; + } + qunlock(space); + if((a = addr2area(seg, (ulong)addr)) == nil) + goto out; + for(fu = a->futex; fu; fu = fu->next) + if(fu->addr == addr) + break; + + switch(op){ + case FUTEX_WAIT: + trace("sys_futex(): FUTEX_WAIT futex=%p addr=%p", fu, addr); + + if(fu == nil){ + if(fu = seg->freefutex){ + seg->freefutex = fu->next; + } else { + fu = kmallocz(sizeof(Futex), 1); + } + fu->ref = 1; + fu->addr = addr; + if(fu->next = a->futex) + fu->next->link = &fu->next; + fu->link = &a->futex; + a->futex = fu; + } else { + incref(fu); + } + + err = 0; + timeout = 0; + if(ptime != nil){ + struct linux_timespec *ts = ptime; + vlong now; + + wakeme(1); + now = nsec(); + if(current->restart->syscall){ + timeout = current->restart->futex.timeout; + } else { + timeout = now + (vlong)ts->tv_sec * 1000000000LL + ts->tv_nsec; + } + if(now < timeout){ + current->timeout = timeout; + setalarm(timeout); + } else { + err = -ETIMEDOUT; + } + } + if(err == 0){ + if(*addr != val){ + err = -EWOULDBLOCK; + } else { + err = sleepq(fu, seg, 1); + } + } + if(ptime != nil){ + current->timeout = 0; + wakeme(0); + } + if(err == -ERESTART) + current->restart->futex.timeout = timeout; + + if(!decref(fu)){ + if(fu->link){ + if(*fu->link = fu->next) + fu->next->link = fu->link; + fu->link = nil; + fu->next = nil; + } + fu->next = seg->freefutex; + seg->freefutex = fu; + } + break; + + case FUTEX_WAKE: + trace("sys_futex(): FUTEX_WAKE futex=%p addr=%p", fu, addr); + err = fu ? wakeq(fu, val < 0 ? 0 : val) : 0; + break; + + case FUTEX_CMP_REQUEUE: + trace("sys_futex(): FUTEX_CMP_REQUEUE futex=%p addr=%p", fu, addr); + if(*addr != val3){ + err = -EAGAIN; + break; + case FUTEX_REQUEUE: + trace("sys_futex(): FUTEX_REQUEUE futex=%p addr=%p", fu, addr); + } + err = fu ? wakeq(fu, val < 0 ? 0 : val) : 0; + if(err > 0){ + val2 = (int)ptime; + + /* BUG: fu2 has to be in the same segment as fu */ + if(a = addr2area(seg, (ulong)addr2)){ + for(fu2 = a->futex; fu2; fu2 = fu2->next){ + if(fu2->addr == addr2){ + err += requeue(fu, fu2, val2); + break; + } + } + } + } + break; + + default: + err = -ENOSYS; + } + +out: + if(seg) + qunlock(seg); + return err; +} diff --git a/linux_emul_base/miscdev.c b/linux_emul_base/miscdev.c new file mode 100644 index 0000000..21ee4aa --- /dev/null +++ b/linux_emul_base/miscdev.c @@ -0,0 +1,156 @@ +#include +#include +#include +#include +#include + +#include "dat.h" +#include "fns.h" +#include "linux.h" + +enum +{ + Mnull, + Mzero, + Mfull, + Mrandom, + Murandom, + Mmax, +}; + +typedef struct Miscfile Miscfile; +struct Miscfile +{ + Ufile; + int m; +}; + +static int +path2m(char *path) +{ + int m; + + m = -1; + if(strcmp(path, "/dev/null")==0){ + m = Mnull; + } else if(strcmp(path, "/dev/zero")==0){ + m = Mzero; + } else if(strcmp(path, "/dev/full")==0){ + m = Mfull; + } else if(strcmp(path, "/dev/random")==0){ + m = Mrandom; + } else if(strcmp(path, "/dev/urandom")==0){ + m = Murandom; + } + + return m; +} + +static int +openmisc(char *path, int mode, int, Ufile **pf) +{ + Miscfile *f; + int m; + + if((m = path2m(path)) < 0) + return -ENOENT; + f = kmallocz(sizeof(*f), 1); + f->ref = 1; + f->mode = mode; + f->path = kstrdup(path); + f->fd = -1; + f->dev = MISCDEV; + f->m = m; + *pf = f; + return 0; +} + +static int +closemisc(Ufile *) +{ + return 0; +} + +static int +readmisc(Ufile *f, void *buf, int len, vlong) +{ + switch(((Miscfile*)f)->m){ + case Mnull: + return 0; + case Mzero: + memset(buf, 0, len); + return len; + case Mfull: + return -EIO; + case Mrandom: + genrandom(buf, len); + return len; + case Murandom: + prng(buf, len); + return len; + default: + return -EIO; + } +} + +static int +writemisc(Ufile *f, void *, int len, vlong) +{ + switch(((Miscfile*)f)->m){ + case Mnull: + case Mzero: + case Mrandom: + case Murandom: + return len; + case Mfull: + return -ENOSPC; + default: + return -EIO; + } +} + +static int +statmisc(char *path, int, Ustat *s) +{ + if(path2m(path) < 0) + return -ENOENT; + + s->mode = 0666 | S_IFCHR; + s->uid = current->uid; + s->gid = current->gid; + s->size = 0; + s->ino = hashpath(path); + s->dev = 0; + s->rdev = 0; + s->atime = s->mtime = s->ctime = boottime/1000000000LL; + return 0; +} + +static int +fstatmisc(Ufile *f, Ustat *s) +{ + return fsstat(f->path, 0, s); +}; + +static Udev miscdev = +{ + .open = openmisc, + .read = readmisc, + .write = writemisc, + .close = closemisc, + .stat = statmisc, + .fstat = fstatmisc, +}; + +void miscdevinit(void) +{ + devtab[MISCDEV] = &miscdev; + + fsmount(&miscdev, "/dev/null"); + fsmount(&miscdev, "/dev/zero"); + fsmount(&miscdev, "/dev/full"); + fsmount(&miscdev, "/dev/random"); + fsmount(&miscdev, "/dev/urandom"); + + srand(truerand()); +} diff --git a/linux_emul_base/mkfile b/linux_emul_base/mkfile new file mode 100644 index 0000000..422112c --- /dev/null +++ b/linux_emul_base/mkfile @@ -0,0 +1,67 @@ +$target + +linuxcall.$O: linuxcalltab.out + +linuxdat.acid: $HFILES main.c trace.c signal.c mem.c file.c + rm -f $target + for(i in main.c){ + $CC -a $i >>$target + } + for(i in bufproc.c error.c exec.c file.c fs.c mem.c poll.c \ + proc.c signal.c stat.c time.c tls.c trace.c trap.c \ + consdev.c dspdev.c miscdev.c pipedev.c \ + ptydev.c rootdev.c sockdev.c procdev.c){ + $CC -aa $i >>$target + } + +$RCBIN/linux: linux + cp linux $RCBIN/linux + +acid:V: linuxdat.acid + +install:V: $RCBIN/linux + + + diff --git a/linux_emul_base/pipedev.c b/linux_emul_base/pipedev.c new file mode 100644 index 0000000..dd4f5f6 --- /dev/null +++ b/linux_emul_base/pipedev.c @@ -0,0 +1,202 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Pipe Pipe; + +struct Pipe +{ + Ufile; + void *bufproc; + ulong atime; + ulong mtime; + int ino; +}; + +enum{ + Maxatomic = 64*1024, +}; + +int +pipewrite(int fd, void *buf, int len) +{ + uchar *p, *e; + int err, n; + + p = buf; + e = p + len; + while(p < e){ + n = e - p; + if(n > Maxatomic) + n = Maxatomic; + if(notifyme(1)) + err = -ERESTART; + else { + err = write(fd, p, n); + notifyme(0); + if(err < 0) + err = mkerror(); + } + if(err < 0){ + if(p == (uchar*)buf) + return err; + break; + } + p += err; + } + return p - (uchar*)buf; +} + +static int +closepipe(Ufile *file) +{ + Pipe *pipe = (Pipe*)file; + + close(pipe->fd); + freebufproc(pipe->bufproc); + + return 0; +} + +static void* +bufprocpipe(Pipe *pipe) +{ + if(pipe->bufproc == nil) + pipe->bufproc = newbufproc(pipe->fd); + return pipe->bufproc; +} + +static int +pollpipe(Ufile *file, void *tab) +{ + Pipe *pipe = (Pipe*)file; + + return pollbufproc(bufprocpipe(pipe), pipe, tab); +} + +static int +readpipe(Ufile *file, void *buf, int len, vlong) +{ + Pipe *pipe = (Pipe*)file; + int ret; + + if((pipe->mode & O_NONBLOCK) || (pipe->bufproc != nil)){ + ret = readbufproc(bufprocpipe(pipe), buf, len, 0, (pipe->mode & O_NONBLOCK)); + } else { + if(notifyme(1)) + return -ERESTART; + ret = read(pipe->fd, buf, len); + notifyme(0); + if(ret < 0) + ret = mkerror(); + } + if(ret > 0) + pipe->atime = time(nil); + return ret; +} + +static int +writepipe(Ufile *file, void *buf, int len, vlong) +{ + Pipe *pipe = (Pipe*)file; + int ret; + + ret = pipewrite(pipe->fd, buf, len); + if(ret > 0) + pipe->mtime = time(nil); + return ret; +} + +static int +ioctlpipe(Ufile *file, int cmd, void *arg) +{ + Pipe *pipe = (Pipe*)file; + + switch(cmd){ + default: + return -ENOTTY; + case 0x541B: + { + int r; + + if(arg == nil) + return -EINVAL; + if((r = nreadablebufproc(bufprocpipe(pipe))) < 0){ + *((int*)arg) = 0; + return r; + } + *((int*)arg) = r; + } + return 0; + } +} + +int sys_pipe(int *fds) +{ + Pipe *file; + int p[2]; + int i, fd; + static int ino = 0x1234; + + trace("sys_pipe(%p)", fds); + + if(pipe(p) < 0) + return mkerror(); + + for(i=0; i<2; i++){ + file = kmallocz(sizeof(Pipe), 1); + file->ref = 1; + file->mode = O_RDWR; + file->dev = PIPEDEV; + file->fd = p[i]; + file->ino = ino++; + file->atime = file->mtime = time(nil); + if((fd = newfd(file, 0)) < 0){ + if(i > 0) + sys_close(fds[0]); + close(p[0]); + close(p[1]); + return fd; + } + fds[i] = fd; + } + return 0; +} + +static void +fillstat(Pipe *pipe, Ustat *s) +{ + s->ino = pipe->ino; + s->mode = 0666 | S_IFIFO; + s->uid = current->uid; + s->gid = current->gid; + s->atime = pipe->atime; + s->mtime = pipe->mtime; + s->size = 0; +} + +static int +fstatpipe(Ufile *file, Ustat *s) +{ + Pipe *pipe = (Pipe*)file; + fillstat(pipe, s); + return 0; +}; + +static Udev pipedev = +{ + .read = readpipe, + .write = writepipe, + .poll = pollpipe, + .close = closepipe, + .ioctl = ioctlpipe, + .fstat = fstatpipe, +}; + +void pipedevinit(void) +{ + devtab[PIPEDEV] = &pipedev; +} diff --git a/linux_emul_base/poll.c b/linux_emul_base/poll.c new file mode 100644 index 0000000..79fb8f5 --- /dev/null +++ b/linux_emul_base/poll.c @@ -0,0 +1,250 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +void pollwait(Ufile *f, Uwaitq *q, void *t) +{ + Uwait *w, **p; + + if(f == nil || t == nil || q == nil) + return; + + p = t; + w = addwaitq(q); + w->file = getfile(f); + w->next = *p; + *p = w; +} + +static void +clearpoll(Uwait **p) +{ + Uwait *w; + + while(w = *p){ + *p = w->next; + delwaitq(w); + } +} + +struct linux_pollfd +{ + int fd; + short events; + short revents; +}; + +int sys_poll(void *p, int nfd, long timeout) +{ + int i, e, err; + Uwait *tab; + Ufile *file; + vlong now, t; + struct linux_pollfd *fds = p; + + trace("sys_poll(%p, %d, %ld)", p, nfd, timeout); + + if(nfd < 0) + return -EINVAL; + + t = 0; + wakeme(1); + if(timeout > 0){ + now = nsec(); + if(current->restart->syscall){ + t = current->restart->poll.timeout; + } else { + t = now + timeout*1000000LL; + } + if(now < t){ + current->timeout = t; + setalarm(t); + } + } + + tab = nil; + for(;;){ + clearpoll(&tab); + + err = 0; + for(i=0; i= 0){ + e = POLLNVAL; + if(file = fdgetfile(fds[i].fd)){ + if(devtab[file->dev]->poll == nil){ + e = POLLIN|POLLOUT; + } else { + e = devtab[file->dev]->poll(file, (err == 0) ? &tab : nil); + } + putfile(file); + e &= fds[i].events | POLLERR | POLLHUP; + } + } + if(fds[i].revents = e){ + trace("sys_poll(): fd %d is ready with %x", fds[i].fd, fds[i].revents); + err++; + } + } + if(err > 0) + break; + if(timeout >= 0 && current->timeout == 0){ + trace("sys_poll(): timeout"); + break; + } + if((err = sleepproc(nil, 1)) < 0){ + trace("sys_poll(): interrupted"); + current->restart->poll.timeout = t; + break; + } + } + clearpoll(&tab); + wakeme(0); + + if(timeout > 0) + current->timeout = 0; + + return err; +} + +int sys_select(int nfd, ulong *rfd, ulong *wfd, ulong *efd, void *ptv) +{ + int i, p, e, w, nwrd, nbits, fd, err; + ulong m; + Uwait *tab; + Ufile *file; + vlong now, t; + struct linux_timeval *tv = ptv; + struct { + int fd; + int ret; + } *ardy, astk[16]; + + trace("sys_select(%d, %p, %p, %p, %p)", nfd, rfd, wfd, efd, ptv); + + if(nfd < 0) + return -EINVAL; + + if(tv != nil) + if(tv->tv_sec < 0 || tv->tv_usec < 0 || tv->tv_usec >= 1000000) + return -EINVAL; + + nwrd = (nfd + (8 * sizeof(m))-1) / (8 * sizeof(m)); + + nbits = 0; + for(w=0; w nelem(astk)){ + ardy = kmalloc(nbits * sizeof(ardy[0])); + } else { + ardy = astk; + } + + t = 0; + wakeme(1); + if(tv != nil){ + now = nsec(); + if(current->restart->syscall){ + t = current->restart->select.timeout; + } else { + t = now + tv->tv_sec*1000000000LL + tv->tv_usec*1000; + } + if(now < t){ + current->timeout = t; + setalarm(t); + } + } + + tab = nil; + for(;;){ + clearpoll(&tab); + + fd = 0; + err = 0; + for(w=0; wdev]->poll == nil){ + e = POLLIN|POLLOUT; + } else { + e = devtab[file->dev]->poll(file, (err == 0) ? &tab : nil); + } + putfile(file); + if(e &= p) { + ardy[err].fd = fd; + ardy[err].ret = e; + if(++err == nbits) + break; + } + } + } + if(err > 0) + break; + if(tv != nil && current->timeout == 0){ + trace("sys_select(): timeout"); + break; + } + if((err = sleepproc(nil, 1)) < 0){ + trace("sys_select(): interrupted"); + current->restart->select.timeout = t; + break; + } + } + clearpoll(&tab); + wakeme(0); + + if(tv != nil){ + current->timeout = 0; + t -= nsec(); + if(t < 0) + t = 0; + tv->tv_sec = (long)(t/1000000000LL); + tv->tv_usec = (long)((t%1000000000LL)/1000); + } + + if(err >= 0){ + if(rfd) memset(rfd, 0, nwrd*sizeof(m)); + if(wfd) memset(wfd, 0, nwrd*sizeof(m)); + if(efd) memset(efd, 0, nwrd*sizeof(m)); + + nbits = 0; + for(i=0; i +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +static int timernotefd; +static void timerproc(void*); + +static int +pidhash(int pid) +{ + return (pid - 1) % MAXPROC; +} + +Uproc* +getproc(int tid) +{ + Uproc *p; + + if(tid > 0){ + p = &proctab.proc[pidhash(tid)]; + if(p->tid == tid) + return p; + } + return nil; +} + +Uproc* +getprocn(int n) +{ + Uproc *p; + + p = &proctab.proc[n]; + if(p->tid > 0) + return p; + return nil; +} + +static Uproc* +allocproc(void) +{ + Uproc *p; + int tid, i; + + for(i=0; itid <= 0){ + proctab.alloc++; + + p->tid = tid; + p->pid = tid; + p->pgid = tid; + p->psid = tid; + return p; + } + } + + trace("allocproc(): out of processes"); + return nil; +} + +static void +freeproc(Uproc *p) +{ + Uwait *w; + + while(w = p->freewait){ + p->freewait = w->next; + free(w); + } + exittrace(p); + free(p->comm); + free(p->root); + free(p->cwd); + free(p->kcwd); + memset(p, 0, sizeof(*p)); + proctab.alloc--; +} + +void initproc(void) +{ + Uproc *p; + char buf[1024]; + int pid; + + proctab.nextpid = 10; + + p = allocproc(); + p->kpid = getpid(); + snprint(buf, sizeof(buf), "/proc/%d/note", p->kpid); + p->notefd = open(buf, OWRITE); + snprint(buf, sizeof(buf), "/proc/%d/args", p->kpid); + p->argsfd = open(buf, ORDWR); + + current = p; + + inittrace(); + inittime(); + initsignal(); + initmem(); + inittls(); + initfile(); + + if((pid = procfork(timerproc, nil, 0)) < 0) + panic("initproc: unable to fork timerproc: %r"); + + snprint(buf, sizeof(buf), "/proc/%d/note", pid); + timernotefd = open(buf, OWRITE); + + current->root = nil; + current->cwd = kstrdup(getwd(buf, sizeof(buf))); + current->kcwd = kstrdup(current->cwd); + current->linkloop = 0; + current->starttime = nsec(); + + inittrap(); +} + +void +setprocname(char *s) +{ + if(current == nil){ + char buf[32]; + int fd; + + snprint(buf, sizeof(buf), "/proc/%d/args", getpid()); + if((fd = open(buf, OWRITE)) >= 0){ + write(fd, s, strlen(s)); + close(fd); + } + } else { + write(current->argsfd, s, strlen(s)); + } +} + +static void +intrnote(void *, char *msg) +{ + if(strncmp(msg, "interrupt", 9) == 0) + noted(NCONT); + noted(NDFLT); +} + +struct kprocforkargs +{ + int flags; + void (*func)(void *aux); + void *aux; +}; + +static int +kprocfork(void *arg) +{ + struct kprocforkargs args; + int pid; + + memmove(&args, arg, sizeof(args)); + + if((pid = rfork(RFPROC|RFMEM|args.flags)) != 0) + return pid; + + notify(intrnote); + + unmapuserspace(); + current = nil; + + profme(); + args.func(args.aux); + longjmp(exitjmp, 1); + return -1; +} + +/* + * procfork starts a kernel process running on kstack. + * that process will have linux memory segments (stack, private, + * shared) unmapped but plan9 segments (text, bss, stack) shared. + * here is no Uproc associated with it! current will be set to nil so + * you cant call sys_????() functions in here. + * procfork returns the plan9 pid. (usefull for posting notes) + */ +int procfork(void (*func)(void *aux), void *aux, int flags) +{ + struct kprocforkargs args; + + args.flags = flags; + args.func = func; + args.aux = aux; + + return onstack(kstack, kprocfork, &args); +} + +static void *Intr = (void*)~0; + +static char Notifyme[] = "notifyme"; +static char Wakeme[] = "wakeme"; +static char Xchange[] = "xchange"; + +static char Wakeup[] = "wakeup"; +static char Abort[] = "abort"; + +int notifyme(int on) +{ + Uproc *p; + + p = current; + qlock(p); + if(on){ + if(p->notified || signalspending(p)){ + qunlock(p); + return 1; + } + if(p->state == nil) + p->state = Notifyme; + } else { + p->state = nil; + } + qunlock(p); + return 0; +} + +void wakeme(int on) +{ + Uproc *p; + + p = current; + qlock(p); + if(on){ + if(p->state == nil) + p->state = Wakeme; + } else { + p->state = nil; + } + qunlock(p); +} + +int sleepproc(QLock *l, int flags) +{ + Uproc *p; + void *ret; + char *x; + + p = current; + qlock(p); + x = p->state; + if(x == nil || x == Wakeme){ + p->xstate = x; + p->state = Xchange; + if(l != nil) + qunlock(l); + qunlock(p); + if(flags && signalspending(p)){ + ret = Intr; + } else { + ret = rendezvous(p, Xchange); + } + if(ret == Intr){ + qlock(p); + if(p->state != Xchange){ + while((ret = rendezvous(p, Xchange)) == Intr) + ; + } else { + p->state = x; + } + qunlock(p); + } + if(l != nil) + qlock(l); + } else { + p->state = Wakeme; + ret = x; + qunlock(p); + } + return (ret == Wakeup) ? 0 : -ERESTART; +} + +static int +wakeup(Uproc *proc, char *m, int force) +{ + char *x; + + if(proc != nil){ + qlock(proc); + x = proc->state; + + if(x == Wakeme){ + proc->state = m; + qunlock(proc); + return 1; + } + if(x == Xchange){ + proc->state = proc->xstate; + proc->xstate = nil; + qunlock(proc); + while(rendezvous(proc, m) == Intr) + ; + return 1; + } + if((m != Wakeup) && (proc->notified == 0)){ + if(x == Notifyme) + proc->state = nil; + if(x == Notifyme || force){ + proc->notified = 1; + qunlock(proc); + write(proc->notefd, "interrupt", 9); + return 1; + } + } + qunlock(proc); + } + return 0; +} + +Uwait* addwaitq(Uwaitq *q) +{ + Uproc *p; + Uwait *w; + + p = current; + if(w = p->freewait){ + p->freewait = w->next; + } else { + w = kmalloc(sizeof(*w)); + } + + w->next = nil; + + w->proc = p; + w->file = nil; + + w->q = q; + qlock(q); + w->nextq = q->w; + q->w = w; + qunlock(q); + + return w; +} + +void delwaitq(Uwait *w) +{ + Uwaitq *q; + Uwait **x; + + q = w->q; + qlock(q); + for(x = &q->w; *x; x=&((*x)->nextq)){ + if(*x == w){ + *x = w->nextq; + break; + } + } + qunlock(q); + + w->q = nil; + w->nextq = nil; + + w->proc = nil; + putfile(w->file); + w->file = nil; + + w->next = current->freewait; + current->freewait = w; +} + +int requeue(Uwaitq *q1, Uwaitq *q2, int nrequeue) +{ + int n; + Uwait *w; + + n = 1000; + for(;;){ + qlock(q1); + if(canqlock(q2)) + break; + qunlock(q1); + if(--n <= 0) + return 0; + sleep(0); + } + n = 0; + while((w = q1->w) && (n < nrequeue)){ + q1->w = w->nextq; + w->q = q2; + w->nextq = q2->w; + q2->w = w; + n++; + } + qunlock(q2); + qunlock(q1); + return n; +} + +int wakeq(Uwaitq *q, int nwake) +{ + int n; + Uwait *w; + + n = 0; + if(q != nil){ + qlock(q); + for(w = q->w; w && n < nwake; w=w->nextq) + n += wakeup(w->proc, Wakeup, 0); + qunlock(q); + } + return n; +} + +int sleepq(Uwaitq *q, QLock *l, int flags) +{ + Uwait *w; + int ret; + + w = addwaitq(q); + ret = sleepproc(l, flags); + delwaitq(w); + + return ret; +} + +static Uproc *alarmq; + +int +procsetalarm(Uproc *proc, vlong t) +{ + Uproc **pp; + int ret; + + if(proc->alarm && t >= proc->alarm) + return 0; + ret = (alarmq == nil) || (t < alarmq->alarm); + for(pp = &alarmq; *pp; pp = &((*pp)->alarmq)){ + if(*pp == proc){ + *pp = proc->alarmq; + break; + } + } + for(pp = &alarmq; *pp; pp = &((*pp)->alarmq)) + if((*pp)->alarm > t) + break; + proc->alarm = t; + proc->alarmq = *pp; + *pp = proc; + return ret; +} + +void +setalarm(vlong t) +{ + qlock(&proctab); + if(procsetalarm(current, t)) + write(timernotefd, "interrupt", 9); + qunlock(&proctab); +} + +/* signal.c */ +extern void alarmtimer(Uproc *proc, vlong now); + +static void +timerproc(void*) +{ + Uproc *h; + vlong now; + long m; + + setprocname("timerproc()"); + + while(proctab.alloc > 0){ + qlock(&proctab); + m = 2000; + now = nsec(); + while(h = alarmq){ + if(now < h->alarm){ + m = (h->alarm - now) / 1000000; + break; + } + alarmq = h->alarmq; + h->alarm = 0; + h->alarmq = nil; + if(h->timeout){ + if(now >= h->timeout){ + h->timeout = 0; + wakeup(h, Wakeup, 0); + } else + procsetalarm(h, h->timeout); + } + alarmtimer(h, now); + } + qunlock(&proctab); + sleep((m + (1000/HZ-1))/(1000/HZ)); + } +} + +/* +static void +timerproc(void *) +{ + Uproc *p; + vlong expire, now, wake, dead; + int err, i, alive; + char c; + + setprocname("timerproc()"); + dead = 0; + for(;;){ + qlock(&proctab); + now = nsec(); + wake = now + 60000000000LL; + alive = 0; + for(i=0; iwstate & WEXITED) + continue; + if(p->kpid <= 0) + continue; + + if(now >= dead){ + if(read(p->argsfd, &c, 1) < 0){ + err = mkerror(); + if(err != -EINTR && err != -ERESTART){ + p->kpid = 0; + qunlock(&proctab); + exitproc(p, SIGKILL, 1); + qlock(&proctab); + continue; + } + } + } + alive++; + expire = p->timeout; + if(expire > 0){ + if(now >= expire){ + p->timeout = 0; + wakeup(p, Wakeup, 0); + } else { + if(expire < wake) + wake = expire; + } + } + expire = alarmtimer(p, now, wake); + if(expire < wake) + wake = expire; + } + qunlock(&proctab); + + if(now >= dead) + dead = now + 5000000000LL; + if(dead < wake) + wake = dead; + if(alive == 0) + break; + wake -= now; + + sleep(wake/1000000LL); + } +} +*/ + +int sys_waitpid(int pid, int *pexit, int opt) +{ + int i, n, m, status; + Uproc *p; + + trace("sys_waitpid(%d, %p, %d)", pid, pexit, opt); + + m = WEXITED; + if(opt & WUNTRACED) + m |= WSTOPPED; + if(opt & WCONTINUED) + m |= WCONTINUED; + + qlock(&proctab); + for(;;){ + n = 0; + for(i=0; iexitsignal != SIGCHLD) && (opt & (WALL|WCLONE))==0) + continue; + if(p->ppid != current->pid) + continue; + if(pid > 0){ + if(p->pid != pid) + continue; + } else if(pid == 0){ + if(p->pgid != current->pgid) + continue; + } else if(pid < -1){ + if(p->pgid != -pid) + continue; + } + n++; + trace("sys_waitpid(): child %d wstate %x", p->pid, p->wstate); + if(p->wevent & m) + goto found; + } + if(n == 0){ + qunlock(&proctab); + trace("sys_waitpid(): no children we can wait for"); + return -ECHILD; + } + if(opt & WNOHANG){ + qunlock(&proctab); + trace("sys_waitpid(): no exited/stoped/cont children"); + return 0; + } + if((i = sleepproc(&proctab, 1)) < 0){ + qunlock(&proctab); + return i; + } + } + +found: + pid = p->pid; + status = p->exitcode; + p->wevent &= ~(p->wevent & m); + if(p->wstate & WEXITED){ + trace("sys_waitpid(): found zombie %d exitcode %d", pid, status); + freeproc(p); + } + qunlock(&proctab); + if(pexit) + *pexit = status; + return pid; +} + +struct linux_rusage { + struct linux_timeval ru_utime; /* user time used */ + struct linux_timeval ru_stime; /* system time used */ + long ru_maxrss; /* maximum resident set size */ + long ru_ixrss; /* integral shared memory size */ + long ru_idrss; /* integral unshared data size */ + long ru_isrss; /* integral unshared stack size */ + long ru_minflt; /* page reclaims */ + long ru_majflt; /* page faults */ + long ru_nswap; /* swaps */ + long ru_inblock; /* block input operations */ + long ru_oublock; /* block output operations */ + long ru_msgsnd; /* messages sent */ + long ru_msgrcv; /* messages received */ + long ru_nsignals; /* signals received */ + long ru_nvcsw; /* voluntary context switches */ + long ru_nivcsw; /* involuntary context switches */ +}; + +int sys_wait4(int pid, int *pexit, int opt, void *prusage) +{ + int ret; + struct linux_rusage *ru = prusage; + + trace("sys_wait4(%d, %p, %d, %p)", pid, pexit, opt, prusage); + + ret = sys_waitpid(pid, pexit, opt); + if(ru != nil) + memset(ru, 0, sizeof(*ru)); + + return ret; +} + +int +threadcount(int pid) +{ + Uproc *p; + int i, n; + + n = 0; + for(i = 0; ipid == pid) + n++; + } + return n; +} + +int +killproc(Uproc *p, Usiginfo *info, int group) +{ + int i, n; + Uproc *w; + int sig, err; + + if((err = sendsignal(p, info, group)) <= 0) + return err; + w = p; + sig = info->signo; + if(group && !wantssignal(w, sig)){ + for(i=1, n = p->tid + 1; ipid != w->pid) + continue; + if(!wantssignal(p, info->signo)) + continue; + w = p; + break; + } + } + wakeup(w, Abort, (sig == SIGKILL || sig == SIGSTOP || sig == SIGALRM)); + return 0; +} + +enum +{ + CLD_EXITED = 1, + CLD_KILLED, + CLD_DUMPED, + CLD_TRAPPED, + CLD_STOPPED, + CLD_CONTINUED, +}; + +/* + * queue the exit signal into the parent process. this + * doesnt do the wakeup like killproc(). + */ +static int +sendexitsignal(Uproc *parent, Uproc *proc, int sig, int code) +{ + Usiginfo si; + + memset(&si, 0, sizeof(si)); + switch(si.signo = sig){ + case SIGCHLD: + switch(code & 0xFF){ + case 0: + si.code = CLD_EXITED; + break; + case SIGSTOP: + si.code = CLD_STOPPED; + break; + case SIGCONT: + si.code = CLD_CONTINUED; + break; + case SIGKILL: + si.code = CLD_KILLED; + break; + default: + si.code = CLD_DUMPED; + break; + } + si.chld.pid = proc->pid; + si.chld.uid = proc->uid; + si.chld.status = code; + } + return sendsignal(parent, &si, 1); +} + +/* + * wakeup all threads who are in the same thread group + * as p including p. must be called with proctab locked. + */ +static void +wakeupall(Uproc *p, char *m, int force) +{ + int pid, i, n; + + pid = p->pid; + for(i=0, n = p->tid; ipid == pid) + wakeup(p, m, force); +} + +static void +zap(void *) +{ + exitproc(current, 0, 0); +} + +void +zapthreads(void) +{ + Uproc *p; + int i, n, z; + + for(;;){ + z = 0; + for(i=1, n = current->tid+1; ipid != current->pid || p == current) + continue; + if(p->kpid <= 0) + continue; + + trace("zapthreads() zapping thread %p", p); + p->tracearg = current; + p->traceproc = zap; + wakeup(p, Abort, 1); + z++; + } + if(z == 0) + break; + sleepproc(&proctab, 0); + } +} + +struct kexitprocargs +{ + Uproc *proc; + int code; + int group; +}; + +#pragma profile off + +static int +kexitproc(void *arg) +{ + struct kexitprocargs *args; + Uproc *proc; + int code, group; + Uproc *parent, *child, **pp; + int i; + + args = arg; + proc = args->proc; + code = args->code; + group = args->group; + + if(proc == current){ + trace("kexitproc: cleartidptr = %p", proc->cleartidptr); + if(okaddr(proc->cleartidptr, sizeof(*proc->cleartidptr), 1)) + *proc->cleartidptr = 0; + sys_futex((ulong*)proc->cleartidptr, 1, MAXPROC, nil, nil, 0); + + qlock(&proctab); + exitsignal(); + qunlock(&proctab); + + exitmem(); + } + + exitfile(proc); + + close(proc->notefd); proc->notefd = -1; + close(proc->argsfd); proc->argsfd = -1; + + qlock(&proctab); + + for(pp = &alarmq; *pp; pp = &((*pp)->alarmq)){ + if(*pp == proc){ + *pp = proc->alarmq; + proc->alarmq = nil; + break; + } + } + + /* reparent children, and reap when zombies */ + for(i=0; ippid != proc->pid) + continue; + child->ppid = 0; + if(child->wstate & WEXITED) + freeproc(child); + } + + /* if we got zapped, just free the proc and wakeup zapper */ + if((proc == current) && (proc->traceproc == zap) && (parent = proc->tracearg)){ + freeproc(proc); + wakeup(parent, Wakeup, 0); + goto zapped; + } + + if(group && proc == current) + zapthreads(); + + parent = getproc(proc->ppid); + if((threadcount(proc->pid)==1) && parent && + (proc->exitsignal == SIGCHLD) && !ignoressignal(parent, SIGCHLD)){ + + /* we are zombie */ + proc->exitcode = code; + proc->wstate = WEXITED; + proc->wevent = proc->wstate; + if(proc == current){ + current->kpid = 0; + sendexitsignal(parent, proc, proc->exitsignal, code); + wakeupall(parent, Abort, 0); + qunlock(&proctab); + longjmp(exitjmp, 1); + } else { + sendexitsignal(parent, proc, proc->exitsignal, code); + } + } else { + /* we are clone */ + if(parent && proc->exitsignal > 0) + sendexitsignal(parent, proc, proc->exitsignal, code); + freeproc(proc); + } + if(parent) + wakeupall(parent, Abort, 0); + +zapped: + qunlock(&proctab); + + if(proc == current) + longjmp(exitjmp, 1); + + return 0; +} + +void exitproc(Uproc *proc, int code, int group) +{ + struct kexitprocargs args; + + trace("exitproc(%p, %d, %d)", proc, code, group); + + args.proc = proc; + args.code = code; + args.group = group; + + if(proc == current){ + onstack(kstack, kexitproc, &args); + } else { + kexitproc(&args); + } +} + +struct kstoparg +{ + Uproc *stopper; + int code; +}; + +static void +stop(void *aux) +{ + struct kstoparg *arg = aux; + + stopproc(current, arg->code, 0); +} + +void stopproc(Uproc *proc, int code, int group) +{ + struct kstoparg *arg; + Uproc *p, *parent; + int i, n, z; + + trace("stopproc(%p, %d, %d)", proc, code, group); + + qlock(&proctab); + proc->exitcode = code; + proc->wstate = WSTOPPED; + proc->wevent = proc->wstate; + + if((proc == current) && (proc->traceproc == stop) && (arg = proc->tracearg)){ + proc->traceproc = nil; + proc->tracearg = nil; + wakeup(arg->stopper, Wakeup, 0); + qunlock(&proctab); + return; + } + + /* put all threads in the stopped state */ + arg = nil; + while(group){ + if(arg == nil){ + arg = kmalloc(sizeof(*arg)); + arg->stopper = current; + arg->code = code; + } + z = 0; + for(i=1, n = proc->tid+1; ipid != proc->pid || p == proc) + continue; + if(p->kpid <= 0) + continue; + if(p->wstate & (WSTOPPED | WEXITED)) + continue; + + trace("stopproc() stopping thread %p", p); + p->tracearg = arg; + p->traceproc = stop; + wakeup(p, Abort, 1); + z++; + } + if(z == 0) + break; + sleepproc(&proctab, 0); + } + free(arg); + + if(parent = getproc(proc->ppid)){ + if(group && !ignoressignal(parent, SIGCHLD)) + sendexitsignal(parent, proc, SIGCHLD, code); + wakeupall(parent, Abort, 0); + } + qunlock(&proctab); +} + +void contproc(Uproc *proc, int code, int group) +{ + Uproc *p, *parent; + int i, n; + + trace("contproc(%p, %d, %d)", proc, code, group); + + qlock(&proctab); + proc->exitcode = code; + proc->wstate = WCONTINUED; + proc->wevent = proc->wstate; + if(group){ + for(i=1, n = proc->tid+1; ipid != proc->pid || p == proc) + continue; + if(p->kpid <= 0) + continue; + if((p->wstate & WSTOPPED) == 0) + continue; + if(p->wstate & (WCONTINUED | WEXITED)) + continue; + + trace("contproc() waking thread %p", p); + p->exitcode = code; + p->wstate = WCONTINUED; + p->wevent = p->wstate; + wakeup(p, Wakeup, 0); + } + } + if(parent = getproc(proc->ppid)){ + if(group && !ignoressignal(parent, SIGCHLD)) + sendexitsignal(parent, proc, SIGCHLD, code); + wakeupall(parent, Abort, 0); + } + qunlock(&proctab); +} + +int sys_exit(int code) +{ + trace("sys_exit(%d)", code); + + exitproc(current, (code & 0xFF)<<8, 0); + return -1; +} + +int sys_exit_group(int code) +{ + trace("sys_exit_group(%d)", code); + + exitproc(current, (code & 0xFF)<<8, 1); + return -1; +} + +struct kcloneprocargs +{ + int flags; + void *newstack; + int *parenttidptr; + void *tlsdescr; + int *childtidptr; +}; + +static int +kcloneproc(void *arg) +{ + struct kcloneprocargs args; + struct linux_user_desc tls; + Ureg ureg; + int rflags, pid, tid; + char buf[80]; + Uproc *new; + + memmove(&args, arg, sizeof(args)); + memmove(&ureg, current->ureg, sizeof(ureg)); + if(args.flags & CLONE_SETTLS){ + if(!okaddr(args.tlsdescr, sizeof(tls), 0)) + return -EFAULT; + memmove(&tls, args.tlsdescr, sizeof(tls)); + } + + qlock(&proctab); + if((new = allocproc()) == nil){ + qunlock(&proctab); + return -EAGAIN; + } + tid = new->tid; + + if(args.flags & CLONE_PARENT_SETTID){ + if(!okaddr(args.parenttidptr, sizeof(*args.parenttidptr), 1)){ + freeproc(new); + qunlock(&proctab); + return -EFAULT; + } + *args.parenttidptr = tid; + } + + rflags = RFPROC; + if(args.flags & CLONE_VM) + rflags |= RFMEM; + + qlock(current); + if((pid = rfork(rflags)) < 0){ + freeproc(new); + qunlock(current); + qunlock(&proctab); + + trace("kcloneproc(): rfork failed: %r"); + return mkerror(); + } + + if(pid){ + /* parent */ + new->kpid = pid; + new->exitsignal = args.flags & 0xFF; + new->innote = 0; + new->ureg = &ureg; + new->syscall = current->syscall; + new->sysret = current->sysret; + new->comm = nil; + new->ncomm = 0; + new->linkloop = 0; + new->root = current->root ? kstrdup(current->root) : nil; + new->cwd = kstrdup(current->cwd); + new->kcwd = kstrdup(current->kcwd); + new->starttime = nsec(); + + snprint(buf, sizeof(buf), "/proc/%d/note", pid); + new->notefd = open(buf, OWRITE); + snprint(buf, sizeof(buf), "/proc/%d/args", pid); + new->argsfd = open(buf, ORDWR); + + if(args.flags & (CLONE_THREAD | CLONE_PARENT)){ + new->ppid = current->ppid; + } else { + new->ppid = current->pid; + } + + if(args.flags & CLONE_THREAD) + new->pid = current->pid; + + new->cleartidptr = nil; + if(args.flags & CLONE_CHILD_CLEARTID) + new->cleartidptr = args.childtidptr; + + new->pgid = current->pgid; + new->psid = current->psid; + new->uid = current->uid; + new->gid = current->gid; + + clonetrace(new, !(args.flags & CLONE_THREAD)); + clonesignal(new, !(args.flags & CLONE_SIGHAND), !(args.flags & CLONE_THREAD)); + clonemem(new, !(args.flags & CLONE_VM)); + clonefile(new, !(args.flags & CLONE_FILES)); + clonetls(new); + qunlock(&proctab); + + while(rendezvous(new, 0) == (void*)~0) + ; + + qunlock(current); + + return tid; + } + + /* child */ + current = new; + profme(); + + /* wait for parent to copy our resources */ + while(rendezvous(new, 0) == (void*)~0) + ; + + trace("kcloneproc(): hello world"); + + if(args.flags & CLONE_SETTLS) + sys_set_thread_area(&tls); + + if(args.flags & CLONE_CHILD_SETTID) + if(okaddr(args.childtidptr, sizeof(*args.childtidptr), 1)) + *args.childtidptr = tid; + + if(args.newstack != nil) + current->ureg->sp = (ulong)args.newstack; + current->sysret(0); + retuser(); + + return -1; +} + +#pragma profile on + +int sys_linux_clone(int flags, void *newstack, int *parenttidptr, int *tlsdescr, void *childtidptr) +{ + struct kcloneprocargs a; + + trace("sys_linux_clone(%x, %p, %p, %p, %p)", flags, newstack, parenttidptr, childtidptr, tlsdescr); + + a.flags = flags; + a.newstack = newstack; + a.parenttidptr = parenttidptr; + a.childtidptr = childtidptr; + a.tlsdescr = tlsdescr; + + return onstack(kstack, kcloneproc, &a); +} + +int sys_fork(void) +{ + trace("sys_fork()"); + + return sys_linux_clone(SIGCHLD, nil, nil, nil, nil); +} + +int sys_vfork(void) +{ + trace("sys_vfork()"); + + return sys_fork(); +} + +int sys_getpid(void) +{ + trace("sys_getpid()"); + + return current->pid; +} + +int sys_getppid(void) +{ + trace("sys_getppid()"); + + return current->ppid; +} + +int sys_gettid(void) +{ + trace("sys_gettid()"); + + return current->tid; +} + +int sys_setpgid(int pid, int pgid) +{ + int i, n; + + trace("sys_setpgid(%d, %d)", pid, pgid); + + if(pgid == 0) + pgid = current->pgid; + if(pid == 0) + pid = current->pid; + + n = 0; + qlock(&proctab); + for(i=0; ipid != pid) + continue; + + p->pgid = pgid; + n++; + } + qunlock(&proctab); + + return n ? 0 : -ESRCH; +} + +int sys_getpgid(int pid) +{ + int i; + int pgid; + + trace("sys_getpgid(%d)", pid); + + pgid = -ESRCH; + if(pid == 0) + return current->pgid; + qlock(&proctab); + for(i=0; ipid != pid) + continue; + + pgid = p->pgid; + break; + } + qunlock(&proctab); + + return pgid; +} + +int sys_getpgrp(void) +{ + trace("sys_getpgrp()"); + + return sys_getpgid(0); +} + +int sys_getuid(void) +{ + trace("sys_getuid()"); + + return current->uid; +} + +int sys_getgid(void) +{ + trace("sys_getgid()"); + + return current->gid; +} + +int sys_setuid(int uid) +{ + trace("sys_setuid(%d)", uid); + + current->uid = uid; + return 0; +} + +int sys_setgid(int gid) +{ + trace("sys_setgid(%d)", gid); + + current->gid = gid; + return 0; +} + +int sys_setresuid(int ruid, int euid, int suid) +{ + trace("sys_setresuid(%d, %d, %d)", ruid, euid, suid); + + return 0; +} + +int sys_setresgid(int rgid, int egid, int sgid) +{ + trace("sys_setresgid(%d, %d, %d)", rgid, egid, sgid); + + return 0; +} +int sys_setreuid(int ruid, int euid) +{ + trace("sys_setreuid(%d, %d)", ruid, euid); + + return 0; +} + +int sys_setregid(int rgid, int egid) +{ + trace("sys_setregid(%d, %d)", rgid, egid); + + return 0; +} + +int sys_getresuid(int *ruid, int *euid, int *suid) +{ + trace("sys_getresuid(%p, %p, %p)", ruid, euid, suid); + + if(ruid == nil) + return -EINVAL; + if(euid == nil) + return -EINVAL; + if(suid == nil) + return -EINVAL; + + *ruid = current->uid; + *euid = current->uid; + *suid = current->uid; + + return 0; +} + +int sys_getresgid(int *rgid, int *egid, int *sgid) +{ + trace("sys_getresgid(%p, %p, %p)", rgid, egid, sgid); + + if(rgid == nil) + return -EINVAL; + if(egid == nil) + return -EINVAL; + if(sgid == nil) + return -EINVAL; + + *rgid = current->gid; + *egid = current->gid; + *sgid = current->gid; + + return 0; +} + +int sys_setsid(void) +{ + int i; + + trace("sys_setsid()"); + + if(current->pid == current->pgid) + return -EPERM; + + qlock(&proctab); + for(i=0; ipid != current->pid) + continue; + p->pgid = current->pid; + p->psid = current->pid; + } + qunlock(&proctab); + + settty(nil); + + return current->pgid; +} + +int sys_getsid(int pid) +{ + int i, pgid; + + trace("sys_getsid(%d)", pid); + + pgid = -ESRCH; + if(pid == 0) + pid = current->pid; + qlock(&proctab); + for(i=0; ipid != pid) + continue; + if(p->pid != p->psid) + continue; + pgid = p->pgid; + break; + } + qunlock(&proctab); + + return pgid; +} + +int sys_getgroups(int size, int *groups) +{ + trace("sys_getgroups(%d, %p)", size, groups); + if(size < 0) + return -EINVAL; + return 0; +} + +int sys_setgroups(int size, int *groups) +{ + trace("sys_setgroups(%d, %p)", size, groups); + return 0; +} + +struct linux_utsname +{ + char sysname[65]; + char nodename[65]; + char release[65]; + char version[65]; + char machine[65]; + char domainname[65]; +}; + +int sys_uname(void *a) +{ + struct linux_utsname *p = a; + + trace("sys_uname(%p)", a); + + strncpy(p->sysname, "Linux", 65); + strncpy(p->nodename, sysname(), 65); + strncpy(p->release, "3.2.1", 65); + strncpy(p->version, "linuxemu", 65); + strncpy(p->machine, "i386", 65); + strncpy(p->domainname, sysname(), 65); + + return 0; +} + +int sys_personality(ulong p) +{ + trace("sys_personality(%lux)", p); + + if(p != 0 && p != 0xffffffff) + return -EINVAL; + return 0; +} + +int sys_tkill(int tid, int sig) +{ + int err; + + trace("sys_tkill(%d, %S)", tid, sig); + + err = -EINVAL; + if(tid > 0){ + Uproc *p; + + err = -ESRCH; + qlock(&proctab); + if(p = getproc(tid)){ + Usiginfo si; + + memset(&si, 0, sizeof(si)); + si.signo = sig; + si.code = SI_TKILL; + si.kill.pid = current->tid; + si.kill.uid = current->uid; + err = killproc(p, &si, 0); + } + qunlock(&proctab); + } + return err; +} + +int sys_tgkill(int pid, int tid, int sig) +{ + int err; + + trace("sys_tgkill(%d, %d, %S)", pid, tid, sig); + + err = -EINVAL; + if(tid > 0){ + Uproc *p; + + err = -ESRCH; + qlock(&proctab); + if((p = getproc(tid)) && (p->pid == pid)){ + Usiginfo si; + + memset(&si, 0, sizeof(si)); + si.signo = sig; + si.code = SI_TKILL; + si.kill.pid = current->tid; + si.kill.uid = current->uid; + err = killproc(p, &si, 0); + } + qunlock(&proctab); + } + return err; +} + +int sys_rt_sigqueueinfo(int pid, int sig, void *info) +{ + int err; + Uproc *p; + Usiginfo si; + + trace("sys_rt_sigqueueinfo(%d, %S, %p)", pid, sig, info); + + err = -ESRCH; + qlock(&proctab); + if(p = getproc(pid)){ + memset(&si, 0, sizeof(si)); + linux2siginfo(info, &si); + si.signo = sig; + si.code = SI_QUEUE; + err = killproc(p, &si, 1); + } + qunlock(&proctab); + return err; +} + +enum { + PIDMAPBITS1 = 8*sizeof(ulong), +}; + +int sys_kill(int pid, int sig) +{ + int i, j, n; + Uproc *p; + Usiginfo si; + ulong pidmap[(MAXPROC + PIDMAPBITS1-1) / PIDMAPBITS1]; + ulong m; + + trace("sys_kill(%d, %S)", pid, sig); + + n = 0; + memset(pidmap, 0, sizeof(pidmap)); + qlock(&proctab); + for(i=0; iwstate & WEXITED) + continue; + if(p->kpid <= 0) + continue; + + if(pid == 0){ + if(p->pgid != current->pgid) + continue; + } else if(pid == -1){ + if(p->pid <= 1) + continue; + if(p->tid == current->tid) + continue; + } else if(pid < -1) { + if(p->pgid != -pid) + continue; + } else { + if(p->pid != pid) + continue; + } + + /* make sure we send only one signal per pid */ + j = pidhash(p->pid); + m = 1 << (j % PIDMAPBITS1); + j /= PIDMAPBITS1; + if(pidmap[j] & m) + continue; + pidmap[j] |= m; + + if(sig > 0){ + memset(&si, 0, sizeof(si)); + si.signo = sig; + si.code = SI_USER; + si.kill.pid = current->tid; + si.kill.uid = current->uid; + killproc(p, &si, 1); + } + n++; + } + qunlock(&proctab); + if(n == 0) + return -ESRCH; + return 0; +} + +int sys_set_tid_address(int *tidptr) +{ + trace("sys_set_tid_address(%p)", tidptr); + + current->cleartidptr = tidptr; + return current->pid; +} + +struct linux_sched_param +{ + int sched_priority; +}; + +int sys_sched_setscheduler(int pid, int policy, void *param) +{ + trace("sys_sched_setscheduler(%d, %d, %p)", pid, policy, param); + + if(getproc(pid) == nil) + return -ESRCH; + return 0; +} + +int sys_sched_getscheduler(int pid) +{ + trace("sys_sched_getscheduler(%d)", pid); + + if(getproc(pid) == nil) + return -ESRCH; + return 0; +} + +int sys_sched_setparam(int pid, void *param) +{ + trace("sys_sched_setparam(%d, %p)", pid, param); + + if(getproc(pid) == nil) + return -ESRCH; + return 0; +} + +int sys_sched_getparam(int pid, void *param) +{ + struct linux_sched_param *p = param; + + trace("sys_sched_getparam(%d, %p)", pid, param); + + if(getproc(pid) == nil) + return -ESRCH; + if(p == nil) + return -EINVAL; + p->sched_priority = 0; + + return 0; +} + +int sys_sched_yield(void) +{ + trace("sys_sched_yield()"); + + sleep(0); + return 0; +} + +enum { + RLIMIT_CPU, + RLIMIT_FSIZE, + RLIMIT_DATA, + RLIMIT_STACK, + RLIMIT_CORE, + RLIMIT_RSS, + RLIMIT_NPROC, + RLIMIT_NOFILE, + RLIMIT_MEMLOCK, + RLIMIT_AS, + RLIMIT_LOCKS, + RLIMIT_SIGPENDING, + RLIMIT_MSGQUEUE, + + RLIM_NLIMITS, + + RLIM_INFINITY = ~0UL, +}; + +struct linux_rlimit +{ + ulong rlim_cur; + ulong rlim_max; +}; + +int sys_getrlimit(long resource, void *rlim) +{ + struct linux_rlimit *r = rlim; + + trace("sys_getrlimit(%ld, %p)", resource, rlim); + + if(resource >= RLIM_NLIMITS) + return -EINVAL; + if(rlim == nil) + return -EFAULT; + + r->rlim_cur = RLIM_INFINITY; + r->rlim_max = RLIM_INFINITY; + + switch(resource){ + case RLIMIT_STACK: + r->rlim_cur = USTACK; + r->rlim_max = USTACK; + break; + case RLIMIT_CORE: + r->rlim_cur = 0; + break; + case RLIMIT_NPROC: + r->rlim_cur = MAXPROC; + r->rlim_max = MAXPROC; + break; + case RLIMIT_NOFILE: + r->rlim_cur = MAXFD; + r->rlim_max = MAXFD; + break; + } + return 0; +} + +int sys_setrlimit(long resource, void *rlim) +{ + trace("sys_setrlimit(%ld, %p)", resource, rlim); + + if(resource >= RLIM_NLIMITS) + return -EINVAL; + if(rlim == nil) + return -EFAULT; + + return -EPERM; +} + diff --git a/linux_emul_base/procdev.c b/linux_emul_base/procdev.c new file mode 100644 index 0000000..1028a76 --- /dev/null +++ b/linux_emul_base/procdev.c @@ -0,0 +1,732 @@ +#include +#include +#include +#include +#include + +#include "dat.h" +#include "fns.h" +#include "linux.h" + +enum { + Qproc, + Qstat, + Qcpuinfo, + Qmeminfo, + Quptime, + Qloadavg, + Qself, + Qpid, + Qcwd, + Qcmdline, + Qenviron, + Qexe, + Qroot, + Qpidstat, + Qpidstatm, + Qstatus, + Qmaps, + Qfd, + Qfd1, + Qtask, + Qtask1, + Qmax, +}; + +static struct { + int mode; + char *name; +} procdevtab[] = { + 0555|S_IFDIR, "proc", + 0444|S_IFREG, "stat", + 0444|S_IFREG, "cpuinfo", + 0444|S_IFREG, "meminfo", + 0444|S_IFREG, "uptime", + 0444|S_IFREG, "loadavg", + 0777|S_IFLNK, "self", + 0555|S_IFDIR, "###", + 0777|S_IFLNK, "cwd", + 0444|S_IFREG, "cmdline", + 0444|S_IFREG, "environ", + 0777|S_IFLNK, "exe", + 0777|S_IFLNK, "root", + 0444|S_IFREG, "stat", + 0444|S_IFREG, "statm", + 0444|S_IFREG, "status", + 0444|S_IFREG, "maps", + 0555|S_IFDIR, "fd", + 0777|S_IFLNK, "###", + 0555|S_IFDIR, "task", + 0555|S_IFDIR, "###", +}; + +typedef struct Procfile Procfile; +struct Procfile +{ + Ufile; + int q; + int pid; + vlong lastoff; + char *data; + int ndata; +}; + +static int +path2q(char *path, int *ppid, int *pfd) +{ + int i, q, pid, fd; + char *x; + + q = -1; + pid = -1; + fd = -1; + path++; + for(i=Qproc; i='0' && path[0]<='9'){ + switch(i){ + case Qpid: + case Qtask1: + pid = atoi(path); + goto match; + case Qfd1: + fd = atoi(path); + goto match; + } + } + if(strcmp(path, procdevtab[i].name) == 0){ +match: if(x == nil){ + q = i; + break; + } + if(i == Qself){ /* hack */ + pid = current->pid; + i = Qpid; + } + if((procdevtab[i].mode & ~0777) == S_IFDIR){ + path = x+1; + if(i == Qtask1) + i = Qpid; + } + } + if(x != nil) + *x = '/'; + } + if(ppid) + *ppid = pid; + if(pfd) + *pfd = fd; + return q; +} + +/* + * the proc device also implements the functionality + * for /dev/std^(in out err) and /dev/fd. we just + * rewrite the path to the names used in /proc. + */ +static char* +rewritepath(char *path) +{ + if(strcmp(path, "/dev/stdin")==0){ + path = kstrdup("/proc/self/fd/0"); + } else if(strcmp(path, "/dev/stdout")==0){ + path = kstrdup("/proc/self/fd/1"); + } else if(strcmp(path, "/dev/stderr")==0){ + path = kstrdup("/proc/self/fd/2"); + } else if(strncmp(path, "/dev/fd", 7) == 0){ + path = allocpath("/proc/self", "fd", path+7); + } else { + path = kstrdup(path); + } + return path; +} + +static int +readlinkproc(char *path, char *buf, int len); + +static int +openproc(char *path, int mode, int perm, Ufile **pf) +{ + char buf[256], *t; + int n, q, pid, err; + Procfile *f; + + err = -ENOENT; + path = rewritepath(path); + if((q = path2q(path, &pid, nil)) < 0) + goto out; + if((procdevtab[q].mode & ~0777) == S_IFLNK){ + n = readlinkproc(path, buf, sizeof(buf)-1); + if(n > 0){ + buf[n] = 0; + err = fsopen(buf, mode, perm, pf); + } + goto out; + } + if((mode & O_ACCMODE) != O_RDONLY){ + err = -EPERM; + goto out; + } + if(q >= Qpid){ + qlock(&proctab); + if(getproc(pid) == nil){ + qunlock(&proctab); + goto out; + } + qunlock(&proctab); + } + + /* hack */ + if(strncmp(path, "/proc/self", 10) == 0){ + t = ksmprint("/proc/%d%s", pid, path+10); + free(path); path = t; + } + + f = kmallocz(sizeof(*f), 1); + f->ref = 1; + f->mode = mode; + f->path = path; path = nil; + f->fd = -1; + f->dev = PROCDEV; + f->q = q; + f->pid = pid; + *pf = f; + err = 0; + +out: + free(path); + return err; +} + +static int +closeproc(Ufile *file) +{ + Procfile *f = (Procfile*)file; + + if(f->data) + free(f->data); + return 0; +} + +enum { + SScpu, + SSswitches, + SSinterrupts, + SSsyscalls, + SSpagefaults, + SStlbmisses, + SStlbpurges, + SSloadavg, + SSidletime, + SSintrtime, + SSmax, +}; + +static char* +sysstat(ulong *prun, ulong *pidle, ulong *pload) +{ + char buf[1024], *p, *e, *t, *data; + ulong dt, swtch, user, sys, load; + static ulong run, idle, intr; + int n, fd; + + data = nil; + swtch = user = sys = load = 0; + + dt = (ulong)(((nsec() - boottime) * HZ) / 1000000000LL) - run; + run += dt; + + n = 0; + if((fd = open("/dev/sysstat", OREAD)) >= 0){ + n = read(fd, buf, sizeof(buf)-1); + close(fd); + } + if(n > 0){ + buf[n] = 0; + p = buf; + while(e = strchr(p, '\n')){ + char *f[SSmax]; + *e = 0; + if(getfields(p, f, SSmax, 1, "\t ") != SSmax) + break; + + if(p == buf){ + swtch += atoi(f[SSswitches]); + + idle += (atoi(f[SSidletime]) * dt)/100; + intr += (atoi(f[SSintrtime]) * dt)/100; + + load = 100-atoi(f[SSidletime]); + + user = run - idle - intr; + sys = run - user; + + data = ksmprint("cpu %lud %lud %lud %lud %lud %lud %lud\n", + user, 0UL, sys, idle, 0UL, intr, 0UL); + } + t = ksmprint("%scpu%d %lud %lud %lud %lud %lud %lud %lud\n", + data, atoi(f[SScpu]), user, 0UL, sys, idle, 0UL, intr, 0UL); + free(data); + data = t; + + p = e+1; + } + t = ksmprint("%sbtime %lud\nctxt %lud\n", data, + (ulong)(boottime/1000000000LL), swtch); + free(data); + data = t; + } + if(prun) + *prun = run; + if(pidle) + *pidle = idle; + if(pload) + *pload = load; + + return data; +} + +static char* +procstat(Uproc *p) +{ + return + (p->wstate & WEXITED) ? "Z (zombie)" : + (p->wstate & WSTOPPED) ? "T (stopped)" : + (p->state == nil) ? "R (running)" : "S (sleeping)"; +} + +static char* +procname(Uproc *p) +{ + char *s; + + p = getproc(p->pid); + if(p == nil || p->comm == nil) + return ""; + if(s = strrchr(p->comm, '/')) + return s+1; + return p->comm; +} + + +static void +gendata(Procfile *f) +{ + char *s, *t; + int i, nproc, nready; + ulong tms[4]; + Uproc *p; + + f->ndata = 0; + if(s = f->data){ + f->data = nil; + free(s); + } + s = nil; + + if(f->q >= Qpid){ + ulong vmsize, vmdat, vmlib, vmshr, vmstk, vmexe; + + qlock(&proctab); + if((p = getproc(f->pid)) == nil){ + qunlock(&proctab); + return; + } + switch(f->q){ + case Qcmdline: + p = getproc(p->pid); + if(p == nil || p->comm == nil) + break; + i = strlen(p->comm)+1; + if(i >= p->ncomm-2) + break; + f->ndata = p->ncomm-i-2; + f->data = kmalloc(f->ndata); + memmove(f->data, p->comm + i, f->ndata); + qunlock(&proctab); + return; + + case Qenviron: + break; + case Qpidstat: + if(proctimes(p, tms) != 0) + memset(tms, 0, sizeof(tms)); + vmsize = procmemstat(p, nil, nil, nil, nil, nil); + s = ksmprint( + "%d (%s) %c %d %d %d %d %d %lud %lud " + "%lud %lud %lud %lud %lud %ld %ld %ld %ld %ld " + "%ld %lud %lud %ld %lud %lud %lud %lud %lud %lud " + "%lud %lud %lud %lud %lud %lud %lud %d %d\n", + p->tid, + procname(p), + procstat(p)[0], + p->ppid, + p->pgid, + p->psid, + 0, /* tty */ + 0, /* tty pgrp */ + 0UL, /* flags */ + 0UL, 0UL, 0UL, 0UL, /* pagefault stats */ + tms[0], /* utime */ + tms[1], /* stime */ + tms[2], /* cutime */ + tms[3], /* cstime */ + 0UL, /* priority */ + 0UL, /* nice */ + 0UL, /* always 0UL */ + 0UL, /* time to next alarm */ + (ulong)(((p->starttime - boottime) * HZ) / 1000000000LL), + vmsize, /* vm size in bytes */ + vmsize, /* vm working set */ + 0UL, /* rlim */ + p->codestart, + p->codeend, + p->stackstart, + 0UL, /* SP */ + 0UL, /* PC */ + 0UL, /* pending signal mask */ + 0UL, /* blocked signal mask */ + 0UL, /* ignored signal mask */ + 0UL, /* catched signal mask */ + 0UL, /* wchan */ + 0UL, /* nswap */ + 0UL, /* nswap children */ + p->exitsignal, + 0); /* cpu */ + break; + case Qpidstatm: + vmsize = procmemstat(p, &vmdat, &vmlib, &vmshr, &vmstk, &vmexe); + s = ksmprint("%lud %lud %lud %lud %lud %lud %lud\n", + vmsize/PAGESIZE, vmsize/PAGESIZE, vmshr/PAGESIZE, + vmexe/PAGESIZE, vmstk/PAGESIZE, vmlib/PAGESIZE, 0UL); + break; + case Qstatus: + s = ksmprint( + "Name:\t%s\n" + "State:\t%s\n" + "Tgid:\t%d\n" + "Pid:\t%d\n" + "PPid:\t%d\n" + "Uid:\t%d\t%d\t%d\t%d\n" + "Gid:\t%d\t%d\t%d\t%d\n" + "FDSize:\t%d\n" + "Threads:\t%d\n", + procname(p), + procstat(p), + p->pid, + p->tid, + p->ppid, + p->uid, p->uid, p->uid, p->uid, + p->gid, p->gid, p->gid, p->gid, + MAXFD, + threadcount(p->pid)); + break; + case Qmaps: + break; + } + qunlock(&proctab); + } else { + ulong run, idle, load; + + nproc = nready = 0; + qlock(&proctab); + for(i=0; istate == nil) + nready++; + } + i = proctab.nextpid; + qunlock(&proctab); + + switch(f->q){ + case Qstat: + s = sysstat(nil, nil, nil); + t = ksmprint( + "%s" + "processes %d\n" + "procs_running %d\n" + "procs_blocked %d\n", + s, + i, + nready, + nproc-nready); + free(s); + s = t; + break; + case Qcpuinfo: + break; + case Qmeminfo: + break; + case Quptime: + free(sysstat(&run, &idle, nil)); + s = ksmprint("%lud.%lud %lud.%lud\n", run/HZ, run%HZ, idle/HZ, idle%HZ); + break; + case Qloadavg: + free(sysstat(nil, nil, &load)); + s = ksmprint("%lud.%lud 0 0 %d/%d %d\n", load/100, load%100, nready, nproc, i); + break; + } + } + + f->data = s; + f->ndata = s ? strlen(s) : 0; +} + +static vlong +sizeproc(Ufile *file) +{ + Procfile *f = (Procfile*)file; + + if(f->data == nil) + gendata(f); + return f->ndata; +} + +static int +readproc(Ufile *file, void *buf, int len, vlong off) +{ + Procfile *f = (Procfile*)file; + int ret; + + if((f->data == nil) || (off != f->lastoff)) + gendata(f); + ret = 0; + if(f->data && (off < f->ndata)){ + ret = f->ndata - off; + if(ret > len) + ret = len; + memmove(buf, f->data + off, ret); + f->lastoff = off + ret; + } + return ret; +} + +static int +readlinkproc(char *path, char *buf, int len) +{ + int err, q, pid, fd; + char *data; + Uproc *p; + Ufile *a; + + err = -ENOENT; + path = rewritepath(path); + if((q = path2q(path, &pid, &fd)) < 0) + goto out; + data = nil; + if(q >= Qpid){ + qlock(&proctab); + if((p = getproc(pid)) == nil){ + qunlock(&proctab); + goto out; + } + switch(q){ + case Qcwd: + data = kstrdup(p->cwd); + break; + case Qexe: + p = getproc(p->pid); + if(p == nil || p->comm == nil) + break; + data = kstrdup(p->comm); + break; + case Qroot: + data = kstrdup(p->root ? p->root : "/"); + break; + case Qfd1: + a = procfdgetfile(p, fd); + if(a == nil || a->path == nil){ + putfile(a); + qunlock(&proctab); + goto out; + } + data = kstrdup(a->path); + putfile(a); + break; + } + qunlock(&proctab); + } else { + switch(q){ + case Qself: + data = ksmprint("/proc/%d", current->pid); + break; + } + } + err = 0; + if(data){ + err = strlen(data); + if(err > len) + err = len; + memmove(buf, data, err); + free(data); + } +out: + free(path); + return err; +} + +static int +readdirproc(Ufile *file, Udirent **pd) +{ + Procfile *f = (Procfile*)file; + char buf[12]; + Uproc *p; + Ufile *a; + int n, i; + + n = 0; + switch(f->q){ + case Qproc: + for(i=f->q+1; (procdevtab[i].mode & ~0777) != S_IFDIR; i++){ + if((*pd = newdirent(f->path, procdevtab[i].name, procdevtab[i].mode)) == nil) + break; + pd = &((*pd)->next); + n++; + } + /* no break */ + case Qtask: + qlock(&proctab); + for(i=0; iq == Qproc) && (p->pid != p->tid)) + continue; + if((f->q == Qtask) && (p->pid != f->pid)) + continue; + snprint(buf, sizeof(buf), "%d", p->tid); + if((*pd = newdirent(f->path, buf, procdevtab[i].mode)) == nil) + break; + pd = &((*pd)->next); + n++; + } + qunlock(&proctab); + break; + + case Qpid: + if((*pd = newdirent(f->path, procdevtab[Qtask].name, procdevtab[Qtask].mode)) == nil) + break; + pd = &((*pd)->next); + n++; + /* no break */ + case Qtask1: + if((*pd = newdirent(f->path, procdevtab[Qfd].name, procdevtab[Qfd].mode)) == nil) + break; + pd = &((*pd)->next); + n++; + for(i=Qpid+1; (procdevtab[i].mode & ~0777) != S_IFDIR; i++){ + if((*pd = newdirent(f->path, procdevtab[i].name, procdevtab[i].mode)) == nil) + break; + pd = &((*pd)->next); + n++; + } + break; + + case Qfd: + qlock(&proctab); + if((p = getproc(f->pid)) == nil){ + qunlock(&proctab); + break; + } + for(i=0; ipath == nil){ + putfile(a); + continue; + } + putfile(a); + snprint(buf, sizeof(buf), "%d", i); + if((*pd = newdirent(f->path, buf, procdevtab[Qfd1].mode)) == nil) + break; + pd = &((*pd)->next); + n++; + } + qunlock(&proctab); + break; + } + + return n; +} + +static int +statproc(char *path, int, Ustat *s) +{ + int q, pid, fd, uid, gid, err; + ulong ctime; + Uproc *p; + Ufile *a; + + err = -ENOENT; + path = rewritepath(path); + if((q = path2q(path, &pid, &fd)) < 0) + goto out; + if(q >= Qpid){ + qlock(&proctab); + if((p = getproc(pid)) == nil){ + qunlock(&proctab); + goto out; + } + if(q == Qfd1){ + a = procfdgetfile(p, fd); + if(a == nil || a->path == nil){ + putfile(a); + qunlock(&proctab); + goto out; + } + putfile(a); + } + uid = p->uid; + gid = p->gid; + ctime = p->starttime/1000000000LL; + qunlock(&proctab); + } else { + uid = current->uid; + gid = current->gid; + ctime = boottime/1000000000LL; + } + err = 0; + s->mode = procdevtab[q].mode; + s->uid = uid; + s->gid = gid; + s->size = 0; + s->ino = hashpath(path); + s->dev = 0; + s->rdev = 0; + s->atime = s->mtime = s->ctime = ctime; +out: + free(path); + return err; +} + +static int +fstatproc(Ufile *f, Ustat *s) +{ + return fsstat(f->path, 0, s); +}; + +static Udev procdev = +{ + .open = openproc, + .read = readproc, + .size = sizeproc, + .readlink = readlinkproc, + .readdir = readdirproc, + .close = closeproc, + .stat = statproc, + .fstat = fstatproc, +}; + +void procdevinit(void) +{ + devtab[PROCDEV] = &procdev; + + fsmount(&procdev, "/proc"); + fsmount(&procdev, "/dev/fd"); + fsmount(&procdev, "/dev/stdin"); + fsmount(&procdev, "/dev/stdout"); + fsmount(&procdev, "/dev/stderr"); +} diff --git a/linux_emul_base/ptydev.c b/linux_emul_base/ptydev.c new file mode 100644 index 0000000..80de36f --- /dev/null +++ b/linux_emul_base/ptydev.c @@ -0,0 +1,944 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Termios Termios; +typedef struct Winsize Winsize; +typedef struct Cbuf Cbuf; +typedef struct Tty Tty; +typedef struct Pty Pty; +typedef struct Ptyfile Ptyfile; + +/* cflags */ +enum { + IGNBRK = 01, + BRKINT = 02, + IGNPAR = 04, + PARMRK = 010, + INPCK = 020, + ISTRIP = 040, + INLCR = 0100, + IGNCR = 0200, + ICRNL = 0400, + IUCLC = 01000, + IXON = 02000, + IXANY = 04000, + IXOFF = 010000, + IMAXBEL = 020000, + IUTF8 = 040000, +}; + +/* oflags */ +enum { + OPOST = 01, + OLCUC = 02, + ONLCR = 04, + OCRNL = 010, + ONOCR = 020, + ONLRET = 040, + OFILL = 0100, + OFDEL = 0200, + NLDLY = 0400, + NL0 = 0, + NL1 = 0400, + CRDLY = 03000, + CR0 = 0, + CR1 = 01000, + CR2 = 02000, + CR3 = 03000, + TABDLY = 014000, + TAB0 = 0, + TAB1 = 04000, + TAB2 = 010000, + TAB3 = 014000, + XTABS = 014000, + BSDLY = 020000, + BS0 = 0, + BS1 = 020000, + VTDLY = 040000, + VT0 = 0, + VT1 = 040000, + FFDLY = 0100000, + FF0 = 0, + FF1 = 0100000, +}; + +/* cflags */ +enum { + CSIZE = 060, + CS5 = 0, + CS6 = 020, + CS7 = 040, + CS8 = 060, + CREAD = 0200, + CLOCAL = 04000, + HUPCL = 02000, +}; + +/* lflags */ +enum { + ISIG = 01, + ICANON = 02, + XCASE = 04, + ECHO = 010, + ECHOE = 020, + ECHOK = 040, + ECHONL = 0100, + NOFLSH = 0200, + TOSTOP = 0400, + ECHOCTL = 01000, + ECHOPRT = 02000, + ECHOKE = 04000, + FLUSH0 = 010000, + PENDIN = 040000, + IEXTEN = 0100000, +}; + +/* cc */ +enum { + VINTR = 0, + VQUIT, + VERASE, + VKILL, + VEOF, + VTIME, + VMIN, + VSWTCH, + VSTART, + VSTOP, + VSUSP, + VEOL, + VREPRINT, + VDISCARD, + VERASEW, + VLNEXT, + VEOL2, + NCCS, +}; + +struct Termios +{ + int iflag; /* input modes */ + int oflag; /* output modes */ + int cflag; /* control modes */ + int lflag; /* local modes */ + uchar cline; + uchar cc[NCCS]; /* control characters */ +}; + +struct Winsize +{ + ushort row; + ushort col; + ushort px; + ushort py; +}; + +struct Cbuf +{ + int rp; + int wp; + char cb[256]; +}; + +struct Tty +{ + Termios t; + Winsize winsize; + + int escaped; + int eol; + + int pgid; + + Cbuf wb; + Cbuf rb; +}; + +struct Pty +{ + Tty; + + int id; + int closed; + int locked; + + struct { + Uwaitq r; + Uwaitq w; + } q[2]; + + Ref; + QLock; +}; + +struct Ptyfile +{ + Ufile; + + Pty *pty; + + int master; +}; + +static Pty *ptys[64]; + +int cbput(Cbuf *b, char c) +{ + int x; + x = b->wp+1&(sizeof(b->cb)-1); + if(x == b->rp) + return -1; + b->cb[b->wp] = c; + b->wp = x; + return 0; +} + +int cbget(Cbuf *b) +{ + char c; + if(b->rp == b->wp) + return -1; + c = b->cb[b->rp]; + b->rp = (b->rp + 1) & (sizeof(b->cb)-1); + return c; +} + +int cbfill(Cbuf *b) +{ + return (b->wp - b->rp) & (sizeof(b->cb)-1); +} + +void ttyinit(Tty *t) +{ + memset(&t->t, 0, sizeof(t->t)); + + t->t.iflag = ICRNL; + t->t.oflag = OPOST|ONLCR|NL0|CR0|TAB0|BS0|VT0|FF0; + t->t.lflag = ICANON|IEXTEN|ECHO|ECHOE|ECHOK; + + if(current) + t->pgid = current->pgid; +} + +int ttywrite(Tty *t, char *buf, int len) +{ + char *p, *e; + + for(p=buf, e=buf+len; pt.oflag & OPOST) == 0) { + if(cbput(&t->wb, c) < 0) + break; + continue; + } + switch(c) { + case '\n': + if(t->t.oflag & ONLCR) { + if(cbput(&t->wb, '\r') < 0) + goto done; + } + if(cbput(&t->wb, c) < 0) + goto done; + break; + + case '\t': + if((t->t.oflag & TAB3) == TAB3) { + int tab; + + tab = 8; + while(tab--) + cbput(&t->wb, ' '); + break; + } + /* Fall Through */ + default: + if(t->t.oflag & OLCUC) + if(c >= 'a' && c <= 'z') + c = 'A' + (c-'a'); + if(cbput(&t->wb, c) < 0) + goto done; + } + } +done: + return p-buf; +} + +int ttycanread(Tty *t, int *n) +{ + int x; + + x = cbfill(&t->rb); + if(t->t.lflag & ICANON){ + if(t->eol == 0) + return 0; + } else { + if(x == 0) + return 0; + } + if(n != nil) + *n = x; + return 1; +} + +int ttyread(Tty *t, char *buf, int len) +{ + char *p, *e; + + if((t->t.lflag & ICANON) && t->eol == 0) + return 0; + + for(p=buf, e=buf+len; prb)) < 0) + break; + + if(c==0 || c=='\n'){ + t->eol--; + if(t->t.lflag & ICANON){ + if(c == 0) + break; + *p++ = c; + break; + } + } + + *p = c; + } + return p-buf; +} + + +static void +echo(Tty *t, char c) +{ + if(t->t.lflag & ECHO) { + switch(c) { + case '\r': + if(t->t.oflag & OCRNL) { + cbput(&t->wb, '\n'); + break; + } + cbput(&t->wb, c); + break; + case '\n': + if(t->t.oflag & ONLCR) + cbput(&t->wb, '\r'); + cbput(&t->wb, '\n'); + break; + case '\t': + if((t->t.oflag & TAB3) == TAB3) { + int tab; + + tab = 8; + while(tab--) + cbput(&t->wb, ' '); + break; + } + /* Fall Through */ + default: + cbput(&t->wb, c); + break; + } + } + else + if(c == '\n' && (t->t.lflag&(ECHONL|ICANON)) == (ECHONL|ICANON)) { + if(t->t.oflag & ONLCR) + cbput(&t->wb, '\r'); + cbput(&t->wb, '\n'); + } +} + +static int +bs(Tty *t) +{ + char c; + int x; + + if(cbfill(&t->rb) == 0) + return 0; + x = (t->rb.wp-1)&(sizeof(t->rb.cb)-1); + c = t->rb.cb[x]; + if(c == 0 || c == '\n') + return 0; + t->rb.wp = x; + echo(t, '\b'); + if(t->t.lflag & ECHOE) { + echo(t, ' '); + echo(t, '\b'); + } + return 1; +} + +int ttywriteinput(Tty *t, char *buf, int len) +{ + char *p, *e; + + for(p=buf, e=buf+len; pt.iflag & ISTRIP) + c &= 0177; + + if((t->t.iflag & IXON) && c == t->t.cc[VSTOP]) { + p++; + break; + } + + switch(c) { + case '\r': + if(t->t.iflag & IGNCR) + continue; + if(t->t.iflag & ICRNL) + c = '\n'; + break; + case '\n': + if(t->t.iflag&INLCR) + c = '\r'; + break; + } + + if(t->t.lflag & ISIG) { + if(c == t->t.cc[VINTR]){ + if(t->pgid > 0) + sys_kill(-t->pgid, SIGINT); + continue; + } + if(c == t->t.cc[VQUIT]){ + if(t->pgid > 0) + sys_kill(-t->pgid, SIGQUIT); + continue; + } + } + + if((t->t.lflag & ICANON) && t->escaped == 0) { + if(c == t->t.cc[VERASE]) { + bs(t); + continue; + } + if(c == t->t.cc[VKILL]) { + while(bs(t)) + ; + if(t->t.lflag & ECHOK) + echo(t, '\n'); + continue; + } + } + + if(t->escaped == 0 && (c == t->t.cc[VEOF] || c == '\n')) + t->eol++; + + if((t->t.lflag & ICANON) == 0) { + echo(t, c); + cbput(&t->rb, c); + continue; + } + + if(t->escaped) + echo(t, '\b'); + + if(c != t->t.cc[VEOF]) + echo(t, c); + + if(c != '\\') { + if(c == t->t.cc[VEOF]) + c = 0; + cbput(&t->rb, c); + t->escaped = 0; + continue; + } + if(t->escaped) { + cbput(&t->rb, '\\'); + t->escaped = 0; + } + else + t->escaped = 1; + } + + return p-buf; +} + +int ttycanreadoutput(Tty *t, int *n) +{ + int x; + + x = cbfill(&t->wb); + if(n != nil) + *n = x; + return x > 0 ? 1 : 0; +} + +int ttyreadoutput(Tty *t, char *buf, int len) +{ + char *p, *e; + + for(p=buf, e=buf+len; pwb)) < 0) + break; + *p = c; + } + return p-buf; +} + +static int +pollpty(Ufile *f, void *tab) +{ + Ptyfile *p = (Ptyfile*)f; + int err; + int n; + + if(p->pty == nil) + return 0; + + qlock(p->pty); + if(p->master){ + pollwait(p, &p->pty->q[1].r, tab); + n = ttycanreadoutput(p->pty, nil); + } else { + pollwait(p, &p->pty->q[0].r, tab); + n = ttycanread(p->pty, nil); + } + err = POLLOUT; + if(n){ + err |= POLLIN; + } else if(p->master==0 && p->pty->closed){ + err |= (POLLIN | POLLHUP); + } + qunlock(p->pty); + + return err; +} + +static int +readpty(Ufile *f, void *data, int len, vlong) +{ + int err; + Ptyfile *p = (Ptyfile*)f; + + if(p->pty == nil) + return -EPERM; + qlock(p->pty); + for(;;){ + if(p->master){ + err = ttycanreadoutput(p->pty, nil); + } else { + err = ttycanread(p->pty, nil); + } + if(err > 0){ + if(p->master){ + err = ttyreadoutput(p->pty, (char*)data, len); + }else{ + err = ttyread(p->pty, (char*)data, len); + } + } else { + if(p->master == 0 && p->pty->closed){ + err = -EIO; + } else if(p->mode & O_NONBLOCK){ + err = -EAGAIN; + } else { + if((err = sleepq(&p->pty->q[p->master].r, p->pty, 1)) == 0) + continue; + } + } + wakeq(&p->pty->q[!p->master].w, MAXPROC); + break; + } + qunlock(p->pty); + + return err; +} + +static int +writepty(Ufile *f, void *data, int len, vlong) +{ + Ptyfile *p = (Ptyfile*)f; + int err; + + if(p->pty == nil) + return -EPERM; + if(len == 0) + return len; + + qlock(p->pty); + for(;;){ + if(p->pty->closed){ + err = -EIO; + break; + } + if(p->master){ + err = ttywriteinput(p->pty, (char*)data, len); + } else{ + err = ttywrite(p->pty, (char*)data, len); + } + if(err == 0){ + if((err = sleepq(&p->pty->q[p->master].w, p->pty, 1)) == 0) + continue; + } else { + if(ttycanread(p->pty, nil)) + wakeq(&p->pty->q[0].r, MAXPROC); + if(ttycanreadoutput(p->pty, nil)) + wakeq(&p->pty->q[1].r, MAXPROC); + } + break; + } + qunlock(p->pty); + + return err; +} + +static int +closepty(Ufile *f) +{ + Ptyfile *p = (Ptyfile*)f; + + if(p->pty == nil) + return 0; + + qlock(p->pty); + if(p->master) + p->pty->closed = 1; + if(!decref(p->pty)){ + ptys[p->pty->id] = nil; + qunlock(p->pty); + free(p->pty); + } else { + wakeq(&p->pty->q[0].r, MAXPROC); + wakeq(&p->pty->q[0].w, MAXPROC); + wakeq(&p->pty->q[1].r, MAXPROC); + wakeq(&p->pty->q[1].w, MAXPROC); + qunlock(p->pty); + } + return 0; +} + +static int +changetty(Ptyfile *tty) +{ + Ufile *old; + + if(old = gettty()){ + putfile(old); + return (old == tty) ? 0 : -EPERM; + } + tty->pty->pgid = current->pgid; + settty(tty); + return 0; +} + +static int +ioctlpty(Ufile *f, int cmd, void *arg) +{ + Ptyfile *p = (Ptyfile*)f; + int err, pid; + + if(p->pty == nil) + return -ENOTTY; + + trace("ioctlpty(%s, %lux, %p)", p->path, (ulong)cmd, arg); + + err = 0; + qlock(p->pty); + switch(cmd){ + default: + trace("ioctlpty: unknown: 0x%x", cmd); + err = -ENOTTY; + break; + + case 0x5401: /* TCGETS */ + memmove(arg, &p->pty->t, sizeof(Termios)); + break; + + case 0x5402: /* TCSETS */ + case 0x5403: /* TCSETSW */ + case 0x5404: /* TCSETSF */ + memmove(&p->pty->t, arg, sizeof(Termios)); + break; + + case 0x5422: // TIOCNOTTY + if((f = gettty()) && (f != p)){ + putfile(f); + err = -ENOTTY; + break; + } + settty(nil); + break; + + case 0x540E: // TIOCSCTTY + err = changetty(p); + break; + + case 0x540F: // TIOCGPGRP + *(int*)arg = p->pty->pgid; + break; + + case 0x5410: // TIOCSPGRP + p->pty->pgid = *(int*)arg; + break; + + case 0x5413: // TIOCGWINSZ + memmove(arg, &p->pty->winsize, sizeof(Winsize)); + break; + + case 0x5414: // TIOCSWINSZ + if(memcmp(&p->pty->winsize, arg, sizeof(Winsize)) == 0) + break; + memmove(&p->pty->winsize, arg, sizeof(Winsize)); + if((pid = p->pty->pgid) > 0){ + qunlock(p->pty); + + sys_kill(-pid, SIGWINCH); + return 0; + } + break; + case 0x40045431: // TIOCSPTLCK + if(p->master) + p->pty->locked = *(int*)arg; + break; + + case 0x80045430: + *(int*)arg = p->pty->id; + break; + + case 0x541B: + if(arg == nil) + break; + if(p->master){ + ttycanreadoutput(p->pty, &err); + } else { + ttycanread(p->pty, &err); + } + if(err < 0){ + *((int*)arg) = 0; + break; + } + *((int*)arg) = err; + err = 0; + break; + } + qunlock(p->pty); + + return err; +} + +static int +openpty(char *path, int mode, int perm, Ufile **pf) +{ + Pty *pty; + Ptyfile *p; + int id; + int master; + + USED(perm); + + if(strcmp("/dev/tty", path)==0){ + if(*pf = gettty()) + return 0; + return -ENOTTY; + } else if(strcmp("/dev/pts", path)==0){ + pty = nil; + master = -1; + } else if(strcmp("/dev/ptmx", path)==0){ + master = 1; + for(id=0; idref = 1; + + ttyinit(pty); + + ptys[pty->id = id] = pty; + } else { + master = 0; + if(strncmp("/dev/pts/", path, 9) != 0) + return -ENOENT; + id = atoi(path + 9); + if(id < 0 || id >= nelem(ptys)) + return -ENOENT; + if((pty = ptys[id]) == nil) + return -ENOENT; + + qlock(pty); + if(pty->closed || pty->locked){ + qunlock(pty); + return -EIO; + } + incref(pty); + qunlock(pty); + } + + p = kmallocz(sizeof(*p), 1); + p->dev = PTYDEV; + p->ref = 1; + p->fd = -1; + p->mode = mode; + p->path = kstrdup(path); + p->pty = pty; + p->master = master; + + if(!master && !(mode & O_NOCTTY)) + changetty(p); + + *pf = p; + + return 0; +} + +static int +readdirpty(Ufile *f, Udirent **pd) +{ + Ptyfile *p = (Ptyfile*)f; + int i, n; + + *pd = nil; + if(p->pty != nil) + return -EPERM; + n = 0; + for(i=0; ipath, buf, S_IFCHR | 0666)) == nil) + break; + pd = &((*pd)->next); + n++; + } + return n; +} + +static int +fstatpty(Ufile *f, Ustat *s) +{ + Ptyfile *p = (Ptyfile*)f; + + if(p->pty != nil){ + s->mode = 0666 | S_IFCHR; + if(p->master){ + s->rdev = 5<<8 | 2; + } else { + s->rdev = 3<<8; + } + } else { + s->mode = 0777 | S_IFDIR; + s->rdev = 0; + } + s->ino = hashpath(p->path); + s->dev = 0; + s->uid = current->uid; + s->gid = current->gid; + s->size = 0; + s->atime = s->mtime = s->ctime = boottime/1000000000LL; + return 0; +}; + +static int +statpty(char *path, int, Ustat *s) +{ + if(strcmp("/dev/tty", path)==0){ + s->mode = 0666 | S_IFCHR; + } else if(strcmp("/dev/ptmx", path)==0){ + s->mode = 0666 | S_IFCHR; + s->rdev = 5<<8 | 2; + } else if(strcmp("/dev/pts", path)==0){ + s->mode = 0777 | S_IFDIR; + } else if(strncmp("/dev/pts/", path, 9)==0){ + int id; + + id = atoi(path + 9); + if(id < 0 || id >= nelem(ptys)) + return -ENOENT; + if(ptys[id] == nil) + return -ENOENT; + + s->mode = 0666 | S_IFCHR; + s->rdev = 3<<8; + } else { + return -ENOENT; + } + s->ino = hashpath(path); + s->uid = current->uid; + s->gid = current->gid; + s->size = 0; + s->atime = s->mtime = s->ctime = boottime/1000000000LL; + return 0; +} + +static int +chmodpty(char *path, int mode) +{ + USED(path); + USED(mode); + + return 0; +} + +static int +chownpty(char *path, int uid, int gid, int link) +{ + USED(path); + USED(uid); + USED(gid); + USED(link); + + return 0; +} + +static int +fchmodpty(Ufile *f, int mode) +{ + USED(f); + USED(mode); + + return 0; +} + +static int +fchownpty(Ufile *f, int uid, int gid) +{ + USED(f); + USED(uid); + USED(gid); + + return 0; +} + +static Udev ptydev = +{ + .open = openpty, + .read = readpty, + .write = writepty, + .poll = pollpty, + .close = closepty, + .readdir = readdirpty, + .ioctl = ioctlpty, + .fstat = fstatpty, + .stat = statpty, + .fchmod = fchmodpty, + .fchown = fchownpty, + .chmod = chmodpty, + .chown = chownpty, +}; + +void ptydevinit(void) +{ + devtab[PTYDEV] = &ptydev; + fsmount(&ptydev, "/dev/pts"); + fsmount(&ptydev, "/dev/ptmx"); + fsmount(&ptydev, "/dev/tty"); +} diff --git a/linux_emul_base/rootdev.c b/linux_emul_base/rootdev.c new file mode 100644 index 0000000..c0f3987 --- /dev/null +++ b/linux_emul_base/rootdev.c @@ -0,0 +1,1286 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Rfile Rfile; +typedef struct Rpath Rpath; + +struct Rfile +{ + Ufile; + struct + { + Dir *d; + int i; + int n; + } diraux; +}; + +struct Rpath +{ + Ref; + + Rpath *hash; + + int deleted; + char str[]; +}; + +static Rpath *rpathtab[64]; +static QLock rpathtablk; + +static Rpath ** +rpathent(char *path) +{ + Rpath **prp; + + prp = &rpathtab[hashpath(path) % nelem(rpathtab)]; + while(*prp){ + if(strcmp(path, (*prp)->str) == 0) + break; + prp = &((*prp)->hash); + } + return prp; +} + +static char* +linkname(char *name) +{ + if(strncmp(name, ".udir.L.", 8) == 0) + name += 8; + return name; +} + +static char* +udirpath(char *base, char *name, char type) +{ + char buf[9]; + + strcpy(buf, ".udir.T."); + buf[6] = type; + return allocpath(base, buf, name); +} + +static int +udirget(char *base, char *name, char type, char **val) +{ + char *f, *b; + int n, r, s; + int fd; + + r = -1; + f = udirpath(base, name, type); + if((fd = open(shortpath(current->kcwd, f), OREAD)) < 0) + goto out; + if(val == nil){ + r = 0; + goto out; + } + if((s = seek(fd, 0, 2)) < 0) + goto out; + b = kmalloc(s+1); + n = 0; + if(s > 0){ + seek(fd, 0, 0); + if((n = read(fd, b, s)) < 0){ + free(b); + goto out; + } + } + b[n] = 0; + + r = 0; + *val = b; +out: + free(f); + close(fd); + return r; +} + +static char* +resolvepath1(char *path, int link) +{ + char *r, *b, *p, *o, *e; + char **a; + + int n; + int i; + + r = nil; + a = nil; + n = 0; + + b = kstrdup(path); + for(p=b; *p; p++){ + if(*p == '/'){ + if((n % 16) == 0) + a = krealloc(a, sizeof(a[0]) * (n+16)); + a[n++] = p; + } + } + + e = nil; + for(i=n-1; i>=0; i--){ + char *t; + char *f; + + o = e; + e = a[i]; + *e++ = 0; + + f = linkname(e); + t = nil; + + if(!udirget(b, f, 'L', &t)){ + if(t == nil) + break; + if(link && o==nil){ + free(t); + if(f != e) + break; + t = udirpath(b, e, 'L'); + } + r = fullpath(b, t); + free(t); + if(o && o[1]){ + t = r; + r = fullpath(t, &o[1]); + free(t); + } + break; + } + + --e; + if(o) *o = '/'; + } + free(b); + free(a); + + return r; +} + +static char * +resolvepath(char *path, int link) +{ + char *t; + int x; + + x = 0; + path = kstrdup(path); + while(t = resolvepath1(path, link)){ + if(++x > 8){ + free(t); + free(path); + return nil; + } + free(path); path = t; + } + return path; +} + +static int +ropen(char *path, int mode, int perm, Ufile **pf) +{ + Ufile *f; + int err; + char *s, *t; + int mode9, perm9; + int fd; + char *base; + char *name; + Rpath **prp; + + trace("ropen(%s, %#o, %#o, ...)", path, mode, perm); + + base = nil; + name = nil; + mode9 = mode & 3; + perm9 = (perm & ~current->umask) & 0777; + + s = shortpath(current->kcwd, path); + + if(mode & O_CREAT) { + Dir *d; + + err = -EINVAL; + if((base = basepath(path, &name)) == nil) + goto out; + + /* resolve base directory */ + if((d = dirstat(shortpath(current->kcwd, base))) == nil){ + err = mkerror(); + if(t = resolvepath1(base, 0)){ + free(base); base = t; + t = allocpath(t, nil, name); + err = fsopen(t, mode, perm, pf); + free(t); + } + goto out; + } + err = -ENOTDIR; + if((d->mode & DMDIR) == 0){ + free(d); + goto out; + } + free(d); + + /* check if here is a symlink in the way */ + t = udirpath(base, name, 'L'); + if((fd = open(shortpath(current->kcwd, t), OREAD)) >= 0){ + free(t); + close(fd); + + if(mode & O_EXCL){ + err = -EEXIST; + goto out; + } + + if((t = resolvepath1(path, 0)) == nil) + goto out; + err = fsopen(t, mode, perm, pf); + free(t); + goto out; + } + free(t); + + if(mode & (O_EXCL | O_TRUNC)){ + if(mode & O_EXCL) + mode9 |= OEXCL; + fd = create(s, mode9, perm9); + } else { + /* try open first to avoid truncating existing the file */ + if((fd = open(s, mode9)) < 0) + fd = create(s, mode9, perm9); + } + if(fd < 0){ + err = mkerror(); + goto out; + } + } else { + if(((mode & 3) == O_RDWR) || ((mode & 3) == O_WRONLY)) + if(mode & O_TRUNC) + mode9 |= OTRUNC; + + if((fd = open(s, mode9)) < 0){ + err = mkerror(); + if(t = resolvepath1(path, 0)){ + err = fsopen(t, mode, perm, pf); + free(t); + } + goto out; + } + } + + qlock(&rpathtablk); + prp = rpathent(path); + if(*prp != nil){ + incref(*prp); + } else { + Rpath *rp; + + rp = kmalloc(sizeof(*rp) + strlen(path) + 1); + rp->ref = 1; + rp->hash = nil; + rp->deleted = 0; + strcpy(rp->str, path); + *prp = rp; + } + qunlock(&rpathtablk); + + f = kmallocz(sizeof(Rfile), 1); + f->ref = 1; + f->path = kstrdup(path); + f->dev = ROOTDEV; + f->mode = mode; + f->fd = fd; + f->off = 0; + *pf = f; + + err = 0; + +out: + free(base); + free(name); + + return err; +} + +static int +rclose(Ufile *f) +{ + Rpath **prp; + Rfile *file = (Rfile*)f; + static char path[1024]; /* protected by rpathtablk */ + + qlock(&rpathtablk); + prp = rpathent(file->path); + if(!decref(*prp)){ + Rpath *rp = *prp; + *prp = rp->hash; + if(rp->deleted){ + if(fd2path(file->fd, path, sizeof(path)) == 0) + remove(shortpath(current->kcwd, path)); + } + free(rp); + } + qunlock(&rpathtablk); + + close(file->fd); + return 0; +} + +static int +rread(Ufile *f, void *buf, int len, vlong off) +{ + Rfile *file = (Rfile*)f; + int ret, n; + + n = ret = 0; + if(notifyme(1)) + return -ERESTART; + while((n < len) && ((ret = pread(file->fd, (uchar*)buf + n, len - n, off + n)) > 0)) + n += ret; + notifyme(0); + if(ret < 0) + return mkerror(); + return n; +} + +static int +rwrite(Ufile *f, void *buf, int len, vlong off) +{ + Rfile *file = (Rfile*)f; + int ret; + + if(notifyme(1)) + return -ERESTART; + ret = pwrite(file->fd, buf, len, off); + notifyme(0); + if(ret < 0) + ret = mkerror(); + return ret; +} + +static vlong +rsize(Ufile *f) +{ + Rfile *file = (Rfile*)f; + + return seek(file->fd, 0, 2); +} + +static int +raccess(char *path, int mode) +{ + static char omode[] = { + 0, // --- + OEXEC, // --x + OWRITE, // -w- + ORDWR, // -wx + OREAD, // r-- + OEXEC, // r-x + ORDWR, // rw- + ORDWR // rwx + }; + + int err; + int fd; + Dir *d; + char *s; + + err = -EINVAL; + if(mode & ~07) + return err; + + s = shortpath(current->kcwd, path); + if((d = dirstat(s)) == nil){ + err = mkerror(); + if(path = resolvepath1(path, 0)){ + err = fsaccess(path, mode); + free(path); + } + goto out; + } + + /* ignore the exec bit... firefox gets confused */ + mode &= ~01; + if((mode == 0) || (d->mode & DMDIR)){ + err = 0; + } else { + err = -EACCES; + if((mode & 01) && ((d->mode & 0111) == 0)) + goto out; + if((mode & 02) && ((d->mode & 0222) == 0)) + goto out; + if((mode & 04) && ((d->mode & 0444) == 0)) + goto out; + if((fd = open(s, omode[mode])) >= 0){ + close(fd); + err = 0; + } + } +out: + free(d); + return err; +} + +static ulong +dir2statmode(Dir *d) +{ + ulong mode; + + mode = d->mode & 0777; + if(d->mode & DMDIR) + mode |= S_IFDIR; + else if(strcmp(d->name, "cons") == 0) + mode |= S_IFCHR; + else if(strncmp(d->name, "PTS.", 4) == 0) + mode |= S_IFCHR; + else if(strcmp(d->name, "zero") == 0) + mode |= S_IFCHR | 0222; + else if(strcmp(d->name, "null") == 0) + mode |= S_IFCHR | 0222; + else if(strncmp(d->name, ".udir.", 6) == 0){ + switch(d->name[6]){ + case 'L': + mode |= S_IFLNK; + break; + case 'S': + mode |= S_IFSOCK; + break; + case 'F': + mode |= S_IFIFO; + break; + case 'C': + mode |= S_IFCHR; + break; + case 'B': + mode |= S_IFBLK; + break; + } + } else if(d->type == '|') + mode |= S_IFIFO; + else if(d->type == 'H') + mode |= S_IFBLK; + else + mode |= S_IFREG; + + return mode; +} + +static void +dir2ustat(Dir *d, Ustat *s) +{ + s->mode = dir2statmode(d); + s->uid = current->uid; + s->gid = current->gid; + s->size = d->length; + s->atime = d->atime; + s->mtime = d->mtime; + s->ctime = d->mtime; + s->ino = 0; // use d->qid? + s->dev = 0; + s->rdev = 0; +} + +static int +rstat(char *path, int link, Ustat *s) +{ + Dir *d; + int err; + char *t; + + if((d = dirstat(shortpath(current->kcwd, path))) == nil){ + if(link){ + char *base; + char *name; + if(base = basepath(path, &name)){ + t = udirpath(base, name, 'L'); + free(name); + free(base); + d = dirstat(shortpath(current->kcwd, t)); + free(t); + } + + } + } + if(d == nil){ + err = mkerror(); + if(t = resolvepath1(path, 0)){ + err = fsstat(t, link, s); + free(t); + } + return err; + } + + dir2ustat(d, s); + s->ino = hashpath(path); + + free(d); + return 0; +} + +static int +rfstat(Ufile *f, Ustat *s) +{ + Dir *d; + + if((d = dirfstat(f->fd)) == nil) + return mkerror(); + + dir2ustat(d, s); + s->ino = hashpath(f->path); + + free(d); + return 0; +} + +static char* +fixname(char *name) +{ + if(name == nil) + return nil; + if(strncmp(name, ".udir.", 6) == 0){ + if(name[6] && name[7]=='.') + name += 8; + } + return name; +} + +static int +rreaddir(Ufile *f, Udirent **pd) +{ + Dir *d; + int i, n; + + seek(f->fd, 0, 0); + n = dirreadall(f->fd, &d); + if(n < 0) + return mkerror(); + for(i=0; ipath, fixname(d[i].name), dir2statmode(&d[i]))) == nil) + break; + pd = &((*pd)->next); + } + free(d); + return i; +} + +static int +rreadlink(char *path, char *buf, int len) +{ + int err; + int fd; + + char *t; + char *name; + char *base; + + trace("rreadlink(%s)", path); + + if((base = basepath(path, &name)) == nil) + return -EINVAL; + + /* resolve base path */ + if((fd = open(shortpath(current->kcwd, base), OREAD)) < 0){ + err = mkerror(); + if(t = resolvepath1(base, 0)){ + free(base); base = t; + t = allocpath(base, nil, name); + err = fsreadlink(t, buf, len); + free(t); + } + goto out; + } + close(fd); + + /* check if path is regular file */ + if((fd = open(shortpath(current->kcwd, path), OREAD)) >= 0){ + close(fd); + err = -EINVAL; + goto out; + } + + t = udirpath(base, name, 'L'); + if((fd = open(shortpath(current->kcwd, t), OREAD)) < 0){ + err = mkerror(); + free(t); + goto out; + } + free(t); + if((err = read(fd, buf, len)) < 0) + err = mkerror(); + close(fd); +out: + free(base); + free(name); + return err; +} + +enum { + COPYSIZE = 8*1024, +}; + +static int +copyfile(char *from, char *to) +{ + int err, fromfd, tofd; + char *buf, *s; + Dir *ent; + Dir *dir; + + dir = nil; + buf = nil; + ent = nil; + + tofd = -1; + + trace("copyfile(%s, %s)", from, to); + + if((fromfd = open(shortpath(current->kcwd, from), OREAD)) < 0){ + err = mkerror(); + goto out; + } + if((dir = dirfstat(fromfd)) == nil){ + err = mkerror(); + goto out; + } + s = shortpath(current->kcwd, to); + if((err = open(s, OREAD)) >= 0){ + close(err); + err = -EEXIST; + goto out; + } + if(dir->mode & DMDIR){ + int n; + if((tofd = create(s, OREAD, dir->mode)) < 0){ + err = mkerror(); + goto out; + } + close(tofd); + tofd = -1; + while((n = dirread(fromfd, &ent)) > 0){ + int i; + + for(i=0; imode)) < 0){ + err = mkerror(); + goto out; + } + buf = kmalloc(COPYSIZE); + for(;;){ + err = read(fromfd, buf, COPYSIZE); + if(err == 0) + break; + if(err < 0){ + err = mkerror(); + goto out; + } + if(write(tofd, buf, err) != err){ + err = mkerror(); + goto out; + } + } + } + + err = 0; +out: + free(ent); + free(dir); + free(buf); + close(fromfd); + close(tofd); + return err; +} + +static int +removefile(char *path) +{ + int err; + int n; + Dir *d; + int fd; + char *s; + + trace("removefile(%s)", path); + + s = shortpath(current->kcwd, path); + + if((d = dirstat(s)) == nil) + return mkerror(); + if(remove(s) == 0){ + free(d); + return 0; + } + if((d->mode & DMDIR) == 0){ + free(d); + return mkerror(); + } + free(d); + if((fd = open(s, OREAD)) < 0) + return mkerror(); + err = 0; + d = nil; + while((n = dirread(fd, &d)) > 0){ + char *t; + int i; + + for(i=0; ikcwd, from), &d) < 0) + err = mkerror(); + goto out; + } + } + t = ksmprint("%s%d%d.tmp", to, current->pid, current->tid); + if((err = copyfile(from, t)) == 0){ + Dir d; + + nulldir(&d); + d.name = &y[1]; + + remove(shortpath(current->kcwd, to)); + if(dirwstat(shortpath(current->kcwd, t), &d) < 0) { + err = mkerror(); + } else { + removefile(from); + } + } + if(err != 0) + removefile(t); + free(t); +out: + free(from); + free(to); + + return err; +} + +static int +rmkdir(char *path, int mode) +{ + int err; + Dir *d; + int fd; + int mode9; + + char *base; + char *name; + char *t; + + trace("rmkdir(%s, %#o)", path, mode); + + if((base = basepath(path, &name)) == nil) + return -EINVAL; + + if((d = dirstat(shortpath(current->kcwd, base))) == nil){ + err = mkerror(); + if(t = resolvepath1(base, 0)){ + free(base); base = t; + t = allocpath(base, nil, name); + err = fsmkdir(t, mode); + free(t); + } + goto out; + } + err = -ENOTDIR; + if((d->mode & DMDIR) == 0){ + free(d); + goto out; + } + free(d); + + err = -EEXIST; + t = udirpath(base, name, 'L'); + if(d = dirstat(shortpath(current->kcwd, t))){ + free(d); + free(t); + goto out; + } + free(t); + + mode9 = DMDIR | ((mode & ~current->umask) & 0777); + if((fd = create(shortpath(current->kcwd, path), OREAD|OEXCL, mode9)) < 0){ + err = mkerror(); + goto out; + } + close(fd); + err = 0; + +out: + free(name); + free(base); + return err; +} + +static void +combinedir(Dir *ndir, Dir *odir) +{ + if(ndir->mode != ~0) + ndir->mode = (odir->mode & ~0777) | (ndir->mode & 0777); +} + +static int +uwstat(char *path, Dir *ndir, int link) +{ + int err; + Dir *dir; + char *s; + + trace("uwstat(%s, ..., %d)", path, link); + + s = shortpath(current->kcwd, path); + if((dir = dirstat(s)) == nil){ + err = mkerror(); + if(link){ + char *base; + char *name; + + if(base = basepath(path, &name)){ + char *t; + + t = udirpath(base, name, 'L'); + free(base); + free(name); + + err = uwstat(t, ndir, 0); + free(t); + } + } + return err; + } + combinedir(ndir, dir); + err = 0; + if(dirwstat(s, ndir) < 0) + err = mkerror(); + free(dir); + return err; +} + +static int +uwfstat(Ufile *f, Dir *ndir) +{ + int err; + Dir *dir; + + if((dir = dirfstat(f->fd)) == nil){ + err = mkerror(); + goto out; + } + combinedir(ndir, dir); + err = 0; + if(dirfwstat(f->fd, ndir) < 0) + err = mkerror(); +out: + free(dir); + return err; +} + +static int +rutime(char *path, long atime, long mtime) +{ + Dir ndir; + int err; + + trace("rutime(%s, %ld, %ld)", path, atime, mtime); + + nulldir(&ndir); + ndir.atime = atime; + ndir.mtime = mtime; + + if((err = uwstat(path, &ndir, 1)) < 0){ + char *t; + + if(t = resolvepath1(path, 0)){ + err = fsutime(t, atime, mtime); + free(t); + } + } + return err; +} + +static int +rchmod(char *path, int mode) +{ + Dir ndir; + int err; + + trace("rchmod(%s, %#o)", path, mode); + + nulldir(&ndir); + ndir.mode = mode; + + if((err = uwstat(path, &ndir, 1)) < 0){ + char *t; + + if(t = resolvepath1(path, 0)){ + err = fschmod(t, mode); + free(t); + } + } + return err; +} + +static int +rchown(char *path, int uid, int gid, int link) +{ + Ustat s; + + USED(uid); + USED(gid); + + /* FIXME, just return the right errorcode for now */ + return fsstat(path, link, &s); +} + +static int +rtruncate(char *path, vlong size) +{ + Dir ndir; + int err; + + trace("rtruncate(%s, %lld)", path, size); + + nulldir(&ndir); + ndir.length = size; + + if((err = uwstat(path, &ndir, 0)) < 0){ + char *t; + + if(t = resolvepath1(path, 0)){ + err = fstruncate(t, size); + free(t); + } + } + return err; +} + +static int +rfchmod(Ufile *f, int mode) +{ + Dir ndir; + + nulldir(&ndir); + ndir.mode = mode; + return uwfstat(f, &ndir); +} + +static int +rfchown(Ufile *f, int uid, int gid) +{ + USED(f); + USED(uid); + USED(gid); + + return 0; +} + +static int +rftruncate(Ufile *f, vlong size) +{ + Dir ndir; + + nulldir(&ndir); + ndir.length = size; + return uwfstat(f, &ndir); +} + +static int +runlink(char *path, int rmdir) +{ + int err; + Dir *dir; + char *t, *s; + char *base; + char *name; + char *rpath; + Rpath **prp; + + trace("runlink(%s, %d)", path, rmdir); + + rpath = nil; + dir = nil; + err = -EINVAL; + if((base = basepath(path, &name)) == nil) + goto out; + if(dir = dirstat(shortpath(current->kcwd, path))){ + rpath = kstrdup(path); + } else { + rpath = udirpath(base, name, 'L'); + dir = dirstat(shortpath(current->kcwd, rpath)); + } + if(dir == nil){ + err = mkerror(); + if(t = resolvepath1(path, 0)){ + err = fsunlink(t, rmdir); + free(t); + } + goto out; + } + if(rmdir){ + if((dir->mode & DMDIR) == 0){ + err = -ENOTDIR; + goto out; + } + } else { + if(dir->mode & DMDIR){ + err = -EISDIR; + goto out; + } + } + + s = shortpath(current->kcwd, rpath); + + qlock(&rpathtablk); + prp = rpathent(path); + if(*prp){ + Dir ndir; + + t = ksmprint(".%s.%d.deleted", name, current->kpid); + nulldir(&ndir); + ndir.name = t; + trace("runlink: file %s still in use renaming to -> %s", path, t); + if(dirwstat(s, &ndir) < 0){ + qunlock(&rpathtablk); + err = mkerror(); + free(t); + goto out; + } + free(t); + (*prp)->deleted = 1; + qunlock(&rpathtablk); + + } else { + int x; + qunlock(&rpathtablk); + + x = 0; + while(remove(s) < 0){ + err = mkerror(); + if(++x > 8){ + /* old debian bug clashes with mntgen */ + if(strcmp(base, "/")==0 && strstr(path, ".dpkg-")) + err = -ENOENT; + goto out; + } + } + } + err = 0; +out: + free(dir); + free(name); + free(base); + free(rpath); + + return err; +} + +static int +rlink(char *old, char *new, int sym) +{ + int err; + int fd; + char *base; + char *name; + char *t; + + trace("rlink(%s, %s, %d)", old, new, sym); + + if((base = basepath(new, &name)) == nil) + return -EINVAL; + + /* resolve base directory */ + if((fd = open(shortpath(current->kcwd, base), OREAD)) < 0){ + err = mkerror(); + if(t = resolvepath1(base, 0)){ + free(base); base = t; + t = allocpath(base, nil, name); + err = fslink(old, t, sym); + free(t); + } + goto out; + } + close(fd); + + if(sym == 0){ + if((err = resolvefromtopath(&old, &new)) == 0) + err = copyfile(old, new); + free(old); + free(new); + goto out; + } + + /* check if regular file is in the way */ + err = -EEXIST; + if((fd = open(shortpath(current->kcwd, new), OREAD)) >= 0){ + close(fd); + goto out; + } + + /* try to create the link, will fail if alreadt exists */ + t = udirpath(base, name, 'L'); + if((fd = create(shortpath(current->kcwd, t), OWRITE|OEXCL, 0777)) < 0){ + err = mkerror(); + free(t); + goto out; + } + free(t); + + if(write(fd, old, strlen(old)) < 0){ + err = mkerror(); + close(fd); + goto out; + } + close(fd); + err = 0; +out: + free(base); + free(name); + return err; +} + +static Udev rootdev = +{ + .open = ropen, + .access = raccess, + .stat = rstat, + .link = rlink, + .unlink = runlink, + .rename = rrename, + .mkdir = rmkdir, + .utime = rutime, + .chmod = rchmod, + .chown = rchown, + .truncate = rtruncate, + + .read = rread, + .write = rwrite, + .size = rsize, + .close = rclose, + + .fstat = rfstat, + .readdir = rreaddir, + .readlink = rreadlink, + + .fchmod = rfchmod, + .fchown = rfchown, + .ftruncate = rftruncate, +}; + +void rootdevinit(void) +{ + devtab[ROOTDEV] = &rootdev; + + fsmount(&rootdev, ""); +} diff --git a/linux_emul_base/signal.c b/linux_emul_base/signal.c new file mode 100644 index 0000000..50687a1 --- /dev/null +++ b/linux_emul_base/signal.c @@ -0,0 +1,1387 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Signal Signal; +typedef struct Action Action; +typedef struct Queue Queue; +typedef struct Timers Timers; +typedef struct Handlers Handlers; +typedef struct Private Private; + +struct Signal +{ + Usiginfo; + Signal *next; +}; + +struct Action +{ + void *handler; + int flags; + uvlong block; +}; + +struct Queue +{ + Ref; + QLock; + + Signal *head; + Signal **tailp; + Signal *free; + Signal a[64]; + + Ufile *tty; +}; + +struct Timers +{ + Ref; + struct { + vlong interval; + vlong expire; + } itimer[2]; +}; + +struct Handlers +{ + Ref; + QLock; + Action a[SIGMAX-1]; +}; + +struct Private +{ + Handlers *h; + Queue *q; + Timers *t; + + struct { + ulong sp; + ulong size; + } altstack; + + uvlong block; + + Urestart *freerestart; +}; + +enum +{ + SIG_ERR = -1, + SIG_DFL = 0, + SIG_IGN = 1, + SIG_HOLD = 2, +}; + +enum +{ + SA_NOCLDSTOP = 1, + SA_NOCLDWAIT = 2, + SA_SIGINFO = 4, + SA_ONSTACK = 0x08000000, + SA_RESTART = 0x10000000, + SA_NODEFER = 0x40000000, + SA_RESETHAND = 0x80000000, +}; + +enum +{ + SS_ONSTACK = 1, + SS_DISABLE = 2, +}; + +#define MASK(sig) (1LL << ((sig)-1)) + +static void +nextsignal(uvlong rblock, int wait); + +static int +getsignal(Private *p, Usiginfo *pinfo, int wait); + +static void +initrestart(Uproc *proc) +{ + Urestart *r; + + r = &proc->restart0; + r->syscall = nil; + r->link = nil; + proc->restart = r; +} + +static void +poprestart(Private *p) +{ + Urestart *r; + + for(;;){ + r = current->restart; + if(r->link==nil || r->syscall) + break; + current->restart = r->link; + + r->link = p->freerestart; + p->freerestart = r; + } + if(r->syscall) + current->syscall = r->syscall; +} + +static Queue* +mkqueue(void) +{ + Queue *q; + int i; + + q = kmallocz(sizeof(Queue), 1); + q->ref = 1; + q->head = nil; + q->tailp = &q->head; + for(i=0; ia); i++) + q->a[i].next = (i+1 == nelem(q->a)) ? nil : &q->a[i+1]; + q->free = q->a; + + return q; +} + +static Handlers* +mkhandlers(void) +{ + Handlers *h; + int i; + + h = kmallocz(sizeof(Handlers), 1); + h->ref = 1; + for(i=1; ia[i-1].handler = (void*)SIG_DFL; + return h; +} + +static Timers* +mktimers(void) +{ + Timers *t; + + t = kmallocz(sizeof(Timers), 1); + t->ref = 1; + return t; +} + +/* bits.s */ +extern int get_ds(void); +extern int get_cs(void); +static ulong user_cs, user_ds; + +void initsignal(void) +{ + Private *p; + + if(user_ds==0 && user_cs==0){ + user_ds = get_ds(); + user_cs = get_cs(); + } + + p = kmallocz(sizeof(*p), 1); + p->block = 0; + + p->q = mkqueue(); + p->h = mkhandlers(); + p->t = mktimers(); + + current->signal = p; + initrestart(current); +} + +void exitsignal(void) +{ + Private *p; + Queue *q; + Timers *t; + Signal **i; + Handlers *h; + Urestart *r; + + if((p = current->signal) == nil) + return; + current->signal = nil; + q = p->q; + qlock(q); +again: + for(i=&q->head; *i; i=&((*i)->next)){ + Signal *r; + r = *i; + if(!r->group && (r->topid == current->tid)){ + if((*i = r->next) == nil) + q->tailp = i; + r->next = q->free; + q->free = r; + goto again; + } + } + qunlock(q); + if(!decref(q)){ + putfile(q->tty); + q->tty = nil; + free(q); + } + h = p->h; + if(!decref(h)) + free(h); + t = p->t; + if(!decref(t)) + free(t); + while(r = current->restart){ + if(r->link == nil) + break; + current->restart = r->link; + r->link = p->freerestart; + p->freerestart = r; + } + current->restart = nil; + while(r = p->freerestart){ + p->freerestart = r->link; + free(r); + } + free(p); +} + +void clonesignal(Uproc *new, int copyhand, int newproc) +{ + Private *p, *n; + + if((p = current->signal) == nil) + return; + + n = kmallocz(sizeof(*n), 1); + if(copyhand){ + n->h = mkhandlers(); + + qlock(p->h); + memmove(n->h->a, p->h->a, sizeof(n->h->a)); + qunlock(p->h); + } else { + incref(p->h); + n->h = p->h; + } + + qlock(p->q); + if(newproc){ + n->q = mkqueue(); + n->q->tty = getfile(p->q->tty); + n->t = mktimers(); + n->altstack = p->altstack; + } else { + incref(p->q); + n->q = p->q; + incref(p->t); + n->t = p->t; + } + qunlock(p->q); + + n->block = p->block; + new->signal = n; + + initrestart(new); +} + +void +settty(Ufile *tty) +{ + Private *p; + Ufile *old; + + if((p = current->signal) == nil) + return; + tty = getfile(tty); + qlock(p->q); + old = p->q->tty; + p->q->tty = tty; + qunlock(p->q); + putfile(old); +} + +Ufile* +gettty(void) +{ + Private *p; + Ufile *tty; + + if((p = current->signal) == nil) + return nil; + qlock(p->q); + tty = getfile(p->q->tty); + qunlock(p->q); + return tty; +} + +int ignoressignal(Uproc *proc, int sig) +{ + Private *p; + int a, f; + + if((p = proc->signal) == nil) + return 1; + qlock(p->h); + a = (int)p->h->a[sig-1].handler; + f = p->h->a[sig-1].flags; + qunlock(p->h); + switch(sig){ + case SIGKILL: + case SIGSTOP: + return 0; + case SIGCHLD: + if(f & SA_NOCLDWAIT) + return 1; + break; + case SIGWINCH: + case SIGURG: + if(a == SIG_DFL) + return 1; + } + return (a == SIG_IGN); +} + +int wantssignal(Uproc *proc, int sig) +{ + Private *p; + + p = proc->signal; + if(p == nil || p->block & MASK(sig)) + return 0; + return !ignoressignal(proc, sig); +} + +int sendsignal(Uproc *proc, Usiginfo *info, int group) +{ + Private *p; + Signal *s; + + trace("sendsignal(%S) to %d from %d", + info->signo, proc->tid, (current != nil) ? current->tid : 0); + + if(ignoressignal(proc, info->signo)){ + trace("sendsignal(): ignored signal %S", info->signo); + return 0; + } + + p = proc->signal; + qlock(p->q); + if(info->signo < SIGRT1){ + for(s=p->q->head; s; s=s->next){ + if(!s->group && (s->topid != proc->tid)) + continue; + if(s->signo == info->signo){ + qunlock(p->q); + trace("sendsignal(): droping follow up signal %S", info->signo); + return 0; + } + } + } + if((s = p->q->free) == nil){ + qunlock(p->q); + trace("sendsignal(): out of signal buffers"); + return -EAGAIN; + } + p->q->free = s->next; + s->next = nil; + memmove(s, info, sizeof(*info)); + s->group = group; + s->topid = group ? proc->pid : proc->tid; + *p->q->tailp = s; + p->q->tailp = &s->next; + qunlock(p->q); + return 1; +} + +int +signalspending(Uproc *proc) +{ + Private *p; + Signal *s; + int ret; + + p = proc->signal; + if(p == nil || p->q->head == nil) + return 0; + + ret = 0; + qlock(p->q); + for(s=p->q->head; s; s=s->next){ + if(!s->group && (s->topid != current->tid)) + continue; + if(MASK(s->signo) & p->block) + continue; + ret = 1; + break; + } + qunlock(p->q); + + return ret; +} + +static int +getsignal(Private *p, Usiginfo *pinfo, int wait) +{ + Signal *r; + Signal **i; + int sig; + + if(!wait && p->q->head == nil) + return 0; + + sig = 0; + qlock(p->q); + for(;;){ + for(i=&p->q->head; *i; i=&((*i)->next)){ + r = *i; + + if(!r->group && (r->topid != current->tid)) + continue; + + if(p->block & MASK(r->signo)){ + if(sig == 0) + sig = -r->signo; + continue; + } + sig = r->signo; + + /* dequeue nonblocked signal */ + memmove(pinfo, r, sizeof(*pinfo)); + if((*i = r->next) == nil) + p->q->tailp = i; + r->next = p->q->free; + p->q->free = r; + break; + } + if(wait && sig <= 0){ + if(sleepproc(p->q, 0) == 0) + continue; + } + break; + } + qunlock(p->q); + + return sig; +} + +static uvlong +sigset2uvlong(uchar *set, int setsize) +{ + uvlong r; + int i; + + r = 0; + if(setsize > sizeof(uvlong)) + setsize = sizeof(uvlong); + for(i=0; i> (i*8)) & 0xff); + } else { + set[i] = 0; + } + } +} + +struct linux_siginfo { + int signo; + int errno; + int code; + + union { + int _pad[29]; + + /* kill() */ + struct { + int pid; /* sender's pid */ + int uid; /* sender's uid */ + } kill; + + /* POSIX.1b timers */ + struct { + int tid; /* timer id */ + int overrun; /* overrun count */ + int val; /* same as below */ + } timer; + + /* POSIX.1b signals */ + struct { + int pid; /* sender's pid */ + int uid; /* sender's uid */ + int val; + } rt; + + /* SIGCHLD */ + struct { + int pid; /* which child */ + int uid; /* sender's uid */ + int status; /* exit code */ + long utime; + long stime; + } chld; + + /* SIGILL, SIGFPE, SIGSEGV, SIGBUS */ + struct { + void *addr; /* faulting insn/memory ref. */ + int trapno; /* TRAP # which caused the signal */ + } fault; + + /* SIGPOLL */ + struct { + long band; /* POLL_IN, POLL_OUT, POLL_MSG */ + int fd; + } poll; + }; +}; + +void +siginfo2linux(Usiginfo *info, void *p) +{ + struct linux_siginfo *li = p; + int sig; + + sig = info->signo; + + li->signo = sig; + li->errno = info->errno; + li->code = info->code; + + switch(sig){ + case SIGALRM: + li->timer.tid = info->timer.tid; + li->timer.overrun = info->timer.overrun; + li->timer.val = info->timer.val; + break; + case SIGCHLD: + li->chld.pid = info->chld.pid; + li->chld.uid = info->chld.uid; + li->chld.status = info->chld.status; + li->chld.utime = info->chld.utime; + li->chld.stime = info->chld.stime; + break; + case SIGILL: + case SIGBUS: + case SIGFPE: + case SIGSEGV: + li->fault.addr = info->fault.addr; + li->fault.trapno = info->fault.trapno; + break; + case SIGPOLL: + li->poll.fd = info->poll.fd; + li->poll.band = info->poll.band; + break; + case SIGRT1: + case SIGRT2: + case SIGRT3: + case SIGRT4: + case SIGRT5: + case SIGRT6: + case SIGRT7: + case SIGRT8: + li->rt.pid = info->rt.pid; + li->rt.uid = info->rt.uid; + li->rt.val = info->rt.val; + break; + default: + li->kill.pid = info->kill.pid; + li->kill.uid = info->kill.uid; + } +} + +void +linux2siginfo(void *p, Usiginfo *info) +{ + struct linux_siginfo *li = p; + int sig; + + sig = li->signo; + + info->signo = sig; + info->errno = li->errno; + info->code = li->code; + + switch(sig){ + case SIGALRM: + info->timer.tid = li->timer.tid; + info->timer.overrun = li->timer.overrun; + info->timer.val = li->timer.val; + break; + case SIGCHLD: + info->chld.pid = li->chld.pid; + info->chld.uid = li->chld.uid; + info->chld.status = li->chld.status; + info->chld.utime = li->chld.utime; + info->chld.stime = li->chld.stime; + break; + case SIGILL: + case SIGBUS: + case SIGFPE: + case SIGSEGV: + info->fault.addr = li->fault.addr; + info->fault.trapno = li->fault.trapno; + break; + case SIGPOLL: + info->poll.fd = li->poll.fd; + info->poll.band = li->poll.band; + break; + case SIGRT1: + case SIGRT2: + case SIGRT3: + case SIGRT4: + case SIGRT5: + case SIGRT6: + case SIGRT7: + case SIGRT8: + info->rt.pid = li->rt.pid; + info->rt.uid = li->rt.uid; + info->rt.val = li->rt.val; + break; + default: + info->kill.pid = li->kill.pid; + info->kill.uid = li->kill.uid; + } +} + +struct linux_sigcontext { + ulong gs; + ulong fs; + ulong es; + ulong ds; + ulong di; + ulong si; + ulong bp; + ulong sp; + ulong bx; + ulong dx; + ulong cx; + ulong ax; + ulong trapno; + ulong err; + ulong ip; + ulong cs; + ulong flags; + ulong sp_at_signal; + ulong ss; + void* fpstate; + ulong oldmask; + ulong cr2; +}; + +static void +ureg2linuxsigcontext(Ureg *u, struct linux_sigcontext *sc) +{ + sc->gs = u->gs; + sc->fs = u->fs; + sc->es = u->es; + sc->ds = u->ds; + sc->di = u->di; + sc->si = u->si; + sc->bp = u->bp; + sc->sp = u->sp; + sc->bx = u->bx; + sc->dx = u->dx; + sc->cx = u->cx; + sc->ax = u->ax; + sc->trapno = u->trap; + sc->err = u->ecode; + sc->ip = u->pc; + sc->cs = u->cs; + sc->flags = u->flags; + sc->sp_at_signal = u->sp; + sc->ss = u->ss; + sc->cr2 = 0; +} + +struct linux_sigset { + ulong sig[2]; +}; + +struct linux_signalstack { + ulong sp; + int flags; + ulong size; +}; + +struct linux_ucontext { + ulong flags; + struct linux_ucontext *link; + struct linux_signalstack stack; + struct linux_sigcontext context; + struct linux_sigset sigmask; +}; + +static void +linuxsigcontext2ureg(struct linux_sigcontext *sc, Ureg *u) +{ + u->pc = sc->ip; + u->sp = sc->sp; + u->ax = sc->ax; + u->bx = sc->bx; + u->cx = sc->cx; + u->dx = sc->dx; + u->di = sc->di; + u->si = sc->si; + u->bp = sc->bp; + + u->cs = sc->cs; + u->ss = sc->ss; + u->ds = sc->ds; + u->es = sc->es; + u->fs = sc->fs; + u->gs = sc->gs; +} + +struct linux_sigframe { + void *ret; + int sig; + + union { + struct linux_sigcontext sc; + + struct { + struct linux_siginfo *pinfo; + struct linux_ucontext *puc; + + struct linux_siginfo info; + struct linux_ucontext uc; + } rt; + }; +}; + +#pragma profile off + +static int +linuxstackflags(Private *p, ulong sp) +{ + if(p->altstack.size == 0 || p->altstack.sp == 0) + return SS_DISABLE; + if(sp - p->altstack.sp < p->altstack.size) + return SS_ONSTACK; + return 0; +} + +static void +linuxsignal(Private *p, Action *a, Usiginfo *i, uvlong rblock) +{ + struct linux_sigframe _frame; + struct linux_sigframe *f; + Ureg *u; + int stackflags; + + u = current->ureg; + + stackflags = linuxstackflags(p, u->sp); + if((a->flags & SA_ONSTACK) && (stackflags == 0)){ + trace("linuxsignal: altstack %lux %lux", p->altstack.sp, p->altstack.size); + f = (struct linux_sigframe*)(p->altstack.sp + p->altstack.size); + f--; + } else { + f = &_frame; + } + + trace("linuxsignal(): frame %p", f); + memset(f, 0, sizeof(*f)); + + f->sig = i->signo; + + if(a->flags & SA_SIGINFO){ + f->ret = linux_rtsigreturn; + siginfo2linux(i, &f->rt.info); + f->rt.pinfo = &f->rt.info; + + f->rt.uc.stack.sp = p->altstack.sp; + f->rt.uc.stack.size = p->altstack.size; + f->rt.uc.stack.flags = stackflags; + + ureg2linuxsigcontext(u, &f->rt.uc.context); + f->rt.uc.context.oldmask = rblock & 0xFFFFFFFF; + f->rt.uc.sigmask.sig[0] = rblock & 0xFFFFFFFF; + f->rt.uc.sigmask.sig[1] = (rblock >> 32) & 0xFFFFFFFF; + f->rt.puc = &f->rt.uc; + u->cx = (ulong)f->rt.puc; + u->dx = (ulong)f->rt.pinfo; + } else { + f->ret = linux_sigreturn; + ureg2linuxsigcontext(u, &f->sc); + f->sc.oldmask = rblock & 0xFFFFFFFF; + u->cx = 0; + u->dx = 0; + } + + u->di = 0; + u->si = 0; + u->bp = 0; + u->bx = 0; + + u->ax = (ulong)i->signo; + + u->sp = (ulong)f; + u->pc = (ulong)a->handler; + + u->cs = user_cs; + u->ss = user_ds; + u->ds = user_ds; + u->es = user_ds; + + p->block |= a->block; + + trace("linuxsignal(): retuser pc=%lux sp=%lux", u->pc, u->sp); + retuser(); +} + +int +sys_sigreturn(void) +{ + struct linux_sigframe *f; + Private *p; + Ureg *u; + + trace("sys_sigreturn()"); + + p = current->signal; + u = current->ureg; + + f = (struct linux_sigframe*)(u->sp - 4); + + trace("sys_sigreturn(): frame %p", f); + + linuxsigcontext2ureg(&f->sc, u); + p->block &= ~0xFFFFFFFF; + p->block |= f->sc.oldmask; + nextsignal(p->block, 0); + poprestart(p); + + trace("sys_sigreturn(): retuser pc=%lux sp=%lux", u->pc, u->sp); + retuser(); + + return -1; +} + +int +sys_rt_sigreturn(void) +{ + struct linux_sigframe *f; + Private *p; + Ureg *u; + + trace("sys_rt_sigreturn()"); + + p = current->signal; + u = current->ureg; + + f = (struct linux_sigframe*)(u->sp - 4); + trace("sys_rt_sigreturn(): frame %p", f); + + linuxsigcontext2ureg(&f->rt.uc.context, u); + p->block = (uvlong)f->rt.uc.sigmask.sig[0] | (uvlong)f->rt.uc.sigmask.sig[1]<<32; + nextsignal(p->block, 0); + poprestart(p); + + trace("sys_rt_sigreturn(): pc=%lux sp=%lux", u->pc, u->sp); + retuser(); + + return -1; +} + +/* + * nextsignal transfers execution to the next pending + * signal or just returns. after the signal got executed, + * the block mask is restored to rblock. if heres no + * pending signal and wait is non zero the current + * process is suspended until here is a signal available. + */ + +static void +nextsignal(uvlong rblock, int wait) +{ + Private *p; + int sig; + Usiginfo info; + Action a; + Urestart *r; + + for(;;){ + if((p = current->signal) == nil) + return; + + if(current->wstate & WSTOPPED){ + p->block = ~(MASK(SIGCONT) | MASK(SIGKILL)); + sig = getsignal(p, &info, 1); + p->block = rblock; + if(sig <= 0) + return; + if(sig == SIGCONT){ + contproc(current, sig, info.group); + continue; + } + } else { + if((sig = getsignal(p, &info, wait)) <= 0) + return; + if(sig == SIGCONT) + continue; + if(sig == SIGSTOP){ + stopproc(current, sig, info.group); + continue; + } + } + break; + } + + trace("nextsignal(): signal %S", sig); + + qlock(p->h); + a = p->h->a[sig-1]; + if(a.flags & SA_RESETHAND) + p->h->a[sig-1].handler = (void*)SIG_DFL; + if(a.flags & SA_NODEFER == 0) + a.block |= MASK(sig); + qunlock(p->h); + + switch((int)a.handler){ + case SIG_DFL: + switch(sig){ + case SIGCHLD: + case SIGWINCH: + case SIGURG: + goto Ignored; + } + /* no break */ + case SIG_ERR: + trace("nextsignal(): signal %S causes exit", sig); + exitproc(current, sig, 1); +Ignored: + case SIG_IGN: + case SIG_HOLD: + trace("nextsignal(): signal %S ignored", sig); + return; + } + + if(current->restart->syscall){ + if(a.flags & SA_RESTART){ + if(r = p->freerestart) + p->freerestart = r->link; + if(r == nil) + r = kmalloc(sizeof(*r)); + r->syscall = nil; + r->link = current->restart; + current->restart = r; + } else { + trace("nextsignal(): interrupting syscall %s", current->syscall); + current->sysret(-EINTR); + } + } + + linuxsignal(p, &a, &info, rblock); +} + +void handlesignals(void) +{ + Private *p; + + if(p = current->signal) + nextsignal(p->block, 0); +} + +int +sys_rt_sigsuspend(uchar *set, int setsize) +{ + Private *p; + uvlong b, rblock; + + trace("sys_rt_sigsuspend(%p, %d)", set, setsize); + + p = current->signal; + b = sigset2uvlong(set, setsize); + b &= ~(MASK(SIGKILL) | MASK(SIGSTOP)); + + rblock = p->block; + p->block = b; + + /* + * if a signal got handled, it will pop out after the the + * sigsuspend syscall with return value set to -EINTR + */ + current->sysret(-EINTR); + + for(;;) + nextsignal(rblock, 1); +} + +#pragma profile on + +struct linux_altstack +{ + ulong sp; + int flags; + ulong size; +}; + +int sys_sigaltstack(void *stk, void *ostk) +{ + Private *p; + struct linux_altstack *a = stk, *oa = ostk; + int flags; + ulong sp, size; + + trace("sys_sigaltstack(%lux, %lux)", (ulong)stk, (ulong)ostk); + + p = current->signal; + sp = p->altstack.sp; + size = p->altstack.size; + flags = linuxstackflags(p, current->ureg->sp); + + if(a){ + if(flags == SS_ONSTACK) + return -EPERM; + + if(a->flags == SS_DISABLE){ + p->altstack.sp = 0; + p->altstack.size = 0; + } else { + p->altstack.sp = a->sp; + p->altstack.size = a->size; + } + + trace("sys_signalstack(): new altstack %lux-%lux", + p->altstack.sp, p->altstack.sp + p->altstack.size); + } + if(oa){ + oa->sp = sp; + oa->size = size; + oa->flags = flags; + } + + return 0; +} + +struct linux_sigaction +{ + void *handler; + ulong flags; + void *restorer; + uchar mask[]; +}; + +int sys_rt_sigaction(int sig, void *pact, void *poact, int setsize) +{ + Private *p; + Action *a; + struct linux_sigaction *act; + struct linux_sigaction *oact; + void *handler; + int flags; + uvlong block; + + trace("sys_rt_sigaction(%S, %p, %p, %d)", sig, pact, poact, setsize); + + p = current->signal; + act = (struct linux_sigaction*)pact; + oact = (struct linux_sigaction*)poact; + + if((sig < 1) || (sig >= SIGMAX)) + return -EINVAL; + + qlock(p->h); + a = &p->h->a[sig-1]; + handler = a->handler; + flags = a->flags; + block = a->block; + if(act){ + trace("flags = %x", a->flags); + a->handler = act->handler; + a->flags = act->flags; + a->block = sigset2uvlong(act->mask, setsize); + } + if(oact){ + oact->handler = handler; + oact->flags = flags; + oact->restorer = 0; + uvlong2sigset(oact->mask, setsize, block); + } + qunlock(p->h); + + return 0; +} + +int sys_rt_sigpending(uchar *set, int setsize) +{ + Private *p; + Signal *s; + uvlong m; + + trace("sys_rt_sigpending(%p, %d)", set, setsize); + + p = current->signal; + m = 0LL; + qlock(p->q); + for(s=p->q->head; s; s=s->next){ + if(!s->group && (s->topid != current->tid)) + continue; + m |= MASK(s->signo); + } + qunlock(p->q); + + uvlong2sigset(set, setsize, m); + return 0; +} + +enum +{ + SIG_BLOCK = 0, + SIG_UNBLOCK = 1, + SIG_SETMASK = 2, +}; + +int sys_rt_sigprocmask(int how, uchar *act, uchar *oact, int setsize) +{ + Private *p; + uvlong m, block; + + trace("sys_rt_sigprocmask(%d, %p, %p, %d)", how, act, oact, setsize); + + p = current->signal; + block = p->block; + if(act){ + m = sigset2uvlong(act, setsize); + m &= ~(MASK(SIGKILL) | MASK(SIGSTOP)); + switch(how){ + default: + return -EINVAL; + case SIG_BLOCK: + p->block |= m; + break; + case SIG_UNBLOCK: + p->block &= ~m; + break; + case SIG_SETMASK: + p->block = m; + break; + } + } + if(oact) + uvlong2sigset(oact, setsize, block); + return 0; +} + +struct linux_itimer +{ + struct linux_timeval it_interval; + struct linux_timeval it_value; +}; + +static vlong +hzround(vlong t) +{ + vlong q = 1000000000LL/HZ; + return (t + q-1) / q; +} + +int sys_setitimer(int which, void *value, void *ovalue) +{ + Private *p; + Timers *t; + vlong now, rem, delta; + struct linux_itimer *nv = value, *ov = ovalue; + + trace("sys_setitimer(%d, %p, %p)", which, value, ovalue); + + p = current->signal; + t = p->t; + + if(which < 0 || which >= nelem(t->itimer)) + return -EINVAL; + + now = nsec(); + delta = t->itimer[which].interval; + rem = t->itimer[which].expire - now; + if(rem < 0) + rem = 0; + if(nv != nil){ + trace("nv->{interval->{%ld, %ld}, value->{%ld, %ld}}", + nv->it_interval.tv_sec, nv->it_interval.tv_usec, + nv->it_value.tv_sec, nv->it_value.tv_usec); + t->itimer[which].interval = hzround(nv->it_interval.tv_sec*1000000000LL + + nv->it_interval.tv_usec*1000); + t->itimer[which].expire = (now + nv->it_value.tv_sec*1000000000LL + + nv->it_value.tv_usec*1000); + setalarm(t->itimer[which].expire); + } + + if(ov != nil){ + ov->it_interval.tv_sec = delta / 1000000000LL; + ov->it_interval.tv_usec = (delta % 1000000000LL)/1000; + ov->it_value.tv_sec = rem / 1000000000LL; + ov->it_value.tv_usec = (rem % 1000000000LL)/1000; + trace("ov->{interval->{%ld, %ld}, value->{%ld, %ld}}", + ov->it_interval.tv_sec, ov->it_interval.tv_usec, + ov->it_value.tv_sec, ov->it_value.tv_usec); + } + + return 0; +} + +int sys_getitimer(int which, void *value) +{ + Private *p; + Timers *t; + vlong rem, delta; + struct linux_itimer *v = value; + + trace("sys_getitimer(%d, %p)", which, value); + + p = current->signal; + t = p->t; + + if(value == nil) + return -EINVAL; + if(which < 0 || which >= nelem(t->itimer)) + return -EINVAL; + + delta =t->itimer[which].interval; + rem = t->itimer[which].expire - nsec(); + + if(rem < 0) + rem = 0; + v->it_interval.tv_sec = delta / 1000000000LL; + v->it_interval.tv_usec = (delta % 1000000000LL)/1000; + v->it_value.tv_sec = rem / 1000000000LL; + v->it_value.tv_usec = (rem % 1000000000LL)/1000; + + return 0; +} + +int sys_alarm(long seconds) +{ + Private *p; + Timers *t; + vlong old, now; + + trace("sys_alarm(%ld)", seconds); + p = current->signal; + t = p->t; + now = nsec(); + old = t->itimer[0].expire - now; + if(old < 0) + old = 0; + t->itimer[0].interval = 0; + if(seconds > 0){ + t->itimer[0].expire = now + (vlong)seconds * 1000000000LL; + setalarm(t->itimer[0].expire); + } else { + t->itimer[0].expire = 0; + } + return old / 1000000000LL; +} + +int +Sfmt(Fmt *f) +{ + static char *t[] = { + [SIGHUP] = "SIGHUP", + [SIGINT] = "SIGINT", + [SIGQUIT] = "SIGQUIT", + [SIGILL] = "SIGILL", + [SIGTRAP] = "SIGTRAP", + [SIGABRT] = "SIGABRT", + [SIGBUS] = "SIGBUS", + [SIGFPE] = "SIGFPE", + [SIGKILL] = "SIGKILL", + [SIGUSR1] = "SIGUSR1", + [SIGSEGV] = "SIGSEGV", + [SIGUSR2] = "SIGUSR2", + [SIGPIPE] = "SIGPIPE", + [SIGALRM] = "SIGALRM", + [SIGTERM] = "SIGTERM", + [SIGSTKFLT] = "SIGSTKFLT", + [SIGCHLD] = "SIGCHLD", + [SIGCONT] = "SIGCONT", + [SIGSTOP] = "SIGSTOP", + [SIGTSTP] = "SIGTSTP", + [SIGTTIN] = "SIGTTIN", + [SIGTTOU] = "SIGTTOU", + [SIGURG] = "SIGURG", + [SIGXCPU] = "SIGXCPU", + [SIGXFSZ] = "SIGXFSZ", + [SIGVTALRM] = "SIGVTALRM", + [SIGPROF] = "SIGPROF", + [SIGWINCH] = "SIGWINCH", + [SIGIO] = "SIGIO", + [SIGPWR] = "SIGPWR", + [SIGSYS] = "SIGSYS", + [SIGRT1] = "SIGRT1", + [SIGRT2] = "SIGRT2", + [SIGRT3] = "SIGRT3", + [SIGRT4] = "SIGRT4", + [SIGRT5] = "SIGRT5", + [SIGRT6] = "SIGRT6", + [SIGRT7] = "SIGRT7", + [SIGRT8] = "SIGRT8", + }; + + int sig; + + sig = va_arg(f->args, int); + if(sig < 1 || sig >= SIGMAX) + return fmtprint(f, "%d", sig); + return fmtprint(f, "%d [%s]", sig, t[sig]); +} + +/* proc.c */ +extern int procsetalarm(Uproc *proc, vlong t); + +void +alarmtimer(Uproc *proc, vlong now) +{ + Private *p; + Timers *t; + vlong expire, delta; + Usiginfo si; + int i, overrun; + + if((p = proc->signal) == nil) + return; + t = p->t; + for(i=0; i < nelem(t->itimer); i++){ + expire = t->itimer[i].expire; + if(expire <= 0) + continue; + if(now < expire){ + procsetalarm(proc, expire); + continue; + } + overrun = 0; + delta = (t->itimer[i].interval); + if(delta > 0){ + expire += delta; + while(expire <= now){ + expire += delta; + overrun++; + } + procsetalarm(proc, expire); + } else { + expire = 0; + } + t->itimer[i].expire = expire; + + memset(&si, 0, sizeof(si)); + si.signo = SIGALRM; + si.code = SI_TIMER; + si.timer.tid = i; + si.timer.overrun = overrun; + killproc(proc, &si, 1); + } +} diff --git a/linux_emul_base/sockdev.c b/linux_emul_base/sockdev.c new file mode 100644 index 0000000..2197327 --- /dev/null +++ b/linux_emul_base/sockdev.c @@ -0,0 +1,1163 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +typedef struct Socket Socket; +typedef struct Connectproc Connectproc; +typedef struct Listenproc Listenproc; + +enum { + Ctlsize = 128, +}; + +struct Socket +{ + Ufile; + + int family; + int stype; + int protocol; + + int other; + char net[40]; + char name[Ctlsize]; + + int naddr; + uchar addr[40]; + + void *bufproc; + Connectproc *connectproc; + Listenproc *listenproc; + + int connected; + int error; + + Socket *next; +}; + +struct Connectproc +{ + Ref; + QLock; + Socket *sock; + int notefd; + Uwaitq wq; + char str[Ctlsize]; +}; + +struct Listenproc +{ + Ref; + QLock; + Socket *sock; + int notefd; + Uwaitq wq; + Socket *q; + char str[Ctlsize]; +}; + +enum +{ + AF_UNIX =1, + AF_INET =2, + AF_INET6 =10, +}; + +enum +{ + SOCK_STREAM =1, + SOCK_DGRAM =2, + SOCK_RAW =3, +}; + +static char* +srvname(char *npath, char *path, int len) +{ + char *p; + + p = strrchr(path, '/'); + if(p == 0) + p = path; + else + p++; + snprint(npath, len, "/srv/UD.%s", p); + return npath; +} + +static int +srvunixsock(int fd, char *path) +{ + int ret; + int sfd; + char buf[8+Ctlsize+1]; + + sfd = -1; + ret = -1; + if(fd < 0) + goto out; + srvname(buf, path, sizeof(buf)); + remove(buf); + if((sfd = create(buf, OWRITE, 0666)) < 0) + goto out; + sprint(buf, "%d", fd); + if(write(sfd, buf, strlen(buf)) < 0) + goto out; + ret = 0; +out: + if(sfd >= 0) + close(sfd); + return ret; +} + +static void +unsrvunixsock(char *path) +{ + char buf[8+Ctlsize+1]; + + srvname(buf, path, sizeof(buf)); + remove(buf); +} + +static Socket* +allocsock(int family, int stype, int protocol) +{ + Socket *sock; + + sock = kmallocz(sizeof(*sock), 1); + sock->family = family; + sock->stype = stype; + sock->protocol = protocol; + sock->fd = -1; + sock->other = -1; + sock->ref = 1; + sock->dev = SOCKDEV; + sock->mode = O_RDWR; + + return sock; +} + +static int +newsock(int family, int stype, int protocol) +{ + Socket *sock; + char *net; + char buf[Ctlsize]; + int pfd[2]; + int cfd, dfd; + int n; + int err; + + trace("newsock(%d, %d, %d)", family, stype, protocol); + + err = -EINVAL; + switch(family){ + case AF_INET: + case AF_INET6: + switch(stype){ + case SOCK_DGRAM: + net = "udp"; + break; + case SOCK_STREAM: + net = "tcp"; + break; + default: + trace("newsock() unknown socket type %d/%d", family, stype); + return err; + } + break; + case AF_UNIX: + net = nil; + break; + + default: + trace("newsock() unknown network family %d", family); + return err; + } + + sock = allocsock(family, stype, protocol); + cfd = -1; + if(net == nil){ + if(pipe(pfd) < 0){ + err = mkerror(); + goto errout; + } + sock->other = pfd[1]; + sock->fd = pfd[0]; + } else { + snprint(buf, sizeof(buf), "/net/%s/clone", net); + if((cfd = open(buf, ORDWR)) < 0){ + err = mkerror(); + goto errout; + } + n = read(cfd, buf, sizeof(buf)-1); + if(n < 0) + err = mkerror(); + if(n <= 0) + goto errout; + buf[n] = 0; + n = atoi(buf); + snprint(buf, sizeof(buf), "/net/%s/%d/data", net, n); + if((dfd = open(buf, ORDWR)) < 0){ + err = mkerror(); + goto errout; + } + close(cfd); + sock->fd = dfd; + snprint(sock->net, sizeof(sock->net), "/net/%s", net); + snprint(sock->name, sizeof(sock->name), "%s/%d", sock->net, n); + } + return newfd(sock, FD_CLOEXEC); + +errout: + close(cfd); + free(sock); + return err; +} + +static void +freeconnectproc(Connectproc *cp) +{ + if(cp == nil) + return; + qlock(cp); + cp->sock = nil; + if(decref(cp)){ + write(cp->notefd, "interrupt", 9); + qunlock(cp); + return; + } + qunlock(cp); + close(cp->notefd); + free(cp); +} + +static void +freelistenproc(Listenproc *lp) +{ + Socket *q; + + if(lp == nil) + return; + qlock(lp); + lp->sock = nil; + if(decref(lp)){ + write(lp->notefd, "interrupt", 9); + qunlock(lp); + return; + } + while(q = lp->q){ + lp->q = q->next; + putfile(q); + } + qunlock(lp); + close(lp->notefd); + free(lp); +} + +static int +closesock(Ufile *file) +{ + Socket *sock = (Socket*)file; + + close(sock->fd); + close(sock->other); + freebufproc(sock->bufproc); + freeconnectproc(sock->connectproc); + freelistenproc(sock->listenproc); + return 0; +} + + +static void +connectproc(void *aux) +{ + int fd, cfd, other; + char buf[Ctlsize], tmp[8+Ctlsize+1]; + Connectproc *cp; + Socket *sock; + int err; + + cp = (Connectproc*)aux; + qlock(cp); + if((sock = cp->sock) == nil) + goto out; + + snprint(buf, sizeof(buf), "connectproc() %s", cp->str); + setprocname(buf); + + err = 0; + switch(sock->family){ + case AF_UNIX: + fd = sock->fd; + other = sock->other; + qunlock(cp); + + err = -ECONNREFUSED; + srvname(tmp, cp->str, sizeof(buf)); + if((cfd = open(tmp, ORDWR)) < 0) + break; + + memset(buf, 0, sizeof(buf)); + snprint(buf, sizeof(buf), "linuxemu.%d.%lux", getpid(), (ulong)sock); + if(srvunixsock(other, buf) < 0){ + close(cfd); + break; + } + + /* + * write Ctrlsize-1 bytes so concurrent writes will not be merged together as + * Ctrlsize-1 is the size used in read(). see /sys/src/ape/lib/bsd/accept.c:87 + * this should be fixed in ape's connect() as well. + */ + if(write(cfd, buf, sizeof(buf)-1) != sizeof(buf)-1){ + close(cfd); + unsrvunixsock(buf); + break; + } + close(cfd); + if((read(fd, tmp, strlen(buf)) != strlen(buf)) || memcmp(buf, tmp, strlen(buf))){ + unsrvunixsock(buf); + break; + } + unsrvunixsock(buf); + err = 0; + break; + + default: + snprint(buf, sizeof(buf), "%s/ctl", sock->name); + qunlock(cp); + if((cfd = open(buf, ORDWR)) < 0){ + err = mkerror(); + break; + } + if(fprint(cfd, "connect %s", cp->str) < 0) + err = mkerror(); + close(cfd); + } + + qlock(cp); + if((sock = cp->sock) == nil) + goto out; + if(err == 0){ + close(sock->other); + sock->other = -1; + sock->connected = 1; + } + sock->error = err; +out: + wakeq(&cp->wq, MAXPROC); + qunlock(cp); + freeconnectproc(cp); +} + +static int +sockaddr2str(Socket *sock, uchar *addr, int addrlen, char *buf, int nbuf) +{ + int err; + + err = -EINVAL; + switch(sock->family){ + case AF_INET: + if(addrlen < 8) + break; + err = snprint(buf, nbuf, "%d.%d.%d.%d!%d", + (int)(addr[4]), + (int)(addr[5]), + (int)(addr[6]), + (int)(addr[7]), + (int)(((ulong)addr[2]<<8)|(ulong)addr[3])); + break; + + case AF_INET6: + /* TODO */ + break; + + case AF_UNIX: + if(addrlen <= 2) + break; + addrlen -= 2; + if(addrlen >= nbuf) + addrlen = nbuf-1; + memmove(buf, addr+2, addrlen); + buf[addrlen] = 0; + err = addrlen; + break; + } + + return err; +} + +static int +connectsock(Socket *sock, uchar *addr, int addrlen) +{ + Connectproc *cp; + int err; + char buf[Ctlsize]; + int pid; + + if(sock->connected) + return -EISCONN; + if(sock->connectproc) + return -EALREADY; + + if((err = sockaddr2str(sock, addr, addrlen, buf, sizeof(buf))) < 0) + return err; + + cp = kmallocz(sizeof(*cp), 1); + cp->ref = 2; + cp->sock = sock; + strncpy(cp->str, buf, sizeof(cp->str)); + + qlock(cp); + sock->error = 0; + if((pid = procfork(connectproc, cp, 0)) < 0){ + qunlock(cp); + free(cp); + return mkerror(); + } + snprint(buf, sizeof(buf), "/proc/%d/note", pid); + cp->notefd = open(buf, OWRITE); + + if(addrlen > sizeof(sock->addr)) + addrlen = sizeof(sock->addr); + sock->naddr = addrlen; + memmove(sock->addr, addr, addrlen); + + sock->connectproc = cp; + if(sock->mode & O_NONBLOCK){ + qunlock(cp); + return -EINPROGRESS; + } + if((err = sleepq(&cp->wq, cp, 1)) == 0) + err = sock->error; + qunlock(cp); + + /* + * crazy shit is going on! + * see: http://www.madore.org/~david/computers/connect-intr.html + */ + if(err != -EINTR && err != -ERESTART){ + sock->connectproc = nil; + freeconnectproc(cp); + } + return err; +} + +static int +shutdownsock(Socket *sock, int how) +{ + USED(how); + + freebufproc(sock->bufproc); + sock->bufproc = nil; + freeconnectproc(sock->connectproc); + sock->connectproc = nil; + freelistenproc(sock->listenproc); + sock->listenproc = nil; + close(sock->fd); + sock->fd = -1; + sock->connected = 0; + + return 0; +} + +static int +bindsock(Socket *sock, uchar *addr, int addrlen) +{ + int port; + int cfd; + char buf[Ctlsize]; + + port = -1; + switch(sock->family){ + default: + return -EINVAL; + + case AF_UNIX: + break; + case AF_INET: + if(addrlen < 4) + return -EINVAL; + port = (int)(((ulong)addr[2]<<8)|(ulong)addr[3]); + break; + case AF_INET6: + /* TODO */ + return -EINVAL; + } + + if(port >= 0){ + snprint(buf, sizeof(buf), "%s/ctl", sock->name); + if((cfd = open(buf, ORDWR)) < 0) + return mkerror(); + if((fprint(cfd, "announce %d", port) < 0) || (fprint(cfd, "bind %d", port) < 0)){ + close(cfd); + return mkerror(); + } + close(cfd); + } + + if(addrlen > sizeof(sock->addr)) + addrlen = sizeof(sock->addr); + sock->naddr = addrlen; + memmove(sock->addr, addr, addrlen); + + return 0; +} + +static int +strtoip(char *str, uchar *ip, int iplen) +{ + int i, d, v6; + char *p, *k; + + i = 0; + v6 = 1; + memset(ip, 0, iplen); + for(p = str; *p; p++){ + if(*p == ':'){ + if(p[1] == ':'){ + p++; + i = iplen; + for(k = p+1; *k; k++){ + if(*k == ':'){ + v6 = 1; + i -= 2; + } + if(*k == '.'){ + v6 = 0; + i -= 1; + } + } + i -= v6+1; + } else { + i += 2; + } + continue; + } else if(*p == '.'){ + i++; + continue; + } + + for(k = p; *k && *k != '.' && *k != ':'; k++) + ; + if(*k == '.'){ + v6 = 0; + } else if(*k == ':'){ + v6 = 1; + } + + if(i < 0 || i + v6+1 > iplen) + return -1; + + if(*p >= '0' && *p <= '9'){ + d = *p - '0'; + } else if(v6 && (*p >= 'a' && *p <= 'f')){ + d = 0x0A + *p - 'a'; + } else if(v6 && (*p >= 'A' && *p <= 'F')){ + d = 0x0A + *p - 'A'; + } else { + return -1; + } + + if(v6){ + d |= ((int)ip[i]<<12 | (int)ip[i+1]<<4); + ip[i] = (d>>8) & 0xFF; + ip[i+1] = d & 0xFF; + } else { + ip[i] = ip[i]*10 + d; + } + } + + return i + v6+1; +} + +static int +getsockaddr(Socket *sock, int remote, uchar *addr, int len) +{ + char buf[Ctlsize]; + char *p; + uchar *a; + int fd; + int n, port; + + a = addr; + switch(sock->family){ + case AF_UNIX: + if(len < sock->naddr) + break; + memmove(a, sock->addr, sock->naddr); + return sock->naddr; + case AF_INET: + case AF_INET6: + snprint(buf, sizeof(buf), "%s/%s", sock->name, remote?"remote":"local"); + if((fd = open(buf, OREAD)) < 0) + return mkerror(); + if((n = read(fd, buf, sizeof(buf)-1)) < 0){ + close(fd); + return mkerror(); + } + close(fd); + if(n > 0 && buf[n-1] == '\n') + n--; + buf[n] = 0; + break; + default: + return -EINVAL; + } + + if((p = strrchr(buf, '!')) == nil) + return -EINVAL; + *p++ = 0; + port = atoi(p); + + trace("getsockaddr(): ip=%s port=%d", buf, port); + + switch(sock->family){ + case AF_INET: + if(len < 8) + break; + if(len > 16) + len = 16; + memset(a, 0, len); + a[0] = sock->family & 0xFF; + a[1] = (sock->family>>8) & 0xFF; + a[2] = (port >> 8) & 0xFF; + a[3] = port & 0xFF; + if(strtoip(buf, &a[4], 4) < 0) + break; + return len; + + case AF_INET6: + /* TODO */ + break; + } + + return -EINVAL; +} + +static void +listenproc(void *aux) +{ + Listenproc *lp; + Socket *sock, *q; + char buf[Ctlsize], tmp[8+Ctlsize+1]; + int cfd, fd, n; + + lp = (Listenproc*)aux; + qlock(lp); + if((sock = lp->sock) == nil) + goto out; + + snprint(buf, sizeof(buf), "listenproc() %s", lp->str); + setprocname(buf); + + for(;;){ + n = 0; + cfd = -1; + switch(sock->family){ + case AF_UNIX: + srvunixsock(sock->other, lp->str); + close(sock->other); + sock->other = -1; + fd = sock->fd; + qunlock(lp); + n = read(fd, buf, sizeof(buf)-1); + qlock(lp); + break; + + default: + snprint(buf, sizeof(buf), "%s/listen", sock->name); + qunlock(lp); + if((cfd = open(buf, ORDWR)) >= 0) + n = read(cfd, buf, sizeof(buf)-1); + qlock(lp); + if(n <= 0) + close(cfd); + } + if(n <= 0) + break; + buf[n] = 0; + + if((sock = lp->sock) == nil){ + close(cfd); + break; + } + + switch(sock->family){ + case AF_UNIX: + srvname(tmp, buf, sizeof(tmp)); + if((fd = open(tmp, ORDWR)) < 0) + break; + unsrvunixsock(buf); + if(write(fd, buf, strlen(buf)) != strlen(buf)){ + close(fd); + fd = -1; + } + buf[0] = 0; + break; + + default: + n = atoi(buf); + snprint(buf, sizeof(buf), "%s/%d", sock->net, n); + snprint(tmp, sizeof(tmp), "%s/data", buf); + fd = open(tmp, ORDWR); + close(cfd); + break; + } + + if(fd < 0) + continue; + + q = allocsock(sock->family, sock->stype, sock->protocol); + strncpy(q->net, sock->net, sizeof(q->net)); + strncpy(q->name, buf, sizeof(q->name)); + + if(sock->family == AF_UNIX){ + memmove(q->addr, sock->addr, q->naddr = sock->naddr); + } else { + q->naddr = getsockaddr(q, 0, q->addr, sizeof(q->addr)); + } + + q->fd = fd; + q->connected = 1; + q->next = lp->q; + lp->q = q; + wakeq(&lp->wq, MAXPROC); + } + + if(sock->family == AF_UNIX) + unsrvunixsock(lp->str); +out: + wakeq(&lp->wq, MAXPROC); + qunlock(lp); + freelistenproc(lp); +} + + +static int +listensock(Socket *sock) +{ + Listenproc *lp; + int pid, err; + char buf[Ctlsize]; + + trace("listensock()"); + + if(sock->listenproc) + return 0; + if((err = sockaddr2str(sock, sock->addr, sock->naddr, buf, sizeof(buf))) < 0) + return err; + + lp = kmallocz(sizeof(*lp), 1); + lp->ref = 2; + lp->sock = sock; + strncpy(lp->str, buf, sizeof(lp->str)); + + qlock(lp); + if((pid = procfork(listenproc, lp, 0)) < 0){ + qunlock(lp); + free(lp); + return mkerror(); + } + snprint(buf, sizeof(buf), "/proc/%d/note", pid); + lp->notefd = open(buf, OWRITE); + sock->listenproc = lp; + qunlock(lp); + + return 0; +} + +static int +getsockname(Socket *sock, uchar *addr, int *paddrlen) +{ + int ret; + + trace("getsockname(%p, %p, %p (%x))", sock, addr, paddrlen, paddrlen ? *paddrlen : 0); + + if(addr == nil || paddrlen == nil) + return -EINVAL; + + ret = sock->naddr; + memmove(addr, sock->addr, ret); + *paddrlen = ret; + + return ret; +} + +static int +getpeername(Socket *sock, uchar *addr, int *paddrlen) +{ + int ret; + + trace("getpeername(%p, %p, %p (%x))", sock, addr, paddrlen, paddrlen ? *paddrlen : 0); + + if(addr == nil || paddrlen == nil) + return -EINVAL; + + if((ret = getsockaddr(sock, 1, addr, *paddrlen)) > 0) + *paddrlen = ret; + return ret; +} + +static int +acceptsock(Socket *sock, uchar *addr, int *paddrlen) +{ + Listenproc *lp; + Socket *nsock; + int err; + + trace("acceptsock(%p, %p, %p (%x))", sock, addr, paddrlen, paddrlen ? *paddrlen : 0); + + if((lp = sock->listenproc) == nil) + return -EINVAL; + + qlock(lp); + for(;;){ + if(nsock = lp->q){ + lp->q = nsock->next; + nsock->next = nil; + qunlock(lp); + + if(addr != nil && paddrlen != nil){ + err = getsockaddr(nsock, 1, addr, *paddrlen); + *paddrlen = err < 0 ? 0 : err; + } + return newfd(nsock, FD_CLOEXEC); + } + + if(sock->mode & O_NONBLOCK){ + err = -EAGAIN; + break; + } + + if((err = sleepq(&lp->wq, lp, 1)) < 0) + break; + } + qunlock(lp); + + return err; +} + +static int +socketpair(int family, int stype, int protocol, int sv[2]) +{ + Socket *sock; + int p[2]; + int i, fd; + + trace("socketpair(%d, %d, %d, %p)", family, stype, protocol, sv); + + if(family != AF_UNIX) + return -EAFNOSUPPORT; + if(pipe(p) < 0) + return mkerror(); + for(i=0; i<2; i++){ + sock = allocsock(family, stype, protocol); + sock->fd = p[i]; + sock->connected = 1; + if((fd = newfd(sock, FD_CLOEXEC)) < 0){ + if(i > 0) + sys_close(sv[0]); + close(p[0]); + close(p[1]); + return fd; + } + sv[i] = fd; + } + return 0; +} + +static void* +bufprocsock(Socket *sock) +{ + if(sock->bufproc == nil) + sock->bufproc = newbufproc(sock->fd); + return sock->bufproc; +} + +static int +pollsock(Ufile *file, void *tab) +{ + Socket *sock = (Socket*)file; + Listenproc *lp; + Connectproc *cp; + + if(!sock->connected){ + if(lp = sock->listenproc){ + qlock(lp); + pollwait(file, &lp->wq, tab); + if(lp->q){ + qunlock(lp); + return POLLIN; + } + qunlock(lp); + } + if(cp = sock->connectproc){ + qlock(cp); + pollwait(file, &cp->wq, tab); + if(sock->error < 0){ + qunlock(cp); + return POLLOUT; + } + qunlock(cp); + } + return 0; + } + + return pollbufproc(bufprocsock(sock), sock, tab); +} + +static int +readsock(Ufile *file, void *buf, int len, vlong) +{ + Socket *sock = (Socket*)file; + int ret; + + if(!sock->connected) + return -ENOTCONN; + if((sock->mode & O_NONBLOCK) || (sock->bufproc != nil)){ + ret = readbufproc(bufprocsock(sock), buf, len, 0, (sock->mode & O_NONBLOCK)); + } else { + if(notifyme(1)) + return -ERESTART; + ret = read(sock->fd, buf, len); + notifyme(0); + if(ret < 0) + ret = mkerror(); + } + return ret; +} + +extern int pipewrite(int fd, void *buf, int len); + +static int +writesock(Ufile *file, void *buf, int len, vlong) +{ + Socket *sock = (Socket*)file; + int ret; + + if(!sock->connected) + return -ENOTCONN; + if(sock->family == AF_UNIX) + return pipewrite(sock->fd, buf, len); + if(notifyme(1)) + return -ERESTART; + ret = write(sock->fd, buf, len); + notifyme(0); + if(ret < 0) + ret = mkerror(); + return ret; +} + +static int +ioctlsock(Ufile *file, int cmd, void *arg) +{ + Socket *sock = (Socket*)file; + + switch(cmd){ + default: + return -ENOTTY; + case 0x541B: + { + int r; + + if(arg == nil) + return -EINVAL; + if((r = nreadablebufproc(bufprocsock(sock))) < 0){ + *((int*)arg) = 0; + return r; + } + *((int*)arg) = r; + } + return 0; + } +} + +static int +sendto(Socket *sock, void *data, int len, int, uchar *, int) +{ + trace("sendto(%p, %p, %d, ...)", sock, data, len); + + return writesock(sock, data, len, sock->off); +} + +static int +recvfrom(Socket *sock, void *data, int len, int flags, uchar *addr, int addrlen) +{ + int ret; + + trace("recvfrom(%p, %p, %d, %x, %p, %d)", sock, data, len, flags, addr, addrlen); + + if(flags & 2){ + if(!sock->connected) + return -ENOTCONN; + ret = readbufproc(bufprocsock(sock), data, len, 1, 1); + } else { + ret = readsock(sock, data, len, sock->off); + } + if(addr){ + memmove(addr, sock->addr, sock->naddr); + } + return ret; +} + +enum { + SOL_SOCKET = 1, + + SO_DEBUG = 1, + SO_REUSEADDR, + SO_TYPE, + SO_ERROR, +}; + +static int +getoptsock(Socket *sock, int lvl, int opt, char *ov, int *ol) +{ + trace("getoptsock(%p, %d, %d, %p, %p)", sock, lvl, opt, ov, ol); + + switch(lvl){ + default: + Default: + return -EINVAL; + + case SOL_SOCKET: + switch(opt){ + default: + goto Default; + case SO_ERROR: + *ol = sizeof(int); + *((int*)ov) = sock->error; + break; + } + break; + } + + return 0; +} + +enum { + SYS_SOCKET=1, + SYS_BIND, + SYS_CONNECT, + SYS_LISTEN, + SYS_ACCEPT, + SYS_GETSOCKNAME, + SYS_GETPEERNAME, + SYS_SOCKETPAIR, + SYS_SEND, + SYS_RECV, + SYS_SENDTO, + SYS_RECVFROM, + SYS_SHUTDOWN, + SYS_SETSOCKOPT, + SYS_GETSOCKOPT, + SYS_SENDMSG, + SYS_RECVMSG, +}; + +int sys_linux_socketcall(int call, int *arg) +{ + Socket *sock; + int ret; + + trace("sys_linux_socketcall(%d, %p)", call, arg); + + if(call == SYS_SOCKET) + return newsock(arg[0], arg[1], arg[2]); + + if(call == SYS_SOCKETPAIR) + return socketpair(arg[0], arg[1], arg[2], (int*)arg[3]); + + if((sock = (Socket*)fdgetfile(arg[0])) == nil) + return -EBADF; + + if(sock->dev != SOCKDEV){ + putfile(sock); + return -ENOTSOCK; + } + + ret = -1; + switch(call){ + case SYS_CONNECT: + ret = connectsock(sock, (void*)arg[1], arg[2]); + break; + case SYS_SENDTO: + ret = sendto(sock, (void*)arg[1], arg[2], arg[3], (void*)arg[4], arg[5]); + break; + case SYS_RECVFROM: + ret = recvfrom(sock, (void*)arg[1], arg[2], arg[3], (void*)arg[4], arg[5]); + break; + case SYS_SEND: + ret = sendto(sock, (void*)arg[1], arg[2], arg[3], nil, 0); + break; + case SYS_RECV: + ret = recvfrom(sock, (void*)arg[1], arg[2], arg[3], nil, 0); + break; + case SYS_GETSOCKNAME: + ret = getsockname(sock, (void*)arg[1], (void*)arg[2]); + break; + case SYS_GETPEERNAME: + ret = getpeername(sock, (void*)arg[1], (void*)arg[2]); + break; + case SYS_SHUTDOWN: + ret = shutdownsock(sock, arg[1]); + break; + case SYS_BIND: + ret = bindsock(sock, (void*)arg[1], arg[2]); + break; + case SYS_LISTEN: + ret = listensock(sock); + break; + case SYS_ACCEPT: + ret = acceptsock(sock, (void*)arg[1], (void*)arg[2]); + break; + case SYS_SETSOCKOPT: + ret = 0; + break; + case SYS_GETSOCKOPT: + ret = getoptsock(sock, (int)arg[1], (int)arg[2], (char*)arg[3], (int*)arg[4]); + break; + case SYS_SENDMSG: + case SYS_RECVMSG: + default: + trace("socketcall(): call %d not implemented", call); + } + + putfile(sock); + + return ret; +} + +static void +fillstat(Ustat *s) +{ + s->mode = 0666 | S_IFSOCK; + s->uid = current->uid; + s->gid = current->gid; + s->size = 0; +} + +static int +fstatsock(Ufile *, Ustat *s) +{ + fillstat(s); + return 0; +}; + +static Udev sockdev = +{ + .read = readsock, + .write = writesock, + .poll = pollsock, + .close = closesock, + .ioctl = ioctlsock, + .fstat = fstatsock, +}; + +void sockdevinit(void) +{ + devtab[SOCKDEV] = &sockdev; +} diff --git a/linux_emul_base/stat.c b/linux_emul_base/stat.c new file mode 100644 index 0000000..0a0aaaa --- /dev/null +++ b/linux_emul_base/stat.c @@ -0,0 +1,437 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +int +ufstat(int fd, Ustat *ps) +{ + Ufile *f; + int err; + + err = -EBADF; + if((f = fdgetfile(fd)) == nil) + goto out; + err = -EPERM; + if(devtab[f->dev]->fstat == nil) + goto out; + memset(ps, 0, sizeof(Ustat)); + err = devtab[f->dev]->fstat(f, ps); +out: + putfile(f); + return err; +} + +struct linux_stat { + ushort st_dev; + ushort __pad1; + ulong st_ino; + ushort st_mode; + ushort st_nlink; + ushort st_uid; + ushort st_gid; + ushort st_rdev; + ushort __pad2; + ulong st_size; + ulong st_blksize; + ulong st_blocks; + ulong st_atime; + ulong __unused1; + ulong st_mtime; + ulong __unused2; + ulong st_ctime; + ulong __unused3; + ulong __unused4; + ulong __unused5; +}; + +static void +ustat2linuxstat(Ustat *x, struct linux_stat *s) +{ + memset(s, 0, sizeof(*s)); + s->st_dev = x->dev; + s->st_ino = x->ino; + s->st_mode = x->mode; + s->st_nlink = 1; + s->st_uid = x->uid; + s->st_gid = x->gid; + s->st_size = x->size; + s->st_rdev = x->rdev; + s->st_blksize = 4096; + s->st_blocks = (x->size+s->st_blksize-1) / s->st_blksize; + s->st_atime = x->atime; + s->st_mtime = x->mtime; + s->st_ctime = x->ctime; +} + + +struct linux_stat64 { + uvlong lst_dev; + uint __pad1; + uint __lst_ino; + uint lst_mode; + uint lst_nlink; + uint lst_uid; + uint lst_gid; + uvlong lst_rdev; + uint __pad2; + vlong lst_size; + uint lst_blksize; + uvlong lst_blocks; + uint lst_atime; + uint lst_atime_nsec; + uint lst_mtime; + uint lst_mtime_nsec; + uint lst_ctime; + uint lst_ctime_nsec; + uvlong lst_ino; +}; + +static void +ustat2linuxstat64(Ustat *x, struct linux_stat64 *s) +{ + memset(s, 0, sizeof(*s)); + s->lst_dev = x->dev; + s->lst_ino = x->ino; + s->__lst_ino = x->ino & 0xFFFFFFFF; + s->lst_mode = x->mode; + s->lst_nlink = 1; + s->lst_uid = x->uid; + s->lst_gid = x->gid; + s->lst_size = x->size; + s->lst_rdev = x->rdev; + s->lst_blksize = 4096; // good as any + s->lst_blocks = (x->size+s->lst_blksize-1) / s->lst_blksize; + s->lst_atime = x->atime; + s->lst_mtime = x->mtime; + s->lst_ctime = x->ctime; +} + +int sys_linux_stat(char *path, void *st) +{ + int err; + Ustat x; + + trace("sys_linux_stat(%s, %p)", path, st); + err = fsstat(path, 0, &x); + if(err < 0) + return err; + ustat2linuxstat(&x, (struct linux_stat*)st); + return err; +} + +int sys_linux_lstat(char *path, void *st) +{ + int err; + Ustat x; + + trace("sys_linux_lstat(%s, %p)", path, st); + + if((path = fsfullpath(path)) == nil) + return -EFAULT; + err = fsstat(path, 1, &x); + free(path); + + if(err < 0) + return err; + ustat2linuxstat(&x, (struct linux_stat*)st); + return err; +} + +int sys_linux_stat64(char *path, void *st) +{ + int err; + Ustat x; + + trace("sys_linux_stat64(%s, %p)", path, st); + + if((path = fsfullpath(path)) == nil) + return -EFAULT; + err = fsstat(path, 0, &x); + free(path); + + if(err < 0) + return err; + ustat2linuxstat64(&x, (struct linux_stat64*)st); + return err; +} + +int sys_linux_lstat64(char *path, void *st) +{ + int err; + Ustat x; + + trace("sys_linux_lstat64(%s, %p)", path, st); + + if((path = fsfullpath(path)) == nil) + return -EFAULT; + err = fsstat(path, 1, &x); + free(path); + + if(err < 0) + return err; + ustat2linuxstat64(&x, (struct linux_stat64*)st); + return err; +} + +int sys_linux_fstat(int fd, void *st) +{ + int err; + Ustat x; + + trace("sys_linux_fstat(%d, %p)", fd, st); + + err = ufstat(fd, &x); + if(err < 0) + return err; + ustat2linuxstat(&x, (struct linux_stat*)st); + return err; +} + +int sys_linux_fstat64(int fd, void *st) +{ + int err; + Ustat x; + + trace("sys_linux_fstat64(%d, %p)", fd, st); + + err = ufstat(fd, &x); + if(err < 0) + return err; + ustat2linuxstat64(&x, (struct linux_stat64*)st); + return err; +} + +static int +getdents(int fd, void *buf, int len, int (*fconv)(Udirent *, void *, int, int)) +{ + Ufile *f; + Udirent *t, *x; + uchar *p, *e; + int o, r, err; + + if((f = fdgetfile(fd)) == nil) + return -EBADF; + o = 0; + p = buf; + e = p + len; + t = f->rdaux; + if(t == nil || f->off == 0){ + f->rdaux = nil; + while(x = t){ + t = t->next; + free(x); + } + if((err = devtab[f->dev]->readdir(f, &t)) <= 0){ + putfile(f); + return err; + } + f->rdaux = t; + } + for(; t; t=t->next){ + /* just calculate size */ + r = fconv(t, nil, 0, e - p); + if(r <= 0) + break; + if(o >= f->off){ + /* convert */ + f->off = o + r; + r = fconv(t, p, t->next ? f->off : 0, e - p); + p += r; + } + o += r; + } + putfile(f); + return p - (uchar*)buf; +} + +Udirent* +newdirent(char *path, char *name, int mode) +{ + Udirent *d; + int nlen; + char *s; + + nlen = strlen(name); + d = kmallocz(sizeof(*d) + nlen + 1, 1); + d->mode = mode; + strcpy(d->name, name); + s = allocpath(path, nil, d->name); + d->ino = hashpath(s); + free(s); + + return d; +} + +struct linux_dirent { + long d_ino; + long d_off; + ushort d_reclen; + char d_name[]; +}; + +static int +udirent2linux(Udirent *u, void *d, int off, int left) +{ + int n; + struct linux_dirent *e = d; + + n = sizeof(*e) + strlen(u->name) + 1; + if(n > left) + return 0; + if(e){ + e->d_ino = u->ino & 0xFFFFFFFF; + e->d_off = off; + e->d_reclen = n; + strcpy(e->d_name, u->name); + } + return n; +} + +struct linux_dirent64 { + uvlong d_ino; + vlong d_off; + ushort d_reclen; + uchar d_type; + char d_name[]; +}; + +static int +udirent2linux64(Udirent *u, void *d, int off, int left) +{ + int n; + struct linux_dirent64 *e = d; + + n = sizeof(*e) + strlen(u->name) + 1; + if(n > left) + return 0; + if(e){ + e->d_ino = u->ino; + e->d_off = off; + e->d_reclen = n; + e->d_type = (u->mode>>12)&15; + strcpy(e->d_name, u->name); + } + return n; +} + +int sys_linux_getdents(int fd, void *buf, int nbuf) +{ + trace("sys_linux_getdents(%d, %p, %x)", fd, buf, nbuf); + + return getdents(fd, buf, nbuf, udirent2linux); +} + +int sys_linux_getdents64(int fd, void *buf, int nbuf) +{ + trace("sys_linux_getdents64(%d, %p, %x)", fd, buf, nbuf); + + return getdents(fd, buf, nbuf, udirent2linux64); +} + +struct linux_statfs { + long f_type; + long f_bsize; + long f_blocks; + long f_bfree; + long f_bavail; + long f_files; + long f_ffree; + long f_fsid[2]; + long f_namelen; + long f_frsize; + long f_spare[5]; +}; + +int sys_statfs(char *name, void *pstatfs) +{ + struct linux_statfs *s = pstatfs; + + trace("sys_statfs(%s, %p)", name, s); + + if((s == nil) || (name == nil)) + return -EINVAL; + + memset(s, 0, sizeof(*s)); + + s->f_namelen = 256; + s->f_bsize = 4096; + s->f_blocks = 0x80000000; + s->f_bavail = s->f_bfree = 0x80000000; + s->f_files = s->f_ffree = 0x40000000; + + if(strncmp(name, "/dev/pts", 8) == 0){ + s->f_type = 0x1cd1; + return 0; + } + + memmove(&s->f_type, "PLN9", 4); + memmove(s->f_fsid, "PLAN9_FS", 8); + + return 0; +} + +int +sys_getxattr(char *path, char *name, void *value, int size) +{ + trace("sys_getxattr(%s, %s, %p, %x)", path, name, value, size); + + return -EOPNOTSUPP; +} + +int +sys_lgetxattr(char *path, char *name, void *value, int size) +{ + trace("sys_lgetxattr(%s, %s, %p, %x)", path, name, value, size); + + return -EOPNOTSUPP; +} + +int +sys_fgetxattr(int fd, char *name, void *value, int size) +{ + Ufile *f; + int err; + + trace("sys_fgetxattr(%d, %s, %p, %x)", fd, name, value, size); + + if((f = fdgetfile(fd)) == nil) + return -EBADF; + err = -EOPNOTSUPP; + putfile(f); + + return err; +} + +int +sys_setxattr(char *path, char *name, void *value, int flags, int size) +{ + trace("sys_setxattr(%s, %s, %p, %x, %x)", path, name, value, flags, size); + + return -EOPNOTSUPP; +} + +int +sys_lsetxattr(char *path, char *name, void *value, int flags, int size) +{ + trace("sys_lsetxattr(%s, %s, %p, %x, %x)", path, name, value, flags, size); + + return -EOPNOTSUPP; +} + +int +sys_fsetxattr(int fd, char *name, void *value, int size, int flags) +{ + Ufile *f; + int err; + + trace("sys_fsetxattr(%d, %s, %p, %x, %x)", fd, name, value, flags, size); + + if((f = fdgetfile(fd)) == nil) + return -EBADF; + err = -EOPNOTSUPP; + putfile(f); + return err; +} diff --git a/linux_emul_base/time.c b/linux_emul_base/time.c new file mode 100644 index 0000000..f11a929 --- /dev/null +++ b/linux_emul_base/time.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +struct linux_timezone +{ + int tz_minuteswest; + int tz_dsttime; +}; + +static struct linux_timezone systz; + +void +inittime(void) +{ + Tm *t; + + boottime = nsec(); + + systz.tz_minuteswest = 0; + systz.tz_dsttime = 0; + + if(t = localtime(time(nil))) + systz.tz_minuteswest = t->tzoff / 60; +} + +int sys_time(long *p) +{ + return time(p); +} + +int sys_clock_gettime(int clock, void *t) +{ + struct linux_timespec *ts = t; + vlong x; + + trace("sys_clock_gettime(%d, %p)", clock, t); + x = nsec(); + ts->tv_sec = (long)(x/1000000000LL); + ts->tv_nsec = (long)(x%1000000000LL); + return 0; +} + +int sys_gettimeofday(void *tvp, void *tzp) +{ + struct linux_timeval *tv = tvp; + struct linux_timezone *tz = tzp; + vlong t; + + trace("sys_gettimeofday(%p, %p)", tvp, tzp); + + t = nsec(); + tv->tv_sec = (long)(t/1000000000LL); + tv->tv_usec = (long)((t%1000000000LL)/1000); + + if(tz) + *tz = systz; + + return 0; +} + +int sys_nanosleep(void *rqp, void *rmp) +{ + struct linux_timespec *req = rqp; + struct linux_timespec *rem = rmp; + vlong t, now; + int err; + + trace("sys_nanosleep(%p, %p)", rqp, rmp); + + if(req == nil) + return -EFAULT; + if(req->tv_sec < 0 || req->tv_nsec < 0 || req->tv_nsec >= 1000000000LL) + return -EINVAL; + + now = nsec(); + if(current->restart->syscall){ + t = current->restart->nanosleep.timeout; + } else { + t = now + req->tv_sec*1000000000LL + req->tv_nsec; + } + + if(now < t){ + if(notifyme(1)) + err = -1; + else { + err = sleep((t - now) / 1000000LL); + notifyme(0); + } + if(err < 0){ + now = nsec(); + if(now < t){ + current->restart->nanosleep.timeout = t; + if(rem != nil){ + t -= now; + rem->tv_sec = (long)(t/1000000000LL); + rem->tv_nsec = (long)(t%1000000000LL); + } + return -ERESTART; + } + } + } + + return 0; +} + +int proctimes(Uproc *p, ulong *t) +{ + char buf[1024], *f[12]; + int fd, n; + + t[0] = t[1] = t[2] = t[3] = 0; + snprint(buf, sizeof(buf), "/proc/%d/status", p->kpid); + if((fd = open(buf, OREAD)) < 0) + return mkerror(); + if((n = read(fd, buf, sizeof(buf)-1)) <= 0){ + close(fd); + return mkerror(); + } + close(fd); + buf[n] = 0; + if(getfields(buf, f, 12, 1, "\t ") != 12) + return -EIO; + t[0] = atoi(f[2])*HZ / 1000; + t[1] = atoi(f[3])*HZ / 1000; + t[2] = atoi(f[4])*HZ / 1000; + t[3] = atoi(f[5])*HZ / 1000; + return 0; +} + +struct linux_tms +{ + long tms_utime; + long tms_stime; + long tms_cutime; + long tms_cstime; +}; + +int sys_times(void *m) +{ + struct linux_tms *x = m; + ulong t[4]; + int err; + + trace("sys_times(%p)", m); + + if(x != nil){ + if((err = proctimes(current, t)) < 0) + return err; + x->tms_utime = t[0]; + x->tms_stime = t[1]; + x->tms_cutime = t[2]; + x->tms_cstime = t[3]; + } + return (HZ*(nsec() - boottime)) / 1000000000LL; +} \ No newline at end of file diff --git a/linux_emul_base/tls.c b/linux_emul_base/tls.c new file mode 100644 index 0000000..7e67b7b --- /dev/null +++ b/linux_emul_base/tls.c @@ -0,0 +1,232 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +enum { + Index, + Type, + Flags, + DPL, + Base, + Limit, + Nfields, +}; + +static int +descempty(struct linux_user_desc *info) +{ + return info->base_addr==0 && info->limit==0 && + info->contents==0 && info->read_exec_only==1 && + info->seg_32bit==0 && info->limit_in_pages==0 && + info->seg_not_present==1 && info->useable==0; +} + +int sys_set_thread_area(void *pinfo) +{ + struct linux_user_desc *info = pinfo; + char buf[1024]; + char *p, *e, *f[Nfields]; + int n, fd, idx, err; + + trace("sys_set_thread_area(%p)", pinfo); + + err = -ENOSYS; + if((fd = open("/dev/gdt", ORDWR)) < 0) + goto out; + + idx = info->entry_number; + if(idx == -1){ + err = -ESRCH; + if((n = read(fd, buf, sizeof(buf)-1)) <= 0) + goto out; + buf[n] = 0; + p = buf; + while(e = strchr(p, '\n')){ + *e = 0; + if(getfields(p, f, nelem(f), 1, " ") != nelem(f)) + goto out; + idx = strtoul(f[Index], nil, 16); + if(idx >= 8*sizeof(current->tlsmask)) + break; + if((current->tlsmask & (1<= 8*sizeof(current->tlsmask)) + goto out; + + buf[0] = 0; + if(!info->seg_not_present) + strcat(buf, "P"); + if(info->limit_in_pages) + strcat(buf, "G"); + if(info->useable) + strcat(buf, "U"); + if(info->contents & 2){ + /* code segment */ + if(info->contents & 1) + strcat(buf, "C"); + if(info->seg_32bit) + strcat(buf, "D"); + if(!info->read_exec_only) + strcat(buf, "R"); + if(buf[0] == 0) + strcat(buf, "-"); + + if(fprint(fd, "%x code %s 3 %lux %lux\n", + idx, buf, (ulong)info->base_addr, (ulong)info->limit) < 0) + goto out; + } else { + /* data segment */ + if(info->contents & 1) + strcat(buf, "E"); + if(info->seg_32bit) + strcat(buf, "B"); + if(!info->read_exec_only) + strcat(buf, "W"); + if(buf[0] == 0) + strcat(buf, "-"); + + if(fprint(fd, "%x data %s 3 %lux %lux\n", + idx, buf, (ulong)info->base_addr, (ulong)info->limit) < 0) + goto out; + } + + err = 0; + info->entry_number = idx; + if(!descempty(info)){ + current->tlsmask |= 1<tlsmask &= ~(1<= 0) + close(fd); + return err; +} + +int sys_get_thread_area(void *pinfo) +{ + struct linux_user_desc *info = pinfo; + int err, n, fd, idx; + char buf[1024]; + char *p, *e, *f[Nfields]; + + trace("sys_get_thread_area(%p)", pinfo); + + err = -ENOSYS; + if((fd = open("/dev/gdt", OREAD)) < 0) + goto out; + + err = -EINVAL; + if((n = read(fd, buf, sizeof(buf)-1)) <= 0) + goto out; + buf[n] = 0; + p = buf; + while(e = strchr(p, '\n')){ + *e = 0; + if(getfields(p, f, nelem(f), 1, " ") != nelem(f)) + goto out; + idx = strtoul(f[Index], nil, 16); + if(idx >= 8*sizeof(current->tlsmask)) + break; + if(idx == info->entry_number) + goto found; + p = e+1; + } + goto out; + +found: + info->contents = 0; + if(strcmp(f[Type], "code") == 0) + info->contents |= 2; + info->seg_not_present = 1; + info->limit_in_pages = 0; + info->seg_32bit = 0; + info->read_exec_only = 1; + info->useable = 0; + for(p = f[Flags]; *p; p++){ + switch(*p){ + case 'P': + info->seg_not_present = 0; + break; + case 'G': + info->limit_in_pages = 1; + break; + case 'B': + case 'D': + info->seg_32bit = 1; + break; + case 'W': + case 'R': + info->read_exec_only = 0; + break; + case 'U': + info->useable = 1; + break; + case 'E': + case 'C': + info->contents |= 1; + break; + } + } + + info->base_addr = strtoul(f[Base], nil, 16); + info->limit = strtoul(f[Limit], nil, 16); + + err = 0; + +out: + if(fd >= 0) + close(fd); + return err; +} + +static void +cleardesc(struct linux_user_desc *info) +{ + info->base_addr=0; + info->limit=0; + info->contents=0; + info->read_exec_only=1; + info->seg_32bit=0; + info->limit_in_pages=0; + info->seg_not_present=1; + info->useable=0; +} + +void inittls(void) +{ + struct linux_user_desc info; + int i; + + for(i=0; i<8*sizeof(current->tlsmask); i++){ + if((current->tlsmask & (1 << i)) == 0) + continue; + cleardesc(&info); + info.entry_number = i; + sys_set_thread_area(&info); + } + current->tlsmask = 0; +} + +void clonetls(Uproc *new) +{ + new->tlsmask = current->tlsmask; +} + +int sys_modify_ldt(int func, void *data, int count) +{ + trace("sys_modify_ldt(%d, %p, %x)", func, data, count); + + return -ENOSYS; +} diff --git a/linux_emul_base/trace.c b/linux_emul_base/trace.c new file mode 100644 index 0000000..2207786 --- /dev/null +++ b/linux_emul_base/trace.c @@ -0,0 +1,107 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" + +#undef trace + +static char magic[] = "TRACEBUF"; + +typedef struct Tracebuf Tracebuf; +struct Tracebuf +{ + char magic[8]; + int wp; + char lines[256][80]; +}; + +static void* +alloctrace(void) +{ + Tracebuf *t; + + t = kmallocz(sizeof(*t), 1); + memmove(t->magic, magic, sizeof(t->magic)); + return t; +} + +static void +checktrace(Tracebuf *t) +{ + if(memcmp(t->magic, magic, sizeof(t->magic)) != 0) + panic("tracebuffer corrupted"); +} + +static void +freetrace(Tracebuf *t) +{ + if(t == nil) + return; + checktrace(t); + memset(t, 0, sizeof(*t)); + free(t); +} + +static void +vputtrace(Tracebuf *t, char *fmt, va_list a) +{ + char *s; + + checktrace(t); + s = t->lines[t->wp++ % nelem(t->lines)]; + vsnprint(s, sizeof(t->lines[0]), fmt, a); + if(debug > 1) + fprint(2, "%d\t%s\n", (current != nil) ? current->tid : 0, s); +} + +void inittrace(void) +{ + if(debug > 0) + current->trace = alloctrace(); +} + +void exittrace(Uproc *proc) +{ + Tracebuf *t; + + if(t = proc->trace){ + proc->trace = nil; + freetrace(t); + } +} + +void clonetrace(Uproc *new, int copy) +{ + Tracebuf *t; + + if((t = current->trace) == nil){ + new->trace = nil; + return; + } + + if(copy){ + Tracebuf *x; + + x = kmalloc(sizeof(*t)); + memmove(x, t, sizeof(*t)); + new->trace = x; + + return; + } + + new->trace = alloctrace(); +} + +void tprint(char *fmt, ...) +{ + va_list a; + Uproc *p; + + p = current; + if(p && p->trace){ + va_start(a, fmt); + vputtrace((Tracebuf*)p->trace, fmt, a); + va_end(a); + } +} diff --git a/linux_emul_base/trap.c b/linux_emul_base/trap.c new file mode 100644 index 0000000..eda75a9 --- /dev/null +++ b/linux_emul_base/trap.c @@ -0,0 +1,110 @@ +#include +#include +#include +#include "dat.h" +#include "fns.h" +#include "linux.h" + +#pragma profile off + +void +retuser(void) +{ + Uproc *p; + Ureg *u; + + p = current; + u = p->ureg; + p->ureg = nil; + if(p->innote == 0) + jumpureg(u); + p->innote = 0; + noted(NCONT); +} + +static void +handletrap(void *v, char *m) +{ + Uproc *p; + Usiginfo si; + + p = current; + p->innote = 1; + p->ureg = v; + + if(strncmp(m, "interrupt", 9) == 0){ + if(p->notified){ + p->notified = 0; + } else { + memset(&si, 0, sizeof(si)); + si.signo = SIGINT; + sendsignal(p, &si, 0); + } + goto handled; + } + + if(p->traceproc) + goto traced; + + if(strncmp(m, "sys: trap: general protection violation", 39) == 0) + if(linuxcall() == 0) + goto handled; + + if(strncmp(m, "sys: write on closed pipe", 25) == 0) + goto handled; + + if(strncmp(m, "sys: trap: invalid opcode", 25) == 0){ + memset(&si, 0, sizeof(si)); + si.signo = SIGILL; + si.code = ILL_ILLOPC; + si.fault.addr = (void*)p->ureg->pc; + sendsignal(p, &si, 0); + goto handled; + } + + if(strncmp(m, "sys: trap: divide error", 23) == 0){ + memset(&si, 0, sizeof(si)); + si.signo = SIGFPE; + si.code = FPE_INTDIV; + si.fault.addr = (void*)p->ureg->pc; + sendsignal(p, &si, 0); + goto handled; + } + + if(strncmp(m, "sys: trap: overflow", 19) == 0){ + memset(&si, 0, sizeof(si)); + si.signo = SIGFPE; + si.code = FPE_INTOVF; + si.fault.addr = (void*)p->ureg->pc; + sendsignal(p, &si, 0); + goto handled; + } + + trace("handletrap: %s", m); + if(debug) + noted(NDFLT); + + exitproc(p, SIGKILL, 1); + +handled: + if(p->traceproc) +traced: p->traceproc(p->tracearg); + + handlesignals(); + retuser(); +} + +#pragma profile on + + +void inittrap(void) +{ + ulong f; + + /* disable FPU faults */ + f = getfcr(); + f &= ~(FPINEX|FPOVFL|FPUNFL|FPZDIV|FPINVAL); + setfcr(f); + + notify(handletrap); +} diff --git a/bsd_man2_all b/ref/bsd_man2_all similarity index 100% rename from bsd_man2_all rename to ref/bsd_man2_all -- 2.41.0